test: reorganise tests/ui/ into feature subdirectories

This commit is contained in:
2026-06-21 16:22:17 +02:00
parent fec536ef16
commit 2ab0b13eb6
13 changed files with 2 additions and 2 deletions
+22
View File
@@ -0,0 +1,22 @@
// @ts-check
const { test: setup } = require('@playwright/test');
const path = require('path');
const fs = require('fs');
const USER = process.env.GRAV_TEST_USER;
const PASS = process.env.GRAV_TEST_PASS;
const AUTH_FILE = path.join(__dirname, '../.auth/user.json');
setup('authenticate', async ({ page }) => {
if (!USER || !PASS) throw new Error('GRAV_TEST_USER and GRAV_TEST_PASS must be set in .env');
fs.mkdirSync(path.dirname(AUTH_FILE), { recursive: true });
await page.goto('/login');
await page.fill('input[name="username"]', USER);
await page.fill('input[name="password"]', PASS);
await page.click('button[type="submit"]');
await page.waitForSelector('text=successfully logged in', { timeout: 10_000 });
await page.context().storageState({ path: AUTH_FILE });
});
+62
View File
@@ -0,0 +1,62 @@
// @ts-check
// Tests: A1A5 — 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;
// ── A1A3: 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();
});