diff --git a/packages/e2e/tests/edgedriver.e2e.test.ts b/packages/e2e/tests/edgedriver.e2e.test.ts index ae16b3e..a451251 100644 --- a/packages/e2e/tests/edgedriver.e2e.test.ts +++ b/packages/e2e/tests/edgedriver.e2e.test.ts @@ -4,39 +4,59 @@ import { remote } from 'webdriverio' import fs from 'node:fs/promises' import path from 'node:path' import os from 'node:os' -import { beforeEach, describe, it } from 'vitest' +import { beforeEach, afterEach, describe, it } from 'vitest' import { start, download, findEdgePath } from 'edgedriver' +/** + * Force-kill any leftover msedgedriver/Edge processes. Sessions started via + * `wdio:edgedriverOptions.binary` have their driver process managed + * internally by WebdriverIO - deleteSession() ends the WebDriver session but + * doesn't guarantee that process has actually exited yet. A lingering + * instance either blocks deleting its own binary on Windows (EPERM: file + * still in use) or, on Linux/macOS, silently keeps running and consuming + * memory into the next test (surfaces as a bare "Killed" + exit 137). + */ +function killDriverProcesses() { + const isWindows = process.platform === 'win32' + const patterns = isWindows ? ['msedgedriver.exe', 'msedge.exe'] : ['msedgedriver', 'microsoft-edge'] + for (const pattern of patterns) { + try { + execSync(isWindows ? `taskkill /IM ${pattern} /F /T` : `pkill -9 -f ${pattern}`, { stdio: 'ignore' }) + } catch { + // no matching process running, nothing to clean up + } + } +} + describe('Edgedriver E2E Tests', () => { - const port = 4444 + /** + * Give every test its own driver cache dir instead of the shared default + * (EDGEDRIVER_CACHE_DIR or os.tmpdir()). Tests used to all download into + * the same /tmp/msedgedriver and delete-then-redownload it between tests, + * which raced against whatever process the previous test hadn't fully + * torn down yet. + */ + let cacheDir = '' beforeEach(async () => { - const tempDir = process.env.EDGEDRIVER_CACHE_DIR || os.tmpdir() - const edgedriverCachePath = tempDir ? path.resolve(tempDir, 'msedgedriver') : '' - if (edgedriverCachePath && await fs.lstat(edgedriverCachePath).catch(() => false)) { - console.log(`Removing existing Edge binary at ${edgedriverCachePath}`) - await fs.unlink(edgedriverCachePath) - } - - const edgedriverPath = process.env.EDGE_BINARY_PATH ? path.resolve(process.env.EDGE_BINARY_PATH, 'msedgedriver') : '' - if (edgedriverPath && await fs.lstat(edgedriverPath).catch(() => false)) { - console.log(`Removing existing Edge binary at ${edgedriverPath}`) - await fs.unlink(edgedriverPath) - } + cacheDir = await fs.mkdtemp(path.join(os.tmpdir(), 'edgedriver-e2e-')) + killDriverProcesses() + }) - try { - // Kill pending processes - execSync(`kill -9 $(lsof -t -i :${port})`) - console.log(`Successfully killed process on port ${port}`) - } catch { - console.log(`No process found running on port ${port}, or insufficient permissions.`) - } + afterEach(async () => { + killDriverProcesses() + /** + * maxRetries/retryDelay: Windows can lag briefly between a process + * exiting and the OS actually releasing its handle on the binary, + * even after killDriverProcesses() above returns. + */ + await fs.rm(cacheDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }) }) it('start edgedriver manually', async () => { const port = 4444 - const cp = await start({ port }) + const cp = await start({ port, cacheDir }) try { await waitPort({ port: 4444 }) @@ -57,11 +77,12 @@ describe('Edgedriver E2E Tests', () => { await browser.deleteSession() } finally { cp.kill() + await new Promise((resolve) => cp.once('exit', resolve)) } }) it('start specific edgedriver', async () => { - const binary = await download() + const binary = await download(undefined, cacheDir) const browser = await remote({ automationProtocol: 'webdriver', @@ -80,7 +101,7 @@ describe('Edgedriver E2E Tests', () => { }) it('start with missing architecture', async () => { - const binary = await download('152.0.4191.77') + const binary = await download('152.0.4191.77', cacheDir) const browser = await remote({ automationProtocol: 'webdriver',