* ci: streamline PR pipeline gates * test: annotate ci-test-scope stderr assertions for lint rule --------- Co-authored-by: Colin <colin@solvely.net>
This commit is contained in:
@@ -11,6 +11,8 @@
|
||||
// node scripts/run-tests.cjs --suite integration # *.integration.test.cjs
|
||||
// node scripts/run-tests.cjs --suite install # *.install.test.cjs
|
||||
// node scripts/run-tests.cjs --suite slow # *.slow.test.cjs
|
||||
// node scripts/run-tests.cjs --files "a.test.cjs b.test.cjs"
|
||||
// node scripts/run-tests.cjs --files-from /tmp/selected-tests.txt
|
||||
//
|
||||
// Suite grouping convention: filename suffix marker before `.test.cjs`.
|
||||
// A file named `foo.security.test.cjs` belongs to the `security` suite.
|
||||
@@ -28,6 +30,8 @@ const MARKED_SUITES = ['integration', 'install', 'security', 'slow'];
|
||||
function parseArgs(argv) {
|
||||
let suite = null;
|
||||
let seen = false;
|
||||
let files = null;
|
||||
let filesFrom = null;
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const a = argv[i];
|
||||
if (a === '--suite') {
|
||||
@@ -50,11 +54,50 @@ function parseArgs(argv) {
|
||||
if (!suite) {
|
||||
return { error: '--suite requires a value' };
|
||||
}
|
||||
} else if (a === '--files') {
|
||||
if (files !== null) {
|
||||
return { error: 'duplicate --files flag' };
|
||||
}
|
||||
const v = argv[i + 1];
|
||||
if (!v || v.startsWith('--')) {
|
||||
return { error: '--files requires a value' };
|
||||
}
|
||||
files = v;
|
||||
i++;
|
||||
} else if (a.startsWith('--files=')) {
|
||||
if (files !== null) {
|
||||
return { error: 'duplicate --files flag' };
|
||||
}
|
||||
files = a.slice('--files='.length);
|
||||
if (!files) {
|
||||
return { error: '--files requires a value' };
|
||||
}
|
||||
} else if (a === '--files-from') {
|
||||
if (filesFrom !== null) {
|
||||
return { error: 'duplicate --files-from flag' };
|
||||
}
|
||||
const v = argv[i + 1];
|
||||
if (!v || v.startsWith('--')) {
|
||||
return { error: '--files-from requires a value' };
|
||||
}
|
||||
filesFrom = v;
|
||||
i++;
|
||||
} else if (a.startsWith('--files-from=')) {
|
||||
if (filesFrom !== null) {
|
||||
return { error: 'duplicate --files-from flag' };
|
||||
}
|
||||
filesFrom = a.slice('--files-from='.length);
|
||||
if (!filesFrom) {
|
||||
return { error: '--files-from requires a value' };
|
||||
}
|
||||
} else {
|
||||
return { error: `unknown argument: ${a}` };
|
||||
}
|
||||
}
|
||||
return { suite };
|
||||
if (files !== null && filesFrom !== null) {
|
||||
return { error: '--files and --files-from cannot be combined' };
|
||||
}
|
||||
return { suite, files, filesFrom };
|
||||
}
|
||||
|
||||
// Return the marked suite name embedded in a filename, or null if it's unmarked.
|
||||
@@ -79,6 +122,38 @@ function selectFiles(allFiles, suite) {
|
||||
return allFiles.filter(f => suiteOf(f) === suite);
|
||||
}
|
||||
|
||||
function splitFileList(value) {
|
||||
if (!value) return [];
|
||||
return value
|
||||
.split(/[,\s]+/)
|
||||
.map(v => v.trim())
|
||||
.filter(Boolean)
|
||||
.map(v => v.replace(/^tests[\\/]/, ''));
|
||||
}
|
||||
|
||||
function selectExplicitFiles(allFiles, filesValue, filesFrom) {
|
||||
const fs = require('fs');
|
||||
const requested = filesFrom
|
||||
? splitFileList(fs.readFileSync(filesFrom, 'utf8'))
|
||||
: splitFileList(filesValue);
|
||||
const available = new Set(allFiles);
|
||||
const selected = [];
|
||||
const missing = [];
|
||||
for (const file of requested) {
|
||||
if (available.has(file)) {
|
||||
selected.push(file);
|
||||
} else {
|
||||
missing.push(file);
|
||||
}
|
||||
}
|
||||
if (missing.length > 0) {
|
||||
return {
|
||||
error: `requested test file(s) not found: ${missing.join(', ')}`,
|
||||
};
|
||||
}
|
||||
return { files: [...new Set(selected)] };
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const parsed = parseArgs(args);
|
||||
@@ -107,7 +182,18 @@ function main() {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const selected = selectFiles(allFiles, suite).map(f => join(testDir, f));
|
||||
let selectedNames;
|
||||
if (parsed.files !== null || parsed.filesFrom !== null) {
|
||||
const explicit = selectExplicitFiles(allFiles, parsed.files, parsed.filesFrom);
|
||||
if (explicit.error) {
|
||||
console.error(`run-tests: ${explicit.error}`);
|
||||
process.exit(2);
|
||||
}
|
||||
selectedNames = explicit.files;
|
||||
} else {
|
||||
selectedNames = selectFiles(allFiles, suite);
|
||||
}
|
||||
const selected = selectedNames.map(f => join(testDir, f));
|
||||
|
||||
if (selected.length === 0) {
|
||||
// Empty suite: report and exit 0 so empty lanes (e.g. `security` before
|
||||
|
||||
Reference in New Issue
Block a user