automation_model 1.0.428-dev → 1.0.428-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,15 +557,20 @@ 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
  }
481
565
  }
482
566
  return { rerun: false };
483
567
  }
484
- async _locate(selectors, info, _params, timeout = 30000) {
568
+ async _locate(selectors, info, _params, timeout) {
569
+ if (!timeout) {
570
+ timeout = 30000;
571
+ }
485
572
  for (let i = 0; i < 3; i++) {
486
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
573
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
487
574
  for (let j = 0; j < selectors.locators.length; j++) {
488
575
  let selector = selectors.locators[j];
489
576
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -495,17 +582,44 @@ class StableBrowser {
495
582
  }
496
583
  throw new Error("unable to locate element " + JSON.stringify(selectors));
497
584
  }
498
- async _locate_internal(selectors, info, _params, timeout = 30000) {
499
- let highPriorityTimeout = 5000;
500
- let visibleOnlyTimeout = 6000;
501
- let startTime = performance.now();
502
- let locatorsCount = 0;
503
- //let arrayMode = Array.isArray(selectors);
585
+ async _findFrameScope(selectors, timeout = 30000) {
504
586
  let scope = this.page;
587
+ if (selectors.frame) {
588
+ return selectors.frame;
589
+ }
505
590
  if (selectors.iframe_src || selectors.frameLocators) {
506
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
591
+ const findFrame = async (frame, framescope) => {
592
+ for (let i = 0; i < frame.selectors.length; i++) {
593
+ let frameLocator = frame.selectors[i];
594
+ if (frameLocator.css) {
595
+ let testframescope = framescope.frameLocator(frameLocator.css);
596
+ if (frameLocator.index) {
597
+ testframescope = framescope.nth(frameLocator.index);
598
+ }
599
+ try {
600
+ await testframescope.owner().evaluateHandle(() => true, null, {
601
+ timeout: 5000,
602
+ });
603
+ framescope = testframescope;
604
+ break;
605
+ }
606
+ catch (error) {
607
+ console.error("frame not found " + frameLocator.css);
608
+ }
609
+ }
610
+ }
611
+ if (frame.children) {
612
+ return await findFrame(frame.children, framescope);
613
+ }
614
+ return framescope;
615
+ };
507
616
  while (true) {
508
617
  let frameFound = false;
618
+ if (selectors.nestFrmLoc) {
619
+ scope = await findFrame(selectors.nestFrmLoc, scope);
620
+ frameFound = true;
621
+ break;
622
+ }
509
623
  if (selectors.frameLocators) {
510
624
  for (let i = 0; i < selectors.frameLocators.length; i++) {
511
625
  let frameLocator = selectors.frameLocators[i];
@@ -531,6 +645,25 @@ class StableBrowser {
531
645
  }
532
646
  }
533
647
  }
648
+ if (!scope) {
649
+ scope = this.page;
650
+ }
651
+ return scope;
652
+ }
653
+ async _getDocumentBody(selectors, timeout = 30000) {
654
+ let scope = await this._findFrameScope(selectors, timeout);
655
+ return scope.evaluate(() => {
656
+ var bodyContent = document.body.innerHTML;
657
+ return bodyContent;
658
+ });
659
+ }
660
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
661
+ let highPriorityTimeout = 5000;
662
+ let visibleOnlyTimeout = 6000;
663
+ let startTime = performance.now();
664
+ let locatorsCount = 0;
665
+ //let arrayMode = Array.isArray(selectors);
666
+ let scope = await this._findFrameScope(selectors, timeout);
534
667
  let selectorsLocators = null;
535
668
  selectorsLocators = selectors.locators;
536
669
  // group selectors by priority
@@ -666,86 +799,121 @@ class StableBrowser {
666
799
  }
667
800
  return result;
668
801
  }
669
- async click(selectors, _params, options = {}, world = null) {
670
- this._validateSelectors(selectors);
802
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
671
803
  const startTime = Date.now();
672
- if (options && options.context) {
673
- selectors.locators[0].text = options.context;
804
+ let timeout = 30000;
805
+ if (options && options.timeout) {
806
+ timeout = options.timeout;
674
807
  }
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;
808
+ while (true) {
809
+ try {
810
+ const result = await locate_element(this.context, elementDescription, "click");
811
+ if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
812
+ const selectors = {
813
+ frame: result === null || result === void 0 ? void 0 : result.frame,
814
+ locators: [
815
+ {
816
+ css: result === null || result === void 0 ? void 0 : result.css,
817
+ },
818
+ ],
819
+ };
820
+ await this.click(selectors, _params, options, world);
821
+ return;
822
+ }
823
+ }
824
+ catch (e) {
825
+ if (performance.now() - startTime > timeout) {
826
+ throw e;
827
+ }
828
+ }
829
+ await new Promise((resolve) => setTimeout(resolve, 3000));
830
+ }
831
+ }
832
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
833
+ const startTime = Date.now();
834
+ let timeout = 30000;
835
+ if (options && options.timeout) {
836
+ timeout = options.timeout;
837
+ }
838
+ while (true) {
839
+ try {
840
+ const result = await locate_element(this.context, elementDescription, "fill", value);
841
+ if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
842
+ const selectors = {
843
+ frame: result === null || result === void 0 ? void 0 : result.frame,
844
+ locators: [
845
+ {
846
+ css: result === null || result === void 0 ? void 0 : result.css,
847
+ },
848
+ ],
849
+ };
850
+ await this.clickType(selectors, value, false, _params, options, world);
851
+ return;
852
+ }
853
+ }
854
+ catch (e) {
855
+ if (performance.now() - startTime > timeout) {
856
+ throw e;
857
+ }
858
+ }
859
+ await new Promise((resolve) => setTimeout(resolve, 3000));
860
+ }
861
+ }
862
+ async click(selectors, _params, options = {}, world = null) {
863
+ const state = {
864
+ selectors,
865
+ _params,
866
+ options,
867
+ world,
868
+ text: "Click element",
869
+ type: Types.CLICK,
870
+ operation: "click",
871
+ log: "***** click on " + selectors.element_name + " *****\n",
872
+ };
682
873
  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));
874
+ await _preCommand(state, this);
875
+ if (state.options && state.options.context) {
876
+ state.selectors.locators[0].text = state.options.context;
877
+ }
686
878
  try {
687
- await this._highlightElements(element);
688
- await element.click();
879
+ await state.element.click();
689
880
  await new Promise((resolve) => setTimeout(resolve, 1000));
690
881
  }
691
882
  catch (e) {
692
883
  // await this.closeUnexpectedPopups();
693
- info.log += "click failed, will try again" + "\n";
694
- element = await this._locate(selectors, info, _params);
695
- await element.dispatchEvent("click");
884
+ state.element = await this._locate(selectors, state.info, _params);
885
+ await state.element.dispatchEvent("click");
696
886
  await new Promise((resolve) => setTimeout(resolve, 1000));
697
887
  }
698
888
  await this.waitForPageLoad();
699
- return info;
889
+ return state.info;
700
890
  }
701
891
  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;
892
+ await _commandError(state, e, this);
708
893
  }
