diff --git a/tests/setup.ts b/tests/setup.ts index 6b81d53..2f8dae2 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -2,7 +2,9 @@ // This file runs before all tests import { beforeAll, afterAll } from 'vitest'; -import { execSync } from 'child_process'; +import { execSync, execFileSync } from 'child_process'; + +const isWindows = process.platform === 'win32'; // Track if we're in cleanup mode let isCleaningUp = false; @@ -17,7 +19,7 @@ afterAll(() => { }); /** - * Cleanup function to kill all Firefox and geckodriver processes + * Cleanup function to kill leftover test Firefox and geckodriver processes * This ensures no zombie processes are left after test runs */ function cleanup() { @@ -26,6 +28,15 @@ function cleanup() { } isCleaningUp = true; + if (isWindows) { + cleanupWindows(); + } else { + cleanupUnix(); + } + isCleaningUp = false; +} + +function cleanupUnix() { try { // Find Firefox processes started with --marionette (test instances) const firefoxPids = execSync('pgrep -f "firefox.*marionette" || true', { @@ -57,8 +68,57 @@ function cleanup() { console.log('✅ Global cleanup: All test Firefox processes terminated'); } catch { // Ignore errors - processes might already be dead - } finally { - isCleaningUp = false; + } +} + +/** + * Windows only. Retrieve all process ids matching the provided name and + * optional command line pattern. + */ +function findWindowsProcessIds(imageName: string, commandLinePattern?: string): number[] { + const filter = commandLinePattern + ? `Name='${imageName}' AND CommandLine LIKE '%${commandLinePattern}%'` + : `Name='${imageName}'`; + const script = + `Get-CimInstance Win32_Process -Filter "${filter}" | ` + + 'Select-Object -ExpandProperty ProcessId'; + const output = execFileSync( + 'powershell.exe', + ['-NoProfile', '-NonInteractive', '-Command', script], + { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true } + ); + return output + .split(/\r?\n/) + .map((line) => line.trim()) + .filter((line) => /^\d+$/.test(line)) + .map(Number); +} + +function cleanupWindows() { + try { + const firefoxPids = findWindowsProcessIds('firefox.exe', '--marionette'); + for (const pid of firefoxPids) { + try { + // /T kills the whole process tree, replacing the Unix child-kill step + execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore' }); + } catch { + // Ignore errors - process might already be dead + } + } + + for (const pid of findWindowsProcessIds('geckodriver.exe')) { + try { + execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore' }); + } catch { + // Ignore errors - process might already be dead + } + } + + if (firefoxPids.length > 0) { + console.log('✅ Global cleanup: All test Firefox processes terminated'); + } + } catch { + // Ignore errors - processes might already be dead } }