@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,4 +1,5 @@
|
|
|
1
1
|
import type { Clip } from './types.js';
|
|
2
|
+
/** Options for `recordFrames`. */
|
|
2
3
|
export type FrameRecorderOptions = {
|
|
3
4
|
/** The image format `capture` returns (default png). */
|
|
4
5
|
extension?: 'png' | 'jpg';
|
|
@@ -6,10 +7,18 @@ export type FrameRecorderOptions = {
|
|
|
6
7
|
maxFps?: number;
|
|
7
8
|
};
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
* step runs, then encode them with FFmpeg as H.264 MP4 at their real timing. Drivers
|
|
11
|
-
* native screen recorder (Bun WebView, WebDriver) use it; it needs `ffmpeg` on the PATH
|
|
12
|
-
*
|
|
10
|
+
* Starts recording a clip from any driver that can take a screenshot: capture frames back to back
|
|
11
|
+
* while the step runs, then encode them with FFmpeg as H.264 MP4 at their real timing. Drivers
|
|
12
|
+
* without a native screen recorder (Bun WebView, WebDriver) use it; it needs `ffmpeg` on the PATH.
|
|
13
|
+
*
|
|
14
|
+
* Capturing starts immediately and continues until `stop` is called. A `capture` that throws or
|
|
15
|
+
* returns no bytes just skips that frame. The returned `stop(output)` ends capture, writes the MP4 to
|
|
16
|
+
* `output` and resolves to the clip (`durationMs` is wall-clock time since the call), or to `null`
|
|
17
|
+
* when no frame was captured or FFmpeg is missing or fails. It writes no poster. Frames and the
|
|
18
|
+
* FFmpeg frame list are left in `workDirectory`.
|
|
19
|
+
*
|
|
20
|
+
* @param capture returns one screenshot as encoded image bytes in `options.extension`'s format.
|
|
21
|
+
* @param workDirectory where the frames are written; created if missing.
|
|
13
22
|
*/
|
|
14
23
|
export declare function recordFrames(capture: () => Promise<Uint8Array>, workDirectory: string, options?: FrameRecorderOptions): Promise<{
|
|
15
24
|
stop(output: string): Promise<Clip | null>;
|
|
@@ -1,11 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Frame-based clip recorder
|
|
3
|
+
*
|
|
4
|
+
* Builds a video clip for drivers that can only take screenshots: frames are captured repeatedly while
|
|
5
|
+
* a step runs and encoded afterwards with FFmpeg at their real timing. It owns no driver logic; the
|
|
6
|
+
* caller supplies the capture function. A missing or failing `ffmpeg` produces no clip, never an error.
|
|
7
|
+
*/
|
|
1
8
|
import { spawnSync } from 'node:child_process';
|
|
2
9
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
10
|
import path from 'node:path';
|
|
4
11
|
/**
|
|
5
|
-
*
|
|
6
|
-
* step runs, then encode them with FFmpeg as H.264 MP4 at their real timing. Drivers
|
|
7
|
-
* native screen recorder (Bun WebView, WebDriver) use it; it needs `ffmpeg` on the PATH
|
|
8
|
-
*
|
|
12
|
+
* Starts recording a clip from any driver that can take a screenshot: capture frames back to back
|
|
13
|
+
* while the step runs, then encode them with FFmpeg as H.264 MP4 at their real timing. Drivers
|
|
14
|
+
* without a native screen recorder (Bun WebView, WebDriver) use it; it needs `ffmpeg` on the PATH.
|
|
15
|
+
*
|
|
16
|
+
* Capturing starts immediately and continues until `stop` is called. A `capture` that throws or
|
|
17
|
+
* returns no bytes just skips that frame. The returned `stop(output)` ends capture, writes the MP4 to
|
|
18
|
+
* `output` and resolves to the clip (`durationMs` is wall-clock time since the call), or to `null`
|
|
19
|
+
* when no frame was captured or FFmpeg is missing or fails. It writes no poster. Frames and the
|
|
20
|
+
* FFmpeg frame list are left in `workDirectory`.
|
|
21
|
+
*
|
|
22
|
+
* @param capture returns one screenshot as encoded image bytes in `options.extension`'s format.
|
|
23
|
+
* @param workDirectory where the frames are written; created if missing.
|
|
9
24
|
*/
|
|
10
25
|
export async function recordFrames(capture, workDirectory, options = {}) {
|
|
11
26
|
mkdirSync(workDirectory, { recursive: true });
|
|
@@ -1,23 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module HTTP driver
|
|
3
|
+
*
|
|
4
|
+
* A driver (and a bare client) for acting on or observing a system over HTTP, with no browser or
|
|
5
|
+
* device: a fetch with a base URL, a cookie jar and timeline logging. It is the fastest driver and
|
|
6
|
+
* runs any number of attempts side by side, each with its own cookie jar. Uses the global `fetch`.
|
|
7
|
+
*/
|
|
1
8
|
import type { Driver, Timeline } from './types.js';
|
|
9
|
+
/** The response of an `HttpClient` call; the body is read in full. */
|
|
2
10
|
export type HttpResponse = {
|
|
3
11
|
status: number;
|
|
12
|
+
/** True for a 2xx status. A non-2xx status does not throw. */
|
|
4
13
|
ok: boolean;
|
|
5
14
|
headers: Headers;
|
|
15
|
+
/** The body as text. */
|
|
6
16
|
text: string;
|
|
17
|
+
/** The body parsed as JSON when the content type is JSON and it parses; `null` otherwise (also for a literal JSON `null`). */
|
|
7
18
|
json: unknown;
|
|
8
19
|
};
|
|
20
|
+
/** Per-call options of an `HttpClient`. */
|
|
9
21
|
export type HttpRequestOptions = {
|
|
22
|
+
/** A string is sent as is; anything else is sent as JSON with a `content-type: application/json` header. */
|
|
10
23
|
body?: unknown;
|
|
24
|
+
/** Added to the client's headers for this call and overriding them (and the cookie header) on conflict. */
|
|
11
25
|
headers?: Record<string, string>;
|
|
12
26
|
/** What the timeline says this call was for; `METHOD /path` when omitted. */
|
|
13
27
|
label?: string;
|
|
14
28
|
};
|
|
15
29
|
/** What every event and recogniser receives from an HTTP client: a fetch with a base URL and a cookie jar. */
|
|
16
30
|
export type HttpClient = {
|
|
31
|
+
/** The URL that request paths are resolved against with `new URL(path, baseUrl)`: a path starting with `/` replaces any path in the base URL. */
|
|
17
32
|
baseUrl: string;
|
|
18
33
|
/** Sent with every request; set an `authorization` header here after signing in. */
|
|
19
34
|
headers: Record<string, string>;
|
|
35
|
+
/** Cookie name to value, stored from `Set-Cookie` responses and sent back on every request. Only the name and value are kept (no expiry, path or domain handling). */
|
|
20
36
|
cookies: Map<string, string>;
|
|
37
|
+
/** Sends a request. Rejects on a network error or timeout; resolves for any HTTP status. Each call adds a `system` entry (method, path, status, never bodies) to the timeline. */
|
|
21
38
|
request(method: string, path: string, options?: HttpRequestOptions): Promise<HttpResponse>;
|
|
22
39
|
get(path: string, options?: HttpRequestOptions): Promise<HttpResponse>;
|
|
23
40
|
post(path: string, body?: unknown, options?: HttpRequestOptions): Promise<HttpResponse>;
|
|
@@ -25,17 +42,33 @@ export type HttpClient = {
|
|
|
25
42
|
patch(path: string, body?: unknown, options?: HttpRequestOptions): Promise<HttpResponse>;
|
|
26
43
|
delete(path: string, options?: HttpRequestOptions): Promise<HttpResponse>;
|
|
27
44
|
};
|
|
45
|
+
/** Options for `httpDriver` and `httpClient`. */
|
|
28
46
|
export type HttpDriverOptions = {
|
|
47
|
+
/** The driver's name and the `source` recorded on timeline entries; `http` when omitted. */
|
|
29
48
|
name?: string;
|
|
49
|
+
/** Headers sent with every request. */
|
|
30
50
|
headers?: Record<string, string>;
|
|
31
51
|
/** Per request, in milliseconds (default 30 000). */
|
|
32
52
|
timeoutMs?: number;
|
|
33
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Creates an `HttpClient` that logs each call to `timeline`. Use it directly when you need a client
|
|
56
|
+
* outside a driver; `httpDriver` creates one per path attempt.
|
|
57
|
+
*
|
|
58
|
+
* @param baseUrl the URL request paths are resolved against.
|
|
59
|
+
*/
|
|
34
60
|
export declare function httpClient(baseUrl: string, timeline: Timeline, options?: HttpDriverOptions): HttpClient;
|
|
35
61
|
/**
|
|
36
62
|
* The fastest driver: a client of an HTTP API, with no browser or device. Use it for actors that
|
|
37
63
|
* only talk to the system (a partner webhook, an operator API, a back office job) or to drive a
|
|
38
64
|
* whole chart at the API level. Each path attempt gets a fresh cookie jar; every call is logged to
|
|
39
|
-
* the step's timeline (method, path, status; never bodies).
|
|
65
|
+
* the step's timeline (method, path, status; never bodies). Has no screenshots or clips, and no limit on
|
|
66
|
+
* concurrent attempts.
|
|
67
|
+
*
|
|
68
|
+
* @param baseUrl the API's base URL, or a function returning it (called for each attempt).
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* const driver = httpDriver('http://127.0.0.1:4000', { headers: { 'x-test': '1' } });
|
|
72
|
+
* // events then receive an HttpClient: run: (api) => api.post('/orders', { sku: 'A1' })
|
|
40
73
|
*/
|
|
41
74
|
export declare function httpDriver(baseUrl: string | (() => string | Promise<string>), options?: HttpDriverOptions): Driver<HttpClient>;
|
|
@@ -8,6 +8,12 @@ function parseBody(text, contentType) {
|
|
|
8
8
|
return null;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates an `HttpClient` that logs each call to `timeline`. Use it directly when you need a client
|
|
13
|
+
* outside a driver; `httpDriver` creates one per path attempt.
|
|
14
|
+
*
|
|
15
|
+
* @param baseUrl the URL request paths are resolved against.
|
|
16
|
+
*/
|
|
11
17
|
export function httpClient(baseUrl, timeline, options = {}) {
|
|
12
18
|
const source = options.name ?? 'http';
|
|
13
19
|
const client = {
|
|
@@ -55,7 +61,14 @@ export function httpClient(baseUrl, timeline, options = {}) {
|
|
|
55
61
|
* The fastest driver: a client of an HTTP API, with no browser or device. Use it for actors that
|
|
56
62
|
* only talk to the system (a partner webhook, an operator API, a back office job) or to drive a
|
|
57
63
|
* whole chart at the API level. Each path attempt gets a fresh cookie jar; every call is logged to
|
|
58
|
-
* the step's timeline (method, path, status; never bodies).
|
|
64
|
+
* the step's timeline (method, path, status; never bodies). Has no screenshots or clips, and no limit on
|
|
65
|
+
* concurrent attempts.
|
|
66
|
+
*
|
|
67
|
+
* @param baseUrl the API's base URL, or a function returning it (called for each attempt).
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const driver = httpDriver('http://127.0.0.1:4000', { headers: { 'x-test': '1' } });
|
|
71
|
+
* // events then receive an HttpClient: run: (api) => api.post('/orders', { sku: 'A1' })
|
|
59
72
|
*/
|
|
60
73
|
export function httpDriver(baseUrl, options = {}) {
|
|
61
74
|
return {
|
|
@@ -1,29 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Run types and function driver
|
|
3
|
+
*
|
|
4
|
+
* The contracts between a run and the things it drives: `Driver` (owns the system under test for one
|
|
5
|
+
* path attempt), `Implementation` (how each event is made to happen and each state recognised), `Seed`,
|
|
6
|
+
* event sources and privacy rules, plus the status vocabulary and `Timeline`. These shapes are what
|
|
7
|
+
* integrators write against, so they change only by adding optional fields. Also holds
|
|
8
|
+
* `functionDriver`, the simplest complete driver.
|
|
9
|
+
*
|
|
10
|
+
* @see docs/statecharts.md
|
|
11
|
+
*/
|
|
1
12
|
import type { CloudEvent } from '../events/cloudevents.js';
|
|
2
13
|
import type { StateValue } from '../graph.js';
|
|
3
14
|
import type { PlannedPath } from '../plan.js';
|
|
15
|
+
/** `fast` runs without pacing and takes screenshots only; `showcase` paces steps like a person and records a clip of every transition. */
|
|
4
16
|
export type RunMode = 'fast' | 'showcase';
|
|
17
|
+
/** The result of a path, state, transition or journey: `flaky` passed only on the retry; `not-reached` was never run, was blocked, or has no covering path. */
|
|
5
18
|
export type Status = 'passed' | 'failed' | 'flaky' | 'not-reached';
|
|
19
|
+
/** Who or what makes an event happen: a `user` action, a `system` call, the passing of `time`, or a `hand-off` from a child chart (inserted by the planner, usually a no-op). */
|
|
6
20
|
export type EventKind = 'user' | 'system' | 'time' | 'hand-off';
|
|
7
21
|
/** `client` names which client's screen it is in a multi-client run; `also` holds the other clients' screens at the same moment. */
|
|
8
22
|
export type Image = {
|
|
23
|
+
/** Path to the PNG. A driver returns an absolute path; in a manifest it is relative to the run directory. */
|
|
9
24
|
png: string;
|
|
25
|
+
/** Path to a WebP thumbnail, when one was made. */
|
|
10
26
|
webp?: string;
|
|
11
27
|
client?: string;
|
|
12
28
|
also?: Image[];
|
|
13
29
|
};
|
|
30
|
+
/** A recorded video of one transition. */
|
|
14
31
|
export type Clip = {
|
|
32
|
+
/** Path to the MP4 (relative to the run directory in a manifest). */
|
|
15
33
|
video: string;
|
|
34
|
+
/** Path to a poster image, when one was made. */
|
|
16
35
|
poster?: string;
|
|
17
36
|
durationMs: number;
|
|
37
|
+
/** The client whose screen was recorded, in a multi-client run. */
|
|
18
38
|
client?: string;
|
|
19
39
|
};
|
|
40
|
+
/** One thing that happened during a step, which becomes a caption on the step's clip. */
|
|
20
41
|
export type TimelineEntry = {
|
|
42
|
+
/** Milliseconds since the step began (see `Timeline.restart`). */
|
|
21
43
|
atMs: number;
|
|
44
|
+
/** What kind of thing happened. */
|
|
22
45
|
kind: 'tap' | 'type' | 'system' | 'screen' | 'toast' | 'note' | 'time' | 'notification';
|
|
46
|
+
/** The caption. */
|
|
23
47
|
label: string;
|
|
48
|
+
/** For a tap, where it landed (page pixels for the Playwright driver). */
|
|
24
49
|
x?: number;
|
|
25
50
|
y?: number;
|
|
51
|
+
/** For typing, what was typed; shown in the caption. */
|
|
26
52
|
text?: string;
|
|
53
|
+
/** For a system call, what it was (never bodies). */
|
|
27
54
|
system?: {
|
|
28
55
|
source?: string;
|
|
29
56
|
endpoint?: string;
|
|
@@ -40,26 +67,37 @@ type TimelineState = {
|
|
|
40
67
|
};
|
|
41
68
|
/**
|
|
42
69
|
* What a step records while it runs: taps, typing, system calls, the screen change. `for(client)`
|
|
43
|
-
* returns a view onto the same timeline that tags every entry with that client.
|
|
70
|
+
* returns a view onto the same timeline that tags every entry with that client. The runner restarts
|
|
71
|
+
* it at the start of every step, so entries are relative to the step they belong to.
|
|
44
72
|
*/
|
|
45
73
|
export declare class Timeline {
|
|
46
74
|
private readonly state;
|
|
75
|
+
/** The client this view tags entries with; `undefined` on the root timeline. */
|
|
47
76
|
readonly client: string | undefined;
|
|
48
77
|
constructor(state?: TimelineState, client?: string);
|
|
78
|
+
/** Every entry added since the last `restart`, in order, shared by all views of the timeline. */
|
|
49
79
|
get entries(): TimelineEntry[];
|
|
80
|
+
/** Clears the entries and makes now the origin for `atMs`. */
|
|
50
81
|
restart(): void;
|
|
82
|
+
/** Appends an entry stamped with the time since the origin, and with this view's client when it has one. */
|
|
51
83
|
add(entry: Omit<TimelineEntry, 'atMs'>): void;
|
|
84
|
+
/** A view of the same timeline that tags what it adds with `client`. */
|
|
52
85
|
for(client: string): Timeline;
|
|
53
86
|
}
|
|
87
|
+
/** What a state recogniser found. */
|
|
54
88
|
export type RecognizerResult = {
|
|
89
|
+
/** Whether the state is showing. */
|
|
55
90
|
matched: boolean;
|
|
91
|
+
/** What was looked at, shown to explain a failure. `expected` is `visible` or `hidden`. */
|
|
56
92
|
signals: {
|
|
57
93
|
signal: string;
|
|
58
94
|
expected: 'visible' | 'hidden' | string;
|
|
59
95
|
visible: boolean;
|
|
60
96
|
}[];
|
|
61
97
|
};
|
|
98
|
+
/** The result of one of a state's `meta.checks`; a failed check fails the state. */
|
|
62
99
|
export type CheckResult = {
|
|
100
|
+
/** The check statement, quoted from the spec. */
|
|
63
101
|
check: string;
|
|
64
102
|
passed: boolean;
|
|
65
103
|
expected?: string;
|
|
@@ -87,7 +125,12 @@ export type Focus = {
|
|
|
87
125
|
* required; media hooks are used when present. `combineDrivers` runs several at once.
|
|
88
126
|
*/
|
|
89
127
|
export type Driver<C> = {
|
|
128
|
+
/** Shown in reports as the driver the run used. */
|
|
90
129
|
name: string;
|
|
130
|
+
/**
|
|
131
|
+
* Creates a fresh context for one attempt of one path. `attempt` is 1, or 2 for the retry;
|
|
132
|
+
* `directory` is a scratch directory for this attempt; the driver should log what it does to `timeline`.
|
|
133
|
+
*/
|
|
91
134
|
open(input: {
|
|
92
135
|
path: PlannedPath;
|
|
93
136
|
attempt: number;
|
|
@@ -95,6 +138,10 @@ export type Driver<C> = {
|
|
|
95
138
|
directory: string;
|
|
96
139
|
timeline: Timeline;
|
|
97
140
|
}): Promise<C>;
|
|
141
|
+
/**
|
|
142
|
+
* Releases the context. `traceFile` is where a trace may be written when `failed`; return the path
|
|
143
|
+
* actually written as `trace` (or one per client as `traces`) to attach it to the report.
|
|
144
|
+
*/
|
|
98
145
|
close(ctx: C, input: {
|
|
99
146
|
failed: boolean;
|
|
100
147
|
traceFile: string;
|
|
@@ -124,10 +171,12 @@ export type Driver<C> = {
|
|
|
124
171
|
* single device is not). Runs use at most this many workers; 1 when omitted.
|
|
125
172
|
*/
|
|
126
173
|
concurrency?: number;
|
|
174
|
+
/** Releases what is shared by all contexts (a browser, a server); called once after the last path. */
|
|
127
175
|
dispose?(): Promise<void>;
|
|
128
176
|
};
|
|
129
177
|
/** How one event is made to happen. Keyed by the event's exact name in the spec. */
|
|
130
178
|
export type EventImplementation<C> = {
|
|
179
|
+
/** Who makes the event happen; recorded with the event in the manifest. */
|
|
131
180
|
kind: EventKind;
|
|
132
181
|
/** The client that does it, in a multi-client run: its screen is recorded for the clip. */
|
|
133
182
|
client?: string;
|
|
@@ -135,12 +184,14 @@ export type EventImplementation<C> = {
|
|
|
135
184
|
how: string;
|
|
136
185
|
/** Test data the event needs before the path starts (a condition modelled as an event). */
|
|
137
186
|
prepare?(ctx: C, tools: StepTools): Promise<void>;
|
|
187
|
+
/** Makes the event happen. The runner then waits for the target state to be recognised, so `run` does not need to wait for it; it should throw only when the event cannot be done. */
|
|
138
188
|
run(ctx: C, tools: StepTools): Promise<void>;
|
|
139
189
|
/** Set when the event cannot be triggered reliably yet: the path stops before it. */
|
|
140
190
|
blocked?: string;
|
|
141
191
|
};
|
|
142
192
|
/** How one state is recognised from the outside, and what is checked there. */
|
|
143
193
|
export type StateImplementation<C> = {
|
|
194
|
+
/** Looks at the system from the outside and says whether the state is showing. Called repeatedly (with growing pauses, until `stepTimeoutMs`) while the runner waits for the state, so it must be cheap and side-effect free. */
|
|
144
195
|
recognize(ctx: C): Promise<RecognizerResult>;
|
|
145
196
|
/** The client whose screen shows it, in a multi-client run: that screen is the screenshot. */
|
|
146
197
|
client?: string;
|
|
@@ -152,6 +203,7 @@ export type StateImplementation<C> = {
|
|
|
152
203
|
lookIn?(ctx: C): Promise<void>;
|
|
153
204
|
/** Within-state actions after the screenshot (dismiss a success message). */
|
|
154
205
|
settle?(ctx: C): Promise<void>;
|
|
206
|
+
/** Results for the state's `meta.checks`, evaluated once the state is recognised. */
|
|
155
207
|
checks?(ctx: C): Promise<CheckResult[]>;
|
|
156
208
|
};
|
|
157
209
|
/**
|
|
@@ -184,8 +236,15 @@ export type Seed<C> = {
|
|
|
184
236
|
/** Set when the configuration cannot be seeded yet: paths start from another seed. */
|
|
185
237
|
blocked?: string;
|
|
186
238
|
};
|
|
239
|
+
/**
|
|
240
|
+
* How a driver makes each event of the spec happen and recognises each state: everything the charts
|
|
241
|
+
* do not say. Events and states are keyed by their exact names in the spec; only leaf states need a
|
|
242
|
+
* recogniser.
|
|
243
|
+
*/
|
|
187
244
|
export type Implementation<C> = {
|
|
245
|
+
/** By event name; an event with no entry is reported as blocked. */
|
|
188
246
|
events: Record<string, EventImplementation<C>>;
|
|
247
|
+
/** By state name; only leaf states need one. */
|
|
189
248
|
states: Record<string, StateImplementation<C>>;
|
|
190
249
|
/**
|
|
191
250
|
* Named seeds: configurations paths can start from. Journeys are cut at every seeded
|
|
@@ -207,13 +266,16 @@ export type Implementation<C> = {
|
|
|
207
266
|
};
|
|
208
267
|
/** Where business events come from: a CloudEvents endpoint, a log, a message queue. */
|
|
209
268
|
export type EventSource = {
|
|
269
|
+
/** Identifies the source; a name starting with `log:` marks a log file, anything else counts as a CloudEvents source. */
|
|
210
270
|
name: string;
|
|
211
271
|
/** Where "now" is in the source; `collect` returns what arrived after this mark. */
|
|
212
272
|
mark(): unknown;
|
|
273
|
+
/** Returns what arrived since `mark` (everything when omitted): CloudEvents and/or raw log text. */
|
|
213
274
|
collect(mark?: unknown): Promise<{
|
|
214
275
|
events: CloudEvent[];
|
|
215
276
|
text?: string;
|
|
216
277
|
}>;
|
|
278
|
+
/** Releases the source; called when the run ends. */
|
|
217
279
|
close?(): Promise<void>;
|
|
218
280
|
};
|
|
219
281
|
/** How a business event named in `meta.events` is recognised in what a source collected. */
|
|
@@ -222,6 +284,7 @@ export type EventMatcher = {
|
|
|
222
284
|
} | {
|
|
223
285
|
pattern: RegExp;
|
|
224
286
|
} | ((events: CloudEvent[], text: string) => boolean);
|
|
287
|
+
/** Rules for what may appear on screen and in a manifest. They extend the defaults (see `DEFAULT_PRIVACY`) except `allowHosts`, which replaces them. */
|
|
225
288
|
export type PrivacyRules = {
|
|
226
289
|
/** Patterns that must never appear on screen or in a manifest. */
|
|
227
290
|
deny?: {
|
|
@@ -234,8 +297,18 @@ export type PrivacyRules = {
|
|
|
234
297
|
allowHosts?: string[];
|
|
235
298
|
};
|
|
236
299
|
/**
|
|
237
|
-
* A driver for anything you can reach from
|
|
238
|
-
* returns a fresh context per path attempt; recognisers and events work on it directly.
|
|
300
|
+
* A driver for anything you can reach from code: a domain model, an API client, a CLI. `create`
|
|
301
|
+
* returns a fresh context per path attempt; recognisers and events work on it directly. It takes
|
|
302
|
+
* no screenshots or clips, and any number of attempts can run side by side unless `concurrency` says otherwise.
|
|
303
|
+
*
|
|
304
|
+
* @param create builds the context for one attempt; it may be async.
|
|
305
|
+
* @param options.name the driver's name in reports; `function` when omitted.
|
|
306
|
+
* @param options.dispose called with the context when the attempt ends.
|
|
307
|
+
* @param options.concurrency how many contexts may be open at once; unlimited when omitted.
|
|
308
|
+
* @see docs/statecharts.md
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* const driver = functionDriver(() => new TodoModel());
|
|
239
312
|
*/
|
|
240
313
|
export declare function functionDriver<C>(create: (input: {
|
|
241
314
|
path: PlannedPath;
|
|
@@ -1,31 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What a step records while it runs: taps, typing, system calls, the screen change. `for(client)`
|
|
3
|
-
* returns a view onto the same timeline that tags every entry with that client.
|
|
3
|
+
* returns a view onto the same timeline that tags every entry with that client. The runner restarts
|
|
4
|
+
* it at the start of every step, so entries are relative to the step they belong to.
|
|
4
5
|
*/
|
|
5
6
|
export class Timeline {
|
|
6
7
|
state;
|
|
8
|
+
/** The client this view tags entries with; `undefined` on the root timeline. */
|
|
7
9
|
client;
|
|
8
10
|
constructor(state = { origin: Date.now(), entries: [] }, client) {
|
|
9
11
|
this.state = state;
|
|
10
12
|
this.client = client;
|
|
11
13
|
}
|
|
14
|
+
/** Every entry added since the last `restart`, in order, shared by all views of the timeline. */
|
|
12
15
|
get entries() {
|
|
13
16
|
return this.state.entries;
|
|
14
17
|
}
|
|
18
|
+
/** Clears the entries and makes now the origin for `atMs`. */
|
|
15
19
|
restart() {
|
|
16
20
|
this.state.origin = Date.now();
|
|
17
21
|
this.state.entries = [];
|
|
18
22
|
}
|
|
23
|
+
/** Appends an entry stamped with the time since the origin, and with this view's client when it has one. */
|
|
19
24
|
add(entry) {
|
|
20
25
|
this.state.entries.push({ atMs: Date.now() - this.state.origin, ...(this.client ? { client: this.client } : {}), ...entry });
|
|
21
26
|
}
|
|
27
|
+
/** A view of the same timeline that tags what it adds with `client`. */
|
|
22
28
|
for(client) {
|
|
23
29
|
return new Timeline(this.state, client);
|
|
24
30
|
}
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
|
-
* A driver for anything you can reach from
|
|
28
|
-
* returns a fresh context per path attempt; recognisers and events work on it directly.
|
|
33
|
+
* A driver for anything you can reach from code: a domain model, an API client, a CLI. `create`
|
|
34
|
+
* returns a fresh context per path attempt; recognisers and events work on it directly. It takes
|
|
35
|
+
* no screenshots or clips, and any number of attempts can run side by side unless `concurrency` says otherwise.
|
|
36
|
+
*
|
|
37
|
+
* @param create builds the context for one attempt; it may be async.
|
|
38
|
+
* @param options.name the driver's name in reports; `function` when omitted.
|
|
39
|
+
* @param options.dispose called with the context when the attempt ends.
|
|
40
|
+
* @param options.concurrency how many contexts may be open at once; unlimited when omitted.
|
|
41
|
+
* @see docs/statecharts.md
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* const driver = functionDriver(() => new TodoModel());
|
|
29
45
|
*/
|
|
30
46
|
export function functionDriver(create, options = {}) {
|
|
31
47
|
return {
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module UI signals
|
|
3
|
+
*
|
|
4
|
+
* A driver-neutral vocabulary for saying what is on screen (`testId`, `role`, `label`, `text`, `css`),
|
|
5
|
+
* and `screen(...)`, which builds a state recogniser from signals. Every UI driver (Playwright, Bun
|
|
6
|
+
* WebView, WebDriver, Detox) resolves the same signals against its own UI tree, so an implementation
|
|
7
|
+
* written with them runs on any of them. Also holds the wire form and the in-page probe used by drivers
|
|
8
|
+
* that evaluate JavaScript in a page. This module is browser-safe and has no driver dependencies.
|
|
9
|
+
*
|
|
10
|
+
* @see docs/statecharts.md
|
|
11
|
+
*/
|
|
1
12
|
import type { StateImplementation } from './run/types.js';
|
|
2
13
|
/**
|
|
3
14
|
* A UI signal: something visible (or not) that proves where the app is. Test IDs first, then
|
|
@@ -19,35 +30,56 @@ export type Signal = {
|
|
|
19
30
|
} | {
|
|
20
31
|
css: string;
|
|
21
32
|
};
|
|
33
|
+
/** A signal for the element with this test ID (`data-testid` on the web, the accessibility identifier on native). */
|
|
22
34
|
export declare const testId: (id: string) => Signal;
|
|
35
|
+
/** A signal for visible text: a string matches as a case-insensitive substring unless `exact`, a RegExp is tested as is. */
|
|
23
36
|
export declare const text: (value: string | RegExp, options?: {
|
|
24
37
|
exact?: boolean;
|
|
25
38
|
}) => Signal;
|
|
39
|
+
/** A signal for an element with an accessibility role (`button`, `heading`, ...), optionally filtered by its accessible name (same matching as `text`). */
|
|
26
40
|
export declare const role: (r: string, name?: string | RegExp, options?: {
|
|
27
41
|
exact?: boolean;
|
|
28
42
|
}) => Signal;
|
|
43
|
+
/** A signal for a form control by its label text. */
|
|
29
44
|
export declare const label: (value: string | RegExp) => Signal;
|
|
45
|
+
/** A signal for a CSS selector. Web only: the WebDriver and Detox drivers throw for it in native sessions, so prefer the other signals. */
|
|
30
46
|
export declare const css: (selector: string) => Signal;
|
|
47
|
+
/** Describes a signal in words for recogniser output and failure messages, e.g. `button "Add"`. */
|
|
31
48
|
export declare function describeSignal(signal: Signal): string;
|
|
49
|
+
/** What a screen looks like, as signals. */
|
|
32
50
|
export type SignatureSpec<S = Signal> = {
|
|
51
|
+
/** Every one must be visible. */
|
|
33
52
|
all?: S[];
|
|
53
|
+
/** At least one must be visible (ignored when empty). */
|
|
34
54
|
any?: S[];
|
|
55
|
+
/** None may be visible. */
|
|
35
56
|
none?: S[];
|
|
36
57
|
};
|
|
58
|
+
/** A `SignatureSpec` plus the optional parts of a `StateImplementation`, which are passed through. */
|
|
37
59
|
export type ScreenSpec<C, S = Signal> = SignatureSpec<S> & {
|
|
60
|
+
/** Each `check` is a statement from the state's `meta.checks`; it passes when its `signal` is visible. */
|
|
38
61
|
checks?: {
|
|
39
62
|
check: string;
|
|
40
63
|
signal: S;
|
|
41
64
|
}[];
|
|
65
|
+
/** See `StateImplementation.transient`. */
|
|
42
66
|
transient?: boolean;
|
|
67
|
+
/** See `StateImplementation.sameAs`. */
|
|
43
68
|
sameAs?: string;
|
|
69
|
+
/** See `StateImplementation.lookIn`. */
|
|
44
70
|
lookIn?: (ctx: C) => Promise<void>;
|
|
71
|
+
/** See `StateImplementation.settle`. */
|
|
45
72
|
settle?: (ctx: C) => Promise<void>;
|
|
46
73
|
};
|
|
47
74
|
/**
|
|
48
|
-
*
|
|
49
|
-
* recognised when every `all` is visible, one `any` is visible and no `none` is
|
|
50
|
-
*
|
|
75
|
+
* Builds a `screen()` function for a driver from one question: is this signal visible right now?
|
|
76
|
+
* A state is recognised when every `all` is visible, one `any` is visible and no `none` is; the result
|
|
77
|
+
* lists every signal looked at with whether it was visible, for failure reports. An `isVisible` that
|
|
78
|
+
* rejects counts as not visible. Drivers with their own locators (WebDriver's) pass their signal type
|
|
79
|
+
* and how to describe it.
|
|
80
|
+
*
|
|
81
|
+
* @param isVisible answers whether one signal is visible in the context.
|
|
82
|
+
* @param describe names a signal in words.
|
|
51
83
|
*/
|
|
52
84
|
export declare function signatureScreen<C, S = Signal>(isVisible: (ctx: C, signal: S) => Promise<boolean>, describe: (signal: S) => string): (spec: ScreenSpec<C, S>) => StateImplementation<C>;
|
|
53
85
|
/**
|
|
@@ -58,14 +90,25 @@ export declare function signatureScreen<C, S = Signal>(isVisible: (ctx: C, signa
|
|
|
58
90
|
export type SignalContext = {
|
|
59
91
|
/** Open a URL (a path resolves against the driver's base URL; a deep link in a native app). */
|
|
60
92
|
goto(url: string): Promise<void>;
|
|
93
|
+
/** Whether the signal is visible now. */
|
|
61
94
|
isVisible(signal: Signal): Promise<boolean>;
|
|
95
|
+
/** Actions a person does. Drivers log each to the step's timeline, which becomes the clip's captions. */
|
|
62
96
|
user: {
|
|
97
|
+
/** Taps or clicks the target; `label` is the caption shown for it. */
|
|
63
98
|
tap(target: Signal, label: string): Promise<void>;
|
|
99
|
+
/** Types `text` into the target; `label` is the caption shown for it. */
|
|
64
100
|
type(target: Signal, text: string, label: string): Promise<void>;
|
|
65
101
|
};
|
|
66
102
|
};
|
|
67
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Builds a state implementation that is recognised from signals on any driver that provides a
|
|
105
|
+
* `SignalContext`.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* states: { 'No todos': screen({ all: [text('Nothing to do yet')], none: [role('listitem')] }) }
|
|
109
|
+
*/
|
|
68
110
|
export declare const screen: (spec: ScreenSpec<SignalContext, Signal>) => StateImplementation<SignalContext>;
|
|
111
|
+
/** A string or regular expression as JSON. */
|
|
69
112
|
type WireMatch = {
|
|
70
113
|
text: string;
|
|
71
114
|
exact: boolean;
|
|
@@ -73,6 +116,7 @@ type WireMatch = {
|
|
|
73
116
|
regex: string;
|
|
74
117
|
flags: string;
|
|
75
118
|
};
|
|
119
|
+
/** A `Signal` as plain JSON, the form sent to the in-page `DOM_PROBE`. */
|
|
76
120
|
export type WireSignal = {
|
|
77
121
|
kind: 'css';
|
|
78
122
|
selector: string;
|
|
@@ -90,7 +134,7 @@ export type WireSignal = {
|
|
|
90
134
|
kind: 'text';
|
|
91
135
|
match: WireMatch;
|
|
92
136
|
};
|
|
93
|
-
/**
|
|
137
|
+
/** Converts a signal to its JSON form for a probe that runs inside the page. */
|
|
94
138
|
export declare function toWire(signal: Signal): WireSignal;
|
|
95
139
|
/** Where a probe found a signal: whether it is visible, and the centre of the first visible match in CSS pixels. */
|
|
96
140
|
export type DomProbeResult = {
|
package/dist/core/src/signals.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
/** A signal for the element with this test ID (`data-testid` on the web, the accessibility identifier on native). */
|
|
1
2
|
export const testId = (id) => ({ testId: id });
|
|
3
|
+
/** A signal for visible text: a string matches as a case-insensitive substring unless `exact`, a RegExp is tested as is. */
|
|
2
4
|
export const text = (value, options = {}) => ({ text: value, ...options });
|
|
5
|
+
/** A signal for an element with an accessibility role (`button`, `heading`, ...), optionally filtered by its accessible name (same matching as `text`). */
|
|
3
6
|
export const role = (r, name, options = {}) => ({ role: r, ...(name ? { name } : {}), ...options });
|
|
7
|
+
/** A signal for a form control by its label text. */
|
|
4
8
|
export const label = (value) => ({ label: value });
|
|
9
|
+
/** A signal for a CSS selector. Web only: the WebDriver and Detox drivers throw for it in native sessions, so prefer the other signals. */
|
|
5
10
|
export const css = (selector) => ({ css: selector });
|
|
11
|
+
/** Describes a signal in words for recogniser output and failure messages, e.g. `button "Add"`. */
|
|
6
12
|
export function describeSignal(signal) {
|
|
7
13
|
if ('testId' in signal)
|
|
8
14
|
return `test ID "${signal.testId}"`;
|
|
@@ -15,9 +21,14 @@ export function describeSignal(signal) {
|
|
|
15
21
|
return `text ${String(signal.text)}`;
|
|
16
22
|
}
|
|
17
23
|
/**
|
|
18
|
-
*
|
|
19
|
-
* recognised when every `all` is visible, one `any` is visible and no `none` is
|
|
20
|
-
*
|
|
24
|
+
* Builds a `screen()` function for a driver from one question: is this signal visible right now?
|
|
25
|
+
* A state is recognised when every `all` is visible, one `any` is visible and no `none` is; the result
|
|
26
|
+
* lists every signal looked at with whether it was visible, for failure reports. An `isVisible` that
|
|
27
|
+
* rejects counts as not visible. Drivers with their own locators (WebDriver's) pass their signal type
|
|
28
|
+
* and how to describe it.
|
|
29
|
+
*
|
|
30
|
+
* @param isVisible answers whether one signal is visible in the context.
|
|
31
|
+
* @param describe names a signal in words.
|
|
21
32
|
*/
|
|
22
33
|
export function signatureScreen(isVisible, describe) {
|
|
23
34
|
const visible = (ctx, signal) => isVisible(ctx, signal).catch(() => false);
|
|
@@ -58,10 +69,16 @@ export function signatureScreen(isVisible, describe) {
|
|
|
58
69
|
};
|
|
59
70
|
};
|
|
60
71
|
}
|
|
61
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Builds a state implementation that is recognised from signals on any driver that provides a
|
|
74
|
+
* `SignalContext`.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* states: { 'No todos': screen({ all: [text('Nothing to do yet')], none: [role('listitem')] }) }
|
|
78
|
+
*/
|
|
62
79
|
export const screen = signatureScreen((ctx, signal) => ctx.isVisible(signal), describeSignal);
|
|
63
80
|
const wire = (value, exact = false) => typeof value === 'string' ? { text: value, exact } : { regex: value.source, flags: value.flags };
|
|
64
|
-
/**
|
|
81
|
+
/** Converts a signal to its JSON form for a probe that runs inside the page. */
|
|
65
82
|
export function toWire(signal) {
|
|
66
83
|
if ('testId' in signal)
|
|
67
84
|
return { kind: 'testId', id: signal.testId };
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Static chart composition
|
|
3
|
+
*
|
|
4
|
+
* Flattens a bundle of charts into one machine by inlining each child chart into the state that
|
|
5
|
+
* invokes it, so a plain graph traversal sees the whole product. Pure and deterministic; the input
|
|
6
|
+
* bundle is not modified. State names are global across the composed product (nothing is renamed or
|
|
7
|
+
* namespaced), which is why they must be unique across charts.
|
|
8
|
+
*
|
|
9
|
+
* @see docs/statecharts.md
|
|
10
|
+
*/
|
|
1
11
|
import type { MachineConfig, SpecBundle } from './types.js';
|
|
12
|
+
/** A child chart's final state and the event it passes to the parent, which the planner and replay insert automatically. */
|
|
2
13
|
export type HandOff = {
|
|
3
14
|
finalState: string;
|
|
4
15
|
event: string;
|
|
5
16
|
parentState: string;
|
|
6
17
|
childChart: string;
|
|
7
18
|
};
|
|
19
|
+
/** The result of composing a bundle. */
|
|
8
20
|
export type Composition = {
|
|
21
|
+
/** The single flattened machine (a deep copy; the bundle is untouched). */
|
|
9
22
|
machine: MachineConfig;
|
|
10
23
|
/** Which chart each state came from. */
|
|
11
24
|
chartOf: Map<string, string>;
|
|
@@ -15,7 +28,7 @@ export type Composition = {
|
|
|
15
28
|
hosts: Map<string, string>;
|
|
16
29
|
};
|
|
17
30
|
/**
|
|
18
|
-
*
|
|
31
|
+
* Inlines every child chart where its parent invokes it, so XState's graph traversal (which cannot
|
|
19
32
|
* enter an invoked actor) walks straight through. Supports:
|
|
20
33
|
*
|
|
21
34
|
* - XState `invoke: { src, id, onDone }` and SCXML `<invoke>` + `done.invoke.<id>`;
|
|
@@ -24,5 +37,14 @@ export type Composition = {
|
|
|
24
37
|
*
|
|
25
38
|
* A child's final states stay as states (they keep their screenshots and checks) but stop being
|
|
26
39
|
* final: each takes the parent's transition for its hand-off event.
|
|
40
|
+
*
|
|
41
|
+
* Every state without an `id` gets its own name as `id`. A child's `initial` replaces the host
|
|
42
|
+
* state's `initial`, and the host's transitions that consumed a hand-off event are removed from it.
|
|
43
|
+
*
|
|
44
|
+
* @throws Error when the root chart is not loaded, an invoked chart is not loaded, charts invoke
|
|
45
|
+
* each other in a cycle, or a child's final state has no matching transition (or `onDone`) in the parent.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* const { machine, handOffs } = composeCharts(loadSpecDirectory('./specs'));
|
|
27
49
|
*/
|
|
28
50
|
export declare function composeCharts(bundle: SpecBundle): Composition;
|