@eyeo/get-browser-binary 0.14.0 → 0.16.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 +2 -2
- package/README.md +7 -11
- package/RELEASE_NOTES.md +34 -0
- package/index.js +1 -1
- package/package.json +2 -2
- package/src/browser.js +5 -4
- package/src/chromium.js +32 -94
- package/src/edge.js +20 -18
- package/src/firefox.js +15 -8
- package/src/utils.js +19 -1
- package/test/browsers.js +42 -53
- package/test/runner.js +4 -2
- package/test/docker/arm64.Dockerfile +0 -27
package/.gitlab-ci.yml
CHANGED
|
@@ -29,9 +29,9 @@ test:basic:
|
|
|
29
29
|
|
|
30
30
|
test:browsers:linux:
|
|
31
31
|
stage: test
|
|
32
|
-
image: docker:
|
|
32
|
+
image: docker:24.0.5
|
|
33
33
|
services:
|
|
34
|
-
- docker:
|
|
34
|
+
- docker:24.0.5-dind
|
|
35
35
|
before_script:
|
|
36
36
|
- docker build -f test/docker/Dockerfile -t browsers .
|
|
37
37
|
script:
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ the right side.
|
|
|
32
32
|
|
|
33
33
|
- Chromium >= 77 (Chromium ARM >= 92)
|
|
34
34
|
- Firefox >= 68
|
|
35
|
-
- Edge >= 95
|
|
35
|
+
- Edge >= 95 (Windows Edge >= 79)
|
|
36
36
|
|
|
37
37
|
Note: Installing Edge is not supported on Windows. It is assumed to be installed
|
|
38
38
|
because it is the default browser on that platform. On macOS, only the latest
|
|
@@ -133,20 +133,16 @@ docker run --shm-size=512m -e TEST_ARGS="--grep chromium.*latest --timeout 10000
|
|
|
133
133
|
|
|
134
134
|
#### ARM architecture (M1/M2 Apple Silicon)
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
docker build -f test/docker/arm64.Dockerfile -t browsers-arm .
|
|
138
|
-
docker run --shm-size=512m -e TEST_ARGS="--grep (chromium|firefox).*latest" -it browsers-arm
|
|
139
|
-
```
|
|
136
|
+
The run is done emulating the AMD architecture. Requirements:
|
|
140
137
|
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
- macOS >= 13 (Ventura)
|
|
139
|
+
- Rosetta
|
|
140
|
+
- The feature "Use Rosetta for x86/amd64 emulation on Apple Silicon" enabled in Docker
|
|
143
141
|
|
|
144
|
-
|
|
145
|
-
(unstable results):
|
|
142
|
+
The `--platform` option should be used when running the image:
|
|
146
143
|
|
|
147
144
|
```shell
|
|
148
|
-
docker
|
|
149
|
-
docker run --platform linux/amd64 --shm-size=512m -e TEST_ARGS="--grep firefox.*68.0" -it browsers-amd
|
|
145
|
+
docker run --platform linux/amd64 --shm-size=512m -e TEST_ARGS="--grep chromium.*latest" -it browsers
|
|
150
146
|
```
|
|
151
147
|
|
|
152
148
|
## Building the documentation
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
# 0.16.0
|
|
2
|
+
|
|
3
|
+
- Starts using selenium's automated driver management (#67)
|
|
4
|
+
- Loads the extension in Firefox by local path instead of being copied to a
|
|
5
|
+
temporary folder (!104)
|
|
6
|
+
- Checks if extensions can be loaded beforehand, by ensuring they contain a
|
|
7
|
+
`manifest.json` file. If not, a new `Extension manifest file not found` error
|
|
8
|
+
will be thrown (#71)
|
|
9
|
+
- Refactors headless runs by not using webdriver's deprecated headless() method
|
|
10
|
+
(#72)
|
|
11
|
+
|
|
12
|
+
### Testing
|
|
13
|
+
|
|
14
|
+
- Fixes an issue with `npm test` which would fail when the `grep` option would
|
|
15
|
+
include parenthesis (#69)
|
|
16
|
+
- Adds the browser version information in the "runs" test (#74)
|
|
17
|
+
|
|
18
|
+
### Notes for integrators
|
|
19
|
+
|
|
20
|
+
- Please make sure that your selenium-webdriver package version is at least
|
|
21
|
+
4.15.0 (the one used in this release) (#67)
|
|
22
|
+
- If you experience chromedriver issues, try deleting the `/browser-snapshots`
|
|
23
|
+
cache folder.
|
|
24
|
+
|
|
25
|
+
# 0.15.0
|
|
26
|
+
|
|
27
|
+
- Fixes the downloads of Chromium versions < 91 by replacing the remaining
|
|
28
|
+
usages of omahaproxy API (now removed) with chromiumdash (#55)
|
|
29
|
+
- Updates to selenium webdriver 4.15.0 (#66)
|
|
30
|
+
|
|
31
|
+
### Testing
|
|
32
|
+
|
|
33
|
+
- Uses AMD emulation on ARM docker test runs (!93)
|
|
34
|
+
|
|
1
35
|
# 0.14.0
|
|
2
36
|
|
|
3
37
|
- Increases minimum supported browser versions (#64)
|
package/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import {Chromium} from "./src/chromium.js";
|
|
|
19
19
|
import {Firefox} from "./src/firefox.js";
|
|
20
20
|
import {Edge} from "./src/edge.js";
|
|
21
21
|
|
|
22
|
-
export {download, takeFullPageScreenshot, snapshotsBaseDir}
|
|
22
|
+
export {download, takeFullPageScreenshot, snapshotsBaseDir, getMajorVersion}
|
|
23
23
|
from "./src/utils.js";
|
|
24
24
|
|
|
25
25
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eyeo/get-browser-binary",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Install browser binaries and matching webdrivers",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"geckodriver": "3.1.0",
|
|
34
34
|
"got": "^12.5.3",
|
|
35
35
|
"jimp": "^0.22.4",
|
|
36
|
-
"selenium-webdriver": "^4.
|
|
36
|
+
"selenium-webdriver": "^4.15.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"eslint": "^8.33.0",
|
package/src/browser.js
CHANGED
|
@@ -84,11 +84,11 @@ export class Browser {
|
|
|
84
84
|
/**
|
|
85
85
|
* @typedef {Object} driverOptions
|
|
86
86
|
* @property {boolean} [headless=true] Run the browser in headless mode,
|
|
87
|
-
* or not. In Chromium >= 111
|
|
87
|
+
* or not. In Chromium >= 111 and Edge >= 114 the
|
|
88
88
|
* {@link https://developer.chrome.com/articles/new-headless/ new headless mode}
|
|
89
89
|
* is used.
|
|
90
|
-
* @property {Array.<string>} [extensionPaths=[]]
|
|
91
|
-
* browser.
|
|
90
|
+
* @property {Array.<string>} [extensionPaths=[]] Paths to folders containing
|
|
91
|
+
* unpacked extensions which will be loaded to the browser.
|
|
92
92
|
* @property {boolean} [incognito=false] Runs the browser in incognito mode,
|
|
93
93
|
* or not.
|
|
94
94
|
* @property {boolean} [insecure=false] Forces the browser to accept insecure
|
|
@@ -110,7 +110,8 @@ export class Browser {
|
|
|
110
110
|
* browser install files to complete. When set to 0 there is no time limit.
|
|
111
111
|
* @return {webdriver}
|
|
112
112
|
* @throws {Error} Unsupported browser version, Unsupported platform, Browser
|
|
113
|
-
* download failed, Driver download failed, Unable to start driver
|
|
113
|
+
* download failed, Driver download failed, Unable to start driver, Manifest
|
|
114
|
+
* file not found.
|
|
114
115
|
*/
|
|
115
116
|
static async getDriver(version, options = {}, downloadTimeout = 0) {
|
|
116
117
|
// to be implemented by the subclass
|
package/src/chromium.js
CHANGED
|
@@ -24,9 +24,8 @@ import chrome from "selenium-webdriver/chrome.js";
|
|
|
24
24
|
import extractZip from "extract-zip";
|
|
25
25
|
|
|
26
26
|
import {Browser} from "./browser.js";
|
|
27
|
-
import {download,
|
|
28
|
-
|
|
29
|
-
from "./utils.js";
|
|
27
|
+
import {download, getMajorVersion, checkVersion, checkPlatform, errMsg,
|
|
28
|
+
snapshotsBaseDir, platformArch, checkExtensionPaths} from "./utils.js";
|
|
30
29
|
|
|
31
30
|
/**
|
|
32
31
|
* Browser and webdriver functionality for Chromium.
|
|
@@ -85,10 +84,20 @@ export class Chromium extends Browser {
|
|
|
85
84
|
let url;
|
|
86
85
|
let chromiumBase;
|
|
87
86
|
try {
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
let majorVersion = getMajorVersion(chromiumVersion);
|
|
88
|
+
if (majorVersion < 91) {
|
|
89
|
+
// Below v91, base branch position only exists once per milestone in
|
|
90
|
+
// the /fetch_milestones endpoint
|
|
91
|
+
url = "https://chromiumdash.appspot.com/fetch_milestones?only_branched=true";
|
|
92
|
+
let data = await got(url).json();
|
|
93
|
+
for (let {milestone, chromium_main_branch_position: base} of data) {
|
|
94
|
+
if (milestone == majorVersion) {
|
|
95
|
+
chromiumBase = base;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!chromiumBase)
|
|
100
|
+
throw new Error(`${errMsg.browserVersionCheck}: ${url}`);
|
|
92
101
|
}
|
|
93
102
|
else {
|
|
94
103
|
url = `https://chromiumdash.appspot.com/fetch_version?version=${chromiumVersion}`;
|
|
@@ -106,8 +115,7 @@ export class Chromium extends Browser {
|
|
|
106
115
|
// Linux example: "Chromium 112.0.5615.49 built on Debian 11.6"
|
|
107
116
|
// Windows example: "114.0.5735.0"
|
|
108
117
|
let versionNumber = installedVersion.split(" ")[1] || installedVersion;
|
|
109
|
-
|
|
110
|
-
return {binary, versionNumber, base};
|
|
118
|
+
return {binary, versionNumber};
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
/**
|
|
@@ -124,18 +132,10 @@ export class Chromium extends Browser {
|
|
|
124
132
|
static async installBrowser(version = "latest", downloadTimeout = 0) {
|
|
125
133
|
const MIN_VERSION = process.arch == "arm64" ? 92 : 77;
|
|
126
134
|
|
|
127
|
-
let binary;
|
|
128
|
-
let versionNumber;
|
|
129
|
-
let base;
|
|
130
|
-
|
|
131
|
-
// https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/46
|
|
132
|
-
if (platformArch == "linux-arm64")
|
|
133
|
-
return await Chromium.#getInstalledBrowserInfo("/usr/bin/chromium");
|
|
134
|
-
|
|
135
135
|
checkVersion(version, MIN_VERSION, Chromium.#CHANNELS);
|
|
136
|
-
versionNumber = await Chromium.#getVersionForChannel(version);
|
|
136
|
+
let versionNumber = await Chromium.#getVersionForChannel(version);
|
|
137
137
|
|
|
138
|
-
base = await Chromium.#getBase(versionNumber);
|
|
138
|
+
let base = await Chromium.#getBase(versionNumber);
|
|
139
139
|
let startBase = base;
|
|
140
140
|
let [platformDir, fileName] = {
|
|
141
141
|
"win32-ia32": ["Win", "chrome-win.zip"],
|
|
@@ -148,6 +148,7 @@ export class Chromium extends Browser {
|
|
|
148
148
|
let browserDir;
|
|
149
149
|
let snapshotsDir = path.join(snapshotsBaseDir, "chromium");
|
|
150
150
|
|
|
151
|
+
let binary;
|
|
151
152
|
while (true) {
|
|
152
153
|
browserDir = path.join(snapshotsDir, `chromium-${platformArch}-${base}`);
|
|
153
154
|
binary = Chromium.#getBinaryPath(browserDir);
|
|
@@ -186,72 +187,7 @@ export class Chromium extends Browser {
|
|
|
186
187
|
}
|
|
187
188
|
await extractZip(archive, {dir: browserDir});
|
|
188
189
|
|
|
189
|
-
return {binary, versionNumber
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
static async #installDriver(startBase) {
|
|
193
|
-
let [dir, zip, driverBinary] = {
|
|
194
|
-
"win32-ia32": ["Win", "chromedriver_win32.zip", "chromedriver.exe"],
|
|
195
|
-
"win32-x64": ["Win_x64", "chromedriver_win32.zip", "chromedriver.exe"],
|
|
196
|
-
"linux-x64": ["Linux_x64", "chromedriver_linux64.zip", "chromedriver"],
|
|
197
|
-
"linux-arm64": ["", "", "chromedriver"],
|
|
198
|
-
"darwin-x64": ["Mac", "chromedriver_mac64.zip", "chromedriver"],
|
|
199
|
-
"darwin-arm64": ["Mac_Arm", "chromedriver_mac64.zip", "chromedriver"]
|
|
200
|
-
}[platformArch];
|
|
201
|
-
|
|
202
|
-
let cacheDir = path.join(snapshotsBaseDir, "chromium", "cache",
|
|
203
|
-
"chromedriver");
|
|
204
|
-
let archive = path.join(cacheDir, `${startBase}-${zip}`);
|
|
205
|
-
try {
|
|
206
|
-
await fs.promises.access(archive);
|
|
207
|
-
await extractZip(archive, {dir: cacheDir});
|
|
208
|
-
}
|
|
209
|
-
catch (e) { // zip file is either not cached or corrupted
|
|
210
|
-
if (platformArch == "linux-arm64") {
|
|
211
|
-
// https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/46
|
|
212
|
-
// It's unclear how electron releases match Chromium versions. Once that
|
|
213
|
-
// is figured out, the link below will depend on the Chromium version.
|
|
214
|
-
// https://stackoverflow.com/questions/38732822/compile-chromedriver-on-arm
|
|
215
|
-
let url = "https://github.com/electron/electron/releases/download/v26.2.0/chromedriver-v26.2.0-linux-arm64.zip";
|
|
216
|
-
try {
|
|
217
|
-
await download(url, archive);
|
|
218
|
-
}
|
|
219
|
-
catch (err) {
|
|
220
|
-
throw new Error(`${errMsg.driverDownload}: ${url}\n${err}`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
let base = startBase;
|
|
225
|
-
while (true) {
|
|
226
|
-
let url = `https://commondatastorage.googleapis.com/chromium-browser-snapshots/${dir}/${base}/${zip}`;
|
|
227
|
-
try {
|
|
228
|
-
await download(url, archive);
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
catch (err) {
|
|
232
|
-
if (err.name == "HTTPError") {
|
|
233
|
-
base--;
|
|
234
|
-
archive = path.join(cacheDir, `${base}-${zip}`);
|
|
235
|
-
if (base <= startBase - Chromium.#MAX_VERSION_DECREMENTS)
|
|
236
|
-
throw new Error(`${errMsg.driverDownload}: Chromium base ${startBase}`);
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
throw new Error(`${errMsg.driverDownload}: ${url}\n${err}`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
await extractZip(archive, {dir: cacheDir});
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
await killDriverProcess("chromedriver");
|
|
248
|
-
let driverPath = path.join(cacheDir, zip.split(".")[0], driverBinary);
|
|
249
|
-
if (platformArch == "linux-arm64")
|
|
250
|
-
driverPath = path.join(cacheDir, driverBinary);
|
|
251
|
-
await fs.promises.rm(driverPath, {force: true});
|
|
252
|
-
await extractZip(archive, {dir: cacheDir});
|
|
253
|
-
|
|
254
|
-
return driverPath;
|
|
190
|
+
return {binary, versionNumber};
|
|
255
191
|
}
|
|
256
192
|
|
|
257
193
|
/** @see Browser.getDriver */
|
|
@@ -259,21 +195,24 @@ export class Chromium extends Browser {
|
|
|
259
195
|
headless = true, extensionPaths = [], incognito = false, insecure = false,
|
|
260
196
|
extraArgs = [], customBrowserBinary
|
|
261
197
|
} = {}, downloadTimeout = 0) {
|
|
262
|
-
let {binary, versionNumber
|
|
198
|
+
let {binary, versionNumber} = customBrowserBinary ?
|
|
263
199
|
await Chromium.#getInstalledBrowserInfo(customBrowserBinary) :
|
|
264
200
|
await Chromium.installBrowser(version, downloadTimeout);
|
|
265
|
-
|
|
266
|
-
let serviceBuilder = new chrome.ServiceBuilder(driverPath);
|
|
201
|
+
|
|
267
202
|
let options = new chrome.Options().addArguments("no-sandbox", ...extraArgs);
|
|
268
|
-
if (extensionPaths.length > 0)
|
|
203
|
+
if (extensionPaths.length > 0) {
|
|
204
|
+
await checkExtensionPaths(extensionPaths);
|
|
269
205
|
options.addArguments(`load-extension=${extensionPaths.join(",")}`);
|
|
206
|
+
}
|
|
270
207
|
if (headless) {
|
|
271
|
-
|
|
272
|
-
// https://
|
|
273
|
-
if (
|
|
208
|
+
let majorVersion = getMajorVersion(versionNumber);
|
|
209
|
+
// https://www.selenium.dev/blog/2023/headless-is-going-away/
|
|
210
|
+
if (majorVersion >= 109)
|
|
274
211
|
options.addArguments("headless=new");
|
|
212
|
+
else if (majorVersion >= 96)
|
|
213
|
+
options.addArguments("headless=chrome");
|
|
275
214
|
else
|
|
276
|
-
options.headless
|
|
215
|
+
options.addArguments("headless");
|
|
277
216
|
}
|
|
278
217
|
if (insecure)
|
|
279
218
|
options.addArguments("ignore-certificate-errors");
|
|
@@ -284,7 +223,6 @@ export class Chromium extends Browser {
|
|
|
284
223
|
let builder = new webdriver.Builder();
|
|
285
224
|
builder.forBrowser("chrome");
|
|
286
225
|
builder.setChromeOptions(options);
|
|
287
|
-
builder.setChromeService(serviceBuilder);
|
|
288
226
|
|
|
289
227
|
return builder.build();
|
|
290
228
|
}
|
package/src/edge.js
CHANGED
|
@@ -27,7 +27,7 @@ import extractZip from "extract-zip";
|
|
|
27
27
|
|
|
28
28
|
import {Browser} from "./browser.js";
|
|
29
29
|
import {download, killDriverProcess, wait, getMajorVersion, checkVersion,
|
|
30
|
-
checkPlatform, errMsg, snapshotsBaseDir,
|
|
30
|
+
checkPlatform, errMsg, snapshotsBaseDir, checkExtensionPaths}
|
|
31
31
|
from "./utils.js";
|
|
32
32
|
|
|
33
33
|
let {By} = webdriver;
|
|
@@ -93,7 +93,7 @@ export class Edge extends Browser {
|
|
|
93
93
|
return `${programFiles}\\Microsoft\\Edge\\Application\\msedge.exe`;
|
|
94
94
|
case "linux":
|
|
95
95
|
return channel == "stable" ?
|
|
96
|
-
"microsoft-edge" :
|
|
96
|
+
"/usr/bin/microsoft-edge" : `/usr/bin/microsoft-edge-${channel}`;
|
|
97
97
|
case "darwin":
|
|
98
98
|
return `${process.env.HOME}/Applications/${Edge.#darwinApp}.app/Contents/MacOS/${Edge.#darwinApp}`;
|
|
99
99
|
default:
|
|
@@ -179,7 +179,7 @@ export class Edge extends Browser {
|
|
|
179
179
|
return installedVersion.trim().replace(/.*\s/, "");
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
static async #
|
|
182
|
+
static async #installOldWindowsDriver(binary) {
|
|
183
183
|
async function extractEdgeZip(archive, cacheDir, driverPath) {
|
|
184
184
|
await killDriverProcess("msedgedriver");
|
|
185
185
|
await fs.promises.rm(driverPath, {force: true});
|
|
@@ -188,17 +188,12 @@ export class Edge extends Browser {
|
|
|
188
188
|
|
|
189
189
|
let binaryPath = binary || Edge.#getBinaryPath();
|
|
190
190
|
let versionNumber = await Edge.#getInstalledVersionNumber(binaryPath);
|
|
191
|
-
let [zip, driverBinary] = {
|
|
192
|
-
"win32-ia32": ["edgedriver_win32.zip", "msedgedriver.exe"],
|
|
193
|
-
"win32-x64": ["edgedriver_win64.zip", "msedgedriver.exe"],
|
|
194
|
-
"linux-x64": ["edgedriver_linux64.zip", "msedgedriver"],
|
|
195
|
-
"darwin-x64": ["edgedriver_mac64.zip", "msedgedriver"],
|
|
196
|
-
"darwin-arm64": ["edgedriver_mac64_m1.zip", "msedgedriver"]
|
|
197
|
-
}[platformArch];
|
|
198
191
|
let cacheDir = path.join(snapshotsBaseDir, "edge", "cache",
|
|
199
192
|
`edgedriver-${versionNumber}`);
|
|
193
|
+
let zip = process.arch == "ia32" ?
|
|
194
|
+
"edgedriver_win32.zip" : "edgedriver_win64.zip";
|
|
200
195
|
let archive = path.join(cacheDir, `${versionNumber}-${zip}`);
|
|
201
|
-
let driverPath = path.join(cacheDir,
|
|
196
|
+
let driverPath = path.join(cacheDir, "msedgedriver.exe");
|
|
202
197
|
|
|
203
198
|
try {
|
|
204
199
|
await fs.promises.access(archive);
|
|
@@ -243,29 +238,36 @@ export class Edge extends Browser {
|
|
|
243
238
|
await Edge.#getInstalledVersionNumber(binary);
|
|
244
239
|
}
|
|
245
240
|
|
|
246
|
-
let driverPath = await Edge.#installDriver(binary);
|
|
247
|
-
let serviceBuilder = new edge.ServiceBuilder(driverPath);
|
|
248
|
-
|
|
249
241
|
let options = new edge.Options().addArguments("no-sandbox", ...extraArgs);
|
|
250
242
|
if (headless) {
|
|
251
243
|
if (versionNumber && getMajorVersion(versionNumber) >= 114)
|
|
252
244
|
options.addArguments("headless=new");
|
|
253
245
|
else
|
|
254
|
-
options.headless
|
|
246
|
+
options.addArguments("headless");
|
|
255
247
|
}
|
|
256
|
-
if (extensionPaths.length > 0)
|
|
248
|
+
if (extensionPaths.length > 0) {
|
|
249
|
+
await checkExtensionPaths(extensionPaths);
|
|
257
250
|
options.addArguments(`load-extension=${extensionPaths.join(",")}`);
|
|
251
|
+
}
|
|
258
252
|
if (incognito)
|
|
259
253
|
options.addArguments("inprivate");
|
|
260
254
|
if (insecure)
|
|
261
255
|
options.addArguments("ignore-certificate-errors");
|
|
262
256
|
if (platform == "linux")
|
|
263
|
-
options.setEdgeChromiumBinaryPath(
|
|
257
|
+
options.setEdgeChromiumBinaryPath(binary);
|
|
264
258
|
|
|
265
259
|
let builder = new webdriver.Builder();
|
|
266
260
|
builder.forBrowser("MicrosoftEdge");
|
|
267
261
|
builder.setEdgeOptions(options);
|
|
268
|
-
|
|
262
|
+
|
|
263
|
+
if (getMajorVersion(versionNumber) < 95) {
|
|
264
|
+
// Selenium's automated driver download doesn't work on old Edge versions.
|
|
265
|
+
// Support to installing old drivers is only needed by Windows CI jobs.
|
|
266
|
+
// https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/2#note_1189235804
|
|
267
|
+
let driverPath = await Edge.#installOldWindowsDriver(binary);
|
|
268
|
+
let serviceBuilder = new edge.ServiceBuilder(driverPath);
|
|
269
|
+
builder.setEdgeService(serviceBuilder);
|
|
270
|
+
}
|
|
269
271
|
|
|
270
272
|
let driver;
|
|
271
273
|
// On Windows CI, occasionally a SessionNotCreatedError is thrown, likely
|
package/src/firefox.js
CHANGED
|
@@ -23,11 +23,12 @@ import fs from "fs";
|
|
|
23
23
|
import got from "got";
|
|
24
24
|
import webdriver from "selenium-webdriver";
|
|
25
25
|
import firefox from "selenium-webdriver/firefox.js";
|
|
26
|
+
import {Command} from "selenium-webdriver/lib/command.js";
|
|
26
27
|
|
|
27
28
|
import {Browser} from "./browser.js";
|
|
28
29
|
import {download, extractTar, extractDmg, killDriverProcess, wait,
|
|
29
30
|
getMajorVersion, checkVersion, checkPlatform, errMsg, snapshotsBaseDir,
|
|
30
|
-
platformArch} from "./utils.js";
|
|
31
|
+
platformArch, checkExtensionPaths} from "./utils.js";
|
|
31
32
|
|
|
32
33
|
let {until, By} = webdriver;
|
|
33
34
|
|
|
@@ -98,10 +99,6 @@ export class Firefox extends Browser {
|
|
|
98
99
|
static async installBrowser(version = "latest", downloadTimeout = 0) {
|
|
99
100
|
const MIN_VERSION = 68;
|
|
100
101
|
|
|
101
|
-
// https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/46
|
|
102
|
-
if (platformArch == "linux-arm64")
|
|
103
|
-
return await Firefox.#getInstalledBrowserInfo("/usr/bin/firefox");
|
|
104
|
-
|
|
105
102
|
checkVersion(version, MIN_VERSION, Firefox.#CHANNELS);
|
|
106
103
|
let versionNumber = await Firefox.#getVersionForChannel(version);
|
|
107
104
|
|
|
@@ -156,7 +153,7 @@ export class Firefox extends Browser {
|
|
|
156
153
|
|
|
157
154
|
let options = new firefox.Options();
|
|
158
155
|
if (headless)
|
|
159
|
-
options.headless
|
|
156
|
+
options.addArguments("--headless");
|
|
160
157
|
if (incognito)
|
|
161
158
|
options.addArguments("--private");
|
|
162
159
|
if (insecure)
|
|
@@ -188,8 +185,18 @@ export class Firefox extends Browser {
|
|
|
188
185
|
}, 30000, `${errMsg.driverStart}: geckodriver`, 1000);
|
|
189
186
|
|
|
190
187
|
for (let extensionPath of extensionPaths) {
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
await checkExtensionPaths([extensionPath]);
|
|
189
|
+
|
|
190
|
+
// This is based on Selenium's Firefox `installAddon` function. Rather
|
|
191
|
+
// than setting the "addon" parameter, which is the actual extension
|
|
192
|
+
// base64 encoded, we need to set the "path" which is the filepath to the
|
|
193
|
+
// extension. This allows downstream to test upgrade paths by updating the
|
|
194
|
+
// extension source code and reloading it.
|
|
195
|
+
// See https://github.com/SeleniumHQ/selenium/blob/trunk/javascript/node/selenium-webdriver/firefox.js
|
|
196
|
+
await driver.execute(
|
|
197
|
+
new Command("install addon")
|
|
198
|
+
.setParameter("path", extensionPath)
|
|
199
|
+
.setParameter("temporary", true));
|
|
193
200
|
}
|
|
194
201
|
return driver;
|
|
195
202
|
}
|
package/src/utils.js
CHANGED
|
@@ -34,7 +34,8 @@ export const errMsg = {
|
|
|
34
34
|
browserDownload: "Browser download failed",
|
|
35
35
|
browserNotInstalled: "Browser is not installed",
|
|
36
36
|
browserVersionCheck: "Checking the browser version failed",
|
|
37
|
-
elemNotFound: "HTML element not found"
|
|
37
|
+
elemNotFound: "HTML element not found",
|
|
38
|
+
manifestNotFound: "Extension manifest file not found"
|
|
38
39
|
};
|
|
39
40
|
export const platformArch = `${process.platform}-${process.arch}`;
|
|
40
41
|
|
|
@@ -238,6 +239,12 @@ export async function takeFullPageScreenshot(driver, hideScrollbars = true) {
|
|
|
238
239
|
return fullScreenshot;
|
|
239
240
|
}
|
|
240
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Returns the major version of a browser version number.
|
|
244
|
+
* @param {string} versionNumber Full browser version number.
|
|
245
|
+
* @return {number} Major version number.
|
|
246
|
+
* @throws {Error} Unsupported browser version.
|
|
247
|
+
*/
|
|
241
248
|
export function getMajorVersion(versionNumber) {
|
|
242
249
|
let majorVersion = parseInt(versionNumber && versionNumber.split(".")[0], 10);
|
|
243
250
|
if (isNaN(majorVersion))
|
|
@@ -258,3 +265,14 @@ export function checkPlatform() {
|
|
|
258
265
|
if (!["win32", "linux", "darwin"].includes(process.platform))
|
|
259
266
|
throw new Error(`${errMsg.unsupportedPlatform}: ${process.platform}`);
|
|
260
267
|
}
|
|
268
|
+
|
|
269
|
+
export async function checkExtensionPaths(extensionPaths) {
|
|
270
|
+
for (let extensionPath of extensionPaths) {
|
|
271
|
+
try {
|
|
272
|
+
await fs.promises.access(path.join(extensionPath, "manifest.json"));
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
throw new Error(`${errMsg.manifestNotFound}: ${extensionPath}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
package/test/browsers.js
CHANGED
|
@@ -25,8 +25,6 @@ import {BROWSERS, snapshotsBaseDir, takeFullPageScreenshot} from "../index.js";
|
|
|
25
25
|
import {killDriverProcess} from "../src/utils.js";
|
|
26
26
|
import {TEST_SERVER_URL} from "./test-server.js";
|
|
27
27
|
|
|
28
|
-
import "geckodriver"; // Required to set the driver path on Windows
|
|
29
|
-
|
|
30
28
|
const VERSIONS = {
|
|
31
29
|
chromium: ["latest", "77.0.3865.0", "beta", "dev"],
|
|
32
30
|
firefox: ["latest", "68.0", "beta"],
|
|
@@ -106,42 +104,21 @@ function getExtension(browser, version) {
|
|
|
106
104
|
return {extensionPaths, manifest};
|
|
107
105
|
}
|
|
108
106
|
|
|
109
|
-
async function
|
|
110
|
-
let armLinux = process.platform == "linux" && process.arch == "arm64";
|
|
111
|
-
let cacheDir = path.join(snapshotsBaseDir, browser, "cache");
|
|
112
|
-
let cacheFiles;
|
|
113
|
-
try {
|
|
114
|
-
cacheFiles = await fs.promises.readdir(cacheDir);
|
|
115
|
-
}
|
|
116
|
-
catch (err) {
|
|
117
|
-
if (!armLinux)
|
|
118
|
-
throw err;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let browserCacheTime = null;
|
|
107
|
+
async function getInstallFileCTime(browser) {
|
|
122
108
|
// Browsers installed at OS level are not tested
|
|
123
|
-
if (browser
|
|
124
|
-
|
|
125
|
-
let browserZip =
|
|
126
|
-
cacheFiles.find(elem => installTypes.some(type => elem.includes(type)));
|
|
127
|
-
let browserStat = await fs.promises.stat(path.join(cacheDir, browserZip));
|
|
128
|
-
browserCacheTime = browserStat.ctimeMs;
|
|
129
|
-
}
|
|
109
|
+
if (browser == "edge" && process.platform == "win32")
|
|
110
|
+
return null;
|
|
130
111
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
let driverStat =
|
|
140
|
-
await fs.promises.stat(path.join(cacheDir, driverDir, driverZip));
|
|
141
|
-
driverCacheTime = driverStat.ctimeMs;
|
|
142
|
-
}
|
|
112
|
+
const installTypes = [".zip", ".dmg", ".bz2", ".deb", ".exe"];
|
|
113
|
+
|
|
114
|
+
let cacheDir = path.join(snapshotsBaseDir, browser, "cache");
|
|
115
|
+
let cacheFiles = await fs.promises.readdir(cacheDir);
|
|
116
|
+
let browserZip =
|
|
117
|
+
cacheFiles.find(elem => installTypes.some(type => elem.includes(type)));
|
|
118
|
+
if (!browserZip)
|
|
119
|
+
throw new Error(`Files in ${cacheDir} don't belong to any known install file types: ${installTypes}`);
|
|
143
120
|
|
|
144
|
-
return
|
|
121
|
+
return (await fs.promises.stat(path.join(cacheDir, browserZip))).ctimeMs;
|
|
145
122
|
}
|
|
146
123
|
|
|
147
124
|
const browserNames = {
|
|
@@ -159,6 +136,11 @@ async function basicUrlTest(driver, browser) {
|
|
|
159
136
|
expect(text).toEqual("Test server basic page");
|
|
160
137
|
}
|
|
161
138
|
|
|
139
|
+
// Adding the browser version to the test title for logging purposes
|
|
140
|
+
function addVersionToTitle(ctx, version) {
|
|
141
|
+
ctx.test.title = `${ctx.test.title} [v${version}]`;
|
|
142
|
+
}
|
|
143
|
+
|
|
162
144
|
for (let browser of Object.keys(BROWSERS)) {
|
|
163
145
|
describe(`Browser: ${browser}`, () => {
|
|
164
146
|
before(async() => {
|
|
@@ -171,7 +153,7 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
171
153
|
for (let version of VERSIONS[browser]) {
|
|
172
154
|
describe(`Version: ${version}`, () => {
|
|
173
155
|
let driver = null;
|
|
174
|
-
let
|
|
156
|
+
let firstInstallFileCTime = null;
|
|
175
157
|
let customBrowserBinary = null;
|
|
176
158
|
let failedInstall = false;
|
|
177
159
|
|
|
@@ -217,31 +199,32 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
217
199
|
expect(installedVersion).toEqual(
|
|
218
200
|
expect.stringContaining(normalize(versionNumber)));
|
|
219
201
|
|
|
220
|
-
|
|
221
|
-
|
|
202
|
+
addVersionToTitle(this, versionNumber);
|
|
203
|
+
|
|
204
|
+
// Data used in further tests
|
|
222
205
|
customBrowserBinary = binary;
|
|
206
|
+
firstInstallFileCTime = await getInstallFileCTime(browser);
|
|
223
207
|
});
|
|
224
208
|
|
|
225
|
-
it("runs", async()
|
|
209
|
+
it("runs", async function() {
|
|
226
210
|
driver = await BROWSERS[browser].getDriver(version);
|
|
227
211
|
await basicUrlTest(driver, browser);
|
|
228
212
|
|
|
229
|
-
|
|
230
|
-
|
|
213
|
+
let browserVersion =
|
|
214
|
+
(await driver.getCapabilities()).getBrowserVersion();
|
|
215
|
+
addVersionToTitle(this, browserVersion);
|
|
231
216
|
});
|
|
232
217
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
await quitDriver();
|
|
238
|
-
}
|
|
218
|
+
// This test depends on running the "installs" test
|
|
219
|
+
it("uses cached install files", async function() {
|
|
220
|
+
if (!firstInstallFileCTime)
|
|
221
|
+
this.skip();
|
|
239
222
|
|
|
240
223
|
// assigning `driver` to allow the afterEach hook quit the driver
|
|
241
224
|
driver = await BROWSERS[browser].getDriver(version);
|
|
242
|
-
let
|
|
225
|
+
let secondInstallFileCTime = await getInstallFileCTime(browser);
|
|
243
226
|
|
|
244
|
-
expect(
|
|
227
|
+
expect(secondInstallFileCTime).toEqual(firstInstallFileCTime);
|
|
245
228
|
});
|
|
246
229
|
|
|
247
230
|
// This test depends on running the "installs" test
|
|
@@ -288,6 +271,11 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
288
271
|
expect(fullImg.bitmap.height).toBeGreaterThan(partImg.bitmap.height);
|
|
289
272
|
});
|
|
290
273
|
|
|
274
|
+
// The only reason the geckodriver package is required in package.json
|
|
275
|
+
// is to install a specific version needed by Firefox 68.0, otherwise it
|
|
276
|
+
// fails to load extensions. When the oldest Firefox version is bumped,
|
|
277
|
+
// Selenium's automated driver download should be able to manage it.
|
|
278
|
+
// https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/44
|
|
291
279
|
it("loads an extension", async() => {
|
|
292
280
|
// Chromium's old headless mode doesn't support loading extensions
|
|
293
281
|
let headless = browser == "firefox" || (browser == "chromium" &&
|
|
@@ -324,10 +312,11 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
324
312
|
}
|
|
325
313
|
});
|
|
326
314
|
|
|
327
|
-
it("does not
|
|
328
|
-
let
|
|
329
|
-
|
|
330
|
-
|
|
315
|
+
it("does not load an invalid extension", async() => {
|
|
316
|
+
let extensionPaths = [process.cwd()];
|
|
317
|
+
|
|
318
|
+
await expect(BROWSERS[browser].getDriver("latest", {extensionPaths}))
|
|
319
|
+
.rejects.toThrow(`Extension manifest file not found: ${extensionPaths[0]}`);
|
|
331
320
|
});
|
|
332
321
|
});
|
|
333
322
|
}
|
package/test/runner.js
CHANGED
|
@@ -53,10 +53,12 @@ export async function killTestServer() {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
async function run() {
|
|
56
|
-
let args = process.argv.
|
|
56
|
+
let args = process.argv.slice(2); // Keep npm args from "--" onwards
|
|
57
|
+
args = args.map(v => v.startsWith("-") ? v : `"${v}"`);
|
|
58
|
+
|
|
57
59
|
await runTestServer();
|
|
58
60
|
try {
|
|
59
|
-
execSync(`npm run test-suite
|
|
61
|
+
execSync(`npm run test-suite ${args.join(" ")}`, {stdio: "inherit"});
|
|
60
62
|
}
|
|
61
63
|
finally {
|
|
62
64
|
await killTestServer();
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Duplicated from registry.gitlab.com/eyeo/docker/get-browser-binary:node18
|
|
2
|
-
# https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/46
|
|
3
|
-
FROM node:18-bullseye-slim
|
|
4
|
-
# General packages
|
|
5
|
-
RUN apt-get update && apt-get install -y git procps wget unzip bzip2 gnupg
|
|
6
|
-
# xvfb (headful browser run)
|
|
7
|
-
RUN apt-get install -y libgtk-3-0 libxt6 xvfb libnss3 libxss1
|
|
8
|
-
# General browser dependencies
|
|
9
|
-
RUN apt-get install -y libgconf-2-4 libasound2 libgbm1
|
|
10
|
-
# Edge dependencies
|
|
11
|
-
RUN apt-get install -y fonts-liberation libatomic1 xdg-utils libu2f-udev
|
|
12
|
-
|
|
13
|
-
# Chromium ARM
|
|
14
|
-
RUN apt-get install -y chromium
|
|
15
|
-
# https://askubuntu.com/questions/1360864/google-chrome-not-launching
|
|
16
|
-
RUN mkdir -p "/root/.config/chromium/Crash Reports/pending/"
|
|
17
|
-
|
|
18
|
-
# Firefox ARM
|
|
19
|
-
RUN apt-get install -y firefox-esr
|
|
20
|
-
|
|
21
|
-
COPY package*.json get-browser-binary/
|
|
22
|
-
RUN cd get-browser-binary && npm install
|
|
23
|
-
|
|
24
|
-
COPY . get-browser-binary/
|
|
25
|
-
|
|
26
|
-
ENV TEST_ARGS="--grep Browser"
|
|
27
|
-
ENTRYPOINT get-browser-binary/test/docker/entrypoint.sh
|