automation_model 1.0.465-dev → 1.0.465-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 +576 -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(),
@@ -177,7 +193,9 @@ class StableBrowser {
177
193
  time: new Date().toISOString(),
178
194
  };
179
195
  this.context.webLogger.push(obj);
180
- (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
196
+ if (msg.type() === "error") {
197
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
198
+ }
181
199
  });
182
200
  }
183
201
  registerRequestListener(page, context, logFile) {
@@ -185,7 +203,6 @@ class StableBrowser {
185
203
  this.context.networkLogger = [];
186
204
  }
187
205
  page.on("request", async (data) => {
188
- var _a;
189
206
  const startTime = new Date().getTime();
190
207
  try {
191
208
  const pageUrl = new URL(page.url());
@@ -210,10 +227,10 @@ class StableBrowser {
210
227
  startTime,
211
228
  };
212
229
  context.networkLogger.push(obj);
213
- (_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" });
214
231
  }
215
232
  catch (error) {
216
- console.error("Error in request listener", error);
233
+ // console.error("Error in request listener", error);
217
234
  context.networkLogger.push({
218
235
  error: "not able to listen",
219
236
  message: error.message,
@@ -235,20 +252,6 @@ class StableBrowser {
235
252
  timeout: 60000,
236
253
  });
237
254
  }
238
- _validateSelectors(selectors) {
239
- if (!selectors) {
240
- throw new Error("selectors is null");
241
- }
242
- if (!selectors.locators) {
243
- throw new Error("selectors.locators is null");
244
- }
245
- if (!Array.isArray(selectors.locators)) {
246
- throw new Error("selectors.locators expected to be array");
247
- }
248
- if (selectors.locators.length === 0) {
249
- throw new Error("selectors.locators expected to be non empty array");
250
- }
251
- }
252
255
  _fixUsingParams(text, _params) {
253
256
  if (!_params || typeof text !== "string") {
254
257
  return text;
@@ -316,7 +319,7 @@ class StableBrowser {
316
319
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
317
320
  }
318
321
  }
319
- if (locator === null || locator === void 0 ? void 0 : locator.engine) {
322
+ if (locator?.engine) {
320
323
  if (locator.engine === "css") {
321
324
  locatorReturn = scope.locator(locator.selector);
322
325
  }
@@ -340,7 +343,7 @@ class StableBrowser {
340
343
  if (css && css.locator) {
341
344
  css = css.locator;
342
345
  }
343
- 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);
344
347
  if (result.elementCount === 0) {
345
348
  return;
346
349
  }
@@ -355,7 +358,7 @@ class StableBrowser {
355
358
  }
356
359
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
357
360
  //const stringifyText = JSON.stringify(text);
358
- return await scope.evaluate(([text, tag, regex, partial]) => {
361
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
359
362
  function isParent(parent, child) {
360
363
  let currentNode = child.parentNode;
361
364
  while (currentNode !== null) {
@@ -392,7 +395,7 @@ class StableBrowser {
392
395
  }
393
396
  document.collectAllShadowDomElements = collectAllShadowDomElements;
394
397
  if (!tag) {
395
- tag = "*";
398
+ tag = "*:not(script, style, head)";
396
399
  }
397
400
  let regexpSearch = document.getRegex(text);
398
401
  if (regexpSearch) {
@@ -474,6 +477,15 @@ class StableBrowser {
474
477
  }, [text1, tag1, regex1, partial1]);
475
478
  }
476
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
+ }
477
489
  let locatorSearch = selectorHierarchy[index];
478
490
  try {
479
491
  locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
@@ -486,13 +498,18 @@ class StableBrowser {
486
498
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
487
499
  let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
488
500
  if (!locatorString) {
501
+ info.failCause.textNotFound = true;
502
+ info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
489
503
  return;
490
504
  }
491
505
  locator = this._getLocator({ css: locatorString }, scope, _params);
492
506
  }
493
507
  else if (locatorSearch.text) {
494
- 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);
495
510
  if (result.elementCount === 0) {
511
+ info.failCause.textNotFound = true;
512
+ info.failCause.lastError = "failed to locate element by text: " + text;
496
513
  return;
497
514
  }
498
515
  locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
@@ -509,6 +526,9 @@ class StableBrowser {
509
526
  // cssHref = true;
510
527
  // }
511
528
  let count = await locator.count();
529
+ if (count > 0 && !info.failCause.count) {
530
+ info.failCause.count = count;
531
+ }
512
532
  //info.log += "total elements found " + count + "\n";
513
533
  //let visibleCount = 0;
514
534
  let visibleLocator = null;
@@ -526,6 +546,8 @@ class StableBrowser {
526
546
  foundLocators.push(locator.nth(j));
527
547
  }
528
548
  else {
549
+ info.failCause.visible = visible;
550
+ info.failCause.enabled = enabled;
529
551
  if (!info.printMessages) {
530
552
  info.printMessages = {};
531
553
  }
@@ -537,6 +559,11 @@ class StableBrowser {
537
559
  }
538
560
  }
539
561
  async closeUnexpectedPopups(info, _params) {
562
+ if (!info) {
563
+ info = {};
564
+ info.failCause = {};
565
+ info.log = "";
566
+ }
540
567
  if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
541
568
  if (!info) {
542
569
  info = {};
@@ -577,7 +604,10 @@ class StableBrowser {
577
604
  }
578
605
  return { rerun: false };
579
606
  }
580
- async _locate(selectors, info, _params, timeout = 30000) {
607
+ async _locate(selectors, info, _params, timeout) {
608
+ if (!timeout) {
609
+ timeout = 30000;
610
+ }
581
611
  for (let i = 0; i < 3; i++) {
582
612
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
583
613
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -591,16 +621,15 @@ class StableBrowser {
591
621
  }
592
622
  throw new Error("unable to locate element " + JSON.stringify(selectors));
593
623
  }
594
- async _locate_internal(selectors, info, _params, timeout = 30000) {
595
- let highPriorityTimeout = 5000;
596
- let visibleOnlyTimeout = 6000;
597
- let startTime = performance.now();
598
- let locatorsCount = 0;
599
- //let arrayMode = Array.isArray(selectors);
624
+ async _findFrameScope(selectors, timeout = 30000, info) {
625
+ if (!info) {
626
+ info = {};
627
+ info.failCause = {};
628
+ info.log = "";
629
+ }
600
630
  let scope = this.page;
601
- // for the simple click usecase
602
631
  if (selectors.frame) {
603
- scope = selectors.frame;
632
+ return selectors.frame;
604
633
  }
605
634
  if (selectors.iframe_src || selectors.frameLocators) {
606
635
  const findFrame = async (frame, framescope) => {
@@ -628,7 +657,6 @@ class StableBrowser {
628
657
  }
629
658
  return framescope;
630
659
  };
631
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
632
660
  while (true) {
633
661
  let frameFound = false;
634
662
  if (selectors.nestFrmLoc) {
@@ -652,6 +680,8 @@ class StableBrowser {
652
680
  if (!scope) {
653
681
  info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
654
682
  if (performance.now() - startTime > timeout) {
683
+ info.failCause.iframeNotFound = true;
684
+ info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
655
685
  throw new Error("unable to locate iframe " + selectors.iframe_src);
656
686
  }
657
687
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -661,6 +691,30 @@ class StableBrowser {
661
691
  }
662
692
  }
663
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);
664
718
  let selectorsLocators = null;
665
719
  selectorsLocators = selectors.locators;
666
720
  // group selectors by priority
@@ -762,6 +816,8 @@ class StableBrowser {
762
816
  }
763
817
  this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
764
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";
765
821
  throw new Error("failed to locate first element no elements found, " + info.log);
766
822
  }
767
823
  async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
@@ -793,6 +849,9 @@ class StableBrowser {
793
849
  });
794
850
  result.locatorIndex = i;
795
851
  }
852
+ if (foundLocators.length > 1) {
853
+ info.failCause.foundMultiple = true;
854
+ }
796
855
  }
797
856
  return result;
798
857
  }
@@ -805,12 +864,12 @@ class StableBrowser {
805
864
  while (true) {
806
865
  try {
807
866
  const result = await locate_element(this.context, elementDescription, "click");
808
- if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
867
+ if (result?.elementNumber >= 0) {
809
868
  const selectors = {
810
- frame: result === null || result === void 0 ? void 0 : result.frame,
869
+ frame: result?.frame,
811
870
  locators: [
812
871
  {
813
- css: result === null || result === void 0 ? void 0 : result.css,
872
+ css: result?.css,
814
873
  },
815
874
  ],
816
875
  };
@@ -820,7 +879,8 @@ class StableBrowser {
820
879
  }
821
880
  catch (e) {
822
881
  if (performance.now() - startTime > timeout) {
823
- throw e;
882
+ // throw e;
883
+ await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
824
884
  }
825
885
  }
826
886
  await new Promise((resolve) => setTimeout(resolve, 3000));
@@ -835,12 +895,12 @@ class StableBrowser {
835
895
  while (true) {
836
896
  try {
837
897
  const result = await locate_element(this.context, elementDescription, "fill", value);
838
- if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
898
+ if (result?.elementNumber >= 0) {
839
899
  const selectors = {
840
- frame: result === null || result === void 0 ? void 0 : result.frame,
900
+ frame: result?.frame,
841
901
  locators: [
842
902
  {
843
- css: result === null || result === void 0 ? void 0 : result.css,
903
+ css: result?.css,
844
904
  },
845
905
  ],
846
906
  };
@@ -850,92 +910,68 @@ class StableBrowser {
850
910
  }
851
911
  catch (e) {
852
912
  if (performance.now() - startTime > timeout) {
853
- throw e;
913
+ // throw e;
914
+ await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
854
915
  }
855
916
  }
856
917
  await new Promise((resolve) => setTimeout(resolve, 3000));
857
918
  }
858
919
  }
859
920
  async click(selectors, _params, options = {}, world = null) {
860
- this._validateSelectors(selectors);
861
- const startTime = Date.now();
862
- if (options && options.context) {
863
- selectors.locators[0].text = options.context;
864
- }
865
- const info = {};
866
- info.log = "***** click on " + selectors.element_name + " *****\n";
867
- info.operation = "click";
868
- info.selectors = selectors;
869
- let error = null;
870
- let screenshotId = null;
871
- 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
+ };
872
931
  try {
873
- let element = await this._locate(selectors, info, _params);
874
- await this.scrollIfNeeded(element, info);
875
- ({ 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
+ }
876
936
  try {
877
- await this._highlightElements(element);
878
- await element.click();
879
- await new Promise((resolve) => setTimeout(resolve, 1000));
937
+ await state.element.click();
938
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
880
939
  }
881
940
  catch (e) {
882
941
  // await this.closeUnexpectedPopups();
883
- info.log += "click failed, will try again" + "\n";
884
- element = await this._locate(selectors, info, _params);
885
- await element.dispatchEvent("click");
886
- 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));
887
945
  }
888
946
  await this.waitForPageLoad();
889
- return info;
947
+ return state.info;
890
948
  }
891
949
  catch (e) {
892
- this.logger.error("click failed " + info.log);
893
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
894
- info.screenshotPath = screenshotPath;
895
- Object.assign(e, { info: info });
896
- error = e;
897
- throw e;
950
+ await _commandError(state, e, this);
898
951
  }
899
952
  finally {
900
- const endTime = Date.now();
901
- this._reportToWorld(world, {
902
- element_name: selectors.element_name,
903
- type: Types.CLICK,
904
- text: `Click element`,
905
- screenshotId,
906
- result: error
907
- ? {
908
- status: "FAILED",
909
- startTime,
910
- endTime,
911
- message: error === null || error === void 0 ? void 0 : error.message,
912
- }
913
- : {
914
- status: "PASSED",
915
- startTime,
916
- endTime,
917
- },
918
- info: info,
919
- });
953
+ _commandFinally(state, this);
920
954
  }
921
955
  }
922
956
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
923
- this._validateSelectors(selectors);
924
- const startTime = Date.now();
925
- const info = {};
926
- info.log = "";
927
- info.operation = "setCheck";
928
- info.checked = checked;
929
- info.selectors = selectors;
930
- let error = null;
931
- let screenshotId = null;
932
- 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
+ };
933
967
  try {
934
- let element = await this._locate(selectors, info, _params);
935
- ({ 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));
936
972
  try {
937
- await this._highlightElements(element);
938
- await element.setChecked(checked);
973
+ // await this._highlightElements(element);
974
+ await state.element.setChecked(checked);
939
975
  await new Promise((resolve) => setTimeout(resolve, 1000));
940
976
  }
941
977
  catch (e) {
@@ -944,179 +980,108 @@ class StableBrowser {
944
980
  }
945
981
  else {
946
982
  //await this.closeUnexpectedPopups();
947
- info.log += "setCheck failed, will try again" + "\n";
948
- element = await this._locate(selectors, info, _params);
949
- 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 });
950
986
  await new Promise((resolve) => setTimeout(resolve, 1000));
951
987
  }
952
988
  }
953
989
  await this.waitForPageLoad();
954
- return info;
990
+ return state.info;
955
991
  }
956
992
  catch (e) {
957
- this.logger.error("setCheck failed " + info.log);
958
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
959
- info.screenshotPath = screenshotPath;
960
- Object.assign(e, { info: info });
961
- error = e;
962
- throw e;
993
+ await _commandError(state, e, this);
963
994
  }
964
995
  finally {
965
- const endTime = Date.now();
966
- this._reportToWorld(world, {
967
- element_name: selectors.element_name,
968
- type: checked ? Types.CHECK : Types.UNCHECK,
969
- text: checked ? `Check element` : `Uncheck element`,
970
- screenshotId,
971
- result: error
972
- ? {
973
- status: "FAILED",
974
- startTime,
975
- endTime,
976
- message: error === null || error === void 0 ? void 0 : error.message,
977
- }
978
- : {
979
- status: "PASSED",
980
- startTime,
981
- endTime,
982
- },
983
- info: info,
984
- });
996
+ _commandFinally(state, this);
985
997
  }
986
998
  }
987
999
  async hover(selectors, _params, options = {}, world = null) {
988
- this._validateSelectors(selectors);
989
- const startTime = Date.now();
990
- const info = {};
991
- info.log = "";
992
- info.operation = "hover";
993
- info.selectors = selectors;
994
- let error = null;
995
- let screenshotId = null;
996
- 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
+ };
997
1010
  try {
998
- let element = await this._locate(selectors, info, _params);
999
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1011
+ await _preCommand(state, this);
1000
1012
  try {
1001
- await this._highlightElements(element);
1002
- await element.hover();
1013
+ await state.element.hover();
1003
1014
  await new Promise((resolve) => setTimeout(resolve, 1000));
1004
1015
  }
1005
1016
  catch (e) {
1006
1017
  //await this.closeUnexpectedPopups();
1007
- info.log += "hover failed, will try again" + "\n";
1008
- element = await this._locate(selectors, info, _params);
1009
- 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 });
1010
1021
  await new Promise((resolve) => setTimeout(resolve, 1000));
1011
1022
  }
1012
1023
  await this.waitForPageLoad();
1013
- return info;
1024
+ return state.info;
1014
1025
  }
1015
1026
  catch (e) {
1016
- this.logger.error("hover failed " + info.log);
1017
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1018
- info.screenshotPath = screenshotPath;
1019
- Object.assign(e, { info: info });
1020
- error = e;
1021
- throw e;
1027
+ await _commandError(state, e, this);
1022
1028
  }
1023
1029
  finally {
1024
- const endTime = Date.now();
1025
- this._reportToWorld(world, {
1026
- element_name: selectors.element_name,
1027
- type: Types.HOVER,
1028
- text: `Hover element`,
1029
- screenshotId,
1030
- result: error
1031
- ? {
1032
- status: "FAILED",
1033
- startTime,
1034
- endTime,
1035
- message: error === null || error === void 0 ? void 0 : error.message,
1036
- }
1037
- : {
1038
- status: "PASSED",
1039
- startTime,
1040
- endTime,
1041
- },
1042
- info: info,
1043
- });
1030
+ _commandFinally(state, this);
1044
1031
  }
1045
1032
  }
1046
1033
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
1047
- this._validateSelectors(selectors);
1048
1034
  if (!values) {
1049
1035
  throw new Error("values is null");
1050
1036
  }
1051
- const startTime = Date.now();
1052
- let error = null;
1053
- let screenshotId = null;
1054
- let screenshotPath = null;
1055
- const info = {};
1056
- info.log = "";
1057
- info.operation = "selectOptions";
1058
- 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
+ };
1059
1048
  try {
1060
- let element = await this._locate(selectors, info, _params);
1061
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1049
+ await _preCommand(state, this);
1062
1050
  try {
1063
- await this._highlightElements(element);
1064
- await element.selectOption(values);
1051
+ await state.element.selectOption(values);
1065
1052
  }
1066
1053
  catch (e) {
1067
1054
  //await this.closeUnexpectedPopups();
1068
- info.log += "selectOption failed, will try force" + "\n";
1069
- 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 });
1070
1057
  }
1071
1058
  await this.waitForPageLoad();
1072
- return info;
1059
+ return state.info;
1073
1060
  }
1074
1061
  catch (e) {
1075
- this.logger.error("selectOption failed " + info.log);
1076
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1077
- info.screenshotPath = screenshotPath;
1078
- Object.assign(e, { info: info });
1079
- this.logger.info("click failed, will try next selector");
1080
- error = e;
1081
- throw e;
1062
+ await _commandError(state, e, this);
1082
1063
  }
1083
1064
  finally {
1084
- const endTime = Date.now();
1085
- this._reportToWorld(world, {
1086
- element_name: selectors.element_name,
1087
- type: Types.SELECT,
1088
- text: `Select option: ${values}`,
1089
- value: values.toString(),
1090
- screenshotId,
1091
- result: error
1092
- ? {
1093
- status: "FAILED",
1094
- startTime,
1095
- endTime,
1096
- message: error === null || error === void 0 ? void 0 : error.message,
1097
- }
1098
- : {
1099
- status: "PASSED",
1100
- startTime,
1101
- endTime,
1102
- },
1103
- info: info,
1104
- });
1065
+ _commandFinally(state, this);
1105
1066
  }
1106
1067
  }
1107
1068
  async type(_value, _params = null, options = {}, world = null) {
1108
- const startTime = Date.now();
1109
- let error = null;
1110
- let screenshotId = null;
1111
- let screenshotPath = null;
1112
- const info = {};
1113
- info.log = "";
1114
- info.operation = "type";
1115
- _value = this._fixUsingParams(_value, _params);
1116
- 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
+ };
1117
1082
  try {
1118
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1119
- const valueSegment = _value.split("&&");
1083
+ await _preCommand(state, this);
1084
+ const valueSegment = state.value.split("&&");
1120
1085
  for (let i = 0; i < valueSegment.length; i++) {
1121
1086
  if (i > 0) {
1122
1087
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1136,134 +1101,76 @@ class StableBrowser {
1136
1101
  await this.page.keyboard.type(value);
1137
1102
  }
1138
1103
  }
1139
- return info;
1104
+ return state.info;
1140
1105
  }
1141
1106
  catch (e) {
1142
- //await this.closeUnexpectedPopups();
1143
- this.logger.error("type failed " + info.log);
1144
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1145
- info.screenshotPath = screenshotPath;
1146
- Object.assign(e, { info: info });
1147
- error = e;
1148
- throw e;
1107
+ await _commandError(state, e, this);
1149
1108
  }
1150
1109
  finally {
1151
- const endTime = Date.now();
1152
- this._reportToWorld(world, {
1153
- type: Types.TYPE_PRESS,
1154
- screenshotId,
1155
- value: _value,
1156
- text: `type value: ${_value}`,
1157
- result: error
1158
- ? {
1159
- status: "FAILED",
1160
- startTime,
1161
- endTime,
1162
- message: error === null || error === void 0 ? void 0 : error.message,
1163
- }
1164
- : {
1165
- status: "PASSED",
1166
- startTime,
1167
- endTime,
1168
- },
1169
- info: info,
1170
- });
1110
+ _commandFinally(state, this);
1171
1111
  }
1172
1112
  }
1173
1113
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1174
- // set input value for non fillable inputs like date, time, range, color, etc.
1175
- this._validateSelectors(selectors);
1176
- const startTime = Date.now();
1177
- const info = {};
1178
- info.log = "***** set input value " + selectors.element_name + " *****\n";
1179
- info.operation = "setInputValue";
1180
- info.selectors = selectors;
1181
- value = this._fixUsingParams(value, _params);
1182
- info.value = value;
1183
- let error = null;
1184
- let screenshotId = null;
1185
- 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
+ };
1186
1125
  try {
1187
- value = await this._replaceWithLocalData(value, this);
1188
- let element = await this._locate(selectors, info, _params);
1189
- await this.scrollIfNeeded(element, info);
1190
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1191
- await this._highlightElements(element);
1126
+ await _preCommand(state, this);
1127
+ let value = await this._replaceWithLocalData(state.value, this);
1192
1128
  try {
1193
- await element.evaluateHandle((el, value) => {
1129
+ await state.element.evaluateHandle((el, value) => {
1194
1130
  el.value = value;
1195
1131
  }, value);
1196
1132
  }
1197
1133
  catch (error) {
1198
1134
  this.logger.error("setInputValue failed, will try again");
1199
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1200
- info.screenshotPath = screenshotPath;
1201
- Object.assign(error, { info: info });
1202
- await element.evaluateHandle((el, value) => {
1135
+ await _screenshot(state, this);
1136
+ Object.assign(error, { info: state.info });
1137
+ await state.element.evaluateHandle((el, value) => {
1203
1138
  el.value = value;
1204
1139
  });
1205
1140
  }
1206
1141
  }
1207
1142
  catch (e) {
1208
- this.logger.error("setInputValue failed " + info.log);
1209
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1210
- info.screenshotPath = screenshotPath;
1211
- Object.assign(e, { info: info });
1212
- error = e;
1213
- throw e;
1143
+ await _commandError(state, e, this);
1214
1144
  }
1215
1145
  finally {
1216
- const endTime = Date.now();
1217
- this._reportToWorld(world, {
1218
- element_name: selectors.element_name,
1219
- type: Types.SET_INPUT,
1220
- text: `Set input value`,
1221
- value: value,
1222
- screenshotId,
1223
- result: error
1224
- ? {
1225
- status: "FAILED",
1226
- startTime,
1227
- endTime,
1228
- message: error === null || error === void 0 ? void 0 : error.message,
1229
- }
1230
- : {
1231
- status: "PASSED",
1232
- startTime,
1233
- endTime,
1234
- },
1235
- info: info,
1236
- });
1146
+ _commandFinally(state, this);
1237
1147
  }
1238
1148
  }
1239
1149
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1240
- this._validateSelectors(selectors);
1241
- const startTime = Date.now();
1242
- let error = null;
1243
- let screenshotId = null;
1244
- let screenshotPath = null;
1245
- const info = {};
1246
- info.log = "";
1247
- info.operation = Types.SET_DATE_TIME;
1248
- info.selectors = selectors;
1249
- 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
+ };
1250
1162
  try {
1251
- value = await this._replaceWithLocalData(value, this);
1252
- let element = await this._locate(selectors, info, _params);
1253
- //insert red border around the element
1254
- await this.scrollIfNeeded(element, info);
1255
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1256
- await this._highlightElements(element);
1163
+ await _preCommand(state, this);
1257
1164
  try {
1258
- await element.click();
1165
+ await state.element.click();
1259
1166
  await new Promise((resolve) => setTimeout(resolve, 500));
1260
1167
  if (format) {
1261
- value = dayjs(value).format(format);
1262
- await element.fill(value);
1168
+ state.value = dayjs(state.value).format(format);
1169
+ await state.element.fill(state.value);
1263
1170
  }
1264
1171
  else {
1265
- const dateTimeValue = await getDateTimeValue({ value, element });
1266
- await element.evaluateHandle((el, dateTimeValue) => {
1172
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1173
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1267
1174
  el.value = ""; // clear input
1268
1175
  el.value = dateTimeValue;
1269
1176
  }, dateTimeValue);
@@ -1276,20 +1183,19 @@ class StableBrowser {
1276
1183
  }
1277
1184
  catch (err) {
1278
1185
  //await this.closeUnexpectedPopups();
1279
- this.logger.error("setting date time input failed " + JSON.stringify(info));
1186
+ this.logger.error("setting date time input failed " + JSON.stringify(state.info));
1280
1187
  this.logger.info("Trying again");
1281
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1282
- info.screenshotPath = screenshotPath;
1283
- Object.assign(err, { info: info });
1188
+ await _screenshot(state, this);
1189
+ Object.assign(err, { info: state.info });
1284
1190
  await element.click();
1285
1191
  await new Promise((resolve) => setTimeout(resolve, 500));
1286
1192
  if (format) {
1287
- value = dayjs(value).format(format);
1288
- await element.fill(value);
1193
+ state.value = dayjs(state.value).format(format);
1194
+ await state.element.fill(state.value);
1289
1195
  }
1290
1196
  else {
1291
- const dateTimeValue = await getDateTimeValue({ value, element });
1292
- await element.evaluateHandle((el, dateTimeValue) => {
1197
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1198
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1293
1199
  el.value = ""; // clear input
1294
1200
  el.value = dateTimeValue;
1295
1201
  }, dateTimeValue);
@@ -1302,60 +1208,39 @@ class StableBrowser {
1302
1208
  }
1303
1209
  }
1304
1210
  catch (e) {
1305
- error = e;
1306
- throw e;
1211
+ await _commandError(state, e, this);
1307
1212
  }
1308
1213
  finally {
1309
- const endTime = Date.now();
1310
- this._reportToWorld(world, {
1311
- element_name: selectors.element_name,
1312
- type: Types.SET_DATE_TIME,
1313
- screenshotId,
1314
- value: value,
1315
- text: `setDateTime input with value: ${value}`,
1316
- result: error
1317
- ? {
1318
- status: "FAILED",
1319
- startTime,
1320
- endTime,
1321
- message: error === null || error === void 0 ? void 0 : error.message,
1322
- }
1323
- : {
1324
- status: "PASSED",
1325
- startTime,
1326
- endTime,
1327
- },
1328
- info: info,
1329
- });
1214
+ _commandFinally(state, this);
1330
1215
  }
1331
1216
  }
1332
1217
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1333
1218
  _value = unEscapeString(_value);
1334
- this._validateSelectors(selectors);
1335
- const startTime = Date.now();
1336
- let error = null;
1337
- let screenshotId = null;
1338
- let screenshotPath = null;
1339
- const info = {};
1340
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1341
- info.operation = "clickType";
1342
- info.selectors = selectors;
1343
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
+ };
1344
1232
  if (newValue !== _value) {
1345
1233
  //this.logger.info(_value + "=" + newValue);
1346
1234
  _value = newValue;
1347
1235
  }
1348
- info.value = _value;
1349
1236
  try {
1350
- let element = await this._locate(selectors, info, _params);
1351
- //insert red border around the element
1352
- await this.scrollIfNeeded(element, info);
1353
- await this._highlightElements(element);
1237
+ await _preCommand(state, this);
1238
+ state.info.value = _value;
1354
1239
  if (options === null || options === undefined || !options.press) {
1355
1240
  try {
1356
- let currentValue = await element.inputValue();
1241
+ let currentValue = await state.element.inputValue();
1357
1242
  if (currentValue) {
1358
- await element.fill("");
1243
+ await state.element.fill("");
1359
1244
  }
1360
1245
  }
1361
1246
  catch (e) {
@@ -1364,22 +1249,22 @@ class StableBrowser {
1364
1249
  }
1365
1250
  if (options === null || options === undefined || options.press) {
1366
1251
  try {
1367
- await element.click({ timeout: 5000 });
1252
+ await state.element.click({ timeout: 5000 });
1368
1253
  }
1369
1254
  catch (e) {
1370
- await element.dispatchEvent("click");
1255
+ await state.element.dispatchEvent("click");
1371
1256
  }
1372
1257
  }
1373
1258
  else {
1374
1259
  try {
1375
- await element.focus();
1260
+ await state.element.focus();
1376
1261
  }
1377
1262
  catch (e) {
1378
- await element.dispatchEvent("focus");
1263
+ await state.element.dispatchEvent("focus");
1379
1264
  }
1380
1265
  }
1381
1266
  await new Promise((resolve) => setTimeout(resolve, 500));
1382
- const valueSegment = _value.split("&&");
1267
+ const valueSegment = state.value.split("&&");
1383
1268
  for (let i = 0; i < valueSegment.length; i++) {
1384
1269
  if (i > 0) {
1385
1270
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1399,14 +1284,14 @@ class StableBrowser {
1399
1284
  await new Promise((resolve) => setTimeout(resolve, 500));
1400
1285
  }
1401
1286
  }
1402
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1287
+ await _screenshot(state, this);
1403
1288
  if (enter === true) {
1404
1289
  await new Promise((resolve) => setTimeout(resolve, 2000));
1405
1290
  await this.page.keyboard.press("Enter");
1406
1291
  await this.waitForPageLoad();
1407
1292
  }
1408
1293
  else if (enter === false) {
1409
- await element.dispatchEvent("change");
1294
+ await state.element.dispatchEvent("change");
1410
1295
  //await this.page.keyboard.press("Tab");
1411
1296
  }
1412
1297
  else {
@@ -1415,104 +1300,50 @@ class StableBrowser {
1415
1300
  await this.waitForPageLoad();
1416
1301
  }
1417
1302
  }
1418
- return info;
1303
+ return state.info;
1419
1304
  }
1420
1305
  catch (e) {
1421
- //await this.closeUnexpectedPopups();
1422
- this.logger.error("fill failed " + JSON.stringify(info));
1423
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1424
- info.screenshotPath = screenshotPath;
1425
- Object.assign(e, { info: info });
1426
- error = e;
1427
- throw e;
1306
+ await _commandError(state, e, this);
1428
1307
  }
1429
1308
  finally {
1430
- const endTime = Date.now();
1431
- this._reportToWorld(world, {
1432
- element_name: selectors.element_name,
1433
- type: Types.FILL,
1434
- screenshotId,
1435
- value: _value,
1436
- text: `clickType input with value: ${_value}`,
1437
- result: error
1438
- ? {
1439
- status: "FAILED",
1440
- startTime,
1441
- endTime,
1442
- message: error === null || error === void 0 ? void 0 : error.message,
1443
- }
1444
- : {
1445
- status: "PASSED",
1446
- startTime,
1447
- endTime,
1448
- },
1449
- info: info,
1450
- });
1309
+ _commandFinally(state, this);
1451
1310
  }
1452
1311
  }
1453
1312
  async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1454
- this._validateSelectors(selectors);
1455
- value = unEscapeString(value);
1456
- const startTime = Date.now();
1457
- let error = null;
1458
- let screenshotId = null;
1459
- let screenshotPath = null;
1460
- const info = {};
1461
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1462
- info.operation = "fill";
1463
- info.selectors = selectors;
1464
- 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
+ };
1465
1324
  try {
1466
- let element = await this._locate(selectors, info, _params);
1467
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1468
- await this._highlightElements(element);
1469
- await element.fill(value);
1470
- await element.dispatchEvent("change");
1325
+ await _preCommand(state, this);
1326
+ await state.element.fill(value);
1327
+ await state.element.dispatchEvent("change");
1471
1328
  if (enter) {
1472
1329
  await new Promise((resolve) => setTimeout(resolve, 2000));
1473
1330
  await this.page.keyboard.press("Enter");
1474
1331
  }
1475
1332
  await this.waitForPageLoad();
1476
- return info;
1333
+ return state.info;
1477
1334
  }
1478
1335
  catch (e) {
1479
- //await this.closeUnexpectedPopups();
1480
- this.logger.error("fill failed " + info.log);
1481
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1482
- info.screenshotPath = screenshotPath;
1483
- Object.assign(e, { info: info });
1484
- error = e;
1485
- throw e;
1336
+ await _commandError(state, e, this);
1486
1337
  }
1487
1338
  finally {
1488
- const endTime = Date.now();
1489
- this._reportToWorld(world, {
1490
- element_name: selectors.element_name,
1491
- type: Types.FILL,
1492
- screenshotId,
1493
- value,
1494
- text: `Fill input with value: ${value}`,
1495
- result: error
1496
- ? {
1497
- status: "FAILED",
1498
- startTime,
1499
- endTime,
1500
- message: error === null || error === void 0 ? void 0 : error.message,
1501
- }
1502
- : {
1503
- status: "PASSED",
1504
- startTime,
1505
- endTime,
1506
- },
1507
- info: info,
1508
- });
1339
+ _commandFinally(state, this);
1509
1340
  }
1510
1341
  }
1511
1342
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1512
1343
  return await this._getText(selectors, 0, _params, options, info, world);
1513
1344
  }
1514
1345
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1515
- this._validateSelectors(selectors);
1346
+ _validateSelectors(selectors);
1516
1347
  let screenshotId = null;
1517
1348
  let screenshotPath = null;
1518
1349
  if (!info.log) {
@@ -1556,166 +1387,124 @@ class StableBrowser {
1556
1387
  }
1557
1388
  }
1558
1389
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1559
- var _a;
1560
- this._validateSelectors(selectors);
1561
1390
  if (!pattern) {
1562
1391
  throw new Error("pattern is null");
1563
1392
  }
1564
1393
  if (!text) {
1565
1394
  throw new Error("text is null");
1566
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
+ };
1567
1412
  const newValue = await this._replaceWithLocalData(text, world);
1568
1413
  if (newValue !== text) {
1569
1414
  this.logger.info(text + "=" + newValue);
1570
1415
  text = newValue;
1571
1416
  }
1572
- const startTime = Date.now();
1573
- let error = null;
1574
- let screenshotId = null;
1575
- let screenshotPath = null;
1576
- const info = {};
1577
- info.log =
1578
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1579
- info.operation = "containsPattern";
1580
- info.selectors = selectors;
1581
- info.value = text;
1582
- info.pattern = pattern;
1583
1417
  let foundObj = null;
1584
1418
  try {
1585
- 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);
1586
1422
  if (foundObj && foundObj.element) {
1587
- await this.scrollIfNeeded(foundObj.element, info);
1423
+ await this.scrollIfNeeded(foundObj.element, state.info);
1588
1424
  }
1589
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1425
+ await _screenshot(state, this);
1590
1426
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1591
1427
  pattern = pattern.replace("{text}", escapedText);
1592
1428
  let regex = new RegExp(pattern, "im");
1593
- 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))) {
1594
- 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;
1595
1431
  throw new Error("element doesn't contain text " + text);
1596
1432
  }
1597
- return info;
1433
+ return state.info;
1598
1434
  }
1599
1435
  catch (e) {
1600
- //await this.closeUnexpectedPopups();
1601
- this.logger.error("verify element contains text failed " + info.log);
1602
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1603
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1604
- info.screenshotPath = screenshotPath;
1605
- Object.assign(e, { info: info });
1606
- error = e;
1607
- throw e;
1436
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1437
+ await _commandError(state, e, this);
1608
1438
  }
1609
1439
  finally {
1610
- const endTime = Date.now();
1611
- this._reportToWorld(world, {
1612
- element_name: selectors.element_name,
1613
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1614
- value: pattern,
1615
- text: `Verify element contains pattern: ${pattern}`,
1616
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1617
- result: error
1618
- ? {
1619
- status: "FAILED",
1620
- startTime,
1621
- endTime,
1622
- message: error === null || error === void 0 ? void 0 : error.message,
1623
- }
1624
- : {
1625
- status: "PASSED",
1626
- startTime,
1627
- endTime,
1628
- },
1629
- info: info,
1630
- });
1440
+ _commandFinally(state, this);
1631
1441
  }
1632
1442
  }
1633
1443
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1634
- var _a, _b, _c;
1635
- this._validateSelectors(selectors);
1636
- 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
+ };
1637
1459
  if (!text) {
1638
1460
  throw new Error("text is null");
1639
1461
  }
1640
- const startTime = Date.now();
1641
- let error = null;
1642
- let screenshotId = null;
1643
- let screenshotPath = null;
1644
- const info = {};
1645
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1646
- info.operation = "containsText";
1647
- info.selectors = selectors;
1462
+ text = unEscapeString(text);
1648
1463
  const newValue = await this._replaceWithLocalData(text, world);
1649
1464
  if (newValue !== text) {
1650
1465
  this.logger.info(text + "=" + newValue);
1651
1466
  text = newValue;
1652
1467
  }
1653
- info.value = text;
1654
1468
  let foundObj = null;
1655
1469
  try {
1656
- 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);
1657
1472
  if (foundObj && foundObj.element) {
1658
- await this.scrollIfNeeded(foundObj.element, info);
1473
+ await this.scrollIfNeeded(foundObj.element, state.info);
1659
1474
  }
1660
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1475
+ await _screenshot(state, this);
1661
1476
  const dateAlternatives = findDateAlternatives(text);
1662
1477
  const numberAlternatives = findNumberAlternatives(text);
1663
1478
  if (dateAlternatives.date) {
1664
1479
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1665
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1666
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1667
- return info;
1480
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1481
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1482
+ return state.info;
1668
1483
  }
1669
1484
  }
1670
1485
  throw new Error("element doesn't contain text " + text);
1671
1486
  }
1672
1487
  else if (numberAlternatives.number) {
1673
1488
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1674
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1675
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1676
- return info;
1489
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1490
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1491
+ return state.info;
1677
1492
  }
1678
1493
  }
1679
1494
  throw new Error("element doesn't contain text " + text);
1680
1495
  }
