@haibun/web-playwright 1.59.0 → 1.60.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 (181) hide show
  1. package/build/cycles.d.ts +4 -0
  2. package/build/cycles.d.ts.map +1 -0
  3. package/build/cycles.js +83 -0
  4. package/build/cycles.js.map +1 -0
  5. package/build/interactionSteps.d.ts +221 -0
  6. package/build/interactionSteps.d.ts.map +1 -0
  7. package/build/interactionSteps.js +515 -0
  8. package/build/interactionSteps.js.map +1 -0
  9. package/build/monitor/LogEntry.d.ts +4 -0
  10. package/build/monitor/LogEntry.d.ts.map +1 -0
  11. package/build/monitor/LogEntry.js +4 -0
  12. package/build/monitor/LogEntry.js.map +1 -0
  13. package/build/monitor/MonitorHandler.d.ts.map +1 -1
  14. package/build/monitor/MonitorHandler.js +24 -46
  15. package/build/monitor/MonitorHandler.js.map +1 -1
  16. package/build/monitor/artifactDisplayBase.d.ts +2 -0
  17. package/build/monitor/artifactDisplayBase.d.ts.map +1 -0
  18. package/build/monitor/artifactDisplayBase.js +2 -0
  19. package/build/monitor/artifactDisplayBase.js.map +1 -0
  20. package/build/monitor/artifactDisplays/HtmlArtifactDisplay.d.ts +9 -0
  21. package/build/monitor/artifactDisplays/HtmlArtifactDisplay.d.ts.map +1 -0
  22. package/build/monitor/artifactDisplays/HtmlArtifactDisplay.js +31 -0
  23. package/build/monitor/artifactDisplays/HtmlArtifactDisplay.js.map +1 -0
  24. package/build/monitor/artifactDisplays/ImageArtifactDisplay.d.ts +9 -0
  25. package/build/monitor/artifactDisplays/ImageArtifactDisplay.d.ts.map +1 -0
  26. package/build/monitor/artifactDisplays/ImageArtifactDisplay.js +17 -0
  27. package/build/monitor/artifactDisplays/ImageArtifactDisplay.js.map +1 -0
  28. package/build/monitor/artifactDisplays/JsonArtifactDisplay.d.ts +9 -0
  29. package/build/monitor/artifactDisplays/JsonArtifactDisplay.d.ts.map +1 -0
  30. package/build/monitor/artifactDisplays/JsonArtifactDisplay.js +18 -0
  31. package/build/monitor/artifactDisplays/JsonArtifactDisplay.js.map +1 -0
  32. package/build/monitor/artifactDisplays/JsonArtifactHTTPTrace.d.ts +14 -0
  33. package/build/monitor/artifactDisplays/JsonArtifactHTTPTrace.d.ts.map +1 -0
  34. package/build/monitor/artifactDisplays/JsonArtifactHTTPTrace.js +69 -0
  35. package/build/monitor/artifactDisplays/JsonArtifactHTTPTrace.js.map +1 -0
  36. package/build/monitor/artifactDisplays/MermaidDiagram.d.ts +22 -0
  37. package/build/monitor/artifactDisplays/MermaidDiagram.d.ts.map +1 -0
  38. package/build/monitor/artifactDisplays/MermaidDiagram.js +199 -0
  39. package/build/monitor/artifactDisplays/MermaidDiagram.js.map +1 -0
  40. package/build/monitor/artifactDisplays/ResolvedFeaturesArtifactDisplay.d.ts +9 -0
  41. package/build/monitor/artifactDisplays/ResolvedFeaturesArtifactDisplay.d.ts.map +1 -0
  42. package/build/monitor/artifactDisplays/ResolvedFeaturesArtifactDisplay.js +38 -0
  43. package/build/monitor/artifactDisplays/ResolvedFeaturesArtifactDisplay.js.map +1 -0
  44. package/build/monitor/artifactDisplays/SequenceDiagramGenerator.d.ts +32 -0
  45. package/build/monitor/artifactDisplays/SequenceDiagramGenerator.d.ts.map +1 -0
  46. package/build/monitor/artifactDisplays/SequenceDiagramGenerator.js +258 -0
  47. package/build/monitor/artifactDisplays/SequenceDiagramGenerator.js.map +1 -0
  48. package/build/monitor/artifactDisplays/SpeechArtifactDisplay.d.ts +9 -0
  49. package/build/monitor/artifactDisplays/SpeechArtifactDisplay.d.ts.map +1 -0
  50. package/build/monitor/artifactDisplays/SpeechArtifactDisplay.js +19 -0
  51. package/build/monitor/artifactDisplays/SpeechArtifactDisplay.js.map +1 -0
  52. package/build/monitor/artifactDisplays/VideoArtifactDisplay.d.ts +9 -0
  53. package/build/monitor/artifactDisplays/VideoArtifactDisplay.d.ts.map +1 -0
  54. package/build/monitor/artifactDisplays/VideoArtifactDisplay.js +21 -0
  55. package/build/monitor/artifactDisplays/VideoArtifactDisplay.js.map +1 -0
  56. package/build/monitor/artifactDisplays/VideoStartArtifactDisplay.d.ts +9 -0
  57. package/build/monitor/artifactDisplays/VideoStartArtifactDisplay.d.ts.map +1 -0
  58. package/build/monitor/artifactDisplays/VideoStartArtifactDisplay.js +17 -0
  59. package/build/monitor/artifactDisplays/VideoStartArtifactDisplay.js.map +1 -0
  60. package/build/monitor/artifactDisplays/artifactDisplayBase.d.ts +20 -0
  61. package/build/monitor/artifactDisplays/artifactDisplayBase.d.ts.map +1 -0
  62. package/build/monitor/artifactDisplays/artifactDisplayBase.js +40 -0
  63. package/build/monitor/artifactDisplays/artifactDisplayBase.js.map +1 -0
  64. package/build/monitor/artifactDisplays/htmlArtifactDisplay.d.ts +9 -0
  65. package/build/monitor/artifactDisplays/htmlArtifactDisplay.d.ts.map +1 -0
  66. package/build/monitor/artifactDisplays/htmlArtifactDisplay.js +31 -0
  67. package/build/monitor/artifactDisplays/htmlArtifactDisplay.js.map +1 -0
  68. package/build/monitor/artifactDisplays/imageArtifactDisplay.d.ts +9 -0
  69. package/build/monitor/artifactDisplays/imageArtifactDisplay.d.ts.map +1 -0
  70. package/build/monitor/artifactDisplays/imageArtifactDisplay.js +17 -0
  71. package/build/monitor/artifactDisplays/imageArtifactDisplay.js.map +1 -0
  72. package/build/monitor/artifactDisplays/jsonArtifactDisplay.d.ts +9 -0
  73. package/build/monitor/artifactDisplays/jsonArtifactDisplay.d.ts.map +1 -0
  74. package/build/monitor/artifactDisplays/jsonArtifactDisplay.js +18 -0
  75. package/build/monitor/artifactDisplays/jsonArtifactDisplay.js.map +1 -0
  76. package/build/monitor/artifactDisplays/jsonArtifactHTTPTrace.d.ts +14 -0
  77. package/build/monitor/artifactDisplays/jsonArtifactHTTPTrace.d.ts.map +1 -0
  78. package/build/monitor/artifactDisplays/jsonArtifactHTTPTrace.js +72 -0
  79. package/build/monitor/artifactDisplays/jsonArtifactHTTPTrace.js.map +1 -0
  80. package/build/monitor/artifactDisplays/mermaidDiagram.d.ts +22 -0
  81. package/build/monitor/artifactDisplays/mermaidDiagram.d.ts.map +1 -0
  82. package/build/monitor/artifactDisplays/mermaidDiagram.js +199 -0
  83. package/build/monitor/artifactDisplays/mermaidDiagram.js.map +1 -0
  84. package/build/monitor/artifactDisplays/resolvedFeaturesArtifactDisplay.d.ts +9 -0
  85. package/build/monitor/artifactDisplays/resolvedFeaturesArtifactDisplay.d.ts.map +1 -0
  86. package/build/monitor/artifactDisplays/resolvedFeaturesArtifactDisplay.js +36 -0
  87. package/build/monitor/artifactDisplays/resolvedFeaturesArtifactDisplay.js.map +1 -0
  88. package/build/monitor/artifactDisplays/sequenceDiagramGenerator.d.ts +32 -0
  89. package/build/monitor/artifactDisplays/sequenceDiagramGenerator.d.ts.map +1 -0
  90. package/build/monitor/artifactDisplays/sequenceDiagramGenerator.js +299 -0
  91. package/build/monitor/artifactDisplays/sequenceDiagramGenerator.js.map +1 -0
  92. package/build/monitor/artifactDisplays/speechArtifactDisplay.d.ts +9 -0
  93. package/build/monitor/artifactDisplays/speechArtifactDisplay.d.ts.map +1 -0
  94. package/build/monitor/artifactDisplays/speechArtifactDisplay.js +38 -0
  95. package/build/monitor/artifactDisplays/speechArtifactDisplay.js.map +1 -0
  96. package/build/monitor/artifactDisplays/videoArtifactDisplay.d.ts +9 -0
  97. package/build/monitor/artifactDisplays/videoArtifactDisplay.d.ts.map +1 -0
  98. package/build/monitor/artifactDisplays/videoArtifactDisplay.js +22 -0
  99. package/build/monitor/artifactDisplays/videoArtifactDisplay.js.map +1 -0
  100. package/build/monitor/artifactDisplays/videoStartArtifactDisplay.d.ts +9 -0
  101. package/build/monitor/artifactDisplays/videoStartArtifactDisplay.d.ts.map +1 -0
  102. package/build/monitor/artifactDisplays/videoStartArtifactDisplay.js +30 -0
  103. package/build/monitor/artifactDisplays/videoStartArtifactDisplay.js.map +1 -0
  104. package/build/monitor/graph/feature-bases.d.ts +5 -0
  105. package/build/monitor/graph/feature-bases.d.ts.map +1 -0
  106. package/build/monitor/graph/feature-bases.js +36 -0
  107. package/build/monitor/graph/feature-bases.js.map +1 -0
  108. package/build/monitor/graph/generateMermaidGraph.d.ts +4 -0
  109. package/build/monitor/graph/generateMermaidGraph.d.ts.map +1 -0
  110. package/build/monitor/graph/generateMermaidGraph.js +142 -0
  111. package/build/monitor/graph/generateMermaidGraph.js.map +1 -0
  112. package/build/monitor/graph/graphUtils.d.ts +3 -0
  113. package/build/monitor/graph/graphUtils.d.ts.map +1 -0
  114. package/build/monitor/graph/graphUtils.js +16 -0
  115. package/build/monitor/graph/graphUtils.js.map +1 -0
  116. package/build/monitor/graph/mermaidGraphLinks.d.ts +3 -0
  117. package/build/monitor/graph/mermaidGraphLinks.d.ts.map +1 -0
  118. package/build/monitor/graph/mermaidGraphLinks.js +31 -0
  119. package/build/monitor/graph/mermaidGraphLinks.js.map +1 -0
  120. package/build/monitor/htmlArtifactDisplay.d.ts +2 -0
  121. package/build/monitor/htmlArtifactDisplay.d.ts.map +1 -0
  122. package/build/monitor/htmlArtifactDisplay.js +2 -0
  123. package/build/monitor/htmlArtifactDisplay.js.map +1 -0
  124. package/build/monitor/imageArtifactDisplay.d.ts +2 -0
  125. package/build/monitor/imageArtifactDisplay.d.ts.map +1 -0
  126. package/build/monitor/imageArtifactDisplay.js +2 -0
  127. package/build/monitor/imageArtifactDisplay.js.map +1 -0
  128. package/build/monitor/jsonArtifactDisplay.d.ts +2 -0
  129. package/build/monitor/jsonArtifactDisplay.d.ts.map +1 -0
  130. package/build/monitor/jsonArtifactDisplay.js +2 -0
  131. package/build/monitor/jsonArtifactDisplay.js.map +1 -0
  132. package/build/monitor/jsonArtifactHTTPTrace.d.ts +2 -0
  133. package/build/monitor/jsonArtifactHTTPTrace.d.ts.map +1 -0
  134. package/build/monitor/jsonArtifactHTTPTrace.js +2 -0
  135. package/build/monitor/jsonArtifactHTTPTrace.js.map +1 -0
  136. package/build/monitor/messages.d.ts +9 -21
  137. package/build/monitor/messages.d.ts.map +1 -1
  138. package/build/monitor/messages.js +83 -216
  139. package/build/monitor/messages.js.map +1 -1
  140. package/build/monitor/monitor.d.ts +9 -4
  141. package/build/monitor/monitor.d.ts.map +1 -1
  142. package/build/monitor/monitor.js +8 -18
  143. package/build/monitor/monitor.js.map +1 -1
  144. package/build/monitor/resolvedFeaturesArtifactDisplay.d.ts +2 -0
  145. package/build/monitor/resolvedFeaturesArtifactDisplay.d.ts.map +1 -0
  146. package/build/monitor/resolvedFeaturesArtifactDisplay.js +2 -0
  147. package/build/monitor/resolvedFeaturesArtifactDisplay.js.map +1 -0
  148. package/build/monitor/sequenceDiagramGenerator.d.ts +32 -0
  149. package/build/monitor/sequenceDiagramGenerator.d.ts.map +1 -0
  150. package/build/monitor/sequenceDiagramGenerator.js +299 -0
  151. package/build/monitor/sequenceDiagramGenerator.js.map +1 -0
  152. package/build/monitor/speechArtifactDisplay.d.ts +2 -0
  153. package/build/monitor/speechArtifactDisplay.d.ts.map +1 -0
  154. package/build/monitor/speechArtifactDisplay.js +2 -0
  155. package/build/monitor/speechArtifactDisplay.js.map +1 -0
  156. package/build/monitor/test-utils.d.ts +9 -0
  157. package/build/monitor/test-utils.d.ts.map +1 -0
  158. package/build/monitor/test-utils.js +70 -0
  159. package/build/monitor/test-utils.js.map +1 -0
  160. package/build/monitor/videoArtifactDisplay.d.ts +2 -0
  161. package/build/monitor/videoArtifactDisplay.d.ts.map +1 -0
  162. package/build/monitor/videoArtifactDisplay.js +2 -0
  163. package/build/monitor/videoArtifactDisplay.js.map +1 -0
  164. package/build/monitor/videoStartArtifactDisplay.d.ts +2 -0
  165. package/build/monitor/videoStartArtifactDisplay.d.ts.map +1 -0
  166. package/build/monitor/videoStartArtifactDisplay.js +2 -0
  167. package/build/monitor/videoStartArtifactDisplay.js.map +1 -0
  168. package/build/web-playwright.d.ts +78 -77
  169. package/build/web-playwright.d.ts.map +1 -1
  170. package/build/web-playwright.js +12 -571
  171. package/build/web-playwright.js.map +1 -1
  172. package/package.json +9 -5
  173. package/web/monitor.html +261 -260
  174. package/build/monitor/monitorHandler.d.ts +0 -16
  175. package/build/monitor/monitorHandler.d.ts.map +0 -1
  176. package/build/monitor/monitorHandler.js +0 -108
  177. package/build/monitor/monitorHandler.js.map +0 -1
  178. package/build/monitor/vite.config.d.ts +0 -4
  179. package/build/monitor/vite.config.d.ts.map +0 -1
  180. package/build/monitor/vite.config.js +0 -91
  181. package/build/monitor/vite.config.js.map +0 -1
