automation_model 1.0.466-dev → 1.0.466-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 (44) hide show
  1. package/lib/api.d.ts +42 -1
  2. package/lib/api.js +224 -49
  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/axe/axe.mini.js +12 -0
  7. package/lib/browser_manager.d.ts +5 -3
  8. package/lib/browser_manager.js +44 -19
  9. package/lib/browser_manager.js.map +1 -1
  10. package/lib/command_common.d.ts +6 -0
  11. package/lib/command_common.js +138 -0
  12. package/lib/command_common.js.map +1 -0
  13. package/lib/environment.js +5 -3
  14. package/lib/environment.js.map +1 -1
  15. package/lib/error-messages.d.ts +6 -0
  16. package/lib/error-messages.js +188 -0
  17. package/lib/error-messages.js.map +1 -0
  18. package/lib/index.d.ts +1 -0
  19. package/lib/index.js +1 -0
  20. package/lib/index.js.map +1 -1
  21. package/lib/init_browser.d.ts +1 -1
  22. package/lib/init_browser.js +18 -3
  23. package/lib/init_browser.js.map +1 -1
  24. package/lib/locate_element.js +6 -3
  25. package/lib/locate_element.js.map +1 -1
  26. package/lib/locator.d.ts +36 -0
  27. package/lib/locator.js +165 -0
  28. package/lib/locator.js.map +1 -1
  29. package/lib/network.d.ts +3 -0
  30. package/lib/network.js +144 -0
  31. package/lib/network.js.map +1 -0
  32. package/lib/stable_browser.d.ts +24 -19
  33. package/lib/stable_browser.js +574 -789
  34. package/lib/stable_browser.js.map +1 -1
  35. package/lib/table.d.ts +13 -0
  36. package/lib/table.js +187 -0
  37. package/lib/table.js.map +1 -0
  38. package/lib/test_context.d.ts +1 -0
  39. package/lib/test_context.js +12 -12
  40. package/lib/test_context.js.map +1 -1
  41. package/lib/utils.d.ts +3 -1
  42. package/lib/utils.js +68 -1
  43. package/lib/utils.js.map +1 -1
  44. package/package.json +4 -5
@@ -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,13 @@ 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);
89
100
  }
