automation_model 1.0.458-dev → 1.0.458-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.
@@ -10,12 +10,14 @@ 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";
18
+ import { locate_element } from "./locate_element.js";
19
+ import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot } from "./command_common.js";
20
+ import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
19
21
  const Types = {
20
22
  CLICK: "click_element",
21
23
  NAVIGATE: "navigate",
@@ -42,20 +44,26 @@ const Types = {
42
44
  VERIFY_VISUAL: "verify_visual",
43
45
  LOAD_DATA: "load_data",
44
46
  SET_INPUT: "set_input",
47
+ WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
45
48
  };
46
49
  export const apps = {};
47
50
  class StableBrowser {
51
+ browser;
52
+ page;
53
+ logger;
54
+ context;
55
+ world;
56
+ project_path = null;
57
+ webLogFile = null;
58
+ networkLogger = null;
59
+ configuration = null;
60
+ appName = "main";
48
61
  constructor(browser, page, logger = null, context = null, world = null) {
49
62
  this.browser = browser;
50
63
  this.page = page;
51
64
  this.logger = logger;
52
65
  this.context = context;
53
66
  this.world = world;
54
- this.project_path = null;
55
- this.webLogFile = null;
56
- this.networkLogger = null;
57
- this.configuration = null;
58
- this.appName = "main";
59
67
  if (!this.logger) {
60
68
  this.logger = console;
61
69
  }
@@ -80,11 +88,13 @@ class StableBrowser {
80
88
  catch (e) {
81
89
  this.logger.error("unable to read ai_config.json");
82
90
  }
83
- context.pageLoading = { status: false };
84
- context.pages = [this.page];
85
91
  const logFolder = path.join(this.project_path, "logs", "web");
86
92
  this.world = world;
93
+ context.pages = [this.page];
94
+ context.pageLoading = { status: false };
87
95
  this.registerEventListeners(this.context);
96
+ registerNetworkEvents(this.world, this, this.context, this.page);
97
+ registerDownloadEvent(this.page, this.world, this.context);
88
98
  }
89
99
  registerEventListeners(context) {
90
100
  this.registerConsoleLogListener(this.page, context);
@@ -93,10 +103,17 @@ class StableBrowser {
93
103
  context.pageLoading = { status: false };
94
104
  }
95
105
  context.playContext.on("page", async function (page) {
106
+ if (this.configuration && this.configuration.closePopups === true) {
107
+ console.log("close unexpected popups");
108
+ await page.close();
109
+ return;
110
+ }
96
111
  context.pageLoading.status = true;
97
112
  this.page = page;
98
113
  context.page = page;
99
114
  context.pages.push(page);
115
+ registerNetworkEvents(this.world, this, context, this.page);
116
+ registerDownloadEvent(this.page, this.world, context);
100
117
  page.on("close", async () => {
101
118
  if (this.context && this.context.pages && this.context.pages.length > 1) {
102
119
  this.context.pages.pop();
@@ -126,9 +143,9 @@ class StableBrowser {
126
143
  if (this.appName === appName) {
127
144
  return;
128
145
  }
129
- let newContextCreated = false;
146
+ let navigate = false;
130
147
  if (!apps[appName]) {
131
- let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
148
+ let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
132
149
  newContextCreated = true;
133
150
  apps[appName] = {
134
151
  context: newContext,
@@ -141,8 +158,7 @@ class StableBrowser {
141
158
  this._copyContext(apps[appName], this);
142
159
  apps[this.appName] = tempContext;
143
160
  this.appName = appName;
144
- if (newContextCreated) {
145
- this.registerEventListeners(this.context);
161
+ if (navigate) {
146
162
  await this.goto(this.context.environment.baseUrl);
147
163
  await this.waitForPageLoad();
148
164
  }
@@ -168,7 +184,6 @@ class StableBrowser {
168
184
  this.context.webLogger = [];
169
185
  }
170
186
  page.on("console", async (msg) => {
171
- var _a;
172
187
  const obj = {
173
188
  type: msg.type(),
174
189
  text: msg.text(),
@@ -176,7 +191,9 @@ class StableBrowser {
176
191
  time: new Date().toISOString(),
177
192
  };
178
193
  this.context.webLogger.push(obj);
179
- (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
194
+ if (msg.type() === "error") {
195
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
196
+ }
180
197
  });
181
198
  }
182
199
  registerRequestListener(page, context, logFile) {
@@ -184,7 +201,6 @@ class StableBrowser {
184
201
  this.context.networkLogger = [];
185
202
  }
186
203
  page.on("request", async (data) => {
187
- var _a;
188
204
  const startTime = new Date().getTime();
189
205
  try {
190
206
  const pageUrl = new URL(page.url());
@@ -209,10 +225,10 @@ class StableBrowser {
209
225
  startTime,
210
226
  };
211
227
  context.networkLogger.push(obj);
212
- (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
228
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
213
229
  }
214
230
  catch (error) {
215
- console.error("Error in request listener", error);
231
+ // console.error("Error in request listener", error);
216
232
  context.networkLogger.push({
217
233
  error: "not able to listen",
218
234
  message: error.message,
@@ -234,20 +250,6 @@ class StableBrowser {
234
250
  timeout: 60000,
235
251
  });
236
252
  }
237
- _validateSelectors(selectors) {
238
- if (!selectors) {
239
- throw new Error("selectors is null");
240
- }
241
- if (!selectors.locators) {
242
- throw new Error("selectors.locators is null");
243
- }
244
- if (!Array.isArray(selectors.locators)) {
245
- throw new Error("selectors.locators expected to be array");
246
- }
247
- if (selectors.locators.length === 0) {
248
- throw new Error("selectors.locators expected to be non empty array");
249
- }
250
- }
251
253
  _fixUsingParams(text, _params) {
252
254
  if (!_params || typeof text !== "string") {
253
255
  return text;
@@ -315,7 +317,7 @@ class StableBrowser {
315
317
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
316
318
  }
317
319
  }
318
- if (locator === null || locator === void 0 ? void 0 : locator.engine) {
320
+ if (locator?.engine) {
319
321
  if (locator.engine === "css") {
320
322
  locatorReturn = scope.locator(locator.selector);
321
323
  }
@@ -339,7 +341,7 @@ class StableBrowser {
339
341
  if (css && css.locator) {
340
342
  css = css.locator;
341
343
  }
342
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
344
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
343
345
  if (result.elementCount === 0) {
344
346
  return;
345
347
  }
@@ -354,7 +356,7 @@ class StableBrowser {
354
356
  }
355
357
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
356
358
  //const stringifyText = JSON.stringify(text);
357
- return await scope.evaluate(([text, tag, regex, partial]) => {
359
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
358
360
  function isParent(parent, child) {
359
361
  let currentNode = child.parentNode;
360
362
  while (currentNode !== null) {
@@ -366,6 +368,15 @@ class StableBrowser {
366
368
  return false;
367
369
  }
368
370
  document.isParent = isParent;
371
+ function getRegex(str) {
372
+ const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
373
+ if (!match) {
374
+ return null;
375
+ }
376
+ let [_, pattern, flags] = match;
377
+ return new RegExp(pattern, flags);
378
+ }
379
+ document.getRegex = getRegex;
369
380
  function collectAllShadowDomElements(element, result = []) {
370
381
  // Check and add the element if it has a shadow root
371
382
  if (element.shadowRoot) {
@@ -382,7 +393,11 @@ class StableBrowser {
382
393
  }
383
394
  document.collectAllShadowDomElements = collectAllShadowDomElements;
384
395
  if (!tag) {
385
- tag = "*";
396
+ tag = "*:not(script, style, head)";
397
+ }
398
+ let regexpSearch = document.getRegex(text);
399
+ if (regexpSearch) {
400
+ regex = true;
386
401
  }
387
402
  let elements = Array.from(document.querySelectorAll(tag));
388
403
  let shadowHosts = [];
@@ -399,7 +414,9 @@ class StableBrowser {
399
414
  let randomToken = null;
400
415
  const foundElements = [];
401
416
  if (regex) {
402
- let regexpSearch = new RegExp(text, "im");
417
+ if (!regexpSearch) {
418
+ regexpSearch = new RegExp(text, "im");
419
+ }
403
420
  for (let i = 0; i < elements.length; i++) {
404
421
  const element = elements[i];
405
422
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -413,8 +430,8 @@ class StableBrowser {
413
430
  for (let i = 0; i < elements.length; i++) {
414
431
  const element = elements[i];
415
432
  if (partial) {
416
- if ((element.innerText && element.innerText.trim().includes(text)) ||
417
- (element.value && element.value.includes(text))) {
433
+ if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
434
+ (element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
418
435
  foundElements.push(element);
419
436
  }
420
437
  }
@@ -458,19 +475,39 @@ class StableBrowser {
458
475
  }, [text1, tag1, regex1, partial1]);
459
476
  }
460
477
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
478
+ if (!info) {
479
+ info = {};
480
+ }
481
+ if (!info.failCause) {
482
+ info.failCause = {};
483
+ }
484
+ if (!info.log) {
485
+ info.log = "";
486
+ }
461
487
  let locatorSearch = selectorHierarchy[index];
488
+ try {
489
+ locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
490
+ }
491
+ catch (e) {
492
+ console.error(e);
493
+ }
462
494
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
463
495
  let locator = null;
464
496
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
465
497
  let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
466
498
  if (!locatorString) {
499
+ info.failCause.textNotFound = true;
500
+ info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
467
501
  return;
468
502
  }
469
503
  locator = this._getLocator({ css: locatorString }, scope, _params);
470
504
  }
471
505
  else if (locatorSearch.text) {
472
- let result = await this._locateElementByText(scope, this._fixUsingParams(locatorSearch.text, _params), locatorSearch.tag, false, locatorSearch.partial === true, _params);
506
+ let text = this._fixUsingParams(locatorSearch.text, _params);
507
+ let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
473
508
  if (result.elementCount === 0) {
509
+ info.failCause.textNotFound = true;
510
+ info.failCause.lastError = "failed to locate element by text: " + text;
474
511
  return;
475
512
  }
476
513
  locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
@@ -487,6 +524,9 @@ class StableBrowser {
487
524
  // cssHref = true;
488
525
  // }
489
526
  let count = await locator.count();
527
+ if (count > 0 && !info.failCause.count) {
528
+ info.failCause.count = count;
529
+ }
490
530
  //info.log += "total elements found " + count + "\n";
491
531
  //let visibleCount = 0;
492
532
  let visibleLocator = null;
@@ -504,6 +544,8 @@ class StableBrowser {
504
544
  foundLocators.push(locator.nth(j));
505
545
  }
506
546
  else {
547
+ info.failCause.visible = visible;
548
+ info.failCause.enabled = enabled;
507
549
  if (!info.printMessages) {
508
550
  info.printMessages = {};
509
551
  }
@@ -515,6 +557,11 @@ class StableBrowser {
515
557
  }
516
558
  }
517
559
  async closeUnexpectedPopups(info, _params) {
560
+ if (!info) {
561
+ info = {};
562
+ info.failCause = {};
563
+ info.log = "";
564
+ }
518
565
  if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
519
566
  if (!info) {
520
567
  info = {};
@@ -555,7 +602,10 @@ class StableBrowser {
555
602
  }
556
603
  return { rerun: false };
557
604
  }
558
- async _locate(selectors, info, _params, timeout = 30000) {
605
+ async _locate(selectors, info, _params, timeout) {
606
+ if (!timeout) {
607
+ timeout = 30000;
608
+ }
559
609
  for (let i = 0; i < 3; i++) {
560
610
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
561
611
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -569,35 +619,46 @@ class StableBrowser {
569
619
  }
570
620
  throw new Error("unable to locate element " + JSON.stringify(selectors));
571
621
  }
572
- async _locate_internal(selectors, info, _params, timeout = 30000) {
573
- let highPriorityTimeout = 5000;
574
- let visibleOnlyTimeout = 6000;
575
- let startTime = performance.now();
576
- let locatorsCount = 0;
577
- //let arrayMode = Array.isArray(selectors);
622
+ async _findFrameScope(selectors, timeout = 30000, info) {
623
+ if (!info) {
624
+ info = {};
625
+ info.failCause = {};
626
+ info.log = "";
627
+ }
578
628
  let scope = this.page;
629
+ if (selectors.frame) {
630
+ return selectors.frame;
631
+ }
579
632
  if (selectors.iframe_src || selectors.frameLocators) {
580
- const findFrame = (frame, framescope) => {
633
+ const findFrame = async (frame, framescope) => {
581
634
  for (let i = 0; i < frame.selectors.length; i++) {
582
635
  let frameLocator = frame.selectors[i];
583
636
  if (frameLocator.css) {
584
- framescope = framescope.frameLocator(frameLocator.css);
637
+ let testframescope = framescope.frameLocator(frameLocator.css);
585
638
  if (frameLocator.index) {
586
- framescope = framescope.nth(frameLocator.index);
639
+ testframescope = framescope.nth(frameLocator.index);
640
+ }
641
+ try {
642
+ await testframescope.owner().evaluateHandle(() => true, null, {
643
+ timeout: 5000,
644
+ });
645
+ framescope = testframescope;
646
+ break;
647
+ }
648
+ catch (error) {
649
+ console.error("frame not found " + frameLocator.css);
587
650
  }
588
- break;
589
651
  }
590
652
  }
591
653
  if (frame.children) {
592
- return findFrame(frame.children, framescope);
654
+ return await findFrame(frame.children, framescope);
593
655
  }
594
656
  return framescope;
595
657
  };
596
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
597
658
  while (true) {
598
659
  let frameFound = false;
599
660
  if (selectors.nestFrmLoc) {
600
- scope = findFrame(selectors.nestFrmLoc, scope);
661
+ scope = await findFrame(selectors.nestFrmLoc, scope);
601
662
  frameFound = true;
602
663
  break;
603
664
  }
@@ -617,6 +678,8 @@ class StableBrowser {
617
678
  if (!scope) {
618
679
  info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
619
680
  if (performance.now() - startTime > timeout) {
681
+ info.failCause.iframeNotFound = true;
682
+ info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
620
683
  throw new Error("unable to locate iframe " + selectors.iframe_src);
621
684
  }
622
685
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -626,6 +689,30 @@ class StableBrowser {
626
689
  }
627
690
  }
628
691
  }
692
+ if (!scope) {
693
+ scope = this.page;
694
+ }
695
+ return scope;
696
+ }
697
+ async _getDocumentBody(selectors, timeout = 30000, info) {
698
+ let scope = await this._findFrameScope(selectors, timeout, info);
699
+ return scope.evaluate(() => {
700
+ var bodyContent = document.body.innerHTML;
701
+ return bodyContent;
702
+ });
703
+ }
704
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
705
+ if (!info) {
706
+ info = {};
707
+ info.failCause = {};
708
+ info.log = "";
709
+ }
710
+ let highPriorityTimeout = 5000;
711
+ let visibleOnlyTimeout = 6000;
712
+ let startTime = performance.now();
713
+ let locatorsCount = 0;
714
+ //let arrayMode = Array.isArray(selectors);
715
+ let scope = await this._findFrameScope(selectors, timeout, info);
629
716
  let selectorsLocators = null;
630
717
  selectorsLocators = selectors.locators;
631
718
  // group selectors by priority
@@ -727,6 +814,8 @@ class StableBrowser {
727
814
  }
728
815
  this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
729
816
  info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
817
+ info.failCause.locatorNotFound = true;
818
+ info.failCause.lastError = "failed to locate unique element";
730
819
  throw new Error("failed to locate first element no elements found, " + info.log);
731
820
  }
732
821
  async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
@@ -758,89 +847,129 @@ class StableBrowser {
758
847
  });
759
848
  result.locatorIndex = i;
760
849
  }
850
+ if (foundLocators.length > 1) {
851
+ info.failCause.foundMultiple = true;
852
+ }
761
853
  }
762
854
  return result;
763
855
  }
764
- async click(selectors, _params, options = {}, world = null) {
765
- this._validateSelectors(selectors);
856
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
766
857
  const startTime = Date.now();
767
- if (options && options.context) {
768
- selectors.locators[0].text = options.context;
858
+ let timeout = 30000;
859
+ if (options && options.timeout) {
860
+ timeout = options.timeout;
769
861
  }
770
- const info = {};
771
- info.log = "***** click on " + selectors.element_name + " *****\n";
772
- info.operation = "click";
773
- info.selectors = selectors;
774
- let error = null;
775
- let screenshotId = null;
776
- let screenshotPath = null;
862
+ while (true) {
863
+ try {
864
+ const result = await locate_element(this.context, elementDescription, "click");
865
+ if (result?.elementNumber >= 0) {
866
+ const selectors = {
867
+ frame: result?.frame,
868
+ locators: [
869
+ {
870
+ css: result?.css,
871
+ },
872
+ ],
873
+ };
874
+ await this.click(selectors, _params, options, world);
875
+ return;
876
+ }
877
+ }
878
+ catch (e) {
879
+ if (performance.now() - startTime > timeout) {
880
+ // throw e;
881
+ await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
882
+ }
883
+ }
884
+ await new Promise((resolve) => setTimeout(resolve, 3000));
885
+ }
886
+ }
887
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
888
+ const startTime = Date.now();
889
+ let timeout = 30000;
890
+ if (options && options.timeout) {
891
+ timeout = options.timeout;
892
+ }
893
+ while (true) {
894
+ try {
895
+ const result = await locate_element(this.context, elementDescription, "fill", value);
896
+ if (result?.elementNumber >= 0) {
897
+ const selectors = {
898
+ frame: result?.frame,
899
+ locators: [
900
+ {
901
+ css: result?.css,
902
+ },
903
+ ],
904
+ };
905
+ await this.clickType(selectors, value, false, _params, options, world);
906
+ return;
907
+ }
908
+ }
909
+ catch (e) {
910
+ if (performance.now() - startTime > timeout) {
911
+ // throw e;
912
+ await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
913
+ }
914
+ }
915
+ await new Promise((resolve) => setTimeout(resolve, 3000));
916
+ }
917
+ }
918
+ async click(selectors, _params, options = {}, world = null) {
919
+ const state = {
920
+ selectors,
921
+ _params,
922
+ options,
923
+ world,
924
+ text: "Click element",
925
+ type: Types.CLICK,
926
+ operation: "click",
927
+ log: "***** click on " + selectors.element_name + " *****\n",
928
+ };
777
929
  try {
778
- let element = await this._locate(selectors, info, _params);
779
- await this.scrollIfNeeded(element, info);
780
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
930
+ await _preCommand(state, this);
931
+ if (state.options && state.options.context) {
932
+ state.selectors.locators[0].text = state.options.context;
933
+ }
781
934
  try {
782
- await this._highlightElements(element);
783
- await element.click();
784
- await new Promise((resolve) => setTimeout(resolve, 1000));
935
+ await state.element.click();
936
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
785
937
  }
786
938
  catch (e) {
787
939
  // await this.closeUnexpectedPopups();
788
- info.log += "click failed, will try again" + "\n";
789
- element = await this._locate(selectors, info, _params);
790
- await element.dispatchEvent("click");
791
- await new Promise((resolve) => setTimeout(resolve, 1000));
940
+ state.element = await this._locate(selectors, state.info, _params);
941
+ await state.element.dispatchEvent("click");
942
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
792
943
  }
793
944
  await this.waitForPageLoad();
794
- return info;
945
+ return state.info;
795
946
  }
796
947
  catch (e) {
797
- this.logger.error("click failed " + info.log);
798
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
799
- info.screenshotPath = screenshotPath;
800
- Object.assign(e, { info: info });
801
- error = e;
802
- throw e;
948
+ await _commandError(state, e, this);
803
949
  }
804
950
  finally {
805
- const endTime = Date.now();
806
- this._reportToWorld(world, {
807
- element_name: selectors.element_name,
808
- type: Types.CLICK,
809
- text: `Click element`,
810
- screenshotId,
811
- result: error
812
- ? {
813
- status: "FAILED",
814
- startTime,
815
- endTime,
816
- message: error === null || error === void 0 ? void 0 : error.message,
817
- }
818
- : {
819
- status: "PASSED",
820
- startTime,
821
- endTime,
822
- },
823
- info: info,
824
- });
951
+ _commandFinally(state, this);
825
952
  }
826
953
  }
827
954
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
828
- this._validateSelectors(selectors);
829
- const startTime = Date.now();
830
- const info = {};
831
- info.log = "";
832
- info.operation = "setCheck";
833
- info.checked = checked;
834
- info.selectors = selectors;
835
- let error = null;
836
- let screenshotId = null;
837
- let screenshotPath = null;
955
+ const state = {
956
+ selectors,
957
+ _params,
958
+ options,
959
+ world,
960
+ type: checked ? Types.CHECK : Types.UNCHECK,
961
+ text: checked ? `Check element` : `Uncheck element`,
962
+ operation: "setCheck",
963
+ log: "***** check " + selectors.element_name + " *****\n",
964
+ };
838
965
  try {
839
- let element = await this._locate(selectors, info, _params);
840
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
966
+ await _preCommand(state, this);
967
+ state.info.checked = checked;
968
+ // let element = await this._locate(selectors, info, _params);
969
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
841
970
  try {
842
- await this._highlightElements(element);
843
- await element.setChecked(checked);
971
+ // await this._highlightElements(element);
972
+ await state.element.setChecked(checked);
844
973
  await new Promise((resolve) => setTimeout(resolve, 1000));
845
974
  }
846
975
  catch (e) {
@@ -849,179 +978,108 @@ class StableBrowser {
849
978
  }
850
979
  else {
851
980
  //await this.closeUnexpectedPopups();
852
- info.log += "setCheck failed, will try again" + "\n";
853
- element = await this._locate(selectors, info, _params);
854
- await element.setChecked(checked, { timeout: 5000, force: true });
981
+ state.info.log += "setCheck failed, will try again" + "\n";
982
+ state.element = await this._locate(selectors, state.info, _params);
983
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
855
984
  await new Promise((resolve) => setTimeout(resolve, 1000));
856
985
  }
857
986
  }
858
987
  await this.waitForPageLoad();
859
- return info;
988
+ return state.info;
860
989
  }
861
990
  catch (e) {
862
- this.logger.error("setCheck failed " + info.log);
863
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
864
- info.screenshotPath = screenshotPath;
865
- Object.assign(e, { info: info });
866
- error = e;
867
- throw e;
991
+ await _commandError(state, e, this);
868
992
  }
869
993
  finally {
870
- const endTime = Date.now();
871
- this._reportToWorld(world, {
872
- element_name: selectors.element_name,
873
- type: checked ? Types.CHECK : Types.UNCHECK,
874
- text: checked ? `Check element` : `Uncheck element`,
875
- screenshotId,
876
- result: error
877
- ? {
878
- status: "FAILED",
879
- startTime,
880
- endTime,
881
- message: error === null || error === void 0 ? void 0 : error.message,
882
- }
883
- : {
884
- status: "PASSED",
885
- startTime,
886
- endTime,
887
- },
888
- info: info,
889
- });
994
+ _commandFinally(state, this);
890
995
  }
891
996
  }
892
997
  async hover(selectors, _params, options = {}, world = null) {
893
- this._validateSelectors(selectors);
894
- const startTime = Date.now();
895
- const info = {};
896
- info.log = "";
897
- info.operation = "hover";
898
- info.selectors = selectors;
899
- let error = null;
900
- let screenshotId = null;
901
- let screenshotPath = null;
998
+ const state = {
999
+ selectors,
1000
+ _params,
1001
+ options,
1002
+ world,
1003
+ type: Types.HOVER,
1004
+ text: `Hover element`,
1005
+ operation: "hover",
1006
+ log: "***** hover " + selectors.element_name + " *****\n",
1007
+ };
902
1008
  try {
903
- let element = await this._locate(selectors, info, _params);
904
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1009
+ await _preCommand(state, this);
905
1010
  try {
906
- await this._highlightElements(element);
907
- await element.hover();
1011
+ await state.element.hover();
908
1012
  await new Promise((resolve) => setTimeout(resolve, 1000));
909
1013
  }
910
1014
  catch (e) {
911
1015
  //await this.closeUnexpectedPopups();
912
- info.log += "hover failed, will try again" + "\n";
913
- element = await this._locate(selectors, info, _params);
914
- await element.hover({ timeout: 10000 });
1016
+ state.info.log += "hover failed, will try again" + "\n";
1017
+ state.element = await this._locate(selectors, state.info, _params);
1018
+ await state.element.hover({ timeout: 10000 });
915
1019
  await new Promise((resolve) => setTimeout(resolve, 1000));
916
1020
  }
917
1021
  await this.waitForPageLoad();
918
- return info;
1022
+ return state.info;
919
1023
  }
920
1024
  catch (e) {
921
- this.logger.error("hover failed " + info.log);
922
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
923
- info.screenshotPath = screenshotPath;
924
- Object.assign(e, { info: info });
925
- error = e;
926
- throw e;
1025
+ await _commandError(state, e, this);
927
1026
  }
928
1027
  finally {
929
- const endTime = Date.now();
930
- this._reportToWorld(world, {
931
- element_name: selectors.element_name,
932
- type: Types.HOVER,
933
- text: `Hover element`,
934
- screenshotId,
935
- result: error
936
- ? {
937
- status: "FAILED",
938
- startTime,
939
- endTime,
940
- message: error === null || error === void 0 ? void 0 : error.message,
941
- }
942
- : {
943
- status: "PASSED",
944
- startTime,
945
- endTime,
946
- },
947
- info: info,
948
- });
1028
+ _commandFinally(state, this);
949
1029
  }
950
1030
  }
951
1031
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
952
- this._validateSelectors(selectors);
953
1032
  if (!values) {
954
1033
  throw new Error("values is null");
955
1034
  }
956
- const startTime = Date.now();
957
- let error = null;
958
- let screenshotId = null;
959
- let screenshotPath = null;
960
- const info = {};
961
- info.log = "";
962
- info.operation = "selectOptions";
963
- info.selectors = selectors;
1035
+ const state = {
1036
+ selectors,
1037
+ _params,
1038
+ options,
1039
+ world,
1040
+ value: values.toString(),
1041
+ type: Types.SELECT,
1042
+ text: `Select option: ${values}`,
1043
+ operation: "selectOption",
1044
+ log: "***** select option " + selectors.element_name + " *****\n",
1045
+ };
964
1046
  try {
965
- let element = await this._locate(selectors, info, _params);
966
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1047
+ await _preCommand(state, this);
967
1048
  try {
968
- await this._highlightElements(element);
969
- await element.selectOption(values);
1049
+ await state.element.selectOption(values);
970
1050
  }
971
1051
  catch (e) {
972
1052
  //await this.closeUnexpectedPopups();
973
- info.log += "selectOption failed, will try force" + "\n";
974
- await element.selectOption(values, { timeout: 10000, force: true });
1053
+ state.info.log += "selectOption failed, will try force" + "\n";
1054
+ await state.element.selectOption(values, { timeout: 10000, force: true });
975
1055
  }
976
1056
  await this.waitForPageLoad();
977
- return info;
1057
+ return state.info;
978
1058
  }
979
1059
  catch (e) {
980
- this.logger.error("selectOption failed " + info.log);
981
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
982
- info.screenshotPath = screenshotPath;
983
- Object.assign(e, { info: info });
984
- this.logger.info("click failed, will try next selector");
985
- error = e;
986
- throw e;
1060
+ await _commandError(state, e, this);
987
1061
  }
988
1062
  finally {
989
- const endTime = Date.now();
990
- this._reportToWorld(world, {
991
- element_name: selectors.element_name,
992
- type: Types.SELECT,
993
- text: `Select option: ${values}`,
994
- value: values.toString(),
995
- screenshotId,
996
- result: error
997
- ? {
998
- status: "FAILED",
999
- startTime,
1000
- endTime,
1001
- message: error === null || error === void 0 ? void 0 : error.message,
1002
- }
1003
- : {
1004
- status: "PASSED",
1005
- startTime,
1006
- endTime,
1007
- },
1008
- info: info,
1009
- });
1063
+ _commandFinally(state, this);
1010
1064
  }
1011
1065
  }
1012
1066
  async type(_value, _params = null, options = {}, world = null) {
1013
- const startTime = Date.now();
1014
- let error = null;
1015
- let screenshotId = null;
1016
- let screenshotPath = null;
1017
- const info = {};
1018
- info.log = "";
1019
- info.operation = "type";
1020
- _value = this._fixUsingParams(_value, _params);
1021
- info.value = _value;
1067
+ const state = {
1068
+ value: _value,
1069
+ _params,
1070
+ options,
1071
+ world,
1072
+ locate: false,
1073
+ scroll: false,
1074
+ highlight: false,
1075
+ type: Types.TYPE_PRESS,
1076
+ text: `Type value: ${_value}`,
1077
+ operation: "type",
1078
+ log: "",
1079
+ };
1022
1080
  try {
1023
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1024
- const valueSegment = _value.split("&&");
1081
+ await _preCommand(state, this);
1082
+ const valueSegment = state.value.split("&&");
1025
1083
  for (let i = 0; i < valueSegment.length; i++) {
1026
1084
  if (i > 0) {
1027
1085
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1041,134 +1099,76 @@ class StableBrowser {
1041
1099
  await this.page.keyboard.type(value);
1042
1100
  }
1043
1101
  }
1044
- return info;
1102
+ return state.info;
1045
1103
  }
1046
1104
  catch (e) {
1047
- //await this.closeUnexpectedPopups();
1048
- this.logger.error("type failed " + info.log);
1049
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1050
- info.screenshotPath = screenshotPath;
1051
- Object.assign(e, { info: info });
1052
- error = e;
1053
- throw e;
1105
+ await _commandError(state, e, this);
1054
1106
  }
1055
1107
  finally {
1056
- const endTime = Date.now();
1057
- this._reportToWorld(world, {
1058
- type: Types.TYPE_PRESS,
1059
- screenshotId,
1060
- value: _value,
1061
- text: `type value: ${_value}`,
1062
- result: error
1063
- ? {
1064
- status: "FAILED",
1065
- startTime,
1066
- endTime,
1067
- message: error === null || error === void 0 ? void 0 : error.message,
1068
- }
1069
- : {
1070
- status: "PASSED",
1071
- startTime,
1072
- endTime,
1073
- },
1074
- info: info,
1075
- });
1108
+ _commandFinally(state, this);
1076
1109
  }
1077
1110
  }
1078
1111
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1079
- // set input value for non fillable inputs like date, time, range, color, etc.
1080
- this._validateSelectors(selectors);
1081
- const startTime = Date.now();
1082
- const info = {};
1083
- info.log = "***** set input value " + selectors.element_name + " *****\n";
1084
- info.operation = "setInputValue";
1085
- info.selectors = selectors;
1086
- value = this._fixUsingParams(value, _params);
1087
- info.value = value;
1088
- let error = null;
1089
- let screenshotId = null;
1090
- let screenshotPath = null;
1112
+ const state = {
1113
+ selectors,
1114
+ _params,
1115
+ value,
1116
+ options,
1117
+ world,
1118
+ type: Types.SET_INPUT,
1119
+ text: `Set input value`,
1120
+ operation: "setInputValue",
1121
+ log: "***** set input value " + selectors.element_name + " *****\n",
1122
+ };
1091
1123
  try {
1092
- value = await this._replaceWithLocalData(value, this);
1093
- let element = await this._locate(selectors, info, _params);
1094
- await this.scrollIfNeeded(element, info);
1095
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1096
- await this._highlightElements(element);
1124
+ await _preCommand(state, this);
1125
+ let value = await this._replaceWithLocalData(state.value, this);
1097
1126
  try {
1098
- await element.evaluateHandle((el, value) => {
1127
+ await state.element.evaluateHandle((el, value) => {
1099
1128
  el.value = value;
1100
1129
  }, value);
1101
1130
  }
1102
1131
  catch (error) {
1103
1132
  this.logger.error("setInputValue failed, will try again");
1104
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1105
- info.screenshotPath = screenshotPath;
1106
- Object.assign(error, { info: info });
1107
- await element.evaluateHandle((el, value) => {
1133
+ await _screenshot(state, this);
1134
+ Object.assign(error, { info: state.info });
1135
+ await state.element.evaluateHandle((el, value) => {
1108
1136
  el.value = value;
1109
1137
  });
1110
1138
  }
1111
1139
  }
1112
1140
  catch (e) {
1113
- this.logger.error("setInputValue failed " + info.log);
1114
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1115
- info.screenshotPath = screenshotPath;
1116
- Object.assign(e, { info: info });
1117
- error = e;
1118
- throw e;
1141
+ await _commandError(state, e, this);
1119
1142
  }
1120
1143
  finally {
1121
- const endTime = Date.now();
1122
- this._reportToWorld(world, {
1123
- element_name: selectors.element_name,
1124
- type: Types.SET_INPUT,
1125
- text: `Set input value`,
1126
- value: value,
1127
- screenshotId,
1128
- result: error
1129
- ? {
1130
- status: "FAILED",
1131
- startTime,
1132
- endTime,
1133
- message: error === null || error === void 0 ? void 0 : error.message,
1134
- }
1135
- : {
1136
- status: "PASSED",
1137
- startTime,
1138
- endTime,
1139
- },
1140
- info: info,
1141
- });
1144
+ _commandFinally(state, this);
1142
1145
  }
1143
1146
  }
1144
1147
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1145
- this._validateSelectors(selectors);
1146
- const startTime = Date.now();
1147
- let error = null;
1148
- let screenshotId = null;
1149
- let screenshotPath = null;
1150
- const info = {};
1151
- info.log = "";
1152
- info.operation = Types.SET_DATE_TIME;
1153
- info.selectors = selectors;
1154
- info.value = value;
1148
+ const state = {
1149
+ selectors,
1150
+ _params,
1151
+ value: await this._replaceWithLocalData(value, this),
1152
+ options,
1153
+ world,
1154
+ type: Types.SET_DATE_TIME,
1155
+ text: `Set date time value: ${value}`,
1156
+ operation: "setDateTime",
1157
+ log: "***** set date time value " + selectors.element_name + " *****\n",
1158
+ throwError: false,
1159
+ };
1155
1160
  try {
1156
- value = await this._replaceWithLocalData(value, this);
1157
- let element = await this._locate(selectors, info, _params);
1158
- //insert red border around the element
1159
- await this.scrollIfNeeded(element, info);
1160
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1161
- await this._highlightElements(element);
1161
+ await _preCommand(state, this);
1162
1162
  try {
1163
- await element.click();
1163
+ await state.element.click();
1164
1164
  await new Promise((resolve) => setTimeout(resolve, 500));
1165
1165
  if (format) {
1166
- value = dayjs(value).format(format);
1167
- await element.fill(value);
1166
+ state.value = dayjs(state.value).format(format);
1167
+ await state.element.fill(state.value);
1168
1168
  }
1169
1169
  else {
1170
- const dateTimeValue = await getDateTimeValue({ value, element });
1171
- await element.evaluateHandle((el, dateTimeValue) => {
1170
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1171
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1172
1172
  el.value = ""; // clear input
1173
1173
  el.value = dateTimeValue;
1174
1174
  }, dateTimeValue);
@@ -1181,20 +1181,19 @@ class StableBrowser {
1181
1181
  }
1182
1182
  catch (err) {
1183
1183
  //await this.closeUnexpectedPopups();
1184
- this.logger.error("setting date time input failed " + JSON.stringify(info));
1184
+ this.logger.error("setting date time input failed " + JSON.stringify(state.info));
1185
1185
  this.logger.info("Trying again");
1186
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1187
- info.screenshotPath = screenshotPath;
1188
- Object.assign(err, { info: info });
1186
+ await _screenshot(state, this);
1187
+ Object.assign(err, { info: state.info });
1189
1188
  await element.click();
1190
1189
  await new Promise((resolve) => setTimeout(resolve, 500));
1191
1190
  if (format) {
1192
- value = dayjs(value).format(format);
1193
- await element.fill(value);
1191
+ state.value = dayjs(state.value).format(format);
1192
+ await state.element.fill(state.value);
1194
1193
  }
1195
1194
  else {
1196
- const dateTimeValue = await getDateTimeValue({ value, element });
1197
- await element.evaluateHandle((el, dateTimeValue) => {
1195
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1196
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1198
1197
  el.value = ""; // clear input
1199
1198
  el.value = dateTimeValue;
1200
1199
  }, dateTimeValue);
@@ -1207,61 +1206,39 @@ class StableBrowser {
1207
1206
  }
1208
1207
  }
1209
1208
  catch (e) {
1210
- error = e;
1211
- throw e;
1209
+ await _commandError(state, e, this);
1212
1210
  }
1213
1211
  finally {
1214
- const endTime = Date.now();
1215
- this._reportToWorld(world, {
1216
- element_name: selectors.element_name,
1217
- type: Types.SET_DATE_TIME,
1218
- screenshotId,
1219
- value: value,
1220
- text: `setDateTime input with value: ${value}`,
1221
- result: error
1222
- ? {
1223
- status: "FAILED",
1224
- startTime,
1225
- endTime,
1226
- message: error === null || error === void 0 ? void 0 : error.message,
1227
- }
1228
- : {
1229
- status: "PASSED",
1230
- startTime,
1231
- endTime,
1232
- },
1233
- info: info,
1234
- });
1212
+ _commandFinally(state, this);
1235
1213
  }
1236
1214
  }
1237
1215
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1238
1216
  _value = unEscapeString(_value);
1239
- this._validateSelectors(selectors);
1240
- const startTime = Date.now();
1241
- let error = null;
1242
- let screenshotId = null;
1243
- let screenshotPath = null;
1244
- const info = {};
1245
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1246
- info.operation = "clickType";
1247
- info.selectors = selectors;
1248
1217
  const newValue = await this._replaceWithLocalData(_value, world);
1218
+ const state = {
1219
+ selectors,
1220
+ _params,
1221
+ value: newValue,
1222
+ originalValue: _value,
1223
+ options,
1224
+ world,
1225
+ type: Types.FILL,
1226
+ text: `Click type input with value: ${_value}`,
1227
+ operation: "clickType",
1228
+ log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
1229
+ };
1249
1230
  if (newValue !== _value) {
1250
1231
  //this.logger.info(_value + "=" + newValue);
1251
1232
  _value = newValue;
1252
1233
  }
1253
- info.value = _value;
1254
1234
  try {
1255
- let element = await this._locate(selectors, info, _params);
1256
- //insert red border around the element
1257
- await this.scrollIfNeeded(element, info);
1258
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1259
- await this._highlightElements(element);
1235
+ await _preCommand(state, this);
1236
+ state.info.value = _value;
1260
1237
  if (options === null || options === undefined || !options.press) {
1261
1238
  try {
1262
- let currentValue = await element.inputValue();
1239
+ let currentValue = await state.element.inputValue();
1263
1240
  if (currentValue) {
1264
- await element.fill("");
1241
+ await state.element.fill("");
1265
1242
  }
1266
1243
  }
1267
1244
  catch (e) {
@@ -1270,22 +1247,22 @@ class StableBrowser {
1270
1247
  }
1271
1248
  if (options === null || options === undefined || options.press) {
1272
1249
  try {
1273
- await element.click({ timeout: 5000 });
1250
+ await state.element.click({ timeout: 5000 });
1274
1251
  }
1275
1252
  catch (e) {
1276
- await element.dispatchEvent("click");
1253
+ await state.element.dispatchEvent("click");
1277
1254
  }
1278
1255
  }
1279
1256
  else {
1280
1257
  try {
1281
- await element.focus();
1258
+ await state.element.focus();
1282
1259
  }
1283
1260
  catch (e) {
1284
- await element.dispatchEvent("focus");
1261
+ await state.element.dispatchEvent("focus");
1285
1262
  }
1286
1263
  }
1287
1264
  await new Promise((resolve) => setTimeout(resolve, 500));
1288
- const valueSegment = _value.split("&&");
1265
+ const valueSegment = state.value.split("&&");
1289
1266
  for (let i = 0; i < valueSegment.length; i++) {
1290
1267
  if (i > 0) {
1291
1268
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1305,13 +1282,14 @@ class StableBrowser {
1305
1282
  await new Promise((resolve) => setTimeout(resolve, 500));
1306
1283
  }
1307
1284
  }
1285
+ await _screenshot(state, this);
1308
1286
  if (enter === true) {
1309
1287
  await new Promise((resolve) => setTimeout(resolve, 2000));
1310
1288
  await this.page.keyboard.press("Enter");
1311
1289
  await this.waitForPageLoad();
1312
1290
  }
1313
1291
  else if (enter === false) {
1314
- await element.dispatchEvent("change");
1292
+ await state.element.dispatchEvent("change");
1315
1293
  //await this.page.keyboard.press("Tab");
1316
1294
  }
1317
1295
  else {
@@ -1320,104 +1298,50 @@ class StableBrowser {
1320
1298
  await this.waitForPageLoad();
1321
1299
  }
1322
1300
  }
1323
- return info;
1301
+ return state.info;
1324
1302
  }
1325
1303
  catch (e) {
1326
- //await this.closeUnexpectedPopups();
1327
- this.logger.error("fill failed " + JSON.stringify(info));
1328
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1329
- info.screenshotPath = screenshotPath;
1330
- Object.assign(e, { info: info });
1331
- error = e;
1332
- throw e;
1304
+ await _commandError(state, e, this);
1333
1305
  }
1334
1306
  finally {
1335
- const endTime = Date.now();
1336
- this._reportToWorld(world, {
1337
- element_name: selectors.element_name,
1338
- type: Types.FILL,
1339
- screenshotId,
1340
- value: _value,
1341
- text: `clickType input with value: ${_value}`,
1342
- result: error
1343
- ? {
1344
- status: "FAILED",
1345
- startTime,
1346
- endTime,
1347
- message: error === null || error === void 0 ? void 0 : error.message,
1348
- }
1349
- : {
1350
- status: "PASSED",
1351
- startTime,
1352
- endTime,
1353
- },
1354
- info: info,
1355
- });
1307
+ _commandFinally(state, this);
1356
1308
  }
1357
1309
  }
1358
1310
  async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1359
- this._validateSelectors(selectors);
1360
- value = unEscapeString(value);
1361
- const startTime = Date.now();
1362
- let error = null;
1363
- let screenshotId = null;
1364
- let screenshotPath = null;
1365
- const info = {};
1366
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1367
- info.operation = "fill";
1368
- info.selectors = selectors;
1369
- info.value = value;
1311
+ const state = {
1312
+ selectors,
1313
+ _params,
1314
+ value: unEscapeString(value),
1315
+ options,
1316
+ world,
1317
+ type: Types.FILL,
1318
+ text: `Fill input with value: ${value}`,
1319
+ operation: "fill",
1320
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1321
+ };
1370
1322
  try {
1371
- let element = await this._locate(selectors, info, _params);
1372
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1373
- await this._highlightElements(element);
1374
- await element.fill(value);
1375
- await element.dispatchEvent("change");
1323
+ await _preCommand(state, this);
1324
+ await state.element.fill(value);
1325
+ await state.element.dispatchEvent("change");
1376
1326
  if (enter) {
1377
- await new Promise((resolve) => setTimeout(resolve, 2000));
1378
- await this.page.keyboard.press("Enter");
1379
- }
1380
- await this.waitForPageLoad();
1381
- return info;
1382
- }
1383
- catch (e) {
1384
- //await this.closeUnexpectedPopups();
1385
- this.logger.error("fill failed " + info.log);
1386
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1387
- info.screenshotPath = screenshotPath;
1388
- Object.assign(e, { info: info });
1389
- error = e;
1390
- throw e;
1391
- }
1392
- finally {
1393
- const endTime = Date.now();
1394
- this._reportToWorld(world, {
1395
- element_name: selectors.element_name,
1396
- type: Types.FILL,
1397
- screenshotId,
1398
- value,
1399
- text: `Fill input with value: ${value}`,
1400
- result: error
1401
- ? {
1402
- status: "FAILED",
1403
- startTime,
1404
- endTime,
1405
- message: error === null || error === void 0 ? void 0 : error.message,
1406
- }
1407
- : {
1408
- status: "PASSED",
1409
- startTime,
1410
- endTime,
1411
- },
1412
- info: info,
1413
- });
1327
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1328
+ await this.page.keyboard.press("Enter");
1329
+ }
1330
+ await this.waitForPageLoad();
1331
+ return state.info;
1332
+ }
1333
+ catch (e) {
1334
+ await _commandError(state, e, this);
1335
+ }
1336
+ finally {
1337
+ _commandFinally(state, this);
1414
1338
  }
1415
1339
  }
1416
1340
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1417
1341
  return await this._getText(selectors, 0, _params, options, info, world);
1418
1342
  }
1419
1343
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1420
- this._validateSelectors(selectors);
1344
+ _validateSelectors(selectors);
1421
1345
  let screenshotId = null;
1422
1346
  let screenshotPath = null;
1423
1347
  if (!info.log) {
@@ -1461,166 +1385,124 @@ class StableBrowser {
1461
1385
  }
1462
1386
  }
1463
1387
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1464
- var _a;
1465
- this._validateSelectors(selectors);
1466
1388
  if (!pattern) {
1467
1389
  throw new Error("pattern is null");
1468
1390
  }
1469
1391
  if (!text) {
1470
1392
  throw new Error("text is null");
1471
1393
  }
1394
+ const state = {
1395
+ selectors,
1396
+ _params,
1397
+ pattern,
1398
+ value: pattern,
1399
+ options,
1400
+ world,
1401
+ locate: false,
1402
+ scroll: false,
1403
+ screenshot: false,
1404
+ highlight: false,
1405
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1406
+ text: `Verify element contains pattern: ${pattern}`,
1407
+ operation: "containsPattern",
1408
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1409
+ };
1472
1410
  const newValue = await this._replaceWithLocalData(text, world);
1473
1411
  if (newValue !== text) {
1474
1412
  this.logger.info(text + "=" + newValue);
1475
1413
  text = newValue;
1476
1414
  }
1477
- const startTime = Date.now();
1478
- let error = null;
1479
- let screenshotId = null;
1480
- let screenshotPath = null;
1481
- const info = {};
1482
- info.log =
1483
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1484
- info.operation = "containsPattern";
1485
- info.selectors = selectors;
1486
- info.value = text;
1487
- info.pattern = pattern;
1488
1415
  let foundObj = null;
1489
1416
  try {
1490
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1417
+ await _preCommand(state, this);
1418
+ state.info.pattern = pattern;
1419
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1491
1420
  if (foundObj && foundObj.element) {
1492
- await this.scrollIfNeeded(foundObj.element, info);
1421
+ await this.scrollIfNeeded(foundObj.element, state.info);
1493
1422
  }
1494
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1423
+ await _screenshot(state, this);
1495
1424
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1496
1425
  pattern = pattern.replace("{text}", escapedText);
1497
1426
  let regex = new RegExp(pattern, "im");
1498
- 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))) {
1499
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1427
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1428
+ state.info.foundText = foundObj?.text;
1500
1429
  throw new Error("element doesn't contain text " + text);
1501
1430
  }
1502
- return info;
1431
+ return state.info;
1503
1432
  }
1504
1433
  catch (e) {
1505
- //await this.closeUnexpectedPopups();
1506
- this.logger.error("verify element contains text failed " + info.log);
1507
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1508
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1509
- info.screenshotPath = screenshotPath;
1510
- Object.assign(e, { info: info });
1511
- error = e;
1512
- throw e;
1434
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1435
+ await _commandError(state, e, this);
1513
1436
  }
1514
1437
  finally {
1515
- const endTime = Date.now();
1516
- this._reportToWorld(world, {
1517
- element_name: selectors.element_name,
1518
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1519
- value: pattern,
1520
- text: `Verify element contains pattern: ${pattern}`,
1521
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1522
- result: error
1523
- ? {
1524
- status: "FAILED",
1525
- startTime,
1526
- endTime,
1527
- message: error === null || error === void 0 ? void 0 : error.message,
1528
- }
1529
- : {
1530
- status: "PASSED",
1531
- startTime,
1532
- endTime,
1533
- },
1534
- info: info,
1535
- });
1438
+ _commandFinally(state, this);
1536
1439
  }
1537
1440
  }
1538
1441
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1539
- var _a, _b, _c;
1540
- this._validateSelectors(selectors);
1541
- text = unEscapeString(text);
1442
+ const state = {
1443
+ selectors,
1444
+ _params,
1445
+ value: text,
1446
+ options,
1447
+ world,
1448
+ locate: false,
1449
+ scroll: false,
1450
+ screenshot: false,
1451
+ highlight: false,
1452
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1453
+ text: `Verify element contains text: ${text}`,
1454
+ operation: "containsText",
1455
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1456
+ };
1542
1457
  if (!text) {
1543
1458
  throw new Error("text is null");
1544
1459
  }
1545
- const startTime = Date.now();
1546
- let error = null;
1547
- let screenshotId = null;
1548
- let screenshotPath = null;
1549
- const info = {};
1550
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1551
- info.operation = "containsText";
1552
- info.selectors = selectors;
1460
+ text = unEscapeString(text);
1553
1461
  const newValue = await this._replaceWithLocalData(text, world);
1554
1462
  if (newValue !== text) {
1555
1463
  this.logger.info(text + "=" + newValue);
1556
1464
  text = newValue;
1557
1465
  }
1558
- info.value = text;
1559
1466
  let foundObj = null;
1560
1467
  try {
1561
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1468
+ await _preCommand(state, this);
1469
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1562
1470
  if (foundObj && foundObj.element) {
1563
- await this.scrollIfNeeded(foundObj.element, info);
1471
+ await this.scrollIfNeeded(foundObj.element, state.info);
1564
1472
  }
1565
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1473
+ await _screenshot(state, this);
1566
1474
  const dateAlternatives = findDateAlternatives(text);
1567
1475
  const numberAlternatives = findNumberAlternatives(text);
1568
1476
  if (dateAlternatives.date) {
1569
1477
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1570
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1571
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1572
- return info;
1478
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1479
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1480
+ return state.info;
1573
1481
  }
1574
1482
  }
1575
1483
  throw new Error("element doesn't contain text " + text);
1576
1484
  }
1577
1485
  else if (numberAlternatives.number) {
1578
1486
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1579
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1580
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1581
- return info;
1487
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1488
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1489
+ return state.info;
1582
1490
  }
1583
1491
  }
1584
1492
  throw new Error("element doesn't contain text " + text);
1585
1493
  }
1586
- 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))) {
1587
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1588
- info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1494
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1495
+ state.info.foundText = foundObj?.text;
1496
+ state.info.value = foundObj?.value;
1589
1497
  throw new Error("element doesn't contain text " + text);
1590
1498
  }
1591
- return info;
1499
+ return state.info;
1592
1500
  }
1593
1501
  catch (e) {
1594
- //await this.closeUnexpectedPopups();
1595
- this.logger.error("verify element contains text failed " + info.log);
1596
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1597
- info.screenshotPath = screenshotPath;
1598
- Object.assign(e, { info: info });
1599
- error = e;
1600
- throw e;
1502
+ await _commandError(state, e, this);
1601
1503
  }
1602
1504
  finally {
1603
- const endTime = Date.now();
1604
- this._reportToWorld(world, {
1605
- element_name: selectors.element_name,
1606
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1607
- text: `Verify element contains text: ${text}`,
1608
- value: text,
1609
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1610
- result: error
1611
- ? {
1612
- status: "FAILED",
1613
- startTime,
1614
- endTime,
1615
- message: error === null || error === void 0 ? void 0 : error.message,
1616
- }
1617
- : {
1618
- status: "PASSED",
1619
- startTime,
1620
- endTime,
1621
- },
1622
- info: info,
1623
- });
1505
+ _commandFinally(state, this);
1624
1506
  }
1625
1507
  }
1626
1508
  _getDataFile(world = null) {
@@ -1849,7 +1731,6 @@ class StableBrowser {
1849
1731
  }
1850
1732
  async takeScreenshot(screenshotPath) {
1851
1733
  const playContext = this.context.playContext;
1852
- const client = await playContext.newCDPSession(this.page);
1853
1734
  // Using CDP to capture the screenshot
1854
1735
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1855
1736
  document.body.scrollWidth,
@@ -1859,20 +1740,28 @@ class StableBrowser {
1859
1740
  document.body.clientWidth,
1860
1741
  document.documentElement.clientWidth,
1861
1742
  ])));
1862
- const { data } = await client.send("Page.captureScreenshot", {
1863
- format: "png",
1864
- // clip: {
1865
- // x: 0,
1866
- // y: 0,
1867
- // width: viewportWidth,
1868
- // height: viewportHeight,
1869
- // scale: 1,
1870
- // },
1871
- });
1872
- if (!screenshotPath) {
1873
- return data;
1743
+ let screenshotBuffer = null;
1744
+ if (this.context.browserName === "chromium") {
1745
+ const client = await playContext.newCDPSession(this.page);
1746
+ const { data } = await client.send("Page.captureScreenshot", {
1747
+ format: "png",
1748
+ // clip: {
1749
+ // x: 0,
1750
+ // y: 0,
1751
+ // width: viewportWidth,
1752
+ // height: viewportHeight,
1753
+ // scale: 1,
1754
+ // },
1755
+ });
1756
+ await client.detach();
1757
+ if (!screenshotPath) {
1758
+ return data;
1759
+ }
1760
+ screenshotBuffer = Buffer.from(data, "base64");
1761
+ }
1762
+ else {
1763
+ screenshotBuffer = await this.page.screenshot();
1874
1764
  }
1875
- let screenshotBuffer = Buffer.from(data, "base64");
1876
1765
  let image = await Jimp.read(screenshotBuffer);
1877
1766
  // Get the image dimensions
1878
1767
  const { width, height } = image.bitmap;
@@ -1885,130 +1774,71 @@ class StableBrowser {
1885
1774
  else {
1886
1775
  fs.writeFileSync(screenshotPath, screenshotBuffer);
1887
1776
  }
1888
- await client.detach();
1889
1777
  }
1890
1778
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1891
- this._validateSelectors(selectors);
1892
- const startTime = Date.now();
1893
- let error = null;
1894
- let screenshotId = null;
1895
- 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
+ };
1896
1789
  await new Promise((resolve) => setTimeout(resolve, 2000));
1897
- const info = {};
1898
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
1899
- info.operation = "verify";
1900
- info.selectors = selectors;
1901
1790
  try {
1902
- const element = await this._locate(selectors, info, _params);
1903
- if (element) {
1904
- await this.scrollIfNeeded(element, info);
1905
- }
1906
- await this._highlightElements(element);
1907
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1908
- await expect(element).toHaveCount(1, { timeout: 10000 });
1909
- return info;
1791
+ await _preCommand(state, this);
1792
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1793
+ return state.info;
1910
1794
  }
1911
1795
  catch (e) {
1912
- //await this.closeUnexpectedPopups();
1913
- this.logger.error("verify failed " + info.log);
1914
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1915
- info.screenshotPath = screenshotPath;
1916
- Object.assign(e, { info: info });
1917
- error = e;
1918
- throw e;
1796
+ await _commandError(state, e, this);
1919
1797
  }
1920
1798
  finally {
1921
- const endTime = Date.now();
1922
- this._reportToWorld(world, {
1923
- element_name: selectors.element_name,
1924
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1925
- text: "Verify element exists in page",
1926
- screenshotId,
1927
- result: error
1928
- ? {
1929
- status: "FAILED",
1930
- startTime,
1931
- endTime,
1932
- message: error === null || error === void 0 ? void 0 : error.message,
1933
- }
1934
- : {
1935
- status: "PASSED",
1936
- startTime,
1937
- endTime,
1938
- },
1939
- info: info,
1940
- });
1799
+ _commandFinally(state, this);
1941
1800
  }
1942
1801
  }
1943
1802
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1944
- this._validateSelectors(selectors);
1945
- const startTime = Date.now();
1946
- let error = null;
1947
- let screenshotId = null;
1948
- 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
+ };
1949
1815
  await new Promise((resolve) => setTimeout(resolve, 2000));
1950
- const info = {};
1951
- info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
1952
- info.operation = "extract";
1953
- info.selectors = selectors;
1954
1816
  try {
1955
- const element = await this._locate(selectors, info, _params);
1956
- await this._highlightElements(element);
1957
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1817
+ await _preCommand(state, this);
1958
1818
  switch (attribute) {
1959
1819
  case "inner_text":
1960
- info.value = await element.innerText();
1820
+ state.value = await state.element.innerText();
1961
1821
  break;
1962
1822
  case "href":
1963
- info.value = await element.getAttribute("href");
1823
+ state.value = await state.element.getAttribute("href");
1964
1824
  break;
1965
1825
  case "value":
1966
- info.value = await element.inputValue();
1826
+ state.value = await state.element.inputValue();
1967
1827
  break;
1968
1828
  default:
1969
- info.value = await element.getAttribute(attribute);
1829
+ state.value = await state.element.getAttribute(attribute);
1970
1830
  break;
1971
1831
  }
1972
- this[variable] = info.value;
1973
- if (world) {
1974
- world[variable] = info.value;
1975
- }
1976
- this.setTestData({ [variable]: info.value }, world);
1977
- this.logger.info("set test data: " + variable + "=" + info.value);
1978
- 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;
1979
1836
  }
1980
1837
  catch (e) {
1981
- //await this.closeUnexpectedPopups();
1982
- this.logger.error("extract failed " + info.log);
1983
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1984
- info.screenshotPath = screenshotPath;
1985
- Object.assign(e, { info: info });
1986
- error = e;
1987
- throw e;
1838
+ await _commandError(state, e, this);
1988
1839
  }
1989
1840
  finally {
1990
- const endTime = Date.now();
1991
- this._reportToWorld(world, {
1992
- element_name: selectors.element_name,
1993
- type: Types.EXTRACT_ATTRIBUTE,
1994
- variable: variable,
1995
- value: info.value,
1996
- text: "Extract attribute from element",
1997
- screenshotId,
1998
- result: error
1999
- ? {
2000
- status: "FAILED",
2001
- startTime,
2002
- endTime,
2003
- message: error === null || error === void 0 ? void 0 : error.message,
2004
- }
2005
- : {
2006
- status: "PASSED",
2007
- startTime,
2008
- endTime,
2009
- },
2010
- info: info,
2011
- });
1841
+ _commandFinally(state, this);
2012
1842
  }
2013
1843
  }
2014
1844
  async extractEmailData(emailAddress, options, world) {
@@ -2085,7 +1915,8 @@ class StableBrowser {
2085
1915
  catch (e) {
2086
1916
  errorCount++;
2087
1917
  if (errorCount > 3) {
2088
- throw e;
1918
+ // throw e;
1919
+ await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
2089
1920
  }
2090
1921
  // ignore
2091
1922
  }
@@ -2196,7 +2027,8 @@ class StableBrowser {
2196
2027
  info.screenshotPath = screenshotPath;
2197
2028
  Object.assign(e, { info: info });
2198
2029
  error = e;
2199
- throw e;
2030
+ // throw e;
2031
+ await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
2200
2032
  }
2201
2033
  finally {
2202
2034
  const endTime = Date.now();
@@ -2209,7 +2041,7 @@ class StableBrowser {
2209
2041
  status: "FAILED",
2210
2042
  startTime,
2211
2043
  endTime,
2212
- message: error === null || error === void 0 ? void 0 : error.message,
2044
+ message: error?.message,
2213
2045
  }
2214
2046
  : {
2215
2047
  status: "PASSED",
@@ -2222,52 +2054,58 @@ class StableBrowser {
2222
2054
  }
2223
2055
  async verifyTextExistInPage(text, options = {}, world = null) {
2224
2056
  text = unEscapeString(text);
2225
- 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
+ };
2226
2069
  const timeout = this._getLoadTimeout(options);
2227
- let error = null;
2228
- let screenshotId = null;
2229
- let screenshotPath = null;
2230
2070
  await new Promise((resolve) => setTimeout(resolve, 2000));
2231
- const info = {};
2232
- info.log = "***** verify text " + text + " exists in page *****\n";
2233
- info.operation = "verifyTextExistInPage";
2234
2071
  const newValue = await this._replaceWithLocalData(text, world);
2235
2072
  if (newValue !== text) {
2236
2073
  this.logger.info(text + "=" + newValue);
2237
2074
  text = newValue;
2238
2075
  }
2239
- info.text = text;
2240
2076
  let dateAlternatives = findDateAlternatives(text);
2241
2077
  let numberAlternatives = findNumberAlternatives(text);
2242
2078
  try {
2079
+ await _preCommand(state, this);
2080
+ state.info.text = text;
2243
2081
  while (true) {
2244
2082
  const frames = this.page.frames();
2245
2083
  let results = [];
2246
2084
  for (let i = 0; i < frames.length; i++) {
2247
2085
  if (dateAlternatives.date) {
2248
2086
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2249
- 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, {});
2250
2088
  result.frame = frames[i];
2251
2089
  results.push(result);
2252
2090
  }
2253
2091
  }
2254
2092
  else if (numberAlternatives.number) {
2255
2093
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2256
- 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, {});
2257
2095
  result.frame = frames[i];
2258
2096
  results.push(result);
2259
2097
  }
2260
2098
  }
2261
2099
  else {
2262
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2100
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2263
2101
  result.frame = frames[i];
2264
2102
  results.push(result);
2265
2103
  }
2266
2104
  }
2267
- info.results = results;
2105
+ state.info.results = results;
2268
2106
  const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2269
2107
  if (resultWithElementsFound.length === 0) {
2270
- if (Date.now() - startTime > timeout) {
2108
+ if (Date.now() - state.startTime > timeout) {
2271
2109
  throw new Error(`Text ${text} not found in page`);
2272
2110
  }
2273
2111
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -2279,44 +2117,89 @@ class StableBrowser {
2279
2117
  await this._highlightElements(frame, dataAttribute);
2280
2118
  const element = await frame.$(dataAttribute);
2281
2119
  if (element) {
2282
- await this.scrollIfNeeded(element, info);
2120
+ await this.scrollIfNeeded(element, state.info);
2283
2121
  await element.dispatchEvent("bvt_verify_page_contains_text");
2284
2122
  }
2285
2123
  }
2286
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2287
- return info;
2124
+ await _screenshot(state, this);
2125
+ return state.info;
2288
2126
  }
2289
2127
  // await expect(element).toHaveCount(1, { timeout: 10000 });
2290
2128
  }
2291
2129
  catch (e) {
2292
- //await this.closeUnexpectedPopups();
2293
- this.logger.error("verify text exist in page failed " + info.log);
2294
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2295
- info.screenshotPath = screenshotPath;
2296
- Object.assign(e, { info: info });
2297
- error = e;
2298
- throw e;
2130
+ await _commandError(state, e, this);
2299
2131
  }
2300
2132
  finally {
2301
- const endTime = Date.now();
2302
- this._reportToWorld(world, {
2303
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2304
- text: "Verify text exists in page",
2305
- screenshotId,
2306
- result: error
2307
- ? {
2308
- status: "FAILED",
2309
- startTime,
2310
- endTime,
2311
- 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
+ }
2312
2172
  }
2313
- : {
2314
- status: "PASSED",
2315
- startTime,
2316
- endTime,
2317
- },
2318
- info: info,
2319
- });
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);
2320
2203
  }
2321
2204
  }
2322
2205
  _getServerUrl() {
@@ -2379,7 +2262,8 @@ class StableBrowser {
2379
2262
  info.screenshotPath = screenshotPath;
2380
2263
  Object.assign(e, { info: info });
2381
2264
  error = e;
2382
- throw e;
2265
+ // throw e;
2266
+ await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
2383
2267
  }
2384
2268
  finally {
2385
2269
  const endTime = Date.now();
@@ -2392,7 +2276,7 @@ class StableBrowser {
2392
2276
  status: "FAILED",
2393
2277
  startTime,
2394
2278
  endTime,
2395
- message: error === null || error === void 0 ? void 0 : error.message,
2279
+ message: error?.message,
2396
2280
  }
2397
2281
  : {
2398
2282
  status: "PASSED",
@@ -2424,7 +2308,7 @@ class StableBrowser {
2424
2308
  this.logger.info("Table data verified");
2425
2309
  }
2426
2310
  async getTableData(selectors, _params = null, options = {}, world = null) {
2427
- this._validateSelectors(selectors);
2311
+ _validateSelectors(selectors);
2428
2312
  const startTime = Date.now();
2429
2313
  let error = null;
2430
2314
  let screenshotId = null;
@@ -2446,7 +2330,8 @@ class StableBrowser {
2446
2330
  info.screenshotPath = screenshotPath;
2447
2331
  Object.assign(e, { info: info });
2448
2332
  error = e;
2449
- throw e;
2333
+ // throw e;
2334
+ await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
2450
2335
  }
2451
2336
  finally {
2452
2337
  const endTime = Date.now();
@@ -2460,7 +2345,7 @@ class StableBrowser {
2460
2345
  status: "FAILED",
2461
2346
  startTime,
2462
2347
  endTime,
2463
- message: error === null || error === void 0 ? void 0 : error.message,
2348
+ message: error?.message,
2464
2349
  }
2465
2350
  : {
2466
2351
  status: "PASSED",
@@ -2472,7 +2357,7 @@ class StableBrowser {
2472
2357
  }
2473
2358
  }
2474
2359
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2475
- this._validateSelectors(selectors);
2360
+ _validateSelectors(selectors);
2476
2361
  if (!query) {
2477
2362
  throw new Error("query is null");
2478
2363
  }
@@ -2611,7 +2496,8 @@ class StableBrowser {
2611
2496
  info.screenshotPath = screenshotPath;
2612
2497
  Object.assign(e, { info: info });
2613
2498
  error = e;
2614
- throw e;
2499
+ // throw e;
2500
+ await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
2615
2501
  }
2616
2502
  finally {
2617
2503
  const endTime = Date.now();
@@ -2625,7 +2511,7 @@ class StableBrowser {
2625
2511
  status: "FAILED",
2626
2512
  startTime,
2627
2513
  endTime,
2628
- message: error === null || error === void 0 ? void 0 : error.message,
2514
+ message: error?.message,
2629
2515
  }
2630
2516
  : {
2631
2517
  status: "PASSED",
@@ -2637,27 +2523,7 @@ class StableBrowser {
2637
2523
  }
2638
2524
  }
2639
2525
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2640
- if (!value) {
2641
- return value;
2642
- }
2643
- // find all the accurance of {{(.*?)}} and replace with the value
2644
- let regex = /{{(.*?)}}/g;
2645
- let matches = value.match(regex);
2646
- if (matches) {
2647
- const testData = this.getTestData(world);
2648
- for (let i = 0; i < matches.length; i++) {
2649
- let match = matches[i];
2650
- let key = match.substring(2, match.length - 2);
2651
- let newValue = objectPath.get(testData, key, null);
2652
- if (newValue !== null) {
2653
- value = value.replace(match, newValue);
2654
- }
2655
- }
2656
- }
2657
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2658
- return await decrypt(value, null, totpWait);
2659
- }
2660
- return value;
2526
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2661
2527
  }
2662
2528
  _getLoadTimeout(options) {
2663
2529
  let timeout = 15000;
@@ -2717,7 +2583,7 @@ class StableBrowser {
2717
2583
  status: "FAILED",
2718
2584
  startTime,
2719
2585
  endTime,
2720
- message: error === null || error === void 0 ? void 0 : error.message,
2586
+ message: error?.message,
2721
2587
  }
2722
2588
  : {
2723
2589
  status: "PASSED",
@@ -2728,39 +2594,28 @@ class StableBrowser {
2728
2594
  }
2729
2595
  }
2730
2596
  async closePage(options = {}, world = null) {
2731
- const startTime = Date.now();
2732
- let error = null;
2733
- let screenshotId = null;
2734
- let screenshotPath = null;
2735
- 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
+ };
2736
2609
  try {
2610
+ await _preCommand(state, this);
2737
2611
  await this.page.close();
2738
2612
  }
2739
2613
  catch (e) {
2740
2614
  console.log(".");
2615
+ await _commandError(state, e, this);
2741
2616
  }
2742
2617
  finally {
2743
- await new Promise((resolve) => setTimeout(resolve, 2000));
2744
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2745
- const endTime = Date.now();
2746
- this._reportToWorld(world, {
2747
- type: Types.CLOSE_PAGE,
2748
- text: "close page",
2749
- screenshotId,
2750
- result: error
2751
- ? {
2752
- status: "FAILED",
2753
- startTime,
2754
- endTime,
2755
- message: error === null || error === void 0 ? void 0 : error.message,
2756
- }
2757
- : {
2758
- status: "PASSED",
2759
- startTime,
2760
- endTime,
2761
- },
2762
- info: info,
2763
- });
2618
+ _commandFinally(state, this);
2764
2619
  }
2765
2620
  }
2766
2621
  async setViewportSize(width, hight, options = {}, world = null) {
@@ -2780,6 +2635,7 @@ class StableBrowser {
2780
2635
  }
2781
2636
  catch (e) {
2782
2637
  console.log(".");
2638
+ await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
2783
2639
  }
2784
2640
  finally {
2785
2641
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2794,7 +2650,7 @@ class StableBrowser {
2794
2650
  status: "FAILED",
2795
2651
  startTime,
2796
2652
  endTime,
2797
- message: error === null || error === void 0 ? void 0 : error.message,
2653
+ message: error?.message,
2798
2654
  }
2799
2655
  : {
2800
2656
  status: "PASSED",
@@ -2816,6 +2672,7 @@ class StableBrowser {
2816
2672
  }
2817
2673
  catch (e) {
2818
2674
  console.log(".");
2675
+ await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
2819
2676
  }
2820
2677
  finally {
2821
2678
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2830,7 +2687,7 @@ class StableBrowser {
2830
2687
  status: "FAILED",
2831
2688
  startTime,
2832
2689
  endTime,
2833
- message: error === null || error === void 0 ? void 0 : error.message,
2690
+ message: error?.message,
2834
2691
  }
2835
2692
  : {
2836
2693
  status: "PASSED",
@@ -2863,6 +2720,31 @@ class StableBrowser {
2863
2720
  }
2864
2721
  world.attach(JSON.stringify(properties), { mediaType: "application/json" });
2865
2722
  }
2723
+ async beforeStep(world, step) {
2724
+ this.stepName = step.pickleStep.text;
2725
+ this.logger.info("step: " + this.stepName);
2726
+ if (this.stepIndex === undefined) {
2727
+ this.stepIndex = 0;
2728
+ }
2729
+ else {
2730
+ this.stepIndex++;
2731
+ }
2732
+ if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
2733
+ if (this.context.browserObject.context) {
2734
+ await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
2735
+ }
2736
+ }
2737
+ }
2738
+ async afterStep(world, step) {
2739
+ this.stepName = null;
2740
+ if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
2741
+ if (this.context.browserObject.context) {
2742
+ await this.context.browserObject.context.tracing.stopChunk({
2743
+ path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
2744
+ });
2745
+ }
2746
+ }
2747
+ }
2866
2748
  }
2867
2749
  function createTimedPromise(promise, label) {
2868
2750
  return promise