@@ -3,78 +3,27 @@ import { pathToFileURL } from 'url';
3
3
  import { OK } from '@haibun/core/build/lib/defs.js';
4
4
  import { WEB_PAGE, WEB_CONTROL } from '@haibun/core/build/lib/domain-types.js';
5
5
  import { BrowserFactory, BROWSERS } from './BrowserFactory.js';
6
- import { actionNotOK, getStepperOption, boolOrError, intOrError, stringOrError, findStepperFromOption, sleep, optionOrError } from '@haibun/core/build/lib/util/index.js';
6
+ import { actionNotOK, getStepperOption, boolOrError, intOrError, stringOrError, findStepperFromOption, optionOrError } from '@haibun/core/build/lib/util/index.js';
7
7
  import { EExecutionMessageType } from '@haibun/core/build/lib/interfaces/logger.js';
8
8
  import { EMediaTypes } from '@haibun/domain-storage/build/media-types.js';
9
- import { restSteps } from './rest-playwright.js';
10
9
  import { MonitorHandler } from './monitor/MonitorHandler.js';
11
- import { rmSync } from 'fs';
12
10
  import { AStepper } from '@haibun/core/build/lib/astepper.js';
11
+ import { cycles } from './cycles.js';
12
+ import { interactionSteps } from './interactionSteps.js';
13
+ import { restSteps } from './rest-playwright.js';
14
+ /**
15
+ * This is the infrastructure for web-playwright.
16
+ *
17
+ * @see {@link interactionSteps} for interaction steps
18
+ * @see {@link restSteps} for rest steps
19
+ */
13
20
  export const LAST_REST_RESPONSE = 'LAST_REST_RESPONSE';
