@eyeo/get-browser-binary 0.16.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 CHANGED
@@ -27,42 +27,52 @@ test:basic:
27
27
  - npm run lint
28
28
  - npm test -- --grep "Utils"
29
29
 
30
- test:browsers:linux:
30
+ .linux:
31
31
  stage: test
32
32
  image: docker:24.0.5
33
33
  services:
34
34
  - docker:24.0.5-dind
35
35
  before_script:
36
36
  - docker build -f test/docker/Dockerfile -t browsers .
37
+
38
+ test:browsers:linux:
39
+ extends: .linux
37
40
  script:
38
- - docker run --shm-size=512m -t browsers
41
+ - docker run --shm-size=512m -t -e TEST_ARGS="--grep ^.*Browser((?!Version:.dev).)*\$" browsers
39
42
 
40
- test:browsers:windows:
43
+ test:browsers:linux:dev:
44
+ extends: .linux
45
+ script:
46
+ - docker run --shm-size=512m -t -e TEST_ARGS="--grep Version:.dev" browsers
47
+ allow_failure: true
48
+
49
+ .windows:
41
50
  stage: test
42
- variables:
43
- CI_PROJECT_ID_MAINSTREAM: 36688302
44
51
  before_script:
45
- - Invoke-WebRequest
46
- -Uri "${Env:CI_API_V4_URL}/projects/${Env:CI_PROJECT_ID_MAINSTREAM}/packages/generic/microsoft-edge/79.0.309/MicrosoftEdgeEnterpriseX64.msi"
47
- -Headers @{'JOB-TOKEN' = $Env:CI_JOB_TOKEN}
48
- -OutFile 'MicrosoftEdgeEnterpriseX64.msi'
49
- - Start-Process msiexec
50
- -ArgumentList "/i MicrosoftEdgeEnterpriseX64.msi /norestart /qn" -Wait
51
- - choco upgrade -y --no-progress nodejs --version 18.17.1
52
+ - choco install -y microsoft-edge
52
53
  - npm install
54
+ tags:
55
+ - eyeo-windows
56
+ cache: {}
57
+ # Retrying to mitigate Edge install issues
58
+ retry: 1
59
+
60
+ test:browsers:windows:
61
+ extends: .windows
53
62
  script:
54
63
  # Running Edge tests only on the preinstalled version
55
64
  # https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/29
56
65
  - npm test -- --grep "edge.*latest"
57
- - npm test -- --grep "chromium"
66
+ - npm test -- --grep "^.*chromium((?!Version:.dev).)*$"
58
67
  # Running only a subset of Firefox tests to avoid low OS resources error
59
68
  # https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/2
60
69
  - npm test -- --grep "firefox.*installs"
61
- tags:
62
- - shared-windows
63
- - windows
64
- - windows-1809
65
- cache: {}
70
+
71
+ test:browsers:windows:dev:
72
+ extends: .windows
73
+ script:
74
+ - npm test -- --grep "chromium.*Version:.dev"
75
+ allow_failure: true
66
76
 
67
77
  docs:
68
78
  stage: docs
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 (Windows Edge >= 79)
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
@@ -43,6 +43,22 @@ Edge version is supported.
43
43
  Set the `VERBOSE` environment variable to `"true"` to get verbose logging on
44
44
  download requests.
45
45
 
46
+ ### Offline execution
47
+
48
+ It is possible to run browsers offline as long as they have been previously
49
+ installed. Example:
50
+
51
+ ```javascript
52
+ // Online
53
+ let {binary} = await BROWSERS[browser].installBrowser(version);
54
+ console.log(binary); // keep the browser binary location to use it offline
55
+ let driver = await BROWSERS[browser].getDriver(version); // let the driver binary download
56
+
57
+ // Offline
58
+ let customBrowserBinary = "<browser binary location>";
59
+ let driver = await BROWSERS[browser].getDriver(version, {customBrowserBinary});
60
+ ```
61
+
46
62
  ## Development
47
63
 
48
64
  ### Prerequisites
package/RELEASE_NOTES.md CHANGED
@@ -1,6 +1,29 @@
1
+ # Unreleased
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
+
16
+ # 0.17.0
17
+
18
+ - Stops calling `driver.close()` in `enableExtensionInIncognito()`
19
+ on non-Windows platforms (!114)
20
+ - Add instructions for offline execution (!110)
21
+ - Adds option to setup proxy through PAC file for Firefox (!115)
22
+
1
23
  # 0.16.0
2
24
 
