automation_model 1.0.400-dev → 1.0.400-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,17 +2,22 @@
2
2
  import { expect } from "@playwright/test";
3
3
  import dayjs from "dayjs";
4
4
  import fs from "fs";
5
+ import { Jimp } from "jimp";
5
6
  import path from "path";
6
7
  import reg_parser from "regex-parser";
7
- import sharp from "sharp";
8
8
  import { findDateAlternatives, findNumberAlternatives } from "./analyze_helper.js";
9
9
  import { getDateTimeValue } from "./date_time.js";
10
10
  import drawRectangle from "./drawRect.js";
11
11
  //import { closeUnexpectedPopups } from "./popups.js";
12
12
  import { getTableCells, getTableData } from "./table_analyze.js";
13
- import objectPath from "object-path";
14
- import { decrypt } from "./utils.js";
13
+ import { maskValue, replaceWithLocalTestData } from "./utils.js";
15
14
  import csv from "csv-parser";
15
+ import { Readable } from "node:stream";
16
+ import readline from "readline";
17
+ import { getContext } from "./init_browser.js";
18
+ import { locate_element } from "./locate_element.js";
19
+ import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot } from "./command_common.js";
20
+ import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
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,45 @@ 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
+ registerNetworkEvents(this.world, this, this.context, this.page);
96
+ registerDownloadEvent(this.page, this.world, this.context);
97
+ }
98
+ registerEventListeners(context) {
99
+ this.registerConsoleLogListener(this.page, context);
100
+ this.registerRequestListener(this.page, context, this.webLogFile);
101
+ if (!context.pageLoading) {
102
+ context.pageLoading = { status: false };
103
+ }
80
104
  context.playContext.on("page", async function (page) {
105
+ if (this.configuration && this.configuration.closePopups === true) {
106
+ console.log("close unexpected popups");
107
+ await page.close();
108
+ return;
109
+ }
81
110
  context.pageLoading.status = true;
82
111
  this.page = page;
83
112
  context.page = page;
84
113
  context.pages.push(page);
85
- this.webLogFile = this.getWebLogFile(logFolder);
86
- this.registerConsoleLogListener(page, context, this.webLogFile);
87
- this.registerRequestListener();
114
+ registerNetworkEvents(this.world, this, context, this.page);
115
+ registerDownloadEvent(this.page, this.world, context);
116
+ page.on("close", async () => {
117
+ if (this.context && this.context.pages && this.context.pages.length > 1) {
118
+ this.context.pages.pop();
119
+ this.page = this.context.pages[this.context.pages.length - 1];
120
+ this.context.page = this.page;
121
+ try {
122
+ let title = await this.page.title();
123
+ console.log("Switched to page " + title);
124
+ }
125
+ catch (error) {
126
+ console.error("Error on page close", error);
127
+ }
128
+ }
129
+ });
88
130
  try {
89
131
  await this.waitForPageLoad();
90
132
  console.log("Switch page: " + (await page.title()));
@@ -95,6 +137,36 @@ class StableBrowser {
95
137
  context.pageLoading.status = false;
96
138
  }.bind(this));
97
139
  }
140
+ async switchApp(appName) {
141
+ // check if the current app (this.appName) is the same as the new app
142
+ if (this.appName === appName) {
143
+ return;
144
+ }
145
+ let navigate = false;
146
+ if (!apps[appName]) {
147
+ let newContext = await getContext(null, false, this.logger, appName, false, this);
148
+ navigate = true;
149
+ apps[appName] = {
150
+ context: newContext,
151
+ browser: newContext.browser,
152
+ page: newContext.page,
153
+ };
154
+ }
155
+ const tempContext = {};
156
+ this._copyContext(this, tempContext);
157
+ this._copyContext(apps[appName], this);
158
+ apps[this.appName] = tempContext;
159
+ this.appName = appName;
160
+ if (navigate) {
161
+ await this.goto(this.context.environment.baseUrl);
162
+ await this.waitForPageLoad();
163
+ }
164
+ }
165
+ _copyContext(from, to) {
166
+ to.browser = from.browser;
167
+ to.page = from.page;
168
+ to.context = from.context;
169
+ }
98
170
  getWebLogFile(logFolder) {
99
171
  if (!fs.existsSync(logFolder)) {
100
172
  fs.mkdirSync(logFolder, { recursive: true });
@@ -106,37 +178,63 @@ class StableBrowser {
106
178
  const fileName = nextIndex + ".json";
107
179
  return path.join(logFolder, fileName);
108
180
  }
109
- registerConsoleLogListener(page, context, logFile) {
181
+ registerConsoleLogListener(page, context) {
110
182
  if (!this.context.webLogger) {
111
183
  this.context.webLogger = [];
112
184
  }
113
185
  page.on("console", async (msg) => {
114
- this.context.webLogger.push({
186
+ const obj = {
115
187
  type: msg.type(),
116
188
  text: msg.text(),
117
189
  location: msg.location(),
118
190
  time: new Date().toISOString(),
119
- });
120
- await fs.promises.writeFile(logFile, JSON.stringify(this.context.webLogger, null, 2));
191
+ };
192
+ this.context.webLogger.push(obj);
193
+ if (msg.type() === "error") {
194
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
195
+ }
121
196
  });
122
197
  }
123
- registerRequestListener() {
124
- this.page.on("request", async (data) => {
198
+ registerRequestListener(page, context, logFile) {
199
+ if (!this.context.networkLogger) {
200
+ this.context.networkLogger = [];
201
+ }
202
+ page.on("request", async (data) => {
203
+ const startTime = new Date().getTime();
125
204
  try {
126
- const pageUrl = new URL(this.page.url());
205
+ const pageUrl = new URL(page.url());
127
206
  const requestUrl = new URL(data.url());
128
207
  if (pageUrl.hostname === requestUrl.hostname) {
129
208
  const method = data.method();
130
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
209
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
131
210
  const token = await data.headerValue("Authorization");
132
211
  if (token) {
133
- this.context.authtoken = token;
212
+ context.authtoken = token;
134
213
  }
135
214
  }
136
215
  }
216
+ const response = await data.response();
217
+ const endTime = new Date().getTime();
218
+ const obj = {
219
+ url: data.url(),
220
+ method: data.method(),
221
+ postData: data.postData(),
222
+ error: data.failure() ? data.failure().errorText : null,
223
+ duration: endTime - startTime,
224
+ startTime,
225
+ };
226
+ context.networkLogger.push(obj);
227
+ this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
137
228
  }
138
229
  catch (error) {
139
230
  console.error("Error in request listener", error);
231
+ context.networkLogger.push({
232
+ error: "not able to listen",
233
+ message: error.message,
234
+ stack: error.stack,
235
+ time: new Date().toISOString(),
236
+ });
237
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
140
238
  }
141
239
  });
142
240
  }
@@ -151,20 +249,6 @@ class StableBrowser {
151
249
  timeout: 60000,
152
250
  });
153
251
  }
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
252
  _fixUsingParams(text, _params) {
169
253
  if (!_params || typeof text !== "string") {
170
254
  return text;
@@ -179,27 +263,84 @@ class StableBrowser {
179
263
  }
180
264
  return text;
181
265
  }
182
- _getLocator(locator, scope, _params) {
183
- if (locator.type === "pw_selector") {
184
- return scope.locator(locator.selector);
266
+ _fixLocatorUsingParams(locator, _params) {
267
+ // check if not null
268
+ if (!locator) {
269
+ return locator;
270
+ }
271
+ // clone the locator
272
+ locator = JSON.parse(JSON.stringify(locator));
273
+ this.scanAndManipulate(locator, _params);
274
+ return locator;
275
+ }
276
+ _isObject(value) {
277
+ return value && typeof value === "object" && value.constructor === Object;
278
+ }
279
+ scanAndManipulate(currentObj, _params) {
280
+ for (const key in currentObj) {
281
+ if (typeof currentObj[key] === "string") {
282
+ // Perform string manipulation
283
+ currentObj[key] = this._fixUsingParams(currentObj[key], _params);
284
+ }
285
+ else if (this._isObject(currentObj[key])) {
286
+ // Recursively scan nested objects
287
+ this.scanAndManipulate(currentObj[key], _params);
288
+ }
185
289
  }
290
+ }
291
+ _getLocator(locator, scope, _params) {
292
+ locator = this._fixLocatorUsingParams(locator, _params);
293
+ let locatorReturn;
186
294
  if (locator.role) {
187
295
  if (locator.role[1].nameReg) {
188
296
  locator.role[1].name = reg_parser(locator.role[1].nameReg);
189
297
  delete locator.role[1].nameReg;
190
298
  }
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]);
299
+ // if (locator.role[1].name) {
300
+ // locator.role[1].name = this._fixUsingParams(locator.role[1].name, _params);
301
+ // }
302
+ locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
195
303
  }
196
304
  if (locator.css) {
197
- return scope.locator(this._fixUsingParams(locator.css, _params));
305
+ locatorReturn = scope.locator(locator.css);
198
306
  }
199
- throw new Error("unknown locator type");
307
+ // handle role/name locators
308
+ // locator.selector will be something like: textbox[name="Username"i]
309
+ if (locator.engine === "internal:role") {
310
+ // extract the role, name and the i/s flags using regex
311
+ const match = locator.selector.match(/(.*)\[(.*)="(.*)"(.*)\]/);
312
+ if (match) {
313
+ const role = match[1];
314
+ const name = match[3];
315
+ const flags = match[4];
316
+ locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
317
+ }
318
+ }
319
+ if (locator?.engine) {
320
+ if (locator.engine === "css") {
321
+ locatorReturn = scope.locator(locator.selector);
322
+ }
323
+ else {
324
+ let selector = locator.selector;
325
+ if (locator.engine === "internal:attr") {
326
+ if (!selector.startsWith("[")) {
327
+ selector = `[${selector}]`;
328
+ }
329
+ }
330
+ locatorReturn = scope.locator(`${locator.engine}=${selector}`);
331
+ }
332
+ }
333
+ if (!locatorReturn) {
334
+ console.error(locator);
335
+ throw new Error("Locator undefined");
336
+ }
337
+ return locatorReturn;
200
338
  }
201
339
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
202
- let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
340
+ if (css && css.locator) {
341
+ css = css.locator;
342
+ }
343
+ let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, false, _params);
203
344
  if (result.elementCount === 0) {
204
345
  return;
205
346
  }
@@ -214,7 +355,7 @@ class StableBrowser {
214
355
  }
215
356
  async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
216
357
  //const stringifyText = JSON.stringify(text);
217
- return await scope.evaluate(([text, tag, regex, partial]) => {
358
+ return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
218
359
  function isParent(parent, child) {
219
360
  let currentNode = child.parentNode;
220
361
  while (currentNode !== null) {
@@ -226,6 +367,15 @@ class StableBrowser {
226
367
  return false;
227
368
  }
228
369
  document.isParent = isParent;
370
+ function getRegex(str) {
371
+ const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
372
+ if (!match) {
373
+ return null;
374
+ }
375
+ let [_, pattern, flags] = match;
376
+ return new RegExp(pattern, flags);
377
+ }
378
+ document.getRegex = getRegex;
229
379
  function collectAllShadowDomElements(element, result = []) {
230
380
  // Check and add the element if it has a shadow root
231
381
  if (element.shadowRoot) {
@@ -244,6 +394,10 @@ class StableBrowser {
244
394
  if (!tag) {
245
395
  tag = "*";
246
396
  }
397
+ let regexpSearch = document.getRegex(text);
398
+ if (regexpSearch) {
399
+ regex = true;
400
+ }
247
401
  let elements = Array.from(document.querySelectorAll(tag));
248
402
  let shadowHosts = [];
249
403
  document.collectAllShadowDomElements(document, shadowHosts);
@@ -259,7 +413,9 @@ class StableBrowser {
259
413
  let randomToken = null;
260
414
  const foundElements = [];
261
415
  if (regex) {
262
- let regexpSearch = new RegExp(text, "im");
416
+ if (!regexpSearch) {
417
+ regexpSearch = new RegExp(text, "im");
418
+ }
263
419
  for (let i = 0; i < elements.length; i++) {
264
420
  const element = elements[i];
265
421
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -273,8 +429,8 @@ class StableBrowser {
273
429
  for (let i = 0; i < elements.length; i++) {
274
430
  const element = elements[i];
275
431
  if (partial) {
276
- if ((element.innerText && element.innerText.trim().includes(text)) ||
277
- (element.value && element.value.includes(text))) {
432
+ if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
433
+ (element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
278
434
  foundElements.push(element);
279
435
  }
280
436
  }
@@ -319,6 +475,12 @@ class StableBrowser {
319
475
  }
320
476
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
321
477
  let locatorSearch = selectorHierarchy[index];
478
+ try {
479
+ locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
480
+ }
481
+ catch (e) {
482
+ console.error(e);
483
+ }
322
484
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
323
485
  let locator = null;
324
486
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
@@ -407,15 +569,20 @@ class StableBrowser {
407
569
  if (result.foundElements.length > 0) {
408
570
  let dialogCloseLocator = result.foundElements[0].locator;
409
571
  await dialogCloseLocator.click();
572
+ // wait for the dialog to close
573
+ await dialogCloseLocator.waitFor({ state: "hidden" });
410
574
  return { rerun: true };
411
575
  }
412
576
  }
413
577
  }
414
578
  return { rerun: false };
415
579
  }
416
- async _locate(selectors, info, _params, timeout = 30000) {
580
+ async _locate(selectors, info, _params, timeout) {
581
+ if (!timeout) {
582
+ timeout = 30000;
583
+ }
417
584
  for (let i = 0; i < 3; i++) {
418
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
585
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
419
586
  for (let j = 0; j < selectors.locators.length; j++) {
420
587
  let selector = selectors.locators[j];
421
588
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -427,17 +594,44 @@ class StableBrowser {
427
594
  }
428
595
  throw new Error("unable to locate element " + JSON.stringify(selectors));
429
596
  }
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);
597
+ async _findFrameScope(selectors, timeout = 30000) {
436
598
  let scope = this.page;
599
+ if (selectors.frame) {
600
+ return selectors.frame;
601
+ }
437
602
  if (selectors.iframe_src || selectors.frameLocators) {
438
- info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
603
+ const findFrame = async (frame, framescope) => {
604
+ for (let i = 0; i < frame.selectors.length; i++) {
605
+ let frameLocator = frame.selectors[i];
606
+ if (frameLocator.css) {
607
+ let testframescope = framescope.frameLocator(frameLocator.css);
608
+ if (frameLocator.index) {
609
+ testframescope = framescope.nth(frameLocator.index);
610
+ }
611
+ try {
612
+ await testframescope.owner().evaluateHandle(() => true, null, {
613
+ timeout: 5000,
614
+ });
615
+ framescope = testframescope;
616
+ break;
617
+ }
618
+ catch (error) {
619
+ console.error("frame not found " + frameLocator.css);
620
+ }
621
+ }
622
+ }
623
+ if (frame.children) {
624
+ return await findFrame(frame.children, framescope);
625
+ }
626
+ return framescope;
627
+ };
439
628
  while (true) {
440
629
  let frameFound = false;
630
+ if (selectors.nestFrmLoc) {
631
+ scope = await findFrame(selectors.nestFrmLoc, scope);
632
+ frameFound = true;
633
+ break;
634
+ }
441
635
  if (selectors.frameLocators) {
442
636
  for (let i = 0; i < selectors.frameLocators.length; i++) {
443
637
  let frameLocator = selectors.frameLocators[i];
@@ -463,6 +657,25 @@ class StableBrowser {
463
657
  }
464
658
  }
465
659
  }
660
+ if (!scope) {
661
+ scope = this.page;
662
+ }
663
+ return scope;
664
+ }
665
+ async _getDocumentBody(selectors, timeout = 30000) {
666
+ let scope = await this._findFrameScope(selectors, timeout);
667
+ return scope.evaluate(() => {
668
+ var bodyContent = document.body.innerHTML;
669
+ return bodyContent;
670
+ });
671
+ }
672
+ async _locate_internal(selectors, info, _params, timeout = 30000) {
673
+ let highPriorityTimeout = 5000;
674
+ let visibleOnlyTimeout = 6000;
675
+ let startTime = performance.now();
676
+ let locatorsCount = 0;
677
+ //let arrayMode = Array.isArray(selectors);
678
+ let scope = await this._findFrameScope(selectors, timeout);
466
679
  let selectorsLocators = null;
467
680
  selectorsLocators = selectors.locators;
468
681
  // group selectors by priority
@@ -598,83 +811,121 @@ class StableBrowser {
598
811
  }
599
812
  return result;
600
813
  }
601
- async click(selectors, _params, options = {}, world = null) {
602
- this._validateSelectors(selectors);
814
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
603
815
  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;
816
+ let timeout = 30000;
817
+ if (options && options.timeout) {
818
+ timeout = options.timeout;
819
+ }
820
+ while (true) {
821
+ try {
822
+ const result = await locate_element(this.context, elementDescription, "click");
823
+ if (result?.elementNumber >= 0) {
824
+ const selectors = {
825
+ frame: result?.frame,
826
+ locators: [
827
+ {
828
+ css: result?.css,
829
+ },
830
+ ],
831
+ };
832
+ await this.click(selectors, _params, options, world);
833
+ return;
834
+ }
835
+ }
836
+ catch (e) {
837
+ if (performance.now() - startTime > timeout) {
838
+ throw e;
839
+ }
840
+ }
841
+ await new Promise((resolve) => setTimeout(resolve, 3000));
842
+ }
843
+ }
844
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
845
+ const startTime = Date.now();
846
+ let timeout = 30000;
847
+ if (options && options.timeout) {
848
+ timeout = options.timeout;
849
+ }
850
+ while (true) {
851
+ try {
852
+ const result = await locate_element(this.context, elementDescription, "fill", value);
853
+ if (result?.elementNumber >= 0) {
854
+ const selectors = {
855
+ frame: result?.frame,
856
+ locators: [
857
+ {
858
+ css: result?.css,
859
+ },
860
+ ],
861
+ };
862
+ await this.clickType(selectors, value, false, _params, options, world);
863
+ return;
864
+ }
865
+ }
866
+ catch (e) {
867
+ if (performance.now() - startTime > timeout) {
868
+ throw e;
869
+ }
870
+ }
871
+ await new Promise((resolve) => setTimeout(resolve, 3000));
872
+ }
873
+ }
874
+ async click(selectors, _params, options = {}, world = null) {
875
+ const state = {
876
+ selectors,
877
+ _params,
878
+ options,
879
+ world,
880
+ text: "Click element",
881
+ type: Types.CLICK,
882
+ operation: "click",
883
+ log: "***** click on " + selectors.element_name + " *****\n",
884
+ };
611
885
  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));
886
+ await _preCommand(state, this);
887
+ if (state.options && state.options.context) {
888
+ state.selectors.locators[0].text = state.options.context;
889
+ }
615
890
  try {
616
- await this._highlightElements(element);
617
- await element.click({ timeout: 5000 });
891
+ await state.element.click();
618
892
  await new Promise((resolve) => setTimeout(resolve, 1000));
619
893
  }
620
894
  catch (e) {
621
895
  // 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 });
896
+ state.element = await this._locate(selectors, state.info, _params);
897
+ await state.element.dispatchEvent("click");
625
898
  await new Promise((resolve) => setTimeout(resolve, 1000));
626
899
  }
627
900
  await this.waitForPageLoad();
628
- return info;
901
+ return state.info;
629
902
  }
630
903
  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;
904
+ await _commandError(state, e, this);
637
905
  }
638
906
  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
- });
907
+ _commandFinally(state, this);
659
908
  }
660
909
  }
661
910
  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;
911
+ const state = {
912
+ selectors,
913
+ _params,
914
+ options,
915
+ world,
916
+ type: checked ? Types.CHECK : Types.UNCHECK,
917
+ text: checked ? `Check element` : `Uncheck element`,
918
+ operation: "setCheck",
919
+ log: "***** check " + selectors.element_name + " *****\n",
920
+ };
672
921
  try {
673
- let element = await this._locate(selectors, info, _params);
674
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
922
+ await _preCommand(state, this);
923
+ state.info.checked = checked;
924
+ // let element = await this._locate(selectors, info, _params);
925
+ // ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
675
926
  try {
676
- await this._highlightElements(element);
677
- await element.setChecked(checked, { timeout: 5000 });
927
+ // await this._highlightElements(element);
928
+ await state.element.setChecked(checked);
678
929
  await new Promise((resolve) => setTimeout(resolve, 1000));
679
930
  }
680
931
  catch (e) {
@@ -683,179 +934,108 @@ class StableBrowser {
683
934
  }
684
935
  else {
685
936
  //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 });
937
+ state.info.log += "setCheck failed, will try again" + "\n";
938
+ state.element = await this._locate(selectors, state.info, _params);
939
+ await state.element.setChecked(checked, { timeout: 5000, force: true });
689
940
  await new Promise((resolve) => setTimeout(resolve, 1000));
690
941
  }
691
942
  }
692
943
  await this.waitForPageLoad();
693
- return info;
944
+ return state.info;
694
945
  }
695
946
  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;
947
+ await _commandError(state, e, this);
702
948
  }
703
949
  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
- });
950
+ _commandFinally(state, this);
724
951
  }
725
952
  }
726
953
  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;
954
+ const state = {
955
+ selectors,
956
+ _params,
957
+ options,
958
+ world,
959
+ type: Types.HOVER,
960
+ text: `Hover element`,
961
+ operation: "hover",
962
+ log: "***** hover " + selectors.element_name + " *****\n",
963
+ };
736
964
  try {
737
- let element = await this._locate(selectors, info, _params);
738
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
965
+ await _preCommand(state, this);
739
966
  try {
740
- await this._highlightElements(element);
741
- await element.hover({ timeout: 10000 });
967
+ await state.element.hover();
742
968
  await new Promise((resolve) => setTimeout(resolve, 1000));
743
969
  }
744
970
  catch (e) {
745
971
  //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 });
972
+ state.info.log += "hover failed, will try again" + "\n";
973
+ state.element = await this._locate(selectors, state.info, _params);
974
+ await state.element.hover({ timeout: 10000 });
749
975
  await new Promise((resolve) => setTimeout(resolve, 1000));
750
976
  }
751
977
  await this.waitForPageLoad();
752
- return info;
978
+ return state.info;
753
979
  }
754
980
  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;
981
+ await _commandError(state, e, this);
761
982
  }
762
983
  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
- });
984
+ _commandFinally(state, this);
783
985
  }
784
986
  }
785
987
  async selectOption(selectors, values, _params = null, options = {}, world = null) {
786
- this._validateSelectors(selectors);
787
988
  if (!values) {
788
989
  throw new Error("values is null");
789
990
  }
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;
991
+ const state = {
992
+ selectors,
993
+ _params,
994
+ options,
995
+ world,
996
+ value: values.toString(),
997
+ type: Types.SELECT,
998
+ text: `Select option: ${values}`,
999
+ operation: "selectOption",
1000
+ log: "***** select option " + selectors.element_name + " *****\n",
1001
+ };
798
1002
  try {
799
- let element = await this._locate(selectors, info, _params);
800
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1003
+ await _preCommand(state, this);
801
1004
  try {
802
- await this._highlightElements(element);
803
- await element.selectOption(values, { timeout: 5000 });
1005
+ await state.element.selectOption(values);
804
1006
  }
805
1007
  catch (e) {
806
1008
  //await this.closeUnexpectedPopups();
807
- info.log += "selectOption failed, will try force" + "\n";
808
- await element.selectOption(values, { timeout: 10000, force: true });
1009
+ state.info.log += "selectOption failed, will try force" + "\n";
1010
+ await state.element.selectOption(values, { timeout: 10000, force: true });
809
1011
  }
810
1012
  await this.waitForPageLoad();
811
- return info;
1013
+ return state.info;
812
1014
  }
813
1015
  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;
1016
+ await _commandError(state, e, this);
821
1017
  }
822
1018
  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
- });
1019
+ _commandFinally(state, this);
844
1020
  }
845
1021
  }
846
1022
  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;
1023
+ const state = {
1024
+ value: _value,
1025
+ _params,
1026
+ options,
1027
+ world,
1028
+ locate: false,
1029
+ scroll: false,
1030
+ highlight: false,
1031
+ type: Types.TYPE_PRESS,
1032
+ text: `Type value: ${_value}`,
1033
+ operation: "type",
1034
+ log: "",
1035
+ };
856
1036
  try {
857
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
858
- const valueSegment = _value.split("&&");
1037
+ await _preCommand(state, this);
1038
+ const valueSegment = state.value.split("&&");
859
1039
  for (let i = 0; i < valueSegment.length; i++) {
860
1040
  if (i > 0) {
861
1041
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -875,134 +1055,53 @@ class StableBrowser {
875
1055
  await this.page.keyboard.type(value);
876
1056
  }
877
1057
  }
878
- return info;
1058
+ return state.info;
879
1059
  }
880
1060
  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;
1061
+ await _commandError(state, e, this);
888
1062
  }
889
1063
  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
- });
1064
+ _commandFinally(state, this);
910
1065
  }
911
1066
  }
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;
1067
+ async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1068
+ const state = {
1069
+ selectors,
1070
+ _params,
1071
+ value,
1072
+ options,
1073
+ world,
1074
+ type: Types.SET_INPUT,
1075
+ text: `Set input value`,
1076
+ operation: "setInputValue",
1077
+ log: "***** set input value " + selectors.element_name + " *****\n",
1078
+ };
923
1079
  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);