14
21
  export var EMonitoringTypes;
15
22
  (function (EMonitoringTypes) {
16
23
  EMonitoringTypes["MONITOR_ALL"] = "all";
17
24
  EMonitoringTypes["MONITOR_EACH"] = "each";
18
25
  })(EMonitoringTypes || (EMonitoringTypes = {}));
19
- const cycles = (wp) => ({
20
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
21
- async onFailure(result, step) {
22
- if (wp.bf?.hasPage(wp.getWorld().tag, wp.tab)) {
23
- await wp.captureFailureScreenshot(EExecutionMessageType.ON_FAILURE, step);
24
- }
25
- },
26
- async startFeature() {
27
- if (wp.monitor === EMonitoringTypes.MONITOR_EACH) {
28
- await wp.createMonitor();
29
- }
30
- },
31
- async endFeature({ shouldClose = true }) {
32
- // leave web server running if there was a failure and it's the last feature
33
- if (shouldClose) {
34
- for (const file of wp.downloaded) {
35
- wp.getWorld().logger.debug(`removing ${JSON.stringify(file)}`);
36
- rmSync(file);
37
- wp.downloaded = [];
38
- }
39
- if (wp.hasFactory) {
40
- if (wp.captureVideo) {
41
- const page = await wp.getPage();
42
- const path = await wp.storage.getRelativePath(await page.video().path());
43
- const artifact = { artifactType: 'video', path };
44
- const context = {
45
- incident: EExecutionMessageType.FEATURE_END,
46
- artifact,
47
- tag: wp.getWorld().tag
48
- };
49
- wp.getWorld().logger.log('feature video', context);
50
- }
51
- // close the context, which closes any pages
52
- if (wp.hasFactory) {
53
- await wp.bf?.closeContext(wp.getWorld().tag);
54
- }
55
- await wp.bf?.close();
56
- wp.bf = undefined;
57
- wp.hasFactory = false;
58
- }
59
- }
60
- if (wp.monitor === EMonitoringTypes.MONITOR_EACH) {
61
- await wp.callClosers();
62
- await WebPlaywright.monitorHandler.writeMonitor();
63
- }
64
- },
65
- async startExecution() {
66
- if (wp.monitor === EMonitoringTypes.MONITOR_ALL) {
67
- await wp.createMonitor();
68
- }
69
- },
70
- async endExecution() {
71
- if (wp.monitor === EMonitoringTypes.MONITOR_ALL) {
72
- await wp.callClosers();
73
- await WebPlaywright.monitorHandler.writeMonitor();
74
- }
75
- },
76
- });
77
- class WebPlaywright extends AStepper {
26
+ export class WebPlaywright extends AStepper {
78
27
  cycles = cycles(this);
79
28
  static STORAGE = 'STORAGE';
80
29
  static PERSISTENT_DIRECTORY = 'PERSISTENT_DIRECTORY';
@@ -124,13 +73,10 @@ class WebPlaywright extends AStepper {
124
73
  downloaded = [];
125
74
  captureVideo;
126
75
  closers = [];
127
- logElementError;
128
76
  monitor;
129
77
  static monitorHandler;
130
- userAgentPages = {};
131
78
  apiUserAgent;
132
79
  extraHTTPHeaders = {};
133
- BROWSER_STATE_PATH = undefined;
134
80
  expectedDownload;
135
81
  headless;
136
82
  async setWorld(world, steppers) {
@@ -216,512 +162,7 @@ class WebPlaywright extends AStepper {
216
162
  }
217
163
  steps = {
218
164
  ...restSteps(this),
219
- openDevTools: {
220
- gwta: `open devtools`,
221
- action: async () => {
222
- await this.withPage(async (page) => {
223
- await page.goto('about:blank');
224
- await sleep(2000);
225
- const targetId = await fetch('http://localhost:9223/json/list');
226
- await page.goto(`devtools://devtools/bundled/inspector.html?ws=localhost:9223/devtools/page/${targetId}&panel=network`);
227
- });
228
- return OK;
229
- },
230
- },
231
- // INPUT
232
- press: {
233
- gwta: `press {key}`,
234
- action: async ({ key }) => {
235
- await this.withPage(async (page) => await page.keyboard.press(key));
236
- return OK;
237
- },
238
- },
239
- type: {
240
- gwta: `type {text}`,
241
- action: async ({ text }) => {
242
- await this.withPage(async (page) => await page.keyboard.type(text));
243
- return OK;
244
- },
245
- },
246
- inputVariable: {
247
- gwta: `input {what} for {field}`,
248
- action: async ({ what, field }) => {
249
- await this.withPage(async (page) => await page.locator(field).fill(what));
250
- return OK;
251
- },
252
- },
253
- selectionOption: {
254
- gwta: `select {option} for {field: ${WEB_CONTROL}}`,
255
- action: async ({ option, field }) => {
256
- await this.withPage(async (page) => await page.selectOption(field, { label: option }));
257
- // FIXME have to use id value
258
- return OK;
259
- },
260
- },
261
- // ASSERTIONS
262
- dialogIs: {
263
- gwta: 'dialog {what} {type} says {value}',
264
- action: async ({ what, type, value }) => {
265
- const cur = this.getWorld().shared.get(what)?.[type];
266
- return Promise.resolve(cur === value ? OK : actionNotOK(`${what} is ${cur}`));
267
- },
268
- },
269
- dialogIsUnset: {
270
- gwta: 'dialog {what} {type} not set',
271
- action: async ({ what, type }) => {
272
- const cur = this.getWorld().shared.get(what)?.[type];
273
- return Promise.resolve(!cur ? OK : actionNotOK(`${what} is ${cur}`));
274
- },
275
- },
276
- seeTestId: {
277
- gwta: 'has test id {testId}',
278
- action: async ({ testId }) => {
279
- const found = await this.withPage(async (page) => await page.getByTestId(testId));
280
- return found ? OK : actionNotOK(`Did not find test id ${testId}`);
281
- },
282
- },
283
- seeTextIn: {
284
- gwta: 'in {selector}, see {text}',
285
- action: async ({ text, selector }) => {
286
- return await this.sees(text, selector);
287
- },
288
- },
289
- seeText: {
290
- gwta: 'see {text}',
291
- action: async ({ text }) => {
292
- return await this.sees(text, 'body');
293
- },
294
- },
295
- waitFor: {
296
- gwta: 'wait for {what}',
297
- action: async ({ what }) => {
298
- const selector = what.match(/^[.#]/) ? what : `text=${what}`;
299
- const found = await this.withPage(async (page) => await page.waitForSelector(selector));
300
- if (found) {
301
- return OK;
302
- }
303
- return actionNotOK(`Did not find ${what}`);
304
- },
305
- },
306
- createMonitor: {
307
- gwta: 'create monitor',
308
- action: async () => {
309
- await this.createMonitor();
310
- return OK;
311
- },
312
- },
313
- finishMonitor: {
314
- gwta: 'finish monitor',
315
- action: async () => {
316
- await WebPlaywright.monitorHandler.writeMonitor();
317
- return OK;
318
- },
319
- },
320
- onNewPage: {
321
- gwta: `on a new tab`,
322
- action: async () => {
323
- this.newTab();
324
- return Promise.resolve(OK);
325
- },
326
- },
327
- waitForTabX: {
328
- gwta: `pause until current tab is {tab}`,
329
- action: async ({ tab }) => {
330
- const waitForTab = parseInt(tab, 10);
331
- let timedOut = false;
332
- setTimeout(() => {
333
- timedOut = true;
334
- }, 5000);
335
- while (this.tab !== waitForTab && !timedOut) {
336
- await sleep(100);
337
- }
338
- return this.tab === waitForTab ? OK : actionNotOK(`current tab is ${this.tab}, not ${waitForTab}`);
339
- },
340
- },
341
- onTabX: {
342
- gwta: `on tab {tab}`,
343
- action: async ({ tab }) => {
344
- this.tab = parseInt(tab, 10);
345
- return Promise.resolve(OK);
346
- },
347
- },
348
- beOnPage: {
349
- gwta: `be on the {name} ${WEB_PAGE}`,
350
- action: async ({ name }) => {
351
- const nowon = await this.withPage(async (page) => {
352
- await page.waitForURL(name);
353
- return page.url();
354
- });
355
- if (nowon === name) {
356
- return OK;
357
- }
358
- return actionNotOK(`expected ${name} but on ${nowon}`);
359
- },
360
- },
361
- extensionContext: {
362
- gwta: `open extension popup for tab {tab}`,
363
- action: async ({ tab }) => {
364
- if (!this.factoryOptions?.persistentDirectory || this.factoryOptions?.launchOptions.headless) {
365
- throw Error(`extensions require ${WebPlaywright.PERSISTENT_DIRECTORY} and not HEADLESS`);
366
- }
367
- const browserContext = await this.getExistingBrowserContext();
368
- if (!browserContext) {
369
- throw Error(`no browserContext`);
370
- }
371
- const background = browserContext?.serviceWorkers()[0];
372
- if (!background) {
373
- // background = await context.waitForEvent("serviceworker");
374
- }
375
- console.debug('background', background, browserContext.serviceWorkers());
376
- const extensionId = background.url().split('/')[2];
377
- this.getWorld().shared.set('extensionContext', extensionId);
378
- await this.withPage(async (page) => {
379
- const popupURI = `chrome-extension://${extensionId}/popup.html?${tab}`;
380
- return await page.goto(popupURI);
381
- });
382
- return OK;
383
- },
384
- },
385
- cookieIs: {
386
- gwta: 'cookie {name} is {value}',
387
- action: async ({ name, value }) => {
388
- const cookies = await this.getCookies();
389
- const found = cookies?.find((c) => c.name === name && c.value === value);
390
- return found ? OK : actionNotOK(`did not find cookie ${name} with value ${value} from ${JSON.stringify(cookies)}`);
391
- },
392
- },
393
- URIContains: {
394
- gwta: 'URI includes {what}',
395
- action: async ({ what }) => {
396
- const uri = await this.withPage(async (page) => await page.url());
397
- return uri.includes(what) ? OK : actionNotOK(`current URI ${uri} does not contain ${what}`);
398
- },
399
- },
400
- URIQueryParameterIs: {
401
- gwta: 'URI query parameter {what} is {value}',
402
- action: async ({ what, value }) => {
403
- const uri = await this.withPage(async (page) => await page.url());
404
- const found = new URL(uri).searchParams.get(what);
405
- if (found === value) {
406
- return OK;
407
- }
408
- return actionNotOK(`URI query ${what} contains "${found}"", not "${value}""`);
409
- },
410
- },
411
- URIStartsWith: {
412
- gwta: 'URI starts with {start}',
413
- action: async ({ start }) => {
414
- const uri = await this.withPage(async (page) => await page.url());
415
- return uri.startsWith(start) ? OK : actionNotOK(`current URI ${uri} does not start with ${start}`);
416
- },
417
- },
418
- URIMatches: {
419
- gwta: 'URI matches {what}',
420
- action: async ({ what }) => {
421
- const uri = await this.withPage(async (page) => await page.url());
422
- return uri.match(what) ? OK : actionNotOK(`current URI ${uri} does not match ${what}`);
423
- },
424
- },
425
- caseInsensitiveURIMatches: {
426
- gwta: 'URI case insensitively matches {what}',
427
- action: async ({ what }) => {
428
- const uri = await this.withPage(async (page) => await page.url());
429
- const matcher = new RegExp(what, 'i');
430
- return uri.match(matcher) ? OK : actionNotOK(`current URI ${uri} does not match ${what}`);
431
- },
432
- },
433
- // CLICK
434
- clickByAltText: {
435
- gwta: 'click by alt text {altText}',
436
- action: async ({ altText }) => {
437
- await this.withPage(async (page) => await page.getByAltText(altText).click());
438
- return OK;
439
- },
440
- },
441
- clickByTestId: {
442
- gwta: 'click by test id {testId}',
443
- action: async ({ testId }) => {
444
- await this.withPage(async (page) => await page.getByTestId(testId).click());
445
- return OK;
446
- },
447
- },
448
- clickByPlaceholder: {
449
- gwta: 'click by placeholder {placeholder}',
450
- action: async ({ placeholder }) => {
451
- await this.withPage(async (page) => await page.getByPlaceholder(placeholder).click());
452
- return OK;
453
- },
454
- },
455
- clickByRole: {
456
- gwta: 'click by role {roleStr}',
457
- action: async ({ roleStr }) => {
458
- const [role, ...restStr] = roleStr.split(' ');
459
- let rest;
460
- try {
461
- rest = JSON.parse(restStr.join(' '));
462
- }
463
- catch (e) {
464
- return actionNotOK(`could not parse role ${roleStr} as JSON: ${e}`);
465
- }
466
- await this.withPage(async (page) => await page.getByRole(role, rest || {}).click());
467
- return OK;
468
- },
469
- },
470
- clickByLabel: {
471
- gwta: 'click by label {label}',
472
- action: async ({ title: label }) => {
473
- await this.withPage(async (page) => await page.getByLabel(label).click());
474
- return OK;
475
- },
476
- },
477
- clickByTitle: {
478
- gwta: 'click by title {title}',
479
- action: async ({ title }) => {
480
- await this.withPage(async (page) => await page.getByTitle(title).click());
481
- return OK;
482
- },
483
- },
484
- clickByText: {
485
- gwta: 'click by text {text}',
486
- action: async ({ text }) => {
487
- await this.withPage(async (page) => await page.getByText(text).click());
488
- return OK;
489
- },
490
- },
491
- clickOn: {
492
- gwta: 'click on (?<name>.[^s]+)',
493
- action: async ({ name }) => {
494
- const what = this.getWorld().shared.get(name) || `text=${name}`;
495
- await this.withPage(async (page) => await page.click(what));
496
- return OK;
497
- },
498
- },
499
- clickCheckbox: {
500
- gwta: 'click the checkbox (?<name>.+)',
501
- action: async ({ name }) => {
502
- const what = this.getWorld().shared.get(name) || name;
503
- this.getWorld().logger.log(`click ${name} ${what}`);
504
- await this.withPage(async (page) => await page.click(what));
505
- return OK;
506
- },
507
- },
508
- clickShared: {
509
- gwta: 'click `(?<id>.+)`',
510
- action: async ({ id }) => {
511
- const name = this.getWorld().shared.get(id);
512
- await this.withPage(async (page) => await page.click(name));
513
- return OK;
514
- },
515
- },
516
- clickQuoted: {
517
- gwta: 'click "(?<name>.+)"',
518
- action: async ({ name }) => {
519
- await this.withPage(async (page) => await page.click(`text=${name}`));
520
- return OK;
521
- },
522
- },
523
- clickLink: {
524
- // TODO: generalize modifier
525
- gwta: 'click( with alt)? the link {name}',
526
- action: async ({ name }, featureStep) => {
527
- const modifier = featureStep.in.match(/ with alt /) ? { modifiers: ['Alt'] } : {};
528
- const field = this.getWorld().shared.get(name) || name;
529
- await this.withPage(async (page) => await page.click(field, modifier));
530
- return OK;
531
- },
532
- },
533
- clickButton: {
534
- gwta: 'click the button (?<id>.+)',
535
- action: async ({ id }) => {
536
- const field = this.getWorld().shared.get(id) || id;
537
- await this.withPage(async (page) => await page.click(field));
538
- return OK;
539
- },
540
- },
541
- // NAVIGATION
542
- // formerly On the {name} ${WEB_PAGE}
543
- gotoPage: {
544
- gwta: `go to the {name} ${WEB_PAGE}`,
545
- action: async ({ name }) => {
546
- const response = await this.withPage(async (page) => {
547
- return await page.goto(name);
548
- });
549
- const messageContext = {
550
- incident: EExecutionMessageType.ACTION,
551
- incidentDetails: { ...response?.allHeaders, summary: response?.statusText() }
552
- };
553
- return response?.ok ? OK : actionNotOK(`response not ok`, { messageContext });
554
- },
555
- },
556
- reloadPage: {
557
- gwta: 'reload page',
558
- action: async () => {
559
- await this.withPage(async (page) => await page.reload());
560
- return OK;
561
- },
562
- },
563
- goBack: {
564
- gwta: 'go back',
565
- action: async () => {
566
- await this.withPage(async (page) => await page.goBack());
567
- return OK;
568
- },
569
- },
570
- blur: {
571
- gwta: 'blur {what}',
572
- action: async ({ what }) => {
573
- await this.withPage(async (page) => await page.locator(what).evaluate((e) => e.blur()));
574
- return OK;
575
- },
576
- },
577
- // BROWSER
578
- usingBrowserVar: {
579
- gwta: 'using {browser} browser',
580
- action: async ({ browser }) => {
581
- if (!BROWSERS[browser]) {
582
- throw Error(`browserType not recognized ${browser} from ${BROWSERS.toString()}`);
583
- }
584
- return Promise.resolve(this.setBrowser(browser));
585
- },
586
- },
587
- // FILE DOWNLOAD/UPLOAD
588
- uploadFile: {
589
- gwta: 'upload file {file} using {selector}',
590
- action: async ({ file, selector }) => {
591
- await this.withPage(async (page) => await page.setInputFiles(selector, file));
592
- return OK;
593
- },
594
- },
595
- waitForFileChooser: {
596
- gwta: 'upload file {file} with {selector}',
597
- action: async ({ file, selector }) => {
598
- try {
599
- await this.withPage(async (page) => {
600
- const [fileChooser] = await Promise.all([page.waitForEvent('filechooser'), page.locator('#uploadFile').click()]);
601
- const changeButton = page.locator(selector);
602
- await changeButton.click();
603
- await fileChooser.setFiles(file);
604
- });
605
- return OK;
606
- }
607
- catch (e) {
608
- return actionNotOK(e);
609
- }
610
- },
611
- },
612
- expectDownload: {
613
- gwta: 'expect a download',
614
- action: async () => {
615
- try {
616
- this.expectedDownload = this.withPage(async (page) => page.waitForEvent('download'));
617
- return Promise.resolve(OK);
618
- }
619
- catch (e) {
620
- return Promise.resolve(actionNotOK(e));
621
- }
622
- },
623
- },
624
- receiveDownload: {
625
- gwta: 'receive download as {file}',
626
- action: async ({ file }) => {
627
- try {
628
- const download = await this.expectedDownload;
629
- await download.saveAs(file);
630
- this.downloaded.push(file);
631
- return OK;
632
- }
633
- catch (e) {
634
- return actionNotOK(e);
635
- }
636
- },
637
- },
638
- waitForDownload: {
639
- gwta: 'save download to {file}',
640
- action: async ({ file }) => {
641
- try {
642
- const download = await this.withPage(async (page) => page.waitForEvent('download'));
643
- await download.saveAs(file);
644
- this.downloaded.push(file);
645
- return OK;
646
- }
647
- catch (e) {
648
- return actionNotOK(e);
649
- }
650
- },
651
- },
652
- // MISC
653
- withFrame: {
654
- gwta: 'with frame {name}',
655
- action: async ({ name }) => {
656
- this.withFrame = name;
657
- return Promise.resolve(OK);
658
- },
659
- },
660
- captureDialog: {
661
- gwta: 'Accept next dialog to {where}',
662
- action: async ({ where }) => {
663
- await this.withPage((page) => {
664
- return page.on('dialog', async (dialog) => {
665
- const res = {
666
- defaultValue: dialog.defaultValue(),
667
- message: dialog.message(),
668
- type: dialog.type(),
669
- };
670
- await dialog.accept();
671
- this.getWorld().shared.setJSON(where, res);
672
- });
673
- });
674
- return Promise.resolve(OK);
675
- },
676
- },
677
- takeScreenshot: {
678
- gwta: 'take a screenshot',
679
- action: async (notUsed, featureStep) => {
680
- try {
681
- await this.captureScreenshotAndLog(EExecutionMessageType.ACTION, featureStep);
682
- return OK;
683
- }
684
- catch (e) {
685
- return actionNotOK(e);
686
- }
687
- },
688
- },
689
- assertOpen: {
690
- gwta: '{what} is expanded with the {using}',
691
- action: async ({ what, using }) => {
692
- const isVisible = await this.withPage(async (page) => await page.isVisible(what));
693
- if (!isVisible) {
694
- await this.withPage(async (page) => await page.click(using));
695
- }
696
- return OK;
697
- },
698
- },
699
- setToURIQueryParameter: {
700
- gwta: 'save URI query parameter {what} to {where}',
701
- action: async ({ what, where }) => {
702
- const uri = await this.withPage(async (page) => await page.url());
703
- const found = new URL(uri).searchParams.get(what);
704
- this.getWorld().shared.set(where, found);
705
- return OK;
706
- },
707
- },
708
- resizeWindow: {
709
- gwta: 'resize window to {width}x{height}',
710
- action: async ({ width, height }) => {
711
- await this.withPage(async (page) => await page.setViewportSize({ width: parseInt(width), height: parseInt(height) }));
712
- return OK;
713
- },
714
- },
715
- resizeAvailable: {
716
- gwta: 'resize window to largest dimensions',
717
- action: async () => {
718
- await this.withPage(async (page) => {
719
- const { availHeight: height, availWidth: width } = await page.evaluate(() => ({ availHeight: window.screen.availHeight, availWidth: window.screen.availWidth }));
720
- return await page.setViewportSize({ width, height });
721
- });
722
- return OK;
723
- },
724
- },
165
+ ...interactionSteps(this),
725
166
  };
726
167
  setBrowser(browser) {
727
168
  this.factoryOptions.type = browser;