Shuffle/e2e/tests/web/krit09-abmeldung.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

63 lines
3 KiB
TypeScript

import { expect, test } from '@playwright/test';
import { GoTrueClient, randomEmail, randomPassword } from '../../support/gotrue';
import { extractTokenHash, waitForMail } from '../../support/mailpit';
import { enableSemantics, expectSignedIn, expectSignedInViaLoginRedirect, login, readSession, shiftBrowserClock } from '../../support/web';
const gotrue = new GoTrueClient();
test.describe('KTO-01 Kriterium 9 (web): Passwort-Reset beendet andere Sitzungen', () => {
test('KTO-01 Kriterium 9: zweiter Browser-Kontext ist nach dem Reset und dem naechsten (erzwungenen) Refresh abgemeldet', async ({
page,
browser,
}) => {
const email = randomEmail();
const oldPassword = randomPassword();
await gotrue.createConfirmedUser(email, oldPassword);
// Sitzung B: ein zweites Geraet in eigenem Kontext.
await login(page, email, oldPassword);
await expectSignedIn(page);
// Reset ueber Sitzung A (API): neues Passwort setzen beendet alle anderen Sitzungen.
await gotrue.recover(email);
const { tokenHash } = extractTokenHash(await waitForMail(email));
const recovery = await gotrue.verify(tokenHash, 'recovery');
const update = await gotrue.updateUser(recovery.body.access_token, { password: randomPassword() });
expect(update.status).toBe(200);
// Der Access-Token laeuft erst nach jwt_expiry (AUTH-CFG-14, bis 60 min, F1) ab. Der Test
// erzwingt den Refresh, indem er die Browser-Uhr um 2 h vorstellt (Session.expiresAt kommt in
// supabase_flutter aus dem JWT-Claim `exp`, ein geaendertes `expires_at` im Storage wirkt nicht).
await shiftBrowserClock(page, 2 * 60 * 60 * 1000);
await page.reload();
await enableSemantics(page);
// Der Refresh scheitert (refresh_token_not_found): supabase_flutter loescht die Sitzung aus dem Storage.
await expect.poll(async () => await readSession(page), { timeout: 15_000 }).toBeNull();
// Abgemeldet: /login leitet nicht mehr auf die Startseite (Gegenprobe zu Krit. 5).
await page.goto('/login');
await enableSemantics(page);
await page.waitForTimeout(1_000);
await expect(page).toHaveURL(/\/login$/);
await expect(page.getByRole('button', { name: 'Sign in', exact: true })).toBeVisible();
});
test('KTO-01 Kriterium 9: Gegenprobe ohne Reset und mit abgelaufenem Access-Token bleibt die Sitzung bestehen (Refresh klappt)', async ({
page,
}) => {
const email = randomEmail();
const password = randomPassword();
await gotrue.createConfirmedUser(email, password);
await login(page, email, password);
await expectSignedIn(page);
await shiftBrowserClock(page, 2 * 60 * 60 * 1000);
const before = (await readSession(page))?.access_token;
expect(before, 'Sitzung vor dem Reload').toBeTruthy();
await page.reload();
await expectSignedInViaLoginRedirect(page);
// Der Refresh lief wirklich: neues Access-Token im Storage.
await expect
.poll(async () => (await readSession(page))?.access_token ?? before, { timeout: 15_000 })
.not.toBe(before);
});
});