/** * timezone-verify.spec.ts — 18-04 round-trip verification * * Verifies the admin Timezone section with the real 18-02 API endpoints. * Runs on desktop profile only (admin UI is desktop-focused). * * NOTE: the IANA picker input is type="text" with list="iana-zones" which gives * it the ARIA combobox role (not textbox) in Chromium. */ import { test, expect } from '@playwright/test'; test.describe('Admin Timezone section — 18-04 round-trip', () => { test.beforeEach(async ({ page }) => { await page.goto('/admin'); await expect(page.getByRole('heading', { name: 'Admin Settings' })).toBeVisible(); // Wait for the Timezone section to load (requires the 18-02 GET endpoint) const tzSection = page.getByRole('region', { name: 'Timezone' }); await expect(tzSection).toBeVisible({ timeout: 15_000 }); }); test('Timezone section is visible on /admin', async ({ page }) => { await expect(page.getByRole('region', { name: 'Timezone' })).toBeVisible(); }); test('Timezone input (combobox) is visible and pre-filled', async ({ page }) => { // ARIA role for is combobox const input = page.getByRole('combobox', { name: 'Household timezone' }); await expect(input).toBeVisible(); const val = await input.inputValue(); expect(val.length, 'Input should have a non-empty timezone').toBeGreaterThan(0); }); test('Save button is disabled when timezone is unchanged', async ({ page }) => { const tzSection = page.getByRole('region', { name: 'Timezone' }); const saveBtn = tzSection.getByRole('button', { name: /Save|Saving/ }); await expect(saveBtn).toBeVisible(); await expect(saveBtn).toBeDisabled(); }); test('Changing the input enables Save', async ({ page }) => { const tzSection = page.getByRole('region', { name: 'Timezone' }); const input = page.getByRole('combobox', { name: 'Household timezone' }); await input.fill('America/Chicago'); const saveBtn = tzSection.getByRole('button', { name: /Save/ }); await expect(saveBtn).toBeEnabled(); }); test('Save persists timezone across reload', async ({ page }) => { const input = page.getByRole('combobox', { name: 'Household timezone' }); const tzSection = page.getByRole('region', { name: 'Timezone' }); // Set to a known value await input.fill('America/Chicago'); const saveBtn = tzSection.getByRole('button', { name: /^Save$/ }); await expect(saveBtn).toBeEnabled(); await saveBtn.click(); // Wait for save to complete (button re-disables when stored === input) await expect(saveBtn).toBeDisabled({ timeout: 15_000 }); // Reload and verify persistence await page.reload(); await expect(page.getByRole('heading', { name: 'Admin Settings' })).toBeVisible(); await expect(tzSection).toBeVisible({ timeout: 15_000 }); const inputAfterReload = page.getByRole('combobox', { name: 'Household timezone' }); await expect(inputAfterReload).toHaveValue('America/Chicago'); // system-default note should be gone after explicit set await expect(tzSection.getByText('Using system default')).toHaveCount(0); }); test('"Use detected" affordance sets input to browser zone', async ({ page }) => { const tzSection = page.getByRole('region', { name: 'Timezone' }); const detectedBtn = tzSection.getByRole('button', { name: /Use detected:/ }); // The button is only visible when detected TZ != stored TZ const count = await detectedBtn.count(); if (count === 0) { test.skip(); return; } // Get the detected zone from the button label const btnText = await detectedBtn.textContent(); const match = btnText?.match(/Use detected:\s*(.+)/); const detectedZone = match?.[1]?.trim(); expect(detectedZone, 'Detected zone should be non-empty').toBeTruthy(); await detectedBtn.click(); const input = page.getByRole('combobox', { name: 'Household timezone' }); await expect(input).toHaveValue(detectedZone!); }); });