automation_model 1.0.413-dev → 1.0.413-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,8 @@ 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";
17
19
  const Types = {
18
20
  CLICK: "click_element",
19
21
  NAVIGATE: "navigate",
@@ -41,15 +43,19 @@ const Types = {
41
43
  LOAD_DATA: "load_data",
42
44
  SET_INPUT: "set_input",
43
45
  };
46
+ export const apps = {};
44
47
  class StableBrowser {
45
- constructor(browser, page, logger = null, context = null) {
48
+ constructor(browser, page, logger = null, context = null, world = null) {
46
49
  this.browser = browser;
47
50
  this.page = page;
48
51
  this.logger = logger;
49
52
  this.context = context;
53
+ this.world = world;
50
54
  this.project_path = null;
51
55
  this.webLogFile = null;
56
+ this.networkLogger = null;
52
57
  this.configuration = null;
58
+ this.appName = "main";
53
59
  if (!this.logger) {
54
60
  this.logger = console;
55
61
  }
@@ -75,23 +81,34 @@ class StableBrowser {
75
81
  this.logger.error("unable to read ai_config.json");
76
82
  }
77
83
  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();
84
+ this.world = world;
81
85
  context.pages = [this.page];
82
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
+ }
83
95
  context.playContext.on("page", async function (page) {
84
96
  context.pageLoading.status = true;
85
97
  this.page = page;
86
98
  context.page = page;
87
99
  context.pages.push(page);
88
100
  page.on("close", async () => {
89
- if (this.context && this.context.pages && this.context.pages.length > 0) {
101
+ if (this.context && this.context.pages && this.context.pages.length > 1) {
90
102
  this.context.pages.pop();
91
103
  this.page = this.context.pages[this.context.pages.length - 1];
92
104
  this.context.page = this.page;
93
- let title = await this.page.title();
94
- console.log("Switched to page " + title);
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
+ }
95
112
  }
96
113
  });
97
114
  try {
@@ -104,6 +121,36 @@ class StableBrowser {
104
121
  context.pageLoading.status = false;
105
122
  }.bind(this));
106
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
+ }
107
154
  getWebLogFile(logFolder) {
108
155
  if (!fs.existsSync(logFolder)) {
109
156
  fs.mkdirSync(logFolder, { recursive: true });
@@ -115,37 +162,63 @@ class StableBrowser {
115
162
  const fileName = nextIndex + ".json";
116
163
  return path.join(logFolder, fileName);
117
164
  }
118
- registerConsoleLogListener(page, context, logFile) {
165
+ registerConsoleLogListener(page, context) {
119
166
  if (!this.context.webLogger) {
120
167
  this.context.webLogger = [];
121
168
  }
122
169
  page.on("console", async (msg) => {
123
- this.context.webLogger.push({
170
+ var _a;
171
+ const obj = {
124
172
  type: msg.type(),
125
173
  text: msg.text(),
126
174
  location: msg.location(),
127
175
  time: new Date().toISOString(),
128
- });
129
- 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" });
130
179
  });
131
180
  }
132
- registerRequestListener() {
133
- 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();
134
188
  try {
135
- const pageUrl = new URL(this.page.url());
189
+ const pageUrl = new URL(page.url());
136
190
  const requestUrl = new URL(data.url());
137
191
  if (pageUrl.hostname === requestUrl.hostname) {
138
192
  const method = data.method();
139
- if (method === "POST" || method === "GET" || method === "PUT" || method === "DELETE" || method === "PATCH") {
193
+ if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
140
194
  const token = await data.headerValue("Authorization");
141
195
  if (token) {
142
- this.context.authtoken = token;
196
+ context.authtoken = token;
143
197
  }
144
198
  }
145
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" });
146
212
  }
147
213
  catch (error) {
148
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));
149
222
  }
150
223
  });
151
224
  }
@@ -226,7 +299,7 @@ class StableBrowser {
226
299
  // }
227
300
  locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
228
301
  }
229
- if (locator.css || locator.engine === "css") {
302
+ if (locator.css) {
230
303
  locatorReturn = scope.locator(locator.css);
231
304
  }
232
305
  // handle role/name locators
@@ -242,25 +315,29 @@ class StableBrowser {
242
315
  }
243
316
  }
244
317
  if (locator === null || locator === void 0 ? void 0 : locator.engine) {
245
- if (locator.engine === "internal:attr") {
246
- selector = `[${selector}]`;
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}`);
247
329
  }
248
- locatorReturn = scope.locator(`${locator.engine}=${selector}`);
249
330
  }
250
331
  if (!locatorReturn) {
251
332
  console.error(locator);
252
333
  throw new Error("Locator undefined");
253
- // } else {
254
- // const count = locatorReturn.count();
255
- // if (count === 0) {
256
- // throw new Error("Elements not found");
257
- // } else if (count > 1) {
258
- // throw new Error("Multiple elements found");
259
- // }
260
334
  }
261
335
  return locatorReturn;
262
336
  }
263
337
  async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
338
+ if (css && css.locator) {
339
+ css = css.locator;
340
+ }
264
341
  let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
265
342
  if (result.elementCount === 0) {
266
343
  return;
@@ -288,6 +365,15 @@ class StableBrowser {
288
365
  return false;
289
366
  }
290
367
  document.isParent = isParent;
368
+ function getRegex(str) {
369
+ const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
370
+ if (!match) {
371
+ return null;
372
+ }
373
+ let [_, pattern, flags] = match;
374
+ return new RegExp(pattern, flags);
375
+ }
376
+ document.getRegex = getRegex;
291
377
  function collectAllShadowDomElements(element, result = []) {
292
378
  // Check and add the element if it has a shadow root
293
379
  if (element.shadowRoot) {
@@ -306,6 +392,10 @@ class StableBrowser {
306
392
  if (!tag) {
307
393
  tag = "*";
308
394
  }
395
+ let regexpSearch = document.getRegex(text);
396
+ if (regexpSearch) {
397
+ regex = true;
398
+ }
309
399
  let elements = Array.from(document.querySelectorAll(tag));
310
400
  let shadowHosts = [];
311
401
  document.collectAllShadowDomElements(document, shadowHosts);
@@ -321,7 +411,9 @@ class StableBrowser {
321
411
  let randomToken = null;
322
412
  const foundElements = [];
323
413
  if (regex) {
324
- let regexpSearch = new RegExp(text, "im");
414
+ if (!regexpSearch) {
415
+ regexpSearch = new RegExp(text, "im");
416
+ }
325
417
  for (let i = 0; i < elements.length; i++) {
326
418
  const element = elements[i];
327
419
  if ((element.innerText && regexpSearch.test(element.innerText)) ||
@@ -335,8 +427,8 @@ class StableBrowser {
335
427
  for (let i = 0; i < elements.length; i++) {
336
428
  const element = elements[i];
337
429
  if (partial) {
338
- if ((element.innerText && element.innerText.trim().includes(text)) ||
339
- (element.value && element.value.includes(text))) {
430
+ if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
431
+ (element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
340
432
  foundElements.push(element);
341
433
  }
342
434
  }
@@ -469,6 +561,8 @@ class StableBrowser {
469
561
  if (result.foundElements.length > 0) {
470
562
  let dialogCloseLocator = result.foundElements[0].locator;
471
563
  await dialogCloseLocator.click();
564
+ // wait for the dialog to close
565
+ await dialogCloseLocator.waitFor({ state: "hidden" });
472
566
  return { rerun: true };
473
567
  }
474
568
  }
@@ -477,7 +571,7 @@ class StableBrowser {
477
571
  }
478
572
  async _locate(selectors, info, _params, timeout = 30000) {
479
573
  for (let i = 0; i < 3; i++) {
480
- info.log += "attempt " + i + ": totoal locators " + selectors.locators.length + "\n";
574
+ info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
481
575
  for (let j = 0; j < selectors.locators.length; j++) {
482
576
  let selector = selectors.locators[j];
483
577
  info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
@@ -497,9 +591,30 @@ class StableBrowser {
497
591
  //let arrayMode = Array.isArray(selectors);
498
592
  let scope = this.page;
499
593
  if (selectors.iframe_src || selectors.frameLocators) {
594
+ const findFrame = (frame, framescope) => {
595
+ for (let i = 0; i < frame.selectors.length; i++) {
596
+ let frameLocator = frame.selectors[i];
597
+ if (frameLocator.css) {
598
+ framescope = framescope.frameLocator(frameLocator.css);
599
+ if (frameLocator.index) {
600
+ framescope = framescope.nth(frameLocator.index);
601
+ }
602
+ break;
603
+ }
604
+ }
605
+ if (frame.children) {
606
+ return findFrame(frame.children, framescope);
607
+ }
608
+ return framescope;
609
+ };
500
610
  info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
501
611
  while (true) {
502
612
  let frameFound = false;
613
+ if (selectors.nestFrmLoc) {
614
+ scope = findFrame(selectors.nestFrmLoc, scope);
615
+ frameFound = true;
616
+ break;
617
+ }
503
618
  if (selectors.frameLocators) {
504
619
  for (let i = 0; i < selectors.frameLocators.length; i++) {
505
620
  let frameLocator = selectors.frameLocators[i];
@@ -679,14 +794,14 @@ class StableBrowser {
679
794
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
680
795
  try {
681
796
  await this._highlightElements(element);
682
- await element.click({ timeout: 5000 });
797
+ await element.click();
683
798
  await new Promise((resolve) => setTimeout(resolve, 1000));
684
799
  }
685
800
  catch (e) {
686
801
  // await this.closeUnexpectedPopups();
687
802
  info.log += "click failed, will try again" + "\n";
688
803
  element = await this._locate(selectors, info, _params);
689
- await element.click({ timeout: 10000, force: true });
804
+ await element.dispatchEvent("click");
690
805
  await new Promise((resolve) => setTimeout(resolve, 1000));
691
806
  }
692
807
  await this.waitForPageLoad();
@@ -739,7 +854,7 @@ class StableBrowser {
739
854
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
740
855
  try {
741
856
  await this._highlightElements(element);
742
- await element.setChecked(checked, { timeout: 5000 });
857
+ await element.setChecked(checked);
743
858
  await new Promise((resolve) => setTimeout(resolve, 1000));
744
859
  }
745
860
  catch (e) {
@@ -803,7 +918,7 @@ class StableBrowser {
803
918
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
804
919
  try {
805
920
  await this._highlightElements(element);
806
- await element.hover({ timeout: 10000 });
921
+ await element.hover();
807
922
  await new Promise((resolve) => setTimeout(resolve, 1000));
808
923
  }
809
924
  catch (e) {
@@ -865,7 +980,7 @@ class StableBrowser {
865
980
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
866
981
  try {
867
982
  await this._highlightElements(element);
868
- await element.selectOption(values, { timeout: 5000 });
983
+ await element.selectOption(values);
869
984
  }
870
985
  catch (e) {
871
986
  //await this.closeUnexpectedPopups();
@@ -1153,7 +1268,6 @@ class StableBrowser {
1153
1268
  let element = await this._locate(selectors, info, _params);
1154
1269
  //insert red border around the element
1155
1270
  await this.scrollIfNeeded(element, info);
1156
- ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1157
1271
  await this._highlightElements(element);
1158
1272
  if (options === null || options === undefined || !options.press) {
1159
1273
  try {
@@ -1203,6 +1317,7 @@ class StableBrowser {
1203
1317
  await new Promise((resolve) => setTimeout(resolve, 500));
1204
1318
  }
1205
1319
  }
1320
+ ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1206
1321
  if (enter === true) {
1207
1322
  await new Promise((resolve) => setTimeout(resolve, 2000));
1208
1323
  await this.page.keyboard.press("Enter");
@@ -1268,7 +1383,7 @@ class StableBrowser {
1268
1383
  let element = await this._locate(selectors, info, _params);
1269
1384
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1270
1385
  await this._highlightElements(element);
1271
- await element.fill(value, { timeout: 10000 });
1386
+ await element.fill(value);
1272
1387
  await element.dispatchEvent("change");
1273
1388
  if (enter) {
1274
1389
  await new Promise((resolve) => setTimeout(resolve, 2000));
@@ -1487,7 +1602,7 @@ class StableBrowser {
1487
1602
  return info;
1488
1603
  }
1489
1604
  catch (e) {
1490
- //await this.closeUnexpectedPopups();
1605
+ await this.closeUnexpectedPopups();
1491
1606
  this.logger.error("verify element contains text failed " + info.log);
1492
1607
  ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
1493
1608
  info.screenshotPath = screenshotPath;
@@ -1535,6 +1650,29 @@ class StableBrowser {
1535
1650
  }
1536
1651
  return dataFile;
1537
1652
  }
1653
+ async waitForUserInput(message, world = null) {
1654
+ if (!message) {
1655
+ message = "# Wait for user input. Press any key to continue";
1656
+ }
1657
+ else {
1658
+ message = "# Wait for user input. " + message;
1659
+ }
1660
+ message += "\n";
1661
+ const value = await new Promise((resolve) => {
1662
+ const rl = readline.createInterface({
1663
+ input: process.stdin,
1664
+ output: process.stdout,
1665
+ });
1666
+ rl.question(message, (answer) => {
1667
+ rl.close();
1668
+ resolve(answer);
1669
+ });
1670
+ });
1671
+ if (value) {
1672
+ this.logger.info(`{{userInput}} was set to: ${value}`);
1673
+ }
1674
+ this.setTestData({ userInput: value }, world);
1675
+ }
1538
1676
  setTestData(testData, world = null) {
1539
1677
  if (!testData) {
1540
1678
  return;
@@ -1722,7 +1860,6 @@ class StableBrowser {
1722
1860
  }
1723
1861
  async takeScreenshot(screenshotPath) {
1724
1862
  const playContext = this.context.playContext;
1725
- const client = await playContext.newCDPSession(this.page);
1726
1863
  // Using CDP to capture the screenshot
1727
1864
  const viewportWidth = Math.max(...(await this.page.evaluate(() => [
1728
1865
  document.body.scrollWidth,
@@ -1732,41 +1869,40 @@ class StableBrowser {
1732
1869
  document.body.clientWidth,
1733
1870
  document.documentElement.clientWidth,
1734
1871
  ])));
1735
- const viewportHeight = Math.max(...(await this.page.evaluate(() => [
1736
- document.body.scrollHeight,
1737
- document.documentElement.scrollHeight,
1738
- document.body.offsetHeight,
1739
- document.documentElement.offsetHeight,
1740
- document.body.clientHeight,
1741
- document.documentElement.clientHeight,
1742
- ])));
1743
- const { data } = await client.send("Page.captureScreenshot", {
1744
- format: "png",
1745
- clip: {
1746
- x: 0,
1747
- y: 0,
1748
- width: viewportWidth,
1749
- height: viewportHeight,
1750
- scale: 1,
1751
- },
1752
- });
1753
- if (!screenshotPath) {
1754
- return data;
1755
- }
1756
- let screenshotBuffer = Buffer.from(data, "base64");
1757
- const sharpBuffer = sharp(screenshotBuffer);
1758
- const metadata = await sharpBuffer.metadata();
1759
- //check if you are on retina display and reduce the quality of the image
1760
- if (metadata.width > viewportWidth || metadata.height > viewportHeight) {
1761
- screenshotBuffer = await sharpBuffer
1762
- .resize(viewportWidth, viewportHeight, {
1763
- fit: sharp.fit.inside,
1764
- withoutEnlargement: true,
1765
- })
1766
- .toBuffer();
1767
- }
1768
- fs.writeFileSync(screenshotPath, screenshotBuffer);
1769
- await client.detach();
1872
+ let screenshotBuffer = null;
1873
+ if (this.context.browserName === "chromium") {
1874
+ const client = await playContext.newCDPSession(this.page);
1875
+ const { data } = await client.send("Page.captureScreenshot", {
1876
+ format: "png",
1877
+ // clip: {
1878
+ // x: 0,
1879
+ // y: 0,
1880
+ // width: viewportWidth,
1881
+ // height: viewportHeight,
1882
+ // scale: 1,
1883
+ // },
1884
+ });
1885
+ await client.detach();
1886
+ if (!screenshotPath) {
1887
+ return data;
1888
+ }
1889
+ screenshotBuffer = Buffer.from(data, "base64");
1890
+ }
1891
+ else {
1892
+ screenshotBuffer = await this.page.screenshot();
1893
+ }
1894
+ let image = await Jimp.read(screenshotBuffer);
1895
+ // Get the image dimensions
1896
+ const { width, height } = image.bitmap;
1897
+ const resizeRatio = viewportWidth / width;
1898
+ // Resize the image to fit within the viewport dimensions without enlarging
1899
+ if (width > viewportWidth) {
1900
+ image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
1901
+ await image.write(screenshotPath);
1902
+ }
1903
+ else {
1904
+ fs.writeFileSync(screenshotPath, screenshotBuffer);
1905
+ }
1770
1906
  }
1771
1907
  async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
1772
1908
  this._validateSelectors(selectors);
@@ -2574,13 +2710,13 @@ class StableBrowser {
2574
2710
  }
2575
2711
  catch (e) {
2576
2712
  if (e.label === "networkidle") {
2577
- console.log("waitted for the network to be idle timeout");
2713
+ console.log("waited for the network to be idle timeout");
2578
2714
  }
2579
2715
  else if (e.label === "load") {
2580
- console.log("waitted for the load timeout");
2716
+ console.log("waited for the load timeout");
2581
2717
  }
2582
2718
  else if (e.label === "domcontentloaded") {
2583
- console.log("waitted for the domcontent loaded timeout");
2719
+ console.log("waited for the domcontent loaded timeout");
2584
2720
  }
2585
2721
  console.log(".");
2586
2722
  }
@@ -2723,33 +2859,18 @@ class StableBrowser {
2723
2859
  }
2724
2860
  async scrollIfNeeded(element, info) {
2725
2861
  try {
2726
- let didScroll = await element.evaluate((node) => {
2727
- const rect = node.getBoundingClientRect();
2728
- if (rect &&
2729
- rect.top >= 0 &&
2730
- rect.left >= 0 &&
2731
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2732
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
2733
- return false;
2734
- }
2735
- else {
2736
- node.scrollIntoView({
2737
- behavior: "smooth",
2738
- block: "center",
2739
- inline: "center",
2740
- });
2741
- return true;
2742
- }
2862
+ await element.scrollIntoViewIfNeeded({
2863
+ timeout: 2000,
2743
2864
  });
2744
- if (didScroll) {
2745
- await new Promise((resolve) => setTimeout(resolve, 500));
2746
- if (info) {
2747
- info.box = await element.boundingBox();
2748
- }
2865
+ await new Promise((resolve) => setTimeout(resolve, 500));
2866
+ if (info) {
2867
+ info.box = await element.boundingBox({
2868
+ timeout: 1000,
2869
+ });
2749
2870
  }
2750
2871
  }
2751
2872
  catch (e) {
2752
- console.log("scroll failed");
2873
+ console.log("#-#");
2753
2874
  }
2754
2875
  }
2755
2876
  _reportToWorld(world, properties) {