automation_model 1.0.451-dev → 1.0.451-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.
@@ -2,20 +2,22 @@
2
2
  import { expect } from "@playwright/test";
3
3
  import dayjs from "dayjs";
4
4
  import fs from "fs";
5
+ import { Jimp } from "jimp";
5
6
  import path from "path";
6
7
  import reg_parser from "regex-parser";
7
- import sharp from "sharp";
8
8
  import { findDateAlternatives, findNumberAlternatives } from "./analyze_helper.js";
9
9
  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,10 +143,10 @@ 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
148
  let newContext = await getContext(null, false, this.logger, appName, false, this);
132
- newContextCreated = true;
149
+ navigate = true;
133
150
  apps[appName] = {
134
151
  context: newContext,
135
152
  browser: newContext.browser,
@@ -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,32 +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);
582
- break;
628
+ let testframescope = framescope.frameLocator(frameLocator.css);
629
+ if (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);
641
+ }
583
642
  }
584
643
  }
585
644
  if (frame.children) {
586
- return findFrame(frame.children, framescope);
645
+ return await findFrame(frame.children, framescope);
587
646
  }
588
647
  return framescope;
589
648
  };
590
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
591
649
  while (true) {
592
650
  let frameFound = false;
593
651
  if (selectors.nestFrmLoc) {
594
- scope = findFrame(selectors.nestFrmLoc, scope);
652
+ scope = await findFrame(selectors.nestFrmLoc, scope);
595
653
  frameFound = true;
596
654
  break;
597
655
  }
@@ -611,6 +669,8 @@ class StableBrowser {
611
669
  if (!scope) {
612
670
  info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
613
671
  if (performance.now() - startTime > timeout) {
672
+ info.failCause.iframeNotFound = true;
673
+ info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
614
674
  throw new Error("unable to locate iframe " + selectors.iframe_src);
615
675
  }
616
676
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -620,6 +680,30 @@ class StableBrowser {
620
680
  }
621
681
  }
622
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);
623
707
  let selectorsLocators = null;
624
708
  selectorsLocators = selectors.locators;
625
709
  // group selectors by priority
@@ -721,6 +805,8 @@ class StableBrowser {
721
805
  }
722
806
  this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
723
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";
724
810
  throw new Error("failed to locate first element no elements found, " + info.log);
725
811
  }
726
812
  async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
@@ -752,89 +838,129 @@ class StableBrowser {
752
838
  });
753
839
  result.locatorIndex = i;
754
840
  }
841
+ if (foundLocators.length > 1) {
842
+ info.failCause.foundMultiple = true;
843
+ }
755
844
  }
756
845
  return result;
757
846
  }
758
- async click(selectors, _params, options = {}, world = null) {
759
- this._validateSelectors(selectors);
847
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
760
848
  const startTime = Date.now();
761
- if (options && options.context) {
762
- selectors.locators[0].text = options.context;
849
+ let timeout = 30000;
850
+ if (options && options.timeout) {
851
+ timeout = options.timeout;
763
852
  }
764
- const info = {};
765
- info.log = "***** click on " + selectors.element_name + " *****\n";
766
- info.operation = "click";
767
- info.selectors = selectors;
768
- let error = null;
769
- let screenshotId = null;
770
- 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
+ };
771
920
  try {
772
- let element = await this._locate(selectors, info, _params);
773
- await this.scrollIfNeeded(element, info);
774
- ({ 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
+ }
775
925
  try {
776
- await this._highlightElements(element);
777
- await element.click();
926
+ await state.element.click();
778
927
  await new Promise((resolve) => setTimeout(resolve, 1000));
779
928
  }
780
929
  catch (e) {
781
930
  // await this.closeUnexpectedPopups();
782
- info.log += "click failed, will try again" + "\n";
783
- element = await this._locate(selectors, info, _params);
784
- await element.dispatchEvent("click");
931
+ state.element = await this._locate(selectors, state.info, _params);
932
+ await state.element.dispatchEvent("click");
785
933
  await new Promise((resolve) => setTimeout(resolve, 1000));
786
934
  }
787
935
  await this.waitForPageLoad();
788
- return info;
936
+ return state.info;
789
937
  }
790
938
  catch (e) {
791
- this.logger.error("click failed " + info.log);
792
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
793
- info.screenshotPath = screenshotPath;
794
- Object.assign(e, { info: info });
795
- error = e;
796
- throw e;
939
+ await _commandError(state, e, this);
797
940
  }
798
941
  finally {
799
- const endTime = Date.now();
800
- this._reportToWorld(world, {
801
- element_name: selectors.element_name,
802
- type: Types.CLICK,
803
- text: `Click element`,
804
- screenshotId,
805
- result: error
806
- ? {
807
- status: "FAILED",
808
- startTime,
809
- endTime,
810
- message: error === null || error === void 0 ? void 0 : error.message,
811
- }
812
- : {
813
- status: "PASSED",
814
- startTime,
815
- endTime,
816
- },
817
- info: info,
818
- });
942
+ _commandFinally(state, this);
819
943
  }
820
944
  }
821
945
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
822
- this._validateSelectors(selectors);
823
- const startTime = Date.now();
824
- const info = {};
825
- info.log = "";
826
- info.operation = "setCheck";
827
- info.checked = checked;
828
- info.selectors = selectors;
829
- let error = null;
830
- let screenshotId = null;
831
- 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
+ };
832
956
  try {
833
- let element = await this._locate(selectors, info, _params);
834
- ({ 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));
835
961
  try {
836
- await this._highlightElements(element);
837
- await element.setChecked(checked);
962
+ // await this._highlightElements(element);
963
+ await state.element.setChecked(checked);
838
964
  await new Promise((resolve) => setTimeout(resolve, 1000));
839
965
  }
840
966
  catch (e) {
@@ -843,179 +969,108 @@ class StableBrowser {
843
969
  }
844
970
  else {
845
971
  //await this.closeUnexpectedPopups();
846
- info.log += "setCheck failed, will try again" + "\n";
847
- element = await this._locate(selectors, info, _params);
848
- 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 });
849
975
  await new Promise((resolve) => setTimeout(resolve, 1000));
850
976
  }
851
977
  }
852
978
  await this.waitForPageLoad();
853
- return info;
979
+ return state.info;
854
980
  }
855
981
  catch (e) {
856
- this.logger.error("setCheck failed " + info.log);
857
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
858
- info.screenshotPath = screenshotPath;
859
- Object.assign(e, { info: info });
860
- error = e;
861
- throw e;
982
+ await _commandError(state, e, this);
862
983
  }