709
894
  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
- });
895
+ _commandFinally(state, this);
730
896
  }
731
897
  }
732
898
  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;
899
+ const state = {
900
+ selectors,
901
+ _params,
902
+ options,
903
+ world,
904
+ type: checked ? Types.CHECK : Types.UNCHECK,
905
+ text: checked ? `Check element` : `Uncheck element`,
906
+ operation: "setCheck",
907
+ log: "***** check " + selectors.element_name + " *****\n",
908
+ };
743
909
  try {
744
- let element = await this._locate(selectors, info, _params);
745
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
910
+ await _preCommand(state, this);
911
+ state.info.checked = checked;
912
+ // let element = await this._locate(selectors, info, _params);
913
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
746
914
  try {
747
- await this._highlightElements(element);
748
- await element.setChecked(checked);
915
+ // await this._highlightElements(element);
916
+ await state.element.setChecked(checked);
749
917
  await new Promise((resolve) => setTimeout(resolve, 1000));
750
918
  }
751
919
  catch (e) {
@@ -754,179 +922,107 @@ class StableBrowser {
754
922
  }
755
923
  else {
756
924
  //await this.closeUnexpectedPopups();
757
- 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 });
925
+ state.info.log += "setCheck failed, will try again" + "\n";
926
+ state.element = await this._locate(selectors, state.info, _params);
927
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
760
928
  await new Promise((resolve) => setTimeout(resolve, 1000));
761
929
  }
