n8n/packages/testing/playwright/scripts/select-affected-e2e.mjs
n8n-cat-bot[bot] ff3657ded7
Some checks are pending
Build: Benchmark Image / build (push) Waiting to run
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (22.22.3) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.15.0) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Sync API Docs / sync-public-api (push) Waiting to run
ci: Activate V8 E2E impact map filter in PR CI (#31757)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 16:15:01 +00:00

78 lines
3.1 KiB
JavaScript

#!/usr/bin/env node
// @ts-check
/**
* Resolve changed files → the E2E specs that must run, using the coverage
* impact map. Prints the janitor select-e2e JSON ({ specs, unmapped, mode }).
*
* node select-affected-e2e.mjs <changed-file> [<changed-file> ...]
* CHANGED_FILES=a.ts,b.vue node select-affected-e2e.mjs
*
* MAP SOURCE SEAM — this is the one spot to change when moving off the committed
* file. Today the map is the committed snapshot the nightly publishes. To pull
* it from a remote webhook instead, replace `resolveMapPath()` with a fetch to a
* temp file. Keep it FAIL-OPEN: on ANY failure return null so selection degrades
* to BROAD (run everything) — a missing/stale/unreachable map must never cause a
* test to be skipped.
*/
import { execFileSync } from 'node:child_process';
import { existsSync, accessSync, constants as fsConstants } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
const JANITOR_CLI = path.resolve(__dirname, '..', '..', 'janitor', 'dist', 'cli.js');
const COMMITTED_MAP = path.join(REPO_ROOT, '.github', 'test-metrics', 'e2e-impact-map.json');
/**
* @param {{ mapPath?: string }} [opts]
* @returns {string | null} path to a readable impact map, or null → fail-open broad.
*
* Wrapped in try/catch so any I/O failure (race deletion, perms, fs error) is
* swallowed into a null return — never throws. The caller then omits --map,
* which makes select-e2e fail open to broad (run the full suite).
*/
export function resolveMapPath(opts = {}) {
// FUTURE: fetch a remote webhook to a temp file here and return that path;
// keep the try/catch — return null on failure (fail-open).
const target = opts.mapPath ?? COMMITTED_MAP;
try {
if (!existsSync(target)) return null;
accessSync(target, fsConstants.R_OK);
return target;
} catch {
return null;
}
}
/**
* Build the janitor argv for `select-e2e`. Pure (no I/O) so the wrapper's glue
* can be tested without spawning a subprocess. If `mapPath` is null we omit
* `--map`, which makes select-e2e fail open to broad.
*
* @param {{ changedFiles: string, mapPath: string | null, allSpecs?: string }} input
* @returns {string[]}
*/
export function buildArgs({ changedFiles, mapPath, allSpecs }) {
const args = ['select-e2e', `--changed-files=${changedFiles}`];
if (mapPath) args.push(`--map=${mapPath}`);
if (allSpecs) args.push(`--all-specs=${allSpecs}`);
return args;
}
function runAsScript() {
const changedFiles = process.argv.slice(2).join(',') || process.env.CHANGED_FILES || '';
const mapPath = resolveMapPath();
const args = buildArgs({ changedFiles, mapPath, allSpecs: process.env.ALL_SPECS_FILE });
const out = execFileSync('node', [JANITOR_CLI, ...args], { encoding: 'utf-8' });
process.stdout.write(out);
}
// Only execute when invoked directly — keeps the module importable for tests.
if (process.argv[1] && path.resolve(process.argv[1]) === __filename) {
runAsScript();
}