863
984
  finally {
864
- const endTime = Date.now();
865
- this._reportToWorld(world, {
866
- element_name: selectors.element_name,
867
- type: checked ? Types.CHECK : Types.UNCHECK,
868
- text: checked ? `Check element` : `Uncheck element`,
869
- screenshotId,
870
- result: error
871
- ? {
872
- status: "FAILED",
873
- startTime,
874
- endTime,
875
- message: error === null || error === void 0 ? void 0 : error.message,
876
- }
877
- : {
878
- status: "PASSED",
879
- startTime,
880
- endTime,
881
- },
882
- info: info,
883
- });
985
+ _commandFinally(state, this);
884
986
  }
885
987
  }
886
988
  async hover(selectors, _params, options = {}, world = null) {
887
- this._validateSelectors(selectors);
888
- const startTime = Date.now();
889
- const info = {};
890
- info.log = "";
891
- info.operation = "hover";
892
- info.selectors = selectors;
893
- let error = null;
894
- let screenshotId = null;
895
- 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
+ };
896
999
  try {
897
- let element = await this._locate(selectors, info, _params);
898
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1000
+ await _preCommand(state, this);
899
1001
  try {
900
- await this._highlightElements(element);
901
- await element.hover();
1002
+ await state.element.hover();
902
1003
  await new Promise((resolve) => setTimeout(resolve, 1000));
903
1004
  }
904
1005
  catch (e) {
905
1006
  //await this.closeUnexpectedPopups();
906
- info.log += "hover failed, will try again" + "\n";
907
- element = await this._locate(selectors, info, _params);
908
- 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 });
909
1010
  await new Promise((resolve) => setTimeout(resolve, 1000));
910
1011
  }
911
1012
  await this.waitForPageLoad();
912
- return info;
1013
+ return state.info;
913
1014
  }
914
1015
  catch (e) {
915
- this.logger.error("hover failed " + info.log);
916
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
917
- info.screenshotPath = screenshotPath;
918
- Object.assign(e, { info: info });
919
- error = e;
920
- throw e;
1016
+ await _commandError(state, e, this);
921
1017
  }
922
1018
  finally {
923
- const endTime = Date.now();
924
- this._reportToWorld(world, {
925
- element_name: selectors.element_name,
926
- type: Types.HOVER,
927
- text: `Hover element`,
928
- screenshotId,
929
- result: error
930
- ? {
931
- status: "FAILED",
932
- startTime,
933
- endTime,
934
- message: error === null || error === void 0 ? void 0 : error.message,
935
- }
936
- : {
937
- status: "PASSED",
938
- startTime,
939
- endTime,
940
- },
941
- info: info,
942
- });
1019
+ _commandFinally(state, this);
943
1020
  }
944
1021
  }
945
1022
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
946
- this._validateSelectors(selectors);
947
1023
  if (!values) {
948
1024
  throw new Error("values is null");
949
1025
  }
950
- const startTime = Date.now();
951
- let error = null;
952
- let screenshotId = null;
953
- let screenshotPath = null;
954
- const info = {};
955
- info.log = "";
956
- info.operation = "selectOptions";
957
- 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
+ };
958
1037
  try {
959
- let element = await this._locate(selectors, info, _params);
960
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1038
+ await _preCommand(state, this);
961
1039
  try {
962
- await this._highlightElements(element);
963
- await element.selectOption(values);
1040
+ await state.element.selectOption(values);
964
1041
  }
965
1042
  catch (e) {
966
1043
  //await this.closeUnexpectedPopups();
967
- info.log += "selectOption failed, will try force" + "\n";
968
- 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 });
969
1046
  }
970
1047
  await this.waitForPageLoad();
971
- return info;
1048
+ return state.info;
972
1049
  }
973
1050
  catch (e) {
974
- this.logger.error("selectOption failed " + info.log);
975
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
976
- info.screenshotPath = screenshotPath;
977
- Object.assign(e, { info: info });
978
- this.logger.info("click failed, will try next selector");
979
- error = e;
980
- throw e;
1051
+ await _commandError(state, e, this);
981
1052
  }
982
1053
  finally {
983
- const endTime = Date.now();
984
- this._reportToWorld(world, {
985
- element_name: selectors.element_name,
986
- type: Types.SELECT,
987
- text: `Select option: ${values}`,
988
- value: values.toString(),
989
- screenshotId,
990
- result: error
991
- ? {
992
- status: "FAILED",
993
- startTime,
994
- endTime,
995
- message: error === null || error === void 0 ? void 0 : error.message,
996
- }
997
- : {
998
- status: "PASSED",
999
- startTime,
1000
- endTime,
1001
- },
1002
- info: info,
1003
- });
1054
+ _commandFinally(state, this);
1004
1055
  }
1005
1056
  }
1006
1057
  async type(_value, _params = null, options = {}, world = null) {
1007
- const startTime = Date.now();
1008
- let error = null;
1009
- let screenshotId = null;
1010
- let screenshotPath = null;
1011
- const info = {};
1012
- info.log = "";
1013
- info.operation = "type";
1014
- _value = this._fixUsingParams(_value, _params);
1015
- 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
+ };
1016
1071
  try {
1017
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1018
- const valueSegment = _value.split("&&");
1072
+ await _preCommand(state, this);
1073
+ const valueSegment = state.value.split("&&");
1019
1074
  for (let i = 0; i < valueSegment.length; i++) {
1020
1075
  if (i > 0) {
1021
1076
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1035,134 +1090,76 @@ class StableBrowser {
1035
1090
  await this.page.keyboard.type(value);
1036
1091
  }
1037
1092
  }
1038
- return info;
1093
+ return state.info;
1039
1094
  }
1040
1095
  catch (e) {
1041
- //await this.closeUnexpectedPopups();
1042
- this.logger.error("type failed " + info.log);
1043
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1044
- info.screenshotPath = screenshotPath;
1045
- Object.assign(e, { info: info });
1046
- error = e;
1047
- throw e;
1096
+ await _commandError(state, e, this);
1048
1097
  }
1049
1098
  finally {
1050
- const endTime = Date.now();
1051
- this._reportToWorld(world, {
1052
- type: Types.TYPE_PRESS,
1053
- screenshotId,
1054
- value: _value,
1055
- text: `type value: ${_value}`,
1056
- result: error
1057
- ? {
1058
- status: "FAILED",
1059
- startTime,
1060
- endTime,
1061
- message: error === null || error === void 0 ? void 0 : error.message,
1062
- }
1063
- : {
1064
- status: "PASSED",
1065
- startTime,
1066
- endTime,
1067
- },
1068
- info: info,
1069
- });
1099
+ _commandFinally(state, this);
1070
1100
  }
1071
1101
  }
1072
1102
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1073
- // set input value for non fillable inputs like date, time, range, color, etc.
1074
- this._validateSelectors(selectors);
1075
- const startTime = Date.now();
1076
- const info = {};
1077
- info.log = "***** set input value " + selectors.element_name + " *****\n";
1078
- info.operation = "setInputValue";
1079
- info.selectors = selectors;
1080
- value = this._fixUsingParams(value, _params);
1081
- info.value = value;
1082
- let error = null;
1083
- let screenshotId = null;
1084
- 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
+ };
1085
1114
  try {
1086
- value = await this._replaceWithLocalData(value, this);
1087
- let element = await this._locate(selectors, info, _params);
1088
- await this.scrollIfNeeded(element, info);
1089
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1090
- await this._highlightElements(element);
1115
+ await _preCommand(state, this);
1116
+ let value = await this._replaceWithLocalData(state.value, this);
1091
1117
  try {
1092
- await element.evaluateHandle((el, value) => {
1118
+ await state.element.evaluateHandle((el, value) => {
1093
1119
  el.value = value;
1094
1120
  }, value);
1095
1121
  }
1096
1122
  catch (error) {
1097
1123
  this.logger.error("setInputValue failed, will try again");
1098
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1099
- info.screenshotPath = screenshotPath;
1100
- Object.assign(error, { info: info });
1101
- await element.evaluateHandle((el, value) => {
1124
+ await _screenshot(state, this);
1125
+ Object.assign(error, { info: state.info });
1126
+ await state.element.evaluateHandle((el, value) => {
1102
1127
  el.value = value;
1103
1128
  });
1104
1129
  }
1105
1130
  }
1106
1131
  catch (e) {
1107
- this.logger.error("setInputValue failed " + info.log);
1108
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1109
- info.screenshotPath = screenshotPath;
1110
- Object.assign(e, { info: info });
1111
- error = e;
1112
- throw e;
1132
+ await _commandError(state, e, this);
1113
1133
  }
1114
1134
  finally {
1115
- const endTime = Date.now();
1116
- this._reportToWorld(world, {
1117
- element_name: selectors.element_name,
1118
- type: Types.SET_INPUT,
1119
- text: `Set input value`,
1120
- value: value,
1121
- screenshotId,
1122
- result: error
1123
- ? {
1124
- status: "FAILED",
1125
- startTime,
1126
- endTime,
1127
- message: error === null || error === void 0 ? void 0 : error.message,
1128
- }
1129
- : {
1130
- status: "PASSED",
1131
- startTime,
1132
- endTime,
1133
- },
1134
- info: info,
1135
- });
1135
+ _commandFinally(state, this);
1136
1136
  }
1137
1137
  }
1138
1138
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1139
- this._validateSelectors(selectors);
1140
- const startTime = Date.now();
1141
- let error = null;
1142
- let screenshotId = null;
1143
- let screenshotPath = null;
1144
- const info = {};
1145
- info.log = "";
1146
- info.operation = Types.SET_DATE_TIME;
1147
- info.selectors = selectors;
1148
- 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
+ };
1149
1151
  try {
1150
- value = await this._replaceWithLocalData(value, this);
1151
- let element = await this._locate(selectors, info, _params);
1152
- //insert red border around the element
1153
- await this.scrollIfNeeded(element, info);
1154
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1155
- await this._highlightElements(element);
1152
+ await _preCommand(state, this);
1156
1153
  try {
1157
- await element.click();
1154
+ await state.element.click();
1158
1155
  await new Promise((resolve) => setTimeout(resolve, 500));
1159
1156
  if (format) {
1160
- value = dayjs(value).format(format);
1161
- await element.fill(value);
1157
+ state.value = dayjs(state.value).format(format);
1158
+ await state.element.fill(state.value);
1162
1159
  }
1163
1160
  else {
1164
- const dateTimeValue = await getDateTimeValue({ value, element });
1165
- await element.evaluateHandle((el, dateTimeValue) => {
1161
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1162
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1166
1163
  el.value = ""; // clear input
1167
1164
  el.value = dateTimeValue;
1168
1165
  }, dateTimeValue);
@@ -1175,20 +1172,19 @@ class StableBrowser {
1175
1172
  }
1176
1173
  catch (err) {
1177
1174
  //await this.closeUnexpectedPopups();
1178
- this.logger.error("setting date time input failed " + JSON.stringify(info));
1175
+ this.logger.error("setting date time input failed " + JSON.stringify(state.info));
1179
1176
  this.logger.info("Trying again");
1180
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1181
- info.screenshotPath = screenshotPath;
1182
- Object.assign(err, { info: info });
1177
+ await _screenshot(state, this);
1178
+ Object.assign(err, { info: state.info });
1183
1179
  await element.click();
1184
1180
  await new Promise((resolve) => setTimeout(resolve, 500));
1185
1181
  if (format) {
1186
- value = dayjs(value).format(format);
1187
- await element.fill(value);
1182
+ state.value = dayjs(state.value).format(format);
1183
+ await state.element.fill(state.value);
1188
1184
  }
1189
1185
  else {
1190
- const dateTimeValue = await getDateTimeValue({ value, element });
1191
- await element.evaluateHandle((el, dateTimeValue) => {
1186
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1187
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1192
1188
  el.value = ""; // clear input
1193
1189
  el.value = dateTimeValue;
1194
1190
  }, dateTimeValue);
@@ -1201,61 +1197,39 @@ class StableBrowser {
1201
1197
  }
1202
1198
  }
1203
1199
  catch (e) {
1204
- error = e;
1205
- throw e;
1200
+ await _commandError(state, e, this);
1206
1201
  }
1207
1202
  finally {
1208
- const endTime = Date.now();
1209
- this._reportToWorld(world, {
1210
- element_name: selectors.element_name,
1211
- type: Types.SET_DATE_TIME,
1212
- screenshotId,
1213
- value: value,
1214
- text: `setDateTime input with value: ${value}`,
1215
- result: error
1216
- ? {
1217
- status: "FAILED",
1218
- startTime,
1219
- endTime,
1220
- message: error === null || error === void 0 ? void 0 : error.message,
1221
- }
1222
- : {
1223
- status: "PASSED",
1224
- startTime,
1225
- endTime,
1226
- },
1227
- info: info,
1228
- });
1203
+ _commandFinally(state, this);
1229
1204
  }
1230
1205
  }
1231
1206
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1232
1207
  _value = unEscapeString(_value);
1233
- this._validateSelectors(selectors);
1234
- const startTime = Date.now();
1235
- let error = null;
1236
- let screenshotId = null;
1237
- let screenshotPath = null;
1238
- const info = {};
1239
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1240
- info.operation = "clickType";
1241
- info.selectors = selectors;
1242
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
+ };
1243
1221
  if (newValue !== _value) {
1244
1222
  //this.logger.info(_value + "=" + newValue);
1245
1223
  _value = newValue;
1246
1224
  }
1247
- info.value = _value;
1248
1225
  try {
1249
- let element = await this._locate(selectors, info, _params);
1250
- //insert red border around the element
1251
- await this.scrollIfNeeded(element, info);
1252
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1253
- await this._highlightElements(element);
1226
+ await _preCommand(state, this);
1227
+ state.info.value = _value;
1254
1228
  if (options === null || options === undefined || !options.press) {
1255
1229
  try {
1256
- let currentValue = await element.inputValue();
1230
+ let currentValue = await state.element.inputValue();
1257
1231
  if (currentValue) {
1258
- await element.fill("");
1232
+ await state.element.fill("");
1259
1233
  }
1260
1234
  }
1261
1235
  catch (e) {
@@ -1264,22 +1238,22 @@ class StableBrowser {
1264
1238
  }
1265
1239
  if (options === null || options === undefined || options.press) {
1266
1240
  try {
1267
- await element.click({ timeout: 5000 });
1241
+ await state.element.click({ timeout: 5000 });
1268
1242
  }
1269
1243
  catch (e) {
1270
- await element.dispatchEvent("click");
1244
+ await state.element.dispatchEvent("click");
1271
1245
  }
1272
1246
  }
1273
1247
  else {
1274
1248
  try {
1275
- await element.focus();
1249
+ await state.element.focus();
1276
1250
  }
1277
1251
  catch (e) {
1278
- await element.dispatchEvent("focus");
1252
+ await state.element.dispatchEvent("focus");
1279
1253
  }
1280
1254
  }
1281
1255
  await new Promise((resolve) => setTimeout(resolve, 500));
1282
- const valueSegment = _value.split("&&");
1256
+ const valueSegment = state.value.split("&&");
1283
1257
  for (let i = 0; i < valueSegment.length; i++) {
1284
1258
  if (i > 0) {
1285
1259
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1299,13 +1273,14 @@ class StableBrowser {
1299
1273
  await new Promise((resolve) => setTimeout(resolve, 500));
1300
1274
  }
1301
1275
  }
1276
+ await _screenshot(state, this);
1302
1277
  if (enter === true) {
1303
1278
  await new Promise((resolve) => setTimeout(resolve, 2000));
1304
1279
  await this.page.keyboard.press("Enter");
1305
1280
  await this.waitForPageLoad();
1306
1281
  }
1307
1282
  else if (enter === false) {
1308
- await element.dispatchEvent("change");
1283
+ await state.element.dispatchEvent("change");
1309
1284
  //await this.page.keyboard.press("Tab");
1310
1285
  }
1311
1286
  else {
@@ -1314,104 +1289,50 @@ class StableBrowser {
1314
1289
  await this.waitForPageLoad();
1315
1290
  }
1316
1291
  }
1317
- return info;
1292
+ return state.info;
1318
1293
  }
1319
1294
  catch (e) {
1320
- //await this.closeUnexpectedPopups();
1321
- this.logger.error("fill failed " + JSON.stringify(info));
1322
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1323
- info.screenshotPath = screenshotPath;
1324
- Object.assign(e, { info: info });
1325
- error = e;
1326
- throw e;
1295
+ await _commandError(state, e, this);
1327
1296
  }
1328
1297
  finally {
1329
- const endTime = Date.now();
1330
- this._reportToWorld(world, {
1331
- element_name: selectors.element_name,
1332
- type: Types.FILL,
1333
- screenshotId,
1334
- value: _value,
1335
- text: `clickType input with value: ${_value}`,
1336
- result: error
1337
- ? {
1338
- status: "FAILED",
1339
- startTime,
1340
- endTime,
1341
- message: error === null || error === void 0 ? void 0 : error.message,
1342
- }
1343
- : {
1344
- status: "PASSED",
1345
- startTime,
1346
- endTime,
1347
- },
1348
- info: info,
1349
- });
1350
- }
1351
- }
1352
- async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1353
- this._validateSelectors(selectors);
1354
- value = unEscapeString(value);
1355
- const startTime = Date.now();
1356
- let error = null;
1357
- let screenshotId = null;
1358
- let screenshotPath = null;
1359
- const info = {};
1360
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1361
- info.operation = "fill";
1362
- info.selectors = selectors;
1363
- info.value = value;
1364
- try {
1365
- let element = await this._locate(selectors, info, _params);
1366
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1367
- await this._highlightElements(element);
1368
- await element.fill(value);
1369
- await element.dispatchEvent("change");
1370
- if (enter) {
1371
- await new Promise((resolve) => setTimeout(resolve, 2000));
1372
- await this.page.keyboard.press("Enter");
1373
- }
1374
- await this.waitForPageLoad();
1375
- return info;
1298
+ _commandFinally(state, this);
1376
1299
  }
1377
- catch (e) {
1378
- //await this.closeUnexpectedPopups();
1379
- this.logger.error("fill failed " + info.log);
1380
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1381
- info.screenshotPath = screenshotPath;
1382
- Object.assign(e, { info: info });
1383
- error = e;
1384
- throw e;
1385
- }
1386
- finally {
1387
- const endTime = Date.now();
1388
- this._reportToWorld(world, {
1389
- element_name: selectors.element_name,
1390
- type: Types.FILL,
1391
- screenshotId,
1392
- value,
1393
- text: `Fill input with value: ${value}`,
1394
- result: error
1395
- ? {
1396
- status: "FAILED",
1397
- startTime,
1398
- endTime,
1399
- message: error === null || error === void 0 ? void 0 : error.message,
1400
- }
1401
- : {
1402
- status: "PASSED",
1403
- startTime,
1404
- endTime,
1405
- },
1406
- info: info,
1407
- });
1300
+ }
1301
+ async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
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
+ };
1313
+ try {
1314
+ await _preCommand(state, this);
1315
+ await state.element.fill(value);
1316
+ await state.element.dispatchEvent("change");
1317
+ if (enter) {
1318
+ await new Promise((resolve) => setTimeout(resolve, 2000));
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);
1408
1329
  }
1409
1330
  }
1410
1331
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1411
1332
  return await this._getText(selectors, 0, _params, options, info, world);
1412
1333
  }
1413
1334
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1414
- this._validateSelectors(selectors);
1335
+ _validateSelectors(selectors);
1415
1336
  let screenshotId = null;
1416
1337
  let screenshotPath = null;
1417
1338
  if (!info.log) {
@@ -1455,166 +1376,124 @@ class StableBrowser {
1455
1376
  }
1456
1377
  }
1457
1378
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1458
- var _a;
1459
- this._validateSelectors(selectors);
1460
1379
  if (!pattern) {
1461
1380
  throw new Error("pattern is null");
1462
1381
  }
1463
1382
  if (!text) {
1464
1383
  throw new Error("text is null");
1465
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
+ };
1466
1401
  const newValue = await this._replaceWithLocalData(text, world);
1467
1402
  if (newValue !== text) {
1468
1403
  this.logger.info(text + "=" + newValue);
1469
1404
  text = newValue;
1470
1405
  }
1471
- const startTime = Date.now();
1472
- let error = null;
1473
- let screenshotId = null;
1474
- let screenshotPath = null;
1475
- const info = {};
1476
- info.log =
1477
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1478
- info.operation = "containsPattern";
1479
- info.selectors = selectors;
1480
- info.value = text;
1481
- info.pattern = pattern;
1482
1406
  let foundObj = null;
1483
1407
  try {
1484
- 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);
1485
1411
  if (foundObj && foundObj.element) {
1486
- await this.scrollIfNeeded(foundObj.element, info);
1412
+ await this.scrollIfNeeded(foundObj.element, state.info);
1487
1413
  }
1488
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1414
+ await _screenshot(state, this);
1489
1415
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1490
1416
  pattern = pattern.replace("{text}", escapedText);
1491
1417
  let regex = new RegExp(pattern, "im");
1492
- 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))) {
1493
- 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;
1494
1420
  throw new Error("element doesn't contain text " + text);
1495
1421
  }
