automation_model 1.0.447-dev → 1.0.447-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,15 +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.configuration = null;
56
- this.appName = "main";
65
+ this.world = world;
57
66
  if (!this.logger) {
58
67
  this.logger = console;
59
68
  }
@@ -78,20 +87,32 @@ class StableBrowser {
78
87
  catch (e) {
79
88
  this.logger.error("unable to read ai_config.json");
80
89
  }
81
- context.pageLoading = { status: false };
82
- context.pages = [this.page];
83
90
  const logFolder = path.join(this.project_path, "logs", "web");
84
- this.webLogFile = this.getWebLogFile(logFolder);
85
- 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);
86
97
  }
87
98
  registerEventListeners(context) {
88
- this.registerConsoleLogListener(this.page, context, this.webLogFile);
89
- this.registerRequestListener();
99
+ this.registerConsoleLogListener(this.page, context);
100
+ this.registerRequestListener(this.page, context, this.webLogFile);
101
+ if (!context.pageLoading) {
102
+ context.pageLoading = { status: false };
103
+ }
90
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
+ }
91
110
  context.pageLoading.status = true;
92
111
  this.page = page;
93
112
  context.page = page;
94
113
  context.pages.push(page);
114
+ registerNetworkEvents(this.world, this, context, this.page);
115
+ registerDownloadEvent(this.page, this.world, context);
95
116
  page.on("close", async () => {
96
117
  if (this.context && this.context.pages && this.context.pages.length > 1) {
97
118
  this.context.pages.pop();
@@ -121,10 +142,10 @@ class StableBrowser {
121
142
  if (this.appName === appName) {
122
143
  return;
123
144
  }
124
- let newContextCreated = false;
145
+ let navigate = false;
125
146
  if (!apps[appName]) {
126
147
  let newContext = await getContext(null, false, this.logger, appName, false, this);
127
- newContextCreated = true;
148
+ navigate = true;
128
149
  apps[appName] = {
129
150
  context: newContext,
130
151
  browser: newContext.browser,
@@ -136,8 +157,7 @@ class StableBrowser {
136
157
  this._copyContext(apps[appName], this);
137
158
  apps[this.appName] = tempContext;
138
159
  this.appName = appName;
139
- if (newContextCreated) {
140
- this.registerEventListeners(this.context);
160
+ if (navigate) {
141
161
  await this.goto(this.context.environment.baseUrl);
142
162
  await this.waitForPageLoad();
143
163
  }
@@ -158,37 +178,63 @@ class StableBrowser {
158
178
  const fileName = nextIndex + ".json";
159
179
  return path.join(logFolder, fileName);
160
180
  }
161
- registerConsoleLogListener(page, context, logFile) {
181
+ registerConsoleLogListener(page, context) {
162
182
  if (!this.context.webLogger) {
163
183
  this.context.webLogger = [];
164
184
  }
165
185
  page.on("console", async (msg) => {
166
- this.context.webLogger.push({
186
+ const obj = {
167
187
  type: msg.type(),
168
188
  text: msg.text(),
169
189
  location: msg.location(),
170
190
  time: new Date().toISOString(),
171
- });
172
- 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
+ }
173
196
  });
174
197
  }
175
- registerRequestListener() {
176
- this.page.on("request", async (data) => {
198
+ registerRequestListener(page, context, logFile) {
199
+ if (!this.context.networkLogger) {
200
+ this.context.networkLogger = [];
201
+ }
202
+ page.on("request", async (data) => {
203
+ const startTime = new Date().getTime();
177
204
  try {
178
- const pageUrl = new URL(this.page.url());
205
+ const pageUrl = new URL(page.url());
179
206
  const requestUrl = new URL(data.url());
180
207
  if (pageUrl.hostname === requestUrl.hostname) {
181
208
  const method = data.method();
182
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
209
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
183
210
  const token = await data.headerValue("Authorization");
184
211
  if (token) {
185
- this.context.authtoken = token;
212
+ context.authtoken = token;
186
213
  }
187
214
  }
188
215
  }
216
+ const response = await data.response();
217
+ const endTime = new Date().getTime();
218
+ const obj = {
219
+ url: data.url(),
220
+ method: data.method(),
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" });
189
228
  }
190
229
  catch (error) {
191
230
  console.error("Error in request listener", error);
231
+ context.networkLogger.push({
232
+ error: "not able to listen",
233
+ message: error.message,
234
+ stack: error.stack,
235
+ time: new Date().toISOString(),
236
+ });
237
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
192
238
  }
193
239
  });
194
240
  }
@@ -203,20 +249,6 @@ class StableBrowser {
203
249
  timeout: 60000,
204
250
  });
205
251
  }
206
- _validateSelectors(selectors) {
207
- if (!selectors) {
208
- throw new Error("selectors is null");
209
- }
210
- if (!selectors.locators) {
211
- throw new Error("selectors.locators is null");
212
- }
213
- if (!Array.isArray(selectors.locators)) {
214
- throw new Error("selectors.locators expected to be array");
215
- }
216
- if (selectors.locators.length === 0) {
217
- throw new Error("selectors.locators expected to be non empty array");
218
- }
219
- }
220
252
  _fixUsingParams(text, _params) {
221
253
  if (!_params || typeof text !== "string") {
222
254
  return text;
@@ -284,7 +316,7 @@ class StableBrowser {
284
316
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
285
317
  }
286
318
  }
287
- if (locator === null || locator === void 0 ? void 0 : locator.engine) {
319
+ if (locator?.engine) {
288
320
  if (locator.engine === "css") {
289
321
  locatorReturn = scope.locator(locator.selector);
290
322
  }
@@ -305,7 +337,10 @@ class StableBrowser {
305
337
  return locatorReturn;
306
338
  }
307
339
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
308
- 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);
309
344
  if (result.elementCount === 0) {
310
345
  return;
311
346
  }
@@ -320,7 +355,7 @@ class StableBrowser {
320
355
  }
321
356
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
322
357
  //const stringifyText = JSON.stringify(text);
323
- return await scope.evaluate(([text, tag, regex, partial]) => {
358
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
324
359
  function isParent(parent, child) {
325
360
  let currentNode = child.parentNode;
326
361
  while (currentNode !== null) {
@@ -332,6 +367,15 @@ class StableBrowser {
332
367
  return false;
333
368
  }
334
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;
335
379
  function collectAllShadowDomElements(element, result = []) {
336
380
  // Check and add the element if it has a shadow root
337
381
  if (element.shadowRoot) {
@@ -348,7 +392,11 @@ class StableBrowser {
348
392
  }
349
393
  document.collectAllShadowDomElements = collectAllShadowDomElements;
350
394
  if (!tag) {
351
- tag = "*";
395
+ tag = "*:not(script, style, head)";
396
+ }
397
+ let regexpSearch = document.getRegex(text);
398
+ if (regexpSearch) {
399
+ regex = true;
352
400
  }
353
401
  let elements = Array.from(document.querySelectorAll(tag));
354
402
  let shadowHosts = [];
@@ -365,7 +413,9 @@ class StableBrowser {
365
413
  let randomToken = null;
366
414
  const foundElements = [];
367
415
  if (regex) {
368
- let regexpSearch = new RegExp(text, "im");
416
+ if (!regexpSearch) {
417
+ regexpSearch = new RegExp(text, "im");
418
+ }
369
419
  for (let i = 0; i < elements.length; i++) {
370
420
  const element = elements[i];
371
421
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -379,8 +429,8 @@ class StableBrowser {
379
429
  for (let i = 0; i < elements.length; i++) {
380
430
  const element = elements[i];
381
431
  if (partial) {
382
- if ((element.innerText && element.innerText.trim().includes(text)) ||
383
- (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()))) {
384
434
  foundElements.push(element);
385
435
  }
386
436
  }
@@ -425,18 +475,29 @@ class StableBrowser {
425
475
  }
426
476
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
427
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
+ }
428
484
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
429
485
  let locator = null;
430
486
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
431
487
  let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
432
488
  if (!locatorString) {
489
+ info.failCause.textNotFound = true;
490
+ info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
433
491
  return;
434
492
  }
435
493
  locator = this._getLocator({ css: locatorString }, scope, _params);
436
494
  }
437
495
  else if (locatorSearch.text) {
438
- 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);
439
498
  if (result.elementCount === 0) {
499
+ info.failCause.textNotFound = true;
500
+ info.failCause.lastError = "failed to locate element by text: " + text;
440
501
  return;
441
502
  }
442
503
  locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
@@ -453,6 +514,9 @@ class StableBrowser {
453
514
  // cssHref = true;
454
515
  // }
455
516
  let count = await locator.count();
517
+ if (count > 0 && !info.failCause.count) {
518
+ info.failCause.count = count;
519
+ }
456
520
  //info.log += "total elements found " + count + "\n";
457
521
  //let visibleCount = 0;
458
522
  let visibleLocator = null;
@@ -470,6 +534,8 @@ class StableBrowser {
470
534
  foundLocators.push(locator.nth(j));
471
535
  }
472
536
  else {
537
+ info.failCause.visible = visible;
538
+ info.failCause.enabled = enabled;
473
539
  if (!info.printMessages) {
474
540
  info.printMessages = {};
475
541
  }
@@ -481,6 +547,11 @@ class StableBrowser {
481
547
  }
482
548
  }
483
549
  async closeUnexpectedPopups(info, _params) {
550
+ if (!info) {
551
+ info = {};
552
+ info.failCause = {};
553
+ info.log = "";
554
+ }
484
555
  if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
485
556
  if (!info) {
486
557
  info = {};
@@ -521,7 +592,10 @@ class StableBrowser {
521
592
  }
522
593
  return { rerun: false };
523
594
  }
524
- async _locate(selectors, info, _params, timeout = 30000) {
595
+ async _locate(selectors, info, _params, timeout) {
596
+ if (!timeout) {
597
+ timeout = 30000;
598
+ }
525
599
  for (let i = 0; i < 3; i++) {
526
600
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
527
601
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -535,32 +609,46 @@ class StableBrowser {
535
609
  }
536
610
  throw new Error("unable to locate element " + JSON.stringify(selectors));
537
611
  }
538
- async _locate_internal(selectors, info, _params, timeout = 30000) {
539
- let highPriorityTimeout = 5000;
540
- let visibleOnlyTimeout = 6000;
541
- let startTime = performance.now();
542
- let locatorsCount = 0;
543
- //let arrayMode = Array.isArray(selectors);
612
+ async _findFrameScope(selectors, timeout = 30000, info) {
613
+ if (!info) {
614
+ info = {};
615
+ info.failCause = {};
616
+ info.log = "";
617
+ }
544
618
  let scope = this.page;
619
+ if (selectors.frame) {
620
+ return selectors.frame;
621
+ }
545
622
  if (selectors.iframe_src || selectors.frameLocators) {
546
- const findFrame = (frame, framescope) => {
623
+ const findFrame = async (frame, framescope) => {
547
624
  for (let i = 0; i < frame.selectors.length; i++) {
548
625
  let frameLocator = frame.selectors[i];
549
626
  if (frameLocator.css) {
550
- framescope = framescope.frameLocator(frameLocator.css);
551
- 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
+ }
552
641
  }
553
642
  }
554
643
  if (frame.children) {
555
- return findFrame(frame.children, framescope);
644
+ return await findFrame(frame.children, framescope);
556
645
  }
557
646
  return framescope;
558
647
  };
559
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
560
648
  while (true) {
561
649
  let frameFound = false;
562
650
  if (selectors.nestFrmLoc) {
563
- scope = findFrame(selectors.nestFrmLoc, scope);
651
+ scope = await findFrame(selectors.nestFrmLoc, scope);
564
652
  frameFound = true;
565
653
  break;
566
654
  }
@@ -580,6 +668,8 @@ class StableBrowser {
580
668
  if (!scope) {
581
669
  info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
582
670
  if (performance.now() - startTime > timeout) {
671
+ info.failCause.iframeNotFound = true;
672
+ info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
583
673
  throw new Error("unable to locate iframe " + selectors.iframe_src);
584
674
  }
585
675
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -589,6 +679,30 @@ class StableBrowser {
589
679
  }
590
680
  }
591
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);
592
706
  let selectorsLocators = null;
593
707
  selectorsLocators = selectors.locators;
594
708
  // group selectors by priority
@@ -690,6 +804,8 @@ class StableBrowser {
690
804
  }
691
805
  this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
692
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";
693
809
  throw new Error("failed to locate first element no elements found, " + info.log);
694
810
  }
695
811
  async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
@@ -721,89 +837,129 @@ class StableBrowser {
721
837
  });
722
838
  result.locatorIndex = i;
723
839
  }
840
+ if (foundLocators.length > 1) {
841
+ info.failCause.foundMultiple = true;
842
+ }
724
843
  }
725
844
  return result;
726
845
  }
727
- async click(selectors, _params, options = {}, world = null) {
728
- this._validateSelectors(selectors);
846
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
729
847
  const startTime = Date.now();
730
- if (options && options.context) {
731
- selectors.locators[0].text = options.context;
848
+ let timeout = 30000;
849
+ if (options && options.timeout) {
850
+ timeout = options.timeout;
732
851
  }
733
- const info = {};
734
- info.log = "***** click on " + selectors.element_name + " *****\n";
735
- info.operation = "click";
736
- info.selectors = selectors;
737
- let error = null;
738
- let screenshotId = null;
739
- 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
+ };
740
919
  try {
741
- let element = await this._locate(selectors, info, _params);
742
- await this.scrollIfNeeded(element, info);
743
- ({ 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
+ }
744
924
  try {
745
- await this._highlightElements(element);
746
- await element.click();
925
+ await state.element.click();
747
926
  await new Promise((resolve) => setTimeout(resolve, 1000));
748
927
  }
749
928
  catch (e) {
750
929
  // await this.closeUnexpectedPopups();
751
- info.log += "click failed, will try again" + "\n";
752
- element = await this._locate(selectors, info, _params);
753
- await element.dispatchEvent("click");
930
+ state.element = await this._locate(selectors, state.info, _params);
931
+ await state.element.dispatchEvent("click");
754
932
  await new Promise((resolve) => setTimeout(resolve, 1000));
755
933
  }
756
934
  await this.waitForPageLoad();
757
- return info;
935
+ return state.info;
758
936
  }
759
937
  catch (e) {
760
- this.logger.error("click failed " + info.log);
761
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
762
- info.screenshotPath = screenshotPath;
763
- Object.assign(e, { info: info });
764
- error = e;
765
- throw e;
938
+ await _commandError(state, e, this);
766
939
  }
767
940
  finally {
768
- const endTime = Date.now();
769
- this._reportToWorld(world, {
770
- element_name: selectors.element_name,
771
- type: Types.CLICK,
772
- text: `Click element`,
773
- screenshotId,
774
- result: error
775
- ? {
776
- status: "FAILED",
777
- startTime,
778
- endTime,
779
- message: error === null || error === void 0 ? void 0 : error.message,
780
- }
781
- : {
782
- status: "PASSED",
783
- startTime,
784
- endTime,
785
- },
786
- info: info,
787
- });
941
+ _commandFinally(state, this);
788
942
  }
789
943
  }
790
944
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
791
- this._validateSelectors(selectors);
792
- const startTime = Date.now();
793
- const info = {};
794
- info.log = "";
795
- info.operation = "setCheck";
796
- info.checked = checked;
797
- info.selectors = selectors;
798
- let error = null;
799
- let screenshotId = null;
800
- 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
+ };
801
955
  try {
802
- let element = await this._locate(selectors, info, _params);
803
- ({ 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));
804
960
  try {
805
- await this._highlightElements(element);
806
- await element.setChecked(checked);
961
+ // await this._highlightElements(element);
962
+ await state.element.setChecked(checked);
807
963
  await new Promise((resolve) => setTimeout(resolve, 1000));
808
964
  }
809
965
  catch (e) {
@@ -812,179 +968,108 @@ class StableBrowser {
812
968
  }
813
969
  else {
814
970
  //await this.closeUnexpectedPopups();
815
- info.log += "setCheck failed, will try again" + "\n";
816
- element = await this._locate(selectors, info, _params);
817
- 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 });
818
974
  await new Promise((resolve) => setTimeout(resolve, 1000));
819
975
  }
820
976
  }
821
977
  await this.waitForPageLoad();
822
- return info;
978
+ return state.info;
823
979
  }
824
980
  catch (e) {
825
- this.logger.error("setCheck failed " + info.log);
826
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
827
- info.screenshotPath = screenshotPath;
828
- Object.assign(e, { info: info });
829
- error = e;
830
- throw e;
981
+ await _commandError(state, e, this);
831
982
  }
832
983
  finally {
833
- const endTime = Date.now();
834
- this._reportToWorld(world, {
835
- element_name: selectors.element_name,
836
- type: checked ? Types.CHECK : Types.UNCHECK,
837
- text: checked ? `Check element` : `Uncheck element`,
838
- screenshotId,
839
- result: error
840
- ? {
841
- status: "FAILED",
842
- startTime,
843
- endTime,
844
- message: error === null || error === void 0 ? void 0 : error.message,
845
- }
846
- : {
847
- status: "PASSED",
848
- startTime,
849
- endTime,
850
- },
851
- info: info,
852
- });
984
+ _commandFinally(state, this);
853
985
  }
854
986
  }
855
987
  async hover(selectors, _params, options = {}, world = null) {
856
- this._validateSelectors(selectors);
857
- const startTime = Date.now();
858
- const info = {};
859
- info.log = "";
860
- info.operation = "hover";
861
- info.selectors = selectors;
862
- let error = null;
863
- let screenshotId = null;
864
- 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
+ };
865
998
  try {
866
- let element = await this._locate(selectors, info, _params);
867
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
999
+ await _preCommand(state, this);
868
1000
  try {
869
- await this._highlightElements(element);
870
- await element.hover();
1001
+ await state.element.hover();
871
1002
  await new Promise((resolve) => setTimeout(resolve, 1000));
872
1003
  }
873
1004
  catch (e) {
874
1005
  //await this.closeUnexpectedPopups();
875
- info.log += "hover failed, will try again" + "\n";
876
- element = await this._locate(selectors, info, _params);
877
- 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 });
878
1009
  await new Promise((resolve) => setTimeout(resolve, 1000));
879
1010
  }
880
1011
  await this.waitForPageLoad();
881
- return info;
1012
+ return state.info;
882
1013
  }
883
1014
  catch (e) {
884
- this.logger.error("hover failed " + info.log);
885
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
886
- info.screenshotPath = screenshotPath;
887
- Object.assign(e, { info: info });
888
- error = e;
889
- throw e;
1015
+ await _commandError(state, e, this);
890
1016
  }
891
1017
  finally {
892
- const endTime = Date.now();
893
- this._reportToWorld(world, {
894
- element_name: selectors.element_name,
895
- type: Types.HOVER,
896
- text: `Hover element`,
897
- screenshotId,
898
- result: error
899
- ? {
900
- status: "FAILED",
901
- startTime,
902
- endTime,
903
- message: error === null || error === void 0 ? void 0 : error.message,
904
- }
905
- : {
906
- status: "PASSED",
907
- startTime,
908
- endTime,
909
- },
910
- info: info,
911
- });
1018
+ _commandFinally(state, this);
912
1019
  }
913
1020
  }
914
1021
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
915
- this._validateSelectors(selectors);
916
1022
  if (!values) {
917
1023
  throw new Error("values is null");
918
1024
  }
919
- const startTime = Date.now();
920
- let error = null;
921
- let screenshotId = null;
922
- let screenshotPath = null;
923
- const info = {};
924
- info.log = "";
925
- info.operation = "selectOptions";
926
- 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
+ };
927
1036
  try {
928
- let element = await this._locate(selectors, info, _params);
929
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1037
+ await _preCommand(state, this);
930
1038
  try {
931
- await this._highlightElements(element);
932
- await element.selectOption(values);
1039
+ await state.element.selectOption(values);
933
1040
  }
934
1041
  catch (e) {
935
1042
  //await this.closeUnexpectedPopups();
936
- info.log += "selectOption failed, will try force" + "\n";
937
- 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 });
938
1045
  }
939
1046
  await this.waitForPageLoad();
940
- return info;
1047
+ return state.info;
941
1048
  }
942
1049
  catch (e) {
943
- this.logger.error("selectOption failed " + info.log);
944
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
945
- info.screenshotPath = screenshotPath;
946
- Object.assign(e, { info: info });
947
- this.logger.info("click failed, will try next selector");
948
- error = e;
949
- throw e;
1050
+ await _commandError(state, e, this);
950
1051
  }
951
1052
  finally {
952
- const endTime = Date.now();
953
- this._reportToWorld(world, {
954
- element_name: selectors.element_name,
955
- type: Types.SELECT,
956
- text: `Select option: ${values}`,
957
- value: values.toString(),
958
- screenshotId,
959
- result: error
960
- ? {
961
- status: "FAILED",
962
- startTime,
963
- endTime,
964
- message: error === null || error === void 0 ? void 0 : error.message,
965
- }
966
- : {
967
- status: "PASSED",
968
- startTime,
969
- endTime,
970
- },
971
- info: info,
972
- });
1053
+ _commandFinally(state, this);
973
1054
  }
974
1055
  }
975
1056
  async type(_value, _params = null, options = {}, world = null) {
976
- const startTime = Date.now();
977
- let error = null;
978
- let screenshotId = null;
979
- let screenshotPath = null;
980
- const info = {};
981
- info.log = "";
982
- info.operation = "type";
983
- _value = this._fixUsingParams(_value, _params);
984
- 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
+ };
985
1070
  try {
986
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
987
- const valueSegment = _value.split("&&");
1071
+ await _preCommand(state, this);
1072
+ const valueSegment = state.value.split("&&");
988
1073
  for (let i = 0; i < valueSegment.length; i++) {
989
1074
  if (i > 0) {
990
1075
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1004,134 +1089,76 @@ class StableBrowser {
1004
1089
  await this.page.keyboard.type(value);
1005
1090
  }
1006
1091
  }
1007
- return info;
1092
+ return state.info;
1008
1093
  }
1009
1094
  catch (e) {
1010
- //await this.closeUnexpectedPopups();
1011
- this.logger.error("type failed " + info.log);
1012
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1013
- info.screenshotPath = screenshotPath;
1014
- Object.assign(e, { info: info });
1015
- error = e;
1016
- throw e;
1095
+ await _commandError(state, e, this);
1017
1096
  }
1018
1097
  finally {
1019
- const endTime = Date.now();
1020
- this._reportToWorld(world, {
1021
- type: Types.TYPE_PRESS,
1022
- screenshotId,
1023
- value: _value,
1024
- text: `type value: ${_value}`,
1025
- result: error
1026
- ? {
1027
- status: "FAILED",
1028
- startTime,
1029
- endTime,
1030
- message: error === null || error === void 0 ? void 0 : error.message,
1031
- }
1032
- : {
1033
- status: "PASSED",
1034
- startTime,
1035
- endTime,
1036
- },
1037
- info: info,
1038
- });
1098
+ _commandFinally(state, this);
1039
1099
  }
1040
1100
  }
1041
1101
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1042
- // set input value for non fillable inputs like date, time, range, color, etc.
1043
- this._validateSelectors(selectors);
1044
- const startTime = Date.now();
1045
- const info = {};
1046
- info.log = "***** set input value " + selectors.element_name + " *****\n";
1047
- info.operation = "setInputValue";
1048
- info.selectors = selectors;
1049
- value = this._fixUsingParams(value, _params);
1050
- info.value = value;
1051
- let error = null;
1052
- let screenshotId = null;
1053
- 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
+ };
1054
1113
  try {
1055
- value = await this._replaceWithLocalData(value, this);
1056
- let element = await this._locate(selectors, info, _params);
1057
- await this.scrollIfNeeded(element, info);
1058
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1059
- await this._highlightElements(element);
1114
+ await _preCommand(state, this);
1115
+ let value = await this._replaceWithLocalData(state.value, this);
1060
1116
  try {
1061
- await element.evaluateHandle((el, value) => {
1117
+ await state.element.evaluateHandle((el, value) => {
1062
1118
  el.value = value;
1063
1119
  }, value);
1064
1120
  }
1065
1121
  catch (error) {
1066
1122
  this.logger.error("setInputValue failed, will try again");
1067
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1068
- info.screenshotPath = screenshotPath;
1069
- Object.assign(error, { info: info });
1070
- await element.evaluateHandle((el, value) => {
1123
+ await _screenshot(state, this);
1124
+ Object.assign(error, { info: state.info });
1125
+ await state.element.evaluateHandle((el, value) => {
1071
1126
  el.value = value;
1072
1127
  });
1073
1128
  }
1074
1129
  }
1075
1130
  catch (e) {
1076
- this.logger.error("setInputValue failed " + info.log);
1077
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1078
- info.screenshotPath = screenshotPath;
1079
- Object.assign(e, { info: info });
1080
- error = e;
1081
- throw e;
1131
+ await _commandError(state, e, this);
1082
1132
  }
1083
1133
  finally {
1084
- const endTime = Date.now();
1085
- this._reportToWorld(world, {
1086
- element_name: selectors.element_name,
1087
- type: Types.SET_INPUT,
1088
- text: `Set input value`,
1089
- value: value,
1090
- screenshotId,
1091
- result: error
1092
- ? {
1093
- status: "FAILED",
1094
- startTime,
1095
- endTime,
1096
- message: error === null || error === void 0 ? void 0 : error.message,
1097
- }
1098
- : {
1099
- status: "PASSED",
1100
- startTime,
1101
- endTime,
1102
- },
1103
- info: info,
1104
- });
1134
+ _commandFinally(state, this);
1105
1135
  }
1106
1136
  }
1107
1137
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1108
- this._validateSelectors(selectors);
1109
- const startTime = Date.now();
1110
- let error = null;
1111
- let screenshotId = null;
1112
- let screenshotPath = null;
1113
- const info = {};
1114
- info.log = "";
1115
- info.operation = Types.SET_DATE_TIME;
1116
- info.selectors = selectors;
1117
- 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
+ };
1118
1150
  try {
1119
- value = await this._replaceWithLocalData(value, this);
1120
- let element = await this._locate(selectors, info, _params);
1121
- //insert red border around the element
1122
- await this.scrollIfNeeded(element, info);
1123
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1124
- await this._highlightElements(element);
1151
+ await _preCommand(state, this);
1125
1152
  try {
1126
- await element.click();
1153
+ await state.element.click();
1127
1154
  await new Promise((resolve) => setTimeout(resolve, 500));
1128
1155
  if (format) {
1129
- value = dayjs(value).format(format);
1130
- await element.fill(value);
1156
+ state.value = dayjs(state.value).format(format);
1157
+ await state.element.fill(state.value);
1131
1158
  }
1132
1159
  else {
1133
- const dateTimeValue = await getDateTimeValue({ value, element });
1134
- await element.evaluateHandle((el, dateTimeValue) => {
1160
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1161
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1135
1162
  el.value = ""; // clear input
1136
1163
  el.value = dateTimeValue;
1137
1164
  }, dateTimeValue);
@@ -1144,20 +1171,19 @@ class StableBrowser {
1144
1171
  }
1145
1172
  catch (err) {
1146
1173
  //await this.closeUnexpectedPopups();
1147
- this.logger.error("setting date time input failed " + JSON.stringify(info));
1174
+ this.logger.error("setting date time input failed " + JSON.stringify(state.info));
1148
1175
  this.logger.info("Trying again");
1149
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1150
- info.screenshotPath = screenshotPath;
1151
- Object.assign(err, { info: info });
1176
+ await _screenshot(state, this);
1177
+ Object.assign(err, { info: state.info });
1152
1178
  await element.click();
1153
1179
  await new Promise((resolve) => setTimeout(resolve, 500));
1154
1180
  if (format) {
1155
- value = dayjs(value).format(format);
1156
- await element.fill(value);
1181
+ state.value = dayjs(state.value).format(format);
1182
+ await state.element.fill(state.value);
1157
1183
  }
1158
1184
  else {
1159
- const dateTimeValue = await getDateTimeValue({ value, element });
1160
- await element.evaluateHandle((el, dateTimeValue) => {
1185
+ const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
1186
+ await state.element.evaluateHandle((el, dateTimeValue) => {
1161
1187
  el.value = ""; // clear input
1162
1188
  el.value = dateTimeValue;
1163
1189
  }, dateTimeValue);
@@ -1170,61 +1196,39 @@ class StableBrowser {
1170
1196
  }
1171
1197
  }
1172
1198
  catch (e) {
1173
- error = e;
1174
- throw e;
1199
+ await _commandError(state, e, this);
1175
1200
  }
1176
1201
  finally {
1177
- const endTime = Date.now();
1178
- this._reportToWorld(world, {
1179
- element_name: selectors.element_name,
1180
- type: Types.SET_DATE_TIME,
1181
- screenshotId,
1182
- value: value,
1183
- text: `setDateTime input with value: ${value}`,
1184
- result: error
1185
- ? {
1186
- status: "FAILED",
1187
- startTime,
1188
- endTime,
1189
- message: error === null || error === void 0 ? void 0 : error.message,
1190
- }
1191
- : {
1192
- status: "PASSED",
1193
- startTime,
1194
- endTime,
1195
- },
1196
- info: info,
1197
- });
1202
+ _commandFinally(state, this);
1198
1203
  }
1199
1204
  }
1200
1205
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1201
1206
  _value = unEscapeString(_value);
1202
- this._validateSelectors(selectors);
1203
- const startTime = Date.now();
1204
- let error = null;
1205
- let screenshotId = null;
1206
- let screenshotPath = null;
1207
- const info = {};
1208
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1209
- info.operation = "clickType";
1210
- info.selectors = selectors;
1211
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
+ };
1212
1220
  if (newValue !== _value) {
1213
1221
  //this.logger.info(_value + "=" + newValue);
1214
1222
  _value = newValue;
1215
1223
  }
1216
- info.value = _value;
1217
- try {
1218
- let element = await this._locate(selectors, info, _params);
1219
- //insert red border around the element
1220
- await this.scrollIfNeeded(element, info);
1221
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1222
- await this._highlightElements(element);
1224
+ try {
1225
+ await _preCommand(state, this);
1226
+ state.info.value = _value;
1223
1227
  if (options === null || options === undefined || !options.press) {
1224
1228
  try {
1225
- let currentValue = await element.inputValue();
1229
+ let currentValue = await state.element.inputValue();
1226
1230
  if (currentValue) {
1227
- await element.fill("");
1231
+ await state.element.fill("");
1228
1232
  }
1229
1233
  }
1230
1234
  catch (e) {
@@ -1233,22 +1237,22 @@ class StableBrowser {
1233
1237
  }
1234
1238
  if (options === null || options === undefined || options.press) {
1235
1239
  try {
1236
- await element.click({ timeout: 5000 });
1240
+ await state.element.click({ timeout: 5000 });
1237
1241
  }
1238
1242
  catch (e) {
1239
- await element.dispatchEvent("click");
1243
+ await state.element.dispatchEvent("click");
1240
1244
  }
1241
1245
  }
1242
1246
  else {
1243
1247
  try {
1244
- await element.focus();
1248
+ await state.element.focus();
1245
1249
  }
1246
1250
  catch (e) {
1247
- await element.dispatchEvent("focus");
1251
+ await state.element.dispatchEvent("focus");
1248
1252
  }
1249
1253
  }
1250
1254
  await new Promise((resolve) => setTimeout(resolve, 500));
1251
- const valueSegment = _value.split("&&");
1255
+ const valueSegment = state.value.split("&&");
1252
1256
  for (let i = 0; i < valueSegment.length; i++) {
1253
1257
  if (i > 0) {
1254
1258
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1268,13 +1272,14 @@ class StableBrowser {
1268
1272
  await new Promise((resolve) => setTimeout(resolve, 500));
1269
1273
  }
1270
1274
  }
1275
+ await _screenshot(state, this);
1271
1276
  if (enter === true) {
1272
1277
  await new Promise((resolve) => setTimeout(resolve, 2000));
1273
1278
  await this.page.keyboard.press("Enter");
1274
1279
  await this.waitForPageLoad();
1275
1280
  }
1276
1281
  else if (enter === false) {
1277
- await element.dispatchEvent("change");
1282
+ await state.element.dispatchEvent("change");
1278
1283
  //await this.page.keyboard.press("Tab");
1279
1284
  }
1280
1285
  else {
@@ -1283,104 +1288,50 @@ class StableBrowser {
1283
1288
  await this.waitForPageLoad();
1284
1289
  }
1285
1290
  }
1286
- return info;
1291
+ return state.info;
1287
1292
  }
1288
1293
  catch (e) {
1289
- //await this.closeUnexpectedPopups();
1290
- this.logger.error("fill failed " + JSON.stringify(info));
1291
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1292
- info.screenshotPath = screenshotPath;
1293
- Object.assign(e, { info: info });
1294
- error = e;
1295
- throw e;
1294
+ await _commandError(state, e, this);
1296
1295
  }
1297
1296
  finally {
1298
- const endTime = Date.now();
1299
- this._reportToWorld(world, {
1300
- element_name: selectors.element_name,
1301
- type: Types.FILL,
1302
- screenshotId,
1303
- value: _value,
1304
- text: `clickType input with value: ${_value}`,
1305
- result: error
1306
- ? {
1307
- status: "FAILED",
1308
- startTime,
1309
- endTime,
1310
- message: error === null || error === void 0 ? void 0 : error.message,
1311
- }
1312
- : {
1313
- status: "PASSED",
1314
- startTime,
1315
- endTime,
1316
- },
1317
- info: info,
1318
- });
1297
+ _commandFinally(state, this);
1319
1298
  }
1320
1299
  }
1321
1300
  async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1322
- this._validateSelectors(selectors);
1323
- value = unEscapeString(value);
1324
- const startTime = Date.now();
1325
- let error = null;
1326
- let screenshotId = null;
1327
- let screenshotPath = null;
1328
- const info = {};
1329
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1330
- info.operation = "fill";
1331
- info.selectors = selectors;
1332
- 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
+ };
1333
1312
  try {
1334
- let element = await this._locate(selectors, info, _params);
1335
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1336
- await this._highlightElements(element);
1337
- await element.fill(value);
1338
- await element.dispatchEvent("change");
1313
+ await _preCommand(state, this);
1314
+ await state.element.fill(value);
1315
+ await state.element.dispatchEvent("change");
1339
1316
  if (enter) {
1340
1317
  await new Promise((resolve) => setTimeout(resolve, 2000));
1341
1318
  await this.page.keyboard.press("Enter");
1342
1319
  }
1343
1320
  await this.waitForPageLoad();
1344
- return info;
1321
+ return state.info;
1345
1322
  }
1346
1323
  catch (e) {
1347
- //await this.closeUnexpectedPopups();
1348
- this.logger.error("fill failed " + info.log);
1349
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1350
- info.screenshotPath = screenshotPath;
1351
- Object.assign(e, { info: info });
1352
- error = e;
1353
- throw e;
1324
+ await _commandError(state, e, this);
1354
1325
  }
1355
1326
  finally {
1356
- const endTime = Date.now();
1357
- this._reportToWorld(world, {
1358
- element_name: selectors.element_name,
1359
- type: Types.FILL,
1360
- screenshotId,
1361
- value,
1362
- text: `Fill input with value: ${value}`,
1363
- result: error
1364
- ? {
1365
- status: "FAILED",
1366
- startTime,
1367
- endTime,
1368
- message: error === null || error === void 0 ? void 0 : error.message,
1369
- }
1370
- : {
1371
- status: "PASSED",
1372
- startTime,
1373
- endTime,
1374
- },
1375
- info: info,
1376
- });
1327
+ _commandFinally(state, this);
1377
1328
  }
1378
1329
  }
1379
1330
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1380
1331
  return await this._getText(selectors, 0, _params, options, info, world);
1381
1332
  }
1382
1333
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1383
- this._validateSelectors(selectors);
1334
+ _validateSelectors(selectors);
1384
1335
  let screenshotId = null;
1385
1336
  let screenshotPath = null;
1386
1337
  if (!info.log) {
@@ -1424,166 +1375,124 @@ class StableBrowser {
1424
1375
  }
1425
1376
  }
1426
1377
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1427
- var _a;
1428
- this._validateSelectors(selectors);
1429
1378
  if (!pattern) {
1430
1379
  throw new Error("pattern is null");
1431
1380
  }
1432
1381
  if (!text) {
1433
1382
  throw new Error("text is null");
1434
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
+ };
1435
1400
  const newValue = await this._replaceWithLocalData(text, world);
1436
1401
  if (newValue !== text) {
1437
1402
  this.logger.info(text + "=" + newValue);
1438
1403
  text = newValue;
1439
1404
  }
1440
- const startTime = Date.now();
1441
- let error = null;
1442
- let screenshotId = null;
1443
- let screenshotPath = null;
1444
- const info = {};
1445
- info.log =
1446
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1447
- info.operation = "containsPattern";
1448
- info.selectors = selectors;
1449
- info.value = text;
1450
- info.pattern = pattern;
1451
1405
  let foundObj = null;
1452
1406
  try {
1453
- 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);
1454
1410
  if (foundObj && foundObj.element) {
1455
- await this.scrollIfNeeded(foundObj.element, info);
1411
+ await this.scrollIfNeeded(foundObj.element, state.info);
1456
1412
  }
1457
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1413
+ await _screenshot(state, this);
1458
1414
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1459
1415
  pattern = pattern.replace("{text}", escapedText);
1460
1416
  let regex = new RegExp(pattern, "im");
1461
- 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))) {
1462
- 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;
1463
1419
  throw new Error("element doesn't contain text " + text);
1464
1420
  }
1465
- return info;
1421
+ return state.info;
1466
1422
  }
1467
1423
  catch (e) {
1468
- //await this.closeUnexpectedPopups();
1469
- this.logger.error("verify element contains text failed " + info.log);
1470
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1471
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1472
- info.screenshotPath = screenshotPath;
1473
- Object.assign(e, { info: info });
1474
- error = e;
1475
- throw e;
1424
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1425
+ await _commandError(state, e, this);
1476
1426
  }
1477
1427
  finally {
1478
- const endTime = Date.now();
1479
- this._reportToWorld(world, {
1480
- element_name: selectors.element_name,
1481
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1482
- value: pattern,
1483
- text: `Verify element contains pattern: ${pattern}`,
1484
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1485
- result: error
1486
- ? {
1487
- status: "FAILED",
1488
- startTime,
1489
- endTime,
1490
- message: error === null || error === void 0 ? void 0 : error.message,
1491
- }
1492
- : {
1493
- status: "PASSED",
1494
- startTime,
1495
- endTime,
1496
- },
1497
- info: info,
1498
- });
1428
+ _commandFinally(state, this);
1499
1429
  }
1500
1430
  }
1501
1431
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1502
- var _a, _b, _c;
1503
- this._validateSelectors(selectors);
1504
- 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
+ };
1505
1447
  if (!text) {
1506
1448
  throw new Error("text is null");
1507
1449
  }
1508
- const startTime = Date.now();
1509
- let error = null;
1510
- let screenshotId = null;
1511
- let screenshotPath = null;
1512
- const info = {};
1513
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1514
- info.operation = "containsText";
1515
- info.selectors = selectors;
1450
+ text = unEscapeString(text);
1516
1451
  const newValue = await this._replaceWithLocalData(text, world);
1517
1452
  if (newValue !== text) {
1518
1453
  this.logger.info(text + "=" + newValue);
1519
1454
  text = newValue;
1520
1455
  }
1521
- info.value = text;
1522
1456
  let foundObj = null;
1523
1457
  try {
1524
- 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);
1525
1460
  if (foundObj && foundObj.element) {
1526
- await this.scrollIfNeeded(foundObj.element, info);
1461
+ await this.scrollIfNeeded(foundObj.element, state.info);
1527
1462
  }
1528
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1463
+ await _screenshot(state, this);
1529
1464
  const dateAlternatives = findDateAlternatives(text);
1530
1465
  const numberAlternatives = findNumberAlternatives(text);
1531
1466
  if (dateAlternatives.date) {
1532
1467
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1533
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1534
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1535
- return info;
1468
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1469
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1470
+ return state.info;
1536
1471
  }
1537
1472
  }
1538
1473
  throw new Error("element doesn't contain text " + text);
1539
1474
  }
1540
1475
  else if (numberAlternatives.number) {
1541
1476
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1542
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1543
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1544
- return info;
1477
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1478
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1479
+ return state.info;
1545
1480
  }
1546
1481
  }
1547
1482
  throw new Error("element doesn't contain text " + text);
1548
1483
  }
1549
- 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))) {
1550
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1551
- 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;
1552
1487
  throw new Error("element doesn't contain text " + text);
1553
1488
  }
1554
- return info;
1489
+ return state.info;
1555
1490
  }
1556
1491
  catch (e) {
1557
- //await this.closeUnexpectedPopups();
1558
- this.logger.error("verify element contains text failed " + info.log);
1559
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1560
- info.screenshotPath = screenshotPath;
1561
- Object.assign(e, { info: info });
1562
- error = e;
1563
- throw e;
1492
+ await _commandError(state, e, this);
1564
1493
  }
1565
1494
  finally {
1566
- const endTime = Date.now();
1567
- this._reportToWorld(world, {
1568
- element_name: selectors.element_name,
1569
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1570
- text: `Verify element contains text: ${text}`,
1571
- value: text,
1572
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1573
- result: error
1574
- ? {
1575
- status: "FAILED",
1576
- startTime,
1577
- endTime,
1578
- message: error === null || error === void 0 ? void 0 : error.message,
1579
- }
1580
- : {
1581
- status: "PASSED",
1582
- startTime,
1583
- endTime,
1584
- },
1585
- info: info,
1586
- });
1495
+ _commandFinally(state, this);
1587
1496
  }
1588
1497
  }
1589
1498
  _getDataFile(world = null) {
@@ -1812,7 +1721,6 @@ class StableBrowser {
1812
1721
  }
1813
1722
  async takeScreenshot(screenshotPath) {
1814
1723
  const playContext = this.context.playContext;
1815
- const client = await playContext.newCDPSession(this.page);
1816
1724
  // Using CDP to capture the screenshot
1817
1725
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1818
1726
  document.body.scrollWidth,
@@ -1822,164 +1730,109 @@ class StableBrowser {
1822
1730
  document.body.clientWidth,
1823
1731
  document.documentElement.clientWidth,
1824
1732
  ])));
1825
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1826
- document.body.scrollHeight,
1827
- document.documentElement.scrollHeight,
1828
- document.body.offsetHeight,
1829
- document.documentElement.offsetHeight,
1830
- document.body.clientHeight,
1831
- document.documentElement.clientHeight,
1832
- ])));
1833
- const { data } = await client.send("Page.captureScreenshot", {
1834
- format: "png",
1835
- // clip: {
1836
- // x: 0,
1837
- // y: 0,
1838
- // width: viewportWidth,
1839
- // height: viewportHeight,
1840
- // scale: 1,
1841
- // },
1842
- });
1843
- if (!screenshotPath) {
1844
- return data;
1845
- }
1846
- let screenshotBuffer = Buffer.from(data, "base64");
1847
- const sharpBuffer = sharp(screenshotBuffer);
1848
- const metadata = await sharpBuffer.metadata();
1849
- //check if you are on retina display and reduce the quality of the image
1850
- if (metadata.width > viewportWidth || metadata.height > viewportHeight) {
1851
- screenshotBuffer = await sharpBuffer
1852
- .resize(viewportWidth, viewportHeight, {
1853
- fit: sharp.fit.inside,
1854
- withoutEnlargement: true,
1855
- })
1856
- .toBuffer();
1857
- }
1858
- fs.writeFileSync(screenshotPath, screenshotBuffer);
1859
- 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
+ }
1860
1767
  }