90
101
  registerEventListeners(context) {
91
102
  this.registerConsoleLogListener(this.page, context);
@@ -94,10 +105,17 @@ class StableBrowser {
94
105
  context.pageLoading = { status: false };
95
106
  }
96
107
  context.playContext.on("page", async function (page) {
108
+ if (this.configuration && this.configuration.closePopups === true) {
109
+ console.log("close unexpected popups");
110
+ await page.close();
111
+ return;
112
+ }
97
113
  context.pageLoading.status = true;
98
114
  this.page = page;
99
115
  context.page = page;
100
116
  context.pages.push(page);
117
+ registerNetworkEvents(this.world, this, context, this.page);
118
+ registerDownloadEvent(this.page, this.world, context);
101
119
  page.on("close", async () => {
102
120
  if (this.context && this.context.pages && this.context.pages.length > 1) {
103
121
  this.context.pages.pop();
@@ -127,9 +145,9 @@ class StableBrowser {
127
145
  if (this.appName === appName) {
128
146
  return;
129
147
  }
130
- let newContextCreated = false;
148
+ let navigate = false;
131
149
  if (!apps[appName]) {
132
- let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
150
+ let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
133
151
  newContextCreated = true;
134
152
  apps[appName] = {
135
153
  context: newContext,
@@ -142,8 +160,7 @@ class StableBrowser {
142
160
  this._copyContext(apps[appName], this);
143
161
  apps[this.appName] = tempContext;
144
162
  this.appName = appName;
145
- if (newContextCreated) {
146
- this.registerEventListeners(this.context);
163
+ if (navigate) {
147
164
  await this.goto(this.context.environment.baseUrl);
148
165
  await this.waitForPageLoad();
149
166
  }
@@ -169,7 +186,6 @@ class StableBrowser {
169
186
  this.context.webLogger = [];
170
187
  }
171
188
  page.on("console", async (msg) => {
172
- var _a;
173
189
  const obj = {
174
190
  type: msg.type(),
175
191
  text: msg.text(),
@@ -178,7 +194,7 @@ class StableBrowser {
178
194
  };
179
195
  this.context.webLogger.push(obj);
180
196
  if (msg.type() === "error") {
181
- (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
197
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
182
198
  }
183
199
  });
184
200
  }
@@ -187,7 +203,6 @@ class StableBrowser {
187
203
  this.context.networkLogger = [];
188
204
  }
189
205
  page.on("request", async (data) => {
190
- var _a;
191
206
  const startTime = new Date().getTime();
192
207
  try {
193
208
  const pageUrl = new URL(page.url());
@@ -212,10 +227,10 @@ class StableBrowser {
212
227
  startTime,
213
228
  };
214
229
  context.networkLogger.push(obj);
215
- (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
230
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
216
231
  }
217
232
  catch (error) {
218
- console.error("Error in request listener", error);
233
+ // console.error("Error in request listener", error);
219
234
  context.networkLogger.push({
220
235
  error: "not able to listen",
221
236
  message: error.message,
@@ -237,20 +252,6 @@ class StableBrowser {
237
252
  timeout: 60000,
238
253
  });
239
254
  }
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
255
  _fixUsingParams(text, _params) {
255
256
  if (!_params || typeof text !== "string") {
256
257
  return text;
@@ -318,7 +319,7 @@ class StableBrowser {
318
319
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
319
320
  }
320
321
  }
321
- if (locator === null || locator === void 0 ? void 0 : locator.engine) {
322
+ if (locator?.engine) {
322
323
  if (locator.engine === "css") {
323
324
  locatorReturn = scope.locator(locator.selector);
324
325
  }
@@ -342,7 +343,7 @@ class StableBrowser {
342
343
  if (css && css.locator) {
343
344
  css = css.locator;
344
345
  }
345
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
346
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
346
347
  if (result.elementCount === 0) {
347
348
  return;
348
349
  }
@@ -357,7 +358,7 @@ class StableBrowser {
357
358
  }
358
359
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
359
360
  //const stringifyText = JSON.stringify(text);
360
- return await scope.evaluate(([text, tag, regex, partial]) => {
361
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
361
362
  function isParent(parent, child) {
362
363
  let currentNode = child.parentNode;
363
364
  while (currentNode !== null) {
@@ -394,7 +395,7 @@ class StableBrowser {
394
395
  }
395
396
  document.collectAllShadowDomElements = collectAllShadowDomElements;
396
397
  if (!tag) {
397
- tag = "*";
398
+ tag = "*:not(script, style, head)";
398
399
  }
399
400
  let regexpSearch = document.getRegex(text);
400
401
  if (regexpSearch) {
@@ -476,6 +477,15 @@ class StableBrowser {
476
477
  }, [text1, tag1, regex1, partial1]);
477
478
  }
478
479
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
480
+ if (!info) {
481
+ info = {};
482
+ }
483
+ if (!info.failCause) {
484
+ info.failCause = {};
485
+ }
486
+ if (!info.log) {
487
+ info.log = "";
488
+ }
479
489
  let locatorSearch = selectorHierarchy[index];
480
490
  try {
481
491
  locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
@@ -488,13 +498,18 @@ class StableBrowser {
488
498
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
489
499
  let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
490
500
  if (!locatorString) {
501
+ info.failCause.textNotFound = true;
502
+ info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
491
503
  return;
492
504
  }
493
505
  locator = this._getLocator({ css: locatorString }, scope, _params);
494
506
  }
495
507
  else if (locatorSearch.text) {
496
- let result = await this._locateElementByText(scope, this._fixUsingParams(locatorSearch.text, _params), locatorSearch.tag, false, locatorSearch.partial === true, _params);
508
+ let text = this._fixUsingParams(locatorSearch.text, _params);
509
+ let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
497
510
  if (result.elementCount === 0) {
511
+ info.failCause.textNotFound = true;
512
+ info.failCause.lastError = "failed to locate element by text: " + text;
498
513
  return;
499
514
  }
500
515
  locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
@@ -511,6 +526,9 @@ class StableBrowser {
511
526
  // cssHref = true;
512
527
  // }
513
528
  let count = await locator.count();
529
+ if (count > 0 && !info.failCause.count) {
530
+ info.failCause.count = count;
531
+ }
514
532
  //info.log += "total elements found " + count + "\n";
515
533
  //let visibleCount = 0;
516
534
  let visibleLocator = null;
@@ -528,6 +546,8 @@ class StableBrowser {
528
546
  foundLocators.push(locator.nth(j));
529
547
  }
530
548
  else {
549
+ info.failCause.visible = visible;
550
+ info.failCause.enabled = enabled;
531
551
  if (!info.printMessages) {
532
552
  info.printMessages = {};
533
553
  }
@@ -539,6 +559,11 @@ class StableBrowser {
539
559
  }
540
560
  }
541
561
  async closeUnexpectedPopups(info, _params) {
562
+ if (!info) {
563
+ info = {};
564
+ info.failCause = {};
565
+ info.log = "";
566
+ }
542
567
  if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
543
568
  if (!info) {
544
569
  info = {};
@@ -579,7 +604,10 @@ class StableBrowser {
579
604
  }
580
605
  return { rerun: false };
581
606
  }
582
- async _locate(selectors, info, _params, timeout = 30000) {
607
+ async _locate(selectors, info, _params, timeout) {
608
+ if (!timeout) {
609
+ timeout = 30000;
610
+ }
583
611
  for (let i = 0; i < 3; i++) {
584
612
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
585
613
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -593,16 +621,15 @@ class StableBrowser {
593
621
  }
594
622
  throw new Error("unable to locate element " + JSON.stringify(selectors));
595
623
  }
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);
624
+ async _findFrameScope(selectors, timeout = 30000, info) {
625
+ if (!info) {
626
+ info = {};
627
+ info.failCause = {};
628
+ info.log = "";
629
+ }
602
630
  let scope = this.page;
603
- // for the simple click usecase
604
631
  if (selectors.frame) {
605
- scope = selectors.frame;
632
+ return selectors.frame;
606
633
  }
607
634
  if (selectors.iframe_src || selectors.frameLocators) {
608
635
  const findFrame = async (frame, framescope) => {
@@ -630,7 +657,6 @@ class StableBrowser {
630
657
  }
631
658
  return framescope;
632
659
  };
633
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
634
660
  while (true) {
635
661
  let frameFound = false;
636
662
  if (selectors.nestFrmLoc) {
@@ -654,6 +680,8 @@ class StableBrowser {
654
680
  if (!scope) {
655
681
  info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
656
682
  if (performance.now() - startTime > timeout) {
683
+ info.failCause.iframeNotFound = true;
684
+ info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
657
685
  throw new Error("unable to locate iframe " + selectors.iframe_src);
658
686
  }
659
687
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -663,6 +691,30 @@ class StableBrowser {
663
691
  }
664
692
  }
665
693
  }
694
+ if (!scope) {
695
+ scope = this.page;
696
+ }
697
+ return scope;
698
+ }
699
+ async _getDocumentBody(selectors, timeout = 30000, info) {
700
+ let scope = await this._findFrameScope(selectors, timeout, info);
701
+ return scope.evaluate(() => {
702
+ var bodyContent = document.body.innerHTML;
703
+ return bodyContent;
704
+ });
705
+ }
706
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
707
+ if (!info) {
708
+ info = {};
709
+ info.failCause = {};
710
+ info.log = "";
711
+ }
712
+ let highPriorityTimeout = 5000;
713
+ let visibleOnlyTimeout = 6000;
714
+ let startTime = performance.now();
715
+ let locatorsCount = 0;
716
+ //let arrayMode = Array.isArray(selectors);
717
+ let scope = await this._findFrameScope(selectors, timeout, info);
666
718
  let selectorsLocators = null;
667
719
  selectorsLocators = selectors.locators;
668
720
  // group selectors by priority
@@ -764,6 +816,8 @@ class StableBrowser {
764
816
  }
765
817
  this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
766
818
  info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
819
+ info.failCause.locatorNotFound = true;
820
+ info.failCause.lastError = "failed to locate unique element";
767
821
  throw new Error("failed to locate first element no elements found, " + info.log);
768
822
  }
769
823
  async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
@@ -795,6 +849,9 @@ class StableBrowser {
795
849
  });
796
850
  result.locatorIndex = i;
797
851
  }
852
+ if (foundLocators.length > 1) {
853
+ info.failCause.foundMultiple = true;
854
+ }
798
855
  }
799
856
  return result;
800
857
  }
@@ -807,12 +864,12 @@ class StableBrowser {
807
864
  while (true) {
808
865
  try {
809
866
  const result = await locate_element(this.context, elementDescription, "click");
810
- if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
867
+ if (result?.elementNumber >= 0) {
811
868
  const selectors = {
812
- frame: result === null || result === void 0 ? void 0 : result.frame,
869
+ frame: result?.frame,
813
870
  locators: [
814
871
  {
815
- css: result === null || result === void 0 ? void 0 : result.css,
872
+ css: result?.css,
816
873
  },
817
874
  ],
818
875
  };
@@ -822,7 +879,8 @@ class StableBrowser {
822
879
  }
823
880
  catch (e) {
824
881
  if (performance.now() - startTime > timeout) {
825
- throw e;
882
+ // throw e;
883
+ await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
826
884
  }
827
885
  }
828
886
  await new Promise((resolve) => setTimeout(resolve, 3000));
@@ -837,12 +895,12 @@ class StableBrowser {
837
895
  while (true) {
838
896
  try {
839
897
  const result = await locate_element(this.context, elementDescription, "fill", value);
840
- if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
898
+ if (result?.elementNumber >= 0) {
841
899
  const selectors = {
842
- frame: result === null || result === void 0 ? void 0 : result.frame,
900
+ frame: result?.frame,
843
901
  locators: [
844
902
  {
845
- css: result === null || result === void 0 ? void 0 : result.css,
903
+ css: result?.css,
846
904
  },
847
905
  ],
848
906
  };
@@ -852,92 +910,68 @@ class StableBrowser {
852
910
  }
853
911
  catch (e) {
854
912
  if (performance.now() - startTime > timeout) {
855
- throw e;
913
+ // throw e;
914
+ await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
856
915
  }
857
916
  }
858
917
  await new Promise((resolve) => setTimeout(resolve, 3000));
859
918
  }
860
919
  }
861
920
  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;
921
+ const state = {
922
+ selectors,
923
+ _params,
924
+ options,
925
+ world,
926
+ text: "Click element",
927
+ type: Types.CLICK,
928
+ operation: "click",
929
+ log: "***** click on " + selectors.element_name + " *****\n",
930
+ };
874
931
  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));
932
+ await _preCommand(state, this);
933
+ if (state.options && state.options.context) {
934
+ state.selectors.locators[0].text = state.options.context;
935
+ }
878
936
  try {
879
- await this._highlightElements(element);
880
- await element.click();
881
- await new Promise((resolve) => setTimeout(resolve, 1000));
937
+ await state.element.click();
938
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
882
939
  }
883
940
  catch (e) {
884
941
  // 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));
942
+ state.element = await this._locate(selectors, state.info, _params);
943
+ await state.element.dispatchEvent("click");
944
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
889
945
  }
890
946
  await this.waitForPageLoad();
891
- return info;
947
+ return state.info;
892
948
  }
893
949
  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;
950
+ await _commandError(state, e, this);
900
951
  }
901
952
  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
- });
953
+ _commandFinally(state, this);
922
954
  }
923
955
  }
924
956
  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;
957
+ const state = {
958
+ selectors,
959
+ _params,
960
+ options,
961
+ world,
962
+ type: checked ? Types.CHECK : Types.UNCHECK,
963
+ text: checked ? `Check element` : `Uncheck element`,
964
+ operation: "setCheck",
965
+ log: "***** check " + selectors.element_name + " *****\n",
966
+ };
935
967
  try {
936
- let element = await this._locate(selectors, info, _params);
937
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
968
+ await _preCommand(state, this);
969
+ state.info.checked = checked;
970
+ // let element = await this._locate(selectors, info, _params);
971
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
938
972
  try {
939
- await this._highlightElements(element);
940
- await element.setChecked(checked);
973
+ // await this._highlightElements(element);
974
+ await state.element.setChecked(checked);
941
975
  await new Promise((resolve) => setTimeout(resolve, 1000));
942
976
  }
943
977
  catch (e) {
@@ -946,179 +980,108 @@ class StableBrowser {
946
980
  }
947
981
  else {
948
982
  //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 });
983
+ state.info.log += "setCheck failed, will try again" + "\n";
984
+ state.element = await this._locate(selectors, state.info, _params);
985
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
952
986
  await new Promise((resolve) => setTimeout(resolve, 1000));
953
987
  }
954
988
  }
955
989
  await this.waitForPageLoad();
956
- return info;
990
+ return state.info;
957
991
  }
958
992
  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;
993
+ await _commandError(state, e, this);
965
994
  }
966
995
  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
- });
996
+ _commandFinally(state, this);
987
997
  }
988
998
  }
989
999
  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;
1000
+ const state = {
1001
+ selectors,
1002
+ _params,
1003
+ options,
1004
+ world,
1005
+ type: Types.HOVER,
1006
+ text: `Hover element`,
1007
+ operation: "hover",
1008
+ log: "***** hover " + selectors.element_name + " *****\n",
1009
+ };
999
1010
  try {
1000
- let element = await this._locate(selectors, info, _params);
1001
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1011
+ await _preCommand(state, this);
1002
1012
  try {
1003
- await this._highlightElements(element);
1004
- await element.hover();
1013
+ await state.element.hover();
1005
1014
  await new Promise((resolve) => setTimeout(resolve, 1000));
1006
1015
  }
1007
1016
  catch (e) {
1008
1017
  //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 });
1018
+ state.info.log += "hover failed, will try again" + "\n";
1019
+ state.element = await this._locate(selectors, state.info, _params);
1020
+ await state.element.hover({ timeout: 10000 });
1012
1021
  await new Promise((resolve) => setTimeout(resolve, 1000));
1013
1022
  }
1014
1023
  await this.waitForPageLoad();
1015
- return info;
1024
+ return state.info;
1016
1025
  }
1017
1026
  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;
1027
+ await _commandError(state, e, this);
1024
1028
  }
1025
1029
  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
- });
1030
+ _commandFinally(state, this);
1046
1031
  }
1047
1032
  }
1048
1033
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
1049
- this._validateSelectors(selectors);
1050
1034
  if (!values) {
1051
1035
  throw new Error("values is null");
1052
1036
  }
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;
1037
+ const state = {
1038
+ selectors,
1039
+ _params,
1040
+ options,
1041
+ world,
1042
+ value: values.toString(),
1043
+ type: Types.SELECT,
1044
+ text: `Select option: ${values}`,
1045
+ operation: "selectOption",
1046
+ log: "***** select option " + selectors.element_name + " *****\n",
1047
+ };
1061
1048
  try {
1062
- let element = await this._locate(selectors, info, _params);
1063
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1049
+ await _preCommand(state, this);
1064
1050
  try {
1065
- await this._highlightElements(element);
1066
- await element.selectOption(values);
1051
+ await state.element.selectOption(values);
1067
1052
  }
1068
1053
  catch (e) {
1069
1054
  //await this.closeUnexpectedPopups();
1070
- info.log += "selectOption failed, will try force" + "\n";
1071
- await element.selectOption(values, { timeout: 10000, force: true });
1055
+ state.info.log += "selectOption failed, will try force" + "\n";
1056
+ await state.element.selectOption(values, { timeout: 10000, force: true });
1072
1057
  }
1073
1058
  await this.waitForPageLoad();
1074
- return info;
1059
+ return state.info;
1075
1060
  }
1076
1061
  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;
1062
+ await _commandError(state, e, this);
1084
1063
  }
1085
1064
  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
- });
1065
+ _commandFinally(state, this);
1107
1066
  }
1108
1067
  }
1109
1068
  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;
1069
+ const state = {
1070
+ value: _value,
1071
+ _params,
1072
+ options,
1073
+ world,
1074
+ locate: false,
1075
+ scroll: false,
1076
+ highlight: false,
1077
+ type: Types.TYPE_PRESS,
1078
+ text: `Type value: ${_value}`,
1079
+ operation: "type",
1080
+ log: "",
1081
+ };
1119
1082
  try {
1120
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1121
- const valueSegment = _value.split("&&");
1083
+ await _preCommand(state, this);
1084
+ const valueSegment = state.value.split("&&");
1122
1085
  for (let i = 0; i < valueSegment.length; i++) {
1123
1086
  if (i > 0) {
1124
1087
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1138,134 +1101,76 @@ class StableBrowser {
1138
1101
  await this.page.keyboard.type(value);
1139
1102
  }
1140
1103
  }
1141
- return info;
1104
+ return state.info;
1142
1105
  }
1143
1106
  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;
1107
+ await _commandError(state, e, this);
1151
1108
  }
1152
1109
  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
- });
1110
+ _commandFinally(state, this);
1173
1111
  }
1174
1112
  }
1175
1113
  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;
1114
+ const state = {
1115
+ selectors,
1116
+ _params,
1117
+ value,
1118
+ options,
1119
+ world,
1120
+ type: Types.SET_INPUT,
1121
+ text: `Set input value`,
1122
+ operation: "setInputValue",
1123
+ log: "***** set input value " + selectors.element_name + " *****\n",
1124
+ };
1188
1125
  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);
1126
+ await _preCommand(state, this);
1127
+ let value = await this._replaceWithLocalData(state.value, this);
1194
1128
  try {
1195
- await element.evaluateHandle((el, value) => {
1129
+ await state.element.evaluateHandle((el, value) => {
1196
1130
  el.value = value;
1197
1131
  }, value);
1198
1132
  }
1199
1133
  catch (error) {
1200
1134
  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) => {
1135
+ await _screenshot(state, this);
1136
+ Object.assign(error, { info: state.info });
1137
+ await state.element.evaluateHandle((el, value) => {
1205
1138
  el.value = value;
1206
1139
  });
1207
1140
  }
1208
1141
  }
1209
1142
  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;
1143
+ await _commandError(state, e, this);
1216
1144
  }
1217
1145
  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
- });
1146
+ _commandFinally(state, this);
1239
1147
  }
1240
1148
  }