762
930
  }
763
931
  await this.waitForPageLoad();
764
- return info;
932
+ return state.info;
765
933
  }
766
934
  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;
935
+ await _commandError(state, e, this);
773
936
  }
774
937
  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
- });
938
+ _commandFinally(state, this);
795
939
  }
796
940
  }
797
941
  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;
942
+ const state = {
943
+ selectors,
944
+ _params,
945
+ options,
946
+ world,
947
+ type: Types.HOVER,
948
+ text: `Hover element`,
949
+ operation: "hover",
950
+ log: "***** hover " + selectors.element_name + " *****\n",
951
+ };
807
952
  try {
808
- let element = await this._locate(selectors, info, _params);
809
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
953
+ await _preCommand(state, this);
810
954
  try {
811
- await this._highlightElements(element);
812
- await element.hover();
955
+ await state.element.hover();
813
956
  await new Promise((resolve) => setTimeout(resolve, 1000));
814
957
  }
815
958
  catch (e) {
816
959
  //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 });
960
+ state.info.log += "hover failed, will try again" + "\n";
961
+ state.element = await this._locate(selectors, state.info, _params);
962
+ await state.element.hover({ timeout: 10000 });
820
963
  await new Promise((resolve) => setTimeout(resolve, 1000));
821
964
  }
822
965
  await this.waitForPageLoad();
823
- return info;
966
+ return state.info;
824
967
  }
825
968
  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;
969
+ await _commandError(state, e, this);
832
970
  }
833
971
  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
- });
972
+ _commandFinally(state, this);
854
973
  }
855
974
  }
856
975
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
857
- this._validateSelectors(selectors);
858
976
  if (!values) {
859
977
  throw new Error("values is null");
860
978
  }
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;
979
+ const state = {
980
+ selectors,
981
+ _params,
982
+ options,
983
+ world,
984
+ type: Types.SELECT,
985
+ text: `Select option: ${values}`,
986
+ operation: "selectOption",
987
+ log: "***** select option " + selectors.element_name + " *****\n",
988
+ };
869
989
  try {
870
- let element = await this._locate(selectors, info, _params);
871
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
990
+ await _preCommand(state, this);
872
991
  try {
873
- await this._highlightElements(element);
874
- await element.selectOption(values);
992
+ await state.element.selectOption(values);
875
993
  }
876
994
  catch (e) {
877
995
  //await this.closeUnexpectedPopups();
878
- info.log += "selectOption failed, will try force" + "\n";
879
- await element.selectOption(values, { timeout: 10000, force: true });
996
+ state.info.log += "selectOption failed, will try force" + "\n";
997
+ await state.element.selectOption(values, { timeout: 10000, force: true });
880
998
  }
881
999
  await this.waitForPageLoad();
882
- return info;
1000
+ return state.info;
883
1001
  }
884
1002
  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;
1003
+ await _commandError(state, e, this);
892
1004
  }
893
1005
  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
- });
1006
+ _commandFinally(state, this);
915
1007
  }
916
1008
  }
917
1009
  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;
