@itmetsam/samurai-framework 0.21.4 → 0.22.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 (178) hide show
  1. package/dist/api.d.ts +8 -1
  2. package/dist/api.js +1 -0
  3. package/dist/browser/browser-context.d.ts +18 -6
  4. package/dist/browser/browser-context.js +25 -7
  5. package/dist/browser/browser.d.ts +11 -2
  6. package/dist/browser/browser.js +31 -8
  7. package/dist/browser/cdp.d.ts +11 -0
  8. package/dist/browser/cdp.js +28 -0
  9. package/dist/browser/chrome-launch.d.ts +4 -0
  10. package/dist/browser/chrome-launch.js +11 -3
  11. package/dist/browser/context-cookie-store.d.ts +17 -0
  12. package/dist/browser/context-cookie-store.js +90 -0
  13. package/dist/browser/index.d.ts +8 -1
  14. package/dist/browser/index.js +5 -1
  15. package/dist/browser/keyboard.d.ts +35 -0
  16. package/dist/browser/keyboard.js +83 -0
  17. package/dist/browser/launch-firefox.js +1 -1
  18. package/dist/browser/launch-flags.d.ts +4 -1
  19. package/dist/browser/launch-flags.js +18 -5
  20. package/dist/browser/mouse.d.ts +42 -0
  21. package/dist/browser/mouse.js +66 -0
  22. package/dist/browser/page-navigation.d.ts +0 -2
  23. package/dist/browser/page-navigation.js +1 -6
  24. package/dist/browser/page-settings.d.ts +1 -1
  25. package/dist/browser/page.d.ts +20 -6
  26. package/dist/browser/page.js +24 -8
  27. package/dist/browser/session-capabilities.d.ts +10 -0
  28. package/dist/browser/session-capabilities.js +11 -0
  29. package/dist/browser/session.d.ts +2 -1
  30. package/dist/browser/session.js +3 -3
  31. package/dist/browser/storage-state.d.ts +4 -0
  32. package/dist/browser/storage-state.js +17 -0
  33. package/dist/browser/use-applier.d.ts +38 -0
  34. package/dist/browser/use-applier.js +121 -0
  35. package/dist/browser/use-commands.d.ts +36 -0
  36. package/dist/browser/use-commands.js +120 -0
  37. package/dist/browser/viewport.d.ts +3 -2
  38. package/dist/browser/viewport.js +15 -2
  39. package/dist/cli/args.d.ts +8 -4
  40. package/dist/cli/args.js +8 -5
  41. package/dist/cli/console-reporter.js +5 -1
  42. package/dist/cli/main.js +1 -0
  43. package/dist/cli/usage.d.ts +1 -1
  44. package/dist/cli/usage.js +4 -1
  45. package/dist/config/base-url.d.ts +4 -0
  46. package/dist/config/base-url.js +6 -0
  47. package/dist/config/config.js +8 -0
  48. package/dist/config/fully-parallel.d.ts +3 -0
  49. package/dist/config/fully-parallel.js +6 -0
  50. package/dist/config/group-use.d.ts +8 -0
  51. package/dist/config/group-use.js +41 -0
  52. package/dist/config/resolved-config.d.ts +2 -0
  53. package/dist/config/resolved-config.js +3 -0
  54. package/dist/config/storage-state-file.d.ts +3 -0
  55. package/dist/config/storage-state-file.js +20 -0
  56. package/dist/config/use-options.d.ts +18 -0
  57. package/dist/config/use-options.js +184 -0
  58. package/dist/config/web-server-options.d.ts +7 -0
  59. package/dist/config/web-server-options.js +111 -0
  60. package/dist/locator/check-actions.js +1 -1
  61. package/dist/locator/drag-actions.d.ts +17 -0
  62. package/dist/locator/drag-actions.js +80 -0
  63. package/dist/locator/element-actions.d.ts +10 -4
  64. package/dist/locator/element-actions.js +23 -16
  65. package/dist/locator/element-state.d.ts +5 -0
  66. package/dist/locator/element-state.js +2 -1
  67. package/dist/locator/event-actions.d.ts +10 -0
  68. package/dist/locator/event-actions.js +81 -0
  69. package/dist/locator/input-actions.d.ts +2 -11
  70. package/dist/locator/input-actions.js +3 -56
  71. package/dist/locator/key-actions.d.ts +11 -0
  72. package/dist/locator/key-actions.js +52 -0
  73. package/dist/locator/key-map.d.ts +16 -0
  74. package/dist/locator/key-map.js +76 -0
  75. package/dist/locator/locator.d.ts +35 -5
  76. package/dist/locator/locator.js +70 -16
  77. package/dist/locator/page-scripts.d.ts +2 -0
  78. package/dist/locator/page-scripts.js +7 -0
  79. package/dist/locator/pointer-actions.d.ts +44 -0
  80. package/dist/locator/pointer-actions.js +117 -0
  81. package/dist/locator/pointer-element-actions.d.ts +21 -0
  82. package/dist/locator/pointer-element-actions.js +42 -0
  83. package/dist/logger/warn-once.d.ts +4 -0
  84. package/dist/logger/warn-once.js +13 -0
  85. package/dist/request/api-request-context.d.ts +65 -0
  86. package/dist/request/api-request-context.js +198 -0
  87. package/dist/request/api-response.d.ts +34 -0
  88. package/dist/request/api-response.js +57 -0
  89. package/dist/request/cookie-jar.d.ts +48 -0
  90. package/dist/request/cookie-jar.js +164 -0
  91. package/dist/request/http-transport.d.ts +31 -0
  92. package/dist/request/http-transport.js +113 -0
  93. package/dist/request/redirect.d.ts +10 -0
  94. package/dist/request/redirect.js +21 -0
  95. package/dist/request/request-body.d.ts +27 -0
  96. package/dist/request/request-body.js +71 -0
  97. package/dist/request/request.d.ts +16 -0
  98. package/dist/request/request.js +20 -0
  99. package/dist/runner/all-hooks.d.ts +7 -1
  100. package/dist/runner/all-hooks.js +17 -1
  101. package/dist/runner/attachments.d.ts +16 -0
  102. package/dist/runner/attachments.js +60 -0
  103. package/dist/runner/fixture-args.d.ts +15 -0
  104. package/dist/runner/fixture-args.js +95 -0
  105. package/dist/runner/fixture-defs.d.ts +22 -0
  106. package/dist/runner/fixture-defs.js +104 -0
  107. package/dist/runner/fixture-scope.d.ts +33 -0
  108. package/dist/runner/fixture-scope.js +156 -0
  109. package/dist/runner/fixtures.d.ts +37 -0
  110. package/dist/runner/fixtures.js +40 -0
  111. package/dist/runner/global-hooks.d.ts +13 -0
  112. package/dist/runner/global-hooks.js +66 -0
  113. package/dist/runner/hooks.d.ts +6 -1
  114. package/dist/runner/hooks.js +15 -3
  115. package/dist/runner/options.d.ts +13 -2
  116. package/dist/runner/output-tail.d.ts +14 -0
  117. package/dist/runner/output-tail.js +30 -0
  118. package/dist/runner/pool-schedule.d.ts +22 -0
  119. package/dist/runner/pool-schedule.js +55 -0
  120. package/dist/runner/pool-units.d.ts +34 -0
  121. package/dist/runner/pool-units.js +85 -0
  122. package/dist/runner/pooled-run.d.ts +7 -5
  123. package/dist/runner/pooled-run.js +19 -9
  124. package/dist/runner/registry.d.ts +3 -39
  125. package/dist/runner/registry.js +99 -21
  126. package/dist/runner/reporter.d.ts +17 -1
  127. package/dist/runner/reporter.js +15 -2
  128. package/dist/runner/request-fixture.d.ts +14 -0
  129. package/dist/runner/request-fixture.js +25 -0
  130. package/dist/runner/run-hooks.d.ts +16 -0
  131. package/dist/runner/run-hooks.js +52 -0
  132. package/dist/runner/run-test.d.ts +1 -3
  133. package/dist/runner/run-test.js +98 -35
  134. package/dist/runner/run.d.ts +1 -0
  135. package/dist/runner/run.js +1 -0
  136. package/dist/runner/select.d.ts +15 -4
  137. package/dist/runner/select.js +38 -12
  138. package/dist/runner/serial-run.d.ts +23 -0
  139. package/dist/runner/serial-run.js +88 -0
  140. package/dist/runner/serial.d.ts +34 -0
  141. package/dist/runner/serial.js +63 -0
  142. package/dist/runner/shard.d.ts +22 -0
  143. package/dist/runner/shard.js +73 -0
  144. package/dist/runner/steps.d.ts +13 -0
  145. package/dist/runner/steps.js +67 -0
  146. package/dist/runner/suite.d.ts +8 -3
  147. package/dist/runner/suite.js +12 -3
  148. package/dist/runner/test-annotations.d.ts +16 -0
  149. package/dist/runner/test-annotations.js +47 -0
  150. package/dist/runner/test-function.d.ts +115 -0
  151. package/dist/runner/test-function.js +1 -0
  152. package/dist/runner/test-info.d.ts +104 -0
  153. package/dist/runner/test-info.js +170 -0
  154. package/dist/runner/test-runner.d.ts +1 -0
  155. package/dist/runner/test-runner.js +64 -19
  156. package/dist/runner/test-timeout.d.ts +22 -0
  157. package/dist/runner/test-timeout.js +45 -0
  158. package/dist/runner/use-options.d.ts +7 -0
  159. package/dist/runner/use-options.js +25 -0
  160. package/dist/runner/wait-for-url.d.ts +20 -0
  161. package/dist/runner/wait-for-url.js +65 -0
  162. package/dist/runner/web-server.d.ts +16 -0
  163. package/dist/runner/web-server.js +165 -0
  164. package/dist/runner/worker-pool.d.ts +13 -5
  165. package/dist/runner/worker-pool.js +85 -51
  166. package/dist/runner/worker-protocol.d.ts +8 -0
  167. package/dist/runner/worker-session.js +5 -2
  168. package/dist/runner/worker.d.ts +2 -0
  169. package/dist/runner/worker.js +7 -1
  170. package/dist/script/element-handle.d.ts +1 -1
  171. package/dist/script/element-handle.js +1 -1
  172. package/dist/types/config.d.ts +86 -2
  173. package/dist/types/extend.d.ts +30 -0
  174. package/dist/types/test.d.ts +138 -0
  175. package/dist/types/use.d.ts +85 -0
  176. package/package.json +1 -1
  177. package/dist/browser/emulation.d.ts +0 -61
  178. package/dist/browser/emulation.js +0 -180
