vikunja/frontend/tests/e2e/user/email-confirmation.spec.ts

169 lines
6.4 KiB
TypeScript

import {test, expect} from '../../support/fixtures'
import {UserFactory} from '../../factories/user'
import {TokenFactory} from '../../factories/token'
import {TEST_PASSWORD, TEST_PASSWORD_HASH} from '../../support/constants'
test.describe('Email Confirmation', () => {
let user
let confirmationToken
test.beforeEach(async ({page, apiContext}) => {
await UserFactory.truncate()
await TokenFactory.truncate()
// Create a user with status = 1 (StatusEmailConfirmationRequired)
const users = await UserFactory.create(1, {
username: 'unconfirmeduser',
email: 'unconfirmed@example.com',
password: TEST_PASSWORD_HASH,
status: 1, // StatusEmailConfirmationRequired
})
user = users[0]
// Create an email confirmation token for this user
// kind: 2 = TokenEmailConfirm
confirmationToken = 'test-email-confirm-token-12345678901234567890123456789012'
await TokenFactory.create(1, {
user_id: user.id,
kind: 2,
token: confirmationToken,
})
})
test('Should fail login before email is confirmed', async ({page, apiContext}) => {
await page.goto('/login')
await page.locator('input[id=username]').fill(user.username)
await page.locator('input[id=password]').fill(TEST_PASSWORD)
await page.locator('.button').filter({hasText: 'Login'}).click()
await expect(page.locator('div.message.danger')).toContainText('Email address of the user not confirmed')
})
test('Should confirm email and allow login', async ({page, apiContext}) => {
// Setup response promise for the confirmation API call
const confirmEmailPromise = page.waitForResponse(response =>
response.url().includes('/user/confirm') && response.request().method() === 'POST',
)
// Manually set the token in localStorage before visiting the page
// This simulates what happens when the user clicks the email link
await page.goto('/login')
await page.evaluate((token) => {
localStorage.setItem('emailConfirmToken', token)
}, confirmationToken)
await page.reload()
// Wait for the confirmation API call to complete
const confirmResponse = await confirmEmailPromise
expect(confirmResponse.status()).toBe(200)
// Should show success message
await expect(page.locator('.message.success')).toBeVisible({timeout: 10000})
await expect(page.locator('.message.success')).toContainText('You successfully confirmed your email')
// Now login should work
await page.locator('input[id=username]').fill(user.username)
await page.locator('input[id=password]').fill(TEST_PASSWORD)
await page.locator('.button').filter({hasText: 'Login'}).click()
// Should successfully log in
await expect(page).toHaveURL(/\//)
await expect(page).not.toHaveURL(/\/login/)
// Check that the username appears in the greeting
await expect(page.locator('body')).toContainText(user.username)
})
test('Should fail with invalid confirmation token', async ({page, apiContext}) => {
// Setup response promise for the confirmation API call
const confirmEmailPromise = page.waitForResponse(response =>
response.url().includes('/user/confirm') && response.request().method() === 'POST',
)
// Try to confirm with an invalid token
const invalidToken = 'invalid-token-that-does-not-exist-in-database'
await page.goto('/login')
await page.evaluate((token) => {
localStorage.setItem('emailConfirmToken', token)
}, invalidToken)
await page.reload()
// Wait for the confirmation API call to fail
await confirmEmailPromise
// Should show error message
await expect(page.locator('.message.danger')).toBeVisible({timeout: 10000})
// Login should still fail
await page.locator('input[id=username]').fill(user.username)
await page.locator('input[id=password]').fill(TEST_PASSWORD)
await page.locator('.button').filter({hasText: 'Login'}).click()
await expect(page.locator('div.message.danger')).toContainText('Email address of the user not confirmed')
})
test('Should not allow using the same token twice', async ({page, apiContext}) => {
// First confirmation - should work
let confirmEmailPromise = page.waitForResponse(response =>
response.url().includes('/user/confirm') && response.request().method() === 'POST',
)
await page.goto('/login')
await page.evaluate((token) => {
localStorage.setItem('emailConfirmToken', token)
}, confirmationToken)
await page.reload()
let confirmResponse = await confirmEmailPromise
expect(confirmResponse.status()).toBe(200)
await expect(page.locator('.message.success')).toBeVisible({timeout: 10000})
await expect(page.locator('.message.success')).toContainText('You successfully confirmed your email')
// Try to use the same token again - should fail
confirmEmailPromise = page.waitForResponse(response =>
response.url().includes('/user/confirm') && response.request().method() === 'POST',
)
await page.goto('/login')
await page.evaluate((token) => {
localStorage.setItem('emailConfirmToken', token)
}, confirmationToken)
await page.reload()
await confirmEmailPromise
await expect(page.locator('.message.danger')).toBeVisible({timeout: 10000})
})
test('Should confirm email when clicking link from email (via query parameter)', async ({page, apiContext}) => {
// Setup response promise for the confirmation API call
const confirmEmailPromise = page.waitForResponse(response =>
response.url().includes('/user/confirm') && response.request().method() === 'POST',
)
// Simulate clicking the email confirmation link with query parameter
// This is what happens when a user clicks the link in their email
await page.goto(`/?userEmailConfirm=${confirmationToken}`)
// Should redirect to login page
await expect(page).toHaveURL(/\/login/)
// Wait for the confirmation API call to complete
const confirmResponse = await confirmEmailPromise
expect(confirmResponse.status()).toBe(200)
// Should show success message
await expect(page.locator('.message.success')).toBeVisible({timeout: 10000})
await expect(page.locator('.message.success')).toContainText('You successfully confirmed your email')
// Now login should work
await page.locator('input[id=username]').fill(user.username)
await page.locator('input[id=password]').fill(TEST_PASSWORD)
await page.locator('.button').filter({hasText: 'Login'}).click()
// Should successfully log in
await expect(page).toHaveURL(/\//)
await expect(page).not.toHaveURL(/\/login/)
// Check that the username appears in the greeting
await expect(page.locator('body')).toContainText(user.username)
})
})