1861
1768
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1862
- this._validateSelectors(selectors);
1863
- const startTime = Date.now();
1864
- let error = null;
1865
- let screenshotId = null;
1866
- 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
+ };
1867
1779
  await new Promise((resolve) => setTimeout(resolve, 2000));
1868
- const info = {};
1869
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
1870
- info.operation = "verify";
1871
- info.selectors = selectors;
1872
1780
  try {
1873
- const element = await this._locate(selectors, info, _params);
1874
- if (element) {
1875
- await this.scrollIfNeeded(element, info);
1876
- }
1877
- await this._highlightElements(element);
1878
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1879
- await expect(element).toHaveCount(1, { timeout: 10000 });
1880
- return info;
1781
+ await _preCommand(state, this);
1782
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1783
+ return state.info;
1881
1784
  }
1882
1785
  catch (e) {
1883
- //await this.closeUnexpectedPopups();
1884
- this.logger.error("verify failed " + info.log);
1885
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1886
- info.screenshotPath = screenshotPath;
1887
- Object.assign(e, { info: info });
1888
- error = e;
1889
- throw e;
1786
+ await _commandError(state, e, this);
1890
1787
  }
1891
1788
  finally {
1892
- const endTime = Date.now();
1893
- this._reportToWorld(world, {
1894
- element_name: selectors.element_name,
1895
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1896
- text: "Verify element exists in page",
1897
- screenshotId,
1898
- result: error
1899
- ? {
1900
- status: "FAILED",
1901
- startTime,
1902
- endTime,
1903
- message: error === null || error === void 0 ? void 0 : error.message,
1904
- }
1905
- : {
1906
- status: "PASSED",
1907
- startTime,
1908
- endTime,
1909
- },
1910
- info: info,
1911
- });
1789
+ _commandFinally(state, this);
1912
1790
  }