@@ -1,7 +1,10 @@
1
1
  import type { Browser } from "../browser/browser.js";
2
2
  import type Page from "../browser/page.js";
3
+ import type { BrowserContext } from "../browser/browser-context.js";
3
4
  import type Locator from "../locator/locator.js";
5
+ import type { APIRequestContext } from "../request/api-request-context.js";
4
6
  import type { SupportedBrowser } from "./browser.js";
7
+ import type { UseOptions } from "./use.js";
5
8
 
6
9
  /** What a test function receives */
7
10
  export interface TestFixtures {
@@ -19,6 +22,106 @@ export interface TestFixtures {
19
22
  inputs: Readonly<Record<string, string>>;
20
23
  /** Saves the text values the test declared as outputs */
21
24
  outputs: TestOutputs;
25
+ /** The browser context (cookies, storage) of `page`; same as `page.context()` */
26
+ context: BrowserContext;
27
+ /** The run's `baseURL` (the environment's, else the project's); `undefined` when none is set */
28
+ baseURL: string | undefined;
29
+ /**
30
+ * Makes HTTP requests without a browser: its own cookies, the run's `baseURL`, `use.extraHTTPHeaders` and
31
+ * `use.acceptInsecureCerts`. For requests that share the page's cookies use `page.request`.
32
+ */
33
+ request: APIRequestContext;
34
+ /** About the running test: its title, retry, attachments; same as `test.info()` */
35
+ testInfo: TestInfo;
36
+ /** Whether the page runs as a phone (`use.isMobile`); always false on Firefox, which cannot do it */
37
+ isMobile: boolean;
38
+ /** Whether the page has touch input (`use.hasTouch`); always false on Firefox, which cannot do it */
39
+ hasTouch: boolean;
40
+ }
41
+
42
+ /** A file or text a test attached to its result */
43
+ export interface TestAttachment {
44
+ name: string;
45
+ contentType: string;
46
+ /** Absolute path of the saved file */
47
+ path: string;
48
+ }
49
+
50
+ /** An attachment as the report lists it */
51
+ export interface TestAttachmentEntry {
52
+ name: string;
53
+ contentType: string;
54
+ /** Relative to the result folder */
55
+ path: string;
56
+ }
57
+
58
+ /** A note on a test (`{ type: "issue", description: "PROJ-1" }`) that the report lists */
59
+ export interface TestAnnotation {
60
+ type: string;
61
+ description?: string;
62
+ }
63
+
64
+ /** A step the test ran with `test.step`, as the report lists it */
65
+ export interface TestStepEntry {
66
+ title: string;
67
+ /** `"skipped"`: the step ended the test with `test.skip(...)` */
68
+ status: "passed" | "failed" | "skipped";
69
+ /** Milliseconds */
70
+ duration: number;
71
+ /** The message of what the step threw, or that the test ended before it finished */
72
+ error?: string;
73
+ /** The steps started inside this one; absent when there are none */
74
+ steps?: TestStepEntry[];
75
+ }
76
+
77
+ /** What `testInfo` and `test.info()` tell about the running test (hooks included) */
78
+ export interface TestInfo {
79
+ /** The test's own title */
80
+ readonly title: string;
81
+ /** The `describe` titles, then the title */
82
+ readonly titlePath: string[];
83
+ /** Absolute path of the spec file */
84
+ readonly file: string;
85
+ /** Where the test is written; absent when unknown */
86
+ readonly line?: number;
87
+ readonly column?: number;
88
+ /** The attempt, counting from 0: 1 is the first run again */
89
+ readonly retry: number;
90
+ readonly workerIndex: number;
91
+ /** The browser this run is in. There is no `projects` matrix yet, so `name` is the browser name */
92
+ readonly project: { name: string };
93
+ /** The run settings that apply to the test */
94
+ readonly config: { baseURL: string | undefined; timeout: number };
95
+ /** Push notes here; they are listed in the report entry */
96
+ annotations: TestAnnotation[];
97
+ /** Undefined while the test body runs; set before the `afterEach` hooks */
98
+ readonly status: "passed" | "failed" | "skipped" | undefined;
99
+ /** What the test is expected to end as: `"failed"` after `test.fail()`, else `"passed"` */
100
+ readonly expectedStatus: "passed" | "failed";
101
+ /** The time limit of the test in milliseconds (0: none); see `setTimeout` */
102
+ readonly timeout: number;
103
+ /** Sets the limit of this test, counted from its start; `0` removes it */
104
+ setTimeout(ms: number): void;
105
+ /** Triples the timeout, once, and adds a `slow` annotation; nothing happens when `condition` is false */
106
+ slow(condition?: boolean, description?: string): void;
107
+ /** Expects the test to fail (`expectedStatus` is `"failed"`) and adds a `fail` annotation */
108
+ fail(condition?: boolean, description?: string): void;
109
+ /** Ends the test as skipped when `condition` is not false */
110
+ skip(condition?: boolean, description?: string): void;
111
+ /** Ends the test as skipped (known to be broken) when `condition` is not false */
112
+ fixme(condition?: boolean, description?: string): void;
113
+ /** Milliseconds since the attempt started; fixed once the body is done */
114
+ readonly duration: number;
115
+ /** The errors of the test body or of its `beforeEach` hooks; filled before the `afterEach` hooks */
116
+ readonly errors: TestError[];
117
+ /** Folder for the files this attempt attaches (created when the first one is saved) */
118
+ readonly outputDir: string;
119
+ readonly attachments: readonly TestAttachment[];
120
+ /** Saves a file in `outputDir` and lists it in the report entry; give `body` or `path`, not both */
121
+ attach(
122
+ name: string,
123
+ options: { body?: string | Buffer; path?: string; contentType?: string },
124
+ ): Promise<void>;
22
125
  }
23
126
 
24
127
  /** The `outputs` fixture */
@@ -73,13 +176,25 @@ export interface Suite {
73
176
  afterEach?: EachHook[];
74
177
  beforeAll?: AllHook[];
75
178
  afterAll?: AllHook[];
179
+ /** `test.fail(true)` and `test.slow(true)` written in this scope: they apply to every test below */
180
+ marks?: { type: "fail" | "slow"; description?: string }[];
181
+ /** The `test.use()` calls written in this scope, in order; they apply to every test below */
182
+ use?: UseOptions[];
183
+ /** Set by `describe.configure({ mode })`, `describe.serial` or `describe.parallel`; absent: `"default"` */
184
+ mode?: SuiteMode;
76
185
  }
77
186
 
187
+ /** How the tests of a suite run: `"serial"` in order on one worker, the rest stops at the first failure */
188
+ export type SuiteMode = "default" | "parallel" | "serial";
189
+
78
190
  export interface RegisteredTestCase extends TestCaseBase {
79
191
  /** Path of the spec file that registered the test */
80
192
  file: string;
81
193
  /** describe titles, then the test title */
82
194
  titlePath: string[];
195
+ /** Where the test is written, when known */
196
+ line?: number;
197
+ column?: number;
83
198
  /** The `describe` block (or the file) the test is written in */
84
199
  suite: Suite;
85
200
  /** What applies to the test: its own modifier, or the one of a suite around it */
@@ -90,6 +205,12 @@ export interface RegisteredTestCase extends TestCaseBase {
90
205
  inputs?: Record<string, string>;
91
206
  /** The names of the outputs the test declared */
92
207
  outputs?: string[];
208
+ /** Registered with `test.fail("title", fn)`: expected to fail; `description` is the `{ reason }` given */
209
+ expectedFail?: { description?: string };
210
+ /** The custom fixtures of the `test` it was written with (`test.extend()`); absent for the plain `test` */
211
+ extension?: ReadonlyMap<string, readonly unknown[]>;
212
+ /** The group this run of the test is for, when groups were selected; the same test runs once per selected group */
213
+ group?: string;
93
214
  }
94
215
 
95
216
  export interface TestCaseBase {
@@ -118,6 +239,12 @@ interface TestResultSuccess extends TestResultIdentity, TestRetries {
118
239
  inputs?: Record<string, string>;
119
240
  /** The outputs the test saved; absent when it declares none */
120
241
  outputs?: Record<string, string>;
242
+ /** Files the test attached, relative to the result folder; absent when it attached none */
243
+ attachments?: TestAttachmentEntry[];
244
+ /** The notes the test pushed to `testInfo.annotations`; absent when there are none */
245
+ annotations?: TestAnnotation[];
246
+ /** The steps the test ran with `test.step`; absent when it ran none */
247
+ steps?: TestStepEntry[];
121
248
  }
122
249
 
123
250
  /** The error sits flat on the entry, as in the report: `message`, `type`, `stack`, and for assertions `expected` and `actual` */
@@ -132,6 +259,12 @@ interface TestResultFailed extends TestResultIdentity, TestError, TestRetries {
132
259
  inputs?: Record<string, string>;
133
260
  /** The outputs the test saved; absent when it declares none */
134
261
  outputs?: Record<string, string>;
262
+ /** Files the test attached, relative to the result folder; absent when it attached none */
263
+ attachments?: TestAttachmentEntry[];
264
+ /** The notes the test pushed to `testInfo.annotations`; absent when there are none */
265
+ annotations?: TestAnnotation[];
266
+ /** The steps the test ran with `test.step`; absent when it ran none */
267
+ steps?: TestStepEntry[];
135
268
  }
136
269
 
137
270
  /** What a result says about the retries of its test; absent when it passed or failed on the first attempt */
@@ -168,6 +301,8 @@ interface TestResultIdentity {
168
301
  file: string;
169
302
  /** The browser the test ran in */
170
303
  browser: SupportedBrowser;
304
+ /** The group the test ran for, when the run selected groups (`--group`); absent otherwise */
305
+ group?: string;
171
306
  /** The worker process that ran the test; absent when the test ran in the main process */
172
307
  worker?: number;
173
308
  startTime: number;
@@ -177,6 +312,7 @@ interface TestResultBase {
177
312
  name: string;
178
313
  file: string;
179
314
  browser: SupportedBrowser;
315
+ group?: string;
180
316
  worker?: number;
181
317
  startTime: number;
182
318
  status: "started";
@@ -211,4 +347,6 @@ interface TestSummaryBase {
211
347
  environment: string;
212
348
  /** The browsers the run used, in the order they ran */
213
349
  browsers: SupportedBrowser[];
350
+ /** The slice of the selected tests the run held (`--shard`); absent for a whole run */
351
+ shard?: { current: number; total: number };
214
352
  }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * How the page looks to the site, and what the browser starts with. One vocabulary for every level: config `use`,
3
+ * a group's `use`, `test.use()` (fixed before the test starts) and `page.use()` / `context.use()` (applied now).
4
+ * At run time `null` puts the real value back; in config a later layer's `null` cancels an earlier value.
5
+ */
6
+ export interface UseOptions {
7
+ /** A BCP 47 tag, e.g. "nl-NL": `navigator.language`, `Intl` formatting and the `Accept-Language` header */
8
+ locale?: string | null;
9
+ /** An IANA timezone, e.g. "Europe/Amsterdam" */
10
+ timezone?: string | null;
11
+ /** The `User-Agent` header and `navigator.userAgent` */
12
+ userAgent?: string | null;
13
+ /** Needs the geolocation permission: see `page.grantPermission("geolocation")` */
14
+ geolocation?: {
15
+ latitude: number;
16
+ longitude: number;
17
+ accuracy?: number;
18
+ } | null;
19
+ /** `true`: no network (`navigator.onLine` is false and requests fail); `false`: back online */
20
+ offline?: boolean | null;
21
+ /** "portrait" and "landscape" mean the primary orientation of that kind */
22
+ orientation?: Orientation | null;
23
+ /** The size `screen.width` and `screen.height` report */
24
+ screen?: { width: number; height: number } | null;
25
+ /** `false` runs pages without JavaScript */
26
+ javaScriptEnabled?: boolean | null;
27
+ /** Touch input: `navigator.maxTouchPoints` is 1. Chrome only. */
28
+ hasTouch?: boolean | null;
29
+ /** Accept certificates that are self-signed, expired or for another host. Fixed when the browser starts. */
30
+ acceptInsecureCerts?: boolean;
31
+ /** A phone-like browser (the layout viewport follows the page's `<meta name="viewport">`). Chrome only. */
32
+ isMobile?: boolean;
33
+ /** Cookies and local storage to start with: a JSON file (relative to the project folder) or the object itself */
34
+ storageState?: string | StorageState;
35
+ /** Headers added to every request of the page */
36
+ extraHTTPHeaders?: Record<string, string>;
37
+ /** What `prefers-color-scheme` reports */
38
+ colorScheme?: ColorScheme;
39
+ }
40
+
41
+ /** The options `page.use()` and `context.use()` take: those that can change while the browser runs */
42
+ export type RuntimeUseOptions = Pick<
43
+ UseOptions,
44
+ | "locale"
45
+ | "timezone"
46
+ | "userAgent"
47
+ | "geolocation"
48
+ | "offline"
49
+ | "orientation"
50
+ | "screen"
51
+ | "javaScriptEnabled"
52
+ | "hasTouch"
53
+ >;
54
+
55
+ export type Orientation =
56
+ | "portrait"
57
+ | "landscape"
58
+ | "portrait-primary"
59
+ | "portrait-secondary"
60
+ | "landscape-primary"
61
+ | "landscape-secondary";
62
+
63
+ export type ColorScheme = "light" | "dark" | "no-preference";
64
+
65
+ /** Cookies and local storage, in the format Playwright's `storageState` files use */
66
+ export interface StorageState {
67
+ cookies?: StorageStateCookie[];
68
+ origins?: {
69
+ origin: string;
70
+ localStorage: { name: string; value: string }[];
71
+ }[];
72
+ }
73
+
74
+ export interface StorageStateCookie {
75
+ name: string;
76
+ value: string;
77
+ domain: string;
78
+ /** @default "/" */
79
+ path?: string;
80
+ /** Seconds since the epoch; -1 or left out is a session cookie */
81
+ expires?: number;
82
+ httpOnly?: boolean;
83
+ secure?: boolean;
84
+ sameSite?: "Strict" | "Lax" | "None";
85
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itmetsam/samurai-framework",
3
- "version": "0.21.4",
3
+ "version": "0.22.0",
4
4
  "description": "Browser test framework over WebDriver BiDi: auto-waiting locators, environments and secrets, a recorder, and a command line.",
5
5
  "main": "dist/api.js",
6
6
  "exports": {
@@ -1,61 +0,0 @@
1
- import { UnsupportedOperationError } from "../transport/unsupported.js";
2
- import type { BiDiConnector } from "../transport/bidi-connection.js";
3
- declare const ORIENTATIONS: readonly ["portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary"];
4
- type OrientationType = (typeof ORIENTATIONS)[number];
5
- /**
6
- * What to pretend about the browser. `undefined` leaves an option as it is, `null` puts the real value back.
7
- * Which options a browser supports differs: an unsupported one throws UnsupportedOperationError.
8
- */
9
- export interface EmulationOptions {
10
- /** A BCP 47 tag, e.g. "nl-NL": `navigator.language`, `Intl` formatting */
11
- locale?: string | null;
12
- /** An IANA timezone, e.g. "Europe/Amsterdam" */
13
- timezone?: string | null;
14
- userAgent?: string | null;
15
- /** Needs the geolocation permission: see `page.grantPermission("geolocation")` */
16
- geolocation?: {
17
- latitude: number;
18
- longitude: number;
19
- accuracy?: number;
20
- } | null;
21
- /** `true`: no network (`navigator.onLine` is false and requests fail); `false` or `null`: back online */
22
- offline?: boolean | null;
23
- /** "portrait" and "landscape" mean the primary orientation of that kind */
24
- orientation?: "portrait" | "landscape" | OrientationType | null;
25
- /** The size `screen.width` and `screen.height` report */
26
- screen?: {
27
- width: number;
28
- height: number;
29
- } | null;
30
- /** How many touch points `navigator.maxTouchPoints` reports */
31
- touch?: number | null;
32
- /** `false` turns JavaScript off; `true` or `null` on again */
33
- javaScriptEnabled?: boolean | null;
34
- }
35
- export type EmulationTarget = {
36
- contexts: string[];
37
- } | {
38
- userContexts: string[];
39
- };
40
- /** A part of the options could not be applied; what was applied before it stays applied */
41
- export declare class EmulationError extends Error {
42
- /** The options that were applied before the failure, in the order they were applied */
43
- readonly applied: string[];
44
- readonly failed: string;
45
- readonly cause: unknown;
46
- constructor(failed: string, applied: string[], cause: unknown);
47
- }
48
- /** The browser does not implement an emulation command */
49
- export declare class EmulationUnsupportedError extends UnsupportedOperationError {
50
- readonly option: string;
51
- readonly applied: string[];
52
- constructor(option: string, applied: string[]);
53
- }
54
- /** Throws RangeError when a value is invalid, without sending anything */
55
- export declare function validateEmulation(options: EmulationOptions): void;
56
- /**
57
- * Applies the options to `target`, one command after the other. Nothing is rolled back when one fails:
58
- * the error says what was applied.
59
- */
60
- export declare function applyEmulation(connector: BiDiConnector, target: EmulationTarget, options: EmulationOptions): Promise<void>;
61
- export {};
@@ -1,180 +0,0 @@
1
- import { isUnsupported, UnsupportedOperationError, } from "../transport/unsupported.js";
2
- const ORIENTATIONS = [
3
- "portrait-primary",
4
- "portrait-secondary",
5
- "landscape-primary",
6
- "landscape-secondary",
7
- ];
8
- /** A part of the options could not be applied; what was applied before it stays applied */
9
- export class EmulationError extends Error {
10
- constructor(failed, applied, cause) {
11
- const detail = cause instanceof Error ? cause.message : String(cause);
12
- super(`emulate(): ${failed} failed${applied.length ? ` after applying ${applied.join(", ")}` : ""}: ${detail}`);
13
- this.name = "EmulationError";
14
- this.failed = failed;
15
- this.applied = applied;
16
- this.cause = cause;
17
- }
18
- }
19
- /** The browser does not implement an emulation command */
20
- export class EmulationUnsupportedError extends UnsupportedOperationError {
21
- constructor(option, applied) {
22
- super(`emulate(): ${option} is not supported by the browser`);
23
- this.name = "EmulationUnsupportedError";
24
- this.option = option;
25
- this.applied = applied;
26
- }
27
- }
28
- const isNumber = (value) => typeof value === "number" && Number.isFinite(value);
29
- const isPositiveInteger = (value) => Number.isInteger(value) && value > 0;
30
- /** Turns the options into commands, in a fixed order; throws RangeError for an invalid value, before anything is sent */
31
- function plan(options) {
32
- const steps = [];
33
- if (options.locale !== undefined) {
34
- if (options.locale !== null) {
35
- try {
36
- if (options.locale === "" ||
37
- Intl.getCanonicalLocales(options.locale).length !== 1)
38
- throw new RangeError();
39
- }
40
- catch {
41
- throw new RangeError(`emulate(): invalid locale "${options.locale}"`);
42
- }
43
- }
44
- steps.push({
45
- option: "locale",
46
- method: "emulation.setLocaleOverride",
47
- params: { locale: options.locale },
48
- });
49
- }
50
- if (options.timezone !== undefined) {
51
- if (options.timezone !== null) {
52
- try {
53
- new Intl.DateTimeFormat(undefined, { timeZone: options.timezone });
54
- }
55
- catch {
56
- throw new RangeError(`emulate(): unknown timezone "${options.timezone}"`);
57
- }
58
- }
59
- steps.push({
60
- option: "timezone",
61
- method: "emulation.setTimezoneOverride",
62
- params: { timezone: options.timezone },
63
- });
64
- }
65
- if (options.userAgent !== undefined) {
66
- if (options.userAgent === "")
67
- throw new RangeError("emulate(): userAgent must not be empty");
68
- steps.push({
69
- option: "userAgent",
70
- method: "emulation.setUserAgentOverride",
71
- params: { userAgent: options.userAgent },
72
- });
73
- }
74
- if (options.geolocation !== undefined) {
75
- const place = options.geolocation;
76
- if (place !== null) {
77
- if (!isNumber(place.latitude) ||
78
- place.latitude < -90 ||
79
- place.latitude > 90) {
80
- throw new RangeError("emulate(): latitude must be between -90 and 90");
81
- }
82
- if (!isNumber(place.longitude) ||
83
- place.longitude < -180 ||
84
- place.longitude > 180) {
85
- throw new RangeError("emulate(): longitude must be between -180 and 180");
86
- }
87
- if (place.accuracy !== undefined &&
88
- (!isNumber(place.accuracy) || place.accuracy < 0)) {
89
- throw new RangeError("emulate(): accuracy must be 0 or more");
90
- }
91
- }
92
- steps.push({
93
- option: "geolocation",
94
- method: "emulation.setGeolocationOverride",
95
- params: { coordinates: place },
96
- });
97
- }
98
- if (options.offline !== undefined) {
99
- steps.push({
100
- option: "offline",
101
- method: "emulation.setNetworkConditions",
102
- params: {
103
- networkConditions: options.offline ? { type: "offline" } : null,
104
- },
105
- });
106
- }
107
- if (options.orientation !== undefined) {
108
- let screenOrientation = null;
109
- if (options.orientation !== null) {
110
- const type = (options.orientation === "portrait" ||
111
- options.orientation === "landscape"
112
- ? `${options.orientation}-primary`
113
- : options.orientation);
114
- if (!ORIENTATIONS.includes(type))
115
- throw new RangeError(`emulate(): unknown orientation "${options.orientation}"`);
116
- screenOrientation = {
117
- natural: type.startsWith("portrait") ? "portrait" : "landscape",
118
- type,
119
- };
120
- }
121
- steps.push({
122
- option: "orientation",
123
- method: "emulation.setScreenOrientationOverride",
124
- params: { screenOrientation },
125
- });
126
- }
127
- if (options.screen !== undefined) {
128
- const area = options.screen;
129
- if (area !== null &&
130
- !(isPositiveInteger(area.width) && isPositiveInteger(area.height))) {
131
- throw new RangeError("emulate(): screen width and height must be positive integers");
132
- }
133
- steps.push({
134
- option: "screen",
135
- method: "emulation.setScreenSettingsOverride",
136
- params: { screenArea: area },
137
- });
138
- }
139
- if (options.touch !== undefined) {
140
- if (options.touch !== null && !isPositiveInteger(options.touch)) {
141
- throw new RangeError("emulate(): touch must be a positive integer");
142
- }
143
- steps.push({
144
- option: "touch",
145
- method: "emulation.setTouchOverride",
146
- params: { maxTouchPoints: options.touch },
147
- });
148
- }
149
- if (options.javaScriptEnabled !== undefined) {
150
- steps.push({
151
- option: "javaScriptEnabled",
152
- method: "emulation.setScriptingEnabled",
153
- params: { enabled: options.javaScriptEnabled === false ? false : null },
154
- });
155
- }
156
- return steps;
157
- }
158
- /** Throws RangeError when a value is invalid, without sending anything */
159
- export function validateEmulation(options) {
160
- plan(options);
161
- }
162
- /**
163
- * Applies the options to `target`, one command after the other. Nothing is rolled back when one fails:
164
- * the error says what was applied.
165
- */
166
- export async function applyEmulation(connector, target, options) {
167
- const applied = [];
168
- for (const step of plan(options)) {
169
- try {
170
- await connector.send(step.method, { ...step.params, ...target });
171
- }
172
- catch (err) {
173
- if (isUnsupported(err)) {
174
- throw new EmulationUnsupportedError(step.option, applied);
175
- }
176
- throw new EmulationError(step.option, applied, err);
177
- }
178
- applied.push(step.option);
179
- }
180
- }