automation_model 1.0.399-dev → 1.0.399-main

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";
@@ -13,6 +13,11 @@ import { getTableCells, getTableData } from "./table_analyze.js";
13
13
  import objectPath from "object-path";
14
14
  import { decrypt } from "./utils.js";
15
15
  import csv from "csv-parser";
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";
16
21
  const Types = {
17
22
  CLICK: "click_element",
18
23
  NAVIGATE: "navigate",
@@ -37,16 +42,27 @@ const Types = {
37
42
  SET_DATE_TIME: "set_date_time",
38
43
  SET_VIEWPORT: "set_viewport",
39
44
  VERIFY_VISUAL: "verify_visual",
45
+ LOAD_DATA: "load_data",
46
+ SET_INPUT: "set_input",
40
47
  };
48
+ export const apps = {};
41
49
  class StableBrowser {
42
- constructor(browser, page, logger = null, context = null) {
50
+ browser;
51
+ page;
52
+ logger;
53
+ context;
54
+ world;
55
+ project_path = null;
56
+ webLogFile = null;
57
+ networkLogger = null;
58
+ configuration = null;
59
+ appName = "main";
60
+ constructor(browser, page, logger = null, context = null, world = null) {
43
61
  this.browser = browser;
44
62
  this.page = page;
45
63
  this.logger = logger;
46
64
  this.context = context;
47
- this.project_path = null;
48
- this.webLogFile = null;
49
- this.configuration = null;
65
+ this.world = world;
50
66
  if (!this.logger) {
51
67
  this.logger = console;
52
68
  }
@@ -72,19 +88,36 @@ class StableBrowser {
72
88
  this.logger.error("unable to read ai_config.json");
73
89
  }
74
90
  const logFolder = path.join(this.project_path, "logs", "web");
75
- this.webLogFile = this.getWebLogFile(logFolder);
76
- this.registerConsoleLogListener(page, context, this.webLogFile);
77
- this.registerRequestListener();
91
+ this.world = world;
78
92
  context.pages = [this.page];
79
93
  context.pageLoading = { status: false };
94
+ this.registerEventListeners(this.context);
95
+ }
96
+ registerEventListeners(context) {
97
+ this.registerConsoleLogListener(this.page, context);
98
+ this.registerRequestListener(this.page, context, this.webLogFile);
99
+ if (!context.pageLoading) {
100
+ context.pageLoading = { status: false };
101
+ }
80
102
  context.playContext.on("page", async function (page) {
81
103
  context.pageLoading.status = true;
82
104
  this.page = page;
83
105
  context.page = page;
84
106
  context.pages.push(page);
85
- this.webLogFile = this.getWebLogFile(logFolder);
86
- this.registerConsoleLogListener(page, context, this.webLogFile);
87
- this.registerRequestListener();
107
+ page.on("close", async () => {
108
+ if (this.context && this.context.pages && this.context.pages.length > 1) {
109
+ this.context.pages.pop();
110
+ this.page = this.context.pages[this.context.pages.length - 1];
111
+ this.context.page = this.page;
112
+ try {
113
+ let title = await this.page.title();
114
+ console.log("Switched to page " + title);
115
+ }
116
+ catch (error) {
117
+ console.error("Error on page close", error);
118
+ }
119
+ }
120
+ });
88
121
  try {
89
122
  await this.waitForPageLoad();
90
123
  console.log("Switch page: " + (await page.title()));
@@ -95,6 +128,36 @@ class StableBrowser {
95
128
  context.pageLoading.status = false;
96
129
  }.bind(this));
97
130
  }
131
+ async switchApp(appName) {
132
+ // check if the current app (this.appName) is the same as the new app
133
+ if (this.appName === appName) {
134
+ return;
135
+ }
136
+ let navigate = false;
137
+ if (!apps[appName]) {
138
+ let newContext = await getContext(null, false, this.logger, appName, false, this);
139
+ navigate = true;
140
+ apps[appName] = {
141
+ context: newContext,
142
+ browser: newContext.browser,
143
+ page: newContext.page,
144
+ };
145
+ }
146
+ const tempContext = {};
147
+ this._copyContext(this, tempContext);
148
+ this._copyContext(apps[appName], this);
149
+ apps[this.appName] = tempContext;
150
+ this.appName = appName;
151
+ if (navigate) {
152
+ await this.goto(this.context.environment.baseUrl);
153
+ await this.waitForPageLoad();
154
+ }
155
+ }
156
+ _copyContext(from, to) {
157
+ to.browser = from.browser;
158
+ to.page = from.page;
159
+ to.context = from.context;
160
+ }
98
161
  getWebLogFile(logFolder) {
99
162
  if (!fs.existsSync(logFolder)) {
100
163
  fs.mkdirSync(logFolder, { recursive: true });
@@ -106,37 +169,63 @@ class StableBrowser {
106
169
  const fileName = nextIndex + ".json";
107
170
  return path.join(logFolder, fileName);
108
171
  }
109
- registerConsoleLogListener(page, context, logFile) {
172
+ registerConsoleLogListener(page, context) {
110
173
  if (!this.context.webLogger) {
111
174
  this.context.webLogger = [];
112
175
  }
113
176
  page.on("console", async (msg) => {
114
- this.context.webLogger.push({
177
+ const obj = {
115
178
  type: msg.type(),
116
179
  text: msg.text(),
117
180
  location: msg.location(),
118
181
  time: new Date().toISOString(),
119
- });
120
- await fs.promises.writeFile(logFile, JSON.stringify(this.context.webLogger, null, 2));
182
+ };
183
+ this.context.webLogger.push(obj);
184
+ if (msg.type() === "error") {
185
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
186
+ }
121
187
  });
122
188
  }
123
- registerRequestListener() {
124
- this.page.on("request", async (data) => {
189
+ registerRequestListener(page, context, logFile) {
190
+ if (!this.context.networkLogger) {
191
+ this.context.networkLogger = [];
192
+ }
193
+ page.on("request", async (data) => {
194
+ const startTime = new Date().getTime();
125
195
  try {
126
- const pageUrl = new URL(this.page.url());
196
+ const pageUrl = new URL(page.url());
127
197
  const requestUrl = new URL(data.url());
128
198
  if (pageUrl.hostname === requestUrl.hostname) {
129
199
  const method = data.method();
130
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
200
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
131
201
  const token = await data.headerValue("Authorization");
132
202
  if (token) {
133
- this.context.authtoken = token;
203
+ context.authtoken = token;
134
204
  }
135
205
  }
136
206
  }
207
+ const response = await data.response();
208
+ const endTime = new Date().getTime();
209
+ const obj = {
210
+ url: data.url(),
211
+ method: data.method(),
212
+ postData: data.postData(),
213
+ error: data.failure() ? data.failure().errorText : null,
214
+ duration: endTime - startTime,
215
+ startTime,
216
+ };
217
+ context.networkLogger.push(obj);
218
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
137
219
  }
138
220
  catch (error) {
139
221
  console.error("Error in request listener", error);
222
+ context.networkLogger.push({
223
+ error: "not able to listen",
224
+ message: error.message,
225
+ stack: error.stack,
226
+ time: new Date().toISOString(),
227
+ });
228
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
140
229
  }
141
230
  });
142
231
  }
@@ -151,20 +240,6 @@ class StableBrowser {
151
240
  timeout: 60000,
152
241
  });
153
242
  }
154
- _validateSelectors(selectors) {
155
- if (!selectors) {
156
- throw new Error("selectors is null");
157
- }
158
- if (!selectors.locators) {
159
- throw new Error("selectors.locators is null");
160
- }
161
- if (!Array.isArray(selectors.locators)) {
162
- throw new Error("selectors.locators expected to be array");
163
- }
164
- if (selectors.locators.length === 0) {
165
- throw new Error("selectors.locators expected to be non empty array");
166
- }
167
- }
168
243
  _fixUsingParams(text, _params) {
169
244
  if (!_params || typeof text !== "string") {
170
245
  return text;
@@ -179,27 +254,84 @@ class StableBrowser {
179
254
  }
180
255
  return text;
181
256
  }
182
- _getLocator(locator, scope, _params) {
183
- if (locator.type === "pw_selector") {
184
- return scope.locator(locator.selector);
257
+ _fixLocatorUsingParams(locator, _params) {
258
+ // check if not null
259
+ if (!locator) {
260
+ return locator;
261
+ }
262
+ // clone the locator
263
+ locator = JSON.parse(JSON.stringify(locator));
264
+ this.scanAndManipulate(locator, _params);
265
+ return locator;
266
+ }
267
+ _isObject(value) {
268
+ return value && typeof value === "object" && value.constructor === Object;
269
+ }
270
+ scanAndManipulate(currentObj, _params) {
271
+ for (const key in currentObj) {
272
+ if (typeof currentObj[key] === "string") {
273
+ // Perform string manipulation
274
+ currentObj[key] = this._fixUsingParams(currentObj[key], _params);
275
+ }
276
+ else if (this._isObject(currentObj[key])) {
277
+ // Recursively scan nested objects
278
+ this.scanAndManipulate(currentObj[key], _params);
279
+ }
185
280
  }
281
+ }
282
+ _getLocator(locator, scope, _params) {
283
+ locator = this._fixLocatorUsingParams(locator, _params);
284
+ let locatorReturn;
186
285
  if (locator.role) {
187
286
  if (locator.role[1].nameReg) {
188
287
  locator.role[1].name = reg_parser(locator.role[1].nameReg);
189
288
  delete locator.role[1].nameReg;
190
289
  }
191
- if (locator.role[1].name) {
192
- locator.role[1].name = this._fixUsingParams(locator.role[1].name, _params);
193
- }
194
- return scope.getByRole(locator.role[0], locator.role[1]);
290
+ // if (locator.role[1].name) {
291
+ // locator.role[1].name = this._fixUsingParams(locator.role[1].name, _params);
292
+ // }
293
+ locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
195
294
  }
196
295
  if (locator.css) {
197
- return scope.locator(this._fixUsingParams(locator.css, _params));
296
+ locatorReturn = scope.locator(locator.css);
198
297
  }
199
- throw new Error("unknown locator type");
298
+ // handle role/name locators
299
+ // locator.selector will be something like: textbox[name="Username"i]
300
+ if (locator.engine === "internal:role") {
301
+ // extract the role, name and the i/s flags using regex
302
+ const match = locator.selector.match(/(.*)\[(.*)="(.*)"(.*)\]/);
303
+ if (match) {
304
+ const role = match[1];
305
+ const name = match[3];
306
+ const flags = match[4];
307
+ locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
308
+ }
309
+ }
310
+ if (locator?.engine) {
311
+ if (locator.engine === "css") {
312
+ locatorReturn = scope.locator(locator.selector);
313
+ }
314
+ else {
315
+ let selector = locator.selector;
316
+ if (locator.engine === "internal:attr") {
317
+ if (!selector.startsWith("[")) {
318
+ selector = `[${selector}]`;
319
+ }
320
+ }
321
+ locatorReturn = scope.locator(`${locator.engine}=${selector}`);
322
+ }
323
+ }
324
+ if (!locatorReturn) {
325
+ console.error(locator);
326
+ throw new Error("Locator undefined");
327
+ }
328
+ return locatorReturn;
200
329
  }
201
330
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
202
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
331
+ if (css && css.locator) {
332
+ css = css.locator;
333
+ }
334
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, false, _params);
203
335
  if (result.elementCount === 0) {
204
336
  return;
205
337
  }
@@ -214,7 +346,7 @@ class StableBrowser {
214
346
  }
215
347
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
216
348
  //const stringifyText = JSON.stringify(text);
217
- return await scope.evaluate(([text, tag, regex, partial]) => {
349
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
218
350
  function isParent(parent, child) {
219
351
  let currentNode = child.parentNode;
220
352
  while (currentNode !== null) {
@@ -226,6 +358,15 @@ class StableBrowser {
226
358
  return false;
227
359
  }
228
360
  document.isParent = isParent;
361
+ function getRegex(str) {
362
+ const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
363
+ if (!match) {
364
+ return null;
365
+ }
366
+ let [_, pattern, flags] = match;
367
+ return new RegExp(pattern, flags);
368
+ }
369
+ document.getRegex = getRegex;
229
370
  function collectAllShadowDomElements(element, result = []) {
230
371
  // Check and add the element if it has a shadow root
231
372
  if (element.shadowRoot) {
@@ -244,6 +385,10 @@ class StableBrowser {
244
385
  if (!tag) {
245
386
  tag = "*";
246
387
  }
388
+ let regexpSearch = document.getRegex(text);
389
+ if (regexpSearch) {
390
+ regex = true;
391
+ }
247
392
  let elements = Array.from(document.querySelectorAll(tag));
248
393
  let shadowHosts = [];
249
394
  document.collectAllShadowDomElements(document, shadowHosts);
@@ -259,7 +404,9 @@ class StableBrowser {
259
404
  let randomToken = null;
260
405
  const foundElements = [];
261
406
  if (regex) {
262
- let regexpSearch = new RegExp(text, "im");
407
+ if (!regexpSearch) {
408
+ regexpSearch = new RegExp(text, "im");
409
+ }
263
410
  for (let i = 0; i < elements.length; i++) {
264
411
  const element = elements[i];
265
412
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -273,8 +420,8 @@ class StableBrowser {
273
420
  for (let i = 0; i < elements.length; i++) {
274
421
  const element = elements[i];
275
422
  if (partial) {
276
- if ((element.innerText && element.innerText.trim().includes(text)) ||
277
- (element.value && element.value.includes(text))) {
423
+ if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
424
+ (element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
278
425
  foundElements.push(element);
279
426
  }
280
427
  }
@@ -319,6 +466,12 @@ class StableBrowser {
319
466
  }
320
467
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
321
468
  let locatorSearch = selectorHierarchy[index];
469
+ try {
470
+ locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
471
+ }
472
+ catch (e) {
473
+ console.error(e);
474
+ }
322
475
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
323
476
  let locator = null;
324
477
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
@@ -407,15 +560,20 @@ class StableBrowser {
407
560
  if (result.foundElements.length > 0) {
408
561
  let dialogCloseLocator = result.foundElements[0].locator;
409
562
  await dialogCloseLocator.click();
563
+ // wait for the dialog to close
564
+ await dialogCloseLocator.waitFor({ state: "hidden" });
410
565
  return { rerun: true };
411
566
  }
412
567
  }
413
568
  }
414
569
  return { rerun: false };
415
570
  }
416
- async _locate(selectors, info, _params, timeout = 30000) {
571
+ async _locate(selectors, info, _params, timeout) {
572
+ if (!timeout) {
573
+ timeout = 30000;
574
+ }
417
575
  for (let i = 0; i < 3; i++) {
418
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
576
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
419
577
  for (let j = 0; j < selectors.locators.length; j++) {
420
578
  let selector = selectors.locators[j];
421
579
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -427,17 +585,44 @@ class StableBrowser {
427
585
  }
428
586
  throw new Error("unable to locate element " + JSON.stringify(selectors));
429
587
  }
430
- async _locate_internal(selectors, info, _params, timeout = 30000) {
431
- let highPriorityTimeout = 5000;
432
- let visibleOnlyTimeout = 6000;
433
- let startTime = performance.now();
434
- let locatorsCount = 0;
435
- //let arrayMode = Array.isArray(selectors);
588
+ async _findFrameScope(selectors, timeout = 30000) {
436
589
  let scope = this.page;
590
+ if (selectors.frame) {
591
+ return selectors.frame;
592
+ }
437
593
  if (selectors.iframe_src || selectors.frameLocators) {
438
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
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
+ };
439
619
  while (true) {
440
620
  let frameFound = false;
621
+ if (selectors.nestFrmLoc) {
622
+ scope = await findFrame(selectors.nestFrmLoc, scope);
623
+ frameFound = true;
624
+ break;
625
+ }
441
626
  if (selectors.frameLocators) {
442
627
  for (let i = 0; i < selectors.frameLocators.length; i++) {
443
628
  let frameLocator = selectors.frameLocators[i];
@@ -463,6 +648,25 @@ class StableBrowser {
463
648
  }
464
649
  }
465
650
  }
651
+ if (!scope) {
652
+ scope = this.page;
653
+ }
654
+ return scope;
655
+ }
656
+ async _getDocumentBody(selectors, timeout = 30000) {
657
+ let scope = await this._findFrameScope(selectors, timeout);
658
+ return scope.evaluate(() => {
659
+ var bodyContent = document.body.innerHTML;
660
+ return bodyContent;
661
+ });
662
+ }
663
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
664
+ let highPriorityTimeout = 5000;
665
+ let visibleOnlyTimeout = 6000;
666
+ let startTime = performance.now();
667
+ let locatorsCount = 0;
668
+ //let arrayMode = Array.isArray(selectors);
669
+ let scope = await this._findFrameScope(selectors, timeout);
466
670
  let selectorsLocators = null;
467
671
  selectorsLocators = selectors.locators;
468
672
  // group selectors by priority
@@ -598,83 +802,121 @@ class StableBrowser {
598
802
  }
599
803
  return result;
600
804
  }
601
- async click(selectors, _params, options = {}, world = null) {
602
- this._validateSelectors(selectors);
805
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
603
806
  const startTime = Date.now();
604
- const info = {};
605
- info.log = "***** click on " + selectors.element_name + " *****\n";
606
- info.operation = "click";
607
- info.selectors = selectors;
608
- let error = null;
609
- let screenshotId = null;
610
- let screenshotPath = null;
807
+ let timeout = 30000;
808
+ if (options && options.timeout) {
809
+ timeout = options.timeout;
810
+ }
811
+ while (true) {
812
+ try {
813
+ const result = await locate_element(this.context, elementDescription, "click");
814
+ if (result?.elementNumber >= 0) {
815
+ const selectors = {
816
+ frame: result?.frame,
817
+ locators: [
818
+ {
819
+ css: result?.css,
820
+ },
821
+ ],
822
+ };
823
+ await this.click(selectors, _params, options, world);
824
+ return;
825
+ }
826
+ }
827
+ catch (e) {
828
+ if (performance.now() - startTime > timeout) {
829
+ throw e;
830
+ }
831
+ }
832
+ await new Promise((resolve) => setTimeout(resolve, 3000));
833
+ }
834
+ }
835
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
836
+ const startTime = Date.now();
837
+ let timeout = 30000;
838
+ if (options && options.timeout) {
839
+ timeout = options.timeout;
840
+ }
841
+ while (true) {
842
+ try {
843
+ const result = await locate_element(this.context, elementDescription, "fill", value);
844
+ if (result?.elementNumber >= 0) {
845
+ const selectors = {
846
+ frame: result?.frame,
847
+ locators: [
848
+ {
849
+ css: result?.css,
850
+ },
851
+ ],
852
+ };
853
+ await this.clickType(selectors, value, false, _params, options, world);
854
+ return;
855
+ }
856
+ }
857
+ catch (e) {
858
+ if (performance.now() - startTime > timeout) {
859
+ throw e;
860
+ }
861
+ }
862
+ await new Promise((resolve) => setTimeout(resolve, 3000));
863
+ }
864
+ }
865
+ async click(selectors, _params, options = {}, world = null) {
866
+ const state = {
867
+ selectors,
868
+ _params,
869
+ options,
870
+ world,
871
+ text: "Click element",
872
+ type: Types.CLICK,
873
+ operation: "click",
874
+ log: "***** click on " + selectors.element_name + " *****\n",
875
+ };
611
876
  try {
612
- let element = await this._locate(selectors, info, _params);
613
- await this.scrollIfNeeded(element, info);
614
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
877
+ await _preCommand(state, this);
878
+ if (state.options && state.options.context) {
879
+ state.selectors.locators[0].text = state.options.context;
880
+ }
615
881
  try {
616
- await this._highlightElements(element);
617
- await element.click({ timeout: 5000 });
882
+ await state.element.click();
618
883
  await new Promise((resolve) => setTimeout(resolve, 1000));
619
884
  }
620
885
  catch (e) {
621
886
  // await this.closeUnexpectedPopups();
622
- info.log += "click failed, will try again" + "\n";
623
- element = await this._locate(selectors, info, _params);
624
- await element.click({ timeout: 10000, force: true });
887
+ state.element = await this._locate(selectors, state.info, _params);
888
+ await state.element.dispatchEvent("click");
625
889
  await new Promise((resolve) => setTimeout(resolve, 1000));
626
890
  }
627
891
  await this.waitForPageLoad();
628
- return info;
892
+ return state.info;
629
893
  }
630
894
  catch (e) {
631
- this.logger.error("click failed " + info.log);
632
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
633
- info.screenshotPath = screenshotPath;
634
- Object.assign(e, { info: info });
635
- error = e;
636
- throw e;
895
+ await _commandError(state, e, this);
637
896
  }
638
897
  finally {
639
- const endTime = Date.now();
640
- this._reportToWorld(world, {
641
- element_name: selectors.element_name,
642
- type: Types.CLICK,
643
- text: `Click element`,
644
- screenshotId,
645
- result: error
646
- ? {
647
- status: "FAILED",
648
- startTime,
649
- endTime,
650
- message: error === null || error === void 0 ? void 0 : error.message,
651
- }
652
- : {
653
- status: "PASSED",
654
- startTime,
655
- endTime,
656
- },
657
- info: info,
658
- });
898
+ _commandFinally(state, this);
659
899
  }
660
900
  }
661
901
  async setCheck(selectors, checked = true, _params, options = {}, world = null) {
662
- this._validateSelectors(selectors);
663
- const startTime = Date.now();
664
- const info = {};
665
- info.log = "";
666
- info.operation = "setCheck";
667
- info.checked = checked;
668
- info.selectors = selectors;
669
- let error = null;
670
- let screenshotId = null;
671
- let screenshotPath = null;
902
+ const state = {
903
+ selectors,
904
+ _params,
905
+ options,
906
+ world,
907
+ type: checked ? Types.CHECK : Types.UNCHECK,
908
+ text: checked ? `Check element` : `Uncheck element`,
909
+ operation: "setCheck",
910
+ log: "***** check " + selectors.element_name + " *****\n",
911
+ };
672
912
  try {
673
- let element = await this._locate(selectors, info, _params);
674
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
913
+ await _preCommand(state, this);
914
+ state.info.checked = checked;
915
+ // let element = await this._locate(selectors, info, _params);
916
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
675
917
  try {
676
- await this._highlightElements(element);
677
- await element.setChecked(checked, { timeout: 5000 });
918
+ // await this._highlightElements(element);
919
+ await state.element.setChecked(checked);
678
920
  await new Promise((resolve) => setTimeout(resolve, 1000));
679
921
  }
680
922
  catch (e) {
@@ -683,179 +925,108 @@ class StableBrowser {
683
925
  }
684
926
  else {
685
927
  //await this.closeUnexpectedPopups();
686
- info.log += "setCheck failed, will try again" + "\n";
687
- element = await this._locate(selectors, info, _params);
688
- await element.setChecked(checked, { timeout: 5000, force: true });
928
+ state.info.log += "setCheck failed, will try again" + "\n";
929
+ state.element = await this._locate(selectors, state.info, _params);
930
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
689
931
  await new Promise((resolve) => setTimeout(resolve, 1000));
690
932
  }
691
933
  }
692
934
  await this.waitForPageLoad();
693
- return info;
935
+ return state.info;
694
936
  }
695
937
  catch (e) {
696
- this.logger.error("setCheck failed " + info.log);
697
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
698
- info.screenshotPath = screenshotPath;
699
- Object.assign(e, { info: info });
700
- error = e;
701
- throw e;
938
+ await _commandError(state, e, this);
702
939
  }
703
940
  finally {
704
- const endTime = Date.now();
705
- this._reportToWorld(world, {
706
- element_name: selectors.element_name,
707
- type: checked ? Types.CHECK : Types.UNCHECK,
708
- text: checked ? `Check element` : `Uncheck element`,
709
- screenshotId,
710
- result: error
711
- ? {
712
- status: "FAILED",
713
- startTime,
714
- endTime,
715
- message: error === null || error === void 0 ? void 0 : error.message,
716
- }
717
- : {
718
- status: "PASSED",
719
- startTime,
720
- endTime,
721
- },
722
- info: info,
723
- });
941
+ _commandFinally(state, this);
724
942
  }
725
943
  }
726
944
  async hover(selectors, _params, options = {}, world = null) {
727
- this._validateSelectors(selectors);
728
- const startTime = Date.now();
729
- const info = {};
730
- info.log = "";
731
- info.operation = "hover";
732
- info.selectors = selectors;
733
- let error = null;
734
- let screenshotId = null;
735
- let screenshotPath = null;
945
+ const state = {
946
+ selectors,
947
+ _params,
948
+ options,
949
+ world,
950
+ type: Types.HOVER,
951
+ text: `Hover element`,
952
+ operation: "hover",
953
+ log: "***** hover " + selectors.element_name + " *****\n",
954
+ };
736
955
  try {
737
- let element = await this._locate(selectors, info, _params);
738
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
956
+ await _preCommand(state, this);
739
957
  try {
740
- await this._highlightElements(element);
741
- await element.hover({ timeout: 10000 });
958
+ await state.element.hover();
742
959
  await new Promise((resolve) => setTimeout(resolve, 1000));
743
960
  }
744
961
  catch (e) {
745
962
  //await this.closeUnexpectedPopups();
746
- info.log += "hover failed, will try again" + "\n";
747
- element = await this._locate(selectors, info, _params);
748
- await element.hover({ timeout: 10000 });
963
+ state.info.log += "hover failed, will try again" + "\n";
964
+ state.element = await this._locate(selectors, state.info, _params);
965
+ await state.element.hover({ timeout: 10000 });
749
966
  await new Promise((resolve) => setTimeout(resolve, 1000));
750
967
  }
751
968
  await this.waitForPageLoad();
752
- return info;
969
+ return state.info;
753
970
  }
754
971
  catch (e) {
755
- this.logger.error("hover failed " + info.log);
756
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
757
- info.screenshotPath = screenshotPath;
758
- Object.assign(e, { info: info });
759
- error = e;
760
- throw e;
972
+ await _commandError(state, e, this);
761
973
  }
762
974
  finally {
763
- const endTime = Date.now();
764
- this._reportToWorld(world, {
765
- element_name: selectors.element_name,
766
- type: Types.HOVER,
767
- text: `Hover element`,
768
- screenshotId,
769
- result: error
770
- ? {
771
- status: "FAILED",
772
- startTime,
773
- endTime,
774
- message: error === null || error === void 0 ? void 0 : error.message,
775
- }
776
- : {
777
- status: "PASSED",
778
- startTime,
779
- endTime,
780
- },
781
- info: info,
782
- });
975
+ _commandFinally(state, this);
783
976
  }
784
977
  }
785
978
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
786
- this._validateSelectors(selectors);
787
979
  if (!values) {
788
980
  throw new Error("values is null");
789
981
  }
790
- const startTime = Date.now();
791
- let error = null;
792
- let screenshotId = null;
793
- let screenshotPath = null;
794
- const info = {};
795
- info.log = "";
796
- info.operation = "selectOptions";
797
- info.selectors = selectors;
982
+ const state = {
983
+ selectors,
984
+ _params,
985
+ options,
986
+ world,
987
+ value: values.toString(),
988
+ type: Types.SELECT,
989
+ text: `Select option: ${values}`,
990
+ operation: "selectOption",
991
+ log: "***** select option " + selectors.element_name + " *****\n",
992
+ };
798
993
  try {
799
- let element = await this._locate(selectors, info, _params);
800
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
994
+ await _preCommand(state, this);
801
995
  try {
802
- await this._highlightElements(element);
803
- await element.selectOption(values, { timeout: 5000 });
996
+ await state.element.selectOption(values);
804
997
  }
805
998
  catch (e) {
806
999
  //await this.closeUnexpectedPopups();
807
- info.log += "selectOption failed, will try force" + "\n";
808
- await element.selectOption(values, { timeout: 10000, force: true });
1000
+ state.info.log += "selectOption failed, will try force" + "\n";
1001
+ await state.element.selectOption(values, { timeout: 10000, force: true });
809
1002
  }
810
1003
  await this.waitForPageLoad();
811
- return info;
1004
+ return state.info;
812
1005
  }
813
1006
  catch (e) {
814
- this.logger.error("selectOption failed " + info.log);
815
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
816
- info.screenshotPath = screenshotPath;
817
- Object.assign(e, { info: info });
818
- this.logger.info("click failed, will try next selector");
819
- error = e;
820
- throw e;
1007
+ await _commandError(state, e, this);
821
1008
  }
822
1009
  finally {
823
- const endTime = Date.now();
824
- this._reportToWorld(world, {
825
- element_name: selectors.element_name,
826
- type: Types.SELECT,
827
- text: `Select option: ${values}`,
828
- value: values.toString(),
829
- screenshotId,
830
- result: error
831
- ? {
832
- status: "FAILED",
833
- startTime,
834
- endTime,
835
- message: error === null || error === void 0 ? void 0 : error.message,
836
- }
837
- : {
838
- status: "PASSED",
839
- startTime,
840
- endTime,
841
- },
842
- info: info,
843
- });
1010
+ _commandFinally(state, this);
844
1011
  }
845
1012
  }
846
1013
  async type(_value, _params = null, options = {}, world = null) {
847
- const startTime = Date.now();
848
- let error = null;
849
- let screenshotId = null;
850
- let screenshotPath = null;
851
- const info = {};
852
- info.log = "";
853
- info.operation = "type";
854
- _value = this._fixUsingParams(_value, _params);
855
- info.value = _value;
1014
+ const state = {
1015
+ value: _value,
1016
+ _params,
1017
+ options,
1018
+ world,
1019
+ locate: false,
1020
+ scroll: false,
1021
+ highlight: false,
1022
+ type: Types.TYPE_PRESS,
1023
+ text: `Type value: ${_value}`,
1024
+ operation: "type",
1025
+ log: "",
1026
+ };
856
1027
  try {
857
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
858
- const valueSegment = _value.split("&&");
1028
+ await _preCommand(state, this);
1029
+ const valueSegment = state.value.split("&&");
859
1030
  for (let i = 0; i < valueSegment.length; i++) {
860
1031
  if (i > 0) {
861
1032
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -875,134 +1046,53 @@ class StableBrowser {
875
1046
  await this.page.keyboard.type(value);
876
1047
  }
877
1048
  }
878
- return info;
1049
+ return state.info;
879
1050
  }
880
1051
  catch (e) {
881
- //await this.closeUnexpectedPopups();
882
- this.logger.error("type failed " + info.log);
883
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
884
- info.screenshotPath = screenshotPath;
885
- Object.assign(e, { info: info });
886
- error = e;
887
- throw e;
1052
+ await _commandError(state, e, this);
888
1053
  }
889
1054
  finally {
890
- const endTime = Date.now();
891
- this._reportToWorld(world, {
892
- type: Types.TYPE_PRESS,
893
- screenshotId,
894
- value: _value,
895
- text: `type value: ${_value}`,
896
- result: error
897
- ? {
898
- status: "FAILED",
899
- startTime,
900
- endTime,
901
- message: error === null || error === void 0 ? void 0 : error.message,
902
- }
903
- : {
904
- status: "PASSED",
905
- startTime,
906
- endTime,
907
- },
908
- info: info,
909
- });
1055
+ _commandFinally(state, this);
910
1056
  }
911
1057
  }
912
- async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
913
- this._validateSelectors(selectors);
914
- const startTime = Date.now();
915
- let error = null;
916
- let screenshotId = null;
917
- let screenshotPath = null;
918
- const info = {};
919
- info.log = "";
920
- info.operation = Types.SET_DATE_TIME;
921
- info.selectors = selectors;
922
- info.value = value;
1058
+ async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1059
+ const state = {
1060
+ selectors,
1061
+ _params,
1062
+ value,
1063
+ options,
1064
+ world,
1065
+ type: Types.SET_INPUT,
1066
+ text: `Set input value`,
1067
+ operation: "setInputValue",
1068
+ log: "***** set input value " + selectors.element_name + " *****\n",
1069
+ };
923
1070
  try {
924
- value = await this._replaceWithLocalData(value, this);
925
- let element = await this._locate(selectors, info, _params);
926
- //insert red border around the element
927
- await this.scrollIfNeeded(element, info);
928
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
929
- await this._highlightElements(element);
1071
+ await _preCommand(state, this);
1072
+ let value = await this._replaceWithLocalData(state.value, this);
930
1073
  try {
931
- await element.click();
932
- await new Promise((resolve) => setTimeout(resolve, 500));
933
- if (format) {
934
- value = dayjs(value).format(format);
935
- await element.fill(value);
936
- }
937
- else {
938
- const dateTimeValue = await getDateTimeValue({ value, element });
939
- await element.evaluateHandle((el, dateTimeValue) => {
940
- el.value = ""; // clear input
941
- el.value = dateTimeValue;
942
- }, dateTimeValue);
943
- }
944
- if (enter) {
945
- await new Promise((resolve) => setTimeout(resolve, 2000));
946
- await this.page.keyboard.press("Enter");
947
- await this.waitForPageLoad();
948
- }
1074
+ await state.element.evaluateHandle((el, value) => {
1075
+ el.value = value;
1076
+ }, value);
949
1077
  }
950
1078
  catch (error) {
951
- //await this.closeUnexpectedPopups();
952
- this.logger.error("setting date time input failed " + JSON.stringify(info));
953
- this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
954
- info.screenshotPath = screenshotPath;
955
- Object.assign(error, { info: info });
956
- await element.click();
957
- await new Promise((resolve) => setTimeout(resolve, 500));
958
- if (format) {
959
- value = dayjs(value).format(format);
960
- await element.fill(value);
961
- }
962
- else {
963
- const dateTimeValue = await getDateTimeValue({ value, element });
964
- await element.evaluateHandle((el, dateTimeValue) => {
965
- el.value = ""; // clear input
966
- el.value = dateTimeValue;
967
- }, dateTimeValue);
968
- }
969
- if (enter) {
970
- await new Promise((resolve) => setTimeout(resolve, 2000));
971
- await this.page.keyboard.press("Enter");
972
- await this.waitForPageLoad();
973
- }
1079
+ this.logger.error("setInputValue failed, will try again");
1080
+ await _screenshot(state, this);
1081
+ Object.assign(error, { info: state.info });
1082
+ await state.element.evaluateHandle((el, value) => {
1083
+ el.value = value;
1084
+ });
974
1085
  }
975
1086
  }
976
- catch (error) {
977
- error = e;
978
- throw e;
979
- }
980
- finally {
981
- const endTime = Date.now();
982
- this._reportToWorld(world, {
983
- element_name: selectors.element_name,
984
- type: Types.SET_DATE_TIME,
985
- screenshotId,
986
- value: value,
987
- text: `setDateTime input with value: ${value}`,
988
- result: error
989
- ? {
990
- status: "FAILED",
991
- startTime,
992
- endTime,
993
- message: error === null || error === void 0 ? void 0 : error.message,
994
- }
995
- : {
996
- status: "PASSED",
997
- startTime,
998
- endTime,
999
- },
1000
- info: info,
1001
- });
1087
+ catch (e) {
1088
+ await _commandError(state, e, this);
1089
+ }
1090
+ finally {
1091
+ _commandFinally(state, this);
1002
1092
  }
1003
1093
  }
1004
- async setDateTime(selectors, value, enter = false, _params = null, options = {}, world = null) {
1005
- this._validateSelectors(selectors);
1094
+ async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1095
+ _validateSelectors(selectors);
1006
1096
  const startTime = Date.now();
1007
1097
  let error = null;
1008
1098
  let screenshotId = null;
@@ -1013,6 +1103,7 @@ class StableBrowser {
1013
1103
  info.selectors = selectors;
1014
1104
  info.value = value;
1015
1105
  try {
1106
+ value = await this._replaceWithLocalData(value, this);
1016
1107
  let element = await this._locate(selectors, info, _params);
1017
1108
  //insert red border around the element
1018
1109
  await this.scrollIfNeeded(element, info);
@@ -1021,28 +1112,51 @@ class StableBrowser {
1021
1112
  try {
1022
1113
  await element.click();
1023
1114
  await new Promise((resolve) => setTimeout(resolve, 500));
1024
- const dateTimeValue = await getDateTimeValue({ value, element });
1025
- await element.evaluateHandle((el, dateTimeValue) => {
1026
- el.value = ""; // clear input
1027
- el.value = dateTimeValue;
1028
- }, dateTimeValue);
1115
+ if (format) {
1116
+ value = dayjs(value).format(format);
1117
+ await element.fill(value);
1118
+ }
1119
+ else {
1120
+ const dateTimeValue = await getDateTimeValue({ value, element });
1121
+ await element.evaluateHandle((el, dateTimeValue) => {
1122
+ el.value = ""; // clear input
1123
+ el.value = dateTimeValue;
1124
+ }, dateTimeValue);
1125
+ }
1126
+ if (enter) {
1127
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1128
+ await this.page.keyboard.press("Enter");
1129
+ await this.waitForPageLoad();
1130
+ }
1029
1131
  }
1030
- catch (error) {
1132
+ catch (err) {
1031
1133
  //await this.closeUnexpectedPopups();
1032
1134
  this.logger.error("setting date time input failed " + JSON.stringify(info));
1033
- this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
1135
+ this.logger.info("Trying again");
1136
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1034
1137
  info.screenshotPath = screenshotPath;
1035
- Object.assign(error, { info: info });
1138
+ Object.assign(err, { info: info });
1036
1139
  await element.click();
1037
1140
  await new Promise((resolve) => setTimeout(resolve, 500));
1038
- const dateTimeValue = await getDateTimeValue({ value, element });
1039
- await element.evaluateHandle((el, dateTimeValue) => {
1040
- el.value = ""; // clear input
1041
- el.value = dateTimeValue;
1042
- }, dateTimeValue);
1141
+ if (format) {
1142
+ value = dayjs(value).format(format);
1143
+ await element.fill(value);
1144
+ }
1145
+ else {
1146
+ const dateTimeValue = await getDateTimeValue({ value, element });
1147
+ await element.evaluateHandle((el, dateTimeValue) => {
1148
+ el.value = ""; // clear input
1149
+ el.value = dateTimeValue;
1150
+ }, dateTimeValue);
1151
+ }
1152
+ if (enter) {
1153
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1154
+ await this.page.keyboard.press("Enter");
1155
+ await this.waitForPageLoad();
1156
+ }
1043
1157
  }
1044
1158
  }
1045
- catch (error) {
1159
+ catch (e) {
1046
1160
  error = e;
1047
1161
  throw e;
1048
1162
  }
@@ -1071,32 +1185,31 @@ class StableBrowser {
1071
1185
  }
1072
1186
  }
1073
1187
  async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
1074
- this._validateSelectors(selectors);
1075
- const startTime = Date.now();
1076
- let error = null;
1077
- let screenshotId = null;
1078
- let screenshotPath = null;
1079
- const info = {};
1080
- info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
1081
- info.operation = "clickType";
1082
- info.selectors = selectors;
1083
- const newValue = await this._replaceWithLocalData(_value, world);
1188
+ const state = {
1189
+ selectors,
1190
+ _params,
1191
+ value: unEscapeString(_value),
1192
+ options,
1193
+ world,
1194
+ type: Types.FILL,
1195
+ text: `Click type input with value: ${_value}`,
1196
+ operation: "clickType",
1197
+ log: "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n",
1198
+ };
1199
+ const newValue = await this._replaceWithLocalData(state.value, world);
1084
1200
  if (newValue !== _value) {
1085
1201
  //this.logger.info(_value + "=" + newValue);
1086
1202
  _value = newValue;
1203
+ state.value = newValue;
1087
1204
  }
1088
- info.value = _value;
1089
1205
  try {
1090
- let element = await this._locate(selectors, info, _params);
1091
- //insert red border around the element
1092
- await this.scrollIfNeeded(element, info);
1093
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1094
- await this._highlightElements(element);
1206
+ await _preCommand(state, this);
1207
+ state.info.value = _value;
1095
1208
  if (options === null || options === undefined || !options.press) {
1096
1209
  try {
1097
- let currentValue = await element.inputValue();
1210
+ let currentValue = await state.element.inputValue();
1098
1211
  if (currentValue) {
1099
- await element.fill("");
1212
+ await state.element.fill("");
1100
1213
  }
1101
1214
  }
1102
1215
  catch (e) {
@@ -1105,22 +1218,22 @@ class StableBrowser {
1105
1218
  }
1106
1219
  if (options === null || options === undefined || options.press) {
1107
1220
  try {
1108
- await element.click({ timeout: 5000 });
1221
+ await state.element.click({ timeout: 5000 });
1109
1222
  }
1110
1223
  catch (e) {
1111
- await element.dispatchEvent("click");
1224
+ await state.element.dispatchEvent("click");
1112
1225
  }
1113
1226
  }
1114
1227
  else {
1115
1228
  try {
1116
- await element.focus();
1229
+ await state.element.focus();
1117
1230
  }
1118
1231
  catch (e) {
1119
- await element.dispatchEvent("focus");
1232
+ await state.element.dispatchEvent("focus");
1120
1233
  }
1121
1234
  }
1122
1235
  await new Promise((resolve) => setTimeout(resolve, 500));
1123
- const valueSegment = _value.split("&&");
1236
+ const valueSegment = state.value.split("&&");
1124
1237
  for (let i = 0; i < valueSegment.length; i++) {
1125
1238
  if (i > 0) {
1126
1239
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1140,13 +1253,14 @@ class StableBrowser {
1140
1253
  await new Promise((resolve) => setTimeout(resolve, 500));
1141
1254
  }
1142
1255
  }
1256
+ await _screenshot(state, this);
1143
1257
  if (enter === true) {
1144
1258
  await new Promise((resolve) => setTimeout(resolve, 2000));
1145
1259
  await this.page.keyboard.press("Enter");
1146
1260
  await this.waitForPageLoad();
1147
1261
  }
1148
1262
  else if (enter === false) {
1149
- await element.dispatchEvent("change");
1263
+ await state.element.dispatchEvent("change");
1150
1264
  //await this.page.keyboard.press("Tab");
1151
1265
  }
1152
1266
  else {
@@ -1155,103 +1269,50 @@ class StableBrowser {
1155
1269
  await this.waitForPageLoad();
1156
1270
  }
1157
1271
  }
1158
- return info;
1272
+ return state.info;
1159
1273
  }
1160
1274
  catch (e) {
1161
- //await this.closeUnexpectedPopups();
1162
- this.logger.error("fill failed " + JSON.stringify(info));
1163
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1164
- info.screenshotPath = screenshotPath;
1165
- Object.assign(e, { info: info });
1166
- error = e;
1167
- throw e;
1275
+ await _commandError(state, e, this);
1168
1276
  }
1169
1277
  finally {
1170
- const endTime = Date.now();
1171
- this._reportToWorld(world, {
1172
- element_name: selectors.element_name,
1173
- type: Types.FILL,
1174
- screenshotId,
1175
- value: _value,
1176
- text: `clickType input with value: ${_value}`,
1177
- result: error
1178
- ? {
1179
- status: "FAILED",
1180
- startTime,
1181
- endTime,
1182
- message: error === null || error === void 0 ? void 0 : error.message,
1183
- }
1184
- : {
1185
- status: "PASSED",
1186
- startTime,
1187
- endTime,
1188
- },
1189
- info: info,
1190
- });
1278
+ _commandFinally(state, this);
1191
1279
  }
1192
1280
  }
1193
1281
  async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
1194
- this._validateSelectors(selectors);
1195
- const startTime = Date.now();
1196
- let error = null;
1197
- let screenshotId = null;
1198
- let screenshotPath = null;
1199
- const info = {};
1200
- info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
1201
- info.operation = "fill";
1202
- info.selectors = selectors;
1203
- info.value = value;
1282
+ const state = {
1283
+ selectors,
1284
+ _params,
1285
+ value: unEscapeString(value),
1286
+ options,
1287
+ world,
1288
+ type: Types.FILL,
1289
+ text: `Fill input with value: ${value}`,
1290
+ operation: "fill",
1291
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1292
+ };
1204
1293
  try {
1205
- let element = await this._locate(selectors, info, _params);
1206
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1207
- await this._highlightElements(element);
1208
- await element.fill(value, { timeout: 10000 });
1209
- await element.dispatchEvent("change");
1294
+ await _preCommand(state, this);
1295
+ await state.element.fill(value);
1296
+ await state.element.dispatchEvent("change");
1210
1297
  if (enter) {
1211
1298
  await new Promise((resolve) => setTimeout(resolve, 2000));
1212
1299
  await this.page.keyboard.press("Enter");
1213
1300
  }
1214
1301
  await this.waitForPageLoad();
1215
- return info;
1302
+ return state.info;
1216
1303
  }
1217
1304
  catch (e) {
1218
- //await this.closeUnexpectedPopups();
1219
- this.logger.error("fill failed " + info.log);
1220
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1221
- info.screenshotPath = screenshotPath;
1222
- Object.assign(e, { info: info });
1223
- error = e;
1224
- throw e;
1305
+ await _commandError(state, e, this);
1225
1306
  }
1226
1307
  finally {
1227
- const endTime = Date.now();
1228
- this._reportToWorld(world, {
1229
- element_name: selectors.element_name,
1230
- type: Types.FILL,
1231
- screenshotId,
1232
- value,
1233
- text: `Fill input with value: ${value}`,
1234
- result: error
1235
- ? {
1236
- status: "FAILED",
1237
- startTime,
1238
- endTime,
1239
- message: error === null || error === void 0 ? void 0 : error.message,
1240
- }
1241
- : {
1242
- status: "PASSED",
1243
- startTime,
1244
- endTime,
1245
- },
1246
- info: info,
1247
- });
1308
+ _commandFinally(state, this);
1248
1309
  }
1249
1310
  }
1250
1311
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1251
1312
  return await this._getText(selectors, 0, _params, options, info, world);
1252
1313
  }
1253
1314
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1254
- this._validateSelectors(selectors);
1315
+ _validateSelectors(selectors);
1255
1316
  let screenshotId = null;
1256
1317
  let screenshotPath = null;
1257
1318
  if (!info.log) {
@@ -1295,165 +1356,124 @@ class StableBrowser {
1295
1356
  }
1296
1357
  }
1297
1358
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1298
- var _a;
1299
- this._validateSelectors(selectors);
1300
1359
  if (!pattern) {
1301
1360
  throw new Error("pattern is null");
1302
1361
  }
1303
1362
  if (!text) {
1304
1363
  throw new Error("text is null");
1305
1364
  }
1365
+ const state = {
1366
+ selectors,
1367
+ _params,
1368
+ pattern,
1369
+ value: pattern,
1370
+ options,
1371
+ world,
1372
+ locate: false,
1373
+ scroll: false,
1374
+ screenshot: false,
1375
+ highlight: false,
1376
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1377
+ text: `Verify element contains pattern: ${pattern}`,
1378
+ operation: "containsPattern",
1379
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1380
+ };
1306
1381
  const newValue = await this._replaceWithLocalData(text, world);
1307
1382
  if (newValue !== text) {
1308
1383
  this.logger.info(text + "=" + newValue);
1309
1384
  text = newValue;
1310
1385
  }
1311
- const startTime = Date.now();
1312
- let error = null;
1313
- let screenshotId = null;
1314
- let screenshotPath = null;
1315
- const info = {};
1316
- info.log =
1317
- "***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
1318
- info.operation = "containsPattern";
1319
- info.selectors = selectors;
1320
- info.value = text;
1321
- info.pattern = pattern;
1322
1386
  let foundObj = null;
1323
1387
  try {
1324
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1388
+ await _preCommand(state, this);
1389
+ state.info.pattern = pattern;
1390
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1325
1391
  if (foundObj && foundObj.element) {
1326
- await this.scrollIfNeeded(foundObj.element, info);
1392
+ await this.scrollIfNeeded(foundObj.element, state.info);
1327
1393
  }
1328
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1394
+ await _screenshot(state, this);
1329
1395
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1330
1396
  pattern = pattern.replace("{text}", escapedText);
1331
1397
  let regex = new RegExp(pattern, "im");
1332
- 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))) {
1333
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1398
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1399
+ state.info.foundText = foundObj?.text;
1334
1400
  throw new Error("element doesn't contain text " + text);
1335
1401
  }
1336
- return info;
1402
+ return state.info;
1337
1403
  }
1338
1404
  catch (e) {
1339
- //await this.closeUnexpectedPopups();
1340
- this.logger.error("verify element contains text failed " + info.log);
1341
- this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
1342
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1343
- info.screenshotPath = screenshotPath;
1344
- Object.assign(e, { info: info });
1345
- error = e;
1346
- throw e;
1405
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1406
+ await _commandError(state, e, this);
1347
1407
  }
1348
1408
  finally {
1349
- const endTime = Date.now();
1350
- this._reportToWorld(world, {
1351
- element_name: selectors.element_name,
1352
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1353
- value: pattern,
1354
- text: `Verify element contains pattern: ${pattern}`,
1355
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1356
- result: error
1357
- ? {
1358
- status: "FAILED",
1359
- startTime,
1360
- endTime,
1361
- message: error === null || error === void 0 ? void 0 : error.message,
1362
- }
1363
- : {
1364
- status: "PASSED",
1365
- startTime,
1366
- endTime,
1367
- },
1368
- info: info,
1369
- });
1409
+ _commandFinally(state, this);
1370
1410
  }
1371
1411
  }
1372
1412
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1373
- var _a, _b, _c;
1374
- this._validateSelectors(selectors);
1413
+ const state = {
1414
+ selectors,
1415
+ _params,
1416
+ value: text,
1417
+ options,
1418
+ world,
1419
+ locate: false,
1420
+ scroll: false,
1421
+ screenshot: false,
1422
+ highlight: false,
1423
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1424
+ text: `Verify element contains text: ${text}`,
1425
+ operation: "containsText",
1426
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1427
+ };
1375
1428
  if (!text) {
1376
1429
  throw new Error("text is null");
1377
1430
  }
1378
- const startTime = Date.now();
1379
- let error = null;
1380
- let screenshotId = null;
1381
- let screenshotPath = null;
1382
- const info = {};
1383
- info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
1384
- info.operation = "containsText";
1385
- info.selectors = selectors;
1431
+ text = unEscapeString(text);
1386
1432
  const newValue = await this._replaceWithLocalData(text, world);
1387
1433
  if (newValue !== text) {
1388
1434
  this.logger.info(text + "=" + newValue);
1389
1435
  text = newValue;
1390
1436
  }
1391
- info.value = text;
1392
1437
  let foundObj = null;
1393
1438
  try {
1394
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1439
+ await _preCommand(state, this);
1440
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1395
1441
  if (foundObj && foundObj.element) {
1396
- await this.scrollIfNeeded(foundObj.element, info);
1442
+ await this.scrollIfNeeded(foundObj.element, state.info);
1397
1443
  }
1398
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1444
+ await _screenshot(state, this);
1399
1445
  const dateAlternatives = findDateAlternatives(text);
1400
1446
  const numberAlternatives = findNumberAlternatives(text);
1401
1447
  if (dateAlternatives.date) {
1402
1448
  for (let i = 0; i < dateAlternatives.dates.length; i++) {
1403
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
1404
- ((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
1405
- return info;
1449
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1450
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1451
+ return state.info;
1406
1452
  }
1407
1453
  }
1408
1454
  throw new Error("element doesn't contain text " + text);
1409
1455
  }
1410
1456
  else if (numberAlternatives.number) {
1411
1457
  for (let i = 0; i < numberAlternatives.numbers.length; i++) {
1412
- if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
1413
- ((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
1414
- return info;
1458
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1459
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1460
+ return state.info;
1415
1461
  }
1416
1462
  }
1417
1463
  throw new Error("element doesn't contain text " + text);
1418
1464
  }
1419
- 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))) {
1420
- info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
1421
- info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
1465
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1466
+ state.info.foundText = foundObj?.text;
1467
+ state.info.value = foundObj?.value;
1422
1468
  throw new Error("element doesn't contain text " + text);
1423
1469
  }
1424
- return info;
1470
+ return state.info;
1425
1471
  }
1426
1472
  catch (e) {
1427
- //await this.closeUnexpectedPopups();
1428
- this.logger.error("verify element contains text failed " + info.log);
1429
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1430
- info.screenshotPath = screenshotPath;
1431
- Object.assign(e, { info: info });
1432
- error = e;
1433
- throw e;
1473
+ await _commandError(state, e, this);
1434
1474
  }
1435
1475
  finally {
1436
- const endTime = Date.now();
1437
- this._reportToWorld(world, {
1438
- element_name: selectors.element_name,
1439
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1440
- text: `Verify element contains text: ${text}`,
1441
- value: text,
1442
- screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
1443
- result: error
1444
- ? {
1445
- status: "FAILED",
1446
- startTime,
1447
- endTime,
1448
- message: error === null || error === void 0 ? void 0 : error.message,
1449
- }
1450
- : {
1451
- status: "PASSED",
1452
- startTime,
1453
- endTime,
1454
- },
1455
- info: info,
1456
- });
1476
+ _commandFinally(state, this);
1457
1477
  }
1458
1478
  }
1459
1479
  _getDataFile(world = null) {
@@ -1472,6 +1492,29 @@ class StableBrowser {
1472
1492
  }
1473
1493
  return dataFile;
1474
1494
  }
1495
+ async waitForUserInput(message, world = null) {
1496
+ if (!message) {
1497
+ message = "# Wait for user input. Press any key to continue";
1498
+ }
1499
+ else {
1500
+ message = "# Wait for user input. " + message;
1501
+ }
1502
+ message += "\n";
1503
+ const value = await new Promise((resolve) => {
1504
+ const rl = readline.createInterface({
1505
+ input: process.stdin,
1506
+ output: process.stdout,
1507
+ });
1508
+ rl.question(message, (answer) => {
1509
+ rl.close();
1510
+ resolve(answer);
1511
+ });
1512
+ });
1513
+ if (value) {
1514
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1515
+ }
1516
+ this.setTestData({ userInput: value }, world);
1517
+ }
1475
1518
  setTestData(testData, world = null) {
1476
1519
  if (!testData) {
1477
1520
  return;
@@ -1499,7 +1542,7 @@ class StableBrowser {
1499
1542
  const data = fs.readFileSync(filePath, "utf8");
1500
1543
  const results = [];
1501
1544
  return new Promise((resolve, reject) => {
1502
- const readableStream = new stream.Readable();
1545
+ const readableStream = new Readable();
1503
1546
  readableStream._read = () => { }; // _read is required but you can noop it
1504
1547
  readableStream.push(data);
1505
1548
  readableStream.push(null);
@@ -1659,7 +1702,6 @@ class StableBrowser {
1659
1702
  }
1660
1703
  async takeScreenshot(screenshotPath) {
1661
1704
  const playContext = this.context.playContext;
1662
- const client = await playContext.newCDPSession(this.page);
1663
1705
  // Using CDP to capture the screenshot
1664
1706
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1665
1707
  document.body.scrollWidth,
@@ -1669,97 +1711,67 @@ class StableBrowser {
1669
1711
  document.body.clientWidth,
1670
1712
  document.documentElement.clientWidth,
1671
1713
  ])));
1672
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1673
- document.body.scrollHeight,
1674
- document.documentElement.scrollHeight,
1675
- document.body.offsetHeight,
1676
- document.documentElement.offsetHeight,
1677
- document.body.clientHeight,
1678
- document.documentElement.clientHeight,
1679
- ])));
1680
- const { data } = await client.send("Page.captureScreenshot", {
1681
- format: "png",
1682
- clip: {
1683
- x: 0,
1684
- y: 0,
1685
- width: viewportWidth,
1686
- height: viewportHeight,
1687
- scale: 1,
1688
- },
1689
- });
1690
- if (!screenshotPath) {
1691
- return data;
1692
- }
1693
- let screenshotBuffer = Buffer.from(data, "base64");
1694
- const sharpBuffer = sharp(screenshotBuffer);
1695
- const metadata = await sharpBuffer.metadata();
1696
- //check if you are on retina display and reduce the quality of the image
1697
- if (metadata.width > viewportWidth || metadata.height > viewportHeight) {
1698
- screenshotBuffer = await sharpBuffer
1699
- .resize(viewportWidth, viewportHeight, {
1700
- fit: sharp.fit.inside,
1701
- withoutEnlargement: true,
1702
- })
1703
- .toBuffer();
1704
- }
1705
- fs.writeFileSync(screenshotPath, screenshotBuffer);
1706
- await client.detach();
1714
+ let screenshotBuffer = null;
1715
+ if (this.context.browserName === "chromium") {
1716
+ const client = await playContext.newCDPSession(this.page);
1717
+ const { data } = await client.send("Page.captureScreenshot", {
1718
+ format: "png",
1719
+ // clip: {
1720
+ // x: 0,
1721
+ // y: 0,
1722
+ // width: viewportWidth,
1723
+ // height: viewportHeight,
1724
+ // scale: 1,
1725
+ // },
1726
+ });
1727
+ await client.detach();
1728
+ if (!screenshotPath) {
1729
+ return data;
1730
+ }
1731
+ screenshotBuffer = Buffer.from(data, "base64");
1732
+ }
1733
+ else {
1734
+ screenshotBuffer = await this.page.screenshot();
1735
+ }
1736
+ let image = await Jimp.read(screenshotBuffer);
1737
+ // Get the image dimensions
1738
+ const { width, height } = image.bitmap;
1739
+ const resizeRatio = viewportWidth / width;
1740
+ // Resize the image to fit within the viewport dimensions without enlarging
1741
+ if (width > viewportWidth) {
1742
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1743
+ await image.write(screenshotPath);
1744
+ }
1745
+ else {
1746
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1747
+ }
1707
1748
  }
1708
1749
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1709
- this._validateSelectors(selectors);
1710
- const startTime = Date.now();
1711
- let error = null;
1712
- let screenshotId = null;
1713
- let screenshotPath = null;
1750
+ const state = {
1751
+ selectors,
1752
+ _params,
1753
+ options,
1754
+ world,
1755
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1756
+ text: `Verify element exists in page`,
1757
+ operation: "verifyElementExistInPage",
1758
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1759
+ };
1714
1760
  await new Promise((resolve) => setTimeout(resolve, 2000));
1715
- const info = {};
1716
- info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
1717
- info.operation = "verify";
1718
- info.selectors = selectors;
1719
1761
  try {
1720
- const element = await this._locate(selectors, info, _params);
1721
- if (element) {
1722
- await this.scrollIfNeeded(element, info);
1723
- }
1724
- await this._highlightElements(element);
1725
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1726
- await expect(element).toHaveCount(1, { timeout: 10000 });
1727
- return info;
1762
+ await _preCommand(state, this);
1763
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1764
+ return state.info;
1728
1765
  }
1729
1766
  catch (e) {
1730
- //await this.closeUnexpectedPopups();
1731
- this.logger.error("verify failed " + info.log);
1732
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1733
- info.screenshotPath = screenshotPath;
1734
- Object.assign(e, { info: info });
1735
- error = e;
1736
- throw e;
1767
+ await _commandError(state, e, this);
1737
1768
  }
1738
1769
  finally {
1739
- const endTime = Date.now();
1740
- this._reportToWorld(world, {
1741
- element_name: selectors.element_name,
1742
- type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1743
- text: "Verify element exists in page",
1744
- screenshotId,
1745
- result: error
1746
- ? {
1747
- status: "FAILED",
1748
- startTime,
1749
- endTime,
1750
- message: error === null || error === void 0 ? void 0 : error.message,
1751
- }
1752
- : {
1753
- status: "PASSED",
1754
- startTime,
1755
- endTime,
1756
- },
1757
- info: info,
1758
- });
1770
+ _commandFinally(state, this);
1759
1771
  }
1760
1772
  }
1761
1773
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1762
- this._validateSelectors(selectors);
1774
+ _validateSelectors(selectors);
1763
1775
  const startTime = Date.now();
1764
1776
  let error = null;
1765
1777
  let screenshotId = null;
@@ -1818,7 +1830,7 @@ class StableBrowser {
1818
1830
  status: "FAILED",
1819
1831
  startTime,
1820
1832
  endTime,
1821
- message: error === null || error === void 0 ? void 0 : error.message,
1833
+ message: error?.message,
1822
1834
  }
1823
1835
  : {
1824
1836
  status: "PASSED",
@@ -2027,7 +2039,7 @@ class StableBrowser {
2027
2039
  status: "FAILED",
2028
2040
  startTime,
2029
2041
  endTime,
2030
- message: error === null || error === void 0 ? void 0 : error.message,
2042
+ message: error?.message,
2031
2043
  }
2032
2044
  : {
2033
2045
  status: "PASSED",
@@ -2063,20 +2075,20 @@ class StableBrowser {
2063
2075
  for (let i = 0; i < frames.length; i++) {
2064
2076
  if (dateAlternatives.date) {
2065
2077
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2066
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2078
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
2067
2079
  result.frame = frames[i];
2068
2080
  results.push(result);
2069
2081
  }
2070
2082
  }
2071
2083
  else if (numberAlternatives.number) {
2072
2084
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2073
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2085
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
2074
2086
  result.frame = frames[i];
2075
2087
  results.push(result);
2076
2088
  }
2077
2089
  }
2078
2090
  else {
2079
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2091
+ const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
2080
2092
  result.frame = frames[i];
2081
2093
  results.push(result);
2082
2094
  }
@@ -2125,7 +2137,7 @@ class StableBrowser {
2125
2137
  status: "FAILED",
2126
2138
  startTime,
2127
2139
  endTime,
2128
- message: error === null || error === void 0 ? void 0 : error.message,
2140
+ message: error?.message,
2129
2141
  }
2130
2142
  : {
2131
2143
  status: "PASSED",
@@ -2209,7 +2221,7 @@ class StableBrowser {
2209
2221
  status: "FAILED",
2210
2222
  startTime,
2211
2223
  endTime,
2212
- message: error === null || error === void 0 ? void 0 : error.message,
2224
+ message: error?.message,
2213
2225
  }
2214
2226
  : {
2215
2227
  status: "PASSED",
@@ -2241,7 +2253,7 @@ class StableBrowser {
2241
2253
  this.logger.info("Table data verified");
2242
2254
  }
2243
2255
  async getTableData(selectors, _params = null, options = {}, world = null) {
2244
- this._validateSelectors(selectors);
2256
+ _validateSelectors(selectors);
2245
2257
  const startTime = Date.now();
2246
2258
  let error = null;
2247
2259
  let screenshotId = null;
@@ -2277,7 +2289,7 @@ class StableBrowser {
2277
2289
  status: "FAILED",
2278
2290
  startTime,
2279
2291
  endTime,
2280
- message: error === null || error === void 0 ? void 0 : error.message,
2292
+ message: error?.message,
2281
2293
  }
2282
2294
  : {
2283
2295
  status: "PASSED",
@@ -2289,7 +2301,7 @@ class StableBrowser {
2289
2301
  }
2290
2302
  }
2291
2303
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2292
- this._validateSelectors(selectors);
2304
+ _validateSelectors(selectors);
2293
2305
  if (!query) {
2294
2306
  throw new Error("query is null");
2295
2307
  }
@@ -2442,7 +2454,7 @@ class StableBrowser {
2442
2454
  status: "FAILED",
2443
2455
  startTime,
2444
2456
  endTime,
2445
- message: error === null || error === void 0 ? void 0 : error.message,
2457
+ message: error?.message,
2446
2458
  }
2447
2459
  : {
2448
2460
  status: "PASSED",
@@ -2511,13 +2523,13 @@ class StableBrowser {
2511
2523
  }
2512
2524
  catch (e) {
2513
2525
  if (e.label === "networkidle") {
2514
- console.log("waitted for the network to be idle timeout");
2526
+ console.log("waited for the network to be idle timeout");
2515
2527
  }
2516
2528
  else if (e.label === "load") {
2517
- console.log("waitted for the load timeout");
2529
+ console.log("waited for the load timeout");
2518
2530
  }
2519
2531
  else if (e.label === "domcontentloaded") {
2520
- console.log("waitted for the domcontent loaded timeout");
2532
+ console.log("waited for the domcontent loaded timeout");
2521
2533
  }
2522
2534
  console.log(".");
2523
2535
  }
@@ -2534,7 +2546,7 @@ class StableBrowser {
2534
2546
  status: "FAILED",
2535
2547
  startTime,
2536
2548
  endTime,
2537
- message: error === null || error === void 0 ? void 0 : error.message,
2549
+ message: error?.message,
2538
2550
  }
2539
2551
  : {
2540
2552
  status: "PASSED",
@@ -2552,13 +2564,6 @@ class StableBrowser {
2552
2564
  const info = {};
2553
2565
  try {
2554
2566
  await this.page.close();
2555
- if (this.context && this.context.pages && this.context.pages.length > 0) {
2556
- this.context.pages.pop();
2557
- this.page = this.context.pages[this.context.pages.length - 1];
2558
- this.context.page = this.page;
2559
- let title = await this.page.title();
2560
- console.log("Switched to page " + title);
2561
- }
2562
2567
  }
2563
2568
  catch (e) {
2564
2569
  console.log(".");
@@ -2576,7 +2581,7 @@ class StableBrowser {
2576
2581
  status: "FAILED",
2577
2582
  startTime,
2578
2583
  endTime,
2579
- message: error === null || error === void 0 ? void 0 : error.message,
2584
+ message: error?.message,
2580
2585
  }
2581
2586
  : {
2582
2587
  status: "PASSED",
@@ -2618,7 +2623,7 @@ class StableBrowser {
2618
2623
  status: "FAILED",
2619
2624
  startTime,
2620
2625
  endTime,
2621
- message: error === null || error === void 0 ? void 0 : error.message,
2626
+ message: error?.message,
2622
2627
  }
2623
2628
  : {
2624
2629
  status: "PASSED",
@@ -2654,7 +2659,7 @@ class StableBrowser {
2654
2659
  status: "FAILED",
2655
2660
  startTime,
2656
2661
  endTime,
2657
- message: error === null || error === void 0 ? void 0 : error.message,
2662
+ message: error?.message,
2658
2663
  }
2659
2664
  : {
2660
2665
  status: "PASSED",
@@ -2667,33 +2672,18 @@ class StableBrowser {
2667
2672
  }
2668
2673
  async scrollIfNeeded(element, info) {
2669
2674
  try {
2670
- let didScroll = await element.evaluate((node) => {
2671
- const rect = node.getBoundingClientRect();
2672
- if (rect &&
2673
- rect.top >= 0 &&
2674
- rect.left >= 0 &&
2675
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2676
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
2677
- return false;
2678
- }
2679
- else {
2680
- node.scrollIntoView({
2681
- behavior: "smooth",
2682
- block: "center",
2683
- inline: "center",
2684
- });
2685
- return true;
2686
- }
2675
+ await element.scrollIntoViewIfNeeded({
2676
+ timeout: 2000,
2687
2677
  });
2688
- if (didScroll) {
2689
- await new Promise((resolve) => setTimeout(resolve, 500));
2690
- if (info) {
2691
- info.box = await element.boundingBox();
2692
- }
2678
+ await new Promise((resolve) => setTimeout(resolve, 500));
2679
+ if (info) {
2680
+ info.box = await element.boundingBox({
2681
+ timeout: 1000,
2682
+ });
2693
2683
  }
2694
2684
  }
2695
2685
  catch (e) {
2696
- console.log("scroll failed");
2686
+ console.log("#-#");
2697
2687
  }
2698
2688
  }
2699
2689
  _reportToWorld(world, properties) {
@@ -2854,5 +2844,10 @@ const KEYBOARD_EVENTS = [
2854
2844
  "TVAntennaCable",
2855
2845
  "TVAudioDescription",
2856
2846
  ];
2847
+ function unEscapeString(str) {
2848
+ const placeholder = "__NEWLINE__";
2849
+ str = str.replace(new RegExp(placeholder, "g"), "\n");
2850
+ return str;
2851
+ }
2857
2852
  export { StableBrowser };
2858
2853
  //# sourceMappingURL=stable_browser.js.map