automation_model 1.0.438-dev → 1.0.438-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,19 +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";
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";
18
21
  const Types = {
19
22
  CLICK: "click_element",
20
23
  NAVIGATE: "navigate",
@@ -42,15 +45,24 @@ const Types = {
42
45
  LOAD_DATA: "load_data",
43
46
  SET_INPUT: "set_input",
44
47
  };
48
+ export const apps = {};
45
49
  class StableBrowser {
46
- 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) {
47
61
  this.browser = browser;
48
62
  this.page = page;
49
63
  this.logger = logger;
50
64
  this.context = context;
51
- this.project_path = null;
52
- this.webLogFile = null;
53
- this.configuration = null;
65
+ this.world = world;
54
66
  if (!this.logger) {
55
67
  this.logger = console;
56
68
  }
@@ -76,16 +88,31 @@ class StableBrowser {
76
88
  this.logger.error("unable to read ai_config.json");
77
89
  }
78
90
  const logFolder = path.join(this.project_path, "logs", "web");
79
- this.webLogFile = this.getWebLogFile(logFolder);
80
- this.registerConsoleLogListener(page, context, this.webLogFile);
81
- this.registerRequestListener();
91
+ this.world = world;
82
92
  context.pages = [this.page];
83
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);
97
+ }
98
+ registerEventListeners(context) {
99
+ this.registerConsoleLogListener(this.page, context);
100
+ this.registerRequestListener(this.page, context, this.webLogFile);
101
+ if (!context.pageLoading) {
102
+ context.pageLoading = { status: false };
103
+ }
84
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
+ }
85
110
  context.pageLoading.status = true;
86
111
  this.page = page;
87
112
  context.page = page;
88
113
  context.pages.push(page);
114
+ registerNetworkEvents(this.world, this, context, this.page);
115
+ registerDownloadEvent(this.page, this.world, context);
89
116
  page.on("close", async () => {
90
117
  if (this.context && this.context.pages && this.context.pages.length > 1) {
91
118
  this.context.pages.pop();
@@ -110,6 +137,36 @@ class StableBrowser {
110
137
  context.pageLoading.status = false;
111
138
  }.bind(this));
112
139
  }
140
+ async switchApp(appName) {
141
+ // check if the current app (this.appName) is the same as the new app
142
+ if (this.appName === appName) {
143
+ return;
144
+ }
145
+ let navigate = false;
146
+ if (!apps[appName]) {
147
+ let newContext = await getContext(null, false, this.logger, appName, false, this);
148
+ navigate = true;
149
+ apps[appName] = {
150
+ context: newContext,
151
+ browser: newContext.browser,
152
+ page: newContext.page,
153
+ };
154
+ }
155
+ const tempContext = {};
156
+ this._copyContext(this, tempContext);
157
+ this._copyContext(apps[appName], this);
158
+ apps[this.appName] = tempContext;
159
+ this.appName = appName;
160
+ if (navigate) {
161
+ await this.goto(this.context.environment.baseUrl);
162
+ await this.waitForPageLoad();
163
+ }
164
+ }
165
+ _copyContext(from, to) {
166
+ to.browser = from.browser;
167
+ to.page = from.page;
168
+ to.context = from.context;
169
+ }
113
170
  getWebLogFile(logFolder) {
114
171
  if (!fs.existsSync(logFolder)) {
115
172
  fs.mkdirSync(logFolder, { recursive: true });
@@ -121,37 +178,63 @@ class StableBrowser {
121
178
  const fileName = nextIndex + ".json";
122
179
  return path.join(logFolder, fileName);
123
180
  }
124
- registerConsoleLogListener(page, context, logFile) {
181
+ registerConsoleLogListener(page, context) {
125
182
  if (!this.context.webLogger) {
126
183
  this.context.webLogger = [];
127
184
  }
128
185
  page.on("console", async (msg) => {
129
- this.context.webLogger.push({
186
+ const obj = {
130
187
  type: msg.type(),
131
188
  text: msg.text(),
132
189
  location: msg.location(),
133
190
  time: new Date().toISOString(),
134
- });
135
- 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
+ }
136
196
  });
137
197
  }
138
- registerRequestListener() {
139
- 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();
140
204
  try {
141
- const pageUrl = new URL(this.page.url());
205
+ const pageUrl = new URL(page.url());
142
206
  const requestUrl = new URL(data.url());
143
207
  if (pageUrl.hostname === requestUrl.hostname) {
144
208
  const method = data.method();
145
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
209
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
146
210
  const token = await data.headerValue("Authorization");
147
211
  if (token) {
148
- this.context.authtoken = token;
212
+ context.authtoken = token;
149
213
  }
150
214
  }
151
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" });
152
228
  }
153
229
  catch (error) {
154
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));
155
238
  }
156
239
  });
157
240
  }
@@ -166,20 +249,6 @@ class StableBrowser {
166
249
  timeout: 60000,
167
250
  });
168
251
  }