1913
1791
  }
1914
1792
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1915
- this._validateSelectors(selectors);
1916
- const startTime = Date.now();
1917
- let error = null;
1918
- let screenshotId = null;
1919
- 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
+ };
1920
1805
  await new Promise((resolve) => setTimeout(resolve, 2000));
1921
- const info = {};
1922
- info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
1923
- info.operation = "extract";
1924
- info.selectors = selectors;
1925
1806
  try {
1926
- const element = await this._locate(selectors, info, _params);
1927
- await this._highlightElements(element);
1928
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1807
+ await _preCommand(state, this);
1929
1808
  switch (attribute) {
1930
1809
  case "inner_text":
1931
- info.value = await element.innerText();
1810
+ state.value = await state.element.innerText();
1932
1811
  break;
1933
1812
  case "href":
1934
- info.value = await element.getAttribute("href");
1813
+ state.value = await state.element.getAttribute("href");
1935
1814
  break;
1936
1815
  case "value":
1937
- info.value = await element.inputValue();
1816
+ state.value = await state.element.inputValue();
1938
1817
  break;
1939
1818
  default:
1940
- info.value = await element.getAttribute(attribute);
1819
+ state.value = await state.element.getAttribute(attribute);
1941
1820
  break;
1942
1821
  }
1943
- this[variable] = info.value;
1822
+ state.info.value = state.value;
1823
+ this[variable] = state.value;
1944
1824
  if (world) {
1945
- world[variable] = info.value;
1825
+ world[variable] = state.value;
1946
1826
  }
1947
- this.setTestData({ [variable]: info.value }, world);
1948
- this.logger.info("set test data: " + variable + "=" + info.value);
1949
- return info;
1827
+ this.setTestData({ [variable]: state.value }, world);
1828
+ this.logger.info("set test data: " + variable + "=" + state.value);
1829
+ return state.info;
1950
1830
  }
