Working on LoginPage tests
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
from fasthtml.fastapp import fast_app
|
||||
|
||||
from myfasthtml.core.testclient import TestableForm
|
||||
from myfasthtml.core.testclient import TestableForm, MyTestClient
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -583,3 +584,339 @@ class TestableFormUpdateFieldValues:
|
||||
f"Expected {{'flag': True}}, got {form.fields}"
|
||||
assert isinstance(form.fields["flag"], bool), \
|
||||
f"Expected bool type, got {type(form.fields['flag'])}"
|
||||
|
||||
|
||||
class TestMyTestClientFill:
|
||||
def test_i_can_fill_form_using_input_name(self, mock_client):
|
||||
"""
|
||||
I can fill using the input name
|
||||
"""
|
||||
html = '<form><label for="uid">Username</label><input id="uid" name="username" /></form>'
|
||||
form = TestableForm(mock_client, html)
|
||||
|
||||
form.fill(username="john_doe")
|
||||
|
||||
assert form.fields == {"username": "john_doe"}
|
||||
|
||||
def test_i_can_fill_form_using_label(self, mock_client):
|
||||
"""
|
||||
I can fill using the label associated with the input
|
||||
"""
|
||||
html = '<form><label for="uid">Username</label><input id="uid" name="username" /></form>'
|
||||
form = TestableForm(mock_client, html)
|
||||
|
||||
form.fill(Username="john_doe")
|
||||
|
||||
assert form.fields == {"username": "john_doe"}
|
||||
|
||||
def test_i_cannot_fill_form_with_invalid_field_name(self, mock_client):
|
||||
"""
|
||||
I cannot fill form with invalid field name
|
||||
"""
|
||||
html = '<form><label for="uid">Username</label><input id="uid" name="username" /></form>'
|
||||
form = TestableForm(mock_client, html)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
form.fill(invalid_field="john_doe")
|
||||
|
||||
assert str(excinfo.value) == "Invalid field name 'invalid_field'."
|
||||
|
||||
|
||||
class TestableFormSubmit:
|
||||
"""
|
||||
Test suite for the submit() method of TestableForm class.
|
||||
This module tests form submission for both HTMX and classic forms.
|
||||
"""
|
||||
|
||||
def test_i_can_submit_classic_form_with_post_method(self):
|
||||
"""
|
||||
Test that a classic form with POST method is submitted correctly.
|
||||
|
||||
This ensures the form uses the action and method attributes properly.
|
||||
"""
|
||||
# HTML form with classic submission
|
||||
html = '''
|
||||
<form action="/submit" method="post">
|
||||
<input type="text" name="username" value="john_doe" />
|
||||
<input type="password" name="password" value="secret123" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
# Create the form
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
@rt('/submit')
|
||||
def post(username: str, password: str):
|
||||
return f"Form received {username=}, {password=}"
|
||||
|
||||
form.submit()
|
||||
assert client.get_content() == "Form received username='john_doe', password='secret123'"
|
||||
|
||||
def test_i_can_submit_classic_form_with_get_method(self):
|
||||
"""
|
||||
Test that a classic form with GET method is submitted correctly.
|
||||
|
||||
This ensures GET requests are properly handled.
|
||||
"""
|
||||
html = '''
|
||||
<form action="/search" method="get">
|
||||
<input type="text" name="q" value="python" />
|
||||
<input type="text" name="page" value="1" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
@rt('/search')
|
||||
def get(q: str, page: int):
|
||||
return f"Search results for {q=}, {page=}"
|
||||
|
||||
form.submit()
|
||||
assert client.get_content() == "Search results for q='python', page=1"
|
||||
|
||||
def test_i_can_submit_classic_form_without_method_defaults_to_post(self):
|
||||
"""
|
||||
Test that POST is used by default when method attribute is absent.
|
||||
|
||||
This ensures proper default behavior matching HTML standards.
|
||||
"""
|
||||
html = '''
|
||||
<form action="/submit">
|
||||
<input type="text" name="data" value="test" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
@rt('/submit')
|
||||
def post(data: str):
|
||||
return f"Received {data=}"
|
||||
|
||||
form.submit()
|
||||
assert client.get_content() == "Received data='test'"
|
||||
|
||||
def test_i_can_submit_form_with_htmx_post(self):
|
||||
"""
|
||||
Test that a form with hx_post uses HTMX submission.
|
||||
|
||||
This ensures HTMX-enabled forms are handled correctly.
|
||||
"""
|
||||
html = '''
|
||||
<form hx-post="/htmx-submit">
|
||||
<input type="text" name="username" value="alice" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
@rt('/htmx-submit')
|
||||
def post(username: str):
|
||||
return f"HTMX received {username=}"
|
||||
|
||||
form.submit()
|
||||
assert client.get_content() == "HTMX received username='alice'"
|
||||
|
||||
def test_i_can_submit_form_with_htmx_get(self):
|
||||
"""
|
||||
Test that a form with hx_get uses HTMX submission.
|
||||
|
||||
This ensures HTMX GET requests work properly.
|
||||
"""
|
||||
html = '''
|
||||
<form hx-get="/htmx-search">
|
||||
<input type="text" name="query" value="fasthtml" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
@rt('/htmx-search')
|
||||
def get(query: str):
|
||||
return f"HTMX search for {query=}"
|
||||
|
||||
form.submit()
|
||||
assert client.get_content() == "HTMX search for query='fasthtml'"
|
||||
|
||||
def test_i_can_submit_form_with_filled_fields(self):
|
||||
"""
|
||||
Test that fields filled via fill_form() are submitted correctly.
|
||||
|
||||
This ensures dynamic form filling works as expected.
|
||||
"""
|
||||
html = '''
|
||||
<form action="/login" method="post">
|
||||
<input type="text" name="username" value="" />
|
||||
<input type="password" name="password" value="" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
# Fill the form dynamically
|
||||
form.fill(username="bob", password="secure456")
|
||||
|
||||
@rt('/login')
|
||||
def post(username: str, password: str):
|
||||
return f"Login {username=}, {password=}"
|
||||
|
||||
form.submit()
|
||||
assert client.get_content() == "Login username='bob', password='secure456'"
|
||||
|
||||
def test_i_can_submit_form_with_mixed_field_types(self):
|
||||
"""
|
||||
Test that all field types are submitted with correct values.
|
||||
|
||||
This ensures type conversion and submission work together.
|
||||
"""
|
||||
html = '''
|
||||
<form action="/register" method="post">
|
||||
<input type="text" name="username" value="charlie" />
|
||||
<input type="number" name="age" value="30" />
|
||||
<input type="checkbox" name="newsletter" checked />
|
||||
<select name="country">
|
||||
<option value="US" selected>USA</option>
|
||||
<option value="FR">France</option>
|
||||
</select>
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
@rt('/register')
|
||||
def post(username: str, age: int, newsletter: bool, country: str):
|
||||
return f"Registration {username=}, {age=}, {newsletter=}, {country=}"
|
||||
|
||||
result = form.submit()
|
||||
|
||||
# Note: the types are converted in self.fields
|
||||
expected = "Registration username='charlie', age=30, newsletter=True, country='US'"
|
||||
assert client.get_content() == expected
|
||||
|
||||
def test_i_cannot_submit_classic_form_without_action(self):
|
||||
"""
|
||||
Test that an exception is raised when action attribute is missing.
|
||||
|
||||
This ensures proper error handling for malformed forms.
|
||||
"""
|
||||
html = '''
|
||||
<form method="post">
|
||||
<input type="text" name="data" value="test" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
# Should raise ValueError
|
||||
try:
|
||||
form.submit()
|
||||
assert False, "Expected ValueError to be raised"
|
||||
except ValueError as e:
|
||||
assert "no 'action' attribute" in str(e).lower()
|
||||
|
||||
def test_i_cannot_submit_classic_form_with_empty_action(self):
|
||||
"""
|
||||
Test that an exception is raised when action attribute is empty.
|
||||
|
||||
This ensures validation of the action attribute.
|
||||
"""
|
||||
html = '''
|
||||
<form action="" method="post">
|
||||
<input type="text" name="data" value="test" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
# Should raise ValueError
|
||||
try:
|
||||
form.submit()
|
||||
assert False, "Expected ValueError to be raised"
|
||||
except ValueError as e:
|
||||
assert "no 'action' attribute" in str(e).lower()
|
||||
|
||||
def test_i_can_submit_form_with_case_insensitive_method(self):
|
||||
"""
|
||||
Test that HTTP method is properly normalized to uppercase.
|
||||
|
||||
This ensures method attribute is case-insensitive.
|
||||
"""
|
||||
html = '''
|
||||
<form action="/submit" method="PoSt">
|
||||
<input type="text" name="data" value="test" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
@rt('/submit')
|
||||
def post(data: str):
|
||||
return f"Received {data=}"
|
||||
|
||||
form.submit()
|
||||
assert client.get_content() == "Received data='test'"
|
||||
|
||||
def test_i_can_prioritize_htmx_over_classic_submission(self):
|
||||
"""
|
||||
Test that HTMX is prioritized even when action/method are present.
|
||||
|
||||
This ensures correct priority between HTMX and classic submission.
|
||||
"""
|
||||
html = '''
|
||||
<form action="/classic" method="post" hx-post="/htmx">
|
||||
<input type="text" name="data" value="test" />
|
||||
</form>
|
||||
'''
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
@rt('/classic')
|
||||
def post_classic(data: str):
|
||||
return "Classic submission"
|
||||
|
||||
@rt('/htmx')
|
||||
def post_htmx(data: str):
|
||||
return "HTMX submission"
|
||||
|
||||
form.submit()
|
||||
assert client.get_content() == "HTMX submission"
|
||||
|
||||
def test_i_can_submit_empty_form(self):
|
||||
"""
|
||||
Test that a form without fields can be submitted.
|
||||
|
||||
This ensures robustness for minimal forms.
|
||||
"""
|
||||
html = '<form action="/empty" method="post"></form>'
|
||||
|
||||
test_app, rt = fast_app(default_hdrs=False)
|
||||
client = MyTestClient(test_app)
|
||||
form = TestableForm(client, html)
|
||||
|
||||
@rt('/empty')
|
||||
def post():
|
||||
return "Empty form received"
|
||||
|
||||
form.submit()
|
||||
assert client.get_content() == "Empty form received"
|
||||
|
||||
Reference in New Issue
Block a user