1010
+ const state = {
1011
+ value: _value,
1012
+ _params,
1013
+ options,
1014
+ world,
1015
+ locate: false,
1016
+ scroll: false,
1017
+ highlight: false,
1018
+ type: Types.TYPE_PRESS,
1019
+ text: `Type value: ${_value}`,
1020
+ operation: "type",
1021
+ log: "",
1022
+ };
927
1023
  try {
928
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
929
- const valueSegment = _value.split("&&");
1024
+ await _preCommand(state, this);
1025
+ const valueSegment = state.value.split("&&");
930
1026
  for (let i = 0; i < valueSegment.length; i++) {
931
1027
  if (i > 0) {
932
1028
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -946,108 +1042,53 @@ class StableBrowser {
946
1042
  await this.page.keyboard.type(value);
947
1043
  }
948
1044
  }
949
- return info;
1045
+ return state.info;
950
1046
  }
951
1047
  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;
1048
+ await _commandError(state, e, this);
959
1049
  }
960
1050
  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
- });
1051
+ _commandFinally(state, this);
981
1052
  }
982
1053
  }
983
1054
  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;
1055
+ const state = {
1056
+ selectors,
1057
+ _params,
1058
+ value,
1059
+ options,
1060
+ world,
1061
+ type: Types.SET_INPUT,
1062
+ text: `Set input value`,
1063
+ operation: "setInputValue",
1064
+ log: "***** set input value " + selectors.element_name + " *****\n",
1065
+ };
996
1066
  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);
1067
+ await _preCommand(state, this);
1068
+ let value = await this._replaceWithLocalData(state.value, this);
1002
1069
  try {
1003
- await element.evaluateHandle((el, value) => {
1070
+ await state.element.evaluateHandle((el, value) => {
1004
1071
  el.value = value;
1005
1072
  }, value);
1006
1073
  }
1007
1074
  catch (error) {
1008
1075
  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) => {
1076
+ await _screenshot(state, this);
1077
+ Object.assign(error, { info: state.info });
1078
+ await state.element.evaluateHandle((el, value) => {
1013
1079
  el.value = value;
1014
1080
  });
1015
1081
  }
1016
1082
  }
1017
1083
  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;
1084
+ await _commandError(state, e, this);
1024
1085
  }
1025
1086
  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
- });
1087
+ _commandFinally(state, this);
1047
1088
  }
1048
1089
  }
1049
1090
  async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1050
- this._validateSelectors(selectors);
1091
+ _validateSelectors(selectors);
1051
1092
  const startTime = Date.now();
1052
1093
  let error = null;
1053
1094
  let screenshotId = null;
@@ -1140,30 +1181,28 @@ class StableBrowser {
1140
1181
  }
1141
1182
  }
1142
1183
  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);
1184
+ const state = {
1185
+ selectors,
1186
+ _params,
1187
+ value: unEscapeString(_value),
1188
+ options,
1189
+ world,
1190
+ type: Types.FILL,
1191
+ text: `Click type input with value: ${_value}`,
1192
+ operation: "clickType",
1193
+ log: "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n",
1194
+ };
1195
+ const newValue = await this._replaceWithLocalData(state.value, world);
1153
1196
  if (newValue !== _value) {
1154
1197
  //this.logger.info(_value + "=" + newValue);
1155
1198
  _value = newValue;
1156
1199
  }
1157
- info.value = _value;
1158
1200
  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);
