automation_model 1.0.469-dev → 1.0.469-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,13 +10,15 @@ import { getDateTimeValue } from "./date_time.js";
10
10
  import drawRectangle from "./drawRect.js";
11
11
  //import { closeUnexpectedPopups } from "./popups.js";
12
12
  import { getTableCells, getTableData } from "./table_analyze.js";
13
- import objectPath from "object-path";
14
- import { decrypt } from "./utils.js";
13
+ import { maskValue, replaceWithLocalTestData } from "./utils.js";
15
14
  import csv from "csv-parser";
16
15
  import { Readable } from "node:stream";
17
16
  import readline from "readline";
18
17
  import { getContext } from "./init_browser.js";
19
18
  import { locate_element } from "./locate_element.js";
19
+ import { randomUUID } from "crypto";
20
+ import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
21
+ import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
20
22
  const Types = {
21
23
  CLICK: "click_element",
22
24
  NAVIGATE: "navigate",
@@ -43,20 +45,27 @@ const Types = {
43
45
  VERIFY_VISUAL: "verify_visual",
44
46
  LOAD_DATA: "load_data",
45
47
  SET_INPUT: "set_input",
48
+ WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
46
49
  };
47
50
  export const apps = {};
48
51
  class StableBrowser {
52
+ browser;
53
+ page;
54
+ logger;
55
+ context;
56
+ world;
57
+ project_path = null;
58
+ webLogFile = null;
59
+ networkLogger = null;
60
+ configuration = null;
61
+ appName = "main";
62
+ tags = null;
49
63
  constructor(browser, page, logger = null, context = null, world = null) {
50
64
  this.browser = browser;
51
65
  this.page = page;
52
66
  this.logger = logger;
53
67
  this.context = context;
54
68
  this.world = world;
55
- this.project_path = null;
56
- this.webLogFile = null;
57
- this.networkLogger = null;
58
- this.configuration = null;
59
- this.appName = "main";
60
69
  if (!this.logger) {
61
70
  this.logger = console;
62
71
  }
@@ -81,11 +90,31 @@ class StableBrowser {
81
90
  catch (e) {
82
91
  this.logger.error("unable to read ai_config.json");
83
92
  }
84
- context.pageLoading = { status: false };
85
- context.pages = [this.page];
86
93
  const logFolder = path.join(this.project_path, "logs", "web");
87
94
  this.world = world;
95
+ context.pages = [this.page];
96
+ context.pageLoading = { status: false };
88
97
  this.registerEventListeners(this.context);
98
+ registerNetworkEvents(this.world, this, this.context, this.page);
99
+ registerDownloadEvent(this.page, this.world, this.context);
100
+ }
101
+ async scrollPageToLoadLazyElements() {
102
+ let lastHeight = await this.page.evaluate(() => document.body.scrollHeight);
103
+ let retry = 0;
104
+ while (true) {
105
+ await this.page.evaluate(() => window.scrollBy(0, window.innerHeight));
106
+ await new Promise((resolve) => setTimeout(resolve, 1000));
107
+ let newHeight = await this.page.evaluate(() => document.body.scrollHeight);
108
+ if (newHeight === lastHeight) {
109
+ break;
110
+ }
111
+ lastHeight = newHeight;
112
+ retry++;
113
+ if (retry > 10) {
114
+ break;
115
+ }
116
+ }
117
+ await this.page.evaluate(() => window.scrollTo(0, 0));
89
118
  }
90
119
  registerEventListeners(context) {
91
120
  this.registerConsoleLogListener(this.page, context);
@@ -94,10 +123,17 @@ class StableBrowser {
94
123
  context.pageLoading = { status: false };
95
124
  }
96
125
  context.playContext.on("page", async function (page) {
126
+ if (this.configuration && this.configuration.closePopups === true) {
127
+ console.log("close unexpected popups");
128
+ await page.close();
129
+ return;
130
+ }
97
131
  context.pageLoading.status = true;
98
132
  this.page = page;
99
133
  context.page = page;
100
134
  context.pages.push(page);
135
+ registerNetworkEvents(this.world, this, context, this.page);
136
+ registerDownloadEvent(this.page, this.world, context);
101
137
  page.on("close", async () => {
102
138
  if (this.context && this.context.pages && this.context.pages.length > 1) {
103
139
  this.context.pages.pop();
@@ -127,9 +163,9 @@ class StableBrowser {
127
163
  if (this.appName === appName) {
128
164
  return;
129
165
  }
130
- let newContextCreated = false;
166
+ let navigate = false;
131
167
  if (!apps[appName]) {
132
- let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
168
+ let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
133
169
  newContextCreated = true;
134
170
  apps[appName] = {
135
171
  context: newContext,
@@ -142,8 +178,7 @@ class StableBrowser {
142
178
  this._copyContext(apps[appName], this);
143
179
  apps[this.appName] = tempContext;
144
180
  this.appName = appName;
145
- if (newContextCreated) {
146
- this.registerEventListeners(this.context);
181
+ if (navigate) {
147
182
  await this.goto(this.context.environment.baseUrl);
148
183
  await this.waitForPageLoad();
149
184
  }
@@ -169,7 +204,6 @@ class StableBrowser {
169
204
  this.context.webLogger = [];
170
205
  }
171
206
  page.on("console", async (msg) => {
172
- var _a;
173
207
  const obj = {
174
208
  type: msg.type(),
175
209
  text: msg.text(),
@@ -178,7 +212,7 @@ class StableBrowser {
178
212
  };
179
213
  this.context.webLogger.push(obj);
180
214
  if (msg.type() === "error") {
181
- (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
215
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
182
216
  }
183
217
  });
184
218
  }
@@ -187,7 +221,6 @@ class StableBrowser {
187
221
  this.context.networkLogger = [];
188
222
  }
189
223
  page.on("request", async (data) => {
190
- var _a;
191
224
  const startTime = new Date().getTime();
192
225
  try {
193
226
  const pageUrl = new URL(page.url());
@@ -212,10 +245,10 @@ class StableBrowser {
212
245
  startTime,
213
246
  };
214
247
  context.networkLogger.push(obj);
215
- (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
248
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
216
249
  }
217
250
  catch (error) {
218
- console.error("Error in request listener", error);
251
+ // console.error("Error in request listener", error);
219
252
  context.networkLogger.push({
220
253
  error: "not able to listen",
221
254
  message: error.message,
@@ -237,20 +270,6 @@ class StableBrowser {
237
270
  timeout: 60000,
238
271
  });
239
272
  }
240
- _validateSelectors(selectors) {
241
- if (!selectors) {
242
- throw new Error("selectors is null");
243
- }
244
- if (!selectors.locators) {
245
- throw new Error("selectors.locators is null");
246
- }
247
- if (!Array.isArray(selectors.locators)) {
248
- throw new Error("selectors.locators expected to be array");
249
- }
250
- if (selectors.locators.length === 0) {
251
- throw new Error("selectors.locators expected to be non empty array");
252
- }
253
- }
254
273
  _fixUsingParams(text, _params) {
255
274
  if (!_params || typeof text !== "string") {
256
275
  return text;
@@ -318,7 +337,7 @@ class StableBrowser {
318
337
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
319
338
  }
320
339
  }
321
- if (locator === null || locator === void 0 ? void 0 : locator.engine) {
340
+ if (locator?.engine) {
322
341
  if (locator.engine === "css") {
323
342
  locatorReturn = scope.locator(locator.selector);
324
343
  }
@@ -342,7 +361,7 @@ class StableBrowser {
342
361
  if (css && css.locator) {
343
362
  css = css.locator;
344
363
  }
345
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
364
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
346
365
  if (result.elementCount === 0) {
347
366
  return;
348
367
  }
@@ -357,7 +376,7 @@ class StableBrowser {
357
376
  }
358
377
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
359
378
  //const stringifyText = JSON.stringify(text);
360
- return await scope.evaluate(([text, tag, regex, partial]) => {
379
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
361
380
  function isParent(parent, child) {
362
381
  let currentNode = child.parentNode;
363
382
  while (currentNode !== null) {
@@ -394,7 +413,7 @@ class StableBrowser {
394
413
  }
395
414
  document.collectAllShadowDomElements = collectAllShadowDomElements;
396
415
  if (!tag) {
397
- tag = "*";
416
+ tag = "*:not(script, style, head)";
398
417
  }
399
418
  let regexpSearch = document.getRegex(text);
400
419
  if (regexpSearch) {
@@ -476,6 +495,15 @@ class StableBrowser {
476
495
  }, [text1, tag1, regex1, partial1]);
477
496
  }
478
497
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
498
+ if (!info) {
499
+ info = {};
500
+ }
501
+ if (!info.failCause) {
502
+ info.failCause = {};
503
+ }
504
+ if (!info.log) {
505
+ info.log = "";
506
+ }
479
507
  let locatorSearch = selectorHierarchy[index];
480
508
  try {
481
509
  locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
@@ -488,13 +516,18 @@ class StableBrowser {
488
516
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
489
517
  let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
490
518
  if (!locatorString) {
519
+ info.failCause.textNotFound = true;
520
+ info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
491
521
  return;
492
522
  }
493
523
  locator = this._getLocator({ css: locatorString }, scope, _params);
494
524
  }
495
525
  else if (locatorSearch.text) {
496
- let result = await this._locateElementByText(scope, this._fixUsingParams(locatorSearch.text, _params), locatorSearch.tag, false, locatorSearch.partial === true, _params);
526
+ let text = this._fixUsingParams(locatorSearch.text, _params);
527
+ let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
497
528
  if (result.elementCount === 0) {
529
+ info.failCause.textNotFound = true;
530
+ info.failCause.lastError = "failed to locate element by text: " + text;
498
531
  return;
499
532
  }
500
533
  locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
@@ -511,6 +544,9 @@ class StableBrowser {
511
544
  // cssHref = true;
512
545
  // }
513
546
  let count = await locator.count();
547
+ if (count > 0 && !info.failCause.count) {
548
+ info.failCause.count = count;
549
+ }
514
550
  //info.log += "total elements found " + count + "\n";
515
551
  //let visibleCount = 0;
516
552
  let visibleLocator = null;
@@ -528,6 +564,8 @@ class StableBrowser {
528
564
  foundLocators.push(locator.nth(j));
529
565
  }
530
566
  else {
567
+ info.failCause.visible = visible;
568
+ info.failCause.enabled = enabled;
531
569
  if (!info.printMessages) {
532
570
  info.printMessages = {};
533
571
  }
@@ -539,6 +577,11 @@ class StableBrowser {
539
577
  }
540
578
  }
541
579
  async closeUnexpectedPopups(info, _params) {
580
+ if (!info) {
581
+ info = {};
582
+ info.failCause = {};
583
+ info.log = "";
584
+ }
542
585
  if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
543
586
  if (!info) {
544
587
  info = {};
@@ -579,7 +622,10 @@ class StableBrowser {
579
622
  }
580
623
  return { rerun: false };
581
624
  }
582
- async _locate(selectors, info, _params, timeout = 30000) {
625
+ async _locate(selectors, info, _params, timeout) {
626
+ if (!timeout) {
627
+ timeout = 30000;
628
+ }
583
629
  for (let i = 0; i < 3; i++) {
584
630
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
585
631
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -593,16 +639,15 @@ class StableBrowser {
593
639
  }
594
640
  throw new Error("unable to locate element " + JSON.stringify(selectors));
595
641
  }
596
- async _locate_internal(selectors, info, _params, timeout = 30000) {
597
- let highPriorityTimeout = 5000;
598
- let visibleOnlyTimeout = 6000;
599
- let startTime = performance.now();
600
- let locatorsCount = 0;
601
- //let arrayMode = Array.isArray(selectors);
642
+ async _findFrameScope(selectors, timeout = 30000, info) {
643
+ if (!info) {
644
+ info = {};
645
+ info.failCause = {};
646
+ info.log = "";
647
+ }
602
648
  let scope = this.page;
603
- // for the simple click usecase
604
649
  if (selectors.frame) {
605
- scope = selectors.frame;
650
+ return selectors.frame;
606
651
  }
607
652
  if (selectors.iframe_src || selectors.frameLocators) {
608
653
  const findFrame = async (frame, framescope) => {
@@ -630,7 +675,6 @@ class StableBrowser {
630
675
  }
631
676
  return framescope;
632
677
  };
633
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
634
678
  while (true) {
635
679
  let frameFound = false;
636
680
  if (selectors.nestFrmLoc) {
@@ -654,6 +698,8 @@ class StableBrowser {
654
698
  if (!scope) {
655
699
  info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
656
700
  if (performance.now() - startTime > timeout) {
701
+ info.failCause.iframeNotFound = true;
702
+ info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
657
703
  throw new Error("unable to locate iframe " + selectors.iframe_src);
658
704
  }
659
705
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -663,6 +709,31 @@ class StableBrowser {
663
709
  }
664
710
  }
665
711
  }
712
+ if (!scope) {
713
+ scope = this.page;
714
+ }
715
+ return scope;
716
+ }
717
+ async _getDocumentBody(selectors, timeout = 30000, info) {
718
+ let scope = await this._findFrameScope(selectors, timeout, info);
719
+ return scope.evaluate(() => {
720
+ var bodyContent = document.body.innerHTML;
721
+ return bodyContent;
722
+ });
723
+ }
724
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
725
+ if (!info) {
726
+ info = {};
727
+ info.failCause = {};
728
+ info.log = "";
729
+ }
730
+ let highPriorityTimeout = 5000;
731
+ let visibleOnlyTimeout = 6000;
732
+ let startTime = performance.now();
733
+ let locatorsCount = 0;
734
+ let lazy_scroll = false;
735
+ //let arrayMode = Array.isArray(selectors);
736
+ let scope = await this._findFrameScope(selectors, timeout, info);
666
737
  let selectorsLocators = null;
667
738
  selectorsLocators = selectors.locators;
668
739
  // group selectors by priority
@@ -755,6 +826,10 @@ class StableBrowser {
755
826
  if (performance.now() - startTime > highPriorityTimeout) {
756
827
  info.log += "high priority timeout, will try all elements" + "\n";
757
828
  highPriorityOnly = false;
829
+ if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
830
+ lazy_scroll = true;
831
+ await this.scrollPageToLoadLazyElements();
832
+ }
758
833
  }
759
834
  if (performance.now() - startTime > visibleOnlyTimeout) {
760
835
  info.log += "visible only timeout, will try all elements" + "\n";
@@ -764,6 +839,8 @@ class StableBrowser {
764
839
  }
765
840
  this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
766
841
  info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
842
+ info.failCause.locatorNotFound = true;
843
+ info.failCause.lastError = "failed to locate unique element";
767
844
  throw new Error("failed to locate first element no elements found, " + info.log);
768
845
  }
769
846
  async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
@@ -795,6 +872,9 @@ class StableBrowser {
795
872
  });
796
873
  result.locatorIndex = i;
797
874
  }
875
+ if (foundLocators.length > 1) {
876
+ info.failCause.foundMultiple = true;
877
+ }
798
878
  }
799
879
  return result;
800
880
  }
@@ -807,12 +887,12 @@ class StableBrowser {
807
887
  while (true) {
808
888
  try {
809
889
  const result = await locate_element(this.context, elementDescription, "click");
810
- if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
890
+ if (result?.elementNumber >= 0) {
811
891
  const selectors = {
812
- frame: result === null || result === void 0 ? void 0 : result.frame,
892
+ frame: result?.frame,
813
893
  locators: [
814
894
  {
815
- css: result === null || result === void 0 ? void 0 : result.css,
895
+ css: result?.css,
816
896
  },
817
897
  ],
818
898
  };
@@ -822,7 +902,8 @@ class StableBrowser {
822
902
  }
823
903
  catch (e) {
824
904
  if (performance.now() - startTime > timeout) {
825
- throw e;
905
+ // throw e;
906
+ await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
826
907
  }
827
908
  }
828
909
  await new Promise((resolve) => setTimeout(resolve, 3000));
@@ -837,12 +918,12 @@ class StableBrowser {
837
918
  while (true) {
838
919
  try {
839
920
  const result = await locate_element(this.context, elementDescription, "fill", value);
840
- if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
921
+ if (result?.elementNumber >= 0) {
841
922
  const selectors = {
842
- frame: result === null || result === void 0 ? void 0 : result.frame,
923
+ frame: result?.frame,
843
924
  locators: [
844
925
  {
845
- css: result === null || result === void 0 ? void 0 : result.css,
926
+ css: result?.css,
846
927
  },
847
928
  ],
848
929
  };
@@ -852,92 +933,68 @@ class StableBrowser {
852
933
  }
853
934
  catch (e) {
854
935
  if (performance.now() - startTime > timeout) {
855
- throw e;
936
+ // throw e;
937
+ await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
856
938
  }
857
939
  }
858
940
  await new Promise((resolve) => setTimeout(resolve, 3000));
859
941
  }
860
942
  }
861
943
  async click(selectors, _params, options = {}, world = null) {
862
- this._validateSelectors(selectors);
863
- const startTime = Date.now();
864
- if (options && options.context) {
865
- selectors.locators[0].text = options.context;
866
- }
867
- const info = {};
868
- info.log = "***** click on " + selectors.element_name + " *****\n";
869
- info.operation = "click";
870
- info.selectors = selectors;
871
- let error = null;
872
- let screenshotId = null;
873
- let screenshotPath = null;
944
+ const state = {
945
+ selectors,
946
+ _params,
947
+ options,
948
+ world,
949
+ text: "Click element",
950
+ type: Types.CLICK,
951
+ operation: "click",
952
+ log: "***** click on " + selectors.element_name + " *****\n",
953
+ };
874
954
  try {
875
- let element = await this._locate(selectors, info, _params);
876
- await this.scrollIfNeeded(element, info);
877
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
955
+ await _preCommand(state, this);
956
+ if (state.options && state.options.context) {
957
+ state.selectors.locators[0].text = state.options.context;
958
+ }
878
959
  try {
879
- await this._highlightElements(element);
880
- await element.click();
881
- await new Promise((resolve) => setTimeout(resolve, 1000));
960
+ await state.element.click();
961
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
882
962
  }
883
963
  catch (e) {
884
964
  // await this.closeUnexpectedPopups();
885
- info.log += "click failed, will try again" + "\n";
886
- element = await this._locate(selectors, info, _params);
887
- await element.dispatchEvent("click");
888
- await new Promise((resolve) => setTimeout(resolve, 1000));
965
+ state.element = await this._locate(selectors, state.info, _params);
966
+ await state.element.dispatchEvent("click");
967
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
889
968
  }
890
969
  await this.waitForPageLoad();
891
- return info;
970
+ return state.info;
892
971
  }
893
972
  catch (e) {
894
- this.logger.error("click failed " + info.log);
895
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
896
- info.screenshotPath = screenshotPath;
897
- Object.assign(e, { info: info });
898
- error = e;
899
- throw e;
973
+ await _commandError(state, e, this);
900
974
  }
901
975
  finally {
902
- const endTime = Date.now();
903
- this._reportToWorld(world, {
904
- element_name: selectors.element_name,
905
- type: Types.CLICK,
906
- text: `Click element`,
907
- screenshotId,
908
- result: error
909
- ? {
910
- status: "FAILED",
911
- startTime,
912
- endTime,
913
- message: error === null || error === void 0 ? void 0 : error.message,
914
- }
915
- : {
916
- status: "PASSED",
917
- startTime,
918
- endTime,
919
- },
920
- info: info,
921
- });
976
+ _commandFinally(state, this);
922
977
  }
923
978
  }
924
979
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
925
- this._validateSelectors(selectors);
926
- const startTime = Date.now();
927
- const info = {};
928
- info.log = "";
929
- info.operation = "setCheck";
930
- info.checked = checked;
931
- info.selectors = selectors;
932
- let error = null;
933
- let screenshotId = null;
934
- let screenshotPath = null;
980
+ const state = {
981
+ selectors,
982
+ _params,
983
+ options,
984
+ world,
985
+ type: checked ? Types.CHECK : Types.UNCHECK,
986
+ text: checked ? `Check element` : `Uncheck element`,
987
+ operation: "setCheck",
988
+ log: "***** check " + selectors.element_name + " *****\n",
989
+ };
935
990
  try {
936
- let element = await this._locate(selectors, info, _params);
937
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
991
+ await _preCommand(state, this);
992
+ state.info.checked = checked;
993
+ // let element = await this._locate(selectors, info, _params);
994
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
938
995
  try {
939
- await this._highlightElements(element);
940
- await element.setChecked(checked);
996
+ // await this._highlightElements(element);
997
+ await state.element.setChecked(checked);
941
998
  await new Promise((resolve) => setTimeout(resolve, 1000));
942
999
  }
943
1000
  catch (e) {
@@ -946,179 +1003,108 @@ class StableBrowser {
946
1003
  }
947
1004
  else {
948
1005
  //await this.closeUnexpectedPopups();
949
- info.log += "setCheck failed, will try again" + "\n";
950
- element = await this._locate(selectors, info, _params);
951
- await element.setChecked(checked, { timeout: 5000, force: true });
1006
+ state.info.log += "setCheck failed, will try again" + "\n";
1007
+ state.element = await this._locate(selectors, state.info, _params);
1008
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
952
1009
  await new Promise((resolve) => setTimeout(resolve, 1000));
953
1010
  }
954
1011
  }
955
1012
  await this.waitForPageLoad();
956
- return info;
1013
+ return state.info;
957
1014
  }
958
1015
  catch (e) {
959
- this.logger.error("setCheck failed " + info.log);
960
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
961
- info.screenshotPath = screenshotPath;
962
- Object.assign(e, { info: info });
963
- error = e;
964
- throw e;
1016
+ await _commandError(state, e, this);
965
1017
  }
966
1018
  finally {
967
- const endTime = Date.now();
968
- this._reportToWorld(world, {
969
- element_name: selectors.element_name,
970
- type: checked ? Types.CHECK : Types.UNCHECK,
971
- text: checked ? `Check element` : `Uncheck element`,
972
- screenshotId,
973
- result: error
974
- ? {
975
- status: "FAILED",
976
- startTime,
977
- endTime,
978
- message: error === null || error === void 0 ? void 0 : error.message,
979
- }
980
- : {
981
- status: "PASSED",
982
- startTime,
983
- endTime,
984
- },
985
- info: info,
986
- });
1019
+ _commandFinally(state, this);
987
1020
  }
988
1021
  }
989
1022
  async hover(selectors, _params, options = {}, world = null) {
990
- this._validateSelectors(selectors);
991
- const startTime = Date.now();
992
- const info = {};
993
- info.log = "";
994
- info.operation = "hover";
995
- info.selectors = selectors;
996
- let error = null;
997
- let screenshotId = null;
998
- let screenshotPath = null;
1023
+ const state = {
1024
+ selectors,
1025
+ _params,
1026
+ options,
1027
+ world,
1028
+ type: Types.HOVER,
1029
+ text: `Hover element`,
1030
+ operation: "hover",
1031
+ log: "***** hover " + selectors.element_name + " *****\n",
1032
+ };
999
1033
  try {
1000
- let element = await this._locate(selectors, info, _params);
1001
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1034
+ await _preCommand(state, this);
1002
1035
  try {
1003
- await this._highlightElements(element);
1004
- await element.hover();
1036
+ await state.element.hover();
1005
1037
  await new Promise((resolve) => setTimeout(resolve, 1000));
1006
1038
  }
1007
1039
  catch (e) {
1008
1040
  //await this.closeUnexpectedPopups();
1009
- info.log += "hover failed, will try again" + "\n";
1010
- element = await this._locate(selectors, info, _params);
1011
- await element.hover({ timeout: 10000 });
1041
+ state.info.log += "hover failed, will try again" + "\n";
1042
+ state.element = await this._locate(selectors, state.info, _params);
1043
+ await state.element.hover({ timeout: 10000 });
1012
1044
  await new Promise((resolve) => setTimeout(resolve, 1000));
1013
1045
  }
1014
1046
  await this.waitForPageLoad();
1015
- return info;
1047
+ return state.info;
1016
1048
  }
1017
1049
  catch (e) {
1018
- this.logger.error("hover failed " + info.log);
1019
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1020
- info.screenshotPath = screenshotPath;
1021
- Object.assign(e, { info: info });
1022
- error = e;
1023
- throw e;
1050
+ await _commandError(state, e, this);
1024
1051
  }
1025
1052
  finally {
1026
- const endTime = Date.now();
1027
- this._reportToWorld(world, {
1028
- element_name: selectors.element_name,
1029
- type: Types.HOVER,
1030
- text: `Hover element`,
1031
- screenshotId,
1032
- result: error
1033
- ? {
1034
- status: "FAILED",
1035
- startTime,
1036
- endTime,
1037
- message: error === null || error === void 0 ? void 0 : error.message,
1038
- }
1039
- : {
1040
- status: "PASSED",
1041
- startTime,
1042
- endTime,
1043
- },
1044
- info: info,
1045
- });
1053
+ _commandFinally(state, this);
1046
1054
  }
1047
1055
  }
1048
1056
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
1049
- this._validateSelectors(selectors);
1050
1057
  if (!values) {
1051
1058
  throw new Error("values is null");
1052
1059
  }
1053
- const startTime = Date.now();
1054
- let error = null;
1055
- let screenshotId = null;
1056
- let screenshotPath = null;
1057
- const info = {};
1058
- info.log = "";
1059
- info.operation = "selectOptions";
1060
- info.selectors = selectors;
1060
+ const state = {
1061
+ selectors,
1062
+ _params,
1063
+ options,
1064
+ world,
1065
+ value: values.toString(),
1066
+ type: Types.SELECT,
1067
+ text: `Select option: ${values}`,
1068
+ operation: "selectOption",
1069
+ log: "***** select option " + selectors.element_name + " *****\n",
1070
+ };
1061
1071
  try {
1062
- let element = await this._locate(selectors, info, _params);
1063
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1072
+ await _preCommand(state, this);
1064
1073
  try {
1065
- await this._highlightElements(element);
1066
- await element.selectOption(values);
1074
+ await state.element.selectOption(values);
1067
1075
  }
1068
1076
  catch (e) {
1069
1077
  //await this.closeUnexpectedPopups();
1070
- info.log += "selectOption failed, will try force" + "\n";
1071
- await element.selectOption(values, { timeout: 10000, force: true });
1078
+ state.info.log += "selectOption failed, will try force" + "\n";
1079
+ await state.element.selectOption(values, { timeout: 10000, force: true });
1072
1080
  }
1073
1081
  await this.waitForPageLoad();
1074
- return info;
1082
+ return state.info;
1075
1083
  }
1076
1084
  catch (e) {
1077
- this.logger.error("selectOption failed " + info.log);
1078
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1079
- info.screenshotPath = screenshotPath;
1080
- Object.assign(e, { info: info });
1081
- this.logger.info("click failed, will try next selector");
1082
- error = e;
1083
- throw e;
1085
+ await _commandError(state, e, this);
1084
1086
  }
1085
1087
  finally {
1086
- const endTime = Date.now();
1087
- this._reportToWorld(world, {
1088
- element_name: selectors.element_name,
1089
- type: Types.SELECT,
1090
- text: `Select option: ${values}`,
1091
- value: values.toString(),
1092
- screenshotId,
1093
- result: error
1094
- ? {
1095
- status: "FAILED",
1096
- startTime,
1097
- endTime,
1098
- message: error === null || error === void 0 ? void 0 : error.message,
1099
- }
1100
- : {
1101
- status: "PASSED",
1102
- startTime,
1103
- endTime,
1104
- },
1105
- info: info,
1106
- });
1088
+ _commandFinally(state, this);
1107
1089
  }
1108
1090
  }
1109
1091
  async type(_value, _params = null, options = {}, world = null) {
1110
- const startTime = Date.now();
1111
- let error = null;
1112
- let screenshotId = null;
1113
- let screenshotPath = null;
1114
- const info = {};
1115
- info.log = "";
1116
- info.operation = "type";
1117
- _value = this._fixUsingParams(_value, _params);
1118
- info.value = _value;
1092
+ const state = {
1093
+ value: _value,
1094
+ _params,
1095
+ options,
1096
+ world,
1097
+ locate: false,
1098
+ scroll: false,
1099
+ highlight: false,
1100
+ type: Types.TYPE_PRESS,
1101
+ text: `Type value: ${_value}`,
1102
+ operation: "type",
1103
+ log: "",
1104
+ };
1119
1105
  try {
1120
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1121
- const valueSegment = _value.split("&&");
1106
+ await _preCommand(state, this);
1107
+ const valueSegment = state.value.split("&&");
1122
1108
  for (let i = 0; i < valueSegment.length; i++) {
1123
1109
  if (i > 0) {
1124
1110
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1138,134 +1124,76 @@ class StableBrowser {
1138
1124
  await this.page.keyboard.type(value);
1139
1125
  }
1140
1126
  }
1141
- return info;
1127
+ return state.info;
1142
1128
  }
1143
1129
  catch (e) {
1144
- //await this.closeUnexpectedPopups();
1145
- this.logger.error("type failed " + info.log);
1146
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1147
- info.screenshotPath = screenshotPath;
1148
- Object.assign(e, { info: info });
1149
- error = e;
1150
- throw e;
1130
+ await _commandError(state, e, this);
1151
1131
  }
1152
1132
  finally {
1153
- const endTime = Date.now();
1154
- this._reportToWorld(world, {
1155
- type: Types.TYPE_PRESS,
1156
- screenshotId,
1157
- value: _value,
1158
- text: `type value: ${_value}`,
1159
- result: error
1160
- ? {
1161
- status: "FAILED",
1162
- startTime,
1163
- endTime,
1164
- message: error === null || error === void 0 ? void 0 : error.message,
1165
- }
1166
- : {
1167
- status: "PASSED",
1168
- startTime,
1169
- endTime,
1170
- },
1171
- info: info,
1172
- });
1133
+ _commandFinally(state, this);
1173
1134
  }
1174
1135
  }
1175
1136
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1176
- // set input value for non fillable inputs like date, time, range, color, etc.
1177
- this._validateSelectors(selectors);
1178
- const startTime = Date.now();
1179
- const info = {};
1180
- info.log = "***** set input value " + selectors.element_name + " *****\n";
1181
- info.operation = "setInputValue";
1182
- info.selectors = selectors;
1183
- value = this._fixUsingParams(value, _params);
1184
- info.value = value;
1185
- let error = null;
1186
- let screenshotId = null;
1187
- let screenshotPath = null;
1137
+ const state = {
1138
+ selectors,
1139
+ _params,
1140
+ value,
1141
+ options,
1142
+ world,
1143
+ type: Types.SET_INPUT,
1144
+ text: `Set input value`,
1145
+ operation: "setInputValue",
1146
+ log: "***** set input value " + selectors.element_name + " *****\n",
1147
+ };
1188
1148
  try {
1189
- value = await this._replaceWithLocalData(value, this);
1190
- let element = await this._locate(selectors, info, _params);
1191
- await this.scrollIfNeeded(element, info);
1192
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1193
- await this._highlightElements(element);
1149
+ await _preCommand(state, this);
1150
+ let value = await this._replaceWithLocalData(state.value, this);
1194
1151
  try {
1195
- await element.evaluateHandle((el, value) => {
1152
+ await state.element.evaluateHandle((el, value) => {
1196
1153
  el.value = value;
1197
1154
  }, value);
1198
1155
  }
1199
1156
  catch (error) {
1200
1157
  this.logger.error("setInputValue failed, will try again");
1201
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1202
- info.screenshotPath = screenshotPath;
1203
- Object.assign(error, { info: info });
1204
- await element.evaluateHandle((el, value) => {
1158
+ await _screenshot(state, this);
1159
+ Object.assign(error, { info: state.info });
1160
+ await state.element.evaluateHandle((el, value) => {
1205
1161
  el.value = value;
1206
1162
  });
1207
1163
  }
1208
1164
  }
1209
1165
  catch (e) {
1210
- this.logger.error("setInputValue failed " + info.log);
1211
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1212
- info.screenshotPath = screenshotPath;
1213
- Object.assign(e, { info: info });
1214
- error = e;
1215
- throw e;
1166
+ await _commandError(state, e, this);
1216
1167
  }
1217
1168
  finally {
1218
- const endTime = Date.now();
1219
- this._reportToWorld(world, {
1220
- element_name: selectors.element_name,
1221
- type: Types.SET_INPUT,
1222
- text: `Set input value`,
1223
- value: value,
1224
- screenshotId,
1225
- result: error
1226
- ? {
1227
- status: "FAILED",
1228
- startTime,
1229
- endTime,
1230
- message: error === null || error === void 0 ? void 0 : error.message,
1231
- }
1232
- : {
1233
- status: "PASSED",
1234
- startTime,
1235
- endTime,
1236
- },
1237
- info: info,
1238
- });
1169
+ _commandFinally(state, this);
1239
1170
  }
1240
1171
  }
1241
1172
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1242
- this._validateSelectors(selectors);
1243
- const startTime = Date.now();
1244
- let error = null;
1245
- let screenshotId = null;
1246
- let screenshotPath = null;
1247
- const info = {};
1248
- info.log = "";
1249
- info.operation = Types.SET_DATE_TIME;
1250
- info.selectors = selectors;
1251
- info.value = value;
1173
+ const state = {
1174
+ selectors,
1175
+ _params,
1176
+ value: await this._replaceWithLocalData(value, this),
1177
+ options,
1178
+ world,
1179
+ type: Types.SET_DATE_TIME,
1180
+ text: `Set date time value: ${value}`,
1181
+ operation: "setDateTime",
1182
+ log: "***** set date time value " + selectors.element_name + " *****\n",
1183
+ throwError: false,
1184
+ };
1252
1185
  try {
1253
- value = await this._replaceWithLocalData(value, this);
1254
- let element = await this._locate(selectors, info, _params);
1255
- //insert red border around the element
1256
- await this.scrollIfNeeded(element, info);
1257
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1258
- await this._highlightElements(element);
1186
+ await _preCommand(state, this);
1259
1187
  try {
1260
- await element.click();
1188
+ await state.element.click();
1261
1189
  await new Promise((resolve) => setTimeout(resolve, 500));
1262
1190
  if (format) {
1263
- value = dayjs(value).format(format);
1264
- await element.fill(value);
1191
+ state.value = dayjs(state.value).format(format);
1192
+ await state.element.fill(state.value);
1265
1193
  }
1266
1194
  else {
1267
- const dateTimeValue = await getDateTimeValue({ value, element });
1268
- await element.evaluateHandle((el, dateTimeValue) => {
1195
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1196
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1269
1197
  el.value = ""; // clear input
1270
1198
  el.value = dateTimeValue;
1271
1199
  }, dateTimeValue);
@@ -1278,20 +1206,19 @@ class StableBrowser {
1278
1206
  }
1279
1207
  catch (err) {
1280
1208
  //await this.closeUnexpectedPopups();
1281
- this.logger.error("setting date time input failed " + JSON.stringify(info));
1209
+ this.logger.error("setting date time input failed " + JSON.stringify(state.info));
1282
1210
  this.logger.info("Trying again");
1283
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1284
- info.screenshotPath = screenshotPath;
1285
- Object.assign(err, { info: info });
1211
+ await _screenshot(state, this);
1212
+ Object.assign(err, { info: state.info });
1286
1213
  await element.click();
1287
1214
  await new Promise((resolve) => setTimeout(resolve, 500));
1288
1215
  if (format) {
1289
- value = dayjs(value).format(format);
1290
- await element.fill(value);
1216
+ state.value = dayjs(state.value).format(format);
1217
+ await state.element.fill(state.value);
1291
1218
  }
1292
1219
  else {
1293
- const dateTimeValue = await getDateTimeValue({ value, element });
1294
- await element.evaluateHandle((el, dateTimeValue) => {
1220
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1221
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1295
1222
  el.value = ""; // clear input
1296
1223
  el.value = dateTimeValue;
1297
1224
  }, dateTimeValue);
@@ -1304,60 +1231,39 @@ class StableBrowser {
1304
1231
  }
1305
1232
  }
1306
1233
  catch (e) {
1307
- error = e;
1308
- throw e;
1234
+ await _commandError(state, e, this);
1309
1235
  }
1310
1236
  finally {
1311
- const endTime = Date.now();
1312
- this._reportToWorld(world, {
1313
- element_name: selectors.element_name,
1314
- type: Types.SET_DATE_TIME,
1315
- screenshotId,
1316
- value: value,
1317
- text: `setDateTime input with value: ${value}`,
1318
- result: error
1319
- ? {
1320
- status: "FAILED",
1321
- startTime,
1322
- endTime,
1323
- message: error === null || error === void 0 ? void 0 : error.message,
1324
- }
1325
- : {
1326
- status: "PASSED",
1327
- startTime,
1328
- endTime,
1329
- },
1330
- info: info,
1331
- });
1237
+ _commandFinally(state, this);
1332
1238
  }
1333
1239
  }
1334
1240
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1335
1241
  _value = unEscapeString(_value);
1336
- this._validateSelectors(selectors);
1337
- const startTime = Date.now();
1338
- let error = null;
1339
- let screenshotId = null;
1340
- let screenshotPath = null;
1341
- const info = {};
1342
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1343
- info.operation = "clickType";
1344
- info.selectors = selectors;
1345
1242
  const newValue = await this._replaceWithLocalData(_value, world);
1243
+ const state = {
1244
+ selectors,
1245
+ _params,
1246
+ value: newValue,
1247
+ originalValue: _value,
1248
+ options,
1249
+ world,
1250
+ type: Types.FILL,
1251
+ text: `Click type input with value: ${_value}`,
1252
+ operation: "clickType",
1253
+ log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
1254
+ };
1346
1255
  if (newValue !== _value) {
1347
1256
  //this.logger.info(_value + "=" + newValue);
1348
1257
  _value = newValue;
1349
1258
  }
1350
- info.value = _value;
1351
1259
  try {
1352
- let element = await this._locate(selectors, info, _params);
1353
- //insert red border around the element
1354
- await this.scrollIfNeeded(element, info);
1355
- await this._highlightElements(element);
1260
+ await _preCommand(state, this);
1261
+ state.info.value = _value;
1356
1262
  if (options === null || options === undefined || !options.press) {
1357
1263
  try {
1358
- let currentValue = await element.inputValue();
1264
+ let currentValue = await state.element.inputValue();
1359
1265
  if (currentValue) {
1360
- await element.fill("");
1266
+ await state.element.fill("");
1361
1267
  }
1362
1268
  }
1363
1269
  catch (e) {
@@ -1366,22 +1272,22 @@ class StableBrowser {
1366
1272
  }
1367
1273
  if (options === null || options === undefined || options.press) {
1368
1274
  try {
1369
- await element.click({ timeout: 5000 });
1275
+ await state.element.click({ timeout: 5000 });
1370
1276
  }
1371
1277
  catch (e) {
1372
- await element.dispatchEvent("click");
1278
+ await state.element.dispatchEvent("click");
1373
1279
  }
1374
1280
  }
1375
1281
  else {
1376
1282
  try {
1377
- await element.focus();
1283
+ await state.element.focus();
1378
1284
  }
1379
1285
  catch (e) {
1380
- await element.dispatchEvent("focus");
1286
+ await state.element.dispatchEvent("focus");
1381
1287
  }
1382
1288
  }
1383
1289
  await new Promise((resolve) => setTimeout(resolve, 500));
1384
- const valueSegment = _value.split("&&");
1290
+ const valueSegment = state.value.split("&&");
1385
1291
  for (let i = 0; i < valueSegment.length; i++) {
1386
1292
  if (i > 0) {
1387
1293
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1401,14 +1307,14 @@ class StableBrowser {
1401
1307
  await new Promise((resolve) => setTimeout(resolve, 500));
1402
1308
  }
1403
1309
  }
1404
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1310
+ await _screenshot(state, this);
1405
1311
  if (enter === true) {
1406
1312
  await new Promise((resolve) => setTimeout(resolve, 2000));
1407
1313
  await this.page.keyboard.press("Enter");
1408
1314
  await this.waitForPageLoad();
1409
1315
  }
1410
1316
  else if (enter === false) {
1411
- await element.dispatchEvent("change");
1317
+ await state.element.dispatchEvent("change");
1412
1318
  //await this.page.keyboard.press("Tab");
1413
1319
  }
1414
1320
  else {
@@ -1417,104 +1323,50 @@ class StableBrowser {
1417
1323
  await this.waitForPageLoad();
1418
1324
  }
1419
1325
  }
1420
- return info;
1326
+ return state.info;
1421
1327
  }
1422
1328
  catch (e) {
1423
- //await this.closeUnexpectedPopups();
1424
- this.logger.error("fill failed " + JSON.stringify(info));
1425
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1426
- info.screenshotPath = screenshotPath;
1427
- Object.assign(e, { info: info });
1428
- error = e;
1429
- throw e;
1329
+ await _commandError(state, e, this);
1430
1330
  }
1431
1331
  finally {
1432
- const endTime = Date.now();
1433
- this._reportToWorld(world, {
1434
- element_name: selectors.element_name,
1435
- type: Types.FILL,
1436
- screenshotId,
1437
- value: _value,
1438
- text: `clickType input with value: ${_value}`,
1439
- result: error
1440
- ? {
1441
- status: "FAILED",
1442
- startTime,
1443
- endTime,
1444
- message: error === null || error === void 0 ? void 0 : error.message,
1445
- }
1446
- : {
1447
- status: "PASSED",
1448
- startTime,
1449
- endTime,
1450
- },
1451
- info: info,
1452
- });
1332
+ _commandFinally(state, this);
1453
1333
  }
1454
1334
  }
1455
1335
  async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1456
- this._validateSelectors(selectors);
1457
- value = unEscapeString(value);
1458
- const startTime = Date.now();
1459
- let error = null;
1460
- let screenshotId = null;
1461
- let screenshotPath = null;
1462
- const info = {};
1463
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1464
- info.operation = "fill";
1465
- info.selectors = selectors;
1466
- info.value = value;
1336
+ const state = {
1337
+ selectors,
1338
+ _params,
1339
+ value: unEscapeString(value),
1340
+ options,
1341
+ world,
1342
+ type: Types.FILL,
1343
+ text: `Fill input with value: ${value}`,
1344
+ operation: "fill",
1345
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1346
+ };
1467
1347
  try {
1468
- let element = await this._locate(selectors, info, _params);
1469
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1470
- await this._highlightElements(element);
1471
- await element.fill(value);
1472
- await element.dispatchEvent("change");
1348
+ await _preCommand(state, this);
1349
+ await state.element.fill(value);
1350
+ await state.element.dispatchEvent("change");
1473
1351
  if (enter) {
1474
1352
  await new Promise((resolve) => setTimeout(resolve, 2000));
1475
1353
  await this.page.keyboard.press("Enter");
1476
1354
  }
1477
1355
  await this.waitForPageLoad();
1478
- return info;
1356
+ return state.info;
1479
1357
  }
1480
1358
  catch (e) {
1481
- //await this.closeUnexpectedPopups();
1482
- this.logger.error("fill failed " + info.log);
1483
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1484
- info.screenshotPath = screenshotPath;
1485
- Object.assign(e, { info: info });
1486
- error = e;
1487
- throw e;
1359
+ await _commandError(state, e, this);
1488
1360
  }
1489
1361
  finally {
1490
- const endTime = Date.now();
1491
- this._reportToWorld(world, {
1492
- element_name: selectors.element_name,
1493
- type: Types.FILL,
1494
- screenshotId,
1495
- value,
1496
- text: `Fill input with value: ${value}`,
1497
- result: error
1498
- ? {
1499
- status: "FAILED",
1500
- startTime,
1501
- endTime,
1502
- message: error === null || error === void 0 ? void 0 : error.message,
1503
- }
1504
- : {
1505
- status: "PASSED",
1506
- startTime,
1507
- endTime,
1508
- },
1509
- info: info,
1510
- });
1362
+ _commandFinally(state, this);
1511
1363
  }
1512
1364
  }
1513
1365
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1514
1366
  return await this._getText(selectors, 0, _params, options, info, world);
1515
1367
  }
1516
1368
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1517
- this._validateSelectors(selectors);
1369
+ _validateSelectors(selectors);
1518
1370
  let screenshotId = null;
1519
1371
  let screenshotPath = null;
1520
1372
  if (!info.log) {
@@ -1558,166 +1410,124 @@ class StableBrowser {
1558
1410
  }
1559
1411
  }
1560
1412
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1561
- var _a;
1562
- this._validateSelectors(selectors);
1563
1413
  if (!pattern) {
1564
1414
  throw new Error("pattern is null");
1565
1415
  }
1566
1416
  if (!text) {
1567
1417
  throw new Error("text is null");
1568
1418
  }
1419
+ const state = {
1420
+ selectors,
1421
+ _params,
1422
+ pattern,
1423
+ value: pattern,
1424
+ options,
1425
+ world,
1426
+ locate: false,
1427
+ scroll: false,
1428
+ screenshot: false,
1429
+ highlight: false,
1430
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1431
+ text: `Verify element contains pattern: ${pattern}`,
1432
+ operation: "containsPattern",
1433
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1434
+ };
1569
1435
  const newValue = await this._replaceWithLocalData(text, world);
1570
1436
  if (newValue !== text) {
1571
1437
  this.logger.info(text + "=" + newValue);
1572
1438
  text = newValue;
1573
1439
  }
1574
- const startTime = Date.now();
1575
- let error = null;
1576
- let screenshotId = null;
1577
- let screenshotPath = null;
1578
- const info = {};
1579
- info.log =
1580
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1581
- info.operation = "containsPattern";
1582
- info.selectors = selectors;
1583
- info.value = text;
1584
- info.pattern = pattern;
1585
1440
  let foundObj = null;
1586
1441
  try {
1587
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1442
+ await _preCommand(state, this);
1443
+ state.info.pattern = pattern;
1444
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1588
1445
  if (foundObj && foundObj.element) {
1589
- await this.scrollIfNeeded(foundObj.element, info);
1446
+ await this.scrollIfNeeded(foundObj.element, state.info);
1590
1447
  }
1591
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1448
+ await _screenshot(state, this);
1592
1449
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1593
1450
  pattern = pattern.replace("{text}", escapedText);
1594
1451
  let regex = new RegExp(pattern, "im");
1595
- if (!regex.test(foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) && !((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(text))) {
1596
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1452
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1453
+ state.info.foundText = foundObj?.text;
1597
1454
  throw new Error("element doesn't contain text " + text);
1598
1455
  }
1599
- return info;
1456
+ return state.info;
1600
1457
  }
1601
1458
  catch (e) {
1602
- //await this.closeUnexpectedPopups();
1603
- this.logger.error("verify element contains text failed " + info.log);
1604
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1605
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1606
- info.screenshotPath = screenshotPath;
1607
- Object.assign(e, { info: info });
1608
- error = e;
1609
- throw e;
1459
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1460
+ await _commandError(state, e, this);
1610
1461
  }
1611
1462
  finally {
1612
- const endTime = Date.now();
1613
- this._reportToWorld(world, {
1614
- element_name: selectors.element_name,
1615
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1616
- value: pattern,
1617
- text: `Verify element contains pattern: ${pattern}`,
1618
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1619
- result: error
1620
- ? {
1621
- status: "FAILED",
1622
- startTime,
1623
- endTime,
1624
- message: error === null || error === void 0 ? void 0 : error.message,
1625
- }
1626
- : {
1627
- status: "PASSED",
1628
- startTime,
1629
- endTime,
1630
- },
1631
- info: info,
1632
- });
1463
+ _commandFinally(state, this);
1633
1464
  }
1634
1465
  }
1635
1466
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1636
- var _a, _b, _c;
1637
- this._validateSelectors(selectors);
1638
- text = unEscapeString(text);
1467
+ const state = {
1468
+ selectors,
1469
+ _params,
1470
+ value: text,
1471
+ options,
1472
+ world,
1473
+ locate: false,
1474
+ scroll: false,
1475
+ screenshot: false,
1476
+ highlight: false,
1477
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1478
+ text: `Verify element contains text: ${text}`,
1479
+ operation: "containsText",
1480
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1481
+ };
1639
1482
  if (!text) {
1640
1483
  throw new Error("text is null");
1641
1484
  }
1642
- const startTime = Date.now();
1643
- let error = null;
1644
- let screenshotId = null;
1645
- let screenshotPath = null;
1646
- const info = {};
1647
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1648
- info.operation = "containsText";
1649
- info.selectors = selectors;
1485
+ text = unEscapeString(text);
1650
1486
  const newValue = await this._replaceWithLocalData(text, world);
1651
1487
  if (newValue !== text) {
1652
1488
  this.logger.info(text + "=" + newValue);
1653
1489
  text = newValue;
1654
1490
  }
1655
- info.value = text;
1656
1491
  let foundObj = null;
1657
1492
  try {
1658
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1493
+ await _preCommand(state, this);
1494
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1659
1495
  if (foundObj && foundObj.element) {
1660
- await this.scrollIfNeeded(foundObj.element, info);
1496
+ await this.scrollIfNeeded(foundObj.element, state.info);
1661
1497
  }
1662
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1498
+ await _screenshot(state, this);
1663
1499
  const dateAlternatives = findDateAlternatives(text);
1664
1500
  const numberAlternatives = findNumberAlternatives(text);
1665
1501
  if (dateAlternatives.date) {
1666
1502
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1667
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1668
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1669
- return info;
1503
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1504
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1505
+ return state.info;
1670
1506
  }
1671
1507
  }
1672
1508
  throw new Error("element doesn't contain text " + text);
1673
1509
  }
1674
1510
  else if (numberAlternatives.number) {
1675
1511
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1676
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1677
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1678
- return info;
1512
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1513
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1514
+ return state.info;
1679
1515
  }
1680
1516
  }
1681
1517
  throw new Error("element doesn't contain text " + text);
1682
1518
  }
1683
- else if (!(foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(text)) && !((_c = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _c === void 0 ? void 0 : _c.includes(text))) {
1684
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1685
- info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1519
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1520
+ state.info.foundText = foundObj?.text;
1521
+ state.info.value = foundObj?.value;
1686
1522
  throw new Error("element doesn't contain text " + text);
1687
1523
  }
1688
- return info;
1524
+ return state.info;
1689
1525
  }
1690
1526
  catch (e) {
1691
- //await this.closeUnexpectedPopups();
1692
- this.logger.error("verify element contains text failed " + info.log);
1693
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1694
- info.screenshotPath = screenshotPath;
1695
- Object.assign(e, { info: info });
1696
- error = e;
1697
- throw e;
1527
+ await _commandError(state, e, this);
1698
1528
  }
1699
1529
  finally {
1700
- const endTime = Date.now();
1701
- this._reportToWorld(world, {
1702
- element_name: selectors.element_name,
1703
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1704
- text: `Verify element contains text: ${text}`,
1705
- value: text,
1706
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1707
- result: error
1708
- ? {
1709
- status: "FAILED",
1710
- startTime,
1711
- endTime,
1712
- message: error === null || error === void 0 ? void 0 : error.message,
1713
- }
1714
- : {
1715
- status: "PASSED",
1716
- startTime,
1717
- endTime,
1718
- },
1719
- info: info,
1720
- });
1530
+ _commandFinally(state, this);
1721
1531
  }
1722
1532
  }
1723
1533
  _getDataFile(world = null) {
@@ -1899,11 +1709,9 @@ class StableBrowser {
1899
1709
  if (!fs.existsSync(world.screenshotPath)) {
1900
1710
  fs.mkdirSync(world.screenshotPath, { recursive: true });
1901
1711
  }
1902
- let nextIndex = 1;
1903
- while (fs.existsSync(path.join(world.screenshotPath, nextIndex + ".png"))) {
1904
- nextIndex++;
1905
- }
1906
- const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
1712
+ // to make sure the path doesn't start with -
1713
+ const uuidStr = "id_" + randomUUID();
1714
+ const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
1907
1715
  try {
1908
1716
  await this.takeScreenshot(screenshotPath);
1909
1717
  // let buffer = await this.page.screenshot({ timeout: 4000 });
@@ -1917,7 +1725,7 @@ class StableBrowser {
1917
1725
  catch (e) {
1918
1726
  this.logger.info("unable to take screenshot, ignored");
1919
1727
  }
1920
- result.screenshotId = nextIndex;
1728
+ result.screenshotId = uuidStr;
1921
1729
  result.screenshotPath = screenshotPath;
1922
1730
  if (info && info.box) {
1923
1731
  await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
@@ -1991,127 +1799,69 @@ class StableBrowser {
1991
1799
  }
1992
1800
  }
1993
1801
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1994
- this._validateSelectors(selectors);
1995
- const startTime = Date.now();
1996
- let error = null;
1997
- let screenshotId = null;
1998
- let screenshotPath = null;
1802
+ const state = {
1803
+ selectors,
1804
+ _params,
1805
+ options,
1806
+ world,
1807
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1808
+ text: `Verify element exists in page`,
1809
+ operation: "verifyElementExistInPage",
1810
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1811
+ };
1999
1812
  await new Promise((resolve) => setTimeout(resolve, 2000));
2000
- const info = {};
2001
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
2002
- info.operation = "verify";
2003
- info.selectors = selectors;
2004
1813
  try {
2005
- const element = await this._locate(selectors, info, _params);
2006
- if (element) {
2007
- await this.scrollIfNeeded(element, info);
2008
- }
2009
- await this._highlightElements(element);
2010
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2011
- await expect(element).toHaveCount(1, { timeout: 10000 });
2012
- return info;
1814
+ await _preCommand(state, this);
1815
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1816
+ return state.info;
2013
1817
  }
2014
1818
  catch (e) {
2015
- //await this.closeUnexpectedPopups();
2016
- this.logger.error("verify failed " + info.log);
2017
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2018
- info.screenshotPath = screenshotPath;
2019
- Object.assign(e, { info: info });
2020
- error = e;
2021
- throw e;
1819
+ await _commandError(state, e, this);
2022
1820
  }
2023
1821
  finally {
2024
- const endTime = Date.now();
2025
- this._reportToWorld(world, {
2026
- element_name: selectors.element_name,
2027
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2028
- text: "Verify element exists in page",
2029
- screenshotId,
2030
- result: error
2031
- ? {
2032
- status: "FAILED",
2033
- startTime,
2034
- endTime,
2035
- message: error === null || error === void 0 ? void 0 : error.message,
2036
- }
2037
- : {
2038
- status: "PASSED",
2039
- startTime,
2040
- endTime,
2041
- },
2042
- info: info,
2043
- });
1822
+ _commandFinally(state, this);
2044
1823
  }
2045
1824
  }
2046
1825
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
2047
- this._validateSelectors(selectors);
2048
- const startTime = Date.now();
2049
- let error = null;
2050
- let screenshotId = null;
2051
- let screenshotPath = null;
1826
+ const state = {
1827
+ selectors,
1828
+ _params,
1829
+ attribute,
1830
+ variable,
1831
+ options,
1832
+ world,
1833
+ type: Types.EXTRACT,
1834
+ text: `Extract attribute from element`,
1835
+ operation: "extractAttribute",
1836
+ log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
1837
+ };
2052
1838
  await new Promise((resolve) => setTimeout(resolve, 2000));
2053
- const info = {};
2054
- info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
2055
- info.operation = "extract";
2056
- info.selectors = selectors;
2057
1839
  try {
2058
- const element = await this._locate(selectors, info, _params);
2059
- await this._highlightElements(element);
2060
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1840
+ await _preCommand(state, this);
2061
1841
  switch (attribute) {
2062
1842
  case "inner_text":
2063
- info.value = await element.innerText();
1843
+ state.value = await state.element.innerText();
2064
1844
  break;
2065
1845
  case "href":
2066
- info.value = await element.getAttribute("href");
1846
+ state.value = await state.element.getAttribute("href");
2067
1847
  break;
2068
1848
  case "value":
2069
- info.value = await element.inputValue();
1849
+ state.value = await state.element.inputValue();
2070
1850
  break;
2071
1851
  default:
2072
- info.value = await element.getAttribute(attribute);
1852
+ state.value = await state.element.getAttribute(attribute);
2073
1853
  break;
2074
1854
  }
2075
- this[variable] = info.value;
2076
- if (world) {
2077
- world[variable] = info.value;
2078
- }
2079
- this.setTestData({ [variable]: info.value }, world);
2080
- this.logger.info("set test data: " + variable + "=" + info.value);
2081
- return info;
1855
+ state.info.value = state.value;
1856
+ this.setTestData({ [variable]: state.value }, world);
1857
+ this.logger.info("set test data: " + variable + "=" + state.value);
1858
+ return state.info;
2082
1859
  }
2083
1860
  catch (e) {
2084
- //await this.closeUnexpectedPopups();
2085
- this.logger.error("extract failed " + info.log);
2086
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2087
- info.screenshotPath = screenshotPath;
2088
- Object.assign(e, { info: info });
2089
- error = e;
2090
- throw e;
1861
+ await _commandError(state, e, this);
2091
1862
  }
2092
1863
  finally {
2093
- const endTime = Date.now();
2094
- this._reportToWorld(world, {
2095
- element_name: selectors.element_name,
2096
- type: Types.EXTRACT_ATTRIBUTE,
2097
- variable: variable,
2098
- value: info.value,
2099
- text: "Extract attribute from element",
2100
- screenshotId,
2101
- result: error
2102
- ? {
2103
- status: "FAILED",
2104
- startTime,
2105
- endTime,
2106
- message: error === null || error === void 0 ? void 0 : error.message,
2107
- }
2108
- : {
2109
- status: "PASSED",
2110
- startTime,
2111
- endTime,
2112
- },
2113
- info: info,
2114
- });
1864
+ _commandFinally(state, this);
2115
1865
  }
2116
1866
  }
2117
1867
  async extractEmailData(emailAddress, options, world) {
@@ -2188,7 +1938,8 @@ class StableBrowser {
2188
1938
  catch (e) {
2189
1939
  errorCount++;
2190
1940
  if (errorCount > 3) {
2191
- throw e;
1941
+ // throw e;
1942
+ await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
2192
1943
  }
2193
1944
  // ignore
2194
1945
  }
@@ -2299,11 +2050,12 @@ class StableBrowser {
2299
2050
  info.screenshotPath = screenshotPath;
2300
2051
  Object.assign(e, { info: info });
2301
2052
  error = e;
2302
- throw e;
2053
+ // throw e;
2054
+ await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
2303
2055
  }
2304
2056
  finally {
2305
2057
  const endTime = Date.now();
2306
- this._reportToWorld(world, {
2058
+ _reportToWorld(world, {
2307
2059
  type: Types.VERIFY_PAGE_PATH,
2308
2060
  text: "Verify page path",
2309
2061
  screenshotId,
@@ -2312,7 +2064,7 @@ class StableBrowser {
2312
2064
  status: "FAILED",
2313
2065
  startTime,
2314
2066
  endTime,
2315
- message: error === null || error === void 0 ? void 0 : error.message,
2067
+ message: error?.message,
2316
2068
  }
2317
2069
  : {
2318
2070
  status: "PASSED",
@@ -2325,52 +2077,58 @@ class StableBrowser {
2325
2077
  }
2326
2078
  async verifyTextExistInPage(text, options = {}, world = null) {
2327
2079
  text = unEscapeString(text);
2328
- const startTime = Date.now();
2080
+ const state = {
2081
+ text_search: text,
2082
+ options,
2083
+ world,
2084
+ locate: false,
2085
+ scroll: false,
2086
+ highlight: false,
2087
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2088
+ text: `Verify text exists in page`,
2089
+ operation: "verifyTextExistInPage",
2090
+ log: "***** verify text " + text + " exists in page *****\n",
2091
+ };
2329
2092
  const timeout = this._getLoadTimeout(options);
2330
- let error = null;
2331
- let screenshotId = null;
2332
- let screenshotPath = null;
2333
2093
  await new Promise((resolve) => setTimeout(resolve, 2000));
2334
- const info = {};
2335
- info.log = "***** verify text " + text + " exists in page *****\n";
2336
- info.operation = "verifyTextExistInPage";
2337
2094
  const newValue = await this._replaceWithLocalData(text, world);
2338
2095
  if (newValue !== text) {
2339
2096
  this.logger.info(text + "=" + newValue);
2340
2097
  text = newValue;
2341
2098
  }
2342
- info.text = text;
2343
2099
  let dateAlternatives = findDateAlternatives(text);
2344
2100
  let numberAlternatives = findNumberAlternatives(text);
2345
2101
  try {
2102
+ await _preCommand(state, this);
2103
+ state.info.text = text;
2346
2104
  while (true) {
2347
2105
  const frames = this.page.frames();
2348
2106
  let results = [];
2349
2107
  for (let i = 0; i < frames.length; i++) {
2350
2108
  if (dateAlternatives.date) {
2351
2109
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2352
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2110
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
2353
2111
  result.frame = frames[i];
2354
2112
  results.push(result);
2355
2113
  }
2356
2114
  }
2357
2115
  else if (numberAlternatives.number) {
2358
2116
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2359
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2117
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
2360
2118
  result.frame = frames[i];
2361
2119
  results.push(result);
2362
2120
  }
2363
2121
  }
2364
2122
  else {
2365
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2123
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2366
2124
  result.frame = frames[i];
2367
2125
  results.push(result);
2368
2126
  }
2369
2127
  }
2370
- info.results = results;
2128
+ state.info.results = results;
2371
2129
  const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2372
2130
  if (resultWithElementsFound.length === 0) {
2373
- if (Date.now() - startTime > timeout) {
2131
+ if (Date.now() - state.startTime > timeout) {
2374
2132
  throw new Error(`Text ${text} not found in page`);
2375
2133
  }
2376
2134
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -2382,44 +2140,89 @@ class StableBrowser {
2382
2140
  await this._highlightElements(frame, dataAttribute);
2383
2141
  const element = await frame.$(dataAttribute);
2384
2142
  if (element) {
2385
- await this.scrollIfNeeded(element, info);
2143
+ await this.scrollIfNeeded(element, state.info);
2386
2144
  await element.dispatchEvent("bvt_verify_page_contains_text");
2387
2145
  }
2388
2146
  }
2389
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2390
- return info;
2147
+ await _screenshot(state, this);
2148
+ return state.info;
2391
2149
  }
2392
2150
  // await expect(element).toHaveCount(1, { timeout: 10000 });
2393
2151
  }
2394
2152
  catch (e) {
2395
- //await this.closeUnexpectedPopups();
2396
- this.logger.error("verify text exist in page failed " + info.log);
2397
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2398
- info.screenshotPath = screenshotPath;
2399
- Object.assign(e, { info: info });
2400
- error = e;
2401
- throw e;
2153
+ await _commandError(state, e, this);
2402
2154
  }
2403
2155
  finally {
2404
- const endTime = Date.now();
2405
- this._reportToWorld(world, {
2406
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2407
- text: "Verify text exists in page",
2408
- screenshotId,
2409
- result: error
2410
- ? {
2411
- status: "FAILED",
2412
- startTime,
2413
- endTime,
2414
- message: error === null || error === void 0 ? void 0 : error.message,
2156
+ _commandFinally(state, this);
2157
+ }
2158
+ }
2159
+ async waitForTextToDisappear(text, options = {}, world = null) {
2160
+ text = unEscapeString(text);
2161
+ const state = {
2162
+ text_search: text,
2163
+ options,
2164
+ world,
2165
+ locate: false,
2166
+ scroll: false,
2167
+ highlight: false,
2168
+ type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
2169
+ text: `Verify text does not exist in page`,
2170
+ operation: "verifyTextNotExistInPage",
2171
+ log: "***** verify text " + text + " does not exist in page *****\n",
2172
+ };
2173
+ const timeout = this._getLoadTimeout(options);
2174
+ await new Promise((resolve) => setTimeout(resolve, 2000));
2175
+ const newValue = await this._replaceWithLocalData(text, world);
2176
+ if (newValue !== text) {
2177
+ this.logger.info(text + "=" + newValue);
2178
+ text = newValue;
2179
+ }
2180
+ let dateAlternatives = findDateAlternatives(text);
2181
+ let numberAlternatives = findNumberAlternatives(text);
2182
+ try {
2183
+ await _preCommand(state, this);
2184
+ state.info.text = text;
2185
+ while (true) {
2186
+ const frames = this.page.frames();
2187
+ let results = [];
2188
+ for (let i = 0; i < frames.length; i++) {
2189
+ if (dateAlternatives.date) {
2190
+ for (let j = 0; j < dateAlternatives.dates.length; j++) {
2191
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
2192
+ result.frame = frames[i];
2193
+ results.push(result);
2194
+ }
2415
2195
  }
2416
- : {
2417
- status: "PASSED",
2418
- startTime,
2419
- endTime,
2420
- },
2421
- info: info,
2422
- });
2196
+ else if (numberAlternatives.number) {
2197
+ for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2198
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
2199
+ result.frame = frames[i];
2200
+ results.push(result);
2201
+ }
2202
+ }
2203
+ else {
2204
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2205
+ result.frame = frames[i];
2206
+ results.push(result);
2207
+ }
2208
+ }
2209
+ state.info.results = results;
2210
+ const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2211
+ if (resultWithElementsFound.length === 0) {
2212
+ await _screenshot(state, this);
2213
+ return state.info;
2214
+ }
2215
+ if (Date.now() - state.startTime > timeout) {
2216
+ throw new Error(`Text ${text} found in page`);
2217
+ }
2218
+ await new Promise((resolve) => setTimeout(resolve, 1000));
2219
+ }
2220
+ }
2221
+ catch (e) {
2222
+ await _commandError(state, e, this);
2223
+ }
2224
+ finally {
2225
+ _commandFinally(state, this);
2423
2226
  }
2424
2227
  }
2425
2228
  _getServerUrl() {
@@ -2482,11 +2285,12 @@ class StableBrowser {
2482
2285
  info.screenshotPath = screenshotPath;
2483
2286
  Object.assign(e, { info: info });
2484
2287
  error = e;
2485
- throw e;
2288
+ // throw e;
2289
+ await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
2486
2290
  }
2487
2291
  finally {
2488
2292
  const endTime = Date.now();
2489
- this._reportToWorld(world, {
2293
+ _reportToWorld(world, {
2490
2294
  type: Types.VERIFY_VISUAL,
2491
2295
  text: "Visual verification",
2492
2296
  screenshotId,
@@ -2495,7 +2299,7 @@ class StableBrowser {
2495
2299
  status: "FAILED",
2496
2300
  startTime,
2497
2301
  endTime,
2498
- message: error === null || error === void 0 ? void 0 : error.message,
2302
+ message: error?.message,
2499
2303
  }
2500
2304
  : {
2501
2305
  status: "PASSED",
@@ -2527,7 +2331,7 @@ class StableBrowser {
2527
2331
  this.logger.info("Table data verified");
2528
2332
  }
2529
2333
  async getTableData(selectors, _params = null, options = {}, world = null) {
2530
- this._validateSelectors(selectors);
2334
+ _validateSelectors(selectors);
2531
2335
  const startTime = Date.now();
2532
2336
  let error = null;
2533
2337
  let screenshotId = null;
@@ -2549,11 +2353,12 @@ class StableBrowser {
2549
2353
  info.screenshotPath = screenshotPath;
2550
2354
  Object.assign(e, { info: info });
2551
2355
  error = e;
2552
- throw e;
2356
+ // throw e;
2357
+ await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
2553
2358
  }
2554
2359
  finally {
2555
2360
  const endTime = Date.now();
2556
- this._reportToWorld(world, {
2361
+ _reportToWorld(world, {
2557
2362
  element_name: selectors.element_name,
2558
2363
  type: Types.GET_TABLE_DATA,
2559
2364
  text: "Get table data",
@@ -2563,7 +2368,7 @@ class StableBrowser {
2563
2368
  status: "FAILED",
2564
2369
  startTime,
2565
2370
  endTime,
2566
- message: error === null || error === void 0 ? void 0 : error.message,
2371
+ message: error?.message,
2567
2372
  }
2568
2373
  : {
2569
2374
  status: "PASSED",
@@ -2575,7 +2380,7 @@ class StableBrowser {
2575
2380
  }
2576
2381
  }
2577
2382
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2578
- this._validateSelectors(selectors);
2383
+ _validateSelectors(selectors);
2579
2384
  if (!query) {
2580
2385
  throw new Error("query is null");
2581
2386
  }
@@ -2714,11 +2519,12 @@ class StableBrowser {
2714
2519
  info.screenshotPath = screenshotPath;
2715
2520
  Object.assign(e, { info: info });
2716
2521
  error = e;
2717
- throw e;
2522
+ // throw e;
2523
+ await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
2718
2524
  }
2719
2525
  finally {
2720
2526
  const endTime = Date.now();
2721
- this._reportToWorld(world, {
2527
+ _reportToWorld(world, {
2722
2528
  element_name: selectors.element_name,
2723
2529
  type: Types.ANALYZE_TABLE,
2724
2530
  text: "Analyze table",
@@ -2728,7 +2534,7 @@ class StableBrowser {
2728
2534
  status: "FAILED",
2729
2535
  startTime,
2730
2536
  endTime,
2731
- message: error === null || error === void 0 ? void 0 : error.message,
2537
+ message: error?.message,
2732
2538
  }
2733
2539
  : {
2734
2540
  status: "PASSED",
@@ -2740,27 +2546,7 @@ class StableBrowser {
2740
2546
  }
2741
2547
  }
2742
2548
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2743
- if (!value) {
2744
- return value;
2745
- }
2746
- // find all the accurance of {{(.*?)}} and replace with the value
2747
- let regex = /{{(.*?)}}/g;
2748
- let matches = value.match(regex);
2749
- if (matches) {
2750
- const testData = this.getTestData(world);
2751
- for (let i = 0; i < matches.length; i++) {
2752
- let match = matches[i];
2753
- let key = match.substring(2, match.length - 2);
2754
- let newValue = objectPath.get(testData, key, null);
2755
- if (newValue !== null) {
2756
- value = value.replace(match, newValue);
2757
- }
2758
- }
2759
- }
2760
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2761
- return await decrypt(value, null, totpWait);
2762
- }
2763
- return value;
2549
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2764
2550
  }
2765
2551
  _getLoadTimeout(options) {
2766
2552
  let timeout = 15000;
@@ -2811,7 +2597,7 @@ class StableBrowser {
2811
2597
  await new Promise((resolve) => setTimeout(resolve, 2000));
2812
2598
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2813
2599
  const endTime = Date.now();
2814
- this._reportToWorld(world, {
2600
+ _reportToWorld(world, {
2815
2601
  type: Types.GET_PAGE_STATUS,
2816
2602
  text: "Wait for page load",
2817
2603
  screenshotId,
@@ -2820,7 +2606,7 @@ class StableBrowser {
2820
2606
  status: "FAILED",
2821
2607
  startTime,
2822
2608
  endTime,
2823
- message: error === null || error === void 0 ? void 0 : error.message,
2609
+ message: error?.message,
2824
2610
  }
2825
2611
  : {
2826
2612
  status: "PASSED",
@@ -2831,41 +2617,35 @@ class StableBrowser {
2831
2617
  }
2832
2618
  }
2833
2619
  async closePage(options = {}, world = null) {
2834
- const startTime = Date.now();
2835
- let error = null;
2836
- let screenshotId = null;
2837
- let screenshotPath = null;
2838
- const info = {};
2620
+ const state = {
2621
+ options,
2622
+ world,
2623
+ locate: false,
2624
+ scroll: false,
2625
+ highlight: false,
2626
+ type: Types.CLOSE_PAGE,
2627
+ text: `Close page`,
2628
+ operation: "closePage",
2629
+ log: "***** close page *****\n",
2630
+ throwError: false,
2631
+ };
2839
2632
  try {
2633
+ await _preCommand(state, this);
2840
2634
  await this.page.close();
2841
2635
  }
2842
2636
  catch (e) {
2843
2637
  console.log(".");
2638
+ await _commandError(state, e, this);
2844
2639
  }
2845
2640
  finally {
2846
- await new Promise((resolve) => setTimeout(resolve, 2000));
2847
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2848
- const endTime = Date.now();
2849
- this._reportToWorld(world, {
2850
- type: Types.CLOSE_PAGE,
2851
- text: "close page",
2852
- screenshotId,
2853
- result: error
2854
- ? {
2855
- status: "FAILED",
2856
- startTime,
2857
- endTime,
2858
- message: error === null || error === void 0 ? void 0 : error.message,
2859
- }
2860
- : {
2861
- status: "PASSED",
2862
- startTime,
2863
- endTime,
2864
- },
2865
- info: info,
2866
- });
2641
+ _commandFinally(state, this);
2867
2642
  }
2868
2643
  }
2644
+ saveTestDataAsGlobal(options, world) {
2645
+ const dataFile = this._getDataFile(world);
2646
+ process.env.GLOBAL_TEST_DATA_FILE = dataFile;
2647
+ this.logger.info("Save the scenario test data as global for the following scenarios.");
2648
+ }
2869
2649
  async setViewportSize(width, hight, options = {}, world = null) {
2870
2650
  const startTime = Date.now();
2871
2651
  let error = null;
@@ -2883,12 +2663,13 @@ class StableBrowser {
2883
2663
  }
2884
2664
  catch (e) {
2885
2665
  console.log(".");
2666
+ await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
2886
2667
  }
2887
2668
  finally {
2888
2669
  await new Promise((resolve) => setTimeout(resolve, 2000));
2889
2670
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2890
2671
  const endTime = Date.now();
2891
- this._reportToWorld(world, {
2672
+ _reportToWorld(world, {
2892
2673
  type: Types.SET_VIEWPORT,
2893
2674
  text: "set viewport size to " + width + "x" + hight,
2894
2675
  screenshotId,
@@ -2897,7 +2678,7 @@ class StableBrowser {
2897
2678
  status: "FAILED",
2898
2679
  startTime,
2899
2680
  endTime,
2900
- message: error === null || error === void 0 ? void 0 : error.message,
2681
+ message: error?.message,
2901
2682
  }
2902
2683
  : {
2903
2684
  status: "PASSED",
@@ -2919,12 +2700,13 @@ class StableBrowser {
2919
2700
  }
2920
2701
  catch (e) {
2921
2702
  console.log(".");
2703
+ await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
2922
2704
  }
2923
2705
  finally {
2924
2706
  await new Promise((resolve) => setTimeout(resolve, 2000));
2925
2707
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2926
2708
  const endTime = Date.now();
2927
- this._reportToWorld(world, {
2709
+ _reportToWorld(world, {
2928
2710
  type: Types.GET_PAGE_STATUS,
2929
2711
  text: "page relaod",
2930
2712
  screenshotId,
@@ -2933,7 +2715,7 @@ class StableBrowser {
2933
2715
  status: "FAILED",
2934
2716
  startTime,
2935
2717
  endTime,
2936
- message: error === null || error === void 0 ? void 0 : error.message,
2718
+ message: error?.message,
2937
2719
  }
2938
2720
  : {
2939
2721
  status: "PASSED",
@@ -2960,11 +2742,37 @@ class StableBrowser {
2960
2742
  console.log("#-#");
2961
2743
  }
2962
2744
  }
2963
- _reportToWorld(world, properties) {
2964
- if (!world || !world.attach) {
2965
- return;
2745
+ async beforeStep(world, step) {
2746
+ this.stepName = step.pickleStep.text;
2747
+ this.logger.info("step: " + this.stepName);
2748
+ if (this.stepIndex === undefined) {
2749
+ this.stepIndex = 0;
2750
+ }
2751
+ else {
2752
+ this.stepIndex++;
2753
+ }
2754
+ if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
2755
+ if (this.context.browserObject.context) {
2756
+ await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
2757
+ }
2758
+ }
2759
+ if (this.tags === null && step && step.pickle && step.pickle.tags) {
2760
+ this.tags = step.pickle.tags.map((tag) => tag.name);
2761
+ // check if @global_test_data tag is present
2762
+ if (this.tags.includes("@global_test_data")) {
2763
+ this.saveTestDataAsGlobal({}, world);
2764
+ }
2765
+ }
2766
+ }
2767
+ async afterStep(world, step) {
2768
+ this.stepName = null;
2769
+ if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
2770
+ if (this.context.browserObject.context) {
2771
+ await this.context.browserObject.context.tracing.stopChunk({
2772
+ path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
2773
+ });
2774
+ }
2966
2775
  }
2967
- world.attach(JSON.stringify(properties), { mediaType: "application/json" });
2968
2776
  }
2969
2777
  }
2970
2778
  function createTimedPromise(promise, label) {