create-pmos 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/KIT-VERSION +2 -2
- package/payload/mods/pmos-cockpit/README.md +13 -0
- package/payload/mods/pmos-cockpit/hooks/lib/commands.mjs +2 -1
- package/payload/mods/pmos-cockpit/hooks/lib/grants.mjs +41 -11
- package/payload/mods/pmos-cockpit/hooks/lib/guard.mjs +228 -8
- package/payload/mods/pmos-cockpit/hooks/register.tsx +11 -3
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/scripts/plan-items-stage.js +271 -0
- package/payload/scripts/pmos-log.sh +18 -7
- package/payload/scripts/workflow-state.js +28 -2
- package/payload/state/discoveries.md +1 -1
- package/payload/state/log.md +1 -1
- package/workspace/bin/pmos.js +15 -1
- package/workspace/cut-stub.js +40 -0
- package/workspace/first-paint-audit.js +145 -0
- package/workspace/fixtures/capture/requirements/capture-2026-10-04-1.md +10 -0
- package/workspace/fixtures/initiatives-api/list.json +176 -0
- package/workspace/fixtures/initiatives-api/post-403.json +5 -0
- package/workspace/fixtures/initiatives-api/post-ok.json +10 -0
- package/workspace/fixtures/initiatives-api/roadmap.json +208 -0
- package/workspace/fixtures/initiatives-api/sheet.json +112 -0
- package/workspace/fixtures/report-api/draft.json +177 -0
- package/workspace/fixtures/reports-page/draft-biweekly-2026-10-16.md +97 -0
- package/workspace/fixtures/today-api/provider-rows.json +204 -0
- package/workspace/fixtures/today-api/seam-probe-kinds.json +5 -0
- package/workspace/guide/board.md +81 -0
- package/workspace/guide/breakdown.md +154 -0
- package/workspace/guide/command-palette.md +75 -0
- package/workspace/guide/index.md +27 -17
- package/workspace/guide/initiatives-roadmap.md +130 -0
- package/workspace/guide/initiatives-screen.md +89 -0
- package/workspace/guide/initiatives-timeline.md +57 -0
- package/workspace/guide/integrated-work.md +94 -0
- package/workspace/guide/knowledge-graph.md +3 -68
- package/workspace/guide/layout.md +98 -32
- package/workspace/guide/live-notes.md +3 -3
- package/workspace/guide/pm-only-acts.md +89 -0
- package/workspace/guide/pm-only-lines.md +61 -0
- package/workspace/guide/reports-page.md +80 -0
- package/workspace/guide/reports.md +73 -0
- package/workspace/guide/timeline.md +70 -0
- package/workspace/guide/today.md +85 -0
- package/workspace/guide/validate-with-typesafe.md +1 -1
- package/workspace/lib/actor.js +91 -0
- package/workspace/lib/board-cache.js +32 -2
- package/workspace/lib/board-cli.js +108 -0
- package/workspace/lib/board-fold.js +232 -0
- package/workspace/lib/breakdown-export.js +80 -0
- package/workspace/lib/capture.js +98 -0
- package/workspace/lib/engine/plan-items-stage.js +271 -0
- package/workspace/lib/engine/workflow-state.js +28 -2
- package/workspace/lib/factory-join.js +158 -0
- package/workspace/lib/grant-mint.js +113 -0
- package/workspace/lib/health-rules-factory.js +92 -0
- package/workspace/lib/health-rules-plan.js +164 -0
- package/workspace/lib/health.js +79 -0
- package/workspace/lib/initiative-cli.js +222 -0
- package/workspace/lib/initiative-display.js +183 -0
- package/workspace/lib/initiative-needs.js +127 -0
- package/workspace/lib/initiative-rows.js +206 -0
- package/workspace/lib/initiative-writer.js +141 -0
- package/workspace/lib/issues.js +206 -0
- package/workspace/lib/item-cli.js +195 -0
- package/workspace/lib/item-spans.js +137 -0
- package/workspace/lib/measure-cli.js +100 -0
- package/workspace/lib/measurements.js +189 -0
- package/workspace/lib/mobile-nav.js +57 -53
- package/workspace/lib/needs-providers.js +120 -0
- package/workspace/lib/placements.js +440 -0
- package/workspace/lib/plan-items.js +922 -0
- package/workspace/lib/plan-ledgers.js +85 -0
- package/workspace/lib/plan-rules.js +72 -0
- package/workspace/lib/plan-tree.js +367 -0
- package/workspace/lib/pm-guard.js +174 -0
- package/workspace/lib/pm-lines-needs.js +331 -0
- package/workspace/lib/pmos-verbs.js +66 -0
- package/workspace/lib/proactive.js +7 -4
- package/workspace/lib/report-cli.js +152 -0
- package/workspace/lib/report-draft.js +579 -0
- package/workspace/lib/report-needs.js +213 -0
- package/workspace/lib/route-modules.js +128 -0
- package/workspace/lib/routes-board.js +91 -0
- package/workspace/lib/routes-capture.js +52 -0
- package/workspace/lib/routes-initiatives.js +371 -0
- package/workspace/lib/routes-reports.js +163 -0
- package/workspace/lib/routes-timeline.js +197 -0
- package/workspace/lib/routes-today.js +32 -0
- package/workspace/lib/today-cli.js +99 -0
- package/workspace/lib/today.js +333 -0
- package/workspace/lib/triage.js +163 -0
- package/workspace/lib/verify-work-cli.js +81 -0
- package/workspace/lib/work-check-needs.js +99 -0
- package/workspace/package-lock.json +1 -1
- package/workspace/package.json +5 -5
- package/workspace/public/index.html +29 -19
- package/workspace/public/pm/app.js +299 -30
- package/workspace/public/pm/bd-board.js +113 -0
- package/workspace/public/pm/bd-drag.js +59 -0
- package/workspace/public/pm/bd-model.js +152 -0
- package/workspace/public/pm/bd-timeline.js +233 -0
- package/workspace/public/pm/bd-tree.js +244 -0
- package/workspace/public/pm/board-copy.js +57 -0
- package/workspace/public/pm/breakdown-api.js +58 -0
- package/workspace/public/pm/breakdown-copy.js +138 -0
- package/workspace/public/pm/components.css +3054 -0
- package/workspace/public/pm/cycle-review.js +125 -0
- package/workspace/public/pm/data.js +40 -187
- package/workspace/public/pm/dest-board.js +438 -0
- package/workspace/public/pm/dest-initiatives.js +134 -0
- package/workspace/public/pm/dest-reports.js +270 -0
- package/workspace/public/pm/dest-settings.js +217 -0
- package/workspace/public/pm/dest-today.js +176 -0
- package/workspace/public/pm/dialogs.js +88 -0
- package/workspace/public/pm/display-word.js +56 -0
- package/workspace/public/pm/dock.js +105 -0
- package/workspace/public/pm/fixture.js +8 -227
- package/workspace/public/pm/frame-copy.js +94 -0
- package/workspace/public/pm/handoff-box.js +82 -0
- package/workspace/public/pm/http.js +88 -0
- package/workspace/public/pm/icons.js +53 -0
- package/workspace/public/pm/initiative-list.js +218 -0
- package/workspace/public/pm/initiative-sheet.js +261 -0
- package/workspace/public/pm/initiatives-api.js +59 -0
- package/workspace/public/pm/initiatives-copy.js +215 -0
- package/workspace/public/pm/initiatives-timeline-copy.js +59 -0
- package/workspace/public/pm/initiatives-timeline.js +410 -0
- package/workspace/public/pm/initiatives-ui.js +123 -0
- package/workspace/public/pm/item-sheet.js +251 -0
- package/workspace/public/pm/keep-card.js +204 -0
- package/workspace/public/pm/lifecycle.js +7 -1
- package/workspace/public/pm/mobile.css +49 -20
- package/workspace/public/pm/mobile.js +157 -29
- package/workspace/public/pm/modes-live.js +7 -883
- package/workspace/public/pm/modes.js +32 -1024
- package/workspace/public/pm/new-initiative-sheet.js +74 -0
- package/workspace/public/pm/next-step-ui.js +3 -2
- package/workspace/public/pm/palette-copy.js +51 -0
- package/workspace/public/pm/palette.js +312 -0
- package/workspace/public/pm/panel-breakdown.js +702 -0
- package/workspace/public/pm/panel-cockpit.js +63 -295
- package/workspace/public/pm/panel-detail.js +25 -172
- package/workspace/public/pm/panel-initiatives.js +1 -19
- package/workspace/public/pm/panel-repos.js +31 -81
- package/workspace/public/pm/panel-tabs.js +197 -110
- package/workspace/public/pm/panel-trees.js +5 -2
- package/workspace/public/pm/portfolio-report.js +61 -0
- package/workspace/public/pm/primitives.js +656 -0
- package/workspace/public/pm/print.css +33 -0
- package/workspace/public/pm/provider-cards.js +136 -0
- package/workspace/public/pm/registry.js +194 -0
- package/workspace/public/pm/release-card.js +110 -0
- package/workspace/public/pm/report-view.js +309 -0
- package/workspace/public/pm/reports-copy.js +114 -0
- package/workspace/public/pm/roadmap.js +271 -0
- package/workspace/public/pm/sig-watch.js +41 -0
- package/workspace/public/pm/state.js +8 -4
- package/workspace/public/pm/status-banner.js +13 -5
- package/workspace/public/pm/tab-doc.js +2 -4
- package/workspace/public/pm/tab-term.js +12 -2
- package/workspace/public/pm/tab-web.js +3 -2
- package/workspace/public/pm/timeline-copy.js +48 -0
- package/workspace/public/pm/today-cards.js +244 -0
- package/workspace/public/pm/today-copy.js +97 -0
- package/workspace/public/pm/tokens.css +16 -18
- package/workspace/public/pm/topbar.js +102 -0
- package/workspace/public/pm/triage-card.js +60 -0
- package/workspace/public/pm/undo-hold.js +103 -0
- package/workspace/public/pm/view-addrepo.js +2 -1
- package/workspace/public/pm/view-livenotes.js +3 -1
- package/workspace/public/pm/view-today.js +7 -162
- package/workspace/public/vendor/Sortable.min.js +2 -0
- package/workspace/public/vendor/lucide-sprite.js +79 -0
- package/workspace/replaced-tests.json +2864 -0
- package/workspace/run-tests.js +117 -6
- package/workspace/server.js +312 -36
- package/workspace/sheet-answer.js +61 -0
- package/workspace/skills/measure.skill +41 -0
- package/workspace/skills/report-biweekly.skill +23 -25
- package/workspace/skills/tracker-refresh.skill +50 -0
- package/workspace/test-files.json +20 -4
- package/workspace/test-lists/base.json +6 -0
- package/workspace/test-lists/run2-a.json +6 -0
- package/workspace/test-lists/run2-b.json +13 -0
- package/workspace/test-lists/run2-c.json +19 -0
- package/workspace/test-lists/run2-d.json +22 -0
- package/workspace/test-lists/run3-0.json +5 -0
- package/workspace/test-lists/run3-a.json +5 -0
- package/workspace/test-lists/run3-b.json +6 -0
- package/workspace/test-lists/run3-c.json +15 -0
- package/workspace/test-lists/run3-d.json +6 -0
- package/workspace/test-lists/run4-a.json +8 -0
- package/workspace/test-lists/run4-b.json +6 -0
- package/workspace/test-lists/run4-c.json +13 -0
- package/workspace/test-lists/run4-d.json +9 -0
- package/workspace/test-lists/run5-a.json +6 -0
- package/workspace/test-lists/run5-b.json +6 -0
- package/workspace/test-lists/run5-c.json +15 -0
- package/workspace/test-run.js +27 -1
- package/workspace/third-party/README.md +44 -0
- package/workspace/third-party/lucide-static-1.52.0.LICENSE +43 -0
- package/workspace/third-party/sortablejs-1.15.7.LICENSE +21 -0
- package/workspace/design-parity.js +0 -673
- package/workspace/public/pm/tab-task.js +0 -86
- package/workspace/public/pm/view-guide.js +0 -141
- package/workspace/public/pm/view-knowledge.js +0 -225
- package/workspace/public/pm/view-memory.js +0 -122
- package/workspace/public/pm/view-portfolio.js +0 -153
- package/workspace/public/pm/view-reports.js +0 -190
- package/workspace/public/pm/view-scheduled.js +0 -161
- package/workspace/public/pm/view-skills.js +0 -216
package/workspace/run-tests.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
/* run-tests.js — run the workspace suites side by side.
|
|
4
4
|
*
|
|
5
|
-
* node run-tests.js <group> [--jobs=N] [--shard=I/N] [--list] [--verbose]
|
|
5
|
+
* node run-tests.js <group> [--jobs=N] [--shard=I/N] [--changed[=REF]] [--list] [--verbose]
|
|
6
6
|
* node run-tests.js --check
|
|
7
7
|
*
|
|
8
8
|
* Groups come from test-files.json: unit (test.js + modules), modules, panels, ui, browser
|
|
@@ -17,8 +17,20 @@
|
|
|
17
17
|
* file in exactly one shard), and --check fails when a suite on disk is in no list, so a new suite
|
|
18
18
|
* that nobody wired in is a red build rather than a suite that runs nowhere.
|
|
19
19
|
*
|
|
20
|
+
* --changed is for the LOCAL inner loop only. It narrows the group to the suites a diff against REF
|
|
21
|
+
* (default origin/main) can reach, and runs the whole group whenever it cannot tell. CI never passes
|
|
22
|
+
* it: Workspace CI runs every suite on every PR, so a suite --changed leaves out still runs before
|
|
23
|
+
* the merge. See changedSuites() for the rule.
|
|
24
|
+
*
|
|
20
25
|
* Output: a suite's own output is buffered and printed whole when it finishes, so lines from two
|
|
21
26
|
* suites never interleave. A passing suite prints its last lines; --verbose prints all of it.
|
|
27
|
+
*
|
|
28
|
+
* Fragments (pm-os-absorb Runs 2 to 5). Builders who work at the same time add their suites in a file
|
|
29
|
+
* of their own, test-lists/<name>.json, never in test-files.json: two slices that each append to the
|
|
30
|
+
* same JSON array always conflict. A fragment has the keys modules, panels, exclusive and seconds of
|
|
31
|
+
* test-files.json, plus `helpers` (a -test.js file that is a helper, not a suite, with the reason, as
|
|
32
|
+
* in ELSEWHERE). This file merges every fragment into the lists before it does anything else, so
|
|
33
|
+
* --check, --shard and every group see one list. A fragment that is not valid fails every command.
|
|
22
34
|
*/
|
|
23
35
|
const fs = require('fs');
|
|
24
36
|
const os = require('os');
|
|
@@ -26,7 +38,41 @@ const path = require('path');
|
|
|
26
38
|
const { spawn } = require('child_process');
|
|
27
39
|
|
|
28
40
|
const DIR = __dirname;
|
|
29
|
-
const
|
|
41
|
+
const FRAG_DIR = path.join(DIR, 'test-lists');
|
|
42
|
+
const FRAG_KEYS = ['_comment', 'modules', 'panels', 'exclusive', 'seconds', 'helpers'];
|
|
43
|
+
const fragProblems = [];
|
|
44
|
+
const fragHelpers = {};
|
|
45
|
+
/** test-files.json with every test-lists/*.json fragment merged in (see the header). Problems go to fragProblems. */
|
|
46
|
+
function loadFiles() {
|
|
47
|
+
const files = JSON.parse(fs.readFileSync(path.join(DIR, 'test-files.json'), 'utf8'));
|
|
48
|
+
let names = [];
|
|
49
|
+
try { names = fs.readdirSync(FRAG_DIR).filter((n) => n.endsWith('.json')).sort(); } catch { /* no fragments yet */ }
|
|
50
|
+
for (const n of names) {
|
|
51
|
+
const where = `test-lists/${n}`;
|
|
52
|
+
if (!/^[a-z0-9][a-z0-9-]*\.json$/.test(n)) fragProblems.push(`${where}: a fragment is named <lowercase-id>.json, for example run2-a.json`);
|
|
53
|
+
let frag;
|
|
54
|
+
try { frag = JSON.parse(fs.readFileSync(path.join(FRAG_DIR, n), 'utf8')); } catch (e) { fragProblems.push(`${where} is not valid JSON — ${e.message}`); continue; }
|
|
55
|
+
if (!frag || typeof frag !== 'object' || Array.isArray(frag)) { fragProblems.push(`${where} is not a JSON object`); continue; }
|
|
56
|
+
for (const k of Object.keys(frag)) if (!FRAG_KEYS.includes(k)) fragProblems.push(`${where}: unknown key "${k}" (the keys are ${FRAG_KEYS.slice(1).join(', ')})`);
|
|
57
|
+
for (const k of ['modules', 'panels', 'exclusive']) {
|
|
58
|
+
if (frag[k] === undefined) continue;
|
|
59
|
+
if (!Array.isArray(frag[k]) || !frag[k].every((f) => typeof f === 'string')) { fragProblems.push(`${where}: "${k}" is a list of file names`); continue; }
|
|
60
|
+
files[k] = [...(files[k] || []), ...frag[k]];
|
|
61
|
+
}
|
|
62
|
+
for (const [f, v] of Object.entries(frag.seconds || {})) {
|
|
63
|
+
if (typeof v !== 'number' || !(v > 0)) fragProblems.push(`${where}: seconds of ${f} is a positive number`);
|
|
64
|
+
else if (files.seconds && f in files.seconds) fragProblems.push(`${where}: seconds of ${f} is already recorded elsewhere`);
|
|
65
|
+
else files.seconds = { ...(files.seconds || {}), [f]: v };
|
|
66
|
+
}
|
|
67
|
+
for (const [f, why] of Object.entries(frag.helpers || {})) {
|
|
68
|
+
if (typeof why !== 'string' || !why.trim()) fragProblems.push(`${where}: helper ${f} needs a reason`);
|
|
69
|
+
else if (fragHelpers[f]) fragProblems.push(`${where}: helper ${f} is already named by ${fragHelpers[f].where}`);
|
|
70
|
+
else fragHelpers[f] = { why, where };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return files;
|
|
74
|
+
}
|
|
75
|
+
const FILES = loadFiles();
|
|
30
76
|
const GROUPS = {
|
|
31
77
|
unit: [...FILES.unit, ...FILES.modules],
|
|
32
78
|
modules: FILES.modules,
|
|
@@ -38,7 +84,14 @@ const GROUPS = {
|
|
|
38
84
|
// Suites that run elsewhere on purpose, so --check does not count them as unwired.
|
|
39
85
|
const ELSEWHERE = {
|
|
40
86
|
'offline-tarball.test.js': 'npm run test:offline (needs a packed tarball)',
|
|
87
|
+
// A helper module, not a suite: the Breakdown suites require it (pm-os-absorb Run 1). Its name keeps it out of the
|
|
88
|
+
// tarball (BUNDLE_EXCLUDES has 'pm-*-test.js'), which is why it ends in -test.js.
|
|
89
|
+
'pm-breakdown-stub-test.js': 'not a suite — the helpers pm-breakdown-test.js, pm-breakdown-phone-test.js and first-paint-count.test.js require',
|
|
41
90
|
};
|
|
91
|
+
for (const [f, h] of Object.entries(fragHelpers)) {
|
|
92
|
+
if (ELSEWHERE[f]) fragProblems.push(`${h.where}: helper ${f} is already exempt in run-tests.js`);
|
|
93
|
+
else ELSEWHERE[f] = `${h.why} (${h.where})`;
|
|
94
|
+
}
|
|
42
95
|
const PER_FILE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
43
96
|
|
|
44
97
|
const args = process.argv.slice(2);
|
|
@@ -65,12 +118,60 @@ function shardOf(list, spec) {
|
|
|
65
118
|
return out[i - 1];
|
|
66
119
|
}
|
|
67
120
|
|
|
121
|
+
// Files every page or every suite loads. A change to one can break any suite, so --changed runs
|
|
122
|
+
// the whole group. A changed file no suite can be traced to is treated the same way (see below).
|
|
123
|
+
const WIDE = new Set([
|
|
124
|
+
'server.js', 'test-run.js', 'run-tests.js', 'test-files.json', 'package.json', 'package-lock.json',
|
|
125
|
+
'public/pm/app.js', 'public/pm/state.js', 'public/pm/dom.js', 'public/pm/data.js',
|
|
126
|
+
'public/pm/tokens.css', 'public/pm/fixture.js', 'public/pm/mobile.css', 'lib/find-browser.js',
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
/* The suites in `list` that the diff against `ref` can reach. A suite is selected when its own file
|
|
130
|
+
* changed, when its source names a changed file (`panel-tabs` in pm-tabs-test.js), or when its file
|
|
131
|
+
* name carries the changed file's stem with any panel-/view-/tab- prefix removed (view-memory.js ->
|
|
132
|
+
* pm-r29-memory-test.js). It errs toward running MORE: a WIDE file, or a changed workspace file that
|
|
133
|
+
* no suite can be traced to, selects the whole list. Returns { files, suites, why }. */
|
|
134
|
+
function changedSuites(list, ref) {
|
|
135
|
+
const git = (...a) => {
|
|
136
|
+
const r = require('child_process').spawnSync('git', a, { cwd: DIR, encoding: 'utf8' });
|
|
137
|
+
return r.status === 0 ? r.stdout : null;
|
|
138
|
+
};
|
|
139
|
+
const top = (git('rev-parse', '--show-toplevel') || '').trim();
|
|
140
|
+
if (!top) return { files: [], suites: list, why: 'not a git checkout, so every suite runs' };
|
|
141
|
+
const base = (git('merge-base', 'HEAD', ref) || '').trim();
|
|
142
|
+
if (!base) return { files: [], suites: list, why: `no merge base with ${ref} (fetch it first), so every suite runs` };
|
|
143
|
+
const prefix = path.relative(top, DIR).split(path.sep).join('/') + '/';
|
|
144
|
+
const names = [
|
|
145
|
+
git('diff', '--name-only', base), // committed on this branch and uncommitted changes
|
|
146
|
+
git('ls-files', '--others', '--exclude-standard', '--full-name', DIR),
|
|
147
|
+
].join('\n').split('\n').map((f) => f.trim()).filter(Boolean);
|
|
148
|
+
const files = [...new Set(names)].filter((f) => f.startsWith(prefix)).map((f) => f.slice(prefix.length));
|
|
149
|
+
if (!files.length) return { files, suites: [], why: `nothing under workspace/ differs from ${ref}` };
|
|
150
|
+
const wide = files.filter((f) => WIDE.has(f));
|
|
151
|
+
if (wide.length) return { files, suites: list, why: `${wide.join(', ')} changed, and every suite depends on it` };
|
|
152
|
+
const source = new Map(list.map((f) => [f, fs.existsSync(path.join(DIR, f)) ? fs.readFileSync(path.join(DIR, f), 'utf8') : '']));
|
|
153
|
+
const picked = new Set();
|
|
154
|
+
const untraced = [];
|
|
155
|
+
for (const f of files) {
|
|
156
|
+
if (source.has(f)) { picked.add(f); continue; }
|
|
157
|
+
if (/^(guide|docs)\/|\.md$/.test(f)) continue; // prose: no suite asserts on it except guide.test.js, a unit suite
|
|
158
|
+
const stem = path.basename(f).replace(/\.[^.]+$/, '');
|
|
159
|
+
const key = stem.replace(/^(panel|view|tab)-/, '');
|
|
160
|
+
const hits = list.filter((s) => source.get(s).includes(stem) || s.includes(`-${key}-`) || s.includes(`-${key}.`));
|
|
161
|
+
if (!hits.length) untraced.push(f);
|
|
162
|
+
for (const h of hits) picked.add(h);
|
|
163
|
+
}
|
|
164
|
+
if (untraced.length) return { files, suites: list, why: `no suite names ${untraced.slice(0, 3).join(', ')}${untraced.length > 3 ? ' …' : ''}, so every suite runs` };
|
|
165
|
+
return { files, suites: list.filter((f) => picked.has(f)), why: `${files.length} changed file(s) reach these suites` };
|
|
166
|
+
}
|
|
167
|
+
|
|
68
168
|
function check() {
|
|
69
169
|
const listed = new Set(GROUPS.all);
|
|
70
|
-
const problems = [];
|
|
170
|
+
const problems = [...fragProblems];
|
|
71
171
|
const dupes = GROUPS.all.filter((f, k) => GROUPS.all.indexOf(f) !== k);
|
|
72
|
-
for (const f of new Set(dupes)) problems.push(`${f} is listed more than once in test-files.json
|
|
73
|
-
for (const f of listed) if (!fs.existsSync(path.join(DIR, f))) problems.push(`${f} is listed in test-files.json but does not exist`);
|
|
172
|
+
for (const f of new Set(dupes)) problems.push(`${f} is listed more than once in test-files.json and test-lists/`);
|
|
173
|
+
for (const f of listed) if (!fs.existsSync(path.join(DIR, f))) problems.push(`${f} is listed in test-files.json or test-lists/ but does not exist`);
|
|
174
|
+
for (const f of Object.keys(ELSEWHERE)) if (listed.has(f)) problems.push(`${f} is exempt as a helper but is also listed as a suite`);
|
|
74
175
|
for (const f of FILES.exclusive || []) if (!listed.has(f)) problems.push(`${f} is marked exclusive but is in no test list`);
|
|
75
176
|
const onDisk = fs.readdirSync(DIR).filter((f) => f === 'test.js' || /(\.test\.(js|mjs)|-test\.js)$/.test(f));
|
|
76
177
|
for (const f of onDisk) if (!listed.has(f) && !ELSEWHERE[f]) problems.push(`${f} is a suite on disk that no test list runs. Add it to test-files.json.`);
|
|
@@ -79,7 +180,8 @@ function check() {
|
|
|
79
180
|
if (parts.length !== GROUPS[g].length || new Set(parts).size !== GROUPS[g].length) problems.push(`--shard does not partition "${g}" into ${n}`);
|
|
80
181
|
}
|
|
81
182
|
if (problems.length) { for (const p of problems) console.error(`✗ ${p}`); process.exitCode = 1; return; }
|
|
82
|
-
|
|
183
|
+
const frags = fs.existsSync(FRAG_DIR) ? fs.readdirSync(FRAG_DIR).filter((n) => n.endsWith('.json')).length : 0;
|
|
184
|
+
console.log(`✓ ${listed.size} suites listed, each once, each on disk; ${onDisk.filter((f) => !ELSEWHERE[f]).length} suites on disk, all listed; shards partition every group; ${frags} fragment(s) in test-lists/.`);
|
|
83
185
|
}
|
|
84
186
|
|
|
85
187
|
function runOne(file, verbose) {
|
|
@@ -107,6 +209,7 @@ function runOne(file, verbose) {
|
|
|
107
209
|
|
|
108
210
|
async function main() {
|
|
109
211
|
if (flag('check')) return check();
|
|
212
|
+
if (fragProblems.length) { for (const p of fragProblems) console.error(`✗ ${p}`); process.exitCode = 2; return; }
|
|
110
213
|
if (!group || !GROUPS[group]) {
|
|
111
214
|
console.error(`usage: node run-tests.js <${Object.keys(GROUPS).join('|')}> [--jobs=N] [--shard=I/N] [--list] [--verbose]\n node run-tests.js --check`);
|
|
112
215
|
process.exitCode = 2; return;
|
|
@@ -114,6 +217,14 @@ async function main() {
|
|
|
114
217
|
let list = GROUPS[group];
|
|
115
218
|
const shard = flag('shard');
|
|
116
219
|
list = shard ? shardOf(list, shard) : bySlowest(list);
|
|
220
|
+
const changed = flag('changed');
|
|
221
|
+
if (changed) {
|
|
222
|
+
const ref = changed === true ? 'origin/main' : changed;
|
|
223
|
+
const pick = changedSuites(list, ref);
|
|
224
|
+
console.log(`run-tests: --changed against ${ref}: ${pick.why}. ${pick.suites.length} of ${list.length} suites run here; CI runs all ${list.length}.`);
|
|
225
|
+
list = pick.suites;
|
|
226
|
+
if (!list.length) return;
|
|
227
|
+
}
|
|
117
228
|
if (flag('list')) { console.log(list.join('\n')); return; }
|
|
118
229
|
const cpus = typeof os.availableParallelism === 'function' ? os.availableParallelism() : os.cpus().length;
|
|
119
230
|
// A browser suite runs a server, a tmux daemon and a Chromium of its own, and several of them assert
|