169
- _validateSelectors(selectors) {
170
- if (!selectors) {
171
- throw new Error("selectors is null");
172
- }
173
- if (!selectors.locators) {
174
- throw new Error("selectors.locators is null");
175
- }
176
- if (!Array.isArray(selectors.locators)) {
177
- throw new Error("selectors.locators expected to be array");
178
- }
179
- if (selectors.locators.length === 0) {
180
- throw new Error("selectors.locators expected to be non empty array");
181
- }
182
- }
183
252
  _fixUsingParams(text, _params) {
184
253
  if (!_params || typeof text !== "string") {
185
254
  return text;
@@ -247,7 +316,7 @@ class StableBrowser {
247
316
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
248
317
  }
249
318
  }
250
- if (locator === null || locator === void 0 ? void 0 : locator.engine) {
319
+ if (locator?.engine) {
251
320
  if (locator.engine === "css") {
252
321
  locatorReturn = scope.locator(locator.selector);
253
322
  }
@@ -268,7 +337,10 @@ class StableBrowser {
268
337
  return locatorReturn;
269
338
  }
270
339
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
271
- 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), "*", false, false, _params);
272
344
  if (result.elementCount === 0) {
273
345
  return;
274
346
  }
@@ -283,7 +355,7 @@ class StableBrowser {
283
355
  }
284
356
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
285
357
  //const stringifyText = JSON.stringify(text);
286
- return await scope.evaluate(([text, tag, regex, partial]) => {
358
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
287
359
  function isParent(parent, child) {
288
360
  let currentNode = child.parentNode;
289
361
  while (currentNode !== null) {
@@ -295,6 +367,15 @@ class StableBrowser {
295
367
  return false;
296
368
  }
297
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;
298
379
  function collectAllShadowDomElements(element, result = []) {
299
380
  // Check and add the element if it has a shadow root
300
381
  if (element.shadowRoot) {
@@ -313,6 +394,10 @@ class StableBrowser {
313
394
  if (!tag) {
314
395
  tag = "*";
315
396
  }
397
+ let regexpSearch = document.getRegex(text);
398
+ if (regexpSearch) {
399
+ regex = true;
400
+ }
316
401
  let elements = Array.from(document.querySelectorAll(tag));
317
402
  let shadowHosts = [];
318
403
  document.collectAllShadowDomElements(document, shadowHosts);
@@ -328,7 +413,9 @@ class StableBrowser {
328
413
  let randomToken = null;
329
414
  const foundElements = [];
330
415
  if (regex) {
331
- let regexpSearch = new RegExp(text, "im");
416
+ if (!regexpSearch) {
417
+ regexpSearch = new RegExp(text, "im");
418
+ }
332
419
  for (let i = 0; i < elements.length; i++) {
333
420
  const element = elements[i];
334
421
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -342,8 +429,8 @@ class StableBrowser {
342
429
  for (let i = 0; i < elements.length; i++) {
343
430
  const element = elements[i];
344
431
  if (partial) {
345
- if ((element.innerText && element.innerText.trim().includes(text)) ||
346
- (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()))) {
347
434
  foundElements.push(element);
348
435
  }
349
436
  }
@@ -388,6 +475,12 @@ class StableBrowser {
388
475
  }
389
476
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
390
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
+ }
391
484
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
392
485
  let locator = null;
393
486
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
@@ -476,13 +569,18 @@ class StableBrowser {
476
569
  if (result.foundElements.length > 0) {
477
570
  let dialogCloseLocator = result.foundElements[0].locator;
478
571
  await dialogCloseLocator.click();
572
+ // wait for the dialog to close
573
+ await dialogCloseLocator.waitFor({ state: "hidden" });
479
574
  return { rerun: true };
480
575
  }
481
576
  }
482
577
  }
483
578
  return { rerun: false };
484
579
  }
485
- async _locate(selectors, info, _params, timeout = 30000) {
580
+ async _locate(selectors, info, _params, timeout) {
581
+ if (!timeout) {
582
+ timeout = 30000;
583
+ }
486
584
  for (let i = 0; i < 3; i++) {
487
585
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
488
586
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -496,32 +594,41 @@ class StableBrowser {
496
594
  }
497
595
  throw new Error("unable to locate element " + JSON.stringify(selectors));
498
596
  }
499
- async _locate_internal(selectors, info, _params, timeout = 30000) {
500
- let highPriorityTimeout = 5000;
501
- let visibleOnlyTimeout = 6000;
502
- let startTime = performance.now();
503
- let locatorsCount = 0;
504
- //let arrayMode = Array.isArray(selectors);
597
+ async _findFrameScope(selectors, timeout = 30000) {
505
598
  let scope = this.page;
599
+ if (selectors.frame) {
600
+ return selectors.frame;
601
+ }
506
602
  if (selectors.iframe_src || selectors.frameLocators) {
507
- const findFrame = (frame, framescope) => {
603
+ const findFrame = async (frame, framescope) => {
508
604
  for (let i = 0; i < frame.selectors.length; i++) {
509
605
  let frameLocator = frame.selectors[i];
510
606
  if (frameLocator.css) {
511
- framescope = framescope.frameLocator(frameLocator.css);
512
- break;
607
+ let testframescope = framescope.frameLocator(frameLocator.css);
608
+ if (frameLocator.index) {
609
+ testframescope = framescope.nth(frameLocator.index);
610
+ }
611
+ try {
612
+ await testframescope.owner().evaluateHandle(() => true, null, {
613
+ timeout: 5000,
614
+ });
615
+ framescope = testframescope;
616
+ break;
617
+ }
618
+ catch (error) {
619
+ console.error("frame not found " + frameLocator.css);
620
+ }
513
621
  }
514
622
  }
515
623
  if (frame.children) {
516
- return findFrame(frame.children, framescope);
624
+ return await findFrame(frame.children, framescope);
517
625
  }
518
626
  return framescope;
519
627
  };
520
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
521
628
  while (true) {
522
629
  let frameFound = false;
523
630
  if (selectors.nestFrmLoc) {
524
- scope = findFrame(selectors.nestFrmLoc, scope);
631
+ scope = await findFrame(selectors.nestFrmLoc, scope);
525
632
  frameFound = true;
526
633
  break;
527
634
  }
@@ -550,6 +657,25 @@ class StableBrowser {
550
657
  }
551
658
  }
552
659
  }
660
+ if (!scope) {
661
+ scope = this.page;
662
+ }
663
+ return scope;
664
+ }
665
+ async _getDocumentBody(selectors, timeout = 30000) {
666
+ let scope = await this._findFrameScope(selectors, timeout);
667
+ return scope.evaluate(() => {
668
+ var bodyContent = document.body.innerHTML;
669
+ return bodyContent;
670
+ });
671
+ }
672
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
673
+ let highPriorityTimeout = 5000;
674
+ let visibleOnlyTimeout = 6000;
675
+ let startTime = performance.now();
676
+ let locatorsCount = 0;
677
+ //let arrayMode = Array.isArray(selectors);
678
+ let scope = await this._findFrameScope(selectors, timeout);
553
679
  let selectorsLocators = null;
554
680
  selectorsLocators = selectors.locators;
555
681
  // group selectors by priority
@@ -685,86 +811,121 @@ class StableBrowser {
685
811
  }
686
812
  return result;
687
813
  }
688
- async click(selectors, _params, options = {}, world = null) {
689
- this._validateSelectors(selectors);
814
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
690
815
  const startTime = Date.now();
691
- if (options && options.context) {
692
- selectors.locators[0].text = options.context;
816
+ let timeout = 30000;
817
+ if (options && options.timeout) {
818
+ timeout = options.timeout;
693
819
  }
694
- const info = {};
695
- info.log = "***** click on " + selectors.element_name + " *****\n";
696
- info.operation = "click";
697
- info.selectors = selectors;
698
- let error = null;
699
- let screenshotId = null;
700
- let screenshotPath = null;
820
+ while (true) {
821
+ try {
822
+ const result = await locate_element(this.context, elementDescription, "click");
823
+ if (result?.elementNumber >= 0) {
824
+ const selectors = {
825
+ frame: result?.frame,
826
+ locators: [
827
+ {
828
+ css: result?.css,
829
+ },
830
+ ],
831
+ };
832
+ await this.click(selectors, _params, options, world);
833
+ return;
834
+ }
835
+ }
836
+ catch (e) {
837
+ if (performance.now() - startTime > timeout) {
838
+ throw e;
839
+ }
840
+ }
841
+ await new Promise((resolve) => setTimeout(resolve, 3000));
842
+ }
843
+ }
844
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
845
+ const startTime = Date.now();
846
+ let timeout = 30000;
847
+ if (options && options.timeout) {
848
+ timeout = options.timeout;
849
+ }
850
+ while (true) {
851
+ try {
852
+ const result = await locate_element(this.context, elementDescription, "fill", value);
853
+ if (result?.elementNumber >= 0) {
854
+ const selectors = {
855
+ frame: result?.frame,
856
+ locators: [
857
+ {
858
+ css: result?.css,
859
+ },
860
+ ],
861
+ };
862
+ await this.clickType(selectors, value, false, _params, options, world);
863
+ return;
864
+ }
865
+ }
866
+ catch (e) {
867
+ if (performance.now() - startTime > timeout) {
868
+ throw e;
869
+ }
870
+ }
871
+ await new Promise((resolve) => setTimeout(resolve, 3000));
872
+ }
873
+ }
874
+ async click(selectors, _params, options = {}, world = null) {
875
+ const state = {
876
+ selectors,
877
+ _params,
878
+ options,
879
+ world,
880
+ text: "Click element",
881
+ type: Types.CLICK,
882
+ operation: "click",
883
+ log: "***** click on " + selectors.element_name + " *****\n",
884
+ };
701
885
  try {
702
- let element = await this._locate(selectors, info, _params);
703
- await this.scrollIfNeeded(element, info);
704
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
886
+ await _preCommand(state, this);
887
+ if (state.options && state.options.context) {
888
+ state.selectors.locators[0].text = state.options.context;
889
+ }
705
890
  try {
706
- await this._highlightElements(element);
707
- await element.click();
891
+ await state.element.click();
708
892
  await new Promise((resolve) => setTimeout(resolve, 1000));
709
893
  }
710
894
  catch (e) {
711
895
  // await this.closeUnexpectedPopups();
712
- info.log += "click failed, will try again" + "\n";
713
- element = await this._locate(selectors, info, _params);
714
- await element.dispatchEvent("click");
896
+ state.element = await this._locate(selectors, state.info, _params);
897
+ await state.element.dispatchEvent("click");
715
898
  await new Promise((resolve) => setTimeout(resolve, 1000));
716
899
  }
717
900
  await this.waitForPageLoad();
718
- return info;
901
+ return state.info;
719
902
  }
720
903
  catch (e) {
721
- this.logger.error("click failed " + info.log);
722
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
723
- info.screenshotPath = screenshotPath;
724
- Object.assign(e, { info: info });
725
- error = e;
726
- throw e;
904
+ await _commandError(state, e, this);
727
905
  }
728
906
  finally {
729
- const endTime = Date.now();
730
- this._reportToWorld(world, {
731
- element_name: selectors.element_name,
732
- type: Types.CLICK,
733
- text: `Click element`,
734
- screenshotId,
735
- result: error
736
- ? {
737
- status: "FAILED",
738
- startTime,
739
- endTime,
740
- message: error === null || error === void 0 ? void 0 : error.message,
741
- }
742
- : {
743
- status: "PASSED",
744
- startTime,
745
- endTime,
746
- },
747
- info: info,
748
- });
907
+ _commandFinally(state, this);
749
908
  }
750
909
  }
751
910
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
752
- this._validateSelectors(selectors);
753
- const startTime = Date.now();
754
- const info = {};
755
- info.log = "";
756
- info.operation = "setCheck";
757
- info.checked = checked;
758
- info.selectors = selectors;
759
- let error = null;
760
- let screenshotId = null;
761
- let screenshotPath = null;
911
+ const state = {
912
+ selectors,
913
+ _params,
914
+ options,
915
+ world,
916
+ type: checked ? Types.CHECK : Types.UNCHECK,
917
+ text: checked ? `Check element` : `Uncheck element`,
918
+ operation: "setCheck",
919
+ log: "***** check " + selectors.element_name + " *****\n",
920
+ };
762
921
  try {
763
- let element = await this._locate(selectors, info, _params);
764
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
922
+ await _preCommand(state, this);
923
+ state.info.checked = checked;
924
+ // let element = await this._locate(selectors, info, _params);
925
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
765
926
  try {
766
- await this._highlightElements(element);
767
- await element.setChecked(checked);
927
+ // await this._highlightElements(element);
928
+ await state.element.setChecked(checked);
768
929
  await new Promise((resolve) => setTimeout(resolve, 1000));
769
930
  }
770
931
  catch (e) {
@@ -773,179 +934,108 @@ class StableBrowser {
773
934
  }
774
935
  else {
775
936
  //await this.closeUnexpectedPopups();
776
- info.log += "setCheck failed, will try again" + "\n";
777
- element = await this._locate(selectors, info, _params);
778
- await element.setChecked(checked, { timeout: 5000, force: true });
937
+ state.info.log += "setCheck failed, will try again" + "\n";
938
+ state.element = await this._locate(selectors, state.info, _params);
939
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
779
940
  await new Promise((resolve) => setTimeout(resolve, 1000));
780
941
  }
781
942
  }
782
943
  await this.waitForPageLoad();
783
- return info;
944
+ return state.info;
784
945
  }
785
946
  catch (e) {
786
- this.logger.error("setCheck failed " + info.log);
787
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
788
- info.screenshotPath = screenshotPath;
789
- Object.assign(e, { info: info });
790
- error = e;
791
- throw e;
947
+ await _commandError(state, e, this);
792
948
  }
793
949
  finally {
794
- const endTime = Date.now();
795
- this._reportToWorld(world, {
796
- element_name: selectors.element_name,
797
- type: checked ? Types.CHECK : Types.UNCHECK,
798
- text: checked ? `Check element` : `Uncheck element`,
799
- screenshotId,
800
- result: error
801
- ? {
802
- status: "FAILED",
803
- startTime,
804
- endTime,
805
- message: error === null || error === void 0 ? void 0 : error.message,
806
- }
807
- : {
808
- status: "PASSED",
809
- startTime,
810
- endTime,
811
- },
812
- info: info,
813
- });
950
+ _commandFinally(state, this);
814
951
  }
815
952
  }
816
953
  async hover(selectors, _params, options = {}, world = null) {
817
- this._validateSelectors(selectors);
818
- const startTime = Date.now();
819
- const info = {};
820
- info.log = "";
821
- info.operation = "hover";
822
- info.selectors = selectors;
823
- let error = null;
824
- let screenshotId = null;
825
- let screenshotPath = null;
954
+ const state = {
955
+ selectors,
956
+ _params,
957
+ options,
958
+ world,
959
+ type: Types.HOVER,
960
+ text: `Hover element`,
961
+ operation: "hover",
962
+ log: "***** hover " + selectors.element_name + " *****\n",
963
+ };
826
964
  try {
827
- let element = await this._locate(selectors, info, _params);
828
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
965
+ await _preCommand(state, this);
829
966
  try {
830
- await this._highlightElements(element);
831
- await element.hover();
967
+ await state.element.hover();
832
968
  await new Promise((resolve) => setTimeout(resolve, 1000));
833
969
  }
834
970
  catch (e) {
835
971
  //await this.closeUnexpectedPopups();
836
- info.log += "hover failed, will try again" + "\n";
837
- element = await this._locate(selectors, info, _params);
838
- await element.hover({ timeout: 10000 });
972
+ state.info.log += "hover failed, will try again" + "\n";
973
+ state.element = await this._locate(selectors, state.info, _params);
974
+ await state.element.hover({ timeout: 10000 });
839
975
  await new Promise((resolve) => setTimeout(resolve, 1000));
840
976
  }
841
977
  await this.waitForPageLoad();
842
- return info;
978
+ return state.info;
843
979
  }
844
980
  catch (e) {
845
- this.logger.error("hover failed " + info.log);
846
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
847
- info.screenshotPath = screenshotPath;
848
- Object.assign(e, { info: info });
849
- error = e;
850
- throw e;
981
+ await _commandError(state, e, this);
851
982
  }
852
983
  finally {
853
- const endTime = Date.now();
854
- this._reportToWorld(world, {
855
- element_name: selectors.element_name,
856
- type: Types.HOVER,
857
- text: `Hover element`,
858
- screenshotId,
859
- result: error
860
- ? {
861
- status: "FAILED",
862
- startTime,
863
- endTime,
864
- message: error === null || error === void 0 ? void 0 : error.message,
865
- }
866
- : {
867
- status: "PASSED",
868
- startTime,
869
- endTime,
870
- },
871
- info: info,
872
- });
984
+ _commandFinally(state, this);
873
985
  }
874
986
  }
875
987
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
876
- this._validateSelectors(selectors);
877
988
  if (!values) {
878
989
  throw new Error("values is null");
879
990
  }
880
- const startTime = Date.now();
881
- let error = null;
882
- let screenshotId = null;
883
- let screenshotPath = null;
884
- const info = {};
885
- info.log = "";
886
- info.operation = "selectOptions";
887
- info.selectors = selectors;
991
+ const state = {
992
+ selectors,
993
+ _params,
994
+ options,
995
+ world,
996
+ value: values.toString(),
997
+ type: Types.SELECT,
998
+ text: `Select option: ${values}`,
999
+ operation: "selectOption",
1000
+ log: "***** select option " + selectors.element_name + " *****\n",
1001
+ };
888
1002
  try {
889
- let element = await this._locate(selectors, info, _params);
890
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1003
+ await _preCommand(state, this);
891
1004
  try {
892
- await this._highlightElements(element);
893
- await element.selectOption(values);
1005
+ await state.element.selectOption(values);
894
1006
  }
895
1007
  catch (e) {
896
1008
  //await this.closeUnexpectedPopups();
897
- info.log += "selectOption failed, will try force" + "\n";
898
- await element.selectOption(values, { timeout: 10000, force: true });
1009
+ state.info.log += "selectOption failed, will try force" + "\n";
1010
+ await state.element.selectOption(values, { timeout: 10000, force: true });
899
1011
  }
900
1012
  await this.waitForPageLoad();
901
- return info;
1013
+ return state.info;
902
1014
  }
903
1015
  catch (e) {
904
- this.logger.error("selectOption failed " + info.log);
905
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
906
- info.screenshotPath = screenshotPath;
907
- Object.assign(e, { info: info });
908
- this.logger.info("click failed, will try next selector");
909
- error = e;
910
- throw e;
1016
+ await _commandError(state, e, this);
911
1017
  }
912
1018
  finally {
913
- const endTime = Date.now();
914
- this._reportToWorld(world, {
915
- element_name: selectors.element_name,
916
- type: Types.SELECT,
917
- text: `Select option: ${values}`,
918
- value: values.toString(),
919
- screenshotId,
920
- result: error
921
- ? {
922
- status: "FAILED",
923
- startTime,
924
- endTime,
925
- message: error === null || error === void 0 ? void 0 : error.message,
926
- }
927
- : {
928
- status: "PASSED",
929
- startTime,
930
- endTime,
931
- },
932
- info: info,
933
- });
1019
+ _commandFinally(state, this);
934
1020
  }
935
1021
  }
936
1022
  async type(_value, _params = null, options = {}, world = null) {
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 = "type";
944
- _value = this._fixUsingParams(_value, _params);
945
- info.value = _value;
1023
+ const state = {
1024
+ value: _value,
1025
+ _params,
1026
+ options,
1027
+ world,
1028
+ locate: false,
1029
+ scroll: false,
1030
+ highlight: false,
1031
+ type: Types.TYPE_PRESS,
1032
+ text: `Type value: ${_value}`,
1033
+ operation: "type",
1034
+ log: "",
1035
+ };
946
1036
  try {
947
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
948
- const valueSegment = _value.split("&&");
1037
+ await _preCommand(state, this);
1038
+ const valueSegment = state.value.split("&&");
949
1039
  for (let i = 0; i < valueSegment.length; i++) {
950
1040
  if (i > 0) {
951
1041
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -965,108 +1055,53 @@ class StableBrowser {
965
1055
  await this.page.keyboard.type(value);
966
1056
  }
967
1057
  }
968
- return info;
1058
+ return state.info;
969
1059
  }
970
1060
  catch (e) {
971
- //await this.closeUnexpectedPopups();
972
- this.logger.error("type failed " + info.log);
973
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
974
- info.screenshotPath = screenshotPath;
975
- Object.assign(e, { info: info });
976
- error = e;
977
- throw e;
1061
+ await _commandError(state, e, this);
978
1062
  }
979
1063
  finally {
980
- const endTime = Date.now();
981
- this._reportToWorld(world, {
982
- type: Types.TYPE_PRESS,
983
- screenshotId,
984
- value: _value,
985
- text: `type value: ${_value}`,
986
- result: error
987
- ? {
988
- status: "FAILED",
989
- startTime,
990
- endTime,
991
- message: error === null || error === void 0 ? void 0 : error.message,
992
- }
993
- : {
994
- status: "PASSED",
995
- startTime,
996
- endTime,
997
- },
998
- info: info,
999
- });
1064
+ _commandFinally(state, this);
1000
1065
  }
1001
1066
  }
1002
1067
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1003
- // set input value for non fillable inputs like date, time, range, color, etc.
1004
- this._validateSelectors(selectors);
1005
- const startTime = Date.now();
1006
- const info = {};
1007
- info.log = "***** set input value " + selectors.element_name + " *****\n";
1008
- info.operation = "setInputValue";
1009
- info.selectors = selectors;
1010
- value = this._fixUsingParams(value, _params);
1011
- info.value = value;
1012
- let error = null;
1013
- let screenshotId = null;
1014
- let screenshotPath = null;
1068
+ const state = {
1069
+ selectors,
1070
+ _params,
1071
+ value,
1072
+ options,
1073
+ world,
1074
+ type: Types.SET_INPUT,
1075
+ text: `Set input value`,
1076
+ operation: "setInputValue",
1077
+ log: "***** set input value " + selectors.element_name + " *****\n",
1078
+ };
1015
1079
  try {
1016
- value = await this._replaceWithLocalData(value, this);
1017
- let element = await this._locate(selectors, info, _params);
1018
- await this.scrollIfNeeded(element, info);
1019
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1020
- await this._highlightElements(element);
1080
+ await _preCommand(state, this);
1081
+ let value = await this._replaceWithLocalData(state.value, this);
1021
1082
  try {
1022
- await element.evaluateHandle((el, value) => {
1083
+ await state.element.evaluateHandle((el, value) => {
1023
1084
  el.value = value;
1024
1085
  }, value);
1025
1086
  }
1026
1087
  catch (error) {
1027
1088
  this.logger.error("setInputValue failed, will try again");
1028
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1029
- info.screenshotPath = screenshotPath;
1030
- Object.assign(error, { info: info });
1031
- await element.evaluateHandle((el, value) => {
1089
+ await _screenshot(state, this);
1090
+ Object.assign(error, { info: state.info });
1091
+ await state.element.evaluateHandle((el, value) => {
1032
1092
  el.value = value;
1033
1093
  });
1034
1094
  }
1035
1095
  }
1036
1096
  catch (e) {
1037
- this.logger.error("setInputValue failed " + info.log);
1038
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1039
- info.screenshotPath = screenshotPath;
1040
- Object.assign(e, { info: info });
1041
- error = e;
1042
- throw e;
1097
+ await _commandError(state, e, this);
1043
1098
  }
1044
1099
  finally {
1045
- const endTime = Date.now();
1046
- this._reportToWorld(world, {
1047
- element_name: selectors.element_name,
1048
- type: Types.SET_INPUT,
1049
- text: `Set input value`,
1050
- value: value,
1051
- screenshotId,
1052
- result: error
1053
- ? {
1054
- status: "FAILED",
1055
- startTime,
1056
- endTime,
1057
- message: error === null || error === void 0 ? void 0 : error.message,
1058
- }
1059
- : {
1060
- status: "PASSED",
1061
- startTime,
1062
- endTime,
1063
- },
1064
- info: info,
1065
- });
1100
+ _commandFinally(state, this);
1066
1101
  }
1067
1102
  }
1068
1103
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1069
- this._validateSelectors(selectors);
1104
+ _validateSelectors(selectors);
1070
1105
  const startTime = Date.now();
1071
1106
  let error = null;
1072
1107
  let screenshotId = null;
@@ -1159,32 +1194,32 @@ class StableBrowser {
1159
1194
  }
1160
1195
  }
1161
1196
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1162
- this._validateSelectors(selectors);
1163
- const startTime = Date.now();
1164
- let error = null;
1165
- let screenshotId = null;
1166
- let screenshotPath = null;
1167
- const info = {};
1168
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1169
- info.operation = "clickType";
1170
- info.selectors = selectors;
1197
+ _value = unEscapeString(_value);
1171
1198
  const newValue = await this._replaceWithLocalData(_value, world);
1199
+ const state = {
1200
+ selectors,
1201
+ _params,
1202
+ value: newValue,
1203
+ originalValue: _value,
1204
+ options,
1205
+ world,
1206
+ type: Types.FILL,
1207
+ text: `Click type input with value: ${_value}`,
1208
+ operation: "clickType",
1209
+ log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
1210
+ };
1172
1211
  if (newValue !== _value) {
1173
1212
  //this.logger.info(_value + "=" + newValue);
1174
1213
  _value = newValue;
1175
1214
  }
1176
- info.value = _value;
1177
1215
  try {
1178
- let element = await this._locate(selectors, info, _params);
1179
- //insert red border around the element
1180
- await this.scrollIfNeeded(element, info);
1181
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1182
- await this._highlightElements(element);
1216
+ await _preCommand(state, this);
1217
+ state.info.value = _value;
1183
1218
  if (options === null || options === undefined || !options.press) {
1184
1219
  try {
1185
- let currentValue = await element.inputValue();
1220
+ let currentValue = await state.element.inputValue();
1186
1221
  if (currentValue) {
1187
- await element.fill("");
1222
+ await state.element.fill("");
1188
1223
  }
1189
1224
  }
1190
1225
  catch (e) {
@@ -1193,22 +1228,22 @@ class StableBrowser {
1193
1228
  }
1194
1229
  if (options === null || options === undefined || options.press) {
1195
1230
  try {
1196
- await element.click({ timeout: 5000 });
1231
+ await state.element.click({ timeout: 5000 });
1197
1232
  }
1198
1233
  catch (e) {
1199
- await element.dispatchEvent("click");
1234
+ await state.element.dispatchEvent("click");
1200
1235
  }
1201
1236
  }
1202
1237
  else {
1203
1238
  try {
1204
- await element.focus();
1239
+ await state.element.focus();
1205
1240
  }
1206
1241
  catch (e) {
1207
- await element.dispatchEvent("focus");
1242
+ await state.element.dispatchEvent("focus");
1208
1243
  }
1209
1244
  }
1210
1245
  await new Promise((resolve) => setTimeout(resolve, 500));
1211
- const valueSegment = _value.split("&&");
1246
+ const valueSegment = state.value.split("&&");
1212
1247
  for (let i = 0; i < valueSegment.length; i++) {
1213
1248
  if (i > 0) {
1214
1249
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1228,13 +1263,14 @@ class StableBrowser {
1228
1263
  await new Promise((resolve) => setTimeout(resolve, 500));
1229
1264
  }
1230
1265
  }
1266
+ await _screenshot(state, this);
1231
1267
  if (enter === true) {
1232
1268
  await new Promise((resolve) => setTimeout(resolve, 2000));
1233
1269
  await this.page.keyboard.press("Enter");
1234
1270
  await this.waitForPageLoad();
1235
1271
  }
1236
1272
  else if (enter === false) {
1237
- await element.dispatchEvent("change");
1273
+ await state.element.dispatchEvent("change");
1238
1274
  //await this.page.keyboard.press("Tab");
1239
1275
  }
1240
1276
  else {
@@ -1243,103 +1279,50 @@ class StableBrowser {
1243
1279
  await this.waitForPageLoad();
1244
1280
  }
1245
1281
  }
1246
- return info;
1282
+ return state.info;
1247
1283
  }
1248
1284
  catch (e) {
1249
- //await this.closeUnexpectedPopups();
1250
- this.logger.error("fill failed " + JSON.stringify(info));
1251
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1252
- info.screenshotPath = screenshotPath;
1253
- Object.assign(e, { info: info });
1254
- error = e;
1255
- throw e;
1285
+ await _commandError(state, e, this);
1256
1286
  }
1257
1287
  finally {
1258
- const endTime = Date.now();
1259
- this._reportToWorld(world, {
1260
- element_name: selectors.element_name,
1261
- type: Types.FILL,
1262
- screenshotId,
1263
- value: _value,
1264
- text: `clickType input with value: ${_value}`,
1265
- result: error
1266
- ? {
1267
- status: "FAILED",
1268
- startTime,
1269
- endTime,
1270
- message: error === null || error === void 0 ? void 0 : error.message,
1271
- }
1272
- : {
1273
- status: "PASSED",
1274
- startTime,
1275
- endTime,
1276
- },
1277
- info: info,
1278
- });
1288
+ _commandFinally(state, this);
1279
1289
  }
1280
1290
  }
1281
1291
  async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1282
- this._validateSelectors(selectors);
1283
- const startTime = Date.now();
1284
- let error = null;
1285
- let screenshotId = null;
1286
- let screenshotPath = null;
1287
- const info = {};
1288
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1289
- info.operation = "fill";
1290
- info.selectors = selectors;
1291
- info.value = value;
1292
+ const state = {
1293
+ selectors,
1294
+ _params,
1295
+ value: unEscapeString(value),
1296
+ options,
1297
+ world,
1298
+ type: Types.FILL,
1299
+ text: `Fill input with value: ${value}`,
1300
+ operation: "fill",
1301
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1302
+ };
1292
1303
  try {
1293
- let element = await this._locate(selectors, info, _params);
1294
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1295
- await this._highlightElements(element);
1296
- await element.fill(value);
1297
- await element.dispatchEvent("change");
1304
+ await _preCommand(state, this);
1305
+ await state.element.fill(value);
1306
+ await state.element.dispatchEvent("change");
1298
1307
  if (enter) {
1299
1308
  await new Promise((resolve) => setTimeout(resolve, 2000));
1300
1309
  await this.page.keyboard.press("Enter");
1301
1310
  }
1302
1311
  await this.waitForPageLoad();
1303
- return info;
1312
+ return state.info;
1304
1313
  }
1305
1314
  catch (e) {
1306
- //await this.closeUnexpectedPopups();
1307
- this.logger.error("fill failed " + info.log);
1308
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1309
- info.screenshotPath = screenshotPath;
1310
- Object.assign(e, { info: info });
1311
- error = e;
1312
- throw e;
1315
+ await _commandError(state, e, this);
1313
1316
  }
1314
1317
  finally {
1315
- const endTime = Date.now();
1316
- this._reportToWorld(world, {
1317
- element_name: selectors.element_name,
1318
- type: Types.FILL,
1319
- screenshotId,
1320
- value,
1321
- text: `Fill input with value: ${value}`,
1322
- result: error
1323
- ? {
1324
- status: "FAILED",
1325
- startTime,
1326
- endTime,
1327
- message: error === null || error === void 0 ? void 0 : error.message,
1328
- }
1329
- : {
1330
- status: "PASSED",
1331
- startTime,
1332
- endTime,
1333
- },
1334
- info: info,
1335
- });
1318
+ _commandFinally(state, this);
1336
1319
  }
1337
1320
  }
1338
1321
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1339
1322
  return await this._getText(selectors, 0, _params, options, info, world);
1340
1323
  }
1341
1324
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1342
- this._validateSelectors(selectors);
1325
+ _validateSelectors(selectors);
1343
1326
  let screenshotId = null;
1344
1327
  let screenshotPath = null;
1345
1328
  if (!info.log) {
@@ -1383,165 +1366,124 @@ class StableBrowser {
1383
1366
  }
1384
1367
  }
1385
1368
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1386
- var _a;
1387
- this._validateSelectors(selectors);
1388
1369
  if (!pattern) {
1389
1370
  throw new Error("pattern is null");
1390
1371
  }
1391
1372
  if (!text) {
1392
1373
  throw new Error("text is null");
1393
1374
  }
1375
+ const state = {
1376
+ selectors,
1377
+ _params,
1378
+ pattern,
1379
+ value: pattern,
1380
+ options,
1381
+ world,
1382
+ locate: false,
1383
+ scroll: false,
1384
+ screenshot: false,
1385
+ highlight: false,
1386
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1387
+ text: `Verify element contains pattern: ${pattern}`,
1388
+ operation: "containsPattern",
1389
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1390
+ };
1394
1391
  const newValue = await this._replaceWithLocalData(text, world);
1395
1392
  if (newValue !== text) {
1396
1393
  this.logger.info(text + "=" + newValue);
1397
1394
  text = newValue;
1398
1395
  }
1399
- const startTime = Date.now();
1400
- let error = null;
1401
- let screenshotId = null;
1402
- let screenshotPath = null;
1403
- const info = {};
1404
- info.log =
1405
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1406
- info.operation = "containsPattern";
1407
- info.selectors = selectors;
1408
- info.value = text;
1409
- info.pattern = pattern;
1410
1396
  let foundObj = null;
1411
1397
  try {
1412
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1398
+ await _preCommand(state, this);
1399
+ state.info.pattern = pattern;
1400
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1413
1401
  if (foundObj && foundObj.element) {
1414
- await this.scrollIfNeeded(foundObj.element, info);
1402
+ await this.scrollIfNeeded(foundObj.element, state.info);
1415
1403
  }
1416
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1404
+ await _screenshot(state, this);
1417
1405
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1418
1406
  pattern = pattern.replace("{text}", escapedText);
1419
1407
  let regex = new RegExp(pattern, "im");
1420
- 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))) {
1421
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1408
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1409
+ state.info.foundText = foundObj?.text;
1422
1410
  throw new Error("element doesn't contain text " + text);
1423
1411
  }
1424
- return info;
1412
+ return state.info;
1425
1413
  }
1426
1414
  catch (e) {
1427
- //await this.closeUnexpectedPopups();
1428
- this.logger.error("verify element contains text failed " + info.log);
1429
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1430
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1431
- info.screenshotPath = screenshotPath;
1432
- Object.assign(e, { info: info });
1433
- error = e;
1434
- throw e;
1415
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1416
+ await _commandError(state, e, this);
1435
1417
  }
1436
1418
  finally {
1437
- const endTime = Date.now();
1438
- this._reportToWorld(world, {
1439
- element_name: selectors.element_name,
1440
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1441
- value: pattern,
1442
- text: `Verify element contains pattern: ${pattern}`,
1443
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1444
- result: error
1445
- ? {
1446
- status: "FAILED",
1447
- startTime,
1448
- endTime,
1449
- message: error === null || error === void 0 ? void 0 : error.message,
1450
- }
1451
- : {
1452
- status: "PASSED",
1453
- startTime,
1454
- endTime,
1455
- },
1456
- info: info,
1457
- });
1419
+ _commandFinally(state, this);
1458
1420
  }
1459
1421
  }
1460
1422
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1461
- var _a, _b, _c;
1462
- this._validateSelectors(selectors);
1423
+ const state = {
1424
+ selectors,
1425
+ _params,
1426
+ value: text,
1427
+ options,
1428
+ world,
1429
+ locate: false,
1430
+ scroll: false,
1431
+ screenshot: false,
1432
+ highlight: false,
1433
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1434
+ text: `Verify element contains text: ${text}`,
1435
+ operation: "containsText",
1436
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1437
+ };
1463
1438
  if (!text) {
1464
1439
  throw new Error("text is null");
1465
1440
  }
1466
- const startTime = Date.now();
1467
- let error = null;
1468
- let screenshotId = null;
1469
- let screenshotPath = null;
1470
- const info = {};
1471
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1472
- info.operation = "containsText";
1473
- info.selectors = selectors;
1441
+ text = unEscapeString(text);
1474
1442
  const newValue = await this._replaceWithLocalData(text, world);
1475
1443
  if (newValue !== text) {
1476
1444
  this.logger.info(text + "=" + newValue);
1477
1445
  text = newValue;
1478
1446
  }
1479
- info.value = text;
1480
1447
  let foundObj = null;
1481
1448
  try {
1482
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1449
+ await _preCommand(state, this);
1450
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1483
1451
  if (foundObj && foundObj.element) {
1484
- await this.scrollIfNeeded(foundObj.element, info);
1452
+ await this.scrollIfNeeded(foundObj.element, state.info);
1485
1453
  }
1486
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1454
+ await _screenshot(state, this);
1487
1455
  const dateAlternatives = findDateAlternatives(text);
1488
1456
  const numberAlternatives = findNumberAlternatives(text);
1489
1457
  if (dateAlternatives.date) {
1490
1458
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1491
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1492
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1493
- return info;
1459
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1460
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1461
+ return state.info;
1494
1462
  }
1495
1463
  }
1496
1464
  throw new Error("element doesn't contain text " + text);
1497
1465
  }
1498
1466
  else if (numberAlternatives.number) {
1499
1467
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1500
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1501
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1502
- return info;
1468
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1469
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1470
+ return state.info;
1503
1471
  }
1504
1472
  }
1505
1473
  throw new Error("element doesn't contain text " + text);
1506
1474
  }
1507
- 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))) {
1508
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1509
- info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1475
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1476
+ state.info.foundText = foundObj?.text;
1477
+ state.info.value = foundObj?.value;
1510
1478
  throw new Error("element doesn't contain text " + text);
1511
1479
  }
1512
- return info;
1480
+ return state.info;
1513
1481
  }
1514
1482
  catch (e) {
1515
- //await this.closeUnexpectedPopups();
1516
- this.logger.error("verify element contains text failed " + info.log);
1517
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1518
- info.screenshotPath = screenshotPath;
1519
- Object.assign(e, { info: info });
1520
- error = e;
1521
- throw e;
1483
+ await _commandError(state, e, this);
1522
1484
  }
1523
1485
  finally {
1524
- const endTime = Date.now();
1525
- this._reportToWorld(world, {
1526
- element_name: selectors.element_name,
1527
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1528
- text: `Verify element contains text: ${text}`,
1529
- value: text,
1530
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1531
- result: error
1532
- ? {
1533
- status: "FAILED",
1534
- startTime,
1535
- endTime,
1536
- message: error === null || error === void 0 ? void 0 : error.message,
1537
- }
1538
- : {
1539
- status: "PASSED",
1540
- startTime,
1541
- endTime,
1542
- },
1543
- info: info,
1544
- });
1486
+ _commandFinally(state, this);
1545
1487
  }
1546
1488
  }
1547
1489
  _getDataFile(world = null) {
@@ -1770,7 +1712,6 @@ class StableBrowser {
1770
1712
  }
1771
1713
  async takeScreenshot(screenshotPath) {
1772
1714
  const playContext = this.context.playContext;
1773
- const client = await playContext.newCDPSession(this.page);
1774
1715
  // Using CDP to capture the screenshot
1775
1716
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1776
1717
  document.body.scrollWidth,
@@ -1780,97 +1721,67 @@ class StableBrowser {
1780
1721
  document.body.clientWidth,
1781
1722
  document.documentElement.clientWidth,
1782
1723
  ])));