1496
- return info;
1422
+ return state.info;
1497
1423
  }
1498
1424
  catch (e) {
1499
- //await this.closeUnexpectedPopups();
1500
- this.logger.error("verify element contains text failed " + info.log);
1501
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1502
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1503
- info.screenshotPath = screenshotPath;
1504
- Object.assign(e, { info: info });
1505
- error = e;
1506
- throw e;
1425
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1426
+ await _commandError(state, e, this);
1507
1427
  }
1508
1428
  finally {
1509
- const endTime = Date.now();
1510
- this._reportToWorld(world, {
1511
- element_name: selectors.element_name,
1512
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1513
- value: pattern,
1514
- text: `Verify element contains pattern: ${pattern}`,
1515
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1516
- result: error
1517
- ? {
1518
- status: "FAILED",
1519
- startTime,
1520
- endTime,
1521
- message: error === null || error === void 0 ? void 0 : error.message,
1522
- }
1523
- : {
1524
- status: "PASSED",
1525
- startTime,
1526
- endTime,
1527
- },
1528
- info: info,
1529
- });
1429
+ _commandFinally(state, this);
1530
1430
  }
1531
1431
  }
1532
1432
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1533
- var _a, _b, _c;
1534
- this._validateSelectors(selectors);
1535
- 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
+ };
1536
1448
  if (!text) {
1537
1449
  throw new Error("text is null");
1538
1450
  }
