fix(plan): a 1.9 entry keeps its wrapped paths, and a numbered File line names its test
Review findings b4fa1927 and 4b6460e5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1320e3a4d5
commit
fddd0a078c
2 changed files with 103 additions and 10 deletions
|
|
@ -474,9 +474,13 @@ export function fieldPaths(text) {
|
|||
// as before and gets one warning, PLAN_STEP_PATH_UNTICKED):
|
||||
//
|
||||
// - The entries are the field's first non-empty line and each list-item line
|
||||
// (`-`, `*`, `+`, `1.`, `1)`). Any other line continues the entry above it,
|
||||
// so a wrapped description line is no entry, and an empty first line before
|
||||
// a nested list is no entry either.
|
||||
// (`-`, `*`, `+`, `1.`, `1)`). Any other non-empty line continues the entry
|
||||
// above it and is appended to that entry's text with one space before the
|
||||
// entry is read, so a list wrapped after a comma ("`tests/a.test.mjs`," and
|
||||
// "`src/a.mjs`" on the next line) names both files, a wrapped description
|
||||
// line stays description, and an empty first line before a nested list is
|
||||
// no entry. An issue carries the line number of the entry's first line
|
||||
// (review `b4fa1927`).
|
||||
// - An entry's files are the backticked spans it opens with, separated only by
|
||||
// commas or whitespace. Everything from the first other text on is
|
||||
// description and is never read ("`a.test.mjs` - covers `src/parse.mjs`"
|
||||
|
|
@ -486,6 +490,15 @@ export function fieldPaths(text) {
|
|||
// A leading span that fails isPathShape (`parse()`, an option, a URL) is
|
||||
// refused as no path (PLAN_STEP_PATH_NOT_A_PATH).
|
||||
//
|
||||
// - A `Test first:` `File:` line is a line that opens with `File:` (or
|
||||
// `**File:**`) after an optional list marker of the same set (`-`, `*`, `+`,
|
||||
// `1.`, `1)`); its rest is one entry under this grammar. The non-list lines
|
||||
// right after it are appended to it with one space, as above; any list item
|
||||
// (a `Verifies:` or `Pattern:` line too) ends it, and a non-list line after
|
||||
// such an item belongs to that item, never to the `File:` entry. A line that
|
||||
// opens with `File:` always starts a new entry; a `File:` mid-line names no
|
||||
// test (review `4b6460e5`).
|
||||
//
|
||||
// isPathShape is the test fieldPaths puts on a span in the leading position of
|
||||
// an entry (a path, a build file, or a capitalised name such as `LICENSE`), so
|
||||
// a 1.9 entry's leading span is judged exactly as the reader below 1.9 judges
|
||||
|
|
@ -533,8 +546,15 @@ export function entryPaths(text) {
|
|||
const entries = [];
|
||||
String(text ?? '').split('\n').forEach((line, i) => {
|
||||
const item = LIST_ITEM.exec(line);
|
||||
const rest = line.trim();
|
||||
if (item) entries.push({ line: i + 1, entry: line.slice(item[0].length).trim() });
|
||||
else if (entries.length === 0 && line.trim() !== '') entries.push({ line: i + 1, entry: line.trim() });
|
||||
else if (rest === '') return;
|
||||
else if (entries.length === 0) entries.push({ line: i + 1, entry: rest });
|
||||
else {
|
||||
// A wrapped continuation line belongs to the entry above it.
|
||||
const last = entries[entries.length - 1];
|
||||
last.entry = `${last.entry} ${rest}`.trim();
|
||||
}
|
||||
});
|
||||
if (entries.length === 0) {
|
||||
issues.push({ line: 1, entry: '', code: 'PLAN_STEP_PATH_NOT_FIRST' });
|
||||
|
|
@ -599,12 +619,14 @@ export function stepTestsOnly(body, version) {
|
|||
// selector); global, so read it with matchAll only: one match per `File:`.
|
||||
const TEST_FIRST_FILE_LINE = /\bFile:\**[ \t]*`/gi;
|
||||
// From plan_version 1.9: a line that opens with `File:` (after an optional
|
||||
// list marker and `**`), backticked or not.
|
||||
const TEST_FIRST_FILE_LINE_19 = /^\s*(?:[-*+]\s+)?(?:\*\*)?File:(?:\*\*)?/i;
|
||||
// list marker of the set LIST_ITEM accepts, and `**`), backticked or not.
|
||||
const TEST_FIRST_FILE_LINE_19 = /^\s*(?:(?:[-*+]|\d+[.)])\s+)?(?:\*\*)?File:(?:\*\*)?/i;
|
||||
|
||||
/**
|
||||
* plan_version 1.9: the rest of each `Test first:` `File:` line (after `File:`
|
||||
* and any `**`), one entry per line, in order. The one selector both
|
||||
* and any `**`), with the non-list lines right after it appended (one space
|
||||
* each), one entry per `File:` line, in order. Any list item ends the entry;
|
||||
* a line that opens with `File:` starts a new one. The one selector both
|
||||
* testFirstFiles and the plan validator's grammar check read.
|
||||
* @param {Map<string, {text: string}>} fields - parseStepFields(body)
|
||||
* @returns {string[]}
|
||||
|
|
@ -612,9 +634,18 @@ const TEST_FIRST_FILE_LINE_19 = /^\s*(?:[-*+]\s+)?(?:\*\*)?File:(?:\*\*)?/i;
|
|||
export function testFirstFileEntries(fields) {
|
||||
if (!fields || !fields.has('Test first')) return [];
|
||||
const out = [];
|
||||
// Whether the line above is the current `File:` line or its continuation.
|
||||
let open = false;
|
||||
for (const line of String(fields.get('Test first').text ?? '').split('\n')) {
|
||||
const at = TEST_FIRST_FILE_LINE_19.exec(line);
|
||||
if (at) out.push(line.slice(at[0].length).trim());
|
||||
if (at) {
|
||||
out.push(line.slice(at[0].length).trim());
|
||||
open = true;
|
||||
} else if (LIST_ITEM.test(line) || line.trim() === '') {
|
||||
open = false;
|
||||
} else if (open) {
|
||||
out[out.length - 1] = `${out[out.length - 1]} ${line.trim()}`.trim();
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -634,8 +665,10 @@ export function testFirstFileEntries(fields) {
|
|||
* `File:`, so a line with two markers keeps at least 6.3.1's files.
|
||||
*
|
||||
* From 1.9 a line counts when it opens with `File:` (testFirstFileEntries),
|
||||
* backticked or not, and its rest is one entry read by entryPaths: the
|
||||
* leading backticked paths are the test files, the rest is description.
|
||||
* backticked or not, after an optional list marker (`1.` and `1)` included);
|
||||
* its rest, with its wrapped continuation lines, is one entry read by
|
||||
* entryPaths: the leading backticked paths are the test files, the rest is
|
||||
* description.
|
||||
*
|
||||
* Normalised, in order, deduplicated. A `Verifies:` line without a `File:` is
|
||||
* never read.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue