/* eslint-disable no-empty-pattern -- Playwright fixtures require an object-pattern first arg; these fixtures don't depend on any other fixture so the pattern is intentionally empty. */ import { test as base } from '@playwright/test'; import { randomBytes } from 'node:crypto'; // Tests share a single backend/Postgres. To avoid collisions we tag // every resource the test creates with a short random suffix plus the // Playwright worker index. This way two workers running the same spec // in parallel never fight over the same slug or username. export function shortId(): string { return randomBytes(3).toString('hex'); } export function uniqueSlug(prefix: string, workerIndex: number): string { const cleaned = prefix .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, ''); return `e2e-${cleaned}-w${workerIndex}-${shortId()}`; } export function uniqueUsername(prefix: string, workerIndex: number): string { // Username regex is [a-z0-9._-]{2,32}. Mirror the slug format. const cleaned = prefix.toLowerCase().replace(/[^a-z0-9]+/g, ''); return `e2e${cleaned}w${workerIndex}${shortId()}`.slice(0, 32); } export const test = base.extend<{ uniqueSlug: (prefix: string) => string; uniqueUsername: (prefix: string) => string; }>({ uniqueSlug: async ({}, use, testInfo) => { await use((prefix) => uniqueSlug(prefix, testInfo.workerIndex)); }, uniqueUsername: async ({}, use, testInfo) => { await use((prefix) => uniqueUsername(prefix, testInfo.workerIndex)); } }); export { expect } from '@playwright/test';