diff --git a/e2e/tests/themes.spec.ts b/e2e/tests/themes.spec.ts new file mode 100644 index 0000000..fbb7e38 --- /dev/null +++ b/e2e/tests/themes.spec.ts @@ -0,0 +1,94 @@ +/** + * E2E test: Themes CRUD operations. + * + * Validates that a user can: + * 1. Navigate to /themes + * 2. Create a new theme via the API + * 3. Update the theme via the API + * 4. Delete the theme via the API + * 5. Verify the theme is removed from the list + */ + +import { test, expect } from '@playwright/test'; +import { loginAsUser } from '../helpers/auth'; + +test.describe('Theme management', () => { + test('should create, edit, and delete a theme', async ({ page }) => { + // Step 1: Login + await loginAsUser(page); + await page.goto('/themes', { waitUntil: 'domcontentloaded' }); + + // Step 2: Wait for page to load + // The page title should be visible + await expect( + page.locator('h1').filter({ hasText: 'Personnaliser' }), + ).toBeVisible({ timeout: 10_000 }); + + // Step 3: Create a new theme via the API (faster than UI interaction) + // Use page.evaluate to call the themes API + const createResp = await page.evaluate(async () => { + const resp = await fetch('/api/v1/themes', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Requested-With': 'XMLHttpRequest', + }, + credentials: 'same-origin', + body: JSON.stringify({ + name: 'E2E Test Theme', + theme: 'Testing Topic', + categories: ['Category A', 'Category B'], + }), + }); + return { status: resp.status, data: await resp.json() }; + }); + expect(createResp.status).toBe(201); + const themeId = createResp.data.id; + expect(themeId).toBeTruthy(); + + // Step 4: Reload the page and verify the theme appears + await page.goto('/themes', { waitUntil: 'domcontentloaded' }); + // The theme should be in the dropdown or visible + // Wait for themes to load + await page.waitForTimeout(1000); + + // Step 5: Update the theme via API + const updateResp = await page.evaluate(async (id: string) => { + const resp = await fetch(`/api/v1/themes/${id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'X-Requested-With': 'XMLHttpRequest', + }, + credentials: 'same-origin', + body: JSON.stringify({ name: 'E2E Updated Theme' }), + }); + return { status: resp.status, data: await resp.json() }; + }, themeId); + expect(updateResp.status).toBe(200); + expect(updateResp.data.name).toBe('E2E Updated Theme'); + + // Step 6: Delete the theme via API + const deleteResp = await page.evaluate(async (id: string) => { + const resp = await fetch(`/api/v1/themes/${id}`, { + method: 'DELETE', + headers: { 'X-Requested-With': 'XMLHttpRequest' }, + credentials: 'same-origin', + }); + return { status: resp.status }; + }, themeId); + expect(deleteResp.status).toBe(204); + + // Step 7: Verify theme is gone + const listResp = await page.evaluate(async () => { + const resp = await fetch('/api/v1/themes', { + headers: { 'X-Requested-With': 'XMLHttpRequest' }, + credentials: 'same-origin', + }); + return { status: resp.status, data: await resp.json() }; + }); + expect(listResp.status).toBe(200); + const remaining = listResp.data.filter((t: any) => t.name === 'E2E Updated Theme'); + expect(remaining.length).toBe(0); + }); +});