automation_model 1.0.805-dev → 1.0.805-stage

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 (47) hide show
  1. package/lib/api.js +40 -12
  2. package/lib/api.js.map +1 -1
  3. package/lib/auto_page.d.ts +1 -1
  4. package/lib/auto_page.js +104 -58
  5. package/lib/auto_page.js.map +1 -1
  6. package/lib/browser_manager.d.ts +2 -7
  7. package/lib/browser_manager.js +116 -102
  8. package/lib/browser_manager.js.map +1 -1
  9. package/lib/bruno.js.map +1 -1
  10. package/lib/check_performance.d.ts +1 -0
  11. package/lib/check_performance.js +57 -0
  12. package/lib/check_performance.js.map +1 -0
  13. package/lib/command_common.d.ts +2 -2
  14. package/lib/command_common.js +42 -24
  15. package/lib/command_common.js.map +1 -1
  16. package/lib/constants.d.ts +4 -0
  17. package/lib/constants.js +2 -0
  18. package/lib/constants.js.map +1 -0
  19. package/lib/file_checker.js +7 -0
  20. package/lib/file_checker.js.map +1 -1
  21. package/lib/index.js +1 -0
  22. package/lib/index.js.map +1 -1
  23. package/lib/init_browser.d.ts +1 -2
  24. package/lib/init_browser.js +137 -128
  25. package/lib/init_browser.js.map +1 -1
  26. package/lib/locator_log.js.map +1 -1
  27. package/lib/network.d.ts +2 -2
  28. package/lib/network.js +183 -120
  29. package/lib/network.js.map +1 -1
  30. package/lib/route.d.ts +64 -2
  31. package/lib/route.js +496 -251
  32. package/lib/route.js.map +1 -1
  33. package/lib/scripts/axe.mini.js +23978 -1
  34. package/lib/snapshot_validation.js +3 -0
  35. package/lib/snapshot_validation.js.map +1 -1
  36. package/lib/stable_browser.d.ts +12 -8
  37. package/lib/stable_browser.js +355 -96
  38. package/lib/stable_browser.js.map +1 -1
  39. package/lib/table_helper.js +14 -0
  40. package/lib/table_helper.js.map +1 -1
  41. package/lib/test_context.d.ts +1 -0
  42. package/lib/test_context.js +1 -0
  43. package/lib/test_context.js.map +1 -1
  44. package/lib/utils.d.ts +7 -4
  45. package/lib/utils.js +157 -34
  46. package/lib/utils.js.map +1 -1
  47. package/package.json +20 -12
@@ -1,4 +1,5 @@
1
1
  // @ts-nocheck
2
+ import { check_performance } from "./check_performance.js";
2
3
  import { expect } from "@playwright/test";
3
4
  import dayjs from "dayjs";
4
5
  import fs from "fs";
@@ -28,6 +29,7 @@ import { highlightSnapshot, snapshotValidation } from "./snapshot_validation.js"
28
29
  import { loadBrunoParams } from "./bruno.js";
29
30
  import { registerAfterStepRoutes, registerBeforeStepRoutes } from "./route.js";
30
31
  import { existsSync } from "node:fs";
