automation_model 1.0.437-dev → 1.0.437-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,26 @@ 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) {
85
105
  context.pageLoading.status = true;
86
106
  this.page = page;
87
107
  context.page = page;
88
108
  context.pages.push(page);
109
+ registerNetworkEvents(this.world, this, context, this.page);
110
+ registerDownloadEvent(this.page, this.world, context);
89
111
  page.on("close", async () => {
90
112
  if (this.context && this.context.pages && this.context.pages.length > 1) {
91
113
  this.context.pages.pop();
@@ -110,6 +132,36 @@ class StableBrowser {
110
132
  context.pageLoading.status = false;
111
133
  }.bind(this));
112
134
  }
135
+ async switchApp(appName) {
136
+ // check if the current app (this.appName) is the same as the new app
137
+ if (this.appName === appName) {
138
+ return;
139
+ }
140
+ let navigate = false;
141
+ if (!apps[appName]) {
142
+ let newContext = await getContext(null, false, this.logger, appName, false, this);
143
+ navigate = true;
144
+ apps[appName] = {
145
+ context: newContext,
146
+ browser: newContext.browser,
147
+ page: newContext.page,
148
+ };
149
+ }
150
+ const tempContext = {};
151
+ this._copyContext(this, tempContext);
152
+ this._copyContext(apps[appName], this);
153
+ apps[this.appName] = tempContext;
154
+ this.appName = appName;
155
+ if (navigate) {
156
+ await this.goto(this.context.environment.baseUrl);
157
+ await this.waitForPageLoad();
158
+ }
159
+ }
160
+ _copyContext(from, to) {
161
+ to.browser = from.browser;
162
+ to.page = from.page;
163
+ to.context = from.context;
164
+ }
113
165
  getWebLogFile(logFolder) {
114
166
  if (!fs.existsSync(logFolder)) {
115
167
  fs.mkdirSync(logFolder, { recursive: true });
@@ -121,37 +173,63 @@ class StableBrowser {
121
173
  const fileName = nextIndex + ".json";
122
174
  return path.join(logFolder, fileName);
123
175
  }
124
- registerConsoleLogListener(page, context, logFile) {
176
+ registerConsoleLogListener(page, context) {
125
177
  if (!this.context.webLogger) {
126
178
  this.context.webLogger = [];
127
179
  }
128
180
  page.on("console", async (msg) => {
129
- this.context.webLogger.push({
181
+ const obj = {
130
182
  type: msg.type(),
131
183
  text: msg.text(),
132
184
  location: msg.location(),
133
185
  time: new Date().toISOString(),
134
- });
135
- await fs.promises.writeFile(logFile, JSON.stringify(this.context.webLogger, null, 2));
186
+ };
187
+ this.context.webLogger.push(obj);
188
+ if (msg.type() === "error") {
189
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
190
+ }
136
191
  });
137
192
  }
138
- registerRequestListener() {
139
- this.page.on("request", async (data) => {
193
+ registerRequestListener(page, context, logFile) {
194
+ if (!this.context.networkLogger) {
195
+ this.context.networkLogger = [];
196
+ }
197
+ page.on("request", async (data) => {
198
+ const startTime = new Date().getTime();
140
199
  try {
141
- const pageUrl = new URL(this.page.url());
200
+ const pageUrl = new URL(page.url());
142
201
  const requestUrl = new URL(data.url());
143
202
  if (pageUrl.hostname === requestUrl.hostname) {
144
203
  const method = data.method();
145
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
204
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
146
205
  const token = await data.headerValue("Authorization");
147
206
  if (token) {
148
- this.context.authtoken = token;
207
+ context.authtoken = token;
149
208
  }
150
209
  }
151
210
  }
211
+ const response = await data.response();
212
+ const endTime = new Date().getTime();
213
+ const obj = {
214
+ url: data.url(),
215
+ method: data.method(),
216
+ postData: data.postData(),
217
+ error: data.failure() ? data.failure().errorText : null,
218
+ duration: endTime - startTime,
219
+ startTime,
220
+ };
221
+ context.networkLogger.push(obj);
222
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
152
223
  }
153
224
  catch (error) {
154
225
  console.error("Error in request listener", error);
226
+ context.networkLogger.push({
227
+ error: "not able to listen",
228
+ message: error.message,
229
+ stack: error.stack,
230
+ time: new Date().toISOString(),
231
+ });
232
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
155
233
  }
156
234
  });
157
235
  }
@@ -166,20 +244,6 @@ class StableBrowser {
166
244
  timeout: 60000,
167
245
  });
168
246
  }
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
247
  _fixUsingParams(text, _params) {
184
248
  if (!_params || typeof text !== "string") {
185
249
  return text;
@@ -247,7 +311,7 @@ class StableBrowser {
247
311
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
248
312
  }
249
313
  }
250
- if (locator === null || locator === void 0 ? void 0 : locator.engine) {
314
+ if (locator?.engine) {
251
315
  if (locator.engine === "css") {
252
316
  locatorReturn = scope.locator(locator.selector);
253
317
  }
@@ -268,7 +332,10 @@ class StableBrowser {
268
332
  return locatorReturn;
269
333
  }
270
334
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
271
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
335
+ if (css && css.locator) {
336
+ css = css.locator;
337
+ }
338
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, false, _params);
272
339
  if (result.elementCount === 0) {
273
340
  return;
274
341
  }
@@ -283,7 +350,7 @@ class StableBrowser {
283
350
  }
284
351
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
285
352
  //const stringifyText = JSON.stringify(text);
286
- return await scope.evaluate(([text, tag, regex, partial]) => {
353
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
287
354
  function isParent(parent, child) {
288
355
  let currentNode = child.parentNode;
289
356
  while (currentNode !== null) {
@@ -295,6 +362,15 @@ class StableBrowser {
295
362
  return false;
296
363
  }
297
364
  document.isParent = isParent;
365
+ function getRegex(str) {
366
+ const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
367
+ if (!match) {
368
+ return null;
369
+ }
370
+ let [_, pattern, flags] = match;
371
+ return new RegExp(pattern, flags);
372
+ }
373
+ document.getRegex = getRegex;
298
374
  function collectAllShadowDomElements(element, result = []) {
299
375
  // Check and add the element if it has a shadow root
300
376
  if (element.shadowRoot) {
@@ -313,6 +389,10 @@ class StableBrowser {
313
389
  if (!tag) {
314
390
  tag = "*";
315
391
  }
392
+ let regexpSearch = document.getRegex(text);
393
+ if (regexpSearch) {
394
+ regex = true;
395
+ }
316
396
  let elements = Array.from(document.querySelectorAll(tag));
317
397
  let shadowHosts = [];
318
398
  document.collectAllShadowDomElements(document, shadowHosts);
@@ -328,7 +408,9 @@ class StableBrowser {
328
408
  let randomToken = null;
329
409
  const foundElements = [];
330
410
  if (regex) {
331
- let regexpSearch = new RegExp(text, "im");
411
+ if (!regexpSearch) {
412
+ regexpSearch = new RegExp(text, "im");
413
+ }
332
414
  for (let i = 0; i < elements.length; i++) {
333
415
  const element = elements[i];
334
416
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -342,8 +424,8 @@ class StableBrowser {
342
424
  for (let i = 0; i < elements.length; i++) {
343
425
  const element = elements[i];
344
426
  if (partial) {
345
- if ((element.innerText && element.innerText.trim().includes(text)) ||
346
- (element.value && element.value.includes(text))) {
427
+ if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
428
+ (element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
347
429
  foundElements.push(element);
348
430
  }
349
431
  }
@@ -388,6 +470,12 @@ class StableBrowser {
388
470
  }
389
471
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
390
472
  let locatorSearch = selectorHierarchy[index];
473
+ try {
474
+ locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
475
+ }
476
+ catch (e) {
477
+ console.error(e);
478
+ }
391
479
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
392
480
  let locator = null;
393
481
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
@@ -476,13 +564,18 @@ class StableBrowser {
476
564
  if (result.foundElements.length > 0) {
477
565
  let dialogCloseLocator = result.foundElements[0].locator;
478
566
  await dialogCloseLocator.click();
567
+ // wait for the dialog to close
568
+ await dialogCloseLocator.waitFor({ state: "hidden" });
479
569
  return { rerun: true };
480
570
  }
481
571
  }
482
572
  }
483
573
  return { rerun: false };
484
574
  }
485
- async _locate(selectors, info, _params, timeout = 30000) {
575
+ async _locate(selectors, info, _params, timeout) {
576
+ if (!timeout) {
577
+ timeout = 30000;
578
+ }
486
579
  for (let i = 0; i < 3; i++) {
487
580
  info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
488
581
  for (let j = 0; j < selectors.locators.length; j++) {
@@ -496,32 +589,41 @@ class StableBrowser {
496
589
  }
497
590
  throw new Error("unable to locate element " + JSON.stringify(selectors));
498
591
  }
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);
592
+ async _findFrameScope(selectors, timeout = 30000) {
505
593
  let scope = this.page;
594
+ if (selectors.frame) {
595
+ return selectors.frame;
596
+ }
506
597
  if (selectors.iframe_src || selectors.frameLocators) {
507
- const findFrame = (frame, framescope) => {
598
+ const findFrame = async (frame, framescope) => {
508
599
  for (let i = 0; i < frame.selectors.length; i++) {
509
600
  let frameLocator = frame.selectors[i];
510
601
  if (frameLocator.css) {
511
- framescope = framescope.frameLocator(frameLocator.css);
512
- break;
602
+ let testframescope = framescope.frameLocator(frameLocator.css);
603
+ if (frameLocator.index) {
604
+ testframescope = framescope.nth(frameLocator.index);
605
+ }
606
+ try {
607
+ await testframescope.owner().evaluateHandle(() => true, null, {
608
+ timeout: 5000,
609
+ });
610
+ framescope = testframescope;
611
+ break;
612
+ }
613
+ catch (error) {
614
+ console.error("frame not found " + frameLocator.css);
615
+ }
513
616
  }
514
617
  }
515
618
  if (frame.children) {
516
- return findFrame(frame.children, framescope);
619
+ return await findFrame(frame.children, framescope);
517
620
  }
518
621
  return framescope;
519
622
  };
520
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
521
623
  while (true) {
522
624
  let frameFound = false;
523
625
  if (selectors.nestFrmLoc) {
524
- scope = findFrame(selectors.nestFrmLoc, scope);
626
+ scope = await findFrame(selectors.nestFrmLoc, scope);
525
627
  frameFound = true;
526
628
  break;
527
629
  }
@@ -550,6 +652,25 @@ class StableBrowser {
550
652
  }
551
653
  }
552
654
  }
655
+ if (!scope) {
656
+ scope = this.page;
657
+ }
658
+ return scope;
659
+ }
660
+ async _getDocumentBody(selectors, timeout = 30000) {
661
+ let scope = await this._findFrameScope(selectors, timeout);
662
+ return scope.evaluate(() => {
663
+ var bodyContent = document.body.innerHTML;
664
+ return bodyContent;
665
+ });
666
+ }
667
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
668
+ let highPriorityTimeout = 5000;
669
+ let visibleOnlyTimeout = 6000;
670
+ let startTime = performance.now();
671
+ let locatorsCount = 0;
672
+ //let arrayMode = Array.isArray(selectors);
673
+ let scope = await this._findFrameScope(selectors, timeout);
553
674
  let selectorsLocators = null;
554
675
  selectorsLocators = selectors.locators;
555
676
  // group selectors by priority
@@ -685,86 +806,121 @@ class StableBrowser {
685
806
  }
686
807
  return result;
687
808
  }
688
- async click(selectors, _params, options = {}, world = null) {
689
- this._validateSelectors(selectors);
809
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
690
810
  const startTime = Date.now();
691
- if (options && options.context) {
692
- selectors.locators[0].text = options.context;
811
+ let timeout = 30000;
812
+ if (options && options.timeout) {
813
+ timeout = options.timeout;
693
814
  }
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;
815
+ while (true) {
816
+ try {
817
+ const result = await locate_element(this.context, elementDescription, "click");
818
+ if (result?.elementNumber >= 0) {
819
+ const selectors = {
820
+ frame: result?.frame,
821
+ locators: [
822
+ {
823
+ css: result?.css,
824
+ },
825
+ ],
826
+ };
827
+ await this.click(selectors, _params, options, world);
828
+ return;
829
+ }
830
+ }
831
+ catch (e) {
832
+ if (performance.now() - startTime > timeout) {
833
+ throw e;
834
+ }
835
+ }
836
+ await new Promise((resolve) => setTimeout(resolve, 3000));
837
+ }
838
+ }
839
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
840
+ const startTime = Date.now();
841
+ let timeout = 30000;
842
+ if (options && options.timeout) {
843
+ timeout = options.timeout;
844
+ }
845
+ while (true) {
846
+ try {
847
+ const result = await locate_element(this.context, elementDescription, "fill", value);
848
+ if (result?.elementNumber >= 0) {
849
+ const selectors = {
850
+ frame: result?.frame,
851
+ locators: [
852
+ {
853
+ css: result?.css,
854
+ },
855
+ ],
856
+ };
857
+ await this.clickType(selectors, value, false, _params, options, world);
858
+ return;
859
+ }
860
+ }
861
+ catch (e) {
862
+ if (performance.now() - startTime > timeout) {
863
+ throw e;
864
+ }
865
+ }
866
+ await new Promise((resolve) => setTimeout(resolve, 3000));
867
+ }
868
+ }
869
+ async click(selectors, _params, options = {}, world = null) {
870
+ const state = {
871
+ selectors,
872
+ _params,
873
+ options,
874
+ world,
875
+ text: "Click element",
876
+ type: Types.CLICK,
877
+ operation: "click",
878
+ log: "***** click on " + selectors.element_name + " *****\n",
879
+ };
701
880
  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));
881
+ await _preCommand(state, this);
882
+ if (state.options && state.options.context) {
883
+ state.selectors.locators[0].text = state.options.context;
884
+ }
705
885
  try {
706
- await this._highlightElements(element);
707
- await element.click();
886
+ await state.element.click();
708
887
  await new Promise((resolve) => setTimeout(resolve, 1000));
709
888
  }
710
889
  catch (e) {
711
890
  // 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");
891
+ state.element = await this._locate(selectors, state.info, _params);
892
+ await state.element.dispatchEvent("click");
715
893
  await new Promise((resolve) => setTimeout(resolve, 1000));
716
894
  }
717
895
  await this.waitForPageLoad();
718
- return info;
896
+ return state.info;
719
897
  }
720
898
  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;
899
+ await _commandError(state, e, this);
727
900
  }
728
901
  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
- });
902
+ _commandFinally(state, this);
749
903
  }
750
904
  }
751
905
  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;
906
+ const state = {
907
+ selectors,
908
+ _params,
909
+ options,
910
+ world,
911
+ type: checked ? Types.CHECK : Types.UNCHECK,
912
+ text: checked ? `Check element` : `Uncheck element`,
913
+ operation: "setCheck",
914
+ log: "***** check " + selectors.element_name + " *****\n",
915
+ };
762
916
  try {
763
- let element = await this._locate(selectors, info, _params);
764
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
917
+ await _preCommand(state, this);
918
+ state.info.checked = checked;
919
+ // let element = await this._locate(selectors, info, _params);
920
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
765
921
  try {
766
- await this._highlightElements(element);
767
- await element.setChecked(checked);
922
+ // await this._highlightElements(element);
923
+ await state.element.setChecked(checked);
768
924
  await new Promise((resolve) => setTimeout(resolve, 1000));
769
925
  }
770
926
  catch (e) {
@@ -773,179 +929,108 @@ class StableBrowser {
773
929
  }
774
930
  else {
775
931
  //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 });
932
+ state.info.log += "setCheck failed, will try again" + "\n";
933
+ state.element = await this._locate(selectors, state.info, _params);
934
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
779
935
  await new Promise((resolve) => setTimeout(resolve, 1000));
780
936
  }
781
937
  }
782
938
  await this.waitForPageLoad();
783
- return info;
939
+ return state.info;
784
940
  }
785
941
  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;
942
+ await _commandError(state, e, this);
792
943
  }
793
944
  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
- });
945
+ _commandFinally(state, this);
814
946
  }
815
947
  }
816
948
  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;
949
+ const state = {
950
+ selectors,
951
+ _params,
952
+ options,
953
+ world,
954
+ type: Types.HOVER,
955
+ text: `Hover element`,
956
+ operation: "hover",
957
+ log: "***** hover " + selectors.element_name + " *****\n",
958
+ };
826
959
  try {
827
- let element = await this._locate(selectors, info, _params);
828
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
960
+ await _preCommand(state, this);
829
961
  try {
830
- await this._highlightElements(element);
831
- await element.hover();
962
+ await state.element.hover();
832
963
  await new Promise((resolve) => setTimeout(resolve, 1000));
833
964
  }
834
965
  catch (e) {
835
966
  //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 });
967
+ state.info.log += "hover failed, will try again" + "\n";
968
+ state.element = await this._locate(selectors, state.info, _params);
969
+ await state.element.hover({ timeout: 10000 });
839
970
  await new Promise((resolve) => setTimeout(resolve, 1000));
840
971
  }
841
972
  await this.waitForPageLoad();
842
- return info;
973
+ return state.info;
843
974
  }
844
975
  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;
976
+ await _commandError(state, e, this);
851
977
  }
852
978
  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
- });
979
+ _commandFinally(state, this);
873
980
  }
874
981
  }
875
982
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
876
- this._validateSelectors(selectors);
877
983
  if (!values) {
878
984
  throw new Error("values is null");
879
985
  }
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;
986
+ const state = {
987
+ selectors,
988
+ _params,
989
+ options,
990
+ world,
991
+ value: values.toString(),
992
+ type: Types.SELECT,
993
+ text: `Select option: ${values}`,
994
+ operation: "selectOption",
995
+ log: "***** select option " + selectors.element_name + " *****\n",
996
+ };
888
997
  try {
889
- let element = await this._locate(selectors, info, _params);
890
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
998
+ await _preCommand(state, this);
891
999
  try {
892
- await this._highlightElements(element);
893
- await element.selectOption(values);
1000
+ await state.element.selectOption(values);
894
1001
  }
895
1002
  catch (e) {
896
1003
  //await this.closeUnexpectedPopups();
897
- info.log += "selectOption failed, will try force" + "\n";
898
- await element.selectOption(values, { timeout: 10000, force: true });
1004
+ state.info.log += "selectOption failed, will try force" + "\n";
1005
+ await state.element.selectOption(values, { timeout: 10000, force: true });
899
1006
  }
900
1007
  await this.waitForPageLoad();
901
- return info;
1008
+ return state.info;
902
1009
  }
903
1010
  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;
1011
+ await _commandError(state, e, this);
911
1012
  }
912
1013
  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
- });
1014
+ _commandFinally(state, this);
934
1015
  }