1539
- const startTime = Date.now();
1540
- let error = null;
1541
- let screenshotId = null;
1542
- let screenshotPath = null;
1543
- const info = {};
1544
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1545
- info.operation = "containsText";
1546
- info.selectors = selectors;
1451
+ text = unEscapeString(text);
1547
1452
  const newValue = await this._replaceWithLocalData(text, world);
1548
1453
  if (newValue !== text) {
1549
1454
  this.logger.info(text + "=" + newValue);
1550
1455
  text = newValue;
1551
1456
  }
1552
- info.value = text;
1553
1457
  let foundObj = null;
1554
1458
  try {
1555
- 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);
1556
1461
  if (foundObj && foundObj.element) {
1557
- await this.scrollIfNeeded(foundObj.element, info);
1462
+ await this.scrollIfNeeded(foundObj.element, state.info);
1558
1463
  }
1559
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1464
+ await _screenshot(state, this);
1560
1465
  const dateAlternatives = findDateAlternatives(text);
1561
1466
  const numberAlternatives = findNumberAlternatives(text);
1562
1467
  if (dateAlternatives.date) {
1563
1468
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1564
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1565
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1566
- return info;
1469
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1470
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1471
+ return state.info;
1567
1472
  }
1568
1473
  }
1569
1474
  throw new Error("element doesn't contain text " + text);
1570
1475
  }
1571
1476
  else if (numberAlternatives.number) {
1572
1477
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1573
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1574
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1575
- return info;
1478
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1479
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1480
+ return state.info;
1576
1481
  }
1577
1482
  }
1578
1483
  throw new Error("element doesn't contain text " + text);
1579
1484
  }
1580
- 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))) {
1581
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1582
- 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;
1583
1488
  throw new Error("element doesn't contain text " + text);
1584
1489
  }
1585
- return info;
1490
+ return state.info;
1586
1491
  }
1587
1492
  catch (e) {
1588
- //await this.closeUnexpectedPopups();
1589
- this.logger.error("verify element contains text failed " + info.log);
1590
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1591
- info.screenshotPath = screenshotPath;
1592
- Object.assign(e, { info: info });
1593
- error = e;
1594
- throw e;
1493
+ await _commandError(state, e, this);
1595
1494
  }
1596
1495
  finally {
1597
- const endTime = Date.now();
1598
- this._reportToWorld(world, {
1599
- element_name: selectors.element_name,
1600
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1601
- text: `Verify element contains text: ${text}`,
1602
- value: text,
1603
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1604
- result: error
1605
- ? {
1606
- status: "FAILED",
1607
- startTime,
1608
- endTime,
1609
- message: error === null || error === void 0 ? void 0 : error.message,
1610
- }
1611
- : {
1612
- status: "PASSED",
1613
- startTime,
1614
- endTime,
1615
- },
1616
- info: info,
1617
- });
1496
+ _commandFinally(state, this);
1618
1497
  }
