I can bind radio

This commit is contained in:
2025-11-07 21:28:19 +01:00
parent cc11e4edaa
commit e8ecf72205
12 changed files with 965 additions and 156 deletions

View File

@@ -3,7 +3,7 @@ from fastcore.basics import NotStr
from fasthtml.components import *
from myfasthtml.test.matcher import matches, StartsWith, Contains, DoesNotContain, Empty, DoNotCheck, ErrorOutput, \
ErrorComparisonOutput
ErrorComparisonOutput, AttributeForbidden
from myfasthtml.test.testclient import MyFT
@@ -23,6 +23,7 @@ from myfasthtml.test.testclient import MyFT
([Div(), Span()], DoNotCheck()),
(NotStr("123456"), NotStr("123")), # for NotStr, only the beginning is checked
(Div(), Div(Empty())),
(Div(attr1="value1"), Div(AttributeForbidden("attr2"))),
(Div(123), Div(123)),
(Div(Span(123)), Div(Span(123))),
(Div(Span(123)), Div(DoNotCheck())),
@@ -49,9 +50,9 @@ def test_i_can_match(actual, expected):
(Div(attr1="value1"), Div(attr1=Contains("value2")), "The condition 'Contains(value2)' is not satisfied"),
(Div(attr1="value1 value2"), Div(attr1=DoesNotContain("value2")), "The condition 'DoesNotContain(value2)'"),
(NotStr("456"), NotStr("123"), "Notstr values are different"),
(Div(attr="value"), Div(Empty()), "Actual is not empty"),
(Div(120), Div(Empty()), "Actual is not empty"),
(Div(Span()), Div(Empty()), "Actual is not empty"),
(Div(attr="value"), Div(Empty()), "The condition 'Empty()' is not satisfied"),
(Div(120), Div(Empty()), "The condition 'Empty()' is not satisfied"),
(Div(Span()), Div(Empty()), "The condition 'Empty()' is not satisfied"),
(Div(), Div(Span()), "Actual is lesser than expected"),
(Div(), Div(123), "Actual is lesser than expected"),
(Div(Span()), Div(Div()), "The elements are different"),
@@ -59,6 +60,7 @@ def test_i_can_match(actual, expected):
(Div(123), Div(456), "The values are different"),
(Div(Span(), Span()), Div(Span(), Div()), "The elements are different"),
(Div(Span(Div())), Div(Span(Span())), "The elements are different"),
(Div(attr1="value1"), Div(AttributeForbidden("attr1")), "condition 'AttributeForbidden(attr1)' is not satisfied"),
])
def test_i_can_detect_errors(actual, expected, error_message):
with pytest.raises(AssertionError) as exc_info:

View File

