@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,208 +0,0 @@
1
- import type { BiDiConnector } from "../transport/bidi-connection.js";
2
- import { BiDiError } from "../transport/bidi-error.js";
3
- import type { Cookie as BiDiCookie, SameSite } from "../types/bidi-modules/network.js";
4
- import type { PartialCookie, PartitionDescriptor } from "../types/bidi-modules/storage.js";
5
- import { applyEmulation, type EmulationOptions } from "./emulation.js";
6
- import type Page from "./page.js";
7
-
8
- export type { SameSite } from "../types/bidi-modules/network.js";
9
-
10
- /** A cookie as `context.cookies()` returns it */
11
- export interface Cookie {
12
- name: string;
13
- value: string;
14
- domain: string;
15
- path: string;
16
- httpOnly: boolean;
17
- secure: boolean;
18
- sameSite: SameSite;
19
- /** Seconds since the epoch; absent for a session cookie */
20
- expiry?: number;
21
- }
22
-
23
- /** A cookie to add: `domain`, or a `url` to take domain, path and `secure` from */
24
- export interface CookieInput {
25
- name: string;
26
- value: string;
27
- domain?: string;
28
- /** @default "/", or the path of `url` */
29
- path?: string;
30
- url?: string;
31
- httpOnly?: boolean;
32
- secure?: boolean;
33
- sameSite?: SameSite;
34
- /** Seconds since the epoch */
35
- expiry?: number;
36
- }
37
-
38
- export interface CookieFilter {
39
- name?: string;
40
- domain?: string;
41
- path?: string;
42
- }
43
-
44
- /** What a context needs from its browser */
45
- export interface ContextHost {
46
- connector: BiDiConnector;
47
- newPage(options: { type: "tab"; userContext?: string }): Promise<Page>;
48
- /** Called when a context has been closed */
49
- forget(context: BrowserContext): void;
50
- }
51
-
52
- /**
53
- * An isolated browsing session inside one browser: its own cookies, storage and cache. Pages opened in it
54
- * share them; pages of other contexts see none of it.
55
- */
56
- export class BrowserContext {
57
- private opened: Page[] = [];
58
- private isClosed = false;
59
-
60
- constructor(
61
- private host: ContextHost,
62
- /** The BiDi user context id; "default" for the browser's own */
63
- public readonly id: string,
64
- public readonly isDefault: boolean,
65
- ) {}
66
-
67
- /** True once `close()` has run */
68
- public get closed(): boolean {
69
- return this.isClosed;
70
- }
71
-
72
- /** Opens a tab in this context */
73
- public async newPage(): Promise<Page> {
74
- this.assertOpen();
75
- return this.host.newPage({ type: "tab", ...(!this.isDefault && { userContext: this.id }) });
76
- }
77
-
78
- /** The pages opened in this context that are still open */
79
- public pages(): Page[] {
80
- this.opened = this.opened.filter((page) => !page.closed);
81
- return [...this.opened];
82
- }
83
-
84
- /** @internal Called when a page is opened in this context */
85
- public register(page: Page): void {
86
- this.opened.push(page);
87
- page.setContext(this);
88
- }
89
-
90
- /** Adds cookies to this context's storage. Every cookie is checked before any is stored */
91
- public async addCookies(cookies: CookieInput[]): Promise<void> {
92
- this.assertOpen();
93
- const prepared = cookies.map(toBiDiCookie);
94
- for (const cookie of prepared) {
95
- await this.host.connector.send("storage.setCookie", { cookie, partition: this.partition() });
96
- }
97
- }
98
-
99
- /** The cookies of this context, optionally only those matching `filter` */
100
- public async cookies(filter?: CookieFilter): Promise<Cookie[]> {
101
- this.assertOpen();
102
- const { cookies } = await this.host.connector.send("storage.getCookies", {
103
- ...(filter && { filter }),
104
- partition: this.partition(),
105
- });
106
- return cookies.map(toCookie);
107
- }
108
-
109
- /** Deletes all cookies of this context, or those matching `filter` */
110
- public async clearCookies(filter?: CookieFilter): Promise<void> {
111
- this.assertOpen();
112
- await this.host.connector.send("storage.deleteCookies", {
113
- ...(filter && { filter }),
114
- partition: this.partition(),
115
- });
116
- }
117
-
118
- /**
119
- * Pretends things about every page of this context, including pages opened later: locale, timezone, user agent,
120
- * geolocation, offline, orientation, screen size. A page's own `emulate()` wins over the context's.
121
- * @example
122
- * await context.emulate({ locale: "nl-NL", timezone: "Europe/Amsterdam", offline: false });
123
- */
124
- public async emulate(options: EmulationOptions): Promise<void> {
125
- this.assertOpen();
126
- await applyEmulation(this.host.connector, { userContexts: [this.id] }, options);
127
- }
128
-
129
- /**
130
- * Grants, denies or resets a browser permission ("geolocation", "notifications", "camera", …) for `origin`
131
- * in this context. Geolocation emulation only answers once the permission is granted.
132
- */
133
- public async setPermission(
134
- name: string,
135
- state: "granted" | "denied" | "prompt",
136
- origin: string,
137
- ): Promise<void> {
138
- this.assertOpen();
139
- await this.host.connector.send("permissions.setPermission", {
140
- descriptor: { name },
141
- state,
142
- origin,
143
- ...(!this.isDefault && { userContext: this.id }),
144
- });
145
- }
146
-
147
- /** Removes the context; the browser closes its pages. The default context cannot be closed */
148
- public async close(): Promise<void> {
149
- if (this.isDefault) throw new Error("cannot close the default context");
150
- if (this.isClosed) return;
151
- try {
152
- await this.host.connector.send("browser.removeUserContext", { userContext: this.id });
153
- } catch (err) {
154
- // Already gone: the goal is met
155
- if (!(err instanceof BiDiError && err.code === "no such user context")) throw err;
156
- }
157
- this.isClosed = true;
158
- const pages = this.opened;
159
- this.opened = [];
160
- await Promise.all(pages.map((page) => page.detach()));
161
- this.host.forget(this);
162
- }
163
-
164
- private partition(): PartitionDescriptor {
165
- return { type: "storageKey", userContext: this.id };
166
- }
167
-
168
- private assertOpen() {
169
- if (this.isClosed) throw new Error("context closed");
170
- }
171
- }
172
-
173
- function toBiDiCookie(input: CookieInput): PartialCookie {
174
- let { domain, path, secure } = input;
175
- if (input.url !== undefined) {
176
- const url = new URL(input.url);
177
- domain ??= url.hostname;
178
- path ??= url.pathname;
179
- secure ??= url.protocol === "https:";
180
- }
181
- if (domain === undefined) throw new TypeError(`cookie "${input.name}" needs a domain or a url`);
182
- return {
183
- name: input.name,
184
- value: { type: "string", value: input.value },
185
- domain,
186
- path: path ?? "/",
187
- ...(secure !== undefined && { secure }),
188
- ...(input.httpOnly !== undefined && { httpOnly: input.httpOnly }),
189
- ...(input.sameSite !== undefined && { sameSite: input.sameSite }),
190
- ...(input.expiry !== undefined && { expiry: input.expiry }),
191
- };
192
- }
193
-
194
- function toCookie(cookie: BiDiCookie): Cookie {
195
- return {
196
- name: cookie.name,
197
- value:
198
- cookie.value.type === "base64"
199
- ? Buffer.from(cookie.value.value, "base64").toString("utf8")
200
- : cookie.value.value,
201
- domain: cookie.domain,
202
- path: cookie.path,
203
- httpOnly: cookie.httpOnly,
204
- secure: cookie.secure,
205
- sameSite: cookie.sameSite,
206
- ...(cookie.expiry !== undefined && { expiry: cookie.expiry }),
207
- };
208
- }
@@ -1,110 +0,0 @@
1
- import assert from "node:assert";
2
- import { execSync } from "node:child_process";
3
- import { existsSync } from "node:fs";
4
- import type { SupportedBrowser } from "../types/browser.js";
5
- import logger from "../logger/index.js";
6
-
7
- const supportedBrowsers: SupportedBrowser[] = ["firefox"];
8
- const macBrowserPaths: Record<SupportedBrowser, string> = {
9
- // chrome: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
10
- firefox: "/Applications/Firefox.app/Contents/MacOS/Firefox",
11
- };
12
-
13
- export function findBrowser(browserName: SupportedBrowser) {
14
- logger.verbose("Checking install location for %s", browserName);
15
- if (!supportedBrowsers.includes(browserName)) {
16
- throw new Error(
17
- "This browser is not supported. Supported browsers include: " +
18
- supportedBrowsers.join(", "),
19
- );
20
- }
21
-
22
- let browserLocation = "";
23
- const notFoundMsg = `Cannot find browser ${browserName} on this machine! Is it installed?`;
24
- try {
25
- browserLocation = tryFindBrowser(browserName);
26
- } catch (err) {
27
- throw new Error(notFoundMsg);
28
- }
29
-
30
- assert(browserLocation, notFoundMsg);
31
- logger.verbose("Browser found at: %s", browserLocation);
32
-
33
- return browserLocation;
34
- }
35
-
36
- function tryFindBrowser(browserName: SupportedBrowser): string {
37
- logger.debug("Current platform: %s", process.platform);
38
- switch (process.platform) {
39
- case "win32":
40
- return findBrowserWindows(browserName);
41
- case "darwin":
42
- return findBrowserMac(browserName);
43
- case "linux":
44
- return findBrowserLinux(browserName);
45
- default:
46
- throw new Error("This platform is not supported");
47
- }
48
- }
49
-
50
- function findBrowserWindows(browserName: SupportedBrowser): string {
51
- // Function to prevent search the entire file system
52
- // We'll only try the most common paths
53
- const possibleDirs = ["Program\ Files", "Program\ Files (x86)"];
54
-
55
- const where = (dirPath: string) => {
56
- const command: string = `where /r ${dirPath} ${browserName}.exe`;
57
- try {
58
- logger.verbose("Running command: %s", command);
59
-
60
- const whereResults = execSync(command, {
61
- encoding: "utf8",
62
- });
63
- logger.debug("Command results: \n", whereResults);
64
- const results = whereResults.split(/\n/g);
65
- return results[0];
66
- } catch (error) {
67
- logger.verbose("Error while executing %s", command, { error });
68
- }
69
- };
70
-
71
- let result = undefined;
72
-
73
- for (const possibleDir of possibleDirs) {
74
- logger.debug("Checking directory: %s", possibleDir);
75
- result = where(possibleDir);
76
- if (result) break;
77
- }
78
-
79
- if (!result) {
80
- throw new Error("'where' yielded no results");
81
- }
82
- return result;
83
- }
84
-
85
- function findBrowserMac(browserName: SupportedBrowser): string {
86
- const checkPathExists = (path: string) => {
87
- logger.debug("Checking path: %s", path);
88
- const exists = existsSync(path);
89
- if (!exists) {
90
- throw new Error(`Did not find ${browserName} at ${path}`);
91
- }
92
- return path;
93
- };
94
- return checkPathExists(macBrowserPaths[browserName]);
95
- }
96
-
97
- const linuxBinaryNames: Record<SupportedBrowser, string> = {
98
- // chrome: "google-chrome",
99
- firefox: "firefox",
100
- };
101
-
102
- function findBrowserLinux(browserName: SupportedBrowser): string {
103
- const command = `which ${linuxBinaryNames[browserName]}`;
104
- logger.debug("Running command: %s", command);
105
- const result = execSync(command, {
106
- encoding: "utf8",
107
- stdio: "pipe",
108
- });
109
- return result.replace(/\n/g, "");
110
- }
@@ -1,88 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { test } from "node:test";
3
- import { autoReply, FakeWebSocket } from "../testing/fake-websocket.js";
4
- import { BiDiConnector } from "../transport/bidi-connection.js";
5
- import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
6
- import { tmpdir } from "node:os";
7
- import path from "node:path";
8
- import { abandonLaunch, Browser, browserLaunchFlag, ensureBrowserProfile } from "./browser.js";
9
-
10
- function fakeBrowser(handlers: Record<string, (params: any) => object | Error> = {}) {
11
- const ws = new FakeWebSocket();
12
- const connector = new BiDiConnector(ws as unknown as WebSocket);
13
- const stop = autoReply(ws, { "browsingContext.getTree": () => ({ contexts: [] }), ...handlers });
14
- const browser = new Browser({ browserProc: {} as never, biDiConnector: connector });
15
- return { ws, browser, stop };
16
- }
17
-
18
- test("newPage returns a page bound to the created context", async () => {
19
- const { ws, browser, stop } = fakeBrowser({
20
- "browsingContext.create": () => ({ context: "ctx-42", userContext: "default" }),
21
- });
22
- const page = await browser.newPage({ type: "tab" });
23
- assert.equal(page.contextId, "ctx-42");
24
- assert.deepEqual(
25
- ws.sent.find((m) => m.method === "browsingContext.create")!.params,
26
- { type: "tab" },
27
- );
28
- stop();
29
- });
30
-
31
- test("newPage passes background and userContext through", async () => {
32
- const { ws, browser, stop } = fakeBrowser({
33
- "browsingContext.create": () => ({ context: "c", userContext: "u1" }),
34
- });
35
- await browser.newPage({ type: "window", background: true, userContext: "u1" });
36
- assert.deepEqual(ws.sent[0]!.params, { type: "window", background: true, userContext: "u1" });
37
- stop();
38
- });
39
-
40
- test("newPage surfaces a failing create as a BiDiError", async () => {
41
- const { browser, stop } = fakeBrowser({
42
- "browsingContext.create": () => new Error("no window"),
43
- });
44
- await assert.rejects(browser.newPage({ type: "tab" }), { name: "BiDiError", code: "unknown error" });
45
- stop();
46
- });
47
-
48
- test("firefox starts on about:blank, not its privileged home page", () => {
49
- assert.deepEqual(browserLaunchFlag("firefox", { port: 9300, profileDir: "/tmp/profile", headless: true }), [
50
- "--no-sandbox",
51
- "--no-remote",
52
- "--headless",
53
- "--remote-debugging-port=9300",
54
- "--profile",
55
- "/tmp/profile",
56
- "about:blank",
57
- ]);
58
- });
59
-
60
- test("a failed launch stops the connection and the browser it started", () => {
61
- const calls: string[] = [];
62
- abandonLaunch({ kill: () => calls.push("browser") > 0 }, { kill: () => void calls.push("connector") });
63
- assert.deepEqual(calls, ["connector", "browser"]);
64
- });
65
-
66
- test("a launch that failed before connecting still stops the browser", () => {
67
- const calls: string[] = [];
68
- abandonLaunch({ kill: () => calls.push("browser") > 0 }, undefined);
69
- assert.deepEqual(calls, ["browser"]);
70
- });
71
-
72
- test("the firefox profile is written with a blank start page, and an outdated one is rewritten", () => {
73
- const dir = mkdtempSync(path.join(tmpdir(), "samurai-profile-"));
74
- try {
75
- const userJs = path.join(dir, "firefox", "user.js");
76
- ensureBrowserProfile("firefox", userJs);
77
- const written = readFileSync(userJs, "utf8");
78
- assert.match(written, /user_pref\("browser\.startup\.page", 0\);/);
79
- assert.match(written, /user_pref\("browser\.startup\.homepage", "about:blank"\);/);
80
- assert.match(written, /user_pref\("browser\.newtabpage\.enabled", false\);/);
81
-
82
- writeFileSync(userJs, 'user_pref("devtools.chrome.enabled", true);');
83
- ensureBrowserProfile("firefox", userJs);
84
- assert.equal(readFileSync(userJs, "utf8"), written);
85
- } finally {
86
- rmSync(dir, { recursive: true, force: true });
87
- }
88
- });