@eyeo/get-browser-binary 0.4.0 → 0.6.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/.eslintrc.json +2 -1
- package/.gitlab-ci.yml +7 -10
- package/README.md +15 -16
- package/RELEASE_NOTES.md +15 -0
- package/package.json +2 -4
- package/src/browsers.js +169 -329
- package/src/utils.js +1 -2
- package/test/.eslintrc.json +2 -1
- package/test/browsers.js +55 -16
- package/test/docker/Dockerfile +1 -12
package/.eslintrc.json
CHANGED
package/.gitlab-ci.yml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
default:
|
|
2
|
-
image:
|
|
2
|
+
image: registry.gitlab.com/eyeo/docker/get-browser-binary:node16
|
|
3
3
|
interruptible: true
|
|
4
4
|
|
|
5
5
|
stages:
|
|
@@ -37,7 +37,7 @@ test:browsers:linux:
|
|
|
37
37
|
before_script:
|
|
38
38
|
- docker build -f test/docker/Dockerfile -t browsers .
|
|
39
39
|
script:
|
|
40
|
-
- docker run browsers
|
|
40
|
+
- docker run --shm-size=256m browsers
|
|
41
41
|
|
|
42
42
|
test:browsers:windows:
|
|
43
43
|
stage: test
|
|
@@ -51,17 +51,14 @@ test:browsers:windows:
|
|
|
51
51
|
- Start-Process msiexec
|
|
52
52
|
-ArgumentList "/i MicrosoftEdgeEnterpriseX64.msi /norestart /qn" -Wait
|
|
53
53
|
- choco upgrade -y nodejs --version 16.10.0
|
|
54
|
-
- npm install -g npm
|
|
55
54
|
- npm install
|
|
56
55
|
script:
|
|
57
|
-
# Running only a subset of Firefox
|
|
56
|
+
# Running only a subset of Firefox tests to avoid low OS resources error
|
|
58
57
|
# https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/2
|
|
59
|
-
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
# does the trick.
|
|
63
|
-
- $subset_tests = '(firefox|opera).*latest.*downloads'
|
|
64
|
-
- npm test -- --grep """"$subset_tests""""
|
|
58
|
+
- npm test -- --grep "firefox.*latest.*downloads"
|
|
59
|
+
# Running npm v8 on powershell has issues when the grep value contains the
|
|
60
|
+
# pipe (|) literal. Storing that string as a verbatim string (single quotes)
|
|
61
|
+
# and then sorrounding it with four double quotes does the trick.
|
|
65
62
|
- $full_tests = '(chromium|edge.*latest)'
|
|
66
63
|
- npm test -- --grep """"$full_tests""""
|
|
67
64
|
tags:
|
package/README.md
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
# get-browser-binary
|
|
2
2
|
|
|
3
|
-
Download specific browser versions for Chromium, Firefox
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Note: Edge download is only supported on Linux. On other platforms it is assumed
|
|
7
|
-
to be already installed.
|
|
8
|
-
|
|
9
|
-
Note: Using the `operadriver` on Windows is not supported.
|
|
3
|
+
Download specific browser versions for Chromium, Firefox and Edge, and their
|
|
4
|
+
matching [selenium webdriver](https://www.selenium.dev/selenium/docs/api/javascript/index.html).
|
|
10
5
|
|
|
11
6
|
## Getting started
|
|
12
7
|
|
|
@@ -17,12 +12,10 @@ selenium webdriver:
|
|
|
17
12
|
import {BROWSERS} from "@eyeo/get-browser-binary";
|
|
18
13
|
|
|
19
14
|
(async function example() {
|
|
20
|
-
let {
|
|
21
|
-
|
|
22
|
-
let {binary} = await chromium.downloadBinary();
|
|
15
|
+
let {binary} = await BROWSERS.chromium.downloadBinary("latest");
|
|
23
16
|
console.log(`Chromium binary downloaded to ${binary}`);
|
|
24
17
|
|
|
25
|
-
let driver = await chromium.getDriver();
|
|
18
|
+
let driver = await BROWSERS.chromium.getDriver("latest");
|
|
26
19
|
await driver.navigate().to("https://example.com/");
|
|
27
20
|
await driver.quit();
|
|
28
21
|
})();
|
|
@@ -35,6 +28,15 @@ For more information, please refer to the [API documention](https://gitlab.com/e
|
|
|
35
28
|
If you are already on the documentation page, you may find the API contents on
|
|
36
29
|
the right side.
|
|
37
30
|
|
|
31
|
+
### Supported browser versions
|
|
32
|
+
|
|
33
|
+
- Chromium >= 75
|
|
34
|
+
- Firefox >= 60
|
|
35
|
+
- Edge >= 95
|
|
36
|
+
|
|
37
|
+
Note: Edge download is not supported on Windows. It is assumed to be installed
|
|
38
|
+
because it is the default browser on that platform.
|
|
39
|
+
|
|
38
40
|
## Development
|
|
39
41
|
|
|
40
42
|
### Prerequisites
|
|
@@ -79,18 +81,15 @@ Useful to reproduce the CI environment of the `test:browsers:linux` job:
|
|
|
79
81
|
|
|
80
82
|
```shell
|
|
81
83
|
docker build -f test/docker/Dockerfile -t browsers .
|
|
82
|
-
docker run -it browsers
|
|
84
|
+
docker run --shm-size=256m -it browsers
|
|
83
85
|
```
|
|
84
86
|
|
|
85
87
|
The `grep` option can also be used on Docker via the `TEST_ARGS` parameter:
|
|
86
88
|
|
|
87
89
|
```shell
|
|
88
|
-
docker run -e TEST_ARGS="--grep chromium.*latest" -it browsers
|
|
90
|
+
docker run --shm-size=256m -e TEST_ARGS="--grep chromium.*latest" -it browsers
|
|
89
91
|
```
|
|
90
92
|
|
|
91
|
-
Note: For a full automated run, `opera` tests should not use the interactive
|
|
92
|
-
tty `-it` flag.
|
|
93
|
-
|
|
94
93
|
## Building the documentation
|
|
95
94
|
|
|
96
95
|
```shell
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# 0.6.0
|
|
2
|
+
|
|
3
|
+
- Removes Opera support (#26)
|
|
4
|
+
- Improves error handling when the downloaded browser version doesn't match
|
|
5
|
+
known channels (#25)
|
|
6
|
+
- Fixes an Edge running issue on Windows (32-bit) (#23)
|
|
7
|
+
|
|
8
|
+
# 0.5.0
|
|
9
|
+
|
|
10
|
+
- Enables Edge binary downloads on MacOS (!25)
|
|
11
|
+
- Sets unsupported browser versions (#16)
|
|
12
|
+
- Fixes the Edge repo url (!27)
|
|
13
|
+
- Fixes an issue when downloading Opera (#21)
|
|
14
|
+
- Fixes redundant Opera installations on Linux (!24)
|
|
15
|
+
|
|
1
16
|
# 0.4.0
|
|
2
17
|
|
|
3
18
|
- Adds Opera as a supported browser (#7)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eyeo/get-browser-binary",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Download browser binaries and matching webdrivers",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,11 +18,9 @@
|
|
|
18
18
|
"chromedriver": "^90.0.1",
|
|
19
19
|
"dmg": "^0.1.0",
|
|
20
20
|
"extract-zip": "^2.0.1",
|
|
21
|
-
"fs-extra": "^10.0.0",
|
|
22
21
|
"geckodriver": "^3.0.2",
|
|
23
22
|
"got": "^11.8.2",
|
|
24
|
-
"
|
|
25
|
-
"selenium-webdriver": "^4.2.0"
|
|
23
|
+
"selenium-webdriver": "^4.6.1"
|
|
26
24
|
},
|
|
27
25
|
"devDependencies": {
|
|
28
26
|
"eslint": "^8.17.0",
|
package/src/browsers.js
CHANGED
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import path from "path";
|
|
19
|
-
import {exec, execFile
|
|
19
|
+
import {exec, execFile} from "child_process";
|
|
20
20
|
import {promisify} from "util";
|
|
21
21
|
import fs from "fs";
|
|
22
|
-
import fsExtra from "fs-extra";
|
|
23
22
|
|
|
24
23
|
import got from "got";
|
|
25
24
|
import webdriver from "selenium-webdriver";
|
|
26
25
|
import chrome from "selenium-webdriver/chrome.js";
|
|
27
26
|
import firefox from "selenium-webdriver/firefox.js";
|
|
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
30
|
|
|
@@ -38,10 +38,17 @@ import {download, extractTar, extractDmg, getBrowserVersion, killDriverProcess,
|
|
|
38
38
|
export let snapshotsBaseDir = path.join(process.cwd(), "browser-snapshots");
|
|
39
39
|
|
|
40
40
|
let {until, By} = webdriver;
|
|
41
|
-
const ERROR_DOWNLOAD_NOT_SUPPORTED =
|
|
42
|
-
"Downloading this browser is not supported";
|
|
43
41
|
let platform = `${process.platform}-${process.arch}`;
|
|
44
42
|
|
|
43
|
+
function checkVersion(version, minVersion, channels) {
|
|
44
|
+
if (channels.includes(version))
|
|
45
|
+
return;
|
|
46
|
+
|
|
47
|
+
let mainVersion = parseInt(version && version.split(".")[0], 10);
|
|
48
|
+
if (isNaN(mainVersion) || mainVersion < minVersion)
|
|
49
|
+
throw new Error(`Unsupported browser version: ${version}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
45
52
|
/**
|
|
46
53
|
* Base class for browser download functionality. Please see subclasses for
|
|
47
54
|
* browser specific details.
|
|
@@ -60,9 +67,10 @@ class Browser {
|
|
|
60
67
|
* @param {string} version - Either full version number or channel/release.
|
|
61
68
|
* Please find examples on the subclasses.
|
|
62
69
|
* @return {BrowserBinary}
|
|
70
|
+
* @throws {Error} Unsupported browser version.
|
|
63
71
|
*/
|
|
64
72
|
static async downloadBinary(version) {
|
|
65
|
-
|
|
73
|
+
// to be implemented by the subclass
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
/**
|
|
@@ -81,7 +89,7 @@ class Browser {
|
|
|
81
89
|
else {
|
|
82
90
|
({stdout} = await promisify(execFile)(binary, ["--version"]));
|
|
83
91
|
}
|
|
84
|
-
return stdout;
|
|
92
|
+
return stdout.trim();
|
|
85
93
|
}
|
|
86
94
|
|
|
87
95
|
/**
|
|
@@ -107,11 +115,10 @@ class Browser {
|
|
|
107
115
|
* Installs the webdriver matching the browser version and runs the
|
|
108
116
|
* browser. If needed, the browser binary is also installed.
|
|
109
117
|
* @param {string} version - Either full version number or channel/release.
|
|
110
|
-
* Please find examples on the subclasses.
|
|
111
|
-
* no effect.
|
|
118
|
+
* Please find examples on the subclasses.
|
|
112
119
|
* @param {driverOptions?} options - Options to start the browser with.
|
|
113
120
|
* @return {webdriver}
|
|
114
|
-
* @throws {Error} Unsupported
|
|
121
|
+
* @throws {Error} Unsupported browser version.
|
|
115
122
|
*/
|
|
116
123
|
static async getDriver(version, options = {}) {
|
|
117
124
|
// to be implemented by the subclass
|
|
@@ -123,6 +130,7 @@ class Browser {
|
|
|
123
130
|
* @param {webdriver} driver - The driver controlling the browser.
|
|
124
131
|
* @param {string} extensionTitle - Title of the extebsion to be enabled.
|
|
125
132
|
* @return {webdriver}
|
|
133
|
+
* @throws {Error} Unsupported browser version.
|
|
126
134
|
*/
|
|
127
135
|
static async enableExtensionInIncognito(driver, extensionTitle) {
|
|
128
136
|
// Allowing the extension in incognito mode can't happen programmatically:
|
|
@@ -145,28 +153,30 @@ class Chromium extends Browser {
|
|
|
145
153
|
return data.chromium_base_position;
|
|
146
154
|
}
|
|
147
155
|
|
|
148
|
-
static async #
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
os = process.arch == "x64" ? "win64" : "win";
|
|
152
|
-
else if (os == "darwin")
|
|
153
|
-
os = process.arch == "arm64" ? "mac_arm64" : "mac";
|
|
156
|
+
static async #getVersionForChannel(channel) {
|
|
157
|
+
if (channel == "latest")
|
|
158
|
+
channel = "stable";
|
|
154
159
|
|
|
160
|
+
let os = {
|
|
161
|
+
"win32-ia32": "win",
|
|
162
|
+
"win32-x64": "win64",
|
|
163
|
+
"linux-x64": "linux",
|
|
164
|
+
"darwin-x64": "mac",
|
|
165
|
+
"dawrin-arm64": "mac_arm64"
|
|
166
|
+
}[platform];
|
|
155
167
|
let data = await got(`https://omahaproxy.appspot.com/all.json?os=${os}`).json();
|
|
156
168
|
let release = data[0].versions.find(ver => ver.channel == channel);
|
|
157
|
-
let {current_version: version
|
|
169
|
+
let {current_version: version} = release;
|
|
158
170
|
|
|
159
|
-
if (release.true_branch && release.true_branch.includes("_"))
|
|
171
|
+
if (release.true_branch && release.true_branch.includes("_"))
|
|
160
172
|
// A wrong base may be caused by a mini-branch (patched) release
|
|
161
173
|
// In that case, the base is taken from the unpatched version
|
|
162
174
|
version = [...version.split(".").slice(0, 3), "0"].join(".");
|
|
163
|
-
base = await Chromium.#getBranchBasePosition(version);
|
|
164
|
-
}
|
|
165
175
|
|
|
166
|
-
return
|
|
176
|
+
return version;
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
static #
|
|
179
|
+
static #getBinaryPath(dir) {
|
|
170
180
|
switch (process.platform) {
|
|
171
181
|
case "win32":
|
|
172
182
|
return path.join(dir, "chrome-win", "chrome.exe");
|
|
@@ -201,7 +211,7 @@ class Chromium extends Browser {
|
|
|
201
211
|
|
|
202
212
|
try {
|
|
203
213
|
await fs.promises.access(browserDir);
|
|
204
|
-
return {binary: Chromium.#
|
|
214
|
+
return {binary: Chromium.#getBinaryPath(browserDir), revision};
|
|
205
215
|
}
|
|
206
216
|
catch (e) {}
|
|
207
217
|
|
|
@@ -230,7 +240,7 @@ class Chromium extends Browser {
|
|
|
230
240
|
}
|
|
231
241
|
|
|
232
242
|
await extractZip(archive, {dir: browserDir});
|
|
233
|
-
return {binary: Chromium.#
|
|
243
|
+
return {binary: Chromium.#getBinaryPath(browserDir), revision};
|
|
234
244
|
}
|
|
235
245
|
|
|
236
246
|
/**
|
|
@@ -239,15 +249,19 @@ class Chromium extends Browser {
|
|
|
239
249
|
* number (i.e. "77.0.3865.0"). Defaults to "latest".
|
|
240
250
|
* @return {BrowserBinary}
|
|
241
251
|
*/
|
|
242
|
-
static async downloadBinary(version) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
252
|
+
static async downloadBinary(version = "latest") {
|
|
253
|
+
const MIN_VERSION = 75;
|
|
254
|
+
const CHANNELS = ["latest", "beta", "dev"];
|
|
255
|
+
|
|
256
|
+
checkVersion(version, MIN_VERSION, CHANNELS);
|
|
257
|
+
|
|
258
|
+
let versionNumber = CHANNELS.includes(version) ?
|
|
259
|
+
await Chromium.#getVersionForChannel(version) : version;
|
|
260
|
+
|
|
261
|
+
let base = await Chromium.#getBranchBasePosition(versionNumber);
|
|
248
262
|
|
|
249
263
|
let {binary, revision} = await Chromium.#downloadChromium(base);
|
|
250
|
-
return {binary, versionNumber
|
|
264
|
+
return {binary, versionNumber, revision};
|
|
251
265
|
}
|
|
252
266
|
|
|
253
267
|
static async #installDriver(revision, version) {
|
|
@@ -274,9 +288,10 @@ class Chromium extends Browser {
|
|
|
274
288
|
}
|
|
275
289
|
|
|
276
290
|
/** @see Browser.getDriver */
|
|
277
|
-
static async getDriver(version
|
|
278
|
-
|
|
279
|
-
|
|
291
|
+
static async getDriver(version = "latest", {
|
|
292
|
+
headless = true, extensionPaths = [], incognito = false, insecure = false,
|
|
293
|
+
extraArgs = []
|
|
294
|
+
} = {}) {
|
|
280
295
|
let {binary, revision, versionNumber} =
|
|
281
296
|
await Chromium.downloadBinary(version);
|
|
282
297
|
await Chromium.#installDriver(revision, versionNumber);
|
|
@@ -301,14 +316,8 @@ class Chromium extends Browser {
|
|
|
301
316
|
|
|
302
317
|
/** @see Browser.enableExtensionInIncognito */
|
|
303
318
|
static async enableExtensionInIncognito(driver, extensionTitle) {
|
|
304
|
-
let version = await getBrowserVersion(driver);
|
|
305
|
-
// Webdriver capabilities don't include a browser version for Opera
|
|
306
|
-
if (version && version < 75)
|
|
307
|
-
// The UI workaround needs a chromedriver >= 75
|
|
308
|
-
throw new Error(`Only supported on Chromium >= 75. Current version: ${version}`);
|
|
309
|
-
|
|
310
319
|
await driver.navigate().to("chrome://extensions");
|
|
311
|
-
await driver.executeScript(
|
|
320
|
+
await driver.executeScript((...args) => {
|
|
312
321
|
let enable = () => document.querySelector("extensions-manager").shadowRoot
|
|
313
322
|
.querySelector("extensions-detail-view").shadowRoot
|
|
314
323
|
.getElementById("allow-incognito").shadowRoot
|
|
@@ -321,7 +330,7 @@ class Chromium extends Browser {
|
|
|
321
330
|
return new Promise((resolve, reject) => {
|
|
322
331
|
let extensionDetails;
|
|
323
332
|
for (let {shadowRoot} of extensions) {
|
|
324
|
-
if (shadowRoot.getElementById("name").innerHTML !=
|
|
333
|
+
if (shadowRoot.getElementById("name").innerHTML != args[0])
|
|
325
334
|
continue;
|
|
326
335
|
|
|
327
336
|
extensionDetails = shadowRoot.getElementById("detailsButton");
|
|
@@ -332,7 +341,8 @@ class Chromium extends Browser {
|
|
|
332
341
|
|
|
333
342
|
extensionDetails.click();
|
|
334
343
|
setTimeout(() => resolve(enable()), 100);
|
|
335
|
-
})
|
|
344
|
+
});
|
|
345
|
+
}, extensionTitle);
|
|
336
346
|
}
|
|
337
347
|
}
|
|
338
348
|
|
|
@@ -342,13 +352,13 @@ class Chromium extends Browser {
|
|
|
342
352
|
* @extends Browser
|
|
343
353
|
*/
|
|
344
354
|
class Firefox extends Browser {
|
|
345
|
-
static async #
|
|
355
|
+
static async #getVersionForChannel(branch) {
|
|
346
356
|
let data = await got("https://product-details.mozilla.org/1.0/firefox_versions.json").json();
|
|
347
357
|
return branch == "beta" ?
|
|
348
358
|
data.LATEST_FIREFOX_DEVEL_VERSION : data.LATEST_FIREFOX_VERSION;
|
|
349
359
|
}
|
|
350
360
|
|
|
351
|
-
static #
|
|
361
|
+
static #getBinaryPath(dir) {
|
|
352
362
|
switch (process.platform) {
|
|
353
363
|
case "win32":
|
|
354
364
|
return path.join(dir, "core", "firefox.exe");
|
|
@@ -391,7 +401,7 @@ class Firefox extends Browser {
|
|
|
391
401
|
|
|
392
402
|
try {
|
|
393
403
|
await fs.promises.access(browserDir);
|
|
394
|
-
return Firefox.#
|
|
404
|
+
return Firefox.#getBinaryPath(browserDir);
|
|
395
405
|
}
|
|
396
406
|
catch (e) {}
|
|
397
407
|
|
|
@@ -405,7 +415,7 @@ class Firefox extends Browser {
|
|
|
405
415
|
}
|
|
406
416
|
|
|
407
417
|
await Firefox.#extractFirefoxArchive(archive, browserDir);
|
|
408
|
-
return Firefox.#
|
|
418
|
+
return Firefox.#getBinaryPath(browserDir);
|
|
409
419
|
}
|
|
410
420
|
|
|
411
421
|
/**
|
|
@@ -414,18 +424,24 @@ class Firefox extends Browser {
|
|
|
414
424
|
* number (i.e. "68.0"). Defaults to "latest".
|
|
415
425
|
* @return {BrowserBinary}
|
|
416
426
|
*/
|
|
417
|
-
static async downloadBinary(version) {
|
|
418
|
-
|
|
419
|
-
|
|
427
|
+
static async downloadBinary(version = "latest") {
|
|
428
|
+
const MIN_VERSION = 60;
|
|
429
|
+
const CHANNELS = ["latest", "beta"];
|
|
430
|
+
|
|
431
|
+
checkVersion(version, MIN_VERSION, CHANNELS);
|
|
420
432
|
|
|
421
|
-
let
|
|
422
|
-
|
|
433
|
+
let versionNumber = CHANNELS.includes(version) ?
|
|
434
|
+
await Firefox.#getVersionForChannel(version) : version;
|
|
435
|
+
|
|
436
|
+
let binary = await Firefox.#downloadFirefox(versionNumber);
|
|
437
|
+
return {binary, versionNumber};
|
|
423
438
|
}
|
|
424
439
|
|
|
425
440
|
/** @see Browser.getDriver */
|
|
426
|
-
static async getDriver(version
|
|
427
|
-
|
|
428
|
-
|
|
441
|
+
static async getDriver(version = "latest", {
|
|
442
|
+
headless = true, extensionPaths = [], incognito = false, insecure = false,
|
|
443
|
+
extraArgs = []
|
|
444
|
+
} = {}) {
|
|
429
445
|
let {binary} = await Firefox.downloadBinary(version);
|
|
430
446
|
|
|
431
447
|
let options = new firefox.Options();
|
|
@@ -502,7 +518,7 @@ class Edge extends Browser {
|
|
|
502
518
|
return {versionNumber: version, channel: "stable"};
|
|
503
519
|
|
|
504
520
|
let channel = version == "latest" ? "stable" : version;
|
|
505
|
-
let {body} = await got(`https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-${channel}
|
|
521
|
+
let {body} = await got(`https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-${channel}/`);
|
|
506
522
|
let regex = /href="microsoft-edge-(stable|beta|dev)_(.*?)-1_/gm;
|
|
507
523
|
let matches;
|
|
508
524
|
let versionNumbers = [];
|
|
@@ -516,6 +532,29 @@ class Edge extends Browser {
|
|
|
516
532
|
return {versionNumber, channel};
|
|
517
533
|
}
|
|
518
534
|
|
|
535
|
+
static #getDarwinAppName(channel) {
|
|
536
|
+
let extra = channel == "stable" ?
|
|
537
|
+
"" : " " + channel.charAt(0).toUpperCase() + channel.slice(1);
|
|
538
|
+
return `Microsoft Edge${extra}`;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
static #getBinaryPath(channel = "stable") {
|
|
542
|
+
switch (process.platform) {
|
|
543
|
+
case "win32":
|
|
544
|
+
let programFiles = process.env["ProgramFiles(x86)"] ?
|
|
545
|
+
"${Env:ProgramFiles(x86)}" : "${Env:ProgramFiles}";
|
|
546
|
+
return `${programFiles}\\Microsoft\\Edge\\Application\\msedge.exe`;
|
|
547
|
+
case "linux":
|
|
548
|
+
return channel == "stable" ?
|
|
549
|
+
"microsoft-edge" : `microsoft-edge-${channel}`;
|
|
550
|
+
case "darwin":
|
|
551
|
+
let appName = Edge.#getDarwinAppName(channel);
|
|
552
|
+
return `${process.env.HOME}/Applications/${appName}.app/Contents/MacOS/${appName}`;
|
|
553
|
+
default:
|
|
554
|
+
throw new Error(`Unexpected platform: ${process.platform}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
519
558
|
/**
|
|
520
559
|
* Downloads the browser binary file.
|
|
521
560
|
* @param {string} version - Either "latest", "beta", "dev" or a full version
|
|
@@ -524,20 +563,36 @@ class Edge extends Browser {
|
|
|
524
563
|
* @return {BrowserBinary}
|
|
525
564
|
*/
|
|
526
565
|
static async downloadBinary(version = "latest") {
|
|
527
|
-
if (process.platform
|
|
528
|
-
|
|
529
|
-
|
|
566
|
+
if (process.platform == "win32")
|
|
567
|
+
// Edge is mandatory on Windows, can't be uninstalled or downgraded
|
|
568
|
+
// https://support.microsoft.com/en-us/microsoft-edge/why-can-t-i-uninstall-microsoft-edge-ee150b3b-7d7a-9984-6d83-eb36683d526d
|
|
569
|
+
throw new Error("Edge download is not supported in Windows");
|
|
570
|
+
|
|
571
|
+
const MIN_VERSION = 95;
|
|
572
|
+
const CHANNELS = ["latest", "beta", "dev"];
|
|
573
|
+
checkVersion(version, MIN_VERSION, CHANNELS);
|
|
530
574
|
let {versionNumber, channel} = await Edge.#getVersionForChannel(version);
|
|
531
575
|
|
|
532
|
-
let
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
stable: "microsoft-edge",
|
|
537
|
-
beta: "microsoft-edge-beta",
|
|
538
|
-
dev: "microsoft-edge-dev"
|
|
576
|
+
let darwinName = {
|
|
577
|
+
stable: "MicrosoftEdge",
|
|
578
|
+
beta: "MicrosoftEdgeBeta",
|
|
579
|
+
dev: "MicrosoftEdgeDev"
|
|
539
580
|
}[channel];
|
|
581
|
+
let filename = {
|
|
582
|
+
linux: `microsoft-edge-${channel}_${versionNumber}-1_amd64.deb`,
|
|
583
|
+
darwin: `${darwinName}-${versionNumber}.pkg`
|
|
584
|
+
}[process.platform];
|
|
585
|
+
let darwinArch = process.arch == "arm64" ?
|
|
586
|
+
"03adf619-38c6-4249-95ff-4a01c0ffc962" :
|
|
587
|
+
"C1297A47-86C4-4C1F-97FA-950631F94777";
|
|
588
|
+
let downloadUrl = {
|
|
589
|
+
linux: `https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-${channel}/${filename}`,
|
|
590
|
+
darwin: `https://officecdnmac.microsoft.com/pr/${darwinArch}/MacAutoupdate/${filename}`
|
|
591
|
+
}[process.platform];
|
|
540
592
|
|
|
593
|
+
let snapshotsDir = path.join(snapshotsBaseDir, "edge");
|
|
594
|
+
let archive = path.join(snapshotsDir, "cache", filename);
|
|
595
|
+
let binary = Edge.#getBinaryPath(channel);
|
|
541
596
|
try {
|
|
542
597
|
if (await Edge.#getInstalledVersionNumber(binary) == versionNumber)
|
|
543
598
|
return {binary, versionNumber};
|
|
@@ -545,34 +600,38 @@ class Edge extends Browser {
|
|
|
545
600
|
catch (e) {}
|
|
546
601
|
|
|
547
602
|
try {
|
|
548
|
-
await download(
|
|
549
|
-
`https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-${channel}/${filename}`,
|
|
550
|
-
archive
|
|
551
|
-
);
|
|
603
|
+
await download(downloadUrl, archive);
|
|
552
604
|
}
|
|
553
605
|
catch (err) {
|
|
554
|
-
throw new Error(`Edge download failed
|
|
606
|
+
throw new Error(`Edge download failed at ${downloadUrl}\n${err}`);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (process.platform == "linux") {
|
|
610
|
+
await promisify(exec)(`dpkg -i ${archive}`);
|
|
611
|
+
}
|
|
612
|
+
else if (process.platform == "darwin") {
|
|
613
|
+
let appName = Edge.#getDarwinAppName(channel);
|
|
614
|
+
try {
|
|
615
|
+
await fs.promises.rm(`${process.env.HOME}/Applications/${appName}.app`, {recursive: true});
|
|
616
|
+
}
|
|
617
|
+
catch (e) {}
|
|
618
|
+
await promisify(exec)(`installer -pkg ${archive} -target CurrentUserHomeDirectory`);
|
|
555
619
|
}
|
|
556
|
-
await promisify(exec)(`dpkg -i ${archive}`);
|
|
557
620
|
|
|
558
621
|
return {binary, versionNumber};
|
|
559
622
|
}
|
|
560
623
|
|
|
561
624
|
static async #getInstalledVersionNumber(binary) {
|
|
562
625
|
let installedVersion = await Edge.getInstalledVersion(binary);
|
|
563
|
-
|
|
564
|
-
.
|
|
626
|
+
for (let word of ["beta", "dev", "Beta", "Dev"])
|
|
627
|
+
installedVersion = installedVersion.replace(word, "");
|
|
628
|
+
return installedVersion.trim().replace(/.*\s/, "");
|
|
565
629
|
}
|
|
566
630
|
|
|
567
631
|
static async #installDriver() {
|
|
568
|
-
let binary =
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
darwin: "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
572
|
-
linux: "microsoft-edge"
|
|
573
|
-
}[process.platform];
|
|
574
|
-
let version = await Edge.#getInstalledVersionNumber(binary);
|
|
575
|
-
if (!version)
|
|
632
|
+
let binary = Edge.#getBinaryPath();
|
|
633
|
+
let versionNumber = await Edge.#getInstalledVersionNumber(binary);
|
|
634
|
+
if (!versionNumber)
|
|
576
635
|
throw new Error("Edge is not installed");
|
|
577
636
|
|
|
578
637
|
let [zip, driver] = {
|
|
@@ -582,14 +641,11 @@ class Edge extends Browser {
|
|
|
582
641
|
"darwin-x64": ["edgedriver_mac64.zip", "msedgedriver"],
|
|
583
642
|
"darwin-arm64": ["edgedriver_arm64.zip", "msedgedriver"]
|
|
584
643
|
}[platform];
|
|
585
|
-
let cacheDir = path.join(snapshotsBaseDir, "edge", "cache"
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
let archive = path.join(cacheDir, `${version}-${zip}`);
|
|
591
|
-
|
|
592
|
-
let vSplit = version.split(".");
|
|
644
|
+
let cacheDir = path.join(snapshotsBaseDir, "edge", "cache",
|
|
645
|
+
`edgedriver-${versionNumber}`);
|
|
646
|
+
let archive = path.join(cacheDir, `${versionNumber}-${zip}`);
|
|
647
|
+
|
|
648
|
+
let vSplit = versionNumber.split(".");
|
|
593
649
|
let lastNum = parseInt(vSplit[3], 10);
|
|
594
650
|
while (lastNum >= 0) {
|
|
595
651
|
try {
|
|
@@ -604,53 +660,50 @@ class Edge extends Browser {
|
|
|
604
660
|
}
|
|
605
661
|
|
|
606
662
|
if (lastNum < 0)
|
|
607
|
-
throw new Error(`msedgedriver was not found for Edge ${
|
|
663
|
+
throw new Error(`msedgedriver was not found for Edge ${versionNumber}`);
|
|
608
664
|
|
|
609
|
-
await extractZip(archive, {dir: cacheDir});
|
|
610
665
|
await killDriverProcess(Edge.#DRIVER);
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
await fs.promises.
|
|
614
|
-
path.join(destinationDir, driver));
|
|
666
|
+
let driverPath = path.join(cacheDir, driver);
|
|
667
|
+
try {
|
|
668
|
+
await fs.promises.rm(driverPath, {recursive: true});
|
|
615
669
|
}
|
|
670
|
+
catch (e) {} // file does not exist
|
|
671
|
+
await extractZip(archive, {dir: cacheDir});
|
|
672
|
+
|
|
673
|
+
return driverPath;
|
|
616
674
|
}
|
|
617
675
|
|
|
618
676
|
/** @see Browser.getDriver */
|
|
619
|
-
static async getDriver(version
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
677
|
+
static async getDriver(version = "latest", {
|
|
678
|
+
headless = true, extensionPaths = [], incognito = false, insecure = false,
|
|
679
|
+
extraArgs = []
|
|
680
|
+
} = {}) {
|
|
681
|
+
if (process.platform == "linux" || process.platform == "darwin")
|
|
623
682
|
await Edge.downloadBinary(version);
|
|
624
683
|
|
|
625
|
-
await Edge.#installDriver();
|
|
684
|
+
let driverPath = await Edge.#installDriver();
|
|
685
|
+
let serviceBuilder = new edge.ServiceBuilder(driverPath);
|
|
626
686
|
|
|
627
|
-
let
|
|
687
|
+
let options = new edge.Options().addArguments("no-sandbox", ...extraArgs);
|
|
628
688
|
if (headless)
|
|
629
|
-
|
|
689
|
+
options.headless();
|
|
630
690
|
if (extensionPaths.length > 0)
|
|
631
|
-
|
|
691
|
+
options.addArguments(`load-extension=${extensionPaths.join(",")}`);
|
|
632
692
|
if (incognito)
|
|
633
|
-
|
|
693
|
+
options.addArguments("incognito");
|
|
694
|
+
if (insecure)
|
|
695
|
+
options.addArguments("ignore-certificate-errors");
|
|
634
696
|
|
|
635
697
|
let builder = new webdriver.Builder();
|
|
636
698
|
builder.forBrowser("MicrosoftEdge");
|
|
637
|
-
builder.
|
|
638
|
-
|
|
639
|
-
"ms:edgeChromium": true,
|
|
640
|
-
"ms:edgeOptions": {args},
|
|
641
|
-
"acceptInsecureCerts": insecure
|
|
642
|
-
});
|
|
699
|
+
builder.setEdgeOptions(options);
|
|
700
|
+
builder.setEdgeService(serviceBuilder);
|
|
643
701
|
|
|
644
702
|
return builder.build();
|
|
645
703
|
}
|
|
646
704
|
|
|
647
705
|
/** @see Browser.enableExtensionInIncognito */
|
|
648
706
|
static async enableExtensionInIncognito(driver, extensionTitle) {
|
|
649
|
-
let version = await getBrowserVersion(driver);
|
|
650
|
-
if (version < 79)
|
|
651
|
-
// The UI workaround needs a chromium based msedgedriver
|
|
652
|
-
throw new Error(`Only supported on Edge >= 79. Current version: ${version}`);
|
|
653
|
-
|
|
654
707
|
await driver.navigate().to("edge://extensions/");
|
|
655
708
|
for (let elem of await driver.findElements(By.css("[role=listitem]"))) {
|
|
656
709
|
let text = await elem.getAttribute("innerHTML");
|
|
@@ -671,218 +724,6 @@ class Edge extends Browser {
|
|
|
671
724
|
}
|
|
672
725
|
}
|
|
673
726
|
|
|
674
|
-
/**
|
|
675
|
-
* Download functionality for Opera. This class can be used statically.
|
|
676
|
-
* @hideconstructor
|
|
677
|
-
* @extends Browser
|
|
678
|
-
*/
|
|
679
|
-
class Opera extends Browser {
|
|
680
|
-
static async #getVersionForChannel(version) {
|
|
681
|
-
let channelPath = "opera/desktop";
|
|
682
|
-
let filePrefix = "Opera";
|
|
683
|
-
if (version != "latest")
|
|
684
|
-
return {versionNumber: version, channelPath, filePrefix};
|
|
685
|
-
|
|
686
|
-
let {body} = await got(`https://ftp.opera.com/pub/${channelPath}`);
|
|
687
|
-
let regex = /href="(\d.*)\/"/gm;
|
|
688
|
-
let matches = body.match(regex);
|
|
689
|
-
let versionNumber = regex.exec(matches[matches.length - 1])[1];
|
|
690
|
-
|
|
691
|
-
return {versionNumber, channelPath, filePrefix};
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
static #getOperaBinary(dir) {
|
|
695
|
-
switch (process.platform) {
|
|
696
|
-
case "win32":
|
|
697
|
-
return path.join(dir, "launcher.exe");
|
|
698
|
-
case "linux":
|
|
699
|
-
return path.join("/", "usr", "bin", "opera");
|
|
700
|
-
case "darwin":
|
|
701
|
-
return path.join(dir, "Opera.app", "Contents", "MacOS", "Opera");
|
|
702
|
-
default:
|
|
703
|
-
throw new Error(`Unexpected platform: ${process.platform}`);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
static async #extractDeb(archive) {
|
|
708
|
-
let child = spawn("dpkg", ["-i", archive]);
|
|
709
|
-
|
|
710
|
-
child.stdout.on("data", data => {
|
|
711
|
-
if (data.toString().startsWith("Do you want to update Opera")) {
|
|
712
|
-
process.stdin.pipe(child.stdin);
|
|
713
|
-
child.stdin.write("no\r\n");
|
|
714
|
-
}
|
|
715
|
-
});
|
|
716
|
-
|
|
717
|
-
child.stderr.on("data", data => {
|
|
718
|
-
let expectedWarnings = [
|
|
719
|
-
"debconf: unable to initialize frontend",
|
|
720
|
-
"dpkg: warning: downgrading opera-stable",
|
|
721
|
-
"update-alternatives",
|
|
722
|
-
"using /usr/bin/opera",
|
|
723
|
-
"skip creation of",
|
|
724
|
-
"\r\n"
|
|
725
|
-
];
|
|
726
|
-
if (!expectedWarnings.find(err => data.toString().includes(err.trim())))
|
|
727
|
-
console.error(`stderr: ${data.toString()}`);
|
|
728
|
-
});
|
|
729
|
-
|
|
730
|
-
await new Promise((resolve, reject) => child.on("close", code => {
|
|
731
|
-
if (code != 0)
|
|
732
|
-
reject(`dpkg process exited with code ${code}`);
|
|
733
|
-
|
|
734
|
-
resolve();
|
|
735
|
-
}));
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
static async #installOnWindows(archive, dir, filename) {
|
|
739
|
-
let archiveCopy = path.join(dir, filename);
|
|
740
|
-
await fsExtra.copy(archive, archiveCopy);
|
|
741
|
-
await promisify(exec)(`"${archiveCopy}"`);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
static #extractOperaArchive(archive, dir, filename) {
|
|
745
|
-
switch (process.platform) {
|
|
746
|
-
case "win32":
|
|
747
|
-
return Opera.#installOnWindows(archive, dir, filename);
|
|
748
|
-
case "linux":
|
|
749
|
-
return Opera.#extractDeb(archive, dir);
|
|
750
|
-
case "darwin":
|
|
751
|
-
return extractTar(archive, dir);
|
|
752
|
-
default:
|
|
753
|
-
throw new Error(`Unexpected platform: ${process.platform}`);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* Downloads the browser binary file.
|
|
759
|
-
* @param {string} version - Either "latest" or a full version number
|
|
760
|
-
* (i.e. "64.0.3417.92"). Defaults to "latest".
|
|
761
|
-
* @return {BrowserBinary}
|
|
762
|
-
*/
|
|
763
|
-
static async downloadBinary(version = "latest") {
|
|
764
|
-
let {versionNumber, channelPath, filePrefix} =
|
|
765
|
-
await Opera.#getVersionForChannel(version);
|
|
766
|
-
|
|
767
|
-
let [platformDir, fileSuffix] = {
|
|
768
|
-
"win32-ia32": ["win", "Autoupdate.exe"],
|
|
769
|
-
"win32-x64": ["win", "Autoupdate_x64.exe"],
|
|
770
|
-
"linux-x64": ["linux", "amd64.deb"],
|
|
771
|
-
"darwin-x64": ["mac", "Autoupdate.tar.xz"],
|
|
772
|
-
"dawrin-arm64": ["mac", "Autoupdate_arm64.tar.xz"]
|
|
773
|
-
}[platform];
|
|
774
|
-
|
|
775
|
-
let snapshotsDir = path.join(snapshotsBaseDir, "opera");
|
|
776
|
-
let browserDir = path.join(snapshotsDir, `opera-${platform}-${versionNumber}`);
|
|
777
|
-
let filename = `${filePrefix}_${versionNumber}_${fileSuffix}`;
|
|
778
|
-
let archive = path.join(snapshotsDir, "cache", filename);
|
|
779
|
-
|
|
780
|
-
let binary = Opera.#getOperaBinary(browserDir);
|
|
781
|
-
try {
|
|
782
|
-
await fs.promises.access(browserDir);
|
|
783
|
-
return {binary, versionNumber};
|
|
784
|
-
}
|
|
785
|
-
catch (e) {}
|
|
786
|
-
|
|
787
|
-
await fs.promises.mkdir(path.dirname(browserDir), {recursive: true});
|
|
788
|
-
try {
|
|
789
|
-
await fs.promises.access(archive);
|
|
790
|
-
}
|
|
791
|
-
catch (e) {
|
|
792
|
-
await download(
|
|
793
|
-
`https://ftp.opera.com/pub/${channelPath}/${versionNumber}/${platformDir}/${filename}`,
|
|
794
|
-
archive
|
|
795
|
-
);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
await Opera.#extractOperaArchive(archive, browserDir, filename);
|
|
799
|
-
return {binary, versionNumber};
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
static async #installDriver(version, originalVersion) {
|
|
803
|
-
let [zip, driver] = {
|
|
804
|
-
"win32-ia32": ["operadriver_win32.zip", "operadriver.exe"],
|
|
805
|
-
"win32-x64": ["operadriver_win64.zip", "operadriver.exe"],
|
|
806
|
-
"linux-x64": ["operadriver_linux64.zip", "operadriver"],
|
|
807
|
-
"darwin-x64": ["operadriver_mac64.zip", "operadriver"],
|
|
808
|
-
"darwin-arm64": ["operadriver_mac64.zip", "operadriver"]
|
|
809
|
-
}[platform];
|
|
810
|
-
|
|
811
|
-
let {versionNumber} = await Opera.#getVersionForChannel(version);
|
|
812
|
-
versionNumber = versionNumber.split(".")[0];
|
|
813
|
-
|
|
814
|
-
let cacheDir = path.join(snapshotsBaseDir, "opera", "cache",
|
|
815
|
-
`driver-for-opera-${versionNumber}`);
|
|
816
|
-
let archive = path.join(cacheDir, zip);
|
|
817
|
-
|
|
818
|
-
let {body} = await got(`https://github.com/operasoftware/operachromiumdriver/releases?q=Opera+${versionNumber}&expanded=true`);
|
|
819
|
-
let regex = /release-card[\s\S]*Link--primary.*>(.*)<\/a/gm;
|
|
820
|
-
let matches = body.match(regex);
|
|
821
|
-
if (!matches || matches.length == 0)
|
|
822
|
-
throw new Error(`Driver for Opera ${version} was not found`);
|
|
823
|
-
let driverVersion = regex.exec(matches[matches.length - 1])[1];
|
|
824
|
-
|
|
825
|
-
try {
|
|
826
|
-
await download(`https://github.com/operasoftware/operachromiumdriver/releases/download/v.${driverVersion}/${zip}`,
|
|
827
|
-
archive);
|
|
828
|
-
}
|
|
829
|
-
catch (err) {
|
|
830
|
-
throw new Error(`Downloading operadriver failed: ${err}`);
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
await killDriverProcess("operadriver");
|
|
834
|
-
let driverPath = path.join(cacheDir, zip.split(".")[0], driver);
|
|
835
|
-
try {
|
|
836
|
-
await fs.promises.rm(driverPath, {recursive: true});
|
|
837
|
-
}
|
|
838
|
-
catch (e) {} // file does not exist
|
|
839
|
-
await extractZip(archive, {dir: cacheDir});
|
|
840
|
-
await fs.promises.chmod(driverPath, 577);
|
|
841
|
-
|
|
842
|
-
return driverPath;
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
/** @see Browser.getDriver */
|
|
846
|
-
static async getDriver(version = "latest", {
|
|
847
|
-
headless = true, extensionPaths = [], incognito = false, insecure = false,
|
|
848
|
-
extraArgs = []
|
|
849
|
-
} = {}) {
|
|
850
|
-
let {binary, versionNumber} = await Opera.downloadBinary(version);
|
|
851
|
-
let driverPath = await Opera.#installDriver(versionNumber, version);
|
|
852
|
-
// operadriver uses chrome as a service builder:
|
|
853
|
-
// https://github.com/operasoftware/operachromiumdriver/blob/master/docs/desktop.md
|
|
854
|
-
let serviceBuilder = new chrome.ServiceBuilder(driverPath);
|
|
855
|
-
let service = serviceBuilder.build();
|
|
856
|
-
await service.start();
|
|
857
|
-
|
|
858
|
-
let options = new chrome.Options().addArguments("no-sandbox", ...extraArgs);
|
|
859
|
-
if (extensionPaths.length > 0)
|
|
860
|
-
options.addArguments(`load-extension=${extensionPaths.join(",")}`);
|
|
861
|
-
if (headless)
|
|
862
|
-
options.headless();
|
|
863
|
-
if (insecure)
|
|
864
|
-
options.addArguments("ignore-certificate-errors");
|
|
865
|
-
if (incognito)
|
|
866
|
-
options.addArguments("incognito");
|
|
867
|
-
options.setChromeBinaryPath(binary);
|
|
868
|
-
// https://github.com/operasoftware/operachromiumdriver/issues/61#issuecomment-579331657
|
|
869
|
-
options.addArguments("remote-debugging-port=9222");
|
|
870
|
-
|
|
871
|
-
let builder = new webdriver.Builder();
|
|
872
|
-
builder.forBrowser("chrome");
|
|
873
|
-
builder.setChromeOptions(options);
|
|
874
|
-
builder.setChromeService(serviceBuilder);
|
|
875
|
-
|
|
876
|
-
return builder.build();
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
/** @see Browser.enableExtensionInIncognito */
|
|
880
|
-
static async enableExtensionInIncognito(driver, extensionTitle) {
|
|
881
|
-
// Extensions page in Opera has the same web elements as Chromium
|
|
882
|
-
await Chromium.enableExtensionInIncognito(driver, extensionTitle);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
|
|
886
727
|
/**
|
|
887
728
|
* @type {Object}
|
|
888
729
|
* @property {Chromium} chromium - Download functionality for Chromium.
|
|
@@ -892,6 +733,5 @@ class Opera extends Browser {
|
|
|
892
733
|
export const BROWSERS = {
|
|
893
734
|
chromium: Chromium,
|
|
894
735
|
firefox: Firefox,
|
|
895
|
-
edge: Edge
|
|
896
|
-
opera: Opera
|
|
736
|
+
edge: Edge
|
|
897
737
|
};
|
package/src/utils.js
CHANGED
|
@@ -22,7 +22,6 @@ import {promisify} from "util";
|
|
|
22
22
|
import {exec} from "child_process";
|
|
23
23
|
|
|
24
24
|
import got from "got";
|
|
25
|
-
import fsExtra from "fs-extra";
|
|
26
25
|
import dmg from "dmg";
|
|
27
26
|
|
|
28
27
|
/**
|
|
@@ -66,7 +65,7 @@ export async function extractDmg(archive, dir) {
|
|
|
66
65
|
let source = path.join(mpath, target);
|
|
67
66
|
await fs.promises.mkdir(dir);
|
|
68
67
|
try {
|
|
69
|
-
await
|
|
68
|
+
await fs.promises.cp(source, path.join(dir, target), {recursive: true});
|
|
70
69
|
}
|
|
71
70
|
finally {
|
|
72
71
|
try {
|
package/test/.eslintrc.json
CHANGED
package/test/browsers.js
CHANGED
|
@@ -25,13 +25,11 @@ import {killDriverProcess} from "../src/utils.js";
|
|
|
25
25
|
// Required to set the driver path on Windows
|
|
26
26
|
import "chromedriver";
|
|
27
27
|
import "geckodriver";
|
|
28
|
-
import "msedgedriver";
|
|
29
28
|
|
|
30
29
|
const VERSIONS = {
|
|
31
|
-
chromium: [
|
|
32
|
-
firefox: [
|
|
33
|
-
edge: [
|
|
34
|
-
opera: [void 0, "64.0.3417.92"]
|
|
30
|
+
chromium: ["latest", "75.0.3770.0", "beta", "dev"],
|
|
31
|
+
firefox: ["latest", "60.0", "beta"],
|
|
32
|
+
edge: ["latest", "95.0.1020.40", "beta", "dev"]
|
|
35
33
|
};
|
|
36
34
|
const TEST_URL = "https://gitlab.com/eyeo/developer-experience/get-browser-binary";
|
|
37
35
|
let extensionPaths = [path.resolve(process.cwd(), "test", "extension")];
|
|
@@ -74,9 +72,27 @@ function normalize(version) {
|
|
|
74
72
|
return normalized.split("b")[0];
|
|
75
73
|
}
|
|
76
74
|
|
|
75
|
+
function getWindowSize(driver) {
|
|
76
|
+
return driver.executeScript(() => {
|
|
77
|
+
return {height: window.innerHeight, width: window.innerWidth};
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
expect.extend({
|
|
82
|
+
toMeasureLessThan(small, big) {
|
|
83
|
+
let pass = small.width < big.width || small.height < big.height;
|
|
84
|
+
let message = () =>
|
|
85
|
+
`expected small sizes (w: ${small.width}, h: ${small.height}) ` +
|
|
86
|
+
`to be smaller than big sizes (w: ${big.width}, h: ${big.height})`;
|
|
87
|
+
if (pass)
|
|
88
|
+
return {message, pass: true};
|
|
89
|
+
return {message, pass: false};
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
77
93
|
for (let browser of Object.keys(BROWSERS)) {
|
|
78
94
|
describe(`Browser: ${browser}`, function() {
|
|
79
|
-
this.timeout(
|
|
95
|
+
this.timeout(150000);
|
|
80
96
|
|
|
81
97
|
before(async() => {
|
|
82
98
|
try {
|
|
@@ -86,9 +102,10 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
86
102
|
});
|
|
87
103
|
|
|
88
104
|
for (let version of VERSIONS[browser]) {
|
|
89
|
-
describe(`Version: ${version
|
|
105
|
+
describe(`Version: ${version}`, () => {
|
|
90
106
|
let driver = null;
|
|
91
|
-
|
|
107
|
+
|
|
108
|
+
async function quitDriver() {
|
|
92
109
|
if (!driver)
|
|
93
110
|
return;
|
|
94
111
|
|
|
@@ -99,16 +116,18 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
99
116
|
await killDriverProcess("chromedriver");
|
|
100
117
|
else if (browser == "firefox")
|
|
101
118
|
await killDriverProcess("geckodriver");
|
|
102
|
-
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
afterEach(quitDriver);
|
|
103
122
|
|
|
104
123
|
it("downloads", async function() {
|
|
105
|
-
if (browser == "edge" && process.platform
|
|
124
|
+
if (browser == "edge" && process.platform == "win32")
|
|
106
125
|
this.skip();
|
|
107
126
|
|
|
108
127
|
let {binary, versionNumber} =
|
|
109
128
|
await BROWSERS[browser].downloadBinary(version);
|
|
110
|
-
let
|
|
111
|
-
expect(binary).toEqual(expect.stringMatching(
|
|
129
|
+
let browserName = browser == "edge" ? /(edge|Edge)/ : browser;
|
|
130
|
+
expect(binary).toEqual(expect.stringMatching(browserName));
|
|
112
131
|
|
|
113
132
|
let installedVersion =
|
|
114
133
|
await BROWSERS[browser].getInstalledVersion(binary);
|
|
@@ -120,8 +139,7 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
120
139
|
let names = {
|
|
121
140
|
chromium: "chrome",
|
|
122
141
|
firefox: "firefox",
|
|
123
|
-
edge: /(MicrosoftEdge|msedge)
|
|
124
|
-
opera: /(opera|chrome)/
|
|
142
|
+
edge: /(MicrosoftEdge|msedge)/
|
|
125
143
|
};
|
|
126
144
|
|
|
127
145
|
driver = await BROWSERS[browser].getDriver(version);
|
|
@@ -132,10 +150,21 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
132
150
|
});
|
|
133
151
|
|
|
134
152
|
it("supports extra args", async() => {
|
|
153
|
+
let headless = false;
|
|
135
154
|
let extraArgs = browser == "firefox" ?
|
|
136
155
|
["--devtools"] : ["auto-open-devtools-for-tabs"];
|
|
137
|
-
|
|
156
|
+
|
|
157
|
+
driver = await BROWSERS[browser].getDriver(
|
|
158
|
+
version, {headless, extraArgs});
|
|
159
|
+
await driver.navigate().to(TEST_URL);
|
|
160
|
+
let sizeDevToolsOpen = await getWindowSize(driver);
|
|
161
|
+
await quitDriver();
|
|
162
|
+
|
|
163
|
+
driver = await BROWSERS[browser].getDriver(version, {headless});
|
|
138
164
|
await driver.navigate().to(TEST_URL);
|
|
165
|
+
let sizeDevToolsClosed = await getWindowSize(driver);
|
|
166
|
+
|
|
167
|
+
expect(sizeDevToolsOpen).toMeasureLessThan(sizeDevToolsClosed);
|
|
139
168
|
});
|
|
140
169
|
|
|
141
170
|
it("loads an extension", async() => {
|
|
@@ -147,7 +176,7 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
147
176
|
});
|
|
148
177
|
|
|
149
178
|
it("loads an extension in incognito mode", async function() {
|
|
150
|
-
if (browser == "firefox" && version == "
|
|
179
|
+
if (browser == "firefox" && version == "60.0")
|
|
151
180
|
this.skip();
|
|
152
181
|
|
|
153
182
|
driver = await BROWSERS[browser].getDriver(
|
|
@@ -159,5 +188,15 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
159
188
|
});
|
|
160
189
|
});
|
|
161
190
|
}
|
|
191
|
+
|
|
192
|
+
it("does not download unsupported versions", async function() {
|
|
193
|
+
if (browser == "edge" && process.platform == "win32")
|
|
194
|
+
this.skip();
|
|
195
|
+
|
|
196
|
+
for (let unsupported of ["0.0", "invalid"]) {
|
|
197
|
+
await expect(BROWSERS[browser].downloadBinary(unsupported))
|
|
198
|
+
.rejects.toThrow(`Unsupported browser version: ${unsupported}`);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
162
201
|
});
|
|
163
202
|
}
|
package/test/docker/Dockerfile
CHANGED
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
FROM
|
|
2
|
-
|
|
3
|
-
# General packages
|
|
4
|
-
RUN apt-get update && apt-get install -y git procps wget unzip bzip2 gnupg
|
|
5
|
-
# xvfb (headful browser run)
|
|
6
|
-
RUN apt-get install -y libgtk-3-0 libxt6 xvfb libnss3 libxss1
|
|
7
|
-
# General browser dependencies
|
|
8
|
-
RUN apt-get install -y libgconf-2-4 libasound2 libgbm1
|
|
9
|
-
# Edge dependencies
|
|
10
|
-
RUN apt-get install -y fonts-liberation libatomic1 xdg-utils
|
|
11
|
-
# Opera dependencies
|
|
12
|
-
RUN apt-get install -y libcurl4 libgdk-pixbuf2.0-0
|
|
1
|
+
FROM registry.gitlab.com/eyeo/docker/get-browser-binary:node16
|
|
13
2
|
|
|
14
3
|
COPY package*.json get-browser-binary/
|
|
15
4
|
RUN cd get-browser-binary && npm install
|