automation_model 1.0.424-dev → 1.0.424-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,9 +2,9 @@
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";
@@ -14,6 +14,10 @@ import objectPath from "object-path";
14
14
  import { decrypt } from "./utils.js";
15
15
  import csv from "csv-parser";
16
16
  import { Readable } from "node:stream";
17
+ import readline from "readline";
18
+ import { getContext } from "./init_browser.js";
19
+ import { locate_element } from "./locate_element.js";
20
+ import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot } from "./command_common.js";
17
21
  const Types = {
18
22
  CLICK: "click_element",
19
23
  NAVIGATE: "navigate",
@@ -41,15 +45,19 @@ const Types = {
41
45
  LOAD_DATA: "load_data",
42
46
  SET_INPUT: "set_input",
43
47
  };
48
+ export const apps = {};
44
49
  class StableBrowser {
45
- constructor(browser, page, logger = null, context = null) {
50
+ constructor(browser, page, logger = null, context = null, world = null) {
46
51
  this.browser = browser;
47
52
  this.page = page;
48
53
  this.logger = logger;
49
54
  this.context = context;
55
+ this.world = world;
50
56
  this.project_path = null;
51
57
  this.webLogFile = null;
58
+ this.networkLogger = null;
52
59
  this.configuration = null;
60
+ this.appName = "main";
53
61
  if (!this.logger) {
54
62
  this.logger = console;
55
63
  }
@@ -75,11 +83,17 @@ class StableBrowser {
75
83
  this.logger.error("unable to read ai_config.json");
76
84
  }
77
85
  const logFolder = path.join(this.project_path, "logs", "web");
78
- this.webLogFile = this.getWebLogFile(logFolder);
79
- this.registerConsoleLogListener(page, context, this.webLogFile);
80
- this.registerRequestListener();
86
+ this.world = world;
81
87
  context.pages = [this.page];
82
88
  context.pageLoading = { status: false };
89
+ this.registerEventListeners(this.context);
90
+ }
91
+ registerEventListeners(context) {
92
+ this.registerConsoleLogListener(this.page, context);
93
+ this.registerRequestListener(this.page, context, this.webLogFile);
94
+ if (!context.pageLoading) {
95
+ context.pageLoading = { status: false };
96
+ }
83
97
  context.playContext.on("page", async function (page) {
84
98
  context.pageLoading.status = true;
85
99
  this.page = page;
@@ -109,6 +123,36 @@ class StableBrowser {
109
123
  context.pageLoading.status = false;
110
124
  }.bind(this));
111
125
  }
126
+ async switchApp(appName) {
127
+ // check if the current app (this.appName) is the same as the new app
128
+ if (this.appName === appName) {
129
+ return;
130
+ }
131
+ let navigate = false;
132
+ if (!apps[appName]) {
133
+ let newContext = await getContext(null, false, this.logger, appName, false, this);
134
+ navigate = true;
135
+ apps[appName] = {
136
+ context: newContext,
137
+ browser: newContext.browser,
138
+ page: newContext.page,
139
+ };
140
+ }
141
+ const tempContext = {};
142
+ this._copyContext(this, tempContext);
143
+ this._copyContext(apps[appName], this);
144
+ apps[this.appName] = tempContext;
145
+ this.appName = appName;
146
+ if (navigate) {
147
+ await this.goto(this.context.environment.baseUrl);
148
+ await this.waitForPageLoad();
149
+ }
150
+ }
151
+ _copyContext(from, to) {
152
+ to.browser = from.browser;
153
+ to.page = from.page;
154
+ to.context = from.context;
155
+ }
112
156
  getWebLogFile(logFolder) {
113
157
  if (!fs.existsSync(logFolder)) {
114
158
  fs.mkdirSync(logFolder, { recursive: true });
@@ -120,37 +164,65 @@ class StableBrowser {
120
164
  const fileName = nextIndex + ".json";
121
165
  return path.join(logFolder, fileName);
122
166
  }
123
- registerConsoleLogListener(page, context, logFile) {
167
+ registerConsoleLogListener(page, context) {
124
168
  if (!this.context.webLogger) {
125
169
  this.context.webLogger = [];
126
170
  }
127
171
  page.on("console", async (msg) => {
128
- this.context.webLogger.push({
172
+ var _a;
173
+ const obj = {
129
174
  type: msg.type(),
130
175
  text: msg.text(),
131
176
  location: msg.location(),
132
177
  time: new Date().toISOString(),
133
- });
134
- await fs.promises.writeFile(logFile, JSON.stringify(this.context.webLogger, null, 2));
178
+ };
179
+ this.context.webLogger.push(obj);
180
+ if (msg.type() === "error") {
181
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
182
+ }
135
183
  });
136
184
  }
137
- registerRequestListener() {
138
- this.page.on("request", async (data) => {
185
+ registerRequestListener(page, context, logFile) {
186
+ if (!this.context.networkLogger) {
187
+ this.context.networkLogger = [];
188
+ }
189
+ page.on("request", async (data) => {
190
+ var _a;
191
+ const startTime = new Date().getTime();
139
192
  try {
140
- const pageUrl = new URL(this.page.url());
193
+ const pageUrl = new URL(page.url());
141
194
  const requestUrl = new URL(data.url());
142
195
  if (pageUrl.hostname === requestUrl.hostname) {
143
196
  const method = data.method();
144
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
197
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
145
198
  const token = await data.headerValue("Authorization");
146
199
  if (token) {
147
- this.context.authtoken = token;
200
+ context.authtoken = token;
148
201
  }
149
202
  }
150
203
  }
204
+ const response = await data.response();
205
+ const endTime = new Date().getTime();
206
+ const obj = {
207
+ url: data.url(),
208
+ method: data.method(),
209
+ postData: data.postData(),
210
+ error: data.failure() ? data.failure().errorText : null,
211
+ duration: endTime - startTime,
212
+ startTime,
213
+ };
214
+ context.networkLogger.push(obj);
215
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
151
216
  }
152
217
  catch (error) {
153
218
  console.error("Error in request listener", error);
219
+ context.networkLogger.push({
220
+ error: "not able to listen",
221
+ message: error.message,
222
+ stack: error.stack,
223
+ time: new Date().toISOString(),
224
+ });
225
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
154
226
  }
155
227
  });
156
228
  }
@@ -165,20 +237,6 @@ class StableBrowser {
165
237
  timeout: 60000,
166
238
  });
167
239
  }
168
- _validateSelectors(selectors) {
169
- if (!selectors) {
170
- throw new Error("selectors is null");
171
- }
172
- if (!selectors.locators) {
173
- throw new Error("selectors.locators is null");
174
- }
175
- if (!Array.isArray(selectors.locators)) {
176
- throw new Error("selectors.locators expected to be array");
177
- }
178
- if (selectors.locators.length === 0) {
179
- throw new Error("selectors.locators expected to be non empty array");
180
- }
181
- }
182
240
  _fixUsingParams(text, _params) {
183
241
  if (!_params || typeof text !== "string") {
184
242
  return text;
@@ -267,7 +325,10 @@ class StableBrowser {
267
325
  return locatorReturn;
268
326
  }
269
327
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
270
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
328
+ if (css && css.locator) {
329
+ css = css.locator;
330
+ }
331
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, false, _params);
271
332
  if (result.elementCount === 0) {
272
333
  return;
273
334
  }
@@ -282,7 +343,7 @@ class StableBrowser {
282
343
  }
283
344
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
284
345
  //const stringifyText = JSON.stringify(text);
285
- return await scope.evaluate(([text, tag, regex, partial]) => {
346
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
286
347
  function isParent(parent, child) {
287
348
  let currentNode = child.parentNode;
288
349
  while (currentNode !== null) {
@@ -294,6 +355,15 @@ class StableBrowser {
294
355
  return false;
295
356
  }
296
357
  document.isParent = isParent;
358
+ function getRegex(str) {
359
+ const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
360
+ if (!match) {
361
+ return null;
362
+ }
363
+ let [_, pattern, flags] = match;
364
+ return new RegExp(pattern, flags);
365
+ }
366
+ document.getRegex = getRegex;
297
367
  function collectAllShadowDomElements(element, result = []) {
298
368
  // Check and add the element if it has a shadow root
299
369
  if (element.shadowRoot) {
@@ -312,6 +382,10 @@ class StableBrowser {
312
382
  if (!tag) {
313
383
  tag = "*";
314
384
  }
385
+ let regexpSearch = document.getRegex(text);
386
+ if (regexpSearch) {
387
+ regex = true;
388
+ }
315
389
  let elements = Array.from(document.querySelectorAll(tag));
316
390
  let shadowHosts = [];
317
391
  document.collectAllShadowDomElements(document, shadowHosts);
@@ -327,7 +401,9 @@ class StableBrowser {
327
401
  let randomToken = null;
328
402
  const foundElements = [];
329
403
  if (regex) {
330
- let regexpSearch = new RegExp(text, "im");
404
+ if (!regexpSearch) {
405
+ regexpSearch = new RegExp(text, "im");
406
+ }
331
407
  for (let i = 0; i < elements.length; i++) {
332
408
  const element = elements[i];
333
409
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -341,8 +417,8 @@ class StableBrowser {
341
417
  for (let i = 0; i < elements.length; i++) {
342
418
  const element = elements[i];
343
419
  if (partial) {
344
- if ((element.innerText && element.innerText.trim().includes(text)) ||
345
- (element.value && element.value.includes(text))) {
420
+ if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
421
+ (element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
346
422
  foundElements.push(element);
347
423
  }
348
424
  }
@@ -387,6 +463,12 @@ class StableBrowser {
387
463
  }
388
464
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
389
465
  let locatorSearch = selectorHierarchy[index];
466
+ try {
467
+ locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
468
+ }
469
+ catch (e) {
470
+ console.error(e);
471
+ }
390
472
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
391
473
  let locator = null;
392
474
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
@@ -475,6 +557,8 @@ class StableBrowser {
475
557
  if (result.foundElements.length > 0) {
476
558
  let dialogCloseLocator = result.foundElements[0].locator;
477
559
  await dialogCloseLocator.click();
560
+ // wait for the dialog to close
561
+ await dialogCloseLocator.waitFor({ state: "hidden" });
478
562
  return { rerun: true };
479
563
  }
480
564
  }
@@ -483,7 +567,7 @@ class StableBrowser {
483
567
  }
484
568
  async _locate(selectors, info, _params, timeout = 30000) {
485
569
  for (let i = 0; i < 3; i++) {
486
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
570
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
487
571
  for (let j = 0; j < selectors.locators.length; j++) {
488
572
  let selector = selectors.locators[j];
489
573
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -502,10 +586,44 @@ class StableBrowser {
502
586
  let locatorsCount = 0;
503
587
  //let arrayMode = Array.isArray(selectors);
504
588
  let scope = this.page;
589
+ // for the simple click usecase
590
+ if (selectors.frame) {
591
+ scope = selectors.frame;
592
+ }
505
593
  if (selectors.iframe_src || selectors.frameLocators) {
594
+ const findFrame = async (frame, framescope) => {
595
+ for (let i = 0; i < frame.selectors.length; i++) {
596
+ let frameLocator = frame.selectors[i];
597
+ if (frameLocator.css) {
598
+ let testframescope = framescope.frameLocator(frameLocator.css);
599
+ if (frameLocator.index) {
600
+ testframescope = framescope.nth(frameLocator.index);
601
+ }
602
+ try {
603
+ await testframescope.owner().evaluateHandle(() => true, null, {
604
+ timeout: 5000,
605
+ });
606
+ framescope = testframescope;
607
+ break;
608
+ }
609
+ catch (error) {
610
+ console.error("frame not found " + frameLocator.css);
611
+ }
612
+ }
613
+ }
614
+ if (frame.children) {
615
+ return await findFrame(frame.children, framescope);
616
+ }
617
+ return framescope;
618
+ };
506
619
  info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
507
620
  while (true) {
508
621
  let frameFound = false;
622
+ if (selectors.nestFrmLoc) {
623
+ scope = await findFrame(selectors.nestFrmLoc, scope);
624
+ frameFound = true;
625
+ break;
626
+ }
509
627
  if (selectors.frameLocators) {
510
628
  for (let i = 0; i < selectors.frameLocators.length; i++) {
511
629
  let frameLocator = selectors.frameLocators[i];
@@ -666,86 +784,121 @@ class StableBrowser {
666
784
  }
667
785
  return result;
668
786
  }
669
- async click(selectors, _params, options = {}, world = null) {
670
- this._validateSelectors(selectors);
787
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
671
788
  const startTime = Date.now();
672
- if (options && options.context) {
673
- selectors.locators[0].text = options.context;
789
+ let timeout = 30000;
790
+ if (options && options.timeout) {
791
+ timeout = options.timeout;
674
792
  }
675
- const info = {};
676
- info.log = "***** click on " + selectors.element_name + " *****\n";
677
- info.operation = "click";
678
- info.selectors = selectors;
679
- let error = null;
680
- let screenshotId = null;
681
- let screenshotPath = null;
793
+ while (true) {
794
+ try {
795
+ const result = await locate_element(this.context, elementDescription, "click");
796
+ if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
797
+ const selectors = {
798
+ frame: result === null || result === void 0 ? void 0 : result.frame,
799
+ locators: [
800
+ {
801
+ css: result === null || result === void 0 ? void 0 : result.css,
802
+ },
803
+ ],
804
+ };
805
+ await this.click(selectors, _params, options, world);
806
+ return;
807
+ }
808
+ }
809
+ catch (e) {
810
+ if (performance.now() - startTime > timeout) {
811
+ throw e;
812
+ }
813
+ }
814
+ await new Promise((resolve) => setTimeout(resolve, 3000));
815
+ }
816
+ }
817
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
818
+ const startTime = Date.now();
819
+ let timeout = 30000;
820
+ if (options && options.timeout) {
821
+ timeout = options.timeout;
822
+ }
823
+ while (true) {
824
+ try {
825
+ const result = await locate_element(this.context, elementDescription, "fill", value);
826
+ if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
827
+ const selectors = {
828
+ frame: result === null || result === void 0 ? void 0 : result.frame,
829
+ locators: [
830
+ {
831
+ css: result === null || result === void 0 ? void 0 : result.css,
832
+ },
833
+ ],
834
+ };
835
+ await this.clickType(selectors, value, false, _params, options, world);
836
+ return;
837
+ }
838
+ }
839
+ catch (e) {
840
+ if (performance.now() - startTime > timeout) {
841
+ throw e;
842
+ }
843
+ }
844
+ await new Promise((resolve) => setTimeout(resolve, 3000));
845
+ }
846
+ }
847
+ async click(selectors, _params, options = {}, world = null) {
848
+ const state = {
849
+ selectors,
850
+ _params,
851
+ options,
852
+ world,
853
+ text: "Click element",
854
+ type: Types.CLICK,
855
+ operation: "click",
856
+ log: "***** click on " + selectors.element_name + " *****\n",
857
+ };
682
858
  try {
683
- let element = await this._locate(selectors, info, _params);
684
- await this.scrollIfNeeded(element, info);
685
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
859
+ await _preCommand(state, this);
860
+ if (state.options && state.options.context) {
861
+ state.selectors.locators[0].text = state.options.context;
862
+ }
686
863
  try {
687
- await this._highlightElements(element);
688
- await element.click();
864
+ await state.element.click();
689
865
  await new Promise((resolve) => setTimeout(resolve, 1000));
690
866
  }
691
867
  catch (e) {
692
868
  // await this.closeUnexpectedPopups();
693
- info.log += "click failed, will try again" + "\n";
694
- element = await this._locate(selectors, info, _params);
695
- await element.click({ timeout: 10000, force: true });
869
+ state.element = await this._locate(selectors, state.info, _params);
870
+ await state.element.dispatchEvent("click");
696
871
  await new Promise((resolve) => setTimeout(resolve, 1000));
697
872
  }
698
873
  await this.waitForPageLoad();
699
- return info;
874
+ return state.info;
700
875
  }
701
876
  catch (e) {
702
- this.logger.error("click failed " + info.log);
703
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
704
- info.screenshotPath = screenshotPath;
705
- Object.assign(e, { info: info });
706
- error = e;
707
- throw e;
877
+ await _commandError(state, e, this);
708
878
  }
709
879
  finally {
710
- const endTime = Date.now();
711
- this._reportToWorld(world, {
712
- element_name: selectors.element_name,
713
- type: Types.CLICK,
714
- text: `Click element`,
715
- screenshotId,
716
- result: error
717
- ? {
718
- status: "FAILED",
719
- startTime,
720
- endTime,
721
- message: error === null || error === void 0 ? void 0 : error.message,
722
- }
723
- : {
724
- status: "PASSED",
725
- startTime,
726
- endTime,
727
- },
728
- info: info,
729
- });
880
+ _commandFinally(state, this);
730
881
  }
731
882
  }
732
883
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
733
- this._validateSelectors(selectors);
734
- const startTime = Date.now();
735
- const info = {};
736
- info.log = "";
737
- info.operation = "setCheck";
738
- info.checked = checked;
739
- info.selectors = selectors;
740
- let error = null;
741
- let screenshotId = null;
742
- let screenshotPath = null;
884
+ const state = {
885
+ selectors,
886
+ _params,
887
+ options,
888
+ world,
889
+ type: checked ? Types.CHECK : Types.UNCHECK,
890
+ text: checked ? `Check element` : `Uncheck element`,
891
+ operation: "setCheck",
892
+ log: "***** check " + selectors.element_name + " *****\n",
893
+ };
743
894
  try {
744
- let element = await this._locate(selectors, info, _params);
745
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
895
+ _preCommand(state, this);
896
+ state.info.checked = checked;
897
+ // let element = await this._locate(selectors, info, _params);
898
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
746
899
  try {
747
- await this._highlightElements(element);
748
- await element.setChecked(checked);
900
+ // await this._highlightElements(element);
901
+ await state.element.setChecked(checked);
749
902
  await new Promise((resolve) => setTimeout(resolve, 1000));
750
903
  }
751
904
  catch (e) {
@@ -755,178 +908,106 @@ class StableBrowser {
755
908
  else {
756
909
  //await this.closeUnexpectedPopups();
757
910
  info.log += "setCheck failed, will try again" + "\n";
758
- element = await this._locate(selectors, info, _params);
759
- await element.setChecked(checked, { timeout: 5000, force: true });
911
+ state.element = await this._locate(selectors, info, _params);
912
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
760
913
  await new Promise((resolve) => setTimeout(resolve, 1000));
761
914
  }
762
915
  }
763
916
  await this.waitForPageLoad();
764
- return info;
917
+ return state.info;
765
918
  }
766
919
  catch (e) {
767
- this.logger.error("setCheck failed " + info.log);
768
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
769
- info.screenshotPath = screenshotPath;
770
- Object.assign(e, { info: info });
771
- error = e;
772
- throw e;
920
+ await _commandError(state, e, this);
773
921
  }
774
922
  finally {
775
- const endTime = Date.now();
776
- this._reportToWorld(world, {
777
- element_name: selectors.element_name,
778
- type: checked ? Types.CHECK : Types.UNCHECK,
779
- text: checked ? `Check element` : `Uncheck element`,
780
- screenshotId,
781
- result: error
782
- ? {
783
- status: "FAILED",
784
- startTime,
785
- endTime,
786
- message: error === null || error === void 0 ? void 0 : error.message,
787
- }
788
- : {
789
- status: "PASSED",
790
- startTime,
791
- endTime,
792
- },
793
- info: info,
794
- });
923
+ _commandFinally(state, this);
795
924
  }
796
925
  }
797
926
  async hover(selectors, _params, options = {}, world = null) {
798
- this._validateSelectors(selectors);
799
- const startTime = Date.now();
800
- const info = {};
801
- info.log = "";
802
- info.operation = "hover";
803
- info.selectors = selectors;
804
- let error = null;
805
- let screenshotId = null;
806
- let screenshotPath = null;
927
+ const state = {
928
+ selectors,
929
+ _params,
930
+ options,
931
+ world,
932
+ type: Types.HOVER,
933
+ text: `Hover element`,
934
+ operation: "hover",
935
+ log: "***** hover " + selectors.element_name + " *****\n",
936
+ };
807
937
  try {
808
- let element = await this._locate(selectors, info, _params);
809
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
938
+ _preCommand(state, this);
810
939
  try {
811
- await this._highlightElements(element);
812
- await element.hover();
940
+ await state.element.hover();
813
941
  await new Promise((resolve) => setTimeout(resolve, 1000));
814
942
  }
815
943
  catch (e) {
816
944
  //await this.closeUnexpectedPopups();
817
- info.log += "hover failed, will try again" + "\n";
818
- element = await this._locate(selectors, info, _params);
819
- await element.hover({ timeout: 10000 });
945
+ state.info.log += "hover failed, will try again" + "\n";
946
+ state.element = await this._locate(selectors, state.info, _params);
947
+ await state.element.hover({ timeout: 10000 });
820
948
  await new Promise((resolve) => setTimeout(resolve, 1000));
821
949
  }
822
950
  await this.waitForPageLoad();
823
- return info;
951
+ return state.info;
824
952
  }
825
953
  catch (e) {
826
- this.logger.error("hover failed " + info.log);
827
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
828
- info.screenshotPath = screenshotPath;
829
- Object.assign(e, { info: info });
830
- error = e;
831
- throw e;
954
+ await _commandError(state, e, this);
832
955
  }
833
956
  finally {
834
- const endTime = Date.now();
835
- this._reportToWorld(world, {
836
- element_name: selectors.element_name,
837
- type: Types.HOVER,
838
- text: `Hover element`,
839
- screenshotId,
840
- result: error
841
- ? {
842
- status: "FAILED",
843
- startTime,
844
- endTime,
845
- message: error === null || error === void 0 ? void 0 : error.message,
846
- }
847
- : {
848
- status: "PASSED",
849
- startTime,
850
- endTime,
851
- },
852
- info: info,
853
- });
957
+ _commandFinally(state, this);
854
958
  }
855
959
  }
856
960
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
857
- this._validateSelectors(selectors);
858
961
  if (!values) {
859
962
  throw new Error("values is null");
860
963
  }
861
- const startTime = Date.now();
862
- let error = null;
863
- let screenshotId = null;
864
- let screenshotPath = null;
865
- const info = {};
866
- info.log = "";
867
- info.operation = "selectOptions";
868
- info.selectors = selectors;
964
+ const state = {
965
+ selectors,
966
+ _params,
967
+ options,
968
+ world,
969
+ type: Types.SELECT,
970
+ text: `Select option: ${values}`,
971
+ operation: "selectOption",
972
+ log: "***** select option " + selectors.element_name + " *****\n",
973
+ };
869
974
  try {
870
- let element = await this._locate(selectors, info, _params);
871
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
975
+ await _preCommand(state, this);
872
976
  try {
873
- await this._highlightElements(element);
874
- await element.selectOption(values);
977
+ await state.element.selectOption(values);
875
978
  }
876
979
  catch (e) {
877
980
  //await this.closeUnexpectedPopups();
878
- info.log += "selectOption failed, will try force" + "\n";
879
- await element.selectOption(values, { timeout: 10000, force: true });
981
+ state.info.log += "selectOption failed, will try force" + "\n";
982
+ await state.element.selectOption(values, { timeout: 10000, force: true });
880
983
  }
881
984
  await this.waitForPageLoad();
882
- return info;
985
+ return state.info;
883
986
  }
884
987
  catch (e) {
885
- this.logger.error("selectOption failed " + info.log);
886
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
887
- info.screenshotPath = screenshotPath;
888
- Object.assign(e, { info: info });
889
- this.logger.info("click failed, will try next selector");
890
- error = e;
891
- throw e;
988
+ await _commandError(state, e, this);
892
989
  }
893
990
  finally {
894
- const endTime = Date.now();
895
- this._reportToWorld(world, {
896
- element_name: selectors.element_name,
897
- type: Types.SELECT,
898
- text: `Select option: ${values}`,
899
- value: values.toString(),
900
- screenshotId,
901
- result: error
902
- ? {
903
- status: "FAILED",
904
- startTime,
905
- endTime,
906
- message: error === null || error === void 0 ? void 0 : error.message,
907
- }
908
- : {
909
- status: "PASSED",
910
- startTime,
911
- endTime,
912
- },
913
- info: info,
914
- });
991
+ _commandFinally(state, this);
915
992
  }
916
993
  }
917
994
  async type(_value, _params = null, options = {}, world = null) {
918
- const startTime = Date.now();
919
- let error = null;
920
- let screenshotId = null;
921
- let screenshotPath = null;
922
- const info = {};
923
- info.log = "";
924
- info.operation = "type";
925
- _value = this._fixUsingParams(_value, _params);
926
- info.value = _value;
995
+ const state = {
996
+ value: _value,
997
+ _params,
998
+ options,
999
+ world,
1000
+ locate: false,
1001
+ scroll: false,
1002
+ highlight: false,
1003
+ type: Types.TYPE_PRESS,
1004
+ text: `Type value: ${_value}`,
1005
+ operation: "type",
1006
+ log: "",
1007
+ };
927
1008
  try {
928
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
929
- const valueSegment = _value.split("&&");
1009
+ await _preCommand(state, this);
1010
+ const valueSegment = state.value.split("&&");
930
1011
  for (let i = 0; i < valueSegment.length; i++) {
931
1012
  if (i > 0) {
932
1013
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -946,108 +1027,53 @@ class StableBrowser {
946
1027
  await this.page.keyboard.type(value);
947
1028
  }
948
1029
  }
949
- return info;
1030
+ return state.info;
950
1031
  }
951
1032
  catch (e) {
952
- //await this.closeUnexpectedPopups();
953
- this.logger.error("type failed " + info.log);
954
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
955
- info.screenshotPath = screenshotPath;
956
- Object.assign(e, { info: info });
957
- error = e;
958
- throw e;
1033
+ await _commandError(state, e, this);
959
1034
  }
960
1035
  finally {
961
- const endTime = Date.now();
962
- this._reportToWorld(world, {
963
- type: Types.TYPE_PRESS,
964
- screenshotId,
965
- value: _value,
966
- text: `type value: ${_value}`,
967
- result: error
968
- ? {
969
- status: "FAILED",
970
- startTime,
971
- endTime,
972
- message: error === null || error === void 0 ? void 0 : error.message,
973
- }
974
- : {
975
- status: "PASSED",
976
- startTime,
977
- endTime,
978
- },
979
- info: info,
980
- });
1036
+ _commandFinally(state, this);
981
1037
  }
982
1038
  }
983
1039
  async setInputValue(selectors, value, _params = null, options = {}, world = null) {
984
- // set input value for non fillable inputs like date, time, range, color, etc.
985
- this._validateSelectors(selectors);
986
- const startTime = Date.now();
987
- const info = {};
988
- info.log = "***** set input value " + selectors.element_name + " *****\n";
989
- info.operation = "setInputValue";
990
- info.selectors = selectors;
991
- value = this._fixUsingParams(value, _params);
992
- info.value = value;
993
- let error = null;
994
- let screenshotId = null;
995
- let screenshotPath = null;
1040
+ const state = {
1041
+ selectors,
1042
+ _params,
1043
+ value,
1044
+ options,
1045
+ world,
1046
+ type: Types.SET_INPUT,
1047
+ text: `Set input value`,
1048
+ operation: "setInputValue",
1049
+ log: "***** set input value " + selectors.element_name + " *****\n",
1050
+ };
996
1051
  try {
997
- value = await this._replaceWithLocalData(value, this);
998
- let element = await this._locate(selectors, info, _params);
999
- await this.scrollIfNeeded(element, info);
1000
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1001
- await this._highlightElements(element);
1052
+ await _preCommand(state, this);
1053
+ let value = await this._replaceWithLocalData(state.value, this);
1002
1054
  try {
1003
- await element.evaluateHandle((el, value) => {
1055
+ await state.element.evaluateHandle((el, value) => {
1004
1056
  el.value = value;
1005
1057
  }, value);
1006
1058
  }
1007
1059
  catch (error) {
1008
1060
  this.logger.error("setInputValue failed, will try again");
1009
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1010
- info.screenshotPath = screenshotPath;
1011
- Object.assign(error, { info: info });
1012
- await element.evaluateHandle((el, value) => {
1061
+ await _screenshot(state, this);
1062
+ Object.assign(error, { info: state.info });
1063
+ await state.element.evaluateHandle((el, value) => {
1013
1064
  el.value = value;
1014
1065
  });
1015
1066
  }
1016
1067
  }
1017
1068
  catch (e) {
1018
- this.logger.error("setInputValue failed " + info.log);
1019
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1020
- info.screenshotPath = screenshotPath;
1021
- Object.assign(e, { info: info });
1022
- error = e;
1023
- throw e;
1069
+ await _commandError(state, e, this);
1024
1070
  }
1025
1071
  finally {
1026
- const endTime = Date.now();
1027
- this._reportToWorld(world, {
1028
- element_name: selectors.element_name,
1029
- type: Types.SET_INPUT,
1030
- text: `Set input value`,
1031
- value: value,
1032
- screenshotId,
1033
- result: error
1034
- ? {
1035
- status: "FAILED",
1036
- startTime,
1037
- endTime,
1038
- message: error === null || error === void 0 ? void 0 : error.message,
1039
- }
1040
- : {
1041
- status: "PASSED",
1042
- startTime,
1043
- endTime,
1044
- },
1045
- info: info,
1046
- });
1072
+ _commandFinally(state, this);
1047
1073
  }
1048
1074
  }
1049
1075
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1050
- this._validateSelectors(selectors);
1076
+ _validateSelectors(selectors);
1051
1077
  const startTime = Date.now();
1052
1078
  let error = null;
1053
1079
  let screenshotId = null;
@@ -1140,30 +1166,28 @@ class StableBrowser {
1140
1166
  }
1141
1167
  }
1142
1168
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1143
- this._validateSelectors(selectors);
1144
- const startTime = Date.now();
1145
- let error = null;
1146
- let screenshotId = null;
1147
- let screenshotPath = null;
1148
- const info = {};
1149
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1150
- info.operation = "clickType";
1151
- info.selectors = selectors;
1152
- const newValue = await this._replaceWithLocalData(_value, world);
1169
+ const state = {
1170
+ selectors,
1171
+ _params,
1172
+ value: unEscapeString(_value),
1173
+ options,
1174
+ world,
1175
+ type: Types.FILL,
1176
+ text: `Click type input with value: ${_value}`,
1177
+ operation: "clickType",
1178
+ log: "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n",
1179
+ };
1180
+ const newValue = await this._replaceWithLocalData(state.value, world);
1153
1181
  if (newValue !== _value) {
1154
1182
  //this.logger.info(_value + "=" + newValue);
1155
1183
  _value = newValue;
1156
1184
  }
1157
- info.value = _value;
1158
1185
  try {
1159
- let element = await this._locate(selectors, info, _params);
1160
- //insert red border around the element
1161
- await this.scrollIfNeeded(element, info);
1162
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1163
- await this._highlightElements(element);
1186
+ await _preCommand(state, this);
1187
+ state.info.value = _value;
1164
1188
  if (options === null || options === undefined || !options.press) {
1165
1189
  try {
1166
- let currentValue = await element.inputValue();
1190
+ let currentValue = await state.element.inputValue();
1167
1191
  if (currentValue) {
1168
1192
  await element.fill("");
1169
1193
  }
@@ -1174,22 +1198,22 @@ class StableBrowser {
1174
1198
  }
1175
1199
  if (options === null || options === undefined || options.press) {
1176
1200
  try {
1177
- await element.click({ timeout: 5000 });
1201
+ await state.element.click({ timeout: 5000 });
1178
1202
  }
1179
1203
  catch (e) {
1180
- await element.dispatchEvent("click");
1204
+ await state.element.dispatchEvent("click");
1181
1205
  }
1182
1206
  }
1183
1207
  else {
1184
1208
  try {
1185
- await element.focus();
1209
+ await state.element.focus();
1186
1210
  }
1187
1211
  catch (e) {
1188
- await element.dispatchEvent("focus");
1212
+ await state.element.dispatchEvent("focus");
1189
1213
  }
1190
1214
  }
1191
1215
  await new Promise((resolve) => setTimeout(resolve, 500));
1192
- const valueSegment = _value.split("&&");
1216
+ const valueSegment = state.value.split("&&");
1193
1217
  for (let i = 0; i < valueSegment.length; i++) {
1194
1218
  if (i > 0) {
1195
1219
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1209,13 +1233,14 @@ class StableBrowser {
1209
1233
  await new Promise((resolve) => setTimeout(resolve, 500));
1210
1234
  }
1211
1235
  }
1236
+ await _screenshot(state, this);
1212
1237
  if (enter === true) {
1213
1238
  await new Promise((resolve) => setTimeout(resolve, 2000));
1214
1239
  await this.page.keyboard.press("Enter");
1215
1240
  await this.waitForPageLoad();
1216
1241
  }
1217
1242
  else if (enter === false) {
1218
- await element.dispatchEvent("change");
1243
+ await state.element.dispatchEvent("change");
1219
1244
  //await this.page.keyboard.press("Tab");
1220
1245
  }
1221
1246
  else {
@@ -1224,103 +1249,50 @@ class StableBrowser {
1224
1249
  await this.waitForPageLoad();
1225
1250
  }
1226
1251
  }
1227
- return info;
1252
+ return state.info;
1228
1253
  }
1229
1254
  catch (e) {
1230
- //await this.closeUnexpectedPopups();
1231
- this.logger.error("fill failed " + JSON.stringify(info));
1232
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1233
- info.screenshotPath = screenshotPath;
1234
- Object.assign(e, { info: info });
1235
- error = e;
1236
- throw e;
1255
+ await _commandError(state, e, this);
1237
1256
  }
1238
1257
  finally {
1239
- const endTime = Date.now();
1240
- this._reportToWorld(world, {
1241
- element_name: selectors.element_name,
1242
- type: Types.FILL,
1243
- screenshotId,
1244
- value: _value,
1245
- text: `clickType input with value: ${_value}`,
1246
- result: error
1247
- ? {
1248
- status: "FAILED",
1249
- startTime,
1250
- endTime,
1251
- message: error === null || error === void 0 ? void 0 : error.message,
1252
- }
1253
- : {
1254
- status: "PASSED",
1255
- startTime,
1256
- endTime,
1257
- },
1258
- info: info,
1259
- });
1258
+ _commandFinally(state, this);
1260
1259
  }
1261
1260
  }
1262
1261
  async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1263
- this._validateSelectors(selectors);
1264
- const startTime = Date.now();
1265
- let error = null;
1266
- let screenshotId = null;
1267
- let screenshotPath = null;
1268
- const info = {};
1269
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1270
- info.operation = "fill";
1271
- info.selectors = selectors;
1272
- info.value = value;
1262
+ const state = {
1263
+ selectors,
1264
+ _params,
1265
+ value: unEscapeString(value),
1266
+ options,
1267
+ world,
1268
+ type: Types.FILL,
1269
+ text: `Fill input with value: ${value}`,
1270
+ operation: "fill",
1271
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1272
+ };
1273
1273
  try {
1274
- let element = await this._locate(selectors, info, _params);
1275
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1276
- await this._highlightElements(element);
1277
- await element.fill(value);
1278
- await element.dispatchEvent("change");
1274
+ await _preCommand(state, this);
1275
+ await state.element.fill(value);
1276
+ await state.element.dispatchEvent("change");
1279
1277
  if (enter) {
1280
1278
  await new Promise((resolve) => setTimeout(resolve, 2000));
1281
1279
  await this.page.keyboard.press("Enter");
1282
1280
  }
1283
1281
  await this.waitForPageLoad();
1284
- return info;
1282
+ return state.info;
1285
1283
  }
1286
1284
  catch (e) {
1287
- //await this.closeUnexpectedPopups();
1288
- this.logger.error("fill failed " + info.log);
1289
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1290
- info.screenshotPath = screenshotPath;
1291
- Object.assign(e, { info: info });
1292
- error = e;
1293
- throw e;
1285
+ await _commandError(state, e, this);
1294
1286
  }
1295
1287
  finally {
1296
- const endTime = Date.now();
1297
- this._reportToWorld(world, {
1298
- element_name: selectors.element_name,
1299
- type: Types.FILL,
1300
- screenshotId,
1301
- value,
1302
- text: `Fill input with value: ${value}`,
1303
- result: error
1304
- ? {
1305
- status: "FAILED",
1306
- startTime,
1307
- endTime,
1308
- message: error === null || error === void 0 ? void 0 : error.message,
1309
- }
1310
- : {
1311
- status: "PASSED",
1312
- startTime,
1313
- endTime,
1314
- },
1315
- info: info,
1316
- });
1288
+ _commandFinally(state, this);
1317
1289
  }
1318
1290
  }
1319
1291
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1320
1292
  return await this._getText(selectors, 0, _params, options, info, world);
1321
1293
  }
1322
1294
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1323
- this._validateSelectors(selectors);
1295
+ _validateSelectors(selectors);
1324
1296
  let screenshotId = null;
1325
1297
  let screenshotPath = null;
1326
1298
  if (!info.log) {
@@ -1365,113 +1337,99 @@ class StableBrowser {
1365
1337
  }
1366
1338
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1367
1339
  var _a;
1368
- this._validateSelectors(selectors);
1369
1340
  if (!pattern) {
1370
1341
  throw new Error("pattern is null");
1371
1342
  }
1372
1343
  if (!text) {
1373
1344
  throw new Error("text is null");
1374
1345
  }
1346
+ const state = {
1347
+ selectors,
1348
+ _params,
1349
+ pattern,
1350
+ value: text,
1351
+ options,
1352
+ world,
1353
+ locate: false,
1354
+ scroll: false,
1355
+ screenshot: false,
1356
+ highlight: false,
1357
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1358
+ text: `Verify element contains pattern: ${pattern}`,
1359
+ operation: "containsPattern",
1360
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1361
+ };
1375
1362
  const newValue = await this._replaceWithLocalData(text, world);
1376
1363
  if (newValue !== text) {
1377
1364
  this.logger.info(text + "=" + newValue);
1378
1365
  text = newValue;
1379
1366
  }
1380
- const startTime = Date.now();
1381
- let error = null;
1382
- let screenshotId = null;
1383
- let screenshotPath = null;
1384
- const info = {};
1385
- info.log =
1386
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1387
- info.operation = "containsPattern";
1388
- info.selectors = selectors;
1389
- info.value = text;
1390
- info.pattern = pattern;
1391
1367
  let foundObj = null;
1392
1368
  try {
1393
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1369
+ await _preCommand(state, this);
1370
+ state.info.pattern = pattern;
1371
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1394
1372
  if (foundObj && foundObj.element) {
1395
- await this.scrollIfNeeded(foundObj.element, info);
1373
+ await this.scrollIfNeeded(foundObj.element, state.info);
1396
1374
  }
1397
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1375
+ await _screenshot(state, this);
1398
1376
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1399
1377
  pattern = pattern.replace("{text}", escapedText);
1400
1378
  let regex = new RegExp(pattern, "im");
1401
1379
  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))) {
1402
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1380
+ state.info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1403
1381
  throw new Error("element doesn't contain text " + text);
1404
1382
  }
1405
- return info;
1383
+ return state.info;
1406
1384
  }
1407
1385
  catch (e) {
1408
- //await this.closeUnexpectedPopups();
1409
- this.logger.error("verify element contains text failed " + info.log);
1410
1386
  this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1411
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1412
- info.screenshotPath = screenshotPath;
1413
- Object.assign(e, { info: info });
1414
- error = e;
1415
- throw e;
1387
+ await _commandError(state, e, this);
1416
1388
  }
1417
1389
  finally {
1418
- const endTime = Date.now();
1419
- this._reportToWorld(world, {
1420
- element_name: selectors.element_name,
1421
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1422
- value: pattern,
1423
- text: `Verify element contains pattern: ${pattern}`,
1424
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1425
- result: error
1426
- ? {
1427
- status: "FAILED",
1428
- startTime,
1429
- endTime,
1430
- message: error === null || error === void 0 ? void 0 : error.message,
1431
- }
1432
- : {
1433
- status: "PASSED",
1434
- startTime,
1435
- endTime,
1436
- },
1437
- info: info,
1438
- });
1390
+ _commandFinally(state, this);
1439
1391
  }
1440
1392
  }
1441
1393
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1442
1394
  var _a, _b, _c;
1443
- this._validateSelectors(selectors);
1395
+ const state = {
1396
+ selectors,
1397
+ _params,
1398
+ value: text,
1399
+ options,
1400
+ world,
1401
+ locate: false,
1402
+ scroll: false,
1403
+ screenshot: false,
1404
+ highlight: false,
1405
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1406
+ text: `Verify element contains text: ${text}`,
1407
+ operation: "containsText",
1408
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1409
+ };
1444
1410
  if (!text) {
1445
1411
  throw new Error("text is null");
1446
1412
  }
1447
- const startTime = Date.now();
1448
- let error = null;
1449
- let screenshotId = null;
1450
- let screenshotPath = null;
1451
- const info = {};
1452
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1453
- info.operation = "containsText";
1454
- info.selectors = selectors;
1413
+ text = unEscapeString(text);
1455
1414
  const newValue = await this._replaceWithLocalData(text, world);
1456
1415
  if (newValue !== text) {
1457
1416
  this.logger.info(text + "=" + newValue);
1458
1417
  text = newValue;
1459
1418
  }
1460
- info.value = text;
1461
1419
  let foundObj = null;
1462
1420
  try {
1463
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1421
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1464
1422
  if (foundObj && foundObj.element) {
1465
- await this.scrollIfNeeded(foundObj.element, info);
1423
+ await this.scrollIfNeeded(foundObj.element, state.info);
1466
1424
  }
1467
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1425
+ await _screenshot(state, this);
1468
1426
  const dateAlternatives = findDateAlternatives(text);
1469
1427
  const numberAlternatives = findNumberAlternatives(text);
1470
1428
  if (dateAlternatives.date) {
1471
1429
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1472
1430
  if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1473
1431
  ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1474
- return info;
1432
+ return state.info;
1475
1433
  }
1476
1434
  }
1477
1435
  throw new Error("element doesn't contain text " + text);
@@ -1480,49 +1438,23 @@ class StableBrowser {
1480
1438
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1481
1439
  if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1482
1440
  ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1483
- return info;
1441
+ return state.info;
1484
1442
  }
1485
1443
  }
1486
1444
  throw new Error("element doesn't contain text " + text);
1487
1445
  }
1488
1446
  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))) {
1489
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1490
- info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1447
+ state.info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1448
+ state.info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1491
1449
  throw new Error("element doesn't contain text " + text);
1492
1450
  }
1493
- return info;
1451
+ return state.info;
1494
1452
  }
1495
1453
  catch (e) {
1496
- //await this.closeUnexpectedPopups();
1497
- this.logger.error("verify element contains text failed " + info.log);
1498
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1499
- info.screenshotPath = screenshotPath;
1500
- Object.assign(e, { info: info });
1501
- error = e;
1502
- throw e;
1454
+ await _commandError(state, e, this);
1503
1455
  }
1504
1456
  finally {
1505
- const endTime = Date.now();
1506
- this._reportToWorld(world, {
1507
- element_name: selectors.element_name,
1508
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1509
- text: `Verify element contains text: ${text}`,
1510
- value: text,
1511
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1512
- result: error
1513
- ? {
1514
- status: "FAILED",
1515
- startTime,
1516
- endTime,
1517
- message: error === null || error === void 0 ? void 0 : error.message,
1518
- }
1519
- : {
1520
- status: "PASSED",
1521
- startTime,
1522
- endTime,
1523
- },
1524
- info: info,
1525
- });
1457
+ _commandFinally(state, this);
1526
1458
  }
1527
1459
  }
1528
1460
  _getDataFile(world = null) {
@@ -1541,6 +1473,29 @@ class StableBrowser {
1541
1473
  }
1542
1474
  return dataFile;
1543
1475
  }
1476
+ async waitForUserInput(message, world = null) {
1477
+ if (!message) {
1478
+ message = "# Wait for user input. Press any key to continue";
1479
+ }
1480
+ else {
1481
+ message = "# Wait for user input. " + message;
1482
+ }
1483
+ message += "\n";
1484
+ const value = await new Promise((resolve) => {
1485
+ const rl = readline.createInterface({
1486
+ input: process.stdin,
1487
+ output: process.stdout,
1488
+ });
1489
+ rl.question(message, (answer) => {
1490
+ rl.close();
1491
+ resolve(answer);
1492
+ });
1493
+ });
1494
+ if (value) {
1495
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1496
+ }
1497
+ this.setTestData({ userInput: value }, world);
1498
+ }
1544
1499
  setTestData(testData, world = null) {
1545
1500
  if (!testData) {
1546
1501
  return;
@@ -1728,7 +1683,6 @@ class StableBrowser {
1728
1683
  }
1729
1684
  async takeScreenshot(screenshotPath) {
1730
1685
  const playContext = this.context.playContext;
1731
- const client = await playContext.newCDPSession(this.page);
1732
1686
  // Using CDP to capture the screenshot
1733
1687
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1734
1688
  document.body.scrollWidth,
@@ -1738,97 +1692,67 @@ class StableBrowser {
1738
1692
  document.body.clientWidth,
1739
1693
  document.documentElement.clientWidth,
1740
1694
  ])));
1741
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1742
- document.body.scrollHeight,
1743
- document.documentElement.scrollHeight,
1744
- document.body.offsetHeight,
1745
- document.documentElement.offsetHeight,
1746
- document.body.clientHeight,
1747
- document.documentElement.clientHeight,
1748
- ])));
1749
- const { data } = await client.send("Page.captureScreenshot", {
1750
- format: "png",
1751
- // clip: {
1752
- // x: 0,
1753
- // y: 0,
1754
- // width: viewportWidth,
1755
- // height: viewportHeight,
1756
- // scale: 1,
1757
- // },
1758
- });
1759
- if (!screenshotPath) {
1760
- return data;
1761
- }
1762
- let screenshotBuffer = Buffer.from(data, "base64");
1763
- const sharpBuffer = sharp(screenshotBuffer);
1764
- const metadata = await sharpBuffer.metadata();
1765
- //check if you are on retina display and reduce the quality of the image
1766
- if (metadata.width > viewportWidth || metadata.height > viewportHeight) {
1767
- screenshotBuffer = await sharpBuffer
1768
- .resize(viewportWidth, viewportHeight, {
1769
- fit: sharp.fit.inside,
1770
- withoutEnlargement: true,
1771
- })
1772
- .toBuffer();
1773
- }
1774
- fs.writeFileSync(screenshotPath, screenshotBuffer);
1775
- await client.detach();
1695
+ let screenshotBuffer = null;
1696
+ if (this.context.browserName === "chromium") {
1697
+ const client = await playContext.newCDPSession(this.page);
1698
+ const { data } = await client.send("Page.captureScreenshot", {
1699
+ format: "png",
1700
+ // clip: {
1701
+ // x: 0,
1702
+ // y: 0,
1703
+ // width: viewportWidth,
1704
+ // height: viewportHeight,
1705
+ // scale: 1,
1706
+ // },
1707
+ });
1708
+ await client.detach();
1709
+ if (!screenshotPath) {
1710
+ return data;
1711
+ }
1712
+ screenshotBuffer = Buffer.from(data, "base64");
1713
+ }
1714
+ else {
1715
+ screenshotBuffer = await this.page.screenshot();
1716
+ }
1717
+ let image = await Jimp.read(screenshotBuffer);
1718
+ // Get the image dimensions
1719
+ const { width, height } = image.bitmap;
1720
+ const resizeRatio = viewportWidth / width;
1721
+ // Resize the image to fit within the viewport dimensions without enlarging
1722
+ if (width > viewportWidth) {
1723
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1724
+ await image.write(screenshotPath);
1725
+ }
1726
+ else {
1727
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1728
+ }
1776
1729
  }
1777
1730
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1778
- this._validateSelectors(selectors);
1779
- const startTime = Date.now();
1780
- let error = null;
1781
- let screenshotId = null;
1782
- let screenshotPath = null;
1731
+ const state = {
1732
+ selectors,
1733
+ _params,
1734
+ options,
1735
+ world,
1736
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1737
+ text: `Verify element exists in page`,
1738
+ operation: "verifyElementExistInPage",
1739
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1740
+ };
1783
1741
  await new Promise((resolve) => setTimeout(resolve, 2000));
1784
- const info = {};
1785
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
1786
- info.operation = "verify";
1787
- info.selectors = selectors;
1788
1742
  try {
1789
- const element = await this._locate(selectors, info, _params);
1790
- if (element) {
1791
- await this.scrollIfNeeded(element, info);
1792
- }
1793
- await this._highlightElements(element);
1794
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1795
- await expect(element).toHaveCount(1, { timeout: 10000 });
1796
- return info;
1743
+ await _preCommand(state, this);
1744
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1745
+ return state.info;
1797
1746
  }
1798
1747
  catch (e) {
1799
- //await this.closeUnexpectedPopups();
1800
- this.logger.error("verify failed " + info.log);
1801
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1802
- info.screenshotPath = screenshotPath;
1803
- Object.assign(e, { info: info });
1804
- error = e;
1805
- throw e;
1748
+ await _commandError(state, e, this);
1806
1749
  }
1807
1750
  finally {
1808
- const endTime = Date.now();
1809
- this._reportToWorld(world, {
1810
- element_name: selectors.element_name,
1811
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1812
- text: "Verify element exists in page",
1813
- screenshotId,
1814
- result: error
1815
- ? {
1816
- status: "FAILED",
1817
- startTime,
1818
- endTime,
1819
- message: error === null || error === void 0 ? void 0 : error.message,
1820
- }
1821
- : {
1822
- status: "PASSED",
1823
- startTime,
1824
- endTime,
1825
- },
1826
- info: info,
1827
- });
1751
+ _commandFinally(state, this);
1828
1752
  }
1829
1753
  }
1830
1754
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1831
- this._validateSelectors(selectors);
1755
+ _validateSelectors(selectors);
1832
1756
  const startTime = Date.now();
1833
1757
  let error = null;
1834
1758
  let screenshotId = null;
@@ -2132,20 +2056,20 @@ class StableBrowser {
2132
2056
  for (let i = 0; i < frames.length; i++) {
2133
2057
  if (dateAlternatives.date) {
2134
2058
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2135
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2059
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
2136
2060
  result.frame = frames[i];
2137
2061
  results.push(result);
2138
2062
  }
2139
2063
  }
2140
2064
  else if (numberAlternatives.number) {
2141
2065
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2142
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2066
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
2143
2067
  result.frame = frames[i];
2144
2068
  results.push(result);
2145
2069
  }
2146
2070
  }
2147
2071
  else {
2148
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2072
+ const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
2149
2073
  result.frame = frames[i];
2150
2074
  results.push(result);
2151
2075
  }
@@ -2310,7 +2234,7 @@ class StableBrowser {
2310
2234
  this.logger.info("Table data verified");
2311
2235
  }
2312
2236
  async getTableData(selectors, _params = null, options = {}, world = null) {
2313
- this._validateSelectors(selectors);
2237
+ _validateSelectors(selectors);
2314
2238
  const startTime = Date.now();
2315
2239
  let error = null;
2316
2240
  let screenshotId = null;
@@ -2358,7 +2282,7 @@ class StableBrowser {
2358
2282
  }
2359
2283
  }
2360
2284
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2361
- this._validateSelectors(selectors);
2285
+ _validateSelectors(selectors);
2362
2286
  if (!query) {
2363
2287
  throw new Error("query is null");
2364
2288
  }
@@ -2580,13 +2504,13 @@ class StableBrowser {
2580
2504
  }
2581
2505
  catch (e) {
2582
2506
  if (e.label === "networkidle") {
2583
- console.log("waitted for the network to be idle timeout");
2507
+ console.log("waited for the network to be idle timeout");
2584
2508
  }
2585
2509
  else if (e.label === "load") {
2586
- console.log("waitted for the load timeout");
2510
+ console.log("waited for the load timeout");
2587
2511
  }
2588
2512
  else if (e.label === "domcontentloaded") {
2589
- console.log("waitted for the domcontent loaded timeout");
2513
+ console.log("waited for the domcontent loaded timeout");
2590
2514
  }
2591
2515
  console.log(".");
2592
2516
  }
@@ -2729,33 +2653,18 @@ class StableBrowser {
2729
2653
  }
2730
2654
  async scrollIfNeeded(element, info) {
2731
2655
  try {
2732
- let didScroll = await element.evaluate((node) => {
2733
- const rect = node.getBoundingClientRect();
2734
- if (rect &&
2735
- rect.top >= 0 &&
2736
- rect.left >= 0 &&
2737
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2738
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
2739
- return false;
2740
- }
2741
- else {
2742
- node.scrollIntoView({
2743
- behavior: "smooth",
2744
- block: "center",
2745
- inline: "center",
2746
- });
2747
- return true;
2748
- }
2656
+ await element.scrollIntoViewIfNeeded({
2657
+ timeout: 2000,
2749
2658
  });
2750
- if (didScroll) {
2751
- await new Promise((resolve) => setTimeout(resolve, 500));
2752
- if (info) {
2753
- info.box = await element.boundingBox();
2754
- }
2659
+ await new Promise((resolve) => setTimeout(resolve, 500));
2660
+ if (info) {
2661
+ info.box = await element.boundingBox({
2662
+ timeout: 1000,
2663
+ });
2755
2664
  }
2756
2665
  }
2757
2666
  catch (e) {
2758
- console.log("scroll failed");
2667
+ console.log("#-#");
2759
2668
  }
2760
2669
  }
2761
2670
  _reportToWorld(world, properties) {
@@ -2916,5 +2825,10 @@ const KEYBOARD_EVENTS = [
2916
2825
  "TVAntennaCable",
2917
2826
  "TVAudioDescription",
2918
2827
  ];
2828
+ function unEscapeString(str) {
2829
+ const placeholder = "__NEWLINE__";
2830
+ str = str.replace(new RegExp(placeholder, "g"), "\n");
2831
+ return str;
2832
+ }
2919
2833
  export { StableBrowser };
2920
2834
  //# sourceMappingURL=stable_browser.js.map