voyage/tests/lib/test-first-multi-file.test.mjs
2026-10-12 14:42:12 +02:00

82 lines
3.7 KiB
JavaScript

// tests/lib/test-first-multi-file.test.mjs
// Plan step 3 (files-backticks trial 4): below plan_version 1.9 every test file
// on the `Test first:` `File:` line is read, not only the first backticked
// span. 6.3.1 read the first span alone, so a step whose `Files:` were its two
// `Test first:` files was not seen as tests-only when the second file is not a
// test by its path. One reading: testFirstFiles (plan-schema.mjs), used by
// stepTestsOnly; the executor and the step helper say the same in prose.
import { test } from 'node:test';
import { strict as assert } from 'node:assert';
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import * as m from '../../lib/parsers/plan-schema.mjs';
const HERE = dirname(fileURLToPath(import.meta.url));
const ROOT = join(HERE, '..', '..');
/** A 1.8-shaped step section: `Files:` and the `Test first:` lines given. */
const step = (files, testFirst) => [
'### Step 1: x', '',
`- **Files:** ${files}`,
'- **Changes:** x',
'- **Test first:**',
...testFirst,
'- **Verify:** `node --test tests/a.test.mjs` -> expected: exit 0',
].join('\n');
const TWO = step('`tests/a.test.mjs`, `checks/smoke.mjs`', [
' - File: `tests/a.test.mjs`, `checks/smoke.mjs` (new)',
' - Verifies: both checks run',
]);
const testFirstFiles = (body, version) => {
assert.equal(typeof m.testFirstFiles, 'function', 'plan-schema.mjs exports testFirstFiles');
return m.testFirstFiles(m.parseStepFields(body), version);
};
test('a 1.8 step whose Files are its two Test first files is tests-only', () => {
// checks/smoke.mjs is not a test by its path: only the Test first line makes it one.
assert.equal(m.stepTestsOnly(TWO).testsOnly, true, JSON.stringify(m.stepTestsOnly(TWO)));
});
test('testFirstFiles returns every path on the File: line, below 1.9 and without a version', () => {
assert.deepEqual(testFirstFiles(TWO, '1.8'), ['tests/a.test.mjs', 'checks/smoke.mjs']);
assert.deepEqual(testFirstFiles(TWO, null), ['tests/a.test.mjs', 'checks/smoke.mjs']);
assert.deepEqual(testFirstFiles(TWO), ['tests/a.test.mjs', 'checks/smoke.mjs']);
});
test('testFirstFiles: a call, an option or a URL on the File: line is not read; paths are normalised and deduplicated', () => {
const body = step('`tests/a.test.mjs`', [
' - File: `tests/a.test.mjs`, `parse()`, `--watch`, `https://example.invalid/x.mjs`, `./tests/a.test.mjs`',
' - Verifies: x',
]);
assert.deepEqual(testFirstFiles(body, '1.8'), ['tests/a.test.mjs']);
});
test('testFirstFiles: a Verifies: line, or a span before File: on the line, is never read', () => {
const body = step('`tests/a.test.mjs`', [
' - see `lib/before.mjs` File: `tests/a.test.mjs`',
' - Verifies: `lib/other.mjs` returns `src/x.mjs`',
]);
assert.deepEqual(testFirstFiles(body, '1.8'), ['tests/a.test.mjs']);
const none = step('`tests/a.test.mjs`', [' - Verifies: `tests/a.test.mjs` runs']);
assert.deepEqual(testFirstFiles(none, '1.8'), []);
});
test('a code file beside the test on the File: line still makes the step not tests-only when Files has more', () => {
const body = step('`tests/a.test.mjs`, `src/cli.mjs`', [
' - File: `tests/a.test.mjs`, `parse()` (new)',
' - Verifies: x',
]);
assert.equal(m.stepTestsOnly(body).testsOnly, false);
});
test('prose: the executor and the step helper name every file on the Test first File: line', () => {
const phrase = 'every file on the `Test first:` `File:` line';
for (const rel of [['commands', 'trekexecute.md'], ['agents', 'step-implementer.md']]) {
const text = readFileSync(join(ROOT, ...rel), 'utf8');
assert.ok(text.includes(phrase), `${rel.join('/')} says "${phrase}"`);
}
});