Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ concurrency:

jobs:
test:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [20, 22]

steps:
Expand Down Expand Up @@ -53,14 +54,14 @@ jobs:
run: npm run test:coverage

- name: Upload coverage reports to Codecov
if: matrix.node-version == 20
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 20
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false

- name: Upload artifacts
if: matrix.node-version == 20
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 20
uses: actions/upload-artifact@v7
with:
name: dist
Expand Down
7 changes: 0 additions & 7 deletions docs/ci-and-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,6 @@ Release flow
3) The `version-check` job validates the tag vs. package.json.
4) `release` creates a GitHub Release; `publish` publishes to npm.

Windows Integration Tests
- On Windows, vitest has known issues with process forking when running integration tests that spawn Firefox.
- See: https://github.com/mozilla/firefox-devtools-mcp/issues/33
- To work around this, we use a separate test runner (`scripts/run-integration-tests-windows.mjs`) that runs integration tests directly via Node.js without vitest's process isolation.
- The CI workflow detects Windows and automatically uses this runner instead of vitest for integration tests.
- Unit tests still run via vitest on all platforms.

Notes
- If you want Codecov upload to run, switch CI test step to `npm run test:coverage` or generate `coverage/lcov.info`.
- Provenance is enabled for npm publish (Node 20+).
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@
"test:mozlog": "node scripts/test-mozlog.js",
"test:navigation": "node scripts/test-navigation.js",
"test:privileged": "node scripts/test-privileged-context.js",
"test:screenshot": "node scripts/test-screenshot.js",
"test:integration:win": "node scripts/run-integration-tests-windows.mjs"
"test:screenshot": "node scripts/test-screenshot.js"
},
"keywords": [
"mcp",
Expand Down
188 changes: 0 additions & 188 deletions scripts/run-integration-tests-windows.mjs

This file was deleted.

19 changes: 19 additions & 0 deletions tests/helpers/firefox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import { FirefoxClient } from '@/firefox/index.js';
import type { FirefoxLaunchOptions } from '@/firefox/types.js';
import type { SnapshotNode } from '@/firefox/snapshot/types.js';
// Aliased: several helpers below name a promise callback parameter `resolve`.
import { resolve as resolvePath } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';

/**
* Creates a headless Firefox client for testing
Expand Down Expand Up @@ -144,3 +147,19 @@ export async function waitForElementInSnapshot(
export async function waitForPageLoad(delayMs = 300): Promise<void> {
await new Promise((resolve) => setTimeout(resolve, delayMs));
}

/**
* Absolute path to the shared fixtures directory.
*/
export const fixturesPath = resolvePath(
fileURLToPath(new URL('.', import.meta.url)),
'../fixtures'
);

/**
* Builds a file:// URL for a fixture. Interpolating `file://${path}` breaks on
* Windows (the drive letter parses as the host); pathToFileURL does not.
*/
export function fixtureUrl(fileName: string): string {
return pathToFileURL(resolvePath(fixturesPath, fileName)).href;
}
9 changes: 9 additions & 0 deletions tests/helpers/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { rmSync } from 'node:fs';

/**
* Recursive delete that tolerates Windows still holding the handles: `force`
* suppresses ENOENT but not the ENOTEMPTY/EPERM a just-closed file causes.
*/
export function removeDir(dir: string): void {
rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
}
Loading