1681
- 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))) {
1682
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1683
- 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;
1684
1499
  throw new Error("element doesn't contain text " + text);
1685
1500
  }
1686
- return info;
1501
+ return state.info;
1687
1502
  }
1688
1503
  catch (e) {
1689
- //await this.closeUnexpectedPopups();
1690
- this.logger.error("verify element contains text failed " + info.log);
1691
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1692
- info.screenshotPath = screenshotPath;
1693
- Object.assign(e, { info: info });
1694
- error = e;
1695
- throw e;
1504
+ await _commandError(state, e, this);
1696
1505
  }
1697
1506
  finally {
1698
- const endTime = Date.now();
1699
- this._reportToWorld(world, {
1700
- element_name: selectors.element_name,
1701
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1702
- text: `Verify element contains text: ${text}`,
1703
- value: text,
1704
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1705
- result: error
1706
- ? {
1707
- status: "FAILED",
1708
- startTime,
1709
- endTime,
1710
- message: error === null || error === void 0 ? void 0 : error.message,
1711
- }
1712
- : {
1713
- status: "PASSED",
1714
- startTime,
1715
- endTime,
1716
- },
1717
- info: info,
1718
- });
1507
+ _commandFinally(state, this);
1719
1508
  }
1720
1509
  }
1721
1510
  _getDataFile(world = null) {
@@ -1897,11 +1686,9 @@ class StableBrowser {
1897
1686
  if (!fs.existsSync(world.screenshotPath)) {
1898
1687
  fs.mkdirSync(world.screenshotPath, { recursive: true });
1899
1688
  }
1900
- let nextIndex = 1;
1901
- while (fs.existsSync(path.join(world.screenshotPath, nextIndex + ".png"))) {
1902
- nextIndex++;
1903
- }
1904
- 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");
1905
1692
  try {
1906
1693
  await this.takeScreenshot(screenshotPath);
1907
1694
  // let buffer = await this.page.screenshot({ timeout: 4000 });
@@ -1915,7 +1702,7 @@ class StableBrowser {
1915
1702
  catch (e) {
1916
1703
  this.logger.info("unable to take screenshot, ignored");
1917
1704
  }
1918
- result.screenshotId = nextIndex;
1705
+ result.screenshotId = uuidStr;
1919
1706
  result.screenshotPath = screenshotPath;
1920
1707
  if (info && info.box) {
1921
1708
  await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
@@ -1989,127 +1776,69 @@ class StableBrowser {
1989
1776
  }
1990
1777
  }
1991
1778
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1992
- this._validateSelectors(selectors);
1993
- const startTime = Date.now();
1994
- let error = null;
1995
- let screenshotId = null;
1996
- 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
+ };
1997
1789
  await new Promise((resolve) => setTimeout(resolve, 2000));
1998
- const info = {};
1999
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
2000
- info.operation = "verify";
2001
- info.selectors = selectors;
2002
1790
  try {
2003
- const element = await this._locate(selectors, info, _params);
2004
- if (element) {
2005
- await this.scrollIfNeeded(element, info);
2006
- }
2007
- await this._highlightElements(element);
2008
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2009
- await expect(element).toHaveCount(1, { timeout: 10000 });
2010
- return info;
1791
+ await _preCommand(state, this);
1792
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1793
+ return state.info;
2011
1794
  }
2012
1795
  catch (e) {
2013
- //await this.closeUnexpectedPopups();
2014
- this.logger.error("verify failed " + info.log);
2015
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2016
- info.screenshotPath = screenshotPath;
2017
- Object.assign(e, { info: info });
2018
- error = e;
2019
- throw e;
1796
+ await _commandError(state, e, this);
2020
1797
  }
2021
1798
  finally {
2022
- const endTime = Date.now();
2023
- this._reportToWorld(world, {
2024
- element_name: selectors.element_name,
2025
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2026
- text: "Verify element exists in page",
2027
- screenshotId,
2028
- result: error
2029
- ? {
2030
- status: "FAILED",
2031
- startTime,
2032
- endTime,
2033
- message: error === null || error === void 0 ? void 0 : error.message,
2034
- }
2035
- : {
2036
- status: "PASSED",
2037
- startTime,
2038
- endTime,
2039
- },
2040
- info: info,
2041
- });
1799
+ _commandFinally(state, this);
2042
1800
  }
2043
1801
  }
2044
1802
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
2045
- this._validateSelectors(selectors);
2046
- const startTime = Date.now();
2047
- let error = null;
2048
- let screenshotId = null;
2049
- 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
+ };
2050
1815
  await new Promise((resolve) => setTimeout(resolve, 2000));
2051
- const info = {};
2052
- info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
2053
- info.operation = "extract";
2054
- info.selectors = selectors;
2055
1816
  try {
2056
- const element = await this._locate(selectors, info, _params);
2057
- await this._highlightElements(element);
2058
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1817
+ await _preCommand(state, this);
2059
1818
  switch (attribute) {
2060
1819
  case "inner_text":
2061
- info.value = await element.innerText();
1820
+ state.value = await state.element.innerText();
2062
1821
  break;
2063
1822
  case "href":
2064
- info.value = await element.getAttribute("href");
1823
+ state.value = await state.element.getAttribute("href");
2065
1824
  break;
2066
1825
  case "value":
2067
- info.value = await element.inputValue();
1826
+ state.value = await state.element.inputValue();
2068
1827
  break;
2069
1828
  default:
2070
- info.value = await element.getAttribute(attribute);
1829
+ state.value = await state.element.getAttribute(attribute);
2071
1830
  break;
2072
1831
  }
2073
- this[variable] = info.value;
2074
- if (world) {
2075
- world[variable] = info.value;
2076
- }
2077
- this.setTestData({ [variable]: info.value }, world);
2078
- this.logger.info("set test data: " + variable + "=" + info.value);
2079
- 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;
2080
1836
  }
2081
1837
  catch (e) {
2082
- //await this.closeUnexpectedPopups();
2083
- this.logger.error("extract failed " + info.log);
2084
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2085
- info.screenshotPath = screenshotPath;
2086
- Object.assign(e, { info: info });
2087
- error = e;
2088
- throw e;
1838
+ await _commandError(state, e, this);
2089
1839
  }
2090
1840
  finally {
2091
- const endTime = Date.now();
2092
- this._reportToWorld(world, {
2093
- element_name: selectors.element_name,
2094
- type: Types.EXTRACT_ATTRIBUTE,
2095
- variable: variable,
2096
- value: info.value,
2097
- text: "Extract attribute from element",
2098
- screenshotId,
2099
- result: error
2100
- ? {
2101
- status: "FAILED",
2102
- startTime,
2103
- endTime,
2104
- message: error === null || error === void 0 ? void 0 : error.message,
2105
- }
2106
- : {
2107
- status: "PASSED",
2108
- startTime,
2109
- endTime,
2110
- },
2111
- info: info,
2112
- });
1841
+ _commandFinally(state, this);
2113
1842
  }
2114
1843
  }