1241
1149
  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;
1150
+ const state = {
1151
+ selectors,
1152
+ _params,
1153
+ value: await this._replaceWithLocalData(value, this),
1154
+ options,
1155
+ world,
1156
+ type: Types.SET_DATE_TIME,
1157
+ text: `Set date time value: ${value}`,
1158
+ operation: "setDateTime",
1159
+ log: "***** set date time value " + selectors.element_name + " *****\n",
1160
+ throwError: false,
1161
+ };
1252
1162
  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);
1163
+ await _preCommand(state, this);
1259
1164
  try {
1260
- await element.click();
1165
+ await state.element.click();
1261
1166
  await new Promise((resolve) => setTimeout(resolve, 500));
1262
1167
  if (format) {
1263
- value = dayjs(value).format(format);
1264
- await element.fill(value);
1168
+ state.value = dayjs(state.value).format(format);
1169
+ await state.element.fill(state.value);
1265
1170
  }
1266
1171
  else {
1267
- const dateTimeValue = await getDateTimeValue({ value, element });
1268
- await element.evaluateHandle((el, dateTimeValue) => {
1172
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1173
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1269
1174
  el.value = ""; // clear input
1270
1175
  el.value = dateTimeValue;
1271
1176
  }, dateTimeValue);
@@ -1278,20 +1183,19 @@ class StableBrowser {
1278
1183
  }
1279
1184
  catch (err) {
1280
1185
  //await this.closeUnexpectedPopups();
1281
- this.logger.error("setting date time input failed " + JSON.stringify(info));
1186
+ this.logger.error("setting date time input failed " + JSON.stringify(state.info));
1282
1187
  this.logger.info("Trying again");
1283
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1284
- info.screenshotPath = screenshotPath;
1285
- Object.assign(err, { info: info });
1188
+ await _screenshot(state, this);
1189
+ Object.assign(err, { info: state.info });
1286
1190
  await element.click();
1287
1191
  await new Promise((resolve) => setTimeout(resolve, 500));
1288
1192
  if (format) {
1289
- value = dayjs(value).format(format);
1290
- await element.fill(value);
1193
+ state.value = dayjs(state.value).format(format);
1194
+ await state.element.fill(state.value);
1291
1195
  }
1292
1196
  else {
1293
- const dateTimeValue = await getDateTimeValue({ value, element });
1294
- await element.evaluateHandle((el, dateTimeValue) => {
1197
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1198
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1295
1199
  el.value = ""; // clear input
1296
1200
  el.value = dateTimeValue;
1297
1201
  }, dateTimeValue);
@@ -1304,60 +1208,39 @@ class StableBrowser {
1304
1208
  }
1305
1209
  }
1306
1210
  catch (e) {
1307
- error = e;
1308
- throw e;
1211
+ await _commandError(state, e, this);
1309
1212
  }
1310
1213
  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
- });
1214
+ _commandFinally(state, this);
1332
1215
  }
