Files
familienarchiv/frontend/e2e/auth.spec.ts
Marcel ea6b727e44 test(e2e): verify login establishes a working API session
Guards against regressions where the session cookie is set but
the backend rejects it — a URL redirect alone is not enough.

Refs #48
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 19:59:27 +01:00

72 lines
3.0 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { login } from './helpers/auth';
/**
* These tests run WITHOUT the stored session so they can test the login flow itself.
* Playwright's storageState is only applied for the 'chromium' project, which depends
* on the 'setup' project. These tests use a fresh context via test.use({ storageState: undefined }).
*/
test.use({ storageState: { cookies: [], origins: [] } });
test.describe('Authentication', () => {
test('login page renders correctly', async ({ page }) => {
await page.goto('/login');
await expect(page.getByLabel('Benutzername')).toBeVisible();
await expect(page.getByLabel('Passwort')).toBeVisible();
await expect(page.getByRole('button', { name: 'Anmelden' })).toBeVisible();
await page.screenshot({ path: 'test-results/e2e/login-page.png' });
});
test('redirects unauthenticated users to /login', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveURL(/\/login/);
await page.screenshot({ path: 'test-results/e2e/auth-redirect.png' });
});
test('protected routes redirect to /login without session', async ({ page }) => {
for (const url of ['/documents/new', '/persons', '/conversations']) {
await page.goto(url);
await expect(page).toHaveURL(/\/login/);
}
});
test('shows an error for wrong credentials', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Benutzername').fill('nichtexistent');
await page.getByLabel('Passwort').fill('falschespasswort');
await page.getByRole('button', { name: 'Anmelden' }).click();
// Stays on login, shows error
await expect(page).toHaveURL(/\/login/);
await expect(page.locator('.text-red-600')).toBeVisible();
await page.screenshot({ path: 'test-results/e2e/login-error.png' });
});
test('login with valid credentials redirects to home', async ({ page }) => {
await login(page);
await expect(page).toHaveURL('/');
await expect(page.getByPlaceholder('Suche in Titel, Inhalt, Ort...')).toBeVisible();
await page.screenshot({ path: 'test-results/e2e/login-success.png' });
});
test('login establishes a session that authenticates API calls', async ({ page }) => {
// Guards against regressions where the session cookie is set but broken —
// a working URL redirect is not enough evidence that auth works end-to-end.
await login(page);
const response = await page.request.get('/api/users/me');
expect(response.ok()).toBe(true);
await page.screenshot({ path: 'test-results/e2e/auth-session-valid.png' });
});
test('logout clears the session and redirects to /login', async ({ page }) => {
await login(page);
// Logout is inside the user avatar dropdown — open it first
await page.locator('button[aria-haspopup="true"]').click();
await page.getByRole('button', { name: 'Abmelden' }).click();
await expect(page).toHaveURL(/\/login/);
// Confirm session is gone: navigating to / redirects back
await page.goto('/');
await expect(page).toHaveURL(/\/login/);
await page.screenshot({ path: 'test-results/e2e/logout.png' });
});
});