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