@eyeo/get-browser-binary 0.7.0 → 0.9.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.
package/.gitlab-ci.yml CHANGED
@@ -32,12 +32,10 @@ test:browsers:linux:
32
32
  image: docker:20.10.16
33
33
  services:
34
34
  - docker:20.10.16-dind
35
- variables:
36
- DOCKER_DRIVER: overlay2
37
35
  before_script:
38
36
  - docker build -f test/docker/Dockerfile -t browsers .
39
37
  script:
40
- - docker run --shm-size=256m browsers
38
+ - docker run --shm-size=512m -t browsers
41
39
 
42
40
  test:browsers:windows:
43
41
  stage: test
package/.mocharc.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "timeout": 50000
2
+ "timeout": 80000
3
3
  }
package/README.md CHANGED
@@ -88,14 +88,14 @@ Useful to reproduce the CI environment of the `test:browsers:linux` job:
88
88
 
89
89
  ```shell
90
90
  docker build -f test/docker/Dockerfile -t browsers .
91
- docker run --shm-size=256m -it browsers
91
+ docker run --shm-size=512m -it browsers
92
92
  ```
93
93
 
94
94
  The `grep` and `timeout` options can also be used on Docker via the `TEST_ARGS`
95
95
  parameter:
96
96
 
97
97
  ```shell
98
- docker run --shm-size=256m -e TEST_ARGS="--grep chromium.*latest --timeout 100000" -it browsers
98
+ docker run --shm-size=512m -e TEST_ARGS="--grep chromium.*latest --timeout 100000" -it browsers
99
99
  ```
100
100
 
101
101
  By default, tests delete the `./browser-snapshots` before each `Browser` suite