3
- - Starts using selenium's automated driver management (#67)
25
+ - Starts using selenium's automated driver management. That means additional
26
+ `chromedriver` and `msedgedriver` packages are no longer needed (#67)
4
27
  - Loads the extension in Firefox by local path instead of being copied to a
5
28
  temporary folder (!104)
6
29
  - Checks if extensions can be loaded beforehand, by ensuring they contain a
@@ -18,7 +41,9 @@ include parenthesis (#69)
18
41
  ### Notes for integrators
19
42
 
20
43
  - Please make sure that your selenium-webdriver package version is at least
21
- 4.15.0 (the one used in this release) (#67)
44
+ 4.15.0 (the one used in this release). Also make sure that you are not using the
45
+ `chromedriver` nor `msedgedriver` packages, otherwise the automated driver
46
+ management may not work as expected (#67)
22
47
  - If you experience chromedriver issues, try deleting the `/browser-snapshots`
23
48
  cache folder.
24
49
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eyeo/get-browser-binary",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Install browser binaries and matching webdrivers",
5
5
  "repository": {
6
6
  "type": "git",
package/src/browser.js CHANGED
@@ -95,6 +95,8 @@ export class Browser {
95
95
  * certificates, or not.
96
96
  * @property {Array.<string>} [extraArgs=[]] Additional arguments to start
97
97
  * the browser with.
98
+ * @property {string} [proxy] Only for Firefox, path to network
99
+ * proxy autoconfig url (PAC file).
98
100
  * @property {string} [customBrowserBinary] Path to the browser binary to be
99
101
  * used, instead of the browser installed by installBrowser(). This option
100
102
  * overrides the version parameter in getDriver().
package/src/chromium.js CHANGED
@@ -229,46 +229,59 @@ export class Chromium extends Browser {
229
229
 
230
230
  /** @see Browser.enableExtensionInIncognito */
231
231
  static async enableExtensionInIncognito(driver, extensionTitle) {
232
- let handle = await driver.getWindowHandle();
232
+ const currentHandle = await driver.getWindowHandle();
233
233
 
234
- let version = getMajorVersion(
235
- (await driver.getCapabilities()).getBrowserVersion());
236
- if (version >= 115)
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
- await driver.executeScript((...args) => {
243
- let enable = () => document.querySelector("extensions-manager").shadowRoot
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
- let extensions = document.querySelector("extensions-manager").shadowRoot
249
- .getElementById("items-list").shadowRoot
250
- .querySelectorAll("extensions-item");
250
+ const getExtensionDetailsButton = () => {
251
+ const extensions = document
252
+ .querySelector("extensions-manager").shadowRoot
253
+ .getElementById("items-list").shadowRoot
254
+ .querySelectorAll("extensions-item");
251
255
 
252
- return new Promise((resolve, reject) => {
253
- let extensionDetails;
256
+ let detailsButton;
254
257
  for (let {shadowRoot} of extensions) {
255
- if (shadowRoot.getElementById("name").innerHTML != args[0])
258
+ const extensionName = shadowRoot.getElementById("name").innerHTML;
259
+ if (!extensionName.includes(title))
256
260
  continue;
257
261
 
258
- extensionDetails = shadowRoot.getElementById("detailsButton");
262
+ detailsButton = shadowRoot.getElementById("detailsButton");
259
263
  break;
260
264
  }
261
- if (!extensionDetails)
262
- reject(`${args[1]}: ${args[0]}`);
263
265
 
264
- extensionDetails.click();
265
- setTimeout(() => resolve(enable()), 100);
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
- if (version >= 115)
269
- // Closing the previously opened new window
279
+
280
+ if (browserVersion >= 115 && process.platform == "win32")
281
+ // Closing the previously opened new window. Needed on Windows to avoid a
282
+ // further `WebDriverError: disconnected` error
270
283
  await driver.close();
271
284
 
272
- await driver.switchTo().window(handle);
285
+ await driver.switchTo().window(currentHandle);
273
286
  }
274
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. "95.0.1020.40").
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
- const MIN_VERSION = 95;
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
- return channel == "beta" ?
56
- data.LATEST_FIREFOX_DEVEL_VERSION : data.LATEST_FIREFOX_VERSION;
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
- return {
62
- win32: path.join(dir, "core", "firefox.exe"),
63
- linux: path.join(dir, "firefox", "firefox"),
64
- darwin: path.join(dir, "Firefox.app", "Contents", "MacOS", "firefox")
65
- }[process.platform];
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
- let binary = Firefox.#getBinaryPath(browserDir);
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
- let url = `https://archive.mozilla.org/pub/firefox/releases/${versionNumber}/${buildPlatform}/en-US/${fileName}`;
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,14 +169,20 @@ 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
  }
141
176
 
142
177
  /** @see Browser.getDriver */
178
+ /** In Firefox you can define url to proxy autoconfig file.
179
+ * Proxy Autoconfig file determine whether web requests should go through
180
+ * a proxy server or connect directly. This allow to use localhost mapped
181
+ * to any url (f.ex testpages.adblockplus.org)
182
+ */
143
183
  static async getDriver(version = "latest", {
144
184
  headless = true, extensionPaths = [], incognito = false, insecure = false,
145
- extraArgs = [], customBrowserBinary
185
+ extraArgs = [], customBrowserBinary, proxy
146
186
  } = {}, downloadTimeout = 0) {
147
187
  let binary;
148
188
  let versionNumber;
@@ -163,6 +203,10 @@ export class Firefox extends Browser {
163
203
  // Enabled by default on Firefox > 68
164
204
  if (versionNumber && getMajorVersion(versionNumber) == 68)
165
205
  options.setPreference("dom.promise_rejection_events.enabled", true);
206
+ if (proxy) {
207
+ options.setPreference("network.proxy.type", 2);
208
+ options.setPreference("network.proxy.autoconfig_url", proxy);
209
+ }
166
210
 
167
211
  options.setBinary(customBrowserBinary || binary);
168
212
 
package/test/browsers.js CHANGED
@@ -27,10 +27,11 @@ 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", "95.0.1020.40", "beta", "dev"]
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
+ const PROXY_URL_BASIC = "http://testpages.adblockplus.org/basic.html";
34
35
  const TEST_URL_LONG = `${TEST_SERVER_URL}/long.html`;
35
36
 
36
37
  async function switchToHandle(driver, testFn) {
@@ -106,7 +107,8 @@ function getExtension(browser, version) {
106
107
 
107
108
  async function getInstallFileCTime(browser) {
108
109
  // Browsers installed at OS level are not tested
109
- if (browser == "edge" && process.platform == "win32")
110
+ if (browser == "edge" && (process.platform == "win32" ||
111
+ process.platform == "darwin"))
110
112
  return null;
111
113
 
112
114
  const installTypes = [".zip", ".dmg", ".bz2", ".deb", ".exe"];
@@ -141,6 +143,14 @@ function addVersionToTitle(ctx, version) {
141
143
  ctx.test.title = `${ctx.test.title} [v${version}]`;
142
144
  }
143
145
 
146
+ function isOldHeadlessMode(browser, version) {
147
+ // Chromium's old headless mode doesn't support loading extensions
148
+ return browser != "firefox" && (
149
+ !["latest", "beta", "dev"].includes(version) ||
150
+ // Edge on the Windows CI is v79, that is old headless
151
+ (browser == "edge" && process.platform == "win32"));
152
+ }
153
+
144
154
  for (let browser of Object.keys(BROWSERS)) {
145
155
  describe(`Browser: ${browser}`, () => {
146
156
  before(async() => {
@@ -168,6 +178,12 @@ for (let browser of Object.keys(BROWSERS)) {
168
178
  await killDriverProcess("chromedriver");
169
179
  else if (browser == "firefox")
170
180
  await killDriverProcess("geckodriver");
181
+
182
+ // https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/77
183
+ if (process.platform == "win32" && browser == "chromium")
184
+ await killDriverProcess("chrome");
185
+ else if (process.platform == "win32" && browser == "edge")
186
+ await killDriverProcess("msedge");
171
187
  }
172
188
 
173
189
  beforeEach(function() {
@@ -258,6 +274,22 @@ for (let browser of Object.keys(BROWSERS)) {
258
274
  expect(sizeSmall).toMeasureLessThan(sizeDefault);
259
275
  });
260
276
 
277
+ it("supports proxy", async function() {
278
+ if (browser != "firefox")
279
+ this.skip();
280
+
281
+ let headless = true;
282
+ let proxy = "http://localhost:3000/proxy-config.pac";
283
+ let extraArgs = [];
284
+ driver = await BROWSERS[browser].getDriver(
285
+ version, {headless, extraArgs, proxy});
286
+ await driver.navigate().to(PROXY_URL_BASIC);
287
+
288
+ let text = await driver.findElement(By.id("basic")).getText();
289
+ expect(text).toEqual("Test server basic page");
290
+ await quitDriver();
291
+ });
292
+
261
293
  it("takes a full page screenshot", async() => {
262
294
  driver = await BROWSERS[browser].getDriver(version);
263
295
  await driver.navigate().to(TEST_URL_LONG);
@@ -277,9 +309,7 @@ for (let browser of Object.keys(BROWSERS)) {
277
309
  // Selenium's automated driver download should be able to manage it.
278
310
  // https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/44
279
311
  it("loads an extension", async() => {
280
- // Chromium's old headless mode doesn't support loading extensions
281
- let headless = browser == "firefox" || (browser == "chromium" &&
282
- ["latest", "beta", "dev"].includes(version));
312
+ let headless = !isOldHeadlessMode(browser, version);
283
313
  let {extensionPaths} = getExtension(browser, version);
284
314
 
285
315
  driver = await BROWSERS[browser].getDriver(
@@ -299,24 +329,50 @@ for (let browser of Object.keys(BROWSERS)) {
299
329
  );
300
330
  await getHandle(driver, "/index.html");
301
331
  });
332
+
333
+ it("updates an extension", async() => {
334
+ let headless = !isOldHeadlessMode(browser, version);
335
+ let {extensionPaths, manifest} = getExtension(browser, version);
336
+
337
+ let tmpExtensionDir = path.join(snapshotsBaseDir, "extension");
338
+ await fs.promises.rm(tmpExtensionDir, {recursive: true, force: true});
339
+ await fs.promises.cp(extensionPaths[0], tmpExtensionDir,
340
+ {recursive: true});
341
+
342
+ driver = await BROWSERS[browser].getDriver(
343
+ version, {headless, extensionPaths: [tmpExtensionDir]});
344
+ await getHandle(driver, "/index.html");
345
+ let text = await driver.findElement(By.id("title")).getText();
346
+ expect(text).toEqual(`Browser test extension - ${manifest}`);
347
+
348
+ // The page is modified and reloaded to emulate an extension update
349
+ await fs.promises.cp(path.join(tmpExtensionDir, "update.html"),
350
+ path.join(tmpExtensionDir, "index.html"));
351
+ await driver.navigate().refresh();
352
+ text = await driver.findElement(By.id("title")).getText();
353
+ expect(text).toEqual(`Updated test extension - ${manifest}`);
354
+ });
302
355
  });
303
356
  }
304
357
 
305
- it("does not install unsupported versions", async function() {
306
- if (browser == "edge" && process.platform == "win32")
307
- this.skip();
308
358
 
309
- for (let unsupported of ["0.0", "invalid"]) {
310
- await expect(BROWSERS[browser].installBrowser(unsupported))
311
- .rejects.toThrow(`Unsupported browser version: ${unsupported}`);
312
- }
313
- });
359
+ describe("Any version", async() => {
360
+ it("does not install unsupported versions", async function() {
361
+ if (browser == "edge" && process.platform == "win32")
362
+ this.skip();
314
363
 
315
- it("does not load an invalid extension", async() => {
316
- let extensionPaths = [process.cwd()];
364
+ for (let unsupported of ["0.0", "invalid"]) {
365
+ await expect(BROWSERS[browser].installBrowser(unsupported))
366
+ .rejects.toThrow(`Unsupported browser version: ${unsupported}`);
367
+ }
368
+ });
317
369
 
318
- await expect(BROWSERS[browser].getDriver("latest", {extensionPaths}))
319
- .rejects.toThrow(`Extension manifest file not found: ${extensionPaths[0]}`);
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
+ });
320
376
  });
321
377
  });
322
378
  }
@@ -1 +1 @@
1
- <h1>Browser test extension - mv2</h1>
1
+ <h1 id="title">Browser test extension - mv2</h1>
@@ -0,0 +1 @@
1
+ <h1 id="title">Updated test extension - mv2</h1>
@@ -1,3 +1,16 @@
1
1
  "use strict";
2
2
 
3
- chrome.tabs.create({url: "index.html"});
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
+ });
@@ -1 +1 @@
1
- <h1>Browser test extension - mv3</h1>
1
+ <h1 id="title">Browser test extension - mv3</h1>
@@ -0,0 +1 @@
1
+ <h1 id="title">Updated test extension - mv3</h1>
@@ -0,0 +1,7 @@
1
+
2
+ function FindProxyForURL(url, host) {
3
+ if (host === "testpages.adblockplus.org")
4
+ return "PROXY http://localhost:3000";
5
+
6
+ return "DIRECT";
7
+ }