mirror of
https://github.com/Sea-Haven-Industries/meal-order-manager.git
synced 2026-10-07 16:18:54 +00:00
test(form): cover a11y labels, status region, and desc toggle
Structural checks for dual status nodes and no alert(); Playwright for init-time qty labels, aria-pressed chips, overflow More/Less, and failed-submit text landing in role=alert.
This commit is contained in:
parent
d05de42104
commit
cf8ad2baea
1 changed files with 119 additions and 0 deletions
|
|
@ -171,6 +171,26 @@ class TestGenerateFormStructural:
|
|||
assert "data-meal-index" in html
|
||||
assert "data-qty-delta" in html
|
||||
|
||||
def test_a11y_status_and_live_total_markup(self):
|
||||
html = _render()
|
||||
assert 'id="form-status-alert"' in html
|
||||
assert 'id="form-status-info"' in html
|
||||
assert 'id="admin-status-alert"' in html
|
||||
assert 'id="admin-status-info"' in html
|
||||
assert 'role="alert"' in html
|
||||
assert 'role="status"' in html
|
||||
assert 'aria-live="polite"' in html
|
||||
assert 'id="success-heading"' in html
|
||||
assert 'tabindex="-1"' in html
|
||||
|
||||
def test_no_alert_dialogs_in_form_scripts(self):
|
||||
form_js = (SERVER_DIR / "templates" / "form.js").read_text()
|
||||
admin_js = (SERVER_DIR / "templates" / "admin.js").read_text()
|
||||
assert "alert(" not in form_js
|
||||
assert "alert(" not in admin_js
|
||||
# confirm() retained for destructive admin delete / zero-qty save.
|
||||
assert "confirm(" in admin_js
|
||||
|
||||
def test_design_tokens_only_in_definitions(self):
|
||||
css = (SERVER_DIR / "templates" / "form.css").read_text()
|
||||
# Strip :root block, then assert brand/muted/border hexes are gone
|
||||
|
|
@ -273,6 +293,105 @@ class TestGenerateFormPlaywright:
|
|||
assert page.locator("#count-display").inner_text() == "2 meals"
|
||||
assert page.locator("#total-display").inner_text() == "$10.80"
|
||||
|
||||
def test_qty_labels_present_after_init(self, browser_page):
|
||||
page = browser_page
|
||||
card = page.locator('[data-meal-index="0"]')
|
||||
assert (
|
||||
card.locator('[data-qty-delta="-1"]').get_attribute("aria-label")
|
||||
== "Remove one Chicken Bowl"
|
||||
)
|
||||
assert (
|
||||
card.locator('[data-qty-delta="1"]').get_attribute("aria-label")
|
||||
== "Add one Chicken Bowl"
|
||||
)
|
||||
assert card.locator(".qty").get_attribute("aria-label") == "Quantity of Chicken Bowl"
|
||||
|
||||
def test_filter_chip_aria_pressed(self, browser_page):
|
||||
page = browser_page
|
||||
all_btn = page.locator("#filters button", has_text="All Meals")
|
||||
assert all_btn.get_attribute("aria-pressed") == "true"
|
||||
other = page.locator("#filters button").nth(1)
|
||||
other_label = other.inner_text()
|
||||
other.click()
|
||||
page.wait_for_function(
|
||||
"""(label) => {
|
||||
const active = document.querySelector('#filters button.active');
|
||||
return active && active.textContent === label
|
||||
&& active.getAttribute('aria-pressed') === 'true';
|
||||
}""",
|
||||
arg=other_label,
|
||||
)
|
||||
assert all_btn.get_attribute("aria-pressed") == "false"
|
||||
assert other.get_attribute("aria-pressed") == "true"
|
||||
all_btn.click()
|
||||
page.wait_for_function(
|
||||
"""() => {
|
||||
const active = document.querySelector('#filters button.active');
|
||||
return active && active.textContent === 'All Meals'
|
||||
&& active.getAttribute('aria-pressed') === 'true';
|
||||
}"""
|
||||
)
|
||||
|
||||
def test_desc_more_only_when_overflow(self, browser_page):
|
||||
page = browser_page
|
||||
# Ensure All Meals filter so both short and long cards are visible.
|
||||
page.locator("#filters button", has_text="All Meals").click()
|
||||
page.wait_for_selector('[data-meal-index="0"]')
|
||||
page.wait_for_selector('[data-meal-index="1"]')
|
||||
short_card = page.locator('[data-meal-index="0"]')
|
||||
long_card = page.locator('[data-meal-index="1"]')
|
||||
page.wait_for_function(
|
||||
"""() => {
|
||||
const long = document.querySelector('[data-meal-index="1"] .meal-desc-toggle');
|
||||
return !!long;
|
||||
}"""
|
||||
)
|
||||
assert short_card.locator(".meal-desc-toggle").count() == 0
|
||||
toggle = long_card.locator(".meal-desc-toggle")
|
||||
assert toggle.inner_text() == "More"
|
||||
assert toggle.get_attribute("aria-expanded") == "false"
|
||||
toggle.click()
|
||||
assert toggle.inner_text() == "Less"
|
||||
assert toggle.get_attribute("aria-expanded") == "true"
|
||||
assert "is-expanded" in (
|
||||
long_card.locator(".meal-desc").get_attribute("class") or ""
|
||||
)
|
||||
|
||||
def test_submit_failure_lands_in_alert_region(self, browser_page):
|
||||
page = browser_page
|
||||
page.route(
|
||||
"**/api/submit-order**",
|
||||
lambda route: route.fulfill(
|
||||
status=500,
|
||||
content_type="application/json",
|
||||
body='{"error":"Simulated submit failure"}',
|
||||
),
|
||||
)
|
||||
page.locator("#emp-name").select_option(label="Ada Lovelace")
|
||||
page.evaluate(
|
||||
"""() => {
|
||||
document.querySelectorAll('[data-meal-index] .qty').forEach((input) => {
|
||||
input.value = '0';
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
});
|
||||
}"""
|
||||
)
|
||||
page.wait_for_function(
|
||||
"() => document.getElementById('count-display').textContent === '0 meals'"
|
||||
)
|
||||
page.locator('[data-meal-index="0"] [data-qty-delta="1"]').click()
|
||||
page.wait_for_function(
|
||||
"() => document.getElementById('count-display').textContent === '1 meal'"
|
||||
)
|
||||
page.locator("#submit-btn").click()
|
||||
page.wait_for_function(
|
||||
"""() => document.getElementById('form-status-alert').textContent
|
||||
=== 'Simulated submit failure'"""
|
||||
)
|
||||
assert page.locator("#form-status-alert").inner_text() == "Simulated submit failure"
|
||||
assert page.locator("#form-status-info").inner_text() == ""
|
||||
page.unroute("**/api/submit-order**")
|
||||
|
||||
def test_qty_typein_clamps_negative(self, browser_page):
|
||||
page = browser_page
|
||||
# Reset first card via type-in
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue