automation_model 1.0.454-dev → 1.0.454-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,7 +225,7 @@ 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
231
  console.error("Error in request listener", error);
@@ -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
  }
@@ -336,7 +338,10 @@ class StableBrowser {
336
338
  return locatorReturn;
337
339
  }
338
340
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
339
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
341
+ if (css && css.locator) {
342
+ css = css.locator;
343
+ }
344
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
340
345
  if (result.elementCount === 0) {
341
346
  return;
342
347
  }
@@ -351,7 +356,7 @@ class StableBrowser {
351
356
  }
352
357
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
353
358
  //const stringifyText = JSON.stringify(text);
354
- return await scope.evaluate(([text, tag, regex, partial]) => {
359
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
355
360
  function isParent(parent, child) {
356
361
  let currentNode = child.parentNode;
357
362
  while (currentNode !== null) {
@@ -363,6 +368,15 @@ class StableBrowser {
363
368
  return false;
364
369
  }
365
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;
366
380
  function collectAllShadowDomElements(element, result = []) {
367
381
  // Check and add the element if it has a shadow root
368
382
  if (element.shadowRoot) {
@@ -379,7 +393,11 @@ class StableBrowser {
379
393
  }
380
394
  document.collectAllShadowDomElements = collectAllShadowDomElements;
381
395
  if (!tag) {
382
- tag = "*";
396
+ tag = "*:not(script, style, head)";
397
+ }
398
+ let regexpSearch = document.getRegex(text);
399
+ if (regexpSearch) {
400
+ regex = true;
383
401
  }
384
402
  let elements = Array.from(document.querySelectorAll(tag));
385
403
  let shadowHosts = [];
@@ -396,7 +414,9 @@ class StableBrowser {
396
414
  let randomToken = null;
397
415
  const foundElements = [];
398
416
  if (regex) {
399
- let regexpSearch = new RegExp(text, "im");
417
+ if (!regexpSearch) {
418
+ regexpSearch = new RegExp(text, "im");
419
+ }
400
420
  for (let i = 0; i < elements.length; i++) {
401
421
  const element = elements[i];
402
422
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -410,8 +430,8 @@ class StableBrowser {
410
430
  for (let i = 0; i < elements.length; i++) {
411
431
  const element = elements[i];
412
432
  if (partial) {
413
- if ((element.innerText && element.innerText.trim().includes(text)) ||
414
- (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()))) {
415
435
  foundElements.push(element);
416
436
  }
417
437
  }
@@ -456,18 +476,29 @@ class StableBrowser {
456
476
  }
457
477
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
458
478
  let locatorSearch = selectorHierarchy[index];
479
+ try {
480
+ locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
481
+ }
482
+ catch (e) {
483
+ console.error(e);
484
+ }
459
485
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
460
486
  let locator = null;
461
487
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
462
488
  let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
463
489
  if (!locatorString) {
490
+ info.failCause.textNotFound = true;
491
+ info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
464
492
  return;
465
493
  }
466
494
  locator = this._getLocator({ css: locatorString }, scope, _params);
467
495
  }
468
496
  else if (locatorSearch.text) {
469
- let result = await this._locateElementByText(scope, this._fixUsingParams(locatorSearch.text, _params), locatorSearch.tag, false, locatorSearch.partial === true, _params);
497
+ let text = this._fixUsingParams(locatorSearch.text, _params);
498
+ let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
470
499
  if (result.elementCount === 0) {
500
+ info.failCause.textNotFound = true;
501
+ info.failCause.lastError = "failed to locate element by text: " + text;
471
502
  return;
472
503
  }
473
504
  locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
@@ -484,6 +515,9 @@ class StableBrowser {
484
515
  // cssHref = true;
485
516
  // }
486
517
  let count = await locator.count();
518
+ if (count > 0 && !info.failCause.count) {
519
+ info.failCause.count = count;
520
+ }
487
521
  //info.log += "total elements found " + count + "\n";
488
522
  //let visibleCount = 0;
489
523
  let visibleLocator = null;
@@ -501,6 +535,8 @@ class StableBrowser {
501
535
  foundLocators.push(locator.nth(j));
502
536
  }
503
537
  else {
538
+ info.failCause.visible = visible;
539
+ info.failCause.enabled = enabled;
504
540
  if (!info.printMessages) {
505
541
  info.printMessages = {};
506
542
  }
@@ -512,6 +548,11 @@ class StableBrowser {
512
548
  }
513
549
  }
514
550
  async closeUnexpectedPopups(info, _params) {
551
+ if (!info) {
552
+ info = {};
553
+ info.failCause = {};
554
+ info.log = "";
555
+ }
515
556
  if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
516
557
  if (!info) {
517
558
  info = {};
@@ -552,7 +593,10 @@ class StableBrowser {
552
593
  }
553
594
  return { rerun: false };
554
595
  }
555
- async _locate(selectors, info, _params, timeout = 30000) {
596
+ async _locate(selectors, info, _params, timeout) {
597
+ if (!timeout) {
598
+ timeout = 30000;
599
+ }
556
600
  for (let i = 0; i < 3; i++) {
557
601
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
558
602
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -566,35 +610,46 @@ class StableBrowser {
566
610
  }
567
611
  throw new Error("unable to locate element " + JSON.stringify(selectors));
568
612
  }
569
- async _locate_internal(selectors, info, _params, timeout = 30000) {
570
- let highPriorityTimeout = 5000;
571
- let visibleOnlyTimeout = 6000;
572
- let startTime = performance.now();
573
- let locatorsCount = 0;
574
- //let arrayMode = Array.isArray(selectors);
613
+ async _findFrameScope(selectors, timeout = 30000, info) {
614
+ if (!info) {
615
+ info = {};
616
+ info.failCause = {};
617
+ info.log = "";
618
+ }
575
619
  let scope = this.page;
620
+ if (selectors.frame) {
621
+ return selectors.frame;
622
+ }
576
623
  if (selectors.iframe_src || selectors.frameLocators) {
577
- const findFrame = (frame, framescope) => {
624
+ const findFrame = async (frame, framescope) => {
578
625
  for (let i = 0; i < frame.selectors.length; i++) {
579
626
  let frameLocator = frame.selectors[i];
580
627
  if (frameLocator.css) {
581
- framescope = framescope.frameLocator(frameLocator.css);
628
+ let testframescope = framescope.frameLocator(frameLocator.css);
582
629
  if (frameLocator.index) {
583
- framescope = framescope.nth(frameLocator.index);
630
+ testframescope = framescope.nth(frameLocator.index);
631
+ }
632
+ try {
633
+ await testframescope.owner().evaluateHandle(() => true, null, {
634
+ timeout: 5000,
635
+ });
636
+ framescope = testframescope;
637
+ break;
638
+ }
639
+ catch (error) {
640
+ console.error("frame not found " + frameLocator.css);
584
641
  }
585
- break;
586
642
  }
587
643
  }
588
644
  if (frame.children) {
589
- return findFrame(frame.children, framescope);
645
+ return await findFrame(frame.children, framescope);
590
646
  }
591
647
  return framescope;
592
648
  };
593
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
594
649
  while (true) {
595
650
  let frameFound = false;
596
651
  if (selectors.nestFrmLoc) {
597
- scope = findFrame(selectors.nestFrmLoc, scope);
652
+ scope = await findFrame(selectors.nestFrmLoc, scope);
598
653
  frameFound = true;
599
654
  break;
600
655
  }
@@ -614,6 +669,8 @@ class StableBrowser {
614
669
  if (!scope) {
615
670
  info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
616
671
  if (performance.now() - startTime > timeout) {
672
+ info.failCause.iframeNotFound = true;
673
+ info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
617
674
  throw new Error("unable to locate iframe " + selectors.iframe_src);
618
675
  }
619
676
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -623,6 +680,30 @@ class StableBrowser {
623
680
  }
624
681
  }
625
682
  }
683
+ if (!scope) {
684
+ scope = this.page;
685
+ }
686
+ return scope;
687
+ }
688
+ async _getDocumentBody(selectors, timeout = 30000, info) {
689
+ let scope = await this._findFrameScope(selectors, timeout, info);
690
+ return scope.evaluate(() => {
691
+ var bodyContent = document.body.innerHTML;
692
+ return bodyContent;
693
+ });
694
+ }
695
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
696
+ if (!info) {
697
+ info = {};
698
+ info.failCause = {};
699
+ info.log = "";
700
+ }
701
+ let highPriorityTimeout = 5000;
702
+ let visibleOnlyTimeout = 6000;
703
+ let startTime = performance.now();
704
+ let locatorsCount = 0;
705
+ //let arrayMode = Array.isArray(selectors);
706
+ let scope = await this._findFrameScope(selectors, timeout, info);
626
707
  let selectorsLocators = null;
627
708
  selectorsLocators = selectors.locators;
628
709
  // group selectors by priority
@@ -724,6 +805,8 @@ class StableBrowser {
724
805
  }
725
806
  this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
726
807
  info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
808
+ info.failCause.locatorNotFound = true;
809
+ info.failCause.lastError = "failed to locate unique element";
727
810
  throw new Error("failed to locate first element no elements found, " + info.log);
728
811
  }
729
812
  async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
@@ -755,89 +838,129 @@ class StableBrowser {
755
838
  });
756
839
  result.locatorIndex = i;
757
840
  }
841
+ if (foundLocators.length > 1) {
842
+ info.failCause.foundMultiple = true;
843
+ }
758
844
  }
759
845
  return result;
760
846
  }
761
- async click(selectors, _params, options = {}, world = null) {
762
- this._validateSelectors(selectors);
847
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
763
848
  const startTime = Date.now();
764
- if (options && options.context) {
765
- selectors.locators[0].text = options.context;
849
+ let timeout = 30000;
850
+ if (options && options.timeout) {
851
+ timeout = options.timeout;
766
852
  }
767
- const info = {};
768
- info.log = "***** click on " + selectors.element_name + " *****\n";
769
- info.operation = "click";
770
- info.selectors = selectors;
771
- let error = null;
772
- let screenshotId = null;
773
- let screenshotPath = null;
853
+ while (true) {
854
+ try {
855
+ const result = await locate_element(this.context, elementDescription, "click");
856
+ if (result?.elementNumber >= 0) {
857
+ const selectors = {
858
+ frame: result?.frame,
859
+ locators: [
860
+ {
861
+ css: result?.css,
862
+ },
863
+ ],
864
+ };
865
+ await this.click(selectors, _params, options, world);
866
+ return;
867
+ }
868
+ }
869
+ catch (e) {
870
+ if (performance.now() - startTime > timeout) {
871
+ // throw e;
872
+ await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
873
+ }
874
+ }
875
+ await new Promise((resolve) => setTimeout(resolve, 3000));
876
+ }
877
+ }
878
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
879
+ const startTime = Date.now();
880
+ let timeout = 30000;
881
+ if (options && options.timeout) {
882
+ timeout = options.timeout;
883
+ }
884
+ while (true) {
885
+ try {
886
+ const result = await locate_element(this.context, elementDescription, "fill", value);
887
+ if (result?.elementNumber >= 0) {
888
+ const selectors = {
889
+ frame: result?.frame,
890
+ locators: [
891
+ {
892
+ css: result?.css,
893
+ },
894
+ ],
895
+ };
896
+ await this.clickType(selectors, value, false, _params, options, world);
897
+ return;
898
+ }
899
+ }
900
+ catch (e) {
901
+ if (performance.now() - startTime > timeout) {
902
+ // throw e;
903
+ await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
904
+ }
905
+ }
906
+ await new Promise((resolve) => setTimeout(resolve, 3000));
907
+ }
908
+ }
909
+ async click(selectors, _params, options = {}, world = null) {
910
+ const state = {
911
+ selectors,
912
+ _params,
913
+ options,
914
+ world,
915
+ text: "Click element",
916
+ type: Types.CLICK,
917
+ operation: "click",
918
+ log: "***** click on " + selectors.element_name + " *****\n",
919
+ };
774
920
  try {
775
- let element = await this._locate(selectors, info, _params);
776
- await this.scrollIfNeeded(element, info);
777
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
921
+ await _preCommand(state, this);
922
+ if (state.options && state.options.context) {
923
+ state.selectors.locators[0].text = state.options.context;
924
+ }
778
925
  try {
779
- await this._highlightElements(element);
780
- await element.click();
926
+ await state.element.click();
781
927
  await new Promise((resolve) => setTimeout(resolve, 1000));
782
928
  }
783
929
  catch (e) {
784
930
  // await this.closeUnexpectedPopups();
785
- info.log += "click failed, will try again" + "\n";
786
- element = await this._locate(selectors, info, _params);
787
- await element.dispatchEvent("click");
931
+ state.element = await this._locate(selectors, state.info, _params);
932
+ await state.element.dispatchEvent("click");
788
933
  await new Promise((resolve) => setTimeout(resolve, 1000));
789
934
  }
790
935
  await this.waitForPageLoad();
791
- return info;
936
+ return state.info;
792
937
  }
793
938
  catch (e) {
794
- this.logger.error("click failed " + info.log);
795
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
796
- info.screenshotPath = screenshotPath;
797
- Object.assign(e, { info: info });
798
- error = e;
799
- throw e;
939
+ await _commandError(state, e, this);
800
940
  }
801
941
  finally {
802
- const endTime = Date.now();
803
- this._reportToWorld(world, {
804
- element_name: selectors.element_name,
805
- type: Types.CLICK,
806
- text: `Click element`,
807
- screenshotId,
808
- result: error
809
- ? {
810
- status: "FAILED",
811
- startTime,
812
- endTime,
813
- message: error === null || error === void 0 ? void 0 : error.message,
814
- }
815
- : {
816
- status: "PASSED",
817
- startTime,
818
- endTime,
819
- },
820
- info: info,
821
- });
942
+ _commandFinally(state, this);
822
943
  }
823
944
  }
824
945
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
825
- this._validateSelectors(selectors);
826
- const startTime = Date.now();
827
- const info = {};
828
- info.log = "";
829
- info.operation = "setCheck";
830
- info.checked = checked;
831
- info.selectors = selectors;
832
- let error = null;
833
- let screenshotId = null;
834
- let screenshotPath = null;
946
+ const state = {
947
+ selectors,
948
+ _params,
949
+ options,
950
+ world,
951
+ type: checked ? Types.CHECK : Types.UNCHECK,
952
+ text: checked ? `Check element` : `Uncheck element`,
953
+ operation: "setCheck",
954
+ log: "***** check " + selectors.element_name + " *****\n",
955
+ };
835
956
  try {
836
- let element = await this._locate(selectors, info, _params);
837
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
957
+ await _preCommand(state, this);
958
+ state.info.checked = checked;
959
+ // let element = await this._locate(selectors, info, _params);
960
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
838
961
  try {
839
- await this._highlightElements(element);
840
- await element.setChecked(checked);
962
+ // await this._highlightElements(element);
963
+ await state.element.setChecked(checked);
841
964
  await new Promise((resolve) => setTimeout(resolve, 1000));
842
965
  }
843
966
  catch (e) {
@@ -846,179 +969,108 @@ class StableBrowser {
846
969
  }
847
970
  else {
848
971
  //await this.closeUnexpectedPopups();
849
- info.log += "setCheck failed, will try again" + "\n";
850
- element = await this._locate(selectors, info, _params);
851
- await element.setChecked(checked, { timeout: 5000, force: true });
972
+ state.info.log += "setCheck failed, will try again" + "\n";
973
+ state.element = await this._locate(selectors, state.info, _params);
974
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
852
975
  await new Promise((resolve) => setTimeout(resolve, 1000));
853
976
  }
854
977
  }
855
978
  await this.waitForPageLoad();
856
- return info;
979
+ return state.info;
857
980
  }
858
981
  catch (e) {
859
- this.logger.error("setCheck failed " + info.log);
860
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
861
- info.screenshotPath = screenshotPath;
862
- Object.assign(e, { info: info });
863
- error = e;
864
- throw e;
982
+ await _commandError(state, e, this);
865
983
  }
866
984
  finally {
867
- const endTime = Date.now();
868
- this._reportToWorld(world, {
869
- element_name: selectors.element_name,
870
- type: checked ? Types.CHECK : Types.UNCHECK,
871
- text: checked ? `Check element` : `Uncheck element`,
872
- screenshotId,
873
- result: error
874
- ? {
875
- status: "FAILED",
876
- startTime,
877
- endTime,
878
- message: error === null || error === void 0 ? void 0 : error.message,
879
- }
880
- : {
881
- status: "PASSED",
882
- startTime,
883
- endTime,
884
- },
885
- info: info,
886
- });
985
+ _commandFinally(state, this);
887
986
  }
888
987
  }
889
988
  async hover(selectors, _params, options = {}, world = null) {
890
- this._validateSelectors(selectors);
891
- const startTime = Date.now();
892
- const info = {};
893
- info.log = "";
894
- info.operation = "hover";
895
- info.selectors = selectors;
896
- let error = null;
897
- let screenshotId = null;
898
- let screenshotPath = null;
989
+ const state = {
990
+ selectors,
991
+ _params,
992
+ options,
993
+ world,
994
+ type: Types.HOVER,
995
+ text: `Hover element`,
996
+ operation: "hover",
997
+ log: "***** hover " + selectors.element_name + " *****\n",
998
+ };
899
999
  try {
900
- let element = await this._locate(selectors, info, _params);
901
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1000
+ await _preCommand(state, this);
902
1001
  try {
903
- await this._highlightElements(element);
904
- await element.hover();
1002
+ await state.element.hover();
905
1003
  await new Promise((resolve) => setTimeout(resolve, 1000));
906
1004
  }
907
1005
  catch (e) {
908
1006
  //await this.closeUnexpectedPopups();
909
- info.log += "hover failed, will try again" + "\n";
910
- element = await this._locate(selectors, info, _params);
911
- await element.hover({ timeout: 10000 });
1007
+ state.info.log += "hover failed, will try again" + "\n";
1008
+ state.element = await this._locate(selectors, state.info, _params);
1009
+ await state.element.hover({ timeout: 10000 });
912
1010
  await new Promise((resolve) => setTimeout(resolve, 1000));
913
1011
  }
914
1012
  await this.waitForPageLoad();
915
- return info;
1013
+ return state.info;
916
1014
  }
917
1015
  catch (e) {
918
- this.logger.error("hover failed " + info.log);
919
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
920
- info.screenshotPath = screenshotPath;
921
- Object.assign(e, { info: info });
922
- error = e;
923
- throw e;
1016
+ await _commandError(state, e, this);
924
1017
  }
925
1018
  finally {
926
- const endTime = Date.now();
927
- this._reportToWorld(world, {
928
- element_name: selectors.element_name,
929
- type: Types.HOVER,
930
- text: `Hover element`,
931
- screenshotId,
932
- result: error
933
- ? {
934
- status: "FAILED",
935
- startTime,
936
- endTime,
937
- message: error === null || error === void 0 ? void 0 : error.message,
938
- }
939
- : {
940
- status: "PASSED",
941
- startTime,
942
- endTime,
943
- },
944
- info: info,
945
- });
1019
+ _commandFinally(state, this);
946
1020
  }
947
1021
  }
948
1022
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
949
- this._validateSelectors(selectors);
950
1023
  if (!values) {
951
1024
  throw new Error("values is null");
952
1025
  }
953
- const startTime = Date.now();
954
- let error = null;
955
- let screenshotId = null;
956
- let screenshotPath = null;
957
- const info = {};
958
- info.log = "";
959
- info.operation = "selectOptions";
960
- info.selectors = selectors;
1026
+ const state = {
1027
+ selectors,
1028
+ _params,
1029
+ options,
1030
+ world,
1031
+ value: values.toString(),
1032
+ type: Types.SELECT,
1033
+ text: `Select option: ${values}`,
1034
+ operation: "selectOption",
1035
+ log: "***** select option " + selectors.element_name + " *****\n",
1036
+ };
961
1037
  try {
962
- let element = await this._locate(selectors, info, _params);
963
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1038
+ await _preCommand(state, this);
964
1039
  try {
965
- await this._highlightElements(element);
966
- await element.selectOption(values);
1040
+ await state.element.selectOption(values);
967
1041
  }
968
1042
  catch (e) {
969
1043
  //await this.closeUnexpectedPopups();
970
- info.log += "selectOption failed, will try force" + "\n";
971
- await element.selectOption(values, { timeout: 10000, force: true });
1044
+ state.info.log += "selectOption failed, will try force" + "\n";
1045
+ await state.element.selectOption(values, { timeout: 10000, force: true });
972
1046
  }
973
1047
  await this.waitForPageLoad();
974
- return info;
1048
+ return state.info;
975
1049
  }
976
1050
  catch (e) {
977
- this.logger.error("selectOption failed " + info.log);
978
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
979
- info.screenshotPath = screenshotPath;
980
- Object.assign(e, { info: info });
981
- this.logger.info("click failed, will try next selector");
982
- error = e;
983
- throw e;
1051
+ await _commandError(state, e, this);
984
1052
  }
985
1053
  finally {
986
- const endTime = Date.now();
987
- this._reportToWorld(world, {
988
- element_name: selectors.element_name,
989
- type: Types.SELECT,
990
- text: `Select option: ${values}`,
991
- value: values.toString(),
992
- screenshotId,
993
- result: error
994
- ? {
995
- status: "FAILED",
996
- startTime,
997
- endTime,
998
- message: error === null || error === void 0 ? void 0 : error.message,
999
- }
1000
- : {
1001
- status: "PASSED",
1002
- startTime,
1003
- endTime,
1004
- },
1005
- info: info,
1006
- });
1054
+ _commandFinally(state, this);
1007
1055
  }
1008
1056
  }
1009
1057
  async type(_value, _params = null, options = {}, world = null) {
1010
- const startTime = Date.now();
1011
- let error = null;
1012
- let screenshotId = null;
1013
- let screenshotPath = null;
1014
- const info = {};
1015
- info.log = "";
1016
- info.operation = "type";
1017
- _value = this._fixUsingParams(_value, _params);
1018
- info.value = _value;
1058
+ const state = {
1059
+ value: _value,
1060
+ _params,
1061
+ options,
1062
+ world,
1063
+ locate: false,
1064
+ scroll: false,
1065
+ highlight: false,
1066
+ type: Types.TYPE_PRESS,
1067
+ text: `Type value: ${_value}`,
1068
+ operation: "type",
1069
+ log: "",
1070
+ };
1019
1071
  try {
1020
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1021
- const valueSegment = _value.split("&&");
1072
+ await _preCommand(state, this);
1073
+ const valueSegment = state.value.split("&&");
1022
1074
  for (let i = 0; i < valueSegment.length; i++) {
1023
1075
  if (i > 0) {
1024
1076
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1038,134 +1090,76 @@ class StableBrowser {
1038
1090
  await this.page.keyboard.type(value);
1039
1091
  }
1040
1092
  }
1041
- return info;
1093
+ return state.info;
1042
1094
  }
1043
1095
  catch (e) {
1044
- //await this.closeUnexpectedPopups();
1045
- this.logger.error("type failed " + info.log);
1046
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1047
- info.screenshotPath = screenshotPath;
1048
- Object.assign(e, { info: info });
1049
- error = e;
1050
- throw e;
1096
+ await _commandError(state, e, this);
1051
1097
  }
1052
1098
  finally {
1053
- const endTime = Date.now();
1054
- this._reportToWorld(world, {
1055
- type: Types.TYPE_PRESS,
1056
- screenshotId,
1057
- value: _value,
1058
- text: `type value: ${_value}`,
1059
- result: error
1060
- ? {
1061
- status: "FAILED",
1062
- startTime,
1063
- endTime,
1064
- message: error === null || error === void 0 ? void 0 : error.message,
1065
- }
1066
- : {
1067
- status: "PASSED",
1068
- startTime,
1069
- endTime,
1070
- },
1071
- info: info,
1072
- });
1099
+ _commandFinally(state, this);
1073
1100
  }
1074
1101
  }
1075
1102
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1076
- // set input value for non fillable inputs like date, time, range, color, etc.
1077
- this._validateSelectors(selectors);
1078
- const startTime = Date.now();
1079
- const info = {};
1080
- info.log = "***** set input value " + selectors.element_name + " *****\n";
1081
- info.operation = "setInputValue";
1082
- info.selectors = selectors;
1083
- value = this._fixUsingParams(value, _params);
1084
- info.value = value;
1085
- let error = null;
1086
- let screenshotId = null;
1087
- let screenshotPath = null;
1103
+ const state = {
1104
+ selectors,
1105
+ _params,
1106
+ value,
1107
+ options,
1108
+ world,
1109
+ type: Types.SET_INPUT,
1110
+ text: `Set input value`,
1111
+ operation: "setInputValue",
1112
+ log: "***** set input value " + selectors.element_name + " *****\n",
1113
+ };
1088
1114
  try {
1089
- value = await this._replaceWithLocalData(value, this);
1090
- let element = await this._locate(selectors, info, _params);
1091
- await this.scrollIfNeeded(element, info);
1092
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1093
- await this._highlightElements(element);
1115
+ await _preCommand(state, this);
1116
+ let value = await this._replaceWithLocalData(state.value, this);
1094
1117
  try {
1095
- await element.evaluateHandle((el, value) => {
1118
+ await state.element.evaluateHandle((el, value) => {
1096
1119
  el.value = value;
1097
1120
  }, value);
1098
1121
  }
1099
1122
  catch (error) {
1100
1123
  this.logger.error("setInputValue failed, will try again");
1101
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1102
- info.screenshotPath = screenshotPath;
1103
- Object.assign(error, { info: info });
1104
- await element.evaluateHandle((el, value) => {
1124
+ await _screenshot(state, this);
1125
+ Object.assign(error, { info: state.info });
1126
+ await state.element.evaluateHandle((el, value) => {
1105
1127
  el.value = value;
1106
1128
  });
1107
1129
  }
1108
1130
  }
1109
1131
  catch (e) {
1110
- this.logger.error("setInputValue failed " + info.log);
1111
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1112
- info.screenshotPath = screenshotPath;
1113
- Object.assign(e, { info: info });
1114
- error = e;
1115
- throw e;
1132
+ await _commandError(state, e, this);
1116
1133
  }
1117
1134
  finally {
1118
- const endTime = Date.now();
1119
- this._reportToWorld(world, {
1120
- element_name: selectors.element_name,
1121
- type: Types.SET_INPUT,
1122
- text: `Set input value`,
1123
- value: value,
1124
- screenshotId,
1125
- result: error
1126
- ? {
1127
- status: "FAILED",
1128
- startTime,
1129
- endTime,
1130
- message: error === null || error === void 0 ? void 0 : error.message,
1131
- }
1132
- : {
1133
- status: "PASSED",
1134
- startTime,
1135
- endTime,
1136
- },
1137
- info: info,
1138
- });
1135
+ _commandFinally(state, this);
1139
1136
  }
1140
1137
  }
1141
1138
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1142
- this._validateSelectors(selectors);
1143
- const startTime = Date.now();
1144
- let error = null;
1145
- let screenshotId = null;
1146
- let screenshotPath = null;
1147
- const info = {};
1148
- info.log = "";
1149
- info.operation = Types.SET_DATE_TIME;
1150
- info.selectors = selectors;
1151
- info.value = value;
1139
+ const state = {
1140
+ selectors,
1141
+ _params,
1142
+ value: await this._replaceWithLocalData(value, this),
1143
+ options,
1144
+ world,
1145
+ type: Types.SET_DATE_TIME,
1146
+ text: `Set date time value: ${value}`,
1147
+ operation: "setDateTime",
1148
+ log: "***** set date time value " + selectors.element_name + " *****\n",
1149
+ throwError: false,
1150
+ };
1152
1151
  try {
1153
- value = await this._replaceWithLocalData(value, this);
1154
- let element = await this._locate(selectors, info, _params);
1155
- //insert red border around the element
1156
- await this.scrollIfNeeded(element, info);
1157
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1158
- await this._highlightElements(element);
1152
+ await _preCommand(state, this);
1159
1153
  try {
1160
- await element.click();
1154
+ await state.element.click();
1161
1155
  await new Promise((resolve) => setTimeout(resolve, 500));
1162
1156
  if (format) {
1163
- value = dayjs(value).format(format);
1164
- await element.fill(value);
1157
+ state.value = dayjs(state.value).format(format);
1158
+ await state.element.fill(state.value);
1165
1159
  }
1166
1160
  else {
1167
- const dateTimeValue = await getDateTimeValue({ value, element });
1168
- await element.evaluateHandle((el, dateTimeValue) => {
1161
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1162
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1169
1163
  el.value = ""; // clear input
1170
1164
  el.value = dateTimeValue;
1171
1165
  }, dateTimeValue);
@@ -1178,20 +1172,19 @@ class StableBrowser {
1178
1172
  }
1179
1173
  catch (err) {
1180
1174
  //await this.closeUnexpectedPopups();
1181
- this.logger.error("setting date time input failed " + JSON.stringify(info));
1175
+ this.logger.error("setting date time input failed " + JSON.stringify(state.info));
1182
1176
  this.logger.info("Trying again");
1183
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1184
- info.screenshotPath = screenshotPath;
1185
- Object.assign(err, { info: info });
1177
+ await _screenshot(state, this);
1178
+ Object.assign(err, { info: state.info });
1186
1179
  await element.click();
1187
1180
  await new Promise((resolve) => setTimeout(resolve, 500));
1188
1181
  if (format) {
1189
- value = dayjs(value).format(format);
1190
- await element.fill(value);
1182
+ state.value = dayjs(state.value).format(format);
1183
+ await state.element.fill(state.value);
1191
1184
  }
1192
1185
  else {
1193
- const dateTimeValue = await getDateTimeValue({ value, element });
1194
- await element.evaluateHandle((el, dateTimeValue) => {
1186
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1187
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1195
1188
  el.value = ""; // clear input
1196
1189
  el.value = dateTimeValue;
1197
1190
  }, dateTimeValue);
@@ -1204,61 +1197,39 @@ class StableBrowser {
1204
1197
  }
1205
1198
  }
1206
1199
  catch (e) {
1207
- error = e;
1208
- throw e;
1200
+ await _commandError(state, e, this);
1209
1201
  }
1210
1202
  finally {
1211
- const endTime = Date.now();
1212
- this._reportToWorld(world, {
1213
- element_name: selectors.element_name,
1214
- type: Types.SET_DATE_TIME,
1215
- screenshotId,
1216
- value: value,
1217
- text: `setDateTime input with value: ${value}`,
1218
- result: error
1219
- ? {
1220
- status: "FAILED",
1221
- startTime,
1222
- endTime,
1223
- message: error === null || error === void 0 ? void 0 : error.message,
1224
- }
1225
- : {
1226
- status: "PASSED",
1227
- startTime,
1228
- endTime,
1229
- },
1230
- info: info,
1231
- });
1203
+ _commandFinally(state, this);
1232
1204
  }
1233
1205
  }
1234
1206
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1235
1207
  _value = unEscapeString(_value);
1236
- this._validateSelectors(selectors);
1237
- const startTime = Date.now();
1238
- let error = null;
1239
- let screenshotId = null;
1240
- let screenshotPath = null;
1241
- const info = {};
1242
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1243
- info.operation = "clickType";
1244
- info.selectors = selectors;
1245
1208
  const newValue = await this._replaceWithLocalData(_value, world);
1209
+ const state = {
1210
+ selectors,
1211
+ _params,
1212
+ value: newValue,
1213
+ originalValue: _value,
1214
+ options,
1215
+ world,
1216
+ type: Types.FILL,
1217
+ text: `Click type input with value: ${_value}`,
1218
+ operation: "clickType",
1219
+ log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
1220
+ };
1246
1221
  if (newValue !== _value) {
1247
1222
  //this.logger.info(_value + "=" + newValue);
1248
1223
  _value = newValue;
1249
1224
  }
1250
- info.value = _value;
1251
1225
  try {
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);
1226
+ await _preCommand(state, this);
1227
+ state.info.value = _value;
1257
1228
  if (options === null || options === undefined || !options.press) {
1258
1229
  try {
1259
- let currentValue = await element.inputValue();
1230
+ let currentValue = await state.element.inputValue();
1260
1231
  if (currentValue) {
1261
- await element.fill("");
1232
+ await state.element.fill("");
1262
1233
  }
1263
1234
  }
1264
1235
  catch (e) {
@@ -1267,22 +1238,22 @@ class StableBrowser {
1267
1238
  }
1268
1239
  if (options === null || options === undefined || options.press) {
1269
1240
  try {
1270
- await element.click({ timeout: 5000 });
1241
+ await state.element.click({ timeout: 5000 });
1271
1242
  }
1272
1243
  catch (e) {
1273
- await element.dispatchEvent("click");
1244
+ await state.element.dispatchEvent("click");
1274
1245
  }
1275
1246
  }
1276
1247
  else {
1277
1248
  try {
1278
- await element.focus();
1249
+ await state.element.focus();
1279
1250
  }
1280
1251
  catch (e) {
1281
- await element.dispatchEvent("focus");
1252
+ await state.element.dispatchEvent("focus");
1282
1253
  }
1283
1254
  }
1284
1255
  await new Promise((resolve) => setTimeout(resolve, 500));
1285
- const valueSegment = _value.split("&&");
1256
+ const valueSegment = state.value.split("&&");
1286
1257
  for (let i = 0; i < valueSegment.length; i++) {
1287
1258
  if (i > 0) {
1288
1259
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1302,13 +1273,14 @@ class StableBrowser {
1302
1273
  await new Promise((resolve) => setTimeout(resolve, 500));
1303
1274
  }
1304
1275
  }
1276
+ await _screenshot(state, this);
1305
1277
  if (enter === true) {
1306
1278
  await new Promise((resolve) => setTimeout(resolve, 2000));
1307
1279
  await this.page.keyboard.press("Enter");
1308
1280
  await this.waitForPageLoad();
1309
1281
  }
1310
1282
  else if (enter === false) {
1311
- await element.dispatchEvent("change");
1283
+ await state.element.dispatchEvent("change");
1312
1284
  //await this.page.keyboard.press("Tab");
1313
1285
  }
1314
1286
  else {
@@ -1317,104 +1289,50 @@ class StableBrowser {
1317
1289
  await this.waitForPageLoad();
1318
1290
  }
1319
1291
  }
1320
- return info;
1292
+ return state.info;
1321
1293
  }
1322
1294
  catch (e) {
1323
- //await this.closeUnexpectedPopups();
1324
- this.logger.error("fill failed " + JSON.stringify(info));
1325
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1326
- info.screenshotPath = screenshotPath;
1327
- Object.assign(e, { info: info });
1328
- error = e;
1329
- throw e;
1295
+ await _commandError(state, e, this);
1330
1296
  }
1331
1297
  finally {
1332
- const endTime = Date.now();
1333
- this._reportToWorld(world, {
1334
- element_name: selectors.element_name,
1335
- type: Types.FILL,
1336
- screenshotId,
1337
- value: _value,
1338
- text: `clickType input with value: ${_value}`,
1339
- result: error
1340
- ? {
1341
- status: "FAILED",
1342
- startTime,
1343
- endTime,
1344
- message: error === null || error === void 0 ? void 0 : error.message,
1345
- }
1346
- : {
1347
- status: "PASSED",
1348
- startTime,
1349
- endTime,
1350
- },
1351
- info: info,
1352
- });
1298
+ _commandFinally(state, this);
1353
1299
  }
1354
1300
  }
1355
1301
  async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1356
- this._validateSelectors(selectors);
1357
- value = unEscapeString(value);
1358
- const startTime = Date.now();
1359
- let error = null;
1360
- let screenshotId = null;
1361
- let screenshotPath = null;
1362
- const info = {};
1363
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1364
- info.operation = "fill";
1365
- info.selectors = selectors;
1366
- info.value = value;
1302
+ const state = {
1303
+ selectors,
1304
+ _params,
1305
+ value: unEscapeString(value),
1306
+ options,
1307
+ world,
1308
+ type: Types.FILL,
1309
+ text: `Fill input with value: ${value}`,
1310
+ operation: "fill",
1311
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1312
+ };
1367
1313
  try {
1368
- let element = await this._locate(selectors, info, _params);
1369
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1370
- await this._highlightElements(element);
1371
- await element.fill(value);
1372
- await element.dispatchEvent("change");
1314
+ await _preCommand(state, this);
1315
+ await state.element.fill(value);
1316
+ await state.element.dispatchEvent("change");
1373
1317
  if (enter) {
1374
1318
  await new Promise((resolve) => setTimeout(resolve, 2000));
1375
- await this.page.keyboard.press("Enter");
1376
- }
1377
- await this.waitForPageLoad();
1378
- return info;
1379
- }
1380
- catch (e) {
1381
- //await this.closeUnexpectedPopups();
1382
- this.logger.error("fill failed " + info.log);
1383
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1384
- info.screenshotPath = screenshotPath;
1385
- Object.assign(e, { info: info });
1386
- error = e;
1387
- throw e;
1388
- }
1389
- finally {
1390
- const endTime = Date.now();
1391
- this._reportToWorld(world, {
1392
- element_name: selectors.element_name,
1393
- type: Types.FILL,
1394
- screenshotId,
1395
- value,
1396
- text: `Fill input with value: ${value}`,
1397
- result: error
1398
- ? {
1399
- status: "FAILED",
1400
- startTime,
1401
- endTime,
1402
- message: error === null || error === void 0 ? void 0 : error.message,
1403
- }
1404
- : {
1405
- status: "PASSED",
1406
- startTime,
1407
- endTime,
1408
- },
1409
- info: info,
1410
- });
1319
+ await this.page.keyboard.press("Enter");
1320
+ }
1321
+ await this.waitForPageLoad();
1322
+ return state.info;
1323
+ }
1324
+ catch (e) {
1325
+ await _commandError(state, e, this);
1326
+ }
1327
+ finally {
1328
+ _commandFinally(state, this);
1411
1329
  }
1412
1330
  }
1413
1331
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1414
1332
  return await this._getText(selectors, 0, _params, options, info, world);
1415
1333
  }
1416
1334
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1417
- this._validateSelectors(selectors);
1335
+ _validateSelectors(selectors);
1418
1336
  let screenshotId = null;
1419
1337
  let screenshotPath = null;
1420
1338
  if (!info.log) {
@@ -1458,166 +1376,124 @@ class StableBrowser {
1458
1376
  }
1459
1377
  }
1460
1378
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1461
- var _a;
1462
- this._validateSelectors(selectors);
1463
1379
  if (!pattern) {
1464
1380
  throw new Error("pattern is null");
1465
1381
  }
1466
1382
  if (!text) {
1467
1383
  throw new Error("text is null");
1468
1384
  }
1385
+ const state = {
1386
+ selectors,
1387
+ _params,
1388
+ pattern,
1389
+ value: pattern,
1390
+ options,
1391
+ world,
1392
+ locate: false,
1393
+ scroll: false,
1394
+ screenshot: false,
1395
+ highlight: false,
1396
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1397
+ text: `Verify element contains pattern: ${pattern}`,
1398
+ operation: "containsPattern",
1399
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1400
+ };
1469
1401
  const newValue = await this._replaceWithLocalData(text, world);
1470
1402
  if (newValue !== text) {
1471
1403
  this.logger.info(text + "=" + newValue);
1472
1404
  text = newValue;
1473
1405
  }
1474
- const startTime = Date.now();
1475
- let error = null;
1476
- let screenshotId = null;
1477
- let screenshotPath = null;
1478
- const info = {};
1479
- info.log =
1480
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1481
- info.operation = "containsPattern";
1482
- info.selectors = selectors;
1483
- info.value = text;
1484
- info.pattern = pattern;
1485
1406
  let foundObj = null;
1486
1407
  try {
1487
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1408
+ await _preCommand(state, this);
1409
+ state.info.pattern = pattern;
1410
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1488
1411
  if (foundObj && foundObj.element) {
1489
- await this.scrollIfNeeded(foundObj.element, info);
1412
+ await this.scrollIfNeeded(foundObj.element, state.info);
1490
1413
  }
1491
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1414
+ await _screenshot(state, this);
1492
1415
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1493
1416
  pattern = pattern.replace("{text}", escapedText);
1494
1417
  let regex = new RegExp(pattern, "im");
1495
- 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))) {
1496
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1418
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1419
+ state.info.foundText = foundObj?.text;
1497
1420
  throw new Error("element doesn't contain text " + text);
1498
1421
  }
1499
- return info;
1422
+ return state.info;
1500
1423
  }
1501
1424
  catch (e) {
1502
- //await this.closeUnexpectedPopups();
1503
- this.logger.error("verify element contains text failed " + info.log);
1504
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1505
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1506
- info.screenshotPath = screenshotPath;
1507
- Object.assign(e, { info: info });
1508
- error = e;
1509
- throw e;
1425
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1426
+ await _commandError(state, e, this);
1510
1427
  }
1511
1428
  finally {
1512
- const endTime = Date.now();
1513
- this._reportToWorld(world, {
1514
- element_name: selectors.element_name,
1515
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1516
- value: pattern,
1517
- text: `Verify element contains pattern: ${pattern}`,
1518
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1519
- result: error
1520
- ? {
1521
- status: "FAILED",
1522
- startTime,
1523
- endTime,
1524
- message: error === null || error === void 0 ? void 0 : error.message,
1525
- }
1526
- : {
1527
- status: "PASSED",
1528
- startTime,
1529
- endTime,
1530
- },
1531
- info: info,
1532
- });
1429
+ _commandFinally(state, this);
1533
1430
  }
1534
1431
  }
1535
1432
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1536
- var _a, _b, _c;
1537
- this._validateSelectors(selectors);
1538
- text = unEscapeString(text);
1433
+ const state = {
1434
+ selectors,
1435
+ _params,
1436
+ value: text,
1437
+ options,
1438
+ world,
1439
+ locate: false,
1440
+ scroll: false,
1441
+ screenshot: false,
1442
+ highlight: false,
1443
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1444
+ text: `Verify element contains text: ${text}`,
1445
+ operation: "containsText",
1446
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1447
+ };
1539
1448
  if (!text) {
1540
1449
  throw new Error("text is null");
1541
1450
  }
1542
- const startTime = Date.now();
1543
- let error = null;
1544
- let screenshotId = null;
1545
- let screenshotPath = null;
1546
- const info = {};
1547
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1548
- info.operation = "containsText";
1549
- info.selectors = selectors;
1451
+ text = unEscapeString(text);
1550
1452
  const newValue = await this._replaceWithLocalData(text, world);
1551
1453
  if (newValue !== text) {
1552
1454
  this.logger.info(text + "=" + newValue);
1553
1455
  text = newValue;
1554
1456
  }
1555
- info.value = text;
1556
1457
  let foundObj = null;
1557
1458
  try {
1558
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1459
+ await _preCommand(state, this);
1460
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1559
1461
  if (foundObj && foundObj.element) {
1560
- await this.scrollIfNeeded(foundObj.element, info);
1462
+ await this.scrollIfNeeded(foundObj.element, state.info);
1561
1463
  }
1562
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1464
+ await _screenshot(state, this);
1563
1465
  const dateAlternatives = findDateAlternatives(text);
1564
1466
  const numberAlternatives = findNumberAlternatives(text);
1565
1467
  if (dateAlternatives.date) {
1566
1468
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1567
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1568
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1569
- return info;
1469
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1470
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1471
+ return state.info;
1570
1472
  }
1571
1473
  }
1572
1474
  throw new Error("element doesn't contain text " + text);
1573
1475
  }
1574
1476
  else if (numberAlternatives.number) {
1575
1477
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1576
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1577
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1578
- return info;
1478
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1479
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1480
+ return state.info;
1579
1481
  }
1580
1482
  }
1581
1483
  throw new Error("element doesn't contain text " + text);
1582
1484
  }
1583
- 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))) {
1584
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1585
- info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1485
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1486
+ state.info.foundText = foundObj?.text;
1487
+ state.info.value = foundObj?.value;
1586
1488
  throw new Error("element doesn't contain text " + text);
1587
1489
  }
1588
- return info;
1490
+ return state.info;
1589
1491
  }
1590
1492
  catch (e) {
1591
- //await this.closeUnexpectedPopups();
1592
- this.logger.error("verify element contains text failed " + info.log);
1593
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1594
- info.screenshotPath = screenshotPath;
1595
- Object.assign(e, { info: info });
1596
- error = e;
1597
- throw e;
1493
+ await _commandError(state, e, this);
1598
1494
  }
1599
1495
  finally {
1600
- const endTime = Date.now();
1601
- this._reportToWorld(world, {
1602
- element_name: selectors.element_name,
1603
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1604
- text: `Verify element contains text: ${text}`,
1605
- value: text,
1606
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1607
- result: error
1608
- ? {
1609
- status: "FAILED",
1610
- startTime,
1611
- endTime,
1612
- message: error === null || error === void 0 ? void 0 : error.message,
1613
- }
1614
- : {
1615
- status: "PASSED",
1616
- startTime,
1617
- endTime,
1618
- },
1619
- info: info,
1620
- });
1496
+ _commandFinally(state, this);
1621
1497
  }
1622
1498
  }
1623
1499
  _getDataFile(world = null) {
@@ -1846,7 +1722,6 @@ class StableBrowser {
1846
1722
  }
1847
1723
  async takeScreenshot(screenshotPath) {
1848
1724
  const playContext = this.context.playContext;
1849
- const client = await playContext.newCDPSession(this.page);
1850
1725
  // Using CDP to capture the screenshot
1851
1726
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1852
1727
  document.body.scrollWidth,
@@ -1856,163 +1731,105 @@ class StableBrowser {
1856
1731
  document.body.clientWidth,
1857
1732
  document.documentElement.clientWidth,
1858
1733
  ])));
1859
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1860
- document.body.scrollHeight,
1861
- document.documentElement.scrollHeight,
1862
- document.body.offsetHeight,
1863
- document.documentElement.offsetHeight,
1864
- document.body.clientHeight,
1865
- document.documentElement.clientHeight,
1866
- ])));
1867
- const { data } = await client.send("Page.captureScreenshot", {
1868
- format: "png",
1869
- // clip: {
1870
- // x: 0,
1871
- // y: 0,
1872
- // width: viewportWidth,
1873
- // height: viewportHeight,
1874
- // scale: 1,
1875
- // },
1876
- });
1877
- if (!screenshotPath) {
1878
- return data;
1734
+ let screenshotBuffer = null;
1735
+ if (this.context.browserName === "chromium") {
1736
+ const client = await playContext.newCDPSession(this.page);
1737
+ const { data } = await client.send("Page.captureScreenshot", {
1738
+ format: "png",
1739
+ // clip: {
1740
+ // x: 0,
1741
+ // y: 0,
1742
+ // width: viewportWidth,
1743
+ // height: viewportHeight,
1744
+ // scale: 1,
1745
+ // },
1746
+ });
1747
+ await client.detach();
1748
+ if (!screenshotPath) {
1749
+ return data;
1750
+ }
1751
+ screenshotBuffer = Buffer.from(data, "base64");
1752
+ }
1753
+ else {
1754
+ screenshotBuffer = await this.page.screenshot();
1879
1755
  }
1880
- let screenshotBuffer = Buffer.from(data, "base64");
1881
1756
  let image = await Jimp.read(screenshotBuffer);
1882
1757
  // Get the image dimensions
1883
1758
  const { width, height } = image.bitmap;
1759
+ const resizeRatio = viewportWidth / width;
1884
1760
  // Resize the image to fit within the viewport dimensions without enlarging
1885
- if (width > viewportWidth || height > viewportHeight) {
1886
- image = image.resize({ w: viewportWidth, h: viewportHeight }); // Resize the image while maintaining aspect ratio
1761
+ if (width > viewportWidth) {
1762
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1887
1763
  await image.write(screenshotPath);
1888
1764
  }
1889
1765
  else {
1890
1766
  fs.writeFileSync(screenshotPath, screenshotBuffer);
1891
1767
  }
1892
- await client.detach();
1893
1768
  }
1894
1769
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1895
- this._validateSelectors(selectors);
1896
- const startTime = Date.now();
1897
- let error = null;
1898
- let screenshotId = null;
1899
- let screenshotPath = null;
1770
+ const state = {
1771
+ selectors,
1772
+ _params,
1773
+ options,
1774
+ world,
1775
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1776
+ text: `Verify element exists in page`,
1777
+ operation: "verifyElementExistInPage",
1778
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1779
+ };
1900
1780
  await new Promise((resolve) => setTimeout(resolve, 2000));
1901
- const info = {};
1902
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
1903
- info.operation = "verify";
1904
- info.selectors = selectors;
1905
1781
  try {
1906
- const element = await this._locate(selectors, info, _params);
1907
- if (element) {
1908
- await this.scrollIfNeeded(element, info);
1909
- }
1910
- await this._highlightElements(element);
1911
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1912
- await expect(element).toHaveCount(1, { timeout: 10000 });
1913
- return info;
1782
+ await _preCommand(state, this);
1783
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1784
+ return state.info;
1914
1785
  }
1915
1786
  catch (e) {
1916
- //await this.closeUnexpectedPopups();
1917
- this.logger.error("verify failed " + info.log);
1918
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1919
- info.screenshotPath = screenshotPath;
1920
- Object.assign(e, { info: info });
1921
- error = e;
1922
- throw e;
1787
+ await _commandError(state, e, this);
1923
1788
  }
1924
1789
  finally {
1925
- const endTime = Date.now();
1926
- this._reportToWorld(world, {
1927
- element_name: selectors.element_name,
1928
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1929
- text: "Verify element exists in page",
1930
- screenshotId,
1931
- result: error
1932
- ? {
1933
- status: "FAILED",
1934
- startTime,
1935
- endTime,
1936
- message: error === null || error === void 0 ? void 0 : error.message,
1937
- }
1938
- : {
1939
- status: "PASSED",
1940
- startTime,
1941
- endTime,
1942
- },
1943
- info: info,
1944
- });
1790
+ _commandFinally(state, this);
1945
1791
  }
1946
1792
  }
1947
1793
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1948
- this._validateSelectors(selectors);
1949
- const startTime = Date.now();
1950
- let error = null;
1951
- let screenshotId = null;
1952
- let screenshotPath = null;
1794
+ const state = {
1795
+ selectors,
1796
+ _params,
1797
+ attribute,
1798
+ variable,
1799
+ options,
1800
+ world,
1801
+ type: Types.EXTRACT,
1802
+ text: `Extract attribute from element`,
1803
+ operation: "extractAttribute",
1804
+ log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
1805
+ };
1953
1806
  await new Promise((resolve) => setTimeout(resolve, 2000));
1954
- const info = {};
1955
- info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
1956
- info.operation = "extract";
1957
- info.selectors = selectors;
1958
1807
  try {
1959
- const element = await this._locate(selectors, info, _params);
1960
- await this._highlightElements(element);
1961
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1808
+ await _preCommand(state, this);
1962
1809
  switch (attribute) {
1963
1810
  case "inner_text":
1964
- info.value = await element.innerText();
1811
+ state.value = await state.element.innerText();
1965
1812
  break;
1966
1813
  case "href":
1967
- info.value = await element.getAttribute("href");
1814
+ state.value = await state.element.getAttribute("href");
1968
1815
  break;
1969
1816
  case "value":
1970
- info.value = await element.inputValue();
1817
+ state.value = await state.element.inputValue();
1971
1818
  break;
1972
1819
  default:
1973
- info.value = await element.getAttribute(attribute);
1820
+ state.value = await state.element.getAttribute(attribute);
1974
1821
  break;
1975
1822
  }
1976
- this[variable] = info.value;
1977
- if (world) {
1978
- world[variable] = info.value;
1979
- }
1980
- this.setTestData({ [variable]: info.value }, world);
1981
- this.logger.info("set test data: " + variable + "=" + info.value);
1982
- return info;
1823
+ state.info.value = state.value;
1824
+ this.setTestData({ [variable]: state.value }, world);
1825
+ this.logger.info("set test data: " + variable + "=" + state.value);
1826
+ return state.info;
1983
1827
  }