1333
1216
  }
1334
1217
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1335
1218
  _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
1219
  const newValue = await this._replaceWithLocalData(_value, world);
1220
+ const state = {
1221
+ selectors,
1222
+ _params,
1223
+ value: newValue,
1224
+ originalValue: _value,
1225
+ options,
1226
+ world,
1227
+ type: Types.FILL,
1228
+ text: `Click type input with value: ${_value}`,
1229
+ operation: "clickType",
1230
+ log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
1231
+ };
1346
1232
  if (newValue !== _value) {
1347
1233
  //this.logger.info(_value + "=" + newValue);
1348
1234
  _value = newValue;
1349
1235
  }
1350
- info.value = _value;
1351
1236
  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);
1237
+ await _preCommand(state, this);
1238
+ state.info.value = _value;
1356
1239
  if (options === null || options === undefined || !options.press) {
1357
1240
  try {
1358
- let currentValue = await element.inputValue();
1241
+ let currentValue = await state.element.inputValue();
1359
1242
  if (currentValue) {
1360
- await element.fill("");
1243
+ await state.element.fill("");
1361
1244
  }
1362
1245
  }
1363
1246
  catch (e) {
@@ -1366,22 +1249,22 @@ class StableBrowser {
1366
1249
  }
1367
1250
  if (options === null || options === undefined || options.press) {
1368
1251
  try {
1369
- await element.click({ timeout: 5000 });
1252
+ await state.element.click({ timeout: 5000 });
1370
1253
  }
1371
1254
  catch (e) {
1372
- await element.dispatchEvent("click");
1255
+ await state.element.dispatchEvent("click");
1373
1256
  }
1374
1257
  }
1375
1258
  else {
1376
1259
  try {
1377
- await element.focus();
1260
+ await state.element.focus();
1378
1261
  }
1379
1262
  catch (e) {
1380
- await element.dispatchEvent("focus");
1263
+ await state.element.dispatchEvent("focus");
1381
1264
  }
1382
1265
  }
1383
1266
  await new Promise((resolve) => setTimeout(resolve, 500));
1384
- const valueSegment = _value.split("&&");
1267
+ const valueSegment = state.value.split("&&");
1385
1268
  for (let i = 0; i < valueSegment.length; i++) {
1386
1269
  if (i > 0) {
1387
1270
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1401,14 +1284,14 @@ class StableBrowser {
1401
1284
  await new Promise((resolve) => setTimeout(resolve, 500));
1402
1285
  }
1403
1286
  }
1404
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1287
+ await _screenshot(state, this);
1405
1288
  if (enter === true) {
1406
1289
  await new Promise((resolve) => setTimeout(resolve, 2000));
1407
1290
  await this.page.keyboard.press("Enter");
1408
1291
  await this.waitForPageLoad();
1409
1292
  }
1410
1293
  else if (enter === false) {
1411
- await element.dispatchEvent("change");
1294
+ await state.element.dispatchEvent("change");
1412
1295
  //await this.page.keyboard.press("Tab");
1413
1296
  }
1414
1297
  else {
@@ -1417,104 +1300,50 @@ class StableBrowser {
1417
1300
  await this.waitForPageLoad();
1418
1301
  }
1419
1302
  }
1420
- return info;
1303
+ return state.info;
1421
1304
  }
1422
1305
  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;
1306
+ await _commandError(state, e, this);
1430
1307
  }
1431
1308
  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
- });
1309
+ _commandFinally(state, this);
1453
1310
  }
