660 lines
24 KiB
Python
660 lines
24 KiB
Python
"""
|
|
Step 2 — Settings Tests
|
|
- test_settings_page_loads: Settings page renders all sections
|
|
- test_language_change_to_french: Switching language to French updates all labels reactively
|
|
- test_language_change_persists_reload: Language preference survives page reload
|
|
- test_username_change: Changing user name persists to database
|
|
- test_username_persists_reload: User name survives page reload
|
|
- test_username_empty_rejected: Empty name shows validation error
|
|
- test_username_whitespace_only_rejected: Whitespace-only name is rejected
|
|
- test_uuid_displayed_readonly: UUID is displayed and the field is read-only
|
|
- test_theme_toggle_dark: Switching to dark theme applies data-theme attribute
|
|
- test_theme_persists_reload: Theme preference survives page reload
|
|
- test_download_db_button: Download DB button is present and triggers a download
|
|
- test_restore_db_button: Restore DB button is visible and enabled
|
|
- test_restore_db_cancel_dialog: Declining confirm dialog aborts the restore
|
|
- test_restore_db_flow: Full restore round-trip with spinner and report
|
|
- test_restore_db_invalid_file: Uploading a non-SQLite file shows error report
|
|
- test_restore_db_incomplete_db: Uploading a DB missing required tables shows error
|
|
- test_export_import_enabled: Export/Import buttons are present and enabled (wired in Step 3b)
|
|
- test_initialize_db_button: Initialize DB button is present and enabled
|
|
- test_initialize_db_dialog_requires_checkbox: Confirm button is disabled until checkbox is checked
|
|
- test_initialize_db_dialog_cancel: Canceling the dialog does not reset the database
|
|
- test_initialize_db_flow: Full initialize flow resets database and reloads page
|
|
"""
|
|
import os
|
|
import tempfile
|
|
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_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 _download_via_button(page: Page, trigger_selector: str):
|
|
"""Click a button that opens the save-filename dialog, accept the default name, return the Download."""
|
|
page.locator(trigger_selector).click()
|
|
page.wait_for_selector('[data-testid="save-file-confirm"]')
|
|
with page.expect_download() as download_info:
|
|
page.locator('[data-testid="save-file-confirm"]').click()
|
|
return download_info.value
|
|
|
|
|
|
def test_settings_page_loads(page: Page, app_url: str):
|
|
"""Settings page renders all expected sections."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
expect(page.locator('[data-testid="settings-language"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="settings-username"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="settings-uuid"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="settings-theme"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="settings-export"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="settings-import"]')).to_be_visible()
|
|
|
|
|
|
def test_language_change_to_french(page: Page, app_url: str):
|
|
"""Switching language to French updates nav labels reactively."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Change to French
|
|
page.select_option('[data-testid="settings-language"]', 'fr')
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('language').then(v => v === 'fr')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# Nav labels should now be in French
|
|
expect(page.locator('a[href="#/home"] span')).to_have_text('Accueil')
|
|
expect(page.locator('a[href="#/exercises"] span')).to_have_text('Exercices')
|
|
expect(page.locator('a[href="#/settings"] span')).to_have_text('Réglages')
|
|
|
|
# Reset to English for other tests
|
|
page.select_option('[data-testid="settings-language"]', 'en')
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('language').then(v => v === 'en')",
|
|
timeout=5000,
|
|
)
|
|
|
|
|
|
def test_language_change_persists_reload(page: Page, app_url: str):
|
|
"""Language preference is persisted and restored after reload."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Change to French and wait for DB confirmation
|
|
page.select_option('[data-testid="settings-language"]', 'fr')
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('language').then(v => v === 'fr')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# Reload
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
|
|
# Nav should still be in French
|
|
expect(page.locator('a[href="#/home"] span')).to_have_text('Accueil')
|
|
|
|
# Navigate to settings to check the dropdown value
|
|
page.click('a[href="#/settings"]')
|
|
page.wait_for_selector('[data-testid="settings-language"]')
|
|
expect(page.locator('[data-testid="settings-language"]')).to_have_value('fr')
|
|
|
|
# Reset to English
|
|
page.select_option('[data-testid="settings-language"]', 'en')
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('language').then(v => v === 'en')",
|
|
timeout=5000,
|
|
)
|
|
|
|
|
|
def test_username_change(page: Page, app_url: str):
|
|
"""Changing user name persists to the database."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
username_input = page.locator('[data-testid="settings-username"]')
|
|
username_input.fill('Alice')
|
|
username_input.blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'Alice')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# Verify in DB
|
|
stored = page.evaluate("async () => await window.__repos.settings.get('user_name')")
|
|
assert stored == 'Alice', f"Expected 'Alice', got '{stored}'"
|
|
|
|
# Cleanup: restore default
|
|
username_input.fill('User')
|
|
username_input.blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'User')",
|
|
timeout=5000,
|
|
)
|
|
|
|
|
|
def test_username_persists_reload(page: Page, app_url: str):
|
|
"""User name survives page reload."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
username_input = page.locator('[data-testid="settings-username"]')
|
|
username_input.fill('Bob')
|
|
username_input.blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'Bob')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# Reload and go back to settings
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/settings"]')
|
|
page.wait_for_selector('[data-testid="settings-username"]')
|
|
|
|
expect(page.locator('[data-testid="settings-username"]')).to_have_value('Bob')
|
|
|
|
# Cleanup
|
|
username_input = page.locator('[data-testid="settings-username"]')
|
|
username_input.fill('User')
|
|
username_input.blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'User')",
|
|
timeout=5000,
|
|
)
|
|
|
|
|
|
def test_username_empty_rejected(page: Page, app_url: str):
|
|
"""Empty name shows a validation error and is not saved."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Get original name
|
|
original = page.evaluate("async () => await window.__repos.settings.get('user_name')")
|
|
|
|
username_input = page.locator('[data-testid="settings-username"]')
|
|
username_input.fill('')
|
|
username_input.blur()
|
|
page.wait_for_timeout(500)
|
|
|
|
# Error should be visible
|
|
expect(page.locator('[data-testid="settings-name-error"]')).to_be_visible()
|
|
|
|
# DB should still have the original name
|
|
stored = page.evaluate("async () => await window.__repos.settings.get('user_name')")
|
|
assert stored == original, f"Name should not have changed, got '{stored}'"
|
|
|
|
|
|
def test_username_whitespace_only_rejected(page: Page, app_url: str):
|
|
"""Whitespace-only name is rejected."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
original = page.evaluate("async () => await window.__repos.settings.get('user_name')")
|
|
|
|
username_input = page.locator('[data-testid="settings-username"]')
|
|
username_input.fill(' ')
|
|
username_input.blur()
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="settings-name-error"]')).to_be_visible()
|
|
|
|
stored = page.evaluate("async () => await window.__repos.settings.get('user_name')")
|
|
assert stored == original, f"Name should not have changed, got '{stored}'"
|
|
|
|
|
|
def test_uuid_displayed_readonly(page: Page, app_url: str):
|
|
"""UUID is displayed and the field is read-only."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
uuid_input = page.locator('[data-testid="settings-uuid"]')
|
|
expect(uuid_input).to_be_visible()
|
|
|
|
# Should have readonly attribute
|
|
expect(uuid_input).to_have_attribute('readonly', '')
|
|
|
|
# Value should be a valid UUID
|
|
value = uuid_input.input_value()
|
|
import re
|
|
assert re.match(
|
|
r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
|
|
value,
|
|
), f"UUID field does not contain a valid UUID: {value}"
|
|
|
|
|
|
def test_theme_toggle_dark(page: Page, app_url: str):
|
|
"""Switching to dark theme applies the data-theme attribute on <html>."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Switch to dark
|
|
page.select_option('[data-testid="settings-theme"]', 'dark')
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('theme').then(v => v === 'dark')",
|
|
timeout=5000,
|
|
)
|
|
|
|
theme_attr = page.evaluate("document.documentElement.getAttribute('data-theme')")
|
|
assert theme_attr == 'dark', f"Expected data-theme='dark', got '{theme_attr}'"
|
|
|
|
# Verify CSS variables changed (bg should be dark)
|
|
bg = page.evaluate(
|
|
"getComputedStyle(document.documentElement).getPropertyValue('--bg-color1').trim()"
|
|
)
|
|
assert bg == '#1a1a1a', f"Expected dark bg '#1a1a1a', got '{bg}'"
|
|
|
|
# Reset to light
|
|
page.select_option('[data-testid="settings-theme"]', 'light')
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('theme').then(v => v === 'light')",
|
|
timeout=5000,
|
|
)
|
|
|
|
|
|
def test_theme_persists_reload(page: Page, app_url: str):
|
|
"""Theme preference survives page reload."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Switch to dark and wait for DB confirmation
|
|
page.select_option('[data-testid="settings-theme"]', 'dark')
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('theme').then(v => v === 'dark')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# Reload
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
|
|
# data-theme should still be dark (applied at startup)
|
|
theme_attr = page.evaluate("document.documentElement.getAttribute('data-theme')")
|
|
assert theme_attr == 'dark', f"Expected data-theme='dark' after reload, got '{theme_attr}'"
|
|
|
|
# Go to settings, dropdown should show dark
|
|
page.click('a[href="#/settings"]')
|
|
page.wait_for_selector('[data-testid="settings-theme"]')
|
|
expect(page.locator('[data-testid="settings-theme"]')).to_have_value('dark')
|
|
|
|
# Reset to light
|
|
page.select_option('[data-testid="settings-theme"]', 'light')
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('theme').then(v => v === 'light')",
|
|
timeout=5000,
|
|
)
|
|
|
|
|
|
def test_download_db_button(page: Page, app_url: str):
|
|
"""Download DB button is present and triggers a download."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
download_btn = page.locator('[data-testid="settings-download-db"]')
|
|
expect(download_btn).to_be_visible()
|
|
expect(download_btn).to_be_enabled()
|
|
|
|
# Click and verify a download is initiated
|
|
download = _download_via_button(page, '[data-testid="settings-download-db"]')
|
|
assert download.suggested_filename.startswith('trainus-')
|
|
assert download.suggested_filename.endswith('.sqlite3')
|
|
|
|
|
|
def test_restore_db_button(page: Page, app_url: str):
|
|
"""Restore DB button is visible and enabled."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
restore_btn = page.locator('[data-testid="settings-restore-db"]')
|
|
expect(restore_btn).to_be_visible()
|
|
expect(restore_btn).to_be_enabled()
|
|
|
|
|
|
def test_restore_db_cancel_dialog(page: Page, app_url: str, tmp_path_factory):
|
|
"""Declining the confirm dialog aborts the restore (data unchanged)."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Set a known username
|
|
page.locator('[data-testid="settings-username"]').fill('KeepMe')
|
|
page.locator('[data-testid="settings-username"]').blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'KeepMe')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# Download DB so we have a file to upload
|
|
tmp_dir = tmp_path_factory.mktemp("cancel_dialog")
|
|
db_path = str(tmp_dir / "backup.sqlite3")
|
|
download = _download_via_button(page, '[data-testid="settings-download-db"]')
|
|
download.save_as(db_path)
|
|
|
|
# Change username after download
|
|
page.locator('[data-testid="settings-username"]').fill('Changed')
|
|
page.locator('[data-testid="settings-username"]').blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'Changed')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# Dismiss the confirm dialog
|
|
page.once('dialog', lambda dialog: dialog.dismiss())
|
|
|
|
# Open file chooser and supply the downloaded file
|
|
with page.expect_file_chooser() as fc_info:
|
|
page.locator('[data-testid="settings-restore-db"]').click()
|
|
file_chooser = fc_info.value
|
|
file_chooser.set_files(db_path)
|
|
|
|
# Give a moment for any (unwanted) action — should NOT happen
|
|
page.wait_for_timeout(1000)
|
|
|
|
# Username should still be the changed value
|
|
expect(page.locator('[data-testid="settings-username"]')).to_have_value('Changed')
|
|
# No report should appear
|
|
expect(page.locator('[data-testid="restore-report"]')).not_to_be_visible()
|
|
|
|
|
|
def test_restore_db_flow(page: Page, app_url: str, tmp_path_factory):
|
|
"""Full restore: download DB, change data, restore, verify report and restored data."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# 1. Set a known username and wait for persistence
|
|
page.locator('[data-testid="settings-username"]').fill('BeforeRestore')
|
|
page.locator('[data-testid="settings-username"]').blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'BeforeRestore')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# 2. Download the DB (contains 'BeforeRestore')
|
|
tmp_dir = tmp_path_factory.mktemp("restore_flow")
|
|
db_path = str(tmp_dir / "backup.sqlite3")
|
|
download = _download_via_button(page, '[data-testid="settings-download-db"]')
|
|
download.save_as(db_path)
|
|
|
|
# 3. Change username to something else
|
|
page.locator('[data-testid="settings-username"]').fill('AfterChange')
|
|
page.locator('[data-testid="settings-username"]').blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'AfterChange')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# 4. Accept the confirm dialog automatically
|
|
page.once('dialog', lambda dialog: dialog.accept())
|
|
|
|
# 5. Trigger restore with the downloaded file
|
|
with page.expect_file_chooser() as fc_info:
|
|
page.locator('[data-testid="settings-restore-db"]').click()
|
|
file_chooser = fc_info.value
|
|
file_chooser.set_files(db_path)
|
|
|
|
# 6. Report should appear with success
|
|
report = page.locator('[data-testid="restore-report"]')
|
|
expect(report).to_be_visible(timeout=15000)
|
|
|
|
# Check report indicates success (green header text)
|
|
report_header = report.locator('.report-success')
|
|
expect(report_header).to_be_visible()
|
|
|
|
# 7. Username should now reflect the restored data
|
|
expect(page.locator('[data-testid="settings-username"]')).to_have_value('BeforeRestore')
|
|
|
|
# 8. Dismiss the report
|
|
page.locator('[data-testid="restore-report-dismiss"]').click()
|
|
expect(report).not_to_be_visible()
|
|
|
|
# 2. Download the DB (contains 'BeforeRestore')
|
|
tmp_dir2 = tmp_path_factory.mktemp("restore_flow2")
|
|
db_path2 = str(tmp_dir2 / "backup2.sqlite3")
|
|
download = _download_via_button(page, '[data-testid="settings-download-db"]')
|
|
download.save_as(db_path2)
|
|
|
|
# 3. Change username to something else
|
|
page.locator('[data-testid="settings-username"]').fill('AfterChange')
|
|
page.locator('[data-testid="settings-username"]').blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'AfterChange')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# 4. Accept the confirm dialog automatically
|
|
page.once('dialog', lambda dialog: dialog.accept())
|
|
|
|
# 5. Trigger restore with the downloaded file
|
|
with page.expect_file_chooser() as fc_info:
|
|
page.locator('[data-testid="settings-restore-db"]').click()
|
|
file_chooser = fc_info.value
|
|
file_chooser.set_files(db_path2)
|
|
|
|
# 6. Report should appear with success
|
|
report = page.locator('[data-testid="restore-report"]')
|
|
expect(report).to_be_visible(timeout=15000)
|
|
|
|
# Debug: print report contents if error
|
|
report_text = report.text_content()
|
|
error_msg = page.locator('[data-testid="restore-error-message"]')
|
|
if error_msg.count() > 0:
|
|
assert False, f"Restore failed with error report: {report_text}"
|
|
|
|
# Check report indicates success (green header text)
|
|
report_header = report.locator('.report-success')
|
|
expect(report_header).to_be_visible()
|
|
|
|
# 7. Username should now reflect the restored data
|
|
expect(page.locator('[data-testid="settings-username"]')).to_have_value('BeforeRestore')
|
|
|
|
# 8. Dismiss the report
|
|
page.locator('[data-testid="restore-report-dismiss"]').click()
|
|
expect(report).not_to_be_visible()
|
|
|
|
|
|
def test_restore_db_invalid_file(page: Page, app_url: str):
|
|
"""Uploading a non-SQLite file shows an error report."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Create a temp file with garbage content
|
|
tmp = tempfile.NamedTemporaryFile(suffix='.sqlite3', delete=False)
|
|
tmp.write(b'this is not a sqlite database at all')
|
|
tmp.close()
|
|
|
|
try:
|
|
# Accept the confirm dialog
|
|
page.once('dialog', lambda dialog: dialog.accept())
|
|
|
|
# Trigger restore with the invalid file
|
|
with page.expect_file_chooser() as fc_info:
|
|
page.locator('[data-testid="settings-restore-db"]').click()
|
|
file_chooser = fc_info.value
|
|
file_chooser.set_files(tmp.name)
|
|
|
|
# Report should appear with error
|
|
report = page.locator('[data-testid="restore-report"]')
|
|
expect(report).to_be_visible(timeout=10000)
|
|
|
|
# Check it's an error report
|
|
report_header = report.locator('.report-error')
|
|
expect(report_header).to_be_visible()
|
|
|
|
# Error message should be visible
|
|
expect(page.locator('[data-testid="restore-error-message"]')).to_be_visible()
|
|
finally:
|
|
os.unlink(tmp.name)
|
|
|
|
|
|
def test_restore_db_incomplete_db(page: Page, app_url: str):
|
|
"""Uploading a SQLite DB missing required tables shows an error report."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Create a minimal SQLite DB with no TrainUs tables via the browser
|
|
# (we use page.evaluate to build bytes for a valid but empty SQLite DB)
|
|
db_bytes = page.evaluate("""
|
|
() => {
|
|
// SQLite header for an empty database (100 bytes minimum)
|
|
// We'll use a known minimal valid SQLite file
|
|
const header = new Uint8Array([
|
|
0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66,
|
|
0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00
|
|
]);
|
|
// Return as array for transfer
|
|
return Array.from(header);
|
|
}
|
|
""")
|
|
|
|
# Instead use a simpler approach: create a valid empty SQLite DB file
|
|
# by letting the browser do it
|
|
tmp = tempfile.NamedTemporaryFile(suffix='.sqlite3', delete=False)
|
|
tmp.close()
|
|
|
|
# Write a valid but empty SQLite DB using sqlite3 from Python
|
|
import sqlite3
|
|
conn = sqlite3.connect(tmp.name)
|
|
conn.execute('CREATE TABLE dummy (id INTEGER PRIMARY KEY)')
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
try:
|
|
page.once('dialog', lambda dialog: dialog.accept())
|
|
|
|
with page.expect_file_chooser() as fc_info:
|
|
page.locator('[data-testid="settings-restore-db"]').click()
|
|
file_chooser = fc_info.value
|
|
file_chooser.set_files(tmp.name)
|
|
|
|
# Report should appear with error
|
|
report = page.locator('[data-testid="restore-report"]')
|
|
expect(report).to_be_visible(timeout=10000)
|
|
|
|
report_header = report.locator('.report-error')
|
|
expect(report_header).to_be_visible()
|
|
|
|
expect(page.locator('[data-testid="restore-error-message"]')).to_be_visible()
|
|
finally:
|
|
os.unlink(tmp.name)
|
|
|
|
|
|
def test_export_import_enabled(page: Page, app_url: str):
|
|
"""Export and Import buttons are present and enabled (wired in Step 3b)."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
export_btn = page.locator('[data-testid="settings-export"]')
|
|
import_btn = page.locator('[data-testid="settings-import"]')
|
|
|
|
expect(export_btn).to_be_visible()
|
|
expect(import_btn).to_be_visible()
|
|
expect(export_btn).to_be_enabled()
|
|
expect(import_btn).to_be_enabled()
|
|
|
|
|
|
def test_initialize_db_button(page: Page, app_url: str):
|
|
"""Initialize DB button is present and enabled."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
init_btn = page.locator('[data-testid="settings-initialize-db"]')
|
|
expect(init_btn).to_be_visible()
|
|
expect(init_btn).to_be_enabled()
|
|
|
|
|
|
def test_initialize_db_dialog_requires_checkbox(page: Page, app_url: str):
|
|
"""Confirm button is disabled until the confirmation checkbox is checked."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Open the initialize dialog
|
|
page.locator('[data-testid="settings-initialize-db"]').click()
|
|
|
|
# Dialog should be visible
|
|
dialog = page.locator('[data-testid="initialize-confirm-label"]')
|
|
expect(dialog).to_be_visible()
|
|
|
|
# Confirm button should be disabled initially
|
|
confirm_btn = page.locator('[data-testid="initialize-confirm"]')
|
|
expect(confirm_btn).to_be_disabled()
|
|
|
|
# Check the checkbox
|
|
page.locator('[data-testid="initialize-confirm-checkbox"]').check()
|
|
|
|
# Confirm button should now be enabled
|
|
expect(confirm_btn).to_be_enabled()
|
|
|
|
# Cancel the dialog
|
|
page.locator('[data-testid="initialize-cancel"]').click()
|
|
expect(dialog).not_to_be_visible()
|
|
|
|
|
|
def test_initialize_db_dialog_cancel(page: Page, app_url: str):
|
|
"""Canceling the initialize dialog does not reset the database."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Set a known username
|
|
page.locator('[data-testid="settings-username"]').fill('TestUser')
|
|
page.locator('[data-testid="settings-username"]').blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'TestUser')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# Open the initialize dialog
|
|
page.locator('[data-testid="settings-initialize-db"]').click()
|
|
|
|
# Dialog should be visible
|
|
expect(page.locator('[data-testid="initialize-confirm-label"]')).to_be_visible()
|
|
|
|
# Cancel the dialog
|
|
page.locator('[data-testid="initialize-cancel"]').click()
|
|
|
|
# Dialog should close
|
|
expect(page.locator('[data-testid="initialize-confirm-label"]')).not_to_be_visible()
|
|
|
|
# Username should still be present (database not reset)
|
|
expect(page.locator('[data-testid="settings-username"]')).to_have_value('TestUser')
|
|
|
|
|
|
def test_initialize_db_flow(page: Page, app_url: str):
|
|
"""Full initialize flow: check confirmation, click confirm, page reloads with fresh DB."""
|
|
_go_to_settings(page, app_url)
|
|
|
|
# Set a known username
|
|
page.locator('[data-testid="settings-username"]').fill('WillBeDeleted')
|
|
page.locator('[data-testid="settings-username"]').blur()
|
|
page.wait_for_function(
|
|
"window.__repos.settings.get('user_name').then(v => v === 'WillBeDeleted')",
|
|
timeout=5000,
|
|
)
|
|
|
|
# Open the initialize dialog
|
|
page.locator('[data-testid="settings-initialize-db"]').click()
|
|
|
|
# Dialog should be visible
|
|
expect(page.locator('[data-testid="initialize-confirm-label"]')).to_be_visible()
|
|
|
|
# Check the confirmation checkbox
|
|
page.locator('[data-testid="initialize-confirm-checkbox"]').check()
|
|
|
|
# Click confirm - this will trigger a page reload
|
|
page.locator('[data-testid="initialize-confirm"]').click()
|
|
|
|
# Wait for the page to reload and DB to be ready
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
|
|
# Navigate back to settings
|
|
page.click('a[href="#/settings"]')
|
|
page.wait_for_selector('[data-testid="settings-username"]')
|
|
|
|
# Username should be reset to default 'User'
|
|
expect(page.locator('[data-testid="settings-username"]')).to_have_value('User')
|