Some checks failed
CD / Build and push images (push) Successful in 46s
CI / Lint, typecheck, test (push) Successful in 1m14s
CI / Auth e2e pack (push) Failing after 1m58s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m2s
CD / Promote to Int (push) Successful in 10s
Playwright's request context resolves localhost to ::1 while Vite in the CI container listened on IPv4 only — the fixture-login helper got ECONNREFUSED. Vite now starts with --host in the auth-e2e job. The redirect test also reports the server's error message instead of a bare URL mismatch when a login fails. Part of #20 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
115 lines
4.9 KiB
TypeScript
115 lines
4.9 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
import { contextForUser, latestMailFor, tokenFromMail } from './helpers';
|
|
|
|
/**
|
|
* M1 auth regression pack. Runs against a full local stack with Mailpit
|
|
* (CI job `auth-e2e`, or `deploy/compose` dev stack locally); skipped
|
|
* where no mail catcher is available (e.g. the CD smoke run against a
|
|
* deployed stage — that pipeline runs e2e/smoke.spec.ts only anyway).
|
|
*/
|
|
const MAILPIT_URL = process.env.E2E_MAILPIT_URL;
|
|
test.skip(!MAILPIT_URL, 'requires a Mailpit instance (E2E_MAILPIT_URL)');
|
|
|
|
const BASE_URL = process.env.E2E_BASE_URL ?? 'http://localhost:5173';
|
|
|
|
test('full signup journey: register, mail, verify, first login', async ({ page }) => {
|
|
const stamp = Date.now().toString(36);
|
|
const username = `e2e-${stamp}`;
|
|
const email = `${username}@dorfteich.test`;
|
|
const password = 'ein sehr langes e2e passwort';
|
|
|
|
await page.goto('/signup');
|
|
await page.getByLabel(/username|benutzername/i).fill(username);
|
|
await page.getByLabel(/e-mail/i).fill(email);
|
|
await page.getByLabel(/display name|anzeigename/i).fill(username);
|
|
await page.getByLabel(/^password|^passwort/i).fill(password);
|
|
await page.getByRole('button', { name: /register|registrieren/i }).click();
|
|
|
|
await expect(page.getByRole('heading', { name: /inbox|postfach/i })).toBeVisible();
|
|
|
|
const mail = await latestMailFor(MAILPIT_URL!, email);
|
|
const token = tokenFromMail(mail.text);
|
|
|
|
await page.goto(`/verify-email?token=${token}`);
|
|
await expect(page.getByRole('heading', { name: /confirmed|bestätigt/i })).toBeVisible();
|
|
|
|
await page.getByRole('link', { name: /sign-in|anmeldung/i }).click();
|
|
await page.getByLabel(/username or e-mail|benutzername oder e-mail/i).fill(username);
|
|
await page.getByLabel(/^password|^passwort/i).fill(password);
|
|
await page.getByRole('button', { name: /sign in|anmelden/i }).click();
|
|
|
|
// Signed in: the user menu shows the display name.
|
|
await expect(page.getByRole('button', { name: username })).toBeVisible();
|
|
});
|
|
|
|
test('login rejects a wrong password with a visible error', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await page.getByLabel(/username or e-mail|benutzername oder e-mail/i).fill('fixture-user');
|
|
await page.getByLabel(/^password|^passwort/i).fill('definitiv falsch');
|
|
await page.getByRole('button', { name: /sign in|anmelden/i }).click();
|
|
await expect(page.getByRole('alert')).toBeVisible();
|
|
});
|
|
|
|
test('anonymous visitors are redirected to login and return after', async ({ page }) => {
|
|
await page.goto('/settings');
|
|
await expect(page).toHaveURL(/\/login\?next=%2Fsettings/);
|
|
await page.getByLabel(/username or e-mail|benutzername oder e-mail/i).fill('fixture-user');
|
|
await page.getByLabel(/^password|^passwort/i).fill('fixture passwort 123');
|
|
await page.getByRole('button', { name: /sign in|anmelden/i }).click();
|
|
// Surface the server answer in CI logs instead of a bare URL mismatch.
|
|
const alert = page.getByRole('alert');
|
|
await Promise.race([
|
|
page.waitForURL(/\/settings/),
|
|
alert.waitFor().then(async () => {
|
|
throw new Error(`login failed with: ${await alert.textContent()}`);
|
|
}),
|
|
]);
|
|
});
|
|
|
|
test('fixture user signs out via the menu', async ({ browser }) => {
|
|
const context = await contextForUser(browser, BASE_URL, 'fixture-user');
|
|
const page = await context.newPage();
|
|
await page.goto('/');
|
|
await page.getByRole('button', { name: 'Fixture User' }).click();
|
|
await page.getByRole('menuitem', { name: /sign out|abmelden/i }).click();
|
|
await expect(page).toHaveURL(/\/login/);
|
|
await context.close();
|
|
});
|
|
|
|
test('admin menu and page are reserved for site admins', async ({ browser }) => {
|
|
const admin = await contextForUser(browser, BASE_URL, 'fixture-admin');
|
|
const adminPage = await admin.newPage();
|
|
await adminPage.goto('/admin');
|
|
await expect(adminPage.getByRole('heading', { name: /administration/i })).toBeVisible();
|
|
await admin.close();
|
|
|
|
const member = await contextForUser(browser, BASE_URL, 'fixture-user');
|
|
const memberPage = await member.newPage();
|
|
await memberPage.goto('/admin');
|
|
// Non-admins are bounced to the start page.
|
|
await expect(memberPage).toHaveURL(/\/$/);
|
|
await member.close();
|
|
});
|
|
|
|
test('profile display-name change shows up in the top bar', async ({ browser }) => {
|
|
const context = await contextForUser(browser, BASE_URL, 'fixture-user');
|
|
const page = await context.newPage();
|
|
await page.goto('/settings');
|
|
const nameField = page.getByLabel(/display name|anzeigename/i);
|
|
await nameField.fill('Fixture Umbenannt');
|
|
await page
|
|
.getByRole('button', { name: /^save$|^speichern$/i })
|
|
.first()
|
|
.click();
|
|
await expect(page.getByRole('button', { name: 'Fixture Umbenannt' })).toBeVisible();
|
|
// Restore for the next run (idempotent pack).
|
|
await nameField.fill('Fixture User');
|
|
await page
|
|
.getByRole('button', { name: /^save$|^speichern$/i })
|
|
.first()
|
|
.click();
|
|
await expect(page.getByRole('button', { name: 'Fixture User' })).toBeVisible();
|
|
await context.close();
|
|
});
|