@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.
Files changed (328) hide show
  1. package/README.md +129 -0
  2. package/bin/samurai.mjs +31 -0
  3. package/dist/api.js +3 -0
  4. package/dist/assert/assertion-error.d.ts +23 -0
  5. package/dist/assert/assertion-error.js +45 -0
  6. package/dist/assert/expect.d.ts +55 -0
  7. package/dist/assert/expect.js +188 -0
  8. package/dist/browser/browser-context.d.ts +91 -0
  9. package/dist/browser/browser-context.js +146 -0
  10. package/dist/browser/browser-finder.d.ts +2 -0
  11. package/dist/browser/browser-finder.js +95 -0
  12. package/dist/browser/browser.d.ts +95 -0
  13. package/dist/browser/browser.js +372 -0
  14. package/dist/browser/context-tree.d.ts +23 -0
  15. package/dist/browser/context-tree.js +60 -0
  16. package/dist/browser/dialog.d.ts +37 -0
  17. package/dist/browser/dialog.js +61 -0
  18. package/dist/browser/download.d.ts +71 -0
  19. package/dist/browser/download.js +137 -0
  20. package/dist/browser/downloads-dir.d.ts +8 -0
  21. package/dist/browser/downloads-dir.js +38 -0
  22. package/dist/browser/emulation.d.ts +61 -0
  23. package/dist/browser/emulation.js +144 -0
  24. package/dist/browser/file-chooser.d.ts +39 -0
  25. package/dist/browser/file-chooser.js +86 -0
  26. package/dist/browser/file-paths.d.ts +4 -0
  27. package/dist/browser/file-paths.js +18 -0
  28. package/dist/browser/index.d.ts +4 -0
  29. package/dist/browser/index.js +4 -0
  30. package/dist/browser/navigation-error.d.ts +13 -0
  31. package/dist/browser/navigation-error.js +19 -0
  32. package/dist/browser/page-logs.d.ts +86 -0
  33. package/dist/browser/page-logs.js +149 -0
  34. package/dist/browser/page-wait.d.ts +9 -0
  35. package/dist/browser/page-wait.js +17 -0
  36. package/dist/browser/page.d.ts +370 -0
  37. package/dist/browser/page.js +1038 -0
  38. package/dist/browser/pdf.d.ts +72 -0
  39. package/dist/browser/pdf.js +37 -0
  40. package/dist/browser/screenshot.d.ts +29 -0
  41. package/dist/browser/screenshot.js +42 -0
  42. package/dist/cli/args.d.ts +35 -0
  43. package/dist/cli/args.js +69 -0
  44. package/dist/cli/cli.cli-test.d.ts +1 -0
  45. package/dist/cli/cli.cli-test.js +170 -0
  46. package/dist/cli/console-reporter.d.ts +15 -0
  47. package/dist/cli/console-reporter.js +52 -0
  48. package/dist/cli/init.d.ts +7 -0
  49. package/dist/cli/init.js +61 -0
  50. package/dist/cli/main.d.ts +12 -0
  51. package/dist/cli/main.js +156 -0
  52. package/dist/config/config.d.ts +24 -0
  53. package/dist/config/config.js +42 -0
  54. package/dist/config/default.d.ts +2 -0
  55. package/dist/config/default.js +3 -0
  56. package/dist/config/mask.d.ts +11 -0
  57. package/dist/config/mask.js +85 -0
  58. package/dist/config/run-settings.d.ts +28 -0
  59. package/dist/config/run-settings.js +61 -0
  60. package/dist/config/secrets.d.ts +10 -0
  61. package/dist/config/secrets.js +67 -0
  62. package/dist/config/variables.d.ts +11 -0
  63. package/dist/config/variables.js +26 -0
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.js +15 -0
  66. package/dist/locator/action-timeout-error.d.ts +25 -0
  67. package/dist/locator/action-timeout-error.js +35 -0
  68. package/dist/locator/element-state.d.ts +44 -0
  69. package/dist/locator/element-state.js +109 -0
  70. package/dist/locator/label-locator.d.ts +13 -0
  71. package/dist/locator/label-locator.js +49 -0
  72. package/dist/locator/locator.d.ts +168 -0
  73. package/dist/locator/locator.js +591 -0
  74. package/dist/locator/selector-errors.d.ts +9 -0
  75. package/dist/locator/selector-errors.js +15 -0
  76. package/dist/locator/selector.d.ts +60 -0
  77. package/dist/locator/selector.js +113 -0
  78. package/dist/locator/text-locator.d.ts +13 -0
  79. package/dist/logger/index.d.ts +2 -0
  80. package/dist/logger/index.js +30 -0
  81. package/dist/network/data-collector.d.ts +26 -0
  82. package/dist/network/data-collector.js +77 -0
  83. package/dist/network/network-tracker.d.ts +94 -0
  84. package/dist/network/network-tracker.js +198 -0
  85. package/dist/network/network-wait.d.ts +16 -0
  86. package/dist/network/network-wait.js +24 -0
  87. package/dist/network/response.d.ts +22 -0
  88. package/dist/network/response.js +42 -0
  89. package/dist/network/router.d.ts +120 -0
  90. package/dist/network/router.js +343 -0
  91. package/dist/network/url-match.d.ts +8 -0
  92. package/dist/network/url-match.js +38 -0
  93. package/dist/recorder/apply.d.ts +3 -0
  94. package/dist/recorder/apply.js +10 -0
  95. package/dist/recorder/capture.d.ts +18 -0
  96. package/dist/recorder/capture.js +198 -0
  97. package/dist/recorder/index.d.ts +3 -0
  98. package/dist/recorder/index.js +3 -0
  99. package/dist/recorder/normalise.d.ts +68 -0
  100. package/dist/recorder/normalise.js +104 -0
  101. package/dist/recorder/ranking.d.ts +9 -0
  102. package/dist/recorder/ranking.js +21 -0
  103. package/dist/recorder/recorder.d.ts +52 -0
  104. package/dist/recorder/recorder.js +143 -0
  105. package/dist/recorder/session.d.ts +49 -0
  106. package/dist/recorder/session.js +113 -0
  107. package/dist/runner/module-type.d.ts +6 -0
  108. package/dist/runner/module-type.js +29 -0
  109. package/dist/runner/page-logs-report.d.ts +40 -0
  110. package/dist/runner/page-logs-report.js +44 -0
  111. package/dist/runner/prepare-run.d.ts +14 -0
  112. package/dist/runner/prepare-run.js +20 -0
  113. package/dist/runner/project.d.ts +17 -0
  114. package/dist/runner/project.js +33 -0
  115. package/dist/runner/registry.d.ts +7 -0
  116. package/dist/runner/registry.js +40 -0
  117. package/dist/runner/reporter.d.ts +43 -0
  118. package/dist/runner/reporter.js +83 -0
  119. package/dist/runner/run.d.ts +27 -0
  120. package/dist/runner/run.js +34 -0
  121. package/dist/runner/test-error.d.ts +3 -0
  122. package/dist/runner/test-error.js +18 -0
  123. package/dist/runner/test-runner.d.ts +44 -0
  124. package/dist/runner/test-runner.js +205 -0
  125. package/dist/script/call-function.d.ts +34 -0
  126. package/dist/script/call-function.js +71 -0
  127. package/dist/script/element-handle.d.ts +10 -0
  128. package/{src/script/element-handle.ts → dist/script/element-handle.js} +4 -4
  129. package/dist/script/helpers.d.ts +28 -0
  130. package/dist/script/helpers.js +50 -0
  131. package/dist/script/preload.d.ts +31 -0
  132. package/dist/script/preload.js +71 -0
  133. package/dist/script/serialize.d.ts +29 -0
  134. package/dist/script/serialize.js +207 -0
  135. package/dist/steps/build.d.ts +5 -0
  136. package/dist/steps/build.js +38 -0
  137. package/dist/steps/edit.d.ts +30 -0
  138. package/dist/steps/edit.js +80 -0
  139. package/dist/steps/emit.d.ts +6 -0
  140. package/dist/steps/emit.js +73 -0
  141. package/dist/steps/index.d.ts +5 -0
  142. package/dist/steps/index.js +4 -0
  143. package/dist/steps/model.d.ts +103 -0
  144. package/dist/steps/model.js +1 -0
  145. package/dist/steps/parse.d.ts +3 -0
  146. package/dist/steps/parse.js +272 -0
  147. package/dist/transport/bidi-connection.d.ts +46 -0
  148. package/dist/transport/bidi-connection.js +176 -0
  149. package/dist/transport/bidi-error.d.ts +10 -0
  150. package/dist/transport/bidi-error.js +10 -0
  151. package/dist/transport/subscriptions.d.ts +36 -0
  152. package/dist/transport/subscriptions.js +81 -0
  153. package/{src → dist}/types/test.d.ts +10 -4
  154. package/dist/wait/wait-until.d.ts +20 -0
  155. package/dist/wait/wait-until.js +63 -0
  156. package/package.json +44 -7
  157. package/.claude/settings.local.json +0 -7
  158. package/.env.example +0 -3
  159. package/.github/actions/setup/action.yml +0 -16
  160. package/.github/workflows/ci.yml +0 -71
  161. package/.trunk/configs/.markdownlint.yaml +0 -2
  162. package/.trunk/configs/.yamllint.yaml +0 -7
  163. package/.trunk/configs/svgo.config.mjs +0 -14
  164. package/.trunk/trunk.yaml +0 -34
  165. package/docs/superpowers/bidi-roadmap.md +0 -60
  166. package/docs/superpowers/plans/2026-09-29-auto-wait.md +0 -1827
  167. package/docs/superpowers/plans/2026-09-29-expect.md +0 -1350
  168. package/docs/superpowers/plans/2026-10-01-ci.md +0 -350
  169. package/docs/superpowers/plans/2026-10-01-test-api-alignment.md +0 -1821
  170. package/docs/superpowers/recorder-roadmap.md +0 -52
  171. package/docs/superpowers/specs/2026-09-29-auto-wait-design.md +0 -185
  172. package/docs/superpowers/specs/2026-09-29-bidi-foundation-design.md +0 -130
  173. package/docs/superpowers/specs/2026-09-29-contexts-and-storage-design.md +0 -88
  174. package/docs/superpowers/specs/2026-09-29-downloads-and-file-dialogs-design.md +0 -94
  175. package/docs/superpowers/specs/2026-09-29-emulation-design.md +0 -88
  176. package/docs/superpowers/specs/2026-09-29-expect-design.md +0 -130
  177. package/docs/superpowers/specs/2026-09-29-locate-nodes-design.md +0 -90
  178. package/docs/superpowers/specs/2026-09-29-logs-and-errors-design.md +0 -87
  179. package/docs/superpowers/specs/2026-09-29-navigation-waits-design.md +0 -83
  180. package/docs/superpowers/specs/2026-09-29-network-data-design.md +0 -78
  181. package/docs/superpowers/specs/2026-09-29-network-interception-design.md +0 -91
  182. package/docs/superpowers/specs/2026-09-29-network-tracking-design.md +0 -86
  183. package/docs/superpowers/specs/2026-09-29-page-api-design.md +0 -90
  184. package/docs/superpowers/specs/2026-09-29-preload-scripts-design.md +0 -82
  185. package/docs/superpowers/specs/2026-09-29-script-call-function-design.md +0 -107
  186. package/docs/superpowers/specs/2026-09-29-set-files-design.md +0 -62
  187. package/docs/superpowers/specs/2026-09-29-user-prompts-design.md +0 -75
  188. package/docs/superpowers/specs/2026-10-01-ci-design.md +0 -106
  189. package/docs/superpowers/specs/2026-10-01-test-api-alignment-design.md +0 -241
  190. package/public/logo-dark.svg +0 -236
  191. package/public/logo-light.svg +0 -262
  192. package/samurai.config.ts +0 -7
  193. package/src/assert/assertion-error.test.ts +0 -55
  194. package/src/assert/assertion-error.ts +0 -62
  195. package/src/assert/expect-locator.test.ts +0 -217
  196. package/src/assert/expect.test.ts +0 -73
  197. package/src/assert/expect.ts +0 -299
  198. package/src/browser/browser-context.browser-test.ts +0 -201
  199. package/src/browser/browser-context.test.ts +0 -407
  200. package/src/browser/browser-context.ts +0 -208
  201. package/src/browser/browser-finder.ts +0 -110
  202. package/src/browser/browser.test.ts +0 -88
  203. package/src/browser/browser.ts +0 -515
  204. package/src/browser/context-tree.test.ts +0 -86
  205. package/src/browser/context-tree.ts +0 -70
  206. package/src/browser/dialog.browser-test.ts +0 -144
  207. package/src/browser/dialog.test.ts +0 -322
  208. package/src/browser/dialog.ts +0 -83
  209. package/src/browser/download.test.ts +0 -161
  210. package/src/browser/download.ts +0 -172
  211. package/src/browser/downloads-dir.test.ts +0 -75
  212. package/src/browser/downloads-dir.ts +0 -39
  213. package/src/browser/emulation.browser-test.ts +0 -186
  214. package/src/browser/emulation.test.ts +0 -200
  215. package/src/browser/emulation.ts +0 -204
  216. package/src/browser/file-chooser.test.ts +0 -142
  217. package/src/browser/file-chooser.ts +0 -99
  218. package/src/browser/file-paths.ts +0 -18
  219. package/src/browser/goto.browser-test.ts +0 -44
  220. package/src/browser/navigation-error.ts +0 -26
  221. package/src/browser/navigation.browser-test.ts +0 -172
  222. package/src/browser/page-api.browser-test.ts +0 -228
  223. package/src/browser/page-logs.browser-test.ts +0 -119
  224. package/src/browser/page-logs.test.ts +0 -212
  225. package/src/browser/page-logs.ts +0 -208
  226. package/src/browser/page-wait.ts +0 -19
  227. package/src/browser/page.test.ts +0 -1567
  228. package/src/browser/page.ts +0 -1176
  229. package/src/browser/pdf.ts +0 -62
  230. package/src/browser/screenshot.ts +0 -65
  231. package/src/browser/transfers.browser-test.ts +0 -180
  232. package/src/config/config.ts +0 -33
  233. package/src/config/default.ts +0 -5
  234. package/src/config/fixtures.test.ts +0 -137
  235. package/src/config/mask.test.ts +0 -143
  236. package/src/config/mask.ts +0 -87
  237. package/src/config/run-settings.test.ts +0 -163
  238. package/src/config/run-settings.ts +0 -114
  239. package/src/config/secrets.ts +0 -70
  240. package/src/config/variables.ts +0 -31
  241. package/src/index.ts +0 -10
  242. package/src/locator/action-timeout-error.test.ts +0 -83
  243. package/src/locator/action-timeout-error.ts +0 -72
  244. package/src/locator/element-state.test.ts +0 -181
  245. package/src/locator/element-state.ts +0 -159
  246. package/src/locator/locate-nodes.browser-test.ts +0 -169
  247. package/src/locator/locator-actions.test.ts +0 -355
  248. package/src/locator/locator-chain.test.ts +0 -292
  249. package/src/locator/locator.browser-test.ts +0 -137
  250. package/src/locator/locator.test.ts +0 -179
  251. package/src/locator/locator.ts +0 -656
  252. package/src/locator/selector-errors.ts +0 -18
  253. package/src/locator/selector.test.ts +0 -124
  254. package/src/locator/selector.ts +0 -107
  255. package/src/locator/set-files.browser-test.ts +0 -141
  256. package/src/locator/set-files.test.ts +0 -191
  257. package/src/locator/text-locator.test.ts +0 -118
  258. package/src/logger/index.test.ts +0 -28
  259. package/src/logger/index.ts +0 -40
  260. package/src/network/data-collector.test.ts +0 -111
  261. package/src/network/data-collector.ts +0 -92
  262. package/src/network/network-data.browser-test.ts +0 -192
  263. package/src/network/network-tracker.browser-test.ts +0 -151
  264. package/src/network/network-tracker.test.ts +0 -307
  265. package/src/network/network-tracker.ts +0 -286
  266. package/src/network/network-wait.ts +0 -28
  267. package/src/network/response.test.ts +0 -93
  268. package/src/network/response.ts +0 -58
  269. package/src/network/router.browser-test.ts +0 -260
  270. package/src/network/router.test.ts +0 -528
  271. package/src/network/router.ts +0 -420
  272. package/src/network/url-match.test.ts +0 -69
  273. package/src/network/url-match.ts +0 -41
  274. package/src/runner/page-logs-report.test.ts +0 -144
  275. package/src/runner/page-logs-report.ts +0 -82
  276. package/src/runner/prepare-run.test.ts +0 -33
  277. package/src/runner/prepare-run.ts +0 -36
  278. package/src/runner/registry.test.ts +0 -59
  279. package/src/runner/registry.ts +0 -46
  280. package/src/runner/reporter.test.ts +0 -73
  281. package/src/runner/reporter.ts +0 -109
  282. package/src/runner/test-error.test.ts +0 -39
  283. package/src/runner/test-error.ts +0 -20
  284. package/src/runner/test-runner.ts +0 -184
  285. package/src/script/call-function.browser-test.ts +0 -112
  286. package/src/script/call-function.test.ts +0 -174
  287. package/src/script/call-function.ts +0 -113
  288. package/src/script/helpers.test.ts +0 -116
  289. package/src/script/helpers.ts +0 -68
  290. package/src/script/preload.browser-test.ts +0 -144
  291. package/src/script/preload.test.ts +0 -180
  292. package/src/script/preload.ts +0 -102
  293. package/src/script/serialize.test.ts +0 -182
  294. package/src/script/serialize.ts +0 -203
  295. package/src/testing/browser-fixture.ts +0 -18
  296. package/src/testing/fake-websocket.ts +0 -72
  297. package/src/testing/stub-connector.ts +0 -154
  298. package/src/tests/index.spec.ts +0 -30
  299. package/src/transport/bidi-connection.test.ts +0 -155
  300. package/src/transport/bidi-connection.ts +0 -256
  301. package/src/transport/bidi-error.ts +0 -23
  302. package/src/transport/foundation.browser-test.ts +0 -73
  303. package/src/transport/subscriptions.test.ts +0 -140
  304. package/src/transport/subscriptions.ts +0 -110
  305. package/src/types/bidi-commands.test-d.ts +0 -55
  306. package/src/wait/wait-until.test.ts +0 -117
  307. package/src/wait/wait-until.ts +0 -86
  308. package/tsconfig.json +0 -44
  309. /package/{src/api.ts → dist/api.d.ts} +0 -0
  310. /package/{src/locator/text-locator.ts → dist/locator/text-locator.js} +0 -0
  311. /package/{src → dist}/types/bidi-modules/browser.d.ts +0 -0
  312. /package/{src → dist}/types/bidi-modules/browsing-context.d.ts +0 -0
  313. /package/{src → dist}/types/bidi-modules/emulation.d.ts +0 -0
  314. /package/{src → dist}/types/bidi-modules/input.d.ts +0 -0
  315. /package/{src → dist}/types/bidi-modules/log.d.ts +0 -0
  316. /package/{src → dist}/types/bidi-modules/network.d.ts +0 -0
  317. /package/{src → dist}/types/bidi-modules/permissions.d.ts +0 -0
  318. /package/{src → dist}/types/bidi-modules/script.d.ts +0 -0
  319. /package/{src → dist}/types/bidi-modules/session.d.ts +0 -0
  320. /package/{src → dist}/types/bidi-modules/storage.d.ts +0 -0
  321. /package/{src → dist}/types/bidi-modules/web-extensions.d.ts +0 -0
  322. /package/{src → dist}/types/bidi-protocols/command.d.ts +0 -0
  323. /package/{src → dist}/types/bidi-protocols/error.d.ts +0 -0
  324. /package/{src → dist}/types/bidi-protocols/event.d.ts +0 -0
  325. /package/{src → dist}/types/bidi.d.ts +0 -0
  326. /package/{src → dist}/types/browser.d.ts +0 -0
  327. /package/{src → dist}/types/config.d.ts +0 -0
  328. /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.