1201
+ await _preCommand(state, this);
1202
+ state.info.value = _value;
1164
1203
  if (options === null || options === undefined || !options.press) {
1165
1204
  try {
1166
- let currentValue = await element.inputValue();
1205
+ let currentValue = await state.element.inputValue();
1167
1206
  if (currentValue) {
1168
1207
  await element.fill("");
1169
1208
  }
@@ -1174,22 +1213,22 @@ class StableBrowser {
1174
1213
  }
1175
1214
  if (options === null || options === undefined || options.press) {
1176
1215
  try {
1177
- await element.click({ timeout: 5000 });
1216
+ await state.element.click({ timeout: 5000 });
1178
1217
  }
1179
1218
  catch (e) {
1180
- await element.dispatchEvent("click");
1219
+ await state.element.dispatchEvent("click");
1181
1220
  }
1182
1221
  }
1183
1222
  else {
1184
1223
  try {
1185
- await element.focus();
1224
+ await state.element.focus();
1186
1225
  }
1187
1226
  catch (e) {
1188
- await element.dispatchEvent("focus");
1227
+ await state.element.dispatchEvent("focus");
1189
1228
  }
1190
1229
  }
1191
1230
  await new Promise((resolve) => setTimeout(resolve, 500));
1192
- const valueSegment = _value.split("&&");
1231
+ const valueSegment = state.value.split("&&");
1193
1232
  for (let i = 0; i < valueSegment.length; i++) {
1194
1233
  if (i > 0) {
1195
1234
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1209,13 +1248,14 @@ class StableBrowser {
1209
1248
  await new Promise((resolve) => setTimeout(resolve, 500));
1210
1249
  }
1211
1250
  }
1251
+ await _screenshot(state, this);
1212
1252
  if (enter === true) {
1213
1253
  await new Promise((resolve) => setTimeout(resolve, 2000));
1214
1254
  await this.page.keyboard.press("Enter");
1215
1255
  await this.waitForPageLoad();
1216
1256
  }
1217
1257
  else if (enter === false) {
1218
- await element.dispatchEvent("change");
1258
+ await state.element.dispatchEvent("change");
1219
1259
  //await this.page.keyboard.press("Tab");
1220
1260
  }
1221
1261
  else {
@@ -1224,103 +1264,50 @@ class StableBrowser {
1224
1264
  await this.waitForPageLoad();
1225
1265
  }
1226
1266
  }
1227
- return info;
1267
+ return state.info;
1228
1268
  }
1229
1269
  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;
1270
+ await _commandError(state, e, this);
1237
1271
  }
1238
1272
  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
- });
1273
+ _commandFinally(state, this);
1260
1274
  }
1261
1275
  }
1262
1276
  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;
1277
+ const state = {
1278
+ selectors,
1279
+ _params,
1280
+ value: unEscapeString(value),
1281
+ options,
1282
+ world,
1283
+ type: Types.FILL,
1284
+ text: `Fill input with value: ${value}`,
1285
+ operation: "fill",
1286
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1287
+ };
1273
1288
  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");
1289
+ await _preCommand(state, this);
1290
+ await state.element.fill(value);
1291
+ await state.element.dispatchEvent("change");
1279
1292
  if (enter) {
1280
1293
  await new Promise((resolve) => setTimeout(resolve, 2000));
1281
1294
  await this.page.keyboard.press("Enter");
1282
1295
  }
1283
1296
  await this.waitForPageLoad();
1284
- return info;
1297
+ return state.info;
1285
1298
  }
1286
1299
  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;
1300
+ await _commandError(state, e, this);
1294
1301
  }
1295
1302
  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
- });
1303
+ _commandFinally(state, this);
1317
1304
  }
1318
1305
  }
1319
1306
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1320
1307
  return await this._getText(selectors, 0, _params, options, info, world);
1321
1308
  }
1322
1309
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1323
- this._validateSelectors(selectors);
1310
+ _validateSelectors(selectors);
1324
1311
  let screenshotId = null;
1325
1312
  let screenshotPath = null;
