@crvouga/atlas 0.3.0 → 0.5.0
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/CHANGELOG.md +12 -0
- package/README.md +178 -604
- package/dist/bun/src/driver.d.ts +41 -7
- package/dist/bun/src/driver.js +47 -6
- package/dist/bun/src/index.d.ts +8 -0
- package/dist/bun/src/index.js +8 -0
- package/dist/core/src/cli.d.ts +14 -1
- package/dist/core/src/cli.js +30 -1
- package/dist/core/src/config.d.ts +70 -4
- package/dist/core/src/config.js +62 -3
- package/dist/core/src/events/cloudevents.d.ts +54 -9
- package/dist/core/src/events/cloudevents.js +60 -9
- package/dist/core/src/graph.d.ts +21 -0
- package/dist/core/src/graph.js +26 -0
- package/dist/core/src/index.d.ts +11 -0
- package/dist/core/src/index.js +11 -0
- package/dist/core/src/lint.d.ts +30 -0
- package/dist/core/src/lint.js +13 -0
- package/dist/core/src/plan.d.ts +39 -1
- package/dist/core/src/plan.js +28 -1
- package/dist/core/src/privacy.d.ts +29 -2
- package/dist/core/src/privacy.js +19 -2
- package/dist/core/src/report/formats.d.ts +34 -4
- package/dist/core/src/report/formats.js +24 -4
- package/dist/core/src/report/markdown.d.ts +15 -0
- package/dist/core/src/report/markdown.js +8 -0
- package/dist/core/src/run/clients.d.ts +44 -5
- package/dist/core/src/run/clients.js +46 -2
- package/dist/core/src/run/execute.d.ts +69 -16
- package/dist/core/src/run/execute.js +67 -16
- package/dist/core/src/run/frames.d.ts +13 -4
- package/dist/core/src/run/frames.js +19 -4
- package/dist/core/src/run/http.d.ts +34 -1
- package/dist/core/src/run/http.js +14 -1
- package/dist/core/src/run/types.d.ts +76 -3
- package/dist/core/src/run/types.js +19 -3
- package/dist/core/src/signals.d.ts +49 -5
- package/dist/core/src/signals.js +22 -5
- package/dist/core/src/spec/compose.d.ts +23 -1
- package/dist/core/src/spec/compose.js +10 -1
- package/dist/core/src/spec/index.d.ts +9 -1
- package/dist/core/src/spec/index.js +9 -1
- package/dist/core/src/spec/load.d.ts +22 -1
- package/dist/core/src/spec/load.js +31 -1
- package/dist/core/src/spec/parse.d.ts +16 -2
- package/dist/core/src/spec/parse.js +25 -2
- package/dist/core/src/spec/scxml.d.ts +25 -4
- package/dist/core/src/spec/scxml.js +36 -4
- package/dist/core/src/spec/types.d.ts +52 -1
- package/dist/core/src/spec/types.js +24 -0
- package/dist/detox/src/index.d.ts +54 -11
- package/dist/detox/src/index.js +45 -7
- package/dist/playwright/src/driver.d.ts +39 -3
- package/dist/playwright/src/driver.js +27 -3
- package/dist/playwright/src/index.d.ts +10 -0
- package/dist/playwright/src/index.js +10 -0
- package/dist/playwright/src/media.d.ts +50 -4
- package/dist/playwright/src/media.js +55 -4
- package/dist/playwright/src/signals.d.ts +44 -5
- package/dist/playwright/src/signals.js +29 -5
- package/dist/reports/src/attachment-files.d.ts +44 -0
- package/dist/reports/src/attachment-files.js +121 -0
- package/dist/reports/src/attachment-response.d.ts +31 -0
- package/dist/reports/src/attachment-response.js +55 -0
- package/dist/reports/src/aws-credentials.d.ts +48 -0
- package/dist/reports/src/aws-credentials.js +221 -0
- package/dist/reports/src/browser-navigation.d.ts +14 -0
- package/dist/reports/src/browser-navigation.js +7 -0
- package/dist/reports/src/bundle.d.ts +34 -2
- package/dist/reports/src/bundle.js +38 -2
- package/dist/reports/src/catalog.d.ts +34 -3
- package/dist/reports/src/catalog.js +17 -3
- package/dist/reports/src/cli-stores.d.ts +36 -0
- package/dist/reports/src/cli-stores.js +73 -0
- package/dist/reports/src/clock.d.ts +7 -2
- package/dist/reports/src/clock.js +5 -2
- package/dist/reports/src/completion-order.d.ts +18 -5
- package/dist/reports/src/completion-order.js +18 -5
- package/dist/reports/src/compose.d.ts +80 -0
- package/dist/reports/src/compose.js +117 -0
- package/dist/reports/src/copy-store.d.ts +29 -0
- package/dist/reports/src/copy-store.js +52 -0
- package/dist/reports/src/directory-outline.d.ts +38 -0
- package/dist/reports/src/directory-outline.js +103 -0
- package/dist/reports/src/directory-store.d.ts +20 -0
- package/dist/reports/src/directory-store.js +166 -0
- package/dist/reports/src/doctor.d.ts +29 -0
- package/dist/reports/src/doctor.js +83 -0
- package/dist/reports/src/effective-delta.d.ts +34 -0
- package/dist/reports/src/effective-delta.js +25 -0
- package/dist/reports/src/evidence-detail.d.ts +64 -0
- package/dist/reports/src/evidence-detail.js +92 -0
- package/dist/reports/src/evidence-live.d.ts +40 -4
- package/dist/reports/src/evidence-live.js +52 -4
- package/dist/reports/src/evidence-types.d.ts +70 -1
- package/dist/reports/src/evidence-types.js +5 -1
- package/dist/reports/src/evidence-view.d.ts +58 -6
- package/dist/reports/src/evidence-view.js +44 -5
- package/dist/reports/src/explorer-types.d.ts +40 -3
- package/dist/reports/src/explorer-types.js +8 -0
- package/dist/reports/src/explorer-versions.d.ts +9 -1
- package/dist/reports/src/explorer-versions.js +18 -1
- package/dist/reports/src/export-evidence.d.ts +16 -1
- package/dist/reports/src/export-evidence.js +52 -1
- package/dist/reports/src/fetcher.d.ts +27 -3
- package/dist/reports/src/fetcher.js +11 -3
- package/dist/reports/src/format-date.d.ts +6 -0
- package/dist/reports/src/format-date.js +6 -0
- package/dist/reports/src/format.d.ts +16 -0
- package/dist/reports/src/format.js +10 -0
- package/dist/reports/src/frames.d.ts +36 -3
- package/dist/reports/src/frames.js +22 -3
- package/dist/reports/src/hash.d.ts +18 -0
- package/dist/reports/src/hash.js +13 -0
- package/dist/reports/src/host.d.ts +29 -0
- package/dist/reports/src/host.js +64 -0
- package/dist/reports/src/html-frame.d.ts +5 -6
- package/dist/reports/src/html-frame.js +12 -1
- package/dist/reports/src/http-store.d.ts +52 -0
- package/dist/reports/src/http-store.js +145 -0
- package/dist/reports/src/http.d.ts +7 -0
- package/dist/reports/src/http.js +6 -0
- package/dist/reports/src/ingest.d.ts +54 -0
- package/dist/reports/src/ingest.js +210 -0
- package/dist/reports/src/init.d.ts +40 -0
- package/dist/reports/src/init.js +364 -0
- package/dist/reports/src/junit.d.ts +33 -6
- package/dist/reports/src/junit.js +87 -21
- package/dist/reports/src/keyboard.d.ts +20 -0
- package/dist/reports/src/keyboard.js +11 -0
- package/dist/reports/src/keys.d.ts +21 -3
- package/dist/reports/src/keys.js +21 -3
- package/dist/reports/src/layer-axes.d.ts +48 -0
- package/dist/reports/src/layer-axes.js +71 -0
- package/dist/reports/src/layer-catalog.d.ts +28 -1
- package/dist/reports/src/layer-catalog.js +19 -2
- package/dist/reports/src/layer-merge.d.ts +98 -9
- package/dist/reports/src/layer-merge.js +154 -33
- package/dist/reports/src/layer-source.d.ts +18 -0
- package/dist/reports/src/layer-source.js +14 -0
- package/dist/reports/src/layer-store.d.ts +42 -0
- package/dist/reports/src/layer-store.js +63 -5
- package/dist/reports/src/layer-types.d.ts +34 -1
- package/dist/reports/src/layers-route.d.ts +47 -4
- package/dist/reports/src/layers-route.js +88 -12
- package/dist/reports/src/media-types.d.ts +11 -0
- package/dist/reports/src/media-types.js +22 -0
- package/dist/reports/src/namespace-guard.d.ts +10 -1
- package/dist/reports/src/namespace-guard.js +10 -1
- package/dist/reports/src/navigation.d.ts +12 -0
- package/dist/reports/src/navigation.js +7 -0
- package/dist/reports/src/object-io.d.ts +11 -4
- package/dist/reports/src/object-io.js +4 -4
- package/dist/reports/src/object-store-conformance.d.ts +27 -0
- package/dist/reports/src/object-store-conformance.js +87 -0
- package/dist/reports/src/object-store.d.ts +33 -24
- package/dist/reports/src/object-store.js +17 -24
- package/dist/reports/src/parse-with.d.ts +16 -0
- package/dist/reports/src/parse-with.js +14 -0
- package/dist/reports/src/performance-part.d.ts +25 -1
- package/dist/reports/src/performance-part.js +15 -1
- package/dist/reports/src/performance.d.ts +22 -2
- package/dist/reports/src/performance.js +12 -2
- package/dist/reports/src/persist.d.ts +12 -3
- package/dist/reports/src/persist.js +12 -3
- package/dist/reports/src/phone.d.ts +18 -1
- package/dist/reports/src/phone.js +18 -1
- package/dist/reports/src/protocol.d.ts +30 -4
- package/dist/reports/src/protocol.js +15 -4
- package/dist/reports/src/publication.d.ts +21 -1
- package/dist/reports/src/publication.js +21 -1
- package/dist/reports/src/publish-checks.d.ts +35 -0
- package/dist/reports/src/publish-checks.js +122 -0
- package/dist/reports/src/publish.d.ts +44 -9
- package/dist/reports/src/publish.js +40 -7
- package/dist/reports/src/read.d.ts +46 -16
- package/dist/reports/src/read.js +38 -14
- package/dist/reports/src/recording-bundle.d.ts +13 -2
- package/dist/reports/src/recording-bundle.js +4 -2
- package/dist/reports/src/recording-keys.d.ts +15 -1
- package/dist/reports/src/recording-keys.js +15 -1
- package/dist/reports/src/remote-report.d.ts +28 -7
- package/dist/reports/src/remote-report.js +15 -7
- package/dist/reports/src/report-cli.d.ts +51 -0
- package/dist/reports/src/report-cli.js +485 -0
- package/dist/reports/src/report-index.d.ts +99 -0
- package/dist/reports/src/report-index.js +337 -0
- package/dist/reports/src/report-loader.d.ts +10 -0
- package/dist/reports/src/report-loader.js +6 -0
- package/dist/reports/src/report-parts.d.ts +29 -3
- package/dist/reports/src/report-parts.js +40 -3
- package/dist/reports/src/report-status.d.ts +48 -0
- package/dist/reports/src/report-status.js +156 -0
- package/dist/reports/src/report-types.d.ts +73 -0
- package/dist/reports/src/report-types.js +3 -0
- package/dist/reports/src/reporter.d.ts +67 -0
- package/dist/reports/src/reporter.js +128 -0
- package/dist/reports/src/reports-explorer-route.d.ts +541 -0
- package/dist/reports/src/reports-explorer-route.js +11 -0
- package/dist/reports/src/reports-list-route.d.ts +9 -0
- package/dist/reports/src/reports-list-route.js +9 -0
- package/dist/reports/src/reports-route.d.ts +561 -22
- package/dist/reports/src/reports-route.js +11 -3
- package/dist/reports/src/reports-store.d.ts +19 -2
- package/dist/reports/src/reports-store.js +10 -2
- package/dist/reports/src/resilient-cache.d.ts +14 -1
- package/dist/reports/src/resilient-cache.js +8 -1
- package/dist/reports/src/retention.d.ts +17 -1
- package/dist/reports/src/retention.js +5 -1
- package/dist/reports/src/run-bundle.d.ts +63 -0
- package/dist/reports/src/run-bundle.js +159 -0
- package/dist/reports/src/s3-store.d.ts +79 -0
- package/dist/reports/src/s3-store.js +295 -0
- package/dist/reports/src/scenario.d.ts +14 -4
- package/dist/reports/src/scenario.js +5 -4
- package/dist/reports/src/sigv4.d.ts +72 -0
- package/dist/reports/src/sigv4.js +96 -0
- package/dist/reports/src/snapshot-bundle.d.ts +24 -1
- package/dist/reports/src/snapshot-bundle.js +27 -2
- package/dist/reports/src/snapshot-surface.d.ts +20 -9
- package/dist/reports/src/snapshot-surface.js +32 -19
- package/dist/reports/src/source-content.d.ts +23 -0
- package/dist/reports/src/source-content.js +30 -1
- package/dist/reports/src/source-entries.d.ts +13 -0
- package/dist/reports/src/source-entries.js +6 -0
- package/dist/reports/src/source-part.d.ts +15 -0
- package/dist/reports/src/source-part.js +8 -0
- package/dist/reports/src/source-reader.d.ts +11 -0
- package/dist/reports/src/source-reader.js +5 -0
- package/dist/reports/src/spec-evidence.d.ts +12 -2
- package/dist/reports/src/spec-evidence.js +6 -2
- package/dist/reports/src/spec-inventory.d.ts +15 -1
- package/dist/reports/src/spec-inventory.js +22 -1
- package/dist/reports/src/spec-link.d.ts +28 -0
- package/dist/reports/src/spec-link.js +70 -0
- package/dist/reports/src/spec-set.d.ts +63 -0
- package/dist/reports/src/spec-set.js +177 -0
- package/dist/reports/src/status.d.ts +10 -0
- package/dist/reports/src/status.js +5 -0
- package/dist/reports/src/store-prefix.d.ts +28 -0
- package/dist/reports/src/store-prefix.js +22 -0
- package/dist/reports/src/store-url.d.ts +89 -0
- package/dist/reports/src/store-url.js +160 -0
- package/dist/reports/src/test-catalog-entity.d.ts +11 -0
- package/dist/reports/src/test-catalog-entity.js +6 -0
- package/dist/reports/src/types.d.ts +6 -1
- package/dist/reports/src/use-report-nav.d.ts +35 -0
- package/dist/reports/src/use-report-nav.js +12 -0
- package/dist/schema/src/api.d.ts +799 -0
- package/dist/schema/src/api.js +201 -0
- package/dist/schema/src/cli-results.d.ts +210 -0
- package/dist/schema/src/cli-results.js +124 -0
- package/dist/schema/src/common.d.ts +53 -0
- package/dist/schema/src/common.js +42 -0
- package/dist/schema/src/index.d.ts +14 -0
- package/dist/schema/src/index.js +14 -0
- package/dist/schema/src/layer.d.ts +2273 -0
- package/dist/schema/src/layer.js +462 -0
- package/dist/schema/src/manifest.d.ts +95 -0
- package/dist/schema/src/manifest.js +84 -0
- package/dist/schema/src/report-sources.d.ts +28 -0
- package/dist/schema/src/report-sources.js +25 -0
- package/dist/schema/src/runs-index.d.ts +16 -0
- package/dist/schema/src/runs-index.js +15 -0
- package/dist/schema/src/server-config.d.ts +93 -0
- package/dist/schema/src/server-config.js +73 -0
- package/dist/schema/src/spec.d.ts +69 -0
- package/dist/schema/src/spec.js +58 -0
- package/dist/server/src/app.d.ts +62 -0
- package/dist/server/src/app.js +419 -0
- package/dist/server/src/auth.d.ts +41 -0
- package/dist/server/src/auth.js +59 -0
- package/dist/server/src/config.d.ts +83 -0
- package/dist/server/src/config.js +187 -0
- package/dist/server/src/dev.d.ts +12 -0
- package/dist/server/src/dev.js +80 -0
- package/dist/server/src/events.d.ts +29 -0
- package/dist/server/src/events.js +101 -0
- package/dist/server/src/host.d.ts +30 -0
- package/dist/server/src/host.js +115 -0
- package/dist/server/src/index.d.ts +16 -0
- package/dist/server/src/index.js +16 -0
- package/dist/server/src/llms.d.ts +19 -0
- package/dist/server/src/llms.js +63 -0
- package/dist/server/src/main.d.ts +30 -0
- package/dist/server/src/main.js +112 -0
- package/dist/server/src/openapi.d.ts +59 -0
- package/dist/server/src/openapi.js +374 -0
- package/dist/server/src/problem.d.ts +8 -0
- package/dist/server/src/problem.js +39 -0
- package/dist/server/src/site.d.ts +13 -0
- package/dist/server/src/site.js +43 -0
- package/dist/server/src/store-api.d.ts +28 -0
- package/dist/server/src/store-api.js +159 -0
- package/dist/server/src/validate.d.ts +71 -0
- package/dist/server/src/validate.js +197 -0
- package/dist/vitest/src/index.d.ts +27 -5
- package/dist/vitest/src/index.js +18 -5
- package/dist/webdriver/src/driver.d.ts +63 -11
- package/dist/webdriver/src/driver.js +57 -6
- package/dist/webdriver/src/index.d.ts +8 -0
- package/dist/webdriver/src/index.js +1 -0
- package/dist/webdriver/src/protocol.d.ts +75 -6
- package/dist/webdriver/src/protocol.js +79 -6
- package/docs/agents.md +137 -0
- package/docs/deploying.md +345 -0
- package/docs/dogfooding.md +175 -0
- package/docs/extending.md +242 -0
- package/docs/integrating.md +485 -0
- package/docs/reference/bun.md +141 -0
- package/docs/reference/cli.md +146 -0
- package/docs/reference/core.md +2426 -0
- package/docs/reference/detox.md +150 -0
- package/docs/reference/http-api.md +303 -0
- package/docs/reference/playwright.md +303 -0
- package/docs/reference/reports.md +7098 -0
- package/docs/reference/schema.md +1651 -0
- package/docs/reference/server.md +685 -0
- package/docs/reference/vitest.md +45 -0
- package/docs/reference/webdriver.md +330 -0
- package/docs/report-backends.md +144 -0
- package/docs/report-contract.md +400 -0
- package/docs/reporting-issues.md +160 -0
- package/docs/server.md +285 -0
- package/docs/statecharts.md +641 -0
- package/llms-full.txt +3506 -0
- package/llms.txt +47 -0
- package/package.json +33 -5
- package/packages/core/README.md +2 -1
- package/packages/reports/README.md +69 -2
- package/packages/reports/STORAGE.md +5 -1
- package/packages/schema/README.md +14 -0
- package/packages/schema/schemas/check-detail.schema.json +1032 -0
- package/packages/schema/schemas/checks-result.schema.json +26 -0
- package/packages/schema/schemas/command-error.schema.json +35 -0
- package/packages/schema/schemas/doctor-result.schema.json +60 -0
- package/packages/schema/schemas/effective-delta.schema.json +490 -0
- package/packages/schema/schemas/effective-evidence.schema.json +471 -0
- package/packages/schema/schemas/evidence-page.schema.json +271 -0
- package/packages/schema/schemas/hello-event.schema.json +22 -0
- package/packages/schema/schemas/init-plan.schema.json +347 -0
- package/packages/schema/schemas/layer-rows.schema.json +199 -0
- package/packages/schema/schemas/layer.schema.json +2888 -0
- package/packages/schema/schemas/merge-result.schema.json +40 -0
- package/packages/schema/schemas/outline.schema.json +62 -0
- package/packages/schema/schemas/problem.schema.json +37 -0
- package/packages/schema/schemas/publish-checks.schema.json +1413 -0
- package/packages/schema/schemas/publish-result.schema.json +168 -0
- package/packages/schema/schemas/ready.schema.json +68 -0
- package/packages/schema/schemas/report-event.schema.json +100 -0
- package/packages/schema/schemas/report-status.schema.json +370 -0
- package/packages/schema/schemas/server-config.schema.json +194 -0
- package/packages/schema/schemas/server-info.schema.json +114 -0
- package/packages/schema/schemas/spec-set.schema.json +103 -0
- package/packages/schema/schemas/store-listing.schema.json +29 -0
- package/packages/schema/schemas/store-write-result.schema.json +28 -0
- package/packages/schema/schemas/stream-result.schema.json +59 -0
- package/packages/server/README.md +43 -0
- package/packages/server/openapi.json +6699 -0
- package/packages/visualizer/README.md +27 -5
- package/packages/visualizer/example/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/example/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/example/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/example/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/example/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/example/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/scripts/make-fixtures.ts +2 -0
- package/packages/visualizer/scripts/publish-data.ts +1 -2
- package/packages/visualizer/src/app/AppShell.tsx +9 -2
- package/packages/visualizer/src/app/report-sources.tsx +6 -4
- package/packages/visualizer/src/app/router.tsx +3 -3
- package/packages/visualizer/src/components/Header.module.css +35 -0
- package/packages/visualizer/src/components/Header.tsx +41 -4
- package/packages/visualizer/src/data/adapters/adapter.ts +5 -17
- package/packages/visualizer/src/data/adapters/index.ts +10 -1
- package/packages/visualizer/src/evidence/api.ts +14 -2
- package/packages/visualizer/src/evidence/artifacts.module.css +213 -0
- package/packages/visualizer/src/evidence/artifacts.tsx +272 -0
- package/packages/visualizer/src/evidence/check-detail.tsx +38 -4
- package/packages/visualizer/src/evidence/evidence-context.tsx +74 -12
- package/packages/visualizer/src/evidence/evidence.module.css +20 -0
- package/packages/visualizer/src/evidence/marks.tsx +2 -0
- package/packages/visualizer/src/evidence/overview.tsx +3 -3
- package/packages/visualizer/src/evidence/sources-panel.tsx +88 -34
- package/packages/visualizer/src/evidence/spec-page.tsx +23 -16
- package/packages/visualizer/src/find/Finder.tsx +1 -7
- package/packages/visualizer/src/find/open.ts +4 -0
- package/packages/visualizer/src/live/live-stream.ts +204 -0
- package/packages/visualizer/src/runtime.ts +19 -0
- package/packages/visualizer/src/state/ui-store.ts +6 -2
- package/packages/visualizer/vite/atlas-server-proxy.ts +36 -0
- package/packages/visualizer/vite/example-reports.ts +475 -0
- package/packages/visualizer/vite.config.ts +18 -2
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Run report formats
|
|
3
|
+
*
|
|
4
|
+
* Pure converters from a run's manifest (or a chart) to the formats other tools read: JUnit XML, CTRF
|
|
5
|
+
* JSON, WebVTT captions and a Mermaid state diagram. They take plain data, do no I/O and are
|
|
6
|
+
* deterministic. The manifest is read through a minimal structural type, so only the fields named
|
|
7
|
+
* there are required of it.
|
|
8
|
+
*
|
|
9
|
+
* @see docs/statecharts.md
|
|
10
|
+
*/
|
|
1
11
|
import type { MachineConfig } from '../spec/types.js';
|
|
2
12
|
import type { TimelineEntry } from '../run/types.js';
|
|
3
13
|
type ManifestLike = {
|
|
@@ -23,9 +33,19 @@ type ManifestLike = {
|
|
|
23
33
|
}[];
|
|
24
34
|
}[];
|
|
25
35
|
};
|
|
26
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Renders the manifest as JUnit XML (the format every CI system reads): one test case per path,
|
|
38
|
+
* with `classname` the path kind (`journey` or `generated`). A failed path is a `failure` carrying
|
|
39
|
+
* the last attempt's error, a `not-reached` path is `skipped` (with the blocking reason when there is
|
|
40
|
+
* one), and a flaky path passes with a note in `system-out`. The suite time is the run duration in seconds.
|
|
41
|
+
*/
|
|
27
42
|
export declare function toJUnit(manifest: ManifestLike): string;
|
|
28
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Renders the manifest as CTRF (Common Test Report Format, https://ctrf.io) JSON: one test per
|
|
45
|
+
* path, its steps as `steps`. `flaky` paths are `passed` with `flaky: true`, `not-reached` is
|
|
46
|
+
* `skipped`, and the failed path's trace (if any) is attached. Per-test `duration` is always 0:
|
|
47
|
+
* only the run's start and stop times are recorded.
|
|
48
|
+
*/
|
|
29
49
|
export declare function toCtrf(manifest: ManifestLike): {
|
|
30
50
|
reportFormat: string;
|
|
31
51
|
specVersion: string;
|
|
@@ -62,8 +82,18 @@ export declare function toCtrf(manifest: ManifestLike): {
|
|
|
62
82
|
}[];
|
|
63
83
|
};
|
|
64
84
|
};
|
|
65
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Renders WebVTT captions for a clip, one cue per timeline entry. A cue lasts until the next entry
|
|
87
|
+
* (or the clip's end), at least 500 ms and at most 4 s.
|
|
88
|
+
*
|
|
89
|
+
* @param timeline the step's entries, with `atMs` relative to the start of the clip.
|
|
90
|
+
* @param durationMs the clip's length in milliseconds.
|
|
91
|
+
*/
|
|
66
92
|
export declare function toWebVtt(timeline: TimelineEntry[], durationMs: number): string;
|
|
67
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* Renders a machine as a Mermaid `stateDiagram-v2` with nested and parallel states. States get
|
|
95
|
+
* short ids (`s0`, `s1`, ... in order of first use) and keep their names as labels; `:` and `;` in event
|
|
96
|
+
* names are replaced by spaces because Mermaid reads them as syntax. Written as `chart.mmd` in every run.
|
|
97
|
+
*/
|
|
68
98
|
export declare function toMermaid(machine: MachineConfig): string;
|
|
69
99
|
export {};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { targetOf } from '../spec/types.js';
|
|
2
2
|
const xml = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Renders the manifest as JUnit XML (the format every CI system reads): one test case per path,
|
|
5
|
+
* with `classname` the path kind (`journey` or `generated`). A failed path is a `failure` carrying
|
|
6
|
+
* the last attempt's error, a `not-reached` path is `skipped` (with the blocking reason when there is
|
|
7
|
+
* one), and a flaky path passes with a note in `system-out`. The suite time is the run duration in seconds.
|
|
8
|
+
*/
|
|
4
9
|
export function toJUnit(manifest) {
|
|
5
10
|
const cases = manifest.paths.map((p) => {
|
|
6
11
|
const body = p.status === 'failed'
|
|
@@ -24,7 +29,12 @@ export function toJUnit(manifest) {
|
|
|
24
29
|
''
|
|
25
30
|
].join('\n');
|
|
26
31
|
}
|
|
27
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Renders the manifest as CTRF (Common Test Report Format, https://ctrf.io) JSON: one test per
|
|
34
|
+
* path, its steps as `steps`. `flaky` paths are `passed` with `flaky: true`, `not-reached` is
|
|
35
|
+
* `skipped`, and the failed path's trace (if any) is attached. Per-test `duration` is always 0:
|
|
36
|
+
* only the run's start and stop times are recorded.
|
|
37
|
+
*/
|
|
28
38
|
export function toCtrf(manifest) {
|
|
29
39
|
const status = (s) => (s === 'passed' ? 'passed' : s === 'failed' ? 'failed' : s === 'flaky' ? 'passed' : 'skipped');
|
|
30
40
|
const tests = manifest.paths.map((p) => ({
|
|
@@ -62,7 +72,13 @@ function vttTime(ms) {
|
|
|
62
72
|
const pad = (n, w = 2) => String(n).padStart(w, '0');
|
|
63
73
|
return `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}.${pad(d.getUTCMilliseconds(), 3)}`;
|
|
64
74
|
}
|
|
65
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Renders WebVTT captions for a clip, one cue per timeline entry. A cue lasts until the next entry
|
|
77
|
+
* (or the clip's end), at least 500 ms and at most 4 s.
|
|
78
|
+
*
|
|
79
|
+
* @param timeline the step's entries, with `atMs` relative to the start of the clip.
|
|
80
|
+
* @param durationMs the clip's length in milliseconds.
|
|
81
|
+
*/
|
|
66
82
|
export function toWebVtt(timeline, durationMs) {
|
|
67
83
|
const cues = timeline.map((e, i) => {
|
|
68
84
|
const end = Math.min(timeline[i + 1]?.atMs ?? durationMs, e.atMs + 4_000);
|
|
@@ -71,7 +87,11 @@ export function toWebVtt(timeline, durationMs) {
|
|
|
71
87
|
});
|
|
72
88
|
return `WEBVTT\n\n${cues.join('\n\n')}\n`;
|
|
73
89
|
}
|
|
74
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Renders a machine as a Mermaid `stateDiagram-v2` with nested and parallel states. States get
|
|
92
|
+
* short ids (`s0`, `s1`, ... in order of first use) and keep their names as labels; `:` and `;` in event
|
|
93
|
+
* names are replaced by spaces because Mermaid reads them as syntax. Written as `chart.mmd` in every run.
|
|
94
|
+
*/
|
|
75
95
|
export function toMermaid(machine) {
|
|
76
96
|
const ids = new Map();
|
|
77
97
|
const id = (name) => {
|
|
@@ -1,8 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Markdown chart map
|
|
3
|
+
*
|
|
4
|
+
* Renders a chart (usually the composed product) as a Markdown document for the people who own the
|
|
5
|
+
* product, not for developers: plain Markdown with internal links, so it renders on any code host
|
|
6
|
+
* and diffs with the chart. Pure and deterministic.
|
|
7
|
+
*/
|
|
1
8
|
import type { Journey, MachineConfig } from '../spec/types.js';
|
|
2
9
|
/**
|
|
3
10
|
* A readable map of a chart for the people who own the product: one section per area (parallel
|
|
4
11
|
* regions apart), each state with what it means, what the user sees, and every event that can
|
|
5
12
|
* happen there and where it leads, then the named journeys. Plain Markdown, so it renders on any
|
|
6
13
|
* code host and diffs with the chart.
|
|
14
|
+
*
|
|
15
|
+
* Top-level states become sections, and each region of a top-level parallel state gets its own. A
|
|
16
|
+
* `snapshot` is shown only when it contains whitespace (so an identifier-like snapshot is left out),
|
|
17
|
+
* and a state with `confidence: 'assumed'` is marked as unconfirmed. Links use GitHub's heading anchors.
|
|
18
|
+
*
|
|
19
|
+
* @param machine the chart, composed if child charts should be inlined.
|
|
20
|
+
* @param journeys the named journeys to list at the end; none by default.
|
|
21
|
+
* @see docs/statecharts.md
|
|
7
22
|
*/
|
|
8
23
|
export declare function toMarkdown(machine: MachineConfig, journeys?: Journey[]): string;
|
|
@@ -47,6 +47,14 @@ const cell = (text) => text.replace(/\|/g, '\\|').replace(/\s*\n\s*/g, ' ');
|
|
|
47
47
|
* regions apart), each state with what it means, what the user sees, and every event that can
|
|
48
48
|
* happen there and where it leads, then the named journeys. Plain Markdown, so it renders on any
|
|
49
49
|
* code host and diffs with the chart.
|
|
50
|
+
*
|
|
51
|
+
* Top-level states become sections, and each region of a top-level parallel state gets its own. A
|
|
52
|
+
* `snapshot` is shown only when it contains whitespace (so an identifier-like snapshot is left out),
|
|
53
|
+
* and a state with `confidence: 'assumed'` is marked as unconfirmed. Links use GitHub's heading anchors.
|
|
54
|
+
*
|
|
55
|
+
* @param machine the chart, composed if child charts should be inlined.
|
|
56
|
+
* @param journeys the named journeys to list at the end; none by default.
|
|
57
|
+
* @see docs/statecharts.md
|
|
50
58
|
*/
|
|
51
59
|
export function toMarkdown(machine, journeys = []) {
|
|
52
60
|
const sections = sectionsOf(machine);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Driver, EventImplementation, Implementation, StateImplementation } from './types.js';
|
|
2
|
+
/** Options for `combineDrivers`. */
|
|
2
3
|
export type CombineDriversOptions = {
|
|
3
4
|
/**
|
|
4
5
|
* `focused` (default) screenshots the client a state is recognised on; `all` also screenshots
|
|
@@ -13,16 +14,36 @@ export type CombineDriversOptions = {
|
|
|
13
14
|
* act on (`onClient`, `combineImplementations`), so screenshots and clips come from that client's
|
|
14
15
|
* screen. Timeline entries are tagged with the client that made them.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
+
* The combined driver's `concurrency` is the smallest of the drivers' (1 for any that give none),
|
|
18
|
+
* its `pacing` is the first driver's that has one, and its name lists `<client>:<driver name>`.
|
|
19
|
+
* If any client fails to open, the ones that did open are closed and the open throws naming that client.
|
|
20
|
+
* Closing returns the first client's trace as `trace` and every client's in `traces`. Without
|
|
21
|
+
* a `focus`, `screenshot` and `record` use the first driver that supports them and `text` joins every
|
|
22
|
+
* client's text.
|
|
23
|
+
*
|
|
24
|
+
* @throws Error when `drivers` is empty.
|
|
25
|
+
* @see docs/statecharts.md
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const driver = combineDrivers<{ member: Page; api: HttpClient }>({
|
|
29
|
+
* member: playwrightDriver(),
|
|
30
|
+
* api: httpDriver('http://127.0.0.1:4000')
|
|
31
|
+
* });
|
|
17
32
|
*/
|
|
18
33
|
export declare function combineDrivers<M extends Record<string, unknown>>(drivers: {
|
|
19
34
|
[K in keyof M]: Driver<M[K]>;
|
|
20
35
|
}, options?: CombineDriversOptions): Driver<M>;
|
|
21
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Lifts a state implementation written against one client's context to the combined context and
|
|
38
|
+
* sets its `client`, so its screenshot comes from that client's screen.
|
|
39
|
+
*/
|
|
22
40
|
export declare function onClient<K extends string, C>(client: K, impl: StateImplementation<C>): StateImplementation<Record<K, C>>;
|
|
23
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Lifts an event implementation written against one client's context to the combined context and
|
|
43
|
+
* sets its `client`, so its clip comes from that client's screen and its timeline entries are tagged with it.
|
|
44
|
+
*/
|
|
24
45
|
export declare function onClient<K extends string, C>(client: K, impl: EventImplementation<C>): EventImplementation<Record<K, C>>;
|
|
25
|
-
/** Part of an implementation: what one client does and shows, or what spans clients. */
|
|
46
|
+
/** Part of an implementation (any subset of its fields): what one client does and shows, or what spans clients. */
|
|
26
47
|
export type ImplementationPart<C> = Partial<Implementation<C>>;
|
|
27
48
|
/**
|
|
28
49
|
* Merge per-client implementations into one. Each client's part is written against that client's
|
|
@@ -30,6 +51,21 @@ export type ImplementationPart<C> = Partial<Implementation<C>>;
|
|
|
30
51
|
* driver work unchanged; `shared` holds what needs several clients at once (read an id on one
|
|
31
52
|
* screen, act on it on another). `shared.setup` runs first (seed data), then each client's setup
|
|
32
53
|
* in the order the clients are listed. An event or state implemented twice is an error.
|
|
54
|
+
*
|
|
55
|
+
* A seed with the same name in several parts runs `shared`'s first, then each client's; the combined
|
|
56
|
+
* seed's `at` is the first one's and its `how` joins all of them. Correlation ids are pooled,
|
|
57
|
+
* and `canStart` returns the first reason any part gives.
|
|
58
|
+
*
|
|
59
|
+
* @param parts the implementation of each client, against that client's own context.
|
|
60
|
+
* @param shared events, states, seeds and setup that need several clients at once, against the combined context.
|
|
61
|
+
* @throws Error when an event or state is implemented by more than one part (including `shared`).
|
|
62
|
+
* @see docs/statecharts.md
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* const implementation = combineImplementations<{ member: SignalContext; api: HttpClient }>(
|
|
66
|
+
* { member: { events: {}, states: {} }, api: { events: {}, states: {} } },
|
|
67
|
+
* { setup: async ({ api }) => void (await api.post('/test/reset')) }
|
|
68
|
+
* );
|
|
33
69
|
*/
|
|
34
70
|
export declare function combineImplementations<M extends Record<string, unknown>>(parts: {
|
|
35
71
|
[K in keyof M]?: ImplementationPart<M[K]>;
|
|
@@ -37,6 +73,9 @@ export declare function combineImplementations<M extends Record<string, unknown>
|
|
|
37
73
|
/**
|
|
38
74
|
* Merge parts of one implementation written against the same context, such as one file per area
|
|
39
75
|
* of a large chart (sign-up, checkout, results). Events, states and seeds must each be
|
|
40
|
-
* implemented once; setups run in order,
|
|
76
|
+
* implemented once; setups run in order, `canStart` returns the first reason any part gives, and
|
|
77
|
+
* correlation ids are pooled without duplicates.
|
|
78
|
+
*
|
|
79
|
+
* @throws Error when an event, state or seed appears in more than one part.
|
|
41
80
|
*/
|
|
42
81
|
export declare function mergeImplementations<C>(...parts: ImplementationPart<C>[]): Implementation<C>;
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Multi-client composition
|
|
3
|
+
*
|
|
4
|
+
* Lets one run drive several clients at once (a phone, a desktop browser, an API) by combining their
|
|
5
|
+
* drivers into one driver whose context is a record keyed by client name, and their implementations
|
|
6
|
+
* into one implementation. Each event and state records which client it acts on, which decides whose
|
|
7
|
+
* screen is photographed and recorded. Owns no driver behaviour of its own; it only routes to the
|
|
8
|
+
* drivers it is given.
|
|
9
|
+
*
|
|
10
|
+
* @see docs/statecharts.md
|
|
11
|
+
*/
|
|
1
12
|
import path from 'node:path';
|
|
2
13
|
const inClientDirectory = (file, client) => path.join(path.dirname(file), client, path.basename(file));
|
|
3
14
|
/**
|
|
@@ -7,7 +18,21 @@ const inClientDirectory = (file, client) => path.join(path.dirname(file), client
|
|
|
7
18
|
* act on (`onClient`, `combineImplementations`), so screenshots and clips come from that client's
|
|
8
19
|
* screen. Timeline entries are tagged with the client that made them.
|
|
9
20
|
*
|
|
10
|
-
*
|
|
21
|
+
* The combined driver's `concurrency` is the smallest of the drivers' (1 for any that give none),
|
|
22
|
+
* its `pacing` is the first driver's that has one, and its name lists `<client>:<driver name>`.
|
|
23
|
+
* If any client fails to open, the ones that did open are closed and the open throws naming that client.
|
|
24
|
+
* Closing returns the first client's trace as `trace` and every client's in `traces`. Without
|
|
25
|
+
* a `focus`, `screenshot` and `record` use the first driver that supports them and `text` joins every
|
|
26
|
+
* client's text.
|
|
27
|
+
*
|
|
28
|
+
* @throws Error when `drivers` is empty.
|
|
29
|
+
* @see docs/statecharts.md
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const driver = combineDrivers<{ member: Page; api: HttpClient }>({
|
|
33
|
+
* member: playwrightDriver(),
|
|
34
|
+
* api: httpDriver('http://127.0.0.1:4000')
|
|
35
|
+
* });
|
|
11
36
|
*/
|
|
12
37
|
export function combineDrivers(drivers, options = {}) {
|
|
13
38
|
const names = Object.keys(drivers);
|
|
@@ -95,6 +120,7 @@ export function combineDrivers(drivers, options = {}) {
|
|
|
95
120
|
};
|
|
96
121
|
}
|
|
97
122
|
const scoped = (tools, client) => ({ ...tools, timeline: tools.timeline.for(client) });
|
|
123
|
+
/** Lifts a state or an event implementation to a client of a multi-client context (see the overloads above); a value with `recognize` is a state. */
|
|
98
124
|
export function onClient(client, impl) {
|
|
99
125
|
if ('recognize' in impl) {
|
|
100
126
|
const { recognize, lookIn, settle, checks, ...rest } = impl;
|
|
@@ -121,6 +147,21 @@ export function onClient(client, impl) {
|
|
|
121
147
|
* driver work unchanged; `shared` holds what needs several clients at once (read an id on one
|
|
122
148
|
* screen, act on it on another). `shared.setup` runs first (seed data), then each client's setup
|
|
123
149
|
* in the order the clients are listed. An event or state implemented twice is an error.
|
|
150
|
+
*
|
|
151
|
+
* A seed with the same name in several parts runs `shared`'s first, then each client's; the combined
|
|
152
|
+
* seed's `at` is the first one's and its `how` joins all of them. Correlation ids are pooled,
|
|
153
|
+
* and `canStart` returns the first reason any part gives.
|
|
154
|
+
*
|
|
155
|
+
* @param parts the implementation of each client, against that client's own context.
|
|
156
|
+
* @param shared events, states, seeds and setup that need several clients at once, against the combined context.
|
|
157
|
+
* @throws Error when an event or state is implemented by more than one part (including `shared`).
|
|
158
|
+
* @see docs/statecharts.md
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* const implementation = combineImplementations<{ member: SignalContext; api: HttpClient }>(
|
|
162
|
+
* { member: { events: {}, states: {} }, api: { events: {}, states: {} } },
|
|
163
|
+
* { setup: async ({ api }) => void (await api.post('/test/reset')) }
|
|
164
|
+
* );
|
|
124
165
|
*/
|
|
125
166
|
export function combineImplementations(parts, shared = {}) {
|
|
126
167
|
const clients = Object.keys(parts);
|
|
@@ -197,7 +238,10 @@ export function combineImplementations(parts, shared = {}) {
|
|
|
197
238
|
/**
|
|
198
239
|
* Merge parts of one implementation written against the same context, such as one file per area
|
|
199
240
|
* of a large chart (sign-up, checkout, results). Events, states and seeds must each be
|
|
200
|
-
* implemented once; setups run in order,
|
|
241
|
+
* implemented once; setups run in order, `canStart` returns the first reason any part gives, and
|
|
242
|
+
* correlation ids are pooled without duplicates.
|
|
243
|
+
*
|
|
244
|
+
* @throws Error when an event, state or seed appears in more than one part.
|
|
201
245
|
*/
|
|
202
246
|
export function mergeImplementations(...parts) {
|
|
203
247
|
const events = {};
|
|
@@ -17,6 +17,7 @@ export type Shard = {
|
|
|
17
17
|
};
|
|
18
18
|
/** What a run reports about: the spec, the scope, and the implementation's metadata. */
|
|
19
19
|
export type RunContext<C> = {
|
|
20
|
+
/** The loaded charts. Their files are re-read (to hash them into `specVersion`) when the manifest is built. */
|
|
20
21
|
bundle: SpecBundle;
|
|
21
22
|
composition: Composition;
|
|
22
23
|
graph: ChartGraph;
|
|
@@ -24,20 +25,27 @@ export type RunContext<C> = {
|
|
|
24
25
|
implementation: Implementation<C>;
|
|
25
26
|
/** The journeys of the plan, as the paths they are cut into; journeys are reported from them. */
|
|
26
27
|
journeys?: PlannedJourney[];
|
|
28
|
+
/** The plan's seeds, reported in the manifest with the paths that start from and verify each. */
|
|
27
29
|
seeds?: PlannedSeed[];
|
|
30
|
+
/** How a business event name is recognised, by name; see `EventMatcher`. */
|
|
28
31
|
eventMatchers?: Record<string, EventMatcher>;
|
|
29
32
|
privacy?: PrivacyRules;
|
|
30
33
|
/** Recorded in the manifest as the run's environment. */
|
|
31
34
|
environment?: Record<string, unknown>;
|
|
32
35
|
};
|
|
36
|
+
/** Everything `runPaths` needs: the `RunContext`, the paths to run and how to run them. */
|
|
33
37
|
export type RunOptions<C> = RunContext<C> & {
|
|
38
|
+
/** The whole plan, in plan order. Paths marked `skipRun` are reported but not run. The manifest covers all of them. */
|
|
34
39
|
paths: PlannedPath[];
|
|
35
40
|
driver: Driver<C>;
|
|
36
41
|
mode: RunMode;
|
|
37
42
|
/** Runs land in `<outputRoot>/<run-id>/`. */
|
|
38
43
|
outputRoot: string;
|
|
44
|
+
/** Whether a failed path runs a second time to tell flaky from broken; true unless `false`. */
|
|
39
45
|
retry?: boolean;
|
|
46
|
+
/** How long to wait for a state to be recognised after an event, in milliseconds; 45 000 when omitted. */
|
|
40
47
|
stepTimeoutMs?: number;
|
|
48
|
+
/** Where business events come from. Sources named with a `log:` prefix are treated as log files, not CloudEvents sources. */
|
|
41
49
|
eventSources?: EventSource[];
|
|
42
50
|
/** Path attempts that run at once, capped by the driver's `concurrency`; 1 when omitted. */
|
|
43
51
|
workers?: number;
|
|
@@ -48,6 +56,7 @@ export type RunOptions<C> = RunContext<C> & {
|
|
|
48
56
|
runDir: string;
|
|
49
57
|
outcomes: Record<string, PathOutcome>;
|
|
50
58
|
};
|
|
59
|
+
/** Receives progress lines; writes to stdout when omitted. */
|
|
51
60
|
log?: (line: string) => void;
|
|
52
61
|
};
|
|
53
62
|
type StepResult = {
|
|
@@ -83,44 +92,68 @@ type Observed = {
|
|
|
83
92
|
*/
|
|
84
93
|
export type PathOutcome = {
|
|
85
94
|
id: string;
|
|
95
|
+
/** The path's `key`; outcomes are matched across runs by it, not by `id`. */
|
|
86
96
|
key: string;
|
|
97
|
+
/** `failed` when the last attempt failed, `flaky` when the first failed and the retry passed, `not-reached` when the path stopped at a blocked event, else `passed`. */
|
|
87
98
|
status: Status;
|
|
88
99
|
attempts: Attempt[];
|
|
89
100
|
steps: StepResult[];
|
|
90
101
|
stoppedAt: string | null;
|
|
91
102
|
observed: Observed[];
|
|
103
|
+
/** Where and why the last attempt failed; `null` when it did not. */
|
|
92
104
|
failure: {
|
|
93
105
|
state: string;
|
|
94
106
|
recognizer: RecognizerResult | null;
|
|
95
107
|
looksLike: string[];
|
|
96
108
|
shot: Image | null;
|
|
97
109
|
} | null;
|
|
110
|
+
/** Privacy findings from the text the driver reported; they fail the run. */
|
|
98
111
|
leaks: string[];
|
|
112
|
+
/** Wall-clock milliseconds for all attempts of the path. */
|
|
99
113
|
durationMs: number;
|
|
100
114
|
/** How long the last attempt took to seed (or set up) and recognise its start. */
|
|
101
115
|
setupMs?: number;
|
|
102
116
|
/** The run the outcome was taken from, when it was reused rather than run again. */
|
|
103
117
|
reusedFrom?: string;
|
|
104
118
|
};
|
|
105
|
-
/** A rough cost of a path
|
|
119
|
+
/** A rough cost in milliseconds of a path that has never run (5 s to set up, 2.5 s a step); used to order and shard paths. */
|
|
106
120
|
export declare const estimateMs: (p: PlannedPath) => number;
|
|
107
121
|
/**
|
|
108
122
|
* The paths one shard runs: longest first onto the least loaded shard, by the plan alone, so
|
|
109
123
|
* every machine computes the same split. Paths reported without running go to the first shard.
|
|
110
124
|
*/
|
|
111
125
|
export declare function shardPaths(paths: PlannedPath[], shard: Shard): PlannedPath[];
|
|
112
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* Reads the `outcomes.json` of a run directory: the run id and each finished path's outcome by key.
|
|
128
|
+
* Returns `null` when the file is missing or unreadable, never throws.
|
|
129
|
+
*/
|
|
113
130
|
export declare function readOutcomes(runDir: string): {
|
|
114
131
|
runId: string;
|
|
115
132
|
outcomes: Record<string, PathOutcome>;
|
|
116
133
|
} | null;
|
|
117
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Milliseconds each path took, by path key, taken from the newest of the last 10 runs under
|
|
136
|
+
* `outputRoot` that ran it (outcomes reused from elsewhere are ignored). Empty when the directory
|
|
137
|
+
* does not exist. `runPaths` uses it to start the slowest paths first.
|
|
138
|
+
*/
|
|
118
139
|
export declare function durationHistory(outputRoot: string): Record<string, number>;
|
|
119
|
-
/** The newest run under `outputRoot` that wrote outcomes. */
|
|
140
|
+
/** The directory of the newest run under `outputRoot` that wrote `outcomes.json` (run ids sort by time), or `null`. */
|
|
120
141
|
export declare function latestRun(outputRoot: string): string | null;
|
|
121
142
|
/**
|
|
122
|
-
*
|
|
123
|
-
* the same manifest, however many workers ran them, in whatever order they finished.
|
|
143
|
+
* Folds path outcomes into the run's manifest object, in plan order: the same plan and outcomes
|
|
144
|
+
* always give the same manifest, however many workers ran them, in whatever order they finished.
|
|
145
|
+
*
|
|
146
|
+
* A state or transition is `passed` when a path reached it, `flaky` when only a flaky path did,
|
|
147
|
+
* `failed` when any failure touched it (a failed check fails its state), else `not-reached`. Reaching a
|
|
148
|
+
* state also marks its ancestors reached. The whole manifest is scanned for privacy findings, and any
|
|
149
|
+
* finding sets `privacy.passed` to false. Reads the chart files (for `specVersion`) and runs `git`
|
|
150
|
+
* in the current directory (for `commit` and `branch`, `null` when that fails). Apart from the
|
|
151
|
+
* wall-clock fields (`finishedAt`, `updatedAt`, `durationMs`) and those that depend on the working
|
|
152
|
+
* directory (`commit`, `branch`, `specDirectory`), two folds of the same outcomes are identical.
|
|
153
|
+
*
|
|
154
|
+
* @param outcomes finished or in-progress outcomes by path key; a path with none is `queued` or `running`.
|
|
155
|
+
* @param run.activePaths ids of the paths in flight; they are reported `running` and not counted complete.
|
|
156
|
+
* @param run.extra extra fields merged into `run` of the manifest (workers, shard, reused, mergedFrom).
|
|
124
157
|
*/
|
|
125
158
|
export declare function buildManifest<C>(context: RunContext<C>, paths: PlannedPath[], outcomes: Map<string, PathOutcome>, run: {
|
|
126
159
|
id: string;
|
|
@@ -305,8 +338,13 @@ export declare function buildManifest<C>(context: RunContext<C>, paths: PlannedP
|
|
|
305
338
|
mermaid: string;
|
|
306
339
|
};
|
|
307
340
|
};
|
|
341
|
+
/** The manifest object `buildManifest` returns; it is what `manifest.json` contains. */
|
|
308
342
|
export type Manifest = ReturnType<typeof buildManifest>;
|
|
309
|
-
/**
|
|
343
|
+
/**
|
|
344
|
+
* Writes a run's `junit.xml`, `ctrf.json`, `chart.mmd`, `outcomes.json` and `manifest.json` into
|
|
345
|
+
* `runDir` (each through a temporary file and a rename, so a reader never sees half a file) and returns
|
|
346
|
+
* the manifest. `outcomes.json` holds only finished paths, not those in `run.activePaths`.
|
|
347
|
+
*/
|
|
310
348
|
export declare function writeRun<C>(runDir: string, context: RunContext<C>, paths: PlannedPath[], outcomes: Map<string, PathOutcome>, run: Parameters<typeof buildManifest>[3]): {
|
|
311
349
|
$schema: string;
|
|
312
350
|
schemaVersion: number;
|
|
@@ -480,9 +518,14 @@ export declare function writeRun<C>(runDir: string, context: RunContext<C>, path
|
|
|
480
518
|
};
|
|
481
519
|
};
|
|
482
520
|
/**
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
* stands on its own. Later
|
|
521
|
+
* Composes the outcomes of several runs of the same plan (the shards of one run, or a rerun and
|
|
522
|
+
* the run before it) into one new run directory under `options.outputRoot`, folded in plan order. Media are
|
|
523
|
+
* copied in, so the merged run stands on its own. Later entries of `runDirs` win when two ran the same
|
|
524
|
+
* path; outcomes whose key is not in `paths` are ignored. The merged run's mode and driver name are
|
|
525
|
+
* taken from the run manifests unless `options` gives them.
|
|
526
|
+
*
|
|
527
|
+
* @returns the new `runDir`, its `manifest`, and `ok` (privacy passed and no path failed).
|
|
528
|
+
* @throws Error when a run directory has no `outcomes.json`.
|
|
486
529
|
*/
|
|
487
530
|
export declare function mergeRuns<C>(context: RunContext<C>, paths: PlannedPath[], runDirs: string[], options: {
|
|
488
531
|
outputRoot: string;
|
|
@@ -668,12 +711,22 @@ export declare function mergeRuns<C>(context: RunContext<C>, paths: PlannedPath[
|
|
|
668
711
|
ok: boolean;
|
|
669
712
|
};
|
|
670
713
|
/**
|
|
671
|
-
*
|
|
672
|
-
*
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
714
|
+
* Runs planned paths against a system through a driver and writes the run to
|
|
715
|
+
* `<outputRoot>/<timestamp>-<mode>/`. Paths start from their seed (or the implementation's
|
|
716
|
+
* `setup`) and run on a pool of workers, longest first (by earlier timings, else `estimateMs`),
|
|
717
|
+
* each attempt in its own driver context; every state is recognised from the outside after every step;
|
|
718
|
+
* a failed path is retried once to tell flaky from broken. The manifest is rewritten as each path
|
|
719
|
+
* finishes, from all outcomes so far in plan order, so a crash keeps what ran and the order paths
|
|
720
|
+
* finished in never changes the report.
|
|
721
|
+
*
|
|
722
|
+
* Workers are capped by `driver.concurrency` (1 if the driver gives none) and by the number of paths.
|
|
723
|
+
* Without `implementation.correlate`, a run on several workers reports business events as "no
|
|
724
|
+
* signal" rather than guess which attempt they belong to. While it runs it installs
|
|
725
|
+
* `uncaughtException` and `unhandledRejection` handlers that log and swallow stray errors from
|
|
726
|
+
* drivers, and removes them when it ends. Passed paths of `options.reuse` are kept instead of re-run.
|
|
727
|
+
*
|
|
728
|
+
* @returns the run directory, the final manifest, and `ok` (privacy passed and no path failed).
|
|
729
|
+
* @see docs/statecharts.md
|
|
677
730
|
*/
|
|
678
731
|
export declare function runPaths<C>(options: RunOptions<C>): Promise<{
|
|
679
732
|
runDir: string;
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Path runner and manifest
|
|
3
|
+
*
|
|
4
|
+
* Runs planned paths against the product through a driver and turns what happened into a run: a
|
|
5
|
+
* directory holding `manifest.json`, `junit.xml`, `ctrf.json`, `chart.mmd`, `outcomes.json` and `media/`.
|
|
6
|
+
* The invariants: the manifest is a pure fold of the plan and the per-path outcomes in plan order (so
|
|
7
|
+
* worker scheduling, sharding and reruns never change the report); it is rewritten atomically after
|
|
8
|
+
* every path so a crash keeps what ran; a failed path is retried once, and passing the second time is
|
|
9
|
+
* `flaky`. This module is the only one that touches the driver and the file system for a run. Its
|
|
10
|
+
* outputs follow the manifest schema in `packages/schema` (`schemaVersion: 1`).
|
|
11
|
+
*
|
|
12
|
+
* @see docs/statecharts.md
|
|
13
|
+
*/
|
|
1
14
|
import { execSync } from 'node:child_process';
|
|
2
15
|
import { createHash } from 'node:crypto';
|
|
3
16
|
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
@@ -25,7 +38,7 @@ function specVersion(bundle) {
|
|
|
25
38
|
const relative = (root, file) => path.relative(root, file).split(path.sep).join('/');
|
|
26
39
|
const pad = (n) => String(n).padStart(2, '0');
|
|
27
40
|
const sameStates = (a, b) => a.length === b.length && [...a].sort().join('|') === [...b].sort().join('|');
|
|
28
|
-
/** A rough cost of a path
|
|
41
|
+
/** A rough cost in milliseconds of a path that has never run (5 s to set up, 2.5 s a step); used to order and shard paths. */
|
|
29
42
|
export const estimateMs = (p) => 5_000 + 2_500 * p.steps.length;
|
|
30
43
|
/**
|
|
31
44
|
* The paths one shard runs: longest first onto the least loaded shard, by the plan alone, so
|
|
@@ -73,7 +86,10 @@ function copyMedia(outcome, from, to) {
|
|
|
73
86
|
cpSync(source, path.join(to, file));
|
|
74
87
|
}
|
|
75
88
|
}
|
|
76
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* Reads the `outcomes.json` of a run directory: the run id and each finished path's outcome by key.
|
|
91
|
+
* Returns `null` when the file is missing or unreadable, never throws.
|
|
92
|
+
*/
|
|
77
93
|
export function readOutcomes(runDir) {
|
|
78
94
|
const file = path.join(runDir, 'outcomes.json');
|
|
79
95
|
if (!existsSync(file))
|
|
@@ -86,7 +102,11 @@ export function readOutcomes(runDir) {
|
|
|
86
102
|
return null;
|
|
87
103
|
}
|
|
88
104
|
}
|
|
89
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Milliseconds each path took, by path key, taken from the newest of the last 10 runs under
|
|
107
|
+
* `outputRoot` that ran it (outcomes reused from elsewhere are ignored). Empty when the directory
|
|
108
|
+
* does not exist. `runPaths` uses it to start the slowest paths first.
|
|
109
|
+
*/
|
|
90
110
|
export function durationHistory(outputRoot) {
|
|
91
111
|
const history = {};
|
|
92
112
|
if (!existsSync(outputRoot))
|
|
@@ -104,7 +124,7 @@ export function durationHistory(outputRoot) {
|
|
|
104
124
|
}
|
|
105
125
|
return history;
|
|
106
126
|
}
|
|
107
|
-
/** The newest run under `outputRoot` that wrote outcomes. */
|
|
127
|
+
/** The directory of the newest run under `outputRoot` that wrote `outcomes.json` (run ids sort by time), or `null`. */
|
|
108
128
|
export function latestRun(outputRoot) {
|
|
109
129
|
if (!existsSync(outputRoot))
|
|
110
130
|
return null;
|
|
@@ -114,8 +134,20 @@ export function latestRun(outputRoot) {
|
|
|
114
134
|
return runs.length ? path.join(outputRoot, runs.at(-1)) : null;
|
|
115
135
|
}
|
|
116
136
|
/**
|
|
117
|
-
*
|
|
118
|
-
* the same manifest, however many workers ran them, in whatever order they finished.
|
|
137
|
+
* Folds path outcomes into the run's manifest object, in plan order: the same plan and outcomes
|
|
138
|
+
* always give the same manifest, however many workers ran them, in whatever order they finished.
|
|
139
|
+
*
|
|
140
|
+
* A state or transition is `passed` when a path reached it, `flaky` when only a flaky path did,
|
|
141
|
+
* `failed` when any failure touched it (a failed check fails its state), else `not-reached`. Reaching a
|
|
142
|
+
* state also marks its ancestors reached. The whole manifest is scanned for privacy findings, and any
|
|
143
|
+
* finding sets `privacy.passed` to false. Reads the chart files (for `specVersion`) and runs `git`
|
|
144
|
+
* in the current directory (for `commit` and `branch`, `null` when that fails). Apart from the
|
|
145
|
+
* wall-clock fields (`finishedAt`, `updatedAt`, `durationMs`) and those that depend on the working
|
|
146
|
+
* directory (`commit`, `branch`, `specDirectory`), two folds of the same outcomes are identical.
|
|
147
|
+
*
|
|
148
|
+
* @param outcomes finished or in-progress outcomes by path key; a path with none is `queued` or `running`.
|
|
149
|
+
* @param run.activePaths ids of the paths in flight; they are reported `running` and not counted complete.
|
|
150
|
+
* @param run.extra extra fields merged into `run` of the manifest (workers, shard, reused, mergedFrom).
|
|
119
151
|
*/
|
|
120
152
|
export function buildManifest(context, paths, outcomes, run) {
|
|
121
153
|
const { implementation: impl, graph, scope } = context;
|
|
@@ -406,7 +438,11 @@ function writeAtomic(file, text) {
|
|
|
406
438
|
writeFileSync(temporary, text);
|
|
407
439
|
renameSync(temporary, file);
|
|
408
440
|
}
|
|
409
|
-
/**
|
|
441
|
+
/**
|
|
442
|
+
* Writes a run's `junit.xml`, `ctrf.json`, `chart.mmd`, `outcomes.json` and `manifest.json` into
|
|
443
|
+
* `runDir` (each through a temporary file and a rename, so a reader never sees half a file) and returns
|
|
444
|
+
* the manifest. `outcomes.json` holds only finished paths, not those in `run.activePaths`.
|
|
445
|
+
*/
|
|
410
446
|
export function writeRun(runDir, context, paths, outcomes, run) {
|
|
411
447
|
const manifest = buildManifest(context, paths, outcomes, run);
|
|
412
448
|
writeAtomic(path.join(runDir, 'junit.xml'), toJUnit(manifest));
|
|
@@ -419,9 +455,14 @@ export function writeRun(runDir, context, paths, outcomes, run) {
|
|
|
419
455
|
}
|
|
420
456
|
const newRunId = (mode) => `${new Date().toISOString().replace(/[:.]/g, '-')}-${mode}`;
|
|
421
457
|
/**
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
* stands on its own. Later
|
|
458
|
+
* Composes the outcomes of several runs of the same plan (the shards of one run, or a rerun and
|
|
459
|
+
* the run before it) into one new run directory under `options.outputRoot`, folded in plan order. Media are
|
|
460
|
+
* copied in, so the merged run stands on its own. Later entries of `runDirs` win when two ran the same
|
|
461
|
+
* path; outcomes whose key is not in `paths` are ignored. The merged run's mode and driver name are
|
|
462
|
+
* taken from the run manifests unless `options` gives them.
|
|
463
|
+
*
|
|
464
|
+
* @returns the new `runDir`, its `manifest`, and `ok` (privacy passed and no path failed).
|
|
465
|
+
* @throws Error when a run directory has no `outcomes.json`.
|
|
425
466
|
*/
|
|
426
467
|
export function mergeRuns(context, paths, runDirs, options) {
|
|
427
468
|
const outcomes = new Map();
|
|
@@ -461,12 +502,22 @@ export function mergeRuns(context, paths, runDirs, options) {
|
|
|
461
502
|
return { runDir, manifest, ok: manifest.privacy.passed && manifest.paths.every((r) => r.status !== 'failed') };
|
|
462
503
|
}
|
|
463
504
|
/**
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
505
|
+
* Runs planned paths against a system through a driver and writes the run to
|
|
506
|
+
* `<outputRoot>/<timestamp>-<mode>/`. Paths start from their seed (or the implementation's
|
|
507
|
+
* `setup`) and run on a pool of workers, longest first (by earlier timings, else `estimateMs`),
|
|
508
|
+
* each attempt in its own driver context; every state is recognised from the outside after every step;
|
|
509
|
+
* a failed path is retried once to tell flaky from broken. The manifest is rewritten as each path
|
|
510
|
+
* finishes, from all outcomes so far in plan order, so a crash keeps what ran and the order paths
|
|
511
|
+
* finished in never changes the report.
|
|
512
|
+
*
|
|
513
|
+
* Workers are capped by `driver.concurrency` (1 if the driver gives none) and by the number of paths.
|
|
514
|
+
* Without `implementation.correlate`, a run on several workers reports business events as "no
|
|
515
|
+
* signal" rather than guess which attempt they belong to. While it runs it installs
|
|
516
|
+
* `uncaughtException` and `unhandledRejection` handlers that log and swallow stray errors from
|
|
517
|
+
* drivers, and removes them when it ends. Passed paths of `options.reuse` are kept instead of re-run.
|
|
518
|
+
*
|
|
519
|
+
* @returns the run directory, the final manifest, and `ok` (privacy passed and no path failed).
|
|
520
|
+
* @see docs/statecharts.md
|
|
470
521
|
*/
|
|
471
522
|
export async function runPaths(options) {
|
|
472
523
|
const { driver, implementation: impl, graph, scope } = options;
|