Shuffle/e2e/tests/web/krit04-06-07-anmeldung.spec.ts
cschlaefke aa421b12b0
Some checks failed
CI / flutter (pull_request) Successful in 1m15s
CI / web-build (pull_request) Successful in 53s
CI / supabase (pull_request) Successful in 3s
CI / supabase-tests (pull_request) Successful in 30s
CI / e2e (pull_request) Failing after 3m56s
test(auth): address review findings for e2e
Refs #38

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 10:55:27 +01:00

122 lines
5.5 KiB
TypeScript

import { expect, test } from '@playwright/test';
import { GoTrueClient, randomEmail, randomPassword, waitMailInterval } from '../../support/gotrue';
import { expectMailCount, waitForMail } from '../../support/mailpit';
import { freshBrowserContext, fillField, login, open, pressFirstButton, register, t, textOf } from '../../support/web';
const gotrue = new GoTrueClient();
/** Inhalt des Semantics-Baums, ohne die (je Lauf wechselnde) Adresse. */
async function screenText(page: import('@playwright/test').Page): Promise<string> {
// Eingabefelder (mit der je Lauf wechselnden Adresse) gehoeren nicht zum Vergleich.
return (await page.locator('body').ariaSnapshot()).replace(/^\s*- textbox.*\n?/gm, '').trim();
}
test.describe('KTO-01 Kriterium 4 (web): gleicher Bildschirm fuer neue und vorhandene Adresse', () => {
test('KTO-01 Kriterium 4: Registrierung mit bestehender, bestaetigter Adresse zeigt exakt denselben Bildschirm wie mit neuer Adresse', async ({
page,
browser,
}) => {
// Neue Adresse.
await register(page, { email: randomEmail(), password: randomPassword() });
await expect(page).toHaveURL(/\/register\/check-email$/);
await expect(textOf(page, t.checkEmailTitle)).toBeVisible();
const fresh = await screenText(page);
// Vorhandene, bestaetigte Adresse (nur ueber die API angelegt).
const existing = randomEmail();
await gotrue.createConfirmedUser(existing, randomPassword());
const second = await (await freshBrowserContext(browser)).newPage();
try {
await register(second, { email: existing, password: randomPassword() });
await expect(second).toHaveURL(/\/register\/check-email$/);
await expect(textOf(second, t.checkEmailTitle)).toBeVisible();
expect(await screenText(second)).toBe(fresh);
// Es geht keine Mail an das bestehende Konto.
await expectMailCount(existing, 0);
} finally {
await second.context().close();
}
});
});
test.describe('KTO-01 Kriterium 6 (web): unbestaetigtes Konto', () => {
test('KTO-01 Kriterium 6: Login mit richtigem Passwort zeigt Hinweis und Aktion; „erneut senden“ schickt eine zweite Mail', async ({
page,
}) => {
const email = randomEmail();
const password = randomPassword();
await gotrue.signUp(email, password);
await waitForMail(email);
await waitMailInterval();
await login(page, email, password);
await expect(textOf(page, t.emailNotConfirmed)).toBeVisible();
await page.getByRole('button', { name: t.resendConfirmation }).click();
await expect(textOf(page, t.checkEmailResent)).toBeVisible();
await waitForMail(email, 2);
});
});
test.describe('KTO-01 Kriterium 7 (web): keine Auskunft ueber Konten, Drosselungshinweis', () => {
test('KTO-01 Kriterium 7: falsche E-Mail und falsches Passwort zeigen denselben Text', async ({ page, browser }) => {
const email = randomEmail();
const password = randomPassword();
await gotrue.createConfirmedUser(email, password);
await login(page, email, `${password}-falsch`);
await expect(textOf(page, t.invalidCredentials)).toBeVisible();
const wrongPassword = await screenText(page);
const ctx = await freshBrowserContext(browser);
try {
const other = await ctx.newPage();
await login(other, randomEmail('unknown'), password);
await expect(textOf(other, t.invalidCredentials)).toBeVisible();
expect(await screenText(other)).toBe(wrongPassword);
} finally {
await ctx.close();
}
});
/**
* Die CLI drosselt nie (AUTH-CFG-13/A2), ein echter Test waere nie rot. Die 429-Antwort wird
* deshalb per `page.route` simuliert; der echte Limiter wird im Projekt `throttle` geprueft.
* CORS: Die App (localhost:3000) ruft Kong (127.0.0.1:54321) cross-origin auf; die simulierte
* Antwort braucht deshalb die CORS-Header, sonst sieht die App einen Netzfehler statt 429.
*/
test('KTO-01 Kriterium 7: AUTH-CFG-10 bei 429 erscheint der Drosselungshinweis ohne Dauer', async ({ page }) => {
await page.route('**/auth/v1/token?grant_type=password', async (route) => {
const request = route.request();
if (request.method() === 'OPTIONS') return route.fallback();
await route.fulfill({
status: 429,
contentType: 'application/json',
headers: { 'access-control-allow-origin': request.headers()['origin'] ?? '*' },
body: JSON.stringify({ code: 429, error_code: 'over_request_rate_limit', msg: 'Request rate limit reached' }),
});
});
await login(page, randomEmail(), randomPassword());
await expect(textOf(page, t.rateLimited)).toBeVisible();
await expect(textOf(page, t.invalidCredentials)).toHaveCount(0);
});
});
test.describe('QUE-06 (web): Ablauf nur per Tastatur', () => {
test('QUE-06 Kriterium 3: Anmelden nur mit Tab, Eingabe und Enter (E-Mail-Feld per Tab erreicht)', async ({ page }) => {
const email = randomEmail();
const password = randomPassword();
await gotrue.createConfirmedUser(email, password);
await open(page, '/login');
await expect(page.getByRole('button', { name: 'Sign in', exact: true })).toBeVisible();
// Kein Klick: Tab fokussiert das erste Feld (E-Mail), Tab das zweite (Passwort).
await page.keyboard.press('Tab');
await expect(page.getByRole('textbox', { name: 'Email address' })).toBeFocused();
await page.keyboard.type(email);
await page.keyboard.press('Tab');
await expect(page.getByRole('textbox', { name: 'Password' })).toBeFocused();
await page.keyboard.type(password);
await page.keyboard.press('Enter');
await expect(textOf(page, t.home)).toBeVisible();
});
});