trainUs/tests/test_step22_save_filename_prompt.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

163 lines
6.7 KiB
Python

"""
Step 22 — Save Filename Prompt Tests
Green paths:
- test_dialog_prefilled_with_default_name: Dialog opens with stem prefilled and extension shown
- test_custom_filename_is_used: Editing the stem changes the downloaded file's name
- test_enter_confirms_with_default: Pressing Enter accepts the prefilled default name
- test_empty_stem_falls_back_to_default: Clearing the stem and saving falls back to the default name
Red paths:
- test_cancel_aborts_download: Cancelling the dialog produces no download
- test_close_button_aborts_download: Closing the dialog (X button) produces no download
- test_backup_cancel_does_not_update_last_backup_at: Cancelling a backup save leaves last_backup_at untouched
- test_migration_cancel_keeps_update_disabled: Cancelling the forced migration backup leaves Update disabled
"""
import re
from playwright.sync_api import Page, expect
def _wait_for_db(page: Page, app_url: str):
"""Navigate and wait for the database to be ready."""
page.goto(app_url)
page.wait_for_function(
"document.documentElement.getAttribute('data-db-ready') === 'true'", timeout=15000
)
def _go_to_exercises(page: Page, app_url: str):
"""Navigate to the exercises list and wait for it to load."""
_wait_for_db(page, app_url)
page.click('a[href="#/exercises"]')
page.wait_for_timeout(500)
def _go_to_settings(page: Page, app_url: str):
"""Navigate to the settings page and wait for DB."""
_wait_for_db(page, app_url)
page.click('a[href="#/settings"]')
page.wait_for_selector('[data-testid="settings-language"]')
def _set_db_version(page: Page, version: int):
"""Rewrite the schema_version table so it contains only `version`."""
page.evaluate(f"""
(async () => {{
await window.__db.run("DELETE FROM schema_version");
await window.__db.run("INSERT INTO schema_version (version) VALUES ({version})");
}})()
""")
def test_dialog_prefilled_with_default_name(page: Page, app_url: str):
"""Save dialog opens with the input prefilled and the extension shown alongside it."""
_go_to_exercises(page, app_url)
page.locator('[data-testid="exercise-export-btn"]').click()
input_ = page.locator('[data-testid="save-file-input"]')
expect(input_).to_be_visible()
value = input_.input_value()
assert re.match(r"^trainus-exercises-\d{4}-\d{2}-\d{2}$", value), f"Unexpected default stem: {value}"
expect(page.locator('[data-testid="save-file-extension"]')).to_have_text(".json")
page.locator('[data-testid="save-file-cancel"]').click()
def test_custom_filename_is_used(page: Page, app_url: str):
"""Editing the stem changes the downloaded file's name, keeping the original extension."""
_go_to_exercises(page, app_url)
page.locator('[data-testid="exercise-export-btn"]').click()
page.locator('[data-testid="save-file-input"]').fill("my-custom-export")
with page.expect_download() as download_info:
page.locator('[data-testid="save-file-confirm"]').click()
assert download_info.value.suggested_filename == "my-custom-export.json"
def test_enter_confirms_with_default(page: Page, app_url: str):
"""Pressing Enter in the filename field accepts the prefilled default name."""
_go_to_exercises(page, app_url)
page.locator('[data-testid="exercise-export-btn"]').click()
input_ = page.locator('[data-testid="save-file-input"]')
default_stem = input_.input_value()
with page.expect_download() as download_info:
input_.press("Enter")
assert download_info.value.suggested_filename == f"{default_stem}.json"
def test_empty_stem_falls_back_to_default(page: Page, app_url: str):
"""Clearing the filename field and saving falls back to the default name."""
_go_to_exercises(page, app_url)
page.locator('[data-testid="exercise-export-btn"]').click()
input_ = page.locator('[data-testid="save-file-input"]')
default_stem = input_.input_value()
input_.fill("")
with page.expect_download() as download_info:
page.locator('[data-testid="save-file-confirm"]').click()
assert download_info.value.suggested_filename == f"{default_stem}.json"
def test_cancel_aborts_download(page: Page, app_url: str):
"""Cancelling the save dialog triggers no download."""
_go_to_exercises(page, app_url)
page.locator('[data-testid="exercise-export-btn"]').click()
page.wait_for_selector('[data-testid="save-file-input"]')
downloaded = []
page.on("download", lambda d: downloaded.append(d))
page.locator('[data-testid="save-file-cancel"]').click()
page.wait_for_timeout(500)
expect(page.locator('[data-testid="save-file-input"]')).not_to_be_visible()
assert downloaded == [], "No download should occur when the save dialog is cancelled"
def test_close_button_aborts_download(page: Page, app_url: str):
"""Closing the save dialog via the X button triggers no download."""
_go_to_exercises(page, app_url)
page.locator('[data-testid="exercise-export-btn"]').click()
page.wait_for_selector('[data-testid="save-file-input"]')
downloaded = []
page.on("download", lambda d: downloaded.append(d))
page.locator('[data-testid="modal-close"]').click()
page.wait_for_timeout(500)
expect(page.locator('[data-testid="save-file-input"]')).not_to_be_visible()
assert downloaded == [], "No download should occur when the save dialog is closed"
def test_backup_cancel_does_not_update_last_backup_at(page: Page, app_url: str):
"""Cancelling a database backup save leaves last_backup_at untouched."""
_go_to_settings(page, app_url)
before = page.evaluate("async () => await window.__repos.settings.get('last_backup_at')")
page.locator('[data-testid="settings-download-db"]').click()
page.wait_for_selector('[data-testid="save-file-cancel"]')
page.locator('[data-testid="save-file-cancel"]').click()
page.wait_for_timeout(500)
after = page.evaluate("async () => await window.__repos.settings.get('last_backup_at')")
assert after == before, "last_backup_at should not change when the backup save is cancelled"
def test_migration_cancel_keeps_update_disabled(page: Page, app_url: str):
"""Cancelling the forced migration backup leaves the Update button disabled."""
_wait_for_db(page, app_url)
_set_db_version(page, 0)
page.reload()
page.wait_for_function(
"document.documentElement.getAttribute('data-db-migration') === 'pending'", timeout=15000
)
expect(page.locator('[data-testid="migration-update"]')).to_be_disabled()
page.locator('[data-testid="migration-backup"]').click()
page.wait_for_selector('[data-testid="save-file-cancel"]')
page.locator('[data-testid="save-file-cancel"]').click()
expect(page.locator('[data-testid="migration-update"]')).to_be_disabled()