1951
1831
  catch (e) {
1952
- //await this.closeUnexpectedPopups();
1953
- this.logger.error("extract failed " + info.log);
1954
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1955
- info.screenshotPath = screenshotPath;
1956
- Object.assign(e, { info: info });
1957
- error = e;
1958
- throw e;
1832
+ await _commandError(state, e, this);
1959
1833
  }
1960
1834
  finally {
1961
- const endTime = Date.now();
1962
- this._reportToWorld(world, {
1963
- element_name: selectors.element_name,
1964
- type: Types.EXTRACT_ATTRIBUTE,
1965
- variable: variable,
1966
- value: info.value,
1967
- text: "Extract attribute from element",
1968
- screenshotId,
1969
- result: error
1970
- ? {
1971
- status: "FAILED",
1972
- startTime,
1973
- endTime,
1974
- message: error === null || error === void 0 ? void 0 : error.message,
1975
- }
1976
- : {
1977
- status: "PASSED",
1978
- startTime,
1979
- endTime,
1980
- },
1981
- info: info,
1982
- });
1835
+ _commandFinally(state, this);
1983
1836
  }
1984
1837
  }
1985
1838
  async extractEmailData(emailAddress, options, world) {
@@ -2056,7 +1909,8 @@ class StableBrowser {
2056
1909
  catch (e) {
2057
1910
  errorCount++;
2058
1911
  if (errorCount > 3) {
2059
- throw e;
1912
+ // throw e;
1913
+ await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
2060
1914
  }
2061
1915
  // ignore
2062
1916
  }
@@ -2167,7 +2021,8 @@ class StableBrowser {
2167
2021
  info.screenshotPath = screenshotPath;
2168
2022
  Object.assign(e, { info: info });
2169
2023
  error = e;
2170
- throw e;
2024
+ // throw e;
2025
+ await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
2171
2026
  }
2172
2027
  finally {
2173
2028
  const endTime = Date.now();
@@ -2180,7 +2035,7 @@ class StableBrowser {
2180
2035
  status: "FAILED",
2181
2036
  startTime,
2182
2037
  endTime,
2183
- message: error === null || error === void 0 ? void 0 : error.message,
2038
+ message: error?.message,
2184
2039
  }
2185
2040
  : {
2186
2041
  status: "PASSED",
@@ -2193,49 +2048,55 @@ class StableBrowser {
2193
2048
  }
2194
2049
  async verifyTextExistInPage(text, options = {}, world = null) {
2195
2050
  text = unEscapeString(text);
2196
- 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
+ };
2197
2063
  const timeout = this._getLoadTimeout(options);
2198
- let error = null;
2199
- let screenshotId = null;
2200
- let screenshotPath = null;
2201
2064
  await new Promise((resolve) => setTimeout(resolve, 2000));
2202
- const info = {};
2203
- info.log = "***** verify text " + text + " exists in page *****\n";
2204
- info.operation = "verifyTextExistInPage";
2205
2065
  const newValue = await this._replaceWithLocalData(text, world);
2206
2066
  if (newValue !== text) {
2207
2067
  this.logger.info(text + "=" + newValue);
2208
2068
  text = newValue;
2209
2069
  }
2210
- info.text = text;
2211
2070
  let dateAlternatives = findDateAlternatives(text);
2212
2071
  let numberAlternatives = findNumberAlternatives(text);
2213
2072
  try {
2073
+ await _preCommand(state, this);
2074
+ state.info.text = text;
2214
2075
  while (true) {
2215
2076
  const frames = this.page.frames();
2216
2077
  let results = [];
2217
2078
  for (let i = 0; i < frames.length; i++) {
2218
2079
  if (dateAlternatives.date) {
2219
2080
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2220
- 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, {});
2221
2082
  result.frame = frames[i];
2222
2083
  results.push(result);
2223
2084
  }
2224
2085
  }
2225
2086
  else if (numberAlternatives.number) {
2226
2087
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2227
- 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, {});
2228
2089
  result.frame = frames[i];
2229
2090
  results.push(result);
2230
2091
  }
2231
2092
  }
2232
2093
  else {
2233
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2094
+ const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
2234
2095
  result.frame = frames[i];
2235
2096
  results.push(result);
2236
2097
  }
2237
2098
  }
2238
- info.results = results;
2099
+ state.info.results = results;
2239
2100
  const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
2240
2101
  if (resultWithElementsFound.length === 0) {
2241
2102
  if (Date.now() - startTime > timeout) {
@@ -2250,44 +2111,20 @@ class StableBrowser {
2250
2111
  await this._highlightElements(frame, dataAttribute);
2251
2112
  const element = await frame.$(dataAttribute);
2252
2113
  if (element) {
2253
- await this.scrollIfNeeded(element, info);
2114
+ await this.scrollIfNeeded(element, state.info);
2254
2115
  await element.dispatchEvent("bvt_verify_page_contains_text");
2255
2116
  }
2256
2117
  }
2257
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2258
- return info;
2118
+ await _screenshot(state, this);
2119
+ return state.info;
2259
2120
  }
2260
2121
  // await expect(element).toHaveCount(1, { timeout: 10000 });
2261
2122
  }
2262
2123
  catch (e) {
2263
- //await this.closeUnexpectedPopups();
2264
- this.logger.error("verify text exist in page failed " + info.log);
2265
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2266
- info.screenshotPath = screenshotPath;
2267
- Object.assign(e, { info: info });
2268
- error = e;
2269
- throw e;
2124
+ await _commandError(state, e, this);
2270
2125
  }
2271
2126
  finally {
2272
- const endTime = Date.now();
2273
- this._reportToWorld(world, {
2274
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
2275
- text: "Verify text exists in page",
2276
- screenshotId,
2277
- result: error
2278
- ? {
2279
- status: "FAILED",
2280
- startTime,
2281
- endTime,
2282
- message: error === null || error === void 0 ? void 0 : error.message,
2283
- }
2284
- : {
2285
- status: "PASSED",
2286
- startTime,
2287
- endTime,
2288
- },
2289
- info: info,
2290
- });
2127
+ _commandFinally(state, this);
2291
2128
  }
2292
2129
  }
2293
2130
  _getServerUrl() {
@@ -2350,7 +2187,8 @@ class StableBrowser {
2350
2187
  info.screenshotPath = screenshotPath;
2351
2188
  Object.assign(e, { info: info });
2352
2189
  error = e;
2353
- throw e;
2190
+ // throw e;
2191
+ await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
2354
2192
  }
2355
2193
  finally {
2356
2194
  const endTime = Date.now();
@@ -2363,7 +2201,7 @@ class StableBrowser {
2363
2201
  status: "FAILED",
2364
2202
  startTime,
2365
2203
  endTime,
2366
- message: error === null || error === void 0 ? void 0 : error.message,
2204
+ message: error?.message,
2367
2205
  }
2368
2206
  : {
2369
2207
  status: "PASSED",
@@ -2395,7 +2233,7 @@ class StableBrowser {
2395
2233
  this.logger.info("Table data verified");
2396
2234
  }
2397
2235
  async getTableData(selectors, _params = null, options = {}, world = null) {
2398
- this._validateSelectors(selectors);
2236
+ _validateSelectors(selectors);
2399
2237
  const startTime = Date.now();
2400
2238
  let error = null;
2401
2239
  let screenshotId = null;
@@ -2417,7 +2255,8 @@ class StableBrowser {
2417
2255
  info.screenshotPath = screenshotPath;
2418
2256
  Object.assign(e, { info: info });
2419
2257
  error = e;
2420
- throw e;
2258
+ // throw e;
2259
+ await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
2421
2260
  }
2422
2261
  finally {
2423
2262
  const endTime = Date.now();
@@ -2431,7 +2270,7 @@ class StableBrowser {
2431
2270
  status: "FAILED",
2432
2271
  startTime,
2433
2272
  endTime,
2434
- message: error === null || error === void 0 ? void 0 : error.message,
2273
+ message: error?.message,
2435
2274
  }
2436
2275
  : {
2437
2276
  status: "PASSED",
@@ -2443,7 +2282,7 @@ class StableBrowser {
2443
2282
  }
2444
2283
  }
2445
2284
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2446
- this._validateSelectors(selectors);
2285
+ _validateSelectors(selectors);
2447
2286
  if (!query) {
2448
2287
  throw new Error("query is null");
2449
2288
  }
@@ -2582,7 +2421,8 @@ class StableBrowser {
2582
2421
  info.screenshotPath = screenshotPath;
2583
2422
  Object.assign(e, { info: info });
2584
2423
  error = e;
2585
- throw e;
2424
+ // throw e;
2425
+ await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
2586
2426
  }
2587
2427
  finally {
2588
2428
  const endTime = Date.now();
@@ -2596,7 +2436,7 @@ class StableBrowser {
2596
2436
  status: "FAILED",
2597
2437
  startTime,
2598
2438
  endTime,
2599
- message: error === null || error === void 0 ? void 0 : error.message,
2439
+ message: error?.message,
2600
2440
  }
2601
2441
  : {
2602
2442
  status: "PASSED",
@@ -2608,27 +2448,7 @@ class StableBrowser {
2608
2448
  }
2609
2449
  }
2610
2450
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2611
- if (!value) {
2612
- return value;
2613
- }
2614
- // find all the accurance of {{(.*?)}} and replace with the value
2615
- let regex = /{{(.*?)}}/g;
2616
- let matches = value.match(regex);
2617
- if (matches) {
2618
- const testData = this.getTestData(world);
2619
- for (let i = 0; i < matches.length; i++) {
2620
- let match = matches[i];
2621
- let key = match.substring(2, match.length - 2);
2622
- let newValue = objectPath.get(testData, key, null);
2623
- if (newValue !== null) {
2624
- value = value.replace(match, newValue);
2625
- }
2626
- }
2627
- }
2628
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2629
- return await decrypt(value, null, totpWait);
2630
- }
2631
- return value;
2451
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2632
2452
  }
2633
2453
  _getLoadTimeout(options) {
2634
2454
  let timeout = 15000;
@@ -2688,7 +2508,7 @@ class StableBrowser {
2688
2508
  status: "FAILED",
2689
2509
  startTime,
2690
2510
  endTime,
2691
- message: error === null || error === void 0 ? void 0 : error.message,
2511
+ message: error?.message,
2692
2512
  }
2693
2513
  : {
2694
2514
  status: "PASSED",
@@ -2699,39 +2519,28 @@ class StableBrowser {
2699
2519
  }
2700
2520
  }
2701
2521
  async closePage(options = {}, world = null) {
2702
- const startTime = Date.now();
2703
- let error = null;
2704
- let screenshotId = null;
2705
- let screenshotPath = null;
2706
- 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
+ };
2707
2534
  try {
2535
+ await _preCommand(state, this);
2708
2536
  await this.page.close();
2709
2537
  }
2710
2538
  catch (e) {
2711
2539
  console.log(".");
2540
+ await _commandError(state, e, this);
2712
2541
  }
2713
2542
  finally {
2714
- await new Promise((resolve) => setTimeout(resolve, 2000));
2715
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world));
2716
- const endTime = Date.now();
2717
- this._reportToWorld(world, {
2718
- type: Types.CLOSE_PAGE,
2719
- text: "close page",
2720
- screenshotId,
2721
- result: error
2722
- ? {
2723
- status: "FAILED",
2724
- startTime,
2725
- endTime,
2726
- message: error === null || error === void 0 ? void 0 : error.message,
2727
- }
2728
- : {
2729
- status: "PASSED",
2730
- startTime,
2731
- endTime,
2732
- },
2733
- info: info,
2734
- });
2543
+ _commandFinally(state, this);
2735
2544
  }
2736
2545
  }
2737
2546
  async setViewportSize(width, hight, options = {}, world = null) {
@@ -2751,6 +2560,7 @@ class StableBrowser {
2751
2560
  }
2752
2561
  catch (e) {
2753
2562
  console.log(".");
2563
+ await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
2754
2564
  }
2755
2565
  finally {
2756
2566
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2765,7 +2575,7 @@ class StableBrowser {
2765
2575
  status: "FAILED",
2766
2576
  startTime,
2767
2577
  endTime,
2768
- message: error === null || error === void 0 ? void 0 : error.message,
2578
+ message: error?.message,
2769
2579
  }
2770
2580
  : {
2771
2581
  status: "PASSED",
@@ -2787,6 +2597,7 @@ class StableBrowser {
2787
2597
  }
2788
2598
  catch (e) {
2789
2599
  console.log(".");
2600
+ await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
2790
2601
  }
2791
2602
  finally {
2792
2603
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -2801,7 +2612,7 @@ class StableBrowser {
2801
2612
  status: "FAILED",
2802
2613
  startTime,
2803
2614
  endTime,
2804
- message: error === null || error === void 0 ? void 0 : error.message,
2615
+ message: error?.message,
2805
2616
  }
2806
2617
  : {
2807
2618
  status: "PASSED",