1783
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1784
- document.body.scrollHeight,
1785
- document.documentElement.scrollHeight,
1786
- document.body.offsetHeight,
1787
- document.documentElement.offsetHeight,
1788
- document.body.clientHeight,
1789
- document.documentElement.clientHeight,
1790
- ])));
1791
- const { data } = await client.send("Page.captureScreenshot", {
1792
- format: "png",
1793
- // clip: {
1794
- // x: 0,
1795
- // y: 0,
1796
- // width: viewportWidth,
1797
- // height: viewportHeight,
1798
- // scale: 1,
1799
- // },
1800
- });
1801
- if (!screenshotPath) {
1802
- return data;
1803
- }
1804
- let screenshotBuffer = Buffer.from(data, "base64");
1805
- const sharpBuffer = sharp(screenshotBuffer);
1806
- const metadata = await sharpBuffer.metadata();
1807
- //check if you are on retina display and reduce the quality of the image
1808
- if (metadata.width > viewportWidth || metadata.height > viewportHeight) {
1809
- screenshotBuffer = await sharpBuffer
1810
- .resize(viewportWidth, viewportHeight, {
1811
- fit: sharp.fit.inside,
1812
- withoutEnlargement: true,
1813
- })
1814
- .toBuffer();
1815
- }
1816
- fs.writeFileSync(screenshotPath, screenshotBuffer);
1817
- await client.detach();
1724
+ let screenshotBuffer = null;
1725
+ if (this.context.browserName === "chromium") {
1726
+ const client = await playContext.newCDPSession(this.page);
1727
+ const { data } = await client.send("Page.captureScreenshot", {
1728
+ format: "png",
1729
+ // clip: {
1730
+ // x: 0,
1731
+ // y: 0,
1732
+ // width: viewportWidth,
1733
+ // height: viewportHeight,
1734
+ // scale: 1,
1735
+ // },
1736
+ });
1737
+ await client.detach();
1738
+ if (!screenshotPath) {
1739
+ return data;
1740
+ }
1741
+ screenshotBuffer = Buffer.from(data, "base64");
1742
+ }
1743
+ else {
1744
+ screenshotBuffer = await this.page.screenshot();
1745
+ }
1746
+ let image = await Jimp.read(screenshotBuffer);
1747
+ // Get the image dimensions
1748
+ const { width, height } = image.bitmap;
1749
+ const resizeRatio = viewportWidth / width;
1750
+ // Resize the image to fit within the viewport dimensions without enlarging
1751
+ if (width > viewportWidth) {
1752
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1753
+ await image.write(screenshotPath);
1754
+ }
1755
+ else {
1756
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1757
+ }
1818
1758
  }