1984
1828
  catch (e) {
1985
- //await this.closeUnexpectedPopups();
1986
- this.logger.error("extract failed " + info.log);
1987
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1988
- info.screenshotPath = screenshotPath;
1989
- Object.assign(e, { info: info });
1990
- error = e;
1991
- throw e;
1829
+ await _commandError(state, e, this);
1992
1830
  }
1993
1831
  finally {
1994
- const endTime = Date.now();
1995
- this._reportToWorld(world, {
1996
- element_name: selectors.element_name,
1997
- type: Types.EXTRACT_ATTRIBUTE,
1998
- variable: variable,
1999
- value: info.value,
2000
- text: "Extract attribute from element",
2001
- screenshotId,
2002
- result: error
2003
- ? {
2004
- status: "FAILED",
2005
- startTime,
2006
- endTime,
2007
- message: error === null || error === void 0 ? void 0 : error.message,
2008
- }
2009
- : {
2010
- status: "PASSED",
2011
- startTime,
2012
- endTime,
2013
- },
2014
- info: info,
2015
- });
1832
+ _commandFinally(state, this);
2016
1833
  }
2017
1834
  }
2018
1835
  async extractEmailData(emailAddress, options, world) {
@@ -2089,7 +1906,8 @@ class StableBrowser {
2089
1906
  catch (e) {
2090
1907
  errorCount++;
2091
1908
  if (errorCount > 3) {
2092
- throw e;
1909
+ // throw e;
1910
+ await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
2093
1911
  }
2094
1912
  // ignore
2095
1913
  }
@@ -2200,7 +2018,8 @@ class StableBrowser {
2200
2018
  info.screenshotPath = screenshotPath;
2201
2019
  Object.assign(e, { info: info });
2202
2020
  error = e;
2203
- throw e;
2021
+ // throw e;
2022
+ await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
2204
2023
  }
2205
2024
  finally {
2206
2025
  const endTime = Date.now();
@@ -2213,7 +2032,7 @@ class StableBrowser {
2213
2032
  status: "FAILED",
2214
2033
  startTime,
2215
2034
  endTime,
2216
- message: error === null || error === void 0 ? void 0 : error.message,
2035
+ message: error?.message,
2217
2036
  }
2218
2037
  : {
2219
2038
  status: "PASSED",
@@ -2226,52 +2045,58 @@ class StableBrowser {
2226
2045
  }
2227
2046
  async verifyTextExistInPage(text, options = {}, world = null) {
2228
2047
  text = unEscapeString(text);
2229
- const startTime = Date.now();
2048
+ const state = {
2049
+ text_search: text,
2050
+ options,
2051
+ world,
2052
+ locate: false,
2053
+ scroll: false,
2054
+ highlight: false,
2055
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2056
+ text: `Verify text exists in page`,
2057
+ operation: "verifyTextExistInPage",
2058
+ log: "***** verify text " + text + " exists in page *****\n",
2059
+ };
2230
2060
  const timeout = this._getLoadTimeout(options);
2231
- let error = null;
2232
- let screenshotId = null;
2233
- let screenshotPath = null;
2234
2061
  await new Promise((resolve) => setTimeout(resolve, 2000));
2235
- const info = {};
2236
- info.log = "***** verify text " + text + " exists in page *****\n";
2237
- info.operation = "verifyTextExistInPage";
2238
2062
  const newValue = await this._replaceWithLocalData(text, world);
2239
2063
  if (newValue !== text) {
2240
2064
  this.logger.info(text + "=" + newValue);
2241
2065
  text = newValue;
2242
2066
  }
2243
- info.text = text;
2244
2067
  let dateAlternatives = findDateAlternatives(text);
2245
2068
  let numberAlternatives = findNumberAlternatives(text);
2246
2069
  try {
2070
+ await _preCommand(state, this);
2071
+ state.info.text = text;
2247
2072
  while (true) {
2248
2073
  const frames = this.page.frames();
2249
2074
  let results = [];
2250
2075
  for (let i = 0; i < frames.length; i++) {
2251
2076
  if (dateAlternatives.date) {
2252
2077
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2253
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2078
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
2254
2079
  result.frame = frames[i];
2255
2080
  results.push(result);
2256
2081
  }
2257
2082
  }
2258
2083
  else if (numberAlternatives.number) {
2259
2084
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2260
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2085
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
2261
2086
  result.frame = frames[i];
2262
2087
  results.push(result);
2263
2088
  }
2264
2089
  }
2265
2090
  else {
2266
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2091
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2267
2092
  result.frame = frames[i];
2268
2093
  results.push(result);
2269
2094
  }
2270
2095
  }
2271
- info.results = results;
2096
+ state.info.results = results;
2272
2097
  const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2273
2098
  if (resultWithElementsFound.length === 0) {
2274
- if (Date.now() - startTime > timeout) {
2099
+ if (Date.now() - state.startTime > timeout) {
2275
2100
  throw new Error(`Text ${text} not found in page`);
2276
2101
  }
2277
2102
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -2283,44 +2108,89 @@ class StableBrowser {
2283
2108
  await this._highlightElements(frame, dataAttribute);
2284
2109
  const element = await frame.$(dataAttribute);
2285
2110
  if (element) {
2286
- await this.scrollIfNeeded(element, info);
2111
+ await this.scrollIfNeeded(element, state.info);
2287
2112
  await element.dispatchEvent("bvt_verify_page_contains_text");
2288
2113
  }
2289
2114
  }
2290
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2291
- return info;
2115
+ await _screenshot(state, this);
2116
+ return state.info;
2292
2117
  }
2293
2118
  // await expect(element).toHaveCount(1, { timeout: 10000 });
2294
2119
  }
2295
2120
  catch (e) {
2296
- //await this.closeUnexpectedPopups();
2297
- this.logger.error("verify text exist in page failed " + info.log);
2298
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2299
- info.screenshotPath = screenshotPath;
2300
- Object.assign(e, { info: info });
2301
- error = e;
2302
- throw e;
2121
+ await _commandError(state, e, this);
2303
2122
  }
2304
2123
  finally {
2305
- const endTime = Date.now();
2306
- this._reportToWorld(world, {
2307
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2308
- text: "Verify text exists in page",
2309
- screenshotId,
2310
- result: error
2311
- ? {
2312
- status: "FAILED",
2313
- startTime,
2314
- endTime,
2315
- message: error === null || error === void 0 ? void 0 : error.message,
2124
+ _commandFinally(state, this);
2125
+ }
2126
+ }
2127
+ async waitForTextToDisappear(text, options = {}, world = null) {
2128
+ text = unEscapeString(text);
2129
+ const state = {
2130
+ text_search: text,
2131
+ options,
2132
+ world,
2133
+ locate: false,
2134
+ scroll: false,
2135
+ highlight: false,
2136
+ type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
2137
+ text: `Verify text does not exist in page`,
2138
+ operation: "verifyTextNotExistInPage",
2139
+ log: "***** verify text " + text + " does not exist in page *****\n",
2140
+ };
2141
+ const timeout = this._getLoadTimeout(options);
2142
+ await new Promise((resolve) => setTimeout(resolve, 2000));
2143
+ const newValue = await this._replaceWithLocalData(text, world);
2144
+ if (newValue !== text) {
2145
+ this.logger.info(text + "=" + newValue);
2146
+ text = newValue;
2147
+ }
2148
+ let dateAlternatives = findDateAlternatives(text);
2149
+ let numberAlternatives = findNumberAlternatives(text);
2150
+ try {
2151
+ await _preCommand(state, this);
2152
+ state.info.text = text;
2153
+ while (true) {
2154
+ const frames = this.page.frames();
2155
+ let results = [];
2156
+ for (let i = 0; i < frames.length; i++) {
2157
+ if (dateAlternatives.date) {
2158
+ for (let j = 0; j < dateAlternatives.dates.length; j++) {
2159
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
2160
+ result.frame = frames[i];
2161
+ results.push(result);
2162
+ }
2316
2163
  }
2317
- : {
2318
- status: "PASSED",
2319
- startTime,
2320
- endTime,
2321
- },
2322
- info: info,
2323
- });
2164
+ else if (numberAlternatives.number) {
2165
+ for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2166
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
2167
+ result.frame = frames[i];
2168
+ results.push(result);
2169
+ }
2170
+ }
2171
+ else {
2172
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2173
+ result.frame = frames[i];
2174
+ results.push(result);
2175
+ }
2176
+ }
2177
+ state.info.results = results;
2178
+ const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2179
+ if (resultWithElementsFound.length === 0) {
2180
+ await _screenshot(state, this);
2181
+ return state.info;
2182
+ }
2183
+ if (Date.now() - state.startTime > timeout) {
2184
+ throw new Error(`Text ${text} found in page`);
2185
+ }
2186
+ await new Promise((resolve) => setTimeout(resolve, 1000));
2187
+ }
2188
+ }
2189
+ catch (e) {
2190
+ await _commandError(state, e, this);
2191
+ }
2192
+ finally {
2193
+ _commandFinally(state, this);
2324
2194
  }
2325
2195
  }
2326
2196
  _getServerUrl() {
@@ -2383,7 +2253,8 @@ class StableBrowser {
2383
2253
  info.screenshotPath = screenshotPath;
2384
2254
  Object.assign(e, { info: info });
2385
2255
  error = e;
2386
- throw e;
2256
+ // throw e;
2257
+ await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
2387
2258
  }
2388
2259
  finally {
2389
2260
  const endTime = Date.now();
@@ -2396,7 +2267,7 @@ class StableBrowser {
2396
2267
  status: "FAILED",
2397
2268
  startTime,
2398
2269
  endTime,
2399
- message: error === null || error === void 0 ? void 0 : error.message,
2270
+ message: error?.message,
2400
2271
  }
2401
2272
  : {
2402
2273
  status: "PASSED",
@@ -2428,7 +2299,7 @@ class StableBrowser {
2428
2299
  this.logger.info("Table data verified");
2429
2300
  }
2430
2301
  async getTableData(selectors, _params = null, options = {}, world = null) {
2431
- this._validateSelectors(selectors);
2302
+ _validateSelectors(selectors);
2432
2303
  const startTime = Date.now();
2433
2304
  let error = null;
2434
2305
  let screenshotId = null;
@@ -2450,7 +2321,8 @@ class StableBrowser {
2450
2321
  info.screenshotPath = screenshotPath;
2451
2322
  Object.assign(e, { info: info });
2452
2323
  error = e;
2453
- throw e;
2324
+ // throw e;
2325
+ await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
2454
2326
  }
2455
2327
  finally {
2456
2328
  const endTime = Date.now();
@@ -2464,7 +2336,7 @@ class StableBrowser {
2464
2336
  status: "FAILED",
2465
2337
  startTime,
2466
2338
  endTime,
2467
- message: error === null || error === void 0 ? void 0 : error.message,
2339
+ message: error?.message,
2468
2340
  }
2469
2341
  : {
2470
2342
  status: "PASSED",
@@ -2476,7 +2348,7 @@ class StableBrowser {
2476
2348
  }
2477
2349
  }
2478
2350
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2479
- this._validateSelectors(selectors);
2351
+ _validateSelectors(selectors);
2480
2352
  if (!query) {
2481
2353
  throw new Error("query is null");
2482
2354
  }
@@ -2615,7 +2487,8 @@ class StableBrowser {
2615
2487
  info.screenshotPath = screenshotPath;
2616
2488
  Object.assign(e, { info: info });
2617
2489
  error = e;
2618
- throw e;
2490
+ // throw e;
2491
+ await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
2619
2492
  }
2620
2493
  finally {
2621
2494
  const endTime = Date.now();
@@ -2629,7 +2502,7 @@ class StableBrowser {
2629
2502
  status: "FAILED",
2630
2503
  startTime,
2631
2504
  endTime,
2632
- message: error === null || error === void 0 ? void 0 : error.message,
2505
+ message: error?.message,
2633
2506
  }
2634
2507
  : {
2635
2508
  status: "PASSED",
@@ -2641,27 +2514,7 @@ class StableBrowser {
2641
2514
  }
2642
2515
  }
2643
2516
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2644
- if (!value) {
2645
- return value;
2646
- }
2647
- // find all the accurance of {{(.*?)}} and replace with the value
2648
- let regex = /{{(.*?)}}/g;
2649
- let matches = value.match(regex);
2650
- if (matches) {
2651
- const testData = this.getTestData(world);
2652
- for (let i = 0; i < matches.length; i++) {
2653
- let match = matches[i];
2654
- let key = match.substring(2, match.length - 2);
2655
- let newValue = objectPath.get(testData, key, null);
2656
- if (newValue !== null) {
2657
- value = value.replace(match, newValue);
2658
- }
2659
- }
2660
- }
2661
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2662
- return await decrypt(value, null, totpWait);
2663
- }
2664
- return value;
2517
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2665
2518
  }
2666
2519
  _getLoadTimeout(options) {
2667
2520
  let timeout = 15000;
@@ -2721,7 +2574,7 @@ class StableBrowser {
2721
2574
  status: "FAILED",
2722
2575
  startTime,
2723
2576
  endTime,
2724
- message: error === null || error === void 0 ? void 0 : error.message,
2577
+ message: error?.message,
2725
2578
  }
2726
2579
  : {
2727
2580
  status: "PASSED",
@@ -2732,39 +2585,28 @@ class StableBrowser {
2732
2585
  }
2733
2586
  }
2734
2587
  async closePage(options = {}, world = null) {
2735
- const startTime = Date.now();
2736
- let error = null;
2737
- let screenshotId = null;
2738
- let screenshotPath = null;
2739
- const info = {};
2588
+ const state = {
2589
+ options,
2590
+ world,
2591
+ locate: false,
2592
+ scroll: false,
2593
+ highlight: false,
2594
+ type: Types.CLOSE_PAGE,
2595
+ text: `Close page`,
2596
+ operation: "closePage",
2597
+ log: "***** close page *****\n",
2598
+ throwError: false,
2599
+ };
2740
2600
  try {
2601
+ await _preCommand(state, this);
2741
2602
  await this.page.close();
2742
2603
  }
2743
2604
  catch (e) {
2744
2605
  console.log(".");
2606
+ await _commandError(state, e, this);
2745
2607
  }
2746
2608
  finally {
2747
- await new Promise((resolve) => setTimeout(resolve, 2000));
2748
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2749
- const endTime = Date.now();
2750
- this._reportToWorld(world, {
2751
- type: Types.CLOSE_PAGE,
2752
- text: "close page",
2753
- screenshotId,
2754
- result: error
2755
- ? {
2756
- status: "FAILED",
2757
- startTime,
2758
- endTime,
2759
- message: error === null || error === void 0 ? void 0 : error.message,
2760
- }
2761
- : {
2762
- status: "PASSED",
2763
- startTime,
2764
- endTime,
2765
- },
2766
- info: info,
2767
- });
2609
+ _commandFinally(state, this);
2768
2610
  }
2769
2611
  }
2770
2612
  async setViewportSize(width, hight, options = {}, world = null) {
@@ -2784,6 +2626,7 @@ class StableBrowser {
2784
2626
  }
2785
2627
  catch (e) {
2786
2628
  console.log(".");
2629
+ await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
2787
2630
  }
2788
2631
  finally {
2789
2632
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2798,7 +2641,7 @@ class StableBrowser {
2798
2641
  status: "FAILED",
2799
2642
  startTime,
2800
2643
  endTime,
2801
- message: error === null || error === void 0 ? void 0 : error.message,
2644
+ message: error?.message,
2802
2645
  }
2803
2646
  : {
2804
2647
  status: "PASSED",
@@ -2820,6 +2663,7 @@ class StableBrowser {
2820
2663
  }
2821
2664
  catch (e) {
2822
2665
  console.log(".");
2666
+ await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
2823
2667
  }
2824
2668
  finally {
2825
2669
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2834,7 +2678,7 @@ class StableBrowser {
2834
2678
  status: "FAILED",
2835
2679
  startTime,
2836
2680
  endTime,
2837
- message: error === null || error === void 0 ? void 0 : error.message,
2681
+ message: error?.message,
2838
2682
  }
2839
2683
  : {
2840
2684
  status: "PASSED",
@@ -2867,6 +2711,31 @@ class StableBrowser {
2867
2711
  }
2868
2712
  world.attach(JSON.stringify(properties), { mediaType: "application/json" });
2869
2713
  }
2714
+ async beforeStep(world, step) {
2715
+ this.stepName = step.pickleStep.text;
2716
+ this.logger.info("step: " + this.stepName);
2717
+ if (this.stepIndex === undefined) {
2718
+ this.stepIndex = 0;
2719
+ }
2720
+ else {
2721
+ this.stepIndex++;
2722
+ }
2723
+ if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
2724
+ if (this.context.browserObject.context) {
2725
+ await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
2726
+ }
2727
+ }
2728
+ }
2729
+ async afterStep(world, step) {
2730
+ this.stepName = null;
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.stopChunk({
2734
+ path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
2735
+ });
2736
+ }
2737
+ }
2738
+ }
2870
2739
  }
2871
2740
  function createTimedPromise(promise, label) {
2872
2741
  return promise