// @ts-check // Tests: A1–A5 — authentication and access control // A1/A2/A3 run WITHOUT storageState to test the login page itself. // A4 uses a fresh context without storageState to verify access control. // A5 uses storageState (already logged in). const { test, expect } = require('@playwright/test'); const USER = process.env.GRAV_TEST_USER; const PASS = process.env.GRAV_TEST_PASS; // ── A1–A3: require a clean session (no prior auth) ──────────────────────────── test.describe('login page (unauthenticated)', () => { test.use({ storageState: { cookies: [], origins: [] } }); // A1: Login page loads test('A1: login page renders the login form', async ({ page }) => { await page.goto('/login'); await expect(page.locator('input[name="username"]')).toBeVisible(); await expect(page.locator('input[name="password"]')).toBeVisible(); await expect(page.locator('button[type="submit"], input[type="submit"]')).toBeVisible(); }); // A2: Invalid credentials test('A2: invalid credentials show an error message', async ({ page }) => { await page.goto('/login'); await page.fill('input[name="username"]', 'wronguser'); await page.fill('input[name="password"]', 'wrongpass'); await page.click('button[type="submit"], input[type="submit"]'); // Grav stays on the login page and shows a flash error await expect(page.locator('body')).toContainText(/invalid|incorrect|failed/i, { timeout: 8_000 }); await expect(page).toHaveURL(/login/); }); // A3: Valid login test('A3: valid credentials show success message', async ({ page }) => { if (!USER || !PASS) test.skip(true, 'GRAV_TEST_USER / GRAV_TEST_PASS not set'); await page.goto('/login'); await page.fill('input[name="username"]', USER); await page.fill('input[name="password"]', PASS); await page.click('button[type="submit"], input[type="submit"]'); await expect(page.locator('body')).toContainText('successfully logged in', { timeout: 10_000 }); }); }); // ── A4: /post without auth shows a login form ───────────────────────────────── // Uses a fresh context with no storageState. test('A4: /post without auth renders an inline login form', async ({ browser }) => { const ctx = await browser.newContext({ storageState: { cookies: [], origins: [] } }); const page = await ctx.newPage(); const baseURL = process.env.GRAV_BASE_URL || 'http://localhost:8081'; await page.goto(`${baseURL}/post`); // Grav renders a login form inline at the /post URL (section#grav-login) await expect(page.locator('#grav-login')).toBeVisible({ timeout: 8_000 }); await ctx.close(); }); // ── A5: /post with auth shows the post form ─────────────────────────────────── test('A5: /post with auth renders the post form', async ({ page }) => { await page.goto('/post'); await expect(page.locator('.post-form-wrap')).toBeVisible(); await expect(page.locator('input[name="data[title]"]')).toBeVisible(); });