1454
1311
  }
1455
1312
  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;
1313
+ const state = {
1314
+ selectors,
1315
+ _params,
1316
+ value: unEscapeString(value),
1317
+ options,
1318
+ world,
1319
+ type: Types.FILL,
1320
+ text: `Fill input with value: ${value}`,
1321
+ operation: "fill",
1322
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1323
+ };
1467
1324
  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");
1325
+ await _preCommand(state, this);
1326
+ await state.element.fill(value);
1327
+ await state.element.dispatchEvent("change");
1473
1328
  if (enter) {
1474
1329
  await new Promise((resolve) => setTimeout(resolve, 2000));
1475
1330
  await this.page.keyboard.press("Enter");
1476
1331
  }
1477
1332
  await this.waitForPageLoad();
1478
- return info;
1333
+ return state.info;
1479
1334
  }
1480
1335
  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;
1336
+ await _commandError(state, e, this);
1488
1337
  }
1489
1338
  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
- });
1339
+ _commandFinally(state, this);
1511
1340
  }
1512
1341
  }
1513
1342
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1514
1343
  return await this._getText(selectors, 0, _params, options, info, world);
1515
1344
  }
1516
1345
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1517
- this._validateSelectors(selectors);
1346
+ _validateSelectors(selectors);
1518
1347
  let screenshotId = null;
1519
1348
  let screenshotPath = null;
1520
1349
  if (!info.log) {
@@ -1558,166 +1387,124 @@ class StableBrowser {
1558
1387
  }
1559
1388
  }
1560
1389
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1561
- var _a;
1562
- this._validateSelectors(selectors);
1563
1390
  if (!pattern) {
1564
1391
  throw new Error("pattern is null");
1565
1392
  }
1566
1393
  if (!text) {
1567
1394
  throw new Error("text is null");
1568
1395
  }
1396
+ const state = {
1397
+ selectors,
1398
+ _params,
1399
+ pattern,
1400
+ value: pattern,
1401
+ options,
1402
+ world,
1403
+ locate: false,
1404
+ scroll: false,
1405
+ screenshot: false,
1406
+ highlight: false,
1407
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1408
+ text: `Verify element contains pattern: ${pattern}`,
1409
+ operation: "containsPattern",
1410
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1411
+ };
1569
1412
  const newValue = await this._replaceWithLocalData(text, world);
1570
1413
  if (newValue !== text) {
1571
1414
  this.logger.info(text + "=" + newValue);
1572
1415
  text = newValue;
1573
1416
  }
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
1417
  let foundObj = null;
1586
1418
  try {
1587
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1419
+ await _preCommand(state, this);
1420
+ state.info.pattern = pattern;
1421
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1588
1422
  if (foundObj && foundObj.element) {
1589
- await this.scrollIfNeeded(foundObj.element, info);
1423
+ await this.scrollIfNeeded(foundObj.element, state.info);
1590
1424
  }
1591
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1425
+ await _screenshot(state, this);
1592
1426
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1593
1427
  pattern = pattern.replace("{text}", escapedText);
1594
1428
  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;
1429
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1430
+ state.info.foundText = foundObj?.text;
1597
1431
  throw new Error("element doesn't contain text " + text);
1598
1432
  }
1599
- return info;
1433
+ return state.info;
1600
1434
  }
1601
1435
  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;
1436
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1437
+ await _commandError(state, e, this);
1610
1438
  }
1611
1439
  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
- });
1440
+ _commandFinally(state, this);
1633
1441
  }
1634
1442
  }
1635
1443
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1636
- var _a, _b, _c;
1637
- this._validateSelectors(selectors);
1638
- text = unEscapeString(text);
1444
+ const state = {
1445
+ selectors,
1446
+ _params,
1447
+ value: text,
1448
+ options,
1449
+ world,
1450
+ locate: false,
1451
+ scroll: false,
1452
+ screenshot: false,
1453
+ highlight: false,
1454
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1455
+ text: `Verify element contains text: ${text}`,
1456
+ operation: "containsText",
1457
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1458
+ };
1639
1459
  if (!text) {
1640
1460
  throw new Error("text is null");
1641
1461
  }
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;
1462
+ text = unEscapeString(text);
1650
1463
  const newValue = await this._replaceWithLocalData(text, world);
1651
1464
  if (newValue !== text) {
1652
1465
  this.logger.info(text + "=" + newValue);
1653
1466
  text = newValue;
1654
1467
  }
1655
- info.value = text;
1656
1468
  let foundObj = null;
1657
1469
  try {
1658
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1470
+ await _preCommand(state, this);
1471
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1659
1472
  if (foundObj && foundObj.element) {
1660
- await this.scrollIfNeeded(foundObj.element, info);
1473
+ await this.scrollIfNeeded(foundObj.element, state.info);
1661
1474
  }
1662
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1475
+ await _screenshot(state, this);
1663
1476
  const dateAlternatives = findDateAlternatives(text);
1664
1477
  const numberAlternatives = findNumberAlternatives(text);
1665
1478
  if (dateAlternatives.date) {
1666
1479
  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;
1480
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1481
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1482
+ return state.info;
1670
1483
  }
1671
1484
  }
1672
1485
  throw new Error("element doesn't contain text " + text);
1673
1486
  }
1674
1487
  else if (numberAlternatives.number) {
1675
1488
  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;
1489
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1490
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1491
+ return state.info;
1679
1492
  }
1680
1493
  }
1681
1494
  throw new Error("element doesn't contain text " + text);
1682
1495
  }
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;
1496
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1497
+ state.info.foundText = foundObj?.text;
1498
+ state.info.value = foundObj?.value;
1686
1499
  throw new Error("element doesn't contain text " + text);
