@pixel-point/toolcraft 0.0.8 → 0.0.11
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/README.md +42 -9
- package/package.json +1 -1
- package/src/generate.mjs +42 -5
- package/src/generate.test.mjs +40 -0
- package/src/package-json.mjs +15 -0
- package/src/package-json.test.mjs +14 -1
- package/templates/runtime/contracts/component-contracts.test.ts +251 -47
- package/templates/runtime/contracts/component-contracts.ts +130 -57
- package/templates/runtime/contracts/decision-contracts.test.ts +38 -1
- package/templates/runtime/contracts/decision-contracts.ts +22 -9
- package/templates/runtime/export/export.test.ts +63 -0
- package/templates/runtime/export/export.ts +55 -0
- package/templates/runtime/index.ts +1 -0
- package/templates/runtime/react/canvas-shell.test.tsx +58 -4
- package/templates/runtime/react/canvas-shell.tsx +31 -9
- package/templates/runtime/react/controls-panel.test.tsx +916 -108
- package/templates/runtime/react/controls-panel.tsx +136 -30
- package/templates/runtime/react/runtime-public-api.test.tsx +1 -1
- package/templates/runtime/react/settings-transfer.test.ts +4 -0
- package/templates/runtime/react/settings-transfer.ts +6 -1
- package/templates/runtime/react/timeline-panel.test.tsx +14 -0
- package/templates/runtime/react/timeline-panel.tsx +44 -7
- package/templates/runtime/react/toolcraft-app.integration.test.tsx +9 -1
- package/templates/runtime/react/toolcraft-app.test.tsx +112 -3
- package/templates/runtime/react/toolcraft-app.tsx +56 -37
- package/templates/runtime/schema/define-toolcraft.test.ts +266 -170
- package/templates/runtime/schema/define-toolcraft.ts +140 -246
- package/templates/runtime/schema/runtime-targets.ts +21 -0
- package/templates/runtime/schema/types.ts +44 -0
- package/templates/runtime/state/create-template-state.test.ts +156 -0
- package/templates/runtime/state/create-template-state.ts +38 -8
- package/templates/runtime/state/media-defaults.ts +105 -0
- package/templates/runtime/state/persistence.test.ts +58 -0
- package/templates/runtime/state/persistence.ts +105 -1
- package/templates/runtime/state/reducer.test.ts +280 -4
- package/templates/runtime/state/reducer.ts +195 -9
- package/templates/runtime/state/timeline-loop.test.ts +71 -0
- package/templates/runtime/state/timeline-loop.ts +35 -0
- package/templates/runtime/state/types.ts +27 -0
- package/templates/runtime/testing/performance.test.ts +810 -21
- package/templates/runtime/testing/performance.ts +823 -53
- package/templates/starter/AGENTS.md +24 -18
- package/templates/starter/docs/toolcraft/README.md +8 -4
- package/templates/starter/docs/toolcraft/acceptance-testing.md +43 -10
- package/templates/starter/docs/toolcraft/agent-worklog.md +1 -0
- package/templates/starter/docs/toolcraft/assembly-workflow.md +55 -20
- package/templates/starter/docs/toolcraft/component-rules.md +75 -42
- package/templates/starter/docs/toolcraft/decision-contract.md +2 -0
- package/templates/starter/docs/toolcraft/performance.md +37 -11
- package/templates/starter/docs/toolcraft/schema-reference.md +171 -41
- package/templates/starter/docs/toolcraft/workflow.md +5 -2
- package/templates/starter/e2e/app-browser-acceptance.spec.ts +3 -3
- package/templates/starter/e2e/app-performance.spec.ts +55 -2
- package/templates/starter/e2e/performance-helpers.ts +45 -0
- package/templates/starter/gitignore +1 -0
- package/templates/starter/index.html +1 -0
- package/templates/starter/package.json +3 -0
- package/templates/starter/playwright.config.ts +1 -1
- package/templates/starter/scripts/check-toolcraft-docs.mjs +1 -0
- package/templates/starter/scripts/run-vite-on-free-port.mjs +114 -13
- package/templates/starter/scripts/toolcraft-port.mjs +280 -0
- package/templates/starter/scripts/toolcraft-port.test.mjs +207 -1
- package/templates/starter/src/app/starter-acceptance.test.ts +3412 -479
- package/templates/starter/src/app/starter-acceptance.ts +1453 -97
- package/templates/starter/src/app/starter-performance.test.ts +111 -7
- package/templates/starter/src/app/starter-performance.ts +5 -0
- package/templates/starter/src/app/starter-schema.test.ts +32 -7
- package/templates/starter/src/app/starter-schema.ts +6 -2
- package/templates/starter/vite.config.ts +58 -2
- package/templates/ui/components/control-layout/index.tsx +8 -3
- package/templates/ui/components/controls/actions/actions-control.tsx +56 -5
- package/templates/ui/components/controls/code-textarea/code-textarea-control.tsx +7 -3
- package/templates/ui/components/controls/color/index.ts +4 -1
- package/templates/ui/components/controls/color/palette-control.tsx +34 -4
- package/templates/ui/components/controls/color/style-guide-color-picker-logic.ts +7 -2
- package/templates/ui/components/controls/color/style-guide-color-picker.tsx +2 -2
- package/templates/ui/components/controls/file-drop/file-drop-control.tsx +186 -14
- package/templates/ui/components/controls/file-drop/index.ts +6 -1
- package/templates/ui/components/controls/index.ts +4 -0
- package/templates/ui/components/controls/range-input/range-input-control.tsx +12 -4
- package/templates/ui/components/controls/range-slider/range-slider-value.ts +3 -1
- package/templates/ui/components/controls/select/select-control.tsx +8 -25
- package/templates/ui/components/controls/slider/slider-value.ts +0 -1
- package/templates/ui/components/controls/text-input/text-input-control.tsx +4 -1
- package/templates/ui/components/controls/vector/index.ts +1 -0
- package/templates/ui/components/controls/vector/vector-control.tsx +109 -12
- package/templates/ui/components/panel/panel-actions.tsx +1 -1
- package/templates/ui/components/panel/panel-section.tsx +29 -5
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
collectToolcraftPerformanceSensitiveControls,
|
|
6
6
|
collectToolcraftUnclassifiedPerformanceControls,
|
|
7
7
|
defineToolcraftPerformance,
|
|
8
|
+
defaultToolcraftBrowserCheckPolicy,
|
|
9
|
+
type ToolcraftBrowserCheckPolicy,
|
|
8
10
|
validateToolcraftPerformanceCoverage,
|
|
9
11
|
} from "./performance";
|
|
10
12
|
|
|
@@ -76,6 +78,34 @@ const ordinarySliderSchema = defineToolcraft({
|
|
|
76
78
|
},
|
|
77
79
|
});
|
|
78
80
|
|
|
81
|
+
const rangeSliderSchema = defineToolcraft({
|
|
82
|
+
canvas: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
sizing: { mode: "intrinsic-media" },
|
|
85
|
+
},
|
|
86
|
+
panels: {
|
|
87
|
+
controls: {
|
|
88
|
+
sections: [
|
|
89
|
+
{
|
|
90
|
+
controls: {
|
|
91
|
+
band: {
|
|
92
|
+
defaultValue: [20, 80],
|
|
93
|
+
label: "Band",
|
|
94
|
+
max: 100,
|
|
95
|
+
min: 0,
|
|
96
|
+
performanceReason: "Band changes a lightweight bounded interval.",
|
|
97
|
+
performanceRole: "responsiveness",
|
|
98
|
+
target: "render.band",
|
|
99
|
+
type: "rangeSlider",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
title: "Runtime Controls",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
79
109
|
const largeTextSchema = defineToolcraft({
|
|
80
110
|
canvas: {
|
|
81
111
|
enabled: true,
|
|
@@ -152,6 +182,14 @@ function createViewportZoomStressScenario() {
|
|
|
152
182
|
function createMaxValueStressFixture(value: unknown, reason = "Maximum workload value.") {
|
|
153
183
|
return {
|
|
154
184
|
kind: "max-value" as const,
|
|
185
|
+
loadProfile: {
|
|
186
|
+
hardLimit: value,
|
|
187
|
+
metric: "numeric-max" as const,
|
|
188
|
+
smoothTarget: value,
|
|
189
|
+
smoothTargetRatio: 1,
|
|
190
|
+
target: "render.density",
|
|
191
|
+
userFacingRange: "fully-guaranteed" as const,
|
|
192
|
+
},
|
|
155
193
|
reason,
|
|
156
194
|
value,
|
|
157
195
|
};
|
|
@@ -163,6 +201,14 @@ function createCustomStressFixture(
|
|
|
163
201
|
) {
|
|
164
202
|
return {
|
|
165
203
|
kind: "custom" as const,
|
|
204
|
+
loadProfile: {
|
|
205
|
+
hardLimit: value,
|
|
206
|
+
metric: "custom" as const,
|
|
207
|
+
smoothTarget: value,
|
|
208
|
+
smoothTargetRatio: 1,
|
|
209
|
+
target: "renderer.output",
|
|
210
|
+
userFacingRange: "fully-guaranteed" as const,
|
|
211
|
+
},
|
|
166
212
|
reason,
|
|
167
213
|
value,
|
|
168
214
|
};
|
|
@@ -199,11 +245,60 @@ function createMediaStressFixture(
|
|
|
199
245
|
) {
|
|
200
246
|
return {
|
|
201
247
|
kind: "media" as const,
|
|
248
|
+
loadProfile: {
|
|
249
|
+
hardLimit: value,
|
|
250
|
+
metric: "media-area" as const,
|
|
251
|
+
smoothTarget: value,
|
|
252
|
+
smoothTargetRatio: 1,
|
|
253
|
+
target: "source.image",
|
|
254
|
+
userFacingRange: "fully-guaranteed" as const,
|
|
255
|
+
},
|
|
202
256
|
reason,
|
|
203
257
|
value,
|
|
204
258
|
};
|
|
205
259
|
}
|
|
206
260
|
|
|
261
|
+
function createDegradedMaxValueStressFixture() {
|
|
262
|
+
return {
|
|
263
|
+
kind: "max-value" as const,
|
|
264
|
+
loadProfile: {
|
|
265
|
+
degradationStepPercent: 10 as const,
|
|
266
|
+
evidence: [
|
|
267
|
+
{
|
|
268
|
+
attemptedTarget: 12,
|
|
269
|
+
decision: "Keep 12 available as high density while guaranteeing smooth drag through 11.",
|
|
270
|
+
measuredResult: "At density 12, maxFrameGapMs 148 exceeded the 80ms budget.",
|
|
271
|
+
optimizationAttempted: "Cached glyph atlas and coalesced preview work to requestAnimationFrame.",
|
|
272
|
+
result: "failed" as const,
|
|
273
|
+
scenarioId: "density-drag",
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
hardLimit: 12,
|
|
277
|
+
metric: "numeric-max" as const,
|
|
278
|
+
smoothTarget: 11,
|
|
279
|
+
smoothTargetRatio: 0.9,
|
|
280
|
+
target: "render.density",
|
|
281
|
+
userFacingRange: "experimental-above-smooth" as const,
|
|
282
|
+
},
|
|
283
|
+
reason: "Density 11 is the measured smooth target after hard-limit testing.",
|
|
284
|
+
value: 11,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function createMeasuredWebglEvidence(scenarioId = "text-preview-render") {
|
|
289
|
+
return [
|
|
290
|
+
{
|
|
291
|
+
alternativeStrategy: "webgl" as const,
|
|
292
|
+
decision:
|
|
293
|
+
"Canvas 2D remains selected because cached CPU rendering stayed within budget for this product fixture while preserving export parity.",
|
|
294
|
+
fixture: "large product stress fixture at render scale 2",
|
|
295
|
+
measuredResult:
|
|
296
|
+
"WebGL comparison: maxFrameGapMs 72, maxLongTaskMs 64; Canvas 2D comparison: maxFrameGapMs 68, maxLongTaskMs 58.",
|
|
297
|
+
scenarioId,
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
}
|
|
301
|
+
|
|
207
302
|
function createTextRendererPipeline() {
|
|
208
303
|
return {
|
|
209
304
|
interactionInvalidation: [
|
|
@@ -367,6 +462,61 @@ function createDenseCanvasPipeline() {
|
|
|
367
462
|
} as const;
|
|
368
463
|
}
|
|
369
464
|
|
|
465
|
+
function createMainThreadCanvasCompositePipeline() {
|
|
466
|
+
return {
|
|
467
|
+
interactionInvalidation: [
|
|
468
|
+
{
|
|
469
|
+
interaction: "control-drag",
|
|
470
|
+
invalidates: ["text-texture", "warp-composite", "optical-composite"],
|
|
471
|
+
targets: ["render.density"],
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
interaction: "control-change",
|
|
475
|
+
invalidates: ["text-texture", "warp-composite", "optical-composite"],
|
|
476
|
+
targets: ["render.mode"],
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
interaction: "viewport-zoom",
|
|
480
|
+
invalidates: [],
|
|
481
|
+
mustNotInvalidate: ["text-texture", "warp-composite", "optical-composite"],
|
|
482
|
+
targets: ["canvas.viewport"],
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
passes: [
|
|
486
|
+
{
|
|
487
|
+
cacheKey: ["product.content", "render.density", "canvas.size", "canvas.renderScale"],
|
|
488
|
+
id: "text-texture",
|
|
489
|
+
inputs: ["product.content", "render.density", "canvas.size", "canvas.renderScale"],
|
|
490
|
+
invalidatedBy: ["product.content", "render.density", "canvas.size", "canvas.renderScale"],
|
|
491
|
+
kind: "rasterize",
|
|
492
|
+
output: "intermediate",
|
|
493
|
+
quality: "retina",
|
|
494
|
+
runsOn: "main",
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
cacheKey: ["text-texture", "render.density", "canvas.size"],
|
|
498
|
+
id: "warp-composite",
|
|
499
|
+
inputs: ["text-texture", "render.density", "canvas.size"],
|
|
500
|
+
invalidatedBy: ["text-texture", "render.density", "canvas.size"],
|
|
501
|
+
kind: "composite",
|
|
502
|
+
output: "preview",
|
|
503
|
+
quality: "retina",
|
|
504
|
+
runsOn: "main",
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
cacheKey: ["warp-composite", "render.mode", "canvas.renderScale"],
|
|
508
|
+
id: "optical-composite",
|
|
509
|
+
inputs: ["warp-composite", "render.mode", "canvas.renderScale"],
|
|
510
|
+
invalidatedBy: ["warp-composite", "render.mode", "canvas.renderScale"],
|
|
511
|
+
kind: "composite",
|
|
512
|
+
output: "preview",
|
|
513
|
+
quality: "retina",
|
|
514
|
+
runsOn: "main",
|
|
515
|
+
},
|
|
516
|
+
],
|
|
517
|
+
} as const;
|
|
518
|
+
}
|
|
519
|
+
|
|
370
520
|
function createPixelRendererPipeline() {
|
|
371
521
|
return {
|
|
372
522
|
interactionInvalidation: [
|
|
@@ -600,6 +750,71 @@ function createAnimatedVectorRendererConfig(options: { includeViewportDrag: bool
|
|
|
600
750
|
}
|
|
601
751
|
|
|
602
752
|
describe("Toolcraft template performance contract", () => {
|
|
753
|
+
it("defaults browser checks to agent browser with Playwright fallback", () => {
|
|
754
|
+
const config = defineToolcraftPerformance({
|
|
755
|
+
rendererStrategy: "none",
|
|
756
|
+
rendererWorkload: "none",
|
|
757
|
+
scenarios: [
|
|
758
|
+
{
|
|
759
|
+
automated: true,
|
|
760
|
+
automatedTestName: "perf: opacity drag stays responsive",
|
|
761
|
+
browser: true,
|
|
762
|
+
browserTestName: "browser perf: opacity drag stays responsive",
|
|
763
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
764
|
+
controlLabel: "Opacity",
|
|
765
|
+
expectedObservable: "Dragging Opacity stays responsive.",
|
|
766
|
+
fixture: "opacity responsiveness fixture",
|
|
767
|
+
id: "opacity-drag",
|
|
768
|
+
interaction: "control-drag",
|
|
769
|
+
target: "render.opacity",
|
|
770
|
+
workload: false,
|
|
771
|
+
},
|
|
772
|
+
],
|
|
773
|
+
usesCustomRenderer: false,
|
|
774
|
+
workloadTargets: [],
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
expect(config.browserCheckPolicy).toEqual(defaultToolcraftBrowserCheckPolicy);
|
|
778
|
+
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toEqual([]);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it("rejects explicit Playwright-first browser check policies", () => {
|
|
782
|
+
const config = defineToolcraftPerformance({
|
|
783
|
+
browserCheckPolicy: {
|
|
784
|
+
fallbackRunner: "playwright",
|
|
785
|
+
fallbackWhen: ["agent-browser-unavailable"],
|
|
786
|
+
preferredRunner: "playwright",
|
|
787
|
+
} as unknown as ToolcraftBrowserCheckPolicy,
|
|
788
|
+
rendererStrategy: "none",
|
|
789
|
+
rendererWorkload: "none",
|
|
790
|
+
scenarios: [
|
|
791
|
+
{
|
|
792
|
+
automated: true,
|
|
793
|
+
automatedTestName: "perf: opacity drag stays responsive",
|
|
794
|
+
browser: true,
|
|
795
|
+
browserTestName: "browser perf: opacity drag stays responsive",
|
|
796
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
797
|
+
controlLabel: "Opacity",
|
|
798
|
+
expectedObservable: "Dragging Opacity stays responsive.",
|
|
799
|
+
fixture: "opacity responsiveness fixture",
|
|
800
|
+
id: "opacity-drag",
|
|
801
|
+
interaction: "control-drag",
|
|
802
|
+
target: "render.opacity",
|
|
803
|
+
workload: false,
|
|
804
|
+
},
|
|
805
|
+
],
|
|
806
|
+
usesCustomRenderer: false,
|
|
807
|
+
workloadTargets: [],
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toEqual(
|
|
811
|
+
expect.arrayContaining([
|
|
812
|
+
'browserCheckPolicy.preferredRunner must be "agent-browser" so AI agents use their controlled browser before fallback automation.',
|
|
813
|
+
'browserCheckPolicy.fallbackWhen must include "ci" so CI/non-agent automation has a portable performance runner.',
|
|
814
|
+
]),
|
|
815
|
+
);
|
|
816
|
+
});
|
|
817
|
+
|
|
603
818
|
it("collects performance-sensitive controls from schema", () => {
|
|
604
819
|
expect(collectToolcraftPerformanceSensitiveControls(testSchema)).toEqual([
|
|
605
820
|
expect.objectContaining({
|
|
@@ -654,6 +869,7 @@ describe("Toolcraft template performance contract", () => {
|
|
|
654
869
|
const unclassifiedSchema = defineToolcraft({
|
|
655
870
|
canvas: {
|
|
656
871
|
enabled: true,
|
|
872
|
+
renderScale: true,
|
|
657
873
|
sizing: { mode: "intrinsic-media" },
|
|
658
874
|
},
|
|
659
875
|
panels: {
|
|
@@ -676,6 +892,7 @@ describe("Toolcraft template performance contract", () => {
|
|
|
676
892
|
],
|
|
677
893
|
title: "Runtime Controls",
|
|
678
894
|
},
|
|
895
|
+
timeline: { mode: "playback" },
|
|
679
896
|
},
|
|
680
897
|
});
|
|
681
898
|
|
|
@@ -714,6 +931,64 @@ describe("Toolcraft template performance contract", () => {
|
|
|
714
931
|
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toEqual([]);
|
|
715
932
|
});
|
|
716
933
|
|
|
934
|
+
it("rejects slider performance coverage that does not drag the real control", () => {
|
|
935
|
+
const config = defineToolcraftPerformance({
|
|
936
|
+
rendererStrategy: "none",
|
|
937
|
+
rendererWorkload: "none",
|
|
938
|
+
scenarios: [
|
|
939
|
+
{
|
|
940
|
+
automated: true,
|
|
941
|
+
automatedTestName: "perf: opacity change stays responsive",
|
|
942
|
+
browser: true,
|
|
943
|
+
browserTestName: "browser perf: opacity change stays responsive",
|
|
944
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
945
|
+
controlLabel: "Opacity",
|
|
946
|
+
expectedObservable: "Changing Opacity stays responsive.",
|
|
947
|
+
fixture: "opacity responsiveness fixture",
|
|
948
|
+
id: "opacity-change",
|
|
949
|
+
interaction: "control-change",
|
|
950
|
+
target: "render.opacity",
|
|
951
|
+
workload: false,
|
|
952
|
+
},
|
|
953
|
+
],
|
|
954
|
+
usesCustomRenderer: false,
|
|
955
|
+
workloadTargets: [],
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toContain(
|
|
959
|
+
"render.opacity is a slider and must have a control-drag performance scenario proving live canvas/product feedback while dragging.",
|
|
960
|
+
);
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
it("rejects range slider performance coverage that does not drag the real control", () => {
|
|
964
|
+
const config = defineToolcraftPerformance({
|
|
965
|
+
rendererStrategy: "none",
|
|
966
|
+
rendererWorkload: "none",
|
|
967
|
+
scenarios: [
|
|
968
|
+
{
|
|
969
|
+
automated: true,
|
|
970
|
+
automatedTestName: "perf: band change stays responsive",
|
|
971
|
+
browser: true,
|
|
972
|
+
browserTestName: "browser perf: band change stays responsive",
|
|
973
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
974
|
+
controlLabel: "Band",
|
|
975
|
+
expectedObservable: "Changing Band stays responsive.",
|
|
976
|
+
fixture: "band responsiveness fixture",
|
|
977
|
+
id: "band-change",
|
|
978
|
+
interaction: "control-change",
|
|
979
|
+
target: "render.band",
|
|
980
|
+
workload: false,
|
|
981
|
+
},
|
|
982
|
+
],
|
|
983
|
+
usesCustomRenderer: false,
|
|
984
|
+
workloadTargets: [],
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
expect(validateToolcraftPerformanceCoverage(rangeSliderSchema, config)).toContain(
|
|
988
|
+
"render.band is a rangeSlider and must have a control-drag performance scenario proving live canvas/product feedback while dragging.",
|
|
989
|
+
);
|
|
990
|
+
});
|
|
991
|
+
|
|
717
992
|
it("requires performance-sensitive controls to be listed in workloadTargets", () => {
|
|
718
993
|
const config = defineToolcraftPerformance({
|
|
719
994
|
rendererStrategy: "none",
|
|
@@ -758,6 +1033,54 @@ describe("Toolcraft template performance contract", () => {
|
|
|
758
1033
|
);
|
|
759
1034
|
});
|
|
760
1035
|
|
|
1036
|
+
it("requires workload slider coverage to use real control drag", () => {
|
|
1037
|
+
const config = defineToolcraftPerformance({
|
|
1038
|
+
rendererStrategy: "none",
|
|
1039
|
+
rendererWorkload: "none",
|
|
1040
|
+
scenarios: [
|
|
1041
|
+
{
|
|
1042
|
+
automated: true,
|
|
1043
|
+
automatedTestName: "perf: density change stays responsive",
|
|
1044
|
+
browser: true,
|
|
1045
|
+
browserTestName: "browser perf: density change stays responsive",
|
|
1046
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1047
|
+
controlLabel: "Density",
|
|
1048
|
+
expectedObservable: "Changing Density updates the product without blocking the UI.",
|
|
1049
|
+
fixture: "runtime density fixture",
|
|
1050
|
+
id: "density-change",
|
|
1051
|
+
interaction: "control-change",
|
|
1052
|
+
target: "render.density",
|
|
1053
|
+
values: { default: 4, max: 12, min: 1 },
|
|
1054
|
+
workload: true,
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
automated: true,
|
|
1058
|
+
automatedTestName: "perf: mode change stays responsive",
|
|
1059
|
+
browser: true,
|
|
1060
|
+
browserTestName: "browser perf: mode change stays responsive",
|
|
1061
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1062
|
+
controlLabel: "Mode",
|
|
1063
|
+
expectedObservable: "Changing Mode updates the product without blocking the UI.",
|
|
1064
|
+
fixture: "runtime mode fixture",
|
|
1065
|
+
id: "mode-change",
|
|
1066
|
+
interaction: "control-change",
|
|
1067
|
+
target: "render.mode",
|
|
1068
|
+
values: { default: "soft", max: "sharp", min: "soft" },
|
|
1069
|
+
workload: false,
|
|
1070
|
+
},
|
|
1071
|
+
],
|
|
1072
|
+
usesCustomRenderer: false,
|
|
1073
|
+
workloadTargets: ["render.density"],
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
1077
|
+
expect.arrayContaining([
|
|
1078
|
+
"render.density must have min/default/max workload performance coverage through a real control-drag scenario.",
|
|
1079
|
+
"render.density is a slider and must have a control-drag performance scenario proving live canvas/product feedback while dragging.",
|
|
1080
|
+
]),
|
|
1081
|
+
);
|
|
1082
|
+
});
|
|
1083
|
+
|
|
761
1084
|
it("rejects responsiveness role on semantically workload controls", () => {
|
|
762
1085
|
const misclassifiedSchema = defineToolcraft({
|
|
763
1086
|
canvas: {
|
|
@@ -1061,43 +1384,464 @@ describe("Toolcraft template performance contract", () => {
|
|
|
1061
1384
|
);
|
|
1062
1385
|
});
|
|
1063
1386
|
|
|
1064
|
-
it("
|
|
1387
|
+
it("rejects short large-text values hidden inside custom fixtures", () => {
|
|
1065
1388
|
const config = defineToolcraftPerformance({
|
|
1066
1389
|
rendererStrategy: "none",
|
|
1067
1390
|
rendererWorkload: "none",
|
|
1068
1391
|
scenarios: [
|
|
1069
1392
|
{
|
|
1070
1393
|
automated: true,
|
|
1071
|
-
automatedTestName: "perf:
|
|
1394
|
+
automatedTestName: "perf: preview render stays under budget",
|
|
1072
1395
|
browser: true,
|
|
1073
|
-
browserTestName: "browser perf:
|
|
1074
|
-
budget: {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
stressFixture:
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1396
|
+
browserTestName: "browser perf: preview render stays under budget",
|
|
1397
|
+
budget: { maxLongTaskMs: 80, maxPreviewMs: 1000 },
|
|
1398
|
+
expectedObservable: "Preview renders the heaviest output without freezing.",
|
|
1399
|
+
fixture: "combined large text fixture",
|
|
1400
|
+
id: "preview-render",
|
|
1401
|
+
interaction: "preview-render",
|
|
1402
|
+
stress: true,
|
|
1403
|
+
stressFixture: {
|
|
1404
|
+
kind: "custom",
|
|
1405
|
+
reason: "Combined renderer stress includes pasted product content.",
|
|
1406
|
+
value: {
|
|
1407
|
+
content: "short text",
|
|
1408
|
+
renderScale: 2,
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
workload: false,
|
|
1084
1412
|
},
|
|
1085
1413
|
],
|
|
1086
1414
|
usesCustomRenderer: false,
|
|
1087
|
-
workloadTargets: [
|
|
1415
|
+
workloadTargets: [],
|
|
1088
1416
|
});
|
|
1089
1417
|
|
|
1090
|
-
expect(validateToolcraftPerformanceCoverage(largeTextSchema, config)).toEqual(
|
|
1418
|
+
expect(validateToolcraftPerformanceCoverage(largeTextSchema, config)).toEqual(
|
|
1419
|
+
expect.arrayContaining([
|
|
1420
|
+
"preview-render custom stressFixture.content must contain at least 50000 characters.",
|
|
1421
|
+
"preview-render custom stressFixture.content must contain at least 1000 lines.",
|
|
1422
|
+
]),
|
|
1423
|
+
);
|
|
1091
1424
|
});
|
|
1092
1425
|
|
|
1093
|
-
it("
|
|
1426
|
+
it("rejects custom render scale fixtures that are not concrete high-resolution values", () => {
|
|
1094
1427
|
const config = defineToolcraftPerformance({
|
|
1095
|
-
rendererStrategy: "
|
|
1096
|
-
rendererWorkload: "
|
|
1428
|
+
rendererStrategy: "none",
|
|
1429
|
+
rendererWorkload: "none",
|
|
1097
1430
|
scenarios: [
|
|
1098
1431
|
{
|
|
1099
1432
|
automated: true,
|
|
1100
|
-
automatedTestName: "perf:
|
|
1433
|
+
automatedTestName: "perf: preview render stays under budget",
|
|
1434
|
+
browser: true,
|
|
1435
|
+
browserTestName: "browser perf: preview render stays under budget",
|
|
1436
|
+
budget: { maxLongTaskMs: 80, maxPreviewMs: 1000 },
|
|
1437
|
+
expectedObservable: "Preview renders the heaviest output without freezing.",
|
|
1438
|
+
fixture: "combined render scale fixture",
|
|
1439
|
+
id: "preview-render",
|
|
1440
|
+
interaction: "preview-render",
|
|
1441
|
+
stress: true,
|
|
1442
|
+
stressFixture: {
|
|
1443
|
+
kind: "custom",
|
|
1444
|
+
reason: "Combined renderer stress includes selected resolution scale.",
|
|
1445
|
+
value: {
|
|
1446
|
+
density: 12,
|
|
1447
|
+
renderScale: "2",
|
|
1448
|
+
},
|
|
1449
|
+
},
|
|
1450
|
+
workload: false,
|
|
1451
|
+
},
|
|
1452
|
+
],
|
|
1453
|
+
usesCustomRenderer: false,
|
|
1454
|
+
workloadTargets: [],
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toContain(
|
|
1458
|
+
"preview-render custom stressFixture.renderScale must be a numeric Resolution scale greater than 1 so browser tests prove high-resolution backing pixels.",
|
|
1459
|
+
);
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
it("accepts large text workload scenarios with machine-checkable stress fixtures", () => {
|
|
1463
|
+
const config = defineToolcraftPerformance({
|
|
1464
|
+
rendererStrategy: "none",
|
|
1465
|
+
rendererWorkload: "none",
|
|
1466
|
+
scenarios: [
|
|
1467
|
+
{
|
|
1468
|
+
automated: true,
|
|
1469
|
+
automatedTestName: "perf: content input stays responsive",
|
|
1470
|
+
browser: true,
|
|
1471
|
+
browserTestName: "browser perf: content input stays responsive",
|
|
1472
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1473
|
+
controlLabel: "Content",
|
|
1474
|
+
expectedObservable: "Large text content updates without blocking the UI.",
|
|
1475
|
+
fixture: "large text content fixture",
|
|
1476
|
+
id: "content-change",
|
|
1477
|
+
interaction: "control-change",
|
|
1478
|
+
stressFixture: createLargeTextStressFixture(),
|
|
1479
|
+
target: "product.content",
|
|
1480
|
+
values: { default: "", max: createLargeTextStressValue(), min: "" },
|
|
1481
|
+
workload: true,
|
|
1482
|
+
},
|
|
1483
|
+
],
|
|
1484
|
+
usesCustomRenderer: false,
|
|
1485
|
+
workloadTargets: ["product.content"],
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
expect(validateToolcraftPerformanceCoverage(largeTextSchema, config)).toEqual([]);
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
it("requires max-value fixtures to declare a measured load profile", () => {
|
|
1492
|
+
const config = defineToolcraftPerformance({
|
|
1493
|
+
rendererStrategy: "none",
|
|
1494
|
+
rendererWorkload: "none",
|
|
1495
|
+
scenarios: [
|
|
1496
|
+
{
|
|
1497
|
+
automated: true,
|
|
1498
|
+
automatedTestName: "perf: density drag stays responsive",
|
|
1499
|
+
browser: true,
|
|
1500
|
+
browserTestName: "browser perf: density drag stays responsive",
|
|
1501
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1502
|
+
controlLabel: "Density",
|
|
1503
|
+
expectedObservable: "Dragging Density updates text output without blocking the UI.",
|
|
1504
|
+
fixture: "runtime density fixture",
|
|
1505
|
+
id: "density-drag",
|
|
1506
|
+
interaction: "control-drag",
|
|
1507
|
+
stressFixture: {
|
|
1508
|
+
kind: "max-value",
|
|
1509
|
+
reason: "Density 11 is claimed as the smooth target without hard-limit evidence.",
|
|
1510
|
+
value: 11,
|
|
1511
|
+
},
|
|
1512
|
+
target: "render.density",
|
|
1513
|
+
values: { default: 4, max: 12, min: 1 },
|
|
1514
|
+
workload: true,
|
|
1515
|
+
},
|
|
1516
|
+
],
|
|
1517
|
+
usesCustomRenderer: false,
|
|
1518
|
+
workloadTargets: ["render.density"],
|
|
1519
|
+
});
|
|
1520
|
+
|
|
1521
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1522
|
+
"density-drag stressFixture.loadProfile must declare hardLimit, smoothTarget, and smoothTargetRatio so performance workload is a measured smooth target rather than a hidden toy fixture.",
|
|
1523
|
+
);
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1526
|
+
it("rejects load profiles whose smooth target does not match the applied fixture", () => {
|
|
1527
|
+
const config = defineToolcraftPerformance({
|
|
1528
|
+
rendererStrategy: "none",
|
|
1529
|
+
rendererWorkload: "none",
|
|
1530
|
+
scenarios: [
|
|
1531
|
+
{
|
|
1532
|
+
automated: true,
|
|
1533
|
+
automatedTestName: "perf: density drag stays responsive",
|
|
1534
|
+
browser: true,
|
|
1535
|
+
browserTestName: "browser perf: density drag stays responsive",
|
|
1536
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1537
|
+
controlLabel: "Density",
|
|
1538
|
+
expectedObservable: "Dragging Density updates text output without blocking the UI.",
|
|
1539
|
+
fixture: "runtime density fixture",
|
|
1540
|
+
id: "density-drag",
|
|
1541
|
+
interaction: "control-drag",
|
|
1542
|
+
stressFixture: {
|
|
1543
|
+
kind: "max-value",
|
|
1544
|
+
loadProfile: {
|
|
1545
|
+
hardLimit: 12,
|
|
1546
|
+
metric: "numeric-max",
|
|
1547
|
+
smoothTarget: 11,
|
|
1548
|
+
smoothTargetRatio: 0.9,
|
|
1549
|
+
target: "render.density",
|
|
1550
|
+
userFacingRange: "experimental-above-smooth",
|
|
1551
|
+
},
|
|
1552
|
+
reason: "Density 10 does not match the documented smooth target.",
|
|
1553
|
+
value: 10,
|
|
1554
|
+
},
|
|
1555
|
+
target: "render.density",
|
|
1556
|
+
values: { default: 4, max: 12, min: 1 },
|
|
1557
|
+
workload: true,
|
|
1558
|
+
},
|
|
1559
|
+
],
|
|
1560
|
+
usesCustomRenderer: false,
|
|
1561
|
+
workloadTargets: ["render.density"],
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1564
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
1565
|
+
expect.arrayContaining([
|
|
1566
|
+
"density-drag stressFixture.loadProfile.smoothTarget must match stressFixture.value so browser tests apply the documented smooth workload target.",
|
|
1567
|
+
"density-drag stressFixture.loadProfile.degradationStepPercent must be 10 when smoothTargetRatio is below 1.",
|
|
1568
|
+
"density-drag stressFixture.loadProfile.evidence must include failed measurements for each 10 percent step above smoothTargetRatio 0.9.",
|
|
1569
|
+
]),
|
|
1570
|
+
);
|
|
1571
|
+
});
|
|
1572
|
+
|
|
1573
|
+
it("rejects load profiles whose ratio does not describe the measured smooth target", () => {
|
|
1574
|
+
const config = defineToolcraftPerformance({
|
|
1575
|
+
rendererStrategy: "none",
|
|
1576
|
+
rendererWorkload: "none",
|
|
1577
|
+
scenarios: [
|
|
1578
|
+
{
|
|
1579
|
+
automated: true,
|
|
1580
|
+
automatedTestName: "perf: density drag stays responsive",
|
|
1581
|
+
browser: true,
|
|
1582
|
+
browserTestName: "browser perf: density drag stays responsive",
|
|
1583
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1584
|
+
controlLabel: "Density",
|
|
1585
|
+
expectedObservable: "Dragging Density updates text output without blocking the UI.",
|
|
1586
|
+
fixture: "runtime density fixture",
|
|
1587
|
+
id: "density-drag",
|
|
1588
|
+
interaction: "control-drag",
|
|
1589
|
+
stressFixture: {
|
|
1590
|
+
kind: "max-value",
|
|
1591
|
+
loadProfile: {
|
|
1592
|
+
degradationStepPercent: 10,
|
|
1593
|
+
evidence: [
|
|
1594
|
+
{
|
|
1595
|
+
attemptedTarget: 12,
|
|
1596
|
+
decision:
|
|
1597
|
+
"Density 12 remains experimental because it misses frame budget after optimization.",
|
|
1598
|
+
measuredResult: "At density 12, maxFrameGapMs 148 exceeded the 80ms budget.",
|
|
1599
|
+
optimizationAttempted:
|
|
1600
|
+
"Cached glyph atlas and coalesced preview work to requestAnimationFrame.",
|
|
1601
|
+
result: "failed",
|
|
1602
|
+
scenarioId: "density-drag",
|
|
1603
|
+
},
|
|
1604
|
+
],
|
|
1605
|
+
hardLimit: 12,
|
|
1606
|
+
metric: "numeric-max",
|
|
1607
|
+
smoothTarget: 2,
|
|
1608
|
+
smoothTargetRatio: 0.9,
|
|
1609
|
+
target: "render.density",
|
|
1610
|
+
userFacingRange: "experimental-above-smooth",
|
|
1611
|
+
},
|
|
1612
|
+
reason: "Density 2 is far below the claimed 90 percent smooth target.",
|
|
1613
|
+
value: 2,
|
|
1614
|
+
},
|
|
1615
|
+
target: "render.density",
|
|
1616
|
+
values: { default: 4, max: 12, min: 1 },
|
|
1617
|
+
workload: true,
|
|
1618
|
+
},
|
|
1619
|
+
],
|
|
1620
|
+
usesCustomRenderer: false,
|
|
1621
|
+
workloadTargets: ["render.density"],
|
|
1622
|
+
});
|
|
1623
|
+
|
|
1624
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1625
|
+
"density-drag stressFixture.loadProfile.smoothTargetRatio 0.9 must describe the actual smooth target ratio 0.1667.",
|
|
1626
|
+
);
|
|
1627
|
+
});
|
|
1628
|
+
|
|
1629
|
+
it("rejects fully guaranteed custom load profiles whose smooth target is not the hard limit", () => {
|
|
1630
|
+
const config = defineToolcraftPerformance({
|
|
1631
|
+
rendererStrategy: "canvas-2d",
|
|
1632
|
+
rendererWorkload: "pixel-output",
|
|
1633
|
+
scenarios: [
|
|
1634
|
+
{
|
|
1635
|
+
automated: true,
|
|
1636
|
+
automatedTestName: "perf: combined preview stays responsive",
|
|
1637
|
+
browser: true,
|
|
1638
|
+
browserTestName: "browser perf: combined preview stays responsive",
|
|
1639
|
+
budget: { maxPreviewMs: 200, maxRenderMs: 200 },
|
|
1640
|
+
expectedObservable: "Preview renders the combined source and effect workload.",
|
|
1641
|
+
fixture: "combined custom fixture",
|
|
1642
|
+
id: "combined-preview",
|
|
1643
|
+
interaction: "preview-render",
|
|
1644
|
+
stress: true,
|
|
1645
|
+
stressFixture: {
|
|
1646
|
+
kind: "custom",
|
|
1647
|
+
loadProfile: {
|
|
1648
|
+
hardLimit: { density: 12, sourceMedia: { height: 2160, width: 3840 } },
|
|
1649
|
+
metric: "custom",
|
|
1650
|
+
smoothTarget: { density: 4, sourceMedia: { height: 1080, width: 1920 } },
|
|
1651
|
+
smoothTargetRatio: 1,
|
|
1652
|
+
target: "renderer.output",
|
|
1653
|
+
userFacingRange: "fully-guaranteed",
|
|
1654
|
+
},
|
|
1655
|
+
reason: "This claims full coverage while applying a smaller custom state.",
|
|
1656
|
+
value: { density: 4, sourceMedia: { height: 1080, width: 1920 } },
|
|
1657
|
+
},
|
|
1658
|
+
target: "renderer.output",
|
|
1659
|
+
workload: false,
|
|
1660
|
+
},
|
|
1661
|
+
],
|
|
1662
|
+
usesCustomRenderer: true,
|
|
1663
|
+
workloadTargets: [],
|
|
1664
|
+
});
|
|
1665
|
+
|
|
1666
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1667
|
+
"combined-preview stressFixture.loadProfile.smoothTargetRatio 1 requires smoothTarget to equal hardLimit.",
|
|
1668
|
+
);
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
it("accepts a 10 percent degraded smooth target with hard-limit failure evidence", () => {
|
|
1672
|
+
const config = defineToolcraftPerformance({
|
|
1673
|
+
rendererStrategy: "none",
|
|
1674
|
+
rendererWorkload: "none",
|
|
1675
|
+
scenarios: [
|
|
1676
|
+
{
|
|
1677
|
+
automated: true,
|
|
1678
|
+
automatedTestName: "perf: density drag stays responsive",
|
|
1679
|
+
browser: true,
|
|
1680
|
+
browserTestName: "browser perf: density drag stays responsive",
|
|
1681
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1682
|
+
controlLabel: "Density",
|
|
1683
|
+
expectedObservable: "Dragging Density updates text output without blocking the UI.",
|
|
1684
|
+
fixture: "runtime density fixture",
|
|
1685
|
+
id: "density-drag",
|
|
1686
|
+
interaction: "control-drag",
|
|
1687
|
+
stressFixture: createDegradedMaxValueStressFixture(),
|
|
1688
|
+
target: "render.density",
|
|
1689
|
+
values: { default: 4, max: 12, min: 1 },
|
|
1690
|
+
workload: true,
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
automated: true,
|
|
1694
|
+
automatedTestName: "perf: mode change stays responsive",
|
|
1695
|
+
browser: true,
|
|
1696
|
+
browserTestName: "browser perf: mode change stays responsive",
|
|
1697
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1698
|
+
controlLabel: "Mode",
|
|
1699
|
+
expectedObservable: "Changing Mode updates text output without blocking the UI.",
|
|
1700
|
+
fixture: "runtime mode fixture",
|
|
1701
|
+
id: "mode-change",
|
|
1702
|
+
interaction: "control-change",
|
|
1703
|
+
target: "render.mode",
|
|
1704
|
+
values: { default: "soft", max: "sharp", min: "soft" },
|
|
1705
|
+
workload: false,
|
|
1706
|
+
},
|
|
1707
|
+
],
|
|
1708
|
+
usesCustomRenderer: false,
|
|
1709
|
+
workloadTargets: ["render.density"],
|
|
1710
|
+
});
|
|
1711
|
+
|
|
1712
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
it("requires evidence for every skipped 10 percent degradation step", () => {
|
|
1716
|
+
const degradedFixture = createDegradedMaxValueStressFixture();
|
|
1717
|
+
const config = defineToolcraftPerformance({
|
|
1718
|
+
rendererStrategy: "none",
|
|
1719
|
+
rendererWorkload: "none",
|
|
1720
|
+
scenarios: [
|
|
1721
|
+
{
|
|
1722
|
+
automated: true,
|
|
1723
|
+
automatedTestName: "perf: density drag stays responsive",
|
|
1724
|
+
browser: true,
|
|
1725
|
+
browserTestName: "browser perf: density drag stays responsive",
|
|
1726
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1727
|
+
controlLabel: "Density",
|
|
1728
|
+
expectedObservable: "Dragging Density updates text output without blocking the UI.",
|
|
1729
|
+
fixture: "runtime density fixture",
|
|
1730
|
+
id: "density-drag",
|
|
1731
|
+
interaction: "control-drag",
|
|
1732
|
+
stressFixture: {
|
|
1733
|
+
...degradedFixture,
|
|
1734
|
+
loadProfile: {
|
|
1735
|
+
...degradedFixture.loadProfile,
|
|
1736
|
+
smoothTarget: 10,
|
|
1737
|
+
smoothTargetRatio: 0.8,
|
|
1738
|
+
},
|
|
1739
|
+
value: 10,
|
|
1740
|
+
},
|
|
1741
|
+
target: "render.density",
|
|
1742
|
+
values: { default: 4, max: 12, min: 1 },
|
|
1743
|
+
workload: true,
|
|
1744
|
+
},
|
|
1745
|
+
],
|
|
1746
|
+
usesCustomRenderer: false,
|
|
1747
|
+
workloadTargets: ["render.density"],
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1750
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1751
|
+
"density-drag stressFixture.loadProfile.evidence must include failed measurements for each 10 percent step above smoothTargetRatio 0.8.",
|
|
1752
|
+
);
|
|
1753
|
+
});
|
|
1754
|
+
|
|
1755
|
+
it("rejects numeric hard limits that do not match schema max", () => {
|
|
1756
|
+
const fixture = createDegradedMaxValueStressFixture();
|
|
1757
|
+
const config = defineToolcraftPerformance({
|
|
1758
|
+
rendererStrategy: "none",
|
|
1759
|
+
rendererWorkload: "none",
|
|
1760
|
+
scenarios: [
|
|
1761
|
+
{
|
|
1762
|
+
automated: true,
|
|
1763
|
+
automatedTestName: "perf: density drag stays responsive",
|
|
1764
|
+
browser: true,
|
|
1765
|
+
browserTestName: "browser perf: density drag stays responsive",
|
|
1766
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1767
|
+
controlLabel: "Density",
|
|
1768
|
+
expectedObservable: "Dragging Density updates text output without blocking the UI.",
|
|
1769
|
+
fixture: "runtime density fixture",
|
|
1770
|
+
id: "density-drag",
|
|
1771
|
+
interaction: "control-drag",
|
|
1772
|
+
stressFixture: {
|
|
1773
|
+
...fixture,
|
|
1774
|
+
loadProfile: {
|
|
1775
|
+
...fixture.loadProfile,
|
|
1776
|
+
hardLimit: 10,
|
|
1777
|
+
},
|
|
1778
|
+
},
|
|
1779
|
+
target: "render.density",
|
|
1780
|
+
values: { default: 4, max: 12, min: 1 },
|
|
1781
|
+
workload: true,
|
|
1782
|
+
},
|
|
1783
|
+
],
|
|
1784
|
+
usesCustomRenderer: false,
|
|
1785
|
+
workloadTargets: ["render.density"],
|
|
1786
|
+
});
|
|
1787
|
+
|
|
1788
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1789
|
+
"density-drag stressFixture.loadProfile.hardLimit must match schema max 12.",
|
|
1790
|
+
);
|
|
1791
|
+
});
|
|
1792
|
+
|
|
1793
|
+
it("rejects toy many-items fixtures below their declared useful count", () => {
|
|
1794
|
+
const config = defineToolcraftPerformance({
|
|
1795
|
+
rendererStrategy: "none",
|
|
1796
|
+
rendererWorkload: "none",
|
|
1797
|
+
scenarios: [
|
|
1798
|
+
{
|
|
1799
|
+
automated: true,
|
|
1800
|
+
automatedTestName: "perf: items drag stays responsive",
|
|
1801
|
+
browser: true,
|
|
1802
|
+
browserTestName: "browser perf: items drag stays responsive",
|
|
1803
|
+
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1804
|
+
controlLabel: "Density",
|
|
1805
|
+
expectedObservable: "Dragging Density updates many items without blocking the UI.",
|
|
1806
|
+
fixture: "many item fixture",
|
|
1807
|
+
id: "items-density-drag",
|
|
1808
|
+
interaction: "control-drag",
|
|
1809
|
+
stressFixture: {
|
|
1810
|
+
kind: "many-items",
|
|
1811
|
+
loadProfile: {
|
|
1812
|
+
hardLimit: 100,
|
|
1813
|
+
metric: "count",
|
|
1814
|
+
smoothTarget: 2,
|
|
1815
|
+
smoothTargetRatio: 1,
|
|
1816
|
+
target: "items.count",
|
|
1817
|
+
userFacingRange: "fully-guaranteed",
|
|
1818
|
+
},
|
|
1819
|
+
minCount: 10,
|
|
1820
|
+
reason: "Two items is too small to prove collection performance.",
|
|
1821
|
+
value: 2,
|
|
1822
|
+
},
|
|
1823
|
+
target: "render.density",
|
|
1824
|
+
values: { default: 4, max: 12, min: 1 },
|
|
1825
|
+
workload: true,
|
|
1826
|
+
},
|
|
1827
|
+
],
|
|
1828
|
+
usesCustomRenderer: false,
|
|
1829
|
+
workloadTargets: ["render.density"],
|
|
1830
|
+
});
|
|
1831
|
+
|
|
1832
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1833
|
+
"items-density-drag stressFixture.value must include at least 10 items for many-items fixtures.",
|
|
1834
|
+
);
|
|
1835
|
+
});
|
|
1836
|
+
|
|
1837
|
+
it("requires workload controls in custom renderers to declare an independent workload fixture", () => {
|
|
1838
|
+
const config = defineToolcraftPerformance({
|
|
1839
|
+
rendererStrategy: "canvas-2d",
|
|
1840
|
+
rendererWorkload: "text-output",
|
|
1841
|
+
scenarios: [
|
|
1842
|
+
{
|
|
1843
|
+
automated: true,
|
|
1844
|
+
automatedTestName: "perf: density drag stays responsive",
|
|
1101
1845
|
browser: true,
|
|
1102
1846
|
browserTestName: "browser perf: density drag stays responsive",
|
|
1103
1847
|
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
@@ -1335,6 +2079,49 @@ describe("Toolcraft template performance contract", () => {
|
|
|
1335
2079
|
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
1336
2080
|
});
|
|
1337
2081
|
|
|
2082
|
+
it("rejects main-thread Canvas 2D raster/composite pipelines misclassified as text output", () => {
|
|
2083
|
+
const config = defineToolcraftPerformance({
|
|
2084
|
+
rendererStrategy: "canvas-2d",
|
|
2085
|
+
rendererTechnique: {
|
|
2086
|
+
exportRenderer: "canvas-2d",
|
|
2087
|
+
fidelityRisks: ["rasterized text must preserve the shader-style composite"],
|
|
2088
|
+
layers: [
|
|
2089
|
+
{
|
|
2090
|
+
content: ["text", "noise"],
|
|
2091
|
+
exportMode: "composited",
|
|
2092
|
+
id: "product-text-composite",
|
|
2093
|
+
intentionalRasterizationReason:
|
|
2094
|
+
"The product text is warped, tinted, and exported as final pixels.",
|
|
2095
|
+
kind: "product-foreground",
|
|
2096
|
+
primitiveCount: "medium",
|
|
2097
|
+
renderer: "canvas-2d",
|
|
2098
|
+
uiSelector: '[data-toolcraft-renderer-layer="product-text-composite"]',
|
|
2099
|
+
},
|
|
2100
|
+
],
|
|
2101
|
+
performanceRisks: [
|
|
2102
|
+
"WebGL measured stress evidence was reviewed before keeping the text composite on Canvas 2D.",
|
|
2103
|
+
],
|
|
2104
|
+
previewRenderer: "canvas-2d",
|
|
2105
|
+
productRepresentation: "mixed",
|
|
2106
|
+
rendererStrategy: "canvas-2d",
|
|
2107
|
+
rendererWorkload: "text-output",
|
|
2108
|
+
sourceRepresentation: "dom-text",
|
|
2109
|
+
whyNotAlternativeStrategies: [
|
|
2110
|
+
"WebGL measured stress evidence showed text atlas uploads dominated this fixture.",
|
|
2111
|
+
],
|
|
2112
|
+
},
|
|
2113
|
+
rendererWorkload: "text-output",
|
|
2114
|
+
rendererPipeline: createMainThreadCanvasCompositePipeline(),
|
|
2115
|
+
scenarios: createTextRendererScenarios(),
|
|
2116
|
+
usesCustomRenderer: true,
|
|
2117
|
+
workloadTargets: ["render.density"],
|
|
2118
|
+
});
|
|
2119
|
+
|
|
2120
|
+
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2121
|
+
'Canvas 2D pipelines with main-thread rasterize/composite preview pressure must use rendererWorkload "pixel-output" or move expensive passes off the main thread; received "text-output".',
|
|
2122
|
+
);
|
|
2123
|
+
});
|
|
2124
|
+
|
|
1338
2125
|
it("requires animated custom renderers to test viewport dragging during animation", () => {
|
|
1339
2126
|
const config = createAnimatedVectorRendererConfig({ includeViewportDrag: false });
|
|
1340
2127
|
|
|
@@ -1487,10 +2274,10 @@ describe("Toolcraft template performance contract", () => {
|
|
|
1487
2274
|
});
|
|
1488
2275
|
|
|
1489
2276
|
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1490
|
-
"Detail-heavy Canvas 2D renderers must
|
|
2277
|
+
"Detail-heavy Canvas 2D renderers must include rendererTechnique.measuredAlternativeEvidence for WebGL/WebGPU stress comparison before keeping the pixel work on CPU.",
|
|
1491
2278
|
);
|
|
1492
2279
|
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1493
|
-
'rendererWorkload "pixel-output" should use rendererStrategy "webgl" or "webgpu", received "canvas-2d". Keeping a CPU renderer requires
|
|
2280
|
+
'rendererWorkload "pixel-output" should use rendererStrategy "webgl" or "webgpu", received "canvas-2d". Keeping a CPU renderer requires rendererTechnique.measuredAlternativeEvidence for WebGL/WebGPU stress comparison.',
|
|
1494
2281
|
);
|
|
1495
2282
|
});
|
|
1496
2283
|
|
|
@@ -1503,6 +2290,7 @@ describe("Toolcraft template performance contract", () => {
|
|
|
1503
2290
|
performanceRisks: [
|
|
1504
2291
|
"Measured WebGL stress evidence showed setup overhead exceeded cached Canvas 2D worker output for this static fixture.",
|
|
1505
2292
|
],
|
|
2293
|
+
measuredAlternativeEvidence: createMeasuredWebglEvidence(),
|
|
1506
2294
|
previewRenderer: "canvas-2d",
|
|
1507
2295
|
productRepresentation: "pixel",
|
|
1508
2296
|
rendererStrategy: "canvas-2d",
|
|
@@ -2133,6 +2921,7 @@ describe("Toolcraft template performance contract", () => {
|
|
|
2133
2921
|
renderer: "canvas-2d",
|
|
2134
2922
|
},
|
|
2135
2923
|
],
|
|
2924
|
+
measuredAlternativeEvidence: createMeasuredWebglEvidence(),
|
|
2136
2925
|
performanceRisks: ["dense background redraw can be expensive at large output size"],
|
|
2137
2926
|
previewRenderer: "canvas-2d",
|
|
2138
2927
|
productRepresentation: "mixed",
|