@testim/testim-cli 3.254.0 → 3.256.0

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.
Files changed (61) hide show
  1. package/agent/routers/cliJsCode/service.js +11 -8
  2. package/agent/routers/codim/router.test.js +9 -12
  3. package/agent/routers/codim/service.js +16 -16
  4. package/agent/routers/playground/service.js +5 -7
  5. package/cli.js +6 -6
  6. package/cliAgentMode.js +11 -10
  7. package/codim/codim-cli.js +14 -9
  8. package/commons/featureFlags.js +29 -7
  9. package/commons/httpRequest.js +5 -1
  10. package/commons/httpRequestCounters.js +21 -10
  11. package/commons/initializeUserWithAuth.js +7 -4
  12. package/commons/lazyRequire.js +4 -3
  13. package/commons/preloadTests.js +6 -3
  14. package/commons/prepareRunner.js +7 -5
  15. package/commons/prepareRunnerAndTestimStartUtils.js +51 -45
  16. package/commons/runnerFileCache.js +10 -2
  17. package/commons/testimServicesApi.js +36 -5
  18. package/commons/testimTunnel.test.js +2 -1
  19. package/coverage/SummaryToObjectReport.js +0 -1
  20. package/coverage/jsCoverage.js +12 -10
  21. package/inputFileUtils.js +11 -9
  22. package/npm-shrinkwrap.json +214 -471
  23. package/package.json +4 -3
  24. package/player/services/tabService.js +15 -1
  25. package/player/stepActions/apiStepAction.js +49 -43
  26. package/player/stepActions/baseCliJsStepAction.js +19 -14
  27. package/player/stepActions/baseJsStepAction.js +9 -8
  28. package/player/stepActions/dropFileStepAction.js +1 -3
  29. package/player/stepActions/inputFileStepAction.js +10 -8
  30. package/player/stepActions/locateStepAction.js +2 -0
  31. package/player/stepActions/mouseStepAction.js +21 -22
  32. package/player/stepActions/nodePackageStepAction.js +34 -35
  33. package/player/stepActions/stepAction.js +1 -0
  34. package/player/utils/imageCaptureUtils.js +133 -172
  35. package/player/utils/screenshotUtils.js +16 -13
  36. package/player/utils/windowUtils.js +20 -8
  37. package/player/webdriver.js +25 -22
  38. package/processHandler.js +4 -0
  39. package/reports/junitReporter.js +6 -7
  40. package/reports/reporter.js +34 -39
  41. package/runOptions.d.ts +286 -0
  42. package/runOptions.js +60 -45
  43. package/runner.js +64 -24
  44. package/runners/ParallelWorkerManager.js +12 -12
  45. package/runners/TestPlanRunner.js +14 -15
  46. package/runners/buildCodeTests.js +1 -0
  47. package/runners/runnerUtils.js +11 -2
  48. package/services/branchService.js +11 -5
  49. package/services/gridService.js +36 -40
  50. package/services/localRCASaver.js +4 -0
  51. package/testRunStatus.js +8 -5
  52. package/utils/argsUtils.js +86 -0
  53. package/utils/argsUtils.test.js +32 -0
  54. package/utils/fsUtils.js +154 -0
  55. package/utils/index.js +10 -161
  56. package/utils/promiseUtils.js +13 -2
  57. package/utils/stringUtils.js +4 -2
  58. package/utils/stringUtils.test.js +22 -0
  59. package/utils/timeUtils.js +25 -0
  60. package/utils/utils.test.js +0 -41
  61. package/workers/WorkerExtension.js +6 -7
@@ -1,7 +1,10 @@
1
+ // @ts-check
2
+
1
3
  'use strict';
2
4
 
3
- const Promise = require('bluebird');
5
+ const _ = require('lodash');
4
6
  const Jimp = require('jimp');
7
+ const utils = require('../../utils');
5
8
 
6
9
  const logger = require('../../commons/logger').getLogger('image-capture-utils');
7
10
 
@@ -13,22 +16,17 @@ class RectIsOutsideOfImageError extends Error {
13
16
  }
14
17
  }
15
18
 