1080
+ await _preCommand(state, this);
1081
+ let value = await this._replaceWithLocalData(state.value, this);
930
1082
  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
- }
1083
+ await state.element.evaluateHandle((el, value) => {
1084
+ el.value = value;
1085
+ }, value);
949
1086
  }
950
1087
  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
- }
1088
+ this.logger.error("setInputValue failed, will try again");
1089
+ await _screenshot(state, this);
1090
+ Object.assign(error, { info: state.info });
1091
+ await state.element.evaluateHandle((el, value) => {
1092
+ el.value = value;
1093
+ });
974
1094
  }
975
1095
  }
976
- catch (error) {
977
- error = e;
978
- throw e;
1096
+ catch (e) {
1097
+ await _commandError(state, e, this);
979
1098
  }
980
1099
  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
- });
1100
+ _commandFinally(state, this);
1002
1101
  }
1003
1102
  }
1004
- async setDateTime(selectors, value, enter = false, _params = null, options = {}, world = null) {
1005
- this._validateSelectors(selectors);
1103
+ async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1104
+ _validateSelectors(selectors);
1006
1105
  const startTime = Date.now();
1007
1106
  let error = null;
1008
1107
  let screenshotId = null;
@@ -1013,6 +1112,7 @@ class StableBrowser {
1013
1112
  info.selectors = selectors;
1014
1113
  info.value = value;
1015
1114
  try {
1115
+ value = await this._replaceWithLocalData(value, this);
1016
1116
  let element = await this._locate(selectors, info, _params);
1017
1117
  //insert red border around the element
1018
1118
  await this.scrollIfNeeded(element, info);
@@ -1021,28 +1121,51 @@ class StableBrowser {
1021
1121
  try {
1022
1122
  await element.click();
1023
1123
  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);
1124
+ if (format) {
1125
+ value = dayjs(value).format(format);
1126
+ await element.fill(value);
1127
+ }
1128
+ else {
1129
+ const dateTimeValue = await getDateTimeValue({ value, element });
1130
+ await element.evaluateHandle((el, dateTimeValue) => {
1131
+ el.value = ""; // clear input
1132
+ el.value = dateTimeValue;
1133
+ }, dateTimeValue);
1134
+ }
1135
+ if (enter) {
1136
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1137
+ await this.page.keyboard.press("Enter");
1138
+ await this.waitForPageLoad();
1139
+ }
1029
1140
  }
1030
- catch (error) {
1141
+ catch (err) {
1031
1142
  //await this.closeUnexpectedPopups();
1032
1143
  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)));
1144
+ this.logger.info("Trying again");
1145
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1034
1146
  info.screenshotPath = screenshotPath;
1035
- Object.assign(error, { info: info });
1147
+ Object.assign(err, { info: info });
1036
1148
  await element.click();
1037
1149
  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);
1150
+ if (format) {
1151
+ value = dayjs(value).format(format);
1152
+ await element.fill(value);
1153
+ }
1154
+ else {
1155
+ const dateTimeValue = await getDateTimeValue({ value, element });
1156
+ await element.evaluateHandle((el, dateTimeValue) => {
1157
+ el.value = ""; // clear input
1158
+ el.value = dateTimeValue;
1159
+ }, dateTimeValue);
1160
+ }
1161
+ if (enter) {
1162
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1163
+ await this.page.keyboard.press("Enter");
1164
+ await this.waitForPageLoad();
1165
+ }
1043
1166
  }
