@itmetsam/samurai-framework 0.9.0 → 0.12.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 (331) hide show
  1. package/README.md +4 -5
  2. package/dist/assert/assertion-error.d.ts +1 -1
  3. package/dist/assert/expect.d.ts +5 -50
  4. package/dist/assert/expect.js +5 -182
  5. package/dist/assert/locator-assertions.d.ts +22 -0
  6. package/dist/assert/locator-assertions.js +61 -0
  7. package/dist/assert/pending-assertions.d.ts +7 -0
  8. package/dist/assert/pending-assertions.js +40 -0
  9. package/dist/assert/text-match.d.ts +4 -0
  10. package/dist/assert/text-match.js +11 -0
  11. package/dist/assert/value-assertions.d.ts +23 -0
  12. package/dist/assert/value-assertions.js +82 -0
  13. package/dist/browser/browser-context.d.ts +0 -1
  14. package/dist/browser/browser-context.js +13 -5
  15. package/dist/browser/browser-finder.js +5 -9
  16. package/dist/browser/browser.d.ts +3 -31
  17. package/dist/browser/browser.js +36 -219
  18. package/dist/browser/chrome-launch.d.ts +14 -1
  19. package/dist/browser/chrome-launch.js +44 -1
  20. package/dist/browser/dialog-handling.d.ts +32 -0
  21. package/dist/browser/dialog-handling.js +85 -0
  22. package/dist/browser/document-watcher.d.ts +44 -0
  23. package/dist/browser/document-watcher.js +160 -0
  24. package/dist/browser/download-folders.d.ts +0 -1
  25. package/dist/browser/download-folders.js +7 -15
  26. package/dist/browser/download.js +5 -10
  27. package/dist/browser/downloads-dir.js +2 -2
  28. package/dist/browser/emit.d.ts +7 -0
  29. package/dist/browser/emit.js +16 -0
  30. package/dist/browser/emulation.d.ts +1 -1
  31. package/dist/browser/emulation.js +54 -18
  32. package/dist/browser/file-chooser.js +4 -11
  33. package/dist/browser/history.d.ts +6 -0
  34. package/dist/browser/history.js +89 -0
  35. package/dist/browser/index.d.ts +2 -0
  36. package/dist/browser/index.js +2 -0
  37. package/dist/browser/init-script.d.ts +2 -0
  38. package/dist/browser/init-script.js +16 -0
  39. package/dist/browser/launch-firefox.d.ts +20 -0
  40. package/dist/browser/launch-firefox.js +66 -0
  41. package/dist/browser/launch-flags.d.ts +18 -0
  42. package/dist/browser/launch-flags.js +100 -0
  43. package/dist/browser/lazy.d.ts +21 -0
  44. package/dist/browser/lazy.js +44 -0
  45. package/dist/browser/page-events.d.ts +44 -0
  46. package/dist/browser/page-events.js +58 -0
  47. package/dist/browser/page-logs.js +2 -9
  48. package/dist/browser/page-navigation.d.ts +58 -0
  49. package/dist/browser/page-navigation.js +108 -0
  50. package/dist/browser/page-network.d.ts +69 -0
  51. package/dist/browser/page-network.js +163 -0
  52. package/dist/browser/page-settings.d.ts +35 -0
  53. package/dist/browser/page-settings.js +51 -0
  54. package/dist/browser/page-transfers.d.ts +36 -0
  55. package/dist/browser/page-transfers.js +50 -0
  56. package/dist/browser/page.d.ts +39 -154
  57. package/dist/browser/page.js +134 -758
  58. package/dist/browser/pdf.d.ts +1 -1
  59. package/dist/browser/session.d.ts +16 -0
  60. package/dist/browser/session.js +32 -0
  61. package/dist/browser/viewport.d.ts +19 -0
  62. package/dist/browser/viewport.js +72 -0
  63. package/dist/browser/warm-up.d.ts +1 -1
  64. package/dist/browser/warm-up.js +2 -1
  65. package/dist/browsers/index.d.ts +1 -1
  66. package/dist/browsers/index.js +1 -1
  67. package/dist/browsers/pins.d.ts +0 -2
  68. package/dist/browsers/pins.js +0 -2
  69. package/dist/browsers/store.d.ts +0 -2
  70. package/dist/browsers/store.js +1 -2
  71. package/dist/cli/args.d.ts +19 -7
  72. package/dist/cli/args.js +18 -9
  73. package/dist/cli/browsers.d.ts +1 -2
  74. package/dist/cli/browsers.js +2 -2
  75. package/dist/cli/console-reporter.d.ts +4 -0
  76. package/dist/cli/console-reporter.js +15 -7
  77. package/dist/cli/flow-reporter.js +2 -1
  78. package/dist/cli/flow.d.ts +1 -2
  79. package/dist/cli/flow.js +25 -22
  80. package/dist/cli/init.js +3 -8
  81. package/dist/cli/io.d.ts +10 -0
  82. package/dist/cli/io.js +1 -0
  83. package/dist/cli/main.d.ts +3 -10
  84. package/dist/cli/main.js +11 -137
  85. package/dist/cli/paint.d.ts +2 -0
  86. package/dist/cli/paint.js +4 -0
  87. package/dist/cli/record.d.ts +4 -0
  88. package/dist/cli/record.js +93 -0
  89. package/dist/cli/usage.d.ts +1 -0
  90. package/dist/cli/usage.js +58 -0
  91. package/dist/cli/version.d.ts +2 -0
  92. package/dist/cli/version.js +7 -0
  93. package/dist/config/browsers.d.ts +11 -0
  94. package/dist/config/browsers.js +32 -0
  95. package/dist/config/config.js +8 -1
  96. package/dist/config/mask.d.ts +1 -1
  97. package/dist/config/mask.js +7 -2
  98. package/dist/config/run-settings.d.ts +1 -2
  99. package/dist/config/run-settings.js +1 -6
  100. package/dist/config/timeouts.d.ts +2 -0
  101. package/dist/config/timeouts.js +13 -0
  102. package/dist/config/workers.d.ts +14 -0
  103. package/dist/config/workers.js +29 -0
  104. package/dist/flows/run/branch-context.d.ts +14 -0
  105. package/dist/flows/run/branch-context.js +35 -0
  106. package/dist/flows/run/handlers/condition.d.ts +1 -1
  107. package/dist/flows/run/handlers/end.d.ts +1 -1
  108. package/dist/flows/run/handlers/group.d.ts +1 -1
  109. package/dist/flows/run/handlers/index.d.ts +2 -2
  110. package/dist/flows/run/handlers/parallel.d.ts +1 -1
  111. package/dist/flows/run/handlers/start.d.ts +1 -1
  112. package/dist/flows/run/handlers/support.d.ts +3 -24
  113. package/dist/flows/run/handlers/support.js +1 -1
  114. package/dist/flows/run/handlers/test.d.ts +1 -1
  115. package/dist/flows/run/handlers/types.d.ts +27 -0
  116. package/dist/flows/run/handlers/types.js +1 -0
  117. package/dist/flows/run/index.d.ts +1 -1
  118. package/dist/flows/run/prepare-flow.d.ts +14 -0
  119. package/dist/flows/run/prepare-flow.js +44 -0
  120. package/dist/flows/run/project.d.ts +33 -2
  121. package/dist/flows/run/project.js +139 -49
  122. package/dist/flows/run/run.d.ts +8 -4
  123. package/dist/flows/run/run.js +30 -194
  124. package/dist/flows/run/walker.d.ts +56 -0
  125. package/dist/flows/run/walker.js +225 -0
  126. package/dist/flows/steps/catalog.d.ts +1 -1
  127. package/dist/flows/steps/handler.d.ts +1 -1
  128. package/dist/flows/steps/set.d.ts +1 -1
  129. package/dist/index.js +1 -1
  130. package/dist/locator/actionability.d.ts +33 -0
  131. package/dist/locator/actionability.js +104 -0
  132. package/dist/locator/chain-resolver.d.ts +20 -0
  133. package/dist/locator/chain-resolver.js +102 -0
  134. package/dist/locator/document-swap.d.ts +1 -0
  135. package/dist/locator/document-swap.js +11 -0
  136. package/dist/locator/element-state.d.ts +3 -1
  137. package/dist/locator/element-state.js +11 -3
  138. package/dist/locator/file-actions.d.ts +35 -0
  139. package/dist/locator/file-actions.js +55 -0
  140. package/dist/{script/helpers.js → locator/helper-realm.js} +2 -2
  141. package/dist/locator/input-actions.d.ts +18 -0
  142. package/dist/locator/input-actions.js +80 -0
  143. package/dist/locator/label-locator.d.ts +1 -1
  144. package/dist/locator/label-locator.js +5 -12
  145. package/dist/locator/locator.d.ts +14 -37
  146. package/dist/locator/locator.js +68 -424
  147. package/dist/locator/page-scripts.d.ts +16 -0
  148. package/dist/locator/page-scripts.js +54 -0
  149. package/dist/locator/page-search.d.ts +10 -0
  150. package/dist/locator/page-search.js +19 -0
  151. package/dist/locator/selector-errors.d.ts +0 -4
  152. package/dist/locator/selector-errors.js +0 -7
  153. package/dist/locator/selector.d.ts +14 -13
  154. package/dist/locator/selector.js +10 -16
  155. package/dist/locator/text-locator.d.ts +1 -1
  156. package/dist/locator/text-locator.js +5 -9
  157. package/dist/logger/index.d.ts +6 -0
  158. package/dist/logger/index.js +14 -7
  159. package/dist/network/bidi-wire.d.ts +10 -0
  160. package/dist/network/bidi-wire.js +38 -0
  161. package/dist/network/intercept.d.ts +30 -0
  162. package/dist/network/intercept.js +130 -0
  163. package/dist/network/network-tracker.d.ts +10 -47
  164. package/dist/network/network-tracker.js +34 -68
  165. package/dist/network/network-types.d.ts +33 -0
  166. package/dist/network/network-types.js +1 -0
  167. package/dist/network/network-wait.d.ts +7 -2
  168. package/dist/network/network-wait.js +14 -4
  169. package/dist/network/response.d.ts +1 -1
  170. package/dist/network/response.js +1 -1
  171. package/dist/network/route.d.ts +92 -0
  172. package/dist/network/route.js +142 -0
  173. package/dist/network/router.d.ts +6 -91
  174. package/dist/network/router.js +25 -258
  175. package/dist/recorder/expectation.d.ts +5 -0
  176. package/dist/recorder/expectation.js +20 -0
  177. package/dist/recorder/files-recorder.d.ts +30 -0
  178. package/dist/recorder/files-recorder.js +88 -0
  179. package/dist/recorder/normalise.d.ts +17 -6
  180. package/dist/recorder/normalise.js +96 -91
  181. package/dist/recorder/options.d.ts +56 -0
  182. package/dist/recorder/options.js +1 -0
  183. package/dist/recorder/page-messages.d.ts +35 -0
  184. package/dist/recorder/page-messages.js +26 -0
  185. package/dist/recorder/ranking.js +1 -0
  186. package/dist/recorder/realms.d.ts +6 -0
  187. package/dist/recorder/realms.js +13 -0
  188. package/dist/recorder/recorder.d.ts +10 -74
  189. package/dist/recorder/recorder.js +39 -199
  190. package/dist/recorder/session-browser.d.ts +22 -0
  191. package/dist/recorder/session-browser.js +21 -0
  192. package/dist/recorder/session-secrets.d.ts +8 -0
  193. package/dist/recorder/session-secrets.js +45 -0
  194. package/dist/recorder/session.d.ts +7 -13
  195. package/dist/recorder/session.js +22 -117
  196. package/dist/recorder/spec-source.d.ts +39 -0
  197. package/dist/recorder/spec-source.js +84 -0
  198. package/dist/recorder/toolbar/action-guard.d.ts +21 -0
  199. package/dist/recorder/toolbar/action-guard.js +43 -0
  200. package/dist/recorder/toolbar/actions/assert.d.ts +4 -0
  201. package/dist/recorder/toolbar/actions/assert.js +52 -0
  202. package/dist/recorder/toolbar/actions/insert.d.ts +6 -0
  203. package/dist/recorder/toolbar/actions/insert.js +93 -0
  204. package/dist/recorder/toolbar/actions/pick.d.ts +8 -0
  205. package/dist/recorder/toolbar/actions/pick.js +26 -0
  206. package/dist/recorder/toolbar/actions/reply.d.ts +7 -0
  207. package/dist/recorder/toolbar/actions/reply.js +10 -0
  208. package/dist/recorder/toolbar/controller-fixture.d.ts +47 -0
  209. package/dist/recorder/toolbar/controller-fixture.js +129 -0
  210. package/dist/recorder/toolbar/controller.d.ts +12 -78
  211. package/dist/recorder/toolbar/controller.js +35 -247
  212. package/dist/recorder/toolbar/inspect.d.ts +1 -3
  213. package/dist/recorder/toolbar/inspect.js +2 -2
  214. package/dist/recorder/toolbar/page-scope.d.ts +19 -0
  215. package/dist/recorder/toolbar/page-scope.js +43 -0
  216. package/dist/recorder/toolbar/protocol.d.ts +2 -1
  217. package/dist/recorder/toolbar/source/attach.d.ts +2 -0
  218. package/dist/recorder/toolbar/source/attach.js +41 -0
  219. package/dist/recorder/toolbar/source/dom.d.ts +2 -0
  220. package/dist/recorder/toolbar/source/dom.js +47 -0
  221. package/dist/recorder/toolbar/source/drag.d.ts +2 -0
  222. package/dist/recorder/toolbar/source/drag.js +32 -0
  223. package/dist/recorder/toolbar/source/events-page.d.ts +2 -0
  224. package/dist/recorder/toolbar/source/events-page.js +67 -0
  225. package/dist/recorder/toolbar/source/events-toolbar.d.ts +2 -0
  226. package/dist/recorder/toolbar/source/events-toolbar.js +35 -0
  227. package/dist/recorder/toolbar/source/fields.d.ts +2 -0
  228. package/dist/recorder/toolbar/source/fields.js +44 -0
  229. package/dist/recorder/toolbar/source/handlers.d.ts +2 -0
  230. package/dist/recorder/toolbar/source/handlers.js +134 -0
  231. package/dist/recorder/toolbar/source/names.d.ts +2 -0
  232. package/dist/recorder/toolbar/source/names.js +12 -0
  233. package/dist/recorder/toolbar/source/picking.d.ts +2 -0
  234. package/dist/recorder/toolbar/source/picking.js +72 -0
  235. package/dist/recorder/toolbar/source/placement.d.ts +2 -0
  236. package/dist/recorder/toolbar/source/placement.js +45 -0
  237. package/dist/recorder/toolbar/source/recorder-side.d.ts +2 -0
  238. package/dist/recorder/toolbar/source/recorder-side.js +54 -0
  239. package/dist/recorder/toolbar/source/setup.d.ts +2 -0
  240. package/dist/recorder/toolbar/source/setup.js +50 -0
  241. package/dist/recorder/toolbar/source/views-panels.d.ts +2 -0
  242. package/dist/recorder/toolbar/source/views-panels.js +150 -0
  243. package/dist/recorder/toolbar/source/views-rail.d.ts +2 -0
  244. package/dist/recorder/toolbar/source/views-rail.js +40 -0
  245. package/dist/recorder/toolbar/source/views-render.d.ts +2 -0
  246. package/dist/recorder/toolbar/source/views-render.js +48 -0
  247. package/dist/recorder/toolbar/source.js +30 -856
  248. package/dist/recorder/toolbar/types.d.ts +70 -0
  249. package/dist/recorder/toolbar/types.js +1 -0
  250. package/dist/recorder/toolbar-bridge.d.ts +43 -0
  251. package/dist/recorder/toolbar-bridge.js +106 -0
  252. package/dist/runner/browsers.d.ts +10 -0
  253. package/dist/runner/browsers.js +12 -0
  254. package/dist/runner/groups.d.ts +5 -1
  255. package/dist/runner/groups.js +9 -2
  256. package/dist/runner/options.d.ts +31 -0
  257. package/dist/runner/options.js +1 -0
  258. package/dist/runner/pooled-run.d.ts +43 -0
  259. package/dist/runner/pooled-run.js +75 -0
  260. package/dist/runner/ports.d.ts +2 -0
  261. package/dist/runner/ports.js +2 -0
  262. package/dist/runner/registry.d.ts +1 -1
  263. package/dist/runner/registry.js +1 -1
  264. package/dist/runner/reporter.d.ts +40 -11
  265. package/dist/runner/reporter.js +69 -16
  266. package/dist/runner/run-test.d.ts +16 -0
  267. package/dist/runner/run-test.js +178 -0
  268. package/dist/runner/run.d.ts +5 -2
  269. package/dist/runner/run.js +28 -28
  270. package/dist/runner/select.d.ts +24 -0
  271. package/dist/runner/select.js +77 -0
  272. package/dist/runner/test-dir.d.ts +13 -0
  273. package/dist/runner/test-dir.js +27 -0
  274. package/dist/runner/test-runner.d.ts +27 -50
  275. package/dist/runner/test-runner.js +102 -264
  276. package/dist/runner/worker-pool.d.ts +67 -0
  277. package/dist/runner/worker-pool.js +275 -0
  278. package/dist/runner/worker-process.d.ts +6 -0
  279. package/dist/runner/worker-process.js +54 -0
  280. package/dist/runner/worker-protocol.d.ts +67 -0
  281. package/dist/runner/worker-protocol.js +1 -0
  282. package/dist/runner/worker-session.d.ts +7 -0
  283. package/dist/runner/worker-session.js +60 -0
  284. package/dist/runner/worker.d.ts +26 -0
  285. package/dist/runner/worker.js +121 -0
  286. package/dist/runner/workers-option.d.ts +15 -0
  287. package/dist/runner/workers-option.js +22 -0
  288. package/dist/script/call-function.d.ts +1 -9
  289. package/dist/script/call-function.js +2 -19
  290. package/dist/script/element-handle.d.ts +1 -2
  291. package/dist/script/element-handle.js +1 -2
  292. package/dist/script/serialize.d.ts +3 -12
  293. package/dist/script/serialize.js +53 -62
  294. package/dist/steps/parse/call.d.ts +3 -0
  295. package/dist/steps/parse/call.js +81 -0
  296. package/dist/steps/parse/expectation.d.ts +5 -0
  297. package/dist/steps/parse/expectation.js +71 -0
  298. package/dist/steps/parse/file-entry.d.ts +4 -0
  299. package/dist/steps/parse/file-entry.js +74 -0
  300. package/dist/steps/parse/literals.d.ts +4 -0
  301. package/dist/steps/parse/literals.js +28 -0
  302. package/dist/steps/parse/step.d.ts +4 -0
  303. package/dist/steps/parse/step.js +81 -0
  304. package/dist/steps/parse.js +2 -323
  305. package/dist/transport/bidi-connection.d.ts +4 -2
  306. package/dist/transport/bidi-connection.js +9 -43
  307. package/dist/transport/log-redaction.d.ts +13 -0
  308. package/dist/transport/log-redaction.js +51 -0
  309. package/dist/transport/subscriptions.js +18 -13
  310. package/dist/transport/unsupported.d.ts +7 -0
  311. package/dist/transport/unsupported.js +13 -0
  312. package/dist/types/bidi-modules/browser.d.ts +1 -12
  313. package/dist/types/bidi-modules/browsing-context.d.ts +8 -18
  314. package/dist/types/bidi-modules/emulation.d.ts +29 -21
  315. package/dist/types/bidi-modules/input.d.ts +1 -9
  316. package/dist/types/bidi-modules/network.d.ts +29 -24
  317. package/dist/types/bidi-modules/script.d.ts +0 -9
  318. package/dist/types/bidi-modules/session.d.ts +0 -8
  319. package/dist/types/bidi-modules/storage.d.ts +0 -5
  320. package/dist/types/bidi.d.ts +7 -37
  321. package/dist/types/config.d.ts +17 -3
  322. package/dist/types/test.d.ts +15 -2
  323. package/dist/wait/wait-until.d.ts +1 -5
  324. package/dist/wait/wait-until.js +7 -16
  325. package/package.json +3 -2
  326. package/dist/config/default.d.ts +0 -2
  327. package/dist/config/default.js +0 -3
  328. package/dist/types/bidi-modules/web-extensions.d.ts +0 -9
  329. package/dist/types/bidi-protocols/command.d.ts +0 -7
  330. package/dist/types/bidi-protocols/event.d.ts +0 -17
  331. /package/dist/{script/helpers.d.ts → locator/helper-realm.d.ts} +0 -0
