mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
207 lines
5.9 KiB
Python
207 lines
5.9 KiB
Python
import pytest
|
|
|
|
from textual import on
|
|
from textual.app import App, ComposeResult
|
|
from textual.validation import Number, ValidationResult
|
|
from textual.widgets import Input
|
|
|
|
|
|
class InputApp(App):
|
|
def __init__(self, validate_on=None):
|
|
super().__init__()
|
|
self.messages = []
|
|
self.validator = Number(minimum=1, maximum=5)
|
|
self.validate_on = validate_on
|
|
|
|
def compose(self) -> ComposeResult:
|
|
yield Input(
|
|
validators=self.validator,
|
|
validate_on=self.validate_on,
|
|
)
|
|
|
|
@on(Input.Changed)
|
|
@on(Input.Submitted)
|
|
def on_changed_or_submitted(self, event):
|
|
self.messages.append(event)
|
|
|
|
|
|
async def test_input_changed_message_validation_failure():
|
|
app = InputApp()
|
|
async with app.run_test() as pilot:
|
|
input = app.query_one(Input)
|
|
input.value = "8"
|
|
await pilot.pause()
|
|
assert len(app.messages) == 1
|
|
assert app.messages[0].validation_result == ValidationResult.failure(
|
|
failures=[
|
|
Number.NotInRange(
|
|
value="8",
|
|
validator=app.validator,
|
|
description="Must be between 1 and 5.",
|
|
)
|
|
],
|
|
)
|
|
|
|
|
|
async def test_input_changed_message_validation_success():
|
|
app = InputApp()
|
|
async with app.run_test() as pilot:
|
|
input = app.query_one(Input)
|
|
input.value = "3"
|
|
await pilot.pause()
|
|
assert len(app.messages) == 1
|
|
assert app.messages[0].validation_result == ValidationResult.success()
|
|
|
|
|
|
async def test_input_submitted_message_validation_failure():
|
|
app = InputApp()
|
|
async with app.run_test() as pilot:
|
|
input = app.query_one(Input)
|
|
input.value = "8"
|
|
await input.action_submit()
|
|
await pilot.pause()
|
|
assert len(app.messages) == 2
|
|
assert app.messages[1].validation_result == ValidationResult.failure(
|
|
failures=[
|
|
Number.NotInRange(
|
|
value="8",
|
|
validator=app.validator,
|
|
description="Must be between 1 and 5.",
|
|
)
|
|
],
|
|
)
|
|
|
|
|
|
async def test_input_submitted_message_validation_success():
|
|
app = InputApp()
|
|
async with app.run_test() as pilot:
|
|
input = app.query_one(Input)
|
|
input.value = "3"
|
|
await input.action_submit()
|
|
await pilot.pause()
|
|
assert len(app.messages) == 2
|
|
assert app.messages[1].validation_result == ValidationResult.success()
|
|
|
|
|
|
async def test_on_blur_triggers_validation():
|
|
app = InputApp()
|
|
async with app.run_test() as pilot:
|
|
input = app.query_one(Input)
|
|
input.focus()
|
|
input.value = "3"
|
|
input.remove_class("-valid")
|
|
app.set_focus(None)
|
|
await pilot.pause()
|
|
assert input.has_class("-valid")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"validate_on",
|
|
[
|
|
set(),
|
|
{"blur"},
|
|
{"submitted"},
|
|
{"blur", "submitted"},
|
|
{"fried", "garbage"},
|
|
],
|
|
)
|
|
async def test_validation_on_changed_should_not_happen(validate_on):
|
|
app = InputApp(validate_on)
|
|
async with app.run_test() as pilot:
|
|
# sanity checks
|
|
assert len(app.messages) == 0
|
|
input = app.query_one(Input)
|
|
assert not input.has_class("-valid")
|
|
assert not input.has_class("-invalid")
|
|
|
|
input.value = "3"
|
|
await pilot.pause()
|
|
assert len(app.messages) == 1
|
|
assert app.messages[-1].validation_result is None
|
|
assert not input.has_class("-valid")
|
|
assert not input.has_class("-invalid")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"validate_on",
|
|
[
|
|
set(),
|
|
{"blur"},
|
|
{"changed"},
|
|
{"blur", "changed"},
|
|
{"fried", "garbage"},
|
|
],
|
|
)
|
|
async def test_validation_on_submitted_should_not_happen(validate_on):
|
|
app = InputApp(validate_on)
|
|
async with app.run_test() as pilot:
|
|
# sanity checks
|
|
assert len(app.messages) == 0
|
|
input = app.query_one(Input)
|
|
assert not input.has_class("-valid")
|
|
assert not input.has_class("-invalid")
|
|
|
|
await input.action_submit()
|
|
await pilot.pause()
|
|
assert len(app.messages) == 1
|
|
assert app.messages[-1].validation_result is None
|
|
assert not input.has_class("-valid")
|
|
assert not input.has_class("-invalid")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"validate_on",
|
|
[
|
|
set(),
|
|
{"submitted"},
|
|
{"changed"},
|
|
{"submitted", "changed"},
|
|
{"fried", "garbage"},
|
|
],
|
|
)
|
|
async def test_validation_on_blur_should_not_happen_unless_specified(validate_on):
|
|
app = InputApp(validate_on)
|
|
async with app.run_test() as pilot:
|
|
# sanity checks
|
|
input = app.query_one(Input)
|
|
assert not input.has_class("-valid")
|
|
assert not input.has_class("-invalid")
|
|
|
|
input.focus()
|
|
await pilot.pause()
|
|
app.set_focus(None)
|
|
await pilot.pause()
|
|
assert not input.has_class("-valid")
|
|
assert not input.has_class("-invalid")
|
|
|
|
|
|
async def test_none_validate_on_means_all_validations_happen():
|
|
app = InputApp(None)
|
|
async with app.run_test() as pilot:
|
|
assert len(app.messages) == 0 # sanity checks
|
|
input = app.query_one(Input)
|
|
assert not input.has_class("-valid")
|
|
assert not input.has_class("-invalid")
|
|
|
|
input.value = "3"
|
|
await pilot.pause()
|
|
assert len(app.messages) == 1
|
|
assert app.messages[-1].validation_result is not None
|
|
assert input.has_class("-valid")
|
|
|
|
input.remove_class("-valid")
|
|
|
|
await input.action_submit()
|
|
await pilot.pause()
|
|
assert len(app.messages) == 2
|
|
assert app.messages[-1].validation_result is not None
|
|
assert input.has_class("-valid")
|
|
|
|
input.remove_class("-valid")
|
|
|
|
input.focus()
|
|
await pilot.pause()
|
|
app.set_focus(None)
|
|
await pilot.pause()
|
|
assert input.has_class("-valid")
|