@eyeo/get-browser-binary 0.5.0 → 0.6.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
@@ -53,14 +53,12 @@ test:browsers:windows:
53
53
  - choco upgrade -y nodejs --version 16.10.0
54
54
  - npm install
55
55
  script:
56
- # Running only a subset of Firefox and Opera tests to avoid low OS resources error
56
+ # Running only a subset of Firefox tests to avoid low OS resources error
57
57
  # https://gitlab.com/eyeo/developer-experience/get-browser-binary/-/issues/2
58
- # Regarding grep, running npm v8 on powershell has issues when the grep
59
- # value contains the pipe (|) literal. Storing that string as a verbatim
60
- # string (single quotes) and then sorrounding it with four double quotes
61
- # does the trick.
62
- - $subset_tests = '(firefox|opera).*latest.*downloads'
63
- - npm test -- --grep """"$subset_tests""""
58
+ - npm test -- --grep "firefox.*latest.*downloads"
59
+ # Running npm v8 on powershell has issues when the grep value contains the
60
+ # pipe (|) literal. Storing that string as a verbatim string (single quotes)
61
+ # and then sorrounding it with four double quotes does the trick.
64
62
  - $full_tests = '(chromium|edge.*latest)'
65
63
  - npm test -- --grep """"$full_tests""""
66
64
  tags:
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # get-browser-binary
2
2
 
