From 6e987e2c909698a0da5cdd86c7fd69e8d044dd0a Mon Sep 17 00:00:00 2001 From: fuomag9 <1580624+fuomag9@users.noreply.github.com> Date: Sat, 7 Mar 2026 02:11:45 +0100 Subject: [PATCH] fix more tests paths --- tests/global-setup.ts | 101 +++++++++---------------------------- tests/global-teardown.ts | 2 +- tests/playwright.config.ts | 3 +- 3 files changed, 28 insertions(+), 78 deletions(-) diff --git a/tests/global-setup.ts b/tests/global-setup.ts index bc160d1b..74c5ac02 100644 --- a/tests/global-setup.ts +++ b/tests/global-setup.ts @@ -1,5 +1,6 @@ -import { execFileSync, execFileSync as runCmd } from 'node:child_process'; -import { mkdirSync, writeFileSync } from 'node:fs'; +import { chromium } from '@playwright/test'; +import { execFileSync } from 'node:child_process'; +import { mkdirSync } from 'node:fs'; import { resolve } from 'node:path'; const COMPOSE_ARGS = [ @@ -8,8 +9,8 @@ const COMPOSE_ARGS = [ '-f', 'tests/docker-compose.test.yml', ]; const HEALTH_URL = 'http://localhost:3000/api/health'; -const AUTH_DIR = resolve(process.cwd(), 'tests/.auth'); -const AUTH_FILE = resolve(AUTH_DIR, 'admin.json'); +export const AUTH_DIR = resolve(__dirname, '.auth'); +export const AUTH_FILE = resolve(AUTH_DIR, 'admin.json'); const MAX_WAIT_MS = 180_000; const POLL_INTERVAL_MS = 3_000; @@ -32,7 +33,6 @@ async function waitForHealth(): Promise { await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS)); } - // Dump container logs to help diagnose the failure console.error('[global-setup] Health check timed out. Container logs:'); try { execFileSync('docker', [...COMPOSE_ARGS, 'logs', '--tail=50'], { stdio: 'inherit', cwd: process.cwd() }); @@ -42,78 +42,27 @@ async function waitForHealth(): Promise { } async function seedAuthState(): Promise { - console.log('[global-setup] Seeding auth state...'); - - // Get CSRF token from NextAuth - const csrfRes = await fetch('http://localhost:3000/api/auth/csrf'); - if (!csrfRes.ok) { - throw new Error(`CSRF request failed: ${csrfRes.status}`); - } - const csrfData = await csrfRes.json() as { csrfToken: string }; - console.log('[global-setup] Got CSRF token'); - - const params = new URLSearchParams({ - csrfToken: csrfData.csrfToken, - username: 'testadmin', - password: 'TestPassword2026!', // matches ADMIN_PASSWORD in docker-compose.test.yml - callbackUrl: 'http://localhost:3000', - json: 'true', - }); - - const signinRes = await fetch('http://localhost:3000/api/auth/callback/credentials', { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Cookie: csrfRes.headers.get('set-cookie') ?? '', - }, - body: params.toString(), - redirect: 'manual', - }); - - console.log(`[global-setup] Sign-in response: HTTP ${signinRes.status}`); - - // Collect cookies from both responses - const allCookieHeaders: string[] = []; - for (const [k, v] of csrfRes.headers.entries()) { - if (k === 'set-cookie') allCookieHeaders.push(v); - } - for (const [k, v] of signinRes.headers.entries()) { - if (k === 'set-cookie') allCookieHeaders.push(v); - } - - const cookies = allCookieHeaders.flatMap((header) => - header.split(/,(?=[^ ])/).map((cookie) => { - const parts = cookie.split(';').map((p) => p.trim()); - const [nameVal, ...attrs] = parts; - const eqIdx = nameVal.indexOf('='); - if (eqIdx === -1) return null; - const name = nameVal.slice(0, eqIdx); - const value = nameVal.slice(eqIdx + 1); - const attrMap: Record = {}; - for (const attr of attrs) { - const [k, v] = attr.split('=').map((s) => s.trim()); - attrMap[k.toLowerCase()] = v ?? true; - } - return { - name, - value, - domain: 'localhost', - path: typeof attrMap['path'] === 'string' ? attrMap['path'] : '/', - httpOnly: attrMap['httponly'] === true, - secure: attrMap['secure'] === true, - sameSite: typeof attrMap['samesite'] === 'string' - ? attrMap['samesite'].charAt(0).toUpperCase() + attrMap['samesite'].slice(1).toLowerCase() - : 'Lax', - }; - }).filter(Boolean) - ); - - console.log(`[global-setup] Collected ${cookies.length} cookies`); - - const storageState = { cookies, origins: [] }; + console.log('[global-setup] Seeding auth state via browser login...'); mkdirSync(AUTH_DIR, { recursive: true }); - writeFileSync(AUTH_FILE, JSON.stringify(storageState, null, 2)); - console.log('[global-setup] Auth state saved to', AUTH_FILE); + + const browser = await chromium.launch(); + const page = await browser.newPage(); + + try { + await page.goto('http://localhost:3000/login'); + await page.getByRole('textbox', { name: /username/i }).fill('testadmin'); + await page.getByRole('textbox', { name: /password/i }).fill('TestPassword2026!'); + await page.getByRole('button', { name: /sign in/i }).click(); + + // Wait for redirect away from /login + await page.waitForURL((url) => !url.pathname.includes('/login'), { timeout: 15_000 }); + console.log(`[global-setup] Login succeeded, landed on: ${page.url()}`); + + await page.context().storageState({ path: AUTH_FILE }); + console.log('[global-setup] Auth state saved to', AUTH_FILE); + } finally { + await browser.close(); + } } export default async function globalSetup() { diff --git a/tests/global-teardown.ts b/tests/global-teardown.ts index 25fb6d7d..cd29c69c 100644 --- a/tests/global-teardown.ts +++ b/tests/global-teardown.ts @@ -19,7 +19,7 @@ export default async function globalTeardown() { console.warn('[global-teardown] docker compose down failed:', err); } - const authDir = resolve(process.cwd(), 'tests/.auth'); + const authDir = resolve(__dirname, '.auth'); if (existsSync(authDir)) { rmSync(authDir, { recursive: true, force: true }); console.log('[global-teardown] Removed', authDir); diff --git a/tests/playwright.config.ts b/tests/playwright.config.ts index b887ef49..c288b31b 100644 --- a/tests/playwright.config.ts +++ b/tests/playwright.config.ts @@ -1,4 +1,5 @@ import { defineConfig, devices } from '@playwright/test'; +import { resolve } from 'node:path'; export default defineConfig({ testDir: './e2e', @@ -11,7 +12,7 @@ export default defineConfig({ reporter: 'list', use: { baseURL: 'http://localhost:3000', - storageState: './.auth/admin.json', + storageState: resolve(__dirname, '.auth/admin.json'), trace: 'on-first-retry', }, projects: [