935
1016
  }
936
1017
  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;
1018
+ const state = {
1019
+ value: _value,
1020
+ _params,
1021
+ options,
1022
+ world,
1023
+ locate: false,
1024
+ scroll: false,
1025
+ highlight: false,
1026
+ type: Types.TYPE_PRESS,
1027
+ text: `Type value: ${_value}`,
1028
+ operation: "type",
1029
+ log: "",
1030
+ };
946
1031
  try {
947
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
948
- const valueSegment = _value.split("&&");
1032
+ await _preCommand(state, this);
1033
+ const valueSegment = state.value.split("&&");
949
1034
  for (let i = 0; i < valueSegment.length; i++) {
950
1035
  if (i > 0) {
951
1036
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -965,108 +1050,53 @@ class StableBrowser {
965
1050
  await this.page.keyboard.type(value);
966
1051
  }
967
1052
  }
968
- return info;
1053
+ return state.info;
969
1054
  }
970
1055
  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;
1056
+ await _commandError(state, e, this);
978
1057
  }
979
1058
  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
- });
1059
+ _commandFinally(state, this);
1000
1060
  }
1001
1061
  }
1002
1062
  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;
1063
+ const state = {
1064
+ selectors,
1065
+ _params,
1066
+ value,
1067
+ options,
1068
+ world,
1069
+ type: Types.SET_INPUT,
1070
+ text: `Set input value`,
1071
+ operation: "setInputValue",
1072
+ log: "***** set input value " + selectors.element_name + " *****\n",
1073
+ };
1015
1074
  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);