3
- Download specific browser versions for Chromium, Firefox, Edge and Opera, and
4
- their matching [selenium webdriver](https://www.selenium.dev/selenium/docs/api/javascript/index.html).
3
+ Download specific browser versions for Chromium, Firefox and Edge, and their
4
+ matching [selenium webdriver](https://www.selenium.dev/selenium/docs/api/javascript/index.html).
5
5
 
6
6
  ## Getting started
7
7
 
@@ -33,13 +33,10 @@ the right side.
33
33
  - Chromium >= 75
34
34
  - Firefox >= 60
35
35
  - Edge >= 95
36
- - Opera >= 62
37
36
 
38
37
  Note: Edge download is not supported on Windows. It is assumed to be installed
39
38
  because it is the default browser on that platform.
40
39
 
41
- Note: Using the `operadriver` on Windows is not supported.
42
-
43
40
  ## Development
44
41
 
45
42
  ### Prerequisites
@@ -93,9 +90,6 @@ The `grep` option can also be used on Docker via the `TEST_ARGS` parameter:
93
90
  docker run --shm-size=256m -e TEST_ARGS="--grep chromium.*latest" -it browsers
94
91
  ```
95
92
 
96
- Note: For a full automated run, `opera` tests should not use the interactive
97
- tty `-it` flag.
98
-
99
93
  ## Building the documentation
100
94
 
101
95
  ```shell
package/RELEASE_NOTES.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 0.6.0
2
+
3
+ - Removes Opera support (#26)
4
+ - Improves error handling when the downloaded browser version doesn't match
5
+ known channels (#25)
6
+ - Fixes an Edge running issue on Windows (32-bit) (#23)
7
+
1
8
  # 0.5.0
2
9
 
3
10
  - Enables Edge binary downloads on MacOS (!25)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eyeo/get-browser-binary",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Download browser binaries and matching webdrivers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,10 +18,9 @@
18
18
  "chromedriver": "^90.0.1",
19
19
  "dmg": "^0.1.0",
20
20
  "extract-zip": "^2.0.1",
21
- "fs-extra": "^10.0.0",
22
21
  "geckodriver": "^3.0.2",
23
22
  "got": "^11.8.2",
24
- "selenium-webdriver": "^4.2.0"
23
+ "selenium-webdriver": "^4.6.1"
25
24
  },
26
25
  "devDependencies": {
27
26
  "eslint": "^8.17.0",
package/src/browsers.js CHANGED
@@ -16,10 +16,9 @@
16
16
  */
17
17
 
18
18
  import path from "path";
19
- import {exec, execFile, spawn} from "child_process";
19
+ import {exec, execFile} from "child_process";
20
20
  import {promisify} from "util";
21
21
  import fs from "fs";
22
- import fsExtra from "fs-extra";
23
22
 
24
23
  import got from "got";
25
24
  import webdriver from "selenium-webdriver";
@@ -46,7 +45,7 @@ function checkVersion(version, minVersion, channels) {
46
45
  return;
47
46
 
48
47
  let mainVersion = parseInt(version && version.split(".")[0], 10);
49
- if (mainVersion < minVersion)
48
+ if (isNaN(mainVersion) || mainVersion < minVersion)
50
49
  throw new Error(`Unsupported browser version: ${version}`);
51
50
  }
52
51
 
@@ -542,8 +541,9 @@ class Edge extends Browser {
542
541
  static #getBinaryPath(channel = "stable") {
543
542
  switch (process.platform) {
544
543
  case "win32":
545
- return "${Env:ProgramFiles(x86)}\\Microsoft\\Edge\\Application\\" +
546
- "msedge.exe";
544
+ let programFiles = process.env["ProgramFiles(x86)"] ?
545
+ "${Env:ProgramFiles(x86)}" : "${Env:ProgramFiles}";
546
+ return `${programFiles}\\Microsoft\\Edge\\Application\\msedge.exe`;
547
547
  case "linux":
548
548
  return channel == "stable" ?
549
549
  "microsoft-edge" : `microsoft-edge-${channel}`;
@@ -603,7 +603,7 @@ class Edge extends Browser {
603
603
  await download(downloadUrl, archive);
604
604
  }
605
605
  catch (err) {
606
- throw new Error(`Edge download failed: ${err}`);
606
+ throw new Error(`Edge download failed at ${downloadUrl}\n${err}`);
607
607
  }
608
608
 
609
609
  if (process.platform == "linux") {
@@ -724,242 +724,6 @@ class Edge extends Browser {
724
724
  }
725
725
  }
726
726
 
727
- /**
728
- * Download functionality for Opera. This class can be used statically.
729
- * @hideconstructor
730
- * @extends Browser
731
- */
732
- class Opera extends Browser {
733
- static async #getVersionForChannel(version, platformDir) {
734
- let channelPath = "opera/desktop";
735
- let filePrefix = "Opera";
736
- if (version != "latest")
737
- return {versionNumber: version, channelPath, filePrefix};
738
-
739
- let {body} = await got(`https://ftp.opera.com/pub/${channelPath}`);
740
- let regex = /href="(\d.*)\/"/gm;
741
- let matches = body.match(regex);
742
- let versionNumber;
743
- while (matches.length > 0) {
744
- let result = regex.exec(matches.pop());
745
- if (!result)
746
- continue;
747
-
748
- versionNumber = result[1];
749
- try {
750
- await got(`https://ftp.opera.com/pub/${channelPath}/${versionNumber}/${platformDir}`);
751
- break;
752
- }
753
- catch (e) {}
754
- }
755
-
756
- return {versionNumber, channelPath, filePrefix};
757
- }
758
-
759
- static #getBinaryPath(dir) {
760
- switch (process.platform) {
761
- case "win32":
762
- return path.join(dir, "launcher.exe");
763
- case "linux":
764
- return path.join("/", "usr", "bin", "opera");
765
- case "darwin":
766
- return path.join(dir, "Opera.app", "Contents", "MacOS", "Opera");
767
- default:
768
- throw new Error(`Unexpected platform: ${process.platform}`);
769
- }
770
- }
771
-
772
- static async #extractDeb(archive) {
773
- let child = spawn("dpkg", ["-i", archive]);
774
-
775
- child.stdout.on("data", data => {
776
- if (data.toString().includes("Do you want to update Opera")) {
777
- process.stdin.pipe(child.stdin);
778
- child.stdin.write("no\r\n");
779
- }
780
- });
781
-
782
- child.stderr.on("data", data => {
783
- let expectedWarnings = [
784
- "debconf: unable to initialize frontend",
785
- "dpkg: warning: downgrading opera-stable",
786
- "update-alternatives",
787
- "using /usr/bin/opera",
788
- "skip creation of",
789
- "\r\n"
790
- ];
791
- if (!expectedWarnings.find(err => data.toString().includes(err.trim())))
792
- console.error(`stderr: ${data.toString()}`);
793
- });
794
-
795
- await new Promise((resolve, reject) => child.on("close", code => {
796
- if (code != 0)
797
- reject(`dpkg process exited with code ${code}`);
798
-
799
- resolve();
800
- }));
801
- }
802
-
803
- static async #installOnWindows(archive, dir, filename) {
804
- let archiveCopy = path.join(dir, filename);
805
- await fsExtra.copy(archive, archiveCopy);
806
- await promisify(exec)(`"${archiveCopy}"`);
807
- }
808
-
809
- static #extractOperaArchive(archive, dir, filename) {
810
- switch (process.platform) {
811
- case "win32":
812
- return Opera.#installOnWindows(archive, dir, filename);
813
- case "linux":
814
- return Opera.#extractDeb(archive, dir);
815
- case "darwin":
816
- return extractTar(archive, dir);
817
- default:
818
- throw new Error(`Unexpected platform: ${process.platform}`);
819
- }
820
- }
821
-
822
- /**
823
- * Downloads the browser binary file.
824
- * @param {string} version - Either "latest" or a full version number
825
- * (i.e. "64.0.3417.92"). Defaults to "latest".
826
- * @return {BrowserBinary}
827
- */
828
- static async downloadBinary(version = "latest") {
829
- const MIN_VERSION = 62;
830
- const CHANNELS = ["latest"];
831
-
832
- checkVersion(version, MIN_VERSION, CHANNELS);
833
-
834
- let [platformDir, fileSuffix] = {
835
- "win32-ia32": ["win", "Autoupdate.exe"],
836
- "win32-x64": ["win", "Autoupdate_x64.exe"],
837
- "linux-x64": ["linux", "amd64.deb"],
838
- "darwin-x64": ["mac", "Autoupdate.tar.xz"],
839
- "dawrin-arm64": ["mac", "Autoupdate_arm64.tar.xz"]
840
- }[platform];
841
-
842
- let {versionNumber, channelPath, filePrefix} =
843
- await Opera.#getVersionForChannel(version, platformDir);
844
-
845
- let snapshotsDir = path.join(snapshotsBaseDir, "opera");
846
- let browserDir = path.join(snapshotsDir, `opera-${platform}-${versionNumber}`);
847
- let filename = `${filePrefix}_${versionNumber}_${fileSuffix}`;
848
- let archive = path.join(snapshotsDir, "cache", filename);
849
-
850
- let binary = Opera.#getBinaryPath(browserDir);
851
- try {
852
- if (process.platform == "linux" &&
853
- await Opera.getInstalledVersion("opera") == versionNumber)
854
- return {binary, versionNumber};
855
-
856
- await fs.promises.access(browserDir);
857
- return {binary, versionNumber};
858
- }
859
- catch (e) {}
860
-
861
- await fs.promises.mkdir(path.dirname(browserDir), {recursive: true});
862
- try {
863
- await fs.promises.access(archive);
864
- }
865
- catch (e) {
866
- let url = `https://ftp.opera.com/pub/${channelPath}/${versionNumber}/${platformDir}/${filename}`;
867
- try {
868
- await download(url, archive);
869
- }
870
- catch (err) {
871
- throw new Error(`Browser download unavailable at ${url}\n${err}`);
872
- }
873
- }
874
-
875
- await Opera.#extractOperaArchive(archive, browserDir, filename);
876
- return {binary, versionNumber};
877
- }
878
-
879
- static async #installDriver(version, originalVersion) {
880
- let [zip, driver] = {
881
- "win32-ia32": ["operadriver_win32.zip", "operadriver.exe"],
882
- "win32-x64": ["operadriver_win64.zip", "operadriver.exe"],
883
- "linux-x64": ["operadriver_linux64.zip", "operadriver"],
884
- "darwin-x64": ["operadriver_mac64.zip", "operadriver"],
885
- "darwin-arm64": ["operadriver_mac64.zip", "operadriver"]
886
- }[platform];
887
-
888
- let {versionNumber} = await Opera.#getVersionForChannel(version);
889
- versionNumber = versionNumber.split(".")[0];
890
-
891
- let cacheDir = path.join(snapshotsBaseDir, "opera", "cache",
892
- `operadriver-${versionNumber}`);
893
- let archive = path.join(cacheDir, zip);
894
-
895
- let {body} = await got(`https://github.com/operasoftware/operachromiumdriver/releases?q=Opera+${versionNumber}&expanded=true`);
896
- let regex = /release-card[\s\S]*Link--primary.*>(.*)<\/a/gm;
897
- let matches = body.match(regex);
898
- if (!matches || matches.length == 0)
899
- throw new Error(`Driver for Opera ${version} was not found`);
900
- let driverVersion = regex.exec(matches[matches.length - 1])[1];
901
-
902
- try {
903
- await download(`https://github.com/operasoftware/operachromiumdriver/releases/download/v.${driverVersion}/${zip}`,
904
- archive);
905
- }
906
- catch (err) {
907
- throw new Error(`Downloading operadriver failed: ${err}`);
908
- }
909
-
910
- await killDriverProcess("operadriver");
911
- let driverPath = path.join(cacheDir, zip.split(".")[0], driver);
912
- try {
913
- await fs.promises.rm(driverPath, {recursive: true});
914
- }
915
- catch (e) {} // file does not exist
916
- await extractZip(archive, {dir: cacheDir});
917
- await fs.promises.chmod(driverPath, 577);
918
-
919
- return driverPath;
920
- }
921
-
922
- /** @see Browser.getDriver */
923
- static async getDriver(version = "latest", {
924
- headless = true, extensionPaths = [], incognito = false, insecure = false,
925
- extraArgs = []
926
- } = {}) {
927
- let {binary, versionNumber} = await Opera.downloadBinary(version);
928
- let driverPath = await Opera.#installDriver(versionNumber, version);
929
- // operadriver uses chrome as a service builder:
930
- // https://github.com/operasoftware/operachromiumdriver/blob/master/docs/desktop.md
931
- let serviceBuilder = new chrome.ServiceBuilder(driverPath);
932
- let service = serviceBuilder.build();
933
- await service.start();
934
-
935
- let options = new chrome.Options().addArguments("no-sandbox", ...extraArgs);
936
- if (extensionPaths.length > 0)
937
- options.addArguments(`load-extension=${extensionPaths.join(",")}`);
938
- if (headless)
939
- options.headless();
940
- if (insecure)
941
- options.addArguments("ignore-certificate-errors");
942
- if (incognito)
943
- options.addArguments("incognito");
944
- options.setChromeBinaryPath(binary);
945
- // https://github.com/operasoftware/operachromiumdriver/issues/61#issuecomment-579331657
946
- options.addArguments("remote-debugging-port=9222");
947
-
948
- let builder = new webdriver.Builder();
949
- builder.forBrowser("chrome");
950
- builder.setChromeOptions(options);
951
- builder.setChromeService(serviceBuilder);
952
-
953
- return builder.build();
954
- }
955
-
956
- /** @see Browser.enableExtensionInIncognito */
957
- static async enableExtensionInIncognito(driver, extensionTitle) {
958
- // Extensions page in Opera has the same web elements as Chromium
959
- await Chromium.enableExtensionInIncognito(driver, extensionTitle);
960
- }
961
- }
962
-
963
727
  /**
964
728
  * @type {Object}
965
729
  * @property {Chromium} chromium - Download functionality for Chromium.
@@ -969,6 +733,5 @@ class Opera extends Browser {
969
733
  export const BROWSERS = {
970
734
  chromium: Chromium,
971
735
  firefox: Firefox,
972
- edge: Edge,
973
- opera: Opera
736
+ edge: Edge
974
737
  };
package/src/utils.js CHANGED
@@ -22,7 +22,6 @@ import {promisify} from "util";
22
22
  import {exec} from "child_process";
23
23
 
24
24
  import got from "got";
25
- import fsExtra from "fs-extra";
26
25
  import dmg from "dmg";
27
26
 
28
27
  /**
@@ -66,7 +65,7 @@ export async function extractDmg(archive, dir) {
66
65
  let source = path.join(mpath, target);
67
66
  await fs.promises.mkdir(dir);
68
67
  try {
69
- await fsExtra.copy(source, path.join(dir, target));
68
+ await fs.promises.cp(source, path.join(dir, target), {recursive: true});
70
69
  }
71
70
  finally {
72
71
  try {
package/test/browsers.js CHANGED
@@ -29,8 +29,7 @@ import "geckodriver";
29
29
  const VERSIONS = {
30
30
  chromium: ["latest", "75.0.3770.0", "beta", "dev"],
31
31
  firefox: ["latest", "60.0", "beta"],
32
- edge: ["latest", "95.0.1020.53", "beta", "dev"],
33
- opera: ["latest", "62.0.3331.66"]
32
+ edge: ["latest", "95.0.1020.40", "beta", "dev"]
34
33
  };
35
34
  const TEST_URL = "https://gitlab.com/eyeo/developer-experience/get-browser-binary";
36
35
  let extensionPaths = [path.resolve(process.cwd(), "test", "extension")];
@@ -127,10 +126,7 @@ for (let browser of Object.keys(BROWSERS)) {
127
126
 
128
127
  let {binary, versionNumber} =
129
128
  await BROWSERS[browser].downloadBinary(version);
130
- let browserName =
131
- browser == "opera" ? /(opera|Opera)/ :
132
- browser == "edge" ? /(edge|Edge)/ :
133
- browser;
129
+ let browserName = browser == "edge" ? /(edge|Edge)/ : browser;
134
130
  expect(binary).toEqual(expect.stringMatching(browserName));
135
131
 
136
132
  let installedVersion =
@@ -143,8 +139,7 @@ for (let browser of Object.keys(BROWSERS)) {
143
139
  let names = {
144
140
  chromium: "chrome",
145
141
  firefox: "firefox",
146
- edge: /(MicrosoftEdge|msedge)/,
147
- opera: /(opera|chrome)/
142
+ edge: /(MicrosoftEdge|msedge)/
148
143
  };
149
144
 
150
145
  driver = await BROWSERS[browser].getDriver(version);
@@ -198,10 +193,10 @@ for (let browser of Object.keys(BROWSERS)) {
198
193
  if (browser == "edge" && process.platform == "win32")
199
194
  this.skip();
200
195
 
201
- const UNSUPPORTED = "0.0";
202
-
203
- await expect(BROWSERS[browser].downloadBinary(UNSUPPORTED))
204
- .rejects.toThrow(`Unsupported browser version: ${UNSUPPORTED}`);
196
+ for (let unsupported of ["0.0", "invalid"]) {
197
+ await expect(BROWSERS[browser].downloadBinary(unsupported))
198
+ .rejects.toThrow(`Unsupported browser version: ${unsupported}`);
199
+ }
205
200
  });
206
201
  });
207
202
  }