fix more tests paths

This commit is contained in:
fuomag9
2026-03-07 02:11:45 +01:00
parent 77e9a7d2f1
commit 6e987e2c90
3 changed files with 28 additions and 78 deletions

View File

@@ -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<void> {
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<void> {
}
async function seedAuthState(): Promise<void> {
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<string, string | boolean> = {};
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() {

View File

@@ -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);

View File

@@ -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: [