frontend-coverage.mjs now plans its steps from the command line. No arguments still runs everything, then reports and checks. --shard I/N runs one slice of the chromium+touch workflows into coverage/js/raw without wiping it or reporting; shard 1 also runs the node units and the viewer suite. --merge-only reports and checks over raw coverage the shards wrote, with --update-baseline as before, and refuses an empty or missing raw dir. npm scripts: test:coverage:shard and coverage:merge.
94 lines
5.0 KiB
JavaScript
94 lines
5.0 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { existsSync, mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { plan, runCoverage } from '../../tools/vendor/frontend-coverage.mjs';
|
|
|
|
const steps = argv => plan(argv).map(({ step, ...options }) => (Object.keys(options).length ? { step, ...options } : step));
|
|
|
|
function tempPaths(t) {
|
|
const root = mkdtempSync(join(tmpdir(), 'pm-coverage-cli-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const reportRoot = join(root, 'coverage/js');
|
|
return { root, paths: { repositoryRoot: root, tools: join(root, 'tools'), reportRoot, raw: join(reportRoot, 'raw'), sourceRoot: join(root, 'web/js') } };
|
|
}
|
|
|
|
function recorder() {
|
|
const calls = [];
|
|
const run = (args, env = {}) => { calls.push({ args, env }); };
|
|
return { calls, run };
|
|
}
|
|
|
|
test('no arguments plans the unchanged full run', () => {
|
|
assert.deepEqual(steps([]), [
|
|
'clean', 'node-units', 'viewer', { step: 'workflows', shard: null }, 'report', { step: 'check', updateBaseline: false },
|
|
]);
|
|
assert.deepEqual(steps(['--update-baseline']), [
|
|
'clean', 'node-units', 'viewer', { step: 'workflows', shard: null }, 'report', { step: 'check', updateBaseline: true },
|
|
]);
|
|
});
|
|
|
|
test('--shard 2/3 plans only workflows with --shard 2/3 and no report', () => {
|
|
assert.deepEqual(steps(['--shard', '2/3']), ['prepare', { step: 'workflows', shard: '2/3' }]);
|
|
assert.deepEqual(steps(['--shard=3/3']), ['prepare', { step: 'workflows', shard: '3/3' }]);
|
|
});
|
|
|
|
test('--shard 1/N also runs the node units and the viewer suite', () => {
|
|
assert.deepEqual(steps(['--shard', '1/3']), ['prepare', 'node-units', 'viewer', { step: 'workflows', shard: '1/3' }]);
|
|
assert.deepEqual(steps(['--shard', '1/1']), ['prepare', 'node-units', 'viewer', { step: 'workflows', shard: '1/1' }]);
|
|
});
|
|
|
|
test('--merge-only plans the report and the check over existing raw coverage', () => {
|
|
assert.deepEqual(steps(['--merge-only']), ['require-raw', 'report', { step: 'check', updateBaseline: false }]);
|
|
assert.deepEqual(steps(['--merge-only', '--update-baseline']), ['require-raw', 'report', { step: 'check', updateBaseline: true }]);
|
|
});
|
|
|
|
test('invalid shard and mode combinations are refused', () => {
|
|
for (const argv of [['--shard'], ['--shard', '0/3'], ['--shard', '4/3'], ['--shard', '2'], ['--shard', 'a/b'], ['--shard', '1/0'], ['--shard', '1.5/3']]) {
|
|
assert.throws(() => plan(argv), /--shard needs I\/N/, argv.join(' '));
|
|
}
|
|
assert.throws(() => plan(['--shard', '1/2', '--merge-only']), /either --shard or --merge-only/);
|
|
assert.throws(() => plan(['--shard', '1/2', '--update-baseline']), /--update-baseline .* --merge-only or a full run/);
|
|
assert.throws(() => plan(['--shard', '1/2', '--shard', '2/2']), /--shard once/);
|
|
assert.throws(() => plan(['--report']), /Unknown argument: --report/);
|
|
});
|
|
|
|
test('--merge-only refuses an empty raw dir', async t => {
|
|
const { paths } = tempPaths(t);
|
|
const { calls, run } = recorder();
|
|
await assert.rejects(runCoverage({ argv: ['--merge-only'], run, paths }), /no raw coverage .*coverage\/js\/raw.* run the --shard steps first/i);
|
|
mkdirSync(paths.raw, { recursive: true });
|
|
await assert.rejects(runCoverage({ argv: ['--merge-only'], run, paths }), /no raw coverage/i);
|
|
writeFileSync(join(paths.raw, 'notes.txt'), 'not coverage');
|
|
await assert.rejects(runCoverage({ argv: ['--merge-only'], run, paths }), /no raw coverage/i);
|
|
assert.deepEqual(calls, [], 'nothing is reported or checked over partial or empty coverage');
|
|
});
|
|
|
|
test('a shard keeps raw coverage other shards wrote and passes its shard to Playwright', async t => {
|
|
const { paths } = tempPaths(t);
|
|
mkdirSync(paths.raw, { recursive: true });
|
|
const earlier = join(paths.raw, 'browser-earlier-shard.json');
|
|
writeFileSync(earlier, '{"result":[]}');
|
|
const { calls, run } = recorder();
|
|
await runCoverage({ argv: ['--shard', '2/3'], run, paths });
|
|
assert.ok(existsSync(earlier), 'raw files from another shard survive');
|
|
assert.equal(calls.length, 1, 'shard 2 runs only the workflows');
|
|
const [{ args, env }] = calls;
|
|
assert.deepEqual(args.slice(-6), ['--project', 'chromium', '--project', 'touch', '--shard', '2/3']);
|
|
assert.equal(env.PM_JS_COVERAGE, paths.raw);
|
|
});
|
|
|
|
test('shard 1 runs the node units into the shared raw dir, then the viewer suite, then its workflows', async t => {
|
|
const { root, paths } = tempPaths(t);
|
|
mkdirSync(join(root, 'tests/js'), { recursive: true });
|
|
writeFileSync(join(root, 'tests/js/a.test.js'), '');
|
|
const { calls, run } = recorder();
|
|
await runCoverage({ argv: ['--shard', '1/3'], run, paths });
|
|
assert.equal(calls.length, 3);
|
|
assert.deepEqual(calls[0].args, ['--test', 'tests/js/a.test.js']);
|
|
assert.equal(calls[0].env.NODE_V8_COVERAGE, paths.raw);
|
|
assert.ok(calls[1].args.includes(join(paths.tools, 'playwright.config.mjs')));
|
|
assert.deepEqual(calls[2].args.slice(-2), ['--shard', '1/3']);
|
|
});
|