// @ts-check const { test, expect } = require('@playwright/test'); test.describe('Login page', () => { test.beforeEach(async ({ page }) => { await page.goto('/login'); }); test.describe('Branding', () => { test('page title contains Porchlight', async ({ page }) => { await expect(page).toHaveTitle(/Porchlight/); }); test('page title does not contain FastAPI', async ({ page }) => { const title = await page.title(); expect(title).not.toContain('FastAPI'); }); test('favicon link points to /static/favicon.png', async ({ page }) => { await expect(page.locator('link[rel="icon"]')).toHaveAttribute('href', '/static/favicon.png'); }); test('favicon file is served', async ({ request }) => { const resp = await request.get('/static/favicon.png'); expect(resp.ok()).toBe(true); }); }); test.describe('Site header & logo', () => { test('site header is visible', async ({ page }) => { await expect(page.locator('.site-header')).toBeVisible(); }); test('logo image is visible', async ({ page }) => { await expect(page.locator('.site-logo')).toBeVisible(); }); test('logo src is /static/logo.svg', async ({ page }) => { await expect(page.locator('.site-logo')).toHaveAttribute('src', '/static/logo.svg'); }); test('logo SVG file is served', async ({ request }) => { const resp = await request.get('/static/logo.svg'); expect(resp.ok()).toBe(true); }); test('site title text is Porchlight', async ({ page }) => { const siteTitle = page.locator('.site-title'); await expect(siteTitle).toBeVisible(); await expect(siteTitle).toHaveText('Porchlight'); }); }); test.describe('Accessibility', () => { test('skip link is present', async ({ page }) => { await expect(page.locator('.skip-link')).toHaveCount(1); }); test('main landmark with id="main" exists', async ({ page }) => { await expect(page.locator('main#main')).toHaveCount(1); }); test('polite live region exists', async ({ page }) => { await expect(page.locator('#live[aria-live="polite"]')).toHaveCount(1); }); }); test.describe('Login form structure', () => { test('H1 says "Sign in"', async ({ page }) => { await expect(page.locator('h1')).toHaveText('Sign in'); }); test('password login form exists', async ({ page }) => { await expect(page.locator('form[hx-post="/login/password"]')).toHaveCount(1); }); test('username input is visible', async ({ page }) => { await expect(page.locator('#username')).toBeVisible(); }); test('password input is visible', async ({ page }) => { await expect(page.locator('#password')).toBeVisible(); }); test('submit button is visible', async ({ page }) => { await expect(page.locator('form[hx-post="/login/password"] button[type="submit"]')).toBeVisible(); }); test('WebAuthn login button exists', async ({ page }) => { await expect(page.locator('#webauthn-login-btn')).toHaveCount(1); }); }); test.describe('Theme / styling', () => { test('body background is themed', async ({ page }) => { const bgColor = await page.evaluate(() => getComputedStyle(document.body).backgroundColor); expect(['rgb(250, 250, 249)', 'rgb(28, 25, 23)']).toContain(bgColor); }); test('button uses amber accent color', async ({ page }) => { const btnBg = await page.evaluate(() => { const btn = document.querySelector('button[type="submit"]'); return getComputedStyle(btn).backgroundColor; }); expect(['rgb(217, 119, 6)', 'rgb(245, 158, 11)']).toContain(btnBg); }); test('sections have surface background', async ({ page }) => { const sectionBg = await page.evaluate(() => { const section = document.querySelector('section'); return getComputedStyle(section).backgroundColor; }); expect(['rgb(245, 245, 244)', 'rgb(41, 37, 36)']).toContain(sectionBg); }); test('sections have solid border', async ({ page }) => { const sectionBorder = await page.evaluate(() => { const section = document.querySelector('section'); return getComputedStyle(section).borderStyle; }); expect(sectionBorder).toBe('solid'); }); }); test.describe('Static assets', () => { test('style.css is served', async ({ request }) => { const resp = await request.get('/static/style.css'); expect(resp.ok()).toBe(true); }); test('CSS contains --accent custom property', async ({ request }) => { const resp = await request.get('/static/style.css'); const body = await resp.text(); expect(body).toContain('--accent'); }); test('CSS contains amber accent color #d97706', async ({ request }) => { const resp = await request.get('/static/style.css'); const body = await resp.text(); expect(body).toContain('#d97706'); }); test('CSS contains dark mode media query', async ({ request }) => { const resp = await request.get('/static/style.css'); const body = await resp.text(); expect(body).toContain('prefers-color-scheme: dark'); }); test('CSS contains reduced motion media query', async ({ request }) => { const resp = await request.get('/static/style.css'); const body = await resp.text(); expect(body).toContain('prefers-reduced-motion'); }); }); });