automation_model 1.0.393-dev → 1.0.393-main

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,8 @@ 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";
16
18
  const Types = {
17
19
  CLICK: "click_element",
18
20
  NAVIGATE: "navigate",
@@ -28,6 +30,7 @@ const Types = {
28
30
  SELECT: "select_combobox",
29
31
  VERIFY_PAGE_PATH: "verify_page_path",
30
32
  TYPE_PRESS: "type_press",
33
+ PRESS: "press_key",
31
34
  HOVER: "hover_element",
32
35
  CHECK: "check_element",
33
36
  UNCHECK: "uncheck_element",
@@ -36,6 +39,8 @@ const Types = {
36
39
  SET_DATE_TIME: "set_date_time",
37
40
  SET_VIEWPORT: "set_viewport",
38
41
  VERIFY_VISUAL: "verify_visual",
42
+ LOAD_DATA: "load_data",
43
+ SET_INPUT: "set_input",
39
44
  };
40
45
  class StableBrowser {
41
46
  constructor(browser, page, logger = null, context = null) {
@@ -81,9 +86,20 @@ class StableBrowser {
81
86
  this.page = page;
82
87
  context.page = page;
83
88
  context.pages.push(page);
84
- this.webLogFile = this.getWebLogFile(logFolder);
85
- this.registerConsoleLogListener(page, context, this.webLogFile);
86
- this.registerRequestListener();
89
+ page.on("close", async () => {
90
+ if (this.context && this.context.pages && this.context.pages.length > 1) {
91
+ this.context.pages.pop();
92
+ this.page = this.context.pages[this.context.pages.length - 1];
93
+ this.context.page = this.page;
94
+ try {
95
+ let title = await this.page.title();
96
+ console.log("Switched to page " + title);
97
+ }
98
+ catch (error) {
99
+ console.error("Error on page close", error);
100
+ }
101
+ }
102
+ });
87
103
  try {
88
104
  await this.waitForPageLoad();
89
105
  console.log("Switch page: " + (await page.title()));
@@ -178,24 +194,78 @@ class StableBrowser {
178
194
  }
179
195
  return text;
180
196
  }
181
- _getLocator(locator, scope, _params) {
182
- if (locator.type === "pw_selector") {
183
- return scope.locator(locator.selector);
197
+ _fixLocatorUsingParams(locator, _params) {
198
+ // check if not null
199
+ if (!locator) {
200
+ return locator;
201
+ }
202
+ // clone the locator
203
+ locator = JSON.parse(JSON.stringify(locator));
204
+ this.scanAndManipulate(locator, _params);
205
+ return locator;
206
+ }
207
+ _isObject(value) {
208
+ return value && typeof value === "object" && value.constructor === Object;
209
+ }
210
+ scanAndManipulate(currentObj, _params) {
211
+ for (const key in currentObj) {
212
+ if (typeof currentObj[key] === "string") {
213
+ // Perform string manipulation
214
+ currentObj[key] = this._fixUsingParams(currentObj[key], _params);
215
+ }
216
+ else if (this._isObject(currentObj[key])) {
217
+ // Recursively scan nested objects
218
+ this.scanAndManipulate(currentObj[key], _params);
219
+ }
184
220
  }
221
+ }
222
+ _getLocator(locator, scope, _params) {
223
+ locator = this._fixLocatorUsingParams(locator, _params);
224
+ let locatorReturn;
185
225
  if (locator.role) {
186
226
  if (locator.role[1].nameReg) {
187
227
  locator.role[1].name = reg_parser(locator.role[1].nameReg);
188
228
  delete locator.role[1].nameReg;
189
229
  }
190
- if (locator.role[1].name) {
191
- locator.role[1].name = this._fixUsingParams(locator.role[1].name, _params);
192
- }
193
- return scope.getByRole(locator.role[0], locator.role[1]);
230
+ // if (locator.role[1].name) {
231
+ // locator.role[1].name = this._fixUsingParams(locator.role[1].name, _params);
232
+ // }
233
+ locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
194
234
  }
195
235
  if (locator.css) {
196
- return scope.locator(this._fixUsingParams(locator.css, _params));
236
+ locatorReturn = scope.locator(locator.css);
197
237
  }
198
- throw new Error("unknown locator type");
238
+ // handle role/name locators
239
+ // locator.selector will be something like: textbox[name="Username"i]
240
+ if (locator.engine === "internal:role") {
241
+ // extract the role, name and the i/s flags using regex
242
+ const match = locator.selector.match(/(.*)\[(.*)="(.*)"(.*)\]/);
243
+ if (match) {
244
+ const role = match[1];
245
+ const name = match[3];
246
+ const flags = match[4];
247
+ locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
248
+ }
249
+ }
250
+ if (locator === null || locator === void 0 ? void 0 : locator.engine) {
251
+ if (locator.engine === "css") {
252
+ locatorReturn = scope.locator(locator.selector);
253
+ }
254
+ else {
255
+ let selector = locator.selector;
256
+ if (locator.engine === "internal:attr") {
257
+ if (!selector.startsWith("[")) {
258
+ selector = `[${selector}]`;
259
+ }
260
+ }
261
+ locatorReturn = scope.locator(`${locator.engine}=${selector}`);
262
+ }
263
+ }
264
+ if (!locatorReturn) {
265
+ console.error(locator);
266
+ throw new Error("Locator undefined");
267
+ }
268
+ return locatorReturn;
199
269
  }
200
270
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
201
271
  let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
@@ -414,7 +484,7 @@ class StableBrowser {
414
484
  }
415
485
  async _locate(selectors, info, _params, timeout = 30000) {
416
486
  for (let i = 0; i < 3; i++) {
417
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
487
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
418
488
  for (let j = 0; j < selectors.locators.length; j++) {
419
489
  let selector = selectors.locators[j];
420
490
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -434,9 +504,27 @@ class StableBrowser {
434
504
  //let arrayMode = Array.isArray(selectors);
435
505
  let scope = this.page;
436
506
  if (selectors.iframe_src || selectors.frameLocators) {
507
+ const findFrame = (frame, framescope) => {
508
+ for (let i = 0; i < frame.selectors.length; i++) {
509
+ let frameLocator = frame.selectors[i];
510
+ if (frameLocator.css) {
511
+ framescope = framescope.frameLocator(frameLocator.css);
512
+ break;
513
+ }
514
+ }
515
+ if (frame.children) {
516
+ return findFrame(frame.children, framescope);
517
+ }
518
+ return framescope;
519
+ };
437
520
  info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
438
521
  while (true) {
439
522
  let frameFound = false;
523
+ if (selectors.nestFrmLoc) {
524
+ scope = findFrame(selectors.nestFrmLoc, scope);
525
+ frameFound = true;
526
+ break;
527
+ }
440
528
  if (selectors.frameLocators) {
441
529
  for (let i = 0; i < selectors.frameLocators.length; i++) {
442
530
  let frameLocator = selectors.frameLocators[i];
@@ -600,6 +688,9 @@ class StableBrowser {
600
688
  async click(selectors, _params, options = {}, world = null) {
601
689
  this._validateSelectors(selectors);
602
690
  const startTime = Date.now();
691
+ if (options && options.context) {
692
+ selectors.locators[0].text = options.context;
693
+ }
603
694
  const info = {};
604
695
  info.log = "***** click on " + selectors.element_name + " *****\n";
605
696
  info.operation = "click";
@@ -613,14 +704,14 @@ class StableBrowser {
613
704
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
614
705
  try {
615
706
  await this._highlightElements(element);
616
- await element.click({ timeout: 5000 });
707
+ await element.click();
617
708
  await new Promise((resolve) => setTimeout(resolve, 1000));
618
709
  }
619
710
  catch (e) {
620
711
  // await this.closeUnexpectedPopups();
621
712
  info.log += "click failed, will try again" + "\n";
622
713
  element = await this._locate(selectors, info, _params);
623
- await element.click({ timeout: 10000, force: true });
714
+ await element.dispatchEvent("click");
624
715
  await new Promise((resolve) => setTimeout(resolve, 1000));
625
716
  }
626
717
  await this.waitForPageLoad();
@@ -673,7 +764,7 @@ class StableBrowser {
673
764
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
674
765
  try {
675
766
  await this._highlightElements(element);
676
- await element.setChecked(checked, { timeout: 5000 });
767
+ await element.setChecked(checked);
677
768
  await new Promise((resolve) => setTimeout(resolve, 1000));
678
769
  }
679
770
  catch (e) {
@@ -737,7 +828,7 @@ class StableBrowser {
737
828
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
738
829
  try {
739
830
  await this._highlightElements(element);
740
- await element.hover({ timeout: 10000 });
831
+ await element.hover();
741
832
  await new Promise((resolve) => setTimeout(resolve, 1000));
742
833
  }
743
834
  catch (e) {
@@ -799,7 +890,7 @@ class StableBrowser {
799
890
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
800
891
  try {
801
892
  await this._highlightElements(element);
802
- await element.selectOption(values, { timeout: 5000 });
893
+ await element.selectOption(values);
803
894
  }
804
895
  catch (e) {
805
896
  //await this.closeUnexpectedPopups();
@@ -908,71 +999,45 @@ class StableBrowser {
908
999
  });
909
1000
  }
910
1001
  }
911
- async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1002
+ async setInputValue(selectors, value, _params = null, options = {}, world = null) {
1003
+ // set input value for non fillable inputs like date, time, range, color, etc.
912
1004
  this._validateSelectors(selectors);
913
1005
  const startTime = Date.now();
914
- let error = null;
915
- let screenshotId = null;
916
- let screenshotPath = null;
917
1006
  const info = {};
918
- info.log = "";
919
- info.operation = Types.SET_DATE_TIME;
1007
+ info.log = "***** set input value " + selectors.element_name + " *****\n";
1008
+ info.operation = "setInputValue";
920
1009
  info.selectors = selectors;
1010
+ value = this._fixUsingParams(value, _params);
921
1011
  info.value = value;
1012
+ let error = null;
1013
+ let screenshotId = null;
1014
+ let screenshotPath = null;
922
1015
  try {
923
1016
  value = await this._replaceWithLocalData(value, this);
924
1017
  let element = await this._locate(selectors, info, _params);
925
- //insert red border around the element
926
1018
  await this.scrollIfNeeded(element, info);
927
1019
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
928
1020
  await this._highlightElements(element);
929
1021
  try {
930
- await element.click();
931
- await new Promise((resolve) => setTimeout(resolve, 500));
932
- if (format) {
933
- value = dayjs(value).format(format);
934
- await element.fill(value);
935
- }
936
- else {
937
- const dateTimeValue = await getDateTimeValue({ value, element });
938
- await element.evaluateHandle((el, dateTimeValue) => {
939
- el.value = ""; // clear input
940
- el.value = dateTimeValue;
941
- }, dateTimeValue);
942
- }
943
- if (enter) {
944
- await new Promise((resolve) => setTimeout(resolve, 2000));
945
- await this.page.keyboard.press("Enter");
946
- await this.waitForPageLoad();
947
- }
1022
+ await element.evaluateHandle((el, value) => {
1023
+ el.value = value;
1024
+ }, value);
948
1025
  }
949
1026
  catch (error) {
950
- //await this.closeUnexpectedPopups();
951
- this.logger.error("setting date time input failed " + JSON.stringify(info));
952
- this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
1027
+ this.logger.error("setInputValue failed, will try again");
1028
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
953
1029
  info.screenshotPath = screenshotPath;
954
1030
  Object.assign(error, { info: info });
955
- await element.click();
956
- await new Promise((resolve) => setTimeout(resolve, 500));
957
- if (format) {
958
- value = dayjs(value).format(format);
959
- await element.fill(value);
960
- }
961
- else {
962
- const dateTimeValue = await getDateTimeValue({ value, element });
963
- await element.evaluateHandle((el, dateTimeValue) => {
964
- el.value = ""; // clear input
965
- el.value = dateTimeValue;
966
- }, dateTimeValue);
967
- }
968
- if (enter) {
969
- await new Promise((resolve) => setTimeout(resolve, 2000));
970
- await this.page.keyboard.press("Enter");
971
- await this.waitForPageLoad();
972
- }
1031
+ await element.evaluateHandle((el, value) => {
1032
+ el.value = value;
1033
+ });
973
1034
  }
974
1035
  }
975
- catch (error) {
1036
+ catch (e) {
1037
+ this.logger.error("setInputValue failed " + info.log);
1038
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1039
+ info.screenshotPath = screenshotPath;
1040
+ Object.assign(e, { info: info });
976
1041
  error = e;
977
1042
  throw e;
978
1043
  }
@@ -980,10 +1045,10 @@ class StableBrowser {
980
1045
  const endTime = Date.now();
981
1046
  this._reportToWorld(world, {
982
1047
  element_name: selectors.element_name,
983
- type: Types.SET_DATE_TIME,
984
- screenshotId,
1048
+ type: Types.SET_INPUT,
1049
+ text: `Set input value`,
985
1050
  value: value,
986
- text: `setDateTime input with value: ${value}`,
1051
+ screenshotId,
987
1052
  result: error
988
1053
  ? {
989
1054
  status: "FAILED",
@@ -1000,7 +1065,7 @@ class StableBrowser {
1000
1065
  });
1001
1066
  }
1002
1067
  }
1003
- async setDateTime(selectors, value, enter = false, _params = null, options = {}, world = null) {
1068
+ async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
1004
1069
  this._validateSelectors(selectors);
1005
1070
  const startTime = Date.now();
1006
1071
  let error = null;
@@ -1012,6 +1077,7 @@ class StableBrowser {
1012
1077
  info.selectors = selectors;
1013
1078
  info.value = value;
1014
1079
  try {
1080
+ value = await this._replaceWithLocalData(value, this);
1015
1081
  let element = await this._locate(selectors, info, _params);
1016
1082
  //insert red border around the element
1017
1083
  await this.scrollIfNeeded(element, info);
@@ -1020,28 +1086,51 @@ class StableBrowser {
1020
1086
  try {
1021
1087
  await element.click();
1022
1088
  await new Promise((resolve) => setTimeout(resolve, 500));
1023
- const dateTimeValue = await getDateTimeValue({ value, element });
1024
- await element.evaluateHandle((el, dateTimeValue) => {
1025
- el.value = ""; // clear input
1026
- el.value = dateTimeValue;
1027
- }, dateTimeValue);
1089
+ if (format) {
1090
+ value = dayjs(value).format(format);
1091
+ await element.fill(value);
1092
+ }
1093
+ else {
1094
+ const dateTimeValue = await getDateTimeValue({ value, element });
1095
+ await element.evaluateHandle((el, dateTimeValue) => {
1096
+ el.value = ""; // clear input
1097
+ el.value = dateTimeValue;
1098
+ }, dateTimeValue);
1099
+ }
1100
+ if (enter) {
1101
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1102
+ await this.page.keyboard.press("Enter");
1103
+ await this.waitForPageLoad();
1104
+ }
1028
1105
  }
1029
- catch (error) {
1106
+ catch (err) {
1030
1107
  //await this.closeUnexpectedPopups();
1031
1108
  this.logger.error("setting date time input failed " + JSON.stringify(info));
1032
- this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
1109
+ this.logger.info("Trying again");
1110
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1033
1111
  info.screenshotPath = screenshotPath;
1034
- Object.assign(error, { info: info });
1112
+ Object.assign(err, { info: info });
1035
1113
  await element.click();
1036
1114
  await new Promise((resolve) => setTimeout(resolve, 500));
1037
- const dateTimeValue = await getDateTimeValue({ value, element });
1038
- await element.evaluateHandle((el, dateTimeValue) => {
1039
- el.value = ""; // clear input
1040
- el.value = dateTimeValue;
1041
- }, dateTimeValue);
1115
+ if (format) {
1116
+ value = dayjs(value).format(format);
1117
+ await element.fill(value);
1118
+ }
1119
+ else {
1120
+ const dateTimeValue = await getDateTimeValue({ value, element });
1121
+ await element.evaluateHandle((el, dateTimeValue) => {
1122
+ el.value = ""; // clear input
1123
+ el.value = dateTimeValue;
1124
+ }, dateTimeValue);
1125
+ }
1126
+ if (enter) {
1127
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1128
+ await this.page.keyboard.press("Enter");
1129
+ await this.waitForPageLoad();
1130
+ }
1042
1131
  }
1043
1132
  }
1044
- catch (error) {
1133
+ catch (e) {
1045
1134
  error = e;
1046
1135
  throw e;
1047
1136
  }
@@ -1091,20 +1180,32 @@ class StableBrowser {
1091
1180
  await this.scrollIfNeeded(element, info);
1092
1181
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1093
1182
  await this._highlightElements(element);
1094
- try {
1095
- let currentValue = await element.inputValue();
1096
- if (currentValue) {
1097
- await element.fill("");
1183
+ if (options === null || options === undefined || !options.press) {
1184
+ try {
1185
+ let currentValue = await element.inputValue();
1186
+ if (currentValue) {
1187
+ await element.fill("");
1188
+ }
1189
+ }
1190
+ catch (e) {
1191
+ this.logger.info("unable to clear input value");
1098
1192
  }
1099
1193
  }
1100
- catch (e) {
1101
- this.logger.info("unable to clear input value");
1102
- }
1103
- try {
1104
- await element.click({ timeout: 5000 });
1194
+ if (options === null || options === undefined || options.press) {
1195
+ try {
1196
+ await element.click({ timeout: 5000 });
1197
+ }
1198
+ catch (e) {
1199
+ await element.dispatchEvent("click");
1200
+ }
1105
1201
  }
1106
- catch (e) {
1107
- await element.dispatchEvent("click");
1202
+ else {
1203
+ try {
1204
+ await element.focus();
1205
+ }
1206
+ catch (e) {
1207
+ await element.dispatchEvent("focus");
1208
+ }
1108
1209
  }
1109
1210
  await new Promise((resolve) => setTimeout(resolve, 500));
1110
1211
  const valueSegment = _value.split("&&");
@@ -1192,7 +1293,7 @@ class StableBrowser {
1192
1293
  let element = await this._locate(selectors, info, _params);
1193
1294
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1194
1295
  await this._highlightElements(element);
1195
- await element.fill(value, { timeout: 10000 });
1296
+ await element.fill(value);
1196
1297
  await element.dispatchEvent("change");
1197
1298
  if (enter) {
1198
1299
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -1411,7 +1512,7 @@ class StableBrowser {
1411
1512
  return info;
1412
1513
  }
1413
1514
  catch (e) {
1414
- //await this.closeUnexpectedPopups();
1515
+ await this.closeUnexpectedPopups();
1415
1516
  this.logger.error("verify element contains text failed " + info.log);
1416
1517
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1417
1518
  info.screenshotPath = screenshotPath;
@@ -1459,6 +1560,29 @@ class StableBrowser {
1459
1560
  }
1460
1561
  return dataFile;
1461
1562
  }
1563
+ async waitForUserInput(message, world = null) {
1564
+ if (!message) {
1565
+ message = "# Wait for user input. Press any key to continue";
1566
+ }
1567
+ else {
1568
+ message = "# Wait for user input. " + message;
1569
+ }
1570
+ message += "\n";
1571
+ const value = await new Promise((resolve) => {
1572
+ const rl = readline.createInterface({
1573
+ input: process.stdin,
1574
+ output: process.stdout,
1575
+ });
1576
+ rl.question(message, (answer) => {
1577
+ rl.close();
1578
+ resolve(answer);
1579
+ });
1580
+ });
1581
+ if (value) {
1582
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1583
+ }
1584
+ this.setTestData({ userInput: value }, world);
1585
+ }
1462
1586
  setTestData(testData, world = null) {
1463
1587
  if (!testData) {
1464
1588
  return;
@@ -1486,7 +1610,7 @@ class StableBrowser {
1486
1610
  const data = fs.readFileSync(filePath, "utf8");
1487
1611
  const results = [];
1488
1612
  return new Promise((resolve, reject) => {
1489
- const readableStream = new stream.Readable();
1613
+ const readableStream = new Readable();
1490
1614
  readableStream._read = () => { }; // _read is required but you can noop it
1491
1615
  readableStream.push(data);
1492
1616
  readableStream.push(null);
@@ -1666,13 +1790,13 @@ class StableBrowser {
1666
1790
  ])));
1667
1791
  const { data } = await client.send("Page.captureScreenshot", {
1668
1792
  format: "png",
1669
- clip: {
1670
- x: 0,
1671
- y: 0,
1672
- width: viewportWidth,
1673
- height: viewportHeight,
1674
- scale: 1,
1675
- },
1793
+ // clip: {
1794
+ // x: 0,
1795
+ // y: 0,
1796
+ // width: viewportWidth,
1797
+ // height: viewportHeight,
1798
+ // scale: 1,
1799
+ // },
1676
1800
  });
1677
1801
  if (!screenshotPath) {
1678
1802
  return data;
@@ -1778,7 +1902,8 @@ class StableBrowser {
1778
1902
  if (world) {
1779
1903
  world[variable] = info.value;
1780
1904
  }
1781
- this.logger.info("world." + variable + "=" + info.value);
1905
+ this.setTestData({ [variable]: info.value }, world);
1906
+ this.logger.info("set test data: " + variable + "=" + info.value);
1782
1907
  return info;
1783
1908
  }
1784
1909
  catch (e) {
@@ -1815,6 +1940,91 @@ class StableBrowser {
1815
1940
  });
1816
1941
  }
1817
1942
  }
1943
+ async extractEmailData(emailAddress, options, world) {
1944
+ if (!emailAddress) {
1945
+ throw new Error("email address is null");
1946
+ }
1947
+ // check if address contain @
1948
+ if (emailAddress.indexOf("@") === -1) {
1949
+ emailAddress = emailAddress + "@blinq-mail.io";
1950
+ }
1951
+ else {
1952
+ if (!emailAddress.toLowerCase().endsWith("@blinq-mail.io")) {
1953
+ throw new Error("email address should end with @blinq-mail.io");
1954
+ }
1955
+ }
1956
+ const startTime = Date.now();
1957
+ let timeout = 60000;
1958
+ if (options && options.timeout) {
1959
+ timeout = options.timeout;
1960
+ }
1961
+ const serviceUrl = this._getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
1962
+ const request = {
1963
+ method: "POST",
1964
+ url: serviceUrl,
1965
+ headers: {
1966
+ "Content-Type": "application/json",
1967
+ Authorization: `Bearer ${process.env.TOKEN}`,
1968
+ },
1969
+ data: JSON.stringify({
1970
+ email: emailAddress,
1971
+ }),
1972
+ };
1973
+ let errorCount = 0;
1974
+ while (true) {
1975
+ try {
1976
+ let result = await this.context.api.request(request);
1977
+ // the response body expected to be the following:
1978
+ // {
1979
+ // "status": true,
1980
+ // "content": {
1981
+ // "url": "",
1982
+ // "code": "112112",
1983
+ // "name": "generate_link_or_code"
1984
+ // }
1985
+ //}
1986
+ if ((result && result.data, result.data.status === true)) {
1987
+ let codeOrUrlFound = false;
1988
+ let emailCode = null;
1989
+ let emailUrl = null;
1990
+ // check if a code is returned
1991
+ if (result.data.content && result.data.content.code) {
1992
+ let code = result.data.content.code;
1993
+ this.setTestData({ emailCode: code }, world);
1994
+ this.logger.info("set test data: emailCode = " + code);
1995
+ emailCode = code;
1996
+ codeOrUrlFound = true;
1997
+ }
1998
+ // check if a url is returned
1999
+ if (result.data.content && result.data.content.url) {
2000
+ let url = result.data.content.url;
2001
+ this.setTestData({ emailUrl: url }, world);
2002
+ this.logger.info("set test data: emailUrl = " + url);
2003
+ emailUrl = url;
2004
+ codeOrUrlFound = true;
2005
+ }
2006
+ if (codeOrUrlFound) {
2007
+ return { emailUrl, emailCode };
2008
+ }
2009
+ else {
2010
+ this.logger.info("an email received but no code or url found");
2011
+ }
2012
+ }
2013
+ }
2014
+ catch (e) {
2015
+ errorCount++;
2016
+ if (errorCount > 3) {
2017
+ throw e;
2018
+ }
2019
+ // ignore
2020
+ }
2021
+ // check if the timeout is reached
2022
+ if (Date.now() - startTime > timeout) {
2023
+ throw new Error("timeout reached");
2024
+ }
2025
+ await new Promise((resolve) => setTimeout(resolve, 5000));
2026
+ }
2027
+ }
1818
2028
  async _highlightElements(scope, css) {
1819
2029
  try {
1820
2030
  if (!scope) {
@@ -2037,6 +2247,16 @@ class StableBrowser {
2037
2247
  });
2038
2248
  }
2039
2249
  }
2250
+ _getServerUrl() {
2251
+ let serviceUrl = "https://api.blinq.io";
2252
+ if (process.env.NODE_ENV_BLINQ === "dev") {
2253
+ serviceUrl = "https://dev.api.blinq.io";
2254
+ }
2255
+ else if (process.env.NODE_ENV_BLINQ === "stage") {
2256
+ serviceUrl = "https://stage.api.blinq.io";
2257
+ }
2258
+ return serviceUrl;
2259
+ }
2040
2260
  async visualVerification(text, options = {}, world = null) {
2041
2261
  const startTime = Date.now();
2042
2262
  let error = null;
@@ -2051,13 +2271,7 @@ class StableBrowser {
2051
2271
  throw new Error("TOKEN is not set");
2052
2272
  }
2053
2273
  try {
2054
- let serviceUrl = "https://api.blinq.io";
2055
- if (process.env.NODE_ENV_BLINQ === "dev") {
2056
- serviceUrl = "https://dev.api.blinq.io";
2057
- }
2058
- else if (process.env.NODE_ENV_BLINQ === "stage") {
2059
- serviceUrl = "https://stage.api.blinq.io";
2060
- }
2274
+ let serviceUrl = this._getServerUrl();
2061
2275
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
2062
2276
  info.screenshotPath = screenshotPath;
2063
2277
  const screenshot = await this.takeScreenshot();
@@ -2408,13 +2622,13 @@ class StableBrowser {
2408
2622
  }
2409
2623
  catch (e) {
2410
2624
  if (e.label === "networkidle") {
2411
- console.log("waitted for the network to be idle timeout");
2625
+ console.log("waited for the network to be idle timeout");
2412
2626
  }
2413
2627
  else if (e.label === "load") {
2414
- console.log("waitted for the load timeout");
2628
+ console.log("waited for the load timeout");
2415
2629
  }
2416
2630
  else if (e.label === "domcontentloaded") {
2417
- console.log("waitted for the domcontent loaded timeout");
2631
+ console.log("waited for the domcontent loaded timeout");
2418
2632
  }
2419
2633
  console.log(".");
2420
2634
  }
@@ -2449,13 +2663,6 @@ class StableBrowser {
2449
2663
  const info = {};
2450
2664
  try {
2451
2665
  await this.page.close();
2452
- if (this.context && this.context.pages && this.context.pages.length > 0) {
2453
- this.context.pages.pop();
2454
- this.page = this.context.pages[this.context.pages.length - 1];
2455
- this.context.page = this.page;
2456
- let title = await this.page.title();
2457
- console.log("Switched to page " + title);
2458
- }
2459
2666
  }
2460
2667
  catch (e) {
2461
2668
  console.log(".");
@@ -2564,33 +2771,18 @@ class StableBrowser {
2564
2771
  }
2565
2772
  async scrollIfNeeded(element, info) {
2566
2773
  try {
2567
- let didScroll = await element.evaluate((node) => {
2568
- const rect = node.getBoundingClientRect();
2569
- if (rect &&
2570
- rect.top >= 0 &&
2571
- rect.left >= 0 &&
2572
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2573
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
2574
- return false;
2575
- }
2576
- else {
2577
- node.scrollIntoView({
2578
- behavior: "smooth",
2579
- block: "center",
2580
- inline: "center",
2581
- });
2582
- return true;
2583
- }
2774
+ await element.scrollIntoViewIfNeeded({
2775
+ timeout: 2000,
2584
2776
  });
2585
- if (didScroll) {
2586
- await new Promise((resolve) => setTimeout(resolve, 500));
2587
- if (info) {
2588
- info.box = await element.boundingBox();
2589
- }
2777
+ await new Promise((resolve) => setTimeout(resolve, 500));
2778
+ if (info) {
2779
+ info.box = await element.boundingBox({
2780
+ timeout: 1000,
2781
+ });
2590
2782
  }
2591
2783
  }
2592
2784
  catch (e) {
2593
- console.log("scroll failed");
2785
+ console.log("#-#");
2594
2786
  }
2595
2787
  }
2596
2788
  _reportToWorld(world, properties) {