1819
1759
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1820
- this._validateSelectors(selectors);
1821
- const startTime = Date.now();
1822
- let error = null;
1823
- let screenshotId = null;
1824
- let screenshotPath = null;
1760
+ const state = {
1761
+ selectors,
1762
+ _params,
1763
+ options,
1764
+ world,
1765
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1766
+ text: `Verify element exists in page`,
1767
+ operation: "verifyElementExistInPage",
1768
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1769
+ };
1825
1770
  await new Promise((resolve) => setTimeout(resolve, 2000));
1826
- const info = {};
1827
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
1828
- info.operation = "verify";
1829
- info.selectors = selectors;
1830
1771
  try {
1831
- const element = await this._locate(selectors, info, _params);
1832
- if (element) {
1833
- await this.scrollIfNeeded(element, info);
1834
- }
1835
- await this._highlightElements(element);
1836
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1837
- await expect(element).toHaveCount(1, { timeout: 10000 });
1838
- return info;
1772
+ await _preCommand(state, this);
1773
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1774
+ return state.info;
1839
1775
  }
1840
1776
  catch (e) {
1841
- //await this.closeUnexpectedPopups();
1842
- this.logger.error("verify failed " + info.log);
1843
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1844
- info.screenshotPath = screenshotPath;
1845
- Object.assign(e, { info: info });
1846
- error = e;
1847
- throw e;
1777
+ await _commandError(state, e, this);
1848
1778
  }
