automation_model 1.0.403-dev → 1.0.403-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.
@@ -13,6 +13,9 @@ import { getTableCells, getTableData } from "./table_analyze.js";
13
13
  import objectPath from "object-path";
14
14
  import { decrypt } from "./utils.js";
15
15
  import csv from "csv-parser";
16
+ import { Readable } from "node:stream";
17
+ import readline from "readline";
18
+ import { getContext } from "./init_browser.js";
16
19
  const Types = {
17
20
  CLICK: "click_element",
18
21
  NAVIGATE: "navigate",
@@ -37,16 +40,22 @@ const Types = {
37
40
  SET_DATE_TIME: "set_date_time",
38
41
  SET_VIEWPORT: "set_viewport",
39
42
  VERIFY_VISUAL: "verify_visual",
43
+ LOAD_DATA: "load_data",
44
+ SET_INPUT: "set_input",
40
45
  };
46
+ export const apps = {};
41
47
  class StableBrowser {
42
- constructor(browser, page, logger = null, context = null) {
48
+ constructor(browser, page, logger = null, context = null, world = null) {
43
49
  this.browser = browser;
44
50
  this.page = page;
45
51
  this.logger = logger;
46
52
  this.context = context;
53
+ this.world = world;
47
54
  this.project_path = null;
48
55
  this.webLogFile = null;
56
+ this.networkLogger = null;
49
57
  this.configuration = null;
58
+ this.appName = "main";
50
59
  if (!this.logger) {
51
60
  this.logger = console;
52
61
  }
@@ -72,22 +81,35 @@ class StableBrowser {
72
81
  this.logger.error("unable to read ai_config.json");
73
82
  }
74
83
  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();
84
+ this.world = world;
78
85
  context.pages = [this.page];
79
86
  context.pageLoading = { status: false };
87
+ this.registerEventListeners(this.context);
88
+ }
89
+ registerEventListeners(context) {
90
+ this.registerConsoleLogListener(this.page, context);
91
+ this.registerRequestListener(this.page, context, this.webLogFile);
92
+ if (!context.pageLoading) {
93
+ context.pageLoading = { status: false };
94
+ }
80
95
  context.playContext.on("page", async function (page) {
81
96
  context.pageLoading.status = true;
82
97
  this.page = page;
83
98
  context.page = page;
84
99
  context.pages.push(page);
85
- this.webLogFile = this.getWebLogFile(logFolder);
86
- this.registerConsoleLogListener(page, context, this.webLogFile);
87
- this.registerRequestListener();
88
- page.on("close", () => {
89
- context.pages = context.pages.filter((p) => p !== page);
90
- this.page = context.pages[context.pages.length - 1]; // assuming the last page is the active page
100
+ page.on("close", async () => {
101
+ if (this.context && this.context.pages && this.context.pages.length > 1) {
102
+ this.context.pages.pop();
103
+ this.page = this.context.pages[this.context.pages.length - 1];
104
+ this.context.page = this.page;
105
+ try {
106
+ let title = await this.page.title();
107
+ console.log("Switched to page " + title);
108
+ }
109
+ catch (error) {
110
+ console.error("Error on page close", error);
111
+ }
112
+ }
91
113
  });
92
114
  try {
93
115
  await this.waitForPageLoad();
@@ -99,6 +121,36 @@ class StableBrowser {
99
121
  context.pageLoading.status = false;
100
122
  }.bind(this));
101
123
  }
124
+ async switchApp(appName) {
125
+ // check if the current app (this.appName) is the same as the new app
126
+ if (this.appName === appName) {
127
+ return;
128
+ }
129
+ let navigate = false;
130
+ if (!apps[appName]) {
131
+ let newContext = await getContext(null, false, this.logger, appName, false, this);
132
+ navigate = true;
133
+ apps[appName] = {
134
+ context: newContext,
135
+ browser: newContext.browser,
136
+ page: newContext.page,
137
+ };
138
+ }
139
+ const tempContext = {};
140
+ this._copyContext(this, tempContext);
141
+ this._copyContext(apps[appName], this);
142
+ apps[this.appName] = tempContext;
143
+ this.appName = appName;
144
+ if (navigate) {
145
+ await this.goto(this.context.environment.baseUrl);
146
+ await this.waitForPageLoad();
147
+ }
148
+ }
149
+ _copyContext(from, to) {
150
+ to.browser = from.browser;
151
+ to.page = from.page;
152
+ to.context = from.context;
153
+ }
102
154
  getWebLogFile(logFolder) {
103
155
  if (!fs.existsSync(logFolder)) {
104
156
  fs.mkdirSync(logFolder, { recursive: true });
@@ -110,37 +162,63 @@ class StableBrowser {
110
162
  const fileName = nextIndex + ".json";
111
163
  return path.join(logFolder, fileName);
112
164
  }
113
- registerConsoleLogListener(page, context, logFile) {
165
+ registerConsoleLogListener(page, context) {
114
166
  if (!this.context.webLogger) {
115
167
  this.context.webLogger = [];
116
168
  }
117
169
  page.on("console", async (msg) => {
118
- this.context.webLogger.push({
170
+ var _a;
171
+ const obj = {
119
172
  type: msg.type(),
120
173
  text: msg.text(),
121
174
  location: msg.location(),
122
175
  time: new Date().toISOString(),
123
- });
124
- await fs.promises.writeFile(logFile, JSON.stringify(this.context.webLogger, null, 2));
176
+ };
177
+ this.context.webLogger.push(obj);
178
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
125
179
  });
126
180
  }
127
- registerRequestListener() {
128
- this.page.on("request", async (data) => {
181
+ registerRequestListener(page, context, logFile) {
182
+ if (!this.context.networkLogger) {
183
+ this.context.networkLogger = [];
184
+ }
185
+ page.on("request", async (data) => {
186
+ var _a;
187
+ const startTime = new Date().getTime();
129
188
  try {
130
- const pageUrl = new URL(this.page.url());
189
+ const pageUrl = new URL(page.url());
131
190
  const requestUrl = new URL(data.url());
132
191
  if (pageUrl.hostname === requestUrl.hostname) {
133
192
  const method = data.method();
134
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
193
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
135
194
  const token = await data.headerValue("Authorization");
136
195
  if (token) {
137
- this.context.authtoken = token;
196
+ context.authtoken = token;
138
197
  }
139
198
  }
140
199
  }
200
+ const response = await data.response();
201
+ const endTime = new Date().getTime();
202
+ const obj = {
203
+ url: data.url(),
204
+ method: data.method(),
205
+ postData: data.postData(),
206
+ error: data.failure() ? data.failure().errorText : null,
207
+ duration: endTime - startTime,
208
+ startTime,
209
+ };
210
+ context.networkLogger.push(obj);
211
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
141
212
  }
142
213
  catch (error) {
143
214
  console.error("Error in request listener", error);
215
+ context.networkLogger.push({
216
+ error: "not able to listen",
217
+ message: error.message,
218
+ stack: error.stack,
219
+ time: new Date().toISOString(),
220
+ });
221
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
144
222
  }
145
223
  });
146
224
  }
@@ -210,9 +288,7 @@ class StableBrowser {
210
288
  }
211
289
  _getLocator(locator, scope, _params) {
212
290
  locator = this._fixLocatorUsingParams(locator, _params);
213
- if (locator.type === "pw_selector") {
214
- return scope.locator(locator.selector);
215
- }
291
+ let locatorReturn;
216
292
  if (locator.role) {
217
293
  if (locator.role[1].nameReg) {
218
294
  locator.role[1].name = reg_parser(locator.role[1].nameReg);
@@ -221,20 +297,42 @@ class StableBrowser {
221
297
  // if (locator.role[1].name) {
222
298
  // locator.role[1].name = this._fixUsingParams(locator.role[1].name, _params);
223
299
  // }
224
- return scope.getByRole(locator.role[0], locator.role[1]);
300
+ locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
225
301
  }
226
302
  if (locator.css) {
227
- return scope.locator(locator.css);
303
+ locatorReturn = scope.locator(locator.css);
228
304
  }
229
- if ((locator === null || locator === void 0 ? void 0 : locator.engine) && (locator === null || locator === void 0 ? void 0 : locator.score) <= 520) {
230
- let selector = locator.selector.replace(/"/g, '\\"');
231
- if (locator.engine === "internal:att") {
232
- selector = `[${selector}]`;
305
+ // handle role/name locators
306
+ // locator.selector will be something like: textbox[name="Username"i]
307
+ if (locator.engine === "internal:role") {
308
+ // extract the role, name and the i/s flags using regex
309
+ const match = locator.selector.match(/(.*)\[(.*)="(.*)"(.*)\]/);
310
+ if (match) {
311
+ const role = match[1];
312
+ const name = match[3];
313
+ const flags = match[4];
314
+ locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
233
315
  }
234
- const locator = scope.locator(`${locator.engine}="${selector}"`);
235
- return locator;
236
316
  }
237
- throw new Error("unknown locator type");
317
+ if (locator === null || locator === void 0 ? void 0 : locator.engine) {
318
+ if (locator.engine === "css") {
319
+ locatorReturn = scope.locator(locator.selector);
320
+ }
321
+ else {
322
+ let selector = locator.selector;
323
+ if (locator.engine === "internal:attr") {
324
+ if (!selector.startsWith("[")) {
325
+ selector = `[${selector}]`;
326
+ }
327
+ }
328
+ locatorReturn = scope.locator(`${locator.engine}=${selector}`);
329
+ }
330
+ }
331
+ if (!locatorReturn) {
332
+ console.error(locator);
333
+ throw new Error("Locator undefined");
334
+ }
335
+ return locatorReturn;
238
336
  }
239
337
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
240
338
  let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
@@ -445,6 +543,8 @@ class StableBrowser {
445
543
  if (result.foundElements.length > 0) {
446
544
  let dialogCloseLocator = result.foundElements[0].locator;
447
545
  await dialogCloseLocator.click();
546
+ // wait for the dialog to close
547
+ await dialogCloseLocator.waitFor({ state: "hidden" });
448
548
  return { rerun: true };
449
549
  }
450
550
  }
@@ -453,7 +553,7 @@ class StableBrowser {
453
553
  }
454
554
  async _locate(selectors, info, _params, timeout = 30000) {
455
555
  for (let i = 0; i < 3; i++) {
456
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
556
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
457
557
  for (let j = 0; j < selectors.locators.length; j++) {
458
558
  let selector = selectors.locators[j];
459
559
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -473,9 +573,27 @@ class StableBrowser {
473
573
  //let arrayMode = Array.isArray(selectors);
474
574
  let scope = this.page;
475
575
  if (selectors.iframe_src || selectors.frameLocators) {
576
+ const findFrame = (frame, framescope) => {
577
+ for (let i = 0; i < frame.selectors.length; i++) {
578
+ let frameLocator = frame.selectors[i];
579
+ if (frameLocator.css) {
580
+ framescope = framescope.frameLocator(frameLocator.css);
581
+ break;
582
+ }
583
+ }
584
+ if (frame.children) {
585
+ return findFrame(frame.children, framescope);
586
+ }
587
+ return framescope;
588
+ };
476
589
  info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
477
590
  while (true) {
478
591
  let frameFound = false;
592
+ if (selectors.nestFrmLoc) {
593
+ scope = findFrame(selectors.nestFrmLoc, scope);
594
+ frameFound = true;
595
+ break;
596
+ }
479
597
  if (selectors.frameLocators) {
480
598
  for (let i = 0; i < selectors.frameLocators.length; i++) {
481
599
  let frameLocator = selectors.frameLocators[i];
@@ -639,6 +757,9 @@ class StableBrowser {
639
757
  async click(selectors, _params, options = {}, world = null) {
640
758
  this._validateSelectors(selectors);
641
759
  const startTime = Date.now();
760
+ if (options && options.context) {
761
+ selectors.locators[0].text = options.context;
762
+ }
642
763
  const info = {};
643
764
  info.log = "***** click on " + selectors.element_name + " *****\n";
644
765
  info.operation = "click";
@@ -652,14 +773,14 @@ class StableBrowser {
652
773
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
653
774
  try {
654
775
  await this._highlightElements(element);
655
- await element.click({ timeout: 5000 });
776
+ await element.click();
656
777
  await new Promise((resolve) => setTimeout(resolve, 1000));
657
778
  }
658
779
  catch (e) {
659
780
  // await this.closeUnexpectedPopups();
660
781
  info.log += "click failed, will try again" + "\n";
661
782
  element = await this._locate(selectors, info, _params);
662
- await element.click({ timeout: 10000, force: true });
783
+ await element.dispatchEvent("click");
663
784
  await new Promise((resolve) => setTimeout(resolve, 1000));
664
785
  }
665
786
  await this.waitForPageLoad();
@@ -712,7 +833,7 @@ class StableBrowser {
712
833
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
713
834
  try {
714
835
  await this._highlightElements(element);
715
- await element.setChecked(checked, { timeout: 5000 });
836
+ await element.setChecked(checked);
716
837
  await new Promise((resolve) => setTimeout(resolve, 1000));
717
838
  }
718
839
  catch (e) {
@@ -776,7 +897,7 @@ class StableBrowser {
776
897
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
777
898
  try {
778
899
  await this._highlightElements(element);
779
- await element.hover({ timeout: 10000 });
900
+ await element.hover();
780
901
  await new Promise((resolve) => setTimeout(resolve, 1000));
781
902
  }
782
903
  catch (e) {
@@ -838,7 +959,7 @@ class StableBrowser {
838
959
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
839
960
  try {
840
961
  await this._highlightElements(element);
841
- await element.selectOption(values, { timeout: 5000 });
962
+ await element.selectOption(values);
842
963
  }
843
964
  catch (e) {
844
965
  //await this.closeUnexpectedPopups();
@@ -947,71 +1068,45 @@ class StableBrowser {
947
1068
  });
948
1069
  }
949
1070
  }
950
- async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1071
+ async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1072
+ // set input value for non fillable inputs like date, time, range, color, etc.
951
1073
  this._validateSelectors(selectors);
952
1074
  const startTime = Date.now();
953
- let error = null;
954
- let screenshotId = null;
955
- let screenshotPath = null;
956
1075
  const info = {};
957
- info.log = "";
958
- info.operation = Types.SET_DATE_TIME;
1076
+ info.log = "***** set input value " + selectors.element_name + " *****\n";
1077
+ info.operation = "setInputValue";
959
1078
  info.selectors = selectors;
1079
+ value = this._fixUsingParams(value, _params);
960
1080
  info.value = value;
1081
+ let error = null;
1082
+ let screenshotId = null;
1083
+ let screenshotPath = null;
961
1084
  try {
962
1085
  value = await this._replaceWithLocalData(value, this);
963
1086
  let element = await this._locate(selectors, info, _params);
964
- //insert red border around the element
965
1087
  await this.scrollIfNeeded(element, info);
966
1088
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
967
1089
  await this._highlightElements(element);
968
1090
  try {
969
- await element.click();
970
- await new Promise((resolve) => setTimeout(resolve, 500));
971
- if (format) {
972
- value = dayjs(value).format(format);
973
- await element.fill(value);
974
- }
975
- else {
976
- const dateTimeValue = await getDateTimeValue({ value, element });
977
- await element.evaluateHandle((el, dateTimeValue) => {
978
- el.value = ""; // clear input
979
- el.value = dateTimeValue;
980
- }, dateTimeValue);
981
- }
982
- if (enter) {
983
- await new Promise((resolve) => setTimeout(resolve, 2000));
984
- await this.page.keyboard.press("Enter");
985
- await this.waitForPageLoad();
986
- }
1091
+ await element.evaluateHandle((el, value) => {
1092
+ el.value = value;
1093
+ }, value);
987
1094
  }
988
1095
  catch (error) {
989
- //await this.closeUnexpectedPopups();
990
- this.logger.error("setting date time input failed " + JSON.stringify(info));
991
- this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
1096
+ this.logger.error("setInputValue failed, will try again");
1097
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
992
1098
  info.screenshotPath = screenshotPath;
993
1099
  Object.assign(error, { info: info });
994
- await element.click();
995
- await new Promise((resolve) => setTimeout(resolve, 500));
996
- if (format) {
997
- value = dayjs(value).format(format);
998
- await element.fill(value);
999
- }
1000
- else {
1001
- const dateTimeValue = await getDateTimeValue({ value, element });
1002
- await element.evaluateHandle((el, dateTimeValue) => {
1003
- el.value = ""; // clear input
1004
- el.value = dateTimeValue;
1005
- }, dateTimeValue);
1006
- }
1007
- if (enter) {
1008
- await new Promise((resolve) => setTimeout(resolve, 2000));
1009
- await this.page.keyboard.press("Enter");
1010
- await this.waitForPageLoad();
1011
- }
1100
+ await element.evaluateHandle((el, value) => {
1101
+ el.value = value;
1102
+ });
1012
1103
  }
1013
1104
  }
1014
- catch (error) {
1105
+ catch (e) {
1106
+ this.logger.error("setInputValue failed " + info.log);
1107
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1108
+ info.screenshotPath = screenshotPath;
1109
+ Object.assign(e, { info: info });
1015
1110
  error = e;
1016
1111
  throw e;
1017
1112
  }
@@ -1019,10 +1114,10 @@ class StableBrowser {
1019
1114
  const endTime = Date.now();
1020
1115
  this._reportToWorld(world, {
1021
1116
  element_name: selectors.element_name,
1022
- type: Types.SET_DATE_TIME,
1023
- screenshotId,
1117
+ type: Types.SET_INPUT,
1118
+ text: `Set input value`,
1024
1119
  value: value,
1025
- text: `setDateTime input with value: ${value}`,
1120
+ screenshotId,
1026
1121
  result: error
1027
1122
  ? {
1028
1123
  status: "FAILED",
@@ -1039,7 +1134,7 @@ class StableBrowser {
1039
1134
  });
1040
1135
  }
1041
1136
  }
1042
- async setDateTime(selectors, value, enter = false, _params = null, options = {}, world = null) {
1137
+ async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1043
1138
  this._validateSelectors(selectors);
1044
1139
  const startTime = Date.now();
1045
1140
  let error = null;
@@ -1051,6 +1146,7 @@ class StableBrowser {
1051
1146
  info.selectors = selectors;
1052
1147
  info.value = value;
1053
1148
  try {
1149
+ value = await this._replaceWithLocalData(value, this);
1054
1150
  let element = await this._locate(selectors, info, _params);
1055
1151
  //insert red border around the element
1056
1152
  await this.scrollIfNeeded(element, info);
@@ -1059,28 +1155,51 @@ class StableBrowser {
1059
1155
  try {
1060
1156
  await element.click();
1061
1157
  await new Promise((resolve) => setTimeout(resolve, 500));
1062
- const dateTimeValue = await getDateTimeValue({ value, element });
1063
- await element.evaluateHandle((el, dateTimeValue) => {
1064
- el.value = ""; // clear input
1065
- el.value = dateTimeValue;
1066
- }, dateTimeValue);
1158
+ if (format) {
1159
+ value = dayjs(value).format(format);
1160
+ await element.fill(value);
1161
+ }
1162
+ else {
1163
+ const dateTimeValue = await getDateTimeValue({ value, element });
1164
+ await element.evaluateHandle((el, dateTimeValue) => {
1165
+ el.value = ""; // clear input
1166
+ el.value = dateTimeValue;
1167
+ }, dateTimeValue);
1168
+ }
1169
+ if (enter) {
1170
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1171
+ await this.page.keyboard.press("Enter");
1172
+ await this.waitForPageLoad();
1173
+ }
1067
1174
  }
1068
- catch (error) {
1175
+ catch (err) {
1069
1176
  //await this.closeUnexpectedPopups();
1070
1177
  this.logger.error("setting date time input failed " + JSON.stringify(info));
1071
- this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
1178
+ this.logger.info("Trying again");
1179
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1072
1180
  info.screenshotPath = screenshotPath;
1073
- Object.assign(error, { info: info });
1181
+ Object.assign(err, { info: info });
1074
1182
  await element.click();
1075
1183
  await new Promise((resolve) => setTimeout(resolve, 500));
1076
- const dateTimeValue = await getDateTimeValue({ value, element });
1077
- await element.evaluateHandle((el, dateTimeValue) => {
1078
- el.value = ""; // clear input
1079
- el.value = dateTimeValue;
1080
- }, dateTimeValue);
1184
+ if (format) {
1185
+ value = dayjs(value).format(format);
1186
+ await element.fill(value);
1187
+ }
1188
+ else {
1189
+ const dateTimeValue = await getDateTimeValue({ value, element });
1190
+ await element.evaluateHandle((el, dateTimeValue) => {
1191
+ el.value = ""; // clear input
1192
+ el.value = dateTimeValue;
1193
+ }, dateTimeValue);
1194
+ }
1195
+ if (enter) {
1196
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1197
+ await this.page.keyboard.press("Enter");
1198
+ await this.waitForPageLoad();
1199
+ }
1081
1200
  }
1082
1201
  }
1083
- catch (error) {
1202
+ catch (e) {
1084
1203
  error = e;
1085
1204
  throw e;
1086
1205
  }
@@ -1243,7 +1362,7 @@ class StableBrowser {
1243
1362
  let element = await this._locate(selectors, info, _params);
1244
1363
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1245
1364
  await this._highlightElements(element);
1246
- await element.fill(value, { timeout: 10000 });
1365
+ await element.fill(value);
1247
1366
  await element.dispatchEvent("change");
1248
1367
  if (enter) {
1249
1368
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -1462,7 +1581,7 @@ class StableBrowser {
1462
1581
  return info;
1463
1582
  }
1464
1583
  catch (e) {
1465
- //await this.closeUnexpectedPopups();
1584
+ await this.closeUnexpectedPopups();
1466
1585
  this.logger.error("verify element contains text failed " + info.log);
1467
1586
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1468
1587
  info.screenshotPath = screenshotPath;
@@ -1510,6 +1629,29 @@ class StableBrowser {
1510
1629
  }
1511
1630
  return dataFile;
1512
1631
  }
1632
+ async waitForUserInput(message, world = null) {
1633
+ if (!message) {
1634
+ message = "# Wait for user input. Press any key to continue";
1635
+ }
1636
+ else {
1637
+ message = "# Wait for user input. " + message;
1638
+ }
1639
+ message += "\n";
1640
+ const value = await new Promise((resolve) => {
1641
+ const rl = readline.createInterface({
1642
+ input: process.stdin,
1643
+ output: process.stdout,
1644
+ });
1645
+ rl.question(message, (answer) => {
1646
+ rl.close();
1647
+ resolve(answer);
1648
+ });
1649
+ });
1650
+ if (value) {
1651
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1652
+ }
1653
+ this.setTestData({ userInput: value }, world);
1654
+ }
1513
1655
  setTestData(testData, world = null) {
1514
1656
  if (!testData) {
1515
1657
  return;
@@ -1537,7 +1679,7 @@ class StableBrowser {
1537
1679
  const data = fs.readFileSync(filePath, "utf8");
1538
1680
  const results = [];
1539
1681
  return new Promise((resolve, reject) => {
1540
- const readableStream = new stream.Readable();
1682
+ const readableStream = new Readable();
1541
1683
  readableStream._read = () => { }; // _read is required but you can noop it
1542
1684
  readableStream.push(data);
1543
1685
  readableStream.push(null);
@@ -1717,13 +1859,13 @@ class StableBrowser {
1717
1859
  ])));
1718
1860
  const { data } = await client.send("Page.captureScreenshot", {
1719
1861
  format: "png",
1720
- clip: {
1721
- x: 0,
1722
- y: 0,
1723
- width: viewportWidth,
1724
- height: viewportHeight,
1725
- scale: 1,
1726
- },
1862
+ // clip: {
1863
+ // x: 0,
1864
+ // y: 0,
1865
+ // width: viewportWidth,
1866
+ // height: viewportHeight,
1867
+ // scale: 1,
1868
+ // },
1727
1869
  });
1728
1870
  if (!screenshotPath) {
1729
1871
  return data;
@@ -2549,13 +2691,13 @@ class StableBrowser {
2549
2691
  }
2550
2692
  catch (e) {
2551
2693
  if (e.label === "networkidle") {
2552
- console.log("waitted for the network to be idle timeout");
2694
+ console.log("waited for the network to be idle timeout");
2553
2695
  }
2554
2696
  else if (e.label === "load") {
2555
- console.log("waitted for the load timeout");
2697
+ console.log("waited for the load timeout");
2556
2698
  }
2557
2699
  else if (e.label === "domcontentloaded") {
2558
- console.log("waitted for the domcontent loaded timeout");
2700
+ console.log("waited for the domcontent loaded timeout");
2559
2701
  }
2560
2702
  console.log(".");
2561
2703
  }
@@ -2590,13 +2732,6 @@ class StableBrowser {
2590
2732
  const info = {};
2591
2733
  try {
2592
2734
  await this.page.close();
2593
- if (this.context && this.context.pages && this.context.pages.length > 0) {
2594
- this.context.pages.pop();
2595
- this.page = this.context.pages[this.context.pages.length - 1];
2596
- this.context.page = this.page;
2597
- let title = await this.page.title();
2598
- console.log("Switched to page " + title);
2599
- }
2600
2735
  }
2601
2736
  catch (e) {
2602
2737
  console.log(".");
@@ -2705,33 +2840,18 @@ class StableBrowser {
2705
2840
  }
2706
2841
  async scrollIfNeeded(element, info) {
2707
2842
  try {
2708
- let didScroll = await element.evaluate((node) => {
2709
- const rect = node.getBoundingClientRect();
2710
- if (rect &&
2711
- rect.top >= 0 &&
2712
- rect.left >= 0 &&
2713
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2714
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
2715
- return false;
2716
- }
2717
- else {
2718
- node.scrollIntoView({
2719
- behavior: "smooth",
2720
- block: "center",
2721
- inline: "center",
2722
- });
2723
- return true;
2724
- }
2843
+ await element.scrollIntoViewIfNeeded({
2844
+ timeout: 2000,
2725
2845
  });
2726
- if (didScroll) {
2727
- await new Promise((resolve) => setTimeout(resolve, 500));
2728
- if (info) {
2729
- info.box = await element.boundingBox();
2730
- }
2846
+ await new Promise((resolve) => setTimeout(resolve, 500));
2847
+ if (info) {
2848
+ info.box = await element.boundingBox({
2849
+ timeout: 1000,
2850
+ });
2731
2851
  }
2732
2852
  }
2733
2853
  catch (e) {
2734
- console.log("scroll failed");
2854
+ console.log("#-#");
2735
2855
  }
2736
2856
  }
2737
2857
  _reportToWorld(world, properties) {