import { test as baseTest } from '@playwright/test'; import type { Page } from '@playwright/test'; import { PLAYWRIGHT_BASE_URL, TEST_USER_PASSWORD } from './config'; import { type TestContext, setupTestContext } from '../e2e/utils/api'; import { setupAdminUser, setupEmployeeUser } from '../e2e/utils/members'; export * from '@playwright/test'; export type { TestContext }; export interface EmployeeFixture { page: Page; memberId: string; } export interface AdminFixture { page: Page; memberId: string; } /** * API-based authentication fixture - creates a new user via HTTP requests instead of UI interactions. * This is ~10-25x faster than UI-based authentication (~100-200ms vs ~3-5s). * * Uses page.context().request() to ensure cookies are shared between the API request and page. */ export const test = baseTest.extend< { ctx: TestContext; employee: EmployeeFixture; admin: AdminFixture }, { workerStorageState: string } >({ page: async ({ page }, use) => { // Generate unique email for this test const email = `john+${Date.now()}_${Math.floor(Math.random() * 10000)}@doe.com`; const password = TEST_USER_PASSWORD; const name = 'John Doe'; const timezone = await page.evaluate( () => Intl.DateTimeFormat().resolvedOptions().timeZone ); // Use page.context().request() so cookies are automatically shared with the page const request = page.context().request; // Step 1: Visit the register page to get CSRF token and initial session const csrfResponse = await request.get(`${PLAYWRIGHT_BASE_URL}/register`, { maxRedirects: 0, }); // Extract XSRF-TOKEN from cookies const cookies = csrfResponse.headers()['set-cookie']; let xsrfToken = ''; if (cookies) { const xsrfMatch = cookies.match(/XSRF-TOKEN=([^;]+)/); if (xsrfMatch) { xsrfToken = decodeURIComponent(xsrfMatch[1]); } } // Step 2: Register via API (Laravel Fortify web routes) const registerResponse = await request.post(`${PLAYWRIGHT_BASE_URL}/register`, { headers: { 'X-XSRF-TOKEN': xsrfToken, 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'text/html', }, form: { name, email, password, password_confirmation: password, terms: 'on', timezone, }, maxRedirects: 0, }); // Check if registration was successful (should redirect to dashboard) if (registerResponse.status() !== 302) { console.error('API registration failed, falling back to UI-based registration'); // Fall back to UI-based registration await page.goto(`${PLAYWRIGHT_BASE_URL}/register`); await page.getByLabel('Name').fill(name); await page.getByLabel('Email').fill(email); await page.getByLabel('Password', { exact: true }).fill(password); await page.getByLabel('Confirm Password').fill(password); await page.getByLabel('I agree to the Terms of').click(); await page.getByRole('button', { name: 'Register' }).click(); await page.waitForURL(`${PLAYWRIGHT_BASE_URL}/dashboard`); } else { // Registration succeeded - cookies are already set in the context from the request // Just navigate to dashboard to verify await page.goto(`${PLAYWRIGHT_BASE_URL}/dashboard`); await page.waitForLoadState('domcontentloaded'); } await use(page); }, ctx: async ({ page }, use) => { const ctx = await setupTestContext(page); await use(ctx); }, employee: async ({ page, ctx, browser }, use) => { const { employeePage, employeeMemberId, closeEmployee } = await setupEmployeeUser( page, ctx, browser ); await use({ page: employeePage, memberId: employeeMemberId }); await closeEmployee(); }, admin: async ({ page, ctx, browser }, use) => { const { adminPage, adminMemberId, closeAdmin } = await setupAdminUser(page, ctx, browser); await use({ page: adminPage, memberId: adminMemberId }); await closeAdmin(); }, });