1849
1779
  finally {
1850
- const endTime = Date.now();
1851
- this._reportToWorld(world, {
1852
- element_name: selectors.element_name,
1853
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1854
- text: "Verify element exists in page",
1855
- screenshotId,
1856
- result: error
1857
- ? {
1858
- status: "FAILED",
1859
- startTime,
1860
- endTime,
1861
- message: error === null || error === void 0 ? void 0 : error.message,
1862
- }
1863
- : {
1864
- status: "PASSED",
1865
- startTime,
1866
- endTime,
1867
- },
1868
- info: info,
1869
- });
1780
+ _commandFinally(state, this);
1870
1781
  }
1871
1782
  }
1872
1783
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1873
- this._validateSelectors(selectors);
1784
+ _validateSelectors(selectors);
1874
1785
  const startTime = Date.now();
1875
1786
  let error = null;
1876
1787
  let screenshotId = null;
@@ -1929,7 +1840,7 @@ class StableBrowser {
1929
1840
  status: "FAILED",
1930
1841
  startTime,
1931
1842
  endTime,
1932
- message: error === null || error === void 0 ? void 0 : error.message,
1843
+ message: error?.message,
1933
1844
  }
1934
1845
  : {
1935
1846
  status: "PASSED",
@@ -2138,7 +2049,7 @@ class StableBrowser {
2138
2049
  status: "FAILED",
2139
2050
  startTime,
2140
2051
  endTime,
2141
- message: error === null || error === void 0 ? void 0 : error.message,
2052
+ message: error?.message,
2142
2053
  }
2143
2054
  : {
2144
2055
  status: "PASSED",
@@ -2174,20 +2085,20 @@ class StableBrowser {
2174
2085
  for (let i = 0; i < frames.length; i++) {
2175
2086
  if (dateAlternatives.date) {
2176
2087
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2177
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2088
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
2178
2089
  result.frame = frames[i];
2179
2090
  results.push(result);
2180
2091
  }
2181
2092
  }
2182
2093
  else if (numberAlternatives.number) {
2183
2094
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2184
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2095
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
2185
2096
  result.frame = frames[i];
2186
2097
  results.push(result);
2187
2098
  }
2188
2099
  }
2189
2100
  else {
2190
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2101
+ const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
2191
2102
  result.frame = frames[i];
2192
2103
  results.push(result);
2193
2104
  }
@@ -2236,7 +2147,7 @@ class StableBrowser {
2236
2147
  status: "FAILED",
2237
2148
  startTime,
2238
2149
  endTime,
2239
- message: error === null || error === void 0 ? void 0 : error.message,
2150
+ message: error?.message,
2240
2151
  }
2241
2152
  : {
2242
2153
  status: "PASSED",
@@ -2320,7 +2231,7 @@ class StableBrowser {
2320
2231
  status: "FAILED",
2321
2232
  startTime,
2322
2233
  endTime,
2323
- message: error === null || error === void 0 ? void 0 : error.message,
2234
+ message: error?.message,
2324
2235
  }
2325
2236
  : {
2326
2237
  status: "PASSED",
@@ -2352,7 +2263,7 @@ class StableBrowser {
2352
2263
  this.logger.info("Table data verified");
2353
2264
  }
2354
2265
  async getTableData(selectors, _params = null, options = {}, world = null) {
2355
- this._validateSelectors(selectors);
2266
+ _validateSelectors(selectors);
2356
2267
  const startTime = Date.now();
2357
2268
  let error = null;
2358
2269
  let screenshotId = null;
@@ -2388,7 +2299,7 @@ class StableBrowser {
2388
2299
  status: "FAILED",
2389
2300
  startTime,
2390
2301
  endTime,
2391
- message: error === null || error === void 0 ? void 0 : error.message,
2302
+ message: error?.message,
2392
2303
  }
2393
2304
  : {
2394
2305
  status: "PASSED",
@@ -2400,7 +2311,7 @@ class StableBrowser {
2400
2311
  }
2401
2312
  }
2402
2313
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2403
- this._validateSelectors(selectors);
2314
+ _validateSelectors(selectors);
2404
2315
  if (!query) {
2405
2316
  throw new Error("query is null");
2406
2317
  }
@@ -2553,7 +2464,7 @@ class StableBrowser {
2553
2464
  status: "FAILED",
2554
2465
  startTime,
2555
2466
  endTime,
2556
- message: error === null || error === void 0 ? void 0 : error.message,
2467
+ message: error?.message,
2557
2468
  }
2558
2469
  : {
2559
2470
  status: "PASSED",
@@ -2565,27 +2476,7 @@ class StableBrowser {
2565
2476
  }
2566
2477
  }
2567
2478
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2568
- if (!value) {
2569
- return value;
2570
- }
2571
- // find all the accurance of {{(.*?)}} and replace with the value
2572
- let regex = /{{(.*?)}}/g;
2573
- let matches = value.match(regex);
2574
- if (matches) {
2575
- const testData = this.getTestData(world);
2576
- for (let i = 0; i < matches.length; i++) {
2577
- let match = matches[i];
2578
- let key = match.substring(2, match.length - 2);
2579
- let newValue = objectPath.get(testData, key, null);
2580
- if (newValue !== null) {
2581
- value = value.replace(match, newValue);
2582
- }
2583
- }
2584
- }
2585
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2586
- return await decrypt(value, null, totpWait);
2587
- }
2588
- return value;
2479
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2589
2480
  }
2590
2481
  _getLoadTimeout(options) {
2591
2482
  let timeout = 15000;
@@ -2645,7 +2536,7 @@ class StableBrowser {
2645
2536
  status: "FAILED",
2646
2537
  startTime,
2647
2538
  endTime,
2648
- message: error === null || error === void 0 ? void 0 : error.message,
2539
+ message: error?.message,
2649
2540
  }
2650
2541
  : {
2651
2542
  status: "PASSED",
@@ -2680,7 +2571,7 @@ class StableBrowser {
2680
2571
  status: "FAILED",
2681
2572
  startTime,
2682
2573
  endTime,
2683
- message: error === null || error === void 0 ? void 0 : error.message,
2574
+ message: error?.message,
2684
2575
  }
2685
2576
  : {
2686
2577
  status: "PASSED",
@@ -2722,7 +2613,7 @@ class StableBrowser {
2722
2613
  status: "FAILED",
2723
2614
  startTime,
2724
2615
  endTime,
2725
- message: error === null || error === void 0 ? void 0 : error.message,
2616
+ message: error?.message,
2726
2617
  }
2727
2618
  : {
2728
2619
  status: "PASSED",
@@ -2758,7 +2649,7 @@ class StableBrowser {
2758
2649
  status: "FAILED",
2759
2650
  startTime,
2760
2651
  endTime,
2761
- message: error === null || error === void 0 ? void 0 : error.message,
2652
+ message: error?.message,
2762
2653
  }
2763
2654
  : {
2764
2655
  status: "PASSED",
@@ -2943,5 +2834,10 @@ const KEYBOARD_EVENTS = [
2943
2834
  "TVAntennaCable",
2944
2835
  "TVAudioDescription",
2945
2836
  ];
2837
+ function unEscapeString(str) {
2838
+ const placeholder = "__NEWLINE__";
2839
+ str = str.replace(new RegExp(placeholder, "g"), "\n");
2840
+ return str;
2841
+ }
2946
2842
  export { StableBrowser };
2947
2843
  //# sourceMappingURL=stable_browser.js.map