1326
1313
  if (!info.log) {
@@ -1365,113 +1352,100 @@ class StableBrowser {
1365
1352
  }
1366
1353
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1367
1354
  var _a;
1368
- this._validateSelectors(selectors);
1369
1355
  if (!pattern) {
1370
1356
  throw new Error("pattern is null");
1371
1357
  }
1372
1358
  if (!text) {
1373
1359
  throw new Error("text is null");
1374
1360
  }
1361
+ const state = {
1362
+ selectors,
1363
+ _params,
1364
+ pattern,
1365
+ value: text,
1366
+ options,
1367
+ world,
1368
+ locate: false,
1369
+ scroll: false,
1370
+ screenshot: false,
1371
+ highlight: false,
1372
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1373
+ text: `Verify element contains pattern: ${pattern}`,
1374
+ operation: "containsPattern",
1375
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1376
+ };
1375
1377
  const newValue = await this._replaceWithLocalData(text, world);
1376
1378
  if (newValue !== text) {
1377
1379
  this.logger.info(text + "=" + newValue);
1378
1380
  text = newValue;
1379
1381
  }
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
1382
  let foundObj = null;
1392
1383
  try {
1393
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1384
+ await _preCommand(state, this);
1385
+ state.info.pattern = pattern;
1386
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1394
1387
  if (foundObj && foundObj.element) {
1395
- await this.scrollIfNeeded(foundObj.element, info);
1388
+ await this.scrollIfNeeded(foundObj.element, state.info);
1396
1389
  }
1397
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1390
+ await _screenshot(state, this);
1398
1391
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1399
1392
  pattern = pattern.replace("{text}", escapedText);
1400
1393
  let regex = new RegExp(pattern, "im");
1401
1394
  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;
1395
+ state.info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1403
1396
  throw new Error("element doesn't contain text " + text);
1404
1397
  }
1405
- return info;
1398
+ return state.info;
1406
1399
  }
1407
1400
  catch (e) {
1408
- //await this.closeUnexpectedPopups();
1409
- this.logger.error("verify element contains text failed " + info.log);
1410
1401
  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;
1402
+ await _commandError(state, e, this);
1416
1403
  }
1417
1404
  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
- });
1405
+ _commandFinally(state, this);
1439
1406
  }
1440
1407
  }
1441
1408
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1442
1409
  var _a, _b, _c;
1443
- this._validateSelectors(selectors);
1410
+ const state = {
1411
+ selectors,
1412
+ _params,
1413
+ value: text,
1414
+ options,
1415
+ world,
1416
+ locate: false,
1417
+ scroll: false,
1418
+ screenshot: false,
1419
+ highlight: false,
1420
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1421
+ text: `Verify element contains text: ${text}`,
1422
+ operation: "containsText",
1423
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1424
+ };
1444
1425
  if (!text) {
1445
1426
  throw new Error("text is null");
1446
1427
  }
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;
1428
+ text = unEscapeString(text);
1455
1429
  const newValue = await this._replaceWithLocalData(text, world);
1456
1430
  if (newValue !== text) {
1457
1431
  this.logger.info(text + "=" + newValue);
1458
1432
  text = newValue;
1459
1433
  }
1460
- info.value = text;
1461
1434
  let foundObj = null;
1462
1435
  try {
1463
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1436
+ await _preCommand(state, this);
1437
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1464
1438
  if (foundObj && foundObj.element) {
1465
- await this.scrollIfNeeded(foundObj.element, info);
1439
+ await this.scrollIfNeeded(foundObj.element, state.info);
1466
1440
  }
1467
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1441
+ await _screenshot(state, this);
1468
1442
  const dateAlternatives = findDateAlternatives(text);
1469
1443
  const numberAlternatives = findNumberAlternatives(text);
1470
1444
  if (dateAlternatives.date) {
1471
1445
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1472
1446
  if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1473
1447
  ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1474
- return info;
1448
+ return state.info;
1475
1449
  }
1476
1450
  }
1477
1451
  throw new Error("element doesn't contain text " + text);
@@ -1480,49 +1454,23 @@ class StableBrowser {
1480
1454
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1481
1455
  if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1482
1456
  ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1483
- return info;
1457
+ return state.info;
1484
1458
  }
1485
1459
  }
1486
1460
  throw new Error("element doesn't contain text " + text);
1487
1461
  }
1488
1462
  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;