1044
1167
  }
1045
- catch (error) {
1168
+ catch (e) {
1046
1169
  error = e;
1047
1170
  throw e;
1048
1171
  }
@@ -1071,32 +1194,32 @@ class StableBrowser {
1071
1194
  }
1072
1195
  }
1073
1196
  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;
1197
+ _value = unEscapeString(_value);
1083
1198
  const newValue = await this._replaceWithLocalData(_value, world);
1199
+ const state = {
1200
+ selectors,
1201
+ _params,
1202
+ value: newValue,
1203
+ originalValue: _value,
1204
+ options,
1205
+ world,
1206
+ type: Types.FILL,
1207
+ text: `Click type input with value: ${_value}`,
1208
+ operation: "clickType",
1209
+ log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
1210
+ };
1084
1211
  if (newValue !== _value) {
1085
1212
  //this.logger.info(_value + "=" + newValue);
1086
1213
  _value = newValue;
1087
1214
  }
1088
- info.value = _value;
1089
1215
  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);
1216
+ await _preCommand(state, this);
1217
+ state.info.value = _value;
1095
1218
  if (options === null || options === undefined || !options.press) {
1096
1219
  try {
1097
- let currentValue = await element.inputValue();
1220
+ let currentValue = await state.element.inputValue();
1098
1221
  if (currentValue) {
1099
- await element.fill("");
1222
+ await state.element.fill("");
1100
1223
  }
1101
1224
  }
1102
1225
  catch (e) {
@@ -1105,22 +1228,22 @@ class StableBrowser {
1105
1228
  }
1106
1229
  if (options === null || options === undefined || options.press) {
1107
1230
  try {
1108
- await element.click({ timeout: 5000 });
1231
+ await state.element.click({ timeout: 5000 });
1109
1232
  }
1110
1233
  catch (e) {
1111
- await element.dispatchEvent("click");
1234
+ await state.element.dispatchEvent("click");
1112
1235
  }
1113
1236
  }
1114
1237
  else {
1115
1238
  try {
1116
- await element.focus();
1239
+ await state.element.focus();
1117
1240
  }
1118
1241
  catch (e) {
1119
- await element.dispatchEvent("focus");
1242
+ await state.element.dispatchEvent("focus");
1120
1243
  }
1121
1244
  }
1122
1245
  await new Promise((resolve) => setTimeout(resolve, 500));
1123
- const valueSegment = _value.split("&&");
1246
+ const valueSegment = state.value.split("&&");
1124
1247
  for (let i = 0; i < valueSegment.length; i++) {
1125
1248
  if (i > 0) {
1126
1249
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -1140,13 +1263,14 @@ class StableBrowser {
1140
1263
  await new Promise((resolve) => setTimeout(resolve, 500));
1141
1264
  }
1142
1265
  }
1266
+ await _screenshot(state, this);
1143
1267
  if (enter === true) {
1144
1268
  await new Promise((resolve) => setTimeout(resolve, 2000));
1145
1269
  await this.page.keyboard.press("Enter");
1146
1270
  await this.waitForPageLoad();
1147
1271
  }
1148
1272
  else if (enter === false) {
1149
- await element.dispatchEvent("change");
1273
+ await state.element.dispatchEvent("change");
1150
1274
  //await this.page.keyboard.press("Tab");
1151
1275
  }
1152
1276
  else {
@@ -1155,103 +1279,50 @@ class StableBrowser {
1155
1279
  await this.waitForPageLoad();
1156
1280
  }
1157
1281
  }
1158
- return info;
1282
+ return state.info;
1159
1283
  }
1160
1284
  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;
1285
+ await _commandError(state, e, this);
1168
1286
  }
1169
1287
  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
- });
1288
+ _commandFinally(state, this);
1191
1289
  }
