@itmetsam/samurai-framework 0.1.0 → 0.2.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/README.md +129 -0
- package/bin/samurai.mjs +31 -0
- package/dist/api.js +3 -0
- package/dist/assert/assertion-error.d.ts +23 -0
- package/dist/assert/assertion-error.js +45 -0
- package/dist/assert/expect.d.ts +55 -0
- package/dist/assert/expect.js +188 -0
- package/dist/browser/browser-context.d.ts +91 -0
- package/dist/browser/browser-context.js +146 -0
- package/dist/browser/browser-finder.d.ts +2 -0
- package/dist/browser/browser-finder.js +95 -0
- package/dist/browser/browser.d.ts +95 -0
- package/dist/browser/browser.js +372 -0
- package/dist/browser/context-tree.d.ts +23 -0
- package/dist/browser/context-tree.js +60 -0
- package/dist/browser/dialog.d.ts +37 -0
- package/dist/browser/dialog.js +61 -0
- package/dist/browser/download.d.ts +71 -0
- package/dist/browser/download.js +137 -0
- package/dist/browser/downloads-dir.d.ts +8 -0
- package/dist/browser/downloads-dir.js +38 -0
- package/dist/browser/emulation.d.ts +61 -0
- package/dist/browser/emulation.js +144 -0
- package/dist/browser/file-chooser.d.ts +39 -0
- package/dist/browser/file-chooser.js +86 -0
- package/dist/browser/file-paths.d.ts +4 -0
- package/dist/browser/file-paths.js +18 -0
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.js +4 -0
- package/dist/browser/navigation-error.d.ts +13 -0
- package/dist/browser/navigation-error.js +19 -0
- package/dist/browser/page-logs.d.ts +86 -0
- package/dist/browser/page-logs.js +149 -0
- package/dist/browser/page-wait.d.ts +9 -0
- package/dist/browser/page-wait.js +17 -0
- package/dist/browser/page.d.ts +370 -0
- package/dist/browser/page.js +1038 -0
- package/dist/browser/pdf.d.ts +72 -0
- package/dist/browser/pdf.js +37 -0
- package/dist/browser/screenshot.d.ts +29 -0
- package/dist/browser/screenshot.js +42 -0
- package/dist/cli/args.d.ts +35 -0
- package/dist/cli/args.js +69 -0
- package/dist/cli/cli.cli-test.d.ts +1 -0
- package/dist/cli/cli.cli-test.js +170 -0
- package/dist/cli/console-reporter.d.ts +15 -0
- package/dist/cli/console-reporter.js +52 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.js +61 -0
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.js +156 -0
- package/dist/config/config.d.ts +24 -0
- package/dist/config/config.js +42 -0
- package/dist/config/default.d.ts +2 -0
- package/dist/config/default.js +3 -0
- package/dist/config/mask.d.ts +11 -0
- package/dist/config/mask.js +85 -0
- package/dist/config/run-settings.d.ts +28 -0
- package/dist/config/run-settings.js +61 -0
- package/dist/config/secrets.d.ts +10 -0
- package/dist/config/secrets.js +67 -0
- package/dist/config/variables.d.ts +11 -0
- package/dist/config/variables.js +26 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -0
- package/dist/locator/action-timeout-error.d.ts +25 -0
- package/dist/locator/action-timeout-error.js +35 -0
- package/dist/locator/element-state.d.ts +44 -0
- package/dist/locator/element-state.js +109 -0
- package/dist/locator/label-locator.d.ts +13 -0
- package/dist/locator/label-locator.js +49 -0
- package/dist/locator/locator.d.ts +168 -0
- package/dist/locator/locator.js +591 -0
- package/dist/locator/selector-errors.d.ts +9 -0
- package/dist/locator/selector-errors.js +15 -0
- package/dist/locator/selector.d.ts +60 -0
- package/dist/locator/selector.js +113 -0
- package/dist/locator/text-locator.d.ts +13 -0
- package/dist/logger/index.d.ts +2 -0
- package/dist/logger/index.js +30 -0
- package/dist/network/data-collector.d.ts +26 -0
- package/dist/network/data-collector.js +77 -0
- package/dist/network/network-tracker.d.ts +94 -0
- package/dist/network/network-tracker.js +198 -0
- package/dist/network/network-wait.d.ts +16 -0
- package/dist/network/network-wait.js +24 -0
- package/dist/network/response.d.ts +22 -0
- package/dist/network/response.js +42 -0
- package/dist/network/router.d.ts +120 -0
- package/dist/network/router.js +343 -0
- package/dist/network/url-match.d.ts +8 -0
- package/dist/network/url-match.js +38 -0
- package/dist/recorder/apply.d.ts +3 -0
- package/dist/recorder/apply.js +10 -0
- package/dist/recorder/capture.d.ts +18 -0
- package/dist/recorder/capture.js +198 -0
- package/dist/recorder/index.d.ts +3 -0
- package/dist/recorder/index.js +3 -0
- package/dist/recorder/normalise.d.ts +68 -0
- package/dist/recorder/normalise.js +104 -0
- package/dist/recorder/ranking.d.ts +9 -0
- package/dist/recorder/ranking.js +21 -0
- package/dist/recorder/recorder.d.ts +52 -0
- package/dist/recorder/recorder.js +143 -0
- package/dist/recorder/session.d.ts +49 -0
- package/dist/recorder/session.js +113 -0
- package/dist/runner/module-type.d.ts +6 -0
- package/dist/runner/module-type.js +29 -0
- package/dist/runner/page-logs-report.d.ts +40 -0
- package/dist/runner/page-logs-report.js +44 -0
- package/dist/runner/prepare-run.d.ts +14 -0
- package/dist/runner/prepare-run.js +20 -0
- package/dist/runner/project.d.ts +17 -0
- package/dist/runner/project.js +33 -0
- package/dist/runner/registry.d.ts +7 -0
- package/dist/runner/registry.js +40 -0
- package/dist/runner/reporter.d.ts +43 -0
- package/dist/runner/reporter.js +83 -0
- package/dist/runner/run.d.ts +27 -0
- package/dist/runner/run.js +34 -0
- package/dist/runner/test-error.d.ts +3 -0
- package/dist/runner/test-error.js +18 -0
- package/dist/runner/test-runner.d.ts +44 -0
- package/dist/runner/test-runner.js +205 -0
- package/dist/script/call-function.d.ts +34 -0
- package/dist/script/call-function.js +71 -0
- package/dist/script/element-handle.d.ts +10 -0
- package/{src/script/element-handle.ts → dist/script/element-handle.js} +4 -4
- package/dist/script/helpers.d.ts +28 -0
- package/dist/script/helpers.js +50 -0
- package/dist/script/preload.d.ts +31 -0
- package/dist/script/preload.js +71 -0
- package/dist/script/serialize.d.ts +29 -0
- package/dist/script/serialize.js +207 -0
- package/dist/steps/build.d.ts +5 -0
- package/dist/steps/build.js +38 -0
- package/dist/steps/edit.d.ts +30 -0
- package/dist/steps/edit.js +80 -0
- package/dist/steps/emit.d.ts +6 -0
- package/dist/steps/emit.js +73 -0
- package/dist/steps/index.d.ts +5 -0
- package/dist/steps/index.js +4 -0
- package/dist/steps/model.d.ts +103 -0
- package/dist/steps/model.js +1 -0
- package/dist/steps/parse.d.ts +3 -0
- package/dist/steps/parse.js +272 -0
- package/dist/transport/bidi-connection.d.ts +46 -0
- package/dist/transport/bidi-connection.js +176 -0
- package/dist/transport/bidi-error.d.ts +10 -0
- package/dist/transport/bidi-error.js +10 -0
- package/dist/transport/subscriptions.d.ts +36 -0
- package/dist/transport/subscriptions.js +81 -0
- package/{src → dist}/types/test.d.ts +10 -4
- package/dist/wait/wait-until.d.ts +20 -0
- package/dist/wait/wait-until.js +63 -0
- package/package.json +44 -7
- package/.claude/settings.local.json +0 -7
- package/.env.example +0 -3
- package/.github/actions/setup/action.yml +0 -16
- package/.github/workflows/ci.yml +0 -71
- package/.trunk/configs/.markdownlint.yaml +0 -2
- package/.trunk/configs/.yamllint.yaml +0 -7
- package/.trunk/configs/svgo.config.mjs +0 -14
- package/.trunk/trunk.yaml +0 -34
- package/docs/superpowers/bidi-roadmap.md +0 -60
- package/docs/superpowers/plans/2026-09-29-auto-wait.md +0 -1827
- package/docs/superpowers/plans/2026-09-29-expect.md +0 -1350
- package/docs/superpowers/plans/2026-10-01-ci.md +0 -350
- package/docs/superpowers/plans/2026-10-01-test-api-alignment.md +0 -1821
- package/docs/superpowers/recorder-roadmap.md +0 -52
- package/docs/superpowers/specs/2026-09-29-auto-wait-design.md +0 -185
- package/docs/superpowers/specs/2026-09-29-bidi-foundation-design.md +0 -130
- package/docs/superpowers/specs/2026-09-29-contexts-and-storage-design.md +0 -88
- package/docs/superpowers/specs/2026-09-29-downloads-and-file-dialogs-design.md +0 -94
- package/docs/superpowers/specs/2026-09-29-emulation-design.md +0 -88
- package/docs/superpowers/specs/2026-09-29-expect-design.md +0 -130
- package/docs/superpowers/specs/2026-09-29-locate-nodes-design.md +0 -90
- package/docs/superpowers/specs/2026-09-29-logs-and-errors-design.md +0 -87
- package/docs/superpowers/specs/2026-09-29-navigation-waits-design.md +0 -83
- package/docs/superpowers/specs/2026-09-29-network-data-design.md +0 -78
- package/docs/superpowers/specs/2026-09-29-network-interception-design.md +0 -91
- package/docs/superpowers/specs/2026-09-29-network-tracking-design.md +0 -86
- package/docs/superpowers/specs/2026-09-29-page-api-design.md +0 -90
- package/docs/superpowers/specs/2026-09-29-preload-scripts-design.md +0 -82
- package/docs/superpowers/specs/2026-09-29-script-call-function-design.md +0 -107
- package/docs/superpowers/specs/2026-09-29-set-files-design.md +0 -62
- package/docs/superpowers/specs/2026-09-29-user-prompts-design.md +0 -75
- package/docs/superpowers/specs/2026-10-01-ci-design.md +0 -106
- package/docs/superpowers/specs/2026-10-01-test-api-alignment-design.md +0 -241
- package/public/logo-dark.svg +0 -236
- package/public/logo-light.svg +0 -262
- package/samurai.config.ts +0 -7
- package/src/assert/assertion-error.test.ts +0 -55
- package/src/assert/assertion-error.ts +0 -62
- package/src/assert/expect-locator.test.ts +0 -217
- package/src/assert/expect.test.ts +0 -73
- package/src/assert/expect.ts +0 -299
- package/src/browser/browser-context.browser-test.ts +0 -201
- package/src/browser/browser-context.test.ts +0 -407
- package/src/browser/browser-context.ts +0 -208
- package/src/browser/browser-finder.ts +0 -110
- package/src/browser/browser.test.ts +0 -88
- package/src/browser/browser.ts +0 -515
- package/src/browser/context-tree.test.ts +0 -86
- package/src/browser/context-tree.ts +0 -70
- package/src/browser/dialog.browser-test.ts +0 -144
- package/src/browser/dialog.test.ts +0 -322
- package/src/browser/dialog.ts +0 -83
- package/src/browser/download.test.ts +0 -161
- package/src/browser/download.ts +0 -172
- package/src/browser/downloads-dir.test.ts +0 -75
- package/src/browser/downloads-dir.ts +0 -39
- package/src/browser/emulation.browser-test.ts +0 -186
- package/src/browser/emulation.test.ts +0 -200
- package/src/browser/emulation.ts +0 -204
- package/src/browser/file-chooser.test.ts +0 -142
- package/src/browser/file-chooser.ts +0 -99
- package/src/browser/file-paths.ts +0 -18
- package/src/browser/goto.browser-test.ts +0 -44
- package/src/browser/navigation-error.ts +0 -26
- package/src/browser/navigation.browser-test.ts +0 -172
- package/src/browser/page-api.browser-test.ts +0 -228
- package/src/browser/page-logs.browser-test.ts +0 -119
- package/src/browser/page-logs.test.ts +0 -212
- package/src/browser/page-logs.ts +0 -208
- package/src/browser/page-wait.ts +0 -19
- package/src/browser/page.test.ts +0 -1567
- package/src/browser/page.ts +0 -1176
- package/src/browser/pdf.ts +0 -62
- package/src/browser/screenshot.ts +0 -65
- package/src/browser/transfers.browser-test.ts +0 -180
- package/src/config/config.ts +0 -33
- package/src/config/default.ts +0 -5
- package/src/config/fixtures.test.ts +0 -137
- package/src/config/mask.test.ts +0 -143
- package/src/config/mask.ts +0 -87
- package/src/config/run-settings.test.ts +0 -163
- package/src/config/run-settings.ts +0 -114
- package/src/config/secrets.ts +0 -70
- package/src/config/variables.ts +0 -31
- package/src/index.ts +0 -10
- package/src/locator/action-timeout-error.test.ts +0 -83
- package/src/locator/action-timeout-error.ts +0 -72
- package/src/locator/element-state.test.ts +0 -181
- package/src/locator/element-state.ts +0 -159
- package/src/locator/locate-nodes.browser-test.ts +0 -169
- package/src/locator/locator-actions.test.ts +0 -355
- package/src/locator/locator-chain.test.ts +0 -292
- package/src/locator/locator.browser-test.ts +0 -137
- package/src/locator/locator.test.ts +0 -179
- package/src/locator/locator.ts +0 -656
- package/src/locator/selector-errors.ts +0 -18
- package/src/locator/selector.test.ts +0 -124
- package/src/locator/selector.ts +0 -107
- package/src/locator/set-files.browser-test.ts +0 -141
- package/src/locator/set-files.test.ts +0 -191
- package/src/locator/text-locator.test.ts +0 -118
- package/src/logger/index.test.ts +0 -28
- package/src/logger/index.ts +0 -40
- package/src/network/data-collector.test.ts +0 -111
- package/src/network/data-collector.ts +0 -92
- package/src/network/network-data.browser-test.ts +0 -192
- package/src/network/network-tracker.browser-test.ts +0 -151
- package/src/network/network-tracker.test.ts +0 -307
- package/src/network/network-tracker.ts +0 -286
- package/src/network/network-wait.ts +0 -28
- package/src/network/response.test.ts +0 -93
- package/src/network/response.ts +0 -58
- package/src/network/router.browser-test.ts +0 -260
- package/src/network/router.test.ts +0 -528
- package/src/network/router.ts +0 -420
- package/src/network/url-match.test.ts +0 -69
- package/src/network/url-match.ts +0 -41
- package/src/runner/page-logs-report.test.ts +0 -144
- package/src/runner/page-logs-report.ts +0 -82
- package/src/runner/prepare-run.test.ts +0 -33
- package/src/runner/prepare-run.ts +0 -36
- package/src/runner/registry.test.ts +0 -59
- package/src/runner/registry.ts +0 -46
- package/src/runner/reporter.test.ts +0 -73
- package/src/runner/reporter.ts +0 -109
- package/src/runner/test-error.test.ts +0 -39
- package/src/runner/test-error.ts +0 -20
- package/src/runner/test-runner.ts +0 -184
- package/src/script/call-function.browser-test.ts +0 -112
- package/src/script/call-function.test.ts +0 -174
- package/src/script/call-function.ts +0 -113
- package/src/script/helpers.test.ts +0 -116
- package/src/script/helpers.ts +0 -68
- package/src/script/preload.browser-test.ts +0 -144
- package/src/script/preload.test.ts +0 -180
- package/src/script/preload.ts +0 -102
- package/src/script/serialize.test.ts +0 -182
- package/src/script/serialize.ts +0 -203
- package/src/testing/browser-fixture.ts +0 -18
- package/src/testing/fake-websocket.ts +0 -72
- package/src/testing/stub-connector.ts +0 -154
- package/src/tests/index.spec.ts +0 -30
- package/src/transport/bidi-connection.test.ts +0 -155
- package/src/transport/bidi-connection.ts +0 -256
- package/src/transport/bidi-error.ts +0 -23
- package/src/transport/foundation.browser-test.ts +0 -73
- package/src/transport/subscriptions.test.ts +0 -140
- package/src/transport/subscriptions.ts +0 -110
- package/src/types/bidi-commands.test-d.ts +0 -55
- package/src/wait/wait-until.test.ts +0 -117
- package/src/wait/wait-until.ts +0 -86
- package/tsconfig.json +0 -44
- /package/{src/api.ts → dist/api.d.ts} +0 -0
- /package/{src/locator/text-locator.ts → dist/locator/text-locator.js} +0 -0
- /package/{src → dist}/types/bidi-modules/browser.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/browsing-context.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/emulation.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/input.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/log.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/network.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/permissions.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/script.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/session.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/storage.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/web-extensions.d.ts +0 -0
- /package/{src → dist}/types/bidi-protocols/command.d.ts +0 -0
- /package/{src → dist}/types/bidi-protocols/error.d.ts +0 -0
- /package/{src → dist}/types/bidi-protocols/event.d.ts +0 -0
- /package/{src → dist}/types/bidi.d.ts +0 -0
- /package/{src → dist}/types/browser.d.ts +0 -0
- /package/{src → dist}/types/config.d.ts +0 -0
- /package/{src → dist}/types/env.d.ts +0 -0
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# Continuous integration — design
|
|
2
|
-
|
|
3
|
-
Date: 2026-10-01
|
|
4
|
-
Status: implemented
|
|
5
|
-
Branch: `ci/github-actions`, based on `feat/test-api-alignment` (PR #6)
|
|
6
|
-
Depends on: PR #6 (on `main` today, `tsc` reports errors and every browser test fails on Firefox 157's privileged `about:home`)
|
|
7
|
-
|
|
8
|
-
## Goal
|
|
9
|
-
|
|
10
|
-
Every push to `main` and every pull request runs the typecheck, the unit tests, the browser tests and Trunk's code quality checks on GitHub Actions, using bun.
|
|
11
|
-
|
|
12
|
-
## Non-goals
|
|
13
|
-
|
|
14
|
-
- ESLint. Code quality checks come from Trunk (below), which runs the linters configured in `.trunk/trunk.yaml`.
|
|
15
|
-
- Trunk on the whole repository (`trunk check --all`): existing files are not all formatted yet; CI checks only what a change touches.
|
|
16
|
-
- A macOS or Windows runner. Linux finds the same protocol bugs at a tenth of the macOS minutes.
|
|
17
|
-
- Test coverage reports, caching tuned beyond what `setup-bun` does, release or publish jobs.
|
|
18
|
-
- Pinning the Firefox version (see Risks).
|
|
19
|
-
|
|
20
|
-
## Decisions
|
|
21
|
-
|
|
22
|
-
- **bun, not npm.** `bun install --frozen-lockfile` and `bun run <script>`; `bunx` for one-off binaries. The package scripts are unchanged: `bun run test` still runs `tsx --test` on Node's test runner. CI does not switch to `bun test`.
|
|
23
|
-
- **Node is still installed.** The scripts run `tsx`, which runs on Node, and the runner uses `fs/promises` `glob` (Node 22+). CI pins Node 26, the version the code is developed against.
|
|
24
|
-
- **Four parallel jobs** instead of one job with four steps: a failure names the broken check, and the checks don't wait on each other.
|
|
25
|
-
- **Trunk checks changed files only.** The same `.trunk/trunk.yaml` drives the local pre-commit/pre-push hooks and CI, so it is committed (with Trunk's own `.trunk/.gitignore` and `.trunk/configs/`).
|
|
26
|
-
|
|
27
|
-
## Workflow — `.github/workflows/ci.yml`
|
|
28
|
-
|
|
29
|
-
### Triggers
|
|
30
|
-
|
|
31
|
-
```yaml
|
|
32
|
-
on:
|
|
33
|
-
push:
|
|
34
|
-
branches: [main]
|
|
35
|
-
pull_request:
|
|
36
|
-
|
|
37
|
-
concurrency:
|
|
38
|
-
group: ci-${{ github.ref }}
|
|
39
|
-
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
40
|
-
|
|
41
|
-
permissions:
|
|
42
|
-
contents: read
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
A new push to a pull request cancels that pull request's run in progress; runs on `main` always finish. The workflow only reads the repository.
|
|
46
|
-
|
|
47
|
-
### Shared setup (`typecheck`, `unit`, `browser`)
|
|
48
|
-
|
|
49
|
-
1. `actions/checkout@v4`
|
|
50
|
-
2. `oven-sh/setup-bun@v2` with `bun-version: 1.3.0` (the local version)
|
|
51
|
-
3. `actions/setup-node@v4` with `node-version: 26`
|
|
52
|
-
4. `bun install --frozen-lockfile` — fails when `bun.lock` and `package.json` disagree
|
|
53
|
-
|
|
54
|
-
Each job runs on `ubuntu-latest`.
|
|
55
|
-
|
|
56
|
-
### Jobs
|
|
57
|
-
|
|
58
|
-
| Job id | Name | Command | `timeout-minutes` |
|
|
59
|
-
| ----------- | ----------------------- | ------------------------------------------------ | ----------------- |
|
|
60
|
-
| `typecheck` | Typecheck | `bunx tsc --noEmit -p .` | 5 |
|
|
61
|
-
| `unit` | Unit tests | `bun run test` | 10 |
|
|
62
|
-
| `browser` | Browser tests (Firefox) | `firefox --version`, then `bun run test:browser` | 15 |
|
|
63
|
-
| `trunk` | Code quality (Trunk) | `trunk-io/trunk-action@v1` | 15 |
|
|
64
|
-
|
|
65
|
-
- The browser job uses the Firefox preinstalled on `ubuntu-latest`; the framework finds it with `which firefox`.
|
|
66
|
-
- `firefox --version` is its own step so the log shows which Firefox ran.
|
|
67
|
-
- `timeout-minutes` stops a hung browser run instead of holding a runner for the default 6 hours.
|
|
68
|
-
- The browser test step has its own 12-minute timeout so a hang fails the step and the logs are still uploaded.
|
|
69
|
-
|
|
70
|
-
### The `trunk` job
|
|
71
|
-
|
|
72
|
-
- Steps: `actions/checkout@v4`, then `trunk-io/trunk-action@v1`. It installs the Trunk CLI and the linter versions pinned in `.trunk/trunk.yaml` itself; no bun or Node setup.
|
|
73
|
-
- On a pull request it checks only the files the PR changes; on a push to `main`, only what the push changed. Findings appear as annotations on the changed lines.
|
|
74
|
-
- It needs `checks: write` (job-level `permissions`, on top of the workflow's `contents: read`) to publish those annotations.
|
|
75
|
-
- Linters enabled today: prettier, markdownlint, yamllint, svgo, git-diff-check, and the security scanners checkov, trufflehog, osv-scanner and grype. Changing that list is a change to `.trunk/trunk.yaml`, not to the workflow.
|
|
76
|
-
|
|
77
|
-
### When a job fails
|
|
78
|
-
|
|
79
|
-
The browser job uploads the framework's logs and the report:
|
|
80
|
-
|
|
81
|
-
```yaml
|
|
82
|
-
- if: failure()
|
|
83
|
-
uses: actions/upload-artifact@v4
|
|
84
|
-
with:
|
|
85
|
-
name: browser-test-logs
|
|
86
|
-
path: |
|
|
87
|
-
logs/
|
|
88
|
-
result/
|
|
89
|
-
retention-days: 7
|
|
90
|
-
if-no-files-found: ignore
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
`logs/combined.log` holds the BiDi traffic, which is how browser failures are diagnosed. Test secrets in CI are fake values, and the logger masks registered secrets.
|
|
94
|
-
|
|
95
|
-
## Risks
|
|
96
|
-
|
|
97
|
-
- **Firefox version drift.** `ubuntu-latest` installs Firefox from a PPA; its version can differ from the local Firefox 157, so a browser test can pass locally and fail in CI or the reverse. Mitigation: the version is logged; if a version-specific failure appears, pin it with `browser-actions/setup-firefox` in a follow-up.
|
|
98
|
-
- **Headless Firefox on Linux** has no display; the browser tests already launch with `headless: true`.
|
|
99
|
-
- **Trunk on changed files** can still flag pre-existing problems in a file a PR touches (e.g. formatting of lines the PR did not change). The PR then formats that file; the local `trunk fmt` pre-commit hook does this automatically.
|
|
100
|
-
|
|
101
|
-
## Verification
|
|
102
|
-
|
|
103
|
-
- `ci.yml` is valid YAML and passes `actionlint` (run with `bunx` or the `rhysd/actionlint` Docker image if available locally; otherwise the first GitHub run is the check).
|
|
104
|
-
- The commands each job runs pass locally with bun: `bun install --frozen-lockfile`, `bunx tsc --noEmit -p .`, `bun run test`, `bun run test:browser`.
|
|
105
|
-
- `trunk check` passes locally on the files this branch changes.
|
|
106
|
-
- After pushing the branch and opening a PR against `feat/test-api-alignment`, all four jobs are green on GitHub. A failing check is diagnosed from the run log (and the artifact for the browser job), not guessed.
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
# Test API alignment — design
|
|
2
|
-
|
|
3
|
-
Date: 2026-10-01
|
|
4
|
-
Status: implemented
|
|
5
|
-
Roadmap: [recorder-roadmap.md](../recorder-roadmap.md), item 1
|
|
6
|
-
Depends on: nothing new (builds on the existing runner, `Page` and config)
|
|
7
|
-
|
|
8
|
-
## Goal
|
|
9
|
-
|
|
10
|
-
Give tests the shape the SAMURAI UI and the recorder will generate and parse: a fixtures object, `describe` blocks, `page.goto` relative to the environment's base URL, per-environment variables and secrets.
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
import { test, describe, expect } from "samurai-framework";
|
|
14
|
-
|
|
15
|
-
describe("Checkout", () => {
|
|
16
|
-
test("Checkout with saved card", async ({ page, browser, env, secrets }) => {
|
|
17
|
-
await page.goto("/products");
|
|
18
|
-
await page.locator("input[@id='email']").fill(env.customerEmail);
|
|
19
|
-
await page.locator("input[@id='card']").fill(secrets.TEST_CARD);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
```ts
|
|
25
|
-
// samurai.config.ts
|
|
26
|
-
export default defineConfig({
|
|
27
|
-
baseURL: "https://harbor.shop",
|
|
28
|
-
timeout: 30000,
|
|
29
|
-
expect: { timeout: 5000 },
|
|
30
|
-
defaultEnvironment: "staging",
|
|
31
|
-
environments: {
|
|
32
|
-
dev: { baseURL: "http://localhost:3000", variables: { customerEmail: "dev@example.test" } },
|
|
33
|
-
staging: {
|
|
34
|
-
baseURL: "https://staging.harbor.shop",
|
|
35
|
-
timeout: 45000,
|
|
36
|
-
expect: { timeout: 8000 },
|
|
37
|
-
variables: { customerEmail: "sam@example.test" },
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Non-goals
|
|
44
|
-
|
|
45
|
-
- The `api.<name>` fixture (per-environment HTTP client). Roadmap "Later".
|
|
46
|
-
- Masking secrets in screenshots, videos and traces. Roadmap "Later".
|
|
47
|
-
- Hooks (`beforeEach`, `afterAll`, …), `test.skip`, `test.only`.
|
|
48
|
-
- Secret providers other than environment variables (keychain, vault). The Electron app and CI read those stores themselves and pass values as environment variables.
|
|
49
|
-
- Environments stored only in the Electron app ("In SAMURAI only" in the designs).
|
|
50
|
-
- Group execution (still a TODO in `test-runner.ts`).
|
|
51
|
-
- The "used by N tests" count for secrets and variables (needs the step parser, roadmap item 3).
|
|
52
|
-
|
|
53
|
-
## Decisions
|
|
54
|
-
|
|
55
|
-
- **Clean break.** `test(name, async (page, browser) => …)` is removed. Only the fixtures-object form exists. `src/tests/index.spec.ts` is migrated. One signature keeps the parser in roadmap item 3 simple.
|
|
56
|
-
- **Secrets come only from environment variables.** No provider plugins in the framework.
|
|
57
|
-
- **Every secret is masked, whatever its length.** Short values match as whole tokens only.
|
|
58
|
-
|
|
59
|
-
## Components
|
|
60
|
-
|
|
61
|
-
### Public entry point — `src/api.ts` (new)
|
|
62
|
-
|
|
63
|
-
```ts
|
|
64
|
-
export { test, describe } from "./runner/test-runner.js";
|
|
65
|
-
export { expect } from "./assert/expect.js";
|
|
66
|
-
export type { TestFixtures } from "./types/test.js";
|
|
67
|
-
export { defineConfig } from "./config/config.js";
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
- `package.json` gets `"exports": { ".": "./src/api.ts" }` (pointing at `dist/api.js` once a build step ships), so tests import from `"samurai-framework"`.
|
|
71
|
-
- `src/index.ts` stays the CLI entry point (runs the tests).
|
|
72
|
-
|
|
73
|
-
### `describe` and `test` — `src/runner/test-runner.ts`
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
export interface TestFixtures {
|
|
77
|
-
page: Page;
|
|
78
|
-
browser: Browser;
|
|
79
|
-
env: Readonly<Record<string, string | number | boolean>>;
|
|
80
|
-
secrets: Readonly<Record<string, string>>;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function describe(title: string, fn: () => void): void;
|
|
84
|
-
export function test(title: string, fn: (fixtures: TestFixtures) => Promise<void>): void;
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
- `describe` pushes `title` onto a module-level title stack, calls `fn` synchronously, then pops it (also when `fn` throws). Blocks nest.
|
|
88
|
-
- `fn` returning a Promise throws `describe() callback must be synchronous; it only registers tests`.
|
|
89
|
-
- `test` registers `{ title, titlePath, file, function }`. The full title is `titlePath.join(" > ")`, e.g. `"Checkout > Checkout with saved card"`, the same format as `SamuraiTest.title` in the config.
|
|
90
|
-
- `TestCaseBase.function` changes to `(fixtures: TestFixtures) => Promise<void>`. `TestCaseBase.name` becomes the full title.
|
|
91
|
-
- The reporter keys results by `file` + full title, so two tests with the same name in different `describe` blocks or files don't overwrite each other. Reports show the full title.
|
|
92
|
-
- The runner builds the fixtures per test: `page` and `browser` from `Browser.launch` (as now), `env` and `secrets` from the run (below).
|
|
93
|
-
|
|
94
|
-
### `env` fixture — `src/config/variables.ts` (new)
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
export function createEnvFixture(environment: string, variables: Record<string, string | number | boolean>): TestFixtures["env"];
|
|
98
|
-
export class UnknownVariableError extends Error {}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
- A frozen Proxy over `variables`. Reading a missing name throws `UnknownVariableError`: `Variable "customerEmail" is not defined in environment "staging"`.
|
|
102
|
-
- `Symbol` keys, `then` and `toJSON` are passed through, so `await`, `JSON.stringify` and `console.log` behave normally.
|
|
103
|
-
- Writing throws (`TypeError` from the frozen target).
|
|
104
|
-
|
|
105
|
-
### Run settings — `src/config/run-settings.ts` (new)
|
|
106
|
-
|
|
107
|
-
```ts
|
|
108
|
-
export interface RunOverrides {
|
|
109
|
-
environment?: string; // --env / SAMURAI_ENV
|
|
110
|
-
timeout?: number; // --timeout
|
|
111
|
-
expectTimeout?: number; // --expect-timeout
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface RunSettings {
|
|
115
|
-
environment: string;
|
|
116
|
-
baseURL?: string;
|
|
117
|
-
timeout: number;
|
|
118
|
-
expectTimeout: number;
|
|
119
|
-
variables: Record<string, string | number | boolean>;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function resolveRunSettings(config: SamuraiTestConfig, overrides: RunOverrides): RunSettings;
|
|
123
|
-
export function setRunSettings(settings: RunSettings): void;
|
|
124
|
-
export function getRunSettings(): RunSettings; // throws when no run is active
|
|
125
|
-
export class UnknownEnvironmentError extends Error {}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
- Pure; unit-tested with plain objects.
|
|
129
|
-
- Environment choice, first match wins:
|
|
130
|
-
1. `overrides.environment` (the CLI parses `--env <name>`; `src/index.ts` falls back to `process.env.SAMURAI_ENV`)
|
|
131
|
-
2. `config.defaultEnvironment`
|
|
132
|
-
3. the only key of `config.environments`, when there is exactly one
|
|
133
|
-
4. no `environments` at all → implicit environment `"default"` with the top-level values and empty `variables`
|
|
134
|
-
5. otherwise throw `UnknownEnvironmentError`: `No environment chosen. Available: dev, staging (use --env or set defaultEnvironment)`
|
|
135
|
-
- A chosen name not in `environments` throws `Unknown environment "stg". Available: dev, staging`.
|
|
136
|
-
- Precedence per field: run override > environment > project > built-in default (`timeout` 30000, `expectTimeout` 5000). `baseURL`: environment > project > undefined.
|
|
137
|
-
- `src/index.ts` loads the config once, resolves the settings, calls `setRunSettings`, then starts the runner.
|
|
138
|
-
|
|
139
|
-
### Callers that switch to run settings
|
|
140
|
-
|
|
141
|
-
| Location | Before | After |
|
|
142
|
-
|---|---|---|
|
|
143
|
-
| `src/runner/test-runner.ts` (test timeout) | `readConfig("timeout")` | `getRunSettings().timeout` |
|
|
144
|
-
| `src/wait/wait-until.ts` `resolveTimeout` | `readConfig("expect")?.timeout` | `getRunSettings().expectTimeout` when a run is active, else the config value (unit and browser tests run without a runner) |
|
|
145
|
-
| `Page.goto` | — | `getRunSettings().baseURL` when a run is active |
|
|
146
|
-
|
|
147
|
-
Every other `readConfig` key (`logs`, `bidi`, `navigation`, `browser`, `srcDir`, …) is unchanged; none of them vary per environment.
|
|
148
|
-
|
|
149
|
-
### Config types — `src/types/config.d.ts`
|
|
150
|
-
|
|
151
|
-
- `baseUrl` → `baseURL`.
|
|
152
|
-
- `SamuraiEnvironment` gains `baseURL?: string`.
|
|
153
|
-
- `environments` and `defaultEnvironment` become optional.
|
|
154
|
-
- `timeout` becomes optional, matching its documented `@default 30000` (applied by `resolveRunSettings`).
|
|
155
|
-
- `samurai.config.ts` in this repo must typecheck after the change.
|
|
156
|
-
|
|
157
|
-
### `Page.goto` — `src/browser/page.ts`
|
|
158
|
-
|
|
159
|
-
```ts
|
|
160
|
-
public async goto(url: string, options?: NavigateOptions): Promise<{ navigation: string | null; url: string }>;
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
- `goto` is the primary name. `navigateTo` stays as an alias with the same behaviour (existing browser tests keep using it).
|
|
164
|
-
- A URL starting with `/`, `./` or `../` is relative: resolved with `new URL(url, baseURL)`. No base URL → throws `page.goto("/products") needs a baseURL; set one in samurai.config.ts or the environment`.
|
|
165
|
-
- Anything else keeps today's `navigateTo` behaviour (scheme detection, `https://` default).
|
|
166
|
-
- The base URL comes from the active run settings; a `Page` used outside a run (browser tests) has none unless set with `page.setBaseURL(url)`, which browser tests use.
|
|
167
|
-
|
|
168
|
-
### Secrets — `src/config/secrets.ts` (new)
|
|
169
|
-
|
|
170
|
-
```ts
|
|
171
|
-
export function loadSecrets(environment: string, options?: { cwd?: string; env?: NodeJS.ProcessEnv }): Map<string, string>;
|
|
172
|
-
export function createSecretsFixture(environment: string, values: Map<string, string>): TestFixtures["secrets"];
|
|
173
|
-
export class MissingSecretError extends Error {}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
- **Sources**, later wins:
|
|
177
|
-
1. `.env.<environment>` in the project root, if present, parsed with `util.parseEnv` (no dependency, `process.env` untouched)
|
|
178
|
-
2. real environment variables
|
|
179
|
-
- Only keys `SAMURAI_SECRET_<NAME>` count; the name is the part after the prefix.
|
|
180
|
-
- **Names** must match `[A-Z][A-Z0-9_]*`. Reading `secrets.testCard` throws `Invalid secret name "testCard" (use UPPER_SNAKE_CASE)`.
|
|
181
|
-
- **Missing**: reading an unset name throws `MissingSecretError`: `Secret "TEST_CARD" is not set (expected env var SAMURAI_SECRET_TEST_CARD or .env.staging)`. Lookup happens on read, so tests that don't use a missing secret still run.
|
|
182
|
-
- **The fixture** is a Proxy: reading a name returns its value; `toJSON` and `util.inspect.custom` return `{ NAME: "••••", … }`, so `JSON.stringify(secrets)` and `console.log(secrets)` never print values.
|
|
183
|
-
- `loadSecrets` runs once per run in `src/index.ts`; every loaded value is registered with the masker straight away (not on first read), so values the page logs on its own are masked too.
|
|
184
|
-
|
|
185
|
-
### Masking — `src/config/mask.ts` (new)
|
|
186
|
-
|
|
187
|
-
```ts
|
|
188
|
-
export const MASK = "••••";
|
|
189
|
-
export function registerSecret(name: string, value: string): void;
|
|
190
|
-
export function maskText(text: string): string;
|
|
191
|
-
export function maskDeep<T>(value: T): T; // strings inside objects and arrays
|
|
192
|
-
export function maskFormat(): Logform.Format; // winston format
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
- **Values of 4+ characters**: every occurrence is replaced, longest value first (so a secret containing another secret is masked whole). Regex special characters are escaped.
|
|
196
|
-
- **Values under 4 characters**: matched only as a whole token (`(?<![\p{L}\p{N}])value(?![\p{L}\p{N}])`, unicode flag), so `1` in `step 1` is masked but `1` in `2026` is not. A string that *equals* the value is always masked. Loading a short secret logs a warning: `Secret "PIN" is short; masking it may hide unrelated text in logs`.
|
|
197
|
-
- **Where it applies**:
|
|
198
|
-
- Logger: `maskFormat()` is added to the winston pipeline in `src/logger/index.ts` before `format.json`.
|
|
199
|
-
- Report: the reporter runs `maskDeep` on each `TestResult` before storing it (error `message`, `stack`, `expected`, `actual`, and log entry `text`), so `report.json` and console output are masked.
|
|
200
|
-
- Empty values are ignored (nothing to mask).
|
|
201
|
-
|
|
202
|
-
### Repo hygiene
|
|
203
|
-
|
|
204
|
-
- `.gitignore` gains `.env.*` (keeping `!.env.example`).
|
|
205
|
-
- `.env.example` at the root shows the format:
|
|
206
|
-
```
|
|
207
|
-
# Copy to .env.<environment>, e.g. .env.staging. Never commit real values.
|
|
208
|
-
SAMURAI_SECRET_TEST_CARD=
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Report
|
|
212
|
-
|
|
213
|
-
- `TestSummary` gains `environment: string`; `report.json` records which environment the run used.
|
|
214
|
-
|
|
215
|
-
## Error handling
|
|
216
|
-
|
|
217
|
-
| Situation | Error | When |
|
|
218
|
-
|---|---|---|
|
|
219
|
-
| Unknown or ambiguous environment | `UnknownEnvironmentError` | startup, before any browser launches |
|
|
220
|
-
| Unknown variable | `UnknownVariableError` | on read, fails that test |
|
|
221
|
-
| Invalid or missing secret | `MissingSecretError` / `Error` | on read, fails that test |
|
|
222
|
-
| Relative `goto` without base URL | `Error` | on call, fails that test |
|
|
223
|
-
| Async `describe` callback | `Error` | registration |
|
|
224
|
-
|
|
225
|
-
Test-level errors go through `toTestError` and are masked like any other result.
|
|
226
|
-
|
|
227
|
-
## Testing
|
|
228
|
-
|
|
229
|
-
Unit tests (`*.test.ts`, stub connector where a page is involved):
|
|
230
|
-
|
|
231
|
-
- `describe`/`test`: nested titles, title stack restored after a throwing callback, async callback rejected, duplicate titles in different blocks reported separately.
|
|
232
|
-
- `resolveRunSettings`: each environment-choice rule, each precedence level, the implicit `"default"` environment, both error messages.
|
|
233
|
-
- `env` fixture: read, unknown name, write rejected, `JSON.stringify`.
|
|
234
|
-
- `loadSecrets`: file only, env var only, env var beats file, non-prefixed keys ignored, missing file is fine.
|
|
235
|
-
- `secrets` fixture: read, missing, invalid name, `JSON.stringify` and `util.inspect` print masks.
|
|
236
|
-
- `mask`: long value substring, short value token-only, exact-equals short value, overlapping secrets (longest first), regex characters in values, `maskDeep` on nested results, winston format.
|
|
237
|
-
- `Page.goto`: relative resolution, absolute passthrough, missing base URL error (stub connector checks the URL sent to `browsingContext.navigate`).
|
|
238
|
-
|
|
239
|
-
Browser test (`*.browser-test.ts`): `page.goto("/path")` against a local server with `page.setBaseURL`.
|
|
240
|
-
|
|
241
|
-
Migration: `src/tests/index.spec.ts` uses the new signature and imports from `src/api.ts`; `samurai.config.ts` typechecks.
|