import { test, expect } from '@playwright/test' import { clearMailpit, countMessagesTo, waitForPasswordResetToken, } from './helpers/mailpit' const forgotSuccessMessage = 'Om e-postadressen finns har vi skickat instruktioner för att återställa lösenordet.' test.describe('Password reset', () => { test('login page links to forgot password', async ({ page }) => { await page.goto('/logga-in') await page.getByRole('link', { name: 'Glömt lösenord?' }).click() await expect(page).toHaveURL('/glomt-losenord') await expect( page.getByRole('heading', { name: 'Glömt lösenord?' }), ).toBeVisible() }) test('forgot password page submits and shows success', async ({ page }) => { await page.goto('/glomt-losenord') await page.getByLabel('E-postadress').fill('test@bilhej.se') await page .getByRole('button', { name: 'Skicka återställningslänk' }) .click() await expect(page.getByText(forgotSuccessMessage)).toBeVisible() }) test('unknown email gets same success message as known user', async ({ request, }) => { const known = await request.post('/api/auth/forgot-password', { data: { email: 'test@bilhej.se' }, }) const unknown = await request.post('/api/auth/forgot-password', { data: { email: 'nobody-reset-e2e@bilhej.se' }, }) expect(known.ok()).toBeTruthy() expect(unknown.ok()).toBeTruthy() const knownBody = await known.json() const unknownBody = await unknown.json() expect(knownBody.message).toBe(forgotSuccessMessage) expect(unknownBody.message).toBe(forgotSuccessMessage) expect(unknownBody.testToken).toBeUndefined() }) test('full reset flow with isolated user', async ({ page, request }) => { const email = `reset-e2e-${Date.now()}@bilhej.se` const oldPassword = 'oldpass1234' const newPassword = 'resetpass1234' const register = await request.post('/api/auth/register', { data: { email, password: oldPassword }, }) expect(register.ok()).toBeTruthy() const forgot = await request.post('/api/auth/forgot-password', { data: { email }, }) expect(forgot.ok()).toBeTruthy() const forgotBody = await forgot.json() expect(forgotBody.testToken).toBeTruthy() await page.goto(`/aterstall-losenord?token=${forgotBody.testToken}`) await page.getByLabel('Nytt lösenord').fill(newPassword) await page.getByLabel('Bekräfta lösenord').fill(newPassword) await page.getByRole('button', { name: 'Spara nytt lösenord' }).click() await expect( page.getByText('Lösenordet har uppdaterats. Du kan nu logga in.'), ).toBeVisible({ timeout: 10000 }) await page.goto('/logga-in') await page.getByLabel('E-postadress').fill(email) await page.getByLabel('Lösenord').fill(newPassword) await page.getByRole('button', { name: 'Logga in' }).click() await expect(page).toHaveURL('/') }) test('login fails with old password after reset', async ({ request }) => { const email = `reset-oldpw-${Date.now()}@bilhej.se` const oldPassword = 'oldpass1234' const newPassword = 'resetpass1234' await request.post('/api/auth/register', { data: { email, password: oldPassword }, }) const forgot = await request.post('/api/auth/forgot-password', { data: { email }, }) const { testToken } = await forgot.json() await request.post('/api/auth/reset-password', { data: { token: testToken, password: newPassword }, }) const login = await request.post('/api/auth/login', { data: { email, password: oldPassword }, }) expect(login.status()).toBe(401) }) test('invalid token shows error and link to request new', async ({ page }) => { await page.goto('/aterstall-losenord?token=invalid') await page.getByLabel('Nytt lösenord').fill('newpassword123') await page.getByLabel('Bekräfta lösenord').fill('newpassword123') await page.getByRole('button', { name: 'Spara nytt lösenord' }).click() await expect( page.getByText('Återställningslänken är ogiltig eller har gått ut'), ).toBeVisible() await expect( page.getByRole('link', { name: 'Begär ny länk' }), ).toHaveAttribute('href', '/glomt-losenord') }) test('missing token shows invalid link error', async ({ page }) => { await page.goto('/aterstall-losenord') await expect( page.getByText('Återställningslänken saknar en giltig kod.'), ).toBeVisible() await expect( page.getByRole('link', { name: 'Begär ny länk' }), ).toHaveAttribute('href', '/glomt-losenord') }) test('delivers reset link via Mailpit SMTP', async ({ page, request }) => { const email = `mailpit-e2e-${Date.now()}@bilhej.se` const oldPassword = 'oldpass1234' const newPassword = 'mailpitpass1234' await clearMailpit(request) const register = await request.post('/api/auth/register', { data: { email, password: oldPassword }, }) expect(register.ok()).toBeTruthy() const forgot = await request.post('/api/auth/forgot-password', { data: { email }, }) expect(forgot.ok()).toBeTruthy() const token = await waitForPasswordResetToken(request, email, { publicBaseUrl: 'http://frontend', }) await page.goto(`/aterstall-losenord?token=${token}`) await page.getByLabel('Nytt lösenord').fill(newPassword) await page.getByLabel('Bekräfta lösenord').fill(newPassword) await page.getByRole('button', { name: 'Spara nytt lösenord' }).click() await expect( page.getByText('Lösenordet har uppdaterats. Du kan nu logga in.'), ).toBeVisible({ timeout: 10000 }) const login = await request.post('/api/auth/login', { data: { email, password: newPassword }, }) expect(login.ok()).toBeTruthy() }) test('does not send Mailpit message for unknown email', async ({ request, }) => { await clearMailpit(request) const forgot = await request.post('/api/auth/forgot-password', { data: { email: 'nobody-mailpit-e2e@bilhej.se' }, }) expect(forgot.ok()).toBeTruthy() await new Promise((resolve) => setTimeout(resolve, 2000)) expect(await countMessagesTo(request, 'nobody-mailpit-e2e@bilhej.se')).toBe(0) }) })