automation_model 1.0.417-dev → 1.0.417-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,65 @@ 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
+ if (msg.type() === "error") {
180
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
181
+ }
130
182
  });
131
183
  }
132
- registerRequestListener() {
133
- this.page.on("request", async (data) => {
184
+ registerRequestListener(page, context, logFile) {
185
+ if (!this.context.networkLogger) {
186
+ this.context.networkLogger = [];
187
+ }
188
+ page.on("request", async (data) => {
189
+ var _a;
190
+ const startTime = new Date().getTime();
134
191
  try {
135
- const pageUrl = new URL(this.page.url());
192
+ const pageUrl = new URL(page.url());
136
193
  const requestUrl = new URL(data.url());
137
194
  if (pageUrl.hostname === requestUrl.hostname) {
138
195
  const method = data.method();
139
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
196
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
140
197
  const token = await data.headerValue("Authorization");
141
198
  if (token) {
142
- this.context.authtoken = token;
199
+ context.authtoken = token;
143
200
  }
144
201
  }
145
202
  }
203
+ const response = await data.response();
204
+ const endTime = new Date().getTime();
205
+ const obj = {
206
+ url: data.url(),
207
+ method: data.method(),
208
+ postData: data.postData(),
209
+ error: data.failure() ? data.failure().errorText : null,
210
+ duration: endTime - startTime,
211
+ startTime,
212
+ };
213
+ context.networkLogger.push(obj);
214
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
146
215
  }
147
216
  catch (error) {
148
217
  console.error("Error in request listener", error);
218
+ context.networkLogger.push({
219
+ error: "not able to listen",
220
+ message: error.message,
221
+ stack: error.stack,
222
+ time: new Date().toISOString(),
223
+ });
224
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
149
225
  }
150
226
  });
151
227
  }
@@ -248,7 +324,9 @@ class StableBrowser {
248
324
  else {
249
325
  let selector = locator.selector;
250
326
  if (locator.engine === "internal:attr") {
251
- selector = `[${selector}]`;
327
+ if (!selector.startsWith("[")) {
328
+ selector = `[${selector}]`;
329
+ }
252
330
  }
253
331
  locatorReturn = scope.locator(`${locator.engine}=${selector}`);
254
332
  }
@@ -260,6 +338,9 @@ class StableBrowser {
260
338
  return locatorReturn;
261
339
  }
262
340
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
341
+ if (css && css.locator) {
342
+ css = css.locator;
343
+ }
263
344
  let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
264
345
  if (result.elementCount === 0) {
265
346
  return;
@@ -287,6 +368,15 @@ class StableBrowser {
287
368
  return false;
288
369
  }
289
370
  document.isParent = isParent;
371
+ function getRegex(str) {
372
+ const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
373
+ if (!match) {
374
+ return null;
375
+ }
376
+ let [_, pattern, flags] = match;
377
+ return new RegExp(pattern, flags);
378
+ }
379
+ document.getRegex = getRegex;
290
380
  function collectAllShadowDomElements(element, result = []) {
291
381
  // Check and add the element if it has a shadow root
292
382
  if (element.shadowRoot) {
@@ -305,6 +395,10 @@ class StableBrowser {
305
395
  if (!tag) {
306
396
  tag = "*";
307
397
  }
398
+ let regexpSearch = document.getRegex(text);
399
+ if (regexpSearch) {
400
+ regex = true;
401
+ }
308
402
  let elements = Array.from(document.querySelectorAll(tag));
309
403
  let shadowHosts = [];
310
404
  document.collectAllShadowDomElements(document, shadowHosts);
@@ -320,7 +414,9 @@ class StableBrowser {
320
414
  let randomToken = null;
321
415
  const foundElements = [];
322
416
  if (regex) {
323
- let regexpSearch = new RegExp(text, "im");
417
+ if (!regexpSearch) {
418
+ regexpSearch = new RegExp(text, "im");
419
+ }
324
420
  for (let i = 0; i < elements.length; i++) {
325
421
  const element = elements[i];
326
422
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -334,8 +430,8 @@ class StableBrowser {
334
430
  for (let i = 0; i < elements.length; i++) {
335
431
  const element = elements[i];
336
432
  if (partial) {
337
- if ((element.innerText && element.innerText.trim().includes(text)) ||
338
- (element.value && element.value.includes(text))) {
433
+ if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
434
+ (element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
339
435
  foundElements.push(element);
340
436
  }
341
437
  }
@@ -380,6 +476,12 @@ class StableBrowser {
380
476
  }
381
477
  async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
382
478
  let locatorSearch = selectorHierarchy[index];
479
+ try {
480
+ locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
481
+ }
482
+ catch (e) {
483
+ console.error(e);
484
+ }
383
485
  //info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
384
486
  let locator = null;
385
487
  if (locatorSearch.climb && locatorSearch.climb >= 0) {
@@ -468,6 +570,8 @@ class StableBrowser {
468
570
  if (result.foundElements.length > 0) {
469
571
  let dialogCloseLocator = result.foundElements[0].locator;
470
572
  await dialogCloseLocator.click();
573
+ // wait for the dialog to close
574
+ await dialogCloseLocator.waitFor({ state: "hidden" });
471
575
  return { rerun: true };
472
576
  }
473
577
  }
@@ -476,7 +580,7 @@ class StableBrowser {
476
580
  }
477
581
  async _locate(selectors, info, _params, timeout = 30000) {
478
582
  for (let i = 0; i < 3; i++) {
479
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
583
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
480
584
  for (let j = 0; j < selectors.locators.length; j++) {
481
585
  let selector = selectors.locators[j];
482
586
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -495,10 +599,44 @@ class StableBrowser {
495
599
  let locatorsCount = 0;
496
600
  //let arrayMode = Array.isArray(selectors);
497
601
  let scope = this.page;
602
+ // for the simple click usecase
603
+ if (selectors.frame) {
604
+ scope = selectors.frame;
605
+ }
498
606
  if (selectors.iframe_src || selectors.frameLocators) {
607
+ const findFrame = async (frame, framescope) => {
608
+ for (let i = 0; i < frame.selectors.length; i++) {
609
+ let frameLocator = frame.selectors[i];
610
+ if (frameLocator.css) {
611
+ let testframescope = framescope.frameLocator(frameLocator.css);
612
+ if (frameLocator.index) {
613
+ testframescope = framescope.nth(frameLocator.index);
614
+ }
615
+ try {
616
+ await testframescope.owner().evaluateHandle(() => true, null, {
617
+ timeout: 5000,
618
+ });
619
+ framescope = testframescope;
620
+ break;
621
+ }
622
+ catch (error) {
623
+ console.error("frame not found " + frameLocator.css);
624
+ }
625
+ }
626
+ }
627
+ if (frame.children) {
628
+ return await findFrame(frame.children, framescope);
629
+ }
630
+ return framescope;
631
+ };
499
632
  info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
500
633
  while (true) {
501
634
  let frameFound = false;
635
+ if (selectors.nestFrmLoc) {
636
+ scope = await findFrame(selectors.nestFrmLoc, scope);
637
+ frameFound = true;
638
+ break;
639
+ }
502
640
  if (selectors.frameLocators) {
503
641
  for (let i = 0; i < selectors.frameLocators.length; i++) {
504
642
  let frameLocator = selectors.frameLocators[i];
@@ -659,6 +797,66 @@ class StableBrowser {
659
797
  }
660
798
  return result;
661
799
  }
800
+ async simpleClick(elementDescription, _params, options = {}, world = null) {
801
+ const startTime = Date.now();
802
+ let timeout = 30000;
803
+ if (options && options.timeout) {
804
+ timeout = options.timeout;
805
+ }
806
+ while (true) {
807
+ try {
808
+ const result = await locate_element(this.context, elementDescription, "click");
809
+ if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
810
+ const selectors = {
811
+ frame: result === null || result === void 0 ? void 0 : result.frame,
812
+ locators: [
813
+ {
814
+ css: result === null || result === void 0 ? void 0 : result.css,
815
+ },
816
+ ],
817
+ };
818
+ await this.click(selectors, _params, options, world);
819
+ return;
820
+ }
821
+ }
822
+ catch (e) {
823
+ if (performance.now() - startTime > timeout) {
824
+ throw e;
825
+ }
826
+ }
827
+ await new Promise((resolve) => setTimeout(resolve, 3000));
828
+ }
829
+ }
830
+ async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
831
+ const startTime = Date.now();
832
+ let timeout = 30000;
833
+ if (options && options.timeout) {
834
+ timeout = options.timeout;
835
+ }
836
+ while (true) {
837
+ try {
838
+ const result = await locate_element(this.context, elementDescription, "fill", value);
839
+ if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
840
+ const selectors = {
841
+ frame: result === null || result === void 0 ? void 0 : result.frame,
842
+ locators: [
843
+ {
844
+ css: result === null || result === void 0 ? void 0 : result.css,
845
+ },
846
+ ],
847
+ };
848
+ await this.clickType(selectors, value, false, _params, options, world);
849
+ return;
850
+ }
851
+ }
852
+ catch (e) {
853
+ if (performance.now() - startTime > timeout) {
854
+ throw e;
855
+ }
856
+ }
857
+ await new Promise((resolve) => setTimeout(resolve, 3000));
858
+ }
859
+ }
662
860
  async click(selectors, _params, options = {}, world = null) {
663
861
  this._validateSelectors(selectors);
664
862
  const startTime = Date.now();
@@ -678,14 +876,14 @@ class StableBrowser {
678
876
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
679
877
  try {
680
878
  await this._highlightElements(element);
681
- await element.click({ timeout: 5000 });
879
+ await element.click();
682
880
  await new Promise((resolve) => setTimeout(resolve, 1000));
683
881
  }
684
882
  catch (e) {
685
883
  // await this.closeUnexpectedPopups();
686
884
  info.log += "click failed, will try again" + "\n";
687
885
  element = await this._locate(selectors, info, _params);
688
- await element.click({ timeout: 10000, force: true });
886
+ await element.dispatchEvent("click");
689
887
  await new Promise((resolve) => setTimeout(resolve, 1000));
690
888
  }
691
889
  await this.waitForPageLoad();
@@ -738,7 +936,7 @@ class StableBrowser {
738
936
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
739
937
  try {
740
938
  await this._highlightElements(element);
741
- await element.setChecked(checked, { timeout: 5000 });
939
+ await element.setChecked(checked);
742
940
  await new Promise((resolve) => setTimeout(resolve, 1000));
743
941
  }
744
942
  catch (e) {
@@ -802,7 +1000,7 @@ class StableBrowser {
802
1000
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
803
1001
  try {
804
1002
  await this._highlightElements(element);
805
- await element.hover({ timeout: 10000 });
1003
+ await element.hover();
806
1004
  await new Promise((resolve) => setTimeout(resolve, 1000));
807
1005
  }
808
1006
  catch (e) {
@@ -864,7 +1062,7 @@ class StableBrowser {
864
1062
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
865
1063
  try {
866
1064
  await this._highlightElements(element);
867
- await element.selectOption(values, { timeout: 5000 });
1065
+ await element.selectOption(values);
868
1066
  }
869
1067
  catch (e) {
870
1068
  //await this.closeUnexpectedPopups();
@@ -1152,7 +1350,6 @@ class StableBrowser {
1152
1350
  let element = await this._locate(selectors, info, _params);
1153
1351
  //insert red border around the element
1154
1352
  await this.scrollIfNeeded(element, info);
1155
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1156
1353
  await this._highlightElements(element);
1157
1354
  if (options === null || options === undefined || !options.press) {
1158
1355
  try {
@@ -1202,6 +1399,7 @@ class StableBrowser {
1202
1399
  await new Promise((resolve) => setTimeout(resolve, 500));
1203
1400
  }
1204
1401
  }
1402
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1205
1403
  if (enter === true) {
1206
1404
  await new Promise((resolve) => setTimeout(resolve, 2000));
1207
1405
  await this.page.keyboard.press("Enter");
@@ -1267,7 +1465,7 @@ class StableBrowser {
1267
1465
  let element = await this._locate(selectors, info, _params);
1268
1466
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1269
1467
  await this._highlightElements(element);
1270
- await element.fill(value, { timeout: 10000 });
1468
+ await element.fill(value);
1271
1469
  await element.dispatchEvent("change");
1272
1470
  if (enter) {
1273
1471
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -1486,7 +1684,7 @@ class StableBrowser {
1486
1684
  return info;
1487
1685
  }
1488
1686
  catch (e) {
1489
- //await this.closeUnexpectedPopups();
1687
+ await this.closeUnexpectedPopups();
1490
1688
  this.logger.error("verify element contains text failed " + info.log);
1491
1689
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1492
1690
  info.screenshotPath = screenshotPath;
@@ -1534,6 +1732,29 @@ class StableBrowser {
1534
1732
  }
1535
1733
  return dataFile;
1536
1734
  }
1735
+ async waitForUserInput(message, world = null) {
1736
+ if (!message) {
1737
+ message = "# Wait for user input. Press any key to continue";
1738
+ }
1739
+ else {
1740
+ message = "# Wait for user input. " + message;
1741
+ }
1742
+ message += "\n";
1743
+ const value = await new Promise((resolve) => {
1744
+ const rl = readline.createInterface({
1745
+ input: process.stdin,
1746
+ output: process.stdout,
1747
+ });
1748
+ rl.question(message, (answer) => {
1749
+ rl.close();
1750
+ resolve(answer);
1751
+ });
1752
+ });
1753
+ if (value) {
1754
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1755
+ }
1756
+ this.setTestData({ userInput: value }, world);
1757
+ }
1537
1758
  setTestData(testData, world = null) {
1538
1759
  if (!testData) {
1539
1760
  return;
@@ -1721,7 +1942,6 @@ class StableBrowser {
1721
1942
  }
1722
1943
  async takeScreenshot(screenshotPath) {
1723
1944
  const playContext = this.context.playContext;
1724
- const client = await playContext.newCDPSession(this.page);
1725
1945
  // Using CDP to capture the screenshot
1726
1946
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1727
1947
  document.body.scrollWidth,
@@ -1731,41 +1951,40 @@ class StableBrowser {
1731
1951
  document.body.clientWidth,
1732
1952
  document.documentElement.clientWidth,
1733
1953
  ])));
1734
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1735
- document.body.scrollHeight,
1736
- document.documentElement.scrollHeight,
1737
- document.body.offsetHeight,
1738
- document.documentElement.offsetHeight,
1739
- document.body.clientHeight,
1740
- document.documentElement.clientHeight,
1741
- ])));
1742
- const { data } = await client.send("Page.captureScreenshot", {
1743
- format: "png",
1744
- // clip: {
1745
- // x: 0,
1746
- // y: 0,
1747
- // width: viewportWidth,
1748
- // height: viewportHeight,
1749
- // scale: 1,
1750
- // },
1751
- });
1752
- if (!screenshotPath) {
1753
- return data;
1754
- }
1755
- let screenshotBuffer = Buffer.from(data, "base64");
1756
- const sharpBuffer = sharp(screenshotBuffer);
1757
- const metadata = await sharpBuffer.metadata();
1758
- //check if you are on retina display and reduce the quality of the image
1759
- if (metadata.width > viewportWidth || metadata.height > viewportHeight) {
1760
- screenshotBuffer = await sharpBuffer
1761
- .resize(viewportWidth, viewportHeight, {
1762
- fit: sharp.fit.inside,
1763
- withoutEnlargement: true,
1764
- })
1765
- .toBuffer();
1766
- }
1767
- fs.writeFileSync(screenshotPath, screenshotBuffer);
1768
- await client.detach();
1954
+ let screenshotBuffer = null;
1955
+ if (this.context.browserName === "chromium") {
1956
+ const client = await playContext.newCDPSession(this.page);
1957
+ const { data } = await client.send("Page.captureScreenshot", {
1958
+ format: "png",
1959
+ // clip: {
1960
+ // x: 0,
1961
+ // y: 0,
1962
+ // width: viewportWidth,
1963
+ // height: viewportHeight,
1964
+ // scale: 1,
1965
+ // },
1966
+ });
1967
+ await client.detach();
1968
+ if (!screenshotPath) {
1969
+ return data;
1970
+ }
1971
+ screenshotBuffer = Buffer.from(data, "base64");
1972
+ }
1973
+ else {
1974
+ screenshotBuffer = await this.page.screenshot();
1975
+ }
1976
+ let image = await Jimp.read(screenshotBuffer);
1977
+ // Get the image dimensions
1978
+ const { width, height } = image.bitmap;
1979
+ const resizeRatio = viewportWidth / width;
1980
+ // Resize the image to fit within the viewport dimensions without enlarging
1981
+ if (width > viewportWidth) {
1982
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1983
+ await image.write(screenshotPath);
1984
+ }
1985
+ else {
1986
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1987
+ }
1769
1988
  }
1770
1989
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1771
1990
  this._validateSelectors(selectors);
@@ -2573,13 +2792,13 @@ class StableBrowser {
2573
2792
  }
2574
2793
  catch (e) {
2575
2794
  if (e.label === "networkidle") {
2576
- console.log("waitted for the network to be idle timeout");
2795
+ console.log("waited for the network to be idle timeout");
2577
2796
  }
2578
2797
  else if (e.label === "load") {
2579
- console.log("waitted for the load timeout");
2798
+ console.log("waited for the load timeout");
2580
2799
  }
2581
2800
  else if (e.label === "domcontentloaded") {
2582
- console.log("waitted for the domcontent loaded timeout");
2801
+ console.log("waited for the domcontent loaded timeout");
2583
2802
  }
2584
2803
  console.log(".");
2585
2804
  }
@@ -2722,33 +2941,18 @@ class StableBrowser {
2722
2941
  }
2723
2942
  async scrollIfNeeded(element, info) {
2724
2943
  try {
2725
- let didScroll = await element.evaluate((node) => {
2726
- const rect = node.getBoundingClientRect();
2727
- if (rect &&
2728
- rect.top >= 0 &&
2729
- rect.left >= 0 &&
2730
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2731
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
2732
- return false;
2733
- }
2734
- else {
2735
- node.scrollIntoView({
2736
- behavior: "smooth",
2737
- block: "center",
2738
- inline: "center",
2739
- });
2740
- return true;
2741
- }
2944
+ await element.scrollIntoViewIfNeeded({
2945
+ timeout: 2000,
2742
2946
  });
2743
- if (didScroll) {
2744
- await new Promise((resolve) => setTimeout(resolve, 500));
2745
- if (info) {
2746
- info.box = await element.boundingBox();
2747
- }
2947
+ await new Promise((resolve) => setTimeout(resolve, 500));
2948
+ if (info) {
2949
+ info.box = await element.boundingBox({
2950
+ timeout: 1000,
2951
+ });
2748
2952
  }
2749
2953
  }
2750
2954
  catch (e) {
2751
- console.log("scroll failed");
2955
+ console.log("#-#");
2752
2956
  }
2753
2957
  }
2754
2958
  _reportToWorld(world, properties) {