package/RELEASE_NOTES.md CHANGED
@@ -1,3 +1,31 @@
1
+ # 0.9.0
2
+
3
+ - Fixed a Chromium install issue by increasing the value of
4
+ `MAX_VERSION_DECREMENTS` (!52)
5
+ - `installBrowser()`, `getDriver()` and `download()` have a new optional timeout
6
+ parameter on file downloads (!51)
7
+
8
+ ### Testing
9
+
10
+ - Added a test checking that downloaded browser-snapshot files are actually
11
+ cached (#35)
12
+ - Tests running the minimum firefox version showed occasional failures. That was
13
+ fixed by increasing the shared memory size of the docker image (!50)
14
+
15
+ # 0.8.0
16
+
17
+ - Move `takeFullPageScreenshot` to the utils module (#38)
18
+ - Add keywords to package.json (#36)
19
+
20
+ ### Testing
21
+
22
+ - Log the running browser version on test suites (#37)
23
+
24
+ ### Notes for integrators
25
+
26
+ - `takeFullPageScreenshot()` has been moved from the browsers module to the
27
+ utils module (!46).
28
+
1
29
  # 0.7.0
2
30
 
3
31
  - Implement takeFullPageScreenshot (#32)
package/index.js CHANGED
@@ -16,4 +16,4 @@
16
16
  */
17
17
 
18
18
  export * from "./src/browsers.js";
19
- export {download} from "./src/utils.js";
19
+ export {download, takeFullPageScreenshot} from "./src/utils.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eyeo/get-browser-binary",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Install browser binaries and matching webdrivers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,6 +14,19 @@
14
14
  },
15
15
  "type": "module",
16
16
  "main": "index.js",
17
+ "keywords": [
18
+ "browser",
19
+ "chromium",
20
+ "chrome",
21
+ "firefox",
22
+ "msedge",
23
+ "chromedriver",
24
+ "geckodriver",
25
+ "msedgedriver",
26
+ "selenium",
27
+ "webdriver",
28
+ "selenium-webdriver"
29
+ ],
17
30
  "dependencies": {
18
31
  "dmg": "^0.1.0",
19
32
  "extract-zip": "^2.0.1",
package/src/browsers.js CHANGED
@@ -27,7 +27,6 @@ import firefox from "selenium-webdriver/firefox.js";
27
27
  import edge from "selenium-webdriver/edge.js";
28
28
  import command from "selenium-webdriver/lib/command.js";
29
29
  import extractZip from "extract-zip";
30
- import Jimp from "jimp";
31
30
 
32
31
  import {download, extractTar, extractDmg, killDriverProcess, wait}
33
32
  from "./utils.js";
@@ -83,11 +82,14 @@ class Browser {
83
82
  * subclasses.
84
83
  * @param {string} version - Either full version number or channel/release.
85
84
  * Please find examples on the subclasses.
85
+ * @param {number?} downloadTimeout - Allowed time in ms for the download of
86
+ * install files to complete. When set to 0 there is no time limit. Defaults
87
+ * to 0.
86
88
  * @return {BrowserBinary}
87
89
  * @throws {Error} Unsupported browser version, Unsupported platform, Browser
88
90
  * download failed.
89
91
  */
90
- static async installBrowser(version) {
92
+ static async installBrowser(version, downloadTimeout = 0) {
91
93
  // to be implemented by the subclass
92
94
  }
93
95
 
@@ -135,11 +137,14 @@ class Browser {
135
137
  * @param {string} version - Either full version number or channel/release.
136
138
  * Please find examples on the subclasses.
137
139
  * @param {driverOptions?} options - Options to start the browser with.
140
+ * @param {number?} downloadTimeout - Allowed time in ms for the download of
141
+ * browser install files to complete. When set to 0 there is no time limit.
142
+ * Defaults to 0.
138
143
  * @return {webdriver}
139
144
  * @throws {Error} Unsupported browser version, Unsupported platform, Browser
140
145
  * download failed, Driver download failed, Unable to start driver.
141
146
  */
142
- static async getDriver(version, options = {}) {
147
+ static async getDriver(version, options = {}, downloadTimeout = 0) {
143
148
  // to be implemented by the subclass
144
149
  }
145
150
 
@@ -158,66 +163,6 @@ class Browser {
158
163
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1729315
159
164
  // That is done through the UI, to be implemented by the subclass
160
165
  }
161
-
162
- /**
163
- * @typedef {Object} Jimp
164
- * @see https://github.com/oliver-moran/jimp/tree/master/packages/jimp
165
- */
166
-
167
- /**
168
- * Takes a screenshot of the full page by scrolling from top to bottom.
169
- * @param {webdriver} driver - The driver controlling the browser.
170
- * @property {boolean} hideScrollbars=true - Hides any scrollbars before
171
- * taking the screenshot, or not.
172
- * @return {Jimp} A Jimp image object containing the screenshot.
173
- * @example
174
- * // Getting a base-64 encoded PNG from the returned screenshot
175
- * let image = await Browser.takeFullPageScreenshot(driver);
176
- * let encodedPNG = await image.getBase64Async("image/png");
177
- */
178
- static async takeFullPageScreenshot(driver, hideScrollbars = true) {
179
- // On macOS scrollbars appear and disappear overlapping the content as
180
- // scrolling occurs. Hiding the scrollbars helps getting reproducible
181
- // screenshots.
182
- if (hideScrollbars) {
183
- await driver.executeScript(() => {
184
- if (!document.head)
185
- return;
186
-
187
- let style = document.createElement("style");
188
- style.textContent = "html { overflow-y: scroll; }";
189
- document.head.appendChild(style);
190
- if (document.documentElement.clientWidth == window.innerWidth)
191
- style.textContent = "html::-webkit-scrollbar { display: none; }";
192
- else
193
- document.head.removeChild(style);
194
- });
195
- }
196
-
197
- let fullScreenshot = new Jimp(0, 0);
198
- while (true) {
199
- let [width, height, offset] = await driver.executeScript((...args) => {
200
- window.scrollTo(0, args[0]);
201
- // Math.ceil rounds up potential decimal values on window.scrollY,
202
- // ensuring the loop will not hang due to never reaching enough
203
- // fullScreenshot's height.
204
- return [document.documentElement.clientWidth,
205
- document.documentElement.scrollHeight,
206
- Math.ceil(window.scrollY)];
207
- }, fullScreenshot.bitmap.height);
208
- let data = await driver.takeScreenshot();
209
- let partialScreenshot = await Jimp.read(Buffer.from(data, "base64"));
210
- let combinedScreenshot =
211
- new Jimp(width, offset + partialScreenshot.bitmap.height);
212
- combinedScreenshot.composite(fullScreenshot, 0, 0);
213
- combinedScreenshot.composite(partialScreenshot, 0, offset);
214
- fullScreenshot = combinedScreenshot;
215
-
216
- if (fullScreenshot.bitmap.height >= height)
217
- break;
218
- }
219
- return fullScreenshot;
220
- }
221
166
  }
222
167
 
223
168
  /**
@@ -269,13 +214,16 @@ class Chromium extends Browser {
269
214
  * {@link snapshotsBaseDir} folder, ready to go.
270
215
  * @param {string} version - Either "latest", "beta", "dev" or a full version
271
216
  * number (i.e. "77.0.3865.0"). Defaults to "latest".
217
+ * @param {number?} downloadTimeout - Allowed time in ms for the download of
218
+ * install files to complete. When set to 0 there is no time limit. Defaults
219
+ * to 0.
272
220
  * @return {BrowserBinary}
273
221
  * @throws {Error} Unsupported browser version, Unsupported platform, Browser
274
222
  * download failed.
275
223
  */
276
- static async installBrowser(version = "latest") {
224
+ static async installBrowser(version = "latest", downloadTimeout = 0) {
277
225
  const MIN_VERSION = 75;
278
- const MAX_VERSION_DECREMENTS = 80;
226
+ const MAX_VERSION_DECREMENTS = 200;
279
227
 
280
228
  checkVersion(version, MIN_VERSION, Chromium.#CHANNELS);
281
229
  let versionNumber = await Chromium.#getVersionForChannel(version);
@@ -309,23 +257,27 @@ class Chromium extends Browser {
309
257
  await fs.promises.mkdir(path.dirname(browserDir), {recursive: true});
310
258
 
311
259
  archive = path.join(snapshotsDir, "cache", `${base}-${fileName}`);
260
+ let url = `https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/${platformDir}%2F${base}%2F${fileName}?alt=media`;
312
261
  try {
313
262
  try {
314
263
  await fs.promises.access(archive);
315
264
  }
316
265
  catch (e) {
317
- await download(
318
- `https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/${platformDir}%2F${base}%2F${fileName}?alt=media`,
319
- archive);
266
+ await download(url, archive, downloadTimeout);
320
267
  }
321
268
  break;
322
269
  }
323
- catch (e) {
324
- // Chromium advises decrementing the branch_base_position when no
325
- // matching build was found. See https://www.chromium.org/getting-involved/download-chromium
326
- base--;
327
- if (base <= startBase - MAX_VERSION_DECREMENTS)
328
- throw new Error(`${BROWSER_DOWNLOAD_ERROR}: Chromium base ${startBase}`);
270
+ catch (err) {
271
+ if (err.name == "HTTPError") {
272
+ // Chromium advises decrementing the branch_base_position when no
273
+ // matching build was found. See https://www.chromium.org/getting-involved/download-chromium
274
+ base--;
275
+ if (base <= startBase - MAX_VERSION_DECREMENTS)
276
+ throw new Error(`${BROWSER_DOWNLOAD_ERROR}: Chromium base ${startBase}`);
277
+ }
278
+ else {
279
+ throw new Error(`${BROWSER_DOWNLOAD_ERROR}: ${url}\n${err}`);
280
+ }
329
281
  }
330
282
  }
331
283
  await extractZip(archive, {dir: browserDir});
@@ -363,10 +315,7 @@ class Chromium extends Browser {
363
315
 
364
316
  await killDriverProcess("chromedriver");
365
317
  let driverPath = path.join(cacheDir, zip.split(".")[0], driverBinary);
366
- try {
367
- await fs.promises.rm(driverPath, {recursive: true});
368
- }
369
- catch (e) {} // file does not exist
318
+ await fs.promises.rm(driverPath, {force: true});
370
319
  await extractZip(archive, {dir: cacheDir});
371
320
 
372
321
  return driverPath;
@@ -376,8 +325,9 @@ class Chromium extends Browser {
376
325
  static async getDriver(version = "latest", {
377
326
  headless = true, extensionPaths = [], incognito = false, insecure = false,
378
327
  extraArgs = []
379
- } = {}) {
380
- let {binary, versionNumber, base} = await Chromium.installBrowser(version);
328
+ } = {}, downloadTimeout = 0) {
329
+ let {binary, versionNumber, base} =
330
+ await Chromium.installBrowser(version, downloadTimeout);
381
331
  let driverPath = await Chromium.#installDriver(base, versionNumber);
382
332
  let serviceBuilder = new chrome.ServiceBuilder(driverPath);
383
333
  let options = new chrome.Options().addArguments("no-sandbox", ...extraArgs);
@@ -475,11 +425,14 @@ class Firefox extends Browser {
475
425
  * {@link snapshotsBaseDir} folder, ready to go.
476
426
  * @param {string} version - Either "latest", "beta" or a full version
477
427
  * number (i.e. "68.0"). Defaults to "latest".
428
+ * @param {number?} downloadTimeout - Allowed time in ms for the download of
429
+ * install files to complete. When set to 0 there is no time limit. Defaults
430
+ * to 0.
478
431
  * @return {BrowserBinary}
479
432
  * @throws {Error} Unsupported browser version, Unsupported platform, Browser
480
433
  * download failed.
481
434
  */
482
- static async installBrowser(version = "latest") {
435
+ static async installBrowser(version = "latest", downloadTimeout = 0) {
483
436
  const MIN_VERSION = 60;
484
437
 
485
438
  checkVersion(version, MIN_VERSION, Firefox.#CHANNELS);
@@ -511,7 +464,7 @@ class Firefox extends Browser {
511
464
  catch (e) {
512
465
  let url = `https://archive.mozilla.org/pub/firefox/releases/${versionNumber}/${buildPlatform}/en-US/${fileName}`;
513
466
  try {
514
- await download(url, archive);
467
+ await download(url, archive, downloadTimeout);
515
468
  }
516
469
  catch (err) {
517
470
  throw new Error(`${BROWSER_DOWNLOAD_ERROR}: ${url}\n${err}`);
@@ -526,8 +479,8 @@ class Firefox extends Browser {
526
479
  static async getDriver(version = "latest", {
527
480
  headless = true, extensionPaths = [], incognito = false, insecure = false,
528
481
  extraArgs = []
529
- } = {}) {
530
- let {binary} = await Firefox.installBrowser(version);
482
+ } = {}, downloadTimeout = 0) {
483
+ let {binary} = await Firefox.installBrowser(version, downloadTimeout);
531
484
 
532
485
  let options = new firefox.Options();
533
486
  if (headless)
@@ -662,11 +615,14 @@ class Edge extends Browser {
662
615
  * app (not as a system app). Installing Edge on Windows is not supported.
663
616
  * @param {string} version - Either "latest", "beta", "dev" or a full version
664
617
  * number (i.e. "95.0.1020.40"). Defaults to "latest".
618
+ * @param {number?} downloadTimeout - Allowed time in ms for the download of
619
+ * install files to complete. When set to 0 there is no time limit. Defaults
620
+ * to 0.
665
621
  * @return {BrowserBinary}
666
622
  * @throws {Error} Unsupported browser version, Unsupported platform, Browser
667
623
  * download failed.
668
624
  */
669
- static async installBrowser(version = "latest") {
625
+ static async installBrowser(version = "latest", downloadTimeout = 0) {
670
626
  if (platform == "win32")
671
627
  // Edge is mandatory on Windows, can't be uninstalled or downgraded
672
628
  // https://support.microsoft.com/en-us/microsoft-edge/why-can-t-i-uninstall-microsoft-edge-ee150b3b-7d7a-9984-6d83-eb36683d526d
@@ -703,7 +659,7 @@ class Edge extends Browser {
703
659
  catch (e) {}
704
660
 
705
661
  try {
706
- await download(url, archive);
662
+ await download(url, archive, downloadTimeout);
707
663
  }
708
664
  catch (err) {
709
665
  throw new Error(`${BROWSER_DOWNLOAD_ERROR}: ${url}\n${err}`);
@@ -714,10 +670,7 @@ class Edge extends Browser {
714
670
  }
715
671
  else if (platform == "darwin") {
716
672
  let appName = Edge.#getDarwinAppName(channel);
717
- try {
718
- await fs.promises.rm(`${process.env.HOME}/Applications/${appName}.app`, {recursive: true});
719
- }
720
- catch (e) {}
673
+ await fs.promises.rm(`${process.env.HOME}/Applications/${appName}.app`, {force: true, recursive: true});
721
674
  await promisify(exec)(`installer -pkg ${archive} -target CurrentUserHomeDirectory`);
722
675
  }
723
676
 
@@ -734,10 +687,7 @@ class Edge extends Browser {
734
687
  static async #installDriver() {
735
688
  async function extractEdgeZip(archive, cacheDir, driverPath) {
736
689
  await killDriverProcess("msedgedriver");
737
- try {
738
- await fs.promises.rm(driverPath, {recursive: true});
739
- }
740
- catch (e) {} // file does not exist
690
+ await fs.promises.rm(driverPath, {force: true});
741
691
  await extractZip(archive, {dir: cacheDir});
742
692
  }
743
693
 
@@ -788,9 +738,9 @@ class Edge extends Browser {
788
738
  static async getDriver(version = "latest", {
789
739
  headless = true, extensionPaths = [], incognito = false, insecure = false,
790
740
  extraArgs = []
791
- } = {}) {
741
+ } = {}, downloadTimeout = 0) {
792
742
  if (platform == "linux" || platform == "darwin")
793
- await Edge.installBrowser(version);
743
+ await Edge.installBrowser(version, downloadTimeout);
794
744
 
795
745
  let driverPath = await Edge.#installDriver();
796
746
  let serviceBuilder = new edge.ServiceBuilder(driverPath);
package/src/utils.js CHANGED
@@ -23,32 +23,45 @@ import {exec} from "child_process";
23
23
 
24
24
  import got from "got";
25
25
  import dmg from "dmg";
26
+ import Jimp from "jimp";
27
+
26
28
 
27
29
  /**
28
30
  * Downloads url resources.
29
31
  * @param {string} url - The url of the resource to be downloaded.
30
32
  * @param {string} destFile - The destination file path.
31
- * @throws {TypeError} Invalid URL.
33
+ * @param {number?} timeout - Allowed time in ms for the download to complete.
34
+ * When set to 0 there is no time limit. Defaults to 0.
35
+ * @throws {TypeError} Invalid URL, Download timeout.
32
36
  */
33
- export async function download(url, destFile) {
37
+ export async function download(url, destFile, timeout = 0) {
34
38
  let cacheDir = path.dirname(destFile);
35
-
36
39
  await fs.promises.mkdir(cacheDir, {recursive: true});
37
40
 
38
41
  let tempDest = `${destFile}-${process.pid}`;
39
42
  let writable = fs.createWriteStream(tempDest);
40
-
43
+ let timeoutID;
41
44
  try {
42
- await promisify(pipeline)(got.stream(url), writable);
45
+ let downloading = promisify(pipeline)(got.stream(url), writable);
46
+ if (timeout == 0) {
47
+ await downloading;
48
+ }
49
+ else {
50
+ let timeoutPromise = new Promise((resolve, reject) => {
51
+ timeoutID = setTimeout(
52
+ () => reject(`Download timeout after ${timeout}ms`), timeout);
53
+ });
54
+ await Promise.race([downloading, timeoutPromise]);
55
+ }
43
56
  }
44
57
  catch (error) {
45
- try {
46
- await fs.promises.rm(tempDest, {recursive: true});
47
- }
48
- catch (e) {}
49
-
58
+ await fs.promises.rm(tempDest, {force: true});
50
59
  throw error;
51
60
  }
61
+ finally {
62
+ if (timeoutID)
63
+ clearTimeout(timeoutID);
64
+ }
52
65
 
53
66
  await fs.promises.rename(tempDest, destFile);
54
67
  }
@@ -143,3 +156,62 @@ export function wait(condition, timeout = 0, message, pollTimeout = 100) {
143
156
 
144
157
  return result;
145
158
  }
159
+
160
+ /**
161
+ * @typedef {Object} Jimp
162
+ * @see https://github.com/oliver-moran/jimp/tree/master/packages/jimp
163
+ */
164
+
165
+ /**
166
+ * Takes a screenshot of the full page by scrolling from top to bottom.
167
+ * @param {webdriver} driver - The driver controlling the browser.
168
+ * @property {boolean} hideScrollbars=true - Hides any scrollbars before
169
+ * taking the screenshot, or not.
170
+ * @return {Jimp} A Jimp image object containing the screenshot.
171
+ * @example
172
+ * // Getting a base-64 encoded PNG from the returned Jimp image
173
+ * let image = await takeFullPageScreenshot(driver);
174
+ * let encodedPNG = await image.getBase64Async("image/png");
175
+ */
176
+ export async function takeFullPageScreenshot(driver, hideScrollbars = true) {
177
+ // On macOS scrollbars appear and disappear overlapping the content as
178
+ // scrolling occurs. Hiding the scrollbars helps getting reproducible
179
+ // screenshots.
180
+ if (hideScrollbars) {
181
+ await driver.executeScript(() => {
182
+ if (!document.head)
183
+ return;
184
+ let style = document.createElement("style");
185
+ style.textContent = "html { overflow-y: scroll; }";
186
+ document.head.appendChild(style);
187
+ if (document.documentElement.clientWidth == window.innerWidth)
188
+ style.textContent = "html::-webkit-scrollbar { display: none; }";
189
+ else
190
+ document.head.removeChild(style);
191
+ });
192
+ }
193
+
194
+ let fullScreenshot = new Jimp(0, 0);
195
+ while (true) {
196
+ let [width, height, offset] = await driver.executeScript((...args) => {
197
+ window.scrollTo(0, args[0]);
198
+ // Math.ceil rounds up potential decimal values on window.scrollY,
199
+ // ensuring the loop will not hang due to never reaching enough
200
+ // fullScreenshot's height.
201
+ return [document.documentElement.clientWidth,
202
+ document.documentElement.scrollHeight,
203
+ Math.ceil(window.scrollY)];
204
+ }, fullScreenshot.bitmap.height);
205
+ let data = await driver.takeScreenshot();
206
+ let partialScreenshot = await Jimp.read(Buffer.from(data, "base64"));
207
+ let combinedScreenshot =
208
+ new Jimp(width, offset + partialScreenshot.bitmap.height);
209
+ combinedScreenshot.composite(fullScreenshot, 0, 0);
210
+ combinedScreenshot.composite(partialScreenshot, 0, offset);
211
+ fullScreenshot = combinedScreenshot;
212
+ if (fullScreenshot.bitmap.height >= height)
213
+ break;
214
+ }
215
+
216
+ return fullScreenshot;
217
+ }
package/test/browsers.js CHANGED
@@ -19,7 +19,7 @@ import fs from "fs";
19
19
  import expect from "expect";
20
20
  import path from "path";
21
21
 
22
- import {BROWSERS, snapshotsBaseDir} from "../index.js";
22
+ import {BROWSERS, snapshotsBaseDir, takeFullPageScreenshot} from "../index.js";
23
23
  import {killDriverProcess} from "../src/utils.js";
24
24
  import Jimp from "jimp";
25
25
 
@@ -104,21 +104,47 @@ function getExtension(browser, version) {
104
104
  return {extensionPaths, manifest};
105
105
  }
106
106
 
107
+ async function getCachedTimes(browser) {
108
+ let cacheDir = path.join(snapshotsBaseDir, browser, "cache");
109
+ let cacheFiles = await fs.promises.readdir(cacheDir);
110
+
111
+ let browserCacheTime = null;
112
+ // Edge gets installed at OS level, that's why it's not tested here
113
+ if (browser != "edge") {
114
+ let installTypes = [".zip", ".dmg", ".bz2"];
115
+ let browserZip =
116
+ cacheFiles.find(elem => installTypes.some(type => elem.includes(type)));
117
+ let browserStat = await fs.promises.stat(path.join(cacheDir, browserZip));
118
+ browserCacheTime = browserStat.ctimeMs;
119
+ }
120
+
121
+ let driverCacheTime = null;
122
+ // Firefox install file includes the driver, that's why it's not tested here
123
+ if (browser != "firefox") {
124
+ let driverDir = cacheFiles.find(elem => !elem.endsWith(".zip"));
125
+ let driverFiles = await fs.promises.readdir(path.join(cacheDir, driverDir));
126
+ let driverZip = driverFiles.find(elem => elem.endsWith(".zip"));
127
+ let driverStat =
128
+ await fs.promises.stat(path.join(cacheDir, driverDir, driverZip));
129
+ driverCacheTime = driverStat.ctimeMs;
130
+ }
131
+
132
+ return {browserCTime: browserCacheTime, driverCTime: driverCacheTime};
133
+ }
134
+
107
135
  for (let browser of Object.keys(BROWSERS)) {
108
136
  describe(`Browser: ${browser}`, () => {
109
137
  before(async() => {
110
138
  if (process.env.TEST_KEEP_SNAPSHOTS == "true")
111
139
  return;
112
140
 
113
- try {
114
- await fs.promises.rm(snapshotsBaseDir, {recursive: true});
115
- }
116
- catch (e) {}
141
+ await fs.promises.rm(snapshotsBaseDir, {force: true, recursive: true});
117
142
  });
118
143
 
119
144
  for (let version of VERSIONS[browser]) {
120
145
  describe(`Version: ${version}`, () => {
121
146
  let driver = null;
147
+ let emptyCacheTimes = null;
122
148
 
123
149
  async function quitDriver() {
124
150
  if (!driver)
@@ -140,7 +166,7 @@ for (let browser of Object.keys(BROWSERS)) {
140
166
  this.skip();
141
167
 
142
168
  let {binary, versionNumber} =
143
- await BROWSERS[browser].installBrowser(version);
169
+ await BROWSERS[browser].installBrowser(version, 60000);
144
170
  let browserName = browser == "edge" ? /(edge|Edge)/ : browser;
145
171
  expect(binary).toEqual(expect.stringMatching(browserName));
146
172
 
@@ -148,6 +174,9 @@ for (let browser of Object.keys(BROWSERS)) {
148
174
  await BROWSERS[browser].getInstalledVersion(binary);
149
175
  expect(installedVersion).toEqual(
150
176
  expect.stringContaining(normalize(versionNumber)));
177
+
178
+ // Adding the version number to the test title for logging purposes
179
+ this.test.title = `${this.test.title} [v${versionNumber}]`;
151
180
  });
152
181
 
153
182
  it("runs", async() => {
@@ -162,6 +191,23 @@ for (let browser of Object.keys(BROWSERS)) {
162
191
 
163
192
  expect((await driver.getCapabilities()).getBrowserName())
164
193
  .toEqual(expect.stringMatching(names[browser]));
194
+
195
+ // When running all tests, this saves time on the cache test
196
+ emptyCacheTimes = await getCachedTimes(browser);
197
+ });
198
+
199
+ it("uses cached install files", async() => {
200
+ if (emptyCacheTimes == null) { // Single test case run
201
+ driver = await BROWSERS[browser].getDriver(version);
202
+ emptyCacheTimes = await getCachedTimes(browser);
203
+ await quitDriver();
204
+ }
205
+
206
+ // assigning `driver` to allow the afterEach hook quit the driver
207
+ driver = await BROWSERS[browser].getDriver(version);
208
+ let existingCacheTimes = await getCachedTimes(browser);
209
+
210
+ expect(existingCacheTimes).toEqual(emptyCacheTimes);
165
211
  });
166
212
 
167
213
  it("supports extra args", async() => {
@@ -186,7 +232,7 @@ for (let browser of Object.keys(BROWSERS)) {
186
232
  driver = await BROWSERS[browser].getDriver(version);
187
233
  await driver.navigate().to(TEST_URL_LONG_PAGE);
188
234
 
189
- let fullImg = await BROWSERS[browser].takeFullPageScreenshot(driver);
235
+ let fullImg = await takeFullPageScreenshot(driver);
190
236
  // Taking a regular webdriver screenshot, which should be shorter
191
237
  let data = await driver.takeScreenshot();
192
238
  let partImg = await Jimp.read(Buffer.from(data, "base64"));
package/test/utils.js CHANGED
@@ -25,18 +25,34 @@ describe("Utils", () => {
25
25
  it("defines a browser snapshots folder", () => expect(snapshotsBaseDir)
26
26
  .toBe(path.join(process.cwd(), "browser-snapshots")));
27
27
 
28
+ let resourceUrl = "https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/raw/main/package.json";
28
29
  let destFile = path.join(snapshotsBaseDir, "download-test.txt");
30
+ let expectedFileContents = "\"name\": \"@eyeo/get-browser-binary\"";
29
31
 
30
32
  it("downloads resources", async() => {
31
- let url = "https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/raw/main/package.json";
32
- await download(url, destFile);
33
+ await fs.promises.rm(destFile, {force: true});
34
+ await download(resourceUrl, destFile);
33
35
  let contents = await fs.promises.readFile(destFile, {encoding: "utf8"});
34
- expect(contents).toEqual(
35
- expect.stringContaining("\"name\": \"@eyeo/get-browser-binary\""));
36
+ expect(contents).toEqual(expect.stringContaining(expectedFileContents));
36
37
  });
37
38
 
38
- it("does not download invalid resources", async() => {
39
- expect(download("invalid", destFile)).rejects
40
- .toThrow("TypeError [ERR_INVALID_URL]: Invalid URL");
39
+ it("downloads resources on long timeout", async() => {
40
+ await fs.promises.rm(destFile, {force: true});
41
+ await download(resourceUrl, destFile, 10000);
42
+ let contents = await fs.promises.readFile(destFile, {encoding: "utf8"});
43
+ expect(contents).toEqual(expect.stringContaining(expectedFileContents));
44
+ });
45
+
46
+ it("does not download on short timeout", async() => {
47
+ try {
48
+ await download(resourceUrl, destFile, 10);
49
+ throw new Error("Download timeout didn't throw");
50
+ }
51
+ catch (err) {
52
+ expect(err).toBe("Download timeout after 10ms");
53
+ }
41
54
  });
55
+
56
+ it("does not download invalid resources", () =>
57
+ expect(download("invalid", destFile)).rejects.toThrow("Invalid URL"));
42
58
  });