1192
1290
  }
1193
1291
  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;
1292
+ const state = {
1293
+ selectors,
1294
+ _params,
1295
+ value: unEscapeString(value),
1296
+ options,
1297
+ world,
1298
+ type: Types.FILL,
1299
+ text: `Fill input with value: ${value}`,
1300
+ operation: "fill",
1301
+ log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
1302
+ };
1204
1303
  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");
1304
+ await _preCommand(state, this);
1305
+ await state.element.fill(value);
1306
+ await state.element.dispatchEvent("change");
1210
1307
  if (enter) {
1211
1308
  await new Promise((resolve) => setTimeout(resolve, 2000));
1212
1309
  await this.page.keyboard.press("Enter");
1213
1310
  }
1214
1311
  await this.waitForPageLoad();
1215
- return info;
1312
+ return state.info;
1216
1313
  }
1217
1314
  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;
1315
+ await _commandError(state, e, this);
1225
1316
  }
1226
1317
  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
- });
1318
+ _commandFinally(state, this);
1248
1319
  }
1249
1320
  }
1250
1321
  async getText(selectors, _params = null, options = {}, info = {}, world = null) {
1251
1322
  return await this._getText(selectors, 0, _params, options, info, world);
1252
1323
  }
1253
1324
  async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
1254
- this._validateSelectors(selectors);
1325
+ _validateSelectors(selectors);
1255
1326
  let screenshotId = null;
