From 7d095e159ecf760d7c8debcf95708e716a8ca45f Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 22 Mar 2026 20:00:23 +0100 Subject: [PATCH] test(e2e): add profile page journey (view, update, password change) Includes self-healing password change test that restores admin123 at the end so the shared session remains valid for subsequent specs. Refs #48 Co-Authored-By: Claude Sonnet 4.6 --- frontend/e2e/profile.spec.ts | 108 +++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 frontend/e2e/profile.spec.ts diff --git a/frontend/e2e/profile.spec.ts b/frontend/e2e/profile.spec.ts new file mode 100644 index 00000000..c9f476a1 --- /dev/null +++ b/frontend/e2e/profile.spec.ts @@ -0,0 +1,108 @@ +import { test, expect } from '@playwright/test'; + +/** + * Profile page E2E tests. + * + * Reads top-to-bottom as a single user journey: + * the logged-in admin opens their profile, updates their display name, + * tries a wrong password (sees an error), then successfully changes their + * password and logs back in with the new one. + * + * The password change test restores the original password at the end so the + * shared session remains valid for all subsequent test files. + */ + +test.describe('Profile page', () => { + test('user opens their profile and sees the personal data and password sections', async ({ + page + }) => { + await page.goto('/profile'); + await expect(page.getByRole('heading', { name: /Mein Profil/i })).toBeVisible(); + await expect(page.getByText('Persönliche Daten')).toBeVisible(); + await expect(page.getByText('Passwort ändern')).toBeVisible(); + await page.screenshot({ path: 'test-results/e2e/profile-view.png' }); + }); + + test('user saves updated first and last name and sees confirmation', async ({ page }) => { + await page.goto('/profile'); + await page.waitForSelector('[data-hydrated]'); + + await page.locator('input[name="firstName"]').fill('E2E'); + await page.locator('input[name="lastName"]').fill('Admin'); + + // Two "Speichern" buttons exist — the first belongs to the profile form + await page + .locator('form[action*="updateProfile"]') + .getByRole('button', { name: /Speichern/i }) + .click(); + + await expect(page.getByText('Gespeichert.')).toBeVisible(); + // Nav avatar shows the new initials derived from firstName + lastName + await expect(page.locator('button[aria-haspopup="true"]')).toContainText('EA'); + await page.screenshot({ path: 'test-results/e2e/profile-save.png' }); + }); + + test('shows an error when the current password is wrong', async ({ page }) => { + await page.goto('/profile'); + await page.waitForSelector('[data-hydrated]'); + + await page.locator('input[name="currentPassword"]').fill('definitely-wrong'); + await page.locator('input[name="newPassword"]').fill('NewPass123!'); + await page.locator('input[name="confirmPassword"]').fill('NewPass123!'); + + await page + .locator('form[action*="changePassword"]') + .getByRole('button', { name: /Speichern/i }) + .click(); + + await expect(page.getByText('Das aktuelle Passwort ist falsch.')).toBeVisible(); + await page.screenshot({ path: 'test-results/e2e/profile-wrong-password.png' }); + }); + + test('user changes their password and can log in with the new one', async ({ page }) => { + await page.goto('/profile'); + await page.waitForSelector('[data-hydrated]'); + + // ── Step 1: change to a temporary password ───────────────────────────── + await page.locator('input[name="currentPassword"]').fill('admin123'); + await page.locator('input[name="newPassword"]').fill('TempAdmin456!'); + await page.locator('input[name="confirmPassword"]').fill('TempAdmin456!'); + await page + .locator('form[action*="changePassword"]') + .getByRole('button', { name: /Speichern/i }) + .click(); + + await expect(page.getByText('Passwort erfolgreich geändert.')).toBeVisible(); + + // ── Step 2: navigate away — server session is invalidated ─────────────── + await page.goto('/'); + await expect(page).toHaveURL(/\/login/); + + // ── Step 3: log in with the new password ─────────────────────────────── + await page.getByLabel('Benutzername').fill('admin'); + await page.getByLabel('Passwort').fill('TempAdmin456!'); + await page.getByRole('button', { name: 'Anmelden' }).click(); + await expect(page).toHaveURL('/'); + await page.screenshot({ path: 'test-results/e2e/profile-password-changed.png' }); + + // ── Step 4: restore the original password so subsequent tests still work ─ + await page.goto('/profile'); + await page.waitForSelector('[data-hydrated]'); + await page.locator('input[name="currentPassword"]').fill('TempAdmin456!'); + await page.locator('input[name="newPassword"]').fill('admin123'); + await page.locator('input[name="confirmPassword"]').fill('admin123'); + await page + .locator('form[action*="changePassword"]') + .getByRole('button', { name: /Speichern/i }) + .click(); + await expect(page.getByText('Passwort erfolgreich geändert.')).toBeVisible(); + + // ── Step 5: log back in with the restored password ───────────────────── + await page.goto('/'); + await expect(page).toHaveURL(/\/login/); + await page.getByLabel('Benutzername').fill('admin'); + await page.getByLabel('Passwort').fill('admin123'); + await page.getByRole('button', { name: 'Anmelden' }).click(); + await expect(page).toHaveURL('/'); + }); +});