1463
+ state.info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1464
+ state.info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1491
1465
  throw new Error("element doesn't contain text " + text);
1492
1466
  }
1493
- return info;
1467
+ return state.info;
1494
1468
  }
1495
1469
  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;
1470
+ await _commandError(state, e, this);
1503
1471
  }
1504
1472
  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
- });
1473
+ _commandFinally(state, this);
1526
1474
  }
1527
1475
  }
1528
1476
  _getDataFile(world = null) {
@@ -1541,6 +1489,29 @@ class StableBrowser {
1541
1489
  }
1542
1490
  return dataFile;
1543
1491
  }
1492
+ async waitForUserInput(message, world = null) {
1493
+ if (!message) {
1494
+ message = "# Wait for user input. Press any key to continue";
1495
+ }
1496
+ else {
1497
+ message = "# Wait for user input. " + message;
1498
+ }
1499
+ message += "\n";
1500
+ const value = await new Promise((resolve) => {
1501
+ const rl = readline.createInterface({
1502
+ input: process.stdin,
1503
+ output: process.stdout,
1504
+ });
1505
+ rl.question(message, (answer) => {
1506
+ rl.close();
1507
+ resolve(answer);
1508
+ });
1509
+ });
1510
+ if (value) {
1511
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1512
+ }
1513
+ this.setTestData({ userInput: value }, world);
1514
+ }
1544
1515
  setTestData(testData, world = null) {
1545
1516
  if (!testData) {
1546
1517
  return;
@@ -1728,7 +1699,6 @@ class StableBrowser {
1728
1699
  }
1729
1700
  async takeScreenshot(screenshotPath) {
1730
1701
  const playContext = this.context.playContext;
1731
- const client = await playContext.newCDPSession(this.page);
1732
1702
  // Using CDP to capture the screenshot
1733
1703
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1734
1704
  document.body.scrollWidth,
@@ -1738,97 +1708,67 @@ class StableBrowser {
1738
1708
  document.body.clientWidth,
1739
1709
  document.documentElement.clientWidth,
1740
1710
  ])));
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();
1711
+ let screenshotBuffer = null;
1712
+ if (this.context.browserName === "chromium") {
1713
+ const client = await playContext.newCDPSession(this.page);
1714
+ const { data } = await client.send("Page.captureScreenshot", {
1715
+ format: "png",
1716
+ // clip: {
1717
+ // x: 0,
1718
+ // y: 0,
1719
+ // width: viewportWidth,
1720
+ // height: viewportHeight,
1721
+ // scale: 1,
1722
+ // },
1723
+ });
1724
+ await client.detach();
1725
+ if (!screenshotPath) {
1726
+ return data;
1727
+ }
1728
+ screenshotBuffer = Buffer.from(data, "base64");
1729
+ }
1730
+ else {
1731
+ screenshotBuffer = await this.page.screenshot();
1732
+ }
1733
+ let image = await Jimp.read(screenshotBuffer);
1734
+ // Get the image dimensions
1735
+ const { width, height } = image.bitmap;
1736
+ const resizeRatio = viewportWidth / width;
1737
+ // Resize the image to fit within the viewport dimensions without enlarging
1738
+ if (width > viewportWidth) {
1739
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1740
+ await image.write(screenshotPath);
1741
+ }
1742
+ else {
1743
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1744
+ }
1776
1745
  }
1777
1746
  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;
1747
+ const state = {
1748
+ selectors,
1749
+ _params,
1750
+ options,
1751
+ world,
1752
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1753
+ text: `Verify element exists in page`,
1754
+ operation: "verifyElementExistInPage",
1755
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1756
+ };
1783
1757
  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
1758
  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;
1759
+ await _preCommand(state, this);
1760
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1761
+ return state.info;
1797
1762
  }
1798
1763
  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;
1764
+ await _commandError(state, e, this);
1806
1765
  }
1807
1766
  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
- });
1767
+ _commandFinally(state, this);
1828
1768
  }
