automation_model 1.0.401-dev → 1.0.401-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,19 +81,31 @@ 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();
78
- context.pages = [this.page];
79
- context.pageLoading = { status: false };
84
+ this.world = world;
85
+ this.registerEventListeners(this.context);
86
+ }
87
+ registerEventListeners(context) {
88
+ this.registerConsoleLogListener(this.page, context);
89
+ this.registerRequestListener(this.page, context, this.webLogFile);
80
90
  context.playContext.on("page", async function (page) {
81
91
  context.pageLoading.status = true;
82
92
  this.page = page;
83
93
  context.page = page;
84
94
  context.pages.push(page);
85
- this.webLogFile = this.getWebLogFile(logFolder);
86
- this.registerConsoleLogListener(page, context, this.webLogFile);
87
- this.registerRequestListener();
95
+ page.on("close", async () => {
96
+ if (this.context && this.context.pages && this.context.pages.length > 1) {
97
+ this.context.pages.pop();
98
+ this.page = this.context.pages[this.context.pages.length - 1];
99
+ this.context.page = this.page;
100
+ try {
101
+ let title = await this.page.title();
102
+ console.log("Switched to page " + title);
103
+ }
104
+ catch (error) {
105
+ console.error("Error on page close", error);
106
+ }
107
+ }
108
+ });
88
109
  try {
89
110
  await this.waitForPageLoad();
90
111
  console.log("Switch page: " + (await page.title()));
@@ -95,6 +116,36 @@ class StableBrowser {
95
116
  context.pageLoading.status = false;
96
117
  }.bind(this));
97
118
  }
119
+ async switchApp(appName) {
120
+ // check if the current app (this.appName) is the same as the new app
121
+ if (this.appName === appName) {
122
+ return;
123
+ }
124
+ let navigate = false;
125
+ if (!apps[appName]) {
126
+ let newContext = await getContext(null, false, this.logger, appName, false, this);
127
+ navigate = true;
128
+ apps[appName] = {
129
+ context: newContext,
130
+ browser: newContext.browser,
131
+ page: newContext.page,
132
+ };
133
+ }
134
+ const tempContext = {};
135
+ this._copyContext(this, tempContext);
136
+ this._copyContext(apps[appName], this);
137
+ apps[this.appName] = tempContext;
138
+ this.appName = appName;
139
+ if (navigate) {
140
+ await this.goto(this.context.environment.baseUrl);
141
+ await this.waitForPageLoad();
142
+ }
143
+ }
144
+ _copyContext(from, to) {
145
+ to.browser = from.browser;
146
+ to.page = from.page;
147
+ to.context = from.context;
148
+ }
98
149
  getWebLogFile(logFolder) {
99
150
  if (!fs.existsSync(logFolder)) {
100
151
  fs.mkdirSync(logFolder, { recursive: true });
@@ -106,37 +157,63 @@ class StableBrowser {
106
157
  const fileName = nextIndex + ".json";
107
158
  return path.join(logFolder, fileName);
108
159
  }
109
- registerConsoleLogListener(page, context, logFile) {
160
+ registerConsoleLogListener(page, context) {
110
161
  if (!this.context.webLogger) {
111
162
  this.context.webLogger = [];
112
163
  }
113
164
  page.on("console", async (msg) => {
114
- this.context.webLogger.push({
165
+ var _a;
166
+ const obj = {
115
167
  type: msg.type(),
116
168
  text: msg.text(),
117
169
  location: msg.location(),
118
170
  time: new Date().toISOString(),
119
- });
120
- await fs.promises.writeFile(logFile, JSON.stringify(this.context.webLogger, null, 2));
171
+ };
172
+ this.context.webLogger.push(obj);
173
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
121
174
  });
122
175
  }
123
- registerRequestListener() {
124
- this.page.on("request", async (data) => {
176
+ registerRequestListener(page, context, logFile) {
177
+ if (!this.context.networkLogger) {
178
+ this.context.networkLogger = [];
179
+ }
180
+ page.on("request", async (data) => {
181
+ var _a;
182
+ const startTime = new Date().getTime();
125
183
  try {
126
- const pageUrl = new URL(this.page.url());
184
+ const pageUrl = new URL(page.url());
127
185
  const requestUrl = new URL(data.url());
128
186
  if (pageUrl.hostname === requestUrl.hostname) {
129
187
  const method = data.method();
130
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
188
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
131
189
  const token = await data.headerValue("Authorization");
132
190
  if (token) {
133
- this.context.authtoken = token;
191
+ context.authtoken = token;
134
192
  }
135
193
  }
136
194
  }
195
+ const response = await data.response();
196
+ const endTime = new Date().getTime();
197
+ const obj = {
198
+ url: data.url(),
199
+ method: data.method(),
200
+ postData: data.postData(),
201
+ error: data.failure() ? data.failure().errorText : null,
202
+ duration: endTime - startTime,
203
+ startTime,
204
+ };
205
+ context.networkLogger.push(obj);
206
+ (_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
137
207
  }
138
208
  catch (error) {
139
209
  console.error("Error in request listener", error);
210
+ context.networkLogger.push({
211
+ error: "not able to listen",
212
+ message: error.message,
213
+ stack: error.stack,
214
+ time: new Date().toISOString(),
215
+ });
216
+ // await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
140
217
  }
141
218
  });
142
219
  }
@@ -179,32 +256,78 @@ class StableBrowser {
179
256
  }
180
257
  return text;
181
258
  }
182
- _getLocator(locator, scope, _params) {
183
- if (locator.type === "pw_selector") {
184
- return scope.locator(locator.selector);
259
+ _fixLocatorUsingParams(locator, _params) {
260
+ // check if not null
261
+ if (!locator) {
262
+ return locator;
185
263
  }
264
+ // clone the locator
265
+ locator = JSON.parse(JSON.stringify(locator));
266
+ this.scanAndManipulate(locator, _params);
267
+ return locator;
268
+ }
269
+ _isObject(value) {
270
+ return value && typeof value === "object" && value.constructor === Object;
271
+ }
272
+ scanAndManipulate(currentObj, _params) {
273
+ for (const key in currentObj) {
274
+ if (typeof currentObj[key] === "string") {
275
+ // Perform string manipulation
276
+ currentObj[key] = this._fixUsingParams(currentObj[key], _params);
277
+ }
278
+ else if (this._isObject(currentObj[key])) {
279
+ // Recursively scan nested objects
280
+ this.scanAndManipulate(currentObj[key], _params);
281
+ }
282
+ }
283
+ }
284
+ _getLocator(locator, scope, _params) {
285
+ locator = this._fixLocatorUsingParams(locator, _params);
286
+ let locatorReturn;
186
287
  if (locator.role) {
187
288
  if (locator.role[1].nameReg) {
188
289
  locator.role[1].name = reg_parser(locator.role[1].nameReg);
189
290
  delete locator.role[1].nameReg;
190
291
  }
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]);
292
+ // if (locator.role[1].name) {
293
+ // locator.role[1].name = this._fixUsingParams(locator.role[1].name, _params);
294
+ // }
295
+ locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
195
296
  }
196
297
  if (locator.css) {
197
- return scope.locator(this._fixUsingParams(locator.css, _params));
298
+ locatorReturn = scope.locator(locator.css);
198
299
  }
199
- if ((locator === null || locator === void 0 ? void 0 : locator.engine) && (locator === null || locator === void 0 ? void 0 : locator.score) <= 520) {
200
- let selector = locator.selector.replace(/"/g, "\\\"");
201
- if (locator.engine === "internal:att") {
202
- selector = `[${selector}]`;
300
+ // handle role/name locators
301
+ // locator.selector will be something like: textbox[name="Username"i]
302
+ if (locator.engine === "internal:role") {
303
+ // extract the role, name and the i/s flags using regex
304
+ const match = locator.selector.match(/(.*)\[(.*)="(.*)"(.*)\]/);
305
+ if (match) {
306
+ const role = match[1];
307
+ const name = match[3];
308
+ const flags = match[4];
309
+ locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
310
+ }
311
+ }
312
+ if (locator === null || locator === void 0 ? void 0 : locator.engine) {
313
+ if (locator.engine === "css") {
314
+ locatorReturn = scope.locator(locator.selector);
315
+ }
316
+ else {
317
+ let selector = locator.selector;
318
+ if (locator.engine === "internal:attr") {
319
+ if (!selector.startsWith("[")) {
320
+ selector = `[${selector}]`;
321
+ }
322
+ }
323
+ locatorReturn = scope.locator(`${locator.engine}=${selector}`);
203
324
  }
204
- const locator = scope.locator(`${locator.engine}="${selector}"`);
205
- return locator;
206
325
  }
207
- throw new Error("unknown locator type");
326
+ if (!locatorReturn) {
327
+ console.error(locator);
328
+ throw new Error("Locator undefined");
329
+ }
330
+ return locatorReturn;
208
331
  }
209
332
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
210
333
  let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
@@ -415,6 +538,8 @@ class StableBrowser {
415
538
  if (result.foundElements.length > 0) {
416
539
  let dialogCloseLocator = result.foundElements[0].locator;
417
540
  await dialogCloseLocator.click();
541
+ // wait for the dialog to close
542
+ await dialogCloseLocator.waitFor({ state: "hidden" });
418
543
  return { rerun: true };
419
544
  }
420
545
  }
@@ -423,7 +548,7 @@ class StableBrowser {
423
548
  }
424
549
  async _locate(selectors, info, _params, timeout = 30000) {
425
550
  for (let i = 0; i < 3; i++) {
426
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
551
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
427
552
  for (let j = 0; j < selectors.locators.length; j++) {
428
553
  let selector = selectors.locators[j];
429
554
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -443,9 +568,27 @@ class StableBrowser {
443
568
  //let arrayMode = Array.isArray(selectors);
444
569
  let scope = this.page;
445
570
  if (selectors.iframe_src || selectors.frameLocators) {
571
+ const findFrame = (frame, framescope) => {
572
+ for (let i = 0; i < frame.selectors.length; i++) {
573
+ let frameLocator = frame.selectors[i];
574
+ if (frameLocator.css) {
575
+ framescope = framescope.frameLocator(frameLocator.css);
576
+ break;
577
+ }
578
+ }
579
+ if (frame.children) {
580
+ return findFrame(frame.children, framescope);
581
+ }
582
+ return framescope;
583
+ };
446
584
  info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
447
585
  while (true) {
448
586
  let frameFound = false;
587
+ if (selectors.nestFrmLoc) {
588
+ scope = findFrame(selectors.nestFrmLoc, scope);
589
+ frameFound = true;
590
+ break;
591
+ }
449
592
  if (selectors.frameLocators) {
450
593
  for (let i = 0; i < selectors.frameLocators.length; i++) {
451
594
  let frameLocator = selectors.frameLocators[i];
@@ -609,6 +752,9 @@ class StableBrowser {
609
752
  async click(selectors, _params, options = {}, world = null) {
610
753
  this._validateSelectors(selectors);
611
754
  const startTime = Date.now();
755
+ if (options && options.context) {
756
+ selectors.locators[0].text = options.context;
757
+ }
612
758
  const info = {};
613
759
  info.log = "***** click on " + selectors.element_name + " *****\n";
614
760
  info.operation = "click";
@@ -622,14 +768,14 @@ class StableBrowser {
622
768
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
623
769
  try {
624
770
  await this._highlightElements(element);
625
- await element.click({ timeout: 5000 });
771
+ await element.click();
626
772
  await new Promise((resolve) => setTimeout(resolve, 1000));
627
773
  }
628
774
  catch (e) {
629
775
  // await this.closeUnexpectedPopups();
630
776
  info.log += "click failed, will try again" + "\n";
631
777
  element = await this._locate(selectors, info, _params);
632
- await element.click({ timeout: 10000, force: true });
778
+ await element.dispatchEvent("click");
633
779
  await new Promise((resolve) => setTimeout(resolve, 1000));
634
780
  }
635
781
  await this.waitForPageLoad();
@@ -682,7 +828,7 @@ class StableBrowser {
682
828
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
683
829
  try {
684
830
  await this._highlightElements(element);
685
- await element.setChecked(checked, { timeout: 5000 });
831
+ await element.setChecked(checked);
686
832
  await new Promise((resolve) => setTimeout(resolve, 1000));
687
833
  }
688
834
  catch (e) {
@@ -746,7 +892,7 @@ class StableBrowser {
746
892
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
747
893
  try {
748
894
  await this._highlightElements(element);
749
- await element.hover({ timeout: 10000 });
895
+ await element.hover();
750
896
  await new Promise((resolve) => setTimeout(resolve, 1000));
751
897
  }
752
898
  catch (e) {
@@ -808,7 +954,7 @@ class StableBrowser {
808
954
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
809
955
  try {
810
956
  await this._highlightElements(element);
811
- await element.selectOption(values, { timeout: 5000 });
957
+ await element.selectOption(values);
812
958
  }
813
959
  catch (e) {
814
960
  //await this.closeUnexpectedPopups();
@@ -917,71 +1063,45 @@ class StableBrowser {
917
1063
  });
918
1064
  }
919
1065
  }
920
- async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1066
+ async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1067
+ // set input value for non fillable inputs like date, time, range, color, etc.
921
1068
  this._validateSelectors(selectors);
922
1069
  const startTime = Date.now();
923
- let error = null;
924
- let screenshotId = null;
925
- let screenshotPath = null;
926
1070
  const info = {};
927
- info.log = "";
928
- info.operation = Types.SET_DATE_TIME;
1071
+ info.log = "***** set input value " + selectors.element_name + " *****\n";
1072
+ info.operation = "setInputValue";
929
1073
  info.selectors = selectors;
1074
+ value = this._fixUsingParams(value, _params);
930
1075
  info.value = value;
1076
+ let error = null;
1077
+ let screenshotId = null;
1078
+ let screenshotPath = null;
931
1079
  try {
932
1080
  value = await this._replaceWithLocalData(value, this);
933
1081
  let element = await this._locate(selectors, info, _params);
934
- //insert red border around the element
935
1082
  await this.scrollIfNeeded(element, info);
936
1083
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
937
1084
  await this._highlightElements(element);
938
1085
  try {
939
- await element.click();
940
- await new Promise((resolve) => setTimeout(resolve, 500));
941
- if (format) {
942
- value = dayjs(value).format(format);
943
- await element.fill(value);
944
- }
945
- else {
946
- const dateTimeValue = await getDateTimeValue({ value, element });
947
- await element.evaluateHandle((el, dateTimeValue) => {
948
- el.value = ""; // clear input
949
- el.value = dateTimeValue;
950
- }, dateTimeValue);
951
- }
952
- if (enter) {
953
- await new Promise((resolve) => setTimeout(resolve, 2000));
954
- await this.page.keyboard.press("Enter");
955
- await this.waitForPageLoad();
956
- }
1086
+ await element.evaluateHandle((el, value) => {
1087
+ el.value = value;
1088
+ }, value);
957
1089
  }
958
1090
  catch (error) {
959
- //await this.closeUnexpectedPopups();
960
- this.logger.error("setting date time input failed " + JSON.stringify(info));
961
- this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
1091
+ this.logger.error("setInputValue failed, will try again");
1092
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
962
1093
  info.screenshotPath = screenshotPath;
963
1094
  Object.assign(error, { info: info });
964
- await element.click();
965
- await new Promise((resolve) => setTimeout(resolve, 500));
966
- if (format) {
967
- value = dayjs(value).format(format);
968
- await element.fill(value);
969
- }
970
- else {
971
- const dateTimeValue = await getDateTimeValue({ value, element });
972
- await element.evaluateHandle((el, dateTimeValue) => {
973
- el.value = ""; // clear input
974
- el.value = dateTimeValue;
975
- }, dateTimeValue);
976
- }
977
- if (enter) {
978
- await new Promise((resolve) => setTimeout(resolve, 2000));
979
- await this.page.keyboard.press("Enter");
980
- await this.waitForPageLoad();
981
- }
1095
+ await element.evaluateHandle((el, value) => {
1096
+ el.value = value;
1097
+ });
982
1098
  }
983
1099
  }
984
- catch (error) {
1100
+ catch (e) {
1101
+ this.logger.error("setInputValue failed " + info.log);
1102
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1103
+ info.screenshotPath = screenshotPath;
1104
+ Object.assign(e, { info: info });
985
1105
  error = e;
986
1106
  throw e;
987
1107
  }
@@ -989,10 +1109,10 @@ class StableBrowser {
989
1109
  const endTime = Date.now();
990
1110
  this._reportToWorld(world, {
991
1111
  element_name: selectors.element_name,
992
- type: Types.SET_DATE_TIME,
993
- screenshotId,
1112
+ type: Types.SET_INPUT,
1113
+ text: `Set input value`,
994
1114
  value: value,
995
- text: `setDateTime input with value: ${value}`,
1115
+ screenshotId,
996
1116
  result: error
997
1117
  ? {
998
1118
  status: "FAILED",
@@ -1009,7 +1129,7 @@ class StableBrowser {
1009
1129
  });
1010
1130
  }
1011
1131
  }
1012
- async setDateTime(selectors, value, enter = false, _params = null, options = {}, world = null) {
1132
+ async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1013
1133
  this._validateSelectors(selectors);
1014
1134
  const startTime = Date.now();
1015
1135
  let error = null;
@@ -1021,6 +1141,7 @@ class StableBrowser {
1021
1141
  info.selectors = selectors;
1022
1142
  info.value = value;
1023
1143
  try {
1144
+ value = await this._replaceWithLocalData(value, this);
1024
1145
  let element = await this._locate(selectors, info, _params);
1025
1146
  //insert red border around the element
1026
1147
  await this.scrollIfNeeded(element, info);
@@ -1029,28 +1150,51 @@ class StableBrowser {
1029
1150
  try {
1030
1151
  await element.click();
1031
1152
  await new Promise((resolve) => setTimeout(resolve, 500));
1032
- const dateTimeValue = await getDateTimeValue({ value, element });
1033
- await element.evaluateHandle((el, dateTimeValue) => {
1034
- el.value = ""; // clear input
1035
- el.value = dateTimeValue;
1036
- }, dateTimeValue);
1153
+ if (format) {
1154
+ value = dayjs(value).format(format);
1155
+ await element.fill(value);
1156
+ }
1157
+ else {
1158
+ const dateTimeValue = await getDateTimeValue({ value, element });
1159
+ await element.evaluateHandle((el, dateTimeValue) => {
1160
+ el.value = ""; // clear input
1161
+ el.value = dateTimeValue;
1162
+ }, dateTimeValue);
1163
+ }
1164
+ if (enter) {
1165
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1166
+ await this.page.keyboard.press("Enter");
1167
+ await this.waitForPageLoad();
1168
+ }
1037
1169
  }
1038
- catch (error) {
1170
+ catch (err) {
1039
1171
  //await this.closeUnexpectedPopups();
1040
1172
  this.logger.error("setting date time input failed " + JSON.stringify(info));
1041
- this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
1173
+ this.logger.info("Trying again");
1174
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1042
1175
  info.screenshotPath = screenshotPath;
1043
- Object.assign(error, { info: info });
1176
+ Object.assign(err, { info: info });
1044
1177
  await element.click();
1045
1178
  await new Promise((resolve) => setTimeout(resolve, 500));
1046
- const dateTimeValue = await getDateTimeValue({ value, element });
1047
- await element.evaluateHandle((el, dateTimeValue) => {
1048
- el.value = ""; // clear input
1049
- el.value = dateTimeValue;
1050
- }, dateTimeValue);
1179
+ if (format) {
1180
+ value = dayjs(value).format(format);
1181
+ await element.fill(value);
1182
+ }
1183
+ else {
1184
+ const dateTimeValue = await getDateTimeValue({ value, element });
1185
+ await element.evaluateHandle((el, dateTimeValue) => {
1186
+ el.value = ""; // clear input
1187
+ el.value = dateTimeValue;
1188
+ }, dateTimeValue);
1189
+ }
1190
+ if (enter) {
1191
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1192
+ await this.page.keyboard.press("Enter");
1193
+ await this.waitForPageLoad();
1194
+ }
1051
1195
  }
1052
1196
  }
1053
- catch (error) {
1197
+ catch (e) {
1054
1198
  error = e;
1055
1199
  throw e;
1056
1200
  }
@@ -1213,7 +1357,7 @@ class StableBrowser {
1213
1357
  let element = await this._locate(selectors, info, _params);
1214
1358
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1215
1359
  await this._highlightElements(element);
1216
- await element.fill(value, { timeout: 10000 });
1360
+ await element.fill(value);
1217
1361
  await element.dispatchEvent("change");
1218
1362
  if (enter) {
1219
1363
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -1432,7 +1576,7 @@ class StableBrowser {
1432
1576
  return info;
1433
1577
  }
1434
1578
  catch (e) {
1435
- //await this.closeUnexpectedPopups();
1579
+ await this.closeUnexpectedPopups();
1436
1580
  this.logger.error("verify element contains text failed " + info.log);
1437
1581
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1438
1582
  info.screenshotPath = screenshotPath;
@@ -1480,6 +1624,29 @@ class StableBrowser {
1480
1624
  }
1481
1625
  return dataFile;
1482
1626
  }
1627
+ async waitForUserInput(message, world = null) {
1628
+ if (!message) {
1629
+ message = "# Wait for user input. Press any key to continue";
1630
+ }
1631
+ else {
1632
+ message = "# Wait for user input. " + message;
1633
+ }
1634
+ message += "\n";
1635
+ const value = await new Promise((resolve) => {
1636
+ const rl = readline.createInterface({
1637
+ input: process.stdin,
1638
+ output: process.stdout,
1639
+ });
1640
+ rl.question(message, (answer) => {
1641
+ rl.close();
1642
+ resolve(answer);
1643
+ });
1644
+ });
1645
+ if (value) {
1646
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1647
+ }
1648
+ this.setTestData({ userInput: value }, world);
1649
+ }
1483
1650
  setTestData(testData, world = null) {
1484
1651
  if (!testData) {
1485
1652
  return;
@@ -1507,7 +1674,7 @@ class StableBrowser {
1507
1674
  const data = fs.readFileSync(filePath, "utf8");
1508
1675
  const results = [];
1509
1676
  return new Promise((resolve, reject) => {
1510
- const readableStream = new stream.Readable();
1677
+ const readableStream = new Readable();
1511
1678
  readableStream._read = () => { }; // _read is required but you can noop it
1512
1679
  readableStream.push(data);
1513
1680
  readableStream.push(null);
@@ -1687,13 +1854,13 @@ class StableBrowser {
1687
1854
  ])));
1688
1855
  const { data } = await client.send("Page.captureScreenshot", {
1689
1856
  format: "png",
1690
- clip: {
1691
- x: 0,
1692
- y: 0,
1693
- width: viewportWidth,
1694
- height: viewportHeight,
1695
- scale: 1,
1696
- },
1857
+ // clip: {
1858
+ // x: 0,
1859
+ // y: 0,
1860
+ // width: viewportWidth,
1861
+ // height: viewportHeight,
1862
+ // scale: 1,
1863
+ // },
1697
1864
  });
1698
1865
  if (!screenshotPath) {
1699
1866
  return data;
@@ -2519,13 +2686,13 @@ class StableBrowser {
2519
2686
  }
2520
2687
  catch (e) {
2521
2688
  if (e.label === "networkidle") {
2522
- console.log("waitted for the network to be idle timeout");
2689
+ console.log("waited for the network to be idle timeout");
2523
2690
  }
2524
2691
  else if (e.label === "load") {
2525
- console.log("waitted for the load timeout");
2692
+ console.log("waited for the load timeout");
2526
2693
  }
2527
2694
  else if (e.label === "domcontentloaded") {
2528
- console.log("waitted for the domcontent loaded timeout");
2695
+ console.log("waited for the domcontent loaded timeout");
2529
2696
  }
2530
2697
  console.log(".");
2531
2698
  }
@@ -2560,13 +2727,6 @@ class StableBrowser {
2560
2727
  const info = {};
2561
2728
  try {
2562
2729
  await this.page.close();
2563
- if (this.context && this.context.pages && this.context.pages.length > 0) {
2564
- this.context.pages.pop();
2565
- this.page = this.context.pages[this.context.pages.length - 1];
2566
- this.context.page = this.page;
2567
- let title = await this.page.title();
2568
- console.log("Switched to page " + title);
2569
- }
2570
2730
  }
2571
2731
  catch (e) {
2572
2732
  console.log(".");
@@ -2675,33 +2835,18 @@ class StableBrowser {
2675
2835
  }
2676
2836
  async scrollIfNeeded(element, info) {
2677
2837
  try {
2678
- let didScroll = await element.evaluate((node) => {
2679
- const rect = node.getBoundingClientRect();
2680
- if (rect &&
2681
- rect.top >= 0 &&
2682
- rect.left >= 0 &&
2683
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2684
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
2685
- return false;
2686
- }
2687
- else {
2688
- node.scrollIntoView({
2689
- behavior: "smooth",
2690
- block: "center",
2691
- inline: "center",
2692
- });
2693
- return true;
2694
- }
2838
+ await element.scrollIntoViewIfNeeded({
2839
+ timeout: 2000,
2695
2840
  });
2696
- if (didScroll) {
2697
- await new Promise((resolve) => setTimeout(resolve, 500));
2698
- if (info) {
2699
- info.box = await element.boundingBox();
2700
- }
2841
+ await new Promise((resolve) => setTimeout(resolve, 500));
2842
+ if (info) {
2843
+ info.box = await element.boundingBox({
2844
+ timeout: 1000,
2845
+ });
2701
2846
  }
2702
2847
  }
2703
2848
  catch (e) {
2704
- console.log("scroll failed");
2849
+ console.log("#-#");
2705
2850
  }
2706
2851
  }
2707
2852
  _reportToWorld(world, properties) {