@pixel-point/toolcraft 0.0.14 → 0.0.15
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 +13 -2
- package/package.json +1 -1
- package/scripts/prepare-pack.mjs +2 -28
- package/src/cli-io.mjs +174 -0
- package/src/cli-package-manager-flow.test.mjs +206 -0
- package/src/cli.mjs +7 -495
- package/src/cli.test.mjs +34 -177
- package/src/create-command.mjs +152 -0
- package/src/create-options.mjs +194 -0
- package/src/generate-test-assertions.mjs +27 -0
- package/src/generate-test-doc-assertions.mjs +162 -0
- package/src/generate-test-file-assertions.mjs +251 -0
- package/src/generate-test-fs-utils.mjs +59 -0
- package/src/generate-test-integrity-assertions.mjs +30 -0
- package/src/generate-test-integrity-framework-assertions.mjs +222 -0
- package/src/generate-test-integrity-manifest-assertions.mjs +150 -0
- package/src/generate-test-integrity-ownership-assertions.mjs +127 -0
- package/src/generate-test-integrity-test-utils.mjs +102 -0
- package/src/generate-test-package-assertions.mjs +75 -0
- package/src/generate-test-product-fixture.mjs +84 -0
- package/src/generate-test-product-fixture.test.mjs +73 -0
- package/src/generate-test-standalone-assertions.mjs +209 -0
- package/src/generate-transactional.test.mjs +151 -0
- package/src/generate.mjs +182 -102
- package/src/generate.test.mjs +242 -338
- package/src/generated-integrity-manifest.mjs +127 -0
- package/src/generated-integrity-manifest.test.mjs +151 -0
- package/src/generated-source-policy.mjs +22 -0
- package/src/generated-source-policy.test.mjs +35 -0
- package/src/generated-target-policy.mjs +55 -0
- package/src/generation-transaction.mjs +135 -0
- package/src/generation-transaction.test.mjs +74 -0
- package/src/package-json.mjs +18 -1
- package/src/package-json.test.mjs +38 -5
- package/src/package-manager.mjs +2 -0
- package/src/package-manager.test.mjs +5 -1
- package/src/packaged-template-fallback.test.mjs +126 -0
- package/src/prepare-pack-templates.mjs +34 -0
- package/templates/runtime/contracts/component-contract-builders.ts +95 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +293 -0
- package/templates/runtime/contracts/component-contracts.choices.ts +322 -0
- package/templates/runtime/contracts/component-contracts.inputs.test.ts +210 -0
- package/templates/runtime/contracts/component-contracts.inputs.ts +252 -0
- package/templates/runtime/contracts/component-contracts.media-custom.test.ts +116 -0
- package/templates/runtime/contracts/component-contracts.media-custom.ts +144 -0
- package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +229 -0
- package/templates/runtime/contracts/component-contracts.runtime-labels.test.ts +76 -0
- package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +135 -0
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +148 -0
- package/templates/runtime/contracts/component-contracts.runtime.ts +361 -0
- package/templates/runtime/contracts/component-contracts.test.ts +39 -1458
- package/templates/runtime/contracts/component-contracts.ts +44 -1505
- package/templates/runtime/contracts/component-contracts.visual.test.ts +316 -0
- package/templates/runtime/contracts/component-contracts.visual.ts +385 -0
- package/templates/runtime/contracts/contract-manifest.test.ts +64 -0
- package/templates/runtime/contracts/decision-contracts.test.ts +29 -4
- package/templates/runtime/contracts/decision-contracts.ts +38 -1
- package/templates/runtime/index.ts +1 -0
- package/templates/runtime/react/{runtime-public-api.test.tsx → app-shell/runtime-public-api.test.tsx} +29 -7
- package/templates/runtime/react/{settings-transfer.test.ts → app-shell/settings-transfer.test.ts} +4 -4
- package/templates/runtime/react/{settings-transfer.ts → app-shell/settings-transfer.ts} +3 -3
- package/templates/runtime/react/{toolbar-panel.test.tsx → app-shell/toolbar-panel.test.tsx} +2 -2
- package/templates/runtime/react/{toolbar-panel.tsx → app-shell/toolbar-panel.tsx} +3 -3
- package/templates/runtime/react/{toolcraft-app.integration.test.tsx → app-shell/toolcraft-app.integration.test.tsx} +6 -6
- package/templates/runtime/react/{toolcraft-app.test.tsx → app-shell/toolcraft-app.test.tsx} +3 -3
- package/templates/runtime/react/{toolcraft-app.tsx → app-shell/toolcraft-app.tsx} +11 -8
- package/templates/runtime/react/{toolcraft-root.test.tsx → app-shell/toolcraft-root.test.tsx} +1 -1
- package/templates/runtime/react/{toolcraft-root.tsx → app-shell/toolcraft-root.tsx} +5 -5
- package/templates/runtime/react/{use-toolcraft.ts → app-shell/use-toolcraft.ts} +1 -1
- package/templates/runtime/react/canvas/canvas-default-media-layer.tsx +81 -0
- package/templates/runtime/react/canvas/canvas-media-transform.ts +33 -0
- package/templates/runtime/react/canvas/canvas-shell-interactions.test.tsx +156 -0
- package/templates/runtime/react/canvas/canvas-shell-media-rendering.test.tsx +87 -0
- package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +102 -0
- package/templates/runtime/react/canvas/canvas-shell-test-utils.tsx +108 -0
- package/templates/runtime/react/canvas/canvas-shell-upload.test.tsx +106 -0
- package/templates/runtime/react/canvas/canvas-shell.tsx +146 -0
- package/templates/runtime/react/canvas/canvas-upload-routing.ts +84 -0
- package/templates/runtime/react/{media-file.ts → canvas/media-file.ts} +1 -1
- package/templates/runtime/react/canvas/use-canvas-drop-import.ts +145 -0
- package/templates/runtime/react/canvas/use-canvas-viewport-interactions.ts +227 -0
- package/templates/runtime/react/controls-panel/__tests__/control-conditions.test.ts +89 -0
- package/templates/runtime/react/{controls-panel-filedrop-reorder.test.tsx → controls-panel/__tests__/controls-panel-filedrop-reorder.test.tsx} +4 -4
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.actions-layout.test.tsx +91 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.collection-actions.test.tsx +213 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-layout.test.tsx +366 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-picker-models.test.tsx +169 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.compound-layout.test.tsx +160 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.control-visibility.test.tsx +352 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.curves.test.tsx +226 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.floating-shell.test.tsx +13 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.font-picker.test.tsx +302 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.footer-actions.test.tsx +401 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.gradient-control.test.tsx +139 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.help-policy.test.tsx +103 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.image-picker.test.tsx +16 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.inline-layout.test.tsx +290 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.keyframes.test.tsx +175 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.media-upload-files.test.tsx +121 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.media-upload-image.test.tsx +363 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.media-upload-multi-image.test.tsx +105 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.range-slider-editing.test.tsx +149 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.section-shell.test.tsx +138 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.select-layout.test.tsx +148 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.setup-controls.test.tsx +189 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.slider-rendering.test.tsx +317 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.state-reset.test.tsx +137 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.structure.test.tsx +66 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.text-input.test.tsx +205 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.vector-interaction.test.tsx +388 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.vector-layout.test.tsx +155 -0
- package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +172 -0
- package/templates/runtime/react/{control-conditions.ts → controls-panel/conditions/control-conditions.ts} +26 -3
- package/templates/runtime/react/{control-renderers.ts → controls-panel/control-renderers.ts} +2 -2
- package/templates/runtime/react/controls-panel/controls-panel.tsx +250 -0
- package/templates/runtime/react/controls-panel/keyframes/controls-panel-keyframes.tsx +265 -0
- package/templates/runtime/react/controls-panel/layout/controls-panel-collapse-storage.ts +131 -0
- package/templates/runtime/react/controls-panel/layout/controls-panel-help.tsx +132 -0
- package/templates/runtime/react/controls-panel/layout/controls-panel-inline-layout.tsx +311 -0
- package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +216 -0
- package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +401 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-action-renderer.tsx +137 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-basic-renderers.tsx +421 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-renderer.tsx +286 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-compound-renderers.tsx +366 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.tsx +173 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.test.ts +23 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.ts +55 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-settings-transfer-renderer.tsx +45 -0
- package/templates/runtime/react/controls-panel/testing/controls-panel-test-utils.tsx +300 -0
- package/templates/runtime/react/controls-panel/values/controls-panel-aspect-ratio-values.ts +94 -0
- package/templates/runtime/react/controls-panel/values/controls-panel-collection-values.ts +87 -0
- package/templates/runtime/react/controls-panel/values/controls-panel-color-values.ts +82 -0
- package/templates/runtime/react/controls-panel/values/controls-panel-font-values.ts +69 -0
- package/templates/runtime/react/controls-panel/values/controls-panel-spatial-values.ts +64 -0
- package/templates/runtime/react/controls-panel/values/controls-panel-value-labels.ts +93 -0
- package/templates/runtime/react/controls-panel/values/controls-panel-value-primitives.ts +30 -0
- package/templates/runtime/react/controls-panel/values/controls-panel-values.ts +7 -0
- package/templates/runtime/react/index.ts +13 -13
- package/templates/runtime/react/{layer-tree.ts → layers/layer-tree.ts} +1 -1
- package/templates/runtime/react/layers/layers-panel-drag-drop.test.tsx +190 -0
- package/templates/runtime/react/layers/layers-panel-editing.test.tsx +41 -0
- package/templates/runtime/react/layers/layers-panel-insert-targets.test.ts +87 -0
- package/templates/runtime/react/layers/layers-panel-insert-targets.ts +254 -0
- package/templates/runtime/react/layers/layers-panel-model.ts +15 -0
- package/templates/runtime/react/layers/layers-panel-rendering.test.tsx +43 -0
- package/templates/runtime/react/layers/layers-panel-reorder.ts +148 -0
- package/templates/runtime/react/layers/layers-panel-row.tsx +475 -0
- package/templates/runtime/react/layers/layers-panel-test-utils.tsx +135 -0
- package/templates/runtime/react/layers/layers-panel-visibility.test.tsx +126 -0
- package/templates/runtime/react/layers/layers-panel.tsx +349 -0
- package/templates/runtime/react/layers/use-layers-panel-drag-controller.ts +327 -0
- package/templates/runtime/react/{panel-host-types.ts → panel-host/panel-host-types.ts} +2 -2
- package/templates/runtime/react/{panel-host.test.tsx → panel-host/panel-host.test.tsx} +3 -3
- package/templates/runtime/react/{panel-host.tsx → panel-host/panel-host.tsx} +1 -1
- package/templates/runtime/react/timeline/timeline-easing-editor.tsx +409 -0
- package/templates/runtime/react/timeline/timeline-easing-geometry.ts +42 -0
- package/templates/runtime/react/timeline/timeline-easing-icons.tsx +135 -0
- package/templates/runtime/react/timeline/timeline-easing-model.test.ts +87 -0
- package/templates/runtime/react/timeline/timeline-easing-model.ts +271 -0
- package/templates/runtime/react/timeline/timeline-easing-popover-content.tsx +227 -0
- package/templates/runtime/react/timeline/timeline-easing-popover-layout.ts +69 -0
- package/templates/runtime/react/timeline/timeline-easing-popover.tsx +60 -0
- package/templates/runtime/react/timeline/timeline-event-targets.ts +19 -0
- package/templates/runtime/react/timeline/timeline-expanded-content.tsx +299 -0
- package/templates/runtime/react/timeline/timeline-icon-button.tsx +51 -0
- package/templates/runtime/react/timeline/timeline-keyframe-row.tsx +360 -0
- package/templates/runtime/react/timeline/timeline-keyframes.ts +23 -0
- package/templates/runtime/react/timeline/timeline-panel-floating-layout.test.tsx +328 -0
- package/templates/runtime/react/timeline/timeline-panel-header.tsx +404 -0
- package/templates/runtime/react/timeline/timeline-panel-keyframes.test.tsx +90 -0
- package/templates/runtime/react/timeline/timeline-panel-layout.ts +30 -0
- package/templates/runtime/react/timeline/timeline-panel-responsive-layout.ts +182 -0
- package/templates/runtime/react/timeline/timeline-panel-test-utils.tsx +88 -0
- package/templates/runtime/react/timeline/timeline-panel-transport.test.tsx +202 -0
- package/templates/runtime/react/timeline/timeline-panel-upload.test.tsx +53 -0
- package/templates/runtime/react/timeline/timeline-panel.tsx +449 -0
- package/templates/runtime/react/timeline/timeline-playback-hooks.ts +254 -0
- package/templates/runtime/schema/assembly-contract.ts +271 -0
- package/templates/runtime/schema/control-schema-normalization.ts +55 -0
- package/templates/runtime/schema/controls-panel-actions.ts +141 -0
- package/templates/runtime/schema/controls-panel-layout-groups.ts +144 -0
- package/templates/runtime/schema/controls-panel-normalization.ts +19 -0
- package/templates/runtime/schema/controls-panel-section-layout.ts +140 -0
- package/templates/runtime/schema/controls-panel-section-titles.ts +161 -0
- package/templates/runtime/schema/define-toolcraft-test-utils.ts +18 -0
- package/templates/runtime/schema/define-toolcraft.actions.test.ts +182 -0
- package/templates/runtime/schema/define-toolcraft.assembly.test.ts +246 -0
- package/templates/runtime/schema/define-toolcraft.canvas.test.ts +150 -0
- package/templates/runtime/schema/define-toolcraft.control-layout.test.ts +345 -0
- package/templates/runtime/schema/define-toolcraft.control-sections.test.ts +304 -0
- package/templates/runtime/schema/define-toolcraft.setup-canvas.test.ts +69 -0
- package/templates/runtime/schema/define-toolcraft.setup-persistence.test.ts +66 -0
- package/templates/runtime/schema/define-toolcraft.setup-render-scale-timeline.test.ts +116 -0
- package/templates/runtime/schema/define-toolcraft.setup-settings-transfer.test.ts +291 -0
- package/templates/runtime/schema/define-toolcraft.ts +24 -1449
- package/templates/runtime/schema/panels-schema-normalization.ts +49 -0
- package/templates/runtime/schema/runtime-section-titles.ts +1 -0
- package/templates/runtime/schema/runtime-setup-section.ts +282 -0
- package/templates/runtime/schema/schema-resolvers.ts +168 -0
- package/templates/runtime/schema/types.ts +17 -0
- package/templates/runtime/state/canvas-reducer.ts +106 -0
- package/templates/runtime/state/canvas-state.ts +209 -0
- package/templates/runtime/state/controls-reducer.ts +277 -0
- package/templates/runtime/state/history-patches.ts +174 -0
- package/templates/runtime/state/layer-state.ts +12 -0
- package/templates/runtime/state/layers-reducer.ts +318 -0
- package/templates/runtime/state/media-reducer.ts +283 -0
- package/templates/runtime/state/media-state.ts +105 -0
- package/templates/runtime/state/panels-reducer.ts +58 -0
- package/templates/runtime/state/persistence-merge.ts +53 -0
- package/templates/runtime/state/persistence-reader-canvas.ts +27 -0
- package/templates/runtime/state/persistence-reader-layers.ts +73 -0
- package/templates/runtime/state/persistence-reader-media.ts +82 -0
- package/templates/runtime/state/persistence-reader-panels.ts +56 -0
- package/templates/runtime/state/persistence-reader-primitives.ts +27 -0
- package/templates/runtime/state/persistence-reader-timeline.ts +145 -0
- package/templates/runtime/state/persistence-reader-values.ts +40 -0
- package/templates/runtime/state/persistence-readers.ts +72 -0
- package/templates/runtime/state/persistence-shared.ts +23 -0
- package/templates/runtime/state/persistence-snapshot.ts +57 -0
- package/templates/runtime/state/persistence.ts +16 -581
- package/templates/runtime/state/reducer-canvas-panels.test.ts +324 -0
- package/templates/runtime/state/reducer-controls.test.ts +158 -0
- package/templates/runtime/state/reducer-layers.test.ts +306 -0
- package/templates/runtime/state/reducer-media-import-sizing.test.ts +197 -0
- package/templates/runtime/state/reducer-media-reorder-history.test.ts +116 -0
- package/templates/runtime/state/reducer-media-reset.test.ts +249 -0
- package/templates/runtime/state/reducer-media-test-utils.ts +40 -0
- package/templates/runtime/state/reducer-media-transform.test.ts +52 -0
- package/templates/runtime/state/reducer-test-utils.ts +30 -0
- package/templates/runtime/state/reducer-timeline.test.ts +242 -0
- package/templates/runtime/state/reducer.ts +57 -1785
- package/templates/runtime/state/timeline-reducer.ts +412 -0
- package/templates/runtime/state/timeline-values.test.ts +45 -0
- package/templates/runtime/state/timeline-values.ts +45 -0
- package/templates/runtime/testing/performance-browser-policy.test.ts +81 -0
- package/templates/runtime/testing/performance-browser-policy.ts +102 -0
- package/templates/runtime/testing/performance-budget-validation.ts +84 -0
- package/templates/runtime/testing/performance-control-classification.test.ts +106 -0
- package/templates/runtime/testing/performance-control-classification.ts +101 -0
- package/templates/runtime/testing/performance-coverage-context.ts +44 -0
- package/templates/runtime/testing/performance-coverage-renderer-policy.ts +221 -0
- package/templates/runtime/testing/performance-coverage-scenario-policy.ts +147 -0
- package/templates/runtime/testing/performance-coverage-target-policy.ts +121 -0
- package/templates/runtime/testing/performance-coverage-validator.ts +34 -0
- package/templates/runtime/testing/performance-export-coverage.test.ts +234 -0
- package/templates/runtime/testing/performance-fixture-load-profile.test.ts +360 -0
- package/templates/runtime/testing/performance-fixture-media.test.ts +80 -0
- package/templates/runtime/testing/performance-fixture-stress.test.ts +314 -0
- package/templates/runtime/testing/performance-fixture-validation.ts +255 -0
- package/templates/runtime/testing/performance-fixture-values.ts +275 -0
- package/templates/runtime/testing/performance-fixture-workload.test.ts +140 -0
- package/templates/runtime/testing/performance-load-profile-validation.ts +467 -0
- package/templates/runtime/testing/performance-renderer-coverage.test.ts +325 -0
- package/templates/runtime/testing/performance-renderer-pipeline-coverage.test.ts +115 -0
- package/templates/runtime/testing/performance-renderer-pipeline-interactions.test.ts +201 -0
- package/templates/runtime/testing/performance-renderer-pipeline-passes.test.ts +247 -0
- package/templates/runtime/testing/performance-renderer-pipeline-validation.ts +360 -0
- package/templates/runtime/testing/performance-renderer-technique-validation.ts +329 -0
- package/templates/runtime/testing/performance-renderer-technique.test.ts +275 -0
- package/templates/runtime/testing/performance-renderer-validation.ts +2 -0
- package/templates/runtime/testing/performance-scenario-coverage.test.ts +303 -0
- package/templates/runtime/testing/performance-scenario-validation.ts +49 -0
- package/templates/runtime/testing/performance-schema-derived-workloads.test.ts +231 -0
- package/templates/runtime/testing/performance-schema-queries.ts +181 -0
- package/templates/runtime/testing/performance-test-fixtures/index.ts +4 -0
- package/templates/runtime/testing/performance-test-fixtures/renderer-pipelines.ts +322 -0
- package/templates/runtime/testing/performance-test-fixtures/scenarios.ts +172 -0
- package/templates/runtime/testing/performance-test-fixtures/schemas.ts +151 -0
- package/templates/runtime/testing/performance-test-fixtures/stress-fixtures.ts +133 -0
- package/templates/runtime/testing/performance-test-fixtures.ts +1 -0
- package/templates/runtime/testing/performance-types.ts +338 -0
- package/templates/runtime/testing/performance.ts +15 -2485
- package/templates/starter/AGENTS.md +41 -26
- package/templates/starter/LICENSE.md +18 -95
- package/templates/starter/NOTICE.md +3 -3
- package/templates/starter/docs/toolcraft/README.md +5 -5
- package/templates/starter/docs/toolcraft/acceptance-testing.md +36 -8
- package/templates/starter/docs/toolcraft/assembly-workflow.md +12 -15
- package/templates/starter/docs/toolcraft/component-rules.md +6 -2
- package/templates/starter/docs/toolcraft/core/layout.md +1 -0
- package/templates/starter/docs/toolcraft/core/performance.md +7 -4
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +11 -4
- package/templates/starter/docs/toolcraft/core/timeline-animation.md +1 -0
- package/templates/starter/docs/toolcraft/custom-controls.md +8 -1
- package/templates/starter/docs/toolcraft/decision-contract.md +26 -22
- package/templates/starter/docs/toolcraft/performance.md +42 -16
- package/templates/starter/docs/toolcraft/schema-reference.md +34 -23
- package/templates/starter/docs/toolcraft/workflow.md +25 -17
- package/templates/starter/e2e/app-browser-acceptance-evidence.spec.ts +111 -0
- package/templates/starter/e2e/app-browser-control-coverage.spec.ts +118 -0
- package/templates/starter/e2e/app-browser-performance-evidence.spec.ts +217 -0
- package/templates/starter/e2e/app-browser-performance-probe.spec.ts +322 -0
- package/templates/starter/e2e/app-browser-proof-session.spec.ts +196 -0
- package/templates/starter/e2e/app-browser-runtime-evidence.spec.ts +9 -0
- package/templates/starter/e2e/app-browser-runtime-provenance.spec.ts +240 -0
- package/templates/starter/e2e/app-browser-runtime-reporter.spec.ts +180 -0
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +310 -0
- package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +432 -0
- package/templates/starter/e2e/app-browser-shell.spec.ts +171 -0
- package/templates/starter/e2e/app-browser-timeline-layers-reference.spec.ts +70 -0
- package/templates/starter/e2e/app-performance.spec.ts +0 -491
- package/templates/starter/e2e/browser-acceptance-outcome-helpers.ts +97 -0
- package/templates/starter/e2e/browser-acceptance-transition-helpers.ts +74 -0
- package/templates/starter/e2e/browser-conditional-output-evidence-helpers.ts +230 -0
- package/templates/starter/e2e/browser-control-target-helpers.ts +109 -0
- package/templates/starter/e2e/browser-layer-evidence-helpers.ts +229 -0
- package/templates/starter/e2e/browser-proof-session.ts +392 -0
- package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +136 -0
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +290 -0
- package/templates/starter/e2e/browser-runtime-evidence.ts +27 -0
- package/templates/starter/e2e/browser-state-evidence-helpers.ts +205 -0
- package/templates/starter/e2e/browser-timeline-evidence-helpers.ts +319 -0
- package/templates/starter/e2e/canvas-handle-helpers.ts +86 -4
- package/templates/starter/e2e/export-artifact-helpers.ts +132 -0
- package/templates/starter/e2e/performance-budget-helpers.ts +159 -0
- package/templates/starter/e2e/performance-canvas-helpers.ts +123 -0
- package/templates/starter/e2e/performance-control-helpers.ts +3 -0
- package/templates/starter/e2e/performance-control-layout-helpers.ts +161 -0
- package/templates/starter/e2e/performance-fixture-helpers.ts +217 -0
- package/templates/starter/e2e/performance-frame-probe.ts +247 -0
- package/templates/starter/e2e/performance-helpers.ts +6 -854
- package/templates/starter/e2e/performance-interaction-measurement.ts +251 -0
- package/templates/starter/e2e/performance-measurement-evidence.ts +72 -0
- package/templates/starter/e2e/performance-output-action-helpers.spec.ts +43 -0
- package/templates/starter/e2e/performance-output-action-helpers.ts +109 -0
- package/templates/starter/e2e/performance-probe-helpers.ts +12 -0
- package/templates/starter/e2e/performance-render-scale-helpers.ts +55 -0
- package/templates/starter/e2e/performance-slider-helpers.ts +271 -0
- package/templates/starter/e2e/product-observable-helpers.ts +72 -17
- package/templates/starter/e2e/stable-outcome-helpers.ts +208 -0
- package/templates/starter/package.json +10 -5
- package/templates/starter/playwright.config.ts +5 -2
- package/templates/starter/scripts/check-ai-skills.mjs +14 -8
- package/templates/starter/scripts/check-ai-skills.test.mjs +64 -0
- package/templates/starter/scripts/check-toolcraft-code-health.mjs +108 -0
- package/templates/starter/scripts/check-toolcraft-code-health.test.mjs +213 -0
- package/templates/starter/scripts/check-toolcraft-docs.mjs +47 -34
- package/templates/starter/scripts/check-toolcraft-integrity.mjs +141 -144
- package/templates/starter/scripts/run-browser-performance.mjs +118 -0
- package/templates/starter/scripts/run-browser-performance.test.mjs +53 -0
- package/templates/starter/scripts/toolcraft-code-health-core.mjs +297 -0
- package/templates/starter/scripts/toolcraft-contract-doc-integrity.mjs +77 -0
- package/templates/starter/scripts/toolcraft-contract-doc-integrity.test.mjs +81 -0
- package/templates/starter/scripts/toolcraft-contract-manifest.json +113 -0
- package/templates/starter/scripts/toolcraft-contract-manifest.mjs +128 -0
- package/templates/starter/scripts/toolcraft-contract-manifest.test.mjs +72 -0
- package/templates/starter/scripts/toolcraft-integrity-manifest.mjs +172 -0
- package/templates/starter/scripts/toolcraft-integrity-policy.mjs +60 -0
- package/templates/starter/scripts/toolcraft-product-boundary-ast-utils.mjs +23 -0
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +264 -0
- package/templates/starter/scripts/toolcraft-product-boundary-test-fixtures.mjs +16 -0
- package/templates/starter/scripts/toolcraft-product-boundary.mjs +243 -0
- package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +289 -0
- package/templates/starter/scripts/toolcraft-product-dependency-graph.mjs +176 -0
- package/templates/starter/scripts/toolcraft-product-dependency-graph.test.mjs +196 -0
- package/templates/starter/scripts/toolcraft-product-dependency-resolution.mjs +279 -0
- package/templates/starter/scripts/toolcraft-product-evidence-boundary-ast.mjs +91 -0
- package/templates/starter/scripts/toolcraft-product-evidence-boundary.test.mjs +138 -0
- package/templates/starter/scripts/toolcraft-product-style-boundary.mjs +219 -0
- package/templates/starter/scripts/toolcraft-source-inventory.d.mts +46 -0
- package/templates/starter/scripts/toolcraft-source-inventory.mjs +187 -0
- package/templates/starter/scripts/toolcraft-source-inventory.test.mjs +132 -0
- package/templates/starter/scripts/toolcraft-source-ownership.mjs +167 -0
- package/templates/starter/scripts/toolcraft-source-ownership.test.mjs +113 -0
- package/templates/starter/scripts/toolcraft-static-string.mjs +104 -0
- package/templates/starter/scripts/toolcraft-verification-inventory.mjs +123 -0
- package/templates/starter/scripts/toolcraft-verification-receipt.d.mts +19 -0
- package/templates/starter/scripts/toolcraft-verification-receipt.mjs +284 -0
- package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +282 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-contract.d.mts +32 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-contract.mjs +88 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-contract.test.mjs +87 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.mjs +109 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.test.mjs +111 -0
- package/templates/starter/scripts/toolcraft-workflow-routes.mjs +314 -0
- package/templates/starter/scripts/toolcraft-workflow-routes.test.mjs +204 -0
- package/templates/starter/src/app/acceptance/actions.ts +30 -0
- package/templates/starter/src/app/acceptance/animation-intent.ts +184 -0
- package/templates/starter/src/app/acceptance/canvas-handle-acceptance.ts +70 -0
- package/templates/starter/src/app/acceptance/color-bank-labels.ts +164 -0
- package/templates/starter/src/app/acceptance/conditions.ts +80 -0
- package/templates/starter/src/app/acceptance/control-acceptance-context.ts +70 -0
- package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +271 -0
- package/templates/starter/src/app/acceptance/control-acceptance-kind-rules.ts +149 -0
- package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +189 -0
- package/templates/starter/src/app/acceptance/control-acceptance.ts +70 -0
- package/templates/starter/src/app/acceptance/control-component-rules.ts +277 -0
- package/templates/starter/src/app/acceptance/control-labels.ts +223 -0
- package/templates/starter/src/app/acceptance/control-layout-dependency-rules.ts +107 -0
- package/templates/starter/src/app/acceptance/control-layout-entity-rules.ts +104 -0
- package/templates/starter/src/app/acceptance/control-layout-model.ts +159 -0
- package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +164 -0
- package/templates/starter/src/app/acceptance/control-layout.ts +45 -0
- package/templates/starter/src/app/acceptance/control-order.ts +123 -0
- package/templates/starter/src/app/acceptance/control-parts.ts +47 -0
- package/templates/starter/src/app/acceptance/control-section-inventory.ts +183 -0
- package/templates/starter/src/app/acceptance/controls.ts +27 -0
- package/templates/starter/src/app/acceptance/coverage.ts +81 -0
- package/templates/starter/src/app/acceptance/custom-controls.ts +140 -0
- package/templates/starter/src/app/acceptance/inline-layout.ts +192 -0
- package/templates/starter/src/app/acceptance/media-upload.ts +55 -0
- package/templates/starter/src/app/acceptance/output-background-rules.ts +135 -0
- package/templates/starter/src/app/acceptance/output-export-actions.test.ts +76 -0
- package/templates/starter/src/app/acceptance/output-export-actions.ts +34 -0
- package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +46 -0
- package/templates/starter/src/app/acceptance/output-export-model.ts +146 -0
- package/templates/starter/src/app/acceptance/output-export.ts +45 -0
- package/templates/starter/src/app/acceptance/output-image-export-rules.ts +87 -0
- package/templates/starter/src/app/acceptance/reference-runtime-coverage.ts +96 -0
- package/templates/starter/src/app/acceptance/reference-runtime-feature-inventory.ts +171 -0
- package/templates/starter/src/app/acceptance/reference-runtime-study.ts +73 -0
- package/templates/starter/src/app/acceptance/reference-runtime-timeline.ts +291 -0
- package/templates/starter/src/app/acceptance/reference-runtime.ts +99 -0
- package/templates/starter/src/app/acceptance/runtime-coverage.ts +290 -0
- package/templates/starter/src/app/acceptance/runtime-setup.ts +117 -0
- package/templates/starter/src/app/acceptance/section-title-rules.ts +8 -0
- package/templates/starter/src/app/acceptance/sections.ts +6 -0
- package/templates/starter/src/app/acceptance/semantic.ts +63 -0
- package/templates/starter/src/app/acceptance/timeline-loop.ts +21 -0
- package/templates/starter/src/app/acceptance/types.ts +363 -0
- package/templates/starter/src/app/acceptance/validate-coverage.ts +217 -0
- package/templates/starter/src/app/acceptance/validation-pipeline.test.ts +56 -0
- package/templates/starter/src/app/acceptance/validation-pipeline.ts +60 -0
- package/templates/starter/src/app/acceptance/video-reference-study.ts +217 -0
- package/templates/starter/src/app/starter-acceptance-data.ts +21 -0
- package/templates/starter/src/app/starter-acceptance.animation-intent.test.ts +383 -0
- package/templates/starter/src/app/starter-acceptance.background-export.test.ts +349 -0
- package/templates/starter/src/app/starter-acceptance.base-coverage.test.ts +47 -0
- package/templates/starter/src/app/starter-acceptance.canvas-handles.test.ts +71 -0
- package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +199 -0
- package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +244 -0
- package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +63 -0
- package/templates/starter/src/app/starter-acceptance.control-label-context.test.ts +172 -0
- package/templates/starter/src/app/starter-acceptance.control-labels.test.ts +387 -0
- package/templates/starter/src/app/starter-acceptance.control-naming-rules.test.ts +215 -0
- package/templates/starter/src/app/starter-acceptance.control-order.test.ts +274 -0
- package/templates/starter/src/app/starter-acceptance.control-state.test.ts +275 -0
- package/templates/starter/src/app/starter-acceptance.curves-rules.test.ts +184 -0
- package/templates/starter/src/app/starter-acceptance.custom-control-collections.test.ts +200 -0
- package/templates/starter/src/app/starter-acceptance.custom-control-coverage.test.ts +111 -0
- package/templates/starter/src/app/starter-acceptance.custom-control-fit-check.test.ts +135 -0
- package/templates/starter/src/app/starter-acceptance.custom-control-valid.test.ts +74 -0
- package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +107 -0
- package/templates/starter/src/app/starter-acceptance.framework-boundary.test.ts +327 -0
- package/templates/starter/src/app/starter-acceptance.image-video-export-settings.test.ts +328 -0
- package/templates/starter/src/app/starter-acceptance.keyframes.test.ts +242 -0
- package/templates/starter/src/app/starter-acceptance.layers.test.ts +203 -0
- package/templates/starter/src/app/starter-acceptance.media-upload.fixtures.ts +110 -0
- package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +153 -0
- package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +97 -0
- package/templates/starter/src/app/starter-acceptance.persistence-coverage.test.ts +134 -0
- package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +202 -0
- package/templates/starter/src/app/starter-acceptance.product-readiness-rules.test.ts +14 -0
- package/templates/starter/src/app/starter-acceptance.product-readiness-test-utils.ts +25 -0
- package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +50 -0
- package/templates/starter/src/app/starter-acceptance.product-worklog.test.ts +27 -0
- package/templates/starter/src/app/starter-acceptance.range-slider-rules.test.ts +147 -0
- package/templates/starter/src/app/starter-acceptance.reference-custom-timeline.test.ts +197 -0
- package/templates/starter/src/app/starter-acceptance.reference-foundation.test.ts +304 -0
- package/templates/starter/src/app/starter-acceptance.reference-test-utils.ts +102 -0
- package/templates/starter/src/app/starter-acceptance.reference-timeline.test.ts +267 -0
- package/templates/starter/src/app/starter-acceptance.schema-test-utils.ts +116 -0
- package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +373 -0
- package/templates/starter/src/app/starter-acceptance.section-dependencies.test.ts +260 -0
- package/templates/starter/src/app/starter-acceptance.section-titles.test.ts +180 -0
- package/templates/starter/src/app/starter-acceptance.setup-readiness.test.ts +205 -0
- package/templates/starter/src/app/starter-acceptance.slider-rules.test.ts +470 -0
- package/templates/starter/src/app/starter-acceptance.slider-unit-rules.test.ts +61 -0
- package/templates/starter/src/app/starter-acceptance.source-test-utils.ts +43 -0
- package/templates/starter/src/app/starter-acceptance.test-utils.ts +21 -0
- package/templates/starter/src/app/starter-acceptance.text-control-kind.test.ts +89 -0
- package/templates/starter/src/app/starter-acceptance.timeline-playback.test.ts +264 -0
- package/templates/starter/src/app/starter-acceptance.timeline-test-utils.ts +56 -0
- package/templates/starter/src/app/starter-acceptance.toggle-pair-layout.test.ts +200 -0
- package/templates/starter/src/app/starter-acceptance.toggle-parameter-layout.test.ts +277 -0
- package/templates/starter/src/app/starter-acceptance.ts +151 -4576
- package/templates/starter/src/app/starter-acceptance.video-export-test-utils.ts +37 -0
- package/templates/starter/src/app/starter-acceptance.video-reference-study.test.ts +262 -0
- package/templates/starter/src/app/starter-acceptance.worklog-test-utils.ts +428 -0
- package/templates/starter/src/app/starter-acceptance.worklog.test.ts +283 -0
- package/templates/starter/src/app/starter-automated-runtime-evidence.test.ts +34 -0
- package/templates/starter/src/app/starter-composition.tsx +7 -0
- package/templates/starter/src/app/starter-performance-test-utils.ts +275 -0
- package/templates/starter/src/app/starter-performance.budgets.test.ts +374 -0
- package/templates/starter/src/app/starter-performance.gates.test.ts +128 -0
- package/templates/starter/src/app/starter-performance.renderer-source.test.ts +136 -0
- package/templates/starter/src/app/starter-performance.renderer-technique.test.ts +291 -0
- package/templates/starter/src/app/starter-performance.scenarios.test.ts +355 -0
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.test.ts +253 -0
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +243 -0
- package/templates/starter/src/routes/index.tsx +11 -2
- package/templates/starter/vite.config.ts +8 -0
- package/templates/ui/components/controls/color/color-control-types.ts +38 -0
- package/templates/ui/components/controls/color/color-control.tsx +19 -437
- package/templates/ui/components/controls/color/color-footer.tsx +272 -0
- package/templates/ui/components/controls/color/color-model-slider.tsx +271 -0
- package/templates/ui/components/controls/color/color-opacity-input.tsx +94 -0
- package/templates/ui/components/controls/color/color-surface.tsx +194 -0
- package/templates/ui/components/controls/color/color-value-control.tsx +159 -0
- package/templates/ui/components/controls/color/color-value-utils.ts +170 -0
- package/templates/ui/components/controls/color/index.ts +10 -3
- package/templates/ui/components/controls/color/palette-control-controller.ts +407 -0
- package/templates/ui/components/controls/color/palette-control-layout.ts +14 -0
- package/templates/ui/components/controls/color/palette-control-types.ts +50 -0
- package/templates/ui/components/controls/color/palette-control-view.tsx +174 -0
- package/templates/ui/components/controls/color/palette-control.tsx +6 -544
- package/templates/ui/components/controls/color/style-guide-color-picker-color-utils.ts +39 -0
- package/templates/ui/components/controls/color/style-guide-color-picker-controller.ts +370 -0
- package/templates/ui/components/controls/color/style-guide-color-picker-hue-handlers.ts +66 -0
- package/templates/ui/components/controls/color/style-guide-color-picker-interaction-state.ts +49 -0
- package/templates/ui/components/controls/color/style-guide-color-picker-interactions.ts +2 -2
- package/templates/ui/components/controls/color/style-guide-color-picker-model.ts +125 -0
- package/templates/ui/components/controls/color/style-guide-color-picker-parts.tsx +10 -707
- package/templates/ui/components/controls/color/style-guide-color-picker-surface-drag.ts +93 -0
- package/templates/ui/components/controls/color/style-guide-color-picker-surface-geometry.ts +114 -0
- package/templates/ui/components/controls/color/style-guide-color-picker-surface-preview.ts +45 -0
- package/templates/ui/components/controls/color/style-guide-color-picker-types.ts +84 -0
- package/templates/ui/components/controls/color/style-guide-color-picker-view.tsx +69 -0
- package/templates/ui/components/controls/color/style-guide-color-picker.tsx +6 -495
- package/templates/ui/components/controls/curves/curves-control.tsx +4 -3
- package/templates/ui/components/controls/file-drop/file-drop-control.tsx +39 -515
- package/templates/ui/components/controls/file-drop/file-drop-empty-state.tsx +35 -0
- package/templates/ui/components/controls/file-drop/file-drop-file-list.tsx +160 -0
- package/templates/ui/components/controls/file-drop/file-drop-image-grid.tsx +175 -0
- package/templates/ui/components/controls/file-drop/file-drop-model.tsx +170 -0
- package/templates/ui/components/controls/file-drop/file-drop-single-preview.tsx +63 -0
- package/templates/ui/components/controls/file-drop/file-drop-types.ts +48 -0
- package/templates/ui/components/controls/file-drop/index.ts +2 -2
- package/templates/ui/components/controls/font-picker/font-picker-control.tsx +133 -906
- package/templates/ui/components/controls/font-picker/font-picker-footer.tsx +116 -0
- package/templates/ui/components/controls/font-picker/font-picker-list.tsx +194 -0
- package/templates/ui/components/controls/font-picker/font-picker-popover-content.tsx +191 -0
- package/templates/ui/components/controls/font-picker/font-picker-value.ts +222 -0
- package/templates/ui/components/controls/font-picker/index.ts +4 -1
- package/templates/ui/components/controls/font-picker/use-font-picker-preview-pipeline.ts +163 -0
- package/templates/ui/components/controls/font-picker/use-font-picker-virtual-list.ts +293 -0
- package/templates/ui/components/controls/gradient/gradient-control-utils.ts +2 -25
- package/templates/ui/components/controls/gradient/gradient-control.tsx +4 -464
- package/templates/ui/components/controls/gradient/gradient-stops-controller.ts +332 -0
- package/templates/ui/components/controls/gradient/gradient-stops-track.tsx +159 -0
- package/templates/ui/components/controls/vector/vector-control-types.ts +26 -0
- package/templates/ui/components/controls/vector/vector-control.tsx +10 -486
- package/templates/ui/components/controls/vector/vector-pad-field.tsx +211 -0
- package/templates/ui/components/controls/vector/vector-pad-geometry.ts +61 -0
- package/templates/ui/components/controls/vector/vector-pad-parts.tsx +72 -0
- package/templates/ui/components/controls/vector/vector-pad-variants.ts +20 -0
- package/templates/ui/components/controls/vector/vector-size-field.tsx +104 -0
- package/templates/ui/components/controls/vector/vector-value.ts +46 -0
- package/templates/ui/components/primitives/slider/slider-discrete-state.ts +181 -0
- package/templates/ui/components/primitives/slider/slider-marker-policy.ts +52 -0
- package/templates/ui/components/primitives/slider/slider-pointer-dragging.ts +87 -0
- package/templates/ui/components/primitives/slider/slider-runtime.ts +109 -0
- package/templates/ui/components/primitives/slider/slider-types.ts +21 -0
- package/templates/ui/components/primitives/slider/slider.tsx +2 -403
- package/templates/runtime/react/canvas-shell.test.tsx +0 -554
- package/templates/runtime/react/canvas-shell.tsx +0 -585
- package/templates/runtime/react/controls-panel.test.tsx +0 -5446
- package/templates/runtime/react/controls-panel.tsx +0 -3220
- package/templates/runtime/react/layers-panel.test.tsx +0 -487
- package/templates/runtime/react/layers-panel.tsx +0 -1348
- package/templates/runtime/react/timeline-panel.test.tsx +0 -751
- package/templates/runtime/react/timeline-panel.tsx +0 -3078
- package/templates/runtime/schema/define-toolcraft.test.ts +0 -1742
- package/templates/runtime/state/reducer.test.ts +0 -1703
- package/templates/runtime/testing/performance.test.ts +0 -3081
- package/templates/starter/e2e/app-browser-acceptance.spec.ts +0 -785
- package/templates/starter/src/app/starter-acceptance.test.ts +0 -10068
- package/templates/starter/src/app/starter-performance.test.ts +0 -1583
- package/templates/ui/components/controls/color/style-guide-color-picker-logic.ts +0 -490
- /package/templates/runtime/react/{storage-key-migration.ts → app-shell/storage-key-migration.ts} +0 -0
- /package/templates/runtime/react/{theme-runtime.tsx → app-shell/theme-runtime.tsx} +0 -0
- /package/templates/runtime/react/{curve-geometry.test.ts → controls-panel/__tests__/curve-geometry.test.ts} +0 -0
- /package/templates/runtime/react/{panel-host-config.ts → panel-host/panel-host-config.ts} +0 -0
- /package/templates/runtime/react/{panel-host-geometry.test.ts → panel-host/panel-host-geometry.test.ts} +0 -0
- /package/templates/runtime/react/{panel-host-geometry.ts → panel-host/panel-host-geometry.ts} +0 -0
|
@@ -1,3081 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { defineToolcraft } from "../schema/define-toolcraft";
|
|
4
|
-
import {
|
|
5
|
-
collectToolcraftPerformanceSensitiveControls,
|
|
6
|
-
collectToolcraftUnclassifiedPerformanceControls,
|
|
7
|
-
defineToolcraftPerformance,
|
|
8
|
-
defaultToolcraftBrowserCheckPolicy,
|
|
9
|
-
type ToolcraftBrowserCheckPolicy,
|
|
10
|
-
validateToolcraftPerformanceCoverage,
|
|
11
|
-
} from "./performance";
|
|
12
|
-
|
|
13
|
-
const testSchema = defineToolcraft({
|
|
14
|
-
canvas: {
|
|
15
|
-
enabled: true,
|
|
16
|
-
sizing: { mode: "intrinsic-media" },
|
|
17
|
-
},
|
|
18
|
-
panels: {
|
|
19
|
-
controls: {
|
|
20
|
-
sections: [
|
|
21
|
-
{
|
|
22
|
-
controls: {
|
|
23
|
-
density: {
|
|
24
|
-
defaultValue: 4,
|
|
25
|
-
label: "Density",
|
|
26
|
-
max: 12,
|
|
27
|
-
min: 1,
|
|
28
|
-
performanceReason: "Density changes the amount of rendered output.",
|
|
29
|
-
performanceRole: "workload",
|
|
30
|
-
target: "render.density",
|
|
31
|
-
type: "slider",
|
|
32
|
-
},
|
|
33
|
-
mode: {
|
|
34
|
-
defaultValue: "soft",
|
|
35
|
-
label: "Mode",
|
|
36
|
-
options: [
|
|
37
|
-
{ label: "Soft", value: "soft" },
|
|
38
|
-
{ label: "Sharp", value: "sharp" },
|
|
39
|
-
],
|
|
40
|
-
performanceReason: "Mode changes rendering branches but not workload size.",
|
|
41
|
-
performanceRole: "responsiveness",
|
|
42
|
-
target: "render.mode",
|
|
43
|
-
type: "select",
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
title: "Runtime Controls",
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const ordinarySliderSchema = defineToolcraft({
|
|
54
|
-
canvas: {
|
|
55
|
-
enabled: true,
|
|
56
|
-
sizing: { mode: "intrinsic-media" },
|
|
57
|
-
},
|
|
58
|
-
panels: {
|
|
59
|
-
controls: {
|
|
60
|
-
sections: [
|
|
61
|
-
{
|
|
62
|
-
controls: {
|
|
63
|
-
opacity: {
|
|
64
|
-
defaultValue: 50,
|
|
65
|
-
label: "Opacity",
|
|
66
|
-
max: 100,
|
|
67
|
-
min: 0,
|
|
68
|
-
performanceReason: "Opacity changes a lightweight uniform value.",
|
|
69
|
-
performanceRole: "responsiveness",
|
|
70
|
-
target: "render.opacity",
|
|
71
|
-
type: "slider",
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
title: "Runtime Controls",
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
});
|
|
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
|
-
|
|
109
|
-
const largeTextSchema = defineToolcraft({
|
|
110
|
-
canvas: {
|
|
111
|
-
enabled: true,
|
|
112
|
-
sizing: { mode: "intrinsic-media" },
|
|
113
|
-
},
|
|
114
|
-
panels: {
|
|
115
|
-
controls: {
|
|
116
|
-
sections: [
|
|
117
|
-
{
|
|
118
|
-
controls: {
|
|
119
|
-
content: {
|
|
120
|
-
defaultValue: "",
|
|
121
|
-
label: "Content",
|
|
122
|
-
performanceReason: "Content length changes text layout and renderer workload.",
|
|
123
|
-
performanceRole: "workload",
|
|
124
|
-
target: "product.content",
|
|
125
|
-
type: "code",
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
title: "Runtime Controls",
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
const mediaUploadSchema = defineToolcraft({
|
|
136
|
-
canvas: {
|
|
137
|
-
enabled: true,
|
|
138
|
-
sizing: { mode: "intrinsic-media" },
|
|
139
|
-
upload: true,
|
|
140
|
-
},
|
|
141
|
-
panels: {
|
|
142
|
-
controls: {
|
|
143
|
-
sections: [
|
|
144
|
-
{
|
|
145
|
-
controls: {
|
|
146
|
-
source: {
|
|
147
|
-
defaultValue: null,
|
|
148
|
-
label: "Source image",
|
|
149
|
-
performanceReason: "Uploaded media dimensions change renderer workload.",
|
|
150
|
-
performanceRole: "responsiveness",
|
|
151
|
-
target: "source.image",
|
|
152
|
-
type: "fileDrop",
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
title: "Source",
|
|
156
|
-
},
|
|
157
|
-
],
|
|
158
|
-
title: "Runtime Controls",
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
function createViewportZoomStressScenario() {
|
|
164
|
-
return {
|
|
165
|
-
automated: true,
|
|
166
|
-
automatedTestName: "perf: detailed canvas zoom stays smooth",
|
|
167
|
-
browser: true,
|
|
168
|
-
browserTestName: "browser perf: detailed canvas zoom stays smooth",
|
|
169
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500, maxLongTaskMs: 80 },
|
|
170
|
-
expectedObservable:
|
|
171
|
-
"Zooming detailed product output does not shake the canvas or block frames.",
|
|
172
|
-
fixture: "detailed renderer zoom fixture",
|
|
173
|
-
id: "viewport-zoom-stress",
|
|
174
|
-
interaction: "viewport-zoom-stress" as const,
|
|
175
|
-
stress: true,
|
|
176
|
-
stressFixture: createCombinedRendererStressFixture(),
|
|
177
|
-
target: "canvas.viewport",
|
|
178
|
-
workload: false,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function createMaxValueStressFixture(value: unknown, reason = "Maximum workload value.") {
|
|
183
|
-
return {
|
|
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
|
-
},
|
|
193
|
-
reason,
|
|
194
|
-
value,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function createCustomStressFixture(
|
|
199
|
-
value: unknown = { density: 12 },
|
|
200
|
-
reason = "Combined renderer stress state.",
|
|
201
|
-
) {
|
|
202
|
-
return {
|
|
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
|
-
},
|
|
212
|
-
reason,
|
|
213
|
-
value,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function createCombinedRendererStressFixture() {
|
|
218
|
-
return createCustomStressFixture(
|
|
219
|
-
{ density: 12, zoom: "toolbar" },
|
|
220
|
-
"Stress checks must run with the densest product output visible.",
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function createLargeTextStressValue(): string {
|
|
225
|
-
return Array.from(
|
|
226
|
-
{ length: 1_000 },
|
|
227
|
-
(_, index) =>
|
|
228
|
-
`Line ${String(index + 1).padStart(4, "0")} performance stress text with enough glyphs to exercise layout.`,
|
|
229
|
-
).join("\n");
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function createLargeTextStressFixture() {
|
|
233
|
-
return {
|
|
234
|
-
kind: "large-text" as const,
|
|
235
|
-
minChars: 50_000,
|
|
236
|
-
minLines: 1_000,
|
|
237
|
-
reason: "Long multiline content is the heaviest realistic text workload.",
|
|
238
|
-
value: createLargeTextStressValue(),
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function createMediaStressFixture(
|
|
243
|
-
value: { height: number; width: number } = { height: 1080, width: 1920 },
|
|
244
|
-
reason = "Large uploaded source media is the heaviest realistic import fixture.",
|
|
245
|
-
) {
|
|
246
|
-
return {
|
|
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
|
-
},
|
|
256
|
-
reason,
|
|
257
|
-
value,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
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
|
-
|
|
302
|
-
function createTextRendererPipeline() {
|
|
303
|
-
return {
|
|
304
|
-
interactionInvalidation: [
|
|
305
|
-
{
|
|
306
|
-
interaction: "control-drag",
|
|
307
|
-
invalidates: ["text-layout"],
|
|
308
|
-
targets: ["render.density"],
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
interaction: "control-change",
|
|
312
|
-
invalidates: ["text-layout"],
|
|
313
|
-
targets: ["render.mode"],
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
interaction: "viewport-zoom",
|
|
317
|
-
invalidates: [],
|
|
318
|
-
mustNotInvalidate: ["text-layout"],
|
|
319
|
-
targets: ["canvas.viewport"],
|
|
320
|
-
},
|
|
321
|
-
],
|
|
322
|
-
passes: [
|
|
323
|
-
{
|
|
324
|
-
cacheKey: ["product.content", "render.density", "render.mode"],
|
|
325
|
-
id: "text-layout",
|
|
326
|
-
inputs: ["product.content", "render.density", "render.mode"],
|
|
327
|
-
invalidatedBy: ["product.content", "render.density", "render.mode"],
|
|
328
|
-
kind: "text-layout",
|
|
329
|
-
output: "preview",
|
|
330
|
-
quality: "full",
|
|
331
|
-
runsOn: "main",
|
|
332
|
-
},
|
|
333
|
-
],
|
|
334
|
-
} as const;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
function createAnimatedVectorRendererPipeline() {
|
|
338
|
-
return {
|
|
339
|
-
interactionInvalidation: [
|
|
340
|
-
{
|
|
341
|
-
interaction: "control-drag",
|
|
342
|
-
invalidates: ["vector-build"],
|
|
343
|
-
targets: ["render.density"],
|
|
344
|
-
},
|
|
345
|
-
{
|
|
346
|
-
interaction: "control-change",
|
|
347
|
-
invalidates: ["vector-build"],
|
|
348
|
-
targets: ["render.mode"],
|
|
349
|
-
},
|
|
350
|
-
{
|
|
351
|
-
interaction: "animation-frame",
|
|
352
|
-
invalidates: ["animation-composite"],
|
|
353
|
-
mustNotInvalidate: ["vector-build"],
|
|
354
|
-
targets: ["animation.time"],
|
|
355
|
-
},
|
|
356
|
-
{
|
|
357
|
-
interaction: "timeline-playback",
|
|
358
|
-
invalidates: ["animation-composite"],
|
|
359
|
-
mustNotInvalidate: ["vector-build"],
|
|
360
|
-
targets: ["timeline.currentTime"],
|
|
361
|
-
},
|
|
362
|
-
{
|
|
363
|
-
interaction: "timeline-scrub",
|
|
364
|
-
invalidates: ["animation-composite"],
|
|
365
|
-
mustNotInvalidate: ["vector-build"],
|
|
366
|
-
targets: ["timeline.currentTime"],
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
interaction: "viewport-drag",
|
|
370
|
-
invalidates: [],
|
|
371
|
-
mustNotInvalidate: ["vector-build", "animation-composite"],
|
|
372
|
-
targets: ["canvas.viewport"],
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
interaction: "viewport-zoom",
|
|
376
|
-
invalidates: [],
|
|
377
|
-
mustNotInvalidate: ["vector-build", "animation-composite"],
|
|
378
|
-
targets: ["canvas.viewport"],
|
|
379
|
-
},
|
|
380
|
-
],
|
|
381
|
-
passes: [
|
|
382
|
-
{
|
|
383
|
-
id: "vector-build",
|
|
384
|
-
inputs: ["render.density", "render.mode"],
|
|
385
|
-
invalidatedBy: ["render.density", "render.mode"],
|
|
386
|
-
kind: "vector-build",
|
|
387
|
-
output: "preview",
|
|
388
|
-
quality: "full",
|
|
389
|
-
runsOn: "main",
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
cacheKey: ["vector-build", "animation.time", "timeline.currentTime"],
|
|
393
|
-
id: "animation-composite",
|
|
394
|
-
inputs: ["vector-build", "animation.time", "timeline.currentTime"],
|
|
395
|
-
invalidatedBy: ["animation.time", "timeline.currentTime"],
|
|
396
|
-
kind: "composite",
|
|
397
|
-
output: "preview",
|
|
398
|
-
quality: "preview",
|
|
399
|
-
runsOn: "main",
|
|
400
|
-
},
|
|
401
|
-
],
|
|
402
|
-
} as const;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function createDenseCanvasPipeline() {
|
|
406
|
-
return {
|
|
407
|
-
interactionInvalidation: [
|
|
408
|
-
{
|
|
409
|
-
interaction: "control-drag",
|
|
410
|
-
invalidates: ["dense-background", "export-composite"],
|
|
411
|
-
targets: ["render.density"],
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
interaction: "control-change",
|
|
415
|
-
invalidates: ["export-composite"],
|
|
416
|
-
targets: ["render.mode"],
|
|
417
|
-
},
|
|
418
|
-
{
|
|
419
|
-
interaction: "viewport-zoom",
|
|
420
|
-
invalidates: [],
|
|
421
|
-
mustNotInvalidate: ["dense-background", "export-composite"],
|
|
422
|
-
targets: ["canvas.viewport"],
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
interaction: "export",
|
|
426
|
-
invalidates: ["export-composite"],
|
|
427
|
-
mustNotInvalidate: ["dense-background"],
|
|
428
|
-
targets: ["export.image.resolution"],
|
|
429
|
-
},
|
|
430
|
-
],
|
|
431
|
-
passes: [
|
|
432
|
-
{
|
|
433
|
-
cacheKey: ["render.density", "canvas.size", "canvas.renderScale"],
|
|
434
|
-
id: "dense-background",
|
|
435
|
-
inputs: ["render.density", "canvas.size", "canvas.renderScale"],
|
|
436
|
-
invalidatedBy: ["render.density", "canvas.size", "canvas.renderScale"],
|
|
437
|
-
kind: "rasterize",
|
|
438
|
-
output: "preview",
|
|
439
|
-
quality: "retina",
|
|
440
|
-
runsOn: "worker",
|
|
441
|
-
},
|
|
442
|
-
{
|
|
443
|
-
id: "semantic-foreground",
|
|
444
|
-
inputs: ["product.geometry"],
|
|
445
|
-
invalidatedBy: ["product.geometry"],
|
|
446
|
-
kind: "vector-build",
|
|
447
|
-
output: "preview",
|
|
448
|
-
quality: "full",
|
|
449
|
-
runsOn: "main",
|
|
450
|
-
},
|
|
451
|
-
{
|
|
452
|
-
cacheKey: ["dense-background", "semantic-foreground", "export.image.resolution"],
|
|
453
|
-
id: "export-composite",
|
|
454
|
-
inputs: ["dense-background", "semantic-foreground", "export.image.resolution"],
|
|
455
|
-
invalidatedBy: ["dense-background", "semantic-foreground", "export.image.resolution"],
|
|
456
|
-
kind: "composite",
|
|
457
|
-
output: "export",
|
|
458
|
-
quality: "export",
|
|
459
|
-
runsOn: "export-only",
|
|
460
|
-
},
|
|
461
|
-
],
|
|
462
|
-
} as const;
|
|
463
|
-
}
|
|
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
|
-
|
|
520
|
-
function createPixelRendererPipeline() {
|
|
521
|
-
return {
|
|
522
|
-
interactionInvalidation: [
|
|
523
|
-
{
|
|
524
|
-
interaction: "control-drag",
|
|
525
|
-
invalidates: ["pixel-transform"],
|
|
526
|
-
targets: ["render.density"],
|
|
527
|
-
},
|
|
528
|
-
{
|
|
529
|
-
interaction: "control-change",
|
|
530
|
-
invalidates: ["pixel-transform"],
|
|
531
|
-
targets: ["render.mode"],
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
interaction: "viewport-zoom",
|
|
535
|
-
invalidates: [],
|
|
536
|
-
mustNotInvalidate: ["pixel-transform"],
|
|
537
|
-
targets: ["canvas.viewport"],
|
|
538
|
-
},
|
|
539
|
-
],
|
|
540
|
-
passes: [
|
|
541
|
-
{
|
|
542
|
-
cacheKey: ["render.density", "render.mode", "canvas.size", "canvas.renderScale"],
|
|
543
|
-
id: "pixel-transform",
|
|
544
|
-
inputs: ["render.density", "render.mode", "canvas.size", "canvas.renderScale"],
|
|
545
|
-
invalidatedBy: ["render.density", "render.mode", "canvas.size", "canvas.renderScale"],
|
|
546
|
-
kind: "pixel-transform",
|
|
547
|
-
output: "preview",
|
|
548
|
-
quality: "retina",
|
|
549
|
-
runsOn: "worker",
|
|
550
|
-
},
|
|
551
|
-
],
|
|
552
|
-
} as const;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function createMediaRendererPipeline() {
|
|
556
|
-
return {
|
|
557
|
-
interactionInvalidation: [
|
|
558
|
-
{
|
|
559
|
-
interaction: "media-import",
|
|
560
|
-
invalidates: ["source-decode", "media-preprocess"],
|
|
561
|
-
targets: ["source.image"],
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
interaction: "control-drag",
|
|
565
|
-
invalidates: ["effect-composite"],
|
|
566
|
-
mustNotInvalidate: ["source-decode", "media-preprocess"],
|
|
567
|
-
targets: ["render.density"],
|
|
568
|
-
},
|
|
569
|
-
{
|
|
570
|
-
interaction: "viewport-zoom",
|
|
571
|
-
invalidates: [],
|
|
572
|
-
mustNotInvalidate: ["source-decode", "media-preprocess", "effect-composite"],
|
|
573
|
-
targets: ["canvas.viewport"],
|
|
574
|
-
},
|
|
575
|
-
],
|
|
576
|
-
passes: [
|
|
577
|
-
{
|
|
578
|
-
cacheKey: ["source.image.id", "source.image.width", "source.image.height"],
|
|
579
|
-
id: "source-decode",
|
|
580
|
-
inputs: ["source.image"],
|
|
581
|
-
invalidatedBy: ["source.image"],
|
|
582
|
-
kind: "decode",
|
|
583
|
-
output: "source",
|
|
584
|
-
quality: "full",
|
|
585
|
-
runsOn: "worker",
|
|
586
|
-
},
|
|
587
|
-
{
|
|
588
|
-
cacheKey: ["source-decode", "source.image.id"],
|
|
589
|
-
id: "media-preprocess",
|
|
590
|
-
inputs: ["source-decode"],
|
|
591
|
-
invalidatedBy: ["source-decode"],
|
|
592
|
-
kind: "preprocess",
|
|
593
|
-
output: "intermediate",
|
|
594
|
-
quality: "full",
|
|
595
|
-
runsOn: "worker-or-gpu",
|
|
596
|
-
},
|
|
597
|
-
{
|
|
598
|
-
cacheKey: ["media-preprocess", "render.density"],
|
|
599
|
-
id: "effect-composite",
|
|
600
|
-
inputs: ["media-preprocess", "render.density"],
|
|
601
|
-
invalidatedBy: ["media-preprocess", "render.density"],
|
|
602
|
-
kind: "composite",
|
|
603
|
-
output: "preview",
|
|
604
|
-
quality: "retina",
|
|
605
|
-
runsOn: "gpu",
|
|
606
|
-
},
|
|
607
|
-
],
|
|
608
|
-
} as const;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
function createTextRendererScenarios(
|
|
612
|
-
options: { includeStressPreview?: boolean; includeViewportZoomStress?: boolean } = {},
|
|
613
|
-
) {
|
|
614
|
-
const includeStressPreview = options.includeStressPreview ?? true;
|
|
615
|
-
const includeViewportZoomStress = options.includeViewportZoomStress ?? true;
|
|
616
|
-
|
|
617
|
-
return [
|
|
618
|
-
{
|
|
619
|
-
automated: true,
|
|
620
|
-
automatedTestName: "perf: text preview render stays under budget",
|
|
621
|
-
browser: true,
|
|
622
|
-
browserTestName: "browser perf: text preview render stays under budget",
|
|
623
|
-
budget: { maxLongTaskMs: 80, maxPreviewMs: 1000 },
|
|
624
|
-
expectedObservable: "Text preview renders crisply without freezing.",
|
|
625
|
-
fixture: "native-resolution glyph output fixture",
|
|
626
|
-
id: "text-preview-render",
|
|
627
|
-
interaction: "preview-render" as const,
|
|
628
|
-
stress: includeStressPreview,
|
|
629
|
-
...(includeStressPreview
|
|
630
|
-
? { stressFixture: createCombinedRendererStressFixture() }
|
|
631
|
-
: {}),
|
|
632
|
-
workload: false,
|
|
633
|
-
},
|
|
634
|
-
{
|
|
635
|
-
automated: true,
|
|
636
|
-
automatedTestName: "perf: density drag stays responsive",
|
|
637
|
-
browser: true,
|
|
638
|
-
browserTestName: "browser perf: density drag stays responsive",
|
|
639
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
640
|
-
controlLabel: "Density",
|
|
641
|
-
expectedObservable: "Dragging Density updates text output without blocking the UI.",
|
|
642
|
-
fixture: "runtime density fixture",
|
|
643
|
-
id: "density-drag",
|
|
644
|
-
interaction: "control-drag" as const,
|
|
645
|
-
stressFixture: createMaxValueStressFixture(
|
|
646
|
-
12,
|
|
647
|
-
"Density max is the heaviest glyph output fixture.",
|
|
648
|
-
),
|
|
649
|
-
target: "render.density",
|
|
650
|
-
values: { default: 4, max: 12, min: 1 },
|
|
651
|
-
workloadFixture: createLargeTextStressFixture(),
|
|
652
|
-
workload: true,
|
|
653
|
-
},
|
|
654
|
-
{
|
|
655
|
-
automated: true,
|
|
656
|
-
automatedTestName: "perf: mode change stays responsive",
|
|
657
|
-
browser: true,
|
|
658
|
-
browserTestName: "browser perf: mode change stays responsive",
|
|
659
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
660
|
-
controlLabel: "Mode",
|
|
661
|
-
expectedObservable: "Changing Mode updates text output without blocking the UI.",
|
|
662
|
-
fixture: "runtime mode fixture",
|
|
663
|
-
id: "mode-change",
|
|
664
|
-
interaction: "control-change" as const,
|
|
665
|
-
target: "render.mode",
|
|
666
|
-
values: { default: "soft", max: "sharp", min: "soft" },
|
|
667
|
-
workload: false,
|
|
668
|
-
},
|
|
669
|
-
{
|
|
670
|
-
automated: true,
|
|
671
|
-
automatedTestName: "perf: viewport stays stable",
|
|
672
|
-
browser: true,
|
|
673
|
-
browserTestName: "browser perf: viewport stays stable",
|
|
674
|
-
budget: { maxFrameGapMs: 80 },
|
|
675
|
-
expectedObservable: "Viewport remains stable.",
|
|
676
|
-
fixture: "native-resolution glyph output fixture",
|
|
677
|
-
id: "viewport-stability",
|
|
678
|
-
interaction: "viewport-stability" as const,
|
|
679
|
-
workload: false,
|
|
680
|
-
},
|
|
681
|
-
...(includeViewportZoomStress ? [createViewportZoomStressScenario()] : []),
|
|
682
|
-
];
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
function createAnimationFrameScenario() {
|
|
686
|
-
return {
|
|
687
|
-
automated: true,
|
|
688
|
-
automatedTestName: "perf: animation frame loop stays smooth",
|
|
689
|
-
browser: true,
|
|
690
|
-
browserTestName: "browser perf: animation frame loop stays smooth",
|
|
691
|
-
budget: { maxFrameGapMs: 80, maxLongTaskMs: 80 },
|
|
692
|
-
expectedObservable: "Animated renderer advances without long frame gaps.",
|
|
693
|
-
fixture: "animated renderer fixture",
|
|
694
|
-
id: "animation-frame-loop",
|
|
695
|
-
interaction: "animation-frame" as const,
|
|
696
|
-
stress: true,
|
|
697
|
-
stressFixture: createCustomStressFixture(
|
|
698
|
-
{ animation: "fastest", density: 12 },
|
|
699
|
-
"Animation stress must sample the heaviest animated output.",
|
|
700
|
-
),
|
|
701
|
-
workload: false,
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
function createAnimationViewportDragScenario() {
|
|
706
|
-
return {
|
|
707
|
-
automated: true,
|
|
708
|
-
automatedTestName: "perf: animated canvas drag stays smooth",
|
|
709
|
-
browser: true,
|
|
710
|
-
browserTestName: "browser perf: animated canvas drag stays smooth",
|
|
711
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500, maxLongTaskMs: 80 },
|
|
712
|
-
expectedObservable: "Animated renderer stays smooth while the canvas viewport is dragged.",
|
|
713
|
-
fixture: "animated renderer canvas drag fixture",
|
|
714
|
-
id: "animation-viewport-drag",
|
|
715
|
-
interaction: "animation-viewport-drag" as const,
|
|
716
|
-
stress: true,
|
|
717
|
-
stressFixture: createCustomStressFixture(
|
|
718
|
-
{ animation: "fastest", density: 12 },
|
|
719
|
-
"Animated viewport drag must run while the heaviest animated output is visible.",
|
|
720
|
-
),
|
|
721
|
-
target: "canvas.viewport",
|
|
722
|
-
workload: false,
|
|
723
|
-
};
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
function createAnimatedVectorRendererConfig(options: { includeViewportDrag: boolean }) {
|
|
727
|
-
return defineToolcraftPerformance({
|
|
728
|
-
rendererStrategy: "svg",
|
|
729
|
-
rendererTechnique: {
|
|
730
|
-
exportRenderer: "svg",
|
|
731
|
-
fidelityRisks: ["animated vector output must stay crisp while panning"],
|
|
732
|
-
performanceRisks: ["dense vector animation can jank while the viewport moves"],
|
|
733
|
-
previewRenderer: "svg",
|
|
734
|
-
productRepresentation: "vector",
|
|
735
|
-
rendererStrategy: "svg",
|
|
736
|
-
rendererWorkload: "vector-output",
|
|
737
|
-
sourceRepresentation: "procedural-data",
|
|
738
|
-
whyNotAlternativeStrategies: ["svg preserves semantic vector output for this fixture"],
|
|
739
|
-
},
|
|
740
|
-
rendererWorkload: "vector-output",
|
|
741
|
-
rendererPipeline: createAnimatedVectorRendererPipeline(),
|
|
742
|
-
scenarios: [
|
|
743
|
-
...createTextRendererScenarios(),
|
|
744
|
-
createAnimationFrameScenario(),
|
|
745
|
-
...(options.includeViewportDrag ? [createAnimationViewportDragScenario()] : []),
|
|
746
|
-
],
|
|
747
|
-
usesCustomRenderer: true,
|
|
748
|
-
workloadTargets: ["render.density"],
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
|
|
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
|
-
|
|
818
|
-
it("collects performance-sensitive controls from schema", () => {
|
|
819
|
-
expect(collectToolcraftPerformanceSensitiveControls(testSchema)).toEqual([
|
|
820
|
-
expect.objectContaining({
|
|
821
|
-
controlId: "density",
|
|
822
|
-
target: "render.density",
|
|
823
|
-
}),
|
|
824
|
-
]);
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
it("does not classify every slider as workload-sensitive", () => {
|
|
828
|
-
expect(collectToolcraftPerformanceSensitiveControls(ordinarySliderSchema)).toEqual([]);
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
it("collects explicit workload controls even when labels do not match fallback keywords", () => {
|
|
832
|
-
const explicitWorkloadSchema = defineToolcraft({
|
|
833
|
-
canvas: {
|
|
834
|
-
enabled: true,
|
|
835
|
-
sizing: { mode: "intrinsic-media" },
|
|
836
|
-
},
|
|
837
|
-
panels: {
|
|
838
|
-
controls: {
|
|
839
|
-
sections: [
|
|
840
|
-
{
|
|
841
|
-
controls: {
|
|
842
|
-
complexity: {
|
|
843
|
-
defaultValue: 4,
|
|
844
|
-
label: "Complexity",
|
|
845
|
-
max: 12,
|
|
846
|
-
min: 1,
|
|
847
|
-
performanceReason: "Complexity changes the number of render passes.",
|
|
848
|
-
performanceRole: "workload",
|
|
849
|
-
target: "render.complexity",
|
|
850
|
-
type: "slider",
|
|
851
|
-
},
|
|
852
|
-
},
|
|
853
|
-
},
|
|
854
|
-
],
|
|
855
|
-
title: "Runtime Controls",
|
|
856
|
-
},
|
|
857
|
-
},
|
|
858
|
-
});
|
|
859
|
-
|
|
860
|
-
expect(collectToolcraftPerformanceSensitiveControls(explicitWorkloadSchema)).toEqual([
|
|
861
|
-
expect.objectContaining({
|
|
862
|
-
controlId: "complexity",
|
|
863
|
-
target: "render.complexity",
|
|
864
|
-
}),
|
|
865
|
-
]);
|
|
866
|
-
});
|
|
867
|
-
|
|
868
|
-
it("collects visible controls without explicit performance classification", () => {
|
|
869
|
-
const unclassifiedSchema = defineToolcraft({
|
|
870
|
-
canvas: {
|
|
871
|
-
enabled: true,
|
|
872
|
-
renderScale: true,
|
|
873
|
-
sizing: { mode: "intrinsic-media" },
|
|
874
|
-
},
|
|
875
|
-
panels: {
|
|
876
|
-
controls: {
|
|
877
|
-
sections: [
|
|
878
|
-
{
|
|
879
|
-
controls: {
|
|
880
|
-
mode: {
|
|
881
|
-
defaultValue: "soft",
|
|
882
|
-
label: "Mode",
|
|
883
|
-
options: [
|
|
884
|
-
{ label: "Soft", value: "soft" },
|
|
885
|
-
{ label: "Sharp", value: "sharp" },
|
|
886
|
-
],
|
|
887
|
-
target: "render.mode",
|
|
888
|
-
type: "select",
|
|
889
|
-
},
|
|
890
|
-
},
|
|
891
|
-
},
|
|
892
|
-
],
|
|
893
|
-
title: "Runtime Controls",
|
|
894
|
-
},
|
|
895
|
-
timeline: { mode: "playback" },
|
|
896
|
-
},
|
|
897
|
-
});
|
|
898
|
-
|
|
899
|
-
expect(collectToolcraftUnclassifiedPerformanceControls(unclassifiedSchema)).toEqual([
|
|
900
|
-
expect.objectContaining({
|
|
901
|
-
controlId: "mode",
|
|
902
|
-
target: "render.mode",
|
|
903
|
-
}),
|
|
904
|
-
]);
|
|
905
|
-
});
|
|
906
|
-
|
|
907
|
-
it("allows ordinary sliders to use lightweight responsiveness coverage", () => {
|
|
908
|
-
const config = defineToolcraftPerformance({
|
|
909
|
-
rendererStrategy: "none",
|
|
910
|
-
rendererWorkload: "none",
|
|
911
|
-
scenarios: [
|
|
912
|
-
{
|
|
913
|
-
automated: true,
|
|
914
|
-
automatedTestName: "perf: opacity drag stays responsive",
|
|
915
|
-
browser: true,
|
|
916
|
-
browserTestName: "browser perf: opacity drag stays responsive",
|
|
917
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
918
|
-
controlLabel: "Opacity",
|
|
919
|
-
expectedObservable: "Dragging Opacity stays responsive.",
|
|
920
|
-
fixture: "opacity responsiveness fixture",
|
|
921
|
-
id: "opacity-drag",
|
|
922
|
-
interaction: "control-drag",
|
|
923
|
-
target: "render.opacity",
|
|
924
|
-
workload: false,
|
|
925
|
-
},
|
|
926
|
-
],
|
|
927
|
-
usesCustomRenderer: false,
|
|
928
|
-
workloadTargets: [],
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toEqual([]);
|
|
932
|
-
});
|
|
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
|
-
|
|
992
|
-
it("requires performance-sensitive controls to be listed in workloadTargets", () => {
|
|
993
|
-
const config = defineToolcraftPerformance({
|
|
994
|
-
rendererStrategy: "none",
|
|
995
|
-
rendererWorkload: "none",
|
|
996
|
-
scenarios: [
|
|
997
|
-
{
|
|
998
|
-
automated: true,
|
|
999
|
-
automatedTestName: "perf: density drag stays responsive",
|
|
1000
|
-
browser: true,
|
|
1001
|
-
browserTestName: "browser perf: density drag stays responsive",
|
|
1002
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1003
|
-
controlLabel: "Density",
|
|
1004
|
-
expectedObservable: "Dragging Density stays responsive.",
|
|
1005
|
-
fixture: "runtime density fixture",
|
|
1006
|
-
id: "density-drag",
|
|
1007
|
-
interaction: "control-drag",
|
|
1008
|
-
target: "render.density",
|
|
1009
|
-
workload: false,
|
|
1010
|
-
},
|
|
1011
|
-
{
|
|
1012
|
-
automated: true,
|
|
1013
|
-
automatedTestName: "perf: mode change stays responsive",
|
|
1014
|
-
browser: true,
|
|
1015
|
-
browserTestName: "browser perf: mode change stays responsive",
|
|
1016
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1017
|
-
controlLabel: "Mode",
|
|
1018
|
-
expectedObservable: "Changing Mode updates the product without blocking the UI.",
|
|
1019
|
-
fixture: "runtime mode fixture",
|
|
1020
|
-
id: "mode-change",
|
|
1021
|
-
interaction: "control-change",
|
|
1022
|
-
target: "render.mode",
|
|
1023
|
-
values: { default: "soft", max: "sharp", min: "soft" },
|
|
1024
|
-
workload: false,
|
|
1025
|
-
},
|
|
1026
|
-
],
|
|
1027
|
-
usesCustomRenderer: false,
|
|
1028
|
-
workloadTargets: [],
|
|
1029
|
-
});
|
|
1030
|
-
|
|
1031
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1032
|
-
"render.density is performance-sensitive and must be listed in workloadTargets with min/default/max workload coverage.",
|
|
1033
|
-
);
|
|
1034
|
-
});
|
|
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
|
-
|
|
1084
|
-
it("rejects responsiveness role on semantically workload controls", () => {
|
|
1085
|
-
const misclassifiedSchema = defineToolcraft({
|
|
1086
|
-
canvas: {
|
|
1087
|
-
enabled: true,
|
|
1088
|
-
sizing: { mode: "intrinsic-media" },
|
|
1089
|
-
},
|
|
1090
|
-
panels: {
|
|
1091
|
-
controls: {
|
|
1092
|
-
sections: [
|
|
1093
|
-
{
|
|
1094
|
-
controls: {
|
|
1095
|
-
density: {
|
|
1096
|
-
defaultValue: 4,
|
|
1097
|
-
label: "Density",
|
|
1098
|
-
max: 12,
|
|
1099
|
-
min: 1,
|
|
1100
|
-
performanceReason: "AI incorrectly treated density as lightweight.",
|
|
1101
|
-
performanceRole: "responsiveness",
|
|
1102
|
-
target: "render.density",
|
|
1103
|
-
type: "slider",
|
|
1104
|
-
},
|
|
1105
|
-
},
|
|
1106
|
-
},
|
|
1107
|
-
],
|
|
1108
|
-
title: "Runtime Controls",
|
|
1109
|
-
},
|
|
1110
|
-
},
|
|
1111
|
-
});
|
|
1112
|
-
const config = defineToolcraftPerformance({
|
|
1113
|
-
rendererStrategy: "none",
|
|
1114
|
-
rendererWorkload: "none",
|
|
1115
|
-
scenarios: [
|
|
1116
|
-
{
|
|
1117
|
-
automated: true,
|
|
1118
|
-
automatedTestName: "perf: density drag stays responsive",
|
|
1119
|
-
browser: true,
|
|
1120
|
-
browserTestName: "browser perf: density drag stays responsive",
|
|
1121
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1122
|
-
controlLabel: "Density",
|
|
1123
|
-
expectedObservable: "Dragging Density updates the product without blocking the UI.",
|
|
1124
|
-
fixture: "runtime density fixture",
|
|
1125
|
-
id: "density-drag",
|
|
1126
|
-
interaction: "control-drag",
|
|
1127
|
-
target: "render.density",
|
|
1128
|
-
values: { default: 4, max: 12, min: 1 },
|
|
1129
|
-
workload: false,
|
|
1130
|
-
},
|
|
1131
|
-
],
|
|
1132
|
-
usesCustomRenderer: false,
|
|
1133
|
-
workloadTargets: [],
|
|
1134
|
-
});
|
|
1135
|
-
|
|
1136
|
-
expect(validateToolcraftPerformanceCoverage(misclassifiedSchema, config)).toEqual(
|
|
1137
|
-
expect.arrayContaining([
|
|
1138
|
-
'density (render.density) looks performance-sensitive but declares performanceRole "responsiveness". Use performanceRole "workload" with workloadTargets and min/default/max coverage, or rename/restructure the control if it is truly lightweight.',
|
|
1139
|
-
"render.density is performance-sensitive and must be listed in workloadTargets with min/default/max workload coverage.",
|
|
1140
|
-
]),
|
|
1141
|
-
);
|
|
1142
|
-
});
|
|
1143
|
-
|
|
1144
|
-
it("validates coverage without app-local helper defaults", () => {
|
|
1145
|
-
const config = defineToolcraftPerformance({
|
|
1146
|
-
rendererStrategy: "none",
|
|
1147
|
-
rendererWorkload: "none",
|
|
1148
|
-
scenarios: [
|
|
1149
|
-
{
|
|
1150
|
-
automated: true,
|
|
1151
|
-
automatedTestName: "perf: density drag stays responsive",
|
|
1152
|
-
browser: true,
|
|
1153
|
-
browserTestName: "browser perf: density drag stays responsive",
|
|
1154
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1155
|
-
controlLabel: "Density",
|
|
1156
|
-
expectedObservable: "Dragging Density updates the product without blocking the UI.",
|
|
1157
|
-
fixture: "runtime density fixture",
|
|
1158
|
-
id: "density-drag",
|
|
1159
|
-
interaction: "control-drag",
|
|
1160
|
-
stressFixture: createMaxValueStressFixture(
|
|
1161
|
-
12,
|
|
1162
|
-
"Density max is the heaviest rendered output fixture.",
|
|
1163
|
-
),
|
|
1164
|
-
target: "render.density",
|
|
1165
|
-
values: { default: 4, max: 12, min: 1 },
|
|
1166
|
-
workload: true,
|
|
1167
|
-
},
|
|
1168
|
-
{
|
|
1169
|
-
automated: true,
|
|
1170
|
-
automatedTestName: "perf: mode change stays responsive",
|
|
1171
|
-
browser: true,
|
|
1172
|
-
browserTestName: "browser perf: mode change stays responsive",
|
|
1173
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1174
|
-
controlLabel: "Mode",
|
|
1175
|
-
expectedObservable: "Changing Mode updates the product without blocking the UI.",
|
|
1176
|
-
fixture: "runtime mode fixture",
|
|
1177
|
-
id: "mode-change",
|
|
1178
|
-
interaction: "control-change",
|
|
1179
|
-
target: "render.mode",
|
|
1180
|
-
values: { default: "soft", max: "sharp", min: "soft" },
|
|
1181
|
-
workload: false,
|
|
1182
|
-
},
|
|
1183
|
-
],
|
|
1184
|
-
usesCustomRenderer: false,
|
|
1185
|
-
workloadTargets: ["render.density"],
|
|
1186
|
-
});
|
|
1187
|
-
|
|
1188
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
1189
|
-
});
|
|
1190
|
-
|
|
1191
|
-
it("requires workload scenarios to declare a reusable stress fixture", () => {
|
|
1192
|
-
const config = defineToolcraftPerformance({
|
|
1193
|
-
rendererStrategy: "none",
|
|
1194
|
-
rendererWorkload: "none",
|
|
1195
|
-
scenarios: [
|
|
1196
|
-
{
|
|
1197
|
-
automated: true,
|
|
1198
|
-
automatedTestName: "perf: content input stays responsive",
|
|
1199
|
-
browser: true,
|
|
1200
|
-
browserTestName: "browser perf: content input stays responsive",
|
|
1201
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1202
|
-
controlLabel: "Content",
|
|
1203
|
-
expectedObservable: "Large text content updates without blocking the UI.",
|
|
1204
|
-
fixture: "large text content fixture",
|
|
1205
|
-
id: "content-change",
|
|
1206
|
-
interaction: "control-change",
|
|
1207
|
-
target: "product.content",
|
|
1208
|
-
values: { default: "", max: createLargeTextStressValue(), min: "" },
|
|
1209
|
-
workload: true,
|
|
1210
|
-
},
|
|
1211
|
-
],
|
|
1212
|
-
usesCustomRenderer: false,
|
|
1213
|
-
workloadTargets: ["product.content"],
|
|
1214
|
-
});
|
|
1215
|
-
|
|
1216
|
-
expect(validateToolcraftPerformanceCoverage(largeTextSchema, config)).toContain(
|
|
1217
|
-
"content-change workload scenario must declare stressFixture with the real heaviest value used by browser performance tests.",
|
|
1218
|
-
);
|
|
1219
|
-
});
|
|
1220
|
-
|
|
1221
|
-
it("requires stress scenarios to declare an exact stress fixture value", () => {
|
|
1222
|
-
const config = defineToolcraftPerformance({
|
|
1223
|
-
rendererStrategy: "none",
|
|
1224
|
-
rendererWorkload: "none",
|
|
1225
|
-
scenarios: [
|
|
1226
|
-
{
|
|
1227
|
-
automated: true,
|
|
1228
|
-
automatedTestName: "perf: preview render stays under budget",
|
|
1229
|
-
browser: true,
|
|
1230
|
-
browserTestName: "browser perf: preview render stays under budget",
|
|
1231
|
-
budget: { maxLongTaskMs: 80, maxPreviewMs: 1000 },
|
|
1232
|
-
expectedObservable: "Preview renders the heaviest output without freezing.",
|
|
1233
|
-
fixture: "combined heavy renderer fixture",
|
|
1234
|
-
id: "preview-render",
|
|
1235
|
-
interaction: "preview-render",
|
|
1236
|
-
stress: true,
|
|
1237
|
-
workload: false,
|
|
1238
|
-
},
|
|
1239
|
-
],
|
|
1240
|
-
usesCustomRenderer: false,
|
|
1241
|
-
workloadTargets: [],
|
|
1242
|
-
});
|
|
1243
|
-
|
|
1244
|
-
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toContain(
|
|
1245
|
-
"preview-render stress scenario must declare stressFixture with the real heaviest state used by browser performance tests.",
|
|
1246
|
-
);
|
|
1247
|
-
});
|
|
1248
|
-
|
|
1249
|
-
it("rejects stress fixtures without a browser-applicable value", () => {
|
|
1250
|
-
const config = defineToolcraftPerformance({
|
|
1251
|
-
rendererStrategy: "none",
|
|
1252
|
-
rendererWorkload: "none",
|
|
1253
|
-
scenarios: [
|
|
1254
|
-
{
|
|
1255
|
-
automated: true,
|
|
1256
|
-
automatedTestName: "perf: preview render stays under budget",
|
|
1257
|
-
browser: true,
|
|
1258
|
-
browserTestName: "browser perf: preview render stays under budget",
|
|
1259
|
-
budget: { maxLongTaskMs: 80, maxPreviewMs: 1000 },
|
|
1260
|
-
expectedObservable: "Preview renders the heaviest output without freezing.",
|
|
1261
|
-
fixture: "combined heavy renderer fixture",
|
|
1262
|
-
id: "preview-render",
|
|
1263
|
-
interaction: "preview-render",
|
|
1264
|
-
stress: true,
|
|
1265
|
-
stressFixture: {
|
|
1266
|
-
kind: "custom",
|
|
1267
|
-
reason: "Combined renderer stress state.",
|
|
1268
|
-
},
|
|
1269
|
-
workload: false,
|
|
1270
|
-
},
|
|
1271
|
-
],
|
|
1272
|
-
usesCustomRenderer: false,
|
|
1273
|
-
workloadTargets: [],
|
|
1274
|
-
});
|
|
1275
|
-
|
|
1276
|
-
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toContain(
|
|
1277
|
-
"preview-render stressFixture must include value so browser tests can apply the exact heavy state.",
|
|
1278
|
-
);
|
|
1279
|
-
});
|
|
1280
|
-
|
|
1281
|
-
it("requires custom stress fixtures to be object-shaped heavy states", () => {
|
|
1282
|
-
const config = defineToolcraftPerformance({
|
|
1283
|
-
rendererStrategy: "none",
|
|
1284
|
-
rendererWorkload: "none",
|
|
1285
|
-
scenarios: [
|
|
1286
|
-
{
|
|
1287
|
-
automated: true,
|
|
1288
|
-
automatedTestName: "perf: preview render stays under budget",
|
|
1289
|
-
browser: true,
|
|
1290
|
-
browserTestName: "browser perf: preview render stays under budget",
|
|
1291
|
-
budget: { maxLongTaskMs: 80, maxPreviewMs: 1000 },
|
|
1292
|
-
expectedObservable: "Preview renders the heaviest output without freezing.",
|
|
1293
|
-
fixture: "combined heavy renderer fixture",
|
|
1294
|
-
id: "preview-render",
|
|
1295
|
-
interaction: "preview-render",
|
|
1296
|
-
stress: true,
|
|
1297
|
-
stressFixture: {
|
|
1298
|
-
kind: "custom",
|
|
1299
|
-
reason: "Combined renderer stress state.",
|
|
1300
|
-
value: 12,
|
|
1301
|
-
},
|
|
1302
|
-
workload: false,
|
|
1303
|
-
},
|
|
1304
|
-
],
|
|
1305
|
-
usesCustomRenderer: false,
|
|
1306
|
-
workloadTargets: [],
|
|
1307
|
-
});
|
|
1308
|
-
|
|
1309
|
-
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toContain(
|
|
1310
|
-
"preview-render custom stressFixture.value must be an object with one key per heavy state part so browser tests can apply every key.",
|
|
1311
|
-
);
|
|
1312
|
-
});
|
|
1313
|
-
|
|
1314
|
-
it("rejects empty custom stress fixture objects", () => {
|
|
1315
|
-
const config = defineToolcraftPerformance({
|
|
1316
|
-
rendererStrategy: "none",
|
|
1317
|
-
rendererWorkload: "none",
|
|
1318
|
-
scenarios: [
|
|
1319
|
-
{
|
|
1320
|
-
automated: true,
|
|
1321
|
-
automatedTestName: "perf: preview render stays under budget",
|
|
1322
|
-
browser: true,
|
|
1323
|
-
browserTestName: "browser perf: preview render stays under budget",
|
|
1324
|
-
budget: { maxLongTaskMs: 80, maxPreviewMs: 1000 },
|
|
1325
|
-
expectedObservable: "Preview renders the heaviest output without freezing.",
|
|
1326
|
-
fixture: "combined heavy renderer fixture",
|
|
1327
|
-
id: "preview-render",
|
|
1328
|
-
interaction: "preview-render",
|
|
1329
|
-
stress: true,
|
|
1330
|
-
stressFixture: {
|
|
1331
|
-
kind: "custom",
|
|
1332
|
-
reason: "Combined renderer stress state.",
|
|
1333
|
-
value: {},
|
|
1334
|
-
},
|
|
1335
|
-
workload: false,
|
|
1336
|
-
},
|
|
1337
|
-
],
|
|
1338
|
-
usesCustomRenderer: false,
|
|
1339
|
-
workloadTargets: [],
|
|
1340
|
-
});
|
|
1341
|
-
|
|
1342
|
-
expect(validateToolcraftPerformanceCoverage(ordinarySliderSchema, config)).toContain(
|
|
1343
|
-
"preview-render custom stressFixture.value must include at least one heavy state key.",
|
|
1344
|
-
);
|
|
1345
|
-
});
|
|
1346
|
-
|
|
1347
|
-
it("rejects short stress fixtures for large text workload scenarios", () => {
|
|
1348
|
-
const config = defineToolcraftPerformance({
|
|
1349
|
-
rendererStrategy: "none",
|
|
1350
|
-
rendererWorkload: "none",
|
|
1351
|
-
scenarios: [
|
|
1352
|
-
{
|
|
1353
|
-
automated: true,
|
|
1354
|
-
automatedTestName: "perf: content input stays responsive",
|
|
1355
|
-
browser: true,
|
|
1356
|
-
browserTestName: "browser perf: content input stays responsive",
|
|
1357
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1358
|
-
controlLabel: "Content",
|
|
1359
|
-
expectedObservable: "Large text content updates without blocking the UI.",
|
|
1360
|
-
fixture: "large text content fixture",
|
|
1361
|
-
id: "content-change",
|
|
1362
|
-
interaction: "control-change",
|
|
1363
|
-
stressFixture: {
|
|
1364
|
-
kind: "large-text",
|
|
1365
|
-
minChars: 50_000,
|
|
1366
|
-
minLines: 1_000,
|
|
1367
|
-
reason: "Long multiline content is the heaviest realistic text workload.",
|
|
1368
|
-
value: "short text",
|
|
1369
|
-
},
|
|
1370
|
-
target: "product.content",
|
|
1371
|
-
values: { default: "", max: "short text", min: "" },
|
|
1372
|
-
workload: true,
|
|
1373
|
-
},
|
|
1374
|
-
],
|
|
1375
|
-
usesCustomRenderer: false,
|
|
1376
|
-
workloadTargets: ["product.content"],
|
|
1377
|
-
});
|
|
1378
|
-
|
|
1379
|
-
expect(validateToolcraftPerformanceCoverage(largeTextSchema, config)).toEqual(
|
|
1380
|
-
expect.arrayContaining([
|
|
1381
|
-
"content-change large-text stressFixture.value must contain at least 50000 characters.",
|
|
1382
|
-
"content-change large-text stressFixture.value must contain at least 1000 lines.",
|
|
1383
|
-
]),
|
|
1384
|
-
);
|
|
1385
|
-
});
|
|
1386
|
-
|
|
1387
|
-
it("rejects short large-text values hidden inside custom fixtures", () => {
|
|
1388
|
-
const config = defineToolcraftPerformance({
|
|
1389
|
-
rendererStrategy: "none",
|
|
1390
|
-
rendererWorkload: "none",
|
|
1391
|
-
scenarios: [
|
|
1392
|
-
{
|
|
1393
|
-
automated: true,
|
|
1394
|
-
automatedTestName: "perf: preview render stays under budget",
|
|
1395
|
-
browser: true,
|
|
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,
|
|
1412
|
-
},
|
|
1413
|
-
],
|
|
1414
|
-
usesCustomRenderer: false,
|
|
1415
|
-
workloadTargets: [],
|
|
1416
|
-
});
|
|
1417
|
-
|
|
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
|
-
);
|
|
1424
|
-
});
|
|
1425
|
-
|
|
1426
|
-
it("rejects custom render scale fixtures that are not concrete high-resolution values", () => {
|
|
1427
|
-
const config = defineToolcraftPerformance({
|
|
1428
|
-
rendererStrategy: "none",
|
|
1429
|
-
rendererWorkload: "none",
|
|
1430
|
-
scenarios: [
|
|
1431
|
-
{
|
|
1432
|
-
automated: true,
|
|
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",
|
|
1845
|
-
browser: true,
|
|
1846
|
-
browserTestName: "browser perf: density drag stays responsive",
|
|
1847
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1848
|
-
controlLabel: "Density",
|
|
1849
|
-
expectedObservable: "Dragging Density updates text output without blocking the UI.",
|
|
1850
|
-
fixture: "runtime density fixture",
|
|
1851
|
-
id: "density-drag",
|
|
1852
|
-
interaction: "control-drag",
|
|
1853
|
-
stressFixture: createMaxValueStressFixture(
|
|
1854
|
-
12,
|
|
1855
|
-
"Density max is the heaviest control value.",
|
|
1856
|
-
),
|
|
1857
|
-
target: "render.density",
|
|
1858
|
-
values: { default: 4, max: 12, min: 1 },
|
|
1859
|
-
workload: true,
|
|
1860
|
-
},
|
|
1861
|
-
],
|
|
1862
|
-
usesCustomRenderer: true,
|
|
1863
|
-
workloadTargets: ["render.density"],
|
|
1864
|
-
});
|
|
1865
|
-
|
|
1866
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1867
|
-
"density-drag workload control scenario must declare workloadFixture for the app's heavy baseline state; stressFixture covers the control value only.",
|
|
1868
|
-
);
|
|
1869
|
-
});
|
|
1870
|
-
|
|
1871
|
-
it("rejects toy media workload fixtures for control-drag baselines", () => {
|
|
1872
|
-
const config = defineToolcraftPerformance({
|
|
1873
|
-
rendererStrategy: "webgl",
|
|
1874
|
-
rendererWorkload: "pixel-output",
|
|
1875
|
-
scenarios: [
|
|
1876
|
-
{
|
|
1877
|
-
automated: true,
|
|
1878
|
-
automatedTestName: "perf: density drag stays responsive",
|
|
1879
|
-
browser: true,
|
|
1880
|
-
browserTestName: "browser perf: density drag stays responsive",
|
|
1881
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1882
|
-
controlLabel: "Density",
|
|
1883
|
-
expectedObservable: "Dragging Density updates source media output without blocking the UI.",
|
|
1884
|
-
fixture: "runtime density fixture",
|
|
1885
|
-
id: "density-drag",
|
|
1886
|
-
interaction: "control-drag",
|
|
1887
|
-
stressFixture: createMaxValueStressFixture(
|
|
1888
|
-
12,
|
|
1889
|
-
"Density max is the heaviest control value.",
|
|
1890
|
-
),
|
|
1891
|
-
target: "render.density",
|
|
1892
|
-
values: { default: 4, max: 12, min: 1 },
|
|
1893
|
-
workloadFixture: createMediaStressFixture(
|
|
1894
|
-
{ height: 480, width: 640 },
|
|
1895
|
-
"This small source should be rejected as too light.",
|
|
1896
|
-
),
|
|
1897
|
-
workload: true,
|
|
1898
|
-
},
|
|
1899
|
-
],
|
|
1900
|
-
usesCustomRenderer: true,
|
|
1901
|
-
workloadTargets: ["render.density"],
|
|
1902
|
-
});
|
|
1903
|
-
|
|
1904
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1905
|
-
"density-drag media workloadFixture.value must be at least 1920x1080-equivalent; received 640x480.",
|
|
1906
|
-
);
|
|
1907
|
-
});
|
|
1908
|
-
|
|
1909
|
-
it("accepts workload controls with separate control stress and app baseline fixtures", () => {
|
|
1910
|
-
const config = defineToolcraftPerformance({
|
|
1911
|
-
rendererStrategy: "canvas-2d",
|
|
1912
|
-
rendererTechnique: {
|
|
1913
|
-
exportRenderer: "canvas-2d",
|
|
1914
|
-
fidelityRisks: ["text output must stay crisp"],
|
|
1915
|
-
performanceRisks: ["density changes can relayout text"],
|
|
1916
|
-
previewRenderer: "canvas-2d",
|
|
1917
|
-
productRepresentation: "text",
|
|
1918
|
-
rendererStrategy: "canvas-2d",
|
|
1919
|
-
rendererWorkload: "text-output",
|
|
1920
|
-
sourceRepresentation: "dom-text",
|
|
1921
|
-
whyNotAlternativeStrategies: ["dom output cannot preserve export composition"],
|
|
1922
|
-
},
|
|
1923
|
-
rendererWorkload: "text-output",
|
|
1924
|
-
rendererPipeline: createTextRendererPipeline(),
|
|
1925
|
-
scenarios: createTextRendererScenarios(),
|
|
1926
|
-
usesCustomRenderer: true,
|
|
1927
|
-
workloadTargets: ["render.density"],
|
|
1928
|
-
});
|
|
1929
|
-
|
|
1930
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
1931
|
-
});
|
|
1932
|
-
|
|
1933
|
-
it("rejects workload fixtures that are not paired with scenario stress fixtures", () => {
|
|
1934
|
-
const config = defineToolcraftPerformance({
|
|
1935
|
-
rendererStrategy: "none",
|
|
1936
|
-
rendererWorkload: "none",
|
|
1937
|
-
scenarios: [
|
|
1938
|
-
{
|
|
1939
|
-
automated: true,
|
|
1940
|
-
automatedTestName: "perf: preview baseline stays responsive",
|
|
1941
|
-
browser: true,
|
|
1942
|
-
browserTestName: "browser perf: preview baseline stays responsive",
|
|
1943
|
-
budget: { maxPreviewMs: 1000 },
|
|
1944
|
-
expectedObservable: "Preview renders inside a heavy baseline.",
|
|
1945
|
-
fixture: "baseline fixture",
|
|
1946
|
-
id: "preview-heavy-baseline",
|
|
1947
|
-
interaction: "preview-render",
|
|
1948
|
-
workload: false,
|
|
1949
|
-
workloadFixture: createLargeTextStressFixture(),
|
|
1950
|
-
},
|
|
1951
|
-
],
|
|
1952
|
-
usesCustomRenderer: false,
|
|
1953
|
-
workloadTargets: [],
|
|
1954
|
-
});
|
|
1955
|
-
|
|
1956
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
1957
|
-
"preview-heavy-baseline workloadFixture must be paired with stressFixture so tests apply a heavy baseline and then the measured scenario value.",
|
|
1958
|
-
);
|
|
1959
|
-
});
|
|
1960
|
-
|
|
1961
|
-
it("rejects media-import scenarios that use toy source dimensions", () => {
|
|
1962
|
-
const config = defineToolcraftPerformance({
|
|
1963
|
-
rendererStrategy: "none",
|
|
1964
|
-
rendererWorkload: "none",
|
|
1965
|
-
scenarios: [
|
|
1966
|
-
{
|
|
1967
|
-
automated: true,
|
|
1968
|
-
automatedTestName: "perf: source image import stays responsive",
|
|
1969
|
-
browser: true,
|
|
1970
|
-
browserTestName: "browser perf: source image import stays responsive",
|
|
1971
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
1972
|
-
expectedObservable: "Importing source media does not block the app.",
|
|
1973
|
-
fixture: "large source image fixture",
|
|
1974
|
-
id: "source-media-import",
|
|
1975
|
-
interaction: "media-import",
|
|
1976
|
-
stressFixture: createMediaStressFixture(
|
|
1977
|
-
{ height: 480, width: 640 },
|
|
1978
|
-
"This small fixture should be rejected as too light.",
|
|
1979
|
-
),
|
|
1980
|
-
target: "source.image",
|
|
1981
|
-
workload: true,
|
|
1982
|
-
},
|
|
1983
|
-
],
|
|
1984
|
-
usesCustomRenderer: false,
|
|
1985
|
-
workloadTargets: [],
|
|
1986
|
-
});
|
|
1987
|
-
|
|
1988
|
-
expect(validateToolcraftPerformanceCoverage(mediaUploadSchema, config)).toContain(
|
|
1989
|
-
"source-media-import media stressFixture.value must be at least 1920x1080-equivalent; received 640x480.",
|
|
1990
|
-
);
|
|
1991
|
-
});
|
|
1992
|
-
|
|
1993
|
-
it("requires media-import scenarios to be workload media fixtures", () => {
|
|
1994
|
-
const config = defineToolcraftPerformance({
|
|
1995
|
-
rendererStrategy: "none",
|
|
1996
|
-
rendererWorkload: "none",
|
|
1997
|
-
scenarios: [
|
|
1998
|
-
{
|
|
1999
|
-
automated: true,
|
|
2000
|
-
automatedTestName: "perf: source image import stays responsive",
|
|
2001
|
-
browser: true,
|
|
2002
|
-
browserTestName: "browser perf: source image import stays responsive",
|
|
2003
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
2004
|
-
expectedObservable: "Importing source media does not block the app.",
|
|
2005
|
-
fixture: "large source image fixture",
|
|
2006
|
-
id: "source-media-import",
|
|
2007
|
-
interaction: "media-import",
|
|
2008
|
-
stressFixture: createCustomStressFixture(
|
|
2009
|
-
{ height: 1080, width: 1920 },
|
|
2010
|
-
"Wrong fixture kind for media import.",
|
|
2011
|
-
),
|
|
2012
|
-
target: "source.image",
|
|
2013
|
-
workload: false,
|
|
2014
|
-
},
|
|
2015
|
-
],
|
|
2016
|
-
usesCustomRenderer: false,
|
|
2017
|
-
workloadTargets: [],
|
|
2018
|
-
});
|
|
2019
|
-
|
|
2020
|
-
expect(validateToolcraftPerformanceCoverage(mediaUploadSchema, config)).toEqual(
|
|
2021
|
-
expect.arrayContaining([
|
|
2022
|
-
"source-media-import media-import scenario must set workload true because decoded source size changes renderer workload.",
|
|
2023
|
-
'source-media-import media-import scenario must use stressFixture.kind "media" with a realistic uploaded source size.',
|
|
2024
|
-
]),
|
|
2025
|
-
);
|
|
2026
|
-
});
|
|
2027
|
-
|
|
2028
|
-
it("rejects visible controls without performance scenarios", () => {
|
|
2029
|
-
const config = defineToolcraftPerformance({
|
|
2030
|
-
rendererStrategy: "none",
|
|
2031
|
-
rendererWorkload: "none",
|
|
2032
|
-
scenarios: [
|
|
2033
|
-
{
|
|
2034
|
-
automated: true,
|
|
2035
|
-
automatedTestName: "perf: density drag stays responsive",
|
|
2036
|
-
browser: true,
|
|
2037
|
-
browserTestName: "browser perf: density drag stays responsive",
|
|
2038
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500 },
|
|
2039
|
-
controlLabel: "Density",
|
|
2040
|
-
expectedObservable: "Dragging Density updates the product without blocking the UI.",
|
|
2041
|
-
fixture: "runtime density fixture",
|
|
2042
|
-
id: "density-drag",
|
|
2043
|
-
interaction: "control-drag",
|
|
2044
|
-
target: "render.density",
|
|
2045
|
-
values: { default: 4, max: 12, min: 1 },
|
|
2046
|
-
workload: true,
|
|
2047
|
-
},
|
|
2048
|
-
],
|
|
2049
|
-
usesCustomRenderer: false,
|
|
2050
|
-
workloadTargets: ["render.density"],
|
|
2051
|
-
});
|
|
2052
|
-
|
|
2053
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2054
|
-
"render.mode must have a performance scenario because every visible control can affect app responsiveness.",
|
|
2055
|
-
);
|
|
2056
|
-
});
|
|
2057
|
-
|
|
2058
|
-
it("allows text-output renderers without forcing a GPU strategy", () => {
|
|
2059
|
-
const config = defineToolcraftPerformance({
|
|
2060
|
-
rendererStrategy: "canvas-2d",
|
|
2061
|
-
rendererTechnique: {
|
|
2062
|
-
exportRenderer: "canvas-2d",
|
|
2063
|
-
fidelityRisks: ["glyph metrics must stay crisp at product output size"],
|
|
2064
|
-
performanceRisks: ["large glyph grids can be expensive during drag"],
|
|
2065
|
-
previewRenderer: "canvas-2d",
|
|
2066
|
-
productRepresentation: "text",
|
|
2067
|
-
rendererStrategy: "canvas-2d",
|
|
2068
|
-
rendererWorkload: "text-output",
|
|
2069
|
-
sourceRepresentation: "dom-text",
|
|
2070
|
-
whyNotAlternativeStrategies: ["webgl texture output would rasterize text too early"],
|
|
2071
|
-
},
|
|
2072
|
-
rendererWorkload: "text-output",
|
|
2073
|
-
rendererPipeline: createTextRendererPipeline(),
|
|
2074
|
-
scenarios: createTextRendererScenarios(),
|
|
2075
|
-
usesCustomRenderer: true,
|
|
2076
|
-
workloadTargets: ["render.density"],
|
|
2077
|
-
});
|
|
2078
|
-
|
|
2079
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
2080
|
-
});
|
|
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
|
-
|
|
2125
|
-
it("requires animated custom renderers to test viewport dragging during animation", () => {
|
|
2126
|
-
const config = createAnimatedVectorRendererConfig({ includeViewportDrag: false });
|
|
2127
|
-
|
|
2128
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2129
|
-
"Animated custom renderers must include an animation-viewport-drag performance scenario that samples frames while physically moving the canvas viewport.",
|
|
2130
|
-
);
|
|
2131
|
-
});
|
|
2132
|
-
|
|
2133
|
-
it("requires detail-heavy custom renderers to test real viewport zoom stress", () => {
|
|
2134
|
-
const config = defineToolcraftPerformance({
|
|
2135
|
-
rendererStrategy: "canvas-2d",
|
|
2136
|
-
rendererTechnique: {
|
|
2137
|
-
exportRenderer: "canvas-2d",
|
|
2138
|
-
fidelityRisks: ["glyph metrics must stay crisp at product output size"],
|
|
2139
|
-
performanceRisks: ["large glyph grids can shake or jank during canvas zoom"],
|
|
2140
|
-
previewRenderer: "canvas-2d",
|
|
2141
|
-
productRepresentation: "text",
|
|
2142
|
-
rendererStrategy: "canvas-2d",
|
|
2143
|
-
rendererWorkload: "text-output",
|
|
2144
|
-
sourceRepresentation: "dom-text",
|
|
2145
|
-
whyNotAlternativeStrategies: ["webgl texture output would rasterize text too early"],
|
|
2146
|
-
},
|
|
2147
|
-
rendererWorkload: "text-output",
|
|
2148
|
-
scenarios: createTextRendererScenarios({ includeViewportZoomStress: false }),
|
|
2149
|
-
usesCustomRenderer: true,
|
|
2150
|
-
workloadTargets: ["render.density"],
|
|
2151
|
-
});
|
|
2152
|
-
|
|
2153
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2154
|
-
"Detail-heavy or animated custom renderers must include a viewport-zoom-stress performance scenario that uses real zoom controls while sampling frame gaps and long tasks.",
|
|
2155
|
-
);
|
|
2156
|
-
});
|
|
2157
|
-
|
|
2158
|
-
it("requires detail-heavy custom renderers to prove worst-case preview or animation stress", () => {
|
|
2159
|
-
const config = defineToolcraftPerformance({
|
|
2160
|
-
rendererStrategy: "canvas-2d",
|
|
2161
|
-
rendererTechnique: {
|
|
2162
|
-
exportRenderer: "canvas-2d",
|
|
2163
|
-
fidelityRisks: ["dense texture output must stay visually stable"],
|
|
2164
|
-
layers: [
|
|
2165
|
-
{
|
|
2166
|
-
content: ["dense-pattern"],
|
|
2167
|
-
exportMode: "included",
|
|
2168
|
-
id: "dense-pattern",
|
|
2169
|
-
kind: "product-foreground",
|
|
2170
|
-
primitiveCount: "high",
|
|
2171
|
-
renderer: "canvas-2d",
|
|
2172
|
-
uiSelector: '[data-toolcraft-renderer-layer="dense-pattern"]',
|
|
2173
|
-
},
|
|
2174
|
-
{
|
|
2175
|
-
content: ["geometry"],
|
|
2176
|
-
exportMode: "included",
|
|
2177
|
-
id: "foreground-geometry",
|
|
2178
|
-
kind: "product-foreground",
|
|
2179
|
-
primitiveCount: "low",
|
|
2180
|
-
renderer: "svg",
|
|
2181
|
-
uiSelector: '[data-toolcraft-renderer-layer="foreground-geometry"]',
|
|
2182
|
-
},
|
|
2183
|
-
],
|
|
2184
|
-
performanceRisks: ["maximum density may overload the renderer"],
|
|
2185
|
-
previewRenderer: "canvas-2d",
|
|
2186
|
-
productRepresentation: "mixed",
|
|
2187
|
-
rendererStrategy: "canvas-2d",
|
|
2188
|
-
rendererWorkload: "simple-composition",
|
|
2189
|
-
sourceRepresentation: "procedural-data",
|
|
2190
|
-
whyNotAlternativeStrategies: ["canvas is simple"],
|
|
2191
|
-
},
|
|
2192
|
-
rendererWorkload: "simple-composition",
|
|
2193
|
-
scenarios: createTextRendererScenarios({ includeStressPreview: false }),
|
|
2194
|
-
usesCustomRenderer: true,
|
|
2195
|
-
workloadTargets: ["render.density"],
|
|
2196
|
-
});
|
|
2197
|
-
|
|
2198
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
2199
|
-
expect.arrayContaining([
|
|
2200
|
-
"Detail-heavy custom renderers must include a stress preview-render or animation-frame scenario for the largest product canvas and heaviest workload values.",
|
|
2201
|
-
]),
|
|
2202
|
-
);
|
|
2203
|
-
});
|
|
2204
|
-
|
|
2205
|
-
it("requires high-count Canvas 2D semantic or dense layers to carry measurable stress evidence", () => {
|
|
2206
|
-
const config = defineToolcraftPerformance({
|
|
2207
|
-
rendererStrategy: "canvas-2d",
|
|
2208
|
-
rendererTechnique: {
|
|
2209
|
-
exportRenderer: "canvas-2d",
|
|
2210
|
-
fidelityRisks: ["glyphs must stay legible"],
|
|
2211
|
-
layers: [
|
|
2212
|
-
{
|
|
2213
|
-
content: ["text", "dense-pattern"],
|
|
2214
|
-
exportMode: "included",
|
|
2215
|
-
id: "dense-text-field",
|
|
2216
|
-
intentionalRasterizationReason:
|
|
2217
|
-
"The product exports raster video and PNG, so text is drawn into one canvas frame.",
|
|
2218
|
-
kind: "product-foreground",
|
|
2219
|
-
primitiveCount: "high",
|
|
2220
|
-
renderer: "canvas-2d",
|
|
2221
|
-
uiSelector: '[data-toolcraft-renderer-layer="dense-text-field"]',
|
|
2222
|
-
},
|
|
2223
|
-
],
|
|
2224
|
-
performanceRisks: ["maximum density may redraw many text runs per frame"],
|
|
2225
|
-
previewRenderer: "canvas-2d",
|
|
2226
|
-
productRepresentation: "text",
|
|
2227
|
-
rendererStrategy: "canvas-2d",
|
|
2228
|
-
rendererWorkload: "text-output",
|
|
2229
|
-
sourceRepresentation: "dom-text",
|
|
2230
|
-
whyNotAlternativeStrategies: ["webgl texture output would rasterize text too early"],
|
|
2231
|
-
},
|
|
2232
|
-
rendererWorkload: "text-output",
|
|
2233
|
-
scenarios: createTextRendererScenarios({ includeStressPreview: false }),
|
|
2234
|
-
usesCustomRenderer: true,
|
|
2235
|
-
workloadTargets: ["render.density"],
|
|
2236
|
-
});
|
|
2237
|
-
|
|
2238
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
2239
|
-
expect.arrayContaining([
|
|
2240
|
-
"High-count Canvas 2D renderer layers must include stress preview-render or animation-frame evidence before delivery. If that stress evidence fails, revise renderer strategy instead of only lowering product workload.",
|
|
2241
|
-
]),
|
|
2242
|
-
);
|
|
2243
|
-
});
|
|
2244
|
-
|
|
2245
|
-
it("requires detail-heavy Canvas 2D renderers to document measured WebGL/WebGPU evaluation", () => {
|
|
2246
|
-
const config = defineToolcraftPerformance({
|
|
2247
|
-
rendererStrategy: "canvas-2d",
|
|
2248
|
-
rendererTechnique: {
|
|
2249
|
-
exportRenderer: "canvas-2d",
|
|
2250
|
-
fidelityRisks: ["dense texture output must match source media colors"],
|
|
2251
|
-
layers: [
|
|
2252
|
-
{
|
|
2253
|
-
content: ["bitmap-media", "noise"],
|
|
2254
|
-
exportMode: "included",
|
|
2255
|
-
id: "media-effect",
|
|
2256
|
-
kind: "product-foreground",
|
|
2257
|
-
primitiveCount: "high",
|
|
2258
|
-
renderer: "canvas-2d",
|
|
2259
|
-
uiSelector: '[data-toolcraft-renderer-layer="media-effect"]',
|
|
2260
|
-
},
|
|
2261
|
-
],
|
|
2262
|
-
performanceRisks: ["large uploaded media can require millions of pixel operations"],
|
|
2263
|
-
previewRenderer: "canvas-2d",
|
|
2264
|
-
productRepresentation: "pixel",
|
|
2265
|
-
rendererStrategy: "canvas-2d",
|
|
2266
|
-
rendererWorkload: "pixel-output",
|
|
2267
|
-
sourceRepresentation: "image-media",
|
|
2268
|
-
whyNotAlternativeStrategies: ["canvas is convenient"],
|
|
2269
|
-
},
|
|
2270
|
-
rendererWorkload: "pixel-output",
|
|
2271
|
-
scenarios: createTextRendererScenarios(),
|
|
2272
|
-
usesCustomRenderer: true,
|
|
2273
|
-
workloadTargets: ["render.density"],
|
|
2274
|
-
});
|
|
2275
|
-
|
|
2276
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2277
|
-
"Detail-heavy Canvas 2D renderers must include rendererTechnique.measuredAlternativeEvidence for WebGL/WebGPU stress comparison before keeping the pixel work on CPU.",
|
|
2278
|
-
);
|
|
2279
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
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.',
|
|
2281
|
-
);
|
|
2282
|
-
});
|
|
2283
|
-
|
|
2284
|
-
it("allows pixel-output Canvas 2D only with measured WebGL/WebGPU evidence", () => {
|
|
2285
|
-
const config = defineToolcraftPerformance({
|
|
2286
|
-
rendererStrategy: "canvas-2d",
|
|
2287
|
-
rendererTechnique: {
|
|
2288
|
-
exportRenderer: "canvas-2d",
|
|
2289
|
-
fidelityRisks: ["pixel output must preserve exact color stops"],
|
|
2290
|
-
performanceRisks: [
|
|
2291
|
-
"Measured WebGL stress evidence showed setup overhead exceeded cached Canvas 2D worker output for this static fixture.",
|
|
2292
|
-
],
|
|
2293
|
-
measuredAlternativeEvidence: createMeasuredWebglEvidence(),
|
|
2294
|
-
previewRenderer: "canvas-2d",
|
|
2295
|
-
productRepresentation: "pixel",
|
|
2296
|
-
rendererStrategy: "canvas-2d",
|
|
2297
|
-
rendererWorkload: "pixel-output",
|
|
2298
|
-
sourceRepresentation: "procedural-data",
|
|
2299
|
-
whyNotAlternativeStrategies: [
|
|
2300
|
-
"WebGL measured stress evidence stayed within budget but did not improve interaction latency for this renderer.",
|
|
2301
|
-
],
|
|
2302
|
-
},
|
|
2303
|
-
rendererWorkload: "pixel-output",
|
|
2304
|
-
rendererPipeline: createPixelRendererPipeline(),
|
|
2305
|
-
scenarios: createTextRendererScenarios(),
|
|
2306
|
-
usesCustomRenderer: true,
|
|
2307
|
-
workloadTargets: ["render.density"],
|
|
2308
|
-
});
|
|
2309
|
-
|
|
2310
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
2311
|
-
});
|
|
2312
|
-
|
|
2313
|
-
it("accepts animated custom renderers with stress coverage while dragging the viewport", () => {
|
|
2314
|
-
const config = createAnimatedVectorRendererConfig({ includeViewportDrag: true });
|
|
2315
|
-
|
|
2316
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
2317
|
-
});
|
|
2318
|
-
|
|
2319
|
-
it("requires custom renderers to declare typed renderer technique metadata", () => {
|
|
2320
|
-
const config = defineToolcraftPerformance({
|
|
2321
|
-
rendererStrategy: "canvas-2d",
|
|
2322
|
-
rendererWorkload: "text-output",
|
|
2323
|
-
scenarios: createTextRendererScenarios(),
|
|
2324
|
-
usesCustomRenderer: true,
|
|
2325
|
-
workloadTargets: ["render.density"],
|
|
2326
|
-
});
|
|
2327
|
-
|
|
2328
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
2329
|
-
expect.arrayContaining([
|
|
2330
|
-
"Custom renderers must declare rendererTechnique so renderer choice is machine-checkable.",
|
|
2331
|
-
]),
|
|
2332
|
-
);
|
|
2333
|
-
});
|
|
2334
|
-
|
|
2335
|
-
it("requires custom renderers to declare a render pipeline inventory", () => {
|
|
2336
|
-
const config = defineToolcraftPerformance({
|
|
2337
|
-
rendererStrategy: "canvas-2d",
|
|
2338
|
-
rendererTechnique: {
|
|
2339
|
-
exportRenderer: "canvas-2d",
|
|
2340
|
-
fidelityRisks: ["canvas output can soften edges"],
|
|
2341
|
-
performanceRisks: ["dense output can jank during drag"],
|
|
2342
|
-
previewRenderer: "canvas-2d",
|
|
2343
|
-
productRepresentation: "mixed",
|
|
2344
|
-
rendererStrategy: "canvas-2d",
|
|
2345
|
-
rendererWorkload: "simple-composition",
|
|
2346
|
-
sourceRepresentation: "procedural-data",
|
|
2347
|
-
whyNotAlternativeStrategies: [
|
|
2348
|
-
"svg cannot preserve dense procedural background output",
|
|
2349
|
-
],
|
|
2350
|
-
},
|
|
2351
|
-
rendererWorkload: "simple-composition",
|
|
2352
|
-
scenarios: createTextRendererScenarios(),
|
|
2353
|
-
usesCustomRenderer: true,
|
|
2354
|
-
workloadTargets: ["render.density"],
|
|
2355
|
-
});
|
|
2356
|
-
|
|
2357
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2358
|
-
"Custom renderers must declare rendererPipeline so render passes, cache keys, and invalidation are machine-checkable.",
|
|
2359
|
-
);
|
|
2360
|
-
});
|
|
2361
|
-
|
|
2362
|
-
it("rejects vague renderer pipeline pass dependencies", () => {
|
|
2363
|
-
const config = defineToolcraftPerformance({
|
|
2364
|
-
rendererStrategy: "canvas-2d",
|
|
2365
|
-
rendererTechnique: {
|
|
2366
|
-
exportRenderer: "canvas-2d",
|
|
2367
|
-
fidelityRisks: ["dense text output can soften at small sizes"],
|
|
2368
|
-
performanceRisks: ["text layout can be expensive"],
|
|
2369
|
-
previewRenderer: "canvas-2d",
|
|
2370
|
-
productRepresentation: "text",
|
|
2371
|
-
rendererStrategy: "canvas-2d",
|
|
2372
|
-
rendererWorkload: "text-output",
|
|
2373
|
-
sourceRepresentation: "dom-text",
|
|
2374
|
-
whyNotAlternativeStrategies: ["dom output cannot preserve export composition"],
|
|
2375
|
-
},
|
|
2376
|
-
rendererWorkload: "text-output",
|
|
2377
|
-
rendererPipeline: {
|
|
2378
|
-
interactionInvalidation: [
|
|
2379
|
-
{
|
|
2380
|
-
interaction: "control-drag",
|
|
2381
|
-
invalidates: ["text-layout"],
|
|
2382
|
-
targets: ["render.density"],
|
|
2383
|
-
},
|
|
2384
|
-
],
|
|
2385
|
-
passes: [
|
|
2386
|
-
{
|
|
2387
|
-
cacheKey: ["state"],
|
|
2388
|
-
id: "text-layout",
|
|
2389
|
-
inputs: ["state"],
|
|
2390
|
-
invalidatedBy: ["all values"],
|
|
2391
|
-
kind: "text-layout",
|
|
2392
|
-
output: "preview",
|
|
2393
|
-
quality: "full",
|
|
2394
|
-
runsOn: "main",
|
|
2395
|
-
},
|
|
2396
|
-
],
|
|
2397
|
-
},
|
|
2398
|
-
scenarios: createTextRendererScenarios(),
|
|
2399
|
-
usesCustomRenderer: true,
|
|
2400
|
-
workloadTargets: ["render.density"],
|
|
2401
|
-
});
|
|
2402
|
-
|
|
2403
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
2404
|
-
expect.arrayContaining([
|
|
2405
|
-
'rendererPipeline pass "text-layout" inputs entry "state" is too vague. Name the concrete runtime target, source key, resource key, or cache key part.',
|
|
2406
|
-
'rendererPipeline pass "text-layout" invalidatedBy entry "all values" is too vague. Name the concrete runtime target, source key, resource key, or cache key part.',
|
|
2407
|
-
'rendererPipeline pass "text-layout" cacheKey entry "state" is too vague. Name the concrete runtime target, source key, resource key, or cache key part.',
|
|
2408
|
-
]),
|
|
2409
|
-
);
|
|
2410
|
-
});
|
|
2411
|
-
|
|
2412
|
-
it("requires cache-sensitive renderer pipeline passes to declare cache keys", () => {
|
|
2413
|
-
const config = defineToolcraftPerformance({
|
|
2414
|
-
rendererStrategy: "canvas-2d",
|
|
2415
|
-
rendererTechnique: {
|
|
2416
|
-
exportRenderer: "canvas-2d",
|
|
2417
|
-
fidelityRisks: ["source pixels must stay crisp"],
|
|
2418
|
-
performanceRisks: ["pixel transforms can block the main thread"],
|
|
2419
|
-
previewRenderer: "canvas-2d",
|
|
2420
|
-
productRepresentation: "pixel",
|
|
2421
|
-
rendererStrategy: "canvas-2d",
|
|
2422
|
-
rendererWorkload: "pixel-output",
|
|
2423
|
-
sourceRepresentation: "image-media",
|
|
2424
|
-
whyNotAlternativeStrategies: [
|
|
2425
|
-
"WebGL/WebGPU measured performance evidence showed no improvement for this fixture",
|
|
2426
|
-
],
|
|
2427
|
-
},
|
|
2428
|
-
rendererWorkload: "pixel-output",
|
|
2429
|
-
rendererPipeline: {
|
|
2430
|
-
interactionInvalidation: [
|
|
2431
|
-
{
|
|
2432
|
-
interaction: "control-drag",
|
|
2433
|
-
invalidates: ["pixel-pass"],
|
|
2434
|
-
targets: ["render.density"],
|
|
2435
|
-
},
|
|
2436
|
-
],
|
|
2437
|
-
passes: [
|
|
2438
|
-
{
|
|
2439
|
-
id: "pixel-pass",
|
|
2440
|
-
inputs: ["source.image", "render.density"],
|
|
2441
|
-
invalidatedBy: ["source.image", "render.density"],
|
|
2442
|
-
kind: "pixel-transform",
|
|
2443
|
-
output: "preview",
|
|
2444
|
-
quality: "retina",
|
|
2445
|
-
runsOn: "worker",
|
|
2446
|
-
},
|
|
2447
|
-
],
|
|
2448
|
-
},
|
|
2449
|
-
scenarios: createTextRendererScenarios(),
|
|
2450
|
-
usesCustomRenderer: true,
|
|
2451
|
-
workloadTargets: ["render.density"],
|
|
2452
|
-
});
|
|
2453
|
-
|
|
2454
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2455
|
-
'rendererPipeline pass "pixel-pass" is a cache-sensitive pixel-transform pass and must declare cacheKey so tests can reject full recomputation on every control change.',
|
|
2456
|
-
);
|
|
2457
|
-
});
|
|
2458
|
-
|
|
2459
|
-
it("requires composite renderer pipeline passes to declare cache keys", () => {
|
|
2460
|
-
const config = defineToolcraftPerformance({
|
|
2461
|
-
rendererStrategy: "svg",
|
|
2462
|
-
rendererTechnique: {
|
|
2463
|
-
exportRenderer: "svg",
|
|
2464
|
-
fidelityRisks: ["animated composite must preserve vector output"],
|
|
2465
|
-
performanceRisks: ["compositing can be rebuilt too often"],
|
|
2466
|
-
previewRenderer: "svg",
|
|
2467
|
-
productRepresentation: "vector",
|
|
2468
|
-
rendererStrategy: "svg",
|
|
2469
|
-
rendererWorkload: "vector-output",
|
|
2470
|
-
sourceRepresentation: "procedural-data",
|
|
2471
|
-
whyNotAlternativeStrategies: ["svg preserves semantic vector output"],
|
|
2472
|
-
},
|
|
2473
|
-
rendererWorkload: "vector-output",
|
|
2474
|
-
rendererPipeline: {
|
|
2475
|
-
...createAnimatedVectorRendererPipeline(),
|
|
2476
|
-
passes: createAnimatedVectorRendererPipeline().passes.map((pass) =>
|
|
2477
|
-
pass.id === "animation-composite"
|
|
2478
|
-
? {
|
|
2479
|
-
id: pass.id,
|
|
2480
|
-
inputs: pass.inputs,
|
|
2481
|
-
invalidatedBy: pass.invalidatedBy,
|
|
2482
|
-
kind: pass.kind,
|
|
2483
|
-
output: pass.output,
|
|
2484
|
-
quality: pass.quality,
|
|
2485
|
-
runsOn: pass.runsOn,
|
|
2486
|
-
}
|
|
2487
|
-
: pass,
|
|
2488
|
-
),
|
|
2489
|
-
},
|
|
2490
|
-
scenarios: createTextRendererScenarios(),
|
|
2491
|
-
usesCustomRenderer: true,
|
|
2492
|
-
workloadTargets: ["render.density"],
|
|
2493
|
-
});
|
|
2494
|
-
|
|
2495
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2496
|
-
'rendererPipeline pass "animation-composite" is a cache-sensitive composite pass and must declare cacheKey so tests can reject full recomputation on every control change.',
|
|
2497
|
-
);
|
|
2498
|
-
});
|
|
2499
|
-
|
|
2500
|
-
it("rejects high-frequency viewport interactions that invalidate expensive passes", () => {
|
|
2501
|
-
const config = defineToolcraftPerformance({
|
|
2502
|
-
rendererStrategy: "canvas-2d",
|
|
2503
|
-
rendererTechnique: {
|
|
2504
|
-
exportRenderer: "canvas-2d",
|
|
2505
|
-
fidelityRisks: ["dense background must stay stable while zooming"],
|
|
2506
|
-
layers: [
|
|
2507
|
-
{
|
|
2508
|
-
content: ["dense-pattern"],
|
|
2509
|
-
exportMode: "included",
|
|
2510
|
-
id: "dense-pattern",
|
|
2511
|
-
kind: "background",
|
|
2512
|
-
primitiveCount: "high",
|
|
2513
|
-
renderer: "canvas-2d",
|
|
2514
|
-
},
|
|
2515
|
-
],
|
|
2516
|
-
performanceRisks: [
|
|
2517
|
-
"WebGL measured stress evidence was reviewed before keeping dense background on Canvas 2D",
|
|
2518
|
-
],
|
|
2519
|
-
previewRenderer: "canvas-2d",
|
|
2520
|
-
productRepresentation: "pixel",
|
|
2521
|
-
rendererStrategy: "canvas-2d",
|
|
2522
|
-
rendererWorkload: "pixel-output",
|
|
2523
|
-
sourceRepresentation: "procedural-data",
|
|
2524
|
-
whyNotAlternativeStrategies: [
|
|
2525
|
-
"WebGL stress evidence showed shader setup overhead exceeded static cache reuse",
|
|
2526
|
-
],
|
|
2527
|
-
},
|
|
2528
|
-
rendererWorkload: "pixel-output",
|
|
2529
|
-
rendererPipeline: {
|
|
2530
|
-
interactionInvalidation: [
|
|
2531
|
-
{
|
|
2532
|
-
interaction: "viewport-zoom",
|
|
2533
|
-
invalidates: ["dense-background"],
|
|
2534
|
-
mustNotInvalidate: ["dense-background"],
|
|
2535
|
-
targets: ["canvas.viewport"],
|
|
2536
|
-
},
|
|
2537
|
-
{
|
|
2538
|
-
interaction: "control-drag",
|
|
2539
|
-
invalidates: ["dense-background"],
|
|
2540
|
-
targets: ["render.density"],
|
|
2541
|
-
},
|
|
2542
|
-
],
|
|
2543
|
-
passes: [
|
|
2544
|
-
{
|
|
2545
|
-
cacheKey: ["render.density", "canvas.size", "canvas.renderScale"],
|
|
2546
|
-
id: "dense-background",
|
|
2547
|
-
inputs: ["render.density", "canvas.size", "canvas.renderScale"],
|
|
2548
|
-
invalidatedBy: ["render.density", "canvas.size", "canvas.renderScale"],
|
|
2549
|
-
kind: "rasterize",
|
|
2550
|
-
output: "preview",
|
|
2551
|
-
quality: "retina",
|
|
2552
|
-
runsOn: "worker",
|
|
2553
|
-
},
|
|
2554
|
-
],
|
|
2555
|
-
},
|
|
2556
|
-
scenarios: createTextRendererScenarios(),
|
|
2557
|
-
usesCustomRenderer: true,
|
|
2558
|
-
workloadTargets: ["render.density"],
|
|
2559
|
-
});
|
|
2560
|
-
|
|
2561
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
2562
|
-
expect.arrayContaining([
|
|
2563
|
-
'rendererPipeline viewport-zoom cannot both invalidate and mustNotInvalidate pass "dense-background".',
|
|
2564
|
-
'rendererPipeline viewport-zoom must not invalidate expensive pass "dense-background" (rasterize). Move viewport work to transforms/uniforms or explain it through a cheaper pass.',
|
|
2565
|
-
]),
|
|
2566
|
-
);
|
|
2567
|
-
});
|
|
2568
|
-
|
|
2569
|
-
it("requires media decoding pipelines to include media-import coverage", () => {
|
|
2570
|
-
const config = defineToolcraftPerformance({
|
|
2571
|
-
rendererStrategy: "webgl",
|
|
2572
|
-
rendererTechnique: {
|
|
2573
|
-
exportRenderer: "webgl",
|
|
2574
|
-
fidelityRisks: ["large source media must preserve color"],
|
|
2575
|
-
performanceRisks: ["source decode can be expensive"],
|
|
2576
|
-
previewRenderer: "webgl",
|
|
2577
|
-
productRepresentation: "pixel",
|
|
2578
|
-
rendererStrategy: "webgl",
|
|
2579
|
-
rendererWorkload: "pixel-output",
|
|
2580
|
-
sourceRepresentation: "image-media",
|
|
2581
|
-
whyNotAlternativeStrategies: ["canvas-2d would run per-pixel work on the main thread"],
|
|
2582
|
-
},
|
|
2583
|
-
rendererWorkload: "pixel-output",
|
|
2584
|
-
rendererPipeline: createMediaRendererPipeline(),
|
|
2585
|
-
scenarios: createTextRendererScenarios(),
|
|
2586
|
-
usesCustomRenderer: true,
|
|
2587
|
-
workloadTargets: ["render.density"],
|
|
2588
|
-
});
|
|
2589
|
-
|
|
2590
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2591
|
-
'rendererPipeline pass "source-decode" decodes media, so performance scenarios must include media-import coverage.',
|
|
2592
|
-
);
|
|
2593
|
-
});
|
|
2594
|
-
|
|
2595
|
-
it("requires workload targets to appear in renderer pipeline invalidation targets", () => {
|
|
2596
|
-
const config = defineToolcraftPerformance({
|
|
2597
|
-
rendererStrategy: "canvas-2d",
|
|
2598
|
-
rendererTechnique: {
|
|
2599
|
-
exportRenderer: "canvas-2d",
|
|
2600
|
-
fidelityRisks: ["text output must stay crisp"],
|
|
2601
|
-
performanceRisks: ["density changes can relayout text"],
|
|
2602
|
-
previewRenderer: "canvas-2d",
|
|
2603
|
-
productRepresentation: "text",
|
|
2604
|
-
rendererStrategy: "canvas-2d",
|
|
2605
|
-
rendererWorkload: "text-output",
|
|
2606
|
-
sourceRepresentation: "dom-text",
|
|
2607
|
-
whyNotAlternativeStrategies: ["dom output cannot preserve export composition"],
|
|
2608
|
-
},
|
|
2609
|
-
rendererWorkload: "text-output",
|
|
2610
|
-
rendererPipeline: {
|
|
2611
|
-
...createTextRendererPipeline(),
|
|
2612
|
-
interactionInvalidation: [
|
|
2613
|
-
{
|
|
2614
|
-
interaction: "control-change",
|
|
2615
|
-
invalidates: ["text-layout"],
|
|
2616
|
-
targets: ["render.mode"],
|
|
2617
|
-
},
|
|
2618
|
-
],
|
|
2619
|
-
},
|
|
2620
|
-
scenarios: createTextRendererScenarios(),
|
|
2621
|
-
usesCustomRenderer: true,
|
|
2622
|
-
workloadTargets: ["render.density"],
|
|
2623
|
-
});
|
|
2624
|
-
|
|
2625
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2626
|
-
"Performance workload target render.density must appear in rendererPipeline interactionInvalidation targets.",
|
|
2627
|
-
);
|
|
2628
|
-
});
|
|
2629
|
-
|
|
2630
|
-
it("requires performance scenario interactions to appear in renderer pipeline invalidation", () => {
|
|
2631
|
-
const config = defineToolcraftPerformance({
|
|
2632
|
-
rendererStrategy: "canvas-2d",
|
|
2633
|
-
rendererTechnique: {
|
|
2634
|
-
exportRenderer: "canvas-2d",
|
|
2635
|
-
fidelityRisks: ["text output must stay crisp"],
|
|
2636
|
-
performanceRisks: ["density changes can relayout text"],
|
|
2637
|
-
previewRenderer: "canvas-2d",
|
|
2638
|
-
productRepresentation: "text",
|
|
2639
|
-
rendererStrategy: "canvas-2d",
|
|
2640
|
-
rendererWorkload: "text-output",
|
|
2641
|
-
sourceRepresentation: "dom-text",
|
|
2642
|
-
whyNotAlternativeStrategies: ["dom output cannot preserve export composition"],
|
|
2643
|
-
},
|
|
2644
|
-
rendererWorkload: "text-output",
|
|
2645
|
-
rendererPipeline: {
|
|
2646
|
-
...createTextRendererPipeline(),
|
|
2647
|
-
interactionInvalidation: [
|
|
2648
|
-
{
|
|
2649
|
-
interaction: "control-drag",
|
|
2650
|
-
invalidates: ["text-layout"],
|
|
2651
|
-
targets: ["render.density"],
|
|
2652
|
-
},
|
|
2653
|
-
{
|
|
2654
|
-
interaction: "control-change",
|
|
2655
|
-
invalidates: ["text-layout"],
|
|
2656
|
-
targets: ["render.mode"],
|
|
2657
|
-
},
|
|
2658
|
-
],
|
|
2659
|
-
},
|
|
2660
|
-
scenarios: createTextRendererScenarios(),
|
|
2661
|
-
usesCustomRenderer: true,
|
|
2662
|
-
workloadTargets: ["render.density"],
|
|
2663
|
-
});
|
|
2664
|
-
|
|
2665
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2666
|
-
"Performance scenario viewport-zoom-stress exercises viewport-zoom, so rendererPipeline.interactionInvalidation must declare that interaction.",
|
|
2667
|
-
);
|
|
2668
|
-
});
|
|
2669
|
-
|
|
2670
|
-
it("requires timeline playback scenarios to appear in renderer pipeline invalidation", () => {
|
|
2671
|
-
const config = defineToolcraftPerformance({
|
|
2672
|
-
rendererStrategy: "svg",
|
|
2673
|
-
rendererTechnique: {
|
|
2674
|
-
exportRenderer: "svg",
|
|
2675
|
-
fidelityRisks: ["timeline playback must preserve vector motion"],
|
|
2676
|
-
performanceRisks: ["timeline playback can jank at high density"],
|
|
2677
|
-
previewRenderer: "svg",
|
|
2678
|
-
productRepresentation: "vector",
|
|
2679
|
-
rendererStrategy: "svg",
|
|
2680
|
-
rendererWorkload: "vector-output",
|
|
2681
|
-
sourceRepresentation: "procedural-data",
|
|
2682
|
-
whyNotAlternativeStrategies: ["svg preserves semantic vector output"],
|
|
2683
|
-
},
|
|
2684
|
-
rendererWorkload: "vector-output",
|
|
2685
|
-
rendererPipeline: {
|
|
2686
|
-
...createAnimatedVectorRendererPipeline(),
|
|
2687
|
-
interactionInvalidation: createAnimatedVectorRendererPipeline().interactionInvalidation.filter(
|
|
2688
|
-
(entry) => entry.interaction !== "timeline-playback",
|
|
2689
|
-
),
|
|
2690
|
-
},
|
|
2691
|
-
scenarios: [
|
|
2692
|
-
...createTextRendererScenarios(),
|
|
2693
|
-
{
|
|
2694
|
-
automated: true,
|
|
2695
|
-
automatedTestName: "perf: timeline playback stays smooth",
|
|
2696
|
-
browser: true,
|
|
2697
|
-
browserTestName: "browser perf: timeline playback stays smooth",
|
|
2698
|
-
budget: { maxFrameGapMs: 80, maxLongTaskMs: 80 },
|
|
2699
|
-
expectedObservable: "Timeline playback advances without blocking frames.",
|
|
2700
|
-
fixture: "timeline playback fixture",
|
|
2701
|
-
id: "timeline-playback",
|
|
2702
|
-
interaction: "timeline-playback",
|
|
2703
|
-
stress: true,
|
|
2704
|
-
stressFixture: createCombinedRendererStressFixture(),
|
|
2705
|
-
target: "timeline.currentTime",
|
|
2706
|
-
uiSelector: '[data-slot="timeline-playback-handle"]',
|
|
2707
|
-
workload: false,
|
|
2708
|
-
},
|
|
2709
|
-
],
|
|
2710
|
-
usesCustomRenderer: true,
|
|
2711
|
-
workloadTargets: ["render.density"],
|
|
2712
|
-
});
|
|
2713
|
-
|
|
2714
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2715
|
-
"Performance scenario timeline-playback exercises timeline-playback, so rendererPipeline.interactionInvalidation must declare that interaction.",
|
|
2716
|
-
);
|
|
2717
|
-
});
|
|
2718
|
-
|
|
2719
|
-
it("validates timeline scrub scenarios through renderer pipeline invalidation", () => {
|
|
2720
|
-
const config = defineToolcraftPerformance({
|
|
2721
|
-
rendererStrategy: "svg",
|
|
2722
|
-
rendererTechnique: {
|
|
2723
|
-
exportRenderer: "svg",
|
|
2724
|
-
fidelityRisks: ["timeline scrub must preserve vector motion"],
|
|
2725
|
-
performanceRisks: ["timeline scrub can jank at high density"],
|
|
2726
|
-
previewRenderer: "svg",
|
|
2727
|
-
productRepresentation: "vector",
|
|
2728
|
-
rendererStrategy: "svg",
|
|
2729
|
-
rendererWorkload: "vector-output",
|
|
2730
|
-
sourceRepresentation: "procedural-data",
|
|
2731
|
-
whyNotAlternativeStrategies: ["svg preserves semantic vector output"],
|
|
2732
|
-
},
|
|
2733
|
-
rendererWorkload: "vector-output",
|
|
2734
|
-
rendererPipeline: createAnimatedVectorRendererPipeline(),
|
|
2735
|
-
scenarios: [
|
|
2736
|
-
...createTextRendererScenarios(),
|
|
2737
|
-
{
|
|
2738
|
-
automated: true,
|
|
2739
|
-
automatedTestName: "perf: timeline scrub stays responsive",
|
|
2740
|
-
browser: true,
|
|
2741
|
-
browserTestName: "browser perf: timeline scrub stays responsive",
|
|
2742
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500, maxLongTaskMs: 80 },
|
|
2743
|
-
expectedObservable: "Timeline scrub updates output without blocking frames.",
|
|
2744
|
-
fixture: "timeline scrub fixture",
|
|
2745
|
-
id: "timeline-scrub",
|
|
2746
|
-
interaction: "timeline-scrub",
|
|
2747
|
-
stress: true,
|
|
2748
|
-
stressFixture: createCombinedRendererStressFixture(),
|
|
2749
|
-
target: "timeline.currentTime",
|
|
2750
|
-
uiSelector: '[data-slot="timeline-playback-handle"]',
|
|
2751
|
-
workload: false,
|
|
2752
|
-
},
|
|
2753
|
-
],
|
|
2754
|
-
usesCustomRenderer: true,
|
|
2755
|
-
workloadTargets: ["render.density"],
|
|
2756
|
-
});
|
|
2757
|
-
|
|
2758
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
2759
|
-
});
|
|
2760
|
-
|
|
2761
|
-
it("requires timeline performance scenarios to name the real timeline UI", () => {
|
|
2762
|
-
const config = defineToolcraftPerformance({
|
|
2763
|
-
rendererStrategy: "svg",
|
|
2764
|
-
rendererTechnique: {
|
|
2765
|
-
exportRenderer: "svg",
|
|
2766
|
-
fidelityRisks: ["timeline scrub must preserve vector motion"],
|
|
2767
|
-
performanceRisks: ["timeline scrub can jank at high density"],
|
|
2768
|
-
previewRenderer: "svg",
|
|
2769
|
-
productRepresentation: "vector",
|
|
2770
|
-
rendererStrategy: "svg",
|
|
2771
|
-
rendererWorkload: "vector-output",
|
|
2772
|
-
sourceRepresentation: "procedural-data",
|
|
2773
|
-
whyNotAlternativeStrategies: ["svg preserves semantic vector output"],
|
|
2774
|
-
},
|
|
2775
|
-
rendererWorkload: "vector-output",
|
|
2776
|
-
rendererPipeline: createAnimatedVectorRendererPipeline(),
|
|
2777
|
-
scenarios: [
|
|
2778
|
-
...createTextRendererScenarios(),
|
|
2779
|
-
{
|
|
2780
|
-
automated: true,
|
|
2781
|
-
automatedTestName: "perf: timeline scrub stays responsive",
|
|
2782
|
-
browser: true,
|
|
2783
|
-
browserTestName: "browser perf: timeline scrub stays responsive",
|
|
2784
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500, maxLongTaskMs: 80 },
|
|
2785
|
-
expectedObservable: "Timeline scrub updates output without blocking frames.",
|
|
2786
|
-
fixture: "timeline scrub fixture",
|
|
2787
|
-
id: "timeline-scrub",
|
|
2788
|
-
interaction: "timeline-scrub",
|
|
2789
|
-
stress: true,
|
|
2790
|
-
stressFixture: createCombinedRendererStressFixture(),
|
|
2791
|
-
target: "timeline.currentTime",
|
|
2792
|
-
workload: false,
|
|
2793
|
-
},
|
|
2794
|
-
],
|
|
2795
|
-
usesCustomRenderer: true,
|
|
2796
|
-
workloadTargets: ["render.density"],
|
|
2797
|
-
});
|
|
2798
|
-
|
|
2799
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toContain(
|
|
2800
|
-
"timeline-scrub timeline-scrub scenario must declare controlLabel or uiSelector for its real browser interaction.",
|
|
2801
|
-
);
|
|
2802
|
-
});
|
|
2803
|
-
|
|
2804
|
-
it("validates mask drag scenarios through renderer pipeline invalidation", () => {
|
|
2805
|
-
const config = defineToolcraftPerformance({
|
|
2806
|
-
rendererStrategy: "svg",
|
|
2807
|
-
rendererTechnique: {
|
|
2808
|
-
exportRenderer: "svg",
|
|
2809
|
-
fidelityRisks: ["mask handles must stay aligned with vector output"],
|
|
2810
|
-
performanceRisks: ["dragging a mask handle can rebuild output too often"],
|
|
2811
|
-
previewRenderer: "svg",
|
|
2812
|
-
productRepresentation: "vector",
|
|
2813
|
-
rendererStrategy: "svg",
|
|
2814
|
-
rendererWorkload: "vector-output",
|
|
2815
|
-
sourceRepresentation: "procedural-data",
|
|
2816
|
-
whyNotAlternativeStrategies: ["svg preserves semantic handle geometry"],
|
|
2817
|
-
},
|
|
2818
|
-
rendererWorkload: "vector-output",
|
|
2819
|
-
rendererPipeline: {
|
|
2820
|
-
...createAnimatedVectorRendererPipeline(),
|
|
2821
|
-
interactionInvalidation: [
|
|
2822
|
-
...createAnimatedVectorRendererPipeline().interactionInvalidation,
|
|
2823
|
-
{
|
|
2824
|
-
interaction: "mask-drag",
|
|
2825
|
-
invalidates: ["animation-composite"],
|
|
2826
|
-
mustNotInvalidate: ["vector-build"],
|
|
2827
|
-
targets: ["mask.anchor"],
|
|
2828
|
-
},
|
|
2829
|
-
],
|
|
2830
|
-
},
|
|
2831
|
-
scenarios: [
|
|
2832
|
-
...createTextRendererScenarios(),
|
|
2833
|
-
{
|
|
2834
|
-
automated: true,
|
|
2835
|
-
automatedTestName: "perf: mask drag stays responsive",
|
|
2836
|
-
browser: true,
|
|
2837
|
-
browserTestName: "browser perf: mask drag stays responsive",
|
|
2838
|
-
budget: { maxFrameGapMs: 80, maxInteractionMs: 500, maxLongTaskMs: 80 },
|
|
2839
|
-
expectedObservable: "Dragging the mask handle updates output without blocking frames.",
|
|
2840
|
-
fixture: "mask drag fixture",
|
|
2841
|
-
id: "mask-drag",
|
|
2842
|
-
interaction: "mask-drag",
|
|
2843
|
-
stress: true,
|
|
2844
|
-
stressFixture: createCombinedRendererStressFixture(),
|
|
2845
|
-
target: "mask.anchor",
|
|
2846
|
-
uiSelector: '[data-toolcraft-canvas-handle][data-testid="mask-anchor"]',
|
|
2847
|
-
workload: false,
|
|
2848
|
-
},
|
|
2849
|
-
],
|
|
2850
|
-
usesCustomRenderer: true,
|
|
2851
|
-
workloadTargets: ["render.density"],
|
|
2852
|
-
});
|
|
2853
|
-
|
|
2854
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
2855
|
-
});
|
|
2856
|
-
|
|
2857
|
-
it("rejects renderer technique mismatches and missing explanations", () => {
|
|
2858
|
-
const config = defineToolcraftPerformance({
|
|
2859
|
-
rendererStrategy: "canvas-2d",
|
|
2860
|
-
rendererTechnique: {
|
|
2861
|
-
exportRenderer: "webgl",
|
|
2862
|
-
fidelityRisks: [],
|
|
2863
|
-
performanceRisks: [],
|
|
2864
|
-
previewRenderer: "canvas-2d",
|
|
2865
|
-
productRepresentation: "text",
|
|
2866
|
-
rendererStrategy: "webgl",
|
|
2867
|
-
rendererWorkload: "pixel-output",
|
|
2868
|
-
sourceRepresentation: "reference-runtime",
|
|
2869
|
-
whyNotAlternativeStrategies: [],
|
|
2870
|
-
},
|
|
2871
|
-
rendererWorkload: "text-output",
|
|
2872
|
-
scenarios: createTextRendererScenarios(),
|
|
2873
|
-
usesCustomRenderer: true,
|
|
2874
|
-
workloadTargets: ["render.density"],
|
|
2875
|
-
});
|
|
2876
|
-
|
|
2877
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
2878
|
-
expect.arrayContaining([
|
|
2879
|
-
'rendererTechnique.rendererWorkload "pixel-output" must match rendererWorkload "text-output".',
|
|
2880
|
-
'rendererTechnique.rendererStrategy "webgl" must match rendererStrategy "canvas-2d".',
|
|
2881
|
-
"Custom renderer technique must explain why alternative renderer strategies were rejected.",
|
|
2882
|
-
"Custom renderer technique must list fidelity risks.",
|
|
2883
|
-
"Custom renderer technique must list performance risks.",
|
|
2884
|
-
'productRepresentation "text" requires rendererWorkload "text-output" unless intentionalRasterizationReason is provided.',
|
|
2885
|
-
"Different preview/export renderers require previewExportDifferenceReason.",
|
|
2886
|
-
"Reference runtime renderer changes require referenceRendererChangeReason.",
|
|
2887
|
-
]),
|
|
2888
|
-
);
|
|
2889
|
-
});
|
|
2890
|
-
|
|
2891
|
-
it("allows dense raster backgrounds with semantic svg foreground layers", () => {
|
|
2892
|
-
const config = defineToolcraftPerformance({
|
|
2893
|
-
rendererStrategy: "canvas-2d",
|
|
2894
|
-
rendererTechnique: {
|
|
2895
|
-
exportRenderer: "canvas-2d",
|
|
2896
|
-
fidelityRisks: ["export composite must preserve foreground line and text edges"],
|
|
2897
|
-
layers: [
|
|
2898
|
-
{
|
|
2899
|
-
content: ["dense-pattern"],
|
|
2900
|
-
exportMode: "included",
|
|
2901
|
-
id: "dot-grid-background",
|
|
2902
|
-
kind: "background",
|
|
2903
|
-
primitiveCount: "high",
|
|
2904
|
-
renderer: "canvas-2d",
|
|
2905
|
-
},
|
|
2906
|
-
{
|
|
2907
|
-
content: ["geometry", "text"],
|
|
2908
|
-
exportMode: "included",
|
|
2909
|
-
id: "semantic-foreground",
|
|
2910
|
-
kind: "product-foreground",
|
|
2911
|
-
primitiveCount: "low",
|
|
2912
|
-
renderer: "svg",
|
|
2913
|
-
uiSelector: '[data-toolcraft-renderer-layer="semantic-foreground"]',
|
|
2914
|
-
},
|
|
2915
|
-
{
|
|
2916
|
-
content: ["composite"],
|
|
2917
|
-
exportMode: "composited",
|
|
2918
|
-
id: "png-export",
|
|
2919
|
-
kind: "export-composite",
|
|
2920
|
-
primitiveCount: "low",
|
|
2921
|
-
renderer: "canvas-2d",
|
|
2922
|
-
},
|
|
2923
|
-
],
|
|
2924
|
-
measuredAlternativeEvidence: createMeasuredWebglEvidence(),
|
|
2925
|
-
performanceRisks: ["dense background redraw can be expensive at large output size"],
|
|
2926
|
-
previewRenderer: "canvas-2d",
|
|
2927
|
-
productRepresentation: "mixed",
|
|
2928
|
-
rendererStrategy: "canvas-2d",
|
|
2929
|
-
rendererWorkload: "simple-composition",
|
|
2930
|
-
sourceRepresentation: "procedural-data",
|
|
2931
|
-
whyNotAlternativeStrategies: [
|
|
2932
|
-
"rendering thousands of background dots as DOM nodes would be expensive",
|
|
2933
|
-
"WebGL/WebGPU stress evidence is not needed yet because measured canvas-2d preview budgets pass for this dense static background.",
|
|
2934
|
-
],
|
|
2935
|
-
},
|
|
2936
|
-
rendererWorkload: "simple-composition",
|
|
2937
|
-
rendererPipeline: createDenseCanvasPipeline(),
|
|
2938
|
-
scenarios: createTextRendererScenarios(),
|
|
2939
|
-
usesCustomRenderer: true,
|
|
2940
|
-
workloadTargets: ["render.density"],
|
|
2941
|
-
});
|
|
2942
|
-
|
|
2943
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual([]);
|
|
2944
|
-
});
|
|
2945
|
-
|
|
2946
|
-
it("rejects canvas rasterization of low-count semantic foreground without a reason", () => {
|
|
2947
|
-
const config = defineToolcraftPerformance({
|
|
2948
|
-
rendererStrategy: "canvas-2d",
|
|
2949
|
-
rendererTechnique: {
|
|
2950
|
-
exportRenderer: "canvas-2d",
|
|
2951
|
-
fidelityRisks: ["foreground line edges can become soft"],
|
|
2952
|
-
layers: [
|
|
2953
|
-
{
|
|
2954
|
-
content: ["dense-pattern"],
|
|
2955
|
-
exportMode: "included",
|
|
2956
|
-
id: "dot-grid-background",
|
|
2957
|
-
kind: "background",
|
|
2958
|
-
primitiveCount: "high",
|
|
2959
|
-
renderer: "canvas-2d",
|
|
2960
|
-
},
|
|
2961
|
-
{
|
|
2962
|
-
content: ["geometry", "text"],
|
|
2963
|
-
exportMode: "included",
|
|
2964
|
-
id: "semantic-foreground",
|
|
2965
|
-
kind: "product-foreground",
|
|
2966
|
-
primitiveCount: "low",
|
|
2967
|
-
renderer: "canvas-2d",
|
|
2968
|
-
},
|
|
2969
|
-
],
|
|
2970
|
-
performanceRisks: ["background redraw can be expensive"],
|
|
2971
|
-
previewRenderer: "canvas-2d",
|
|
2972
|
-
productRepresentation: "mixed",
|
|
2973
|
-
rendererStrategy: "canvas-2d",
|
|
2974
|
-
rendererWorkload: "simple-composition",
|
|
2975
|
-
sourceRepresentation: "procedural-data",
|
|
2976
|
-
whyNotAlternativeStrategies: ["canvas is convenient"],
|
|
2977
|
-
},
|
|
2978
|
-
rendererWorkload: "simple-composition",
|
|
2979
|
-
scenarios: createTextRendererScenarios(),
|
|
2980
|
-
usesCustomRenderer: true,
|
|
2981
|
-
workloadTargets: ["render.density"],
|
|
2982
|
-
});
|
|
2983
|
-
|
|
2984
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
2985
|
-
expect.arrayContaining([
|
|
2986
|
-
'rendererTechnique layer "semantic-foreground" uses canvas-2d for low-count semantic geometry/text. Use dom/svg for semantic foreground or provide intentionalRasterizationReason.',
|
|
2987
|
-
]),
|
|
2988
|
-
);
|
|
2989
|
-
});
|
|
2990
|
-
|
|
2991
|
-
it("requires visible foreground and handle layers to declare browser selectors", () => {
|
|
2992
|
-
const config = defineToolcraftPerformance({
|
|
2993
|
-
rendererStrategy: "canvas-2d",
|
|
2994
|
-
rendererTechnique: {
|
|
2995
|
-
exportRenderer: "canvas-2d",
|
|
2996
|
-
fidelityRisks: ["foreground and handles must be verified in browser tests"],
|
|
2997
|
-
layers: [
|
|
2998
|
-
{
|
|
2999
|
-
content: ["dense-pattern"],
|
|
3000
|
-
exportMode: "included",
|
|
3001
|
-
id: "dot-grid-background",
|
|
3002
|
-
kind: "background",
|
|
3003
|
-
primitiveCount: "high",
|
|
3004
|
-
renderer: "canvas-2d",
|
|
3005
|
-
},
|
|
3006
|
-
{
|
|
3007
|
-
content: ["geometry", "text"],
|
|
3008
|
-
exportMode: "included",
|
|
3009
|
-
id: "semantic-foreground",
|
|
3010
|
-
kind: "product-foreground",
|
|
3011
|
-
primitiveCount: "low",
|
|
3012
|
-
renderer: "svg",
|
|
3013
|
-
},
|
|
3014
|
-
{
|
|
3015
|
-
content: ["handles"],
|
|
3016
|
-
exportMode: "excluded",
|
|
3017
|
-
id: "focus-handles",
|
|
3018
|
-
kind: "editing-handles",
|
|
3019
|
-
primitiveCount: "low",
|
|
3020
|
-
renderer: "svg",
|
|
3021
|
-
},
|
|
3022
|
-
],
|
|
3023
|
-
performanceRisks: ["background redraw can be expensive"],
|
|
3024
|
-
previewRenderer: "canvas-2d",
|
|
3025
|
-
productRepresentation: "mixed",
|
|
3026
|
-
rendererStrategy: "canvas-2d",
|
|
3027
|
-
rendererWorkload: "simple-composition",
|
|
3028
|
-
sourceRepresentation: "procedural-data",
|
|
3029
|
-
whyNotAlternativeStrategies: ["background dots are too dense for DOM"],
|
|
3030
|
-
},
|
|
3031
|
-
rendererWorkload: "simple-composition",
|
|
3032
|
-
scenarios: createTextRendererScenarios(),
|
|
3033
|
-
usesCustomRenderer: true,
|
|
3034
|
-
workloadTargets: ["render.density"],
|
|
3035
|
-
});
|
|
3036
|
-
|
|
3037
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
3038
|
-
expect.arrayContaining([
|
|
3039
|
-
'rendererTechnique layer "semantic-foreground" is product-foreground and must declare uiSelector so browser tests can verify the visible renderer layer.',
|
|
3040
|
-
'rendererTechnique layer "focus-handles" is editing-handles and must declare uiSelector so browser tests can verify the visible renderer layer.',
|
|
3041
|
-
]),
|
|
3042
|
-
);
|
|
3043
|
-
});
|
|
3044
|
-
|
|
3045
|
-
it("rejects mixed product representation without a real layer inventory", () => {
|
|
3046
|
-
const config = defineToolcraftPerformance({
|
|
3047
|
-
rendererStrategy: "canvas-2d",
|
|
3048
|
-
rendererTechnique: {
|
|
3049
|
-
exportRenderer: "canvas-2d",
|
|
3050
|
-
fidelityRisks: ["mixed representation must be proven by layers"],
|
|
3051
|
-
layers: [
|
|
3052
|
-
{
|
|
3053
|
-
content: ["dense-pattern"],
|
|
3054
|
-
exportMode: "included",
|
|
3055
|
-
id: "background",
|
|
3056
|
-
kind: "background",
|
|
3057
|
-
primitiveCount: "high",
|
|
3058
|
-
renderer: "canvas-2d",
|
|
3059
|
-
},
|
|
3060
|
-
],
|
|
3061
|
-
performanceRisks: ["dense redraw can be expensive"],
|
|
3062
|
-
previewRenderer: "canvas-2d",
|
|
3063
|
-
productRepresentation: "mixed",
|
|
3064
|
-
rendererStrategy: "canvas-2d",
|
|
3065
|
-
rendererWorkload: "simple-composition",
|
|
3066
|
-
sourceRepresentation: "procedural-data",
|
|
3067
|
-
whyNotAlternativeStrategies: ["canvas is faster than DOM for many dots"],
|
|
3068
|
-
},
|
|
3069
|
-
rendererWorkload: "simple-composition",
|
|
3070
|
-
scenarios: createTextRendererScenarios(),
|
|
3071
|
-
usesCustomRenderer: true,
|
|
3072
|
-
workloadTargets: ["render.density"],
|
|
3073
|
-
});
|
|
3074
|
-
|
|
3075
|
-
expect(validateToolcraftPerformanceCoverage(testSchema, config)).toEqual(
|
|
3076
|
-
expect.arrayContaining([
|
|
3077
|
-
'productRepresentation "mixed" requires rendererTechnique.layers with at least two different content families.',
|
|
3078
|
-
]),
|
|
3079
|
-
);
|
|
3080
|
-
});
|
|
3081
|
-
});
|