Skip to content

Commit cd6c688

Browse files
committed
Implement targeted validation lanes and deterministic test isolation execution for Workspace V2 and tool testing - PR_26146_024-targeted-test-lanes-and-isolation-execution
1 parent 6eec9f5 commit cd6c688

10 files changed

Lines changed: 627 additions & 4 deletions
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Testing Lane Execution Report
2+
3+
Generated: 2026-05-26
4+
PR: PR_26146_024-targeted-test-lanes-and-isolation-execution
5+
6+
## Summary
7+
8+
PASS: 3
9+
WARN: 1
10+
FAIL: 0
11+
SKIP: 1
12+
13+
## Full Samples Smoke
14+
15+
Status: SKIP
16+
Reason: Changed files only add targeted lane execution and Playwright/test isolation infrastructure. No sample JSON, shared sample loader, or sample framework files were changed.
17+
18+
## Lane Results
19+
20+
| Lane | Status | Executed/Skipped Reason | Affected Surface | Evidence |
21+
| --- | --- | --- | --- | --- |
22+
| workspace-contract | WARN | Executed because Workspace V2 test wiring was impacted by Playwright storage isolation. | Workspace Manager V2 contract/lifecycle behavior | `PLAYWRIGHT_BROWSERS_PATH=0 npm run test:lane:workspace-contract` invoked existing `npm run test:workspace-v2`; 64 passed, 8 out-of-scope Workspace/Input Mapping/manifest/Preview Generator expectations failed. |
23+
| tool-runtime | PASS | Executed because tool-runtime lane wiring and storage isolation were added for focused tool suites. | Asset Manager V2, Preview Generator V2, Collision Inspector V2 runtime behavior | `PLAYWRIGHT_BROWSERS_PATH=0 npm run test:lane:tool-runtime`; Asset Manager V2 focused launch/runtime subset 5 passed, Preview Generator V2 7 passed, Collision Inspector V2 4 passed. |
24+
| integration | PASS | Executed because integration lane wiring was added for explicit cross-surface handoff validation. | Game index preview manifest handoff | `PLAYWRIGHT_BROWSERS_PATH=0 npm run test:lane:integration`; focused Pong handoff subset 3 passed. |
25+
| engine-src | PASS | Executed because engine/src lane wiring and node storage isolation were added. | src/ engine and shared runtime capability behavior | `npm run test:lane:engine-src`; 11/11 targeted node test files passed. |
26+
| samples | SKIP | Skipped because samples are on-request or affected-sample only and this PR did not impact samples. | On-request samples lane | Not run. Full samples smoke also skipped. |
27+
28+
## Workspace V2 WARN Details
29+
30+
`npm run test:workspace-v2` was run once through the workspace-contract lane and exited non-zero. The exact failing tests were:
31+
32+
- `Workspace Manager V2 bootstrap > sizes Input Mapping V2 columns and live-highlights mapped non-keyboard inputs`
33+
- `Workspace Manager V2 bootstrap > resolves game manifest schema refs from the game schema during repo discovery`
34+
- `Workspace Manager V2 bootstrap > enables object vector and collision tools only from manifest geometry without fallback defaults`
35+
- `Workspace Manager V2 bootstrap > uses header lifecycle controls and launches tools from fixed Workspace Manager V2 tiles`
36+
- `Workspace Manager V2 bootstrap > keeps Preview Generator V2 repo writer after Asset Manager V2 deletes the preview asset entry`
37+
- `Workspace Manager V2 bootstrap > fails Preview Generator V2 without OK WRITE when live handle read-back verification fails`
38+
- `Workspace Manager V2 bootstrap > loads Gravity Well and Pong manifests as current Workspace Manager V2 manifests`
39+
- `Workspace Manager V2 bootstrap > owns temporary UAT manifest seeding and launches Asset Manager V2 through session context`
40+
41+
These were reported as lane WARN because they are broad Workspace/Input Mapping/manifest/Preview Generator assertions outside this testing-lane infrastructure PR. The full suite was not rerun.
42+
43+
## Static Validation
44+
45+
- PASS `node --check scripts/run-targeted-test-lanes.mjs`
46+
- PASS `node --check scripts/run-node-test-files.mjs`
47+
- PASS `node --check tests/helpers/playwrightStorageIsolation.mjs`
48+
- PASS `node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
49+
- PASS `node --check tests/playwright/tools/AssetManagerV2.spec.mjs`
50+
- PASS `node --check tests/playwright/tools/PreviewGeneratorV2Baseline.spec.mjs`
51+
- PASS `node --check tests/playwright/tools/CollisionInspectorV2.spec.mjs`
52+
- PASS `node --check tests/playwright/games/GameIndexPreviewManifestResolution.spec.mjs`
53+
54+
## Lane Routing Notes
55+
56+
- Targeted execution is now available through `npm run test:lanes` and per-lane scripts.
57+
- `npm run test:workspace-v2` remains unchanged and callable directly.
58+
- The integration lane intentionally validates an explicit Pong handoff subset; broad all-game thumbnail coverage is not part of the default targeted lane.
59+
- The samples lane is available on request through `npm run test:lane:samples`, but the full samples smoke remains manual/on-request unless sample scope is active.

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
"scripts": {
44
"pretest": "node tools/dev/checkSharedExtractionGuard.mjs",
55
"test": "node ./scripts/run-node-tests.mjs",
6+
"test:lanes": "node ./scripts/run-targeted-test-lanes.mjs",
7+
"test:lane:workspace-contract": "node ./scripts/run-targeted-test-lanes.mjs --lane workspace-contract",
8+
"test:lane:tool-runtime": "node ./scripts/run-targeted-test-lanes.mjs --lane tool-runtime",
9+
"test:lane:integration": "node ./scripts/run-targeted-test-lanes.mjs --lane integration",
10+
"test:lane:engine-src": "node ./scripts/run-targeted-test-lanes.mjs --lane engine-src",
11+
"test:lane:samples": "node ./scripts/run-targeted-test-lanes.mjs --lane samples --include-samples",
612
"build:manifest": "node ./scripts/generate-sample-manifest.mjs",
713
"build:curriculum-validation-artifact": "node ./scripts/generate-curriculum-validation-artifact.mjs",
814
"sync:tool-hints": "node ./scripts/sync-tool-hints-from-workspace-manager.mjs",

scripts/run-node-test-files.mjs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
Toolbox Aid
3+
David Quesenberry
4+
05/26/2026
5+
run-node-test-files.mjs
6+
*/
7+
import path from "node:path";
8+
import { existsSync } from "node:fs";
9+
import { registerHooks } from "node:module";
10+
import { fileURLToPath, pathToFileURL } from "node:url";
11+
12+
const repoRoot = fileURLToPath(new URL("..", import.meta.url));
13+
const ROOT_ALIASES = ["/src/", "/games/", "/tools/", "/samples/"];
14+
15+
registerHooks({
16+
resolve(specifier, context, nextResolve) {
17+
if (ROOT_ALIASES.some((prefix) => specifier.startsWith(prefix))) {
18+
const resolved = pathToFileURL(path.join(repoRoot, specifier.slice(1))).href;
19+
return nextResolve(resolved, context);
20+
}
21+
return nextResolve(specifier, context);
22+
}
23+
});
24+
25+
class MemoryStorage {
26+
#items = new Map();
27+
28+
get length() {
29+
return this.#items.size;
30+
}
31+
32+
clear() {
33+
this.#items.clear();
34+
}
35+
36+
getItem(key) {
37+
const normalizedKey = String(key);
38+
return this.#items.has(normalizedKey) ? this.#items.get(normalizedKey) : null;
39+
}
40+
41+
key(index) {
42+
return Array.from(this.#items.keys())[index] ?? null;
43+
}
44+
45+
removeItem(key) {
46+
this.#items.delete(String(key));
47+
}
48+
49+
setItem(key, value) {
50+
this.#items.set(String(key), String(value));
51+
}
52+
}
53+
54+
function installFreshStorage() {
55+
Object.defineProperty(globalThis, "localStorage", {
56+
configurable: true,
57+
value: new MemoryStorage(),
58+
writable: true
59+
});
60+
Object.defineProperty(globalThis, "sessionStorage", {
61+
configurable: true,
62+
value: new MemoryStorage(),
63+
writable: true
64+
});
65+
}
66+
67+
function toRepoRelative(filePath) {
68+
return path.relative(repoRoot, filePath).replace(/\\/g, "/");
69+
}
70+
71+
function resolveTestPath(inputPath) {
72+
const absolutePath = path.resolve(repoRoot, inputPath);
73+
if (!existsSync(absolutePath)) {
74+
throw new Error(`Test file does not exist: ${inputPath}`);
75+
}
76+
return absolutePath;
77+
}
78+
79+
const testFiles = process.argv.slice(2).filter((argument) => !argument.startsWith("--"));
80+
81+
if (testFiles.length === 0) {
82+
console.error("Usage: node ./scripts/run-node-test-files.mjs <test-file> [test-file...]");
83+
process.exit(1);
84+
}
85+
86+
let passed = 0;
87+
88+
for (const inputPath of testFiles) {
89+
const absolutePath = resolveTestPath(inputPath);
90+
const displayPath = toRepoRelative(absolutePath);
91+
installFreshStorage();
92+
const moduleUrl = `${pathToFileURL(absolutePath).href}?laneRun=${Date.now()}-${passed}`;
93+
const testModule = await import(moduleUrl);
94+
if (typeof testModule.run === "function") {
95+
await testModule.run();
96+
}
97+
installFreshStorage();
98+
passed += 1;
99+
console.log(`PASS ${displayPath}`);
100+
}
101+
102+
console.log(`\n${passed}/${testFiles.length} targeted node test file(s) passed.`);

0 commit comments

Comments
 (0)