1687
1500
  }
1688
- return info;
1501
+ return state.info;
1689
1502
  }
1690
1503
  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;
1504
+ await _commandError(state, e, this);
1698
1505
  }
1699
1506
  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
- });
1507
+ _commandFinally(state, this);
1721
1508
  }
1722
1509
  }
1723
1510
  _getDataFile(world = null) {
@@ -1899,11 +1686,9 @@ class StableBrowser {
1899
1686
  if (!fs.existsSync(world.screenshotPath)) {
1900
1687
  fs.mkdirSync(world.screenshotPath, { recursive: true });
1901
1688
  }
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");
1689
+ // to make sure the path doesn't start with -
1690
+ const uuidStr = "id_" + randomUUID();
1691
+ const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
1907
1692
  try {
1908
1693
  await this.takeScreenshot(screenshotPath);
1909
1694
  // let buffer = await this.page.screenshot({ timeout: 4000 });
@@ -1917,7 +1702,7 @@ class StableBrowser {
1917
1702
  catch (e) {
1918
1703
  this.logger.info("unable to take screenshot, ignored");
1919
1704
  }
1920
- result.screenshotId = nextIndex;
1705
+ result.screenshotId = uuidStr;
1921
1706
  result.screenshotPath = screenshotPath;
1922
1707
  if (info && info.box) {
1923
1708
  await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
@@ -1991,127 +1776,69 @@ class StableBrowser {
1991
1776
  }
1992
1777
  }
1993
1778
  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;
1779
+ const state = {
1780
+ selectors,
1781
+ _params,
1782
+ options,
1783
+ world,
1784
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1785
+ text: `Verify element exists in page`,
1786
+ operation: "verifyElementExistInPage",
1787
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1788
+ };
1999
1789
  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
1790
  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;
1791
+ await _preCommand(state, this);
1792
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1793
+ return state.info;
2013
1794
  }
2014
1795
  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;
1796
+ await _commandError(state, e, this);
2022
1797
  }
2023
1798
  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
- });
1799
+ _commandFinally(state, this);
2044
1800
  }
2045
1801
  }
2046
1802
  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;
1803
+ const state = {
1804
+ selectors,
1805
+ _params,
1806
+ attribute,
1807
+ variable,
1808
+ options,
1809
+ world,
1810
+ type: Types.EXTRACT,
1811
+ text: `Extract attribute from element`,
1812
+ operation: "extractAttribute",
1813
+ log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
1814
+ };
2052
1815
  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
1816
  try {
2058
- const element = await this._locate(selectors, info, _params);
2059
- await this._highlightElements(element);
2060
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1817
+ await _preCommand(state, this);
2061
1818
  switch (attribute) {
2062
1819
  case "inner_text":
2063
- info.value = await element.innerText();
1820
+ state.value = await state.element.innerText();
2064
1821
  break;
2065
1822
  case "href":
2066
- info.value = await element.getAttribute("href");
1823
+ state.value = await state.element.getAttribute("href");
2067
1824
  break;
2068
1825
  case "value":
2069
- info.value = await element.inputValue();
1826
+ state.value = await state.element.inputValue();
2070
1827
  break;
2071
1828
  default:
2072
- info.value = await element.getAttribute(attribute);
1829
+ state.value = await state.element.getAttribute(attribute);
2073
1830
  break;
2074
1831
  }
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;
1832
+ state.info.value = state.value;
1833
+ this.setTestData({ [variable]: state.value }, world);
1834
+ this.logger.info("set test data: " + variable + "=" + state.value);
1835
+ return state.info;
2082
1836
  }
2083
1837
  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;
1838
+ await _commandError(state, e, this);
2091
1839
  }
2092
1840
  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
- });
1841
+ _commandFinally(state, this);
2115
1842
  }
2116
1843
  }
