@eyeo/get-browser-binary 0.12.0 → 0.14.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 +41 -19
- package/RELEASE_NOTES.md +40 -0
- package/index.js +19 -2
- package/package.json +7 -4
- package/src/browser.js +134 -0
- package/src/chromium.js +336 -0
- package/src/edge.js +308 -0
- package/src/firefox.js +216 -0
- package/src/utils.js +43 -0
- package/test/browsers.js +69 -40
- package/test/docker/Dockerfile +1 -1
- package/test/docker/arm64.Dockerfile +7 -2
- package/test/extension/mv3/manifest.json +2 -1
- package/test/pages/basic.html +1 -0
- package/test/pages/download-test.txt +1 -0
- package/test/pages/long.html +41 -0
- package/test/runner.js +74 -0
- package/test/start-server.js +20 -0
- package/test/test-server.js +37 -0
- package/test/utils.js +5 -4
- package/src/browsers.js +0 -919
package/src/firefox.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2006-present eyeo GmbH
|
|
3
|
+
*
|
|
4
|
+
* This module is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License
|
|
15
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import path from "path";
|
|
19
|
+
import {exec} from "child_process";
|
|
20
|
+
import {promisify} from "util";
|
|
21
|
+
import fs from "fs";
|
|
22
|
+
|
|
23
|
+
import got from "got";
|
|
24
|
+
import webdriver from "selenium-webdriver";
|
|
25
|
+
import firefox from "selenium-webdriver/firefox.js";
|
|
26
|
+
|
|
27
|
+
import {Browser} from "./browser.js";
|
|
28
|
+
import {download, extractTar, extractDmg, killDriverProcess, wait,
|
|
29
|
+
getMajorVersion, checkVersion, checkPlatform, errMsg, snapshotsBaseDir,
|
|
30
|
+
platformArch} from "./utils.js";
|
|
31
|
+
|
|
32
|
+
let {until, By} = webdriver;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Browser and webdriver functionality for Firefox.
|
|
36
|
+
* @hideconstructor
|
|
37
|
+
* @extends Browser
|
|
38
|
+
*/
|
|
39
|
+
export class Firefox extends Browser {
|
|
40
|
+
static #CHANNELS = ["latest", "beta"];
|
|
41
|
+
|
|
42
|
+
static async #getVersionForChannel(channel) {
|
|
43
|
+
if (!Firefox.#CHANNELS.includes(channel))
|
|
44
|
+
return channel;
|
|
45
|
+
|
|
46
|
+
let url = "https://product-details.mozilla.org/1.0/firefox_versions.json";
|
|
47
|
+
let data;
|
|
48
|
+
try {
|
|
49
|
+
data = await got(url).json();
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
throw new Error(`${errMsg.browserVersionCheck}: ${url}\n${err}`);
|
|
53
|
+
}
|
|
54
|
+
return channel == "beta" ?
|
|
55
|
+
data.LATEST_FIREFOX_DEVEL_VERSION : data.LATEST_FIREFOX_VERSION;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static #getBinaryPath(dir) {
|
|
59
|
+
checkPlatform();
|
|
60
|
+
return {
|
|
61
|
+
win32: path.join(dir, "core", "firefox.exe"),
|
|
62
|
+
linux: path.join(dir, "firefox", "firefox"),
|
|
63
|
+
darwin: path.join(dir, "Firefox.app", "Contents", "MacOS", "firefox")
|
|
64
|
+
}[process.platform];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static #extractFirefoxArchive(archive, dir) {
|
|
68
|
+
switch (process.platform) {
|
|
69
|
+
case "win32":
|
|
70
|
+
return promisify(exec)(`"${archive}" /extractdir=${dir}`);
|
|
71
|
+
case "linux":
|
|
72
|
+
return extractTar(archive, dir);
|
|
73
|
+
case "darwin":
|
|
74
|
+
return extractDmg(archive, dir);
|
|
75
|
+
default:
|
|
76
|
+
checkPlatform();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static async #getInstalledBrowserInfo(binary) {
|
|
81
|
+
let installedVersion = await Firefox.getInstalledVersion(binary);
|
|
82
|
+
// Linux example: "Mozilla Firefox 102.15.0esr"
|
|
83
|
+
let versionNumber = installedVersion.split(" ")[2] || installedVersion;
|
|
84
|
+
return {binary, versionNumber};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Installs the browser. The Firefox executable gets extracted in the
|
|
89
|
+
* {@link snapshotsBaseDir} folder, ready to go.
|
|
90
|
+
* @param {string} [version=latest] Either "latest", "beta" or a full version
|
|
91
|
+
* number (i.e. "68.0").
|
|
92
|
+
* @param {number} [downloadTimeout=0] Allowed time in ms for the download of
|
|
93
|
+
* install files to complete. When set to 0 there is no time limit.
|
|
94
|
+
* @return {BrowserBinary}
|
|
95
|
+
* @throws {Error} Unsupported browser version, Unsupported platform, Browser
|
|
96
|
+
* download failed.
|
|
97
|
+
*/
|
|
98
|
+
static async installBrowser(version = "latest", downloadTimeout = 0) {
|
|
99
|
+
const MIN_VERSION = 68;
|
|
100
|
+
|
|
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
|
+
checkVersion(version, MIN_VERSION, Firefox.#CHANNELS);
|
|
106
|
+
let versionNumber = await Firefox.#getVersionForChannel(version);
|
|
107
|
+
|
|
108
|
+
let [buildPlatform, fileName] = {
|
|
109
|
+
"win32-ia32": ["win32", `Firefox Setup ${versionNumber}.exe`],
|
|
110
|
+
"win32-x64": ["win64", `Firefox Setup ${versionNumber}.exe`],
|
|
111
|
+
"linux-x64": ["linux-x86_64", `firefox-${versionNumber}.tar.bz2`],
|
|
112
|
+
"darwin-x64": ["mac", `Firefox ${versionNumber}.dmg`],
|
|
113
|
+
"darwin-arm64": ["mac", `Firefox ${versionNumber}.dmg`]
|
|
114
|
+
}[platformArch];
|
|
115
|
+
|
|
116
|
+
let snapshotsDir = path.join(snapshotsBaseDir, "firefox");
|
|
117
|
+
let browserDir = path.join(snapshotsDir,
|
|
118
|
+
`firefox-${platformArch}-${versionNumber}`);
|
|
119
|
+
let binary = Firefox.#getBinaryPath(browserDir);
|
|
120
|
+
try {
|
|
121
|
+
await fs.promises.access(browserDir);
|
|
122
|
+
return {binary, versionNumber};
|
|
123
|
+
}
|
|
124
|
+
catch (e) {}
|
|
125
|
+
|
|
126
|
+
let archive = path.join(snapshotsDir, "cache", fileName);
|
|
127
|
+
await fs.promises.mkdir(path.dirname(browserDir), {recursive: true});
|
|
128
|
+
try {
|
|
129
|
+
await fs.promises.access(archive);
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
let url = `https://archive.mozilla.org/pub/firefox/releases/${versionNumber}/${buildPlatform}/en-US/${fileName}`;
|
|
133
|
+
try {
|
|
134
|
+
await download(url, archive, downloadTimeout);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
throw new Error(`${errMsg.browserDownload}: ${url}\n${err}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
await Firefox.#extractFirefoxArchive(archive, browserDir);
|
|
141
|
+
|
|
142
|
+
return {binary, versionNumber};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** @see Browser.getDriver */
|
|
146
|
+
static async getDriver(version = "latest", {
|
|
147
|
+
headless = true, extensionPaths = [], incognito = false, insecure = false,
|
|
148
|
+
extraArgs = [], customBrowserBinary
|
|
149
|
+
} = {}, downloadTimeout = 0) {
|
|
150
|
+
let binary;
|
|
151
|
+
let versionNumber;
|
|
152
|
+
if (!customBrowserBinary) {
|
|
153
|
+
({binary, versionNumber} =
|
|
154
|
+
await Firefox.installBrowser(version, downloadTimeout));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let options = new firefox.Options();
|
|
158
|
+
if (headless)
|
|
159
|
+
options.headless();
|
|
160
|
+
if (incognito)
|
|
161
|
+
options.addArguments("--private");
|
|
162
|
+
if (insecure)
|
|
163
|
+
options.set("acceptInsecureCerts", true);
|
|
164
|
+
if (extraArgs.length > 0)
|
|
165
|
+
options.addArguments(...extraArgs);
|
|
166
|
+
// Enabled by default on Firefox > 68
|
|
167
|
+
if (versionNumber && getMajorVersion(versionNumber) == 68)
|
|
168
|
+
options.setPreference("dom.promise_rejection_events.enabled", true);
|
|
169
|
+
|
|
170
|
+
options.setBinary(customBrowserBinary || binary);
|
|
171
|
+
|
|
172
|
+
let driver;
|
|
173
|
+
// The OS may be low on resources, that's why building the driver is retried
|
|
174
|
+
// https://github.com/mozilla/geckodriver/issues/1560
|
|
175
|
+
await wait(async() => {
|
|
176
|
+
try {
|
|
177
|
+
driver = await new webdriver.Builder()
|
|
178
|
+
.forBrowser("firefox")
|
|
179
|
+
.setFirefoxOptions(options)
|
|
180
|
+
.build();
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
if (err.message != "Failed to decode response from marionette")
|
|
185
|
+
throw err;
|
|
186
|
+
await killDriverProcess("geckodriver");
|
|
187
|
+
}
|
|
188
|
+
}, 30000, `${errMsg.driverStart}: geckodriver`, 1000);
|
|
189
|
+
|
|
190
|
+
for (let extensionPath of extensionPaths) {
|
|
191
|
+
let temporary = true; // Parameter not documented on the webdriver docs
|
|
192
|
+
await driver.installAddon(extensionPath, temporary);
|
|
193
|
+
}
|
|
194
|
+
return driver;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** @see Browser.enableExtensionInIncognito */
|
|
198
|
+
static async enableExtensionInIncognito(driver, extensionTitle) {
|
|
199
|
+
let version = (await driver.getCapabilities()).getBrowserVersion();
|
|
200
|
+
// The UI workaround assumes web elements only present on Firefox >= 87
|
|
201
|
+
checkVersion(version, 87);
|
|
202
|
+
|
|
203
|
+
await driver.navigate().to("about:addons");
|
|
204
|
+
await driver.wait(until.elementLocated(By.name("extension")), 1000).click();
|
|
205
|
+
|
|
206
|
+
for (let elem of await driver.findElements(By.className("card addon"))) {
|
|
207
|
+
let text = await elem.getAttribute("innerHTML");
|
|
208
|
+
if (!text.includes(extensionTitle))
|
|
209
|
+
continue;
|
|
210
|
+
|
|
211
|
+
await elem.click();
|
|
212
|
+
return await driver.findElement(By.name("private-browsing")).click();
|
|
213
|
+
}
|
|
214
|
+
throw new Error(`${errMsg.extensionNotFound}: ${extensionTitle}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
package/src/utils.js
CHANGED
|
@@ -25,6 +25,24 @@ import got from "got";
|
|
|
25
25
|
import dmg from "dmg";
|
|
26
26
|
import Jimp from "jimp";
|
|
27
27
|
|
|
28
|
+
export const errMsg = {
|
|
29
|
+
unsupportedVersion: "Unsupported browser version",
|
|
30
|
+
unsupportedPlatform: "Unsupported platform",
|
|
31
|
+
driverDownload: "Driver download failed",
|
|
32
|
+
driverStart: "Unable to start driver",
|
|
33
|
+
extensionNotFound: "Extension not found",
|
|
34
|
+
browserDownload: "Browser download failed",
|
|
35
|
+
browserNotInstalled: "Browser is not installed",
|
|
36
|
+
browserVersionCheck: "Checking the browser version failed",
|
|
37
|
+
elemNotFound: "HTML element not found"
|
|
38
|
+
};
|
|
39
|
+
export const platformArch = `${process.platform}-${process.arch}`;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Root folder where browser and webdriver files get downloaded and extracted.
|
|
43
|
+
* @type {string}
|
|
44
|
+
*/
|
|
45
|
+
export let snapshotsBaseDir = path.join(process.cwd(), "browser-snapshots");
|
|
28
46
|
|
|
29
47
|
/**
|
|
30
48
|
* Downloads url resources.
|
|
@@ -35,6 +53,10 @@ import Jimp from "jimp";
|
|
|
35
53
|
* @throws {TypeError} Invalid URL, Download timeout.
|
|
36
54
|
*/
|
|
37
55
|
export async function download(url, destFile, timeout = 0) {
|
|
56
|
+
if (process.env.VERBOSE == "true")
|
|
57
|
+
// eslint-disable-next-line no-console
|
|
58
|
+
console.log(`Downloading from ${url} ...`);
|
|
59
|
+
|
|
38
60
|
let cacheDir = path.dirname(destFile);
|
|
39
61
|
await fs.promises.mkdir(cacheDir, {recursive: true});
|
|
40
62
|
|
|
@@ -215,3 +237,24 @@ export async function takeFullPageScreenshot(driver, hideScrollbars = true) {
|
|
|
215
237
|
|
|
216
238
|
return fullScreenshot;
|
|
217
239
|
}
|
|
240
|
+
|
|
241
|
+
export function getMajorVersion(versionNumber) {
|
|
242
|
+
let majorVersion = parseInt(versionNumber && versionNumber.split(".")[0], 10);
|
|
243
|
+
if (isNaN(majorVersion))
|
|
244
|
+
throw new Error(`${errMsg.unsupportedVersion}: ${versionNumber}`);
|
|
245
|
+
|
|
246
|
+
return majorVersion;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function checkVersion(version, minVersion, channels = []) {
|
|
250
|
+
if (channels.includes(version))
|
|
251
|
+
return;
|
|
252
|
+
|
|
253
|
+
if (getMajorVersion(version) < minVersion)
|
|
254
|
+
throw new Error(`${errMsg.unsupportedVersion}: ${version}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function checkPlatform() {
|
|
258
|
+
if (!["win32", "linux", "darwin"].includes(process.platform))
|
|
259
|
+
throw new Error(`${errMsg.unsupportedPlatform}: ${process.platform}`);
|
|
260
|
+
}
|
package/test/browsers.js
CHANGED
|
@@ -18,20 +18,22 @@
|
|
|
18
18
|
import fs from "fs";
|
|
19
19
|
import {expect} from "expect";
|
|
20
20
|
import path from "path";
|
|
21
|
+
import Jimp from "jimp";
|
|
22
|
+
import {By} from "selenium-webdriver";
|
|
21
23
|
|
|
22
24
|
import {BROWSERS, snapshotsBaseDir, takeFullPageScreenshot} from "../index.js";
|
|
23
25
|
import {killDriverProcess} from "../src/utils.js";
|
|
24
|
-
import
|
|
26
|
+
import {TEST_SERVER_URL} from "./test-server.js";
|
|
25
27
|
|
|
26
28
|
import "geckodriver"; // Required to set the driver path on Windows
|
|
27
29
|
|
|
28
30
|
const VERSIONS = {
|
|
29
|
-
chromium: ["latest", "
|
|
30
|
-
firefox: ["latest", "
|
|
31
|
-
edge: ["
|
|
31
|
+
chromium: ["latest", "77.0.3865.0", "beta", "dev"],
|
|
32
|
+
firefox: ["latest", "68.0", "beta"],
|
|
33
|
+
edge: ["latest", "95.0.1020.40", "beta", "dev"]
|
|
32
34
|
};
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
+
const TEST_URL_BASIC = `${TEST_SERVER_URL}/basic.html`;
|
|
36
|
+
const TEST_URL_LONG = `${TEST_SERVER_URL}/long.html`;
|
|
35
37
|
|
|
36
38
|
async function switchToHandle(driver, testFn) {
|
|
37
39
|
for (let handle of await driver.getAllWindowHandles()) {
|
|
@@ -105,14 +107,20 @@ function getExtension(browser, version) {
|
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
async function getCachedTimes(browser) {
|
|
108
|
-
let
|
|
109
|
-
process.platform == "linux" && process.arch == "arm64";
|
|
110
|
+
let armLinux = process.platform == "linux" && process.arch == "arm64";
|
|
110
111
|
let cacheDir = path.join(snapshotsBaseDir, browser, "cache");
|
|
111
|
-
let cacheFiles
|
|
112
|
+
let cacheFiles;
|
|
113
|
+
try {
|
|
114
|
+
cacheFiles = await fs.promises.readdir(cacheDir);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
if (!armLinux)
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
112
120
|
|
|
113
121
|
let browserCacheTime = null;
|
|
114
|
-
//
|
|
115
|
-
if (browser != "edge" && !
|
|
122
|
+
// Browsers installed at OS level are not tested
|
|
123
|
+
if (browser != "edge" && !armLinux) {
|
|
116
124
|
let installTypes = [".zip", ".dmg", ".bz2"];
|
|
117
125
|
let browserZip =
|
|
118
126
|
cacheFiles.find(elem => installTypes.some(type => elem.includes(type)));
|
|
@@ -122,7 +130,8 @@ async function getCachedTimes(browser) {
|
|
|
122
130
|
|
|
123
131
|
let driverCacheTime = null;
|
|
124
132
|
// geckodriver is installed by npm, that's why Firefox is not tested here
|
|
125
|
-
|
|
133
|
+
// for chromiumdriver on arm linux, see https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/46
|
|
134
|
+
if (browser != "firefox" && !armLinux) {
|
|
126
135
|
let driverDir = cacheFiles.find(
|
|
127
136
|
elem => !elem.endsWith(".zip") && !elem.endsWith(".pkg"));
|
|
128
137
|
let driverFiles = await fs.promises.readdir(path.join(cacheDir, driverDir));
|
|
@@ -135,6 +144,21 @@ async function getCachedTimes(browser) {
|
|
|
135
144
|
return {browserCTime: browserCacheTime, driverCTime: driverCacheTime};
|
|
136
145
|
}
|
|
137
146
|
|
|
147
|
+
const browserNames = {
|
|
148
|
+
chromium: "chrome",
|
|
149
|
+
firefox: "firefox",
|
|
150
|
+
edge: /(MicrosoftEdge|msedge)/
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
async function basicUrlTest(driver, browser) {
|
|
154
|
+
await driver.navigate().to(TEST_URL_BASIC);
|
|
155
|
+
expect((await driver.getCapabilities()).getBrowserName())
|
|
156
|
+
.toEqual(expect.stringMatching(browserNames[browser]));
|
|
157
|
+
|
|
158
|
+
let text = await driver.findElement(By.id("basic")).getText();
|
|
159
|
+
expect(text).toEqual("Test server basic page");
|
|
160
|
+
}
|
|
161
|
+
|
|
138
162
|
for (let browser of Object.keys(BROWSERS)) {
|
|
139
163
|
describe(`Browser: ${browser}`, () => {
|
|
140
164
|
before(async() => {
|
|
@@ -149,6 +173,7 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
149
173
|
let driver = null;
|
|
150
174
|
let emptyCacheTimes = null;
|
|
151
175
|
let customBrowserBinary = null;
|
|
176
|
+
let failedInstall = false;
|
|
152
177
|
|
|
153
178
|
async function quitDriver() {
|
|
154
179
|
if (!driver)
|
|
@@ -163,14 +188,27 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
163
188
|
await killDriverProcess("geckodriver");
|
|
164
189
|
}
|
|
165
190
|
|
|
191
|
+
beforeEach(function() {
|
|
192
|
+
if (failedInstall)
|
|
193
|
+
this.skip();
|
|
194
|
+
});
|
|
195
|
+
|
|
166
196
|
afterEach(quitDriver);
|
|
167
197
|
|
|
168
198
|
it("installs", async function() {
|
|
169
199
|
if (browser == "edge" && process.platform == "win32")
|
|
170
200
|
this.skip();
|
|
171
201
|
|
|
172
|
-
let
|
|
173
|
-
|
|
202
|
+
let binary;
|
|
203
|
+
let versionNumber;
|
|
204
|
+
try {
|
|
205
|
+
({binary, versionNumber} =
|
|
206
|
+
await BROWSERS[browser].installBrowser(version, this.timeout()));
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
failedInstall = true;
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
174
212
|
let browserName = browser == "edge" ? /(edge|Edge)/ : browser;
|
|
175
213
|
expect(binary).toEqual(expect.stringMatching(browserName));
|
|
176
214
|
|
|
@@ -185,17 +223,8 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
185
223
|
});
|
|
186
224
|
|
|
187
225
|
it("runs", async() => {
|
|
188
|
-
let names = {
|
|
189
|
-
chromium: "chrome",
|
|
190
|
-
firefox: "firefox",
|
|
191
|
-
edge: /(MicrosoftEdge|msedge)/
|
|
192
|
-
};
|
|
193
|
-
|
|
194
226
|
driver = await BROWSERS[browser].getDriver(version);
|
|
195
|
-
await driver
|
|
196
|
-
|
|
197
|
-
expect((await driver.getCapabilities()).getBrowserName())
|
|
198
|
-
.toEqual(expect.stringMatching(names[browser]));
|
|
227
|
+
await basicUrlTest(driver, browser);
|
|
199
228
|
|
|
200
229
|
// When running all tests, this saves time on the cache test
|
|
201
230
|
emptyCacheTimes = await getCachedTimes(browser);
|
|
@@ -220,41 +249,35 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
220
249
|
if (!customBrowserBinary)
|
|
221
250
|
this.skip();
|
|
222
251
|
|
|
223
|
-
let names = {
|
|
224
|
-
chromium: "chrome",
|
|
225
|
-
firefox: "firefox",
|
|
226
|
-
edge: /(MicrosoftEdge|msedge)/
|
|
227
|
-
};
|
|
228
|
-
|
|
229
252
|
driver =
|
|
230
253
|
await BROWSERS[browser].getDriver(version, {customBrowserBinary});
|
|
231
|
-
await driver
|
|
254
|
+
await basicUrlTest(driver, browser);
|
|
232
255
|
|
|
233
256
|
expect((await driver.getCapabilities()).getBrowserName())
|
|
234
|
-
.toEqual(expect.stringMatching(
|
|
257
|
+
.toEqual(expect.stringMatching(browserNames[browser]));
|
|
235
258
|
});
|
|
236
259
|
|
|
237
260
|
it("supports extra args", async() => {
|
|
238
261
|
let headless = false;
|
|
239
262
|
let extraArgs = browser == "firefox" ?
|
|
240
|
-
["
|
|
263
|
+
["-width=600", "-height=400"] : ["window-size=600,400"];
|
|
241
264
|
|
|
242
265
|
driver = await BROWSERS[browser].getDriver(
|
|
243
266
|
version, {headless, extraArgs});
|
|
244
|
-
await driver.navigate().to(
|
|
245
|
-
let
|
|
267
|
+
await driver.navigate().to(TEST_URL_BASIC);
|
|
268
|
+
let sizeSmall = await getWindowSize(driver);
|
|
246
269
|
await quitDriver();
|
|
247
270
|
|
|
248
271
|
driver = await BROWSERS[browser].getDriver(version, {headless});
|
|
249
|
-
await driver.navigate().to(
|
|
250
|
-
let
|
|
272
|
+
await driver.navigate().to(TEST_URL_BASIC);
|
|
273
|
+
let sizeDefault = await getWindowSize(driver);
|
|
251
274
|
|
|
252
|
-
expect(
|
|
275
|
+
expect(sizeSmall).toMeasureLessThan(sizeDefault);
|
|
253
276
|
});
|
|
254
277
|
|
|
255
278
|
it("takes a full page screenshot", async() => {
|
|
256
279
|
driver = await BROWSERS[browser].getDriver(version);
|
|
257
|
-
await driver.navigate().to(
|
|
280
|
+
await driver.navigate().to(TEST_URL_LONG);
|
|
258
281
|
|
|
259
282
|
let fullImg = await takeFullPageScreenshot(driver);
|
|
260
283
|
// Taking a regular webdriver screenshot, which should be shorter
|
|
@@ -277,7 +300,7 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
277
300
|
});
|
|
278
301
|
|
|
279
302
|
it("loads an extension in incognito mode", async function() {
|
|
280
|
-
if (browser == "firefox" && version == "
|
|
303
|
+
if (browser == "firefox" && version == "68.0")
|
|
281
304
|
this.skip();
|
|
282
305
|
|
|
283
306
|
let {extensionPaths, manifest} = getExtension(browser, version);
|
|
@@ -300,5 +323,11 @@ for (let browser of Object.keys(BROWSERS)) {
|
|
|
300
323
|
.rejects.toThrow(`Unsupported browser version: ${unsupported}`);
|
|
301
324
|
}
|
|
302
325
|
});
|
|
326
|
+
|
|
327
|
+
it("does not run not installed custom browsers", async() => {
|
|
328
|
+
let customBrowserBinary = "not-installed";
|
|
329
|
+
await expect(BROWSERS[browser].getDriver("latest", {customBrowserBinary}))
|
|
330
|
+
.rejects.toThrow(/(Browser is not installed|binary is not a Firefox executable)/);
|
|
331
|
+
});
|
|
303
332
|
});
|
|
304
333
|
}
|
package/test/docker/Dockerfile
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Duplicated from registry.gitlab.com/eyeo/docker/get-browser-binary:
|
|
1
|
+
# Duplicated from registry.gitlab.com/eyeo/docker/get-browser-binary:node18
|
|
2
2
|
# https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/46
|
|
3
|
-
FROM node:
|
|
3
|
+
FROM node:18-bullseye-slim
|
|
4
4
|
# General packages
|
|
5
5
|
RUN apt-get update && apt-get install -y git procps wget unzip bzip2 gnupg
|
|
6
6
|
# xvfb (headful browser run)
|
|
@@ -12,6 +12,11 @@ RUN apt-get install -y fonts-liberation libatomic1 xdg-utils libu2f-udev
|
|
|
12
12
|
|
|
13
13
|
# Chromium ARM
|
|
14
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
|
|
15
20
|
|
|
16
21
|
COPY package*.json get-browser-binary/
|
|
17
22
|
RUN cd get-browser-binary && npm install
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<h1 id="basic">Test server basic page</h1>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Download test
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<h1>Test server long page</h1>
|
|
2
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum a sollicitudin elit. Vivamus ornare fermentum mi vel laoreet. Nullam eget risus gravida, placerat tellus at, mattis orci. Sed libero sapien, facilisis ac dignissim eget, eleifend et massa. Praesent sit amet pharetra elit. Cras vitae lorem elit. Duis sagittis nec libero commodo eleifend. Pellentesque volutpat elit cursus semper varius. Donec tempor sit amet purus id lacinia. Ut dui enim, tincidunt non ornare non, hendrerit non ante. Phasellus vel varius turpis, vulputate ultrices ex. Maecenas egestas magna eget lobortis sodales. Proin ut ex vulputate, efficitur urna eget, facilisis felis. Aliquam et bibendum lectus.
|
|
3
|
+
</p>
|
|
4
|
+
<p>Etiam vitae laoreet mi, eu pulvinar turpis. Nullam volutpat est quis erat imperdiet auctor. Morbi vulputate risus at eleifend vestibulum. Nulla pharetra bibendum enim, sit amet tempus libero posuere in. Phasellus ac dui nunc. Aliquam vehicula nibh lorem, feugiat pharetra urna varius vitae. Suspendisse potenti. Maecenas non rutrum quam. Phasellus nunc mauris, gravida eu augue eget, consectetur scelerisque arcu. Pellentesque venenatis molestie nisi eget imperdiet. Sed convallis convallis nisl, at malesuada lectus tristique eu. Vestibulum rhoncus in sapien et efficitur.
|
|
5
|
+
</p>
|
|
6
|
+
<p>Quisque ac nunc a felis vestibulum pulvinar. Donec sodales mollis blandit. Ut porta tortor et metus semper bibendum. Morbi dignissim volutpat tempus. Donec venenatis ut ipsum id ullamcorper. Vestibulum urna mauris, ultricies quis dapibus semper, finibus non ex. Suspendisse porttitor, justo et tristique tincidunt, erat turpis sagittis velit, gravida imperdiet metus ex ut nulla. Maecenas semper mi ligula, non pharetra tellus gravida nec. Donec ut nunc vel nulla efficitur consectetur in ut tortor. Ut consectetur sit amet tellus id tempor.
|
|
7
|
+
</p>
|
|
8
|
+
<p>Quisque nec egestas risus. Nam hendrerit ex felis, semper finibus enim bibendum in. Suspendisse sed lobortis metus, at eleifend nisi. Etiam nec gravida felis, ut tincidunt nunc. Proin condimentum laoreet lacus eu facilisis. Aenean ut commodo mauris, sit amet imperdiet enim. In dapibus auctor leo, sit amet facilisis nisl venenatis in. Morbi a magna sit amet metus efficitur bibendum. Nunc sit amet purus id purus malesuada dignissim. Donec metus arcu, tincidunt eget malesuada eget, laoreet a ex. Donec tincidunt pretium ex, eget iaculis nisl semper nec. Ut ac tortor est.
|
|
9
|
+
</p>
|
|
10
|
+
<p>Vivamus sed lorem metus. Maecenas eu ipsum finibus, pretium urna quis, ultrices lectus. Donec sem purus, consectetur non lobortis at, ultrices eget nulla. Duis est ligula, venenatis sit amet tortor id, placerat hendrerit enim. Curabitur in fringilla est. Cras quis arcu ut libero venenatis imperdiet. Donec quis nunc non odio gravida porttitor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Maecenas mauris mi, venenatis quis imperdiet at, fringilla ac quam. Pellentesque tincidunt viverra ex, at pulvinar odio placerat et. Nullam suscipit facilisis lorem ut imperdiet. Aenean dictum, purus ut mollis efficitur, leo diam maximus mauris, et facilisis nibh nisl vitae quam.
|
|
11
|
+
</p>
|
|
12
|
+
<p>Ut euismod ut ligula mollis feugiat. Cras congue eros at urna posuere, id pretium turpis sodales. Suspendisse eu sapien ut felis placerat varius ullamcorper id tellus. Vestibulum facilisis sodales accumsan. Nunc enim metus, volutpat sed consequat sed, tincidunt in dui. Curabitur auctor ac nisl sed eleifend. Suspendisse id ante magna. Aliquam est justo, congue id facilisis ut, suscipit in nunc. Nunc at leo nec purus auctor fringilla. Morbi tincidunt aliquet arcu, eu tempus enim eleifend a. Morbi porttitor metus et ultrices pellentesque. Curabitur pharetra sit amet velit quis dictum.
|
|
13
|
+
</p>
|
|
14
|
+
<p>Nam laoreet semper erat, non pellentesque lacus euismod a. Sed quis massa interdum augue pellentesque blandit. Proin nulla neque, bibendum in nisi nec, sollicitudin aliquam erat. Aliquam bibendum nulla augue, eget dictum lorem suscipit vestibulum. Nulla purus nulla, egestas sed diam ultricies, porta hendrerit lectus. Nunc tristique eu felis a sagittis. Pellentesque vulputate fermentum tellus. Morbi leo arcu, tempor ut mollis in, ultricies id odio. Curabitur vel diam ultrices, porttitor diam eu, gravida velit. Aliquam fringilla iaculis diam, tristique rhoncus odio pulvinar et. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec hendrerit molestie velit, eget tempor velit volutpat in. Donec ac felis et nibh vulputate bibendum. Suspendisse pharetra feugiat diam nec rhoncus. Curabitur sodales hendrerit nulla eget venenatis.
|
|
15
|
+
</p>
|
|
16
|
+
<p>Vivamus vel ante ac velit congue tincidunt. Suspendisse potenti. Praesent sed nisl gravida, volutpat enim id, molestie risus. Mauris nec elit rhoncus nunc porttitor sodales. Proin gravida, sem eleifend maximus scelerisque, ipsum turpis elementum diam, eget lacinia quam sem in arcu. Maecenas nec elit vel nisi lacinia iaculis at hendrerit diam. Maecenas lobortis dui vitae dolor gravida ultricies. Integer pretium est lacus, in accumsan tortor convallis sed. Phasellus ornare nisl sit amet risus ultrices, nec vulputate libero vehicula. Mauris iaculis euismod erat vel molestie. Donec molestie tincidunt arcu vel mattis. Cras elementum tempus tellus non fringilla. Curabitur varius erat eget quam blandit porttitor. Nunc tincidunt justo vel turpis interdum, vel congue turpis molestie. In ante felis, gravida vel porta non, ultricies quis est. Suspendisse commodo faucibus felis, at dignissim odio egestas nec.
|
|
17
|
+
</p>
|
|
18
|
+
<p>Aliquam gravida nunc a tristique imperdiet. Donec ac suscipit turpis, in placerat lorem. Cras tellus dui, porttitor vel ultrices quis, euismod quis libero. Phasellus semper nunc turpis, non fermentum ligula tempus eu. Quisque id dui in nisi vestibulum sodales non ac lectus. Etiam augue dui, mattis et placerat rhoncus, pulvinar at magna. Vivamus rhoncus mollis tellus, in auctor lacus consectetur pharetra. Proin feugiat faucibus mi, quis tincidunt lacus lacinia sed. Suspendisse condimentum magna non urna blandit, nec ultricies quam tempor. Aenean eu nunc ac felis eleifend tincidunt eget vitae mi. Sed convallis quis lectus eu hendrerit. Aliquam erat volutpat. Suspendisse efficitur sem quis suscipit egestas.
|
|
19
|
+
</p>
|
|
20
|
+
<p>Nulla egestas quam a volutpat luctus. Donec tincidunt ex ut libero fermentum, quis vulputate nunc fringilla. Nam tempor dignissim lacus, et maximus neque accumsan in. Quisque nec neque porttitor, faucibus mi eget, dapibus nisi. Praesent fringilla, tortor aliquet feugiat euismod, turpis erat eleifend dui, non ultricies lacus mi ut orci. Pellentesque imperdiet vestibulum leo. Ut vitae tristique enim, a finibus ipsum. Nulla commodo metus metus. Curabitur lorem nibh, egestas sed leo quis, finibus rhoncus massa.
|
|
21
|
+
</p>
|
|
22
|
+
<p>Etiam tempor mauris id erat vulputate dignissim. Aliquam erat volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Integer aliquet in purus a laoreet. Nam tincidunt consectetur nisi, nec lacinia tortor mollis quis. Mauris pretium, nisl id imperdiet dictum, elit sem pellentesque tortor, eu faucibus neque ipsum in turpis. Quisque nibh nisi, facilisis eu dignissim sit amet, scelerisque vel purus. Phasellus ullamcorper eu leo quis maximus. Nullam vitae augue rutrum, pellentesque ex sit amet, bibendum purus. Cras sed leo ipsum. Morbi nibh est, rutrum nec convallis et, ultrices quis lorem. Maecenas maximus nisi non nisi ultrices maximus.
|
|
23
|
+
</p>
|
|
24
|
+
<p>Nunc a accumsan est. Morbi placerat lorem et dui molestie, ut pharetra massa mollis. Aenean molestie urna sed dignissim varius. Aliquam erat volutpat. Integer scelerisque, odio a aliquet tincidunt, ex felis volutpat est, id elementum lectus libero id tellus. Aenean non odio quis est rutrum faucibus eget in lorem. Suspendisse potenti. In pretium elit ac congue tincidunt. Vivamus sed sem at nibh fermentum accumsan.
|
|
25
|
+
</p>
|
|
26
|
+
<p>Etiam lacinia sit amet nisl ornare blandit. Nullam ullamcorper aliquet dictum. Suspendisse mauris quam, maximus eu molestie vitae, dapibus fermentum sapien. Fusce mi lectus, consectetur a nunc eget, porta sagittis diam. Vestibulum ac ex id lectus fermentum dictum et in dui. Nulla faucibus pellentesque mi, sit amet ultricies tortor feugiat eu. Suspendisse gravida pellentesque magna. Donec eu mauris diam.
|
|
27
|
+
</p>
|
|
28
|
+
<p>Cras fringilla nisl a tempor venenatis. Nulla ut magna sed metus pretium convallis. Vivamus volutpat ac lectus vel pulvinar. Aliquam dignissim, tortor non fringilla tincidunt, eros erat sodales orci, consequat elementum elit mi sed elit. Donec pharetra rhoncus purus a iaculis. In quis lacus quis nisl facilisis volutpat at in nulla. Fusce a tincidunt velit. Integer eget urna nibh.
|
|
29
|
+
</p>
|
|
30
|
+
<p>Phasellus ac justo lacus. Vestibulum ut pulvinar nunc, et pellentesque urna. Nam venenatis magna eget tortor aliquam dapibus. Vivamus id arcu vitae tortor egestas semper. Maecenas mollis at augue id bibendum. Mauris lobortis velit id enim efficitur vestibulum. Pellentesque at est a odio blandit pretium. Proin nec ornare sem, vel ornare felis.
|
|
31
|
+
</p>
|
|
32
|
+
<p>Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In leo leo, tempor a finibus eu, placerat vel sapien. Nullam nisl augue, tempor ultricies dictum eu, ultricies nec nisi. Nam nibh sapien, porta vel dignissim ac, laoreet nec ligula. Praesent luctus, dui a bibendum placerat, ipsum ex venenatis nulla, eu congue erat justo quis nulla. Nulla est nunc, blandit ac purus nec, ullamcorper pharetra tortor. Aenean lobortis enim non orci rhoncus venenatis. Cras vel metus in nulla porta bibendum ut quis urna. Fusce non ex ac nisi facilisis porta. Etiam vel tellus in lorem porttitor maximus at eget erat. Ut interdum, elit a facilisis hendrerit, neque tellus sodales nisl, at ultrices augue ex sit amet urna.
|
|
33
|
+
</p>
|
|
34
|
+
<p>Donec ornare lorem erat, quis mollis nisl dignissim blandit. Nunc scelerisque, turpis a pellentesque laoreet, arcu massa dictum nisl, sit amet egestas lacus dolor et odio. In metus mauris, venenatis ac odio quis, porttitor cursus eros. Fusce pellentesque consequat congue. Nullam iaculis ipsum in ipsum lobortis efficitur. In et mauris augue. Cras eu faucibus justo. Integer hendrerit gravida massa, non auctor purus efficitur ac. Pellentesque non lorem massa. Aliquam vitae tincidunt risus. Fusce et elit eget ex ullamcorper cursus ac nec quam. Vestibulum cursus mauris vitae ipsum sagittis tempor. Ut eleifend ligula in elementum hendrerit. Vivamus malesuada aliquam placerat.
|
|
35
|
+
</p>
|
|
36
|
+
<p>Fusce pharetra ligula id scelerisque tristique. Interdum et malesuada fames ac ante ipsum primis in faucibus. Cras commodo sagittis neque et vulputate. Curabitur erat est, pulvinar ac semper id, euismod vitae elit. Proin ut dolor semper, suscipit ipsum eget, molestie arcu. Sed sodales mauris nulla, ullamcorper cursus nunc malesuada sit amet. Proin sed condimentum orci. Suspendisse aliquam sem ac purus dictum commodo. Nullam ultricies sapien elit, eget mollis elit porta sit amet. Nulla fermentum, mi porta consequat malesuada, metus dui commodo ante, eu vestibulum arcu erat at tortor. Phasellus dolor nunc, cursus ultrices vestibulum vel, rhoncus auctor ante. Quisque consectetur sed ligula non tempus. Pellentesque faucibus est vitae quam sodales tristique. Proin et fringilla quam, sed faucibus enim. Ut convallis, urna eu condimentum scelerisque, magna tellus iaculis sem, sed rhoncus leo purus a nulla.
|
|
37
|
+
</p>
|
|
38
|
+
<p>Morbi tellus lacus, tempus in orci ac, molestie lobortis neque. Fusce pretium ex nec erat faucibus congue ut a lorem. Morbi a pretium justo. Donec interdum elit vitae blandit tincidunt. Proin nisi magna, imperdiet id accumsan id, maximus vel risus. Nunc quis fermentum est. Nam at nibh ac ipsum tincidunt viverra eget tincidunt massa. Nam convallis metus tortor. Aenean at tincidunt lacus.
|
|
39
|
+
</p>
|
|
40
|
+
<p>Cras ultricies iaculis massa et commodo. Suspendisse efficitur magna purus, id egestas tellus consectetur eu. Nullam ante sapien, mollis sit amet purus sed, volutpat consequat tortor. Cras pharetra tincidunt gravida. Suspendisse non ante iaculis, mattis metus id, elementum metus. Nam elementum suscipit lectus, eget laoreet dui aliquet nec. Cras nec enim vel nulla blandit maximus sed ut elit. Fusce malesuada augue efficitur quam sollicitudin ornare in sit amet velit. Maecenas fringilla scelerisque quam non suscipit. Curabitur nisl velit, commodo ut pretium ut, dictum nec orci. Maecenas quis est interdum, pellentesque erat in, porttitor lacus. In quis nisl luctus, luctus mauris at, condimentum quam. Fusce laoreet quam nec massa pulvinar, et ultricies sem semper.
|
|
41
|
+
</p>
|
package/test/runner.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import {spawn, execSync} from "child_process";
|
|
3
|
+
|
|
4
|
+
let testServerProcess;
|
|
5
|
+
|
|
6
|
+
export function runTestServer() {
|
|
7
|
+
if (testServerProcess)
|
|
8
|
+
return;
|
|
9
|
+
|
|
10
|
+
testServerProcess =
|
|
11
|
+
spawn("node", [path.join(process.cwd(), "test", "start-server.js")]);
|
|
12
|
+
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
function onData(data) {
|
|
15
|
+
console.log(data.toString()); // eslint-disable-line no-console
|
|
16
|
+
|
|
17
|
+
let testServerStarted = data.includes("listening at");
|
|
18
|
+
if (testServerStarted)
|
|
19
|
+
resolve();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function onClose(code) {
|
|
23
|
+
if (code != 0)
|
|
24
|
+
reject(new Error("Failed to start test server"));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
testServerProcess.stderr.on("data", onData);
|
|
28
|
+
testServerProcess.stdout.on("data", onData);
|
|
29
|
+
testServerProcess.on("close", onClose);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function killTestServer() {
|
|
34
|
+
if (!testServerProcess)
|
|
35
|
+
return;
|
|
36
|
+
|
|
37
|
+
await new Promise((resolve, reject) => {
|
|
38
|
+
let timeoutID = setTimeout(() => {
|
|
39
|
+
reject(new Error("Could not stop test server"));
|
|
40
|
+
}, 5000);
|
|
41
|
+
|
|
42
|
+
function onClose() {
|
|
43
|
+
testServerProcess.off("close", onClose);
|
|
44
|
+
resolve();
|
|
45
|
+
clearTimeout(timeoutID);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
testServerProcess.on("close", onClose);
|
|
49
|
+
testServerProcess.kill("SIGKILL");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
testServerProcess = null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function run() {
|
|
56
|
+
let args = process.argv.join(" ").split("-- ")[1];
|
|
57
|
+
await runTestServer();
|
|
58
|
+
try {
|
|
59
|
+
execSync(`npm run test-suite -- ${args}`, {stdio: "inherit"});
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await killTestServer();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
(async() => {
|
|
67
|
+
try {
|
|
68
|
+
await run();
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
console.error(err instanceof Error ? err.stack : `Error: ${err}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
})();
|