1256
1327
  let screenshotPath = null;
1257
1328
  if (!info.log) {
@@ -1295,165 +1366,124 @@ class StableBrowser {
1295
1366
  }
1296
1367
  }
1297
1368
  async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
1298
- var _a;
1299
- this._validateSelectors(selectors);
1300
1369
  if (!pattern) {
1301
1370
  throw new Error("pattern is null");
1302
1371
  }
1303
1372
  if (!text) {
1304
1373
  throw new Error("text is null");
1305
1374
  }
1375
+ const state = {
1376
+ selectors,
1377
+ _params,
1378
+ pattern,
1379
+ value: pattern,
1380
+ options,
1381
+ world,
1382
+ locate: false,
1383
+ scroll: false,
1384
+ screenshot: false,
1385
+ highlight: false,
1386
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1387
+ text: `Verify element contains pattern: ${pattern}`,
1388
+ operation: "containsPattern",
1389
+ log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
1390
+ };
1306
1391
  const newValue = await this._replaceWithLocalData(text, world);
1307
1392
  if (newValue !== text) {
1308
1393
  this.logger.info(text + "=" + newValue);
1309
1394
  text = newValue;
1310
1395
  }
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
1396
  let foundObj = null;
1323
1397
  try {
1324
- foundObj = await this._getText(selectors, 0, _params, options, info, world);
1398
+ await _preCommand(state, this);
1399
+ state.info.pattern = pattern;
1400
+ foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
1325
1401
  if (foundObj && foundObj.element) {
1326
- await this.scrollIfNeeded(foundObj.element, info);
1402
+ await this.scrollIfNeeded(foundObj.element, state.info);
1327
1403
  }
1328
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1404
+ await _screenshot(state, this);
1329
1405
  let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
1330
1406
  pattern = pattern.replace("{text}", escapedText);
1331
1407
  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;
1408
+ if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
1409
+ state.info.foundText = foundObj?.text;
1334
1410
  throw new Error("element doesn't contain text " + text);
1335
1411
  }
1336
- return info;
1412
+ return state.info;
1337
1413
  }
1338
1414
  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;
1415
+ this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
1416
+ await _commandError(state, e, this);
1347
1417
  }
1348
1418
  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
- });
1419
+ _commandFinally(state, this);
1370
1420
  }
1371
1421
  }
1372
1422
  async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
1373
- var _a, _b, _c;
1374
- this._validateSelectors(selectors);
1423
+ const state = {
1424
+ selectors,
1425
+ _params,
1426
+ value: text,
1427
+ options,
1428
+ world,
1429
+ locate: false,
1430
+ scroll: false,
1431
+ screenshot: false,
1432
+ highlight: false,
1433
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1434
+ text: `Verify element contains text: ${text}`,
1435
+ operation: "containsText",
1436
+ log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
1437
+ };
1375
1438
  if (!text) {
1376
1439
  throw new Error("text is null");
1377
1440
  }
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;
1441
+ text = unEscapeString(text);
1386
1442
  const newValue = await this._replaceWithLocalData(text, world);
1387
1443
  if (newValue !== text) {
1388
1444
  this.logger.info(text + "=" + newValue);
1389
1445
  text = newValue;
1390
1446
  }
1391
- info.value = text;
1392
1447
  let foundObj = null;
1393
1448
  try {
1394
- foundObj = await this._getText(selectors, climb, _params, options, info, world);
1449
+ await _preCommand(state, this);
1450
+ foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
1395
1451
  if (foundObj && foundObj.element) {
1396
- await this.scrollIfNeeded(foundObj.element, info);
1452
+ await this.scrollIfNeeded(foundObj.element, state.info);
1397
1453
  }
1398
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1454
+ await _screenshot(state, this);
1399
1455
  const dateAlternatives = findDateAlternatives(text);
1400
1456
  const numberAlternatives = findNumberAlternatives(text);
1401
1457
  if (dateAlternatives.date) {
1402
1458
  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;
1459
+ if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
1460
+ foundObj?.value?.includes(dateAlternatives.dates[i])) {
1461
+ return state.info;
1406
1462
  }
1407
1463
  }
1408
1464
  throw new Error("element doesn't contain text " + text);
1409
1465
  }
1410
1466
  else if (numberAlternatives.number) {
1411
1467
  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;
1468
+ if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
1469
+ foundObj?.value?.includes(numberAlternatives.numbers[i])) {
1470
+ return state.info;
1415
1471
  }
1416
1472
  }
1417
1473
  throw new Error("element doesn't contain text " + text);
1418
1474
  }
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;
1475
+ else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
1476
+ state.info.foundText = foundObj?.text;
1477
+ state.info.value = foundObj?.value;
1422
1478
  throw new Error("element doesn't contain text " + text);
1423
1479
  }
1424
- return info;
1480
+ return state.info;
1425
1481
  }
1426
1482
  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;
1483
+ await _commandError(state, e, this);
1434
1484
  }
1435
1485
  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
- });
1486
+ _commandFinally(state, this);
1457
1487
  }
