automation_model 1.0.448-dev → 1.0.448-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",
@@ -45,16 +47,22 @@ const Types = {
45
47
  };
46
48
  export const apps = {};
47
49
  class StableBrowser {
48
- constructor(browser, page, logger = null, context = null) {
50
+ browser;
51
+ page;
52
+ logger;
53
+ context;
54
+ world;
55
+ project_path = null;
56
+ webLogFile = null;
57
+ networkLogger = null;
58
+ configuration = null;
59
+ appName = "main";
60
+ constructor(browser, page, logger = null, context = null, world = null) {
49
61
  this.browser = browser;
50
62
  this.page = page;
51
63
  this.logger = logger;
52
64
  this.context = context;
53
- this.project_path = null;
54
- this.webLogFile = null;
55
- this.networkLogger = null;
56
- this.configuration = null;
57
- this.appName = "main";
65
+ this.world = world;
58
66
  if (!this.logger) {
59
67
  this.logger = console;
60
68
  }
@@ -79,20 +87,32 @@ class StableBrowser {
79
87
  catch (e) {
80
88
  this.logger.error("unable to read ai_config.json");
81
89
  }
82
- context.pageLoading = { status: false };
83
- context.pages = [this.page];
84
90
  const logFolder = path.join(this.project_path, "logs", "web");
85
- this.webLogFile = this.getWebLogFile(logFolder);
86
- this.registerEventListeners(context);
91
+ this.world = world;
92
+ context.pages = [this.page];
93
+ context.pageLoading = { status: false };
94
+ this.registerEventListeners(this.context);
95
+ registerNetworkEvents(this.world, this, this.context, this.page);
96
+ registerDownloadEvent(this.page, this.world, this.context);
87
97
  }
88
98
  registerEventListeners(context) {
89
- this.registerConsoleLogListener(this.page, context, this.webLogFile);
99
+ this.registerConsoleLogListener(this.page, context);
90
100
  this.registerRequestListener(this.page, context, this.webLogFile);
101
+ if (!context.pageLoading) {
102
+ context.pageLoading = { status: false };
103
+ }
91
104
  context.playContext.on("page", async function (page) {
105
+ if (this.configuration && this.configuration.closePopups === true) {
106
+ console.log("close unexpected popups");
107
+ await page.close();
108
+ return;
109
+ }
92
110
  context.pageLoading.status = true;
93
111
  this.page = page;
94
112
  context.page = page;
95
113
  context.pages.push(page);
114
+ registerNetworkEvents(this.world, this, context, this.page);
115
+ registerDownloadEvent(this.page, this.world, context);
96
116
  page.on("close", async () => {
97
117
  if (this.context && this.context.pages && this.context.pages.length > 1) {
98
118
  this.context.pages.pop();
@@ -122,10 +142,10 @@ class StableBrowser {
122
142
  if (this.appName === appName) {
123
143
  return;
124
144
  }
125
- let newContextCreated = false;
145
+ let navigate = false;
126
146
  if (!apps[appName]) {
127
147
  let newContext = await getContext(null, false, this.logger, appName, false, this);
128
- newContextCreated = true;
148
+ navigate = true;
129
149
  apps[appName] = {
130
150
  context: newContext,
131
151
  browser: newContext.browser,
@@ -137,8 +157,7 @@ class StableBrowser {
137
157
  this._copyContext(apps[appName], this);
138
158
  apps[this.appName] = tempContext;
139
159
  this.appName = appName;
140
- if (newContextCreated) {
141
- this.registerEventListeners(this.context);
160
+ if (navigate) {
142
161
  await this.goto(this.context.environment.baseUrl);
143
162
  await this.waitForPageLoad();
144
163
  }
@@ -159,18 +178,21 @@ class StableBrowser {
159
178
  const fileName = nextIndex + ".json";
160
179
  return path.join(logFolder, fileName);
161
180
  }
162
- registerConsoleLogListener(page, context, logFile) {
181
+ registerConsoleLogListener(page, context) {
163
182
  if (!this.context.webLogger) {
164
183
  this.context.webLogger = [];
165
184
  }
166
185
  page.on("console", async (msg) => {
167
- this.context.webLogger.push({
186
+ const obj = {
168
187
  type: msg.type(),
169
188
  text: msg.text(),
170
189
  location: msg.location(),
171
190
  time: new Date().toISOString(),
172
- });
173
- await fs.promises.writeFile(logFile, JSON.stringify(this.context.webLogger, null, 2));
191
+ };
192
+ this.context.webLogger.push(obj);
193
+ if (msg.type() === "error") {
194
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
195
+ }
174
196
  });
175
197
  }
176
198
  registerRequestListener(page, context, logFile) {
@@ -178,6 +200,7 @@ class StableBrowser {
178
200
  this.context.networkLogger = [];
179
201
  }
180
202
  page.on("request", async (data) => {
203
+ const startTime = new Date().getTime();
181
204
  try {
182
205
  const pageUrl = new URL(page.url());
183
206
  const requestUrl = new URL(data.url());
@@ -190,13 +213,18 @@ class StableBrowser {
190
213
  }
191
214
  }
192
215
  }
193
- context.networkLogger.push({
216
+ const response = await data.response();
217
+ const endTime = new Date().getTime();
218
+ const obj = {
194
219
  url: data.url(),
195
220
  method: data.method(),
196
- headers: data.headers(),
197
- postData: data.postData()
198
- });
199
- await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
221
+ postData: data.postData(),
222
+ error: data.failure() ? data.failure().errorText : null,
223
+ duration: endTime - startTime,
224
+ startTime,
225
+ };
226
+ context.networkLogger.push(obj);
227
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
200
228
  }
201
229
  catch (error) {
202
230
  console.error("Error in request listener", error);
@@ -204,9 +232,9 @@ class StableBrowser {
204
232
  error: "not able to listen",
205
233
  message: error.message,
206
234
  stack: error.stack,
207
- time: new Date().toISOString()
235
+ time: new Date().toISOString(),
208
236
  });
209
- await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
237
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
210
238
  }
211
239
  });
212
240
  }
@@ -221,20 +249,6 @@ class StableBrowser {
221
249
  timeout: 60000,
222
250
  });
223
251
  }
224
- _validateSelectors(selectors) {
225
- if (!selectors) {
226
- throw new Error("selectors is null");
227
- }
228
- if (!selectors.locators) {
229
- throw new Error("selectors.locators is null");
230
- }
231
- if (!Array.isArray(selectors.locators)) {
232
- throw new Error("selectors.locators expected to be array");
233
- }
234
- if (selectors.locators.length === 0) {
235
- throw new Error("selectors.locators expected to be non empty array");
236
- }
237
- }
238
252
  _fixUsingParams(text, _params) {
239
253
  if (!_params || typeof text !== "string") {
240
254
  return text;
@@ -302,7 +316,7 @@ class StableBrowser {
302
316
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
303
317
  }
304
318
  }
305
- if (locator === null || locator === void 0 ? void 0 : locator.engine) {
319
+ if (locator?.engine) {
306
320
  if (locator.engine === "css") {
307
321
  locatorReturn = scope.locator(locator.selector);
308
322
  }
@@ -323,7 +337,10 @@ class StableBrowser {
323
337
  return locatorReturn;
324
338
  }
325
339
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
326
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
340
+ if (css && css.locator) {
341
+ css = css.locator;
342
+ }
343
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
327
344
  if (result.elementCount === 0) {
328
345
  return;
329
346
  }
@@ -338,7 +355,7 @@ class StableBrowser {
338
355
  }
339
356
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
340
357
  //const stringifyText = JSON.stringify(text);
341
- return await scope.evaluate(([text, tag, regex, partial]) => {
358
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
342
359
  function isParent(parent, child) {
343
360
  let currentNode = child.parentNode;
344
361
  while (currentNode !== null) {
@@ -350,6 +367,15 @@ class StableBrowser {
350
367
  return false;
351
368
  }
352
369
  document.isParent = isParent;
370
+ function getRegex(str) {
371
+ const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
372
+ if (!match) {
373
+ return null;
374
+ }
375
+ let [_, pattern, flags] = match;
376
+ return new RegExp(pattern, flags);
377
+ }
378
+ document.getRegex = getRegex;
353
379
  function collectAllShadowDomElements(element, result = []) {
354
380
  // Check and add the element if it has a shadow root
355
381
  if (element.shadowRoot) {
@@ -366,7 +392,11 @@ class StableBrowser {
366
392
  }
367
393
  document.collectAllShadowDomElements = collectAllShadowDomElements;
368
394
  if (!tag) {
369
- tag = "*";
395
+ tag = "*:not(script, style, head)";
396
+ }
397
+ let regexpSearch = document.getRegex(text);
398
+ if (regexpSearch) {
399
+ regex = true;
370
400
  }
371
401
  let elements = Array.from(document.querySelectorAll(tag));
372
402
  let shadowHosts = [];
@@ -383,7 +413,9 @@ class StableBrowser {
383
413
  let randomToken = null;
384
414
  const foundElements = [];
385
415
  if (regex) {
386
- let regexpSearch = new RegExp(text, "im");
416
+ if (!regexpSearch) {
417
+ regexpSearch = new RegExp(text, "im");
418
+ }
387
419
  for (let i = 0; i < elements.length; i++) {
388
420
  const element = elements[i];
389
421
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -397,8 +429,8 @@ class StableBrowser {
397
429
  for (let i = 0; i < elements.length; i++) {
398
430
  const element = elements[i];
399
431
  if (partial) {
400
- if ((element.innerText && element.innerText.trim().includes(text)) ||
401
- (element.value && element.value.includes(text))) {
432
+ if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
433
+ (element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
402
434
  foundElements.push(element);
403
435
  }
404
436
  }
@@ -443,18 +475,29 @@ class StableBrowser {
443
475
  }
444
476
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
445
477
  let locatorSearch = selectorHierarchy[index];
478
+ try {
479
+ locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
480
+ }
481
+ catch (e) {
482
+ console.error(e);
483
+ }
446
484
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
447
485
  let locator = null;
448
486
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
449
487
  let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
450
488
  if (!locatorString) {
489
+ info.failCause.textNotFound = true;
490
+ info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
451
491
  return;
452
492
  }
453
493
  locator = this._getLocator({ css: locatorString }, scope, _params);
454
494
  }
455
495
  else if (locatorSearch.text) {
456
- let result = await this._locateElementByText(scope, this._fixUsingParams(locatorSearch.text, _params), locatorSearch.tag, false, locatorSearch.partial === true, _params);
496
+ let text = this._fixUsingParams(locatorSearch.text, _params);
497
+ let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
457
498
  if (result.elementCount === 0) {
499
+ info.failCause.textNotFound = true;
500
+ info.failCause.lastError = "failed to locate element by text: " + text;
458
501
  return;
459
502
  }
460
503
  locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
@@ -471,6 +514,9 @@ class StableBrowser {
471
514
  // cssHref = true;
472
515
  // }
473
516
  let count = await locator.count();
517
+ if (count > 0 && !info.failCause.count) {
518
+ info.failCause.count = count;
519
+ }
474
520
  //info.log += "total elements found " + count + "\n";
475
521
  //let visibleCount = 0;
476
522
  let visibleLocator = null;
@@ -488,6 +534,8 @@ class StableBrowser {
488
534
  foundLocators.push(locator.nth(j));
489
535
  }
490
536
  else {
537
+ info.failCause.visible = visible;
538
+ info.failCause.enabled = enabled;
491
539
  if (!info.printMessages) {
492
540
  info.printMessages = {};
493
541
  }
@@ -499,6 +547,11 @@ class StableBrowser {
499
547
  }
500
548
  }
501
549
  async closeUnexpectedPopups(info, _params) {
550
+ if (!info) {
551
+ info = {};
552
+ info.failCause = {};
553
+ info.log = "";
554
+ }
502
555
  if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
503
556
  if (!info) {
504
557
  info = {};
@@ -539,7 +592,10 @@ class StableBrowser {
539
592
  }
540
593
  return { rerun: false };
541
594
  }
542
- async _locate(selectors, info, _params, timeout = 30000) {
595
+ async _locate(selectors, info, _params, timeout) {
596
+ if (!timeout) {
597
+ timeout = 30000;
598
+ }
543
599
  for (let i = 0; i < 3; i++) {
544
600
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
545
601
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -553,32 +609,46 @@ class StableBrowser {
553
609
  }
554
610
  throw new Error("unable to locate element " + JSON.stringify(selectors));
555
611
  }
556
- async _locate_internal(selectors, info, _params, timeout = 30000) {
557
- let highPriorityTimeout = 5000;
558
- let visibleOnlyTimeout = 6000;
559
- let startTime = performance.now();
560
- let locatorsCount = 0;
561
- //let arrayMode = Array.isArray(selectors);
612
+ async _findFrameScope(selectors, timeout = 30000, info) {
613
+ if (!info) {
614
+ info = {};
615
+ info.failCause = {};
616
+ info.log = "";
617
+ }
562
618
  let scope = this.page;
619
+ if (selectors.frame) {
620
+ return selectors.frame;
621
+ }
563
622
  if (selectors.iframe_src || selectors.frameLocators) {
564
- const findFrame = (frame, framescope) => {
623
+ const findFrame = async (frame, framescope) => {
565
624
  for (let i = 0; i < frame.selectors.length; i++) {
566
625
  let frameLocator = frame.selectors[i];
567
626
  if (frameLocator.css) {
568
- framescope = framescope.frameLocator(frameLocator.css);
569
- break;
627
+ let testframescope = framescope.frameLocator(frameLocator.css);
628
+ if (frameLocator.index) {
629
+ testframescope = framescope.nth(frameLocator.index);
630
+ }
631
+ try {
632
+ await testframescope.owner().evaluateHandle(() => true, null, {
633
+ timeout: 5000,
634
+ });
635
+ framescope = testframescope;
636
+ break;
637
+ }
638
+ catch (error) {
639
+ console.error("frame not found " + frameLocator.css);
640
+ }
570
641
  }
571
642
  }
572
643
  if (frame.children) {
573
- return findFrame(frame.children, framescope);
644
+ return await findFrame(frame.children, framescope);
574
645
  }
575
646
  return framescope;
576
647
  };
577
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
578
648
  while (true) {
579
649
  let frameFound = false;
580
650
  if (selectors.nestFrmLoc) {
581
- scope = findFrame(selectors.nestFrmLoc, scope);
651
+ scope = await findFrame(selectors.nestFrmLoc, scope);
582
652
  frameFound = true;
583
653
  break;
584
654
  }
@@ -598,6 +668,8 @@ class StableBrowser {
598
668
  if (!scope) {
599
669
  info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
600
670
  if (performance.now() - startTime > timeout) {
671
+ info.failCause.iframeNotFound = true;
672
+ info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
601
673
  throw new Error("unable to locate iframe " + selectors.iframe_src);
602
674
  }
603
675
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -607,6 +679,30 @@ class StableBrowser {
607
679
  }
608
680
  }
609
681
  }
682
+ if (!scope) {
683
+ scope = this.page;
684
+ }
685
+ return scope;
686
+ }
687
+ async _getDocumentBody(selectors, timeout = 30000, info) {
688
+ let scope = await this._findFrameScope(selectors, timeout, info);
689
+ return scope.evaluate(() => {
690
+ var bodyContent = document.body.innerHTML;
691
+ return bodyContent;
692
+ });
693
+ }
694
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
695
+ if (!info) {
696
+ info = {};
697
+ info.failCause = {};
698
+ info.log = "";
699
+ }
700
+ let highPriorityTimeout = 5000;
701
+ let visibleOnlyTimeout = 6000;
702
+ let startTime = performance.now();
703
+ let locatorsCount = 0;
704
+ //let arrayMode = Array.isArray(selectors);
705
+ let scope = await this._findFrameScope(selectors, timeout, info);
610
706
  let selectorsLocators = null;
611
707
  selectorsLocators = selectors.locators;
612
708
  // group selectors by priority
@@ -708,6 +804,8 @@ class StableBrowser {
708
804
  }
709
805
  this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
710
806
  info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
807
+ info.failCause.locatorNotFound = true;
808
+ info.failCause.lastError = "failed to locate unique element";
711
809
  throw new Error("failed to locate first element no elements found, " + info.log);
712
810
  }
713
811
  async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
@@ -739,89 +837,129 @@ class StableBrowser {
739
837
  });
740
838
  result.locatorIndex = i;
741
839
  }
840
+ if (foundLocators.length > 1) {
841
+ info.failCause.foundMultiple = true;
842
+ }
742
843
  }
743
844
  return result;
744
845
  }
745
- async click(selectors, _params, options = {}, world = null) {
746
- this._validateSelectors(selectors);
846
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
747
847
  const startTime = Date.now();
748
- if (options && options.context) {
749
- selectors.locators[0].text = options.context;
848
+ let timeout = 30000;
849
+ if (options && options.timeout) {
850
+ timeout = options.timeout;
750
851
  }
751
- const info = {};
752
- info.log = "***** click on " + selectors.element_name + " *****\n";
753
- info.operation = "click";
754
- info.selectors = selectors;
755
- let error = null;
756
- let screenshotId = null;
757
- let screenshotPath = null;
852
+ while (true) {
853
+ try {
854
+ const result = await locate_element(this.context, elementDescription, "click");
855
+ if (result?.elementNumber >= 0) {
856
+ const selectors = {
857
+ frame: result?.frame,
858
+ locators: [
859
+ {
860
+ css: result?.css,
861
+ },
862
+ ],
863
+ };
864
+ await this.click(selectors, _params, options, world);
865
+ return;
866
+ }
867
+ }
868
+ catch (e) {
869
+ if (performance.now() - startTime > timeout) {
870
+ // throw e;
871
+ await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
872
+ }
873
+ }
874
+ await new Promise((resolve) => setTimeout(resolve, 3000));
875
+ }
876
+ }
877
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
878
+ const startTime = Date.now();
879
+ let timeout = 30000;
880
+ if (options && options.timeout) {
881
+ timeout = options.timeout;
882
+ }
883
+ while (true) {
884
+ try {
885
+ const result = await locate_element(this.context, elementDescription, "fill", value);
886
+ if (result?.elementNumber >= 0) {
887
+ const selectors = {
888
+ frame: result?.frame,
889
+ locators: [
890
+ {
891
+ css: result?.css,
892
+ },
893
+ ],
894
+ };
895
+ await this.clickType(selectors, value, false, _params, options, world);
896
+ return;
897
+ }
898
+ }
899
+ catch (e) {
900
+ if (performance.now() - startTime > timeout) {
901
+ // throw e;
902
+ await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
903
+ }
904
+ }
905
+ await new Promise((resolve) => setTimeout(resolve, 3000));
906
+ }
907
+ }
908
+ async click(selectors, _params, options = {}, world = null) {
909
+ const state = {
910
+ selectors,
911
+ _params,
912
+ options,
913
+ world,
914
+ text: "Click element",
915
+ type: Types.CLICK,
916
+ operation: "click",
917
+ log: "***** click on " + selectors.element_name + " *****\n",
918
+ };
758
919
  try {
759
- let element = await this._locate(selectors, info, _params);
760
- await this.scrollIfNeeded(element, info);
761
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
920
+ await _preCommand(state, this);
921
+ if (state.options && state.options.context) {
922
+ state.selectors.locators[0].text = state.options.context;
923
+ }
762
924
  try {
763
- await this._highlightElements(element);
764
- await element.click();
925
+ await state.element.click();
765
926
  await new Promise((resolve) => setTimeout(resolve, 1000));
766
927
  }
767
928
  catch (e) {
768
929
  // await this.closeUnexpectedPopups();
769
- info.log += "click failed, will try again" + "\n";
770
- element = await this._locate(selectors, info, _params);
771
- await element.dispatchEvent("click");
930
+ state.element = await this._locate(selectors, state.info, _params);
931
+ await state.element.dispatchEvent("click");
772
932
  await new Promise((resolve) => setTimeout(resolve, 1000));
773
933
  }
774
934
  await this.waitForPageLoad();
775
- return info;
935
+ return state.info;
776
936
  }
777
937
  catch (e) {
778
- this.logger.error("click failed " + info.log);
779
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
780
- info.screenshotPath = screenshotPath;
781
- Object.assign(e, { info: info });
782
- error = e;
783
- throw e;
938
+ await _commandError(state, e, this);
784
939
  }
785
940
  finally {
786
- const endTime = Date.now();
787
- this._reportToWorld(world, {
788
- element_name: selectors.element_name,
789
- type: Types.CLICK,
790
- text: `Click element`,
791
- screenshotId,
792
- result: error
793
- ? {
794
- status: "FAILED",
795
- startTime,
796
- endTime,
797
- message: error === null || error === void 0 ? void 0 : error.message,
798
- }
799
- : {
800
- status: "PASSED",
801
- startTime,
802
- endTime,
803
- },
804
- info: info,
805
- });
941
+ _commandFinally(state, this);
806
942
  }
807
943
  }
808
944
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
809
- this._validateSelectors(selectors);
810
- const startTime = Date.now();
811
- const info = {};
812
- info.log = "";
813
- info.operation = "setCheck";
814
- info.checked = checked;
815
- info.selectors = selectors;
816
- let error = null;
817
- let screenshotId = null;
818
- let screenshotPath = null;
945
+ const state = {
946
+ selectors,
947
+ _params,
948
+ options,
949
+ world,
950
+ type: checked ? Types.CHECK : Types.UNCHECK,
951
+ text: checked ? `Check element` : `Uncheck element`,
952
+ operation: "setCheck",
953
+ log: "***** check " + selectors.element_name + " *****\n",
954
+ };
819
955
  try {
820
- let element = await this._locate(selectors, info, _params);
821
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
956
+ await _preCommand(state, this);
957
+ state.info.checked = checked;
958
+ // let element = await this._locate(selectors, info, _params);
959
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
822
960
  try {
823
- await this._highlightElements(element);
824
- await element.setChecked(checked);
961
+ // await this._highlightElements(element);
962
+ await state.element.setChecked(checked);
825
963
  await new Promise((resolve) => setTimeout(resolve, 1000));
826
964
  }
827
965
  catch (e) {
@@ -830,179 +968,108 @@ class StableBrowser {
830
968
  }
831
969
  else {
832
970
  //await this.closeUnexpectedPopups();
833
- info.log += "setCheck failed, will try again" + "\n";
834
- element = await this._locate(selectors, info, _params);
835
- await element.setChecked(checked, { timeout: 5000, force: true });
971
+ state.info.log += "setCheck failed, will try again" + "\n";
972
+ state.element = await this._locate(selectors, state.info, _params);
973
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
836
974
  await new Promise((resolve) => setTimeout(resolve, 1000));
837
975
  }
838
976
  }
839
977
  await this.waitForPageLoad();
840
- return info;
978
+ return state.info;
841
979
  }
842
980
  catch (e) {
843
- this.logger.error("setCheck failed " + info.log);
844
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
845
- info.screenshotPath = screenshotPath;
846
- Object.assign(e, { info: info });
847
- error = e;
848
- throw e;
981
+ await _commandError(state, e, this);
849
982
  }
850
983
  finally {
851
- const endTime = Date.now();
852
- this._reportToWorld(world, {
853
- element_name: selectors.element_name,
854
- type: checked ? Types.CHECK : Types.UNCHECK,
855
- text: checked ? `Check element` : `Uncheck element`,
856
- screenshotId,
857
- result: error
858
- ? {
859
- status: "FAILED",
860
- startTime,
861
- endTime,
862
- message: error === null || error === void 0 ? void 0 : error.message,
863
- }
864
- : {
865
- status: "PASSED",
866
- startTime,
867
- endTime,
868
- },
869
- info: info,
870
- });
984
+ _commandFinally(state, this);
871
985
  }
872
986
  }
873
987
  async hover(selectors, _params, options = {}, world = null) {
874
- this._validateSelectors(selectors);
875
- const startTime = Date.now();
876
- const info = {};
877
- info.log = "";
878
- info.operation = "hover";
879
- info.selectors = selectors;
880
- let error = null;
881
- let screenshotId = null;
882
- let screenshotPath = null;
988
+ const state = {
989
+ selectors,
990
+ _params,
991
+ options,
992
+ world,
993
+ type: Types.HOVER,
994
+ text: `Hover element`,
995
+ operation: "hover",
996
+ log: "***** hover " + selectors.element_name + " *****\n",
997
+ };
883
998
  try {
884
- let element = await this._locate(selectors, info, _params);
885
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
999
+ await _preCommand(state, this);
886
1000
  try {
887
- await this._highlightElements(element);
888
- await element.hover();
1001
+ await state.element.hover();
889
1002
  await new Promise((resolve) => setTimeout(resolve, 1000));
890
1003
  }
891
1004
  catch (e) {
892
1005
  //await this.closeUnexpectedPopups();
893
- info.log += "hover failed, will try again" + "\n";
894
- element = await this._locate(selectors, info, _params);
895
- await element.hover({ timeout: 10000 });
1006
+ state.info.log += "hover failed, will try again" + "\n";
1007
+ state.element = await this._locate(selectors, state.info, _params);
1008
+ await state.element.hover({ timeout: 10000 });
896
1009
  await new Promise((resolve) => setTimeout(resolve, 1000));
897
1010
  }
898
1011
  await this.waitForPageLoad();
899
- return info;
1012
+ return state.info;
900
1013
  }
901
1014
  catch (e) {
902
- this.logger.error("hover failed " + info.log);
903
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
904
- info.screenshotPath = screenshotPath;
905
- Object.assign(e, { info: info });
906
- error = e;
907
- throw e;
1015
+ await _commandError(state, e, this);
908
1016
  }
909
1017
  finally {
910
- const endTime = Date.now();
911
- this._reportToWorld(world, {
912
- element_name: selectors.element_name,
913
- type: Types.HOVER,
914
- text: `Hover element`,
915
- screenshotId,
916
- result: error
917
- ? {
918
- status: "FAILED",
919
- startTime,
920
- endTime,
921
- message: error === null || error === void 0 ? void 0 : error.message,
922
- }
923
- : {
924
- status: "PASSED",
925
- startTime,
926
- endTime,
927
- },
928
- info: info,
929
- });
1018
+ _commandFinally(state, this);
930
1019
  }
931
1020
  }
932
1021
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
933
- this._validateSelectors(selectors);
934
1022
  if (!values) {
935
1023
  throw new Error("values is null");
936
1024
  }
937
- const startTime = Date.now();
938
- let error = null;
939
- let screenshotId = null;
940
- let screenshotPath = null;
941
- const info = {};
942
- info.log = "";
943
- info.operation = "selectOptions";
944
- info.selectors = selectors;
1025
+ const state = {
1026
+ selectors,
1027
+ _params,
1028
+ options,
1029
+ world,
1030
+ value: values.toString(),
1031
+ type: Types.SELECT,
1032
+ text: `Select option: ${values}`,
1033
+ operation: "selectOption",
1034
+ log: "***** select option " + selectors.element_name + " *****\n",
1035
+ };
945
1036
  try {
946
- let element = await this._locate(selectors, info, _params);
947
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1037
+ await _preCommand(state, this);
948
1038
  try {
949
- await this._highlightElements(element);
950
- await element.selectOption(values);
1039
+ await state.element.selectOption(values);
951
1040
  }
952
1041
  catch (e) {
953
1042
  //await this.closeUnexpectedPopups();
954
- info.log += "selectOption failed, will try force" + "\n";
955
- await element.selectOption(values, { timeout: 10000, force: true });
1043
+ state.info.log += "selectOption failed, will try force" + "\n";
1044
+ await state.element.selectOption(values, { timeout: 10000, force: true });
956
1045
  }
957
1046
  await this.waitForPageLoad();
958
- return info;
1047
+ return state.info;
959
1048
  }
960
1049
  catch (e) {
961
- this.logger.error("selectOption failed " + info.log);
962
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
963
- info.screenshotPath = screenshotPath;
964
- Object.assign(e, { info: info });
965
- this.logger.info("click failed, will try next selector");
966
- error = e;
967
- throw e;
1050
+ await _commandError(state, e, this);
968
1051
  }
969
1052
  finally {
970
- const endTime = Date.now();
971
- this._reportToWorld(world, {
972
- element_name: selectors.element_name,
973
- type: Types.SELECT,
974
- text: `Select option: ${values}`,
975
- value: values.toString(),
976
- screenshotId,
977
- result: error
978
- ? {
979
- status: "FAILED",
980
- startTime,
981
- endTime,
982
- message: error === null || error === void 0 ? void 0 : error.message,
983
- }
984
- : {
985
- status: "PASSED",
986
- startTime,
987
- endTime,
988
- },
989
- info: info,
990
- });
1053
+ _commandFinally(state, this);
991
1054
  }
992
1055
  }
993
1056
  async type(_value, _params = null, options = {}, world = null) {
994
- const startTime = Date.now();
995
- let error = null;
996
- let screenshotId = null;
997
- let screenshotPath = null;
998
- const info = {};
999
- info.log = "";
1000
- info.operation = "type";
1001
- _value = this._fixUsingParams(_value, _params);
1002
- info.value = _value;
1057
+ const state = {
1058
+ value: _value,
1059
+ _params,
1060
+ options,
1061
+ world,
1062
+ locate: false,
1063
+ scroll: false,
1064
+ highlight: false,
1065
+ type: Types.TYPE_PRESS,
1066
+ text: `Type value: ${_value}`,
1067
+ operation: "type",
1068
+ log: "",
1069
+ };
1003
1070
  try {
1004
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1005
- const valueSegment = _value.split("&&");
1071
+ await _preCommand(state, this);
1072
+ const valueSegment = state.value.split("&&");
1006
1073
  for (let i = 0; i < valueSegment.length; i++) {
1007
1074
  if (i > 0) {
1008
1075
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1022,134 +1089,76 @@ class StableBrowser {
1022
1089
  await this.page.keyboard.type(value);
1023
1090
  }
1024
1091
  }
1025
- return info;
1092
+ return state.info;
1026
1093
  }
1027
1094
  catch (e) {
1028
- //await this.closeUnexpectedPopups();
1029
- this.logger.error("type failed " + info.log);
1030
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1031
- info.screenshotPath = screenshotPath;
1032
- Object.assign(e, { info: info });
1033
- error = e;
1034
- throw e;
1095
+ await _commandError(state, e, this);
1035
1096
  }
1036
1097
  finally {
1037
- const endTime = Date.now();
1038
- this._reportToWorld(world, {
1039
- type: Types.TYPE_PRESS,
1040
- screenshotId,
1041
- value: _value,
1042
- text: `type value: ${_value}`,
1043
- result: error
1044
- ? {
1045
- status: "FAILED",
1046
- startTime,
1047
- endTime,
1048
- message: error === null || error === void 0 ? void 0 : error.message,
1049
- }
1050
- : {
1051
- status: "PASSED",
1052
- startTime,
1053
- endTime,
1054
- },
1055
- info: info,
1056
- });
1098
+ _commandFinally(state, this);
1057
1099
  }
1058
1100
  }
1059
1101
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1060
- // set input value for non fillable inputs like date, time, range, color, etc.
1061
- this._validateSelectors(selectors);
1062
- const startTime = Date.now();
1063
- const info = {};
1064
- info.log = "***** set input value " + selectors.element_name + " *****\n";
1065
- info.operation = "setInputValue";
1066
- info.selectors = selectors;
1067
- value = this._fixUsingParams(value, _params);
1068
- info.value = value;
1069
- let error = null;
1070
- let screenshotId = null;
1071
- let screenshotPath = null;
1102
+ const state = {
1103
+ selectors,
1104
+ _params,
1105
+ value,
1106
+ options,
1107
+ world,
1108
+ type: Types.SET_INPUT,
1109
+ text: `Set input value`,
1110
+ operation: "setInputValue",
1111
+ log: "***** set input value " + selectors.element_name + " *****\n",
1112
+ };
1072
1113
  try {
1073
- value = await this._replaceWithLocalData(value, this);
1074
- let element = await this._locate(selectors, info, _params);
1075
- await this.scrollIfNeeded(element, info);
1076
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1077
- await this._highlightElements(element);
1114
+ await _preCommand(state, this);
1115
+ let value = await this._replaceWithLocalData(state.value, this);
1078
1116
  try {
1079
- await element.evaluateHandle((el, value) => {
1117
+ await state.element.evaluateHandle((el, value) => {
1080
1118
  el.value = value;
1081
1119
  }, value);
1082
1120
  }
1083
1121
  catch (error) {
1084
1122
  this.logger.error("setInputValue failed, will try again");
1085
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1086
- info.screenshotPath = screenshotPath;
1087
- Object.assign(error, { info: info });
1088
- await element.evaluateHandle((el, value) => {
1123
+ await _screenshot(state, this);
1124
+ Object.assign(error, { info: state.info });
1125
+ await state.element.evaluateHandle((el, value) => {
1089
1126
  el.value = value;
1090
1127
  });
1091
1128
  }
1092
1129
  }
1093
1130
  catch (e) {
1094
- this.logger.error("setInputValue failed " + info.log);
1095
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1096
- info.screenshotPath = screenshotPath;
1097
- Object.assign(e, { info: info });
1098
- error = e;
1099
- throw e;
1131
+ await _commandError(state, e, this);
1100
1132
  }
1101
1133
  finally {
1102
- const endTime = Date.now();
1103
- this._reportToWorld(world, {
1104
- element_name: selectors.element_name,
1105
- type: Types.SET_INPUT,
1106
- text: `Set input value`,
1107
- value: value,
1108
- screenshotId,
1109
- result: error
1110
- ? {
1111
- status: "FAILED",
1112
- startTime,
1113
- endTime,
1114
- message: error === null || error === void 0 ? void 0 : error.message,
1115
- }
1116
- : {
1117
- status: "PASSED",
1118
- startTime,
1119
- endTime,
1120
- },
1121
- info: info,
1122
- });
1134
+ _commandFinally(state, this);
1123
1135
  }
1124
1136
  }
1125
1137
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1126
- this._validateSelectors(selectors);
1127
- const startTime = Date.now();
1128
- let error = null;
1129
- let screenshotId = null;
1130
- let screenshotPath = null;
1131
- const info = {};
1132
- info.log = "";
1133
- info.operation = Types.SET_DATE_TIME;
1134
- info.selectors = selectors;
1135
- info.value = value;
1138
+ const state = {
1139
+ selectors,
1140
+ _params,
1141
+ value: await this._replaceWithLocalData(value, this),
1142
+ options,
1143
+ world,
1144
+ type: Types.SET_DATE_TIME,
1145
+ text: `Set date time value: ${value}`,
1146
+ operation: "setDateTime",
1147
+ log: "***** set date time value " + selectors.element_name + " *****\n",
1148
+ throwError: false,
1149
+ };
1136
1150
  try {
1137
- value = await this._replaceWithLocalData(value, this);
1138
- let element = await this._locate(selectors, info, _params);
1139
- //insert red border around the element
1140
- await this.scrollIfNeeded(element, info);
1141
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1142
- await this._highlightElements(element);
1151
+ await _preCommand(state, this);
1143
1152
  try {
1144
- await element.click();
1153
+ await state.element.click();
1145
1154
  await new Promise((resolve) => setTimeout(resolve, 500));
1146
1155
  if (format) {
1147
- value = dayjs(value).format(format);
1148
- await element.fill(value);
1156
+ state.value = dayjs(state.value).format(format);
1157
+ await state.element.fill(state.value);
1149
1158
  }
1150
1159
  else {
1151
- const dateTimeValue = await getDateTimeValue({ value, element });
1152
- await element.evaluateHandle((el, dateTimeValue) => {
1160
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1161
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1153
1162
  el.value = ""; // clear input
1154
1163
  el.value = dateTimeValue;
1155
1164
  }, dateTimeValue);
@@ -1162,20 +1171,19 @@ class StableBrowser {
1162
1171
  }
1163
1172
  catch (err) {
1164
1173
  //await this.closeUnexpectedPopups();
1165
- this.logger.error("setting date time input failed " + JSON.stringify(info));
1174
+ this.logger.error("setting date time input failed " + JSON.stringify(state.info));
1166
1175
  this.logger.info("Trying again");
1167
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1168
- info.screenshotPath = screenshotPath;
1169
- Object.assign(err, { info: info });
1176
+ await _screenshot(state, this);
1177
+ Object.assign(err, { info: state.info });
1170
1178
  await element.click();
1171
1179
  await new Promise((resolve) => setTimeout(resolve, 500));
1172
1180
  if (format) {
1173
- value = dayjs(value).format(format);
1174
- await element.fill(value);
1181
+ state.value = dayjs(state.value).format(format);
1182
+ await state.element.fill(state.value);
1175
1183
  }
1176
1184
  else {
1177
- const dateTimeValue = await getDateTimeValue({ value, element });
1178
- await element.evaluateHandle((el, dateTimeValue) => {
1185
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1186
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1179
1187
  el.value = ""; // clear input
1180
1188
  el.value = dateTimeValue;
1181
1189
  }, dateTimeValue);
@@ -1188,61 +1196,39 @@ class StableBrowser {
1188
1196
  }
1189
1197
  }
1190
1198
  catch (e) {
1191
- error = e;
1192
- throw e;
1199
+ await _commandError(state, e, this);
1193
1200
  }
1194
1201
  finally {
1195
- const endTime = Date.now();
1196
- this._reportToWorld(world, {
1197
- element_name: selectors.element_name,
1198
- type: Types.SET_DATE_TIME,
1199
- screenshotId,
1200
- value: value,
1201
- text: `setDateTime input with value: ${value}`,
1202
- result: error
1203
- ? {
1204
- status: "FAILED",
1205
- startTime,
1206
- endTime,
1207
- message: error === null || error === void 0 ? void 0 : error.message,
1208
- }
1209
- : {
1210
- status: "PASSED",
1211
- startTime,
1212
- endTime,
1213
- },
1214
- info: info,
1215
- });
1202
+ _commandFinally(state, this);
1216
1203
  }
1217
1204
  }
1218
1205
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1219
1206
  _value = unEscapeString(_value);
1220
- this._validateSelectors(selectors);
1221
- const startTime = Date.now();
1222
- let error = null;
1223
- let screenshotId = null;
1224
- let screenshotPath = null;
1225
- const info = {};
1226
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1227
- info.operation = "clickType";
1228
- info.selectors = selectors;
1229
1207
  const newValue = await this._replaceWithLocalData(_value, world);
1208
+ const state = {
1209
+ selectors,
1210
+ _params,
1211
+ value: newValue,
1212
+ originalValue: _value,
1213
+ options,
1214
+ world,
1215
+ type: Types.FILL,
1216
+ text: `Click type input with value: ${_value}`,
1217
+ operation: "clickType",
1218
+ log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
1219
+ };
1230
1220
  if (newValue !== _value) {
1231
1221
  //this.logger.info(_value + "=" + newValue);
1232
1222
  _value = newValue;
1233
1223
  }
1234
- info.value = _value;
1235
- try {
1236
- let element = await this._locate(selectors, info, _params);
1237
- //insert red border around the element
1238
- await this.scrollIfNeeded(element, info);
1239
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1240
- await this._highlightElements(element);
1224
+ try {
1225
+ await _preCommand(state, this);
1226
+ state.info.value = _value;
1241
1227
  if (options === null || options === undefined || !options.press) {
1242
1228
  try {
1243
- let currentValue = await element.inputValue();
1229
+ let currentValue = await state.element.inputValue();
1244
1230
  if (currentValue) {
1245
- await element.fill("");
1231
+ await state.element.fill("");
1246
1232
  }
1247
1233
  }
1248
1234
  catch (e) {
@@ -1251,22 +1237,22 @@ class StableBrowser {
1251
1237
  }
1252
1238
  if (options === null || options === undefined || options.press) {
1253
1239
  try {
1254
- await element.click({ timeout: 5000 });
1240
+ await state.element.click({ timeout: 5000 });
1255
1241
  }
1256
1242
  catch (e) {
1257
- await element.dispatchEvent("click");
1243
+ await state.element.dispatchEvent("click");
1258
1244
  }
1259
1245
  }
1260
1246
  else {
1261
1247
  try {
1262
- await element.focus();
1248
+ await state.element.focus();
1263
1249
  }
1264
1250
  catch (e) {
1265
- await element.dispatchEvent("focus");
1251
+ await state.element.dispatchEvent("focus");
1266
1252
  }
1267
1253
  }
1268
1254
  await new Promise((resolve) => setTimeout(resolve, 500));
1269
- const valueSegment = _value.split("&&");
1255
+ const valueSegment = state.value.split("&&");
1270
1256
  for (let i = 0; i < valueSegment.length; i++) {
1271
1257
  if (i > 0) {
1272
1258
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1286,13 +1272,14 @@ class StableBrowser {
1286
1272
  await new Promise((resolve) => setTimeout(resolve, 500));
1287
1273
  }
1288
1274
  }
1275
+ await _screenshot(state, this);
1289
1276
  if (enter === true) {
1290
1277
  await new Promise((resolve) => setTimeout(resolve, 2000));
1291
1278
  await this.page.keyboard.press("Enter");
1292
1279
  await this.waitForPageLoad();
1293
1280
  }
1294
1281
  else if (enter === false) {
1295
- await element.dispatchEvent("change");
1282
+ await state.element.dispatchEvent("change");
1296
1283
  //await this.page.keyboard.press("Tab");
1297
1284
  }
1298
1285
  else {
@@ -1301,104 +1288,50 @@ class StableBrowser {
1301
1288
  await this.waitForPageLoad();
1302
1289
  }
1303
1290
  }
1304
- return info;
1291
+ return state.info;
1305
1292
  }
1306
1293
  catch (e) {
1307
- //await this.closeUnexpectedPopups();
1308
- this.logger.error("fill failed " + JSON.stringify(info));
1309
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1310
- info.screenshotPath = screenshotPath;
1311
- Object.assign(e, { info: info });
1312
- error = e;
1313
- throw e;
1294
+ await _commandError(state, e, this);
1314
1295
  }
1315
1296
  finally {
1316
- const endTime = Date.now();
1317
- this._reportToWorld(world, {
1318
- element_name: selectors.element_name,
1319
- type: Types.FILL,
1320
- screenshotId,
1321
- value: _value,
1322
- text: `clickType input with value: ${_value}`,
1323
- result: error
1324
- ? {
1325
- status: "FAILED",
1326
- startTime,
1327
- endTime,
1328
- message: error === null || error === void 0 ? void 0 : error.message,
1329
- }
1330
- : {
1331
- status: "PASSED",
1332
- startTime,
1333
- endTime,
1334
- },
1335
- info: info,
1336
- });
1297
+ _commandFinally(state, this);
1337
1298
  }
1338
1299
  }
1339
1300
  async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1340
- this._validateSelectors(selectors);
1341
- value = unEscapeString(value);
1342
- const startTime = Date.now();
1343
- let error = null;
1344
- let screenshotId = null;
1345
- let screenshotPath = null;
1346
- const info = {};
1347
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1348
- info.operation = "fill";
1349
- info.selectors = selectors;
1350
- info.value = value;
1301
+ const state = {
1302
+ selectors,
1303
+ _params,
1304
+ value: unEscapeString(value),
1305
+ options,
1306
+ world,
1307
+ type: Types.FILL,
1308
+ text: `Fill input with value: ${value}`,
1309
+ operation: "fill",
1310
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1311
+ };
1351
1312
  try {
1352
- let element = await this._locate(selectors, info, _params);
1353
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1354
- await this._highlightElements(element);
1355
- await element.fill(value);
1356
- await element.dispatchEvent("change");
1313
+ await _preCommand(state, this);
1314
+ await state.element.fill(value);
1315
+ await state.element.dispatchEvent("change");
1357
1316
  if (enter) {
1358
1317
  await new Promise((resolve) => setTimeout(resolve, 2000));
1359
1318
  await this.page.keyboard.press("Enter");
1360
1319
  }
1361
1320
  await this.waitForPageLoad();
1362
- return info;
1321
+ return state.info;
1363
1322
  }
1364
1323
  catch (e) {
1365
- //await this.closeUnexpectedPopups();
1366
- this.logger.error("fill failed " + info.log);
1367
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1368
- info.screenshotPath = screenshotPath;
1369
- Object.assign(e, { info: info });
1370
- error = e;
1371
- throw e;
1324
+ await _commandError(state, e, this);
1372
1325
  }
1373
1326
  finally {
1374
- const endTime = Date.now();
1375
- this._reportToWorld(world, {
1376
- element_name: selectors.element_name,
1377
- type: Types.FILL,
1378
- screenshotId,
1379
- value,
1380
- text: `Fill input with value: ${value}`,
1381
- result: error
1382
- ? {
1383
- status: "FAILED",
1384
- startTime,
1385
- endTime,
1386
- message: error === null || error === void 0 ? void 0 : error.message,
1387
- }
1388
- : {
1389
- status: "PASSED",
1390
- startTime,
1391
- endTime,
1392
- },
1393
- info: info,
1394
- });
1327
+ _commandFinally(state, this);
1395
1328
  }
1396
1329
  }
1397
1330
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1398
1331
  return await this._getText(selectors, 0, _params, options, info, world);
1399
1332
  }
1400
1333
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1401
- this._validateSelectors(selectors);
1334
+ _validateSelectors(selectors);
1402
1335
  let screenshotId = null;
1403
1336
  let screenshotPath = null;
1404
1337
  if (!info.log) {
@@ -1442,166 +1375,124 @@ class StableBrowser {
1442
1375
  }
1443
1376
  }
1444
1377
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1445
- var _a;
1446
- this._validateSelectors(selectors);
1447
1378
  if (!pattern) {
1448
1379
  throw new Error("pattern is null");
1449
1380
  }
1450
1381
  if (!text) {
1451
1382
  throw new Error("text is null");
1452
1383
  }
1384
+ const state = {
1385
+ selectors,
1386
+ _params,
1387
+ pattern,
1388
+ value: pattern,
1389
+ options,
1390
+ world,
1391
+ locate: false,
1392
+ scroll: false,
1393
+ screenshot: false,
1394
+ highlight: false,
1395
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1396
+ text: `Verify element contains pattern: ${pattern}`,
1397
+ operation: "containsPattern",
1398
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1399
+ };
1453
1400
  const newValue = await this._replaceWithLocalData(text, world);
1454
1401
  if (newValue !== text) {
1455
1402
  this.logger.info(text + "=" + newValue);
1456
1403
  text = newValue;
1457
1404
  }
1458
- const startTime = Date.now();
1459
- let error = null;
1460
- let screenshotId = null;
1461
- let screenshotPath = null;
1462
- const info = {};
1463
- info.log =
1464
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1465
- info.operation = "containsPattern";
1466
- info.selectors = selectors;
1467
- info.value = text;
1468
- info.pattern = pattern;
1469
1405
  let foundObj = null;
1470
1406
  try {
1471
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1407
+ await _preCommand(state, this);
1408
+ state.info.pattern = pattern;
1409
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1472
1410
  if (foundObj && foundObj.element) {
1473
- await this.scrollIfNeeded(foundObj.element, info);
1411
+ await this.scrollIfNeeded(foundObj.element, state.info);
1474
1412
  }
1475
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1413
+ await _screenshot(state, this);
1476
1414
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1477
1415
  pattern = pattern.replace("{text}", escapedText);
1478
1416
  let regex = new RegExp(pattern, "im");
1479
- 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))) {
1480
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1417
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1418
+ state.info.foundText = foundObj?.text;
1481
1419
  throw new Error("element doesn't contain text " + text);
1482
1420
  }
1483
- return info;
1421
+ return state.info;
1484
1422
  }
1485
1423
  catch (e) {
1486
- //await this.closeUnexpectedPopups();
1487
- this.logger.error("verify element contains text failed " + info.log);
1488
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1489
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1490
- info.screenshotPath = screenshotPath;
1491
- Object.assign(e, { info: info });
1492
- error = e;
1493
- throw e;
1424
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1425
+ await _commandError(state, e, this);
1494
1426
  }
1495
1427
  finally {
1496
- const endTime = Date.now();
1497
- this._reportToWorld(world, {
1498
- element_name: selectors.element_name,
1499
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1500
- value: pattern,
1501
- text: `Verify element contains pattern: ${pattern}`,
1502
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1503
- result: error
1504
- ? {
1505
- status: "FAILED",
1506
- startTime,
1507
- endTime,
1508
- message: error === null || error === void 0 ? void 0 : error.message,
1509
- }
1510
- : {
1511
- status: "PASSED",
1512
- startTime,
1513
- endTime,
1514
- },
1515
- info: info,
1516
- });
1428
+ _commandFinally(state, this);
1517
1429
  }
1518
1430
  }
1519
1431
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1520
- var _a, _b, _c;
1521
- this._validateSelectors(selectors);
1522
- text = unEscapeString(text);
1432
+ const state = {
1433
+ selectors,
1434
+ _params,
1435
+ value: text,
1436
+ options,
1437
+ world,
1438
+ locate: false,
1439
+ scroll: false,
1440
+ screenshot: false,
1441
+ highlight: false,
1442
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1443
+ text: `Verify element contains text: ${text}`,
1444
+ operation: "containsText",
1445
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1446
+ };
1523
1447
  if (!text) {
1524
1448
  throw new Error("text is null");
1525
1449
  }
1526
- const startTime = Date.now();
1527
- let error = null;
1528
- let screenshotId = null;
1529
- let screenshotPath = null;
1530
- const info = {};
1531
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1532
- info.operation = "containsText";
1533
- info.selectors = selectors;
1450
+ text = unEscapeString(text);
1534
1451
  const newValue = await this._replaceWithLocalData(text, world);
1535
1452
  if (newValue !== text) {
1536
1453
  this.logger.info(text + "=" + newValue);
1537
1454
  text = newValue;
1538
1455
  }
1539
- info.value = text;
1540
1456
  let foundObj = null;
1541
1457
  try {
1542
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1458
+ await _preCommand(state, this);
1459
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1543
1460
  if (foundObj && foundObj.element) {
1544
- await this.scrollIfNeeded(foundObj.element, info);
1461
+ await this.scrollIfNeeded(foundObj.element, state.info);
1545
1462
  }
1546
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1463
+ await _screenshot(state, this);
1547
1464
  const dateAlternatives = findDateAlternatives(text);
1548
1465
  const numberAlternatives = findNumberAlternatives(text);
1549
1466
  if (dateAlternatives.date) {
1550
1467
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1551
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1552
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1553
- return info;
1468
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1469
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1470
+ return state.info;
1554
1471
  }
1555
1472
  }
1556
1473
  throw new Error("element doesn't contain text " + text);
1557
1474
  }
1558
1475
  else if (numberAlternatives.number) {
1559
1476
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1560
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1561
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1562
- return info;
1477
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1478
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1479
+ return state.info;
1563
1480
  }
1564
1481
  }
1565
1482
  throw new Error("element doesn't contain text " + text);
1566
1483
  }
1567
- 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))) {
1568
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1569
- info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1484
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1485
+ state.info.foundText = foundObj?.text;
1486
+ state.info.value = foundObj?.value;
1570
1487
  throw new Error("element doesn't contain text " + text);
1571
1488
  }
1572
- return info;
1489
+ return state.info;
1573
1490
  }
1574
1491
  catch (e) {
1575
- //await this.closeUnexpectedPopups();
1576
- this.logger.error("verify element contains text failed " + info.log);
1577
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1578
- info.screenshotPath = screenshotPath;
1579
- Object.assign(e, { info: info });
1580
- error = e;
1581
- throw e;
1492
+ await _commandError(state, e, this);
1582
1493
  }
1583
1494
  finally {
1584
- const endTime = Date.now();
1585
- this._reportToWorld(world, {
1586
- element_name: selectors.element_name,
1587
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1588
- text: `Verify element contains text: ${text}`,
1589
- value: text,
1590
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1591
- result: error
1592
- ? {
1593
- status: "FAILED",
1594
- startTime,
1595
- endTime,
1596
- message: error === null || error === void 0 ? void 0 : error.message,
1597
- }
1598
- : {
1599
- status: "PASSED",
1600
- startTime,
1601
- endTime,
1602
- },
1603
- info: info,
1604
- });
1495
+ _commandFinally(state, this);
1605
1496
  }
1606
1497
  }
1607
1498
  _getDataFile(world = null) {
@@ -1830,7 +1721,6 @@ class StableBrowser {
1830
1721
  }
1831
1722
  async takeScreenshot(screenshotPath) {
1832
1723
  const playContext = this.context.playContext;
1833
- const client = await playContext.newCDPSession(this.page);
1834
1724
  // Using CDP to capture the screenshot
1835
1725
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1836
1726
  document.body.scrollWidth,
@@ -1840,164 +1730,109 @@ class StableBrowser {
1840
1730
  document.body.clientWidth,
1841
1731
  document.documentElement.clientWidth,
1842
1732
  ])));
1843
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1844
- document.body.scrollHeight,
1845
- document.documentElement.scrollHeight,
1846
- document.body.offsetHeight,
1847
- document.documentElement.offsetHeight,
1848
- document.body.clientHeight,
1849
- document.documentElement.clientHeight,
1850
- ])));
1851
- const { data } = await client.send("Page.captureScreenshot", {
1852
- format: "png",
1853
- // clip: {
1854
- // x: 0,
1855
- // y: 0,
1856
- // width: viewportWidth,
1857
- // height: viewportHeight,
1858
- // scale: 1,
1859
- // },
1860
- });
1861
- if (!screenshotPath) {
1862
- return data;
1863
- }
1864
- let screenshotBuffer = Buffer.from(data, "base64");
1865
- const sharpBuffer = sharp(screenshotBuffer);
1866
- const metadata = await sharpBuffer.metadata();
1867
- //check if you are on retina display and reduce the quality of the image
1868
- if (metadata.width > viewportWidth || metadata.height > viewportHeight) {
1869
- screenshotBuffer = await sharpBuffer
1870
- .resize(viewportWidth, viewportHeight, {
1871
- fit: sharp.fit.inside,
1872
- withoutEnlargement: true,
1873
- })
1874
- .toBuffer();
1875
- }
1876
- fs.writeFileSync(screenshotPath, screenshotBuffer);
1877
- await client.detach();
1733
+ let screenshotBuffer = null;
1734
+ if (this.context.browserName === "chromium") {
1735
+ const client = await playContext.newCDPSession(this.page);
1736
+ const { data } = await client.send("Page.captureScreenshot", {
1737
+ format: "png",
1738
+ // clip: {
1739
+ // x: 0,
1740
+ // y: 0,
1741
+ // width: viewportWidth,
1742
+ // height: viewportHeight,
1743
+ // scale: 1,
1744
+ // },
1745
+ });
1746
+ await client.detach();
1747
+ if (!screenshotPath) {
1748
+ return data;
1749
+ }
1750
+ screenshotBuffer = Buffer.from(data, "base64");
1751
+ }
1752
+ else {
1753
+ screenshotBuffer = await this.page.screenshot();
1754
+ }
1755
+ let image = await Jimp.read(screenshotBuffer);
1756
+ // Get the image dimensions
1757
+ const { width, height } = image.bitmap;
1758
+ const resizeRatio = viewportWidth / width;
1759
+ // Resize the image to fit within the viewport dimensions without enlarging
1760
+ if (width > viewportWidth) {
1761
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1762
+ await image.write(screenshotPath);
1763
+ }
1764
+ else {
1765
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1766
+ }
1878
1767
  }
1879
1768
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1880
- this._validateSelectors(selectors);
1881
- const startTime = Date.now();
1882
- let error = null;
1883
- let screenshotId = null;
1884
- let screenshotPath = null;
1769
+ const state = {
1770
+ selectors,
1771
+ _params,
1772
+ options,
1773
+ world,
1774
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1775
+ text: `Verify element exists in page`,
1776
+ operation: "verifyElementExistInPage",
1777
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1778
+ };
1885
1779
  await new Promise((resolve) => setTimeout(resolve, 2000));
1886
- const info = {};
1887
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
1888
- info.operation = "verify";
1889
- info.selectors = selectors;
1890
1780
  try {
1891
- const element = await this._locate(selectors, info, _params);
1892
- if (element) {
1893
- await this.scrollIfNeeded(element, info);
1894
- }
1895
- await this._highlightElements(element);
1896
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1897
- await expect(element).toHaveCount(1, { timeout: 10000 });
1898
- return info;
1781
+ await _preCommand(state, this);
1782
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1783
+ return state.info;
1899
1784
  }
1900
1785
  catch (e) {
1901
- //await this.closeUnexpectedPopups();
1902
- this.logger.error("verify failed " + info.log);
1903
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1904
- info.screenshotPath = screenshotPath;
1905
- Object.assign(e, { info: info });
1906
- error = e;
1907
- throw e;
1786
+ await _commandError(state, e, this);
1908
1787
  }
1909
1788
  finally {
1910
- const endTime = Date.now();
1911
- this._reportToWorld(world, {
1912
- element_name: selectors.element_name,
1913
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1914
- text: "Verify element exists in page",
1915
- screenshotId,
1916
- result: error
1917
- ? {
1918
- status: "FAILED",
1919
- startTime,
1920
- endTime,
1921
- message: error === null || error === void 0 ? void 0 : error.message,
1922
- }
1923
- : {
1924
- status: "PASSED",
1925
- startTime,
1926
- endTime,
1927
- },
1928
- info: info,
1929
- });
1789
+ _commandFinally(state, this);
1930
1790
  }
1931
1791
  }
1932
1792
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1933
- this._validateSelectors(selectors);
1934
- const startTime = Date.now();
1935
- let error = null;
1936
- let screenshotId = null;
1937
- let screenshotPath = null;
1793
+ const state = {
1794
+ selectors,
1795
+ _params,
1796
+ attribute,
1797
+ variable,
1798
+ options,
1799
+ world,
1800
+ type: Types.EXTRACT,
1801
+ text: `Extract attribute from element`,
1802
+ operation: "extractAttribute",
1803
+ log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
1804
+ };
1938
1805
  await new Promise((resolve) => setTimeout(resolve, 2000));
1939
- const info = {};
1940
- info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
1941
- info.operation = "extract";
1942
- info.selectors = selectors;
1943
1806
  try {
1944
- const element = await this._locate(selectors, info, _params);
1945
- await this._highlightElements(element);
1946
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1807
+ await _preCommand(state, this);
1947
1808
  switch (attribute) {
1948
1809
  case "inner_text":
1949
- info.value = await element.innerText();
1810
+ state.value = await state.element.innerText();
1950
1811
  break;
1951
1812
  case "href":
1952
- info.value = await element.getAttribute("href");
1813
+ state.value = await state.element.getAttribute("href");
1953
1814
  break;
1954
1815
  case "value":
1955
- info.value = await element.inputValue();
1816
+ state.value = await state.element.inputValue();
1956
1817
  break;
1957
1818
  default:
1958
- info.value = await element.getAttribute(attribute);
1819
+ state.value = await state.element.getAttribute(attribute);
1959
1820
  break;
1960
1821
  }
1961
- this[variable] = info.value;
1822
+ state.info.value = state.value;
1823
+ this[variable] = state.value;
1962
1824
  if (world) {
1963
- world[variable] = info.value;
1825
+ world[variable] = state.value;
1964
1826
  }
1965
- this.setTestData({ [variable]: info.value }, world);
1966
- this.logger.info("set test data: " + variable + "=" + info.value);
1967
- return info;
1827
+ this.setTestData({ [variable]: state.value }, world);
1828
+ this.logger.info("set test data: " + variable + "=" + state.value);
1829
+ return state.info;
1968
1830
  }
1969
1831
  catch (e) {
1970
- //await this.closeUnexpectedPopups();
1971
- this.logger.error("extract failed " + info.log);
1972
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1973
- info.screenshotPath = screenshotPath;
1974
- Object.assign(e, { info: info });
1975
- error = e;
1976
- throw e;
1832
+ await _commandError(state, e, this);
1977
1833
  }
1978
1834
  finally {
1979
- const endTime = Date.now();
1980
- this._reportToWorld(world, {
1981
- element_name: selectors.element_name,
1982
- type: Types.EXTRACT_ATTRIBUTE,
1983
- variable: variable,
1984
- value: info.value,
1985
- text: "Extract attribute from element",
1986
- screenshotId,
1987
- result: error
1988
- ? {
1989
- status: "FAILED",
1990
- startTime,
1991
- endTime,
1992
- message: error === null || error === void 0 ? void 0 : error.message,
1993
- }
1994
- : {
1995
- status: "PASSED",
1996
- startTime,
1997
- endTime,
1998
- },
1999
- info: info,
2000
- });
1835
+ _commandFinally(state, this);
2001
1836
  }
2002
1837
  }
2003
1838
  async extractEmailData(emailAddress, options, world) {
@@ -2074,7 +1909,8 @@ class StableBrowser {
2074
1909
  catch (e) {
2075
1910
  errorCount++;
2076
1911
  if (errorCount > 3) {
2077
- throw e;
1912
+ // throw e;
1913
+ await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
2078
1914
  }
2079
1915
  // ignore
2080
1916
  }
@@ -2185,7 +2021,8 @@ class StableBrowser {
2185
2021
  info.screenshotPath = screenshotPath;
2186
2022
  Object.assign(e, { info: info });
2187
2023
  error = e;
2188
- throw e;
2024
+ // throw e;
2025
+ await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
2189
2026
  }
2190
2027
  finally {
2191
2028
  const endTime = Date.now();
@@ -2198,7 +2035,7 @@ class StableBrowser {
2198
2035
  status: "FAILED",
2199
2036
  startTime,
2200
2037
  endTime,
2201
- message: error === null || error === void 0 ? void 0 : error.message,
2038
+ message: error?.message,
2202
2039
  }
2203
2040
  : {
2204
2041
  status: "PASSED",
@@ -2211,52 +2048,58 @@ class StableBrowser {
2211
2048
  }
2212
2049
  async verifyTextExistInPage(text, options = {}, world = null) {
2213
2050
  text = unEscapeString(text);
2214
- const startTime = Date.now();
2051
+ const state = {
2052
+ text_search: text,
2053
+ options,
2054
+ world,
2055
+ locate: false,
2056
+ scroll: false,
2057
+ highlight: false,
2058
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2059
+ text: `Verify text exists in page`,
2060
+ operation: "verifyTextExistInPage",
2061
+ log: "***** verify text " + text + " exists in page *****\n",
2062
+ };
2215
2063
  const timeout = this._getLoadTimeout(options);
2216
- let error = null;
2217
- let screenshotId = null;
2218
- let screenshotPath = null;
2219
2064
  await new Promise((resolve) => setTimeout(resolve, 2000));
2220
- const info = {};
2221
- info.log = "***** verify text " + text + " exists in page *****\n";
2222
- info.operation = "verifyTextExistInPage";
2223
2065
  const newValue = await this._replaceWithLocalData(text, world);
2224
2066
  if (newValue !== text) {
2225
2067
  this.logger.info(text + "=" + newValue);
2226
2068
  text = newValue;
2227
2069
  }
2228
- info.text = text;
2229
2070
  let dateAlternatives = findDateAlternatives(text);
2230
2071
  let numberAlternatives = findNumberAlternatives(text);
2231
2072
  try {
2073
+ await _preCommand(state, this);
2074
+ state.info.text = text;
2232
2075
  while (true) {
2233
2076
  const frames = this.page.frames();
2234
2077
  let results = [];
2235
2078
  for (let i = 0; i < frames.length; i++) {
2236
2079
  if (dateAlternatives.date) {
2237
2080
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2238
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2081
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
2239
2082
  result.frame = frames[i];
2240
2083
  results.push(result);
2241
2084
  }
2242
2085
  }
2243
2086
  else if (numberAlternatives.number) {
2244
2087
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2245
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2088
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
2246
2089
  result.frame = frames[i];
2247
2090
  results.push(result);
2248
2091
  }
2249
2092
  }
2250
2093
  else {
2251
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2094
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2252
2095
  result.frame = frames[i];
2253
2096
  results.push(result);
2254
2097
  }
2255
2098
  }
2256
- info.results = results;
2099
+ state.info.results = results;
2257
2100
  const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2258
2101
  if (resultWithElementsFound.length === 0) {
2259
- if (Date.now() - startTime > timeout) {
2102
+ if (Date.now() - state.startTime > timeout) {
2260
2103
  throw new Error(`Text ${text} not found in page`);
2261
2104
  }
2262
2105
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -2268,44 +2111,20 @@ class StableBrowser {
2268
2111
  await this._highlightElements(frame, dataAttribute);
2269
2112
  const element = await frame.$(dataAttribute);
2270
2113
  if (element) {
2271
- await this.scrollIfNeeded(element, info);
2114
+ await this.scrollIfNeeded(element, state.info);
2272
2115
  await element.dispatchEvent("bvt_verify_page_contains_text");
2273
2116
  }
2274
2117
  }
2275
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2276
- return info;
2118
+ await _screenshot(state, this);
2119
+ return state.info;
2277
2120
  }
2278
2121
  // await expect(element).toHaveCount(1, { timeout: 10000 });
2279
2122
  }
2280
2123
  catch (e) {
2281
- //await this.closeUnexpectedPopups();
2282
- this.logger.error("verify text exist in page failed " + info.log);
2283
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2284
- info.screenshotPath = screenshotPath;
2285
- Object.assign(e, { info: info });
2286
- error = e;
2287
- throw e;
2124
+ await _commandError(state, e, this);
2288
2125
  }
2289
2126
  finally {
2290
- const endTime = Date.now();
2291
- this._reportToWorld(world, {
2292
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2293
- text: "Verify text exists in page",
2294
- screenshotId,
2295
- result: error
2296
- ? {
2297
- status: "FAILED",
2298
- startTime,
2299
- endTime,
2300
- message: error === null || error === void 0 ? void 0 : error.message,
2301
- }
2302
- : {
2303
- status: "PASSED",
2304
- startTime,
2305
- endTime,
2306
- },
2307
- info: info,
2308
- });
2127
+ _commandFinally(state, this);
2309
2128
  }
2310
2129
  }
2311
2130
  _getServerUrl() {
@@ -2368,7 +2187,8 @@ class StableBrowser {
2368
2187
  info.screenshotPath = screenshotPath;
2369
2188
  Object.assign(e, { info: info });
2370
2189
  error = e;
2371
- throw e;
2190
+ // throw e;
2191
+ await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
2372
2192
  }
2373
2193
  finally {
2374
2194
  const endTime = Date.now();
@@ -2381,7 +2201,7 @@ class StableBrowser {
2381
2201
  status: "FAILED",
2382
2202
  startTime,
2383
2203
  endTime,
2384
- message: error === null || error === void 0 ? void 0 : error.message,
2204
+ message: error?.message,
2385
2205
  }
2386
2206
  : {
2387
2207
  status: "PASSED",
@@ -2413,7 +2233,7 @@ class StableBrowser {
2413
2233
  this.logger.info("Table data verified");
2414
2234
  }
2415
2235
  async getTableData(selectors, _params = null, options = {}, world = null) {
2416
- this._validateSelectors(selectors);
2236
+ _validateSelectors(selectors);
2417
2237
  const startTime = Date.now();
2418
2238
  let error = null;
2419
2239
  let screenshotId = null;
@@ -2435,7 +2255,8 @@ class StableBrowser {
2435
2255
  info.screenshotPath = screenshotPath;
2436
2256
  Object.assign(e, { info: info });
2437
2257
  error = e;
2438
- throw e;
2258
+ // throw e;
2259
+ await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
2439
2260
  }
2440
2261
  finally {
2441
2262
  const endTime = Date.now();
@@ -2449,7 +2270,7 @@ class StableBrowser {
2449
2270
  status: "FAILED",
2450
2271
  startTime,
2451
2272
  endTime,
2452
- message: error === null || error === void 0 ? void 0 : error.message,
2273
+ message: error?.message,
2453
2274
  }
2454
2275
  : {
2455
2276
  status: "PASSED",
@@ -2461,7 +2282,7 @@ class StableBrowser {
2461
2282
  }
2462
2283
  }
2463
2284
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2464
- this._validateSelectors(selectors);
2285
+ _validateSelectors(selectors);
2465
2286
  if (!query) {
2466
2287
  throw new Error("query is null");
2467
2288
  }
@@ -2600,7 +2421,8 @@ class StableBrowser {
2600
2421
  info.screenshotPath = screenshotPath;
2601
2422
  Object.assign(e, { info: info });
2602
2423
  error = e;
2603
- throw e;
2424
+ // throw e;
2425
+ await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
2604
2426
  }
2605
2427
  finally {
2606
2428
  const endTime = Date.now();
@@ -2614,7 +2436,7 @@ class StableBrowser {
2614
2436
  status: "FAILED",
2615
2437
  startTime,
2616
2438
  endTime,
2617
- message: error === null || error === void 0 ? void 0 : error.message,
2439
+ message: error?.message,
2618
2440
  }
2619
2441
  : {
2620
2442
  status: "PASSED",
@@ -2626,27 +2448,7 @@ class StableBrowser {
2626
2448
  }
2627
2449
  }
2628
2450
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2629
- if (!value) {
2630
- return value;
2631
- }
2632
- // find all the accurance of {{(.*?)}} and replace with the value
2633
- let regex = /{{(.*?)}}/g;
2634
- let matches = value.match(regex);
2635
- if (matches) {
2636
- const testData = this.getTestData(world);
2637
- for (let i = 0; i < matches.length; i++) {
2638
- let match = matches[i];
2639
- let key = match.substring(2, match.length - 2);
2640
- let newValue = objectPath.get(testData, key, null);
2641
- if (newValue !== null) {
2642
- value = value.replace(match, newValue);
2643
- }
2644
- }
2645
- }
2646
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2647
- return await decrypt(value, null, totpWait);
2648
- }
2649
- return value;
2451
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2650
2452
  }
2651
2453
  _getLoadTimeout(options) {
2652
2454
  let timeout = 15000;
@@ -2706,7 +2508,7 @@ class StableBrowser {
2706
2508
  status: "FAILED",
2707
2509
  startTime,
2708
2510
  endTime,
2709
- message: error === null || error === void 0 ? void 0 : error.message,
2511
+ message: error?.message,
2710
2512
  }
2711
2513
  : {
2712
2514
  status: "PASSED",
@@ -2717,39 +2519,28 @@ class StableBrowser {
2717
2519
  }
2718
2520
  }
2719
2521
  async closePage(options = {}, world = null) {
2720
- const startTime = Date.now();
2721
- let error = null;
2722
- let screenshotId = null;
2723
- let screenshotPath = null;
2724
- const info = {};
2522
+ const state = {
2523
+ options,
2524
+ world,
2525
+ locate: false,
2526
+ scroll: false,
2527
+ highlight: false,
2528
+ type: Types.CLOSE_PAGE,
2529
+ text: `Close page`,
2530
+ operation: "closePage",
2531
+ log: "***** close page *****\n",
2532
+ throwError: false,
2533
+ };
2725
2534
  try {
2535
+ await _preCommand(state, this);
2726
2536
  await this.page.close();
2727
2537
  }
2728
2538
  catch (e) {
2729
2539
  console.log(".");
2540
+ await _commandError(state, e, this);
2730
2541
  }
2731
2542
  finally {
2732
- await new Promise((resolve) => setTimeout(resolve, 2000));
2733
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2734
- const endTime = Date.now();
2735
- this._reportToWorld(world, {
2736
- type: Types.CLOSE_PAGE,
2737
- text: "close page",
2738
- screenshotId,
2739
- result: error
2740
- ? {
2741
- status: "FAILED",
2742
- startTime,
2743
- endTime,
2744
- message: error === null || error === void 0 ? void 0 : error.message,
2745
- }
2746
- : {
2747
- status: "PASSED",
2748
- startTime,
2749
- endTime,
2750
- },
2751
- info: info,
2752
- });
2543
+ _commandFinally(state, this);
2753
2544
  }
2754
2545
  }
2755
2546
  async setViewportSize(width, hight, options = {}, world = null) {
@@ -2769,6 +2560,7 @@ class StableBrowser {
2769
2560
  }
2770
2561
  catch (e) {
2771
2562
  console.log(".");
2563
+ await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
2772
2564
  }
2773
2565
  finally {
2774
2566
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2783,7 +2575,7 @@ class StableBrowser {
2783
2575
  status: "FAILED",
2784
2576
  startTime,
2785
2577
  endTime,
2786
- message: error === null || error === void 0 ? void 0 : error.message,
2578
+ message: error?.message,
2787
2579
  }
2788
2580
  : {
2789
2581
  status: "PASSED",
@@ -2805,6 +2597,7 @@ class StableBrowser {
2805
2597
  }
2806
2598
  catch (e) {
2807
2599
  console.log(".");
2600
+ await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
2808
2601
  }
2809
2602
  finally {
2810
2603
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2819,7 +2612,7 @@ class StableBrowser {
2819
2612
  status: "FAILED",
2820
2613
  startTime,
2821
2614
  endTime,
2822
- message: error === null || error === void 0 ? void 0 : error.message,
2615
+ message: error?.message,
2823
2616
  }
2824
2617
  : {
2825
2618
  status: "PASSED",