@eyeo/get-browser-binary 0.18.0 → 0.20.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/.mocharc.json +1 -1
- package/RELEASE_NOTES.md +16 -1
- package/package.json +1 -1
- package/src/browser.js +2 -2
- package/src/chromium.js +35 -7
- package/src/edge.js +51 -21
- package/src/firefox.js +13 -9
- package/src/utils.js +9 -1
- package/test/browsers.js +1 -1
- package/test/extension/mv3/background.js +1 -14
package/.mocharc.json
CHANGED
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
# Unreleased
|
|
1
|
+
# Unreleased
|
|
2
|
+
|
|
3
|
+
# 0.20.0
|
|
4
|
+
|
|
5
|
+
- Enable developer mode for incognito tests (!126)
|
|
6
|
+
- Adds support to beta and dev channels for Edge in macOS (!127)
|
|
7
|
+
- Enable developer mode for latest versions of chromium browsers (!128)
|
|
8
|
+
|
|
9
|
+
# 0.19.0
|
|
10
|
+
|
|
11
|
+
- Fixes an issue starting with Firefox version 135 which prevented the binary to be downloaded (#82)
|
|
12
|
+
- `getInstalledVersion` now returns only the version number (#81)
|
|
13
|
+
|
|
14
|
+
## Testing
|
|
15
|
+
|
|
16
|
+
- Test extensions internal pages can now load normally. The development version of Chromium 133 got the `driver.getAllWindowHandles()` issue fixed, therefore the workaround is no longer needed. (!121)
|
|
2
17
|
|
|
3
18
|
# 0.18.0
|
|
4
19
|
|
package/package.json
CHANGED
package/src/browser.js
CHANGED
|
@@ -48,7 +48,8 @@ export class Browser {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
* Gets the installed version returned by the browser binary.
|
|
51
|
+
* Gets the installed version returned by the browser binary. Subclasses add
|
|
52
|
+
* specific parsing to this function.
|
|
52
53
|
* @param {string} binary The path to the browser binary.
|
|
53
54
|
* @return {string} Installed browser version.
|
|
54
55
|
* @throws {Error} Browser is not installed.
|
|
@@ -124,7 +125,6 @@ export class Browser {
|
|
|
124
125
|
* enables the extension when loaded in incognito.
|
|
125
126
|
* @param {webdriver} driver The driver controlling the browser.
|
|
126
127
|
* @param {string} extensionTitle Title of the extension to be enabled.
|
|
127
|
-
* @return {webdriver}
|
|
128
128
|
* @throws {Error} Unsupported browser version, Extension not found, HTML
|
|
129
129
|
* element not found.
|
|
130
130
|
*/
|
package/src/chromium.js
CHANGED
|
@@ -19,7 +19,7 @@ import path from "path";
|
|
|
19
19
|
import fs from "fs";
|
|
20
20
|
|
|
21
21
|
import got from "got";
|
|
22
|
-
import
|
|
22
|
+
import {Builder} from "selenium-webdriver";
|
|
23
23
|
import chrome from "selenium-webdriver/chrome.js";
|
|
24
24
|
import extractZip from "extract-zip";
|
|
25
25
|
|
|
@@ -27,6 +27,20 @@ import {Browser} from "./browser.js";
|
|
|
27
27
|
import {download, getMajorVersion, checkVersion, checkPlatform, errMsg,
|
|
28
28
|
snapshotsBaseDir, platformArch, checkExtensionPaths} from "./utils.js";
|
|
29
29
|
|
|
30
|
+
async function enableDeveloperMode(driver) {
|
|
31
|
+
await driver.switchTo().newWindow("window");
|
|
32
|
+
await driver.navigate().to("chrome://extensions");
|
|
33
|
+
await driver.executeScript(() => {
|
|
34
|
+
const devModeToggle = document
|
|
35
|
+
.querySelector("extensions-manager").shadowRoot
|
|
36
|
+
.getElementById("toolbar").shadowRoot
|
|
37
|
+
.querySelector("#toolbar #devMode");
|
|
38
|
+
|
|
39
|
+
if (!devModeToggle.checked)
|
|
40
|
+
devModeToggle.click();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
/**
|
|
31
45
|
* Browser and webdriver functionality for Chromium.
|
|
32
46
|
* @hideconstructor
|
|
@@ -110,11 +124,18 @@ export class Chromium extends Browser {
|
|
|
110
124
|
return parseInt(chromiumBase, 10);
|
|
111
125
|
}
|
|
112
126
|
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
/** @see Browser.getInstalledVersion */
|
|
128
|
+
static async getInstalledVersion(binary) {
|
|
129
|
+
const installedVersion = await super.getInstalledVersion(binary);
|
|
130
|
+
|
|
115
131
|
// Linux example: "Chromium 112.0.5615.49 built on Debian 11.6"
|
|
116
132
|
// Windows example: "114.0.5735.0"
|
|
117
|
-
|
|
133
|
+
return installedVersion.split(" ")[1] || installedVersion;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static async #getInstalledBrowserInfo(binary) {
|
|
137
|
+
const versionNumber = await Chromium.getInstalledVersion(binary);
|
|
138
|
+
|
|
118
139
|
return {binary, versionNumber};
|
|
119
140
|
}
|
|
120
141
|
|
|
@@ -204,8 +225,8 @@ export class Chromium extends Browser {
|
|
|
204
225
|
await checkExtensionPaths(extensionPaths);
|
|
205
226
|
options.addArguments(`load-extension=${extensionPaths.join(",")}`);
|
|
206
227
|
}
|
|
228
|
+
let majorVersion = getMajorVersion(versionNumber);
|
|
207
229
|
if (headless) {
|
|
208
|
-
let majorVersion = getMajorVersion(versionNumber);
|
|
209
230
|
// https://www.selenium.dev/blog/2023/headless-is-going-away/
|
|
210
231
|
if (majorVersion >= 109)
|
|
211
232
|
options.addArguments("headless=new");
|
|
@@ -220,11 +241,18 @@ export class Chromium extends Browser {
|
|
|
220
241
|
options.addArguments("incognito");
|
|
221
242
|
options.setChromeBinaryPath(binary);
|
|
222
243
|
|
|
223
|
-
let builder = new
|
|
244
|
+
let builder = new Builder();
|
|
224
245
|
builder.forBrowser("chrome");
|
|
225
246
|
builder.setChromeOptions(options);
|
|
226
247
|
|
|
227
|
-
|
|
248
|
+
const driver = builder.build();
|
|
249
|
+
|
|
250
|
+
// From Chromium 134 on, developer mode needs to be enabled
|
|
251
|
+
// for custom extensions to work properly
|
|
252
|
+
if (majorVersion >= 134)
|
|
253
|
+
await enableDeveloperMode(driver);
|
|
254
|
+
|
|
255
|
+
return driver;
|
|
228
256
|
}
|
|
229
257
|
|
|
230
258
|
/** @see Browser.enableExtensionInIncognito */
|
package/src/edge.js
CHANGED
|
@@ -21,7 +21,7 @@ import {promisify} from "util";
|
|
|
21
21
|
import fs from "fs";
|
|
22
22
|
|
|
23
23
|
import got from "got";
|
|
24
|
-
import
|
|
24
|
+
import {until, By, Builder} from "selenium-webdriver";
|
|
25
25
|
import edge from "selenium-webdriver/edge.js";
|
|
26
26
|
|
|
27
27
|
import {Browser} from "./browser.js";
|
|
@@ -29,9 +29,16 @@ import {download, killDriverProcess, wait, getMajorVersion, checkVersion,
|
|
|
29
29
|
checkPlatform, errMsg, snapshotsBaseDir, checkExtensionPaths}
|
|
30
30
|
from "./utils.js";
|
|
31
31
|
|
|
32
|
-
let {By} = webdriver;
|
|
33
32
|
let {platform} = process;
|
|
34
33
|
|
|
34
|
+
async function enableDeveloperMode(driver) {
|
|
35
|
+
await driver.navigate().to("edge://extensions/");
|
|
36
|
+
const devModeToggle = driver.findElement(By.id("developer-mode"));
|
|
37
|
+
|
|
38
|
+
if (!devModeToggle.checked)
|
|
39
|
+
devModeToggle.click();
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
/**
|
|
36
43
|
* Browser and webdriver functionality for Edge.
|
|
37
44
|
* @hideconstructor
|
|
@@ -83,19 +90,24 @@ export class Edge extends Browser {
|
|
|
83
90
|
return {versionNumber, channel};
|
|
84
91
|
}
|
|
85
92
|
|
|
86
|
-
static #
|
|
93
|
+
static #getDarwinApp(channel = "stable") {
|
|
94
|
+
const firstUppercase =
|
|
95
|
+
String(channel).charAt(0).toUpperCase() + String(channel).slice(1);
|
|
96
|
+
return channel === "stable" ? "Microsoft Edge" : `Microsoft Edge ${firstUppercase}`;
|
|
97
|
+
}
|
|
87
98
|
|
|
88
99
|
static #getBinaryPath(channel = "stable") {
|
|
89
100
|
switch (platform) {
|
|
90
101
|
case "win32":
|
|
91
|
-
|
|
102
|
+
const programFiles = process.env["ProgramFiles(x86)"] ?
|
|
92
103
|
"${Env:ProgramFiles(x86)}" : "${Env:ProgramFiles}";
|
|
93
104
|
return `${programFiles}\\Microsoft\\Edge\\Application\\msedge.exe`;
|
|
94
105
|
case "linux":
|
|
95
106
|
return channel == "stable" ?
|
|
96
107
|
"/usr/bin/microsoft-edge" : `/usr/bin/microsoft-edge-${channel}`;
|
|
97
108
|
case "darwin":
|
|
98
|
-
|
|
109
|
+
const darwinApp = Edge.#getDarwinApp(channel);
|
|
110
|
+
return `${process.env.HOME}/Applications/${darwinApp}.app/Contents/MacOS/${darwinApp}`;
|
|
99
111
|
default:
|
|
100
112
|
checkPlatform();
|
|
101
113
|
}
|
|
@@ -119,9 +131,8 @@ export class Edge extends Browser {
|
|
|
119
131
|
// https://support.microsoft.com/en-us/microsoft-edge/why-can-t-i-uninstall-microsoft-edge-ee150b3b-7d7a-9984-6d83-eb36683d526d
|
|
120
132
|
throw new Error(`${errMsg.unsupportedPlatform}: ${platform}`);
|
|
121
133
|
|
|
122
|
-
if (platform == "darwin" && version
|
|
123
|
-
|
|
124
|
-
throw new Error(`${errMsg.unsupportedVersion}: ${version}. Only "latest" is supported`);
|
|
134
|
+
if (platform == "darwin" && !/latest|beta|dev/.test(version))
|
|
135
|
+
throw new Error(`${errMsg.unsupportedVersion}: ${version}. Only "latest", "beta" or "dev" are supported`);
|
|
125
136
|
|
|
126
137
|
checkVersion(version, Edge.#MIN_VERSION, Edge.#CHANNELS);
|
|
127
138
|
let {versionNumber, channel} = await Edge.#getVersionForChannel(version);
|
|
@@ -135,7 +146,7 @@ export class Edge extends Browser {
|
|
|
135
146
|
let archive = path.join(snapshotsDir, "cache", filename);
|
|
136
147
|
let binary = Edge.#getBinaryPath(channel);
|
|
137
148
|
try {
|
|
138
|
-
if (await Edge
|
|
149
|
+
if (await Edge.getInstalledVersion(binary) == versionNumber)
|
|
139
150
|
return {binary, versionNumber};
|
|
140
151
|
}
|
|
141
152
|
catch (e) {}
|
|
@@ -143,16 +154,14 @@ export class Edge extends Browser {
|
|
|
143
154
|
let url = `https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-${channel}/${filename}`;
|
|
144
155
|
try {
|
|
145
156
|
if (platform == "darwin") {
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
const caskFile = channel === "stable" ? "microsoft-edge.json" : `microsoft-edge@${channel}.json`;
|
|
158
|
+
const caskUrl = `https://formulae.brew.sh/api/cask/${caskFile}`;
|
|
148
159
|
try {
|
|
149
|
-
|
|
160
|
+
({url} = await got(caskUrl).json());
|
|
150
161
|
}
|
|
151
162
|
catch (err) {
|
|
152
163
|
throw new Error(`${errMsg.browserVersionCheck}: ${caskUrl}\n${err}`);
|
|
153
164
|
}
|
|
154
|
-
({url} = process.arch == "arm64" ?
|
|
155
|
-
caskJson.variations.arm64_ventura : caskJson);
|
|
156
165
|
}
|
|
157
166
|
await download(url, archive, downloadTimeout);
|
|
158
167
|
}
|
|
@@ -164,15 +173,16 @@ export class Edge extends Browser {
|
|
|
164
173
|
await promisify(exec)(`dpkg -i ${archive}`);
|
|
165
174
|
}
|
|
166
175
|
else if (platform == "darwin") {
|
|
167
|
-
await fs.promises.rm(`${process.env.HOME}/Applications/${Edge.#
|
|
176
|
+
await fs.promises.rm(`${process.env.HOME}/Applications/${Edge.#getDarwinApp(channel)}.app`, {force: true, recursive: true});
|
|
168
177
|
await promisify(exec)(`installer -pkg ${archive} -target CurrentUserHomeDirectory`);
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
return {binary, versionNumber};
|
|
172
181
|
}
|
|
173
182
|
|
|
174
|
-
|
|
175
|
-
|
|
183
|
+
/** @see Browser.getInstalledVersion */
|
|
184
|
+
static async getInstalledVersion(binary) {
|
|
185
|
+
let installedVersion = await super.getInstalledVersion(binary);
|
|
176
186
|
for (let word of ["beta", "dev", "Beta", "Dev"])
|
|
177
187
|
installedVersion = installedVersion.replace(word, "");
|
|
178
188
|
return installedVersion.trim().replace(/.*\s/, "");
|
|
@@ -194,7 +204,7 @@ export class Edge extends Browser {
|
|
|
194
204
|
else {
|
|
195
205
|
binary = customBrowserBinary || Edge.#getBinaryPath();
|
|
196
206
|
versionNumber =
|
|
197
|
-
await Edge
|
|
207
|
+
await Edge.getInstalledVersion(binary);
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
let options = new edge.Options().addArguments("no-sandbox", ...extraArgs);
|
|
@@ -212,10 +222,10 @@ export class Edge extends Browser {
|
|
|
212
222
|
options.addArguments("inprivate");
|
|
213
223
|
if (insecure)
|
|
214
224
|
options.addArguments("ignore-certificate-errors");
|
|
215
|
-
if (platform
|
|
225
|
+
if (platform === "linux" || platform === "darwin")
|
|
216
226
|
options.setEdgeChromiumBinaryPath(binary);
|
|
217
227
|
|
|
218
|
-
let builder = new
|
|
228
|
+
let builder = new Builder();
|
|
219
229
|
builder.forBrowser("MicrosoftEdge");
|
|
220
230
|
builder.setEdgeOptions(options);
|
|
221
231
|
|
|
@@ -234,12 +244,17 @@ export class Edge extends Browser {
|
|
|
234
244
|
}
|
|
235
245
|
}, 30000, `${errMsg.driverStart}: msedgedriver`, 1000);
|
|
236
246
|
|
|
247
|
+
// Developer mode needs to be enabled
|
|
248
|
+
// for custom extensions to work properly
|
|
249
|
+
await enableDeveloperMode(driver);
|
|
250
|
+
|
|
237
251
|
return driver;
|
|
238
252
|
}
|
|
239
253
|
|
|
240
254
|
/** @see Browser.enableExtensionInIncognito */
|
|
241
255
|
static async enableExtensionInIncognito(driver, extensionTitle) {
|
|
242
256
|
await driver.navigate().to("edge://extensions/");
|
|
257
|
+
|
|
243
258
|
for (let elem of await driver.findElements(By.css("[role=listitem]"))) {
|
|
244
259
|
let text = await elem.getAttribute("innerHTML");
|
|
245
260
|
if (!text.includes(extensionTitle))
|
|
@@ -251,8 +266,23 @@ export class Edge extends Browser {
|
|
|
251
266
|
continue;
|
|
252
267
|
|
|
253
268
|
await button.click();
|
|
254
|
-
|
|
269
|
+
await driver.findElement(By.id("itemAllowIncognito")).click();
|
|
270
|
+
|
|
271
|
+
// On Edge 134, extension gets turned off when incognito mode is enabled
|
|
272
|
+
// We need to turn it back on by clicking the toggle
|
|
273
|
+
try {
|
|
274
|
+
await driver.wait(until.elementLocated(
|
|
275
|
+
By.css('[aria-label="Extension on"]:not(:checked)')), 3000).click();
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
// ignoring timeout errors, as the element is not expected
|
|
279
|
+
// to be present for all Edge versions
|
|
280
|
+
if (err.name !== "TimeoutError")
|
|
281
|
+
throw err;
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
255
284
|
}
|
|
285
|
+
|
|
256
286
|
throw new Error(`${errMsg.elemNotFound}: Details button`);
|
|
257
287
|
}
|
|
258
288
|
throw new Error(`${errMsg.extensionNotFound}: ${extensionTitle}`);
|
package/src/firefox.js
CHANGED
|
@@ -21,7 +21,7 @@ import {promisify} from "util";
|
|
|
21
21
|
import fs from "fs";
|
|
22
22
|
|
|
23
23
|
import got from "got";
|
|
24
|
-
import
|
|
24
|
+
import {until, By, Builder} from "selenium-webdriver";
|
|
25
25
|
import firefox from "selenium-webdriver/firefox.js";
|
|
26
26
|
import {Command} from "selenium-webdriver/lib/command.js";
|
|
27
27
|
|
|
@@ -30,8 +30,6 @@ import {download, extractTar, extractDmg, killDriverProcess, wait,
|
|
|
30
30
|
getMajorVersion, checkVersion, checkPlatform, errMsg, snapshotsBaseDir,
|
|
31
31
|
platformArch, checkExtensionPaths} from "./utils.js";
|
|
32
32
|
|
|
33
|
-
let {until, By} = webdriver;
|
|
34
|
-
|
|
35
33
|
/**
|
|
36
34
|
* Browser and webdriver functionality for Firefox.
|
|
37
35
|
* @hideconstructor
|
|
@@ -106,11 +104,12 @@ export class Firefox extends Browser {
|
|
|
106
104
|
}
|
|
107
105
|
}
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
/** @see Browser.getInstalledVersion */
|
|
108
|
+
static async getInstalledVersion(binary) {
|
|
109
|
+
const installedVersion = await super.getInstalledVersion(binary);
|
|
110
|
+
|
|
111
111
|
// Linux example: "Mozilla Firefox 102.15.0esr"
|
|
112
|
-
|
|
113
|
-
return {binary, versionNumber};
|
|
112
|
+
return installedVersion.split(" ")[2] || installedVersion;
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
/**
|
|
@@ -130,10 +129,15 @@ export class Firefox extends Browser {
|
|
|
130
129
|
checkVersion(version, MIN_VERSION, Firefox.#CHANNELS);
|
|
131
130
|
let versionNumber = await Firefox.#getVersionForChannel(version);
|
|
132
131
|
|
|
132
|
+
const majorVersion = getMajorVersion(versionNumber);
|
|
133
|
+
// Starting from Firefox 135 on Linux, the binary file extension
|
|
134
|
+
// was changed from .bz2 to .xz
|
|
135
|
+
const linuxExtension = majorVersion <= 134 ? "tar.bz2" : "tar.xz";
|
|
136
|
+
|
|
133
137
|
let [buildPlatform, fileName] = {
|
|
134
138
|
"win32-ia32": ["win32", `Firefox Setup ${versionNumber}.exe`],
|
|
135
139
|
"win32-x64": ["win64", `Firefox Setup ${versionNumber}.exe`],
|
|
136
|
-
"linux-x64": ["linux-x86_64", `firefox-${versionNumber}
|
|
140
|
+
"linux-x64": ["linux-x86_64", `firefox-${versionNumber}.${linuxExtension}`],
|
|
137
141
|
"darwin-x64": ["mac", `Firefox ${versionNumber}.dmg`],
|
|
138
142
|
"darwin-arm64": ["mac", `Firefox ${versionNumber}.dmg`]
|
|
139
143
|
}[platformArch];
|
|
@@ -215,7 +219,7 @@ export class Firefox extends Browser {
|
|
|
215
219
|
// https://github.com/mozilla/geckodriver/issues/1560
|
|
216
220
|
await wait(async() => {
|
|
217
221
|
try {
|
|
218
|
-
driver = await new
|
|
222
|
+
driver = await new Builder()
|
|
219
223
|
.forBrowser("firefox")
|
|
220
224
|
.setFirefoxOptions(options)
|
|
221
225
|
.build();
|
package/src/utils.js
CHANGED
|
@@ -91,7 +91,15 @@ export async function download(url, destFile, timeout = 0) {
|
|
|
91
91
|
|
|
92
92
|
export async function extractTar(archive, dir) {
|
|
93
93
|
await fs.promises.mkdir(dir);
|
|
94
|
-
|
|
94
|
+
|
|
95
|
+
let command;
|
|
96
|
+
|
|
97
|
+
if (archive.endsWith(".xz"))
|
|
98
|
+
command = `tar -xf ${archive} -C ${dir}`;
|
|
99
|
+
else
|
|
100
|
+
command = `tar -jxf ${archive} -C ${dir}`;
|
|
101
|
+
|
|
102
|
+
await promisify(exec)(command);
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
export async function extractDmg(archive, dir) {
|
package/test/browsers.js
CHANGED
|
@@ -111,7 +111,7 @@ async function getInstallFileCTime(browser) {
|
|
|
111
111
|
process.platform == "darwin"))
|
|
112
112
|
return null;
|
|
113
113
|
|
|
114
|
-
const installTypes = [".zip", ".dmg", ".bz2", ".deb", ".exe"];
|
|
114
|
+
const installTypes = [".zip", ".dmg", ".bz2", ".xz", ".deb", ".exe"];
|
|
115
115
|
|
|
116
116
|
let cacheDir = path.join(snapshotsBaseDir, browser, "cache");
|
|
117
117
|
let cacheFiles = await fs.promises.readdir(cacheDir);
|
|
@@ -1,16 +1,3 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
});
|
|
3
|
+
chrome.tabs.create({url: "index.html"});
|