1075
+ await _preCommand(state, this);
1076
+ let value = await this._replaceWithLocalData(state.value, this);
1021
1077
  try {
1022
- await element.evaluateHandle((el, value) => {
1078
+ await state.element.evaluateHandle((el, value) => {
1023
1079
  el.value = value;
1024
1080
  }, value);
1025
1081
  }
1026
1082
  catch (error) {
1027
1083
  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) => {
1084
+ await _screenshot(state, this);
1085
+ Object.assign(error, { info: state.info });
1086
+ await state.element.evaluateHandle((el, value) => {
1032
1087
  el.value = value;
1033
1088
  });
1034
1089
  }
1035
1090
  }
1036
1091
  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;
1092
+ await _commandError(state, e, this);
1043
1093
  }
1044
1094
  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
- });
1095
+ _commandFinally(state, this);
1066
1096
  }
1067
1097
  }
1068
1098
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1069
- this._validateSelectors(selectors);
1099
+ _validateSelectors(selectors);
1070
1100
  const startTime = Date.now();
1071
1101
  let error = null;
1072
1102
  let screenshotId = null;
@@ -1159,32 +1189,32 @@ class StableBrowser {
1159
1189
  }
1160
1190
  }
1161
1191
  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;
1192
+ _value = unEscapeString(_value);
1171
1193
  const newValue = await this._replaceWithLocalData(_value, world);
