trainUs/tests/test_step10_pwa.py
David 352a3d5e88
Some checks are pending
CI / Lint & Format (push) Waiting to run
CI / Build (push) Waiting to run
CI / Tests (${{ matrix.browser }}) (chromium) (push) Waiting to run
CI / Tests (${{ matrix.browser }}) (firefox) (push) Waiting to run
Initial commit
2026-06-18 13:06:57 +02:00

124 lines
5.2 KiB
Python

"""
Step 10 — PWA Finalization & Polish tests.
Tests manifest presence and validity, service worker registration,
icon availability, and install prompt absence in test environment.
"""
import json
import pytest
def test_manifest_link_present(page, app_url):
"""HTML page includes a <link rel=manifest> pointing to /manifest.json."""
page.goto(app_url)
handle = page.query_selector('link[rel="manifest"]')
assert handle is not None, "<link rel='manifest'> not found in <head>"
href = handle.get_attribute("href")
assert href == "/manifest.json", f"Unexpected manifest href: {href}"
def test_manifest_json_valid(page, app_url):
"""manifest.json is reachable, valid JSON, and has required PWA fields."""
response = page.request.get(f"{app_url}/manifest.json")
assert response.status == 200, f"manifest.json returned {response.status}"
manifest = json.loads(response.text())
assert manifest.get("name"), "manifest missing 'name'"
assert manifest.get("display") == "standalone", "manifest display should be 'standalone'"
icons = manifest.get("icons", [])
assert len(icons) >= 2, "manifest should declare at least 2 icons"
sizes = {icon["sizes"] for icon in icons}
assert "192x192" in sizes, "192x192 icon missing from manifest"
assert "512x512" in sizes, "512x512 icon missing from manifest"
def test_icon_192_accessible(page, app_url):
"""icon-192.png is served with a 200 status."""
response = page.request.get(f"{app_url}/icon-192.png")
assert response.status == 200, f"icon-192.png returned {response.status}"
def test_icon_512_accessible(page, app_url):
"""icon-512.png is served with a 200 status."""
response = page.request.get(f"{app_url}/icon-512.png")
assert response.status == 200, f"icon-512.png returned {response.status}"
def test_service_worker_registered(page, app_url):
"""Service worker is registered in the browser after page load."""
page.goto(app_url)
page.wait_for_selector('[data-testid="db-loading"]', state="detached", timeout=15000)
registration = page.evaluate(
"() => navigator.serviceWorker.getRegistration().then(r => !!r)"
)
assert registration, "No service worker registration found"
def test_install_prompt_hidden_by_default(page, app_url):
"""Install prompt button is not shown in the test environment (beforeinstallprompt not fired)."""
page.goto(app_url)
page.wait_for_selector('[data-testid="db-loading"]', state="detached", timeout=15000)
btn = page.query_selector('[data-testid="install-prompt-btn"]')
assert btn is None, "Install prompt button should not appear without beforeinstallprompt"
def test_favicon_linked(page, app_url):
"""HTML page includes a <link rel=icon> for the favicon."""
page.goto(app_url)
handle = page.query_selector('link[rel="icon"]')
assert handle is not None, "<link rel='icon'> not found in <head>"
def test_apple_touch_icon_linked(page, app_url):
"""HTML page includes a <link rel=apple-touch-icon>."""
page.goto(app_url)
handle = page.query_selector('link[rel="apple-touch-icon"]')
assert handle is not None, "<link rel='apple-touch-icon'> not found in <head>"
def test_nav_has_aria_label(page, app_url):
"""Main navigation has an aria-label for screen readers."""
page.goto(app_url)
page.wait_for_selector('[data-testid="db-loading"]', state="detached", timeout=15000)
nav = page.query_selector("nav.navbar")
assert nav is not None, "<nav class='navbar'> not found"
aria_label = nav.get_attribute("aria-label")
assert aria_label, "nav.navbar is missing aria-label"
def test_form_required_fields_marked(page, app_url):
"""Exercise form title input is marked as required."""
page.goto(f"{app_url}/#/exercises/new")
page.wait_for_selector('[data-testid="exercise-title"]', timeout=10000)
required = page.get_attribute('[data-testid="exercise-title"]', "required")
assert required is not None, "Exercise title input should have required attribute"
def test_progress_bar_has_aria_role(page, app_url):
"""Execution progress bar has role=progressbar with ARIA value attributes."""
from playwright.sync_api import expect
# Create an exercise and session first — skip if none exist, just verify structure
page.goto(f"{app_url}/#/exercises")
page.wait_for_selector('[data-testid="db-loading"]', state="detached", timeout=15000)
exercises = page.query_selector_all('[data-testid^="exercise-card"]')
sessions = []
if exercises:
page.goto(f"{app_url}/#/sessions")
sessions = page.query_selector_all('[data-testid^="session-card"]')
if not sessions:
pytest.skip("No sessions available to test execution view progress bar")
session_link = page.query_selector('[data-testid^="session-card"] a')
session_link.click()
page.wait_for_selector('[data-testid="session-execute-btn"]', timeout=5000)
page.click('[data-testid="session-execute-btn"]')
page.wait_for_selector('[data-testid="execute-progress"]', timeout=5000)
bar = page.query_selector('[role="progressbar"]')
assert bar is not None, "Progress bar should have role='progressbar'"
assert bar.get_attribute("aria-valuemin") == "0"
assert bar.get_attribute("aria-valuemax") is not None