@eyeo/get-browser-binary 0.17.0 → 0.18.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 +3 -1
- package/README.md +1 -1
- package/RELEASE_NOTES.md +13 -0
- package/package.json +1 -1
- package/src/chromium.js +31 -19
- package/src/edge.js +5 -55
- package/src/firefox.js +46 -11
- package/test/browsers.js +17 -14
- package/test/extension/mv3/background.js +14 -1
package/.gitlab-ci.yml
CHANGED
|
@@ -52,8 +52,10 @@ test:browsers:linux:dev:
|
|
|
52
52
|
- choco install -y microsoft-edge
|
|
53
53
|
- npm install
|
|
54
54
|
tags:
|
|
55
|
-
-
|
|
55
|
+
- eyeo-windows
|
|
56
56
|
cache: {}
|
|
57
|
+
# Retrying to mitigate Edge install issues
|
|
58
|
+
retry: 1
|
|
57
59
|
|
|
58
60
|
test:browsers:windows:
|
|
59
61
|
extends: .windows
|
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 >=
|
|
35
|
+
- Edge >= 114
|
|
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
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Unreleased
|
|
2
2
|
|
|
3
|
+
# 0.18.0
|
|
4
|
+
|
|
5
|
+
- Adds support for Firefox dev channel (!116)
|
|
6
|
+
- Drops support for old Edge versions (#79)
|
|
7
|
+
- Fixes `enableExtensionInIncognito()` to keep working with Chromium +131 (!117)
|
|
8
|
+
|
|
9
|
+
## Testing
|
|
10
|
+
|
|
11
|
+
- Changes the way test extensions load internal pages. With Chromium +133,
|
|
12
|
+
`driver.getAllWindowHandles()` can't see tabs opened by an extension if the url
|
|
13
|
+
option opens an extension page. Please check the example provided by !119 in
|
|
14
|
+
order to workaround that issue.
|
|
15
|
+
|
|
3
16
|
# 0.17.0
|
|
4
17
|
|
|
5
18
|
- Stops calling `driver.close()` in `enableExtensionInIncognito()`
|
package/package.json
CHANGED
package/src/chromium.js
CHANGED
|
@@ -229,47 +229,59 @@ export class Chromium extends Browser {
|
|
|
229
229
|
|
|
230
230
|
/** @see Browser.enableExtensionInIncognito */
|
|
231
231
|
static async enableExtensionInIncognito(driver, extensionTitle) {
|
|
232
|
-
|
|
232
|
+
const currentHandle = await driver.getWindowHandle();
|
|
233
233
|
|
|
234
|
-
|
|
235
|
-
(await driver.getCapabilities()).getBrowserVersion());
|
|
236
|
-
if (
|
|
234
|
+
const browserVersion =
|
|
235
|
+
getMajorVersion((await driver.getCapabilities()).getBrowserVersion());
|
|
236
|
+
if (browserVersion >= 115)
|
|
237
237
|
// On Chromium 115 opening chrome://extensions on the default tab causes
|
|
238
238
|
// WebDriverError: disconnected. Switching to a new window as a workaround
|
|
239
239
|
await driver.switchTo().newWindow("window");
|
|
240
240
|
|
|
241
241
|
await driver.navigate().to("chrome://extensions");
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
|
|
243
|
+
await driver.executeScript((title, errorMsg) => {
|
|
244
|
+
const enableIncognitoMode = () => document
|
|
245
|
+
.querySelector("extensions-manager").shadowRoot
|
|
244
246
|
.querySelector("extensions-detail-view").shadowRoot
|
|
245
247
|
.getElementById("allow-incognito").shadowRoot
|
|
246
248
|
.getElementById("crToggle").click();
|
|
247
249
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
const getExtensionDetailsButton = () => {
|
|
251
|
+
const extensions = document
|
|
252
|
+
.querySelector("extensions-manager").shadowRoot
|
|
253
|
+
.getElementById("items-list").shadowRoot
|
|
254
|
+
.querySelectorAll("extensions-item");
|
|
251
255
|
|
|
252
|
-
|
|
253
|
-
let extensionDetails;
|
|
256
|
+
let detailsButton;
|
|
254
257
|
for (let {shadowRoot} of extensions) {
|
|
255
|
-
|
|
258
|
+
const extensionName = shadowRoot.getElementById("name").innerHTML;
|
|
259
|
+
if (!extensionName.includes(title))
|
|
256
260
|
continue;
|
|
257
261
|
|
|
258
|
-
|
|
262
|
+
detailsButton = shadowRoot.getElementById("detailsButton");
|
|
259
263
|
break;
|
|
260
264
|
}
|
|
261
|
-
if (!extensionDetails)
|
|
262
|
-
reject(`${args[1]}: ${args[0]}`);
|
|
263
265
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
+
return detailsButton;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
return new Promise((resolve, reject) => {
|
|
270
|
+
const detailsButton = getExtensionDetailsButton();
|
|
271
|
+
|
|
272
|
+
if (!detailsButton)
|
|
273
|
+
reject(`${errorMsg}: ${title}`);
|
|
274
|
+
|
|
275
|
+
detailsButton.click();
|
|
276
|
+
setTimeout(() => resolve(enableIncognitoMode()), 100);
|
|
266
277
|
});
|
|
267
278
|
}, extensionTitle, errMsg.extensionNotFound);
|
|
268
|
-
|
|
279
|
+
|
|
280
|
+
if (browserVersion >= 115 && process.platform == "win32")
|
|
269
281
|
// Closing the previously opened new window. Needed on Windows to avoid a
|
|
270
282
|
// further `WebDriverError: disconnected` error
|
|
271
283
|
await driver.close();
|
|
272
284
|
|
|
273
|
-
await driver.switchTo().window(
|
|
285
|
+
await driver.switchTo().window(currentHandle);
|
|
274
286
|
}
|
|
275
287
|
}
|
package/src/edge.js
CHANGED
|
@@ -23,7 +23,6 @@ import fs from "fs";
|
|
|
23
23
|
import got from "got";
|
|
24
24
|
import webdriver from "selenium-webdriver";
|
|
25
25
|
import edge from "selenium-webdriver/edge.js";
|
|
26
|
-
import extractZip from "extract-zip";
|
|
27
26
|
|
|
28
27
|
import {Browser} from "./browser.js";
|
|
29
28
|
import {download, killDriverProcess, wait, getMajorVersion, checkVersion,
|
|
@@ -40,6 +39,7 @@ let {platform} = process;
|
|
|
40
39
|
*/
|
|
41
40
|
export class Edge extends Browser {
|
|
42
41
|
static #CHANNELS = ["latest", "beta", "dev"];
|
|
42
|
+
static #MIN_VERSION = 114;
|
|
43
43
|
|
|
44
44
|
static async #getVersionForChannel(version) {
|
|
45
45
|
let channel = "stable";
|
|
@@ -106,7 +106,7 @@ export class Edge extends Browser {
|
|
|
106
106
|
* which requires root permissions. On MacOS, Edge is installed as a user
|
|
107
107
|
* app (not as a system app). Installing Edge on Windows is not supported.
|
|
108
108
|
* @param {string} [version=latest] Either "latest", "beta", "dev" or a full
|
|
109
|
-
* version number (i.e. "
|
|
109
|
+
* version number (i.e. "114.0.1823.82").
|
|
110
110
|
* @param {number} [downloadTimeout=0] Allowed time in ms for the download of
|
|
111
111
|
* install files to complete. When set to 0 there is no time limit.
|
|
112
112
|
* @return {BrowserBinary}
|
|
@@ -123,8 +123,7 @@ export class Edge extends Browser {
|
|
|
123
123
|
// Only latest Edge is supported on macOS
|
|
124
124
|
throw new Error(`${errMsg.unsupportedVersion}: ${version}. Only "latest" is supported`);
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
checkVersion(version, MIN_VERSION, Edge.#CHANNELS);
|
|
126
|
+
checkVersion(version, Edge.#MIN_VERSION, Edge.#CHANNELS);
|
|
128
127
|
let {versionNumber, channel} = await Edge.#getVersionForChannel(version);
|
|
129
128
|
|
|
130
129
|
let filename = {
|
|
@@ -179,53 +178,13 @@ export class Edge extends Browser {
|
|
|
179
178
|
return installedVersion.trim().replace(/.*\s/, "");
|
|
180
179
|
}
|
|
181
180
|
|
|
182
|
-
static async #installOldWindowsDriver(binary) {
|
|
183
|
-
async function extractEdgeZip(archive, cacheDir, driverPath) {
|
|
184
|
-
await killDriverProcess("msedgedriver");
|
|
185
|
-
await fs.promises.rm(driverPath, {force: true});
|
|
186
|
-
await extractZip(archive, {dir: cacheDir});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
let binaryPath = binary || Edge.#getBinaryPath();
|
|
190
|
-
let versionNumber = await Edge.#getInstalledVersionNumber(binaryPath);
|
|
191
|
-
let cacheDir = path.join(snapshotsBaseDir, "edge", "cache",
|
|
192
|
-
`edgedriver-${versionNumber}`);
|
|
193
|
-
let zip = process.arch == "ia32" ?
|
|
194
|
-
"edgedriver_win32.zip" : "edgedriver_win64.zip";
|
|
195
|
-
let archive = path.join(cacheDir, `${versionNumber}-${zip}`);
|
|
196
|
-
let driverPath = path.join(cacheDir, "msedgedriver.exe");
|
|
197
|
-
|
|
198
|
-
try {
|
|
199
|
-
await fs.promises.access(archive);
|
|
200
|
-
await extractEdgeZip(archive, cacheDir, driverPath);
|
|
201
|
-
}
|
|
202
|
-
catch (e) { // zip file is either not cached or corrupted
|
|
203
|
-
let vSplit = versionNumber.split(".");
|
|
204
|
-
let lastNum = parseInt(vSplit[3], 10);
|
|
205
|
-
while (lastNum >= 0) {
|
|
206
|
-
try {
|
|
207
|
-
let attempt = `${vSplit[0]}.${vSplit[1]}.${vSplit[2]}.${lastNum}`;
|
|
208
|
-
await download(`https://msedgedriver.azureedge.net/${attempt}/${zip}`,
|
|
209
|
-
archive);
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
catch (e2) {
|
|
213
|
-
lastNum--;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (lastNum < 0)
|
|
217
|
-
throw new Error(`${errMsg.driverDownload}: Edge ${versionNumber}`);
|
|
218
|
-
|
|
219
|
-
await extractEdgeZip(archive, cacheDir, driverPath);
|
|
220
|
-
}
|
|
221
|
-
return driverPath;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
181
|
/** @see Browser.getDriver */
|
|
225
182
|
static async getDriver(version = "latest", {
|
|
226
183
|
headless = true, extensionPaths = [], incognito = false, insecure = false,
|
|
227
184
|
extraArgs = [], customBrowserBinary
|
|
228
185
|
} = {}, downloadTimeout = 0) {
|
|
186
|
+
checkVersion(version, Edge.#MIN_VERSION, Edge.#CHANNELS);
|
|
187
|
+
|
|
229
188
|
let binary;
|
|
230
189
|
let versionNumber;
|
|
231
190
|
if (!customBrowserBinary && (platform == "linux" || platform == "darwin")) {
|
|
@@ -260,15 +219,6 @@ export class Edge extends Browser {
|
|
|
260
219
|
builder.forBrowser("MicrosoftEdge");
|
|
261
220
|
builder.setEdgeOptions(options);
|
|
262
221
|
|
|
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
|
-
}
|
|
271
|
-
|
|
272
222
|
let driver;
|
|
273
223
|
// On Windows CI, occasionally a SessionNotCreatedError is thrown, likely
|
|
274
224
|
// due to low OS resources, that's why building the driver is retried
|
package/src/firefox.js
CHANGED
|
@@ -38,12 +38,13 @@ let {until, By} = webdriver;
|
|
|
38
38
|
* @extends Browser
|
|
39
39
|
*/
|
|
40
40
|
export class Firefox extends Browser {
|
|
41
|
-
static #CHANNELS = ["latest", "beta"];
|
|
41
|
+
static #CHANNELS = ["latest", "beta", "dev"];
|
|
42
42
|
|
|
43
43
|
static async #getVersionForChannel(channel) {
|
|
44
44
|
if (!Firefox.#CHANNELS.includes(channel))
|
|
45
45
|
return channel;
|
|
46
46
|
|
|
47
|
+
// https://wiki.mozilla.org/Release_Management/Product_details#firefox_versions.json
|
|
47
48
|
let url = "https://product-details.mozilla.org/1.0/firefox_versions.json";
|
|
48
49
|
let data;
|
|
49
50
|
try {
|
|
@@ -52,17 +53,44 @@ export class Firefox extends Browser {
|
|
|
52
53
|
catch (err) {
|
|
53
54
|
throw new Error(`${errMsg.browserVersionCheck}: ${url}\n${err}`);
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
|
|
57
|
+
if (channel === "beta") {
|
|
58
|
+
// The validated latest Firefox Beta built, exposed for downloading
|
|
59
|
+
return data.LATEST_FIREFOX_RELEASED_DEVEL_VERSION;
|
|
60
|
+
}
|
|
61
|
+
else if (channel === "dev") {
|
|
62
|
+
// Firefox Developer Edition
|
|
63
|
+
return data.FIREFOX_DEVEDITION;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The Firefox Version shipped on the release channel
|
|
67
|
+
return data.LATEST_FIREFOX_VERSION;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static #getAppDir(dir) {
|
|
71
|
+
const appDirRegexes = {
|
|
72
|
+
win32: /^core$/,
|
|
73
|
+
linux: /^firefox$/,
|
|
74
|
+
darwin: /^Firefox.*\.app$/
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const contents = fs.readdirSync(dir);
|
|
78
|
+
const appDir = contents.find(candidateDir =>
|
|
79
|
+
appDirRegexes[process.platform].test(candidateDir));
|
|
80
|
+
|
|
81
|
+
return path.join(dir, appDir);
|
|
57
82
|
}
|
|
58
83
|
|
|
59
84
|
static #getBinaryPath(dir) {
|
|
60
85
|
checkPlatform();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
86
|
+
|
|
87
|
+
const defaultBinaries = {
|
|
88
|
+
win32: path.join(Firefox.#getAppDir(dir), "firefox.exe"),
|
|
89
|
+
linux: path.join(Firefox.#getAppDir(dir), "firefox"),
|
|
90
|
+
darwin: path.join(Firefox.#getAppDir(dir), "Contents", "MacOS", "firefox")
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return defaultBinaries[process.platform];
|
|
66
94
|
}
|
|
67
95
|
|
|
68
96
|
static #extractFirefoxArchive(archive, dir) {
|
|
@@ -113,12 +141,16 @@ export class Firefox extends Browser {
|
|
|
113
141
|
let snapshotsDir = path.join(snapshotsBaseDir, "firefox");
|
|
114
142
|
let browserDir = path.join(snapshotsDir,
|
|
115
143
|
`firefox-${platformArch}-${versionNumber}`);
|
|
116
|
-
|
|
144
|
+
|
|
145
|
+
let binary;
|
|
117
146
|
try {
|
|
118
147
|
await fs.promises.access(browserDir);
|
|
148
|
+
binary = Firefox.#getBinaryPath(browserDir);
|
|
119
149
|
return {binary, versionNumber};
|
|
120
150
|
}
|
|
121
|
-
catch (e) {
|
|
151
|
+
catch (e) {
|
|
152
|
+
// The binary was not already downloaded
|
|
153
|
+
}
|
|
122
154
|
|
|
123
155
|
let archive = path.join(snapshotsDir, "cache", fileName);
|
|
124
156
|
await fs.promises.mkdir(path.dirname(browserDir), {recursive: true});
|
|
@@ -126,7 +158,9 @@ export class Firefox extends Browser {
|
|
|
126
158
|
await fs.promises.access(archive);
|
|
127
159
|
}
|
|
128
160
|
catch (e) {
|
|
129
|
-
|
|
161
|
+
const baseUrl = "https://archive.mozilla.org/pub";
|
|
162
|
+
const channel = version === "dev" ? "devedition" : "firefox";
|
|
163
|
+
let url = `${baseUrl}/${channel}/releases/${versionNumber}/${buildPlatform}/en-US/${fileName}`;
|
|
130
164
|
try {
|
|
131
165
|
await download(url, archive, downloadTimeout);
|
|
132
166
|
}
|
|
@@ -135,6 +169,7 @@ export class Firefox extends Browser {
|
|
|
135
169
|
}
|
|
136
170
|
}
|
|
137
171
|
await Firefox.#extractFirefoxArchive(archive, browserDir);
|
|
172
|
+
binary = Firefox.#getBinaryPath(browserDir);
|
|
138
173
|
|
|
139
174
|
return {binary, versionNumber};
|
|
140
175
|
}
|
package/test/browsers.js
CHANGED
|
@@ -27,8 +27,8 @@ import {TEST_SERVER_URL} from "./test-server.js";
|
|
|
27
27
|
|
|
28
28
|
const VERSIONS = {
|
|
29
29
|
chromium: ["latest", "77.0.3865.0", "beta", "dev"],
|
|
30
|
-
firefox: ["latest", "68.0", "beta"],
|
|
31
|
-
edge: ["latest", "
|
|
30
|
+
firefox: ["latest", "68.0", "beta", "dev"],
|
|
31
|
+
edge: ["latest", "114.0.1823.82", "beta", "dev"]
|
|
32
32
|
};
|
|
33
33
|
const TEST_URL_BASIC = `${TEST_SERVER_URL}/basic.html`;
|
|
34
34
|
const PROXY_URL_BASIC = "http://testpages.adblockplus.org/basic.html";
|
|
@@ -355,21 +355,24 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
355
355
|
});
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
-
it("does not install unsupported versions", async function() {
|
|
359
|
-
if (browser == "edge" && process.platform == "win32")
|
|
360
|
-
this.skip();
|
|
361
358
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
});
|
|
359
|
+
describe("Any version", async() => {
|
|
360
|
+
it("does not install unsupported versions", async function() {
|
|
361
|
+
if (browser == "edge" && process.platform == "win32")
|
|
362
|
+
this.skip();
|
|
367
363
|
|
|
368
|
-
|
|
369
|
-
|
|
364
|
+
for (let unsupported of ["0.0", "invalid"]) {
|
|
365
|
+
await expect(BROWSERS[browser].installBrowser(unsupported))
|
|
366
|
+
.rejects.toThrow(`Unsupported browser version: ${unsupported}`);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
370
369
|
|
|
371
|
-
|
|
372
|
-
|
|
370
|
+
it("does not load an invalid extension", async() => {
|
|
371
|
+
let extensionPaths = [process.cwd()];
|
|
372
|
+
|
|
373
|
+
await expect(BROWSERS[browser].getDriver("latest", {extensionPaths}))
|
|
374
|
+
.rejects.toThrow(`Extension manifest file not found: ${extensionPaths[0]}`);
|
|
375
|
+
});
|
|
373
376
|
});
|
|
374
377
|
});
|
|
375
378
|
}
|
|
@@ -1,3 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// There's an issue with Chrome +133 and selenium webdriver where
|
|
4
|
+
// getAllWindowHandles() can't see tabs opened by an extension if the url option
|
|
5
|
+
// opens an extension page. Example: chrome.tabs.create({url: "index.html"});
|
|
6
|
+
//
|
|
7
|
+
// Workaround: Create the tab with empty options, which allows
|
|
8
|
+
// getAllWindowHandles() to see it, and afterwards update the tab with the
|
|
9
|
+
// intended url.
|
|
10
|
+
|
|
11
|
+
// This timeout must be higher than driver.geAllWindowHandles() polling interval
|
|
12
|
+
const timeout = 1000;
|
|
13
|
+
|
|
14
|
+
chrome.tabs.create({}, ({tabId}) => {
|
|
15
|
+
setTimeout(() => chrome.tabs.update(tabId, {url: "index.html"}), timeout);
|
|
16
|
+
});
|