1194
+ const state = {
1195
+ selectors,
1196
+ _params,
1197
+ value: newValue,
1198
+ originalValue: _value,
1199
+ options,
1200
+ world,
1201
+ type: Types.FILL,
1202
+ text: `Click type input with value: ${_value}`,
1203
+ operation: "clickType",
1204
+ log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
1205
+ };
1172
1206
  if (newValue !== _value) {
1173
1207
  //this.logger.info(_value + "=" + newValue);
1174
1208
  _value = newValue;
1175
1209
  }
1176
- info.value = _value;
1177
1210
  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);
1211
+ await _preCommand(state, this);
1212
+ state.info.value = _value;
1183
1213
  if (options === null || options === undefined || !options.press) {
1184
1214
  try {
1185
- let currentValue = await element.inputValue();
1215
+ let currentValue = await state.element.inputValue();
1186
1216
  if (currentValue) {
1187
- await element.fill("");
1217
+ await state.element.fill("");
1188
1218
  }
1189
1219
  }
1190
1220
  catch (e) {
@@ -1193,22 +1223,22 @@ class StableBrowser {
1193
1223
  }
1194
1224
  if (options === null || options === undefined || options.press) {
1195
1225
  try {
1196
- await element.click({ timeout: 5000 });
1226
+ await state.element.click({ timeout: 5000 });
1197
1227
  }
1198
1228
  catch (e) {
1199
- await element.dispatchEvent("click");
1229
+ await state.element.dispatchEvent("click");
1200
1230
  }
1201
1231
  }
1202
1232
  else {
1203
1233
  try {
1204
- await element.focus();
1234
+ await state.element.focus();
1205
1235
  }
1206
1236
  catch (e) {
1207
- await element.dispatchEvent("focus");
1237
+ await state.element.dispatchEvent("focus");
1208
1238
  }
1209
1239
  }
1210
1240
  await new Promise((resolve) => setTimeout(resolve, 500));
1211
- const valueSegment = _value.split("&&");
1241
+ const valueSegment = state.value.split("&&");
1212
1242
  for (let i = 0; i < valueSegment.length; i++) {
1213
1243
  if (i > 0) {
1214
1244
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1228,13 +1258,14 @@ class StableBrowser {
1228
1258
  await new Promise((resolve) => setTimeout(resolve, 500));
1229
1259
  }
1230
1260
  }
1261
+ await _screenshot(state, this);
1231
1262
  if (enter === true) {
1232
1263
  await new Promise((resolve) => setTimeout(resolve, 2000));
1233
1264
  await this.page.keyboard.press("Enter");
1234
1265
  await this.waitForPageLoad();
1235
1266
  }
1236
1267
  else if (enter === false) {
1237
- await element.dispatchEvent("change");
1268
+ await state.element.dispatchEvent("change");
1238
1269
  //await this.page.keyboard.press("Tab");
1239
1270
  }
1240
1271
  else {
@@ -1243,103 +1274,50 @@ class StableBrowser {
1243
1274
  await this.waitForPageLoad();
1244
1275
  }
1245
1276
  }
1246
- return info;
1277
+ return state.info;
1247
1278
  }
1248
1279
  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;
1280
+ await _commandError(state, e, this);
1256
1281
  }
1257
1282
  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
- });
1283
+ _commandFinally(state, this);
1279
1284
  }
