@pixel-point/toolcraft 0.0.18 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/generate-demand-only-lifecycle.test.mjs +9 -1
- package/src/generate-performance-iteration-execution.test.mjs +3 -3
- package/src/generate-test-delivery-architecture-assertions.mjs +11 -73
- package/src/generate-test-demand-only-lifecycle.mjs +34 -36
- package/src/generate-test-dependency-sandbox.mjs +4 -1
- package/src/generate-test-doc-assertions.mjs +55 -53
- package/src/generate-test-doc-assertions.test.mjs +1 -1
- package/src/generate-test-doc-policy-assertions.mjs +1 -1
- package/src/generate-test-export-doc-assertions.mjs +53 -0
- package/src/generate-test-file-assertions.mjs +51 -8
- package/src/generate-test-integrity-assertions.mjs +5 -2
- package/src/generate-test-integrity-manifest-assertions.mjs +16 -5
- package/src/generate-test-integrity-ownership-assertions.mjs +36 -4
- package/src/generate-test-package-assertions.mjs +4 -0
- package/src/generate-test-product-fixture.mjs +1 -1
- package/src/generate-test-product-fixture.test.mjs +35 -38
- package/src/generate-test-product-iteration.mjs +0 -1
- package/src/generate-test-product-iteration.test.mjs +0 -1
- package/src/generate-test-runtime-boundary-assertions.mjs +20 -0
- package/src/generate-test-standalone-assertions.mjs +0 -16
- package/src/generate-test-standalone-delivery-stages.mjs +2 -86
- package/src/generate.test.mjs +4 -0
- package/src/generated-integrity-manifest.test.mjs +5 -2
- package/src/generated-source-policy.test.mjs +0 -4
- package/src/package-json.mjs +5 -0
- package/src/package-json.test.mjs +21 -0
- package/src/package-manager.mjs +2 -0
- package/src/package-manager.test.mjs +8 -0
- package/src/packaged-template-fallback-helpers.mjs +254 -0
- package/src/packaged-template-fallback.test.mjs +121 -46
- package/src/packaged-template-runtime-assertions.mjs +150 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +18 -3
- package/templates/runtime/contracts/component-contracts.choices.ts +8 -3
- package/templates/runtime/contracts/component-contracts.inputs.ts +2 -1
- package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +18 -4
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +1 -1
- package/templates/runtime/contracts/component-contracts.runtime.ts +7 -4
- package/templates/runtime/contracts/component-contracts.visual.test.ts +20 -0
- package/templates/runtime/contracts/component-contracts.visual.ts +4 -0
- package/templates/runtime/contracts/decision-contracts.test.ts +18 -12
- package/templates/runtime/contracts/decision-contracts.ts +4 -4
- package/templates/runtime/contracts/performance-verification-policy.test.ts +12 -12
- package/templates/runtime/contracts/performance-verification-policy.ts +8 -8
- package/templates/runtime/export/artifact-download.ts +1 -0
- package/templates/runtime/export/artifact-frame-renderer.ts +2 -15
- package/templates/runtime/export/artifact-frame-state.ts +16 -0
- package/templates/runtime/export/artifact-scene-frame.test.ts +3 -3
- package/templates/runtime/export/artifact-scene-frame.ts +3 -3
- package/templates/runtime/export/export-error.ts +5 -0
- package/templates/runtime/export/export-renderer-coverage.ts +54 -0
- package/templates/runtime/export/image-artifact-export.ts +2 -2
- package/templates/runtime/export/index.ts +13 -0
- package/templates/runtime/export/product-export-renderer.test.ts +38 -4
- package/templates/runtime/export/product-export-renderer.ts +0 -41
- package/templates/runtime/export/product-svg-export-renderer.ts +21 -0
- package/templates/runtime/export/svg-artifact-export.test.ts +98 -0
- package/templates/runtime/export/svg-artifact-export.ts +120 -0
- package/templates/runtime/export/svg-document.test.ts +221 -0
- package/templates/runtime/export/svg-document.ts +400 -0
- package/templates/runtime/export/svg-policy.ts +47 -0
- package/templates/runtime/index.ts +1 -0
- package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +10 -0
- package/templates/runtime/react/app-shell/settings-transfer.test.ts +21 -0
- package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +46 -1
- package/templates/runtime/react/app-shell/toolcraft-app.tsx +7 -2
- package/templates/runtime/react/canvas/canvas-shell-interactions.test.tsx +30 -0
- package/templates/runtime/react/canvas/canvas-shell-pinch-interactions.test.tsx +155 -0
- package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +1 -1
- package/templates/runtime/react/canvas/canvas-shell.tsx +16 -5
- package/templates/runtime/react/canvas/canvas-viewport-geometry.ts +133 -0
- package/templates/runtime/react/canvas/use-canvas-viewport-interactions.ts +285 -36
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-applicability-layout.test.tsx +134 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-layout.test.tsx +16 -1
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.render-isolation.test.tsx +2 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +39 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.setup-controls.test.tsx +3 -3
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +1 -4
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +2 -2
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.vector-interaction.test.tsx +6 -6
- package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +2 -2
- package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +40 -11
- package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +31 -2
- package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +4 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-action-renderer.tsx +2 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-basic-renderers.tsx +2 -52
- package/templates/runtime/react/controls-panel/values/controls-panel-color-values.ts +1 -9
- package/templates/runtime/react/controls-panel/values/controls-panel-spatial-values.ts +7 -2
- package/templates/runtime/react/orientation-gizmo/orientation-gizmo-math.ts +9 -69
- package/templates/runtime/schema/artifact-export-actions.test.ts +47 -0
- package/templates/runtime/schema/artifact-export-actions.ts +49 -0
- package/templates/runtime/schema/types.ts +1 -0
- package/templates/runtime/state/canvas-background-state.ts +1 -16
- package/templates/runtime/state/canvas-state.ts +32 -20
- package/templates/runtime/state/control-value-codecs.test.ts +98 -0
- package/templates/runtime/state/control-value-codecs.ts +700 -0
- package/templates/runtime/state/control-value-normalization.test.ts +1 -1
- package/templates/runtime/state/control-value-normalization.ts +127 -135
- package/templates/runtime/state/controls-reducer.ts +26 -3
- package/templates/runtime/state/create-template-state.test.ts +30 -0
- package/templates/runtime/state/create-template-state.ts +52 -31
- package/templates/runtime/state/orientation-pose.ts +80 -0
- package/templates/runtime/state/persistence-control-values.test.ts +59 -0
- package/templates/runtime/state/reducer-controls.test.ts +45 -0
- package/templates/runtime/state/reducer-media-reset.test.ts +1 -1
- package/templates/runtime/state/reducer-timeline.test.ts +70 -0
- package/templates/runtime/state/timeline-reducer.ts +40 -2
- package/templates/runtime/testing/performance-schema-queries.ts +2 -2
- package/templates/starter/AGENTS.md +21 -22
- package/templates/starter/docs/toolcraft/README.md +6 -6
- package/templates/starter/docs/toolcraft/acceptance-testing.md +20 -20
- package/templates/starter/docs/toolcraft/agent-worklog.md +88 -1
- package/templates/starter/docs/toolcraft/assembly-workflow.md +7 -6
- package/templates/starter/docs/toolcraft/component-rules.md +2 -2
- package/templates/starter/docs/toolcraft/core/layout.md +6 -1
- package/templates/starter/docs/toolcraft/core/media-upload.md +1 -1
- package/templates/starter/docs/toolcraft/core/performance.md +8 -10
- package/templates/starter/docs/toolcraft/core/reference-study.md +80 -19
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +2 -1
- package/templates/starter/docs/toolcraft/core/setup-export.md +43 -13
- package/templates/starter/docs/toolcraft/decision-contract.md +2 -2
- package/templates/starter/docs/toolcraft/performance.md +8 -10
- package/templates/starter/docs/toolcraft/renderer-technique.md +2 -2
- package/templates/starter/docs/toolcraft/schema-reference.md +8 -8
- package/templates/starter/docs/toolcraft/workflow.md +14 -14
- package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +38 -0
- package/templates/starter/e2e/app-browser-motion-reference-requirements.spec.ts +411 -0
- package/templates/starter/e2e/app-browser-proof-session.spec.ts +85 -0
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +62 -0
- package/templates/starter/e2e/app-browser-selection-scope-evidence.spec.ts +137 -0
- package/templates/starter/e2e/browser-infinity-canvas-svg-evidence.ts +42 -0
- package/templates/starter/e2e/browser-layer-evidence-helpers.ts +15 -36
- package/templates/starter/e2e/browser-product-raster-snapshot.ts +213 -0
- package/templates/starter/e2e/browser-proof-session.ts +43 -15
- package/templates/starter/e2e/browser-proof-surface-action.ts +79 -0
- package/templates/starter/e2e/browser-proof-types.ts +15 -0
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +22 -6
- package/templates/starter/e2e/browser-selection-scope-evidence.ts +269 -0
- package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +97 -0
- package/templates/starter/e2e/browser-svg-export-evidence.spec.ts +79 -0
- package/templates/starter/e2e/browser-svg-export-evidence.ts +152 -0
- package/templates/starter/e2e/export-artifact-helpers.ts +70 -0
- package/templates/starter/e2e/product-observable-helpers.ts +8 -129
- package/templates/starter/e2e/svg-artifact-inspection.spec.ts +136 -0
- package/templates/starter/e2e/svg-artifact-inspection.ts +331 -0
- package/templates/starter/e2e/svg-download-test-fixtures.ts +34 -0
- package/templates/starter/e2e/toolcraft-delivery-catalog-reporter.ts +198 -15
- package/templates/starter/e2e/toolcraft-feature-verification-reporter.spec.ts +149 -0
- package/templates/starter/e2e/toolcraft-feature-verification-reporter.ts +223 -0
- package/templates/starter/package.json +2 -0
- package/templates/starter/scripts/check-toolcraft-docs.mjs +1 -0
- package/templates/starter/scripts/create-toolcraft-motion-reference-study.mjs +233 -0
- package/templates/starter/scripts/create-toolcraft-motion-reference-study.test.mjs +459 -0
- package/templates/starter/scripts/playwright-test-title-selection.d.mts +45 -0
- package/templates/starter/scripts/run-browser-performance-test-helpers.mjs +0 -27
- package/templates/starter/scripts/run-browser-performance.mjs +0 -6
- package/templates/starter/scripts/run-browser-performance.test.mjs +5 -13
- package/templates/starter/scripts/run-delivery-verification-test-helpers.mjs +0 -34
- package/templates/starter/scripts/run-delivery-verification.test.mjs +135 -31
- package/templates/starter/scripts/run-feature-verification.mjs +212 -0
- package/templates/starter/scripts/run-feature-verification.test.mjs +226 -0
- package/templates/starter/scripts/toolcraft-checkpoint-delivery-state.mjs +21 -8
- package/templates/starter/scripts/toolcraft-checkpoint-delivery-state.test.mjs +32 -92
- package/templates/starter/scripts/toolcraft-checkpoint-transaction.mjs +28 -6
- package/templates/starter/scripts/toolcraft-checkpoint-transaction.test.mjs +8 -63
- package/templates/starter/scripts/toolcraft-delivery-anchor.mjs +28 -8
- package/templates/starter/scripts/toolcraft-delivery-anchor.test.mjs +50 -61
- package/templates/starter/scripts/toolcraft-delivery-architecture-inventory.mjs +0 -4
- package/templates/starter/scripts/toolcraft-delivery-architecture-policy.mjs +0 -1
- package/templates/starter/scripts/toolcraft-delivery-evidence.test.mjs +2 -3
- package/templates/starter/scripts/toolcraft-delivery-executor.mjs +5 -1
- package/templates/starter/scripts/toolcraft-delivery-executor.test.mjs +5 -12
- package/templates/starter/scripts/toolcraft-delivery-functional-context.d.mts +1 -48
- package/templates/starter/scripts/toolcraft-delivery-functional-context.mjs +2 -30
- package/templates/starter/scripts/toolcraft-delivery-functional-context.test.mjs +4 -166
- package/templates/starter/scripts/toolcraft-delivery-functional-initial.test.mjs +4 -20
- package/templates/starter/scripts/toolcraft-delivery-lifecycle.mjs +43 -192
- package/templates/starter/scripts/toolcraft-delivery-performance-iteration-lifecycle.test.mjs +79 -440
- package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +102 -297
- package/templates/starter/scripts/toolcraft-delivery-plan-construction.mjs +46 -97
- package/templates/starter/scripts/toolcraft-delivery-plan-test-helpers.mjs +125 -0
- package/templates/starter/scripts/toolcraft-delivery-plan-validation.test.mjs +58 -0
- package/templates/starter/scripts/toolcraft-delivery-plan.d.mts +19 -31
- package/templates/starter/scripts/toolcraft-delivery-plan.mjs +254 -252
- package/templates/starter/scripts/toolcraft-delivery-plan.test.mjs +29 -457
- package/templates/starter/scripts/toolcraft-delivery-receipt-performance.test.mjs +1 -1
- package/templates/starter/scripts/toolcraft-delivery-receipt-strictness.test.mjs +3 -72
- package/templates/starter/scripts/toolcraft-delivery-receipt-test-helpers.mjs +7 -36
- package/templates/starter/scripts/toolcraft-delivery-receipt.d.mts +3 -5
- package/templates/starter/scripts/toolcraft-delivery-receipt.mjs +15 -31
- package/templates/starter/scripts/toolcraft-delivery-receipt.test.mjs +28 -20
- package/templates/starter/scripts/toolcraft-delivery-receipt.types.test.mts +22 -30
- package/templates/starter/scripts/toolcraft-feature-verification-plan.d.mts +20 -0
- package/templates/starter/scripts/toolcraft-feature-verification-plan.mjs +146 -0
- package/templates/starter/scripts/toolcraft-feature-verification-plan.test.mjs +96 -0
- package/templates/starter/scripts/toolcraft-functional-proof-model.d.mts +3 -18
- package/templates/starter/scripts/toolcraft-functional-proof-model.mjs +39 -127
- package/templates/starter/scripts/toolcraft-functional-proof-model.test.mjs +25 -362
- package/templates/starter/scripts/toolcraft-functional-proof-model.types.test.mts +6 -83
- package/templates/starter/scripts/toolcraft-functional-proof-primitives.d.mts +11 -0
- package/templates/starter/scripts/toolcraft-functional-proof-primitives.mjs +61 -0
- package/templates/starter/scripts/toolcraft-functional-proof-primitives.test.mjs +43 -0
- package/templates/starter/scripts/toolcraft-integrity-policy.mjs +13 -0
- package/templates/starter/scripts/toolcraft-integrity-test-utils.mjs +1 -1
- package/templates/starter/scripts/toolcraft-layer-evidence-facade.test.mjs +58 -22
- package/templates/starter/scripts/toolcraft-motion-reference-analysis-protocol.mjs +111 -0
- package/templates/starter/scripts/toolcraft-motion-reference-analysis-protocol.test.mjs +116 -0
- package/templates/starter/scripts/toolcraft-motion-reference-artifacts.d.mts +37 -0
- package/templates/starter/scripts/toolcraft-motion-reference-artifacts.mjs +308 -0
- package/templates/starter/scripts/toolcraft-motion-reference-artifacts.test.mjs +420 -0
- package/templates/starter/scripts/toolcraft-motion-reference-artifacts.types.test.mts +50 -0
- package/templates/starter/scripts/toolcraft-motion-reference-command-options.mjs +195 -0
- package/templates/starter/scripts/toolcraft-motion-reference-command-options.test.mjs +224 -0
- package/templates/starter/scripts/toolcraft-motion-reference-contact-sheet.mjs +122 -0
- package/templates/starter/scripts/toolcraft-motion-reference-contact-sheet.test.mjs +153 -0
- package/templates/starter/scripts/toolcraft-motion-reference-evidence-assembly.mjs +270 -0
- package/templates/starter/scripts/toolcraft-motion-reference-evidence.test.mjs +432 -0
- package/templates/starter/scripts/toolcraft-motion-reference-ffmpeg-output.mjs +275 -0
- package/templates/starter/scripts/toolcraft-motion-reference-ffmpeg-output.test.mjs +294 -0
- package/templates/starter/scripts/toolcraft-motion-reference-group-inventory.mjs +148 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-filter-graph.mjs +101 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-filter-graph.test.mjs +124 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-source.mjs +259 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-source.test.mjs +338 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-timing.mjs +96 -0
- package/templates/starter/scripts/toolcraft-motion-reference-image-timing.test.mjs +81 -0
- package/templates/starter/scripts/toolcraft-motion-reference-lock-recovery.test.mjs +144 -0
- package/templates/starter/scripts/toolcraft-motion-reference-media-source.mjs +165 -0
- package/templates/starter/scripts/toolcraft-motion-reference-media-source.test.mjs +299 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-discovery.test.mjs +142 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-isolation.test.mjs +234 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-schema.mjs +122 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-store.mjs +89 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-store.test.mjs +74 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal.mjs +264 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-journal.test.mjs +377 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-lease.mjs +254 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-lease.test.mjs +130 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-staging.mjs +128 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication-staging.test.mjs +209 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication.fixtures.mjs +88 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication.mjs +249 -0
- package/templates/starter/scripts/toolcraft-motion-reference-publication.test.mjs +433 -0
- package/templates/starter/scripts/toolcraft-motion-reference-review-output.mjs +38 -0
- package/templates/starter/scripts/toolcraft-motion-reference-review-output.test.mjs +29 -0
- package/templates/starter/scripts/toolcraft-motion-reference-root-safety.test.mjs +84 -0
- package/templates/starter/scripts/toolcraft-motion-reference-roots.mjs +83 -0
- package/templates/starter/scripts/toolcraft-motion-reference-sampling.mjs +283 -0
- package/templates/starter/scripts/toolcraft-motion-reference-sampling.test.mjs +480 -0
- package/templates/starter/scripts/toolcraft-motion-reference-stage-registry.mjs +39 -0
- package/templates/starter/scripts/toolcraft-motion-reference-stage-registry.test.mjs +59 -0
- package/templates/starter/scripts/toolcraft-motion-reference-stage.mjs +71 -0
- package/templates/starter/scripts/toolcraft-motion-reference-stage.test.mjs +61 -0
- package/templates/starter/scripts/toolcraft-motion-review-numeric.mjs +74 -0
- package/templates/starter/scripts/toolcraft-motion-review-numeric.test.mjs +65 -0
- package/templates/starter/scripts/toolcraft-motion-review-partitioning.mjs +268 -0
- package/templates/starter/scripts/toolcraft-motion-review-partitioning.test.mjs +329 -0
- package/templates/starter/scripts/toolcraft-motion-review-protected-units.mjs +120 -0
- package/templates/starter/scripts/toolcraft-motion-review-protected-units.test.mjs +111 -0
- package/templates/starter/scripts/toolcraft-performance-escalation-policy.test.mjs +77 -345
- package/templates/starter/scripts/toolcraft-performance-receipt-validation.test.mjs +19 -21
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +1 -1
- package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +10 -1
- package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +5 -1
- package/templates/starter/scripts/toolcraft-product-observable-facade.test.mjs +46 -1
- package/templates/starter/scripts/toolcraft-receipt-boundary.test.mjs +0 -1
- package/templates/starter/scripts/toolcraft-source-inventory.d.mts +5 -0
- package/templates/starter/scripts/toolcraft-source-inventory.mjs +25 -14
- package/templates/starter/scripts/toolcraft-source-inventory.test.mjs +48 -0
- package/templates/starter/scripts/toolcraft-source-ownership.mjs +0 -1
- package/templates/starter/scripts/toolcraft-source-ownership.test.mjs +30 -5
- package/templates/starter/scripts/toolcraft-verification-receipt-test-helpers.mjs +5 -26
- package/templates/starter/scripts/toolcraft-verification-receipt.d.mts +0 -7
- package/templates/starter/scripts/toolcraft-verification-receipt.mjs +3 -47
- package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +6 -87
- package/templates/starter/scripts/toolcraft-vitest-runtime-contract.d.mts +19 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-contract.mjs +107 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-contract.test.mjs +114 -0
- package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.mjs +13 -2
- package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.test.mjs +110 -0
- package/templates/starter/src/app/acceptance/artifact-export-intent.ts +51 -20
- package/templates/starter/src/app/acceptance/canvas-coverage.ts +1 -0
- package/templates/starter/src/app/acceptance/color-bank-labels.ts +11 -15
- package/templates/starter/src/app/acceptance/color-row-grouping.ts +33 -0
- package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +0 -11
- package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -3
- package/templates/starter/src/app/acceptance/control-component-rules.ts +0 -10
- package/templates/starter/src/app/acceptance/control-labels.ts +30 -1
- package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +18 -0
- package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +25 -17
- package/templates/starter/src/app/acceptance/feature-verification-selection.test.ts +295 -0
- package/templates/starter/src/app/acceptance/feature-verification-selection.ts +161 -0
- package/templates/starter/src/app/acceptance/infinity-canvas.ts +10 -0
- package/templates/starter/src/app/acceptance/interaction-ownership-intent.ts +19 -1
- package/templates/starter/src/app/acceptance/interaction-ownership.ts +18 -2
- package/templates/starter/src/app/acceptance/motion-reference-evidence-validation.mjs +65 -0
- package/templates/starter/src/app/acceptance/motion-reference-evidence-windows.mjs +301 -0
- package/templates/starter/src/app/acceptance/motion-reference-evidence.d.mts +11 -0
- package/templates/starter/src/app/acceptance/motion-reference-evidence.mjs +145 -0
- package/templates/starter/src/app/acceptance/motion-reference-study-behaviors.ts +162 -0
- package/templates/starter/src/app/acceptance/motion-reference-study-coverage.ts +206 -0
- package/templates/starter/src/app/acceptance/motion-reference-study-identity.ts +129 -0
- package/templates/starter/src/app/acceptance/motion-reference-study-partitions.ts +186 -0
- package/templates/starter/src/app/acceptance/motion-reference-study.ts +90 -0
- package/templates/starter/src/app/acceptance/output-export-actions.test.ts +9 -1
- package/templates/starter/src/app/acceptance/output-export-actions.ts +14 -22
- package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +5 -1
- package/templates/starter/src/app/acceptance/output-export-model.ts +4 -0
- package/templates/starter/src/app/acceptance/output-export.ts +4 -1
- package/templates/starter/src/app/acceptance/reference-study-types.ts +167 -17
- package/templates/starter/src/app/acceptance/runtime-coverage.ts +6 -1
- package/templates/starter/src/app/acceptance/selection-scope.test.ts +200 -0
- package/templates/starter/src/app/acceptance/selection-scope.ts +151 -0
- package/templates/starter/src/app/acceptance/transfer-mode-types.ts +114 -0
- package/templates/starter/src/app/acceptance/types.ts +43 -118
- package/templates/starter/src/app/acceptance/validate-coverage.ts +14 -3
- package/templates/starter/src/app/starter-acceptance-data.ts +1 -0
- package/templates/starter/src/app/starter-acceptance.animation-intent.test.ts +3 -0
- package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +81 -8
- package/templates/starter/src/app/starter-acceptance.background-export.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.color-row-grouping.test.ts +118 -0
- package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +1 -0
- package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +1 -0
- package/templates/starter/src/app/starter-acceptance.control-label-context.test.ts +1 -0
- package/templates/starter/src/app/starter-acceptance.control-labels.test.ts +78 -3
- package/templates/starter/src/app/starter-acceptance.control-naming-rules.test.ts +77 -1
- package/templates/starter/src/app/starter-acceptance.custom-control-fit-check.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +74 -3
- package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +5 -1
- package/templates/starter/src/app/starter-acceptance.image-export-settings.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +19 -3
- package/templates/starter/src/app/starter-acceptance.layers.test.ts +2 -1
- package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-evidence.test.ts +17 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-identity.test.ts +163 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-mapping.test.ts +133 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-ownership.test.ts +121 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-partitions.test.ts +210 -0
- package/templates/starter/src/app/starter-acceptance.motion-reference-test-utils.ts +313 -0
- package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +78 -4
- package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +6 -1
- package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +18 -1
- package/templates/starter/src/app/starter-acceptance.product-worklog.test.ts +123 -2
- package/templates/starter/src/app/starter-acceptance.reference-custom-timeline.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.reference-foundation.test.ts +8 -0
- package/templates/starter/src/app/starter-acceptance.reference-timeline.test.ts +7 -0
- package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.timeline-playback.test.ts +5 -0
- package/templates/starter/src/app/starter-acceptance.ts +34 -8
- package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +2 -0
- package/templates/starter/src/app/starter-acceptance.video-reference-study.test.ts +34 -236
- package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +4 -0
- package/templates/starter/src/app/starter-acceptance.worklog-test-utils.ts +122 -6
- package/templates/starter/src/app/starter-acceptance.worklog.test.ts +152 -16
- package/templates/starter/src/app/starter-performance.gates.test.ts +20 -18
- package/templates/starter/src/app/starter-performance.lifecycle.test.ts +22 -6
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +3 -0
- package/templates/starter/tsconfig.json +1 -0
- package/templates/starter/scripts/toolcraft-browser-impact-selection.mjs +0 -26
- package/templates/starter/scripts/toolcraft-delivery-framework-refresh.test.mjs +0 -141
- package/templates/starter/scripts/toolcraft-delivery-functional-changed.test.mjs +0 -468
- package/templates/starter/scripts/toolcraft-delivery-functional-model-lifecycle.test.mjs +0 -254
- package/templates/starter/scripts/toolcraft-delivery-functional-semantic-regression.test.mjs +0 -135
- package/templates/starter/scripts/toolcraft-delivery-performance-iteration-durability.test.mjs +0 -329
- package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +0 -430
- package/templates/starter/scripts/toolcraft-delivery-plan-functional-model.test.mjs +0 -358
- package/templates/starter/scripts/toolcraft-functional-proof-regression-fixtures.mjs +0 -298
- package/templates/starter/scripts/toolcraft-verification-impact-inventory.d.mts +0 -62
- package/templates/starter/scripts/toolcraft-verification-impact-inventory.mjs +0 -344
- package/templates/starter/scripts/toolcraft-verification-impact-inventory.test.mjs +0 -204
- package/templates/starter/scripts/toolcraft-verification-impact-performance-candidates.test.mjs +0 -210
- package/templates/starter/scripts/toolcraft-verification-impact-resolution.mjs +0 -344
- package/templates/starter/scripts/toolcraft-verification-impact-resolution.test.mjs +0 -270
- package/templates/starter/scripts/toolcraft-verification-impact-semantic-resolution.test.mjs +0 -449
- package/templates/starter/scripts/toolcraft-verification-impact-support-resolution.test.mjs +0 -195
- package/templates/starter/scripts/toolcraft-verification-impact.d.mts +0 -132
- package/templates/starter/scripts/toolcraft-verification-impact.mjs +0 -169
- package/templates/starter/scripts/toolcraft-verification-impact.test.mjs +0 -300
- package/templates/starter/scripts/toolcraft-verification-input-roles.mjs +0 -197
- package/templates/starter/scripts/toolcraft-verification-input-roles.test.mjs +0 -225
- package/templates/starter/src/app/acceptance/video-reference-study.ts +0 -217
- package/templates/starter/src/app/starter-verification-impact.json +0 -15
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
import type { Download, Page } from "@playwright/test";
|
|
5
|
+
import {
|
|
6
|
+
TOOLCRAFT_FORBIDDEN_SVG_ELEMENTS,
|
|
7
|
+
TOOLCRAFT_SVG_BACKGROUND_ATTRIBUTE,
|
|
8
|
+
TOOLCRAFT_SVG_NAMESPACE,
|
|
9
|
+
TOOLCRAFT_SVG_NON_RENDERING_CONTAINERS,
|
|
10
|
+
TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE,
|
|
11
|
+
TOOLCRAFT_SVG_VECTOR_PRIMITIVES,
|
|
12
|
+
} from "@repo/toolcraft-runtime";
|
|
13
|
+
|
|
14
|
+
import type { ToolcraftSvgArtifactInspection } from "./export-artifact-helpers";
|
|
15
|
+
|
|
16
|
+
export type ToolcraftInspectedSvgArtifact = Readonly<{
|
|
17
|
+
inspection: ToolcraftSvgArtifactInspection;
|
|
18
|
+
source: string;
|
|
19
|
+
}>;
|
|
20
|
+
|
|
21
|
+
const inspectedSvgArtifacts = new WeakSet<object>();
|
|
22
|
+
|
|
23
|
+
export function assertToolcraftInspectedSvgArtifact(
|
|
24
|
+
inspection: ToolcraftSvgArtifactInspection,
|
|
25
|
+
): void {
|
|
26
|
+
if (!inspectedSvgArtifacts.has(inspection)) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"Infinity SVG evidence requires an artifact from inspectToolcraftSvgDownload.",
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function inspectToolcraftSvgDownload({
|
|
34
|
+
download,
|
|
35
|
+
page,
|
|
36
|
+
}: Readonly<{
|
|
37
|
+
download: Download;
|
|
38
|
+
page: Page;
|
|
39
|
+
}>): Promise<ToolcraftInspectedSvgArtifact> {
|
|
40
|
+
const fileName = download.suggestedFilename();
|
|
41
|
+
if (!fileName.toLowerCase().endsWith(".svg")) {
|
|
42
|
+
throw new Error("Downloaded SVG artifact must use the .svg extension.");
|
|
43
|
+
}
|
|
44
|
+
const downloadPath = await download.path();
|
|
45
|
+
if (!downloadPath) {
|
|
46
|
+
throw new Error("Playwright did not expose the downloaded SVG path.");
|
|
47
|
+
}
|
|
48
|
+
const bytes = await fs.readFile(downloadPath);
|
|
49
|
+
if (bytes.byteLength === 0) {
|
|
50
|
+
throw new Error("Downloaded SVG artifact is empty.");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let source: string;
|
|
54
|
+
try {
|
|
55
|
+
source = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
56
|
+
} catch {
|
|
57
|
+
throw new Error("Downloaded SVG artifact is not valid UTF-8.");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const decoded = await page.evaluate(
|
|
61
|
+
async ({
|
|
62
|
+
backgroundAttribute,
|
|
63
|
+
forbiddenNames,
|
|
64
|
+
namespace,
|
|
65
|
+
nonRenderingNames,
|
|
66
|
+
primitiveNames,
|
|
67
|
+
productSceneAttribute,
|
|
68
|
+
svgSource,
|
|
69
|
+
}) => {
|
|
70
|
+
const parsed = new DOMParser().parseFromString(
|
|
71
|
+
svgSource,
|
|
72
|
+
"image/svg+xml",
|
|
73
|
+
);
|
|
74
|
+
if (parsed.querySelector("parsererror")) {
|
|
75
|
+
throw new Error("Downloaded SVG artifact is not valid XML.");
|
|
76
|
+
}
|
|
77
|
+
if (parsed.doctype) {
|
|
78
|
+
throw new Error("Downloaded SVG artifact cannot contain a doctype.");
|
|
79
|
+
}
|
|
80
|
+
const root = parsed.documentElement;
|
|
81
|
+
if (root.localName !== "svg" || root.namespaceURI !== namespace) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
"Downloaded SVG artifact must have a namespaced <svg> root.",
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const parseDimension = (name: "height" | "width"): number => {
|
|
88
|
+
const raw = root.getAttribute(name)?.trim() ?? "";
|
|
89
|
+
if (!/^(?:\d+\.?\d*|\.\d+)$/u.test(raw)) {
|
|
90
|
+
throw new Error(`Downloaded SVG artifact needs numeric ${name}.`);
|
|
91
|
+
}
|
|
92
|
+
const value = Number(raw);
|
|
93
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
94
|
+
throw new Error(`Downloaded SVG artifact needs positive ${name}.`);
|
|
95
|
+
}
|
|
96
|
+
return value;
|
|
97
|
+
};
|
|
98
|
+
const width = parseDimension("width");
|
|
99
|
+
const height = parseDimension("height");
|
|
100
|
+
const viewBox = (root.getAttribute("viewBox") ?? "")
|
|
101
|
+
.trim()
|
|
102
|
+
.split(/[\s,]+/u)
|
|
103
|
+
.map(Number);
|
|
104
|
+
if (
|
|
105
|
+
viewBox.length !== 4 ||
|
|
106
|
+
viewBox.some((value) => !Number.isFinite(value)) ||
|
|
107
|
+
Number(viewBox[2]) <= 0 ||
|
|
108
|
+
Number(viewBox[3]) <= 0 ||
|
|
109
|
+
Number(viewBox[2]) !== width ||
|
|
110
|
+
Number(viewBox[3]) !== height
|
|
111
|
+
) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
"Downloaded SVG artifact needs a coherent positive viewBox.",
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const productScenes = Array.from(
|
|
118
|
+
root.querySelectorAll(`[${productSceneAttribute}]`),
|
|
119
|
+
);
|
|
120
|
+
const productScene = productScenes[0] ?? null;
|
|
121
|
+
if (
|
|
122
|
+
productScenes.length !== 1 ||
|
|
123
|
+
productScene?.localName !== "g" ||
|
|
124
|
+
productScene.getAttribute(productSceneAttribute) !== "true" ||
|
|
125
|
+
!productScene.parentElement?.isSameNode(root)
|
|
126
|
+
) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
"Downloaded SVG artifact needs one runtime-owned product scene.",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const backgrounds = Array.from(
|
|
133
|
+
root.querySelectorAll(`[${backgroundAttribute}]`),
|
|
134
|
+
);
|
|
135
|
+
if (backgrounds.length > 1) {
|
|
136
|
+
throw new Error("Downloaded SVG artifact has duplicate runtime backgrounds.");
|
|
137
|
+
}
|
|
138
|
+
const background = backgrounds[0] ?? null;
|
|
139
|
+
let backgroundColor: string | null = null;
|
|
140
|
+
if (background) {
|
|
141
|
+
const backgroundFrame = ["x", "y", "width", "height"].map((name) =>
|
|
142
|
+
Number(background.getAttribute(name)),
|
|
143
|
+
);
|
|
144
|
+
if (
|
|
145
|
+
background.getAttribute(backgroundAttribute) !== "true" ||
|
|
146
|
+
background.localName !== "rect" ||
|
|
147
|
+
!background.parentElement?.isSameNode(root) ||
|
|
148
|
+
root.children[0] !== background ||
|
|
149
|
+
backgroundFrame.some((value) => !Number.isFinite(value)) ||
|
|
150
|
+
backgroundFrame.some((value, index) => value !== viewBox[index])
|
|
151
|
+
) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
"Downloaded SVG artifact has an invalid runtime background frame.",
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
backgroundColor = background.getAttribute("fill")?.trim() || null;
|
|
157
|
+
if (!backgroundColor) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
"Downloaded SVG artifact runtime background needs a fill color.",
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const expectedRootChildCount = background ? 2 : 1;
|
|
164
|
+
if (
|
|
165
|
+
root.children.length !== expectedRootChildCount ||
|
|
166
|
+
root.children[expectedRootChildCount - 1] !== productScene
|
|
167
|
+
) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
"Downloaded SVG artifact has unexpected root-owned content.",
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const forbidden = new Set(forbiddenNames);
|
|
174
|
+
const primitives = new Set(primitiveNames);
|
|
175
|
+
const nonRendering = new Set(nonRenderingNames);
|
|
176
|
+
const elements = [root, ...root.querySelectorAll("*")];
|
|
177
|
+
const definedIds = new Set<string>();
|
|
178
|
+
const referencedIds = new Set<string>();
|
|
179
|
+
let vectorElementCount = 0;
|
|
180
|
+
for (const element of elements) {
|
|
181
|
+
for (const node of element.childNodes) {
|
|
182
|
+
if (
|
|
183
|
+
node.nodeType !== Node.ELEMENT_NODE &&
|
|
184
|
+
node.nodeType !== Node.TEXT_NODE
|
|
185
|
+
) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
"Downloaded SVG artifact may contain only elements and text nodes.",
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (element.namespaceURI !== namespace) {
|
|
192
|
+
throw new Error("Downloaded SVG artifact mixes XML namespaces.");
|
|
193
|
+
}
|
|
194
|
+
const name = element.localName.toLowerCase();
|
|
195
|
+
if (forbidden.has(name)) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Downloaded SVG artifact contains forbidden <${element.localName}> content.`,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
if (
|
|
201
|
+
primitives.has(name) &&
|
|
202
|
+
productScene.contains(element)
|
|
203
|
+
) {
|
|
204
|
+
let ancestor = element.parentElement;
|
|
205
|
+
let hiddenDefinition = false;
|
|
206
|
+
while (ancestor && !ancestor.isSameNode(root)) {
|
|
207
|
+
hiddenDefinition ||= nonRendering.has(ancestor.localName.toLowerCase());
|
|
208
|
+
ancestor = ancestor.parentElement;
|
|
209
|
+
}
|
|
210
|
+
if (!hiddenDefinition) vectorElementCount += 1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (const attribute of element.attributes) {
|
|
214
|
+
const attributeName = attribute.localName.toLowerCase();
|
|
215
|
+
const value = attribute.value.trim();
|
|
216
|
+
if (attributeName === "id") {
|
|
217
|
+
if (!value || definedIds.has(value)) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
"Downloaded SVG artifact ids must be non-empty and unique.",
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
definedIds.add(value);
|
|
223
|
+
}
|
|
224
|
+
if (attributeName.startsWith("on")) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Downloaded SVG artifact contains event attribute ${attribute.name}.`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
if (
|
|
230
|
+
attributeName === "href" ||
|
|
231
|
+
attributeName === "src"
|
|
232
|
+
) {
|
|
233
|
+
if (!value.startsWith("#")) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`Downloaded SVG artifact contains external ${attribute.name}.`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
const referencedId = value.slice(1).trim();
|
|
239
|
+
if (!referencedId) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`Downloaded SVG artifact ${attribute.name} must name a local element.`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
referencedIds.add(referencedId);
|
|
245
|
+
}
|
|
246
|
+
const urlPattern = /url\(\s*(['"]?)(.*?)\1\s*\)/giu;
|
|
247
|
+
for (const match of value.matchAll(urlPattern)) {
|
|
248
|
+
const reference = match[2]?.trim() ?? "";
|
|
249
|
+
if (!reference.startsWith("#")) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Downloaded SVG artifact contains an external url() in ${attribute.name}.`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
const referencedId = reference.slice(1).trim();
|
|
255
|
+
if (!referencedId) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
`Downloaded SVG artifact url() in ${attribute.name} must name a local element.`,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
referencedIds.add(referencedId);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
for (const referencedId of referencedIds) {
|
|
265
|
+
if (!definedIds.has(referencedId)) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
`Downloaded SVG artifact local reference #${referencedId} has no target.`,
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (vectorElementCount === 0) {
|
|
272
|
+
throw new Error(
|
|
273
|
+
"Downloaded SVG artifact contains no visible vector primitive.",
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const objectUrl = URL.createObjectURL(
|
|
278
|
+
new Blob([svgSource], { type: "image/svg+xml" }),
|
|
279
|
+
);
|
|
280
|
+
const image = new Image();
|
|
281
|
+
try {
|
|
282
|
+
image.src = objectUrl;
|
|
283
|
+
await image.decode();
|
|
284
|
+
if (image.naturalWidth <= 0 || image.naturalHeight <= 0) {
|
|
285
|
+
throw new Error("Downloaded SVG artifact decoded with empty bounds.");
|
|
286
|
+
}
|
|
287
|
+
} finally {
|
|
288
|
+
URL.revokeObjectURL(objectUrl);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
backgroundColor,
|
|
293
|
+
height,
|
|
294
|
+
vectorElementCount,
|
|
295
|
+
viewBox: [
|
|
296
|
+
Number(viewBox[0]),
|
|
297
|
+
Number(viewBox[1]),
|
|
298
|
+
Number(viewBox[2]),
|
|
299
|
+
Number(viewBox[3]),
|
|
300
|
+
] as const,
|
|
301
|
+
width,
|
|
302
|
+
};
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
backgroundAttribute: TOOLCRAFT_SVG_BACKGROUND_ATTRIBUTE,
|
|
306
|
+
forbiddenNames: TOOLCRAFT_FORBIDDEN_SVG_ELEMENTS,
|
|
307
|
+
namespace: TOOLCRAFT_SVG_NAMESPACE,
|
|
308
|
+
nonRenderingNames: TOOLCRAFT_SVG_NON_RENDERING_CONTAINERS,
|
|
309
|
+
primitiveNames: TOOLCRAFT_SVG_VECTOR_PRIMITIVES,
|
|
310
|
+
productSceneAttribute: TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE,
|
|
311
|
+
svgSource: source,
|
|
312
|
+
},
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
const inspection = Object.freeze({
|
|
316
|
+
byteLength: bytes.byteLength,
|
|
317
|
+
backgroundColor: decoded.backgroundColor,
|
|
318
|
+
contentHash: createHash("sha256").update(bytes).digest("hex"),
|
|
319
|
+
height: decoded.height,
|
|
320
|
+
kind: "svg",
|
|
321
|
+
mediaType: "image/svg+xml",
|
|
322
|
+
vectorElementCount: decoded.vectorElementCount,
|
|
323
|
+
viewBox: decoded.viewBox,
|
|
324
|
+
width: decoded.width,
|
|
325
|
+
});
|
|
326
|
+
inspectedSvgArtifacts.add(inspection);
|
|
327
|
+
return Object.freeze({
|
|
328
|
+
inspection,
|
|
329
|
+
source,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Download, Page } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
export async function createToolcraftSvgFixtureDownload(
|
|
4
|
+
page: Page,
|
|
5
|
+
source: string,
|
|
6
|
+
fileName = "column-graph.svg",
|
|
7
|
+
): Promise<Download> {
|
|
8
|
+
await page.setContent('<button id="svg-download">Download SVG</button>');
|
|
9
|
+
await page.evaluate(
|
|
10
|
+
({ downloadFileName, svgSource }) => {
|
|
11
|
+
const button = document.querySelector<HTMLButtonElement>("#svg-download");
|
|
12
|
+
if (!button) throw new Error("SVG fixture download button is missing.");
|
|
13
|
+
button.addEventListener(
|
|
14
|
+
"click",
|
|
15
|
+
() => {
|
|
16
|
+
const url = URL.createObjectURL(
|
|
17
|
+
new Blob([svgSource], { type: "image/svg+xml;charset=utf-8" }),
|
|
18
|
+
);
|
|
19
|
+
const anchor = document.createElement("a");
|
|
20
|
+
anchor.download = downloadFileName;
|
|
21
|
+
anchor.href = url;
|
|
22
|
+
anchor.click();
|
|
23
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
24
|
+
},
|
|
25
|
+
{ once: true },
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
{ downloadFileName: fileName, svgSource: source },
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const downloadPromise = page.waitForEvent("download");
|
|
32
|
+
await page.locator("#svg-download").click();
|
|
33
|
+
return downloadPromise;
|
|
34
|
+
}
|
|
@@ -1,28 +1,211 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
FullConfig,
|
|
3
|
+
FullResult,
|
|
4
|
+
Reporter,
|
|
5
|
+
Suite,
|
|
6
|
+
} from "@playwright/test/reporter";
|
|
7
|
+
import path from "node:path";
|
|
2
8
|
|
|
3
9
|
import { deriveToolcraftPerformancePaths } from "@repo/toolcraft-runtime";
|
|
4
10
|
|
|
5
11
|
import { createToolcraftDeliveryCatalog } from "../scripts/playwright-test-title-selection.mjs";
|
|
6
12
|
import { getToolcraftPlaywrightContainingFile } from "../scripts/playwright-containing-file.mjs";
|
|
7
|
-
import {
|
|
13
|
+
import { getToolcraftMotionReferenceArtifactErrors } from "../scripts/toolcraft-motion-reference-artifacts.mjs";
|
|
14
|
+
import type { ToolcraftAcceptanceValidationInput } from "../src/app/acceptance/validate-coverage";
|
|
15
|
+
import {
|
|
16
|
+
starterAcceptance,
|
|
17
|
+
starterControlSectionInventory,
|
|
18
|
+
starterProductReadiness,
|
|
19
|
+
starterTransferMode,
|
|
20
|
+
validateToolcraftAcceptanceCoverage,
|
|
21
|
+
} from "../src/app/starter-acceptance";
|
|
8
22
|
import { starterPerformance } from "../src/app/starter-performance";
|
|
9
23
|
import { starterSchema } from "../src/app/starter-schema";
|
|
10
24
|
|
|
25
|
+
type ToolcraftDeliveryCatalogReferenceValidationInput =
|
|
26
|
+
ToolcraftAcceptanceValidationInput & { rootDir: string };
|
|
27
|
+
|
|
28
|
+
type ToolcraftMotionReferenceArtifactValidator =
|
|
29
|
+
typeof getToolcraftMotionReferenceArtifactErrors;
|
|
30
|
+
|
|
31
|
+
type ToolcraftDeliveryCatalogFactoryInput = Readonly<{
|
|
32
|
+
acceptance: typeof starterAcceptance;
|
|
33
|
+
availableTests: readonly Readonly<{ file: string; testName: string }>[];
|
|
34
|
+
derivePerformancePaths: typeof deriveToolcraftPerformancePaths;
|
|
35
|
+
performance: typeof starterPerformance;
|
|
36
|
+
rootDir: string;
|
|
37
|
+
schema: typeof starterSchema;
|
|
38
|
+
}>;
|
|
39
|
+
|
|
40
|
+
type ToolcraftDeliveryCatalogFactory = (
|
|
41
|
+
input: ToolcraftDeliveryCatalogFactoryInput,
|
|
42
|
+
) => unknown;
|
|
43
|
+
|
|
44
|
+
type ToolcraftDeliveryCatalogReporterState =
|
|
45
|
+
| Readonly<{ kind: "idle" }>
|
|
46
|
+
| Readonly<{ error: unknown; kind: "preparation-failed" }>
|
|
47
|
+
| Readonly<{
|
|
48
|
+
catalogInput: ToolcraftDeliveryCatalogFactoryInput;
|
|
49
|
+
kind: "ready";
|
|
50
|
+
referenceValidationInput: ToolcraftDeliveryCatalogReferenceValidationInput;
|
|
51
|
+
}>;
|
|
52
|
+
|
|
53
|
+
type ToolcraftDeliveryCatalogReporterOptions = Readonly<{
|
|
54
|
+
createCatalog?: ToolcraftDeliveryCatalogFactory;
|
|
55
|
+
validateReferences?: typeof validateToolcraftDeliveryCatalogReferences;
|
|
56
|
+
writeError?: (source: string) => void;
|
|
57
|
+
writeOutput?: (source: string) => void;
|
|
58
|
+
}>;
|
|
59
|
+
|
|
60
|
+
function getToolcraftPlaywrightProjectRoot(config: FullConfig): string {
|
|
61
|
+
if (
|
|
62
|
+
typeof config.configFile !== "string" ||
|
|
63
|
+
config.configFile.length === 0 ||
|
|
64
|
+
!path.isAbsolute(config.configFile)
|
|
65
|
+
) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
"Toolcraft delivery catalog requires an absolute Playwright configFile so project-relative reference artifacts resolve from the app root.",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return path.dirname(config.configFile);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
class ToolcraftReferenceValidationError extends Error {
|
|
74
|
+
override readonly name = "ToolcraftReferenceValidationError";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function describeUnexpectedError(
|
|
78
|
+
error: unknown,
|
|
79
|
+
seen: ReadonlySet<unknown> = new Set(),
|
|
80
|
+
): string {
|
|
81
|
+
if (!(error instanceof Error)) return String(error);
|
|
82
|
+
if (seen.has(error)) return "[circular error cause]";
|
|
83
|
+
const nextSeen = new Set(seen).add(error);
|
|
84
|
+
const description = error.stack ?? `${error.name}: ${error.message}`;
|
|
85
|
+
return error.cause === undefined
|
|
86
|
+
? description
|
|
87
|
+
: `${description}\nCaused by: ${describeUnexpectedError(error.cause, nextSeen)}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function describeReporterError(error: unknown): string {
|
|
91
|
+
return error instanceof ToolcraftReferenceValidationError
|
|
92
|
+
? error.message
|
|
93
|
+
: describeUnexpectedError(error);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function assertNever(value: never): never {
|
|
97
|
+
throw new Error(`Unexpected Toolcraft catalog reporter state: ${String(value)}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function validateToolcraftDeliveryCatalogReferences(
|
|
101
|
+
{
|
|
102
|
+
acceptance,
|
|
103
|
+
productReadiness,
|
|
104
|
+
rootDir,
|
|
105
|
+
schema,
|
|
106
|
+
sectionInventory,
|
|
107
|
+
transferMode,
|
|
108
|
+
}: ToolcraftDeliveryCatalogReferenceValidationInput,
|
|
109
|
+
validateArtifacts: ToolcraftMotionReferenceArtifactValidator =
|
|
110
|
+
getToolcraftMotionReferenceArtifactErrors,
|
|
111
|
+
): Promise<void> {
|
|
112
|
+
const semanticErrors = validateToolcraftAcceptanceCoverage({
|
|
113
|
+
acceptance,
|
|
114
|
+
productReadiness,
|
|
115
|
+
schema,
|
|
116
|
+
sectionInventory,
|
|
117
|
+
transferMode,
|
|
118
|
+
});
|
|
119
|
+
const artifactErrors = transferMode.referenceInputs.length === 0
|
|
120
|
+
? []
|
|
121
|
+
: await validateArtifacts({
|
|
122
|
+
referenceInputs: transferMode.referenceInputs,
|
|
123
|
+
rootDir,
|
|
124
|
+
});
|
|
125
|
+
const errors = [...semanticErrors, ...artifactErrors];
|
|
126
|
+
if (errors.length > 0) {
|
|
127
|
+
throw new ToolcraftReferenceValidationError(
|
|
128
|
+
`Toolcraft reference validation failed:\n${errors.join("\n")}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
11
133
|
export default class ToolcraftDeliveryCatalogReporter implements Reporter {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
134
|
+
private readonly createCatalog: ToolcraftDeliveryCatalogFactory;
|
|
135
|
+
private state: ToolcraftDeliveryCatalogReporterState = { kind: "idle" };
|
|
136
|
+
private readonly validateReferences: typeof validateToolcraftDeliveryCatalogReferences;
|
|
137
|
+
private readonly writeError: (source: string) => void;
|
|
138
|
+
private readonly writeOutput: (source: string) => void;
|
|
139
|
+
|
|
140
|
+
constructor(options: ToolcraftDeliveryCatalogReporterOptions = {}) {
|
|
141
|
+
this.createCatalog = options.createCatalog ?? createToolcraftDeliveryCatalog;
|
|
142
|
+
this.validateReferences =
|
|
143
|
+
options.validateReferences ?? validateToolcraftDeliveryCatalogReferences;
|
|
144
|
+
this.writeError = options.writeError ?? ((source) => {
|
|
145
|
+
process.stderr.write(source);
|
|
146
|
+
});
|
|
147
|
+
this.writeOutput = options.writeOutput ?? ((source) => {
|
|
148
|
+
process.stdout.write(source);
|
|
24
149
|
});
|
|
25
|
-
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
onBegin(config: FullConfig, suite: Suite): void {
|
|
153
|
+
try {
|
|
154
|
+
const projectRoot = getToolcraftPlaywrightProjectRoot(config);
|
|
155
|
+
const referenceValidationInput = {
|
|
156
|
+
acceptance: starterAcceptance,
|
|
157
|
+
productReadiness: starterProductReadiness,
|
|
158
|
+
rootDir: projectRoot,
|
|
159
|
+
schema: starterSchema,
|
|
160
|
+
sectionInventory: starterControlSectionInventory,
|
|
161
|
+
transferMode: starterTransferMode,
|
|
162
|
+
};
|
|
163
|
+
const availableTests = suite.allTests().map((entry) => ({
|
|
164
|
+
file: getToolcraftPlaywrightContainingFile(entry, config.rootDir),
|
|
165
|
+
testName: entry.title,
|
|
166
|
+
}));
|
|
167
|
+
const catalogInput = {
|
|
168
|
+
acceptance: starterAcceptance,
|
|
169
|
+
availableTests,
|
|
170
|
+
derivePerformancePaths: deriveToolcraftPerformancePaths,
|
|
171
|
+
performance: starterPerformance,
|
|
172
|
+
rootDir: config.rootDir,
|
|
173
|
+
schema: starterSchema,
|
|
174
|
+
};
|
|
175
|
+
this.state = { catalogInput, kind: "ready", referenceValidationInput };
|
|
176
|
+
} catch (error) {
|
|
177
|
+
this.state = { error, kind: "preparation-failed" };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async onEnd(_result: FullResult): Promise<{ status: "failed" } | void> {
|
|
182
|
+
const state = this.state;
|
|
183
|
+
switch (state.kind) {
|
|
184
|
+
case "idle":
|
|
185
|
+
return this.reportFailure(new Error(
|
|
186
|
+
"Toolcraft delivery catalog reporter did not receive a complete onBegin lifecycle.",
|
|
187
|
+
));
|
|
188
|
+
case "preparation-failed":
|
|
189
|
+
return this.reportFailure(state.error);
|
|
190
|
+
case "ready":
|
|
191
|
+
try {
|
|
192
|
+
await this.validateReferences(state.referenceValidationInput);
|
|
193
|
+
const catalog = this.createCatalog(state.catalogInput);
|
|
194
|
+
this.writeOutput(`${JSON.stringify(catalog)}\n`);
|
|
195
|
+
return;
|
|
196
|
+
} catch (error) {
|
|
197
|
+
return this.reportFailure(error);
|
|
198
|
+
}
|
|
199
|
+
default:
|
|
200
|
+
return assertNever(state);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private reportFailure(error: unknown): { status: "failed" } {
|
|
205
|
+
this.writeError(
|
|
206
|
+
`Toolcraft delivery catalog collection failed:\n${describeReporterError(error)}\n`,
|
|
207
|
+
);
|
|
208
|
+
return { status: "failed" };
|
|
26
209
|
}
|
|
27
210
|
|
|
28
211
|
printsToStdio(): boolean {
|