1619
1498
  }
1620
1499
  _getDataFile(world = null) {
@@ -1843,7 +1722,6 @@ class StableBrowser {
1843
1722
  }
1844
1723
  async takeScreenshot(screenshotPath) {
1845
1724
  const playContext = this.context.playContext;
1846
- const client = await playContext.newCDPSession(this.page);
1847
1725
  // Using CDP to capture the screenshot
1848
1726
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1849
1727
  document.body.scrollWidth,
@@ -1853,164 +1731,105 @@ class StableBrowser {
1853
1731
  document.body.clientWidth,
1854
1732
  document.documentElement.clientWidth,
1855
1733
  ])));
1856
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1857
- document.body.scrollHeight,
1858
- document.documentElement.scrollHeight,
1859
- document.body.offsetHeight,
1860
- document.documentElement.offsetHeight,
1861
- document.body.clientHeight,
1862
- document.documentElement.clientHeight,
1863
- ])));
1864
- const { data } = await client.send("Page.captureScreenshot", {
1865
- format: "png",
1866
- // clip: {
1867
- // x: 0,
1868
- // y: 0,
1869
- // width: viewportWidth,
1870
- // height: viewportHeight,
1871
- // scale: 1,
1872
- // },
1873
- });
1874
- if (!screenshotPath) {
1875
- return data;
1876
- }
1877
- let screenshotBuffer = Buffer.from(data, "base64");
1878
- const sharpBuffer = sharp(screenshotBuffer);
1879
- const metadata = await sharpBuffer.metadata();
1880
- //check if you are on retina display and reduce the quality of the image
1881
- if (metadata.width > viewportWidth || metadata.height > viewportHeight) {
1882
- screenshotBuffer = await sharpBuffer
1883
- .resize(viewportWidth, viewportHeight, {
1884
- fit: sharp.fit.inside,
1885
- withoutEnlargement: true,
1886
- })
1887
- .toBuffer();
1888
- }
1889
- fs.writeFileSync(screenshotPath, screenshotBuffer);
1890
- await client.detach();
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();
1755
+ }
1756
+ let image = await Jimp.read(screenshotBuffer);
1757
+ // Get the image dimensions
1758
+ const { width, height } = image.bitmap;
1759
+ const resizeRatio = viewportWidth / width;
1760
+ // Resize the image to fit within the viewport dimensions without enlarging
1761
+ if (width > viewportWidth) {
1762
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1763
+ await image.write(screenshotPath);
1764
+ }
1765
+ else {
1766
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1767
+ }
1891
1768
  }
1892
1769
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1893
- this._validateSelectors(selectors);
1894
- const startTime = Date.now();
1895
- let error = null;
1896
- let screenshotId = null;
1897
- 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
+ };
1898
1780
  await new Promise((resolve) => setTimeout(resolve, 2000));
1899
- const info = {};
1900
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
1901
- info.operation = "verify";
1902
- info.selectors = selectors;
1903
1781
  try {
1904
- const element = await this._locate(selectors, info, _params);
1905
- if (element) {
1906
- await this.scrollIfNeeded(element, info);
1907
- }
1908
- await this._highlightElements(element);
1909
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1910
- await expect(element).toHaveCount(1, { timeout: 10000 });
1911
- return info;
1782
+ await _preCommand(state, this);
1783
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1784
+ return state.info;
1912
1785
  }
1913
1786
  catch (e) {
1914
- //await this.closeUnexpectedPopups();
1915
- this.logger.error("verify failed " + info.log);
1916
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1917
- info.screenshotPath = screenshotPath;
1918
- Object.assign(e, { info: info });
1919
- error = e;
1920
- throw e;
1787
+ await _commandError(state, e, this);
1921
1788
  }
1922
1789
  finally {
1923
- const endTime = Date.now();
1924
- this._reportToWorld(world, {
1925
- element_name: selectors.element_name,
1926
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1927
- text: "Verify element exists in page",
1928
- screenshotId,
1929
- result: error
1930
- ? {
1931
- status: "FAILED",
1932
- startTime,
1933
- endTime,
1934
- message: error === null || error === void 0 ? void 0 : error.message,
1935
- }
1936
- : {
1937
- status: "PASSED",
1938
- startTime,
1939
- endTime,
1940
- },
1941
- info: info,
1942
- });
1790
+ _commandFinally(state, this);
1943
1791
  }
1944
1792
  }
1945
1793
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1946
- this._validateSelectors(selectors);
1947
- const startTime = Date.now();
1948
- let error = null;
1949
- let screenshotId = null;
1950
- 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
+ };
1951
1806
  await new Promise((resolve) => setTimeout(resolve, 2000));
1952
- const info = {};
1953
- info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
1954
- info.operation = "extract";
1955
- info.selectors = selectors;
1956
1807
  try {
1957
- const element = await this._locate(selectors, info, _params);
1958
- await this._highlightElements(element);
1959
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1808
+ await _preCommand(state, this);
1960
1809
  switch (attribute) {
1961
1810
  case "inner_text":
1962
- info.value = await element.innerText();
1811
+ state.value = await state.element.innerText();
1963
1812
  break;
1964
1813
  case "href":
1965
- info.value = await element.getAttribute("href");
1814
+ state.value = await state.element.getAttribute("href");
1966
1815
  break;
1967
1816
  case "value":
1968
- info.value = await element.inputValue();
1817
+ state.value = await state.element.inputValue();
1969
1818
  break;
1970
1819
  default:
1971
- info.value = await element.getAttribute(attribute);
1820
+ state.value = await state.element.getAttribute(attribute);
1972
1821
  break;
1973
1822
  }
1974
- this[variable] = info.value;
1975
- if (world) {
1976
- world[variable] = info.value;
1977
- }
1978
- this.setTestData({ [variable]: info.value }, world);
1979
- this.logger.info("set test data: " + variable + "=" + info.value);
1980
- 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;
1981
1827
  }
1982
1828
  catch (e) {
1983
- //await this.closeUnexpectedPopups();
1984
- this.logger.error("extract failed " + info.log);
1985
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1986
- info.screenshotPath = screenshotPath;
1987
- Object.assign(e, { info: info });
1988
- error = e;
1989
- throw e;
1829
+ await _commandError(state, e, this);
1990
1830
  }
