From eedfe22654d93f080b7f02c982cc017b4aab82dc Mon Sep 17 00:00:00 2001 From: Viktor Vaczi Date: Sat, 27 Dec 2025 16:35:40 +0100 Subject: [PATCH] Organize test logs into separate directories by test suite and file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - wxWidgets logs: tests/logs/wxwidgets// - KiCad logs: tests/logs/kicad// - Global setup now cleans all log subdirectories recursively - Added globalSetup to KiCad playwright config 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- tests/e2e/utils/fixtures.ts | 9 +++++--- tests/e2e/utils/test-utils.ts | 26 ++++++++++++++++------- tests/global-setup.ts | 36 +++++++++++++++++++++----------- tests/kicad/fixtures.ts | 26 +++++++++++++++++++++++ tests/kicad/pcbnew.spec.ts | 21 ++++++++++++------- tests/playwright-kicad.config.ts | 1 + 6 files changed, 88 insertions(+), 31 deletions(-) create mode 100644 tests/kicad/fixtures.ts diff --git a/tests/e2e/utils/fixtures.ts b/tests/e2e/utils/fixtures.ts index f296b3b..091025d 100644 --- a/tests/e2e/utils/fixtures.ts +++ b/tests/e2e/utils/fixtures.ts @@ -1,5 +1,6 @@ import { test as base } from '@playwright/test'; -import { setupTestLogger, writeTestLogs, TestLogger, MAIN_CANVAS, waitForApp, tryLoadApp, getCanvasBox } from './test-utils'; +import * as path from 'path'; +import { setupTestLogger, writeTestLogs, TestLogger, MAIN_CANVAS, waitForApp, tryLoadApp, getCanvasBox, WXWIDGETS_LOGS_DIR, getTestFileName } from './test-utils'; // Extend base test with automatic logging export const test = base.extend<{ @@ -13,8 +14,10 @@ export const test = base.extend<{ await use(logger); - // Write logs after test completes - writeTestLogs(testName, logger); + // Write logs to wxwidgets// directory + const testFileName = getTestFileName(testInfo.file); + const logsDir = path.join(WXWIDGETS_LOGS_DIR, testFileName); + writeTestLogs(testName, logger, logsDir); logger.cleanup(); }, }); diff --git a/tests/e2e/utils/test-utils.ts b/tests/e2e/utils/test-utils.ts index b32e6ee..e7b2370 100644 --- a/tests/e2e/utils/test-utils.ts +++ b/tests/e2e/utils/test-utils.ts @@ -3,7 +3,9 @@ import * as fs from 'fs'; import * as path from 'path'; export const MAIN_CANVAS = '#canvas'; -export const LOGS_DIR = path.join(__dirname, '..', '..', 'logs'); +export const LOGS_BASE_DIR = path.join(__dirname, '..', '..', 'logs'); +export const WXWIDGETS_LOGS_DIR = path.join(LOGS_BASE_DIR, 'wxwidgets'); +export const KICAD_LOGS_DIR = path.join(LOGS_BASE_DIR, 'kicad'); export interface TestLogger { consoleLogs: string[]; @@ -12,9 +14,9 @@ export interface TestLogger { } // Ensure logs directory exists -export function ensureLogsDir() { - if (!fs.existsSync(LOGS_DIR)) { - fs.mkdirSync(LOGS_DIR, { recursive: true }); +export function ensureLogsDir(dir: string = LOGS_BASE_DIR) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); } } @@ -48,8 +50,11 @@ export function setupTestLogger(page: Page): TestLogger { } // Write logs to files after test completion -export function writeTestLogs(testName: string, logger: TestLogger) { - ensureLogsDir(); +export function writeTestLogs(testName: string, logger: TestLogger, logsDir: string) { + // Ensure logs directory exists + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } // Sanitize test name for filesystem const safeTestName = testName @@ -58,17 +63,22 @@ export function writeTestLogs(testName: string, logger: TestLogger) { .replace(/^-+|-+$/g, ''); // Always write console log file - const logFile = path.join(LOGS_DIR, `${safeTestName}.log`); + const logFile = path.join(logsDir, `${safeTestName}.log`); fs.writeFileSync(logFile, logger.consoleLogs.join('\n')); // Only write error file if there are errors (excluding favicon) const realErrors = logger.errors.filter(e => !e.includes('favicon')); if (realErrors.length > 0) { - const errorFile = path.join(LOGS_DIR, `${safeTestName}.errors.log`); + const errorFile = path.join(logsDir, `${safeTestName}.errors.log`); fs.writeFileSync(errorFile, realErrors.join('\n\n')); } } +// Helper to get test file name without extension +export function getTestFileName(filePath: string): string { + return path.basename(filePath, '.spec.ts'); +} + // Helper to wait for app initialization export async function waitForApp(page: Page, timeout = 30000) { await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout }); diff --git a/tests/global-setup.ts b/tests/global-setup.ts index a85fc4c..bfa9e7d 100644 --- a/tests/global-setup.ts +++ b/tests/global-setup.ts @@ -1,22 +1,34 @@ import * as fs from 'fs'; import * as path from 'path'; +/** + * Recursively clean all files in a directory (keeps directory structure). + */ +function cleanDirectory(dir: string): number { + let count = 0; + if (!fs.existsSync(dir)) return count; + + for (const entry of fs.readdirSync(dir)) { + const fullPath = path.join(dir, entry); + const stat = fs.statSync(fullPath); + if (stat.isFile()) { + fs.unlinkSync(fullPath); + count++; + } else if (stat.isDirectory()) { + count += cleanDirectory(fullPath); + } + } + return count; +} + /** * Global setup for Playwright tests. - * Cleans the logs directory before each test run to prevent stale logs. + * Cleans the logs directory (and subdirectories) before each test run. */ export default async function globalSetup() { const logsDir = path.join(__dirname, 'logs'); - - if (fs.existsSync(logsDir)) { - // Remove all files in logs directory - for (const file of fs.readdirSync(logsDir)) { - const filePath = path.join(logsDir, file); - // Only remove files, not subdirectories - if (fs.statSync(filePath).isFile()) { - fs.unlinkSync(filePath); - } - } - console.log(`[global-setup] Cleaned ${logsDir}`); + const count = cleanDirectory(logsDir); + if (count > 0) { + console.log(`[global-setup] Removed ${count} log files from ${logsDir}`); } } diff --git a/tests/kicad/fixtures.ts b/tests/kicad/fixtures.ts new file mode 100644 index 0000000..f0a06ab --- /dev/null +++ b/tests/kicad/fixtures.ts @@ -0,0 +1,26 @@ +import { test as base } from '@playwright/test'; +import * as path from 'path'; +import { setupTestLogger, writeTestLogs, TestLogger, MAIN_CANVAS, waitForApp, tryLoadApp, getCanvasBox, KICAD_LOGS_DIR, getTestFileName } from '../e2e/utils/test-utils'; + +// Extend base test with automatic logging +export const test = base.extend<{ + testLogger: TestLogger; +}>({ + testLogger: async ({ page }, use, testInfo) => { + // Build test name from describe block + test title + const testName = testInfo.titlePath.join(' - '); + + const logger = setupTestLogger(page); + + await use(logger); + + // Write logs to kicad// directory + const testFileName = getTestFileName(testInfo.file); + const logsDir = path.join(KICAD_LOGS_DIR, testFileName); + writeTestLogs(testName, logger, logsDir); + logger.cleanup(); + }, +}); + +export { expect } from '@playwright/test'; +export { MAIN_CANVAS, waitForApp, tryLoadApp, getCanvasBox }; diff --git a/tests/kicad/pcbnew.spec.ts b/tests/kicad/pcbnew.spec.ts index 1ac9cc4..bb011da 100644 --- a/tests/kicad/pcbnew.spec.ts +++ b/tests/kicad/pcbnew.spec.ts @@ -1,10 +1,14 @@ -import { test, expect } from '@playwright/test'; +import { test, expect } from './fixtures'; /** * PCBnew WASM E2E Tests * * These tests verify that the KiCad PCBnew application runs correctly in the browser. * The WASM files are served from apps/kicad/ via the web server. + * + * Logs are written to tests/logs/: + * - {test-name}.log - console output + * - {test-name}.errors.log - page errors (if any) */ test.describe('PCBnew WASM', () => { @@ -13,21 +17,22 @@ test.describe('PCBnew WASM', () => { await page.goto('/kicad/pcbnew.html'); }); - test('should load PCBnew WASM module', async ({ page }) => { + test('should load PCBnew WASM module', async ({ page, testLogger }) => { // Wait for WASM to initialize (look for canvas or status indicator) // This is a basic smoke test - expand as PCBnew integration matures await expect(page.locator('canvas')).toBeVisible({ timeout: 60000 }); + + // testLogger automatically captures all console output and errors }); - test('should render without JavaScript errors', async ({ page }) => { - const errors: string[] = []; - page.on('pageerror', err => errors.push(err.message)); - + test('should render without JavaScript errors', async ({ page, testLogger }) => { await page.goto('/kicad/pcbnew.html'); await page.waitForTimeout(5000); // Give time for WASM to load - // Filter out known acceptable errors if any - const criticalErrors = errors.filter(e => !e.includes('ResizeObserver')); + // Check captured errors (excluding known acceptable ones) + const criticalErrors = testLogger.errors.filter(e => + !e.includes('ResizeObserver') && !e.includes('favicon') + ); expect(criticalErrors).toHaveLength(0); }); }); diff --git a/tests/playwright-kicad.config.ts b/tests/playwright-kicad.config.ts index 9eb837d..72b942c 100644 --- a/tests/playwright-kicad.config.ts +++ b/tests/playwright-kicad.config.ts @@ -40,6 +40,7 @@ function findFreePort(): number { const port = getOrFindPort(); export default defineConfig({ + globalSetup: './global-setup.ts', testDir: './kicad', fullyParallel: true, forbidOnly: !!process.env.CI,