1458
1488
  }
1459
1489
  _getDataFile(world = null) {
@@ -1472,6 +1502,29 @@ class StableBrowser {
1472
1502
  }
1473
1503
  return dataFile;
1474
1504
  }
1505
+ async waitForUserInput(message, world = null) {
1506
+ if (!message) {
1507
+ message = "# Wait for user input. Press any key to continue";
1508
+ }
1509
+ else {
1510
+ message = "# Wait for user input. " + message;
1511
+ }
1512
+ message += "\n";
1513
+ const value = await new Promise((resolve) => {
1514
+ const rl = readline.createInterface({
1515
+ input: process.stdin,
1516
+ output: process.stdout,
1517
+ });
1518
+ rl.question(message, (answer) => {
1519
+ rl.close();
1520
+ resolve(answer);
1521
+ });
1522
+ });
1523
+ if (value) {
1524
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1525
+ }
1526
+ this.setTestData({ userInput: value }, world);
1527
+ }
1475
1528
  setTestData(testData, world = null) {
1476
1529
  if (!testData) {
1477
1530
  return;
@@ -1499,7 +1552,7 @@ class StableBrowser {
1499
1552
  const data = fs.readFileSync(filePath, "utf8");
1500
1553
  const results = [];
1501
1554
  return new Promise((resolve, reject) => {
1502
- const readableStream = new stream.Readable();
1555
+ const readableStream = new Readable();
1503
1556
  readableStream._read = () => { }; // _read is required but you can noop it
1504
1557
  readableStream.push(data);
1505
1558
  readableStream.push(null);
@@ -1659,7 +1712,6 @@ class StableBrowser {
1659
1712
  }
1660
1713
  async takeScreenshot(screenshotPath) {
1661
1714
  const playContext = this.context.playContext;
1662
- const client = await playContext.newCDPSession(this.page);
1663
1715
  // Using CDP to capture the screenshot
1664
1716
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1665
1717
  document.body.scrollWidth,
@@ -1669,97 +1721,67 @@ class StableBrowser {
1669
1721
  document.body.clientWidth,
1670
1722
  document.documentElement.clientWidth,
1671
1723
  ])));
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();
1724
+ let screenshotBuffer = null;
1725
+ if (this.context.browserName === "chromium") {
1726
+ const client = await playContext.newCDPSession(this.page);
1727
+ const { data } = await client.send("Page.captureScreenshot", {
1728
+ format: "png",
1729
+ // clip: {
1730
+ // x: 0,
1731
+ // y: 0,
1732
+ // width: viewportWidth,
1733
+ // height: viewportHeight,
1734
+ // scale: 1,
1735
+ // },
1736
+ });
1737
+ await client.detach();
1738
+ if (!screenshotPath) {
1739
+ return data;
1740
+ }
1741
+ screenshotBuffer = Buffer.from(data, "base64");
1742
+ }
1743
+ else {
1744
+ screenshotBuffer = await this.page.screenshot();
1745
+ }
1746
+ let image = await Jimp.read(screenshotBuffer);
1747
+ // Get the image dimensions
1748
+ const { width, height } = image.bitmap;
1749
+ const resizeRatio = viewportWidth / width;
1750
+ // Resize the image to fit within the viewport dimensions without enlarging
1751
+ if (width > viewportWidth) {
1752
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1753
+ await image.write(screenshotPath);
1754
+ }
1755
+ else {
1756
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1757
+ }
1707
1758
  }
1708
1759
  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;
1760
+ const state = {
1761
+ selectors,
1762
+ _params,
1763
+ options,
1764
+ world,
1765
+ type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
1766
+ text: `Verify element exists in page`,
1767
+ operation: "verifyElementExistInPage",
1768
+ log: "***** verify element " + selectors.element_name + " exists in page *****\n",
1769
+ };
1714
1770
  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
1771
  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;
1772
+ await _preCommand(state, this);
1773
+ await expect(state.element).toHaveCount(1, { timeout: 10000 });
1774
+ return state.info;
1728
1775
  }
1729
1776
  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;
1777
+ await _commandError(state, e, this);
1737
1778
  }
1738
1779
  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
- });
1780
+ _commandFinally(state, this);
1759
1781
  }
1760
1782
  }
1761
1783
  async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
1762
- this._validateSelectors(selectors);
1784
+ _validateSelectors(selectors);
1763
1785
  const startTime = Date.now();
1764
1786
  let error = null;
1765
1787
  let screenshotId = null;
@@ -1818,7 +1840,7 @@ class StableBrowser {
1818
1840
  status: "FAILED",
1819
1841
  startTime,
1820
1842
  endTime,
1821
- message: error === null || error === void 0 ? void 0 : error.message,
1843
+ message: error?.message,
1822
1844
  }