32
+ import _ from "lodash";
31
33
  export const Types = {
32
34
  CLICK: "click_element",
33
35
  WAIT_ELEMENT: "wait_element",
@@ -46,6 +48,7 @@ export const Types = {
46
48
  VERIFY_PAGE_CONTAINS_NO_TEXT: "verify_page_contains_no_text",
47
49
  ANALYZE_TABLE: "analyze_table",
48
50
  SELECT: "select_combobox", //
51
+ VERIFY_PROPERTY: "verify_element_property",
49
52
  VERIFY_PAGE_PATH: "verify_page_path",
50
53
  VERIFY_PAGE_TITLE: "verify_page_title",
51
54
  TYPE_PRESS: "type_press",
@@ -64,12 +67,11 @@ export const Types = {
64
67
  SET_INPUT: "set_input",
65
68
  WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
66
69
  VERIFY_ATTRIBUTE: "verify_element_attribute",
67
- VERIFY_PROPERTY: "verify_element_property",
68
70
  VERIFY_TEXT_WITH_RELATION: "verify_text_with_relation",
69
71
  BRUNO: "bruno",
70
- SNAPSHOT_VALIDATION: "snapshot_validation",
71
72
  VERIFY_FILE_EXISTS: "verify_file_exists",
72
73
  SET_INPUT_FILES: "set_input_files",
74
+ SNAPSHOT_VALIDATION: "snapshot_validation",
73
75
  REPORT_COMMAND: "report_command",
74
76
  STEP_COMPLETE: "step_complete",
75
77
  SLEEP: "sleep",
@@ -86,6 +88,7 @@ class StableBrowser {
86
88
  context;
87
89
  world;
88
90
  fastMode;
91
+ stepTags;
89
92
  project_path = null;
90
93
  webLogFile = null;
91
94
  networkLogger = null;
@@ -94,14 +97,17 @@ class StableBrowser {
94
97
  tags = null;
95
98
  isRecording = false;
96
99
  initSnapshotTaken = false;
97
- abortedExecution = false;
98
- constructor(browser, page, logger = null, context = null, world = null, fastMode = false) {
100
+ onlyFailuresScreenshot = process.env.SCREENSHOT_ON_FAILURE_ONLY === "true";
101
+ // set to true if the step issue a report
102
+ inStepReport = false;
103
+ constructor(browser, page, logger = null, context = null, world = null, fastMode = false, stepTags = []) {
99
104
  this.browser = browser;
100
105
  this.page = page;
101
106
  this.logger = logger;
102
107
  this.context = context;
103
108
  this.world = world;
104
109
  this.fastMode = fastMode;
110
+ this.stepTags = stepTags;
105
111
  if (!this.logger) {
106
112
  this.logger = console;
107
113
  }
@@ -134,7 +140,7 @@ class StableBrowser {
134
140
  this.fastMode = true;
135
141
  }
136
142
  if (process.env.FAST_MODE === "true") {
137
- console.log("Fast mode enabled from environment variable");
143
+ // console.log("Fast mode enabled from environment variable");
138
144
  this.fastMode = true;
139
145
  }
140
146
  if (process.env.FAST_MODE === "false") {
@@ -177,6 +183,7 @@ class StableBrowser {
177
183
  registerNetworkEvents(this.world, this, context, this.page);
178
184
  registerDownloadEvent(this.page, this.world, context);
179
185
  page.on("close", async () => {
186
+ // return if browser context is already closed
180
187
  if (this.context && this.context.pages && this.context.pages.length > 1) {
181
188
  this.context.pages.pop();
182
189
  this.page = this.context.pages[this.context.pages.length - 1];
@@ -186,7 +193,12 @@ class StableBrowser {
186
193
  console.log("Switched to page " + title);
187
194
  }
188
195
  catch (error) {
189
- console.error("Error on page close", error);
196
+ if (error?.message?.includes("Target page, context or browser has been closed")) {
197
+ // Ignore this error
198
+ }
199
+ else {
200
+ console.error("Error on page close", error);
201
+ }
190
202
  }
191
203
  }
192
204
  });
@@ -195,7 +207,12 @@ class StableBrowser {
195
207
  console.log("Switch page: " + (await page.title()));
196
208
  }
197
209
  catch (e) {
198
- this.logger.error("error on page load " + e);
210
+ if (e?.message?.includes("Target page, context or browser has been closed")) {
211
+ // Ignore this error
212
+ }
213
+ else {
214
+ this.logger.error("error on page load " + e);
215
+ }
199
216
  }
200
217
  context.pageLoading.status = false;
201
218
  }.bind(this));
@@ -207,7 +224,7 @@ class StableBrowser {
207
224
  }
208
225
  let newContextCreated = false;
209
226
  if (!apps[appName]) {
210
- let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
227
+ let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder, null, null, this.tags);
211
228
  newContextCreated = true;
212
229
  apps[appName] = {
213
230
  context: newContext,
@@ -223,7 +240,7 @@ class StableBrowser {
223
240
  if (newContextCreated) {
224
241
  this.registerEventListeners(this.context);
225
242
  await this.goto(this.context.environment.baseUrl);
226
- if (!this.fastMode) {
243
+ if (!this.fastMode && !this.stepTags.includes("fast-mode")) {
227
244
  await this.waitForPageLoad();
228
245
  }
229
246
  }
@@ -315,7 +332,7 @@ class StableBrowser {
315
332
  // async closeUnexpectedPopups() {
316
333
  // await closeUnexpectedPopups(this.page);
317
334
  // }
318
- async goto(url, world = null) {
335
+ async goto(url, world = null, options = {}) {
319
336
  if (!url) {
320
337
  throw new Error("url is null, verify that the environment file is correct");
321
338
  }
@@ -336,16 +353,23 @@ class StableBrowser {
336
353
  screenshot: false,
337
354
  highlight: false,
338
355
  };
356
+ let timeout = 60000;
357
+ if (this.configuration && this.configuration.page_timeout) {
358
+ timeout = this.configuration.page_timeout;
359
+ }
360
+ if (options && options["timeout"]) {
361
+ timeout = options["timeout"];
362
+ }
339
363
  try {
340
364
  await _preCommand(state, this);
341
365
  await this.page.goto(url, {
342
- timeout: 60000,
366
+ timeout: timeout,
343
367
  });
344
368
  await _screenshot(state, this);
345
369
  }
346
370
  catch (error) {
347
371
  console.error("Error on goto", error);
348
- _commandError(state, error, this);
372
+ await _commandError(state, error, this);
349
373
  }
350
374
  finally {
351
375
  await _commandFinally(state, this);
@@ -374,7 +398,7 @@ class StableBrowser {
374
398
  }
375
399
  catch (error) {
376
400
  console.error("Error on goBack", error);
377
- _commandError(state, error, this);
401
+ await _commandError(state, error, this);
378
402
  }
379
403
  finally {
380
404
  await _commandFinally(state, this);
@@ -403,7 +427,7 @@ class StableBrowser {
403
427
  }
404
428
  catch (error) {
405
429
  console.error("Error on goForward", error);
406
- _commandError(state, error, this);
430
+ await _commandError(state, error, this);
407
431
  }
408
432
  finally {
409
433
  await _commandFinally(state, this);
@@ -507,12 +531,6 @@ class StableBrowser {
507
531
  if (!el.setAttribute) {
508
532
  el = el.parentElement;
509
533
  }
510
- // remove any attributes start with data-blinq-id
511
- // for (let i = 0; i < el.attributes.length; i++) {
512
- // if (el.attributes[i].name.startsWith("data-blinq-id")) {
513
- // el.removeAttribute(el.attributes[i].name);
514
- // }
515
- // }
516
534
  el.setAttribute("data-blinq-id-" + randomToken, "");
517
535
  return true;
518
536
  }, [tag1, randomToken]))) {
@@ -684,8 +702,8 @@ class StableBrowser {
684
702
  }
685
703
  getFilePath() {
686
704
  const stackFrames = errorStackParser.parse(new Error());
687
- const stackFrame = stackFrames.findLast((frame) => frame.fileName && frame.fileName.endsWith(".mjs"));
688
- // return stackFrame?.fileName || null;
705
+ const mjsFrames = stackFrames.filter((frame) => frame.fileName && frame.fileName.endsWith(".mjs"));
706
+ const stackFrame = mjsFrames[mjsFrames.length - 2];
689
707
  const filepath = stackFrame?.fileName;
690
708
  if (filepath) {
691
709
  let jsonFilePath = filepath.replace(".mjs", ".json");
@@ -788,6 +806,7 @@ class StableBrowser {
788
806
  info.log += "using strategy " + key + " with locators " + JSON.stringify(strategyLocators) + "\n";
789
807
  selectors.locators = strategyLocators;
790
808
  element = await this._locate_internal(selectors, info, _params, 10_000, allowDisabled);
809
+ err = null;
791
810
  info.selectedStrategy = key;
792
811
  info.log += "element found using strategy " + key + "\n";
793
812
  break;
@@ -797,38 +816,70 @@ class StableBrowser {
797
816
  }
798
817
  }
799
818
  }
800
- if (err) {
801
- throw err;
802
- }
819
+ }
820
+ if (err) {
821
+ throw err;
803
822
  }
804
823
  }
805
824
  else {
806
825
  element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
807
826
  }
808
827
  if (!element.rerun) {
809
- const randomToken = Math.random().toString(36).substring(7);
810
- await element.evaluate((el, randomToken) => {
811
- el.setAttribute("data-blinq-id-" + randomToken, "");
812
- console.log("set data-blinq-id-" + randomToken + " on element", el);
813
- }, randomToken);
814
- // if (element._frame) {
815
- // return element;
816
- // }
828
+ let newElementSelector = "";
829
+ if (this.configuration && this.configuration.stableLocatorStrategy === "csschain") {
830
+ const cssSelector = await element.evaluate((el) => {
831
+ function getCssSelector(el) {
832
+ if (!el || el.nodeType !== 1 || el === document.body)
833
+ return el.tagName.toLowerCase();
834
+ const parent = el.parentElement;
835
+ const tag = el.tagName.toLowerCase();
836
+ // Find the index of the element among its siblings of the same tag
837
+ let index = 1;
838
+ for (let sibling = el.previousElementSibling; sibling; sibling = sibling.previousElementSibling) {
839
+ if (sibling.tagName === el.tagName) {
840
+ index++;
841
+ }
842
+ }
843
+ // Use nth-child if necessary (i.e., if there's more than one of the same tag)
844
+ const siblings = Array.from(parent.children).filter((child) => child.tagName === el.tagName);
845
+ const needsNthChild = siblings.length > 1;
846
+ const selector = needsNthChild ? `${tag}:nth-child(${[...parent.children].indexOf(el) + 1})` : tag;
847
+ return getCssSelector(parent) + " > " + selector;
848
+ }
849
+ const cssSelector = getCssSelector(el);
850
+ return cssSelector;
851
+ });
852
+ newElementSelector = cssSelector;
853
+ }
854
+ else {
855
+ const randomToken = "blinq_" + Math.random().toString(36).substring(7);
856
+ if (this.configuration && this.configuration.stableLocatorStrategy === "data-attribute") {
857
+ const dataAttribute = "data-blinq-id";
858
+ await element.evaluate((el, [dataAttribute, randomToken]) => {
859
+ el.setAttribute(dataAttribute, randomToken);
860
+ }, [dataAttribute, randomToken]);
861
+ newElementSelector = `[${dataAttribute}="${randomToken}"]`;
862
+ }
863
+ else {
864
+ // the default case just return the located element
865
+ // will not work for click and type if the locator is placeholder and the placeholder change due to the click event
866
+ return element;
867
+ }
868
+ }
817
869
  const scope = element._frame ?? element.page();
818
- let newElementSelector = "[data-blinq-id-" + randomToken + "]";
819
870
  let prefixSelector = "";
820
871
  const frameControlSelector = " >> internal:control=enter-frame";
821
872
  const frameSelectorIndex = element._selector.lastIndexOf(frameControlSelector);
822
873
  if (frameSelectorIndex !== -1) {
823
874
  // remove everything after the >> internal:control=enter-frame
824
875
  const frameSelector = element._selector.substring(0, frameSelectorIndex);
825
- prefixSelector = frameSelector + " >> internal:control=enter-frame >>";
876
+ prefixSelector = frameSelector + " >> internal:control=enter-frame >> ";
826
877
  }
827
878
  // if (element?._frame?._selector) {
828
879
  // prefixSelector = element._frame._selector + " >> " + prefixSelector;
829
880
  // }
830
881
  const newSelector = prefixSelector + newElementSelector;
831
- return scope.locator(newSelector);
882
+ return scope.locator(newSelector).first();
832
883
  }
833
884
  }
834
885
  throw new Error("unable to locate element " + JSON.stringify(selectors));
@@ -849,7 +900,7 @@ class StableBrowser {
849
900
  for (let i = 0; i < frame.selectors.length; i++) {
850
901
  let frameLocator = frame.selectors[i];
851
902
  if (frameLocator.css) {
852
- let testframescope = framescope.frameLocator(frameLocator.css);
903
+ let testframescope = framescope.frameLocator(`${frameLocator.css} >> visible=true`);
853
904
  if (frameLocator.index) {
854
905
  testframescope = framescope.nth(frameLocator.index);
855
906
  }
@@ -927,6 +978,15 @@ class StableBrowser {
927
978
  });
928
979
  }
929
980
  async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
981
+ if (selectors.locators && Array.isArray(selectors.locators)) {
982
+ selectors.locators.forEach((locator) => {
983
+ locator.index = locator.index ?? 0;
984
+ locator.visible = locator.visible ?? true;
985
+ if (locator.visible && locator.css && !locator.css.endsWith(">> visible=true")) {
986
+ locator.css = locator.css + " >> visible=true";
987
+ }
988
+ });
989
+ }
930
990
  if (!info) {
931
991
  info = {};
932
992
  info.failCause = {};
@@ -1082,9 +1142,13 @@ class StableBrowser {
1082
1142
  }
1083
1143
  }
1084
1144
  if (foundLocators.length === 1) {
1145
+ let box = null;
1146
+ if (!this.onlyFailuresScreenshot) {
1147
+ box = await foundLocators[0].boundingBox();
1148
+ }
1085
1149
  result.foundElements.push({
1086
1150
  locator: foundLocators[0],
1087
- box: await foundLocators[0].boundingBox(),
1151
+ box: box,
1088
1152
  unique: true,
1089
1153
  });
1090
1154
  result.locatorIndex = i;
@@ -1142,7 +1206,7 @@ class StableBrowser {
1142
1206
  operation: "simpleClick",
1143
1207
  log: "***** click on " + elementDescription + " *****\n",
1144
1208
  };
1145
- _preCommand(state, this);
1209
+ await _preCommand(state, this);
1146
1210
  const startTime = Date.now();
1147
1211
  let timeout = 30000;
1148
1212
  if (options && options.timeout) {
@@ -1191,7 +1255,7 @@ class StableBrowser {
1191
1255
  operation: "simpleClickType",
1192
1256
  log: "***** click type on " + elementDescription + " *****\n",
1193
1257
  };
1194
- _preCommand(state, this);
1258
+ await _preCommand(state, this);
1195
1259
  const startTime = Date.now();
1196
1260
  let timeout = 30000;
1197
1261
  if (options && options.timeout) {
@@ -1239,11 +1303,22 @@ class StableBrowser {
1239
1303
  operation: "click",
1240
1304
  log: "***** click on " + selectors.element_name + " *****\n",
1241
1305
  };
1306
+ check_performance("click_all ***", this.context, true);
1307
+ let stepFastMode = this.stepTags.includes("fast-mode");
1308
+ if (stepFastMode) {
1309
+ state.onlyFailuresScreenshot = true;
1310
+ state.scroll = false;
1311
+ state.highlight = false;
1312
+ }
1242
1313
  try {
1314
+ check_performance("click_preCommand", this.context, true);
1243
1315
  await _preCommand(state, this);
1316
+ check_performance("click_preCommand", this.context, false);
1244
1317
  await performAction("click", state.element, options, this, state, _params);
1245
- if (!this.fastMode) {
1246
- await this.waitForPageLoad();
1318
+ if (!this.fastMode && !this.stepTags.includes("fast-mode")) {
1319
+ check_performance("click_waitForPageLoad", this.context, true);
1320
+ await this.waitForPageLoad({ noSleep: true });
1321
+ check_performance("click_waitForPageLoad", this.context, false);
1247
1322
  }
1248
1323
  return state.info;
1249
1324
  }
@@ -1251,7 +1326,13 @@ class StableBrowser {
1251
1326
  await _commandError(state, e, this);
1252
1327
  }
1253
1328
  finally {
1329
+ check_performance("click_commandFinally", this.context, true);
1254
1330
  await _commandFinally(state, this);
1331
+ check_performance("click_commandFinally", this.context, false);
1332
+ check_performance("click_all ***", this.context, false);
1333
+ if (this.context.profile) {
1334
+ console.log(JSON.stringify(this.context.profile, null, 2));
1335
+ }
1255
1336
  }
1256
1337
  }
1257
1338
  async waitForElement(selectors, _params, options = {}, world = null) {
@@ -1343,7 +1424,7 @@ class StableBrowser {
1343
1424
  }
1344
1425
  }
1345
1426
  }
1346
- await this.waitForPageLoad();
1427
+ //await this.waitForPageLoad();
1347
1428
  return state.info;
1348
1429
  }
1349
1430
  catch (e) {
@@ -1369,7 +1450,7 @@ class StableBrowser {
1369
1450
  await _preCommand(state, this);
1370
1451
  await performAction("hover", state.element, options, this, state, _params);
1371
1452
  await _screenshot(state, this);
1372
- await this.waitForPageLoad();
1453
+ //await this.waitForPageLoad();
1373
1454
  return state.info;
1374
1455
  }
1375
1456
  catch (e) {
@@ -1405,7 +1486,7 @@ class StableBrowser {
1405
1486
  state.info.log += "selectOption failed, will try force" + "\n";
1406
1487
  await state.element.selectOption(values, { timeout: 10000, force: true });
1407
1488
  }
1408
- await this.waitForPageLoad();
1489
+ //await this.waitForPageLoad();
1409
1490
  return state.info;
1410
1491
  }
1411
1492
  catch (e) {
@@ -1509,7 +1590,7 @@ class StableBrowser {
1509
1590
  _text: `Set date time value: ${value} on ${selectors.element_name}`,
1510
1591
  operation: "setDateTime",
1511
1592
  log: "***** set date time value " + selectors.element_name + " *****\n",
1512
- throwError: false,
1593
+ // throwError: false,
1513
1594
  };
1514
1595
  try {
1515
1596
  await _preCommand(state, this);
@@ -1591,6 +1672,14 @@ class StableBrowser {
1591
1672
  }
1592
1673
  try {
1593
1674
  await _preCommand(state, this);
1675
+ const randomToken = "blinq_" + Math.random().toString(36).substring(7);
1676
+ // tag the element
1677
+ let newElementSelector = await state.element.evaluate((el, token) => {
1678
+ // use attribute and not id
1679
+ const attrName = `data-blinq-id-${token}`;
1680
+ el.setAttribute(attrName, "");
1681
+ return `[${attrName}]`;
1682
+ }, randomToken);
1594
1683
  state.info.value = _value;
1595
1684
  if (!options.press) {
1596
1685
  try {
@@ -1616,6 +1705,25 @@ class StableBrowser {
1616
1705
  }
1617
1706
  }
1618
1707
  await new Promise((resolve) => setTimeout(resolve, 500));
1708
+ // check if the element exist after the click (no wait)
1709
+ const count = await state.element.count({ timeout: 0 });
1710
+ if (count === 0) {
1711
+ // the locator changed after the click (placeholder) we need to locate the element using the data-blinq-id
1712
+ const scope = state.element._frame ?? element.page();
1713
+ let prefixSelector = "";
1714
+ const frameControlSelector = " >> internal:control=enter-frame";
1715
+ const frameSelectorIndex = state.element._selector.lastIndexOf(frameControlSelector);
1716
+ if (frameSelectorIndex !== -1) {
1717
+ // remove everything after the >> internal:control=enter-frame
1718
+ const frameSelector = state.element._selector.substring(0, frameSelectorIndex);
1719
+ prefixSelector = frameSelector + " >> internal:control=enter-frame >> ";
1720
+ }
1721
+ // if (element?._frame?._selector) {
1722
+ // prefixSelector = element._frame._selector + " >> " + prefixSelector;
1723
+ // }
1724
+ const newSelector = prefixSelector + newElementSelector;
1725
+ state.element = scope.locator(newSelector).first();
1726
+ }
1619
1727
  const valueSegment = state.value.split("&&");
1620
1728
  for (let i = 0; i < valueSegment.length; i++) {
1621
1729
  if (i > 0) {
@@ -1687,8 +1795,8 @@ class StableBrowser {
1687
1795
  if (enter) {
1688
1796
  await new Promise((resolve) => setTimeout(resolve, 2000));
1689
1797
  await this.page.keyboard.press("Enter");
1798
+ await this.waitForPageLoad();
1690
1799
  }
1691
- await this.waitForPageLoad();
1692
1800
  return state.info;
1693
1801
  }
1694
1802
  catch (e) {
@@ -1947,11 +2055,12 @@ class StableBrowser {
1947
2055
  throw new Error("referanceSnapshot is null");
1948
2056
  }
1949
2057
  let text = null;
1950
- if (fs.existsSync(path.join(this.project_path, "data", "snapshots", this.context.environment.name, referanceSnapshot + ".yml"))) {
1951
- text = fs.readFileSync(path.join(this.project_path, "data", "snapshots", this.context.environment.name, referanceSnapshot + ".yml"), "utf8");
2058
+ const snapshotsFolder = process.env.BVT_TEMP_SNAPSHOTS_FOLDER ?? this.context.snapshotFolder; //path .join(this.project_path, "data", "snapshots");
2059
+ if (fs.existsSync(path.join(snapshotsFolder, referanceSnapshot + ".yml"))) {
2060
+ text = fs.readFileSync(path.join(snapshotsFolder, referanceSnapshot + ".yml"), "utf8");
1952
2061
  }
1953
- else if (fs.existsSync(path.join(this.project_path, "data", "snapshots", this.context.environment.name, referanceSnapshot + ".yaml"))) {
1954
- text = fs.readFileSync(path.join(this.project_path, "data", "snapshots", this.context.environment.name, referanceSnapshot + ".yaml"), "utf8");
2062
+ else if (fs.existsSync(path.join(snapshotsFolder, referanceSnapshot + ".yaml"))) {
2063
+ text = fs.readFileSync(path.join(snapshotsFolder, referanceSnapshot + ".yaml"), "utf8");
1955
2064
  }
1956
2065
  else if (referanceSnapshot.startsWith("yaml:")) {
1957
2066
  text = referanceSnapshot.substring(5);
@@ -1975,7 +2084,13 @@ class StableBrowser {
1975
2084
  scope = await this._findFrameScope(frameSelectors, timeout, state.info);
1976
2085
  }
1977
2086
  const snapshot = await scope.locator("body").ariaSnapshot({ timeout });
2087
+ if (snapshot && snapshot.length <= 10) {
2088
+ console.log("Page snapshot length is suspiciously small:", snapshot);
2089
+ }
1978
2090
  matchResult = snapshotValidation(snapshot, newValue, referanceSnapshot);
2091
+ if (matchResult === undefined) {
2092
+ console.log("snapshotValidation returned undefined");
2093
+ }
1979
2094
  if (matchResult.errorLine !== -1) {
1980
2095
  throw new Error("Snapshot validation failed at line " + matchResult.errorLineText);
1981
2096
  }
@@ -2146,6 +2261,9 @@ class StableBrowser {
2146
2261
  return _getTestData(world, this.context, this);
2147
2262
  }
2148
2263
  async _screenShot(options = {}, world = null, info = null) {
2264
+ if (!options) {
2265
+ options = {};
2266
+ }
2149
2267
  // collect url/path/title
2150
2268
  if (info) {
2151
2269
  if (!info.title) {
@@ -2174,7 +2292,7 @@ class StableBrowser {
2174
2292
  const uuidStr = "id_" + randomUUID();
2175
2293
  const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
2176
2294
  try {
2177
- await this.takeScreenshot(screenshotPath);
2295
+ await this.takeScreenshot(screenshotPath, options.fullPage === true);
2178
2296
  // let buffer = await this.page.screenshot({ timeout: 4000 });
2179
2297
  // // save the buffer to the screenshot path asynchrously
2180
2298
  // fs.writeFile(screenshotPath, buffer, (err) => {
@@ -2195,7 +2313,7 @@ class StableBrowser {
2195
2313
  else if (options && options.screenshot) {
2196
2314
  result.screenshotPath = options.screenshotPath;
2197
2315
  try {
2198
- await this.takeScreenshot(options.screenshotPath);
2316
+ await this.takeScreenshot(options.screenshotPath, options.fullPage === true);
2199
2317
  // let buffer = await this.page.screenshot({ timeout: 4000 });
2200
2318
  // // save the buffer to the screenshot path asynchrously
2201
2319
  // fs.writeFile(options.screenshotPath, buffer, (err) => {
@@ -2213,7 +2331,7 @@ class StableBrowser {
2213
2331
  }
2214
2332
  return result;
2215
2333
  }
2216
- async takeScreenshot(screenshotPath) {
2334
+ async takeScreenshot(screenshotPath, fullPage = false) {
2217
2335
  const playContext = this.context.playContext;
2218
2336
  // Using CDP to capture the screenshot
2219
2337
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
@@ -2238,13 +2356,7 @@ class StableBrowser {
2238
2356
  const client = await playContext.newCDPSession(this.page);
2239
2357
  const { data } = await client.send("Page.captureScreenshot", {
2240
2358
  format: "png",
2241
- // clip: {
2242
- // x: 0,
2243
- // y: 0,
2244
- // width: viewportWidth,
2245
- // height: viewportHeight,
2246
- // scale: 1,
2247
- // },
2359
+ captureBeyondViewport: fullPage,
2248
2360
  });
2249
2361
  await client.detach();
2250
2362
  if (!screenshotPath) {
@@ -2253,7 +2365,7 @@ class StableBrowser {
2253
2365
  screenshotBuffer = Buffer.from(data, "base64");
2254
2366
  }
2255
2367
  else {
2256
- screenshotBuffer = await this.page.screenshot();
2368
+ screenshotBuffer = await this.page.screenshot({ fullPage: fullPage });
2257
2369
  }
2258
2370
  // if (focusedElement) {
2259
2371
  // // console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
@@ -2353,6 +2465,12 @@ class StableBrowser {
2353
2465
  state.info.value = state.value;
2354
2466
  this.setTestData({ [variable]: state.value }, world);
2355
2467
  this.logger.info("set test data: " + variable + "=" + state.value);
2468
+ if (process.env.MODE === "executions") {
2469
+ const globalDataFile = "global_test_data.json";
2470
+ if (existsSync(globalDataFile)) {
2471
+ this.saveTestDataAsGlobal({}, world);
2472
+ }
2473
+ }
2356
2474
  // await new Promise((resolve) => setTimeout(resolve, 500));
2357
2475
  return state.info;
2358
2476
  }
@@ -2424,6 +2542,12 @@ class StableBrowser {
2424
2542
  state.info.value = state.value;
2425
2543
  this.setTestData({ [variable]: state.value }, world);
2426
2544
  this.logger.info("set test data: " + variable + "=" + state.value);
2545
+ if (process.env.MODE === "executions") {
2546
+ const globalDataFile = "global_test_data.json";
2547
+ if (existsSync(globalDataFile)) {
2548
+ this.saveTestDataAsGlobal({}, world);
2549
+ }
2550
+ }
2427
2551
  // await new Promise((resolve) => setTimeout(resolve, 500));
2428
2552
  return state.info;
2429
2553
  }
@@ -2554,7 +2678,7 @@ class StableBrowser {
2554
2678
  let expectedValue;
2555
2679
  try {
2556
2680
  await _preCommand(state, this);
2557
- expectedValue = await replaceWithLocalTestData(state.value, world);
2681
+ expectedValue = await this._replaceWithLocalData(value, world);
2558
2682
  state.info.expectedValue = expectedValue;
2559
2683
  switch (property) {
2560
2684
  case "innerText":
@@ -2680,6 +2804,7 @@ class StableBrowser {
2680
2804
  allowDisabled: true,
2681
2805
  info: {},
2682
2806
  };
2807
+ state.options ??= { timeout: timeoutMs };
2683
2808
  // Initialize startTime outside try block to ensure it's always accessible
2684
2809
  const startTime = Date.now();
2685
2810
  let conditionMet = false;
@@ -2849,6 +2974,12 @@ class StableBrowser {
2849
2974
  emailUrl = url;
2850
2975
  codeOrUrlFound = true;
2851
2976
  }
2977
+ if (process.env.MODE === "executions") {
2978
+ const globalDataFile = "global_test_data.json";
2979
+ if (existsSync(globalDataFile)) {
2980
+ this.saveTestDataAsGlobal({}, world);
2981
+ }
2982
+ }
2852
2983
  if (codeOrUrlFound) {
2853
2984
  return { emailUrl, emailCode };
2854
2985
  }
@@ -3255,7 +3386,16 @@ class StableBrowser {
3255
3386
  text = text.replace(/\\"/g, '"');
3256
3387
  }
3257
3388
  const timeout = this._getFindElementTimeout(options);
3258
- await new Promise((resolve) => setTimeout(resolve, 2000));
3389
+ //if (!this.fastMode && !this.stepTags.includes("fast-mode")) {
3390
+ let stepFastMode = this.stepTags.includes("fast-mode");
3391
+ if (!stepFastMode) {
3392
+ if (!this.fastMode) {
3393
+ await new Promise((resolve) => setTimeout(resolve, 2000));
3394
+ }
3395
+ else {
3396
+ await new Promise((resolve) => setTimeout(resolve, 500));
3397
+ }
3398
+ }
3259
3399
  const newValue = await this._replaceWithLocalData(text, world);
3260
3400
  if (newValue !== text) {
3261
3401
  this.logger.info(text + "=" + newValue);
@@ -3263,6 +3403,11 @@ class StableBrowser {
3263
3403
  }
3264
3404
  let dateAlternatives = findDateAlternatives(text);
3265
3405
  let numberAlternatives = findNumberAlternatives(text);
3406
+ if (stepFastMode) {
3407
+ state.onlyFailuresScreenshot = true;
3408
+ state.scroll = false;
3409
+ state.highlight = false;
3410
+ }
3266
3411
  try {
3267
3412
  await _preCommand(state, this);
3268
3413
  state.info.text = text;
@@ -3382,6 +3527,8 @@ class StableBrowser {
3382
3527
  operation: "verify_text_with_relation",
3383
3528
  log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
3384
3529
  };
3530
+ const cmdStartTime = Date.now();
3531
+ let cmdEndTime = null;
3385
3532
  const timeout = this._getFindElementTimeout(options);
3386
3533
  await new Promise((resolve) => setTimeout(resolve, 2000));
3387
3534
  let newValue = await this._replaceWithLocalData(textAnchor, world);
@@ -3417,6 +3564,17 @@ class StableBrowser {
3417
3564
  await new Promise((resolve) => setTimeout(resolve, 1000));
3418
3565
  continue;
3419
3566
  }
3567
+ else {
3568
+ cmdEndTime = Date.now();
3569
+ if (cmdEndTime - cmdStartTime > 55000) {
3570
+ if (foundAncore) {
3571
+ throw new Error(`Text ${textToVerify} not found in page`);
3572
+ }
3573
+ else {
3574
+ throw new Error(`Text ${textAnchor} not found in page`);
3575
+ }
3576
+ }
3577
+ }
3420
3578
  try {
3421
3579
  for (let i = 0; i < resultWithElementsFound.length; i++) {
3422
3580
  foundAncore = true;
@@ -3429,7 +3587,9 @@ class StableBrowser {
3429
3587
  climbArray1.push("..");
3430
3588
  }
3431
3589
  let climbXpath = "xpath=" + climbArray1.join("/");
3432
- css = css + " >> " + climbXpath;
3590
+ if (Number(climb) > 0) {
3591
+ css = css + " >> " + climbXpath;
3592
+ }
3433
3593
  const count = await frame.locator(css).count();
3434
3594
  for (let j = 0; j < count; j++) {
3435
3595
  const continer = await frame.locator(css).nth(j);
@@ -3555,7 +3715,7 @@ class StableBrowser {
3555
3715
  Object.assign(e, { info: info });
3556
3716
  error = e;
3557
3717
  // throw e;
3558
- await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
3718
+ await _commandError({ text: "visualVerification", operation: "visualVerification", info }, e, this);
3559
3719
  }
3560
3720
  finally {
3561
3721
  const endTime = Date.now();
@@ -3900,10 +4060,26 @@ class StableBrowser {
3900
4060
  registerNetworkEvents(this.world, this, this.context, this.page);
3901
4061
  registerDownloadEvent(this.page, this.world, this.context);
3902
4062
  if (this.onRestoreSaveState) {
3903
- this.onRestoreSaveState(path);
4063
+ await this.onRestoreSaveState(path);
3904
4064
  }
3905
4065
  }
3906
4066
  async waitForPageLoad(options = {}, world = null) {
4067
+ // try {
4068
+ // let currentPagePath = null;
4069
+ // currentPagePath = new URL(this.page.url()).pathname;
4070
+ // if (this.latestPagePath) {
4071
+ // // get the currect page path and compare with the latest page path
4072
+ // if (this.latestPagePath === currentPagePath) {
4073
+ // // if the page path is the same, do not wait for page load
4074
+ // console.log("No page change: " + currentPagePath);
4075
+ // return;
4076
+ // }
4077
+ // }
4078
+ // this.latestPagePath = currentPagePath;
4079
+ // } catch (e) {
4080
+ // console.debug("Error getting current page path: ", e);
4081
+ // }
4082
+ //console.log("Waiting for page load");
3907
4083
  let timeout = this._getLoadTimeout(options);
3908
4084
  const promiseArray = [];
3909
4085
  // let waitForNetworkIdle = true;
@@ -3938,7 +4114,10 @@ class StableBrowser {
3938
4114
  }
3939
4115
  }
3940
4116
  finally {
3941
- await new Promise((resolve) => setTimeout(resolve, 2000));
4117
+ await new Promise((resolve) => setTimeout(resolve, 500));
4118
+ if (options && !options.noSleep) {
4119
+ await new Promise((resolve) => setTimeout(resolve, 1500));
4120
+ }
3942
4121
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
3943
4122
  const endTime = Date.now();
3944
4123
  _reportToWorld(world, {
@@ -3972,7 +4151,7 @@ class StableBrowser {
3972
4151
  _text: `Close the page`,
3973
4152
  operation: "closePage",
3974
4153
  log: "***** close page *****\n",
3975
- throwError: false,
4154
+ // throwError: false,
3976
4155
  };
3977
4156
  try {
3978
4157
  await _preCommand(state, this);
@@ -3992,7 +4171,7 @@ class StableBrowser {
3992
4171
  }
3993
4172
  operation = options.operation;
3994
4173
  // validate operation is one of the supported operations
3995
- if (operation != "click" && operation != "hover+click") {
4174
+ if (operation != "click" && operation != "hover+click" && operation != "hover") {
3996
4175
  throw new Error("operation is not supported");
3997
4176
  }
3998
4177
  const state = {
@@ -4061,6 +4240,17 @@ class StableBrowser {
4061
4240
  state.element = results[0];
4062
4241
  await performAction("hover+click", state.element, options, this, state, _params);
4063
4242
  break;
4243
+ case "hover":
4244
+ if (!options.css) {
4245
+ throw new Error("css is not defined");
4246
+ }
4247
+ const result1 = await findElementsInArea(options.css, cellArea, this, options);
4248
+ if (result1.length === 0) {
4249
+ throw new Error(`Element not found in cell area`);
4250
+ }
4251
+ state.element = result1[0];
4252
+ await performAction("hover", state.element, options, this, state, _params);
4253
+ break;
4064
4254
  default:
4065
4255
  throw new Error("operation is not supported");
4066
4256
  }
@@ -4074,6 +4264,16 @@ class StableBrowser {
4074
4264
  }
4075
4265
  saveTestDataAsGlobal(options, world) {
4076
4266
  const dataFile = _getDataFile(world, this.context, this);
4267
+ if (process.env.MODE === "executions") {
4268
+ const globalDataFile = path.join(this.project_path, "global_test_data.json");
4269
+ const dataFileContents = fs.existsSync(dataFile) ? JSON.parse(fs.readFileSync(dataFile)) : {};
4270
+ const globalDataFileContents = fs.existsSync(globalDataFile) ? JSON.parse(fs.readFileSync(globalDataFile)) : {};
4271
+ const mergedData = JSON.stringify(_.merge({}, dataFileContents, globalDataFileContents), null, 2);
4272
+ fs.writeFileSync(dataFile, mergedData);
4273
+ fs.writeFileSync(globalDataFile, mergedData);
4274
+ this.logger.info("Save the scenario test data to " + dataFile + " as global for the following scenarios.");
4275
+ return;
4276
+ }
4077
4277
  process.env.GLOBAL_TEST_DATA_FILE = dataFile;
4078
4278
  this.logger.info("Save the scenario test data as global for the following scenarios.");
4079
4279
  }
@@ -4176,6 +4376,7 @@ class StableBrowser {
4176
4376
  if (world && world.attach) {
4177
4377
  world.attach(this.context.reportFolder, { mediaType: "text/plain" });
4178
4378
  }
4379
+ this.context.loadedRoutes = null;
4179
4380
  this.beforeScenarioCalled = true;
4180
4381
  if (scenario && scenario.pickle && scenario.pickle.name) {
4181
4382
  this.scenarioName = scenario.pickle.name;
@@ -4189,9 +4390,12 @@ class StableBrowser {
4189
4390
  if (this.tags === null && scenario && scenario.pickle && scenario.pickle.tags) {
4190
4391
  this.tags = scenario.pickle.tags.map((tag) => tag.name);
4191
4392
  // check if @global_test_data tag is present
4192
- if (this.tags.includes("@global_test_data")) {
4393
+ if (this.tags.includes("@global_test_data" /* TAG_CONSTANTS.GLOBAL_TEST_DATA */)) {
4193
4394
  this.saveTestDataAsGlobal({}, world);
4194
4395
  }
4396
+ if (this.tags.includes("@fast_mode" /* TAG_CONSTANTS.FAST_MODE */)) {
4397
+ this.fastMode = true;
4398
+ }
4195
4399
  }
4196
4400
  // update test data based on feature/scenario
4197
4401
  let envName = null;
@@ -4202,14 +4406,51 @@ class StableBrowser {
4202
4406
  await getTestData(envName, world, undefined, this.featureName, this.scenarioName, this.context);
4203
4407
  }
4204
4408
  await loadBrunoParams(this.context, this.context.environment.name);
4409
+ if ((process.env.TRACE === "true" || this.configuration.trace === true) && this.context) {
4410
+ this.trace = true;
4411
+ const traceFolder = path.join(this.context.reportFolder, "trace");
4412
+ if (!fs.existsSync(traceFolder)) {
4413
+ fs.mkdirSync(traceFolder, { recursive: true });
4414
+ }
4415
+ this.traceFolder = traceFolder;
4416
+ await this.context.playContext.tracing.start({ screenshots: true, snapshots: true });
4417
+ }
4418
+ }
4419
+ async afterScenario(world, scenario) {
4420
+ const id = scenario.testCaseStartedId;
4421
+ if (this.trace) {
4422
+ await this.context.playContext.tracing.stop({
4423
+ path: path.join(this.traceFolder, `trace-${id}.zip`),
4424
+ });
4425
+ }
4426
+ }
4427
+ getGherkinKeyword(step) {
4428
+ if (!step?.type) {
4429
+ return "";
4430
+ }
4431
+ switch (step.type) {
4432
+ case "Context":
4433
+ return "Given";
4434
+ case "Action":
4435
+ return "When";
4436
+ case "Outcome":
4437
+ return "Then";
4438
+ case "Conjunction":
4439
+ return "And";
4440
+ default:
4441
+ return "";
4442
+ }
4205
4443
  }
4206
- async afterScenario(world, scenario) { }
4207
4444
  async beforeStep(world, step) {
4208
- if (this.abortedExecution) {
4209
- throw new Error("Aborted");
4445
+ if (step?.pickleStep && this.trace) {
4446
+ const keyword = this.getGherkinKeyword(step.pickleStep);
4447
+ this.traceGroupName = `${keyword} ${step.pickleStep.text}`;
4448
+ await this.context.playContext.tracing.group(this.traceGroupName);
4210
4449
  }
4450
+ this.stepTags = [];
4211
4451
  if (!this.beforeScenarioCalled) {
4212
4452
  this.beforeScenario(world, step);
4453
+ this.context.loadedRoutes = null;
4213
4454
  }
4214
4455
  if (this.stepIndex === undefined) {
4215
4456
  this.stepIndex = 0;
@@ -4219,7 +4460,12 @@ class StableBrowser {
4219
4460
  }
4220
4461
  if (step && step.pickleStep && step.pickleStep.text) {
4221
4462
  this.stepName = step.pickleStep.text;
4222
- this.logger.info("step: " + this.stepName);
4463
+ let printableStepName = this.stepName;
4464
+ // take the printableStepName and replace quated value with \x1b[33m and \x1b[0m
4465
+ printableStepName = printableStepName.replace(/"([^"]*)"/g, (match, p1) => {
4466
+ return `\x1b[33m"${p1}"\x1b[0m`;
4467
+ });
4468
+ this.logger.info("\x1b[38;5;208mstep:\x1b[0m " + printableStepName);
4223
4469
  }
4224
4470
  else if (step && step.text) {
4225
4471
  this.stepName = step.text;
@@ -4234,7 +4480,10 @@ class StableBrowser {
4234
4480
  }
4235
4481
  if (this.initSnapshotTaken === false) {
4236
4482
  this.initSnapshotTaken = true;
4237
- if (world && world.attach && !process.env.DISABLE_SNAPSHOT && !this.fastMode) {
4483
+ if (world &&
4484
+ world.attach &&
4485
+ !process.env.DISABLE_SNAPSHOT &&
4486
+ (!this.fastMode || this.stepTags.includes("fast-mode"))) {
4238
4487
  const snapshot = await this.getAriaSnapshot();
4239
4488
  if (snapshot) {
4240
4489
  await world.attach(JSON.stringify(snapshot), "application/json+snapshot-before");
@@ -4244,7 +4493,11 @@ class StableBrowser {
4244
4493
  this.context.routeResults = null;
4245
4494
  this.context.loadedRoutes = null;
4246
4495
  await registerBeforeStepRoutes(this.context, this.stepName, world);
4247
- networkBeforeStep(this.stepName);
4496
+ networkBeforeStep(this.stepName, this.context);
4497
+ this.inStepReport = false;
4498
+ }
4499
+ setStepTags(tags) {
4500
+ this.stepTags = tags;
4248
4501
  }
4249
4502
  async getAriaSnapshot() {
4250
4503
  try {
@@ -4318,7 +4571,7 @@ class StableBrowser {
4318
4571
  state.payload = payload;
4319
4572
  if (commandStatus === "FAILED") {
4320
4573
  state.throwError = true;
4321
- throw new Error("Command failed");
4574
+ throw new Error(commandText);
4322
4575
  }
4323
4576
  }
4324
4577
  catch (e) {
@@ -4328,26 +4581,22 @@ class StableBrowser {
4328
4581
  await _commandFinally(state, this);
4329
4582
  }
4330
4583
  }
4331
- async afterStep(world, step) {
4584
+ async afterStep(world, step, result) {
4332
4585
  this.stepName = null;
4333
- if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
4334
- if (this.context.browserObject.context) {
4335
- await this.context.browserObject.context.tracing.stopChunk({
4336
- path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
4337
- });
4338
- if (world && world.attach) {
4339
- await world.attach(JSON.stringify({
4340
- type: "trace",
4341
- traceFilePath: `trace-${this.stepIndex}.zip`,
4342
- }), "application/json+trace");
4343
- }
4344
- // console.log("trace file created", `trace-${this.stepIndex}.zip`);
4345
- }
4346
- }
4347
4586
  if (this.context) {
4348
4587
  this.context.examplesRow = null;
4349
4588
  }
4350
- if (world && world.attach && !process.env.DISABLE_SNAPSHOT && !this.fastMode) {
4589
+ if (!this.inStepReport) {
4590
+ // check the step result
4591
+ if (result && result.status === "FAILED" && world && world.attach) {
4592
+ await this.addCommandToReport(result.message ? result.message : "Step failed", "FAILED", `${result.message}`, { type: "text", screenshot: true }, world);
4593
+ }
4594
+ }
4595
+ if (world &&
4596
+ world.attach &&
4597
+ !process.env.DISABLE_SNAPSHOT &&
4598
+ !this.fastMode &&
4599
+ !this.stepTags.includes("fast-mode")) {
4351
4600
  const snapshot = await this.getAriaSnapshot();
4352
4601
  if (snapshot) {
4353
4602
  const obj = {};
@@ -4355,6 +4604,11 @@ class StableBrowser {
4355
4604
  }
4356
4605
  }
4357
4606
  this.context.routeResults = await registerAfterStepRoutes(this.context, world);
4607
+ if (this.context.routeResults) {
4608
+ if (world && world.attach) {
4609
+ await world.attach(JSON.stringify(this.context.routeResults), "application/json+intercept-results");
4610
+ }
4611
+ }
4358
4612
  if (!process.env.TEMP_RUN) {
4359
4613
  const state = {
4360
4614
  world,
@@ -4378,10 +4632,15 @@ class StableBrowser {
4378
4632
  await _commandFinally(state, this);
4379
4633
  }
4380
4634
  }
4381
- networkAfterStep(this.stepName);
4635
+ networkAfterStep(this.stepName, this.context);
4382
4636
  if (process.env.TEMP_RUN === "true") {
4383
4637
  // Put a sleep for some time to allow the browser to finish processing
4384
- await new Promise((resolve) => setTimeout(resolve, 3000));
4638
+ if (!this.stepTags.includes("fast-mode")) {
4639
+ await new Promise((resolve) => setTimeout(resolve, 3000));
4640
+ }
4641
+ }
4642
+ if (this.trace) {
4643
+ await this.context.playContext.tracing.groupEnd();
4385
4644
  }
4386
4645
  }
4387
4646
  }