automation_model 1.0.471-dev → 1.0.471-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 (43) hide show
  1. package/lib/api.d.ts +42 -1
  2. package/lib/api.js +221 -47
  3. package/lib/api.js.map +1 -1
  4. package/lib/auto_page.js +38 -16
  5. package/lib/auto_page.js.map +1 -1
  6. package/lib/browser_manager.d.ts +5 -3
  7. package/lib/browser_manager.js +65 -18
  8. package/lib/browser_manager.js.map +1 -1
  9. package/lib/command_common.d.ts +6 -0
  10. package/lib/command_common.js +148 -0
  11. package/lib/command_common.js.map +1 -0
  12. package/lib/environment.js +5 -3
  13. package/lib/environment.js.map +1 -1
  14. package/lib/error-messages.d.ts +6 -0
  15. package/lib/error-messages.js +188 -0
  16. package/lib/error-messages.js.map +1 -0
  17. package/lib/index.d.ts +1 -0
  18. package/lib/index.js +1 -0
  19. package/lib/index.js.map +1 -1
  20. package/lib/init_browser.d.ts +1 -1
  21. package/lib/init_browser.js +31 -4
  22. package/lib/init_browser.js.map +1 -1
  23. package/lib/locate_element.js +5 -3
  24. package/lib/locate_element.js.map +1 -1
  25. package/lib/locator.d.ts +36 -0
  26. package/lib/locator.js +165 -0
  27. package/lib/locator.js.map +1 -1
  28. package/lib/network.d.ts +3 -0
  29. package/lib/network.js +144 -0
  30. package/lib/network.js.map +1 -0
  31. package/lib/stable_browser.d.ts +25 -19
  32. package/lib/stable_browser.js +633 -812
  33. package/lib/stable_browser.js.map +1 -1
  34. package/lib/table.d.ts +13 -0
  35. package/lib/table.js +187 -0
  36. package/lib/table.js.map +1 -0
  37. package/lib/test_context.d.ts +1 -0
  38. package/lib/test_context.js +12 -12
  39. package/lib/test_context.js.map +1 -1
  40. package/lib/utils.d.ts +3 -1
  41. package/lib/utils.js +68 -1
  42. package/lib/utils.js.map +1 -1
  43. package/package.json +5 -4
@@ -10,13 +10,15 @@ import { getDateTimeValue } from "./date_time.js";
10
10
  import drawRectangle from "./drawRect.js";
11
11
  //import { closeUnexpectedPopups } from "./popups.js";
12
12
  import { getTableCells, getTableData } from "./table_analyze.js";
13
- import objectPath from "object-path";
14
- import { decrypt } from "./utils.js";
13
+ import { maskValue, replaceWithLocalTestData } from "./utils.js";
15
14
  import csv from "csv-parser";
16
15
  import { Readable } from "node:stream";
17
16
  import readline from "readline";
18
17
  import { getContext } from "./init_browser.js";
19
18
  import { locate_element } from "./locate_element.js";
