@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
@@ -0,0 +1,44 @@
1
+ export interface ElementBox {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ }
7
+ export interface ElementState {
8
+ attached: boolean;
9
+ visible: boolean;
10
+ enabled: boolean;
11
+ editable: boolean;
12
+ /** Viewport coordinates after any scroll; null when not attached */
13
+ box: ElementBox | null;
14
+ /**
15
+ * "self" when a click at the element's centre reaches it, otherwise a description of
16
+ * the covering element ("nothing" when no element is there); null when not checked
17
+ */
18
+ hitTarget: string | null;
19
+ }
20
+ export interface ProbeOptions {
21
+ /** Scroll the element into view first when it is not fully inside the viewport */
22
+ scroll: boolean;
23
+ /** Check which element receives a click at the element's centre */
24
+ hitTest: boolean;
25
+ }
26
+ export type WaitForState = "attached" | "detached" | "visible" | "hidden";
27
+ export type CheckName = "attached" | "visible" | "stable" | "enabled" | "editable" | "hit target";
28
+ export type CheckResult = "pass" | "fail" | "pending";
29
+ /** Results keyed by check name, in the order the checks were evaluated */
30
+ export type CheckResults = Partial<Record<CheckName, CheckResult>>;
31
+ /** State of an element that is not in the document (never matched, or gone) */
32
+ export declare const DETACHED_STATE: ElementState;
33
+ /**
34
+ * Function declaration run in the page through `script.callFunction` as `(el, options)`, where `el`
35
+ * is the located element. Returns its state as a plain object in one round trip. Kept as a string so
36
+ * the transpiler cannot inject helpers (`__name`) that do not exist in the page.
37
+ * A node that was removed from the document stays resolvable in the browser, hence `isConnected`.
38
+ */
39
+ export declare const PROBE_ELEMENT: string;
40
+ /** Checks the probe result is a complete state; throws on anything else */
41
+ export declare function parseElementState(value: unknown): ElementState;
42
+ /** Evaluates the required checks in order; after the first failure the rest are pending */
43
+ export declare function evaluateChecks(required: readonly CheckName[], current: ElementState, previous: ElementState | undefined): CheckResults;
44
+ export declare function allPass(results: CheckResults): boolean;
@@ -0,0 +1,109 @@
1
+ const TEXT_INPUT_TYPES = ["text", "search", "email", "url", "tel", "password", "number"];
2
+ /** State of an element that is not in the document (never matched, or gone) */
3
+ export const DETACHED_STATE = {
4
+ attached: false,
5
+ visible: false,
6
+ enabled: false,
7
+ editable: false,
8
+ box: null,
9
+ hitTarget: null,
10
+ };
11
+ /**
12
+ * Function declaration run in the page through `script.callFunction` as `(el, options)`, where `el`
13
+ * is the located element. Returns its state as a plain object in one round trip. Kept as a string so
14
+ * the transpiler cannot inject helpers (`__name`) that do not exist in the page.
15
+ * A node that was removed from the document stays resolvable in the browser, hence `isConnected`.
16
+ */
17
+ export const PROBE_ELEMENT = `(el, options) => {
18
+ if (!el || !el.isConnected) {
19
+ return { attached: false, visible: false, enabled: false, editable: false, box: null, hitTarget: null };
20
+ }
21
+ if (options.scroll) {
22
+ const before = el.getBoundingClientRect();
23
+ const inViewport = before.top >= 0 && before.left >= 0 &&
24
+ before.bottom <= window.innerHeight && before.right <= window.innerWidth;
25
+ if (!inViewport) el.scrollIntoView({ block: "center", inline: "center" });
26
+ }
27
+ const rect = el.getBoundingClientRect();
28
+ const style = getComputedStyle(el);
29
+ const visible = rect.width > 0 && rect.height > 0 &&
30
+ style.display !== "none" && style.visibility !== "hidden" && style.opacity !== "0";
31
+ const enabled = !el.matches(":disabled") && el.getAttribute("aria-disabled") !== "true";
32
+ const tag = el.tagName.toLowerCase();
33
+ const typeable = Boolean(
34
+ (tag === "input" && ${JSON.stringify(TEXT_INPUT_TYPES)}.includes((el.getAttribute("type") || "text").toLowerCase())) ||
35
+ tag === "textarea" || el.isContentEditable);
36
+ const editable = Boolean(enabled && !el.readOnly && typeable);
37
+ let hitTarget = null;
38
+ if (options.hitTest) {
39
+ const top = document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
40
+ hitTarget = !top ? "nothing"
41
+ : top === el || el.contains(top) ? "self"
42
+ : top.tagName.toLowerCase() + (top.id ? "#" + top.id : "") +
43
+ Array.from(top.classList, (name) => "." + name).join("");
44
+ }
45
+ return {
46
+ attached: true, visible, enabled, editable,
47
+ box: { x: rect.left, y: rect.top, width: rect.width, height: rect.height },
48
+ hitTarget,
49
+ };
50
+ }`;
51
+ /** Checks the probe result is a complete state; throws on anything else */
52
+ export function parseElementState(value) {
53
+ if (typeof value !== "object" || value === null) {
54
+ throw new Error(`Expected an element state object but received "${describeType(value)}"`);
55
+ }
56
+ const state = value;
57
+ const flags = ["attached", "visible", "enabled", "editable"];
58
+ if (flags.some((flag) => typeof state[flag] !== "boolean") ||
59
+ !("box" in state) ||
60
+ !("hitTarget" in state)) {
61
+ throw new Error(`Malformed element state: ${JSON.stringify(value)}`);
62
+ }
63
+ return state;
64
+ }
65
+ function describeType(value) {
66
+ return value === null ? "null" : typeof value;
67
+ }
68
+ function sameBox(a, b) {
69
+ return (a !== null &&
70
+ b !== null &&
71
+ a.x === b.x &&
72
+ a.y === b.y &&
73
+ a.width === b.width &&
74
+ a.height === b.height);
75
+ }
76
+ function checkPasses(name, current, previous) {
77
+ switch (name) {
78
+ case "attached":
79
+ return current.attached;
80
+ case "visible":
81
+ return current.visible;
82
+ case "stable":
83
+ return sameBox(current.box, previous?.box ?? null);
84
+ case "enabled":
85
+ return current.enabled;
86
+ case "editable":
87
+ return current.editable;
88
+ case "hit target":
89
+ return current.hitTarget === "self";
90
+ }
91
+ }
92
+ /** Evaluates the required checks in order; after the first failure the rest are pending */
93
+ export function evaluateChecks(required, current, previous) {
94
+ const results = {};
95
+ let failed = false;
96
+ for (const name of required) {
97
+ if (failed) {
98
+ results[name] = "pending";
99
+ continue;
100
+ }
101
+ const pass = checkPasses(name, current, previous);
102
+ results[name] = pass ? "pass" : "fail";
103
+ failed = !pass;
104
+ }
105
+ return results;
106
+ }
107
+ export function allPass(results) {
108
+ return Object.values(results).every((result) => result === "pass");
109
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Function declaration run in the page as `(starts, value, match, ignoreCase)`; returns the elements
3
+ * named by a label whose text matches. BiDi has no label locator, so the framework does it.
4
+ *
5
+ * - An element's labels are its `<label>`s (`el.labels`: `for=`, nesting), the elements its
6
+ * `aria-labelledby` points to, and its `aria-label`.
7
+ * - Searches the descendants of every start node (the whole document when there are none).
8
+ * - Text is whitespace collapsed and trimmed; `full` needs equality, `partial` a substring.
9
+ * - Results are unique and in document order.
10
+ *
11
+ * A string, so the transpiler cannot inject helpers that do not exist in the page.
12
+ */
13
+ export declare const LABEL_LOCATE = "(starts, value, match, ignoreCase) => {\n const normalise = (text) => {\n const collapsed = String(text).replace(/\\s+/g, \" \").trim();\n return ignoreCase ? collapsed.toLowerCase() : collapsed;\n };\n const wanted = normalise(value);\n const textOf = (node) => node.innerText ?? node.textContent ?? \"\";\n const labelsOf = (el) => {\n const texts = [];\n if (el.labels) for (const label of el.labels) texts.push(textOf(label));\n const by = el.getAttribute(\"aria-labelledby\");\n if (by) {\n for (const id of by.split(/\\s+/)) {\n const named = id && document.getElementById(id);\n if (named) texts.push(textOf(named));\n }\n }\n const own = el.getAttribute(\"aria-label\");\n if (own) texts.push(own);\n return texts;\n };\n const roots = starts.length > 0 ? starts : [document];\n const seen = new Set();\n const matches = [];\n for (const root of roots) {\n for (const el of root.querySelectorAll(\"*\")) {\n if (seen.has(el)) continue;\n seen.add(el);\n const found = labelsOf(el).some((text) => {\n const normal = normalise(text);\n return match === \"full\" ? normal === wanted : normal.includes(wanted);\n });\n if (found) matches.push(el);\n }\n }\n return matches.sort((a, b) => (a === b ? 0 : a.compareDocumentPosition(b) & 4 ? -1 : 1));\n}";
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Function declaration run in the page as `(starts, value, match, ignoreCase)`; returns the elements
3
+ * named by a label whose text matches. BiDi has no label locator, so the framework does it.
4
+ *
5
+ * - An element's labels are its `<label>`s (`el.labels`: `for=`, nesting), the elements its
6
+ * `aria-labelledby` points to, and its `aria-label`.
7
+ * - Searches the descendants of every start node (the whole document when there are none).
8
+ * - Text is whitespace collapsed and trimmed; `full` needs equality, `partial` a substring.
9
+ * - Results are unique and in document order.
10
+ *
11
+ * A string, so the transpiler cannot inject helpers that do not exist in the page.
12
+ */
13
+ export const LABEL_LOCATE = `(starts, value, match, ignoreCase) => {
14
+ const normalise = (text) => {
15
+ const collapsed = String(text).replace(/\\s+/g, " ").trim();
16
+ return ignoreCase ? collapsed.toLowerCase() : collapsed;
17
+ };
18
+ const wanted = normalise(value);
19
+ const textOf = (node) => node.innerText ?? node.textContent ?? "";
20
+ const labelsOf = (el) => {
21
+ const texts = [];
22
+ if (el.labels) for (const label of el.labels) texts.push(textOf(label));
23
+ const by = el.getAttribute("aria-labelledby");
24
+ if (by) {
25
+ for (const id of by.split(/\\s+/)) {
26
+ const named = id && document.getElementById(id);
27
+ if (named) texts.push(textOf(named));
28
+ }
29
+ }
30
+ const own = el.getAttribute("aria-label");
31
+ if (own) texts.push(own);
32
+ return texts;
33
+ };
34
+ const roots = starts.length > 0 ? starts : [document];
35
+ const seen = new Set();
36
+ const matches = [];
37
+ for (const root of roots) {
38
+ for (const el of root.querySelectorAll("*")) {
39
+ if (seen.has(el)) continue;
40
+ seen.add(el);
41
+ const found = labelsOf(el).some((text) => {
42
+ const normal = normalise(text);
43
+ return match === "full" ? normal === wanted : normal.includes(wanted);
44
+ });
45
+ if (found) matches.push(el);
46
+ }
47
+ }
48
+ return matches.sort((a, b) => (a === b ? 0 : a.compareDocumentPosition(b) & 4 ? -1 : 1));
49
+ }`;
@@ -0,0 +1,168 @@
1
+ import type { BiDiConnector } from "../transport/bidi-connection.js";
2
+ import type { BrowsingContext, ElementRectangle } from "../types/bidi-modules/browsing-context.js";
3
+ import { type HelperInstaller } from "../script/helpers.js";
4
+ import { ElementHandle } from "../script/element-handle.js";
5
+ import { type ElementScreenshotOptions } from "../browser/screenshot.js";
6
+ import { type WaitForState } from "./element-state.js";
7
+ import { type Selector, type TextOptions } from "./selector.js";
8
+ export type { WaitForState } from "./element-state.js";
9
+ export { InvalidSelectorError, UnsupportedOperationError, } from "./selector-errors.js";
10
+ export type { TextOptions } from "./selector.js";
11
+ export interface ActionOptions {
12
+ /**
13
+ * Time (ms) to wait for the element to become actionable. Defaults to config `expect.timeout`, then 5000.
14
+ * 0 checks once without waiting; the stability check is skipped because it needs two probes
15
+ */
16
+ timeout?: number;
17
+ /** Skip every actionability check except "attached" */
18
+ force?: boolean;
19
+ }
20
+ export default class Locator {
21
+ private biDiConnector;
22
+ private contextId;
23
+ private chain;
24
+ private helpers;
25
+ private alternatives;
26
+ private matched;
27
+ /**
28
+ * `selector` is an xpath, or a chain of selector steps that each search inside the previous step's matches.
29
+ * `helpers` is the page's helper realm. With it the actionability probe is installed once per
30
+ * document and called by name; without it (a locator built by hand) the probe source is sent on every poll.
31
+ * `alternatives` are fallback locators tried in order whenever this chain matches nothing (see `withFallbacks`).
32
+ */
33
+ constructor(selector: string | readonly Selector[], biDiConnector: BiDiConnector, contextId: BrowsingContext, helpers?: HelperInstaller, alternatives?: readonly Locator[]);
34
+ /**
35
+ * The description of this locator used in error messages, e.g. `//form >> role=button[name="Save"]`;
36
+ * fallbacks follow as `or …`
37
+ */
38
+ get selector(): string;
39
+ /** Steps that keep the fallbacks: each fallback gets the same step, so "inside A or B" stays meaningful */
40
+ private child;
41
+ /**
42
+ * A locator that tries this one first and, whenever it matches nothing, each of `fallbacks` in order;
43
+ * the first one with a match wins. For steps with several ways to find the same element (a test id,
44
+ * then role and name, then text), most stable first. `matchedBy` says which one matched last, so a
45
+ * self-healing layer can tell the primary locator has gone stale.
46
+ * @example
47
+ * page.getByTestId("save").withFallbacks(page.getByRole("button", { name: "Save" }), page.getByText("Save"));
48
+ */
49
+ withFallbacks(...fallbacks: Locator[]): Locator;
50
+ /**
51
+ * Which locator found elements the last time this one searched: index 0 is the primary, 1 the first
52
+ * fallback, and so on. `undefined` before any search or when the last search found nothing.
53
+ */
54
+ get matchedBy(): {
55
+ index: number;
56
+ selector: string;
57
+ } | undefined;
58
+ /** Elements matching the label text (`<label>`, `aria-labelledby`, `aria-label`), inside this locator's matches */
59
+ getByLabel(text: string, options?: TextOptions): Locator;
60
+ /** Elements whose `data-testid` attribute equals `testId`, inside this locator's matches */
61
+ getByTestId(testId: string): Locator;
62
+ /** Elements matching `xpath` inside this locator's matches (relative and `//` paths search inside them) */
63
+ locator(xpath: string): Locator;
64
+ /** Elements matching the CSS selector inside this locator's matches */
65
+ getByCss(css: string): Locator;
66
+ /** Elements whose text matches, inside this locator's matches. Exact match unless `match: "partial"` */
67
+ getByText(text: string, options?: TextOptions): Locator;
68
+ /** Elements with the ARIA role (and accessible name, when given), inside this locator's matches */
69
+ getByRole(role: string, options?: {
70
+ name?: string;
71
+ }): Locator;
72
+ /** One locator per element currently matching, in document order */
73
+ all(): Promise<Locator[]>;
74
+ /**
75
+ * Finds the elements the chain matches, in document order. Every step searches inside the previous
76
+ * step's matches; `max` limits the final result. Nothing is cached: the element that matches "first"
77
+ * can change between polls, and a removed node stays resolvable in the browser.
78
+ */
79
+ private resolve;
80
+ /** The primary or the first fallback that currently matches, without its own fallbacks */
81
+ private firstMatching;
82
+ private withoutFallbacks;
83
+ private resolveChain;
84
+ private resolveOne;
85
+ private locateStep;
86
+ private locateNatively;
87
+ /** Text search done in the page, for browsers without BiDi's innerText locator */
88
+ private locateByText;
89
+ /** Runs a page-side search `(starts, value, match, ignoreCase) => elements` */
90
+ private locateInPage;
91
+ /** Calls `fn` in the page; a throwing function becomes a "Failed to locate element" error */
92
+ private call;
93
+ /**
94
+ * Calls a function on the first matching element: `body` sees it as `el`, followed by `params`.
95
+ * Answers `whenMissing` when nothing matches, or the element disappeared meanwhile.
96
+ */
97
+ private callOnElement;
98
+ /** Like `callOnElement`, for operations that cannot go on without the element */
99
+ private callOnExistingElement;
100
+ /** Reads the element's actionability state: one search, one probe */
101
+ private probeState;
102
+ private callProbe;
103
+ /**
104
+ * Probes until every required check passes and returns that state.
105
+ * Throws ActionTimeoutError describing the last probe on timeout.
106
+ */
107
+ private waitForActionable;
108
+ /** Waits until the element is attached, visible, stable, enabled and not covered, then clicks its centre with real pointer input */
109
+ click(options?: ActionOptions): Promise<void>;
110
+ /** Simulate a click event on the center off a page element.
111
+ * This is mostly used internally to focus input elements when using the `fill()` method
112
+ * @example
113
+ * const container = page.locator("div[@id=['clickable-element']");
114
+ * const rect = await container.getBoundingClientRect();
115
+ * await container.clickRect(rect);
116
+ */
117
+ clickRect(rect: Pick<ElementRectangle, "x" | "y" | "width" | "height">): Promise<void>;
118
+ /**
119
+ * Sets the files of an `<input type=file>` (an empty list clears them); the browser fires `input` and `change`.
120
+ * Paths are resolved against the working directory and must exist on the machine the browser runs on.
121
+ * Waits for the input to be attached and enabled; it need not be visible, file inputs are often hidden behind a label.
122
+ * @example
123
+ * await page.locator("input[@type='file']").setInputFiles("./fixtures/avatar.png");
124
+ * await page.locator("input[@type='file']").setInputFiles([]);
125
+ */
126
+ setInputFiles(files: string | string[], options?: {
127
+ timeout?: number;
128
+ }): Promise<void>;
129
+ /**
130
+ * Takes a screenshot of just this element, after scrolling it into view and waiting until it is attached and
131
+ * visible. Returns the image (also written to `options.path`).
132
+ * @example
133
+ * await page.locator("div[@id='chart']").screenshot({ path: "out/chart.png" });
134
+ */
135
+ screenshot(options?: ElementScreenshotOptions & {
136
+ timeout?: number;
137
+ }): Promise<Buffer>;
138
+ /** Waits until the element is attached, then focuses it */
139
+ focus(options?: ActionOptions): Promise<void>;
140
+ /** Waits until the element reaches `state` (default "visible"); does not scroll */
141
+ waitFor({ state, timeout, }?: {
142
+ state?: WaitForState;
143
+ timeout?: number;
144
+ }): Promise<void>;
145
+ /** Returns the browser's getBoundingClientRect result for the given element */
146
+ getBoundingClientRect(): Promise<ElementRectangle>;
147
+ private stringOrNull;
148
+ /** Whether the element exists, has a non-empty box and is not hidden by CSS */
149
+ isVisible(): Promise<boolean>;
150
+ /** Trimmed text content, or null when the element is missing */
151
+ textContent(): Promise<string | null>;
152
+ /** Value of an input/textarea/select, or null when missing */
153
+ inputValue(): Promise<string | null>;
154
+ /** Attribute value, or null when the element or attribute is missing */
155
+ getAttribute(name: string): Promise<string | null>;
156
+ /** References to the elements currently matching, in document order. Internal: for tooling that identifies elements */
157
+ elements(): Promise<ElementHandle[]>;
158
+ /** Number of elements currently matching */
159
+ count(): Promise<number>;
160
+ /** Whether the element exists and is not disabled; does not wait */
161
+ isEnabled(): Promise<boolean>;
162
+ /** Whether the element exists and accepts typing (enabled, not readonly, a text field); does not wait */
163
+ isEditable(): Promise<boolean>;
164
+ /** Wait for certain time (ms). Default: 300ms */
165
+ wait(duration?: number): Promise<void>;
166
+ /** Waits until the field is editable and not covered, then replaces its value by typing `value`; an empty string clears it */
167
+ fill(value: string, options?: ActionOptions): Promise<void>;
168
+ }