1829
1769
  }
1830
1770
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1831
- this._validateSelectors(selectors);
1771
+ _validateSelectors(selectors);
1832
1772
  const startTime = Date.now();
1833
1773
  let error = null;
1834
1774
  let screenshotId = null;
@@ -2132,20 +2072,20 @@ class StableBrowser {
2132
2072
  for (let i = 0; i < frames.length; i++) {
2133
2073
  if (dateAlternatives.date) {
2134
2074
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2135
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2075
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
2136
2076
  result.frame = frames[i];
2137
2077
  results.push(result);
2138
2078
  }
2139
2079
  }
2140
2080
  else if (numberAlternatives.number) {
2141
2081
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2142
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2082
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
2143
2083
  result.frame = frames[i];
2144
2084
  results.push(result);
2145
2085
  }
2146
2086
  }
2147
2087
  else {
2148
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2088
+ const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
2149
2089
  result.frame = frames[i];
2150
2090
  results.push(result);
2151
2091
  }
@@ -2310,7 +2250,7 @@ class StableBrowser {
2310
2250
  this.logger.info("Table data verified");
2311
2251
  }
2312
2252
  async getTableData(selectors, _params = null, options = {}, world = null) {
2313
- this._validateSelectors(selectors);
2253
+ _validateSelectors(selectors);
2314
2254
  const startTime = Date.now();
2315
2255
  let error = null;
2316
2256
  let screenshotId = null;
@@ -2358,7 +2298,7 @@ class StableBrowser {
2358
2298
  }
2359
2299
  }
2360
2300
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2361
- this._validateSelectors(selectors);
2301
+ _validateSelectors(selectors);
2362
2302
  if (!query) {
2363
2303
  throw new Error("query is null");
2364
2304
  }
@@ -2580,13 +2520,13 @@ class StableBrowser {
2580
2520
  }
2581
2521
  catch (e) {
2582
2522
  if (e.label === "networkidle") {
2583
- console.log("waitted for the network to be idle timeout");
2523
+ console.log("waited for the network to be idle timeout");
2584
2524
  }
2585
2525
  else if (e.label === "load") {
2586
- console.log("waitted for the load timeout");
2526
+ console.log("waited for the load timeout");
2587
2527
  }
2588
2528
  else if (e.label === "domcontentloaded") {
2589
- console.log("waitted for the domcontent loaded timeout");
2529
+ console.log("waited for the domcontent loaded timeout");
2590
2530
  }
2591
2531
  console.log(".");
2592
2532
  }
@@ -2729,14 +2669,18 @@ class StableBrowser {
2729
2669
  }
2730
2670
  async scrollIfNeeded(element, info) {
2731
2671
  try {
2732
- await element.scrollIntoViewIfNeeded();
2672
+ await element.scrollIntoViewIfNeeded({
2673
+ timeout: 2000,
2674
+ });
2733
2675
  await new Promise((resolve) => setTimeout(resolve, 500));
2734
2676
  if (info) {
2735
- info.box = await element.boundingBox();
2677
+ info.box = await element.boundingBox({
2678
+ timeout: 1000,
2679
+ });
2736
2680
  }
2737
2681
  }
2738
2682
  catch (e) {
2739
- console.log("scroll failed");
2683
+ console.log("#-#");
2740
2684
  }
2741
2685
  }
2742
2686
  _reportToWorld(world, properties) {
@@ -2897,5 +2841,10 @@ const KEYBOARD_EVENTS = [
2897
2841
  "TVAntennaCable",
2898
2842
  "TVAudioDescription",
2899
2843
  ];
2844
+ function unEscapeString(str) {
2845
+ const placeholder = "__NEWLINE__";
2846
+ str = str.replace(new RegExp(placeholder, "g"), "\n");
2847
+ return str;
2848
+ }
2900
2849
  export { StableBrowser };
2901
2850
  //# sourceMappingURL=stable_browser.js.map