1823
1845
  : {
1824
1846
  status: "PASSED",
@@ -2027,7 +2049,7 @@ class StableBrowser {
2027
2049
  status: "FAILED",
2028
2050
  startTime,
2029
2051
  endTime,
2030
- message: error === null || error === void 0 ? void 0 : error.message,
2052
+ message: error?.message,
2031
2053
  }
2032
2054
  : {
2033
2055
  status: "PASSED",
@@ -2063,20 +2085,20 @@ class StableBrowser {
2063
2085
  for (let i = 0; i < frames.length; i++) {
2064
2086
  if (dateAlternatives.date) {
2065
2087
  for (let j = 0; j < dateAlternatives.dates.length; j++) {
2066
- const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
2088
+ const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
2067
2089
  result.frame = frames[i];
2068
2090
  results.push(result);
2069
2091
  }
2070
2092
  }
2071
2093
  else if (numberAlternatives.number) {
2072
2094
  for (let j = 0; j < numberAlternatives.numbers.length; j++) {
2073
- const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
2095
+ const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
2074
2096
  result.frame = frames[i];
2075
2097
  results.push(result);
2076
2098
  }
2077
2099
  }
2078
2100
  else {
2079
- const result = await this._locateElementByText(frames[i], text, "*", true, {});
2101
+ const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
2080
2102
  result.frame = frames[i];
2081
2103
  results.push(result);
2082
2104
  }
@@ -2125,7 +2147,7 @@ class StableBrowser {
2125
2147
  status: "FAILED",
2126
2148
  startTime,
2127
2149
  endTime,
2128
- message: error === null || error === void 0 ? void 0 : error.message,
2150
+ message: error?.message,
2129
2151
  }
2130
2152
  : {
2131
2153
  status: "PASSED",
@@ -2209,7 +2231,7 @@ class StableBrowser {
2209
2231
  status: "FAILED",
2210
2232
  startTime,
2211
2233
  endTime,
2212
- message: error === null || error === void 0 ? void 0 : error.message,
2234
+ message: error?.message,
2213
2235
  }
2214
2236
  : {
2215
2237
  status: "PASSED",
@@ -2241,7 +2263,7 @@ class StableBrowser {
2241
2263
  this.logger.info("Table data verified");
2242
2264
  }
2243
2265
  async getTableData(selectors, _params = null, options = {}, world = null) {
2244
- this._validateSelectors(selectors);
2266
+ _validateSelectors(selectors);
2245
2267
  const startTime = Date.now();
2246
2268
  let error = null;
2247
2269
  let screenshotId = null;
@@ -2277,7 +2299,7 @@ class StableBrowser {
2277
2299
  status: "FAILED",
2278
2300
  startTime,
2279
2301
  endTime,
2280
- message: error === null || error === void 0 ? void 0 : error.message,
2302
+ message: error?.message,
2281
2303
  }
2282
2304
  : {
2283
2305
  status: "PASSED",
@@ -2289,7 +2311,7 @@ class StableBrowser {
2289
2311
  }
2290
2312
  }
2291
2313
  async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
2292
- this._validateSelectors(selectors);
2314
+ _validateSelectors(selectors);
2293
2315
  if (!query) {
2294
2316
  throw new Error("query is null");
2295
2317
  }
@@ -2442,7 +2464,7 @@ class StableBrowser {
2442
2464
  status: "FAILED",
2443
2465
  startTime,
2444
2466
  endTime,
2445
- message: error === null || error === void 0 ? void 0 : error.message,
2467
+ message: error?.message,
2446
2468
  }
2447
2469
  : {
2448
2470
  status: "PASSED",
@@ -2454,27 +2476,7 @@ class StableBrowser {
2454
2476
  }
2455
2477
  }
2456
2478
  async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
2457
- if (!value) {
2458
- return value;
2459
- }
2460
- // find all the accurance of {{(.*?)}} and replace with the value
2461
- let regex = /{{(.*?)}}/g;
2462
- let matches = value.match(regex);
2463
- if (matches) {
2464
- const testData = this.getTestData(world);
2465
- for (let i = 0; i < matches.length; i++) {
2466
- let match = matches[i];
2467
- let key = match.substring(2, match.length - 2);
2468
- let newValue = objectPath.get(testData, key, null);
2469
- if (newValue !== null) {
2470
- value = value.replace(match, newValue);
2471
- }
2472
- }
2473
- }
2474
- if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
2475
- return await decrypt(value, null, totpWait);
2476
- }
2477
- return value;
2479
+ return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
2478
2480
  }
2479
2481
  _getLoadTimeout(options) {
2480
2482
  let timeout = 15000;
@@ -2511,13 +2513,13 @@ class StableBrowser {
2511
2513
  }
2512
2514
  catch (e) {
2513
2515
  if (e.label === "networkidle") {
2514
- console.log("waitted for the network to be idle timeout");
2516
+ console.log("waited for the network to be idle timeout");
2515
2517
  }
2516
2518
  else if (e.label === "load") {
2517
- console.log("waitted for the load timeout");
2519
+ console.log("waited for the load timeout");
2518
2520
  }
2519
2521
  else if (e.label === "domcontentloaded") {
2520
- console.log("waitted for the domcontent loaded timeout");
2522
+ console.log("waited for the domcontent loaded timeout");
2521
2523
  }
2522
2524
  console.log(".");
2523
2525
  }
@@ -2534,7 +2536,7 @@ class StableBrowser {
2534
2536
  status: "FAILED",
2535
2537
  startTime,
2536
2538
  endTime,
2537
- message: error === null || error === void 0 ? void 0 : error.message,
2539
+ message: error?.message,
2538
2540
  }
2539
2541
  : {
2540
2542
  status: "PASSED",
@@ -2552,13 +2554,6 @@ class StableBrowser {
2552
2554
  const info = {};
2553
2555
  try {
2554
2556
  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
2557
  }
2563
2558
  catch (e) {
2564
2559
  console.log(".");
@@ -2576,7 +2571,7 @@ class StableBrowser {
2576
2571
  status: "FAILED",
2577
2572
  startTime,
2578
2573
  endTime,
2579
- message: error === null || error === void 0 ? void 0 : error.message,
2574
+ message: error?.message,
2580
2575
  }
2581
2576
  : {
2582
2577
  status: "PASSED",
@@ -2618,7 +2613,7 @@ class StableBrowser {
2618
2613
  status: "FAILED",
2619
2614
  startTime,
2620
2615
  endTime,
2621
- message: error === null || error === void 0 ? void 0 : error.message,
2616
+ message: error?.message,
2622
2617
  }
2623
2618
  : {
2624
2619
  status: "PASSED",
@@ -2654,7 +2649,7 @@ class StableBrowser {
2654
2649
  status: "FAILED",
2655
2650
  startTime,
2656
2651
  endTime,
2657
- message: error === null || error === void 0 ? void 0 : error.message,
2652
+ message: error?.message,
2658
2653
  }
2659
2654
  : {
2660
2655
  status: "PASSED",
@@ -2667,33 +2662,18 @@ class StableBrowser {
2667
2662
  }
2668
2663
  async scrollIfNeeded(element, info) {
2669
2664
  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
- }
2665
+ await element.scrollIntoViewIfNeeded({
2666
+ timeout: 2000,
2687
2667
  });
2688
- if (didScroll) {
2689
- await new Promise((resolve) => setTimeout(resolve, 500));
2690
- if (info) {
2691
- info.box = await element.boundingBox();
2692
- }
2668
+ await new Promise((resolve) => setTimeout(resolve, 500));
2669
+ if (info) {
2670
+ info.box = await element.boundingBox({
2671
+ timeout: 1000,
2672
+ });
2693
2673
  }
2694
2674
  }
2695
2675
  catch (e) {
2696
- console.log("scroll failed");
2676
+ console.log("#-#");
2697
2677
  }
2698
2678
  }
2699
2679
  _reportToWorld(world, properties) {
@@ -2854,5 +2834,10 @@ const KEYBOARD_EVENTS = [
2854
2834
  "TVAntennaCable",
2855
2835
  "TVAudioDescription",
2856
2836
  ];
2837
+ function unEscapeString(str) {
2838
+ const placeholder = "__NEWLINE__";
2839
+ str = str.replace(new RegExp(placeholder, "g"), "\n");
2840
+ return str;
2841
+ }
2857
2842
  export { StableBrowser };
2858
2843
  //# sourceMappingURL=stable_browser.js.map