1280
1285
  }
1281
1286
  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;
1287
+ const state = {
1288
+ selectors,
1289
+ _params,
1290
+ value: unEscapeString(value),
1291
+ options,
1292
+ world,
1293
+ type: Types.FILL,
1294
+ text: `Fill input with value: ${value}`,
1295
+ operation: "fill",
1296
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1297
+ };
1292
1298
  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");
1299
+ await _preCommand(state, this);
1300
+ await state.element.fill(value);
1301
+ await state.element.dispatchEvent("change");
1298
1302
  if (enter) {
1299
1303
  await new Promise((resolve) => setTimeout(resolve, 2000));
1300
1304
  await this.page.keyboard.press("Enter");
1301
1305
  }
1302
1306
  await this.waitForPageLoad();
1303
- return info;
1307
+ return state.info;
1304
1308
  }
1305
1309
  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;
1310
+ await _commandError(state, e, this);
1313
1311
  }
1314
1312
  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
- });
1313
+ _commandFinally(state, this);
1336
1314
  }
1337
1315
  }
1338
1316
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1339
1317
  return await this._getText(selectors, 0, _params, options, info, world);
1340
1318
  }
1341
1319
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1342
- this._validateSelectors(selectors);
1320
+ _validateSelectors(selectors);
1343
1321
  let screenshotId = null;
