feat: add comprehensive Playwright UI test suite
25 tests across auth (A1-A5), posting (P1-P5), validation (V1-V4), tracker (T1-T5), and nav (N1-N5). Uses storageState for single login per run. Replaces post-with-photo.spec.js with post.spec.js. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
// @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();
|
||||
});
|
||||
Reference in New Issue
Block a user