19
+ import { randomUUID } from "crypto";
20
+ import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
21
+ import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
20
22
  const Types = {
21
23
  CLICK: "click_element",
22
24
  NAVIGATE: "navigate",
@@ -43,20 +45,27 @@ const Types = {
43
45
  VERIFY_VISUAL: "verify_visual",
44
46
  LOAD_DATA: "load_data",
45
47
  SET_INPUT: "set_input",
48
+ WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
46
49
  };
47
50
  export const apps = {};
48
51
  class StableBrowser {
52
+ browser;
53
+ page;
54
+ logger;
55
+ context;
56
+ world;
57
+ project_path = null;
58
+ webLogFile = null;
59
+ networkLogger = null;
60
+ configuration = null;
61
+ appName = "main";
62
+ tags = null;
49
63
  constructor(browser, page, logger = null, context = null, world = null) {
50
64
  this.browser = browser;
51
65
  this.page = page;
52
66
  this.logger = logger;
53
67
  this.context = context;
54
68
  this.world = world;
55
- this.project_path = null;
56
- this.webLogFile = null;
57
- this.networkLogger = null;
58
- this.configuration = null;
59
- this.appName = "main";
60
69
  if (!this.logger) {
61
70
  this.logger = console;
62
71
  }
@@ -81,11 +90,31 @@ class StableBrowser {
81
90
  catch (e) {
82
91
  this.logger.error("unable to read ai_config.json");
83
92
  }
84
- context.pageLoading = { status: false };
85
- context.pages = [this.page];
86
93
  const logFolder = path.join(this.project_path, "logs", "web");
87
94
  this.world = world;
95
+ context.pages = [this.page];
96
+ context.pageLoading = { status: false };
88
97
  this.registerEventListeners(this.context);
98
+ registerNetworkEvents(this.world, this, this.context, this.page);
99
+ registerDownloadEvent(this.page, this.world, this.context);
100
+ }
101
+ async scrollPageToLoadLazyElements() {
102
+ let lastHeight = await this.page.evaluate(() => document.body.scrollHeight);
103
+ let retry = 0;
104
+ while (true) {
105
+ await this.page.evaluate(() => window.scrollBy(0, window.innerHeight));
106
+ await new Promise((resolve) => setTimeout(resolve, 1000));
107
+ let newHeight = await this.page.evaluate(() => document.body.scrollHeight);
108
+ if (newHeight === lastHeight) {
109
+ break;
110
+ }
111
+ lastHeight = newHeight;
112
+ retry++;
113
+ if (retry > 10) {
114
+ break;
115
+ }
116
+ }
117
+ await this.page.evaluate(() => window.scrollTo(0, 0));
89
118
  }
90
119
  registerEventListeners(context) {
91
120
  this.registerConsoleLogListener(this.page, context);
@@ -94,10 +123,17 @@ class StableBrowser {
94
123
  context.pageLoading = { status: false };
95
124
  }
96
125
  context.playContext.on("page", async function (page) {
126
+ if (this.configuration && this.configuration.closePopups === true) {
127
+ console.log("close unexpected popups");
128
+ await page.close();
129
+ return;
130
+ }
97
131
  context.pageLoading.status = true;
98
132
  this.page = page;
99
133
  context.page = page;
100
134
  context.pages.push(page);
135
+ registerNetworkEvents(this.world, this, context, this.page);
136
+ registerDownloadEvent(this.page, this.world, context);
101
137
  page.on("close", async () => {
102
138
  if (this.context && this.context.pages && this.context.pages.length > 1) {
103
139
  this.context.pages.pop();
@@ -127,9 +163,9 @@ class StableBrowser {
127
163
  if (this.appName === appName) {
128
164
  return;
129
165
  }
130
- let newContextCreated = false;
166
+ let navigate = false;
131
167
  if (!apps[appName]) {
132
- let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
168
+ let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
133
169
  newContextCreated = true;
134
170
  apps[appName] = {
135
171
  context: newContext,
@@ -142,8 +178,7 @@ class StableBrowser {
142
178
  this._copyContext(apps[appName], this);
143
179
  apps[this.appName] = tempContext;
144
180
  this.appName = appName;
145
- if (newContextCreated) {
146
- this.registerEventListeners(this.context);
181
+ if (navigate) {
147
182
  await this.goto(this.context.environment.baseUrl);
148
183
  await this.waitForPageLoad();
149
184
  }
@@ -169,7 +204,6 @@ class StableBrowser {
169
204
  this.context.webLogger = [];
170
205
  }
171
206
  page.on("console", async (msg) => {
172
- var _a;
173
207
  const obj = {
174
208
  type: msg.type(),
175
209
  text: msg.text(),
@@ -178,7 +212,7 @@ class StableBrowser {
178
212
  };
179
213
  this.context.webLogger.push(obj);
180
214
  if (msg.type() === "error") {
181
- (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
215
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
182
216
  }
183
217
  });
184
218
  }
@@ -187,7 +221,6 @@ class StableBrowser {
187
221
  this.context.networkLogger = [];
188
222
  }
189
223
  page.on("request", async (data) => {
190
- var _a;
191
224
  const startTime = new Date().getTime();
192
225
  try {
193
226
  const pageUrl = new URL(page.url());
@@ -212,10 +245,10 @@ class StableBrowser {
212
245
  startTime,
213
246
  };
214
247
  context.networkLogger.push(obj);
215
- (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
248
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
216
249
  }
217
250
  catch (error) {
218
- console.error("Error in request listener", error);
251
+ // console.error("Error in request listener", error);
219
252
  context.networkLogger.push({
220
253
  error: "not able to listen",
221
254
  message: error.message,
@@ -237,20 +270,6 @@ class StableBrowser {
237
270
  timeout: 60000,
238
271
  });
239
272
  }
240
- _validateSelectors(selectors) {
241
- if (!selectors) {
242
- throw new Error("selectors is null");
243
- }
244
- if (!selectors.locators) {
245
- throw new Error("selectors.locators is null");
246
- }
247
- if (!Array.isArray(selectors.locators)) {
248
- throw new Error("selectors.locators expected to be array");
249
- }
250
- if (selectors.locators.length === 0) {
251
- throw new Error("selectors.locators expected to be non empty array");
252
- }
253
- }
254
273
  _fixUsingParams(text, _params) {
255
274
  if (!_params || typeof text !== "string") {
256
275
  return text;
@@ -318,7 +337,7 @@ class StableBrowser {
318
337
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
319
338
  }
320
339
  }
321
- if (locator === null || locator === void 0 ? void 0 : locator.engine) {
340
+ if (locator?.engine) {
322
341
  if (locator.engine === "css") {
323
342
  locatorReturn = scope.locator(locator.selector);
324
343
  }
@@ -342,7 +361,7 @@ class StableBrowser {
342
361
  if (css && css.locator) {
343
362
  css = css.locator;
344
363
  }
345
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, false, _params);
364
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
346
365
  if (result.elementCount === 0) {
347
366
  return;
348
367
  }
@@ -357,7 +376,7 @@ class StableBrowser {
357
376
  }
358
377
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
359
378
  //const stringifyText = JSON.stringify(text);
360
- return await scope.evaluate(([text, tag, regex, partial]) => {
379
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
361
380
  function isParent(parent, child) {
362
381
  let currentNode = child.parentNode;
363
382
  while (currentNode !== null) {
@@ -394,7 +413,7 @@ class StableBrowser {
394
413
  }
395
414
  document.collectAllShadowDomElements = collectAllShadowDomElements;
396
415
  if (!tag) {
397
- tag = "*";
416
+ tag = "*:not(script, style, head)";
398
417
  }
399
418
  let regexpSearch = document.getRegex(text);
400
419
  if (regexpSearch) {
@@ -476,6 +495,15 @@ class StableBrowser {
476
495
  }, [text1, tag1, regex1, partial1]);
477
496
  }
478
497
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
498
+ if (!info) {
499
+ info = {};
500
+ }
501
+ if (!info.failCause) {
502
+ info.failCause = {};
503
+ }
504
+ if (!info.log) {
505
+ info.log = "";
506
+ }
479
507
  let locatorSearch = selectorHierarchy[index];
480
508
  try {
481
509
  locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
@@ -488,13 +516,18 @@ class StableBrowser {
488
516
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
489
517
  let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
490
518
  if (!locatorString) {
519
+ info.failCause.textNotFound = true;
520
+ info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
491
521
  return;
492
522
  }
493
523
  locator = this._getLocator({ css: locatorString }, scope, _params);
494
524
  }
495
525
  else if (locatorSearch.text) {
496
- let result = await this._locateElementByText(scope, this._fixUsingParams(locatorSearch.text, _params), locatorSearch.tag, false, locatorSearch.partial === true, _params);
526
+ let text = this._fixUsingParams(locatorSearch.text, _params);
527
+ let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
497
528
  if (result.elementCount === 0) {
529
+ info.failCause.textNotFound = true;
530
+ info.failCause.lastError = "failed to locate element by text: " + text;
498
531
  return;
499
532
  }
500
533
  locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
@@ -511,6 +544,9 @@ class StableBrowser {
511
544
  // cssHref = true;
512
545
  // }
513
546
  let count = await locator.count();
547
+ if (count > 0 && !info.failCause.count) {
548
+ info.failCause.count = count;
549
+ }
514
550
  //info.log += "total elements found " + count + "\n";
515
551
  //let visibleCount = 0;
516
552
  let visibleLocator = null;
@@ -528,6 +564,8 @@ class StableBrowser {
528
564
  foundLocators.push(locator.nth(j));
529
565
  }
530
566
  else {
567
+ info.failCause.visible = visible;
568
+ info.failCause.enabled = enabled;
531
569
  if (!info.printMessages) {
532
570
  info.printMessages = {};
533
571
  }
@@ -539,6 +577,11 @@ class StableBrowser {
539
577
  }
540
578
  }
541
579
  async closeUnexpectedPopups(info, _params) {
580
+ if (!info) {
581
+ info = {};
582
+ info.failCause = {};
583
+ info.log = "";
584
+ }
542
585
  if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
543
586
  if (!info) {
544
587
  info = {};
@@ -570,16 +613,31 @@ class StableBrowser {
570
613
  }
571
614
  if (result.foundElements.length > 0) {
572
615
  let dialogCloseLocator = result.foundElements[0].locator;
573
- await dialogCloseLocator.click();
574
- // wait for the dialog to close
575
- await dialogCloseLocator.waitFor({ state: "hidden" });
616
+ try {
617
+ await scope?.evaluate(() => {
618
+ window.__isClosingPopups = true;
619
+ });
620
+ await dialogCloseLocator.click();
621
+ // wait for the dialog to close
622
+ await dialogCloseLocator.waitFor({ state: "hidden" });
623
+ }
624
+ catch (e) {
625
+ }
626
+ finally {
627
+ await scope?.evaluate(() => {
628
+ window.__isClosingPopups = false;
629
+ });
630
+ }
576
631
  return { rerun: true };
577
632
  }
578
633
  }
579
634
  }
580
635
  return { rerun: false };
581
636
  }
582
- async _locate(selectors, info, _params, timeout = 30000) {
637
+ async _locate(selectors, info, _params, timeout) {
638
+ if (!timeout) {
639
+ timeout = 30000;
640
+ }
583
641
  for (let i = 0; i < 3; i++) {
584
642
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
585
643
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -593,16 +651,16 @@ class StableBrowser {
593
651
  }
594
652
  throw new Error("unable to locate element " + JSON.stringify(selectors));
595
653
  }
596
- async _locate_internal(selectors, info, _params, timeout = 30000) {
597
- let highPriorityTimeout = 5000;
598
- let visibleOnlyTimeout = 6000;
599
- let startTime = performance.now();
600
- let locatorsCount = 0;
601
- //let arrayMode = Array.isArray(selectors);
654
+ async _findFrameScope(selectors, timeout = 30000, info) {
655
+ if (!info) {
656
+ info = {};
657
+ info.failCause = {};
658
+ info.log = "";
659
+ }
660
+ let startTime = Date.now();
602
661
  let scope = this.page;
603
- // for the simple click usecase
604
662
  if (selectors.frame) {
605
- scope = selectors.frame;
663
+ return selectors.frame;
606
664
  }
607
665
  if (selectors.iframe_src || selectors.frameLocators) {
608
666
  const findFrame = async (frame, framescope) => {
@@ -630,7 +688,6 @@ class StableBrowser {
630
688
  }
631
689
  return framescope;
632
690
  };
633
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
634
691
  while (true) {
635
692
  let frameFound = false;
636
693
  if (selectors.nestFrmLoc) {
@@ -653,7 +710,9 @@ class StableBrowser {
653
710
  }
654
711
  if (!scope) {
655
712
  info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
656
- if (performance.now() - startTime > timeout) {
713
+ if (Date.now() - startTime > timeout) {
714
+ info.failCause.iframeNotFound = true;
715
+ info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
657
716
  throw new Error("unable to locate iframe " + selectors.iframe_src);
658
717
  }
659
718
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -663,6 +722,31 @@ class StableBrowser {
663
722
  }
664
723
  }
665
724
  }
725
+ if (!scope) {
726
+ scope = this.page;
727
+ }
728
+ return scope;
729
+ }
730
+ async _getDocumentBody(selectors, timeout = 30000, info) {
731
+ let scope = await this._findFrameScope(selectors, timeout, info);
732
+ return scope.evaluate(() => {
733
+ var bodyContent = document.body.innerHTML;
734
+ return bodyContent;
735
+ });
736
+ }
737
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
738
+ if (!info) {
739
+ info = {};
740
+ info.failCause = {};
741
+ info.log = "";
742
+ }
743
+ let highPriorityTimeout = 5000;
744
+ let visibleOnlyTimeout = 6000;
745
+ let startTime = Date.now();
746
+ let locatorsCount = 0;
747
+ let lazy_scroll = false;
748
+ //let arrayMode = Array.isArray(selectors);
749
+ let scope = await this._findFrameScope(selectors, timeout, info);
666
750
  let selectorsLocators = null;
667
751
  selectorsLocators = selectors.locators;
668
752
  // group selectors by priority
@@ -749,14 +833,18 @@ class StableBrowser {
749
833
  return maxCountElement.locator;
750
834
  }
751
835
  }
752
- if (performance.now() - startTime > timeout) {
836
+ if (Date.now() - startTime > timeout) {
753
837
  break;
754
838
  }
755
- if (performance.now() - startTime > highPriorityTimeout) {
839
+ if (Date.now() - startTime > highPriorityTimeout) {
756
840
  info.log += "high priority timeout, will try all elements" + "\n";
757
841
  highPriorityOnly = false;
842
+ if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
843
+ lazy_scroll = true;
844
+ await this.scrollPageToLoadLazyElements();
845
+ }
758
846
  }
759
- if (performance.now() - startTime > visibleOnlyTimeout) {
847
+ if (Date.now() - startTime > visibleOnlyTimeout) {
760
848
  info.log += "visible only timeout, will try all elements" + "\n";
761
849
  visibleOnly = false;
762
850
  }
@@ -764,6 +852,8 @@ class StableBrowser {
764
852
  }
765
853
  this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
766
854
  info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
855
+ info.failCause.locatorNotFound = true;
856
+ info.failCause.lastError = "failed to locate unique element";
767
857
  throw new Error("failed to locate first element no elements found, " + info.log);
768
858
  }
769
859
  async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
@@ -795,6 +885,9 @@ class StableBrowser {
795
885
  });
796
886
  result.locatorIndex = i;
797
887
  }
888
+ if (foundLocators.length > 1) {
889
+ info.failCause.foundMultiple = true;
890
+ }
798
891
  }
799
892
  return result;
800
893
  }
@@ -807,12 +900,12 @@ class StableBrowser {
807
900
  while (true) {
808
901
  try {
809
902
  const result = await locate_element(this.context, elementDescription, "click");
810
- if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
903
+ if (result?.elementNumber >= 0) {
811
904
  const selectors = {
812
- frame: result === null || result === void 0 ? void 0 : result.frame,
905
+ frame: result?.frame,
813
906
  locators: [
814
907
  {
815
- css: result === null || result === void 0 ? void 0 : result.css,
908
+ css: result?.css,
816
909
  },
817
910
  ],
818
911
  };
@@ -821,8 +914,9 @@ class StableBrowser {
821
914
  }
822
915
  }
823
916
  catch (e) {
824
- if (performance.now() - startTime > timeout) {
825
- throw e;
917
+ if (Date.now() - startTime > timeout) {
918
+ // throw e;
919
+ await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
826
920
  }
827
921
  }
828
922
  await new Promise((resolve) => setTimeout(resolve, 3000));
@@ -837,12 +931,12 @@ class StableBrowser {
837
931
  while (true) {
838
932
  try {
839
933
  const result = await locate_element(this.context, elementDescription, "fill", value);
840
- if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
934
+ if (result?.elementNumber >= 0) {
841
935
  const selectors = {
842
- frame: result === null || result === void 0 ? void 0 : result.frame,
936
+ frame: result?.frame,
843
937
  locators: [
844
938
  {
845
- css: result === null || result === void 0 ? void 0 : result.css,
939
+ css: result?.css,
846
940
  },
847
941
  ],
848
942
  };
@@ -851,93 +945,69 @@ class StableBrowser {
851
945
  }
852
946
  }
853
947
  catch (e) {
854
- if (performance.now() - startTime > timeout) {
855
- throw e;
948
+ if (Date.now() - startTime > timeout) {
949
+ // throw e;
950
+ await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
856
951
  }
857
952
  }
858
953
  await new Promise((resolve) => setTimeout(resolve, 3000));
859
954
  }
860
955
  }
861
956
  async click(selectors, _params, options = {}, world = null) {
862
- this._validateSelectors(selectors);
863
- const startTime = Date.now();
864
- if (options && options.context) {
865
- selectors.locators[0].text = options.context;
866
- }
867
- const info = {};
868
- info.log = "***** click on " + selectors.element_name + " *****\n";
869
- info.operation = "click";
870
- info.selectors = selectors;
871
- let error = null;
872
- let screenshotId = null;
873
- let screenshotPath = null;
957
+ const state = {
958
+ selectors,
959
+ _params,
960
+ options,
961
+ world,
962
+ text: "Click element",
963
+ type: Types.CLICK,
964
+ operation: "click",
965
+ log: "***** click on " + selectors.element_name + " *****\n",
966
+ };
874
967
  try {
875
- let element = await this._locate(selectors, info, _params);
876
- await this.scrollIfNeeded(element, info);
877
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
968
+ await _preCommand(state, this);
969
+ if (state.options && state.options.context) {
970
+ state.selectors.locators[0].text = state.options.context;
971
+ }
878
972
  try {
879
- await this._highlightElements(element);
880
- await element.click();
881
- await new Promise((resolve) => setTimeout(resolve, 1000));
973
+ await state.element.click();
974
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
882
975
  }
883
976
  catch (e) {
884
977
  // await this.closeUnexpectedPopups();
885
- info.log += "click failed, will try again" + "\n";
886
- element = await this._locate(selectors, info, _params);
887
- await element.dispatchEvent("click");
888
- await new Promise((resolve) => setTimeout(resolve, 1000));
978
+ state.element = await this._locate(selectors, state.info, _params);
979
+ await state.element.dispatchEvent("click");
980
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
889
981
  }
890
982
  await this.waitForPageLoad();
891
- return info;
983
+ return state.info;
892
984
  }
893
985
  catch (e) {
894
- this.logger.error("click failed " + info.log);
895
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
896
- info.screenshotPath = screenshotPath;
897
- Object.assign(e, { info: info });
898
- error = e;
899
- throw e;
986
+ await _commandError(state, e, this);
900
987
  }
901
988
  finally {
902
- const endTime = Date.now();
903
- this._reportToWorld(world, {
904
- element_name: selectors.element_name,
905
- type: Types.CLICK,
906
- text: `Click element`,
907
- screenshotId,
908
- result: error
909
- ? {
910
- status: "FAILED",
911
- startTime,
912
- endTime,
913
- message: error === null || error === void 0 ? void 0 : error.message,
914
- }
915
- : {
916
- status: "PASSED",
917
- startTime,
918
- endTime,
919
- },
920
- info: info,
921
- });
989
+ _commandFinally(state, this);
922
990
  }
923
991
  }
924
992
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
925
- this._validateSelectors(selectors);
926
- const startTime = Date.now();
927
- const info = {};
928
- info.log = "";
929
- info.operation = "setCheck";
930
- info.checked = checked;
931
- info.selectors = selectors;
932
- let error = null;
933
- let screenshotId = null;
934
- let screenshotPath = null;
993
+ const state = {
994
+ selectors,
995
+ _params,
996
+ options,
997
+ world,
998
+ type: checked ? Types.CHECK : Types.UNCHECK,
999
+ text: checked ? `Check element` : `Uncheck element`,
1000
+ operation: "setCheck",
1001
+ log: "***** check " + selectors.element_name + " *****\n",
1002
+ };
935
1003
  try {
936
- let element = await this._locate(selectors, info, _params);
937
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1004
+ await _preCommand(state, this);
1005
+ state.info.checked = checked;
1006
+ // let element = await this._locate(selectors, info, _params);
1007
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
938
1008
  try {
939
- await this._highlightElements(element);
940
- await element.setChecked(checked);
1009
+ // await this._highlightElements(element);
1010
+ await state.element.setChecked(checked);
941
1011
  await new Promise((resolve) => setTimeout(resolve, 1000));
942
1012
  }
943
1013
  catch (e) {
@@ -946,179 +1016,108 @@ class StableBrowser {
946
1016
  }
947
1017
  else {
948
1018
  //await this.closeUnexpectedPopups();
949
- info.log += "setCheck failed, will try again" + "\n";
950
- element = await this._locate(selectors, info, _params);
951
- await element.setChecked(checked, { timeout: 5000, force: true });
1019
+ state.info.log += "setCheck failed, will try again" + "\n";
1020
+ state.element = await this._locate(selectors, state.info, _params);
1021
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
952
1022
  await new Promise((resolve) => setTimeout(resolve, 1000));
953
1023
  }
954
1024
  }
955
1025
  await this.waitForPageLoad();
956
- return info;
1026
+ return state.info;
957
1027
  }
958
1028
  catch (e) {
959
- this.logger.error("setCheck failed " + info.log);
960
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
961
- info.screenshotPath = screenshotPath;
962
- Object.assign(e, { info: info });
963
- error = e;
964
- throw e;
1029
+ await _commandError(state, e, this);
965
1030
  }
966
1031
  finally {
967
- const endTime = Date.now();
968
- this._reportToWorld(world, {
969
- element_name: selectors.element_name,
970
- type: checked ? Types.CHECK : Types.UNCHECK,
971
- text: checked ? `Check element` : `Uncheck element`,
972
- screenshotId,
973
- result: error
974
- ? {
975
- status: "FAILED",
976
- startTime,
977
- endTime,
978
- message: error === null || error === void 0 ? void 0 : error.message,
979
- }
980
- : {
981
- status: "PASSED",
982
- startTime,
983
- endTime,
984
- },
985
- info: info,
986
- });
1032
+ _commandFinally(state, this);
987
1033
  }
988
1034
  }
989
1035
  async hover(selectors, _params, options = {}, world = null) {
990
- this._validateSelectors(selectors);
991
- const startTime = Date.now();
992
- const info = {};
993
- info.log = "";
994
- info.operation = "hover";
995
- info.selectors = selectors;
996
- let error = null;
997
- let screenshotId = null;
998
- let screenshotPath = null;
1036
+ const state = {
1037
+ selectors,
1038
+ _params,
1039
+ options,
1040
+ world,
1041
+ type: Types.HOVER,
1042
+ text: `Hover element`,
1043
+ operation: "hover",
1044
+ log: "***** hover " + selectors.element_name + " *****\n",
1045
+ };
999
1046
  try {
1000
- let element = await this._locate(selectors, info, _params);
1001
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1047
+ await _preCommand(state, this);
1002
1048
  try {
1003
- await this._highlightElements(element);
1004
- await element.hover();
1049
+ await state.element.hover();
1005
1050
  await new Promise((resolve) => setTimeout(resolve, 1000));
1006
1051
  }
1007
1052
  catch (e) {
1008
1053
  //await this.closeUnexpectedPopups();
1009
- info.log += "hover failed, will try again" + "\n";
1010
- element = await this._locate(selectors, info, _params);
1011
- await element.hover({ timeout: 10000 });
1054
+ state.info.log += "hover failed, will try again" + "\n";
1055
+ state.element = await this._locate(selectors, state.info, _params);
1056
+ await state.element.hover({ timeout: 10000 });
1012
1057
  await new Promise((resolve) => setTimeout(resolve, 1000));
1013
1058
  }
1014
1059
  await this.waitForPageLoad();
1015
- return info;
1060
+ return state.info;
1016
1061
  }
1017
1062
  catch (e) {
1018
- this.logger.error("hover failed " + info.log);
1019
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1020
- info.screenshotPath = screenshotPath;
1021
- Object.assign(e, { info: info });
1022
- error = e;
1023
- throw e;
1063
+ await _commandError(state, e, this);
1024
1064
  }
1025
1065
  finally {
1026
- const endTime = Date.now();
1027
- this._reportToWorld(world, {
1028
- element_name: selectors.element_name,
1029
- type: Types.HOVER,
1030
- text: `Hover element`,
1031
- screenshotId,
1032
- result: error
1033
- ? {
1034
- status: "FAILED",
1035
- startTime,
1036
- endTime,
1037
- message: error === null || error === void 0 ? void 0 : error.message,
1038
- }
1039
- : {
1040
- status: "PASSED",
1041
- startTime,
1042
- endTime,
1043
- },
1044
- info: info,
1045
- });
1066
+ _commandFinally(state, this);
1046
1067
  }
1047
1068
  }
1048
1069
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
1049
- this._validateSelectors(selectors);
1050
1070
  if (!values) {
1051
1071
  throw new Error("values is null");
1052
1072
  }
1053
- const startTime = Date.now();
1054
- let error = null;
1055
- let screenshotId = null;
1056
- let screenshotPath = null;
1057
- const info = {};
1058
- info.log = "";
1059
- info.operation = "selectOptions";
1060
- info.selectors = selectors;
1073
+ const state = {
1074
+ selectors,
1075
+ _params,
1076
+ options,
1077
+ world,
1078
+ value: values.toString(),
1079
+ type: Types.SELECT,
1080
+ text: `Select option: ${values}`,
1081
+ operation: "selectOption",
1082
+ log: "***** select option " + selectors.element_name + " *****\n",
1083
+ };
1061
1084
  try {
1062
- let element = await this._locate(selectors, info, _params);
1063
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1085
+ await _preCommand(state, this);
1064
1086
  try {
1065
- await this._highlightElements(element);
1066
- await element.selectOption(values);
1087
+ await state.element.selectOption(values);
1067
1088
  }
1068
1089
  catch (e) {
1069
1090
  //await this.closeUnexpectedPopups();
1070
- info.log += "selectOption failed, will try force" + "\n";
1071
- await element.selectOption(values, { timeout: 10000, force: true });
1091
+ state.info.log += "selectOption failed, will try force" + "\n";
1092
+ await state.element.selectOption(values, { timeout: 10000, force: true });
1072
1093
  }
1073
1094
  await this.waitForPageLoad();
1074
- return info;
1095
+ return state.info;
1075
1096
  }
1076
1097
  catch (e) {
1077
- this.logger.error("selectOption failed " + info.log);
1078
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1079
- info.screenshotPath = screenshotPath;
1080
- Object.assign(e, { info: info });
1081
- this.logger.info("click failed, will try next selector");
1082
- error = e;
1083
- throw e;
1098
+ await _commandError(state, e, this);
1084
1099
  }
1085
1100
  finally {
1086
- const endTime = Date.now();
1087
- this._reportToWorld(world, {
1088
- element_name: selectors.element_name,
1089
- type: Types.SELECT,
1090
- text: `Select option: ${values}`,
1091
- value: values.toString(),
1092
- screenshotId,
1093
- result: error
1094
- ? {
1095
- status: "FAILED",
1096
- startTime,
1097
- endTime,
1098
- message: error === null || error === void 0 ? void 0 : error.message,
1099
- }
1100
- : {
1101
- status: "PASSED",
1102
- startTime,
1103
- endTime,
1104
- },
1105
- info: info,
1106
- });
1101
+ _commandFinally(state, this);
1107
1102
  }
1108
1103
  }
1109
1104
  async type(_value, _params = null, options = {}, world = null) {
1110
- const startTime = Date.now();
1111
- let error = null;
1112
- let screenshotId = null;
1113
- let screenshotPath = null;
1114
- const info = {};
1115
- info.log = "";
1116
- info.operation = "type";
1117
- _value = this._fixUsingParams(_value, _params);
1118
- info.value = _value;
1105
+ const state = {
1106
+ value: _value,
1107
+ _params,
1108
+ options,
1109
+ world,
1110
+ locate: false,
1111
+ scroll: false,
1112
+ highlight: false,
1113
+ type: Types.TYPE_PRESS,
1114
+ text: `Type value: ${_value}`,
1115
+ operation: "type",
1116
+ log: "",
1117
+ };
1119
1118
  try {
1120
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1121
- const valueSegment = _value.split("&&");
1119
+ await _preCommand(state, this);
1120
+ const valueSegment = state.value.split("&&");
1122
1121
  for (let i = 0; i < valueSegment.length; i++) {
1123
1122
  if (i > 0) {
1124
1123
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1138,134 +1137,76 @@ class StableBrowser {
1138
1137
  await this.page.keyboard.type(value);
1139
1138
  }
1140
1139
  }
1141
- return info;
1140
+ return state.info;
1142
1141
  }
1143
1142
  catch (e) {
1144
- //await this.closeUnexpectedPopups();
1145
- this.logger.error("type failed " + info.log);
1146
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1147
- info.screenshotPath = screenshotPath;
1148
- Object.assign(e, { info: info });
1149
- error = e;
1150
- throw e;
1143
+ await _commandError(state, e, this);
1151
1144
  }
1152
1145
  finally {
1153
- const endTime = Date.now();
1154
- this._reportToWorld(world, {
1155
- type: Types.TYPE_PRESS,
1156
- screenshotId,
1157
- value: _value,
1158
- text: `type value: ${_value}`,
1159
- result: error
1160
- ? {
1161
- status: "FAILED",
1162
- startTime,
1163
- endTime,
1164
- message: error === null || error === void 0 ? void 0 : error.message,
1165
- }
1166
- : {
1167
- status: "PASSED",
1168
- startTime,
1169
- endTime,
1170
- },
1171
- info: info,
1172
- });
1146
+ _commandFinally(state, this);
1173
1147
  }
1174
1148
  }
1175
1149
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1176
- // set input value for non fillable inputs like date, time, range, color, etc.
1177
- this._validateSelectors(selectors);
1178
- const startTime = Date.now();
1179
- const info = {};
1180
- info.log = "***** set input value " + selectors.element_name + " *****\n";
1181
- info.operation = "setInputValue";
1182
- info.selectors = selectors;
1183
- value = this._fixUsingParams(value, _params);
1184
- info.value = value;
1185
- let error = null;
1186
- let screenshotId = null;
1187
- let screenshotPath = null;
1150
+ const state = {
1151
+ selectors,
1152
+ _params,
1153
+ value,
1154
+ options,
1155
+ world,
1156
+ type: Types.SET_INPUT,
1157
+ text: `Set input value`,
1158
+ operation: "setInputValue",
1159
+ log: "***** set input value " + selectors.element_name + " *****\n",
1160
+ };
1188
1161
  try {
1189
- value = await this._replaceWithLocalData(value, this);
1190
- let element = await this._locate(selectors, info, _params);
1191
- await this.scrollIfNeeded(element, info);
1192
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1193
- await this._highlightElements(element);
1162
+ await _preCommand(state, this);
1163
+ let value = await this._replaceWithLocalData(state.value, this);
1194
1164
  try {
1195
- await element.evaluateHandle((el, value) => {
1165
+ await state.element.evaluateHandle((el, value) => {
1196
1166
  el.value = value;
1197
1167
  }, value);
1198
1168
  }
1199
1169
  catch (error) {
1200
1170
  this.logger.error("setInputValue failed, will try again");
1201
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1202
- info.screenshotPath = screenshotPath;
1203
- Object.assign(error, { info: info });
1204
- await element.evaluateHandle((el, value) => {
1171
+ await _screenshot(state, this);
1172
+ Object.assign(error, { info: state.info });
1173
+ await state.element.evaluateHandle((el, value) => {
1205
1174
  el.value = value;
1206
1175
  });
1207
1176
  }
1208
1177
  }
1209
1178
  catch (e) {
1210
- this.logger.error("setInputValue failed " + info.log);
1211
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1212
- info.screenshotPath = screenshotPath;
1213
- Object.assign(e, { info: info });
1214
- error = e;
1215
- throw e;
1179
+ await _commandError(state, e, this);
1216
1180
  }
1217
1181
  finally {
1218
- const endTime = Date.now();
1219
- this._reportToWorld(world, {
1220
- element_name: selectors.element_name,
1221
- type: Types.SET_INPUT,
1222
- text: `Set input value`,
1223
- value: value,
1224
- screenshotId,
1225
- result: error
1226
- ? {
1227
- status: "FAILED",
1228
- startTime,
1229
- endTime,
1230
- message: error === null || error === void 0 ? void 0 : error.message,
1231
- }
1232
- : {
1233
- status: "PASSED",
1234
- startTime,
1235
- endTime,
1236
- },
1237
- info: info,
1238
- });
1182
+ _commandFinally(state, this);
1239
1183
  }
1240
1184
  }
1241
1185
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1242
- this._validateSelectors(selectors);
1243
- const startTime = Date.now();
1244
- let error = null;
1245
- let screenshotId = null;
1246
- let screenshotPath = null;
1247
- const info = {};
1248
- info.log = "";
1249
- info.operation = Types.SET_DATE_TIME;
1250
- info.selectors = selectors;
1251
- info.value = value;
1186
+ const state = {
1187
+ selectors,
1188
+ _params,
1189
+ value: await this._replaceWithLocalData(value, this),
1190
+ options,
1191
+ world,
1192
+ type: Types.SET_DATE_TIME,
1193
+ text: `Set date time value: ${value}`,
1194
+ operation: "setDateTime",
1195
+ log: "***** set date time value " + selectors.element_name + " *****\n",
1196
+ throwError: false,
1197
+ };
1252
1198
  try {
1253
- value = await this._replaceWithLocalData(value, this);
1254
- let element = await this._locate(selectors, info, _params);
1255
- //insert red border around the element
1256
- await this.scrollIfNeeded(element, info);
1257
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1258
- await this._highlightElements(element);
1199
+ await _preCommand(state, this);
1259
1200
  try {
1260
- await element.click();
1201
+ await state.element.click();
1261
1202
  await new Promise((resolve) => setTimeout(resolve, 500));
1262
1203
  if (format) {
1263
- value = dayjs(value).format(format);
1264
- await element.fill(value);
1204
+ state.value = dayjs(state.value).format(format);
1205
+ await state.element.fill(state.value);
1265
1206
  }
1266
1207
  else {
1267
- const dateTimeValue = await getDateTimeValue({ value, element });
1268
- await element.evaluateHandle((el, dateTimeValue) => {
1208
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1209
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1269
1210
  el.value = ""; // clear input
1270
1211
  el.value = dateTimeValue;
1271
1212
  }, dateTimeValue);
@@ -1278,20 +1219,19 @@ class StableBrowser {
1278
1219
  }
1279
1220
  catch (err) {
1280
1221
  //await this.closeUnexpectedPopups();
1281
- this.logger.error("setting date time input failed " + JSON.stringify(info));
1222
+ this.logger.error("setting date time input failed " + JSON.stringify(state.info));
1282
1223
  this.logger.info("Trying again");
1283
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1284
- info.screenshotPath = screenshotPath;
1285
- Object.assign(err, { info: info });
1224
+ await _screenshot(state, this);
1225
+ Object.assign(err, { info: state.info });
1286
1226
  await element.click();
1287
1227
  await new Promise((resolve) => setTimeout(resolve, 500));
1288
1228
  if (format) {
1289
- value = dayjs(value).format(format);
1290
- await element.fill(value);
1229
+ state.value = dayjs(state.value).format(format);
1230
+ await state.element.fill(state.value);
1291
1231
  }
1292
1232
  else {
1293
- const dateTimeValue = await getDateTimeValue({ value, element });
1294
- await element.evaluateHandle((el, dateTimeValue) => {
1233
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1234
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1295
1235
  el.value = ""; // clear input
1296
1236
  el.value = dateTimeValue;
1297
1237
  }, dateTimeValue);
@@ -1304,60 +1244,39 @@ class StableBrowser {
1304
1244
  }
1305
1245
  }
1306
1246
  catch (e) {
1307
- error = e;
1308
- throw e;
1247
+ await _commandError(state, e, this);
1309
1248
  }
1310
1249
  finally {
1311
- const endTime = Date.now();
1312
- this._reportToWorld(world, {
1313
- element_name: selectors.element_name,
1314
- type: Types.SET_DATE_TIME,
1315
- screenshotId,
1316
- value: value,
1317
- text: `setDateTime input with value: ${value}`,
1318
- result: error
1319
- ? {
1320
- status: "FAILED",
1321
- startTime,
1322
- endTime,
1323
- message: error === null || error === void 0 ? void 0 : error.message,
1324
- }
1325
- : {
1326
- status: "PASSED",
1327
- startTime,
1328
- endTime,
1329
- },
1330
- info: info,
1331
- });
1250
+ _commandFinally(state, this);
1332
1251
  }
1333
1252
  }
1334
1253
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1335
1254
  _value = unEscapeString(_value);
1336
- this._validateSelectors(selectors);
1337
- const startTime = Date.now();
1338
- let error = null;
1339
- let screenshotId = null;
1340
- let screenshotPath = null;
1341
- const info = {};
1342
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1343
- info.operation = "clickType";
1344
- info.selectors = selectors;
1345
1255
  const newValue = await this._replaceWithLocalData(_value, world);
1256
+ const state = {
1257
+ selectors,
1258
+ _params,
1259
+ value: newValue,
1260
+ originalValue: _value,
1261
+ options,
1262
+ world,
1263
+ type: Types.FILL,
1264
+ text: `Click type input with value: ${_value}`,
1265
+ operation: "clickType",
1266
+ log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
1267
+ };
1346
1268
  if (newValue !== _value) {
1347
1269
  //this.logger.info(_value + "=" + newValue);
1348
1270
  _value = newValue;
1349
1271
  }
1350
- info.value = _value;
1351
1272
  try {
1352
- let element = await this._locate(selectors, info, _params);
1353
- //insert red border around the element
1354
- await this.scrollIfNeeded(element, info);
1355
- await this._highlightElements(element);
1273
+ await _preCommand(state, this);
1274
+ state.info.value = _value;
1356
1275
  if (options === null || options === undefined || !options.press) {
1357
1276
  try {
1358
- let currentValue = await element.inputValue();
1277
+ let currentValue = await state.element.inputValue();
1359
1278
  if (currentValue) {
1360
- await element.fill("");
1279
+ await state.element.fill("");
1361
1280
  }
1362
1281
  }
1363
1282
  catch (e) {
@@ -1366,22 +1285,22 @@ class StableBrowser {
1366
1285
  }
1367
1286
  if (options === null || options === undefined || options.press) {
1368
1287
  try {
1369
- await element.click({ timeout: 5000 });
1288
+ await state.element.click({ timeout: 5000 });
1370
1289
  }
1371
1290
  catch (e) {
1372
- await element.dispatchEvent("click");
1291
+ await state.element.dispatchEvent("click");
1373
1292
  }
1374
1293
  }
1375
1294
  else {
1376
1295
  try {
1377
- await element.focus();
1296
+ await state.element.focus();
1378
1297
  }
1379
1298
  catch (e) {
1380
- await element.dispatchEvent("focus");
1299
+ await state.element.dispatchEvent("focus");
1381
1300
  }
1382
1301
  }
1383
1302
  await new Promise((resolve) => setTimeout(resolve, 500));
1384
- const valueSegment = _value.split("&&");
1303
+ const valueSegment = state.value.split("&&");
1385
1304
  for (let i = 0; i < valueSegment.length; i++) {
1386
1305
  if (i > 0) {
1387
1306
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1401,14 +1320,14 @@ class StableBrowser {
1401
1320
  await new Promise((resolve) => setTimeout(resolve, 500));
1402
1321
  }
1403
1322
  }
1404
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1323
+ await _screenshot(state, this);
1405
1324
  if (enter === true) {
1406
1325
  await new Promise((resolve) => setTimeout(resolve, 2000));
1407
1326
  await this.page.keyboard.press("Enter");
1408
1327
  await this.waitForPageLoad();
1409
1328
  }
1410
1329
  else if (enter === false) {
1411
- await element.dispatchEvent("change");
1330
+ await state.element.dispatchEvent("change");
1412
1331
  //await this.page.keyboard.press("Tab");
1413
1332
  }
1414
1333
  else {
@@ -1417,104 +1336,50 @@ class StableBrowser {
1417
1336
  await this.waitForPageLoad();
1418
1337
  }
1419
1338
  }
1420
- return info;
1421
- }
1422
- catch (e) {
1423
- //await this.closeUnexpectedPopups();
1424
- this.logger.error("fill failed " + JSON.stringify(info));
1425
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1426
- info.screenshotPath = screenshotPath;
1427
- Object.assign(e, { info: info });
1428
- error = e;
1429
- throw e;
1430
- }
1431
- finally {
1432
- const endTime = Date.now();
1433
- this._reportToWorld(world, {
1434
- element_name: selectors.element_name,
1435
- type: Types.FILL,
1436
- screenshotId,
1437
- value: _value,
1438
- text: `clickType input with value: ${_value}`,
1439
- result: error
1440
- ? {
1441
- status: "FAILED",
1442
- startTime,
1443
- endTime,
1444
- message: error === null || error === void 0 ? void 0 : error.message,
1445
- }
1446
- : {
1447
- status: "PASSED",
1448
- startTime,
1449
- endTime,
1450
- },
1451
- info: info,
1452
- });
1453
- }
1454
- }
1455
- async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1456
- this._validateSelectors(selectors);
1457
- value = unEscapeString(value);
1458
- const startTime = Date.now();
1459
- let error = null;
1460
- let screenshotId = null;
1461
- let screenshotPath = null;
1462
- const info = {};
1463
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1464
- info.operation = "fill";
1465
- info.selectors = selectors;
1466
- info.value = value;
1467
- try {
1468
- let element = await this._locate(selectors, info, _params);
1469
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1470
- await this._highlightElements(element);
1471
- await element.fill(value);
1472
- await element.dispatchEvent("change");
1473
- if (enter) {
1474
- await new Promise((resolve) => setTimeout(resolve, 2000));
1475
- await this.page.keyboard.press("Enter");
1476
- }
1477
- await this.waitForPageLoad();
1478
- return info;
1339
+ return state.info;
1479
1340
  }
1480
1341
  catch (e) {
1481
- //await this.closeUnexpectedPopups();
1482
- this.logger.error("fill failed " + info.log);
1483
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1484
- info.screenshotPath = screenshotPath;
1485
- Object.assign(e, { info: info });
1486
- error = e;
1487
- throw e;
1342
+ await _commandError(state, e, this);
1488
1343
  }
1489
- finally {
1490
- const endTime = Date.now();
1491
- this._reportToWorld(world, {
1492
- element_name: selectors.element_name,
1493
- type: Types.FILL,
1494
- screenshotId,
1495
- value,
1496
- text: `Fill input with value: ${value}`,
1497
- result: error
1498
- ? {
1499
- status: "FAILED",
1500
- startTime,
1501
- endTime,
1502
- message: error === null || error === void 0 ? void 0 : error.message,
1503
- }
1504
- : {
1505
- status: "PASSED",
1506
- startTime,
1507
- endTime,
1508
- },
1509
- info: info,
1510
- });
1344
+ finally {
1345
+ _commandFinally(state, this);
1346
+ }
1347
+ }
1348
+ async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1349
+ const state = {
1350
+ selectors,
1351
+ _params,
1352
+ value: unEscapeString(value),
1353
+ options,
1354
+ world,
1355
+ type: Types.FILL,
1356
+ text: `Fill input with value: ${value}`,
1357
+ operation: "fill",
1358
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1359
+ };
1360
+ try {
1361
+ await _preCommand(state, this);
1362
+ await state.element.fill(value);
1363
+ await state.element.dispatchEvent("change");
1364
+ if (enter) {
1365
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1366
+ await this.page.keyboard.press("Enter");
1367
+ }
1368
+ await this.waitForPageLoad();
1369
+ return state.info;
1370
+ }
1371
+ catch (e) {
1372
+ await _commandError(state, e, this);
1373
+ }
1374
+ finally {
1375
+ _commandFinally(state, this);
1511
1376
  }
1512
1377
  }
1513
1378
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1514
1379
  return await this._getText(selectors, 0, _params, options, info, world);
1515
1380
  }
1516
1381
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1517
- this._validateSelectors(selectors);
1382
+ _validateSelectors(selectors);
1518
1383
  let screenshotId = null;
1519
1384
  let screenshotPath = null;
1520
1385
  if (!info.log) {
@@ -1558,166 +1423,124 @@ class StableBrowser {
1558
1423
  }
1559
1424
  }
1560
1425
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1561
- var _a;
1562
- this._validateSelectors(selectors);
1563
1426
  if (!pattern) {
1564
1427
  throw new Error("pattern is null");
1565
1428
  }
1566
1429
  if (!text) {
1567
1430
  throw new Error("text is null");
1568
1431
  }
1432
+ const state = {
1433
+ selectors,
1434
+ _params,
1435
+ pattern,
1436
+ value: pattern,
1437
+ options,
1438
+ world,
1439
+ locate: false,
1440
+ scroll: false,
1441
+ screenshot: false,
1442
+ highlight: false,
1443
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1444
+ text: `Verify element contains pattern: ${pattern}`,
1445
+ operation: "containsPattern",
1446
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1447
+ };
1569
1448
  const newValue = await this._replaceWithLocalData(text, world);
1570
1449
  if (newValue !== text) {
1571
1450
  this.logger.info(text + "=" + newValue);
1572
1451
  text = newValue;
1573
1452
  }
1574
- const startTime = Date.now();
1575
- let error = null;
1576
- let screenshotId = null;
1577
- let screenshotPath = null;
1578
- const info = {};
1579
- info.log =
1580
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1581
- info.operation = "containsPattern";
1582
- info.selectors = selectors;
1583
- info.value = text;
1584
- info.pattern = pattern;
1585
1453
  let foundObj = null;
1586
1454
  try {
1587
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1455
+ await _preCommand(state, this);
1456
+ state.info.pattern = pattern;
1457
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1588
1458
  if (foundObj && foundObj.element) {
1589
- await this.scrollIfNeeded(foundObj.element, info);
1459
+ await this.scrollIfNeeded(foundObj.element, state.info);
1590
1460
  }
1591
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1461
+ await _screenshot(state, this);
1592
1462
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1593
1463
  pattern = pattern.replace("{text}", escapedText);
1594
1464
  let regex = new RegExp(pattern, "im");
1595
- if (!regex.test(foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) && !((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(text))) {
1596
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1465
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1466
+ state.info.foundText = foundObj?.text;
1597
1467
  throw new Error("element doesn't contain text " + text);
1598
1468
  }
1599
- return info;
1469
+ return state.info;
1600
1470
  }
1601
1471
  catch (e) {
1602
- //await this.closeUnexpectedPopups();
1603
- this.logger.error("verify element contains text failed " + info.log);
1604
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1605
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1606
- info.screenshotPath = screenshotPath;
1607
- Object.assign(e, { info: info });
1608
- error = e;
1609
- throw e;
1472
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1473
+ await _commandError(state, e, this);
1610
1474
  }
1611
1475
  finally {
1612
- const endTime = Date.now();
1613
- this._reportToWorld(world, {
1614
- element_name: selectors.element_name,
1615
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1616
- value: pattern,
1617
- text: `Verify element contains pattern: ${pattern}`,
1618
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1619
- result: error
1620
- ? {
1621
- status: "FAILED",
1622
- startTime,
1623
- endTime,
1624
- message: error === null || error === void 0 ? void 0 : error.message,
1625
- }
1626
- : {
1627
- status: "PASSED",
1628
- startTime,
1629
- endTime,
1630
- },
1631
- info: info,
1632
- });
1476
+ _commandFinally(state, this);
1633
1477
  }
1634
1478
  }
1635
1479
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1636
- var _a, _b, _c;
1637
- this._validateSelectors(selectors);
1638
- text = unEscapeString(text);
1480
+ const state = {
1481
+ selectors,
1482
+ _params,
1483
+ value: text,
1484
+ options,
1485
+ world,
1486
+ locate: false,
1487
+ scroll: false,
1488
+ screenshot: false,
1489
+ highlight: false,
1490
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1491
+ text: `Verify element contains text: ${text}`,
1492
+ operation: "containsText",
1493
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1494
+ };
1639
1495
  if (!text) {
1640
1496
  throw new Error("text is null");
1641
1497
  }
1642
- const startTime = Date.now();
1643
- let error = null;
1644
- let screenshotId = null;
1645
- let screenshotPath = null;
1646
- const info = {};
1647
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1648
- info.operation = "containsText";
1649
- info.selectors = selectors;
1498
+ text = unEscapeString(text);
1650
1499
  const newValue = await this._replaceWithLocalData(text, world);
1651
1500
  if (newValue !== text) {
1652
1501
  this.logger.info(text + "=" + newValue);
1653
1502
  text = newValue;
1654
1503
  }
1655
- info.value = text;
1656
1504
  let foundObj = null;
1657
1505
  try {
1658
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1506
+ await _preCommand(state, this);
1507
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1659
1508
  if (foundObj && foundObj.element) {
1660
- await this.scrollIfNeeded(foundObj.element, info);
1509
+ await this.scrollIfNeeded(foundObj.element, state.info);
1661
1510
  }
1662
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1511
+ await _screenshot(state, this);
1663
1512
  const dateAlternatives = findDateAlternatives(text);
1664
1513
  const numberAlternatives = findNumberAlternatives(text);
1665
1514
  if (dateAlternatives.date) {
1666
1515
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1667
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1668
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1669
- return info;
1516
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1517
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1518
+ return state.info;
1670
1519
  }
1671
1520
  }
1672
1521
  throw new Error("element doesn't contain text " + text);
1673
1522
  }
1674
1523
  else if (numberAlternatives.number) {
1675
1524
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1676
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1677
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1678
- return info;
1525
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1526
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1527
+ return state.info;
1679
1528
  }
1680
1529
  }
1681
1530
  throw new Error("element doesn't contain text " + text);
1682
1531
  }
1683
- else if (!(foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(text)) && !((_c = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _c === void 0 ? void 0 : _c.includes(text))) {
1684
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1685
- info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1532
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1533
+ state.info.foundText = foundObj?.text;
1534
+ state.info.value = foundObj?.value;
1686
1535
  throw new Error("element doesn't contain text " + text);
1687
1536
  }
1688
- return info;
1537
+ return state.info;
1689
1538
  }
1690
1539
  catch (e) {
1691
- //await this.closeUnexpectedPopups();
1692
- this.logger.error("verify element contains text failed " + info.log);
1693
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1694
- info.screenshotPath = screenshotPath;
1695
- Object.assign(e, { info: info });
1696
- error = e;
1697
- throw e;
1540
+ await _commandError(state, e, this);
1698
1541
  }
1699
1542
  finally {
1700
- const endTime = Date.now();
1701
- this._reportToWorld(world, {
1702
- element_name: selectors.element_name,
1703
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1704
- text: `Verify element contains text: ${text}`,
1705
- value: text,
1706
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1707
- result: error
1708
- ? {
1709
- status: "FAILED",
1710
- startTime,
1711
- endTime,
1712
- message: error === null || error === void 0 ? void 0 : error.message,
1713
- }
1714
- : {
1715
- status: "PASSED",
1716
- startTime,
1717
- endTime,
1718
- },
1719
- info: info,
1720
- });
1543
+ _commandFinally(state, this);
1721
1544
  }
1722
1545
  }
1723
1546
  _getDataFile(world = null) {
@@ -1899,11 +1722,9 @@ class StableBrowser {
1899
1722
  if (!fs.existsSync(world.screenshotPath)) {
1900
1723
  fs.mkdirSync(world.screenshotPath, { recursive: true });
1901
1724
  }
1902
- let nextIndex = 1;
1903
- while (fs.existsSync(path.join(world.screenshotPath, nextIndex + ".png"))) {
1904
- nextIndex++;
1905
- }
1906
- const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
1725
+ // to make sure the path doesn't start with -
1726
+ const uuidStr = "id_" + randomUUID();
1727
+ const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
1907
1728
  try {
1908
1729
  await this.takeScreenshot(screenshotPath);
1909
1730
  // let buffer = await this.page.screenshot({ timeout: 4000 });
@@ -1917,7 +1738,7 @@ class StableBrowser {
1917
1738
  catch (e) {
1918
1739
  this.logger.info("unable to take screenshot, ignored");
1919
1740
  }
1920
- result.screenshotId = nextIndex;
1741
+ result.screenshotId = uuidStr;
1921
1742
  result.screenshotPath = screenshotPath;
1922
1743
  if (info && info.box) {
1923
1744
  await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
@@ -1991,127 +1812,69 @@ class StableBrowser {
1991
1812
  }
1992
1813
  }
1993
1814
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1994
- this._validateSelectors(selectors);
1995
- const startTime = Date.now();
1996
- let error = null;
1997
- let screenshotId = null;
1998
- let screenshotPath = null;
1815
+ const state = {
1816
+ selectors,
1817
+ _params,
1818
+ options,
1819
+ world,
1820
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1821
+ text: `Verify element exists in page`,
1822
+ operation: "verifyElementExistInPage",
1823
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1824
+ };
1999
1825
  await new Promise((resolve) => setTimeout(resolve, 2000));
2000
- const info = {};
2001
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
2002
- info.operation = "verify";
2003
- info.selectors = selectors;
2004
1826
  try {
2005
- const element = await this._locate(selectors, info, _params);
2006
- if (element) {
2007
- await this.scrollIfNeeded(element, info);
2008
- }
2009
- await this._highlightElements(element);
2010
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2011
- await expect(element).toHaveCount(1, { timeout: 10000 });
2012
- return info;
1827
+ await _preCommand(state, this);
1828
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1829
+ return state.info;
2013
1830
  }
2014
1831
  catch (e) {
2015
- //await this.closeUnexpectedPopups();
2016
- this.logger.error("verify failed " + info.log);
2017
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2018
- info.screenshotPath = screenshotPath;
2019
- Object.assign(e, { info: info });
2020
- error = e;
2021
- throw e;
1832
+ await _commandError(state, e, this);
2022
1833
  }
2023
1834
  finally {
2024
- const endTime = Date.now();
2025
- this._reportToWorld(world, {
2026
- element_name: selectors.element_name,
2027
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2028
- text: "Verify element exists in page",
2029
- screenshotId,
2030
- result: error
2031
- ? {
2032
- status: "FAILED",
2033
- startTime,
2034
- endTime,
2035
- message: error === null || error === void 0 ? void 0 : error.message,
2036
- }
2037
- : {
2038
- status: "PASSED",
2039
- startTime,
2040
- endTime,
2041
- },
2042
- info: info,
2043
- });
1835
+ _commandFinally(state, this);
2044
1836
  }
2045
1837
  }
2046
1838
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
2047
- this._validateSelectors(selectors);
2048
- const startTime = Date.now();
2049
- let error = null;
2050
- let screenshotId = null;
2051
- let screenshotPath = null;
1839
+ const state = {
1840
+ selectors,
1841
+ _params,
1842
+ attribute,
1843
+ variable,
1844
+ options,
1845
+ world,
1846
+ type: Types.EXTRACT,
1847
+ text: `Extract attribute from element`,
1848
+ operation: "extractAttribute",
1849
+ log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
1850
+ };
2052
1851
  await new Promise((resolve) => setTimeout(resolve, 2000));
2053
- const info = {};
2054
- info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
2055
- info.operation = "extract";
2056
- info.selectors = selectors;
2057
1852
  try {
2058
- const element = await this._locate(selectors, info, _params);
2059
- await this._highlightElements(element);
2060
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1853
+ await _preCommand(state, this);
2061
1854
  switch (attribute) {
2062
1855
  case "inner_text":
2063
- info.value = await element.innerText();
1856
+ state.value = await state.element.innerText();
2064
1857
  break;
2065
1858
  case "href":
2066
- info.value = await element.getAttribute("href");
1859
+ state.value = await state.element.getAttribute("href");
2067
1860
  break;
2068
1861
  case "value":
2069
- info.value = await element.inputValue();
1862
+ state.value = await state.element.inputValue();
2070
1863
  break;
2071
1864
  default:
2072
- info.value = await element.getAttribute(attribute);
1865
+ state.value = await state.element.getAttribute(attribute);
2073
1866
  break;
2074
1867
  }
2075
- this[variable] = info.value;
2076
- if (world) {
2077
- world[variable] = info.value;
2078
- }
2079
- this.setTestData({ [variable]: info.value }, world);
2080
- this.logger.info("set test data: " + variable + "=" + info.value);
2081
- return info;
1868
+ state.info.value = state.value;
1869
+ this.setTestData({ [variable]: state.value }, world);
1870
+ this.logger.info("set test data: " + variable + "=" + state.value);
1871
+ return state.info;
2082
1872
  }
2083
1873
  catch (e) {
2084
- //await this.closeUnexpectedPopups();
2085
- this.logger.error("extract failed " + info.log);
2086
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2087
- info.screenshotPath = screenshotPath;
2088
- Object.assign(e, { info: info });
2089
- error = e;
2090
- throw e;
1874
+ await _commandError(state, e, this);
2091
1875
  }
2092
1876
  finally {
2093
- const endTime = Date.now();
2094
- this._reportToWorld(world, {
2095
- element_name: selectors.element_name,
2096
- type: Types.EXTRACT_ATTRIBUTE,
2097
- variable: variable,
2098
- value: info.value,
2099
- text: "Extract attribute from element",
2100
- screenshotId,
2101
- result: error
2102
- ? {
2103
- status: "FAILED",
2104
- startTime,
2105
- endTime,
2106
- message: error === null || error === void 0 ? void 0 : error.message,
2107
- }
2108
- : {
2109
- status: "PASSED",
2110
- startTime,
2111
- endTime,
2112
- },
2113
- info: info,
2114
- });
1877
+ _commandFinally(state, this);
2115
1878
  }
2116
1879
  }
2117
1880
  async extractEmailData(emailAddress, options, world) {
@@ -2188,7 +1951,8 @@ class StableBrowser {
2188
1951
  catch (e) {
2189
1952
  errorCount++;
2190
1953
  if (errorCount > 3) {
2191
- throw e;
1954
+ // throw e;
1955
+ await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
2192
1956
  }
2193
1957
  // ignore
2194
1958
  }
@@ -2299,11 +2063,12 @@ class StableBrowser {
2299
2063
  info.screenshotPath = screenshotPath;
2300
2064
  Object.assign(e, { info: info });
2301
2065
  error = e;
2302
- throw e;
2066
+ // throw e;
2067
+ await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
2303
2068
  }
2304
2069
  finally {
2305
2070
  const endTime = Date.now();
2306
- this._reportToWorld(world, {
2071
+ _reportToWorld(world, {
2307
2072
  type: Types.VERIFY_PAGE_PATH,
2308
2073
  text: "Verify page path",
2309
2074
  screenshotId,
@@ -2312,7 +2077,7 @@ class StableBrowser {
2312
2077
  status: "FAILED",
2313
2078
  startTime,
2314
2079
  endTime,
2315
- message: error === null || error === void 0 ? void 0 : error.message,
2080
+ message: error?.message,
2316
2081
  }
2317
2082
  : {
2318
2083
  status: "PASSED",
@@ -2325,52 +2090,58 @@ class StableBrowser {
2325
2090
  }
2326
2091
  async verifyTextExistInPage(text, options = {}, world = null) {
2327
2092
  text = unEscapeString(text);
2328
- const startTime = Date.now();
2093
+ const state = {
2094
+ text_search: text,
2095
+ options,
2096
+ world,
2097
+ locate: false,
2098
+ scroll: false,
2099
+ highlight: false,
2100
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2101
+ text: `Verify text exists in page`,
2102
+ operation: "verifyTextExistInPage",
2103
+ log: "***** verify text " + text + " exists in page *****\n",
2104
+ };
2329
2105
  const timeout = this._getLoadTimeout(options);
2330
- let error = null;
2331
- let screenshotId = null;
2332
- let screenshotPath = null;
2333
2106
  await new Promise((resolve) => setTimeout(resolve, 2000));
2334
- const info = {};
2335
- info.log = "***** verify text " + text + " exists in page *****\n";
2336
- info.operation = "verifyTextExistInPage";
2337
2107
  const newValue = await this._replaceWithLocalData(text, world);
2338
2108
  if (newValue !== text) {
2339
2109
  this.logger.info(text + "=" + newValue);
2340
2110
  text = newValue;
2341
2111
  }
2342
- info.text = text;
2343
2112
  let dateAlternatives = findDateAlternatives(text);
2344
2113
  let numberAlternatives = findNumberAlternatives(text);
2345
2114
  try {
2115
+ await _preCommand(state, this);
2116
+ state.info.text = text;
2346
2117
  while (true) {
2347
2118
  const frames = this.page.frames();
2348
2119
  let results = [];
2349
2120
  for (let i = 0; i < frames.length; i++) {
2350
2121
  if (dateAlternatives.date) {
2351
2122
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2352
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
2123
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
2353
2124
  result.frame = frames[i];
2354
2125
  results.push(result);
2355
2126
  }
2356
2127
  }
2357
2128
  else if (numberAlternatives.number) {
2358
2129
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2359
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
2130
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
2360
2131
  result.frame = frames[i];
2361
2132
  results.push(result);
2362
2133
  }
2363
2134
  }
2364
2135
  else {
2365
- const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
2136
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2366
2137
  result.frame = frames[i];
2367
2138
  results.push(result);
2368
2139
  }
2369
2140
  }
2370
- info.results = results;
2141
+ state.info.results = results;
2371
2142
  const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2372
2143
  if (resultWithElementsFound.length === 0) {
2373
- if (Date.now() - startTime > timeout) {
2144
+ if (Date.now() - state.startTime > timeout) {
2374
2145
  throw new Error(`Text ${text} not found in page`);
2375
2146
  }
2376
2147
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -2382,44 +2153,89 @@ class StableBrowser {
2382
2153
  await this._highlightElements(frame, dataAttribute);
2383
2154
  const element = await frame.$(dataAttribute);
2384
2155
  if (element) {
2385
- await this.scrollIfNeeded(element, info);
2156
+ await this.scrollIfNeeded(element, state.info);
2386
2157
  await element.dispatchEvent("bvt_verify_page_contains_text");
2387
2158
  }
2388
2159
  }
2389
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2390
- return info;
2160
+ await _screenshot(state, this);
2161
+ return state.info;
2391
2162
  }
2392
2163
  // await expect(element).toHaveCount(1, { timeout: 10000 });
2393
2164
  }
2394
2165
  catch (e) {
2395
- //await this.closeUnexpectedPopups();
2396
- this.logger.error("verify text exist in page failed " + info.log);
2397
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2398
- info.screenshotPath = screenshotPath;
2399
- Object.assign(e, { info: info });
2400
- error = e;
2401
- throw e;
2166
+ await _commandError(state, e, this);
2402
2167
  }
2403
2168
  finally {
2404
- const endTime = Date.now();
2405
- this._reportToWorld(world, {
2406
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2407
- text: "Verify text exists in page",
2408
- screenshotId,
2409
- result: error
2410
- ? {
2411
- status: "FAILED",
2412
- startTime,
2413
- endTime,
2414
- message: error === null || error === void 0 ? void 0 : error.message,
2169
+ _commandFinally(state, this);
2170
+ }
2171
+ }
2172
+ async waitForTextToDisappear(text, options = {}, world = null) {
2173
+ text = unEscapeString(text);
2174
+ const state = {
2175
+ text_search: text,
2176
+ options,
2177
+ world,
2178
+ locate: false,
2179
+ scroll: false,
2180
+ highlight: false,
2181
+ type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
2182
+ text: `Verify text does not exist in page`,
2183
+ operation: "verifyTextNotExistInPage",
2184
+ log: "***** verify text " + text + " does not exist in page *****\n",
2185
+ };
2186
+ const timeout = this._getLoadTimeout(options);
2187
+ await new Promise((resolve) => setTimeout(resolve, 2000));
2188
+ const newValue = await this._replaceWithLocalData(text, world);
2189
+ if (newValue !== text) {
2190
+ this.logger.info(text + "=" + newValue);
2191
+ text = newValue;
2192
+ }
2193
+ let dateAlternatives = findDateAlternatives(text);
2194
+ let numberAlternatives = findNumberAlternatives(text);
2195
+ try {
2196
+ await _preCommand(state, this);
2197
+ state.info.text = text;
2198
+ while (true) {
2199
+ const frames = this.page.frames();
2200
+ let results = [];
2201
+ for (let i = 0; i < frames.length; i++) {
2202
+ if (dateAlternatives.date) {
2203
+ for (let j = 0; j < dateAlternatives.dates.length; j++) {
2204
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
2205
+ result.frame = frames[i];
2206
+ results.push(result);
2207
+ }
2415
2208
  }
2416
- : {
2417
- status: "PASSED",
2418
- startTime,
2419
- endTime,
2420
- },
2421
- info: info,
2422
- });
2209
+ else if (numberAlternatives.number) {
2210
+ for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2211
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
2212
+ result.frame = frames[i];
2213
+ results.push(result);
2214
+ }
2215
+ }
2216
+ else {
2217
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2218
+ result.frame = frames[i];
2219
+ results.push(result);
2220
+ }
2221
+ }
2222
+ state.info.results = results;
2223
+ const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2224
+ if (resultWithElementsFound.length === 0) {
2225
+ await _screenshot(state, this);
2226
+ return state.info;
2227
+ }
2228
+ if (Date.now() - state.startTime > timeout) {
2229
+ throw new Error(`Text ${text} found in page`);
2230
+ }
2231
+ await new Promise((resolve) => setTimeout(resolve, 1000));
2232
+ }
2233
+ }
2234
+ catch (e) {
2235
+ await _commandError(state, e, this);
2236
+ }
2237
+ finally {
2238
+ _commandFinally(state, this);
2423
2239
  }
2424
2240
  }
2425
2241
  _getServerUrl() {
@@ -2482,11 +2298,12 @@ class StableBrowser {
2482
2298
  info.screenshotPath = screenshotPath;
2483
2299
  Object.assign(e, { info: info });
2484
2300
  error = e;
2485
- throw e;
2301
+ // throw e;
2302
+ await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
2486
2303
  }
2487
2304
  finally {
2488
2305
  const endTime = Date.now();
2489
- this._reportToWorld(world, {
2306
+ _reportToWorld(world, {
2490
2307
  type: Types.VERIFY_VISUAL,
2491
2308
  text: "Visual verification",
2492
2309
  screenshotId,
@@ -2495,7 +2312,7 @@ class StableBrowser {
2495
2312
  status: "FAILED",
2496
2313
  startTime,
2497
2314
  endTime,
2498
- message: error === null || error === void 0 ? void 0 : error.message,
2315
+ message: error?.message,
2499
2316
  }
2500
2317
  : {
2501
2318
  status: "PASSED",
@@ -2527,7 +2344,7 @@ class StableBrowser {
2527
2344
  this.logger.info("Table data verified");
2528
2345
  }
2529
2346
  async getTableData(selectors, _params = null, options = {}, world = null) {
2530
- this._validateSelectors(selectors);
2347
+ _validateSelectors(selectors);
2531
2348
  const startTime = Date.now();
2532
2349
  let error = null;
2533
2350
  let screenshotId = null;
@@ -2549,11 +2366,12 @@ class StableBrowser {
2549
2366
  info.screenshotPath = screenshotPath;
2550
2367
  Object.assign(e, { info: info });
2551
2368
  error = e;
2552
- throw e;
2369
+ // throw e;
2370
+ await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
2553
2371
  }
2554
2372
  finally {
2555
2373
  const endTime = Date.now();
2556
- this._reportToWorld(world, {
2374
+ _reportToWorld(world, {
2557
2375
  element_name: selectors.element_name,
2558
2376
  type: Types.GET_TABLE_DATA,
2559
2377
  text: "Get table data",
@@ -2563,7 +2381,7 @@ class StableBrowser {
2563
2381
  status: "FAILED",
2564
2382
  startTime,
2565
2383
  endTime,
2566
- message: error === null || error === void 0 ? void 0 : error.message,
2384
+ message: error?.message,
2567
2385
  }
2568
2386
  : {
2569
2387
  status: "PASSED",
@@ -2575,7 +2393,7 @@ class StableBrowser {
2575
2393
  }
2576
2394
  }
2577
2395
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2578
- this._validateSelectors(selectors);
2396
+ _validateSelectors(selectors);
2579
2397
  if (!query) {
2580
2398
  throw new Error("query is null");
2581
2399
  }
@@ -2714,11 +2532,12 @@ class StableBrowser {
2714
2532
  info.screenshotPath = screenshotPath;
2715
2533
  Object.assign(e, { info: info });
2716
2534
  error = e;
2717
- throw e;
2535
+ // throw e;
2536
+ await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
2718
2537
  }
2719
2538
  finally {
2720
2539
  const endTime = Date.now();
2721
- this._reportToWorld(world, {
2540
+ _reportToWorld(world, {
2722
2541
  element_name: selectors.element_name,
2723
2542
  type: Types.ANALYZE_TABLE,
2724
2543
  text: "Analyze table",
@@ -2728,7 +2547,7 @@ class StableBrowser {
2728
2547
  status: "FAILED",
2729
2548
  startTime,
2730
2549
  endTime,
2731
- message: error === null || error === void 0 ? void 0 : error.message,
2550
+ message: error?.message,
2732
2551
  }
2733
2552
  : {
2734
2553
  status: "PASSED",
@@ -2740,27 +2559,7 @@ class StableBrowser {
2740
2559
  }
2741
2560
  }
2742
2561
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2743
- if (!value) {
2744
- return value;
2745
- }
2746
- // find all the accurance of {{(.*?)}} and replace with the value
2747
- let regex = /{{(.*?)}}/g;
2748
- let matches = value.match(regex);
2749
- if (matches) {
2750
- const testData = this.getTestData(world);
2751
- for (let i = 0; i < matches.length; i++) {
2752
- let match = matches[i];
2753
- let key = match.substring(2, match.length - 2);
2754
- let newValue = objectPath.get(testData, key, null);
2755
- if (newValue !== null) {
2756
- value = value.replace(match, newValue);
2757
- }
2758
- }
2759
- }
2760
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2761
- return await decrypt(value, null, totpWait);
2762
- }
2763
- return value;
2562
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2764
2563
  }
2765
2564
  _getLoadTimeout(options) {
2766
2565
  let timeout = 15000;
@@ -2811,7 +2610,7 @@ class StableBrowser {
2811
2610
  await new Promise((resolve) => setTimeout(resolve, 2000));
2812
2611
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2813
2612
  const endTime = Date.now();
2814
- this._reportToWorld(world, {
2613
+ _reportToWorld(world, {
2815
2614
  type: Types.GET_PAGE_STATUS,
2816
2615
  text: "Wait for page load",
2817
2616
  screenshotId,
@@ -2820,7 +2619,7 @@ class StableBrowser {
2820
2619
  status: "FAILED",
2821
2620
  startTime,
2822
2621
  endTime,
2823
- message: error === null || error === void 0 ? void 0 : error.message,
2622
+ message: error?.message,
2824
2623
  }
2825
2624
  : {
2826
2625
  status: "PASSED",
@@ -2831,41 +2630,35 @@ class StableBrowser {
2831
2630
  }
2832
2631
  }
2833
2632
  async closePage(options = {}, world = null) {
2834
- const startTime = Date.now();
2835
- let error = null;
2836
- let screenshotId = null;
2837
- let screenshotPath = null;
2838
- const info = {};
2633
+ const state = {
2634
+ options,
2635
+ world,
2636
+ locate: false,
2637
+ scroll: false,
2638
+ highlight: false,
2639
+ type: Types.CLOSE_PAGE,
2640
+ text: `Close page`,
2641
+ operation: "closePage",
2642
+ log: "***** close page *****\n",
2643
+ throwError: false,
2644
+ };
2839
2645
  try {
2646
+ await _preCommand(state, this);
2840
2647
  await this.page.close();
2841
2648
  }
2842
2649
  catch (e) {
2843
2650
  console.log(".");
2651
+ await _commandError(state, e, this);
2844
2652
  }
2845
2653
  finally {
2846
- await new Promise((resolve) => setTimeout(resolve, 2000));
2847
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2848
- const endTime = Date.now();
2849
- this._reportToWorld(world, {
2850
- type: Types.CLOSE_PAGE,
2851
- text: "close page",
2852
- screenshotId,
2853
- result: error
2854
- ? {
2855
- status: "FAILED",
2856
- startTime,
2857
- endTime,
2858
- message: error === null || error === void 0 ? void 0 : error.message,
2859
- }
2860
- : {
2861
- status: "PASSED",
2862
- startTime,
2863
- endTime,
2864
- },
2865
- info: info,
2866
- });
2654
+ _commandFinally(state, this);
2867
2655
  }
2868
2656
  }
2657
+ saveTestDataAsGlobal(options, world) {
2658
+ const dataFile = this._getDataFile(world);
2659
+ process.env.GLOBAL_TEST_DATA_FILE = dataFile;
2660
+ this.logger.info("Save the scenario test data as global for the following scenarios.");
2661
+ }
2869
2662
  async setViewportSize(width, hight, options = {}, world = null) {
2870
2663
  const startTime = Date.now();
2871
2664
  let error = null;
@@ -2883,12 +2676,13 @@ class StableBrowser {
2883
2676
  }
2884
2677
  catch (e) {
2885
2678
  console.log(".");
2679
+ await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
2886
2680
  }
2887
2681
  finally {
2888
2682
  await new Promise((resolve) => setTimeout(resolve, 2000));
2889
2683
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2890
2684
  const endTime = Date.now();
2891
- this._reportToWorld(world, {
2685
+ _reportToWorld(world, {
2892
2686
  type: Types.SET_VIEWPORT,
2893
2687
  text: "set viewport size to " + width + "x" + hight,
2894
2688
  screenshotId,
@@ -2897,7 +2691,7 @@ class StableBrowser {
2897
2691
  status: "FAILED",
2898
2692
  startTime,
2899
2693
  endTime,
2900
- message: error === null || error === void 0 ? void 0 : error.message,
2694
+ message: error?.message,
2901
2695
  }
2902
2696
  : {
2903
2697
  status: "PASSED",
@@ -2919,12 +2713,13 @@ class StableBrowser {
2919
2713
  }
2920
2714
  catch (e) {
2921
2715
  console.log(".");
2716
+ await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
2922
2717
  }
2923
2718
  finally {
2924
2719
  await new Promise((resolve) => setTimeout(resolve, 2000));
2925
2720
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2926
2721
  const endTime = Date.now();
2927
- this._reportToWorld(world, {
2722
+ _reportToWorld(world, {
2928
2723
  type: Types.GET_PAGE_STATUS,
2929
2724
  text: "page relaod",
2930
2725
  screenshotId,
@@ -2933,7 +2728,7 @@ class StableBrowser {
2933
2728
  status: "FAILED",
2934
2729
  startTime,
2935
2730
  endTime,
2936
- message: error === null || error === void 0 ? void 0 : error.message,
2731
+ message: error?.message,
2937
2732
  }
2938
2733
  : {
2939
2734
  status: "PASSED",
@@ -2960,11 +2755,37 @@ class StableBrowser {
2960
2755
  console.log("#-#");
2961
2756
  }
2962
2757
  }
2963
- _reportToWorld(world, properties) {
2964
- if (!world || !world.attach) {
2965
- return;
2758
+ async beforeStep(world, step) {
2759
+ this.stepName = step.pickleStep.text;
2760
+ this.logger.info("step: " + this.stepName);
2761
+ if (this.stepIndex === undefined) {
2762
+ this.stepIndex = 0;
2763
+ }
2764
+ else {
2765
+ this.stepIndex++;
2766
+ }
2767
+ if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
2768
+ if (this.context.browserObject.context) {
2769
+ await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
2770
+ }
2771
+ }
2772
+ if (this.tags === null && step && step.pickle && step.pickle.tags) {
2773
+ this.tags = step.pickle.tags.map((tag) => tag.name);
2774
+ // check if @global_test_data tag is present
2775
+ if (this.tags.includes("@global_test_data")) {
2776
+ this.saveTestDataAsGlobal({}, world);
2777
+ }
2778
+ }
2779
+ }
2780
+ async afterStep(world, step) {
2781
+ this.stepName = null;
2782
+ if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
2783
+ if (this.context.browserObject.context) {
2784
+ await this.context.browserObject.context.tracing.stopChunk({
2785
+ path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
2786
+ });
2787
+ }
2966
2788
  }
2967
- world.attach(JSON.stringify(properties), { mediaType: "application/json" });
2968
2789
  }
2969
2790
  }
2970
2791
  function createTimedPromise(promise, label) {