1344
1322
  let screenshotPath = null;
1345
1323
  if (!info.log) {
@@ -1383,165 +1361,124 @@ class StableBrowser {
1383
1361
  }
1384
1362
  }
1385
1363
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1386
- var _a;
1387
- this._validateSelectors(selectors);
1388
1364
  if (!pattern) {
1389
1365
  throw new Error("pattern is null");
1390
1366
  }
1391
1367
  if (!text) {
1392
1368
  throw new Error("text is null");
1393
1369
  }
1370
+ const state = {
1371
+ selectors,
1372
+ _params,
1373
+ pattern,
1374
+ value: pattern,
1375
+ options,
1376
+ world,
1377
+ locate: false,
1378
+ scroll: false,
1379
+ screenshot: false,
1380
+ highlight: false,
1381
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1382
+ text: `Verify element contains pattern: ${pattern}`,
1383
+ operation: "containsPattern",
1384
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1385
+ };
1394
1386
  const newValue = await this._replaceWithLocalData(text, world);
1395
1387
  if (newValue !== text) {
1396
1388
  this.logger.info(text + "=" + newValue);
1397
1389
  text = newValue;
1398
1390
  }
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
1391
  let foundObj = null;
1411
1392
  try {
1412
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1393
+ await _preCommand(state, this);
1394
+ state.info.pattern = pattern;
1395
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1413
1396
  if (foundObj && foundObj.element) {
1414
- await this.scrollIfNeeded(foundObj.element, info);
1397
+ await this.scrollIfNeeded(foundObj.element, state.info);
1415
1398
  }
1416
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1399
+ await _screenshot(state, this);
1417
1400
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1418
1401
  pattern = pattern.replace("{text}", escapedText);
1419
1402
  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;
1403
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1404
+ state.info.foundText = foundObj?.text;
1422
1405
  throw new Error("element doesn't contain text " + text);
1423
1406
  }
1424
- return info;
1407
+ return state.info;
1425
1408
  }
1426
1409
  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;
1410
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1411
+ await _commandError(state, e, this);
1435
1412
  }
1436
1413
  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
- });
1414
+ _commandFinally(state, this);
1458
1415
  }
1459
1416
  }
1460
1417
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1461
- var _a, _b, _c;
1462
- this._validateSelectors(selectors);
1418
+ const state = {
1419
+ selectors,
1420
+ _params,
1421
+ value: text,
1422
+ options,
1423
+ world,
1424
+ locate: false,
1425
+ scroll: false,
1426
+ screenshot: false,
1427
+ highlight: false,
1428
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1429
+ text: `Verify element contains text: ${text}`,
1430
+ operation: "containsText",
1431
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1432
+ };
1463
1433
  if (!text) {
1464
1434
  throw new Error("text is null");
1465
1435
  }
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;
1436
+ text = unEscapeString(text);
1474
1437
  const newValue = await this._replaceWithLocalData(text, world);
1475
1438
  if (newValue !== text) {
1476
1439
  this.logger.info(text + "=" + newValue);
1477
1440
  text = newValue;
1478
1441
  }
1479
- info.value = text;
1480
1442
  let foundObj = null;
