// tests/helpers/bin-path.mjs // A PATH on which only the named programs are found: one directory of small // wrappers, each starting the program from where the current PATH finds it. // A test that needs a test runner to be absent ("could not start") then holds // on every machine, also one where that runner is installed (release review // of 6.3.0, round 9: two tests assumed `jest` is not on PATH). import { accessSync, constants, mkdtempSync, rmSync, statSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { delimiter, join } from 'node:path'; function onPath(name) { for (const dir of String(process.env.PATH ?? '').split(delimiter).filter(Boolean)) { const p = join(dir, name); try { accessSync(p, constants.X_OK); if (statSync(p).isFile()) return p; } catch { /* not here */ } } return null; } /** * @param {string[]} names - the programs the PATH finds (git, for the repo work) * @returns {{path: string, cleanup: () => void}} */ export function pathWith(names) { const dir = mkdtempSync(join(tmpdir(), 'voyage-bin-')); for (const name of names) { const found = onPath(name); if (!found) throw new Error(`${name} is not on PATH`); if (found.includes("'")) throw new Error(`${found}: a quote in the path`); writeFileSync(join(dir, name), `#!/bin/sh\nexec '${found}' "$@"\n`, { mode: 0o755 }); } return { path: dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) }; }