16
- const ImageCaptureUtils = function (tabId, windowUtils, screenshotUtils) {
17
- this.windowUtils = windowUtils;
18
- this.screenshotUtils = screenshotUtils;
19
- };
20
-
21
- function cropImageFromImageData(imageData, imageInfo) {
22
- const image = imageInfo.image || imageInfo;
19
+ async function cropImageFromImageData(imageData, imageInfo) {
20
+ const _image = imageInfo.image || imageInfo;
23
21
  const pixelRatio = imageInfo.devicePixelRatio;
24
22
 
25
- if (!image) {
26
- return Promise.reject(new Error('Failed to get image'));
23
+ if (!_image) {
24
+ throw new Error('Failed to get image');
27
25
  }
28
26
 
29
- const imageRegExMatch = image.match(/^data\:[^;]*\;base64,(.*)$/);
27
+ const imageRegExMatch = _image.match(/^data\:[^;]*\;base64,(.*)$/);
30
28
  if (!imageRegExMatch) {
31
- return Promise.reject(new Error('Image is not in base64 format'));
29
+ throw new Error('Image is not in base64 format');
32
30
  }
33
31
 
34
32
  // chromeCropImage2
@@ -39,108 +37,74 @@ function cropImageFromImageData(imageData, imageInfo) {
39
37
 
40
38
  // elementImage
41
39
  if (!imageData.elementRect) {
42
- const { image, ...rest } = imageData;
43
- logger.warn('missing elementRect', { ...rest });
44
- return Promise.resolve({});
40
+ logger.warn('missing elementRect', _.omit(imageData, 'image'));
41
+ return {};
45
42
  }
46
43
 
47
44
  const { elementRect } = imageData;
48
- return new Promise((resolve, reject) => {
49
- Jimp.read(Buffer.from(imageRegExMatch[1], 'base64'), (err, image) => {
50
- if (err) {
51
- return reject(err);
52
- }
53
- try {
54
- let x = elementRect.left * pixelRatio + offset.left * pixelRatio;
55
- let y = elementRect.top * pixelRatio + offset.top * pixelRatio;
56
- let width = elementRect.width * pixelRatio;
57
- let height = elementRect.height * pixelRatio;
58
-
59
- if (x < 0) {
60
- width += x;
61
- width = width < 0 ? 0 : width;
62
- x = 0;
63
- }
45
+ const image = await Jimp.read(Buffer.from(imageRegExMatch[1], 'base64'));
46
+ let x = elementRect.left * pixelRatio + offset.left * pixelRatio;
47
+ let y = elementRect.top * pixelRatio + offset.top * pixelRatio;
48
+ let width = elementRect.width * pixelRatio;
49
+ let height = elementRect.height * pixelRatio;
50
+
51
+ if (x < 0) {
52
+ width += x;
53
+ width = width < 0 ? 0 : width;
54
+ x = 0;
55
+ }
64
56
 
65
- if (y < 0) {
66
- height += y;
67
- height = height < 0 ? 0 : height;
68
- y = 0;
69
- }
57
+ if (y < 0) {
58
+ height += y;
59
+ height = height < 0 ? 0 : height;
60
+ y = 0;
61
+ }
70
62
 
71
- const imageWidth = image.bitmap.width;
72
- const imageHeight = image.bitmap.height;
73
- if ((x + width) > imageWidth) {
74
- width = imageWidth - x;
75
- }
63
+ const imageWidth = image.bitmap.width;
64
+ const imageHeight = image.bitmap.height;
65
+ if ((x + width) > imageWidth) {
66
+ width = imageWidth - x;
67
+ }
76
68
 
77
- if ((y + height) > imageHeight) {
78
- height = imageHeight - y;
79
- }
69
+ if ((y + height) > imageHeight) {
70
+ height = imageHeight - y;
71
+ }
80
72
 
81
- if (height <= 0 || width <= 0) {
82
- reject(new RectIsOutsideOfImageError('height or width is equal or lower than zero'));
83
- return undefined;
84
- }
73
+ if (height <= 0 || width <= 0) {
74
+ throw new RectIsOutsideOfImageError('height or width is equal or lower than zero');
75
+ }
85
76
 
86
- const cImage = image.crop(x, y, width, height);
87
- cImage.getBase64(Jimp.MIME_PNG, (err, base64) => {
88
- if (err) {
89
- return reject(err);
90
- }
91
- resolve({
92
- elementImage: base64,
93
- });
94
- return undefined;
95
- });
96
- return undefined;
97
- } catch (err) {
98
- return reject(err);
99
- }
100
- });
101
- });
77
+ const cImage = image.crop(x, y, width, height);
78
+ const base64 = await cImage.getBase64Async(Jimp.MIME_PNG);
79
+ return { elementImage: base64 };
102
80
  }
103
81
 
104
-
105
- function chromeStitchImage(fullSize, parts) {
106
- return new Promise((resolve, reject) => {
107
- new Jimp(fullSize.width, fullSize.height, ((err, fullImage) => {
108
- if (err) {
109
- reject(err);
110
- return undefined;
111
- }
112
- return Promise.each(parts, part => new Promise((resolve, reject) => {
113
- const imageRegExMatch = part.image.match(/^data\:[^;]*\;base64,(.*)$/);
114
- Jimp.read(Buffer.from(imageRegExMatch[1], 'base64'), (err, partImage) => {
115
- if (err) {
116
- return reject(err);
117
- }
118
- fullImage.composite(partImage, part.position.left, part.position.top, (err, image) => {
119
- if (err) {
120
- return reject(err);
121
- }
122
- resolve(image);
123
- return undefined;
124
- });
125
- return undefined;
126
- });
127
- })).then(() => {
128
- fullImage.getBase64(Jimp.MIME_PNG, (err, base64) => {
129
- if (err) {
130
- return reject(err);
131
- }
132
- resolve(base64);
133
- return undefined;
134
- });
82
+ async function chromeStitchImage(fullSize, parts) {
83
+ const fullImage = await Jimp.read(fullSize.width, fullSize.height);
84
+
85
+ for (const part of parts) {
86
+ const imageRegExMatch = part.image.match(/^data\:[^;]*\;base64,(.*)$/);
87
+ const partImage = await Jimp.read(Buffer.from(imageRegExMatch[1], 'base64'));
88
+ // From the code or types doesn't seem like `fullImage.composite` has an async Promise API
89
+ await new Promise((resolve, reject) => {
90
+ fullImage.composite(partImage, part.position.left, part.position.top, (err) => {
91
+ if (err) {
92
+ reject(err);
93
+ return;
94
+ }
95
+ resolve();
135
96
  });
136
- }));
137
- });
97
+ });
98
+ }
99
+ const base64 = await fullImage.getBase64Async(Jimp.MIME_PNG);
100
+ return base64;
138
101
  }
139
102
 
140
103
  function stitchImage(fullSize, parts) {
141
104
  return chromeStitchImage(fullSize, parts);
142
105
  }
143
106
 
107
+ // ???????????
144
108
  function uploadDataUrl() {
145
109
  return Promise.resolve();
146
110
  }
@@ -175,94 +139,89 @@ function getElementAbsoluteRectangle(elementRect, pixelRatio) {
175
139
  };
176
140
  }
177
141
 
178
- ImageCaptureUtils.prototype = {
179
- takeViewPortImage() {
180
- return this.screenshotUtils.takeScreenshot()
181
- .then((imageInfo) => ((typeof imageInfo === 'string') ? imageInfo : imageInfo.image));
182
- },
142
+ class ImageCaptureUtils {
143
+ /**
144
+ * @param {unknown} _unusedId
145
+ * @param {import('./windowUtils')} windowUtils
146
+ * @param {import('./screenshotUtils')} screenshotUtils
147
+ * */
148
+ constructor(_unusedId, windowUtils, screenshotUtils) {
149
+ this.windowUtils = windowUtils;
150
+ this.screenshotUtils = screenshotUtils;
151
+ }
152
+
153
+ async takeViewPortImage() {
154
+ const imageInfo = await this.screenshotUtils.takeScreenshot();
155
+ return ((typeof imageInfo === 'string') ? imageInfo : imageInfo.image);
156
+ }
183
157
 
184
158
  takeImageForComparison() {
185
159
  return this.takeViewPortImage();
186
- },
160
+ }
187
161
 
188
- takeAreaDataUrl(areas, format) {
162
+ async takeAreaDataUrl(areas, format) {
189
163
  // Future changes in clickim will pass parameters to this function as a single object
190
164
  if (areas.areas) {
191
165
  areas = areas.areas;
192
166
  }
193
167
 
194
- return this.screenshotUtils.takeScreenshot(format)
195
- .then((imageInfo) => cropImageFromImageData(areas, imageInfo).then((result) => {
196
- result.screenImage = imageInfo.image;
197
- result.absoluteScreenHighlight = getElementAbsoluteRectangle(
198
- areas.elementRect,
199
- imageInfo.devicePixelRatio);
200
- return Promise.resolve(result);
201
- }));
202
- },
168
+ const imageInfo = await this.screenshotUtils.takeScreenshot(format);
169
+ const result = await cropImageFromImageData(areas, imageInfo);
170
+ result.screenImage = imageInfo.image;
171
+ result.absoluteScreenHighlight = getElementAbsoluteRectangle(areas.elementRect, imageInfo.devicePixelRatio);
172
+ return result;
173
+ }
203
174
 
204
- takeArea(areas) {
175
+ async takeArea(areas) {
205
176
  // Future changes in clickim will pass parameters to this function as a single object
206
177
  if (areas.areas) {
207
178
  areas = areas.areas;
208
179
  }
209
180
 
210
- return this.screenshotUtils.takeScreenshot()
211
- .then((imageInfo) => {
212
- const result = {};
213
- result.screenImage = imageInfo.image;
214
- result.absoluteScreenHighlight = getElementAbsoluteRectangle(
215
- areas.elementRect,
216
- imageInfo.devicePixelRatio);
217
- return Promise.resolve(result);
218
- }).then(uploadAllDataUrls);
219
- },
181
+ const imageInfo = await this.screenshotUtils.takeScreenshot();
182
+ return uploadAllDataUrls({
183
+ screenImage: imageInfo.image,
184
+ absoluteScreenHighlight: getElementAbsoluteRectangle(areas.elementRect, imageInfo.devicePixelRatio),
185
+ });
186
+ }
220
187
 
221
188
  forcePixelRatio(forceRatio) {
222
189
  return this.screenshotUtils.forcePixelRatio(forceRatio);
223
- },
190
+ }
224
191
 
225
192
  getCurrentDevicePixelRatio() {
226
193
  return this.screenshotUtils.getCurrentDevicePixelRatio();
227
- },
194
+ }
228
195
 
229
- takeStitchedDataUrl(useImprovedScreenshotStitching) {
230
- const windowUtil = this.windowUtils;
231
- const getCurrentScrollPosition = windowUtil.getCurrentScrollPosition.bind(windowUtil);
196
+ async takeStitchedDataUrl(useImprovedScreenshotStitching) {
197
+ const { windowUtils, screenshotUtils } = this;
232
198
 
233
- const that = this;
234
- const stabilize = () => new Promise(resolve => {
235
- setTimeout(resolve, 250);
236
- });
199
+ const stabilize = () => utils.delay(250);
237
200
  const usingImprovedStitching = Boolean(useImprovedScreenshotStitching);
238
- const scroll = usingImprovedStitching ?
239
- (pos) => windowUtil.scrollToPositionWithoutAnimation.bind(windowUtil)(pos) :
240
- (pos) => windowUtil.scrollToPosition.bind(windowUtil)(pos);
241
-
242
- function createPart(position, crop) {
243
- return scroll(position)
244
- .then(stabilize)
245
- .then(() => that.screenshotUtils.takeScreenshot())
246
- .then(imageInfo => {
247
- const imageData = {
248
- elementRect: crop,
249
- };
250
- return cropImageFromImageData(imageData, imageInfo);
251
- })
252
- .then(cropResult => ({
253
- position: { left: position.x + crop.left, top: position.y + crop.top },
254
- size: { width: crop.width, height: crop.height },
255
- image: cropResult.elementImage,
256
- }));
201
+
202
+ async function createPart(position, crop) {
203
+ if (usingImprovedStitching) {
204
+ await windowUtils.scrollToPositionWithoutAnimation(position);
205
+ } else {
206
+ await windowUtils.scrollToPosition(position);
207
+ }
208
+ await stabilize();
209
+ const imageInfo = await screenshotUtils.takeScreenshot();
210
+ const cropResult = await cropImageFromImageData({ elementRect: crop }, imageInfo);
211
+ return ({
212
+ position: { left: position.x + crop.left, top: position.y + crop.top },
213
+ size: { width: crop.width, height: crop.height },
214
+ image: cropResult.elementImage,
215
+ });
257
216
  }
258
217
 
259
- function takeAllParts(positionsData) {
260
- const takeAllPartsPromises = positionsData.reduce(function (allParts, nextPos) {
261
- const lastPart = allParts[allParts.length - 1];
262
- allParts.push(lastPart.then(createPart.bind(this, nextPos.scrollPos, nextPos.cropData)));
263
- return allParts;
264
- }, [Promise.resolve()]);
265
- return Promise.all(takeAllPartsPromises);
218
+ async function takeAllParts(positionsData) {
219
+ const allParts = [];
220
+ for (const nextPos of positionsData) {
221
+ const part = await createPart(nextPos.scrollPos, nextPos.cropData);
222
+ allParts.push(part);
223
+ }
224
+ return allParts;
266
225
  }
267
226
 
268
227
  function getPartsPositions(fullPageSize, viewPortSize) {
@@ -270,37 +229,39 @@ ImageCaptureUtils.prototype = {
270
229
  const VPW = viewPortSize.width;
271
230
  const FPH = Math.max(fullPageSize.height, viewPortSize.height);
272
231
  const VPH = viewPortSize.height;
273
- const Ws = (Array.apply(null, new Array(Math.ceil(FPW / VPW)))).map((_, i) => ({
232
+ const Ws = Array.from({ length: Math.ceil(FPW / VPW) }, (__, i) => ({
274
233
  scrollX: Math.min(i * VPW, FPW - VPW),
275
234
  cropX: i * VPW - Math.min(i * VPW, FPW - VPW),
276
235
  cropW: VPW - (i * VPW - Math.min(i * VPW, FPW - VPW)),
277
236
  }));
278
- const Hs = (Array.apply(null, new Array(Math.ceil(FPH / VPH)))).map((_, i) => ({
237
+ const Hs = Array.from({ length: Math.ceil(FPH / VPH) }, (__, i) => ({
279
238
  scrollY: Math.min(i * VPH, FPH - VPH),
280
239
  cropY: i * VPH - Math.min(i * VPH, FPH - VPH),
281
240
  cropH: VPH - (i * VPH - Math.min(i * VPH, FPH - VPH)),
282
241
  }));
283
- const positions = Ws.reduce((posList, w) => posList.concat(Hs.map((h) => ({
242
+ return Ws.flatMap(w => Hs.map(h => ({
284
243
  scrollPos: { x: w.scrollX, y: h.scrollY },
285
244
  cropData: {
286
- top: h.cropY, left: w.cropX, width: w.cropW, height: h.cropH,
245
+ top: h.cropY,
246
+ left: w.cropX,
247
+ width: w.cropW,
248
+ height: h.cropH,
287
249
  },
288
- }))), []);
289
- return positions;
250
+ }))
251
+ );
290
252
  }
291
253
 
292
254
  async function createStitchImage(fullPageSize, viewPortSize) {
293
- const originalPosition = await getCurrentScrollPosition();
255
+ const originalPosition = await windowUtils.getCurrentScrollPosition();
294
256
  const positions = getPartsPositions(fullPageSize, viewPortSize);
295
257
  const parts = await takeAllParts(positions);
296
- await windowUtil.scrollToPosition(originalPosition);
297
- parts.shift();
258
+ await windowUtils.scrollToPosition(originalPosition);
298
259
  return stitchImage(fullPageSize, parts);
299
260
  }
300
261
 
301
- return Promise.all([windowUtil.getFullPageSize(), windowUtil.getViewportSize()])
302
- .then(([fullPageSize, viewPortSize]) => createStitchImage(fullPageSize, viewPortSize));
303
- },
304
- };
262
+ const [fullPageSize, viewPortSize] = await Promise.all([windowUtils.getFullPageSize(), windowUtils.getViewportSize()]);
263
+ return await createStitchImage(fullPageSize, viewPortSize);
264
+ }
265
+ }
305
266
 
306
267
  module.exports = ImageCaptureUtils;
@@ -1,9 +1,15 @@
1
+ // @ts-check
2
+
1
3
  'use strict';
2
4
 
3
5
  const Promise = require('bluebird');
4
6
  const pRetry = require('p-retry');
5
7
 
6
8
  class ScreenshotUtils {
9
+ /**
10
+ * @param {any} tabId
11
+ * @param {import('../webdriver')} driver
12
+ */
7
13
  constructor(tabId, driver, options = { takeScreenshots: true }) {
8
14
  this.tabId = tabId;
9
15
  this.driver = driver;
@@ -21,23 +27,21 @@ class ScreenshotUtils {
21
27
  return this.options.takeScreenshots;
22
28
  }
23
29
 
24
- takeScreenshot() {
30
+ async takeScreenshot() {
25
31
  if (!this.shouldTakeScreenshots()) {
26
- return Promise.resolve({ devicePixelRatio: 1, image: '' });
32
+ return { devicePixelRatio: 1, image: '' };
27
33
  }
28
34
  const MAX_RETRY_COUNT = 3;
29
35
  const SCREENSHOT_RETRY_DELAY = 2000;
30
36
  const devicePixelRatioPromise = this.currentDevicePixelRatio ? Promise.resolve(this.currentDevicePixelRatio) : this.getDevicePixelRatio();
31
37
  const getScreenshot = () => Promise.all([devicePixelRatioPromise, this.driver.takeScreenshot()]);
32
- return pRetry(getScreenshot, { retries: MAX_RETRY_COUNT, minTimeout: SCREENSHOT_RETRY_DELAY })
33
- .then(([devicePixelRatio, image]) => {
34
- const base64 = image ? image.value : '';
35
- const dataUrl = `data:image/png;base64,${this.base64AddPadding(base64.replace(/[\r\n]/g, ''))}`;
36
- return {
37
- image: dataUrl,
38
- devicePixelRatio,
39
- };
40
- });
38
+ const [devicePixelRatio, image] = await pRetry(getScreenshot, { retries: MAX_RETRY_COUNT, minTimeout: SCREENSHOT_RETRY_DELAY });
39
+ const base64 = image ? image.value : '';
40
+ const dataUrl = `data:image/png;base64,${this.base64AddPadding(base64.replace(/[\r\n]/g, ''))}`;
41
+ return {
42
+ image: dataUrl,
43
+ devicePixelRatio,
44
+ };
41
45
  }
42
46
 
43
47
  getDevicePixelRatio() {
@@ -49,7 +53,7 @@ class ScreenshotUtils {
49
53
  }
50
54
  }
51
55
 
52
- return this.driver.executeJS(devicePixelRatioJS).then(result => Promise.resolve(result.value));
56
+ return this.driver.executeJS(devicePixelRatioJS).then(result => result.value);
53
57
  }
54
58
 
55
59
  forcePixelRatio(forceRatio = 1) {
@@ -63,4 +67,3 @@ class ScreenshotUtils {
63
67
  }
64
68
 
65
69
  module.exports = ScreenshotUtils;
66
-
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  'use strict';
2
4
 
3
5
  const Promise = require('bluebird');
@@ -7,6 +9,10 @@ const { PageNotAvailableError } = require('../../errors');
7
9
  const logger = require('../../commons/logger').getLogger('window-utils');
8
10
 
9
11
  class WindowUtils {
12
+ /**
13
+ * @param {number} id tab or worker id
14
+ * @param {import('../webdriver')} driver
15
+ * */
10
16
  constructor(id, driver) {
11
17
  this.id = id;
12
18
  this.driver = driver;
@@ -131,12 +137,12 @@ class WindowUtils {
131
137
  });
132
138
  }
133
139
 
134
- setViewportSize(size) {
135
- return this.driver.setViewportSize(size.width, size.height)
136
- .then(() => this.checkSize(size));
140
+ async setViewportSize(size) {
141
+ await this.driver.setViewportSize(size.width, size.height);
142
+ return await this.checkSize(size);
137
143
  }
138
144
 
139
- validatePageIsAvailable() {
145
+ async validatePageIsAvailable() {
140
146
  /* eslint-disable */
141
147
  function pageIsAvailable() {
142
148
  var locationObj;
@@ -153,7 +159,8 @@ class WindowUtils {
153
159
  }
154
160
  /* eslint-enable */
155
161
 
156
- return this.driver.executeJS(pageIsAvailable).then(result => (result.value ? Promise.resolve() : Promise.reject(new PageNotAvailableError())));
162
+ const result = await this.driver.executeJS(pageIsAvailable);
163
+ return await (result.value ? Promise.resolve() : Promise.reject(new PageNotAvailableError()));
157
164
  }
158
165
 
159
166
  focusTab() {
@@ -164,9 +171,14 @@ class WindowUtils {
164
171
  return undefined;
165
172
  }
166
173
 
167
- getOsAndBrowser() {
168
- return pRetry(() => this.driver.getBrowserAndOS(), { retries: 3 })
169
- .then(osAndBrowser => ({ uaBrowserName: osAndBrowser.browser, uaOs: osAndBrowser.os, userAgent: osAndBrowser.userAgent, browserVersion: osAndBrowser.browserVersion }));
174
+ async getOsAndBrowser() {
175
+ const osAndBrowser = await pRetry(() => this.driver.getBrowserAndOS(), { retries: 3 });
176
+ return {
177
+ uaBrowserName: osAndBrowser.browser,
178
+ uaOs: osAndBrowser.os,
179
+ userAgent: osAndBrowser.userAgent,
180
+ browserVersion: osAndBrowser.browserVersion,
181
+ };
170
182
  }
171
183
 
172
184
  getUserAgentInfo() {