@@ -1,327 +1,6 @@
1
1
  import ts from "typescript";
2
- import { parseSize } from "./files.js";
3
- const TEXT_MATCHERS = ["toHaveText", "toContainText", "toHaveValue"];
4
- function stringOf(node) {
5
- return node &&
6
- (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
7
- ? node.text
8
- : undefined;
9
- }
10
- /** `{ key: literal, … }` with only the allowed keys, or undefined when it has anything else */
11
- function readObject(node, allowed) {
12
- if (!node || !ts.isObjectLiteralExpression(node))
13
- return undefined;
14
- const result = {};
15
- for (const property of node.properties) {
16
- if (!ts.isPropertyAssignment(property) ||
17
- !allowed.includes(property.name.getText()))
18
- return undefined;
19
- const value = stringOf(property.initializer);
20
- if (value !== undefined)
21
- result[property.name.getText()] = value;
22
- else if (property.initializer.kind === ts.SyntaxKind.TrueKeyword)
23
- result[property.name.getText()] = true;
24
- else if (property.initializer.kind === ts.SyntaxKind.FalseKeyword)
25
- result[property.name.getText()] = false;
26
- else
27
- return undefined;
28
- }
29
- return result;
30
- }
31
- function readCall(method, args) {
32
- const first = stringOf(args[0]);
33
- if (first === undefined)
34
- return undefined;
35
- switch (method) {
36
- case "locator":
37
- return args.length === 1 ? { method, xpath: first } : undefined;
38
- case "getByCss":
39
- return args.length === 1 ? { method, css: first } : undefined;
40
- case "getByTestId":
41
- return args.length === 1 ? { method, testId: first } : undefined;
42
- case "getByText":
43
- case "getByLabel": {
44
- if (args.length > 2)
45
- return undefined;
46
- const options = args.length === 2 ? readObject(args[1], ["match", "ignoreCase"]) : {};
47
- if (!options)
48
- return undefined;
49
- const { match, ignoreCase } = options;
50
- if ((match !== undefined && match !== "full" && match !== "partial") ||
51
- (ignoreCase !== undefined && typeof ignoreCase !== "boolean")) {
52
- return undefined;
53
- }
54
- return {
55
- method,
56
- text: first,
57
- ...(match !== undefined && { match: match }),
58
- ...(ignoreCase !== undefined && { ignoreCase: ignoreCase }),
59
- };
60
- }
61
- case "getByRole": {
62
- if (args.length > 2)
63
- return undefined;
64
- const options = args.length === 2 ? readObject(args[1], ["name"]) : {};
65
- if (!options ||
66
- (options.name !== undefined && typeof options.name !== "string"))
67
- return undefined;
68
- return {
69
- method,
70
- role: first,
71
- ...(options.name !== undefined && { name: options.name }),
72
- };
73
- }
74
- default:
75
- return undefined;
76
- }
77
- }
78
- /** `page.a(…).b(…)` as calls in order; undefined when it is anything else */
79
- function readChain(node) {
80
- const calls = [];
81
- let current = node;
82
- while (ts.isCallExpression(current)) {
83
- if (!ts.isPropertyAccessExpression(current.expression))
84
- return undefined;
85
- const call = readCall(current.expression.name.text, current.arguments);
86
- if (!call)
87
- return undefined;
88
- calls.unshift(call);
89
- current = current.expression.expression;
90
- }
91
- return ts.isIdentifier(current) && current.text === "page" && calls.length > 0
92
- ? calls
93
- : undefined;
94
- }
95
- function readLocator(node) {
96
- if (ts.isCallExpression(node) &&
97
- ts.isPropertyAccessExpression(node.expression) &&
98
- node.expression.name.text === "withFallbacks") {
99
- const chain = readChain(node.expression.expression);
100
- const fallbacks = node.arguments.map(readChain);
101
- if (!chain ||
102
- node.arguments.length === 0 ||
103
- fallbacks.some((fallback) => !fallback))
104
- return undefined;
105
- return { chain, fallbacks: fallbacks };
106
- }
107
- const chain = readChain(node);
108
- return chain && { chain, fallbacks: [] };
109
- }
110
- /** `env.NAME`, `env["NAME"]`, `secrets.NAME`, `secrets["NAME"]` or a string literal */
111
- function readValue(node) {
112
- if (!node)
113
- return undefined;
114
- const literal = stringOf(node);
115
- if (literal !== undefined)
116
- return { kind: "literal", value: literal };
117
- let object;
118
- let name;
119
- if (ts.isPropertyAccessExpression(node)) {
120
- object = node.expression;
121
- name = node.name.text;
122
- }
123
- else if (ts.isElementAccessExpression(node)) {
124
- object = node.expression;
125
- name = stringOf(node.argumentExpression);
126
- }
127
- else
128
- return undefined;
129
- if (name === undefined || !ts.isIdentifier(object))
130
- return undefined;
131
- if (object.text === "env")
132
- return { kind: "env", name };
133
- if (object.text === "secrets")
134
- return { kind: "secret", name };
135
- return undefined;
136
- }
137
- function readTextMatcher(node) {
138
- if (!node)
139
- return undefined;
140
- const literal = stringOf(node);
141
- if (literal !== undefined)
142
- return literal;
143
- if (ts.isRegularExpressionLiteral(node)) {
144
- const end = node.text.lastIndexOf("/");
145
- return {
146
- regex: {
147
- source: node.text.slice(1, end),
148
- flags: node.text.slice(end + 1),
149
- },
150
- };
151
- }
152
- return undefined;
153
- }
154
- function readExpectation(matcher, args) {
155
- if (matcher === "toBeVisible")
156
- return args.length === 0 ? { matcher } : undefined;
157
- if (matcher === "toHaveCount") {
158
- const [count] = args;
159
- return args.length === 1 && count && ts.isNumericLiteral(count)
160
- ? { matcher, expected: Number(count.text) }
161
- : undefined;
162
- }
163
- if (matcher === "toHaveAttribute") {
164
- const name = stringOf(args[0]);
165
- const expected = readTextMatcher(args[1]);
166
- return args.length === 2 && name !== undefined && expected !== undefined
167
- ? { matcher, name, expected }
168
- : undefined;
169
- }
170
- if (TEXT_MATCHERS.includes(matcher)) {
171
- const expected = readTextMatcher(args[0]);
172
- return args.length === 1 && expected !== undefined
173
- ? { matcher: matcher, expected }
174
- : undefined;
175
- }
176
- return undefined;
177
- }
178
- /** `"path"` or `generatedFile({ name, size, type? })`; undefined for anything else */
179
- function readFileEntry(node) {
180
- const path = stringOf(node);
181
- if (path !== undefined)
182
- return { path };
183
- if (!ts.isCallExpression(node) ||
184
- !ts.isIdentifier(node.expression) ||
185
- node.expression.text !== "generatedFile" ||
186
- node.arguments.length !== 1 ||
187
- !ts.isObjectLiteralExpression(node.arguments[0]))
188
- return undefined;
189
- let name;
190
- let type;
191
- let size;
192
- let sizeSource;
193
- for (const property of node.arguments[0].properties) {
194
- if (!ts.isPropertyAssignment(property))
195
- return undefined;
196
- const value = property.initializer;
197
- switch (property.name.getText()) {
198
- case "name":
199
- name = stringOf(value);
200
- if (name === undefined)
201
- return undefined;
202
- break;
203
- case "type":
204
- type = stringOf(value);
205
- if (type === undefined)
206
- return undefined;
207
- break;
208
- case "size": {
209
- const text = stringOf(value);
210
- size =
211
- text !== undefined
212
- ? parseSize(text)
213
- : ts.isNumericLiteral(value)
214
- ? parseSize(Number(value.text))
215
- : undefined;
216
- if (size === undefined)
217
- return undefined;
218
- sizeSource = value.getText();
219
- break;
220
- }
221
- default:
222
- return undefined;
223
- }
224
- }
225
- if (name === undefined || size === undefined)
226
- return undefined;
227
- return {
228
- generate: {
229
- name,
230
- size,
231
- ...(type !== undefined && { type }),
232
- ...(sizeSource !== undefined && { sizeSource }),
233
- },
234
- };
235
- }
236
- /** One file entry, or an array of them */
237
- function readFiles(node) {
238
- if (!node)
239
- return undefined;
240
- if (ts.isArrayLiteralExpression(node)) {
241
- const entries = node.elements.map(readFileEntry);
242
- return entries.every((entry) => entry !== undefined)
243
- ? entries
244
- : undefined;
245
- }
246
- const entry = readFileEntry(node);
247
- return entry && [entry];
248
- }
249
- /** The step a statement stands for, or undefined when it is outside the recorder's set */
250
- function readStep(statement) {
251
- if (!ts.isExpressionStatement(statement) ||
252
- !ts.isAwaitExpression(statement.expression))
253
- return undefined;
254
- const call = statement.expression.expression;
255
- if (!ts.isCallExpression(call) ||
256
- !ts.isPropertyAccessExpression(call.expression))
257
- return undefined;
258
- const { expression: target, name } = call.expression;
259
- const args = call.arguments;
260
- if (ts.isIdentifier(target) && target.text === "page") {
261
- if (name.text === "goto" && args.length === 1) {
262
- const url = stringOf(args[0]);
263
- return url === undefined ? undefined : { kind: "goto", url };
264
- }
265
- if (name.text === "waitForNetworkIdle" && args.length === 0)
266
- return { kind: "waitForNetworkIdle" };
267
- if (name.text === "waitForTimeout" &&
268
- args.length === 1 &&
269
- ts.isNumericLiteral(args[0])) {
270
- const ms = Number(args[0].text);
271
- return Number.isInteger(ms) ? { kind: "waitForTimeout", ms } : undefined;
272
- }
273
- if (name.text === "screenshot" && args.length === 1) {
274
- const options = readObject(args[0], ["name", "fullPage"]);
275
- if (!options || typeof options.name !== "string")
276
- return undefined;
277
- if (options.fullPage !== undefined &&
278
- typeof options.fullPage !== "boolean")
279
- return undefined;
280
- return {
281
- kind: "screenshot",
282
- name: options.name,
283
- fullPage: options.fullPage === true,
284
- };
285
- }
286
- return undefined;
287
- }
288
- // expect(locator).matcher(…) and expect(locator).not.matcher(…)
289
- const negated = ts.isPropertyAccessExpression(target) && target.name.text === "not";
290
- const subject = negated
291
- ? target.expression
292
- : target;
293
- if (ts.isCallExpression(subject) &&
294
- ts.isIdentifier(subject.expression) &&
295
- subject.expression.text === "expect" &&
296
- subject.arguments.length === 1) {
297
- const locator = readLocator(subject.arguments[0]);
298
- const expectation = readExpectation(name.text, args);
299
- return locator && expectation
300
- ? { kind: "expect", locator, not: negated, expectation }
301
- : undefined;
302
- }
303
- const locator = readLocator(target);
304
- if (!locator)
305
- return undefined;
306
- if (name.text === "click" && args.length === 0)
307
- return { kind: "click", locator };
308
- if (name.text === "press" && args.length === 1) {
309
- const key = stringOf(args[0]);
310
- return key === undefined ? undefined : { kind: "press", locator, key };
311
- }
312
- if (name.text === "fill" && args.length === 1) {
313
- const value = readValue(args[0]);
314
- return value ? { kind: "fill", locator, value } : undefined;
315
- }
316
- if ((name.text === "setInputFiles" || name.text === "dropFiles") &&
317
- args.length === 1) {
318
- const files = readFiles(args[0]);
319
- return files
320
- ? { kind: name.text, locator, files }
321
- : undefined;
322
- }
323
- return undefined;
324
- }
2
+ import { stringOf } from "./parse/literals.js";
3
+ import { readStep } from "./parse/step.js";
325
4
  function parseStatement(source, statement, sourceFile) {
326
5
  const start = statement.getStart(sourceFile);
327
6
  const end = statement.end;
@@ -3,7 +3,7 @@ import { type EventName, type Subscription, type SubscribeOptions } from "./subs
3
3
  /** Time (ms) a command may wait for its reply before it fails with code "timeout" */
4
4
  export declare const DEFAULT_COMMAND_TIMEOUT = 30000;
5
5
  export interface ConnectorOptions {
6
- /** @default 30000 */
6
+ /** @default DEFAULT_COMMAND_TIMEOUT */
7
7
  commandTimeout?: number;
8
8
  }
9
9
  export interface SendOptions {
@@ -28,6 +28,7 @@ export declare class BiDiConnector {
28
28
  /** Which browser is on the other end, when the launcher says; code that must treat browsers apart asks this */
29
29
  browserName?: "firefox" | "chrome";
30
30
  private webSocket;
31
+ private closeListeners;
31
32
  private eventEmitter;
32
33
  private commandTimeout;
33
34
  private subscriptions;
@@ -35,10 +36,11 @@ export declare class BiDiConnector {
35
36
  static connect(url: string, options?: ConnectorOptions): Promise<BiDiConnector>;
36
37
  /** Calls `listener` once when the socket closes or fails (not when `kill()` ends it) */
37
38
  onClosed(listener: () => void): void;
38
- private closeListeners;
39
39
  private notifyClosed;
40
40
  private onWebsocketClose;
41
41
  private onWebsocketError;
42
+ /** The socket is gone: tell the listeners and fail what is waiting for an answer */
43
+ private lose;
42
44
  private get isClosed();
43
45
  /** Rejects every pending command; `reason` gets the pending method names for its message */
44
46
  private rejectAll;
@@ -2,6 +2,7 @@ import EventEmitter from "node:events";
2
2
  import logger from "../logger/index.js";
3
3
  import { WaitTimeoutError } from "../wait/wait-until.js";
4
4
  import { BiDiError } from "./bidi-error.js";
5
+ import { forLog } from "./log-redaction.js";
5
6
  import { SubscriptionManager, } from "./subscriptions.js";
6
7
  /** Time (ms) a command may wait for its reply before it fails with code "timeout" */
7
8
  export const DEFAULT_COMMAND_TIMEOUT = 30000;
@@ -23,18 +24,8 @@ export class BiDiConnector {
23
24
  /** Set once the socket closed or failed: nothing sent after that gets an answer */
24
25
  this.closed = false;
25
26
  this.closeListeners = new Set();
26
- this.onWebsocketClose = () => {
27
- this.closed = true;
28
- this.notifyClosed();
29
- this.rejectAll((pending) => new ConnectionClosedError(`Websocket closed${pendingSuffix(pending)}`));
30
- };
31
- this.onWebsocketError = (ev) => {
32
- this.closed = true;
33
- this.notifyClosed();
34
- this.rejectAll((pending) => new ConnectionClosedError(`Websocket error${pendingSuffix(pending)}`, {
35
- cause: ev,
36
- }));
37
- };
27
+ this.onWebsocketClose = () => this.lose("Websocket closed");
28
+ this.onWebsocketError = (ev) => this.lose("Websocket error", ev);
38
29
  this.messageListener = (ev) => {
39
30
  const message = JSON.parse(ev.data);
40
31
  logger.debug("Received message from BiDi Websocket: ", forLog(message));
@@ -97,6 +88,12 @@ export class BiDiConnector {
97
88
  for (const listener of listeners)
98
89
  listener();
99
90
  }
91
+ /** The socket is gone: tell the listeners and fail what is waiting for an answer */
92
+ lose(what, cause) {
93
+ this.closed = true;
94
+ this.notifyClosed();
95
+ this.rejectAll((pending) => new ConnectionClosedError(`${what}${pendingSuffix(pending)}`, cause === undefined ? undefined : { cause }));
96
+ }
100
97
  get isClosed() {
101
98
  // A socket that is closing or closed (readyState 2 or 3) has already lost the browser
102
99
  const state = this.webSocket.readyState;
@@ -209,37 +206,6 @@ export class BiDiConnector {
209
206
  this.webSocket.close();
210
207
  }
211
208
  }
212
- /** Channels whose messages carry what the tester typed (the recorder's) or adds as a secret (its toolbar's) */
213
- const PRIVATE_CHANNEL = /^samurai-(recorder|toolbar)-/;
214
- /**
215
- * The message as the debug log gets it: a `script.message` on a private channel is logged by channel and
216
- * action only, so a value the tester types or a secret added from the recorder toolbar never reaches the log
217
- * (an added secret is only registered for masking after it is saved).
218
- */
219
- function forLog(message) {
220
- const channel = message.params?.channel;
221
- if (message.type !== "event" ||
222
- message.method !== "script.message" ||
223
- typeof channel !== "string" ||
224
- !PRIVATE_CHANNEL.test(channel))
225
- return message;
226
- const entries = message.params?.data
227
- ?.value;
228
- const field = (key) => {
229
- if (!Array.isArray(entries))
230
- return undefined;
231
- const entry = entries.find((pair) => Array.isArray(pair) && pair[0] === key);
232
- const value = entry?.[1]?.value;
233
- return typeof value === "string" ? value : undefined;
234
- };
235
- return {
236
- ...message,
237
- params: {
238
- ...message.params,
239
- data: { type: field("type"), action: field("action"), redacted: true },
240
- },
241
- };
242
- }
243
209
  function pendingSuffix(pending) {
244
210
  return pending.length > 0 ? ` (pending: ${pending.join(", ")})` : "";
245
211
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The message as the debug log gets it: a `script.message` on a private channel is logged by channel and
3
+ * action only, so a value the tester types or a secret added from the recorder toolbar never reaches the log
4
+ * (an added secret is only registered for masking after it is saved).
5
+ */
6
+ export declare function forLog(message: {
7
+ type?: string;
8
+ method?: string;
9
+ params?: {
10
+ channel?: unknown;
11
+ data?: unknown;
12
+ };
13
+ }): unknown;
@@ -0,0 +1,51 @@
1
+ /** Channels whose messages carry what the tester typed (the recorder's) or adds as a secret (its toolbar's) */
2
+ const PRIVATE_CHANNEL = /^samurai-(recorder|toolbar)-/;
3
+ /**
4
+ * The message as the debug log gets it: a `script.message` on a private channel is logged by channel and
5
+ * action only, so a value the tester types or a secret added from the recorder toolbar never reaches the log
6
+ * (an added secret is only registered for masking after it is saved).
7
+ */
8
+ export function forLog(message) {
9
+ return shortened(redacted(message));
10
+ }
11
+ /** A string longer than this is cut in the log: a file's bytes travel in 1 MB chunks, 50 MB makes 70 MB of debug log */
12
+ const LOG_STRING_LIMIT = 1000;
13
+ /** A copy of `value` with every long string cut to its start and its length; short values come back as they are */
14
+ function shortened(value, depth = 0) {
15
+ if (typeof value === "string")
16
+ return value.length > LOG_STRING_LIMIT
17
+ ? `${value.slice(0, 200)}… (${value.length} characters)`
18
+ : value;
19
+ if (value === null || typeof value !== "object" || depth > 30)
20
+ return value;
21
+ if (Array.isArray(value))
22
+ return value.map((item) => shortened(item, depth + 1));
23
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
24
+ key,
25
+ shortened(item, depth + 1),
26
+ ]));
27
+ }
28
+ function redacted(message) {
29
+ const channel = message.params?.channel;
30
+ if (message.type !== "event" ||
31
+ message.method !== "script.message" ||
32
+ typeof channel !== "string" ||
33
+ !PRIVATE_CHANNEL.test(channel))
34
+ return message;
35
+ const entries = message.params?.data
36
+ ?.value;
37
+ const field = (key) => {
38
+ if (!Array.isArray(entries))
39
+ return undefined;
40
+ const entry = entries.find((pair) => Array.isArray(pair) && pair[0] === key);
41
+ const value = entry?.[1]?.value;
42
+ return typeof value === "string" ? value : undefined;
43
+ };
44
+ return {
45
+ ...message,
46
+ params: {
47
+ ...message.params,
48
+ data: { type: field("type"), action: field("action"), redacted: true },
49
+ },
50
+ };
51
+ }
@@ -21,26 +21,21 @@ export class SubscriptionManager {
21
21
  await Promise.allSettled(acquired.map((event) => this.release(event)));
22
22
  throw err;
23
23
  }
24
- let released = false;
25
24
  return {
26
- unsubscribe: async () => {
27
- if (released)
28
- return;
29
- released = true;
25
+ unsubscribe: once(async () => {
30
26
  await Promise.all(acquired.map((event) => this.release(event)));
31
- },
27
+ }),
32
28
  };
33
29
  }
34
30
  async subscribeScoped(events, contexts) {
35
- const { subscription } = await this.commands.subscribe({ events, contexts });
36
- let released = false;
31
+ const { subscription } = await this.commands.subscribe({
32
+ events,
33
+ contexts,
34
+ });
37
35
  return {
38
- unsubscribe: async () => {
39
- if (released)
40
- return;
41
- released = true;
36
+ unsubscribe: once(async () => {
42
37
  await this.commands.unsubscribe({ subscriptions: [subscription] });
43
- },
38
+ }),
44
39
  };
45
40
  }
46
41
  state(event) {
@@ -79,3 +74,13 @@ export class SubscriptionManager {
79
74
  return step;
80
75
  }
81
76
  }
77
+ /** `fn` runs on the first call only; later calls resolve at once */
78
+ function once(fn) {
79
+ let called = false;
80
+ return async () => {
81
+ if (called)
82
+ return;
83
+ called = true;
84
+ await fn();
85
+ };
86
+ }
@@ -0,0 +1,7 @@
1
+ import { BiDiError } from "./bidi-error.js";
2
+ /** The browser does not implement a command, a locator type or an option */
3
+ export declare class UnsupportedOperationError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ /** Whether the browser answered that it does not know the command: Firefox says "unknown command", others "unsupported operation" */
7
+ export declare function isUnsupported(err: unknown): err is BiDiError;
@@ -0,0 +1,13 @@
1
+ import { BiDiError } from "./bidi-error.js";
2
+ /** The browser does not implement a command, a locator type or an option */
3
+ export class UnsupportedOperationError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "UnsupportedOperationError";
7
+ }
8
+ }
9
+ /** Whether the browser answered that it does not know the command: Firefox says "unknown command", others "unsupported operation" */
10
+ export function isUnsupported(err) {
11
+ return (err instanceof BiDiError &&
12
+ (err.code === "unknown command" || err.code === "unsupported operation"));
13
+ }
@@ -44,18 +44,7 @@ export interface BrowserCommands {
44
44
 
45
45
  /** How downloads are handled. */
46
46
  export type DownloadBehavior =
47
- | { type: "allowed"; destinationFolder: string }
48
- | { type: "denied" };
49
-
50
- /** Union of all possible result types returned by browser module commands. */
51
- export type BrowserResult =
52
- | CloseResult
53
- | CreateUserContextResult
54
- | GetClientWindowsResult
55
- | GetUserContextsResult
56
- | RemoveUserContextResult
57
- | SetClientWindowStateResult
58
- | SetDownloadBehaviorResult;
47
+ { type: "allowed"; destinationFolder: string } | { type: "denied" };
59
48
 
60
49
  /** Result of `browser.close`. */
61
50
  export type CloseResult = EmptyResult;
@@ -55,7 +55,11 @@ export interface BrowsingContextModule {
55
55
  };
56
56
  /** Accepts or dismisses the open user prompt. */
57
57
  "browsingContext.handleUserPrompt": {
58
- params: { context: BrowsingContext; accept?: boolean | undefined; userText?: string | undefined };
58
+ params: {
59
+ context: BrowsingContext;
60
+ accept?: boolean | undefined;
61
+ userText?: string | undefined;
62
+ };
59
63
  result: {};
60
64
  };
61
65
  /** Sets or resets (null) the viewport size and device pixel ratio. */
@@ -92,7 +96,9 @@ export interface BrowsingContextModule {
92
96
  params: {
93
97
  context: BrowsingContext;
94
98
  background?: boolean | undefined;
95
- margin?: { top?: number; bottom?: number; left?: number; right?: number } | undefined;
99
+ margin?:
100
+ | { top?: number; bottom?: number; left?: number; right?: number }
101
+ | undefined;
96
102
  orientation?: "portrait" | "landscape" | undefined;
97
103
  page?: { width?: number; height?: number } | undefined;
98
104
  pageRanges?: (number | string)[] | undefined;
@@ -360,22 +366,6 @@ export type BrowsingContextEvent =
360
366
  | UserPromptClosed
361
367
  | UserPromptOpened;
362
368
 
363
- /** Union of all possible result types returned by browsingContext module commands. */
364
- export type BrowsingContextResult =
365
- | ActivateResult
366
- | CaptureScreenshotResult
367
- | CloseResult
368
- | CreateResult
369
- | GetTreeResult
370
- | HandleUserPromptResult
371
- | LocateNodesResult
372
- | NavigateResult
373
- | PrintResult
374
- | ReloadResult
375
- | SetBypassCSPResult
376
- | SetViewportResult
377
- | TraverseHistoryResult;
378
-
379
369
  /** Result of `browsingContext.activate`. */
380
370
  export type ActivateResult = EmptyResult;
381
371