1481
1443
  try {
1482
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1444
+ await _preCommand(state, this);
1445
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1483
1446
  if (foundObj && foundObj.element) {
1484
- await this.scrollIfNeeded(foundObj.element, info);
1447
+ await this.scrollIfNeeded(foundObj.element, state.info);
1485
1448
  }
1486
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1449
+ await _screenshot(state, this);
1487
1450
  const dateAlternatives = findDateAlternatives(text);
1488
1451
  const numberAlternatives = findNumberAlternatives(text);
1489
1452
  if (dateAlternatives.date) {
1490
1453
  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;
1454
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1455
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1456
+ return state.info;
1494
1457
  }
1495
1458
  }
1496
1459
  throw new Error("element doesn't contain text " + text);
1497
1460
  }
1498
1461
  else if (numberAlternatives.number) {
1499
1462
  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;
1463
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1464
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1465
+ return state.info;
1503
1466
  }
1504
1467
  }
1505
1468
  throw new Error("element doesn't contain text " + text);
1506
1469
  }
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;
1470
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1471
+ state.info.foundText = foundObj?.text;
1472
+ state.info.value = foundObj?.value;
1510
1473
  throw new Error("element doesn't contain text " + text);
1511
1474
  }
1512
- return info;
1475
+ return state.info;
1513
1476
  }
1514
1477
  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;
1478
+ await _commandError(state, e, this);
1522
1479
  }
1523
1480
  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
- });
1481
+ _commandFinally(state, this);
1545
1482
  }
1546
1483
  }
1547
1484
  _getDataFile(world = null) {
@@ -1770,7 +1707,6 @@ class StableBrowser {
1770
1707
  }
1771
1708
  async takeScreenshot(screenshotPath) {
1772
1709
  const playContext = this.context.playContext;
1773
- const client = await playContext.newCDPSession(this.page);
1774
1710
  // Using CDP to capture the screenshot
1775
1711
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1776
1712
  document.body.scrollWidth,
@@ -1780,97 +1716,67 @@ class StableBrowser {
1780
1716
  document.body.clientWidth,
1781
1717
  document.documentElement.clientWidth,
1782
1718
  ])));
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();
1719
+ let screenshotBuffer = null;
1720
+ if (this.context.browserName === "chromium") {
1721
+ const client = await playContext.newCDPSession(this.page);
1722
+ const { data } = await client.send("Page.captureScreenshot", {
1723
+ format: "png",
1724
+ // clip: {
1725
+ // x: 0,
1726
+ // y: 0,
1727
+ // width: viewportWidth,
1728
+ // height: viewportHeight,
1729
+ // scale: 1,
1730
+ // },
1731
+ });
1732
+ await client.detach();
1733
+ if (!screenshotPath) {
1734
+ return data;
1735
+ }
1736
+ screenshotBuffer = Buffer.from(data, "base64");
1737
+ }
1738
+ else {
1739
+ screenshotBuffer = await this.page.screenshot();
1740
+ }
1741
+ let image = await Jimp.read(screenshotBuffer);
1742
+ // Get the image dimensions
1743
+ const { width, height } = image.bitmap;
1744
+ const resizeRatio = viewportWidth / width;
1745
+ // Resize the image to fit within the viewport dimensions without enlarging
1746
+ if (width > viewportWidth) {
1747
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1748
+ await image.write(screenshotPath);
1749
+ }
1750
+ else {
1751
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1752
+ }
1818
1753
  }
1819
1754
  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;
1755
+ const state = {
1756
+ selectors,
1757
+ _params,
1758
+ options,
1759
+ world,
1760
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1761
+ text: `Verify element exists in page`,
1762
+ operation: "verifyElementExistInPage",
1763
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1764
+ };
1825
1765
  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
1766
  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;
1767
+ await _preCommand(state, this);
1768
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1769
+ return state.info;
1839
1770
  }
1840
1771
  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;
1772
+ await _commandError(state, e, this);
1848
1773
  }
1849
1774
  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
- });
1775
+ _commandFinally(state, this);
1870
1776
  }
1871
1777
  }
1872
1778
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1873
- this._validateSelectors(selectors);
1779
+ _validateSelectors(selectors);
1874
1780
  const startTime = Date.now();
1875
1781
  let error = null;
1876
1782
  let screenshotId = null;
@@ -1929,7 +1835,7 @@ class StableBrowser {
1929
1835
  status: "FAILED",
1930
1836
  startTime,
1931
1837
  endTime,
1932
- message: error === null || error === void 0 ? void 0 : error.message,
1838
+ message: error?.message,
1933
1839
  }
