@pixel-point/toolcraft 0.0.16 → 0.0.17
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/src/generate-performance-iteration-execution.test.mjs +2 -2
- package/src/generate-test-demand-only-lifecycle.mjs +14 -3
- package/src/generate-test-dependency-sandbox.mjs +42 -5
- package/src/generate-test-doc-assertions.mjs +68 -18
- package/src/generate-test-doc-assertions.test.mjs +22 -0
- package/src/generate-test-file-assertions.mjs +43 -76
- package/src/generate-test-output-assertions.mjs +95 -0
- package/src/generate-test-package-assertions.mjs +6 -0
- package/src/generate-test-performance-boundary-assertions.mjs +45 -0
- package/src/generate-test-product-fixture.mjs +1 -0
- package/src/generate-test-product-fixture.test.mjs +18 -0
- package/src/generate-test-product-iteration.mjs +1 -0
- package/src/generate-test-product-iteration.test.mjs +1 -0
- package/src/generate-test-standalone-delivery-stages.mjs +14 -6
- package/src/generate.test.mjs +14 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +12 -9
- package/templates/runtime/contracts/component-contracts.choices.ts +10 -9
- package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +17 -2
- package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +1 -1
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +1 -1
- package/templates/runtime/contracts/component-contracts.runtime.ts +3 -2
- package/templates/runtime/contracts/decision-contracts.test.ts +25 -4
- package/templates/runtime/contracts/decision-contracts.ts +3 -3
- package/templates/runtime/contracts/performance-verification-policy.test.ts +59 -6
- package/templates/runtime/contracts/performance-verification-policy.ts +5 -2
- package/templates/runtime/export/artifact-download.test.ts +71 -0
- package/templates/runtime/export/artifact-download.ts +80 -0
- package/templates/runtime/export/artifact-export-request.ts +23 -0
- package/templates/runtime/export/artifact-export-settings.test.ts +75 -0
- package/templates/runtime/export/artifact-export-settings.ts +96 -0
- package/templates/runtime/export/artifact-frame-renderer.test.ts +107 -0
- package/templates/runtime/export/artifact-frame-renderer.ts +96 -0
- package/templates/runtime/export/artifact-frame-state.test.ts +54 -0
- package/templates/runtime/export/artifact-frame-state.ts +44 -0
- package/templates/runtime/export/artifact-scene-frame.test.ts +84 -0
- package/templates/runtime/export/artifact-scene-frame.ts +118 -0
- package/templates/runtime/export/export-background.ts +65 -0
- package/templates/runtime/export/export-error.ts +43 -0
- package/templates/runtime/export/export.test.ts +6 -186
- package/templates/runtime/export/image-artifact-export.test.ts +111 -0
- package/templates/runtime/export/image-artifact-export.ts +145 -0
- package/templates/runtime/export/index.ts +14 -1
- package/templates/runtime/export/product-export-renderer.test.ts +77 -0
- package/templates/runtime/export/product-export-renderer.ts +63 -0
- package/templates/runtime/export/video-artifact-export.test.ts +166 -0
- package/templates/runtime/export/video-artifact-export.ts +164 -0
- package/templates/runtime/export/video-encoding-backend.ts +124 -0
- package/templates/runtime/export/video-encoding-policy.test.ts +68 -0
- package/templates/runtime/export/video-encoding-policy.ts +86 -0
- package/templates/runtime/export/video-frame-schedule.test.ts +58 -0
- package/templates/runtime/export/video-frame-schedule.ts +42 -0
- package/templates/runtime/index.ts +1 -0
- package/templates/runtime/react/app-shell/runtime-public-api-runtime.test.tsx +291 -0
- package/templates/runtime/react/app-shell/runtime-public-api-test-support.ts +21 -0
- package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +4 -303
- package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +12 -27
- package/templates/runtime/react/app-shell/toolcraft-app.tsx +22 -4
- package/templates/runtime/react/app-shell/toolcraft-source-asset-context.test.tsx +16 -57
- package/templates/runtime/react/app-shell/toolcraft-source-asset-context.tsx +75 -434
- package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.test.ts +194 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.ts +227 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-provider-ownership.test.tsx +243 -0
- package/templates/runtime/react/app-shell/toolcraft-source-asset-test-support.ts +51 -0
- package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.test.ts +138 -0
- package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.ts +378 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.footer-actions.test.tsx +7 -12
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +17 -83
- package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +42 -84
- package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +62 -0
- package/templates/runtime/react/panel-host/panel-host-runtime-placement.test.tsx +30 -0
- package/templates/runtime/react/panel-host/panel-host.tsx +4 -0
- package/templates/runtime/scene/scene-bounds.ts +1 -2
- package/templates/runtime/schema/canvas-render-scale.ts +62 -0
- package/templates/runtime/schema/controls-panel-section-id.test.ts +1 -1
- package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +3 -3
- package/templates/runtime/schema/define-toolcraft.setup-background.test.ts +1 -1
- package/templates/runtime/schema/define-toolcraft.setup-render-scale-timeline.test.ts +71 -3
- package/templates/runtime/schema/define-toolcraft.ts +1 -1
- package/templates/runtime/schema/schema-resolvers.ts +0 -56
- package/templates/runtime/schema/types.ts +4 -4
- package/templates/runtime/state/canvas-background-state.ts +6 -0
- package/templates/runtime/testing/performance-render-plan-integration.test.ts +64 -0
- package/templates/runtime/testing/performance-renderer-pipeline-validation.ts +18 -1
- package/templates/starter/AGENTS.md +8 -7
- package/templates/starter/NOTICE.md +6 -0
- package/templates/starter/docs/toolcraft/README.md +2 -1
- package/templates/starter/docs/toolcraft/acceptance-testing.md +8 -6
- package/templates/starter/docs/toolcraft/agent-worklog.md +3 -3
- package/templates/starter/docs/toolcraft/assembly-workflow.md +4 -3
- package/templates/starter/docs/toolcraft/core/media-upload.md +2 -2
- package/templates/starter/docs/toolcraft/core/performance.md +14 -3
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +3 -1
- package/templates/starter/docs/toolcraft/core/setup-export.md +16 -12
- package/templates/starter/docs/toolcraft/core/timeline-animation.md +4 -4
- package/templates/starter/docs/toolcraft/performance.md +20 -6
- package/templates/starter/docs/toolcraft/renderer-technique.md +1 -1
- package/templates/starter/docs/toolcraft/schema-reference.md +2 -6
- package/templates/starter/docs/toolcraft/workflow.md +7 -2
- package/templates/starter/e2e/app-browser-control-coverage.spec.ts +33 -0
- package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +43 -1
- package/templates/starter/e2e/app-browser-render-scale-evidence.spec.ts +60 -6
- package/templates/starter/e2e/app-browser-render-scale-requirements.spec.ts +77 -1
- package/templates/starter/e2e/app-browser-runtime-provenance.spec.ts +8 -0
- package/templates/starter/e2e/app-browser-runtime-reporter.spec.ts +35 -0
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +4 -1
- package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +0 -1
- package/templates/starter/e2e/app-browser-shell.spec.ts +13 -5
- package/templates/starter/e2e/app-performance-path-adapters.ts +8 -1
- package/templates/starter/e2e/app-performance.spec.ts +16 -9
- package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +9 -330
- package/templates/starter/e2e/browser-infinity-canvas-unavailable-image-evidence.ts +370 -0
- package/templates/starter/e2e/browser-media-export-evidence.spec.ts +77 -0
- package/templates/starter/e2e/browser-media-export-evidence.ts +208 -0
- package/templates/starter/e2e/browser-render-scale-evidence.ts +37 -29
- package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +4 -1
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +43 -9
- package/templates/starter/e2e/browser-standard-timeline-evidence.ts +106 -0
- package/templates/starter/e2e/decoded-pixel-observation.spec.ts +74 -0
- package/templates/starter/e2e/decoded-pixel-observation.ts +227 -0
- package/templates/starter/e2e/export-artifact-helpers.ts +206 -25
- package/templates/starter/e2e/image-artifact-inspection.spec.ts +41 -0
- package/templates/starter/e2e/image-artifact-inspection.ts +99 -0
- package/templates/starter/e2e/performance-canvas-helpers.ts +22 -21
- package/templates/starter/e2e/performance-canvas-quality-guard.spec.ts +312 -0
- package/templates/starter/e2e/performance-canvas-quality-guard.ts +196 -0
- package/templates/starter/e2e/performance-canvas-quality-observer.ts +351 -0
- package/templates/starter/e2e/performance-control-layout-helpers.ts +2 -2
- package/templates/starter/e2e/performance-path-adapter-contract.ts +4 -4
- package/templates/starter/e2e/performance-path-helpers.spec.ts +203 -70
- package/templates/starter/e2e/performance-path-helpers.ts +210 -91
- package/templates/starter/e2e/performance-pipeline-evidence-continuity.spec.ts +76 -0
- package/templates/starter/e2e/performance-pipeline-evidence-test-fixtures.ts +11 -2
- package/templates/starter/e2e/performance-pipeline-invariants.ts +47 -4
- package/templates/starter/e2e/performance-render-scale-runner.spec.ts +140 -38
- package/templates/starter/e2e/video-artifact-inspection.spec.ts +64 -0
- package/templates/starter/e2e/video-artifact-inspection.ts +281 -0
- package/templates/starter/gitignore +1 -0
- package/templates/starter/package.json +1 -0
- package/templates/starter/scripts/check-toolcraft-docs.mjs +17 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +60 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +7 -2
- package/templates/starter/scripts/toolcraft-delivery-plan.mjs +7 -3
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +24 -2
- package/templates/starter/scripts/toolcraft-product-boundary.mjs +12 -0
- package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +46 -0
- package/templates/starter/scripts/toolcraft-product-evidence-boundary.test.mjs +85 -20
- package/templates/starter/scripts/toolcraft-product-evidence-import-policy.mjs +43 -0
- package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +168 -0
- package/templates/starter/scripts/toolcraft-typescript-analysis.d.mts +73 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade-test-helpers.mjs +318 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +155 -206
- package/templates/starter/scripts/toolcraft-verification-inventory.mjs +1 -0
- package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +51 -0
- package/templates/starter/scripts/toolcraft-vite-env-activation.test.mjs +128 -59
- package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +113 -0
- package/templates/starter/src/app/acceptance/output-background-rules.ts +1 -1
- package/templates/starter/src/app/acceptance/output-export.ts +13 -2
- package/templates/starter/src/app/acceptance/types.ts +7 -0
- package/templates/starter/src/app/acceptance/validate-coverage.ts +2 -1
- package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +131 -0
- package/templates/starter/src/app/starter-acceptance.keyframes.test.ts +14 -4
- package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +42 -1
- package/templates/starter/src/app/starter-acceptance.ts +2 -0
- package/templates/starter/src/app/starter-performance-test-utils.ts +103 -0
- package/templates/starter/src/app/starter-performance.gates.test.ts +231 -278
- package/templates/starter/src/app/starter-performance.lifecycle.test.ts +261 -0
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +2 -0
- package/templates/starter/src/routes/index.tsx +1 -0
- package/templates/runtime/export/export.ts +0 -166
|
@@ -6,34 +6,35 @@ import test from "node:test";
|
|
|
6
6
|
import { createServer } from "vite";
|
|
7
7
|
|
|
8
8
|
const starterRoot = path.resolve(import.meta.dirname, "..");
|
|
9
|
-
const
|
|
9
|
+
const generatedProofControllerPath = path.join(
|
|
10
10
|
starterRoot,
|
|
11
|
-
"src/toolcraft/runtime/react/app-shell/toolcraft-
|
|
11
|
+
"src/toolcraft/runtime/react/app-shell/toolcraft-unavailable-resource-proof.ts",
|
|
12
12
|
);
|
|
13
|
-
const
|
|
13
|
+
const monorepoProofControllerPath = path.resolve(
|
|
14
14
|
starterRoot,
|
|
15
|
-
"../packages/toolcraft-runtime/src/react/app-shell/toolcraft-
|
|
15
|
+
"../packages/toolcraft-runtime/src/react/app-shell/toolcraft-unavailable-resource-proof.ts",
|
|
16
16
|
);
|
|
17
|
+
let transformedProofControllerSequence = 0;
|
|
17
18
|
|
|
18
|
-
async function
|
|
19
|
+
async function resolveProofControllerSource() {
|
|
19
20
|
try {
|
|
20
21
|
return {
|
|
21
|
-
filePath:
|
|
22
|
+
filePath: generatedProofControllerPath,
|
|
22
23
|
requestPath:
|
|
23
|
-
"/src/toolcraft/runtime/react/app-shell/toolcraft-
|
|
24
|
+
"/src/toolcraft/runtime/react/app-shell/toolcraft-unavailable-resource-proof.ts",
|
|
24
25
|
root: starterRoot,
|
|
25
|
-
source: await fs.readFile(
|
|
26
|
+
source: await fs.readFile(generatedProofControllerPath, "utf8"),
|
|
26
27
|
};
|
|
27
28
|
} catch (error) {
|
|
28
29
|
if (error?.code !== "ENOENT") throw error;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
return {
|
|
32
|
-
filePath:
|
|
33
|
+
filePath: monorepoProofControllerPath,
|
|
33
34
|
requestPath:
|
|
34
|
-
"/packages/toolcraft-runtime/src/react/app-shell/toolcraft-
|
|
35
|
+
"/packages/toolcraft-runtime/src/react/app-shell/toolcraft-unavailable-resource-proof.ts",
|
|
35
36
|
root: path.resolve(starterRoot, ".."),
|
|
36
|
-
source: await fs.readFile(
|
|
37
|
+
source: await fs.readFile(monorepoProofControllerPath, "utf8"),
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -46,7 +47,7 @@ function readInjectedViteEnv(code, mode) {
|
|
|
46
47
|
return JSON.parse(match[1]);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
async function
|
|
50
|
+
async function transformProofController({ mode, requestPath, root }) {
|
|
50
51
|
const server = await createServer({
|
|
51
52
|
configFile: false,
|
|
52
53
|
envDir: false,
|
|
@@ -65,67 +66,135 @@ async function transformRuntime({ mode, requestPath, root }) {
|
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
async function loadTransformedProofController({
|
|
70
|
+
fixtureFlag,
|
|
71
|
+
mode,
|
|
72
|
+
requestPath,
|
|
73
|
+
root,
|
|
74
|
+
}) {
|
|
75
|
+
const previousFixtureValue =
|
|
76
|
+
process.env.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES;
|
|
77
|
+
try {
|
|
78
|
+
if (fixtureFlag === undefined) {
|
|
79
|
+
delete process.env.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES;
|
|
80
|
+
} else {
|
|
81
|
+
process.env.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES = fixtureFlag;
|
|
82
|
+
}
|
|
83
|
+
const code = await transformProofController({ mode, requestPath, root });
|
|
84
|
+
const moduleUrl =
|
|
85
|
+
`data:text/javascript;base64,${Buffer.from(code).toString("base64")}` +
|
|
86
|
+
`#toolcraft-proof-${++transformedProofControllerSequence}`;
|
|
87
|
+
const transformedModule = await import(moduleUrl);
|
|
88
|
+
assert.equal(
|
|
89
|
+
typeof transformedModule.decorateToolcraftUnavailableResourceProofCoordinator,
|
|
90
|
+
"function",
|
|
91
|
+
);
|
|
92
|
+
return {
|
|
93
|
+
code,
|
|
94
|
+
decorateCoordinator:
|
|
95
|
+
transformedModule.decorateToolcraftUnavailableResourceProofCoordinator,
|
|
96
|
+
};
|
|
97
|
+
} finally {
|
|
98
|
+
if (previousFixtureValue === undefined) {
|
|
99
|
+
delete process.env.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES;
|
|
100
|
+
} else {
|
|
101
|
+
process.env.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES = previousFixtureValue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function observeProofControllerDecoration(decorateCoordinator) {
|
|
107
|
+
const addedEvents = [];
|
|
108
|
+
const removedEvents = [];
|
|
109
|
+
const previousDocument = globalThis.document;
|
|
110
|
+
globalThis.document = {
|
|
111
|
+
addEventListener(eventName) {
|
|
112
|
+
addedEvents.push(eventName);
|
|
113
|
+
},
|
|
114
|
+
removeEventListener(eventName) {
|
|
115
|
+
removedEvents.push(eventName);
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
const originalDispose = async () => {};
|
|
119
|
+
const originalResolveResource = async () => new Uint8Array([1]);
|
|
120
|
+
const coordinator = {
|
|
121
|
+
dispose: originalDispose,
|
|
122
|
+
resolveResource: originalResolveResource,
|
|
123
|
+
};
|
|
124
|
+
try {
|
|
125
|
+
const returned = decorateCoordinator(coordinator, {});
|
|
126
|
+
const decorationApplied =
|
|
127
|
+
coordinator.dispose !== originalDispose &&
|
|
128
|
+
coordinator.resolveResource !== originalResolveResource;
|
|
129
|
+
await coordinator.dispose();
|
|
130
|
+
return {
|
|
131
|
+
addedEvents,
|
|
132
|
+
decorationApplied,
|
|
133
|
+
removedEvents,
|
|
134
|
+
returnedOriginal: returned === coordinator,
|
|
135
|
+
};
|
|
136
|
+
} finally {
|
|
137
|
+
if (previousDocument === undefined) delete globalThis.document;
|
|
138
|
+
else globalThis.document = previousDocument;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
68
142
|
test("unavailable-resource proof activation uses Vite-static env access and stays production-disabled", async () => {
|
|
69
|
-
const
|
|
143
|
+
const proofController = await resolveProofControllerSource();
|
|
70
144
|
assert.match(
|
|
71
|
-
|
|
145
|
+
proofController.source,
|
|
72
146
|
/import\.meta\.env\?\.MODE/u,
|
|
73
|
-
`${
|
|
147
|
+
`${proofController.filePath} must read MODE through direct, Node-safe Vite-static access.`,
|
|
74
148
|
);
|
|
75
149
|
assert.match(
|
|
76
|
-
|
|
150
|
+
proofController.source,
|
|
77
151
|
/import\.meta\.env\?\.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES/u,
|
|
78
|
-
`${
|
|
152
|
+
`${proofController.filePath} must read the protected fixture flag through direct, Node-safe Vite-static access.`,
|
|
79
153
|
);
|
|
80
154
|
assert.doesNotMatch(
|
|
81
|
-
|
|
155
|
+
proofController.source,
|
|
82
156
|
/\bconst\s+\w+\s*=\s*import\.meta\s*(?:;|\n)/u,
|
|
83
|
-
`${
|
|
157
|
+
`${proofController.filePath} must not alias import.meta before reading env.`,
|
|
84
158
|
);
|
|
85
159
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"
|
|
160
|
+
for (const scenario of [
|
|
161
|
+
{ enabled: false, fixtureFlag: undefined, mode: "production" },
|
|
162
|
+
{ enabled: false, fixtureFlag: undefined, mode: "development" },
|
|
163
|
+
{ enabled: true, fixtureFlag: undefined, mode: "test" },
|
|
164
|
+
{
|
|
165
|
+
enabled: true,
|
|
166
|
+
fixtureFlag: "unavailable-resource",
|
|
167
|
+
mode: "production",
|
|
168
|
+
},
|
|
169
|
+
]) {
|
|
170
|
+
const transformed = await loadTransformedProofController({
|
|
171
|
+
...proofController,
|
|
172
|
+
fixtureFlag: scenario.fixtureFlag,
|
|
173
|
+
mode: scenario.mode,
|
|
174
|
+
});
|
|
175
|
+
const injectedEnv = readInjectedViteEnv(
|
|
176
|
+
transformed.code,
|
|
177
|
+
scenario.mode,
|
|
94
178
|
);
|
|
95
|
-
assert.equal(
|
|
179
|
+
assert.equal(injectedEnv.MODE, scenario.mode);
|
|
96
180
|
assert.equal(
|
|
97
|
-
|
|
98
|
-
|
|
181
|
+
injectedEnv.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES,
|
|
182
|
+
scenario.fixtureFlag,
|
|
99
183
|
);
|
|
100
|
-
assert.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
184
|
+
assert.deepEqual(
|
|
185
|
+
await observeProofControllerDecoration(
|
|
186
|
+
transformed.decorateCoordinator,
|
|
187
|
+
),
|
|
188
|
+
{
|
|
189
|
+
addedEvents: scenario.enabled
|
|
190
|
+
? ["toolcraft.browser-proof.unavailable-resource-request"]
|
|
191
|
+
: [],
|
|
192
|
+
decorationApplied: scenario.enabled,
|
|
193
|
+
removedEvents: scenario.enabled
|
|
194
|
+
? ["toolcraft.browser-proof.unavailable-resource-request"]
|
|
195
|
+
: [],
|
|
196
|
+
returnedOriginal: true,
|
|
197
|
+
},
|
|
105
198
|
);
|
|
106
|
-
|
|
107
|
-
delete process.env.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES;
|
|
108
|
-
const productionEnv = readInjectedViteEnv(
|
|
109
|
-
await transformRuntime({ ...runtime, mode: "production" }),
|
|
110
|
-
"production",
|
|
111
|
-
);
|
|
112
|
-
assert.equal(productionEnv.MODE, "production");
|
|
113
|
-
assert.equal(
|
|
114
|
-
productionEnv.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES,
|
|
115
|
-
undefined,
|
|
116
|
-
);
|
|
117
|
-
assert.equal(
|
|
118
|
-
productionEnv.MODE === "test" ||
|
|
119
|
-
productionEnv.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES ===
|
|
120
|
-
"unavailable-resource",
|
|
121
|
-
false,
|
|
122
|
-
);
|
|
123
|
-
} finally {
|
|
124
|
-
if (previousFixtureValue === undefined) {
|
|
125
|
-
delete process.env.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES;
|
|
126
|
-
} else {
|
|
127
|
-
process.env.VITE_TOOLCRAFT_BROWSER_PROOF_FIXTURES =
|
|
128
|
-
previousFixtureValue;
|
|
129
|
-
}
|
|
130
199
|
}
|
|
131
200
|
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ResolvedToolcraftAppSchema,
|
|
3
|
+
ToolcraftActionSchema,
|
|
4
|
+
} from "@repo/toolcraft-runtime";
|
|
5
|
+
|
|
6
|
+
import { getControlActions } from "./actions";
|
|
7
|
+
import type {
|
|
8
|
+
ToolcraftComponentAcceptance,
|
|
9
|
+
ToolcraftExportArtifactCoverage,
|
|
10
|
+
} from "./types";
|
|
11
|
+
|
|
12
|
+
const requiredCoverageByRole = {
|
|
13
|
+
"export-image": "all-required-image-export-behavior",
|
|
14
|
+
"export-video": "all-required-video-export-behavior",
|
|
15
|
+
} as const satisfies Record<
|
|
16
|
+
"export-image" | "export-video",
|
|
17
|
+
ToolcraftExportArtifactCoverage
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
function getCoverageValues(
|
|
21
|
+
entry: ToolcraftComponentAcceptance,
|
|
22
|
+
): readonly ToolcraftExportArtifactCoverage[] {
|
|
23
|
+
if (!entry.exportArtifactCoverage) return [];
|
|
24
|
+
return typeof entry.exportArtifactCoverage === "string"
|
|
25
|
+
? [entry.exportArtifactCoverage]
|
|
26
|
+
: entry.exportArtifactCoverage;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isMediaExportAction(
|
|
30
|
+
action: ToolcraftActionSchema | string,
|
|
31
|
+
): action is ToolcraftActionSchema & {
|
|
32
|
+
role: "export-image" | "export-video";
|
|
33
|
+
} {
|
|
34
|
+
return (
|
|
35
|
+
typeof action !== "string" &&
|
|
36
|
+
(action.role === "export-image" || action.role === "export-video")
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function collectMediaExportActions(schema: ResolvedToolcraftAppSchema) {
|
|
41
|
+
return (schema.panels.controls?.sections ?? []).flatMap((section) =>
|
|
42
|
+
Object.values(section.controls).flatMap((control) =>
|
|
43
|
+
control.type !== "panelActions" || !control.target
|
|
44
|
+
? []
|
|
45
|
+
: getControlActions(control)
|
|
46
|
+
.filter(isMediaExportAction)
|
|
47
|
+
.map((action) => ({
|
|
48
|
+
requiredCoverage: requiredCoverageByRole[action.role],
|
|
49
|
+
role: action.role,
|
|
50
|
+
target: control.target,
|
|
51
|
+
value: action.value,
|
|
52
|
+
})),
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getToolcraftExportArtifactCoverageErrors({
|
|
58
|
+
acceptance,
|
|
59
|
+
schema,
|
|
60
|
+
}: Readonly<{
|
|
61
|
+
acceptance: readonly ToolcraftComponentAcceptance[];
|
|
62
|
+
schema: ResolvedToolcraftAppSchema;
|
|
63
|
+
}>): string[] {
|
|
64
|
+
const errors: string[] = [];
|
|
65
|
+
const exportActions = collectMediaExportActions(schema);
|
|
66
|
+
|
|
67
|
+
for (const entry of acceptance) {
|
|
68
|
+
const coverage = getCoverageValues(entry);
|
|
69
|
+
if (coverage.length === 0) continue;
|
|
70
|
+
if (entry.evidence !== "exported-bytes") {
|
|
71
|
+
errors.push(
|
|
72
|
+
`Acceptance "${entry.id}" declares exportArtifactCoverage but evidence is not "exported-bytes".`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (!entry.automated || !entry.browser) {
|
|
76
|
+
errors.push(
|
|
77
|
+
`Acceptance "${entry.id}" exportArtifactCoverage requires automated and browser proof.`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
for (const value of coverage) {
|
|
81
|
+
const hasMatchingAction = exportActions.some(
|
|
82
|
+
(action) =>
|
|
83
|
+
action.target === entry.target &&
|
|
84
|
+
action.requiredCoverage === value &&
|
|
85
|
+
(entry.actionCoverage ?? []).includes(action.value),
|
|
86
|
+
);
|
|
87
|
+
if (!hasMatchingAction) {
|
|
88
|
+
errors.push(
|
|
89
|
+
`Acceptance "${entry.id}" declares ${value} without a matching typed export action in target/actionCoverage.`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const action of exportActions) {
|
|
96
|
+
const hasCoverage = acceptance.some(
|
|
97
|
+
(entry) =>
|
|
98
|
+
entry.target === action.target &&
|
|
99
|
+
entry.evidence === "exported-bytes" &&
|
|
100
|
+
entry.automated &&
|
|
101
|
+
entry.browser &&
|
|
102
|
+
(entry.actionCoverage ?? []).includes(action.value) &&
|
|
103
|
+
getCoverageValues(entry).includes(action.requiredCoverage),
|
|
104
|
+
);
|
|
105
|
+
if (!hasCoverage) {
|
|
106
|
+
errors.push(
|
|
107
|
+
`Typed ${action.role} action "${action.value}" on "${action.target}" requires ${action.requiredCoverage} acceptance coverage.`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return errors;
|
|
113
|
+
}
|
|
@@ -103,7 +103,7 @@ export function getToolcraftOutputBackgroundErrors({
|
|
|
103
103
|
|
|
104
104
|
if (!schemaHasOutputBackgroundToggleControl(controls)) {
|
|
105
105
|
errors.push(
|
|
106
|
-
'Product apps with Export PNG must expose export.includeBackground in runtime Setup as a Switch labeled "Background".
|
|
106
|
+
'Product apps with Export PNG must expose export.includeBackground in runtime Setup as a Switch labeled "Background". Runtime artifact export owns PNG alpha and preserves the background for JPEG and video; bounded live preview must use shouldIncludeToolcraftPreviewBackground(state).',
|
|
107
107
|
);
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -11,7 +11,11 @@ import {
|
|
|
11
11
|
import { getToolcraftOutputExportLayoutErrors } from "./output-export-layout-rules";
|
|
12
12
|
import { buildToolcraftOutputExportFacts } from "./output-export-model";
|
|
13
13
|
import { getToolcraftImageExportErrors } from "./output-image-export-rules";
|
|
14
|
-
import
|
|
14
|
+
import { getToolcraftExportArtifactCoverageErrors } from "./export-artifact-coverage";
|
|
15
|
+
import type {
|
|
16
|
+
ToolcraftComponentAcceptance,
|
|
17
|
+
ToolcraftVisibleControl,
|
|
18
|
+
} from "./types";
|
|
15
19
|
|
|
16
20
|
export {
|
|
17
21
|
isOutputBackgroundToggleControl,
|
|
@@ -20,16 +24,22 @@ export {
|
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
export function getToolcraftOutputExportErrors({
|
|
27
|
+
acceptance,
|
|
23
28
|
controls,
|
|
24
29
|
schema,
|
|
25
30
|
hasVideoExportAction = schemaHasVideoExportPanelAction(schema),
|
|
26
31
|
}: {
|
|
32
|
+
acceptance: readonly ToolcraftComponentAcceptance[];
|
|
27
33
|
controls: readonly ToolcraftVisibleControl[];
|
|
28
34
|
hasVideoExportAction?: boolean;
|
|
29
35
|
schema: ResolvedToolcraftAppSchema;
|
|
30
36
|
}): string[] {
|
|
37
|
+
const exportArtifactCoverageErrors = getToolcraftExportArtifactCoverageErrors({
|
|
38
|
+
acceptance,
|
|
39
|
+
schema,
|
|
40
|
+
});
|
|
31
41
|
if (!schemaHasPngExportPanelAction(schema)) {
|
|
32
|
-
return
|
|
42
|
+
return exportArtifactCoverageErrors;
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
const facts = buildToolcraftOutputExportFacts({
|
|
@@ -41,5 +51,6 @@ export function getToolcraftOutputExportErrors({
|
|
|
41
51
|
...getToolcraftOutputExportLayoutErrors(facts),
|
|
42
52
|
...getToolcraftOutputBackgroundErrors({ controls, facts }),
|
|
43
53
|
...getToolcraftImageExportErrors(facts),
|
|
54
|
+
...exportArtifactCoverageErrors,
|
|
44
55
|
];
|
|
45
56
|
}
|
|
@@ -58,6 +58,10 @@ export type ToolcraftAcceptanceEvidence =
|
|
|
58
58
|
| "timeline-output"
|
|
59
59
|
| "viewport-side-effect";
|
|
60
60
|
|
|
61
|
+
export type ToolcraftExportArtifactCoverage =
|
|
62
|
+
| "all-required-image-export-behavior"
|
|
63
|
+
| "all-required-video-export-behavior";
|
|
64
|
+
|
|
61
65
|
export type ToolcraftReferenceCoverage =
|
|
62
66
|
| "canvas-sizing"
|
|
63
67
|
| "control-mapping"
|
|
@@ -290,6 +294,9 @@ export type ToolcraftComponentAcceptance = {
|
|
|
290
294
|
browserTestName: string;
|
|
291
295
|
componentType: string;
|
|
292
296
|
evidence: ToolcraftAcceptanceEvidence;
|
|
297
|
+
exportArtifactCoverage?:
|
|
298
|
+
| ToolcraftExportArtifactCoverage
|
|
299
|
+
| readonly ToolcraftExportArtifactCoverage[];
|
|
293
300
|
expectedObservable: string;
|
|
294
301
|
fixture: string;
|
|
295
302
|
id: string;
|
|
@@ -160,8 +160,9 @@ const toolcraftAcceptanceValidators: readonly ToolcraftAcceptanceValidator[] = [
|
|
|
160
160
|
{
|
|
161
161
|
path: "schema.export",
|
|
162
162
|
ruleId: "output-export-required",
|
|
163
|
-
validate: ({ controls, hasVideoExportAction, schema }) =>
|
|
163
|
+
validate: ({ acceptance, controls, hasVideoExportAction, schema }) =>
|
|
164
164
|
getToolcraftOutputExportErrors({
|
|
165
|
+
acceptance,
|
|
165
166
|
controls,
|
|
166
167
|
hasVideoExportAction,
|
|
167
168
|
schema,
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { defineToolcraft } from "@repo/toolcraft-runtime";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { getToolcraftExportArtifactCoverageErrors } from "./acceptance/export-artifact-coverage";
|
|
5
|
+
import type { ToolcraftComponentAcceptance } from "./acceptance/types";
|
|
6
|
+
|
|
7
|
+
function createSchema(roles: readonly ("export-image" | "export-video")[]) {
|
|
8
|
+
return defineToolcraft({
|
|
9
|
+
canvas: { enabled: true },
|
|
10
|
+
panels: {
|
|
11
|
+
controls: {
|
|
12
|
+
sections: [
|
|
13
|
+
{
|
|
14
|
+
controls: {
|
|
15
|
+
output: {
|
|
16
|
+
actions: roles.map((role) => ({
|
|
17
|
+
label: role === "export-image" ? "Export Image" : "Export Video",
|
|
18
|
+
role,
|
|
19
|
+
value: role === "export-image" ? "export.image" : "export.video",
|
|
20
|
+
})),
|
|
21
|
+
target: "actions.output",
|
|
22
|
+
type: "panelActions",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
title: "Controls",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createAcceptance(
|
|
34
|
+
overrides: Partial<ToolcraftComponentAcceptance> = {},
|
|
35
|
+
): ToolcraftComponentAcceptance {
|
|
36
|
+
return {
|
|
37
|
+
actionCoverage: ["export.image"],
|
|
38
|
+
automated: true,
|
|
39
|
+
automatedTestName: "exports image",
|
|
40
|
+
browser: true,
|
|
41
|
+
browserTestName: "browser: exports image",
|
|
42
|
+
componentType: "panelActions",
|
|
43
|
+
evidence: "exported-bytes",
|
|
44
|
+
expectedObservable: "Downloads decoded image output.",
|
|
45
|
+
fixture: "image export fixture",
|
|
46
|
+
id: "actions.output",
|
|
47
|
+
kind: "control",
|
|
48
|
+
target: "actions.output",
|
|
49
|
+
userAction: "Click Export Image.",
|
|
50
|
+
...overrides,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe("Toolcraft export artifact acceptance", () => {
|
|
55
|
+
it("requires exact image coverage for an image action", () => {
|
|
56
|
+
const schema = createSchema(["export-image"]);
|
|
57
|
+
|
|
58
|
+
expect(
|
|
59
|
+
getToolcraftExportArtifactCoverageErrors({
|
|
60
|
+
acceptance: [createAcceptance()],
|
|
61
|
+
schema,
|
|
62
|
+
}),
|
|
63
|
+
).toContainEqual(
|
|
64
|
+
expect.stringContaining("all-required-image-export-behavior"),
|
|
65
|
+
);
|
|
66
|
+
expect(
|
|
67
|
+
getToolcraftExportArtifactCoverageErrors({
|
|
68
|
+
acceptance: [
|
|
69
|
+
createAcceptance({
|
|
70
|
+
exportArtifactCoverage: "all-required-image-export-behavior",
|
|
71
|
+
}),
|
|
72
|
+
],
|
|
73
|
+
schema,
|
|
74
|
+
}),
|
|
75
|
+
).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("does not allow image coverage to satisfy video", () => {
|
|
79
|
+
const errors = getToolcraftExportArtifactCoverageErrors({
|
|
80
|
+
acceptance: [
|
|
81
|
+
createAcceptance({
|
|
82
|
+
actionCoverage: ["export.video"],
|
|
83
|
+
exportArtifactCoverage: "all-required-image-export-behavior",
|
|
84
|
+
}),
|
|
85
|
+
],
|
|
86
|
+
schema: createSchema(["export-video"]),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(errors).toEqual([
|
|
90
|
+
expect.stringContaining("without a matching typed export action"),
|
|
91
|
+
expect.stringContaining("all-required-video-export-behavior"),
|
|
92
|
+
]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("requires exported bytes plus automated browser proof", () => {
|
|
96
|
+
const errors = getToolcraftExportArtifactCoverageErrors({
|
|
97
|
+
acceptance: [
|
|
98
|
+
createAcceptance({
|
|
99
|
+
automated: false,
|
|
100
|
+
browser: false,
|
|
101
|
+
evidence: "command-side-effect",
|
|
102
|
+
exportArtifactCoverage: "all-required-image-export-behavior",
|
|
103
|
+
}),
|
|
104
|
+
],
|
|
105
|
+
schema: createSchema(["export-image"]),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(errors).toEqual([
|
|
109
|
+
expect.stringContaining('evidence is not "exported-bytes"'),
|
|
110
|
+
expect.stringContaining("requires automated and browser proof"),
|
|
111
|
+
expect.stringContaining("all-required-image-export-behavior"),
|
|
112
|
+
]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("allows one row to cover both typed actions explicitly", () => {
|
|
116
|
+
expect(
|
|
117
|
+
getToolcraftExportArtifactCoverageErrors({
|
|
118
|
+
acceptance: [
|
|
119
|
+
createAcceptance({
|
|
120
|
+
actionCoverage: ["export.image", "export.video"],
|
|
121
|
+
exportArtifactCoverage: [
|
|
122
|
+
"all-required-image-export-behavior",
|
|
123
|
+
"all-required-video-export-behavior",
|
|
124
|
+
],
|
|
125
|
+
}),
|
|
126
|
+
],
|
|
127
|
+
schema: createSchema(["export-image", "export-video"]),
|
|
128
|
+
}),
|
|
129
|
+
).toEqual([]);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -4,7 +4,6 @@ import type { ToolcraftControlSchema } from "@repo/toolcraft-runtime";
|
|
|
4
4
|
import type { ToolcraftComponentAcceptance } from "./acceptance/types";
|
|
5
5
|
import {
|
|
6
6
|
defineContractSchemaFixture,
|
|
7
|
-
contractSchemaFixture,
|
|
8
7
|
validateContractAcceptance,
|
|
9
8
|
} from "./starter-acceptance.contract-fixtures";
|
|
10
9
|
|
|
@@ -63,9 +62,11 @@ function createKeyframesSchema(
|
|
|
63
62
|
title = "Style",
|
|
64
63
|
) {
|
|
65
64
|
return defineContractSchemaFixture({
|
|
66
|
-
|
|
65
|
+
canvas: {
|
|
66
|
+
enabled: true,
|
|
67
|
+
upload: true,
|
|
68
|
+
},
|
|
67
69
|
panels: {
|
|
68
|
-
...contractSchemaFixture.panels,
|
|
69
70
|
controls: {
|
|
70
71
|
sections: [
|
|
71
72
|
{
|
|
@@ -75,7 +76,16 @@ function createKeyframesSchema(
|
|
|
75
76
|
],
|
|
76
77
|
title: "Controls",
|
|
77
78
|
},
|
|
78
|
-
timeline: {
|
|
79
|
+
timeline: {
|
|
80
|
+
defaultDurationSeconds: 8,
|
|
81
|
+
enabled: true,
|
|
82
|
+
mode: "keyframes",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
toolbar: {
|
|
86
|
+
history: true,
|
|
87
|
+
radar: true,
|
|
88
|
+
zoom: true,
|
|
79
89
|
},
|
|
80
90
|
});
|
|
81
91
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getToolcraftOutputExportErrors,
|
|
5
|
+
schemaHasVideoExportPanelAction,
|
|
6
|
+
} from "./acceptance/output-export";
|
|
4
7
|
import { defineContractSchemaFixture, validateContractAcceptance } from "./starter-acceptance.contract-fixtures";
|
|
5
8
|
|
|
6
9
|
describe("Toolcraft output export synthetic rules", () => {
|
|
@@ -20,6 +23,44 @@ describe("Toolcraft output export synthetic rules", () => {
|
|
|
20
23
|
expect(schemaHasVideoExportPanelAction(playbackSchema)).toBe(false);
|
|
21
24
|
});
|
|
22
25
|
|
|
26
|
+
it("does not let a video-only schema bypass artifact coverage", () => {
|
|
27
|
+
const videoOnlySchema = defineContractSchemaFixture({
|
|
28
|
+
canvas: { enabled: true },
|
|
29
|
+
panels: {
|
|
30
|
+
controls: {
|
|
31
|
+
sections: [
|
|
32
|
+
{
|
|
33
|
+
controls: {
|
|
34
|
+
outputActions: {
|
|
35
|
+
actions: [
|
|
36
|
+
{
|
|
37
|
+
label: "Export Video",
|
|
38
|
+
role: "export-video",
|
|
39
|
+
value: "export.video",
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
target: "actions.output",
|
|
43
|
+
type: "panelActions",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
title: "Controls",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(
|
|
54
|
+
getToolcraftOutputExportErrors({
|
|
55
|
+
acceptance: [],
|
|
56
|
+
controls: [],
|
|
57
|
+
schema: videoOnlySchema,
|
|
58
|
+
}),
|
|
59
|
+
).toEqual([
|
|
60
|
+
expect.stringContaining("all-required-video-export-behavior"),
|
|
61
|
+
]);
|
|
62
|
+
});
|
|
63
|
+
|
|
23
64
|
it("rejects reset actions in sticky footer panelActions", () => {
|
|
24
65
|
const schemaWithFooterReset = defineContractSchemaFixture({
|
|
25
66
|
canvas: {
|
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
ToolcraftControlPartCoverage,
|
|
24
24
|
ToolcraftControlSectionInventoryEntry,
|
|
25
25
|
ToolcraftCustomControlCoverage,
|
|
26
|
+
ToolcraftExportArtifactCoverage,
|
|
26
27
|
ToolcraftLayerCoverage,
|
|
27
28
|
ToolcraftInteractionCapability,
|
|
28
29
|
ToolcraftInteractionEvidenceSource,
|
|
@@ -96,6 +97,7 @@ export type {
|
|
|
96
97
|
ToolcraftControlPartCoverage,
|
|
97
98
|
ToolcraftControlSectionInventoryEntry,
|
|
98
99
|
ToolcraftCustomControlCoverage,
|
|
100
|
+
ToolcraftExportArtifactCoverage,
|
|
99
101
|
ToolcraftLayerCoverage,
|
|
100
102
|
ToolcraftInteractionCapability,
|
|
101
103
|
ToolcraftInteractionEvidenceSource,
|