2117
1844
  async extractEmailData(emailAddress, options, world) {
@@ -2188,7 +1915,8 @@ class StableBrowser {
2188
1915
  catch (e) {
2189
1916
  errorCount++;
2190
1917
  if (errorCount > 3) {
2191
- throw e;
1918
+ // throw e;
1919
+ await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
2192
1920
  }
2193
1921
  // ignore
2194
1922
  }
@@ -2299,11 +2027,12 @@ class StableBrowser {
2299
2027
  info.screenshotPath = screenshotPath;
2300
2028
  Object.assign(e, { info: info });
2301
2029
  error = e;
2302
- throw e;
2030
+ // throw e;
2031
+ await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
2303
2032
  }
2304
2033
  finally {
2305
2034
  const endTime = Date.now();
2306
- this._reportToWorld(world, {
2035
+ _reportToWorld(world, {
2307
2036
  type: Types.VERIFY_PAGE_PATH,
2308
2037
  text: "Verify page path",
2309
2038
  screenshotId,
@@ -2312,7 +2041,7 @@ class StableBrowser {
2312
2041
  status: "FAILED",
2313
2042
  startTime,
2314
2043
  endTime,
2315
- message: error === null || error === void 0 ? void 0 : error.message,
2044
+ message: error?.message,
2316
2045
  }
2317
2046
  : {
2318
2047
  status: "PASSED",
@@ -2325,52 +2054,58 @@ class StableBrowser {
2325
2054
  }
2326
2055
  async verifyTextExistInPage(text, options = {}, world = null) {
2327
2056
  text = unEscapeString(text);
2328
- const startTime = Date.now();
2057
+ const state = {
2058
+ text_search: text,
2059
+ options,
2060
+ world,
2061
+ locate: false,
2062
+ scroll: false,
2063
+ highlight: false,
2064
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2065
+ text: `Verify text exists in page`,
2066
+ operation: "verifyTextExistInPage",
2067
+ log: "***** verify text " + text + " exists in page *****\n",
2068
+ };
2329
2069
  const timeout = this._getLoadTimeout(options);
2330
- let error = null;
2331
- let screenshotId = null;
2332
- let screenshotPath = null;
2333
2070
  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
2071
  const newValue = await this._replaceWithLocalData(text, world);
2338
2072
  if (newValue !== text) {
2339
2073
  this.logger.info(text + "=" + newValue);
2340
2074
  text = newValue;
2341
2075
  }
2342
- info.text = text;
2343
2076
  let dateAlternatives = findDateAlternatives(text);
2344
2077
  let numberAlternatives = findNumberAlternatives(text);
2345
2078
  try {
2079
+ await _preCommand(state, this);
2080
+ state.info.text = text;
2346
2081
  while (true) {
2347
2082
  const frames = this.page.frames();
2348
2083
  let results = [];
2349
2084
  for (let i = 0; i < frames.length; i++) {
2350
2085
  if (dateAlternatives.date) {
2351
2086
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2352
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2087
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
2353
2088
  result.frame = frames[i];
2354
2089
  results.push(result);
2355
2090
  }
2356
2091
  }
2357
2092
  else if (numberAlternatives.number) {
2358
2093
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2359
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2094
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
2360
2095
  result.frame = frames[i];
2361
2096
  results.push(result);
2362
2097
  }
2363
2098
  }
2364
2099
  else {
2365
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2100
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2366
2101
  result.frame = frames[i];
2367
2102
  results.push(result);
2368
2103
  }
2369
2104
  }
2370
- info.results = results;
2105
+ state.info.results = results;
2371
2106
  const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2372
2107
  if (resultWithElementsFound.length === 0) {
2373
- if (Date.now() - startTime > timeout) {
2108
+ if (Date.now() - state.startTime > timeout) {
2374
2109
  throw new Error(`Text ${text} not found in page`);
2375
2110
  }
2376
2111
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -2382,44 +2117,89 @@ class StableBrowser {
2382
2117
  await this._highlightElements(frame, dataAttribute);
2383
2118
  const element = await frame.$(dataAttribute);
2384
2119
  if (element) {
2385
- await this.scrollIfNeeded(element, info);
2120
+ await this.scrollIfNeeded(element, state.info);
2386
2121
  await element.dispatchEvent("bvt_verify_page_contains_text");
2387
2122
  }
2388
2123
  }
2389
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2390
- return info;
2124
+ await _screenshot(state, this);
2125
+ return state.info;
2391
2126
  }
2392
2127
  // await expect(element).toHaveCount(1, { timeout: 10000 });
2393
2128
  }
2394
2129
  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;
2130
+ await _commandError(state, e, this);
2402
2131
  }
2403
2132
  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,
2133
+ _commandFinally(state, this);
2134
+ }
2135
+ }
2136
+ async waitForTextToDisappear(text, options = {}, world = null) {
2137
+ text = unEscapeString(text);
2138
+ const state = {
2139
+ text_search: text,
2140
+ options,
2141
+ world,
2142
+ locate: false,
2143
+ scroll: false,
2144
+ highlight: false,
2145
+ type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
2146
+ text: `Verify text does not exist in page`,
2147
+ operation: "verifyTextNotExistInPage",
2148
+ log: "***** verify text " + text + " does not exist in page *****\n",
2149
+ };
2150
+ const timeout = this._getLoadTimeout(options);
2151
+ await new Promise((resolve) => setTimeout(resolve, 2000));
2152
+ const newValue = await this._replaceWithLocalData(text, world);
2153
+ if (newValue !== text) {
2154
+ this.logger.info(text + "=" + newValue);
2155
+ text = newValue;
2156
+ }
2157
+ let dateAlternatives = findDateAlternatives(text);
2158
+ let numberAlternatives = findNumberAlternatives(text);
2159
+ try {
2160
+ await _preCommand(state, this);
2161
+ state.info.text = text;
2162
+ while (true) {
2163
+ const frames = this.page.frames();
2164
+ let results = [];
2165
+ for (let i = 0; i < frames.length; i++) {
2166
+ if (dateAlternatives.date) {
2167
+ for (let j = 0; j < dateAlternatives.dates.length; j++) {
2168
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
2169
+ result.frame = frames[i];
2170
+ results.push(result);
2171
+ }
2415
2172
  }
2416
- : {
2417
- status: "PASSED",
2418
- startTime,
2419
- endTime,
2420
- },
2421
- info: info,
2422
- });
2173
+ else if (numberAlternatives.number) {
2174
+ for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2175
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
2176
+ result.frame = frames[i];
2177
+ results.push(result);
2178
+ }
2179
+ }
2180
+ else {
2181
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2182
+ result.frame = frames[i];
2183
+ results.push(result);
2184
+ }
2185
+ }
2186
+ state.info.results = results;
2187
+ const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2188
+ if (resultWithElementsFound.length === 0) {
2189
+ await _screenshot(state, this);
2190
+ return state.info;
2191
+ }
2192
+ if (Date.now() - state.startTime > timeout) {
2193
+ throw new Error(`Text ${text} found in page`);
2194
+ }
2195
+ await new Promise((resolve) => setTimeout(resolve, 1000));
2196
+ }
2197
+ }
2198
+ catch (e) {
2199
+ await _commandError(state, e, this);
2200
+ }
2201
+ finally {
2202
+ _commandFinally(state, this);
2423
2203
  }
2424
2204
  }
2425
2205
  _getServerUrl() {
@@ -2482,11 +2262,12 @@ class StableBrowser {
2482
2262
  info.screenshotPath = screenshotPath;
2483
2263
  Object.assign(e, { info: info });
2484
2264
  error = e;
2485
- throw e;
2265
+ // throw e;
2266
+ await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
2486
2267
  }
2487
2268
  finally {
2488
2269
  const endTime = Date.now();
2489
- this._reportToWorld(world, {
2270
+ _reportToWorld(world, {
2490
2271
  type: Types.VERIFY_VISUAL,
2491
2272
  text: "Visual verification",
2492
2273
  screenshotId,
@@ -2495,7 +2276,7 @@ class StableBrowser {
2495
2276
  status: "FAILED",
2496
2277
  startTime,
2497
2278
  endTime,
2498
- message: error === null || error === void 0 ? void 0 : error.message,
2279
+ message: error?.message,
2499
2280
  }
2500
2281
  : {
2501
2282
  status: "PASSED",
@@ -2527,7 +2308,7 @@ class StableBrowser {
2527
2308
  this.logger.info("Table data verified");
2528
2309
  }
2529
2310
  async getTableData(selectors, _params = null, options = {}, world = null) {
2530
- this._validateSelectors(selectors);
2311
+ _validateSelectors(selectors);
2531
2312
  const startTime = Date.now();
2532
2313
  let error = null;
2533
2314
  let screenshotId = null;
@@ -2549,11 +2330,12 @@ class StableBrowser {
2549
2330
  info.screenshotPath = screenshotPath;
2550
2331
  Object.assign(e, { info: info });
2551
2332
  error = e;
2552
- throw e;
2333
+ // throw e;
2334
+ await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
2553
2335
  }
2554
2336
  finally {
2555
2337
  const endTime = Date.now();
2556
- this._reportToWorld(world, {
2338
+ _reportToWorld(world, {
2557
2339
  element_name: selectors.element_name,
2558
2340
  type: Types.GET_TABLE_DATA,
2559
2341
  text: "Get table data",
@@ -2563,7 +2345,7 @@ class StableBrowser {
2563
2345
  status: "FAILED",
2564
2346
  startTime,
2565
2347
  endTime,
2566
- message: error === null || error === void 0 ? void 0 : error.message,
2348
+ message: error?.message,
2567
2349
  }
2568
2350
  : {
2569
2351
  status: "PASSED",
@@ -2575,7 +2357,7 @@ class StableBrowser {
2575
2357
  }
2576
2358
  }
2577
2359
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2578
- this._validateSelectors(selectors);
2360
+ _validateSelectors(selectors);
2579
2361
  if (!query) {
2580
2362
  throw new Error("query is null");
2581
2363
  }
@@ -2714,11 +2496,12 @@ class StableBrowser {
2714
2496
  info.screenshotPath = screenshotPath;
2715
2497
  Object.assign(e, { info: info });
2716
2498
  error = e;
2717
- throw e;
2499
+ // throw e;
2500
+ await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
2718
2501
  }
2719
2502
  finally {
2720
2503
  const endTime = Date.now();
2721
- this._reportToWorld(world, {
2504
+ _reportToWorld(world, {
2722
2505
  element_name: selectors.element_name,
2723
2506
  type: Types.ANALYZE_TABLE,
2724
2507
  text: "Analyze table",
@@ -2728,7 +2511,7 @@ class StableBrowser {
2728
2511
  status: "FAILED",
2729
2512
  startTime,
2730
2513
  endTime,
2731
- message: error === null || error === void 0 ? void 0 : error.message,
2514
+ message: error?.message,
2732
2515
  }
2733
2516
  : {
2734
2517
  status: "PASSED",
@@ -2740,27 +2523,7 @@ class StableBrowser {
2740
2523
  }
2741
2524
  }
2742
2525
  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;
2526
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2764
2527
  }
2765
2528
  _getLoadTimeout(options) {
2766
2529
  let timeout = 15000;
@@ -2811,7 +2574,7 @@ class StableBrowser {
2811
2574
  await new Promise((resolve) => setTimeout(resolve, 2000));
2812
2575
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2813
2576
  const endTime = Date.now();
2814
- this._reportToWorld(world, {
2577
+ _reportToWorld(world, {
2815
2578
  type: Types.GET_PAGE_STATUS,
2816
2579
  text: "Wait for page load",
2817
2580
  screenshotId,
@@ -2820,7 +2583,7 @@ class StableBrowser {
2820
2583
  status: "FAILED",
2821
2584
  startTime,
2822
2585
  endTime,
2823
- message: error === null || error === void 0 ? void 0 : error.message,
2586
+ message: error?.message,
2824
2587
  }
2825
2588
  : {
2826
2589
  status: "PASSED",
@@ -2831,41 +2594,35 @@ class StableBrowser {
2831
2594
  }
2832
2595
  }
2833
2596
  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 = {};
2597
+ const state = {
2598
+ options,
2599
+ world,
2600
+ locate: false,
2601
+ scroll: false,
2602
+ highlight: false,
2603
+ type: Types.CLOSE_PAGE,
2604
+ text: `Close page`,
2605
+ operation: "closePage",
2606
+ log: "***** close page *****\n",
2607
+ throwError: false,
2608
+ };
2839
2609
  try {
2610
+ await _preCommand(state, this);
2840
2611
  await this.page.close();
2841
2612
  }
2842
2613
  catch (e) {
2843
2614
  console.log(".");
2615
+ await _commandError(state, e, this);
2844
2616
  }
2845
2617
  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
- });
2618
+ _commandFinally(state, this);
2867
2619
  }
2868
2620
  }
2621
+ saveTestDataAsGlobal(options, world) {
2622
+ const dataFile = this._getDataFile(world);
2623
+ process.env.GLOBAL_TEST_DATA_FILE = dataFile;
2624
+ this.logger.info("Save the scenario test data as global for the following scenarios.");
2625
+ }
2869
2626
  async setViewportSize(width, hight, options = {}, world = null) {
2870
2627
  const startTime = Date.now();
2871
2628
  let error = null;
@@ -2883,12 +2640,13 @@ class StableBrowser {
2883
2640
  }
2884
2641
  catch (e) {
2885
2642
  console.log(".");
2643
+ await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
2886
2644
  }
2887
2645
  finally {
2888
2646
  await new Promise((resolve) => setTimeout(resolve, 2000));
2889
2647
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2890
2648
  const endTime = Date.now();
2891
- this._reportToWorld(world, {
2649
+ _reportToWorld(world, {
2892
2650
  type: Types.SET_VIEWPORT,
2893
2651
  text: "set viewport size to " + width + "x" + hight,
2894
2652
  screenshotId,
@@ -2897,7 +2655,7 @@ class StableBrowser {
2897
2655
  status: "FAILED",
2898
2656
  startTime,
2899
2657
  endTime,
2900
- message: error === null || error === void 0 ? void 0 : error.message,
2658
+ message: error?.message,
2901
2659
  }
2902
2660
  : {
2903
2661
  status: "PASSED",
@@ -2919,12 +2677,13 @@ class StableBrowser {
2919
2677
  }
2920
2678
  catch (e) {
2921
2679
  console.log(".");
2680
+ await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
2922
2681
  }
2923
2682
  finally {
2924
2683
  await new Promise((resolve) => setTimeout(resolve, 2000));
2925
2684
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2926
2685
  const endTime = Date.now();
2927
- this._reportToWorld(world, {
2686
+ _reportToWorld(world, {
2928
2687
  type: Types.GET_PAGE_STATUS,
2929
2688
  text: "page relaod",
2930
2689
  screenshotId,
@@ -2933,7 +2692,7 @@ class StableBrowser {
2933
2692
  status: "FAILED",
2934
2693
  startTime,
2935
2694
  endTime,
2936
- message: error === null || error === void 0 ? void 0 : error.message,
2695
+ message: error?.message,
2937
2696
  }
2938
2697
  : {
2939
2698
  status: "PASSED",
@@ -2960,11 +2719,37 @@ class StableBrowser {
2960
2719
  console.log("#-#");
2961
2720
  }
2962
2721
  }
2963
- _reportToWorld(world, properties) {
2964
- if (!world || !world.attach) {
2965
- return;
2722
+ async beforeStep(world, step) {
2723
+ this.stepName = step.pickleStep.text;
2724
+ this.logger.info("step: " + this.stepName);
2725
+ if (this.stepIndex === undefined) {
2726
+ this.stepIndex = 0;
2727
+ }
2728
+ else {
2729
+ this.stepIndex++;
2730
+ }
2731
+ if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
2732
+ if (this.context.browserObject.context) {
2733
+ await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
2734
+ }
2735
+ }
2736
+ if (this.tags === null && step && step.pickle && step.pickle.tags) {
2737
+ this.tags = step.pickle.tags.map((tag) => tag.name);
2738
+ // check if @global_test_data tag is present
2739
+ if (this.tags.includes("@global_test_data")) {
2740
+ this.saveTestDataAsGlobal({}, world);
2741
+ }
2742
+ }
2743
+ }
2744
+ async afterStep(world, step) {
2745
+ this.stepName = null;
2746
+ if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
2747
+ if (this.context.browserObject.context) {
2748
+ await this.context.browserObject.context.tracing.stopChunk({
2749
+ path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
2750
+ });
2751
+ }
2966
2752
  }
2967
- world.attach(JSON.stringify(properties), { mediaType: "application/json" });
2968
2753
  }
2969
2754
  }
2970
2755
  function createTimedPromise(promise, label) {