fix more tests paths
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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: [
|
||||
|
||||
Reference in New Issue
Block a user