2115
1844
  async extractEmailData(emailAddress, options, world) {
@@ -2186,7 +1915,8 @@ class StableBrowser {
2186
1915
  catch (e) {
2187
1916
  errorCount++;
2188
1917
  if (errorCount > 3) {
2189
- throw e;
1918
+ // throw e;
1919
+ await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
2190
1920
  }
2191
1921
  // ignore
2192
1922
  }
@@ -2297,11 +2027,12 @@ class StableBrowser {
2297
2027
  info.screenshotPath = screenshotPath;
2298
2028
  Object.assign(e, { info: info });
2299
2029
  error = e;
2300
- throw e;
2030
+ // throw e;
2031
+ await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
2301
2032
  }
2302
2033
  finally {
2303
2034
  const endTime = Date.now();
2304
- this._reportToWorld(world, {
2035
+ _reportToWorld(world, {
2305
2036
  type: Types.VERIFY_PAGE_PATH,
2306
2037
  text: "Verify page path",
2307
2038
  screenshotId,
@@ -2310,7 +2041,7 @@ class StableBrowser {
2310
2041
  status: "FAILED",
2311
2042
  startTime,
2312
2043
  endTime,
2313
- message: error === null || error === void 0 ? void 0 : error.message,
2044
+ message: error?.message,
2314
2045
  }
2315
2046
  : {
2316
2047
  status: "PASSED",
@@ -2323,52 +2054,58 @@ class StableBrowser {
2323
2054
  }
2324
2055
  async verifyTextExistInPage(text, options = {}, world = null) {
2325
2056
  text = unEscapeString(text);
2326
- 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
+ };
2327
2069
  const timeout = this._getLoadTimeout(options);
2328
- let error = null;
2329
- let screenshotId = null;
2330
- let screenshotPath = null;
2331
2070
  await new Promise((resolve) => setTimeout(resolve, 2000));
2332
- const info = {};
2333
- info.log = "***** verify text " + text + " exists in page *****\n";
2334
- info.operation = "verifyTextExistInPage";
2335
2071
  const newValue = await this._replaceWithLocalData(text, world);
2336
2072
  if (newValue !== text) {
2337
2073
  this.logger.info(text + "=" + newValue);
2338
2074
  text = newValue;
2339
2075
  }
2340
- info.text = text;
2341
2076
  let dateAlternatives = findDateAlternatives(text);
2342
2077
  let numberAlternatives = findNumberAlternatives(text);
2343
2078
  try {
2079
+ await _preCommand(state, this);
2080
+ state.info.text = text;
2344
2081
  while (true) {
2345
2082
  const frames = this.page.frames();
2346
2083
  let results = [];
2347
2084
  for (let i = 0; i < frames.length; i++) {
2348
2085
  if (dateAlternatives.date) {
2349
2086
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2350
- 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, {});
2351
2088
  result.frame = frames[i];
2352
2089
  results.push(result);
2353
2090
  }
2354
2091
  }
2355
2092
  else if (numberAlternatives.number) {
2356
2093
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2357
- 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, {});
2358
2095
  result.frame = frames[i];
2359
2096
  results.push(result);
2360
2097
  }
2361
2098
  }
2362
2099
  else {
2363
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2100
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2364
2101
  result.frame = frames[i];
2365
2102
  results.push(result);
2366
2103
  }
2367
2104
  }
2368
- info.results = results;
2105
+ state.info.results = results;
2369
2106
  const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2370
2107
  if (resultWithElementsFound.length === 0) {
2371
- if (Date.now() - startTime > timeout) {
2108
+ if (Date.now() - state.startTime > timeout) {
2372
2109
  throw new Error(`Text ${text} not found in page`);
2373
2110
  }
2374
2111
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -2380,44 +2117,89 @@ class StableBrowser {
2380
2117
  await this._highlightElements(frame, dataAttribute);
2381
2118
  const element = await frame.$(dataAttribute);
2382
2119
  if (element) {
2383
- await this.scrollIfNeeded(element, info);
2120
+ await this.scrollIfNeeded(element, state.info);
2384
2121
  await element.dispatchEvent("bvt_verify_page_contains_text");
2385
2122
  }
2386
2123
  }
2387
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2388
- return info;
2124
+ await _screenshot(state, this);
2125
+ return state.info;
2389
2126
  }
2390
2127
  // await expect(element).toHaveCount(1, { timeout: 10000 });
2391
2128
  }
2392
2129
  catch (e) {
2393
- //await this.closeUnexpectedPopups();
2394
- this.logger.error("verify text exist in page failed " + info.log);
2395
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2396
- info.screenshotPath = screenshotPath;
2397
- Object.assign(e, { info: info });
2398
- error = e;
2399
- throw e;
2130
+ await _commandError(state, e, this);
2400
2131
  }
2401
2132
  finally {
2402
- const endTime = Date.now();
2403
- this._reportToWorld(world, {
2404
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2405
- text: "Verify text exists in page",
2406
- screenshotId,
2407
- result: error
2408
- ? {
2409
- status: "FAILED",
2410
- startTime,
2411
- endTime,
2412
- 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
+ }
2413
2172
  }
2414
- : {
2415
- status: "PASSED",
2416
- startTime,
2417
- endTime,
2418
- },
2419
- info: info,
2420
- });
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);
2421
2203
  }
2422
2204
  }
2423
2205
  _getServerUrl() {
@@ -2480,11 +2262,12 @@ class StableBrowser {
2480
2262
  info.screenshotPath = screenshotPath;
2481
2263
  Object.assign(e, { info: info });
2482
2264
  error = e;
2483
- throw e;
2265
+ // throw e;
2266
+ await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
2484
2267
  }
2485
2268
  finally {
2486
2269
  const endTime = Date.now();
2487
- this._reportToWorld(world, {
2270
+ _reportToWorld(world, {
2488
2271
  type: Types.VERIFY_VISUAL,
2489
2272
  text: "Visual verification",
2490
2273
  screenshotId,
@@ -2493,7 +2276,7 @@ class StableBrowser {
2493
2276
  status: "FAILED",
2494
2277
  startTime,
2495
2278
  endTime,
2496
- message: error === null || error === void 0 ? void 0 : error.message,
2279
+ message: error?.message,
2497
2280
  }
2498
2281
  : {
2499
2282
  status: "PASSED",
@@ -2525,7 +2308,7 @@ class StableBrowser {
2525
2308
  this.logger.info("Table data verified");
2526
2309
  }
2527
2310
  async getTableData(selectors, _params = null, options = {}, world = null) {
2528
- this._validateSelectors(selectors);
2311
+ _validateSelectors(selectors);
2529
2312
  const startTime = Date.now();
2530
2313
  let error = null;
2531
2314
  let screenshotId = null;
@@ -2547,11 +2330,12 @@ class StableBrowser {
2547
2330
  info.screenshotPath = screenshotPath;
2548
2331
  Object.assign(e, { info: info });
2549
2332
  error = e;
2550
- throw e;
2333
+ // throw e;
2334
+ await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
2551
2335
  }
2552
2336
  finally {
2553
2337
  const endTime = Date.now();
2554
- this._reportToWorld(world, {
2338
+ _reportToWorld(world, {
2555
2339
  element_name: selectors.element_name,
2556
2340
  type: Types.GET_TABLE_DATA,
2557
2341
  text: "Get table data",
@@ -2561,7 +2345,7 @@ class StableBrowser {
2561
2345
  status: "FAILED",
2562
2346
  startTime,
2563
2347
  endTime,
2564
- message: error === null || error === void 0 ? void 0 : error.message,
2348
+ message: error?.message,
2565
2349
  }
2566
2350
  : {
2567
2351
  status: "PASSED",
@@ -2573,7 +2357,7 @@ class StableBrowser {
2573
2357
  }
2574
2358
  }
2575
2359
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2576
- this._validateSelectors(selectors);
2360
+ _validateSelectors(selectors);
2577
2361
  if (!query) {
2578
2362
  throw new Error("query is null");
2579
2363
  }
@@ -2712,11 +2496,12 @@ class StableBrowser {
2712
2496
  info.screenshotPath = screenshotPath;
2713
2497
  Object.assign(e, { info: info });
2714
2498
  error = e;
2715
- throw e;
2499
+ // throw e;
2500
+ await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
2716
2501
  }
2717
2502
  finally {
2718
2503
  const endTime = Date.now();
2719
- this._reportToWorld(world, {
2504
+ _reportToWorld(world, {
2720
2505
  element_name: selectors.element_name,
2721
2506
  type: Types.ANALYZE_TABLE,
2722
2507
  text: "Analyze table",
@@ -2726,7 +2511,7 @@ class StableBrowser {
2726
2511
  status: "FAILED",
2727
2512
  startTime,
2728
2513
  endTime,
2729
- message: error === null || error === void 0 ? void 0 : error.message,
2514
+ message: error?.message,
2730
2515
  }
2731
2516
  : {
2732
2517
  status: "PASSED",
@@ -2738,27 +2523,7 @@ class StableBrowser {
2738
2523
  }
2739
2524
  }
2740
2525
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2741
- if (!value) {
2742
- return value;
2743
- }
2744
- // find all the accurance of {{(.*?)}} and replace with the value
2745
- let regex = /{{(.*?)}}/g;
2746
- let matches = value.match(regex);
2747
- if (matches) {
2748
- const testData = this.getTestData(world);
2749
- for (let i = 0; i < matches.length; i++) {
2750
- let match = matches[i];
2751
- let key = match.substring(2, match.length - 2);
2752
- let newValue = objectPath.get(testData, key, null);
2753
- if (newValue !== null) {
2754
- value = value.replace(match, newValue);
2755
- }
2756
- }
2757
- }
2758
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2759
- return await decrypt(value, null, totpWait);
2760
- }
2761
- return value;
2526
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2762
2527
  }
2763
2528
  _getLoadTimeout(options) {
2764
2529
  let timeout = 15000;
@@ -2809,7 +2574,7 @@ class StableBrowser {
2809
2574
  await new Promise((resolve) => setTimeout(resolve, 2000));
2810
2575
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2811
2576
  const endTime = Date.now();
2812
- this._reportToWorld(world, {
2577
+ _reportToWorld(world, {
2813
2578
  type: Types.GET_PAGE_STATUS,
2814
2579
  text: "Wait for page load",
2815
2580
  screenshotId,
@@ -2818,7 +2583,7 @@ class StableBrowser {
2818
2583
  status: "FAILED",
2819
2584
  startTime,
2820
2585
  endTime,
2821
- message: error === null || error === void 0 ? void 0 : error.message,
2586
+ message: error?.message,
2822
2587
  }
2823
2588
  : {
2824
2589
  status: "PASSED",
@@ -2829,41 +2594,35 @@ class StableBrowser {
2829
2594
  }
2830
2595
  }
2831
2596
  async closePage(options = {}, world = null) {
2832
- const startTime = Date.now();
2833
- let error = null;
2834
- let screenshotId = null;
2835
- let screenshotPath = null;
2836
- 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
+ };
2837
2609
  try {
2610
+ await _preCommand(state, this);
2838
2611
  await this.page.close();
2839
2612
  }
2840
2613
  catch (e) {
2841
2614
  console.log(".");
2615
+ await _commandError(state, e, this);
2842
2616
  }
2843
2617
  finally {
2844
- await new Promise((resolve) => setTimeout(resolve, 2000));
2845
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2846
- const endTime = Date.now();
2847
- this._reportToWorld(world, {
2848
- type: Types.CLOSE_PAGE,
2849
- text: "close page",
2850
- screenshotId,
2851
- result: error
2852
- ? {
2853
- status: "FAILED",
2854
- startTime,
2855
- endTime,
2856
- message: error === null || error === void 0 ? void 0 : error.message,
2857
- }
2858
- : {
2859
- status: "PASSED",
2860
- startTime,
2861
- endTime,
2862
- },
2863
- info: info,
2864
- });
2618
+ _commandFinally(state, this);
2865
2619
  }
2866
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
+ }
2867
2626
  async setViewportSize(width, hight, options = {}, world = null) {
2868
2627
  const startTime = Date.now();
2869
2628
  let error = null;
@@ -2881,12 +2640,13 @@ class StableBrowser {
2881
2640
  }
2882
2641
  catch (e) {
2883
2642
  console.log(".");
2643
+ await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
2884
2644
  }
2885
2645
  finally {
2886
2646
  await new Promise((resolve) => setTimeout(resolve, 2000));
2887
2647
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2888
2648
  const endTime = Date.now();
2889
- this._reportToWorld(world, {
2649
+ _reportToWorld(world, {
2890
2650
  type: Types.SET_VIEWPORT,
2891
2651
  text: "set viewport size to " + width + "x" + hight,
2892
2652
  screenshotId,
@@ -2895,7 +2655,7 @@ class StableBrowser {
2895
2655
  status: "FAILED",
2896
2656
  startTime,
2897
2657
  endTime,
2898
- message: error === null || error === void 0 ? void 0 : error.message,
2658
+ message: error?.message,
2899
2659
  }
2900
2660
  : {
2901
2661
  status: "PASSED",
@@ -2917,12 +2677,13 @@ class StableBrowser {
2917
2677
  }
2918
2678
  catch (e) {
2919
2679
  console.log(".");
2680
+ await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
2920
2681
  }
2921
2682
  finally {
2922
2683
  await new Promise((resolve) => setTimeout(resolve, 2000));
2923
2684
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2924
2685
  const endTime = Date.now();
2925
- this._reportToWorld(world, {
2686
+ _reportToWorld(world, {
2926
2687
  type: Types.GET_PAGE_STATUS,
2927
2688
  text: "page relaod",
2928
2689
  screenshotId,
@@ -2931,7 +2692,7 @@ class StableBrowser {
2931
2692
  status: "FAILED",
2932
2693
  startTime,
2933
2694
  endTime,
2934
- message: error === null || error === void 0 ? void 0 : error.message,
2695
+ message: error?.message,
2935
2696
  }
2936
2697
  : {
2937
2698
  status: "PASSED",
@@ -2958,11 +2719,37 @@ class StableBrowser {
2958
2719
  console.log("#-#");
2959
2720
  }
2960
2721
  }
2961
- _reportToWorld(world, properties) {
2962
- if (!world || !world.attach) {
2963
- 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
+ }
2964
2752
  }
2965
- world.attach(JSON.stringify(properties), { mediaType: "application/json" });
2966
2753
  }
2967
2754
  }
2968
2755
  function createTimedPromise(promise, label) {