automation_model 1.0.416-dev → 1.0.416-stage

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  import { expect } from "@playwright/test";
3
3
  import dayjs from "dayjs";
4
4
  import fs from "fs";
5
+ import { Jimp } from "jimp";
5
6
  import path from "path";
6
7
  import reg_parser from "regex-parser";
7
- import sharp from "sharp";
8
8
  import { findDateAlternatives, findNumberAlternatives } from "./analyze_helper.js";
9
9
  import { getDateTimeValue } from "./date_time.js";
10
10
  import drawRectangle from "./drawRect.js";
@@ -14,6 +14,9 @@ import objectPath from "object-path";
14
14
  import { decrypt } from "./utils.js";
15
15
  import csv from "csv-parser";
16
16
  import { Readable } from "node:stream";
17
+ import readline from "readline";
18
+ import { getContext } from "./init_browser.js";
19
+ import { locate_element } from "./locate_element.js";
17
20
  const Types = {
18
21
  CLICK: "click_element",
19
22
  NAVIGATE: "navigate",
@@ -41,15 +44,19 @@ const Types = {
41
44
  LOAD_DATA: "load_data",
42
45
  SET_INPUT: "set_input",
43
46
  };
47
+ export const apps = {};
44
48
  class StableBrowser {
45
- constructor(browser, page, logger = null, context = null) {
49
+ constructor(browser, page, logger = null, context = null, world = null) {
46
50
  this.browser = browser;
47
51
  this.page = page;
48
52
  this.logger = logger;
49
53
  this.context = context;
54
+ this.world = world;
50
55
  this.project_path = null;
51
56
  this.webLogFile = null;
57
+ this.networkLogger = null;
52
58
  this.configuration = null;
59
+ this.appName = "main";
53
60
  if (!this.logger) {
54
61
  this.logger = console;
55
62
  }
@@ -75,23 +82,34 @@ class StableBrowser {
75
82
  this.logger.error("unable to read ai_config.json");
76
83
  }
77
84
  const logFolder = path.join(this.project_path, "logs", "web");
78
- this.webLogFile = this.getWebLogFile(logFolder);
79
- this.registerConsoleLogListener(page, context, this.webLogFile);
80
- this.registerRequestListener();
85
+ this.world = world;
81
86
  context.pages = [this.page];
82
87
  context.pageLoading = { status: false };
88
+ this.registerEventListeners(this.context);
89
+ }
90
+ registerEventListeners(context) {
91
+ this.registerConsoleLogListener(this.page, context);
92
+ this.registerRequestListener(this.page, context, this.webLogFile);
93
+ if (!context.pageLoading) {
94
+ context.pageLoading = { status: false };
95
+ }
83
96
  context.playContext.on("page", async function (page) {
84
97
  context.pageLoading.status = true;
85
98
  this.page = page;
86
99
  context.page = page;
87
100
  context.pages.push(page);
88
101
  page.on("close", async () => {
89
- if (this.context && this.context.pages && this.context.pages.length > 0) {
102
+ if (this.context && this.context.pages && this.context.pages.length > 1) {
90
103
  this.context.pages.pop();
91
104
  this.page = this.context.pages[this.context.pages.length - 1];
92
105
  this.context.page = this.page;
93
- let title = await this.page.title();
94
- console.log("Switched to page " + title);
106
+ try {
107
+ let title = await this.page.title();
108
+ console.log("Switched to page " + title);
109
+ }
110
+ catch (error) {
111
+ console.error("Error on page close", error);
112
+ }
95
113
  }
96
114
  });
97
115
  try {
@@ -104,6 +122,36 @@ class StableBrowser {
104
122
  context.pageLoading.status = false;
105
123
  }.bind(this));
106
124
  }
125
+ async switchApp(appName) {
126
+ // check if the current app (this.appName) is the same as the new app
127
+ if (this.appName === appName) {
128
+ return;
129
+ }
130
+ let navigate = false;
131
+ if (!apps[appName]) {
132
+ let newContext = await getContext(null, false, this.logger, appName, false, this);
133
+ navigate = true;
134
+ apps[appName] = {
135
+ context: newContext,
136
+ browser: newContext.browser,
137
+ page: newContext.page,
138
+ };
139
+ }
140
+ const tempContext = {};
141
+ this._copyContext(this, tempContext);
142
+ this._copyContext(apps[appName], this);
143
+ apps[this.appName] = tempContext;
144
+ this.appName = appName;
145
+ if (navigate) {
146
+ await this.goto(this.context.environment.baseUrl);
147
+ await this.waitForPageLoad();
148
+ }
149
+ }
150
+ _copyContext(from, to) {
151
+ to.browser = from.browser;
152
+ to.page = from.page;
153
+ to.context = from.context;
154
+ }
107
155
  getWebLogFile(logFolder) {
108
156
  if (!fs.existsSync(logFolder)) {
109
157
  fs.mkdirSync(logFolder, { recursive: true });
@@ -115,37 +163,63 @@ class StableBrowser {
115
163
  const fileName = nextIndex + ".json";
116
164
  return path.join(logFolder, fileName);
117
165
  }
118
- registerConsoleLogListener(page, context, logFile) {
166
+ registerConsoleLogListener(page, context) {
119
167
  if (!this.context.webLogger) {
120
168
  this.context.webLogger = [];
121
169
  }
122
170
  page.on("console", async (msg) => {
123
- this.context.webLogger.push({
171
+ var _a;
172
+ const obj = {
124
173
  type: msg.type(),
125
174
  text: msg.text(),
126
175
  location: msg.location(),
127
176
  time: new Date().toISOString(),
128
- });
129
- await fs.promises.writeFile(logFile, JSON.stringify(this.context.webLogger, null, 2));
177
+ };
178
+ this.context.webLogger.push(obj);
179
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
130
180
  });
131
181
  }
132
- registerRequestListener() {
133
- this.page.on("request", async (data) => {
182
+ registerRequestListener(page, context, logFile) {
183
+ if (!this.context.networkLogger) {
184
+ this.context.networkLogger = [];
185
+ }
186
+ page.on("request", async (data) => {
187
+ var _a;
188
+ const startTime = new Date().getTime();
134
189
  try {
135
- const pageUrl = new URL(this.page.url());
190
+ const pageUrl = new URL(page.url());
136
191
  const requestUrl = new URL(data.url());
137
192
  if (pageUrl.hostname === requestUrl.hostname) {
138
193
  const method = data.method();
139
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
194
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
140
195
  const token = await data.headerValue("Authorization");
141
196
  if (token) {
142
- this.context.authtoken = token;
197
+ context.authtoken = token;
143
198
  }
144
199
  }
145
200
  }
201
+ const response = await data.response();
202
+ const endTime = new Date().getTime();
203
+ const obj = {
204
+ url: data.url(),
205
+ method: data.method(),
206
+ postData: data.postData(),
207
+ error: data.failure() ? data.failure().errorText : null,
208
+ duration: endTime - startTime,
209
+ startTime,
210
+ };
211
+ context.networkLogger.push(obj);
212
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
146
213
  }
147
214
  catch (error) {
148
215
  console.error("Error in request listener", error);
216
+ context.networkLogger.push({
217
+ error: "not able to listen",
218
+ message: error.message,
219
+ stack: error.stack,
220
+ time: new Date().toISOString(),
221
+ });
222
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
149
223
  }
150
224
  });
151
225
  }
@@ -226,7 +300,7 @@ class StableBrowser {
226
300
  // }
227
301
  locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
228
302
  }
229
- if (locator.css || locator.engine === "css") {
303
+ if (locator.css) {
230
304
  locatorReturn = scope.locator(locator.css);
231
305
  }
232
306
  // handle role/name locators
@@ -241,34 +315,30 @@ class StableBrowser {
241
315
  locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
242
316
  }
243
317
  }
244
- // locator object { engine: "internal:text", selector: '"Signup / Login"i', priority: 4 },
245
- if (locator.engine === "internal:text") {
246
- // extract the text and the i flag using regex
247
- const match = locator.selector.match(/"(.*)"(.*)/);
248
- if (match) {
249
- const text = match[1];
250
- const flags = match[2];
251
- locatorReturn = scope.locator(`text=${text}`, { exact: flags === "i" });
318
+ if (locator === null || locator === void 0 ? void 0 : locator.engine) {
319
+ if (locator.engine === "css") {
320
+ locatorReturn = scope.locator(locator.selector);
321
+ }
322
+ else {
323
+ let selector = locator.selector;
324
+ if (locator.engine === "internal:attr") {
325
+ if (!selector.startsWith("[")) {
326
+ selector = `[${selector}]`;
327
+ }
328
+ }
329
+ locatorReturn = scope.locator(`${locator.engine}=${selector}`);
252
330
  }
253
- }
254
- if (locator.engine === "internal:attr") {
255
- selector = `[${selector}]`;
256
- locatorReturn = scope.locator(`${locator.engine}=${selector}`);
257
331
  }
258
332
  if (!locatorReturn) {
259
333
  console.error(locator);
260
- throw new Error("Locator " + JSON.stringify(locator) + " not found");
261
- // } else {
262
- // const count = locatorReturn.count();
263
- // if (count === 0) {
264
- // throw new Error("Elements not found");
265
- // } else if (count > 1) {
266
- // throw new Error("Multiple elements found");
267
- // }
334
+ throw new Error("Locator undefined");
268
335
  }
269
336
  return locatorReturn;
270
337
  }
271
338
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
339
+ if (css && css.locator) {
340
+ css = css.locator;
341
+ }
272
342
  let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
273
343
  if (result.elementCount === 0) {
274
344
  return;
@@ -296,6 +366,15 @@ class StableBrowser {
296
366
  return false;
297
367
  }
298
368
  document.isParent = isParent;
369
+ function getRegex(str) {
370
+ const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
371
+ if (!match) {
372
+ return null;
373
+ }
374
+ let [_, pattern, flags] = match;
375
+ return new RegExp(pattern, flags);
376
+ }
377
+ document.getRegex = getRegex;
299
378
  function collectAllShadowDomElements(element, result = []) {
300
379
  // Check and add the element if it has a shadow root
301
380
  if (element.shadowRoot) {
@@ -314,6 +393,10 @@ class StableBrowser {
314
393
  if (!tag) {
315
394
  tag = "*";
316
395
  }
396
+ let regexpSearch = document.getRegex(text);
397
+ if (regexpSearch) {
398
+ regex = true;
399
+ }
317
400
  let elements = Array.from(document.querySelectorAll(tag));
318
401
  let shadowHosts = [];
319
402
  document.collectAllShadowDomElements(document, shadowHosts);
@@ -329,7 +412,9 @@ class StableBrowser {
329
412
  let randomToken = null;
330
413
  const foundElements = [];
331
414
  if (regex) {
332
- let regexpSearch = new RegExp(text, "im");
415
+ if (!regexpSearch) {
416
+ regexpSearch = new RegExp(text, "im");
417
+ }
333
418
  for (let i = 0; i < elements.length; i++) {
334
419
  const element = elements[i];
335
420
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -343,8 +428,8 @@ class StableBrowser {
343
428
  for (let i = 0; i < elements.length; i++) {
344
429
  const element = elements[i];
345
430
  if (partial) {
346
- if ((element.innerText && element.innerText.trim().includes(text)) ||
347
- (element.value && element.value.includes(text))) {
431
+ if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
432
+ (element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
348
433
  foundElements.push(element);
349
434
  }
350
435
  }
@@ -389,6 +474,12 @@ class StableBrowser {
389
474
  }
390
475
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
391
476
  let locatorSearch = selectorHierarchy[index];
477
+ try {
478
+ locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
479
+ }
480
+ catch (e) {
481
+ console.error(e);
482
+ }
392
483
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
393
484
  let locator = null;
394
485
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
@@ -477,6 +568,8 @@ class StableBrowser {
477
568
  if (result.foundElements.length > 0) {
478
569
  let dialogCloseLocator = result.foundElements[0].locator;
479
570
  await dialogCloseLocator.click();
571
+ // wait for the dialog to close
572
+ await dialogCloseLocator.waitFor({ state: "hidden" });
480
573
  return { rerun: true };
481
574
  }
482
575
  }
@@ -485,7 +578,7 @@ class StableBrowser {
485
578
  }
486
579
  async _locate(selectors, info, _params, timeout = 30000) {
487
580
  for (let i = 0; i < 3; i++) {
488
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
581
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
489
582
  for (let j = 0; j < selectors.locators.length; j++) {
490
583
  let selector = selectors.locators[j];
491
584
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -504,10 +597,44 @@ class StableBrowser {
504
597
  let locatorsCount = 0;
505
598
  //let arrayMode = Array.isArray(selectors);
506
599
  let scope = this.page;
600
+ // for the simple click usecase
601
+ if (selectors.frame) {
602
+ scope = selectors.frame;
603
+ }
507
604
  if (selectors.iframe_src || selectors.frameLocators) {
605
+ const findFrame = async (frame, framescope) => {
606
+ for (let i = 0; i < frame.selectors.length; i++) {
607
+ let frameLocator = frame.selectors[i];
608
+ if (frameLocator.css) {
609
+ let testframescope = framescope.frameLocator(frameLocator.css);
610
+ if (frameLocator.index) {
611
+ testframescope = framescope.nth(frameLocator.index);
612
+ }
613
+ try {
614
+ await testframescope.owner().evaluateHandle(() => true, null, {
615
+ timeout: 5000,
616
+ });
617
+ framescope = testframescope;
618
+ break;
619
+ }
620
+ catch (error) {
621
+ console.error("frame not found " + frameLocator.css);
622
+ }
623
+ }
624
+ }
625
+ if (frame.children) {
626
+ return await findFrame(frame.children, framescope);
627
+ }
628
+ return framescope;
629
+ };
508
630
  info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
509
631
  while (true) {
510
632
  let frameFound = false;
633
+ if (selectors.nestFrmLoc) {
634
+ scope = await findFrame(selectors.nestFrmLoc, scope);
635
+ frameFound = true;
636
+ break;
637
+ }
511
638
  if (selectors.frameLocators) {
512
639
  for (let i = 0; i < selectors.frameLocators.length; i++) {
513
640
  let frameLocator = selectors.frameLocators[i];
@@ -668,6 +795,66 @@ class StableBrowser {
668
795
  }
669
796
  return result;
670
797
  }
798
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
799
+ const startTime = Date.now();
800
+ let timeout = 30000;
801
+ if (options && options.timeout) {
802
+ timeout = options.timeout;
803
+ }
804
+ while (true) {
805
+ try {
806
+ const result = await locate_element(this.context, elementDescription, "click");
807
+ if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
808
+ const selectors = {
809
+ frame: result === null || result === void 0 ? void 0 : result.frame,
810
+ locators: [
811
+ {
812
+ css: result === null || result === void 0 ? void 0 : result.css,
813
+ },
814
+ ],
815
+ };
816
+ await this.click(selectors, _params, options, world);
817
+ return;
818
+ }
819
+ }
820
+ catch (e) {
821
+ if (performance.now() - startTime > timeout) {
822
+ throw e;
823
+ }
824
+ }
825
+ await new Promise((resolve) => setTimeout(resolve, 3000));
826
+ }
827
+ }
828
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
829
+ const startTime = Date.now();
830
+ let timeout = 30000;
831
+ if (options && options.timeout) {
832
+ timeout = options.timeout;
833
+ }
834
+ while (true) {
835
+ try {
836
+ const result = await locate_element(this.context, elementDescription, "fill", value);
837
+ if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
838
+ const selectors = {
839
+ frame: result === null || result === void 0 ? void 0 : result.frame,
840
+ locators: [
841
+ {
842
+ css: result === null || result === void 0 ? void 0 : result.css,
843
+ },
844
+ ],
845
+ };
846
+ await this.clickType(selectors, value, false, _params, options, world);
847
+ return;
848
+ }
849
+ }
850
+ catch (e) {
851
+ if (performance.now() - startTime > timeout) {
852
+ throw e;
853
+ }
854
+ }
855
+ await new Promise((resolve) => setTimeout(resolve, 3000));
856
+ }
857
+ }
671
858
  async click(selectors, _params, options = {}, world = null) {
672
859
  this._validateSelectors(selectors);
673
860
  const startTime = Date.now();
@@ -687,14 +874,14 @@ class StableBrowser {
687
874
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
688
875
  try {
689
876
  await this._highlightElements(element);
690
- await element.click({ timeout: 5000 });
877
+ await element.click();
691
878
  await new Promise((resolve) => setTimeout(resolve, 1000));
692
879
  }
693
880
  catch (e) {
694
881
  // await this.closeUnexpectedPopups();
695
882
  info.log += "click failed, will try again" + "\n";
696
883
  element = await this._locate(selectors, info, _params);
697
- await element.click({ timeout: 10000, force: true });
884
+ await element.dispatchEvent("click");
698
885
  await new Promise((resolve) => setTimeout(resolve, 1000));
699
886
  }
700
887
  await this.waitForPageLoad();
@@ -747,7 +934,7 @@ class StableBrowser {
747
934
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
748
935
  try {
749
936
  await this._highlightElements(element);
750
- await element.setChecked(checked, { timeout: 5000 });
937
+ await element.setChecked(checked);
751
938
  await new Promise((resolve) => setTimeout(resolve, 1000));
752
939
  }
753
940
  catch (e) {
@@ -811,7 +998,7 @@ class StableBrowser {
811
998
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
812
999
  try {
813
1000
  await this._highlightElements(element);
814
- await element.hover({ timeout: 10000 });
1001
+ await element.hover();
815
1002
  await new Promise((resolve) => setTimeout(resolve, 1000));
816
1003
  }
817
1004
  catch (e) {
@@ -873,7 +1060,7 @@ class StableBrowser {
873
1060
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
874
1061
  try {
875
1062
  await this._highlightElements(element);
876
- await element.selectOption(values, { timeout: 5000 });
1063
+ await element.selectOption(values);
877
1064
  }
878
1065
  catch (e) {
879
1066
  //await this.closeUnexpectedPopups();
@@ -1161,7 +1348,6 @@ class StableBrowser {
1161
1348
  let element = await this._locate(selectors, info, _params);
1162
1349
  //insert red border around the element
1163
1350
  await this.scrollIfNeeded(element, info);
1164
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1165
1351
  await this._highlightElements(element);
1166
1352
  if (options === null || options === undefined || !options.press) {
1167
1353
  try {
@@ -1211,6 +1397,7 @@ class StableBrowser {
1211
1397
  await new Promise((resolve) => setTimeout(resolve, 500));
1212
1398
  }
1213
1399
  }
1400
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1214
1401
  if (enter === true) {
1215
1402
  await new Promise((resolve) => setTimeout(resolve, 2000));
1216
1403
  await this.page.keyboard.press("Enter");
@@ -1276,7 +1463,7 @@ class StableBrowser {
1276
1463
  let element = await this._locate(selectors, info, _params);
1277
1464
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1278
1465
  await this._highlightElements(element);
1279
- await element.fill(value, { timeout: 10000 });
1466
+ await element.fill(value);
1280
1467
  await element.dispatchEvent("change");
1281
1468
  if (enter) {
1282
1469
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -1495,7 +1682,7 @@ class StableBrowser {
1495
1682
  return info;
1496
1683
  }
1497
1684
  catch (e) {
1498
- //await this.closeUnexpectedPopups();
1685
+ await this.closeUnexpectedPopups();
1499
1686
  this.logger.error("verify element contains text failed " + info.log);
1500
1687
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1501
1688
  info.screenshotPath = screenshotPath;
@@ -1543,6 +1730,29 @@ class StableBrowser {
1543
1730
  }
1544
1731
  return dataFile;
1545
1732
  }
1733
+ async waitForUserInput(message, world = null) {
1734
+ if (!message) {
1735
+ message = "# Wait for user input. Press any key to continue";
1736
+ }
1737
+ else {
1738
+ message = "# Wait for user input. " + message;
1739
+ }
1740
+ message += "\n";
1741
+ const value = await new Promise((resolve) => {
1742
+ const rl = readline.createInterface({
1743
+ input: process.stdin,
1744
+ output: process.stdout,
1745
+ });
1746
+ rl.question(message, (answer) => {
1747
+ rl.close();
1748
+ resolve(answer);
1749
+ });
1750
+ });
1751
+ if (value) {
1752
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1753
+ }
1754
+ this.setTestData({ userInput: value }, world);
1755
+ }
1546
1756
  setTestData(testData, world = null) {
1547
1757
  if (!testData) {
1548
1758
  return;
@@ -1730,7 +1940,6 @@ class StableBrowser {
1730
1940
  }
1731
1941
  async takeScreenshot(screenshotPath) {
1732
1942
  const playContext = this.context.playContext;
1733
- const client = await playContext.newCDPSession(this.page);
1734
1943
  // Using CDP to capture the screenshot
1735
1944
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1736
1945
  document.body.scrollWidth,
@@ -1740,41 +1949,40 @@ class StableBrowser {
1740
1949
  document.body.clientWidth,
1741
1950
  document.documentElement.clientWidth,
1742
1951
  ])));
1743
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1744
- document.body.scrollHeight,
1745
- document.documentElement.scrollHeight,
1746
- document.body.offsetHeight,
1747
- document.documentElement.offsetHeight,
1748
- document.body.clientHeight,
1749
- document.documentElement.clientHeight,
1750
- ])));
1751
- const { data } = await client.send("Page.captureScreenshot", {
1752
- format: "png",
1753
- // clip: {
1754
- // x: 0,
1755
- // y: 0,
1756
- // width: viewportWidth,
1757
- // height: viewportHeight,
1758
- // scale: 1,
1759
- // },
1760
- });
1761
- if (!screenshotPath) {
1762
- return data;
1763
- }
1764
- let screenshotBuffer = Buffer.from(data, "base64");
1765
- const sharpBuffer = sharp(screenshotBuffer);
1766
- const metadata = await sharpBuffer.metadata();
1767
- //check if you are on retina display and reduce the quality of the image
1768
- if (metadata.width > viewportWidth || metadata.height > viewportHeight) {
1769
- screenshotBuffer = await sharpBuffer
1770
- .resize(viewportWidth, viewportHeight, {
1771
- fit: sharp.fit.inside,
1772
- withoutEnlargement: true,
1773
- })
1774
- .toBuffer();
1775
- }
1776
- fs.writeFileSync(screenshotPath, screenshotBuffer);
1777
- await client.detach();
1952
+ let screenshotBuffer = null;
1953
+ if (this.context.browserName === "chromium") {
1954
+ const client = await playContext.newCDPSession(this.page);
1955
+ const { data } = await client.send("Page.captureScreenshot", {
1956
+ format: "png",
1957
+ // clip: {
1958
+ // x: 0,
1959
+ // y: 0,
1960
+ // width: viewportWidth,
1961
+ // height: viewportHeight,
1962
+ // scale: 1,
1963
+ // },
1964
+ });
1965
+ await client.detach();
1966
+ if (!screenshotPath) {
1967
+ return data;
1968
+ }
1969
+ screenshotBuffer = Buffer.from(data, "base64");
1970
+ }
1971
+ else {
1972
+ screenshotBuffer = await this.page.screenshot();
1973
+ }
1974
+ let image = await Jimp.read(screenshotBuffer);
1975
+ // Get the image dimensions
1976
+ const { width, height } = image.bitmap;
1977
+ const resizeRatio = viewportWidth / width;
1978
+ // Resize the image to fit within the viewport dimensions without enlarging
1979
+ if (width > viewportWidth) {
1980
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1981
+ await image.write(screenshotPath);
1982
+ }
1983
+ else {
1984
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1985
+ }
1778
1986
  }
1779
1987
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1780
1988
  this._validateSelectors(selectors);
@@ -2582,13 +2790,13 @@ class StableBrowser {
2582
2790
  }
2583
2791
  catch (e) {
2584
2792
  if (e.label === "networkidle") {
2585
- console.log("waitted for the network to be idle timeout");
2793
+ console.log("waited for the network to be idle timeout");
2586
2794
  }
2587
2795
  else if (e.label === "load") {
2588
- console.log("waitted for the load timeout");
2796
+ console.log("waited for the load timeout");
2589
2797
  }
2590
2798
  else if (e.label === "domcontentloaded") {
2591
- console.log("waitted for the domcontent loaded timeout");
2799
+ console.log("waited for the domcontent loaded timeout");
2592
2800
  }
2593
2801
  console.log(".");
2594
2802
  }
@@ -2731,33 +2939,18 @@ class StableBrowser {
2731
2939
  }
2732
2940
  async scrollIfNeeded(element, info) {
2733
2941
  try {
2734
- let didScroll = await element.evaluate((node) => {
2735
- const rect = node.getBoundingClientRect();
2736
- if (rect &&
2737
- rect.top >= 0 &&
2738
- rect.left >= 0 &&
2739
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2740
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
2741
- return false;
2742
- }
2743
- else {
2744
- node.scrollIntoView({
2745
- behavior: "smooth",
2746
- block: "center",
2747
- inline: "center",
2748
- });
2749
- return true;
2750
- }
2942
+ await element.scrollIntoViewIfNeeded({
2943
+ timeout: 2000,
2751
2944
  });
2752
- if (didScroll) {
2753
- await new Promise((resolve) => setTimeout(resolve, 500));
2754
- if (info) {
2755
- info.box = await element.boundingBox();
2756
- }
2945
+ await new Promise((resolve) => setTimeout(resolve, 500));
2946
+ if (info) {
2947
+ info.box = await element.boundingBox({
2948
+ timeout: 1000,
2949
+ });
2757
2950
  }
2758
2951
  }
2759
2952
  catch (e) {
2760
- console.log("scroll failed");
2953
+ console.log("#-#");
2761
2954
  }
2762
2955
  }
2763
2956
  _reportToWorld(world, properties) {