1934
1840
  : {
1935
1841
  status: "PASSED",
@@ -2138,7 +2044,7 @@ class StableBrowser {
2138
2044
  status: "FAILED",
2139
2045
  startTime,
2140
2046
  endTime,
2141
- message: error === null || error === void 0 ? void 0 : error.message,
2047
+ message: error?.message,
2142
2048
  }
2143
2049
  : {
2144
2050
  status: "PASSED",
@@ -2174,20 +2080,20 @@ class StableBrowser {
2174
2080
  for (let i = 0; i < frames.length; i++) {
2175
2081
  if (dateAlternatives.date) {
2176
2082
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2177
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2083
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
2178
2084
  result.frame = frames[i];
2179
2085
  results.push(result);
2180
2086
  }
2181
2087
  }
2182
2088
  else if (numberAlternatives.number) {
2183
2089
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2184
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2090
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
2185
2091
  result.frame = frames[i];
2186
2092
  results.push(result);
2187
2093
  }
2188
2094
  }
2189
2095
  else {
2190
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2096
+ const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
2191
2097
  result.frame = frames[i];
2192
2098
  results.push(result);
2193
2099
  }
@@ -2236,7 +2142,7 @@ class StableBrowser {
2236
2142
  status: "FAILED",
2237
2143
  startTime,
2238
2144
  endTime,
2239
- message: error === null || error === void 0 ? void 0 : error.message,
2145
+ message: error?.message,
2240
2146
  }
2241
2147
  : {
2242
2148
  status: "PASSED",
@@ -2320,7 +2226,7 @@ class StableBrowser {
2320
2226
  status: "FAILED",
2321
2227
  startTime,
2322
2228
  endTime,
2323
- message: error === null || error === void 0 ? void 0 : error.message,
2229
+ message: error?.message,
2324
2230
  }
2325
2231
  : {
2326
2232
  status: "PASSED",
@@ -2352,7 +2258,7 @@ class StableBrowser {
2352
2258
  this.logger.info("Table data verified");
2353
2259
  }
2354
2260
  async getTableData(selectors, _params = null, options = {}, world = null) {
2355
- this._validateSelectors(selectors);
2261
+ _validateSelectors(selectors);
2356
2262
  const startTime = Date.now();
2357
2263
  let error = null;
2358
2264
  let screenshotId = null;
@@ -2388,7 +2294,7 @@ class StableBrowser {
2388
2294
  status: "FAILED",
2389
2295
  startTime,
2390
2296
  endTime,
2391
- message: error === null || error === void 0 ? void 0 : error.message,
2297
+ message: error?.message,
2392
2298
  }
2393
2299
  : {
2394
2300
  status: "PASSED",
@@ -2400,7 +2306,7 @@ class StableBrowser {
2400
2306
  }
2401
2307
  }
2402
2308
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2403
- this._validateSelectors(selectors);
2309
+ _validateSelectors(selectors);
2404
2310
  if (!query) {
2405
2311
  throw new Error("query is null");
2406
2312
  }
@@ -2553,7 +2459,7 @@ class StableBrowser {
2553
2459
  status: "FAILED",
2554
2460
  startTime,
2555
2461
  endTime,
2556
- message: error === null || error === void 0 ? void 0 : error.message,
2462
+ message: error?.message,
2557
2463
  }
2558
2464
  : {
2559
2465
  status: "PASSED",
@@ -2565,27 +2471,7 @@ class StableBrowser {
2565
2471
  }
2566
2472
  }
2567
2473
  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;
2474
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2589
2475
  }
2590
2476
  _getLoadTimeout(options) {
2591
2477
  let timeout = 15000;
@@ -2645,7 +2531,7 @@ class StableBrowser {
2645
2531
  status: "FAILED",
2646
2532
  startTime,
2647
2533
  endTime,
2648
- message: error === null || error === void 0 ? void 0 : error.message,
2534
+ message: error?.message,
2649
2535
  }
2650
2536
  : {
2651
2537
  status: "PASSED",
@@ -2680,7 +2566,7 @@ class StableBrowser {
2680
2566
  status: "FAILED",
2681
2567
  startTime,
2682
2568
  endTime,
2683
- message: error === null || error === void 0 ? void 0 : error.message,
2569
+ message: error?.message,
2684
2570
  }
2685
2571
  : {
2686
2572
  status: "PASSED",
@@ -2722,7 +2608,7 @@ class StableBrowser {
2722
2608
  status: "FAILED",
2723
2609
  startTime,
2724
2610
  endTime,
2725
- message: error === null || error === void 0 ? void 0 : error.message,
2611
+ message: error?.message,
2726
2612
  }
2727
2613
  : {
2728
2614
  status: "PASSED",
@@ -2758,7 +2644,7 @@ class StableBrowser {
2758
2644
  status: "FAILED",
2759
2645
  startTime,
2760
2646
  endTime,
2761
- message: error === null || error === void 0 ? void 0 : error.message,
2647
+ message: error?.message,
2762
2648
  }
2763
2649
  : {
2764
2650
  status: "PASSED",
@@ -2943,5 +2829,10 @@ const KEYBOARD_EVENTS = [
2943
2829
  "TVAntennaCable",
2944
2830
  "TVAudioDescription",
2945
2831
  ];
2832
+ function unEscapeString(str) {
2833
+ const placeholder = "__NEWLINE__";
2834
+ str = str.replace(new RegExp(placeholder, "g"), "\n");
2835
+ return str;
2836
+ }
2946
2837
  export { StableBrowser };
2947
2838
  //# sourceMappingURL=stable_browser.js.map