1991
1831
  finally {
1992
- const endTime = Date.now();
1993
- this._reportToWorld(world, {
1994
- element_name: selectors.element_name,
1995
- type: Types.EXTRACT_ATTRIBUTE,
1996
- variable: variable,
1997
- value: info.value,
1998
- text: "Extract attribute from element",
1999
- screenshotId,
2000
- result: error
2001
- ? {
2002
- status: "FAILED",
2003
- startTime,
2004
- endTime,
2005
- message: error === null || error === void 0 ? void 0 : error.message,
2006
- }
2007
- : {
2008
- status: "PASSED",
2009
- startTime,
2010
- endTime,
2011
- },
2012
- info: info,
2013
- });
1832
+ _commandFinally(state, this);
2014
1833
  }
2015
1834
  }
2016
1835
  async extractEmailData(emailAddress, options, world) {
@@ -2087,7 +1906,8 @@ class StableBrowser {
2087
1906
  catch (e) {
2088
1907
  errorCount++;
2089
1908
  if (errorCount > 3) {
2090
- throw e;
1909
+ // throw e;
1910
+ await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
2091
1911
  }
2092
1912
  // ignore
2093
1913
  }
@@ -2198,7 +2018,8 @@ class StableBrowser {
2198
2018
  info.screenshotPath = screenshotPath;
2199
2019
  Object.assign(e, { info: info });
2200
2020
  error = e;
2201
- throw e;
2021
+ // throw e;
2022
+ await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
2202
2023
  }
2203
2024
  finally {
2204
2025
  const endTime = Date.now();
@@ -2211,7 +2032,7 @@ class StableBrowser {
2211
2032
  status: "FAILED",
2212
2033
  startTime,
2213
2034
  endTime,
2214
- message: error === null || error === void 0 ? void 0 : error.message,
2035
+ message: error?.message,
2215
2036
  }
2216
2037
  : {
2217
2038
  status: "PASSED",
@@ -2224,52 +2045,58 @@ class StableBrowser {
2224
2045
  }
2225
2046
  async verifyTextExistInPage(text, options = {}, world = null) {
2226
2047
  text = unEscapeString(text);
2227
- 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
+ };
2228
2060
  const timeout = this._getLoadTimeout(options);
2229
- let error = null;
2230
- let screenshotId = null;
2231
- let screenshotPath = null;
2232
2061
  await new Promise((resolve) => setTimeout(resolve, 2000));
2233
- const info = {};
2234
- info.log = "***** verify text " + text + " exists in page *****\n";
2235
- info.operation = "verifyTextExistInPage";
2236
2062
  const newValue = await this._replaceWithLocalData(text, world);
2237
2063
  if (newValue !== text) {
2238
2064
  this.logger.info(text + "=" + newValue);
2239
2065
  text = newValue;
2240
2066
  }
2241
- info.text = text;
2242
2067
  let dateAlternatives = findDateAlternatives(text);
2243
2068
  let numberAlternatives = findNumberAlternatives(text);
2244
2069
  try {
2070
+ await _preCommand(state, this);
2071
+ state.info.text = text;
2245
2072
  while (true) {
2246
2073
  const frames = this.page.frames();
2247
2074
  let results = [];
2248
2075
  for (let i = 0; i < frames.length; i++) {
2249
2076
  if (dateAlternatives.date) {
2250
2077
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2251
- 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, {});
2252
2079
  result.frame = frames[i];
2253
2080
  results.push(result);
2254
2081
  }
2255
2082
  }
2256
2083
  else if (numberAlternatives.number) {
2257
2084
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2258
- 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, {});
2259
2086
  result.frame = frames[i];
2260
2087
  results.push(result);
2261
2088
  }
2262
2089
  }
2263
2090
  else {
2264
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2091
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2265
2092
  result.frame = frames[i];
2266
2093
  results.push(result);
2267
2094
  }
2268
2095
  }
2269
- info.results = results;
2096
+ state.info.results = results;
2270
2097
  const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2271
2098
  if (resultWithElementsFound.length === 0) {
2272
- if (Date.now() - startTime > timeout) {
2099
+ if (Date.now() - state.startTime > timeout) {
2273
2100
  throw new Error(`Text ${text} not found in page`);
2274
2101
  }
2275
2102
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -2281,44 +2108,89 @@ class StableBrowser {
2281
2108
  await this._highlightElements(frame, dataAttribute);
2282
2109
  const element = await frame.$(dataAttribute);
2283
2110
  if (element) {
2284
- await this.scrollIfNeeded(element, info);
2111
+ await this.scrollIfNeeded(element, state.info);
2285
2112
  await element.dispatchEvent("bvt_verify_page_contains_text");
2286
2113
  }
2287
2114
  }
2288
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2289
- return info;
2115
+ await _screenshot(state, this);
2116
+ return state.info;
2290
2117
  }
2291
2118
  // await expect(element).toHaveCount(1, { timeout: 10000 });
2292
2119
  }
2293
2120
  catch (e) {
2294
- //await this.closeUnexpectedPopups();
2295
- this.logger.error("verify text exist in page failed " + info.log);
2296
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2297
- info.screenshotPath = screenshotPath;
2298
- Object.assign(e, { info: info });
2299
- error = e;
2300
- throw e;
2121
+ await _commandError(state, e, this);
2301
2122
  }
2302
2123
  finally {
2303
- const endTime = Date.now();
2304
- this._reportToWorld(world, {
2305
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2306
- text: "Verify text exists in page",
2307
- screenshotId,
2308
- result: error
2309
- ? {
2310
- status: "FAILED",
2311
- startTime,
2312
- endTime,
2313
- 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
+ }
2314
2163
  }
2315
- : {
2316
- status: "PASSED",
2317
- startTime,
2318
- endTime,
2319
- },
2320
- info: info,
2321
- });
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);
2322
2194
  }
2323
2195
  }