@@ -16,14 +16,9 @@ This test suite covers:
from dataclasses import dataclass
import pytest
from fasthtml.components import (
Input, Label
)
from fasthtml.fastapp import fast_app
from myfasthtml.controls.helpers import mk
from myfasthtml.core.bindings import Binding
from myfasthtml.test.testclient import MyTestClient
from myfasthtml.test.testclient import MyTestClient, TestableRadio
@dataclass
@@ -43,66 +38,67 @@ def rt(test_app):
@pytest.fixture
def user(test_app):
def test_client(test_app):
return MyTestClient(test_app)
class TestBindingRadio:
"""Tests for binding Radio button components."""
def test_i_can_read_not_selected_radio(test_client):
html = '''<input type="radio" name="radio_name" value="option1" />'''
def test_i_can_bind_radio_buttons(self, user, rt):
"""
Radio buttons should bind with data.
Selecting a radio should update the label.
"""
@rt("/")
def index():
data = Data("option1")
radio1 = Input(type="radio", name="radio_name", value="option1", checked=True)
radio2 = Input(type="radio", name="radio_name", value="option2")
radio3 = Input(type="radio", name="radio_name", value="option3")
label_elt = Label()
mk.manage_binding(radio1, Binding(data))
mk.manage_binding(radio2, Binding(data))
mk.manage_binding(radio3, Binding(data))
mk.manage_binding(label_elt, Binding(data))
return radio1, radio2, radio3, label_elt
user.open("/")
user.should_see("option1")
# Select second radio
testable_radio2 = user.find_element("input[value='option2']")
testable_radio2.select()
user.should_see("option2")
# Select third radio
testable_radio3 = user.find_element("input[value='option3']")
testable_radio3.select()
user.should_see("option3")
input_elt = TestableRadio(test_client, html)
def test_radio_initial_state(self, user, rt):
"""
Radio buttons should initialize with correct checked state.
"""
@rt("/")
def index():
data = Data("option2")
radio1 = Input(type="radio", name="radio_name", value="option1")
radio2 = Input(type="radio", name="radio_name", value="option2", checked=True)
radio3 = Input(type="radio", name="radio_name", value="option3")
label_elt = Label()
mk.manage_binding(radio1, Binding(data))
mk.manage_binding(radio2, Binding(data))
mk.manage_binding(radio3, Binding(data))
mk.manage_binding(label_elt, Binding(data))
return radio1, radio2, radio3, label_elt
user.open("/")
user.should_see("option2")
assert input_elt.name == "radio_name"
assert input_elt.value is None
def test_i_can_read_selected_radio(test_client):
html = '''<input type="radio" name="radio_name" value="option1" checked="true"/>'''
input_elt = TestableRadio(test_client, html)
assert input_elt.name == "radio_name"
assert input_elt.value == "option1"
def test_i_cannot_read_radio_with_multiple_values(test_client):
html = '''
<input type="radio" name="radio_name" value="option1" checked="true" />
<input type="radio" name="radio_name" value="option2" />
'''
with pytest.raises(AssertionError) as exc_info:
TestableRadio(test_client, html)
assert "Only one radio button per name is supported" in str(exc_info.value)
def test_i_cannot_read_radio_when_no_radio_button(test_client):
html = '''
<input type="text" name="radio_name" value="option1" checked="true" /> '''
with pytest.raises(AssertionError) as exc_info:
TestableRadio(test_client, html)
assert "No radio buttons found" in str(exc_info.value)
def test_i_can_read_input_with_label(test_client):
html = '''<label for="uid">John Doe</label><input id="uid" type="radio" name="username" value="john_doe" />'''
input_elt = TestableRadio(test_client, html)
assert input_elt.fields_mapping == {"John Doe": "username"}
assert input_elt.name == "username"
assert input_elt.value is None
def test_i_can_send_values(test_client, rt):
html = '''<input type="text" name="username" type="radio" value="john_doe" hx_post="/submit"/>'''
@rt('/submit')
def post(username: str):
return f"Input received {username=}"
input_elt = TestableRadio(test_client, html)
input_elt.select()
assert test_client.get_content() == "Input received username='john_doe'"

View File

@@ -15,12 +15,15 @@ This test suite covers:
from dataclasses import dataclass
import pytest
from fasthtml.components import (
Input, Label, Textarea
)
from fasthtml.fastapp import fast_app
from myfasthtml.controls.helpers import mk
from myfasthtml.core.bindings import Binding
from myfasthtml.test.testclient import MyTestClient
@dataclass
@@ -47,6 +50,22 @@ class ListData:
self.value = []
@pytest.fixture
def test_app():
test_app, rt = fast_app(default_hdrs=False)
return test_app
@pytest.fixture
def rt(test_app):
return test_app.route
@pytest.fixture
def user(test_app):
return MyTestClient(test_app)
class TestBindingEdgeCases:
"""Tests for edge cases and special scenarios."""

View File

@@ -49,3 +49,10 @@ def test_i_can_send_values(test_client, rt):
input_elt.send("another name")
assert test_client.get_content() == "Input received username='another name'"
def i_can_find_input_by_name(test_client):
html = '''<label for="uid">Username</label><input id="uid" name="username" value="john_doe" />'''
test_client.find_input("username")
assert False