@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
package/README.md
CHANGED
|
@@ -1,33 +1,89 @@
|
|
|
1
1
|
# Atlas
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
**Specs of any kind, reported as layered evidence.** Atlas lays the results of every suite that
|
|
4
|
+
checks your specs (Gherkin, Quint, DMN, Alloy, OpenSLO, unit and end-to-end tests, linters,
|
|
5
|
+
statecharts) over the specs themselves, across machines, branches, environments and storage
|
|
6
|
+
backends, and tells you in one command whether the product still does what it was specified to do.
|
|
7
|
+
|
|
8
|
+
- **Any spec, any runner.** Anything that ends in "this check passed or failed" publishes a *layer*:
|
|
9
|
+
from a JUnit or CTRF file, a ten-line script, or code. No runner and no notation is privileged.
|
|
10
|
+
- **Layered, never partitioned.** A report is computed from layers, not stored as "the run". Partial
|
|
11
|
+
runs on separate nodes, branches and devices are how a report is built, and they compose without
|
|
12
|
+
coordinating: the same layers give the same report in any order, split any way.
|
|
13
|
+
- **Open evidence.** A result carries whatever your framework produces: screenshots, videos, traces, logs, DOM
|
|
14
|
+
snapshots, timelines, retries, annotations, your own structured data. Any media type, nothing registered, and
|
|
15
|
+
new kinds never break older readers ([extending](docs/extending.md)). Specs stay opinionated (statecharts,
|
|
16
|
+
Gherkin, Quint, DMN, Alloy, OpenSLO); evidence stays open.
|
|
17
|
+
- **Open standards, stable schemas.** JUnit XML and CTRF in, JSON Schema 2020-12 contracts, a
|
|
18
|
+
versioned layer format with a golden fixture, and an `ObjectStore` interface any bucket fits.
|
|
19
|
+
- **One server, live.** A project runs one `atlas serve`: a Bun API that serves the viewer from the same host.
|
|
20
|
+
Every laptop and CI job sends results to it as they finish (`atlas report publish`, or `atlas report stream` while
|
|
21
|
+
a suite runs), it keeps them on its own disk (or in S3, R2, MinIO), and every viewer sees the combined report
|
|
22
|
+
update live, receiving only what changed ([the server](docs/server.md), [deploying](docs/deploying.md)).
|
|
23
|
+
- **Fast and fail-closed.** Publishing is two small writes. A thousand layers of a hundred checks
|
|
24
|
+
combine in about a second. An unreadable layer, a downed backend or a check still running is never
|
|
25
|
+
counted as a pass.
|
|
26
|
+
- **Executable statecharts too.** Atlas can also run a product's flows as statecharts against the
|
|
27
|
+
real app, and publishes those runs as layers like any other ([below](#executable-statecharts)).
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
any suite ──▶ layer ─┐ atlas report status exit 1 unless it passes
|
|
31
|
+
any node ──▶ layer ─┼─▶ backends ─▶ combine ─▶ the visualizer browse it beside the specs
|
|
32
|
+
any branch ──▶ layer ─┘ (directories, buckets, …) combineBackends() your own code
|
|
8
33
|
```
|
|
9
|
-
specs/ atlas lint the charts are pure, reachable, free of silent dead ends,
|
|
10
|
-
app.scxml ──▶ and every journey replays
|
|
11
|
-
child.machine.yaml atlas plan curated journeys + shortest paths covering every transition
|
|
12
|
-
journeys.yaml atlas run each path against the product, through a driver
|
|
13
|
-
│
|
|
14
|
-
▼
|
|
15
|
-
atlas-runs/<run>/manifest.json, junit.xml, ctrf.json, chart.mmd, media/
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
The spec is the source of truth for the product's behaviour, not for the test code: a state says
|
|
19
|
-
what a person sees, an event says what happens in business language ("Adds a todo", "Sync fails"),
|
|
20
|
-
and the implementation says how a driver makes each event happen and recognises each state.
|
|
21
34
|
|
|
22
35
|
> [!IMPORTANT]
|
|
23
36
|
> **Dogfooding phase:** Atlas is developed by using it in real projects. Those projects install the
|
|
24
|
-
> published
|
|
37
|
+
> published package from npm and report bugs and missing features as GitHub issues; they do not
|
|
25
38
|
> change this repository. If you are integrating Atlas, or are an agent working in a project that
|
|
26
|
-
> uses it, read [
|
|
27
|
-
> [reporting issues](docs/reporting-issues.md).
|
|
28
|
-
> [releasing](docs/releasing.md).
|
|
39
|
+
> uses it, read **[integrating Atlas](docs/integrating.md)**, then
|
|
40
|
+
> [using Atlas from another project](docs/dogfooding.md) and [reporting issues](docs/reporting-issues.md).
|
|
41
|
+
> Releases are automatic: see [releasing](https://github.com/crvouga/atlas/blob/main/docs/releasing.md).
|
|
42
|
+
|
|
43
|
+
## For AI agents
|
|
44
|
+
|
|
45
|
+
Atlas is built to be integrated and operated by agents, with nothing to click and nothing to guess.
|
|
29
46
|
|
|
30
|
-
|
|
47
|
+
- **`atlas init`** detects a project's test runners, specs and CI and prints a plan (`--format json`) of the exact commands to
|
|
48
|
+
run each suite and send its results; `--write` creates the CI workflow and an `AGENTS.md` section. Running it twice
|
|
49
|
+
changes nothing.
|
|
50
|
+
- **Every command prints JSON on request** (`--format json`), every document has a published JSON Schema, exit codes mean the
|
|
51
|
+
same everywhere (`0` worked, `1` the answer is no, `2` the command was wrong), and every operation is safe to repeat.
|
|
52
|
+
- **The server describes itself:** `GET /api/openapi.json` (OpenAPI 3.1) and `GET /llms.txt` (a guide for an agent given only
|
|
53
|
+
the address) need no token; errors are RFC 9457 problem documents that say what to do.
|
|
54
|
+
- **Documentation is generated from the code beside it:** `llms.txt` / `llms-full.txt` (also in the package), the TypeScript
|
|
55
|
+
reference in [`docs/reference/`](docs/reference/server.md), the CLI and HTTP references, and the settings tables; tests fail when
|
|
56
|
+
any of it drifts.
|
|
57
|
+
|
|
58
|
+
Start with **[Atlas for AI agents](docs/agents.md)**.
|
|
59
|
+
|
|
60
|
+
## Integrate in five minutes
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
bun add -d @crvouga/atlas # or npm install -D / pnpm add -D; runs on Bun and Node 22.18+
|
|
64
|
+
|
|
65
|
+
# 1. Each suite publishes a layer of what it ran: a JUnit or CTRF file, an Atlas run, or a checks file.
|
|
66
|
+
bunx atlas report publish --store atlas-reports --producer web-e2e --junit out.xml --specs ./specs --environment desktop-web
|
|
67
|
+
|
|
68
|
+
# 2. Any number of nodes, branches and machines do the same into their own stores. Copying stores
|
|
69
|
+
# together is the aggregate: layers are immutable and named by their content.
|
|
70
|
+
bunx atlas report merge --store node-1 --store node-2 --into all
|
|
71
|
+
|
|
72
|
+
# 3. The gate: exit 1 on a failed check, a check still running, an unreadable layer, a backend that
|
|
73
|
+
# is down, or an environment with no results.
|
|
74
|
+
bunx atlas report status --store all --specs ./specs --require-environments desktop-web,ios
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Read | When |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| **[Integrating Atlas](docs/integrating.md)** | Start here. Step by step, with the command and expected output for each step, a CI workflow for sharded nodes, and troubleshooting. |
|
|
80
|
+
| **[The Atlas server](docs/server.md)** | The living application: configuration, tokens, the upload and streaming protocol, the full API, what it refuses, performance. |
|
|
81
|
+
| **[Running Atlas for a project](docs/deploying.md)** | Production: the server (Docker, Kubernetes, auth), sending results from CI and laptops, an S3 bucket and its policies, filters, operating it. |
|
|
82
|
+
| [The report contract](docs/report-contract.md) | The exact rules: the layer document, how layers compose, guarantees, backends, versioning, failure behaviour. |
|
|
83
|
+
| **[Evidence: attachments, timelines and everything else](docs/extending.md)** | What a result can carry, the rules that keep the format compatible forever, and how to attach anything from any framework. |
|
|
84
|
+
| [Report backends and live snapshots](docs/report-backends.md) | Connecting directories, HTTP files and APIs, streaming, and writing another transport. |
|
|
85
|
+
| [`@crvouga/atlas/reports`](packages/reports/README.md) | The programmatic API: `publishChecks`, `combineBackends`, `reportStatus`, `directoryStore`, `reportsHost`. |
|
|
86
|
+
| [`@crvouga/atlas/schema`](packages/schema/README.md) | The JSON Schemas and Zod schemas of every document. |
|
|
31
87
|
|
|
32
88
|
## One package
|
|
33
89
|
|
|
@@ -40,616 +96,134 @@ bun add -d @crvouga/atlas
|
|
|
40
96
|
|
|
41
97
|
| Import | What it is |
|
|
42
98
|
| --- | --- |
|
|
43
|
-
| [`@crvouga/atlas
|
|
99
|
+
| [`@crvouga/atlas/reports/<module>`](packages/reports) | Evidence for any spec: publish layers (`publish-checks`, `ingest`, `reporter` to stream them), store them (`s3-store`, `directory-store`, `http-store` for a server, any `object-store`), combine and gate them (`compose`, `report-status`), copy them (`copy-store`). |
|
|
100
|
+
| [`@crvouga/atlas/server`](packages/server) | The Atlas server as a `fetch` handler (`createAtlasServer`), served over HTTP on Bun and Node (`listen`). |
|
|
101
|
+
| [`@crvouga/atlas/schema`](packages/schema) | The contract: Zod schemas and JSON Schemas for layers, check files, report status, specs, journeys, manifests, timelines and the runs index. |
|
|
102
|
+
| [`@crvouga/atlas`](packages/core) | Spec loading (SCXML and XState), composition, lint, planning, the runner, multi-client runs (`combineDrivers`), `httpDriver` and `functionDriver`, CloudEvents, privacy, config and the `atlas` CLI. `@crvouga/atlas/spec` is a browser-safe subset; `@crvouga/atlas/signals` holds the driver-neutral signals. |
|
|
44
103
|
| [`@crvouga/atlas/playwright`](packages/playwright) | Playwright driver: signal-based screen recognisers, paced taps and typing with a touch overlay, device-resolution screenshots and H.264 clips. |
|
|
45
104
|
| [`@crvouga/atlas/bun`](packages/bun) | Bun WebView driver: Bun's built-in headless browser (the system WebKit on macOS, Chrome elsewhere), nothing to download. |
|
|
46
105
|
| [`@crvouga/atlas/webdriver`](packages/webdriver) | W3C WebDriver driver with no dependencies: Safari, Chrome, Firefox and Edge, and native iOS and Android apps through Appium. |
|
|
47
106
|
| [`@crvouga/atlas/detox`](packages/detox) | Detox driver for iOS and Android apps, inside a Detox test run. |
|
|
48
107
|
| [`@crvouga/atlas/vitest`](packages/vitest) | Run a config as a test suite in Vitest, `bun test` or any Jest-style runner, one test per planned path. |
|
|
49
|
-
| [
|
|
50
|
-
| [`@crvouga/atlas/reports/<module>`](packages/reports) | Publish, store, retain and serve test evidence, and combine it across backends, branches and commits. |
|
|
51
|
-
| [`atlas-visualizer`](packages/visualizer) (command) | Browse charts and runs: every state with its screenshots, every transition with its clip. |
|
|
52
|
-
|
|
53
|
-
## Open standards
|
|
54
|
-
|
|
55
|
-
Atlas invents as little as it can. Everything it reads or writes is a format other tools already
|
|
56
|
-
understand.
|
|
57
|
-
|
|
58
|
-
| Standard | Used for |
|
|
59
|
-
| --- | --- |
|
|
60
|
-
| [W3C SCXML](https://www.w3.org/TR/scxml/) | Charts (`*.scxml`), with business metadata in an `atlas` XML namespace |
|
|
61
|
-
| [XState v5](https://stately.ai/docs/machines) machine config | Charts (`*.machine.json`, `*.machine.yaml`): the JSON shape Stately Studio imports and exports |
|
|
62
|
-
| [JSON Schema 2020-12](https://json-schema.org/draft/2020-12) | The manifest, journeys, state nodes, timelines and runs index (`packages/schema/schemas`) |
|
|
63
|
-
| [JUnit XML](https://github.com/testmoapp/junitxml) | `junit.xml`: one test case per path, for any CI system |
|
|
64
|
-
| [CTRF](https://ctrf.io) | `ctrf.json`: Common Test Report Format |
|
|
65
|
-
| [WebVTT](https://www.w3.org/TR/webvtt1/) | Captions for every clip: each tap, keystroke, system call and screen change |
|
|
66
|
-
| [CloudEvents 1.0](https://cloudevents.io) | Business events the product emits (HTTP structured, binary and batch modes) |
|
|
67
|
-
| [W3C WebDriver](https://www.w3.org/TR/webdriver2/) | Driving browsers and, through Appium, native iOS and Android apps |
|
|
68
|
-
| [WAI-ARIA](https://www.w3.org/TR/wai-aria/) roles and accessible names | Signals that recognise screens the same way on every driver |
|
|
69
|
-
| MP4 / H.264 | Clips of each transition, encoded with FFmpeg |
|
|
70
|
-
| PNG / WebP | Screenshots at device resolution, with WebP thumbnails |
|
|
71
|
-
| [Mermaid](https://mermaid.js.org/syntax/stateDiagram.html) `stateDiagram-v2` | `chart.mmd`, and `atlas export --format mermaid` |
|
|
72
|
-
|
|
73
|
-
## Quick start: the todo example
|
|
74
|
-
|
|
75
|
-
[`examples/todo`](examples/todo) is a dependency-free web app (static HTML and vanilla JavaScript
|
|
76
|
-
served by a small Node server) specified as two charts:
|
|
77
|
-
|
|
78
|
-
- `specs/todo-app.scxml`, the root chart in SCXML: a welcome screen, a "Setting up" state that
|
|
79
|
-
invokes the child chart, and a `<parallel>` "Working on the list" state whose "Sync" and "List"
|
|
80
|
-
regions change independently.
|
|
81
|
-
- `specs/getting-started.machine.yaml`, the child chart in XState YAML. Its two final states hand
|
|
82
|
-
back to the parent through `meta.doneEvent`.
|
|
83
|
-
- `specs/journeys.yaml`, four curated journeys.
|
|
84
|
-
- `specs/getting-started.feature` and `specs/working-the-list.feature`, the same stories in Gherkin.
|
|
85
|
-
- `specs/todo.qnt`, the list, its sync, and its actions as a Quint model.
|
|
86
|
-
|
|
87
|
-
The visualizer lists the features under Gherkin and the model under Quint.
|
|
88
|
-
|
|
89
|
-
It has every kind of event: user events (taps and typing), a **system** event ("Sync fails", which
|
|
90
|
-
the test triggers through a real HTTP endpoint on the example server), a **time** event ("Retry
|
|
91
|
-
timer elapses", which jumps Playwright's clock 30 seconds) and **hand-off** events from the child
|
|
92
|
-
chart. The server reports business events as CloudEvents to an Atlas `cloudEventsHttpSource`, so
|
|
93
|
-
the states' `meta.events` are checked on every run.
|
|
94
|
-
|
|
95
|
-
This repository runs on [Bun](https://bun.sh). Video clips and WebP thumbnails need FFmpeg on the
|
|
96
|
-
`PATH`. From the repository root:
|
|
97
|
-
|
|
98
|
-
```sh
|
|
99
|
-
bun install
|
|
100
|
-
bunx playwright install chromium
|
|
101
|
-
|
|
102
|
-
bun run example:lint # pure, reachable, no undocumented dead ends, journeys replay, everything implemented
|
|
103
|
-
bun run example:plan # 4 journeys + 1 generated path cover all 14 transitions
|
|
104
|
-
bun run example:fast # run every path quickly
|
|
105
|
-
bun run example:run # statechart showcase, then the Gherkin features and the Quint model
|
|
106
|
-
bun test examples # the same spec against an in-memory model, as a test suite
|
|
107
|
-
|
|
108
|
-
bun run example:clients # clients/: one chart across a phone, a desktop and the sync server at once
|
|
109
|
-
bun run example:clients:bun # the same, with the desktop in Bun's WebKit and the phone in Playwright's Chromium
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
To run every chart of the example and browse the results, run these in separate terminals; the
|
|
113
|
-
visualizer updates when a run finishes.
|
|
114
|
-
|
|
115
|
-
```sh
|
|
116
|
-
bun run atlas:run # example:clients, then example:run: every chart of the example, in showcase mode
|
|
117
|
-
bun run atlas:viz # the example's specs, chart runs, Gherkin features, and Quint model
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Each config writes its own run and the visualizer shows one run at a time: the latest is the
|
|
121
|
-
single-browser run, and the run across clients is the one before it in the run picker.
|
|
122
|
-
|
|
123
|
-
A showcase run ends with:
|
|
124
|
-
|
|
125
|
-
```
|
|
126
|
-
States 14/14 passed, 0 failed, 0 not reached
|
|
127
|
-
Transitions 14/14 passed, 0 failed, 0 not reached
|
|
128
|
-
Manifest: examples/todo/atlas-runs/<run-id>/manifest.json
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
`example:run` then runs the Gherkin features in a browser and the Quint model. `atlas:viz` shows those results on the scenario and trace rows.
|
|
108
|
+
| [`atlas-visualizer`](packages/visualizer) (command) | Browse specs, charts and layered reports: every spec with its result per environment, every state with its screenshots, every transition with its clip. |
|
|
132
109
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
and the implementation differ, which is the point: the spec belongs to the product, not to one
|
|
136
|
-
test tool.
|
|
110
|
+
Two commands install with it: `atlas` (`report`, `lint`, `plan`, `run`, `merge`, `export`) and
|
|
111
|
+
`atlas-visualizer`.
|
|
137
112
|
|
|
138
|
-
##
|
|
113
|
+
## Reports for any spec
|
|
139
114
|
|
|
140
|
-
|
|
115
|
+
Atlas separates *what the product should do* (the specs) from *evidence of what it did* (layers).
|
|
116
|
+
Evidence is published by whatever checks the spec, and read back as one report.
|
|
141
117
|
|
|
142
|
-
|
|
118
|
+
**A layer** is one focused recording: the checks one producer ran, on one environment, from one
|
|
119
|
+
commit and branch, at one time. It is immutable and addressed by its content. Publish one from:
|
|
143
120
|
|
|
144
|
-
|
|
145
|
-
- `journeys.yaml` (or `.yml`, `.json`): the curated journeys.
|
|
146
|
-
|
|
147
|
-
The **root** chart is the one no other chart invokes. A chart's id is the SCXML `name` attribute or
|
|
148
|
-
the XState `id`.
|
|
149
|
-
|
|
150
|
-
### The pure subset
|
|
151
|
-
|
|
152
|
-
Atlas walks charts without running any code, so it executes only the parts of a statechart that
|
|
153
|
-
are pure structure:
|
|
154
|
-
|
|
155
|
-
- states, nesting (`initial`), `parallel` regions and `final` states;
|
|
156
|
-
- event transitions, written as a target (`Opens: Open` or `{ target: '#Open' }`);
|
|
157
|
-
- `invoke` that names another chart (`src`, `id`, `onDone`), composed statically;
|
|
158
|
-
- `meta`.
|
|
159
|
-
|
|
160
|
-
Guards, actions, `entry`/`exit`, `context`, `after` (delays), `always`, `output` and `onError` are
|
|
161
|
-
lint errors. A condition becomes a state or an event instead: "Payment declined" is an event the
|
|
162
|
-
system sends, not a guard on "Pays"; "30 seconds pass" is a time event, not an `after`.
|
|
163
|
-
|
|
164
|
-
State names are global across the composed product (transitions target `#<name>`), so they must be
|
|
165
|
-
unique across charts, and they cannot contain dots. Names are business language and may contain
|
|
166
|
-
spaces.
|
|
167
|
-
|
|
168
|
-
### `meta`
|
|
169
|
-
|
|
170
|
-
| Field | Meaning |
|
|
121
|
+
| You have | Publish with |
|
|
171
122
|
| --- | --- |
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
`<atlas:event>`, `<atlas:check>` and `<atlas:source>` repeat to form lists; `<atlas:dead-end>` is
|
|
207
|
-
`meta.deadEnd`; a final state's `atlas:done-event` attribute is `meta.doneEvent`. `atlas export
|
|
208
|
-
--format scxml` writes any chart (or the composed product) back out in this form, and reading it
|
|
209
|
-
again gives the same chart.
|
|
210
|
-
|
|
211
|
-
### Composition
|
|
212
|
-
|
|
213
|
-
A parent state runs a child chart with any of:
|
|
214
|
-
|
|
215
|
-
1. XState `invoke: { src: 'Child', id: 'child', onDone: '#Next' }`, or SCXML `<invoke id="child">`
|
|
216
|
-
plus a `done.invoke.child` transition;
|
|
217
|
-
2. `meta.doneEvent` on the child's final states, each naming the event the parent receives, which
|
|
218
|
-
the parent handles in its `on` (a final whose event the parent does not handle goes to
|
|
219
|
-
`onDone`);
|
|
220
|
-
3. legacy `meta.childMachine: 'Child'` and `meta.childFinalEvents: { <final>: <event> }` on the
|
|
221
|
-
parent state.
|
|
222
|
-
|
|
223
|
-
Atlas inlines each child into the state that invokes it. The child's final states stay as states
|
|
224
|
-
(so they keep their screenshots and checks) but take the parent's transition for their hand-off
|
|
225
|
-
event. When a path reaches one, the hand-off happens by itself: journeys never list hand-off
|
|
226
|
-
events, and the planner inserts them. Give each hand-off event an implementation of kind
|
|
227
|
-
`hand-off` (usually a no-op); events without an implementation count as blocked.
|
|
228
|
-
|
|
229
|
-
### Journeys
|
|
230
|
-
|
|
231
|
-
```yaml
|
|
232
|
-
journeys:
|
|
233
|
-
Keeps working through a sync outage:
|
|
234
|
-
description: Adds todos while the sync is down, and the retry catches up.
|
|
235
|
-
events: [Starts setup, Skips naming the list, Adds a todo, Sync fails, Adds a todo, Retry timer elapses]
|
|
236
|
-
endsIn: [Some todos open, Synced]
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
The linter replays each journey through the composed chart and checks it ends in every `endsIn`
|
|
240
|
-
state. Each journey runs as its own path; generated paths only cover what journeys leave out.
|
|
241
|
-
|
|
242
|
-
## Implementing a spec
|
|
243
|
-
|
|
244
|
-
An implementation tells a driver how to make each event happen and how to recognise each state.
|
|
245
|
-
Events are keyed by their exact name in the spec.
|
|
123
|
+
| JUnit XML from Cucumber, Jest, Vitest, Bun, pytest, `go test`, Playwright, … | `atlas report publish --junit out.xml` |
|
|
124
|
+
| [CTRF](https://ctrf.io) JSON | `atlas report publish --ctrf ctrf.json` |
|
|
125
|
+
| An Atlas statechart run | `atlas report publish --manifest atlas-runs/<run>/manifest.json` |
|
|
126
|
+
| A script, a model checker, an SLO evaluation, a linter | `atlas report publish --checks checks.json`, or `publishChecks(store, …)` |
|
|
127
|
+
|
|
128
|
+
**Layers compose by one rule.** A *slot* is `(producer, kind, spec, check id, environment,
|
|
129
|
+
parameters)`. Each slot shows the newest completed result of any layer that filled it. Branch, commit,
|
|
130
|
+
backend and run are provenance and never split a slot. So desktop web, mobile web, iOS and Android
|
|
131
|
+
results of one check sit side by side; a layer that re-ran one scenario leaves every other scenario as
|
|
132
|
+
the last layer that ran it; and a fix on any branch replaces the failure it fixes.
|
|
133
|
+
|
|
134
|
+
**The report does not depend on how layers arrived.** Order of reading, number of backends, how
|
|
135
|
+
the layers were split across nodes, and duplicates are all invisible. This is tested over random
|
|
136
|
+
partial runs, and it is why you can shard a suite across ten machines, re-run one failing check
|
|
137
|
+
locally, and publish a device lab's layers from elsewhere, with nothing to coordinate.
|
|
138
|
+
|
|
139
|
+
**Backends** hold layers. A backend is any implementation of the seven-method `ObjectStore`
|
|
140
|
+
interface. Atlas ships an S3 store (AWS S3, R2, MinIO and anything that speaks its protocol, with no SDK)
|
|
141
|
+
and a directory store with atomic writes, and `verifyObjectStore` proves an adapter before you trust it. Several backends are one report, and one
|
|
142
|
+
that is down is named and skipped.
|
|
143
|
+
|
|
144
|
+
**The gate** is `atlas report status`. It exits 0 only if some check completed, none failed, none is
|
|
145
|
+
still running, every layer and backend could be read, and every `--require-environments` environment
|
|
146
|
+
has results. `--format json` is a [stable schema](packages/schema/schemas/report-status.schema.json). Exit code 1
|
|
147
|
+
means the report does not pass; 2 means the command itself was wrong (a missing store directory, a bad
|
|
148
|
+
flag).
|
|
149
|
+
|
|
150
|
+
**The server** is `atlas serve`: one process that serves the viewer, takes in results and streams the report to
|
|
151
|
+
every viewer. Point `ATLAS_STORE` at it and every developer's laptop and every CI job sends results with the same
|
|
152
|
+
commands that write to a folder; everyone then looks at one report, live. Results are kept in its data folder
|
|
153
|
+
unless you choose a bucket. `atlas dev` runs the same server with the viewer's dev server in front, so
|
|
154
|
+
development is one host too. See [the server](docs/server.md) and
|
|
155
|
+
[running Atlas for a project](docs/deploying.md).
|
|
246
156
|
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
import { playwrightDriver, role, screen, text } from '@crvouga/atlas/playwright';
|
|
250
|
-
|
|
251
|
-
export default defineConfig({
|
|
252
|
-
specs: './specs',
|
|
253
|
-
driver: () => playwrightDriver({ clock: true }),
|
|
254
|
-
targets: ['http://127.0.0.1:4317'],
|
|
255
|
-
implementation: {
|
|
256
|
-
async setup({ page }) {
|
|
257
|
-
await page.goto('http://127.0.0.1:4317');
|
|
258
|
-
},
|
|
259
|
-
events: {
|
|
260
|
-
'Adds a todo': {
|
|
261
|
-
kind: 'user',
|
|
262
|
-
how: 'Types a todo and taps "Add".',
|
|
263
|
-
async run({ page, user }) {
|
|
264
|
-
await user.type(page.getByLabel('New todo'), 'Water the plants', 'New todo');
|
|
265
|
-
await user.tap(page.getByRole('button', { name: 'Add' }), 'Add');
|
|
266
|
-
}
|
|
267
|
-
},
|
|
268
|
-
'Retry timer elapses': {
|
|
269
|
-
kind: 'time',
|
|
270
|
-
how: 'Jumps the page clock 30 seconds.',
|
|
271
|
-
run: ({ page }) => page.clock.fastForward(30_000)
|
|
272
|
-
}
|
|
273
|
-
},
|
|
274
|
-
states: {
|
|
275
|
-
'No todos': screen({ all: [text('Nothing to do yet')] }),
|
|
276
|
-
Offline: screen({ all: [text('Offline')], none: [text('All changes synced')] })
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
**Events** (`EventImplementation<C>`): `kind` is `user`, `system`, `time` or `hand-off`; `how` is a
|
|
283
|
-
plain-language note shown in reports; `run(ctx, tools)` makes it happen; `prepare(ctx, tools)`
|
|
284
|
-
creates test data before the path starts; `blocked: '<reason>'` marks an event that cannot run yet,
|
|
285
|
-
so paths stop before it and report why.
|
|
286
|
-
|
|
287
|
-
**States** (`StateImplementation<C>`): `recognize(ctx)` returns whether the state is showing and the
|
|
288
|
-
signals it looked at. Optional: `checks(ctx)` (results for `meta.checks`; a failed check fails the
|
|
289
|
-
state), `lookIn(ctx)` (where to look when the change happens off screen, such as reloading or
|
|
290
|
-
opening another tab), `settle(ctx)` (within-state actions after the screenshot, such as
|
|
291
|
-
dismissing a toast), `transient` (shown too briefly to wait for) and `sameAs` (a state the UI cannot
|
|
292
|
-
tell apart from this one).
|
|
293
|
-
|
|
294
|
-
**Setup**: `setup(ctx, { path, tools })` puts the product in the path's start configuration;
|
|
295
|
-
`canStart(active)` returns `true` or the reason a start configuration cannot be set up yet. It is
|
|
296
|
-
optional when seeds cover every start.
|
|
297
|
-
|
|
298
|
-
**Seeds** (`seeds: Record<string, Seed<C>>`): see [Seeds and parallel runs](#seeds-and-parallel-runs).
|
|
299
|
-
|
|
300
|
-
**Correlation**: `correlate(ctx)` returns ids (a user id, a trace id) that tell this attempt's
|
|
301
|
-
business events from those of attempts running beside it.
|
|
302
|
-
|
|
303
|
-
Only leaf states need recognisers. `atlas lint --require-implementations` lists every event and
|
|
304
|
-
leaf state in scope that has no implementation.
|
|
305
|
-
|
|
306
|
-
## Seeds and parallel runs
|
|
307
|
-
|
|
308
|
-
A chart of a whole product is deep: a member reaches a results screen after signing up,
|
|
309
|
-
onboarding, paying and booking. Walking every path from the initial state repeats that prefix
|
|
310
|
-
in every path, so a run gets slower the more of the product it covers. Seeds remove the prefix.
|
|
311
|
-
|
|
312
|
-
A seed puts the system directly in one configuration of the chart, usually through an API or the
|
|
313
|
-
database: create a member who has paid, sign them in, open the screen.
|
|
157
|
+
```sh
|
|
158
|
+
atlas serve # the project's server, at :8080 (ATLAS_TOKEN says who may upload)
|
|
314
159
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
at: 'Paid', // a state name, or a full state value
|
|
320
|
-
how: 'Creates a paid member through the admin API and signs in.',
|
|
321
|
-
async run({ page, api }) {
|
|
322
|
-
const member = await api.createMember({ plan: 'paid' });
|
|
323
|
-
await page.goto(`/sign-in?token=${member.token}`);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
},
|
|
327
|
-
events: { /* … */ },
|
|
328
|
-
states: { /* … */ }
|
|
329
|
-
}
|
|
160
|
+
export ATLAS_STORE=https://atlas.example.com ATLAS_TOKEN=… # on every laptop and CI job
|
|
161
|
+
atlas report publish --producer unit --junit out.xml --specs ./specs # when a suite is done
|
|
162
|
+
my-suite --json | atlas report stream --producer e2e --environment ios # or as its checks finish
|
|
163
|
+
atlas report status --branch main --ignore-modified # the gate, answered by the server
|
|
330
164
|
```
|
|
331
165
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
-
|
|
335
|
-
wherever it passes through a configuration a seed produces, it is cut. Each stretch becomes a path
|
|
336
|
-
that starts from that seed. A stretch that several journeys share becomes one path. The manifest's
|
|
337
|
-
`journeys` lists the paths each journey is made of. A journey passes when all of them pass, fails
|
|
338
|
-
when one fails, and is `not-reached` while any is blocked.
|
|
339
|
-
- **Generated paths start from the nearest seed.** A breadth-first search from every start point at
|
|
340
|
-
once finds, for each uncovered transition, the shortest way to take it from any seed (or from
|
|
341
|
-
`setup`'s start). Ties go to the seed declared first.
|
|
342
|
-
- **Redundant paths are dropped.** A generated path that is a prefix of another path from the same
|
|
343
|
-
start is not planned.
|
|
344
|
-
|
|
345
|
-
The plan is deterministic. The same chart, journeys and seeds give the same paths, ids and keys
|
|
346
|
-
(`key` hashes the start and the events) on every machine, so results can be composed:
|
|
347
|
-
|
|
348
|
-
- **Workers.** `--workers n` (or `workers` in the config) runs n path attempts at once. Each attempt
|
|
349
|
-
gets its own driver context. The longest paths go first, timed by earlier runs.
|
|
350
|
-
- The driver's `concurrency` caps the worker count. Playwright and Bun contexts and HTTP clients
|
|
351
|
-
run side by side. A WebDriver session or a Detox device runs one at a time unless you raise it.
|
|
352
|
-
- The manifest is a pure fold of the path outcomes in plan order, so the same outcomes give the
|
|
353
|
-
same report whatever order they finished in.
|
|
354
|
-
- **Shards.** `--shard i/n` runs one part of the plan, split by the plan alone, so every machine
|
|
355
|
-
agrees on the split. `atlas merge <run-dir>...` composes the shards into one run, with their media
|
|
356
|
-
copied in.
|
|
357
|
-
- **Reruns.** `--rerun-failed` keeps the passed paths of the newest run, matched by key, and runs
|
|
358
|
-
the rest. The result is still one complete run.
|
|
359
|
-
- **Areas.** `--seed <name>` (repeatable) runs only the paths that start from that seed (`start`
|
|
360
|
-
for paths `setup` starts). `--state <name>` scopes the run to one state of the chart. With these you can
|
|
361
|
-
run one area of the product on its own, then merge it into a full run.
|
|
362
|
-
|
|
363
|
-
Seeds are what make the composition sound: a seed must leave the system exactly as the steps that
|
|
364
|
-
lead there would. Atlas checks this both ways and reports it:
|
|
365
|
-
- A seeded path must first recognise its start state.
|
|
366
|
-
- The manifest's `seeds` lists, for each seed, the passed paths that reached the same states through
|
|
367
|
-
real steps (`verifiedBy`).
|
|
368
|
-
|
|
369
|
-
A seed nobody reaches through the steps is a claim that has not been checked yet. In the
|
|
370
|
-
visualizer, seeded states carry a "Seeded" tag.
|
|
371
|
-
|
|
372
|
-
Business events and parallel runs: a shared source, such as a log file, sees every attempt's events.
|
|
373
|
-
`EventSource.mark()` returns a position that `collect(mark)` reads from, so attempts don't consume
|
|
374
|
-
each other's marks.
|
|
375
|
-
- With `correlate(ctx)`, only events and log lines that mention one of the attempt's ids count.
|
|
376
|
-
- Without it, a run on several workers reports business events as "no signal" rather than guessing.
|
|
377
|
-
|
|
378
|
-
## Drivers
|
|
379
|
-
|
|
380
|
-
| Driver | Package | Plays | Speed | Clips |
|
|
381
|
-
| --- | --- | --- | --- | --- |
|
|
382
|
-
| `functionDriver(create)` | `@crvouga/atlas` | A domain model, a CLI, anything reachable from code | Microseconds | No |
|
|
383
|
-
| `httpDriver(baseUrl)` | `@crvouga/atlas` | An API client with a cookie jar: a partner system, an operator API, a back office job | Milliseconds | No |
|
|
384
|
-
| `bunWebViewDriver()` | `@crvouga/atlas/bun` | A web app in Bun's built-in browser (WebKit on macOS, Chrome elsewhere) | A view in milliseconds, screenshots in about 10 ms | From screenshots |
|
|
385
|
-
| `playwrightDriver()` | `@crvouga/atlas/playwright` | A web app (or React Native on the web) in Chromium, with a fake clock | About a second per context | Screencast with a touch overlay |
|
|
386
|
-
| `webdriverDriver()` | `@crvouga/atlas/webdriver` | Safari, Chrome, Firefox, Edge; native iOS and Android apps through Appium | Depends on the server | From screenshots, or Appium's recorder |
|
|
387
|
-
| `detoxDriver()` | `@crvouga/atlas/detox` | A React Native app, inside a Detox test run | Depends on the device | Detox artifacts |
|
|
388
|
-
|
|
389
|
-
The Bun, Playwright, WebDriver and Detox contexts all provide a `SignalContext` (`goto`,
|
|
390
|
-
`isVisible`, `user.tap`, `user.type`), and they resolve the same signals (`testId`, `role`, `label`,
|
|
391
|
-
`text`, `css` from `@crvouga/atlas/signals`), each against its own UI tree: the DOM, or the
|
|
392
|
-
accessibility tree of a native app, where a test ID is the accessibility identifier (React
|
|
393
|
-
Native's `testID`). An implementation written
|
|
394
|
-
against `SignalContext` with `screen` from `@crvouga/atlas/signals` runs on any of them unchanged.
|
|
395
|
-
|
|
396
|
-
`bunWebViewDriver` needs the Bun runtime: run `bun --bun atlas run`. The `atlas` binary runs on
|
|
397
|
-
Node (through tsx) or Bun, and Playwright works under Bun too, so one run can mix Bun's WebKit and
|
|
398
|
-
Playwright's Chromium.
|
|
399
|
-
|
|
400
|
-
## Several clients in one run
|
|
401
|
-
|
|
402
|
-
Product flows cross devices: a member books on an iOS app, an operator approves in a web
|
|
403
|
-
dashboard, a partner confirms over an API. `combineDrivers` runs several drivers as the named
|
|
404
|
-
clients of one run, and `combineImplementations` merges what each client does and shows:
|
|
166
|
+
**The viewer** shows each spec (Gherkin, Quint, DMN, Alloy, OpenSLO, and the rest by file type) with
|
|
167
|
+
its combined result per environment and the layer behind it. See
|
|
168
|
+
[integrating Atlas](docs/integrating.md#6-browse-it).
|
|
405
169
|
|
|
406
170
|
```ts
|
|
407
|
-
import {
|
|
408
|
-
import {
|
|
409
|
-
import {
|
|
410
|
-
import { accessibilityId, webdriverDriver } from '@crvouga/atlas/webdriver';
|
|
411
|
-
|
|
412
|
-
type Clients = { member: SignalContext; operator: SignalContext; partner: HttpClient };
|
|
413
|
-
|
|
414
|
-
export default defineConfig<Clients>({
|
|
415
|
-
specs: './specs',
|
|
416
|
-
driver: combineDrivers<Clients>(
|
|
417
|
-
{
|
|
418
|
-
member: webdriverDriver({
|
|
419
|
-
url: 'http://127.0.0.1:4723', // Appium
|
|
420
|
-
capabilities: { platformName: 'iOS', 'appium:automationName': 'XCUITest', 'appium:app': './build/App.app' }
|
|
421
|
-
}),
|
|
422
|
-
operator: playwrightDriver({ baseUrl: 'http://127.0.0.1:3000', device: { viewport: { width: 1280, height: 800 } } }),
|
|
423
|
-
partner: httpDriver('http://127.0.0.1:4000')
|
|
424
|
-
},
|
|
425
|
-
{ screens: 'all' }
|
|
426
|
-
),
|
|
427
|
-
implementation: combineImplementations<Clients>(
|
|
428
|
-
{
|
|
429
|
-
member: {
|
|
430
|
-
events: { 'Requests a visit': { kind: 'user', how: 'Taps "Request".', run: (m) => m.user.tap(role('button', 'Request'), 'Request') } },
|
|
431
|
-
states: { 'Visit confirmed': screen({ all: [text('Confirmed')] }) }
|
|
432
|
-
},
|
|
433
|
-
operator: {
|
|
434
|
-
setup: (o) => o.goto('/queue'),
|
|
435
|
-
events: { 'Operator approves': { kind: 'user', how: 'Approves it in the queue.', run: (o) => o.user.tap(role('button', 'Approve'), 'Approve') } },
|
|
436
|
-
states: { 'Waiting for approval': screen({ all: [role('row', 'New request')] }) }
|
|
437
|
-
},
|
|
438
|
-
partner: {
|
|
439
|
-
events: { 'Partner confirms': { kind: 'system', how: 'POSTs the confirmation webhook.', run: (p) => p.post('/webhooks/confirm', { ok: true }).then(() => undefined) } }
|
|
440
|
-
}
|
|
441
|
-
},
|
|
442
|
-
{ setup: async ({ partner }) => void (await partner.post('/test/reset')) }
|
|
443
|
-
)
|
|
444
|
-
});
|
|
445
|
-
```
|
|
171
|
+
import { combineBackends } from '@crvouga/atlas/reports/compose';
|
|
172
|
+
import { directoryStore } from '@crvouga/atlas/reports/directory-store';
|
|
173
|
+
import { reportStatus } from '@crvouga/atlas/reports/report-status';
|
|
446
174
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
moment (`screenshot.also`).
|
|
454
|
-
- Timeline entries, clips, states and transitions carry `client` in the manifest;
|
|
455
|
-
`run.environment.clients` names the driver each client ran on; a failed attempt keeps one trace
|
|
456
|
-
per client.
|
|
457
|
-
- If one client cannot open (no simulator booted), the others are closed and the attempt fails
|
|
458
|
-
naming that client.
|
|
459
|
-
|
|
460
|
-
[`examples/todo/clients`](examples/todo/clients) runs a chart (`specs/clients/shared-list.scxml`)
|
|
461
|
-
across a phone, a desktop and the sync server; pick each device's driver with `ATLAS_PHONE` and
|
|
462
|
-
`ATLAS_DESKTOP` (`playwright`, `bun` or `webdriver`).
|
|
463
|
-
|
|
464
|
-
## Writing a driver
|
|
465
|
-
|
|
466
|
-
A driver owns the thing under test for one path attempt: a browser context, a device, a model or
|
|
467
|
-
an API client. Only `open` and `close` are required.
|
|
468
|
-
|
|
469
|
-
```ts
|
|
470
|
-
type Focus = { client?: string };
|
|
471
|
-
|
|
472
|
-
type Driver<C> = {
|
|
473
|
-
name: string;
|
|
474
|
-
open(input: { path: PlannedPath; attempt: number; mode: RunMode; directory: string; timeline: Timeline }): Promise<C>;
|
|
475
|
-
close(ctx: C, input: { failed: boolean; traceFile: string }): Promise<{ trace?: string; traces?: Record<string, string> } | void>;
|
|
476
|
-
screenshot?(ctx: C, file: string, focus?: Focus): Promise<Image | null>;
|
|
477
|
-
record?(ctx: C, workDirectory: string, focus?: Focus): Promise<{ stop(output: string): Promise<Clip | null> }>;
|
|
478
|
-
text?(ctx: C, focus?: Focus): Promise<string>;
|
|
479
|
-
hold?(ctx: C, ms: number, focus?: Focus): Promise<void>;
|
|
480
|
-
pacing?: { before: number; after: number };
|
|
481
|
-
clients?: Record<string, string>;
|
|
482
|
-
concurrency?: number;
|
|
483
|
-
dispose?(): Promise<void>;
|
|
484
|
-
};
|
|
175
|
+
const report = await combineBackends([
|
|
176
|
+
{ id: 'ci', store: directoryStore('all') },
|
|
177
|
+
{ id: 'lab', label: 'Device lab', store: deviceLabStore() }
|
|
178
|
+
]);
|
|
179
|
+
const status = reportStatus(report, { requireEnvironments: ['ios'] });
|
|
180
|
+
if (!status.ok) throw new Error(status.reasons.join('\n'));
|
|
485
181
|
```
|
|
486
182
|
|
|
487
|
-
|
|
488
|
-
A driver without a native screen recorder can encode clips from screenshots with
|
|
489
|
-
`recordFrames(capture, workDirectory)`.
|
|
490
|
-
|
|
491
|
-
- `open` returns a fresh context for each attempt; `close` may return the path of a trace file
|
|
492
|
-
(Playwright writes one for failed attempts).
|
|
493
|
-
- `screenshot` and `record` produce media; `record` is used only in showcase mode, one clip per
|
|
494
|
-
transition.
|
|
495
|
-
- `text` returns the visible text, for the privacy scan.
|
|
496
|
-
- `concurrency` is how many contexts can be open at once (1 when omitted). Workers never exceed
|
|
497
|
-
it, so a driver of one device stays sequential while browser contexts run side by side.
|
|
498
|
-
- `timeline.add({ kind, label, ... })` logs taps, typing, system calls and time jumps; they become
|
|
499
|
-
each clip's captions.
|
|
183
|
+
## Open standards
|
|
500
184
|
|
|
501
|
-
|
|
502
|
-
|
|
185
|
+
Atlas invents as little as it can. Everything it reads or writes is a format other tools already
|
|
186
|
+
understand, or a JSON Schema it publishes.
|
|
503
187
|
|
|
504
|
-
|
|
505
|
-
|
|
188
|
+
| Standard | Used for |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| [JUnit XML](https://github.com/testmoapp/junitxml) | Read: any test runner's results become a layer. Written: `junit.xml`, one test case per statechart path. |
|
|
191
|
+
| [CTRF](https://ctrf.io) | Read: any CTRF report becomes a layer. Written: `ctrf.json` for each statechart run. |
|
|
192
|
+
| [JSON Schema 2020-12](https://json-schema.org/draft/2020-12) | The contract: layers, check files, report status, run manifests, journeys, state nodes, timelines and the runs index (`@crvouga/atlas/schemas/*.schema.json`) |
|
|
193
|
+
| [Gherkin](https://cucumber.io/docs/gherkin/), [Quint](https://quint-lang.org), [DMN](https://www.omg.org/spec/DMN), [Alloy](https://alloytools.org), [OpenSLO](https://openslo.com) | Spec kinds the viewer lays results over, each in its own tab. Any other file is a spec of its own type. |
|
|
194
|
+
| [W3C SCXML](https://www.w3.org/TR/scxml/) | Statecharts (`*.scxml`), with business metadata in an `atlas` XML namespace |
|
|
195
|
+
| [XState v5](https://stately.ai/docs/machines) machine config | Statecharts (`*.machine.json`, `*.machine.yaml`): the JSON shape Stately Studio imports and exports |
|
|
196
|
+
| [WebVTT](https://www.w3.org/TR/webvtt1/) | Captions for every clip: each tap, keystroke, system call and screen change |
|
|
197
|
+
| [CloudEvents 1.0](https://cloudevents.io) | Business events the product emits (HTTP structured, binary and batch modes) |
|
|
198
|
+
| [W3C WebDriver](https://www.w3.org/TR/webdriver2/) | Driving browsers and, through Appium, native iOS and Android apps |
|
|
199
|
+
| [WAI-ARIA](https://www.w3.org/TR/wai-aria/) roles and accessible names | Signals that recognise screens the same way on every driver |
|
|
200
|
+
| MP4 / H.264 | Clips of each transition, encoded with FFmpeg |
|
|
201
|
+
| PNG / WebP | Screenshots at device resolution, with WebP thumbnails |
|
|
202
|
+
| [Mermaid](https://mermaid.js.org/syntax/stateDiagram.html) `stateDiagram-v2` | `chart.mmd`, and `atlas export --format mermaid` |
|
|
506
203
|
|
|
507
|
-
|
|
508
|
-
```
|
|
204
|
+
## Executable statecharts
|
|
509
205
|
|
|
510
|
-
|
|
206
|
+
Atlas can also run a product's flows as statecharts against the real app. Write down how the product
|
|
207
|
+
behaves as statecharts in open formats (W3C SCXML, XState v5 machine config), then let Atlas lint
|
|
208
|
+
them, plan paths that cover every transition, run those paths through any driver (a browser, a
|
|
209
|
+
phone, a plain function), and write a manifest of what happened, with screenshots, video clips,
|
|
210
|
+
captions, business events and standard test reports.
|
|
511
211
|
|
|
512
212
|
```
|
|
513
|
-
atlas lint
|
|
514
|
-
|
|
515
|
-
atlas
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
atlas
|
|
213
|
+
specs/ atlas lint the charts are pure, reachable, free of silent dead ends,
|
|
214
|
+
app.scxml ──▶ and every journey replays
|
|
215
|
+
child.machine.yaml atlas plan curated journeys + shortest paths covering every transition
|
|
216
|
+
journeys.yaml atlas run each path against the product, through a driver
|
|
217
|
+
│
|
|
218
|
+
▼
|
|
219
|
+
atlas-runs/<run>/manifest.json, junit.xml, ctrf.json, chart.mmd, media/
|
|
520
220
|
```
|
|
521
221
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
- the paths each journey is made of;
|
|
528
|
-
- the total and longest path length, and how many transitions stay uncovered.
|
|
529
|
-
- `run` exits non-zero when a path fails or the privacy scan finds something. `--paths` runs only
|
|
530
|
-
the given path ids; `--no-retry` skips the retry of failed paths.
|
|
531
|
-
- `--workers`, `--shard`, `--seed`, `--state`, `--rerun-failed` and `--reuse` are described in
|
|
532
|
-
[Seeds and parallel runs](#seeds-and-parallel-runs).
|
|
533
|
-
- `merge` composes runs of the same plan (shards, or a rerun and its earlier run) into one run.
|
|
534
|
-
- `export` writes the composed product (every child inlined) as SCXML, Mermaid, Markdown or
|
|
535
|
-
XState JSON. The Markdown is a readable map for the people who own the product: one section per
|
|
536
|
-
area (each parallel region apart), every state with its description and what the user sees (a
|
|
537
|
-
`snapshot` written as an identifier is left out), a table of every event that can happen there
|
|
538
|
-
and where it leads, then the named journeys.
|
|
539
|
-
|
|
540
|
-
The config file is TypeScript (loaded with `tsx`) and defaults to `atlas.config.ts` in the current
|
|
541
|
-
directory. Relative paths in it resolve against the config file's directory.
|
|
542
|
-
|
|
543
|
-
## Config reference
|
|
544
|
-
|
|
545
|
-
`defineConfig<C>(config)` returns the config unchanged, typed for the driver's context `C`.
|
|
546
|
-
|
|
547
|
-
| Field | Meaning |
|
|
548
|
-
| --- | --- |
|
|
549
|
-
| `specs` | The specs directory. |
|
|
550
|
-
| `driver` | A `Driver<C>`, or a function returning one (called once per run). |
|
|
551
|
-
| `implementation` | `{ events, states, seeds?, setup?, canStart?, correlate? }`. |
|
|
552
|
-
| `chart` | Run one chart of the product; the root chart when omitted. |
|
|
553
|
-
| `start`, `startLabel` | Where generated paths start (an XState state value) and how to describe it; the initial state when omitted. |
|
|
554
|
-
| `entryEvents` | When running one chart or one state: the only events that enter it from outside. |
|
|
555
|
-
| `state` | Run one state of a chart and what is inside it, with the transitions into and out of it: one area of a chart that models the whole product. |
|
|
556
|
-
| `workers` | Path attempts that run at once, capped by the driver's `concurrency`; one per CPU when omitted. |
|
|
557
|
-
| `beforeRun` | Runs once before any path: prepare what every seed relies on (reference data, configuration). |
|
|
558
|
-
| `output` | Where runs are written; `atlas-runs` beside the config. |
|
|
559
|
-
| `eventSources` | `() => EventSource[]`: where business events come from. |
|
|
560
|
-
| `eventMatchers` | How each business event name is recognised: `{ type }`, `{ pattern }` or a function. |
|
|
561
|
-
| `privacy` | Extra `deny` patterns, `allowEmails` and `allowHosts`. |
|
|
562
|
-
| `targets` | URLs the run reaches, checked against `privacy.allowHosts` before anything runs. |
|
|
563
|
-
| `environment` | Recorded in the manifest. |
|
|
564
|
-
| `stepTimeoutMs` | How long to wait for a state to show up (default 45 000). |
|
|
565
|
-
| `requiredMeta` | `meta` fields every state needs (default `['description']`). |
|
|
566
|
-
|
|
567
|
-
The programmatic API mirrors the CLI: `prepare(config)` loads, lints and plans; `runConfig(config,
|
|
568
|
-
options)` runs; `loadSpecDirectory`, `lintBundle`, `chartScope`, `planChart` and `runPaths` are the
|
|
569
|
-
steps underneath.
|
|
570
|
-
|
|
571
|
-
## How a run works
|
|
572
|
-
|
|
573
|
-
1. **Plan.** Curated journeys come first.
|
|
574
|
-
- When running one chart of a larger product, each journey is cut to its stretch inside the
|
|
575
|
-
chart, starting at an entry transition.
|
|
576
|
-
- With seeds, journeys are also cut wherever they pass through a seeded configuration.
|
|
577
|
-
- Then, for each transition still uncovered, the plan adds the shortest path that takes it from
|
|
578
|
-
the nearest seed or start. The search is breadth-first, so loops stay bounded.
|
|
579
|
-
- Paths stop before blocked events. A blocked path whose runnable part other paths already cover
|
|
580
|
-
is reported but not run.
|
|
581
|
-
2. **Run each path.** Paths run on a pool of workers, longest first. Each gets a fresh driver
|
|
582
|
-
context, then every event's `prepare`, then its seed's `run` (or `setup`). The start state must
|
|
583
|
-
be recognised. Then for each step: run the event, wait for the target state, run its checks,
|
|
584
|
-
collect business events, screenshot, `settle`.
|
|
585
|
-
3. **Retry.** A failed path runs once more. Passing the second time makes it `flaky`.
|
|
586
|
-
4. **Write.** The manifest and reports are rewritten each time a path finishes, from every outcome
|
|
587
|
-
so far in plan order, so a crash keeps what ran. `outcomes.json` keeps the raw outcomes, for
|
|
588
|
-
`atlas merge` and `--rerun-failed`.
|
|
589
|
-
|
|
590
|
-
Every path, state and transition ends as `passed`, `failed`, `flaky` or `not-reached`. Reaching a
|
|
591
|
-
state also reaches its ancestors. A failure records the recogniser's signals and which known states
|
|
592
|
-
the screen looked like instead.
|
|
593
|
-
|
|
594
|
-
### Run modes
|
|
595
|
-
|
|
596
|
-
| Mode | Pacing | Media |
|
|
597
|
-
| --- | --- | --- |
|
|
598
|
-
| `fast` | None | Screenshots of every state (when the driver takes them) |
|
|
599
|
-
| `showcase` | Holds before and after each step; taps glide and type at human speed | Screenshots, plus an H.264 clip of every transition with a poster and WebVTT captions |
|
|
600
|
-
|
|
601
|
-
## Manifest and reports
|
|
602
|
-
|
|
603
|
-
Each run writes `<output>/<timestamp>-<mode>/`:
|
|
604
|
-
|
|
605
|
-
| File | Contents |
|
|
606
|
-
| --- | --- |
|
|
607
|
-
| `manifest.json` | The run (commit, branch, environment, mode, spec version, scope), every chart, the composed map (nodes and edges), every state and transition with status, media, timeline and reasons, every path with its attempts, coverage and privacy results. Schema: `packages/schema/schemas/manifest.schema.json`. |
|
|
608
|
-
| `junit.xml` | One test case per path: failures, skips (with the blocking reason) and flaky passes. |
|
|
609
|
-
| `ctrf.json` | The same as CTRF. |
|
|
610
|
-
| `chart.mmd` | The composed chart as a Mermaid state diagram. |
|
|
611
|
-
| `outcomes.json` | Each path's raw outcome by path key, which the manifest is folded from: for merging shards and reusing passed paths. |
|
|
612
|
-
| `media/paths/<path>/NN.png`, `.webp` | Screenshots of each state a path reached; `failed-NN.png` where it failed. |
|
|
613
|
-
| `media/clips/<path>/NN.mp4`, `.poster.webp`, `.vtt` | Showcase clips of each transition, with captions. |
|
|
614
|
-
| `media/traces/<path>.zip` | Driver traces of failed attempts (Playwright traces with the Playwright driver). |
|
|
615
|
-
|
|
616
|
-
## Business events
|
|
617
|
-
|
|
618
|
-
States list the business events that happen on entering them in `meta.events`. An `EventSource`
|
|
619
|
-
says what actually happened. `mark()` returns a position before each event, and `collect(mark)`
|
|
620
|
-
returns what arrived after it, once the state is recognised:
|
|
621
|
-
|
|
622
|
-
- `cloudEventsHttpSource()` starts an HTTP endpoint that accepts CloudEvents in structured, binary
|
|
623
|
-
or batch mode. Point the product's event bus, webhook relay or collector at its `url`.
|
|
624
|
-
- `logFileSource(file)` collects lines appended to a log, for products whose only event stream is a
|
|
625
|
-
log; match them with `{ pattern }`.
|
|
626
|
-
- Any object with `name`, `mark` and `collect` works, such as an in-memory bus in a unit test.
|
|
627
|
-
|
|
628
|
-
With no matcher, a business event is observed when a CloudEvent's `type` equals its name. Events
|
|
629
|
-
with neither a matcher nor a CloudEvents source are reported as "no signal", never as missing.
|
|
630
|
-
|
|
631
|
-
## Privacy
|
|
632
|
-
|
|
633
|
-
Runs produce media and manifests that get shared, so Atlas refuses to leak real data:
|
|
634
|
-
|
|
635
|
-
- Before running, every `targets` URL must be on `allowHosts` (loopback only by default).
|
|
636
|
-
- After every step, the driver's visible text is scanned, and the manifest is scanned before it is
|
|
637
|
-
written. By default it flags email addresses outside `example.com`/`.org`/`.net` and the `.test`,
|
|
638
|
-
`.example`, `.invalid`, `.localhost` and `.local` domains, anything shaped like a US Social Security
|
|
639
|
-
number, and card numbers other than the well-known test cards. Findings describe the match without
|
|
640
|
-
repeating it.
|
|
641
|
-
- Any finding fails the run. Add `deny` patterns and `allowEmails` in `privacy`.
|
|
642
|
-
|
|
643
|
-
## Visualizer
|
|
644
|
-
|
|
645
|
-
The [`atlas-visualizer`](packages/visualizer) command renders the charts from the specs directory and
|
|
646
|
-
overlays runs from the runs directory: screenshots on states, clips on transitions, statuses and
|
|
647
|
-
history. See its README for how to point it at a project.
|
|
648
|
-
|
|
649
|
-
Its open index lists every place, journey and spec beside one plate. Connect multiple local
|
|
650
|
-
directories, HTTP report stores and streaming
|
|
651
|
-
APIs using [report backends](docs/report-backends.md). The runner publishes reached screens
|
|
652
|
-
and completed steps while paths are still running.
|
|
222
|
+
Each run publishes as a layer (`atlas report publish --manifest <run>/manifest.json`), so it combines
|
|
223
|
+
with every other suite's evidence. Shards, seeds, several clients in one run, drivers (Playwright,
|
|
224
|
+
Bun WebView, WebDriver and Appium, Detox, plain functions), business events and the privacy scan are
|
|
225
|
+
in **[Executable statecharts](docs/statecharts.md)**, which starts with a runnable example,
|
|
226
|
+
[`examples/todo`](https://github.com/crvouga/atlas/tree/main/examples/todo).
|
|
653
227
|
|
|
654
228
|
## License
|
|
655
229
|
|