2324
2196
  _getServerUrl() {
@@ -2381,7 +2253,8 @@ class StableBrowser {
2381
2253
  info.screenshotPath = screenshotPath;
2382
2254
  Object.assign(e, { info: info });
2383
2255
  error = e;
2384
- throw e;
2256
+ // throw e;
2257
+ await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
2385
2258
  }
2386
2259
  finally {
2387
2260
  const endTime = Date.now();
@@ -2394,7 +2267,7 @@ class StableBrowser {
2394
2267
  status: "FAILED",
2395
2268
  startTime,
2396
2269
  endTime,
2397
- message: error === null || error === void 0 ? void 0 : error.message,
2270
+ message: error?.message,
2398
2271
  }
2399
2272
  : {
2400
2273
  status: "PASSED",
@@ -2426,7 +2299,7 @@ class StableBrowser {
2426
2299
  this.logger.info("Table data verified");
2427
2300
  }
2428
2301
  async getTableData(selectors, _params = null, options = {}, world = null) {
2429
- this._validateSelectors(selectors);
2302
+ _validateSelectors(selectors);
2430
2303
  const startTime = Date.now();
2431
2304
  let error = null;
2432
2305
  let screenshotId = null;
@@ -2448,7 +2321,8 @@ class StableBrowser {
2448
2321
  info.screenshotPath = screenshotPath;
2449
2322
  Object.assign(e, { info: info });
2450
2323
  error = e;
2451
- throw e;
2324
+ // throw e;
2325
+ await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
2452
2326
  }
2453
2327
  finally {
2454
2328
  const endTime = Date.now();
@@ -2462,7 +2336,7 @@ class StableBrowser {
2462
2336
  status: "FAILED",
2463
2337
  startTime,
2464
2338
  endTime,
2465
- message: error === null || error === void 0 ? void 0 : error.message,
2339
+ message: error?.message,
2466
2340
  }
2467
2341
  : {
2468
2342
  status: "PASSED",
@@ -2474,7 +2348,7 @@ class StableBrowser {
2474
2348
  }
2475
2349
  }
2476
2350
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2477
- this._validateSelectors(selectors);
2351
+ _validateSelectors(selectors);
2478
2352
  if (!query) {
2479
2353
  throw new Error("query is null");
2480
2354
  }
@@ -2613,7 +2487,8 @@ class StableBrowser {
2613
2487
  info.screenshotPath = screenshotPath;
2614
2488
  Object.assign(e, { info: info });
2615
2489
  error = e;
2616
- throw e;
2490
+ // throw e;
2491
+ await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
2617
2492
  }
2618
2493
  finally {
2619
2494
  const endTime = Date.now();
@@ -2627,7 +2502,7 @@ class StableBrowser {
2627
2502
  status: "FAILED",
2628
2503
  startTime,
2629
2504
  endTime,
2630
- message: error === null || error === void 0 ? void 0 : error.message,
2505
+ message: error?.message,
2631
2506
  }
2632
2507
  : {
2633
2508
  status: "PASSED",
@@ -2639,27 +2514,7 @@ class StableBrowser {
2639
2514
  }
2640
2515
  }
2641
2516
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2642
- if (!value) {
2643
- return value;
2644
- }
2645
- // find all the accurance of {{(.*?)}} and replace with the value
2646
- let regex = /{{(.*?)}}/g;
2647
- let matches = value.match(regex);
2648
- if (matches) {
2649
- const testData = this.getTestData(world);
2650
- for (let i = 0; i < matches.length; i++) {
2651
- let match = matches[i];
2652
- let key = match.substring(2, match.length - 2);
2653
- let newValue = objectPath.get(testData, key, null);
2654
- if (newValue !== null) {
2655
- value = value.replace(match, newValue);
2656
- }
2657
- }
2658
- }
2659
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2660
- return await decrypt(value, null, totpWait);
2661
- }
2662
- return value;
2517
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2663
2518
  }
2664
2519
  _getLoadTimeout(options) {
2665
2520
  let timeout = 15000;
@@ -2719,7 +2574,7 @@ class StableBrowser {
2719
2574
  status: "FAILED",
2720
2575
  startTime,
2721
2576
  endTime,
2722
- message: error === null || error === void 0 ? void 0 : error.message,
2577
+ message: error?.message,
2723
2578
  }
2724
2579
  : {
2725
2580
  status: "PASSED",
@@ -2730,39 +2585,28 @@ class StableBrowser {
2730
2585
  }
2731
2586
  }
2732
2587
  async closePage(options = {}, world = null) {
2733
- const startTime = Date.now();
2734
- let error = null;
2735
- let screenshotId = null;
2736
- let screenshotPath = null;
2737
- 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
+ };
2738
2600
  try {
2601
+ await _preCommand(state, this);
2739
2602
  await this.page.close();
2740
2603
  }
2741
2604
  catch (e) {
2742
2605
  console.log(".");
2606
+ await _commandError(state, e, this);
2743
2607
  }
2744
2608
  finally {
2745
- await new Promise((resolve) => setTimeout(resolve, 2000));
2746
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2747
- const endTime = Date.now();
2748
- this._reportToWorld(world, {
2749
- type: Types.CLOSE_PAGE,
2750
- text: "close page",
2751
- screenshotId,
2752
- result: error
2753
- ? {
2754
- status: "FAILED",
2755
- startTime,
2756
- endTime,
2757
- message: error === null || error === void 0 ? void 0 : error.message,
2758
- }
2759
- : {
2760
- status: "PASSED",
2761
- startTime,
2762
- endTime,
2763
- },
2764
- info: info,
2765
- });
2609
+ _commandFinally(state, this);
2766
2610
  }
2767
2611
  }
2768
2612
  async setViewportSize(width, hight, options = {}, world = null) {
@@ -2782,6 +2626,7 @@ class StableBrowser {
2782
2626
  }
2783
2627
  catch (e) {
2784
2628
  console.log(".");
2629
+ await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
2785
2630
  }
2786
2631
  finally {
2787
2632
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2796,7 +2641,7 @@ class StableBrowser {
2796
2641
  status: "FAILED",
2797
2642
  startTime,
2798
2643
  endTime,
2799
- message: error === null || error === void 0 ? void 0 : error.message,
2644
+ message: error?.message,
2800
2645
  }
2801
2646
  : {
2802
2647
  status: "PASSED",
@@ -2818,6 +2663,7 @@ class StableBrowser {
2818
2663
  }
2819
2664
  catch (e) {
2820
2665
  console.log(".");
2666
+ await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
2821
2667
  }
2822
2668
  finally {
2823
2669
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2832,7 +2678,7 @@ class StableBrowser {
2832
2678
  status: "FAILED",
2833
2679
  startTime,
2834
2680
  endTime,
2835
- message: error === null || error === void 0 ? void 0 : error.message,
2681
+ message: error?.message,
2836
2682
  }
2837
2683
  : {
2838
2684
  status: "PASSED",