trainUs/tests/test_step27_check_for_updates.py
David 63f2f2ba24
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
Add last manual check update date
2026-07-24 09:35:48 +02:00

150 lines
6.1 KiB
Python

"""
Backlog — Manual "Check for updates" button in Settings
- test_check_for_update_button_visible: The button renders in the "Check Updates" section
- test_check_for_update_calls_registration_update: Clicking the button forces a re-check (registration.update())
- test_check_for_update_shows_up_to_date_when_nothing_found: No update found -> a transient "up to date" message appears, then disappears
- test_check_for_update_found_still_offers_choice: A manual check that finds an update still shows the Update now / Later modal, not an auto-apply
- test_up_to_date_message_not_shown_when_update_found: The "up to date" heuristic does not fire when a check does find an update
- test_last_check_date_shown_never_checked_initially: Fresh DB shows "never checked manually"
- test_last_check_date_updates_after_click_and_persists: Clicking the button stamps the last-checked date, which survives a reload
Uses the dev-only test hook `window.__appUpdateTest.mockCheckFn` (see
src/composables/useAppUpdate.js) as a stand-in for `registration.update()` —
simulating a real service-worker update in Playwright would require two
production builds (see Step 24). page.clock drives the "no update found"
timeout heuristic deterministically (as in Step 15/26).
"""
import re
from playwright.sync_api import Page, expect
def _wait_for_db(page: Page, app_url: str):
page.goto(app_url)
page.wait_for_function(
"document.documentElement.getAttribute('data-db-ready') === 'true'",
timeout=15000,
)
def _go_to_settings(page: Page, app_url: str):
"""Navigate to Settings, waiting for DB and the dev test hook to be ready."""
_wait_for_db(page, app_url)
page.click('a[href="#/settings"]')
page.wait_for_selector('[data-testid="settings-language"]')
page.wait_for_function("!!window.__appUpdateTest", timeout=10000)
def _mock_check_fn_no_update(page: Page):
"""Mock registration.update() as a no-op: the check resolves but nothing changes."""
page.evaluate("""() => {
window.__checkCalls = []
window.__appUpdateTest.mockCheckFn = () => {
window.__checkCalls.push(true)
return Promise.resolve()
}
}""")
def _mock_check_fn_found_update(page: Page):
"""Mock registration.update() as if it found a new SW: flips `needRefresh`."""
page.evaluate("""() => {
window.__checkCalls = []
window.__appUpdateTest.mockCheckFn = () => {
window.__checkCalls.push(true)
window.__appUpdateTest.trigger()
return Promise.resolve()
}
}""")
def test_check_for_update_button_visible(page: Page, app_url: str):
"""The 'Check for updates' button renders in the new Application card."""
_go_to_settings(page, app_url)
expect(page.locator('[data-testid="settings-check-for-update"]')).to_be_visible()
def test_check_for_update_calls_registration_update(page: Page, app_url: str):
"""Clicking the button calls through to registration.update()."""
_go_to_settings(page, app_url)
_mock_check_fn_no_update(page)
page.locator('[data-testid="settings-check-for-update"]').click()
calls = page.evaluate("window.__checkCalls")
assert calls == [True], f"registration.update() should have been called, got {calls}"
def test_check_for_update_shows_up_to_date_when_nothing_found(page: Page, app_url: str):
"""No update found: a transient 'up to date' message appears, then disappears."""
page.clock.install()
_go_to_settings(page, app_url)
_mock_check_fn_no_update(page)
up_to_date = page.locator('[data-testid="settings-up-to-date"]')
expect(up_to_date).not_to_be_visible()
page.locator('[data-testid="settings-check-for-update"]').click()
expect(up_to_date).not_to_be_visible() # not shown immediately
page.clock.run_for(2100)
page.wait_for_timeout(100)
expect(up_to_date).to_be_visible()
page.clock.run_for(3100)
page.wait_for_timeout(100)
expect(up_to_date).not_to_be_visible()
def test_check_for_update_found_still_offers_choice(page: Page, app_url: str):
"""A manual check that finds an update still shows Update now / Later — updating stays optional."""
page.clock.install()
_go_to_settings(page, app_url)
_mock_check_fn_found_update(page)
page.locator('[data-testid="settings-check-for-update"]').click()
modal = page.locator('[data-testid="app-update-modal"]')
expect(modal).to_be_visible()
expect(page.locator('[data-testid="app-update-now"]')).to_be_visible()
expect(page.locator('[data-testid="app-update-later"]')).to_be_visible()
def test_up_to_date_message_not_shown_when_update_found(page: Page, app_url: str):
"""The 'up to date' heuristic does not fire when a check does find an update."""
page.clock.install()
_go_to_settings(page, app_url)
_mock_check_fn_found_update(page)
page.locator('[data-testid="settings-check-for-update"]').click()
expect(page.locator('[data-testid="app-update-modal"]')).to_be_visible()
page.clock.run_for(2100)
page.wait_for_timeout(100)
expect(page.locator('[data-testid="settings-up-to-date"]')).not_to_be_visible()
def test_last_check_date_shown_never_checked_initially(page: Page, app_url: str):
"""A fresh DB has never been checked manually, so the fallback text is shown."""
_go_to_settings(page, app_url)
last_check = page.locator('[data-testid="settings-last-update-check"]')
expect(last_check).to_be_visible()
expect(last_check).to_have_text("Never checked manually")
def test_last_check_date_updates_after_click_and_persists(page: Page, app_url: str):
"""Clicking the button stamps a last-checked date, which survives a reload."""
_go_to_settings(page, app_url)
_mock_check_fn_no_update(page)
last_check = page.locator('[data-testid="settings-last-update-check"]')
expect(last_check).to_have_text("Never checked manually")
page.locator('[data-testid="settings-check-for-update"]').click()
expect(last_check).to_have_text(re.compile(r"^Last manual check: "))
page.reload()
page.wait_for_selector('[data-testid="settings-language"]')
expect(last_check).to_have_text(re.compile(r"^Last manual check: "))