Skip to content

Commit f0f75ba

Browse files
authored
feat: support installing a compatible version of ChromeDriver (#548)
This changes enables installing ChromeDriver. The pre-installed ChromeDriver in the Runner didn't work correctly for the installed Chrome browser version because ChromeDriver requires [compatible version](https://developer.chrome.com/docs/chromedriver/downloads/version-selection) with the browser. Now setup-chrome action install the compatible version of ChromeDriver after installing the browser. This feature is disabled by default. To enable this feature, please add 'install-chromedriver: true` into your action manifest: ```yaml steps: - uses: browser-actions/setup-chrome@v1 with: chrome-version: 120 install-chromedriver: true ```
1 parent cdadd16 commit f0f75ba

25 files changed

+1710
-401
lines changed

.github/workflows/build.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,16 @@ jobs:
4141
uses: ./
4242
with:
4343
chrome-version: ${{ matrix.version }}
44+
install-chromedriver: true
4445
id: setup-chrome
4546
- if: runner.os == 'Linux' || runner.os == 'macOS'
4647
run: |
4748
"${{ steps.setup-chrome.outputs.chrome-path }}" --version
49+
"${{ steps.setup-chrome.outputs.chromedriver-path }}" --version
4850
- if: runner.os == 'Windows'
4951
run: |
5052
(Get-Item (Get-Command "${{ steps.setup-chrome.outputs.chrome-path }}").Source).VersionInfo.ProductVersion
53+
(Get-Item (Get-Command "${{ steps.setup-chrome.outputs.chromedriver-path }}").Source).VersionInfo.ProductVersion
5154
5255
test-install-dependencies:
5356
needs: [build]
@@ -82,7 +85,9 @@ jobs:
8285
uses: ./
8386
with:
8487
chrome-version: 120
88+
install-chromedriver: true
8589
install-dependencies: true
8690
id: setup-chrome
8791
- run: |
8892
"${{ steps.setup-chrome.outputs.chrome-path }}" --version
93+
"${{ steps.setup-chrome.outputs.chromedriver-path }}" --version

.github/workflows/manual-test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ jobs:
5555
- if: runner.os == 'Linux' || runner.os == 'macOS'
5656
run: |
5757
"${{ steps.setup-chrome.outputs.chrome-path }}" --version
58+
"${{ steps.setup-chrome.outputs.chromedriver-path }}" --version
5859
- if: runner.os == 'Windows'
5960
run: |
6061
(Get-Item (Get-Command "${{ steps.setup-chrome.outputs.chrome-path }}").Source).VersionInfo.ProductVersion
62+
(Get-Item (Get-Command "${{ steps.setup-chrome.outputs.chromedriver-path }}").Source).VersionInfo.ProductVersion

README.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
# setup-chrome
66

7-
This action sets by Google Chrome/Chromium for use in actions by:
7+
This action sets-up Google Chrome/Chromium for GitHub Actions. This action supports the following features:
88

9-
- [X] Install and setup latest Chromium
10-
- [X] Cross platform runner (macOS, Linux, Windows)
11-
- [X] Install Google Chrome by channel (stable, beta, dev, and canary)
12-
- [X] Install by version number (88.0.4324, or 88.0)
9+
- Install and setup the Google Chrome onto the runner.
10+
- Install a specific version of Google Chrome/Chromium by the version number, commit position, and release channel.
11+
- Cross-platform runner support (Windows, macOS, Linux) and self-hosted runner support.
12+
- Install the compatible versions of ChromeDriver with the browser.
1313

1414
## Usage
1515

@@ -31,6 +31,17 @@ steps:
3131
chrome-version: 120
3232
```
3333

34+
The action support installing the compatible ChromeDriver with the browser.
35+
You can use the `install-chromedriver` to install the ChromeDriver.
36+
37+
```yaml
38+
steps:
39+
- uses: browser-actions/setup-chrome@v1
40+
with:
41+
chrome-version: 120
42+
install-chromedriver: true
43+
```
44+
3445
If you use the self-hosted runner, your runner may not have the required dependencies on the system.
3546
You can install the dependencies by using the `install-dependencies` parameter.
3647
It installs the required dependencies for the Google Chrome/Chromium to run automatically.
@@ -77,11 +88,15 @@ steps:
7788
Default: `latest`
7889
- `install-dependencies`: *(Optional)* Install the required dependencies for the Google Chrome/Chromium to run.
7990
Default: `false`
91+
- `install-chromedriver`: *(Optional)* Install the compatible ChromeDriver with the browser.
92+
Default: `false`
8093

8194
### Outputs
8295

8396
- `chrome-path`: The installed Google Chrome/Chromium binary path.
8497
- `chrome-version`: The installed Google Chrome/Chromium version.
98+
- `chromedriver-path`: The installed ChromeDriver binary path.
99+
- `chromedriver-version`: The installed ChromeDriver version.
85100

86101
[snapshots]: https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html
87102

__test__/channel_linux.test.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import * as fs from "node:fs";
2+
import * as exec from "@actions/exec";
3+
import * as tc from "@actions/tool-cache";
4+
import { afterEach, describe, expect, test, vi } from "vitest";
5+
import * as cache from "../src/cache";
6+
import { LinuxChannelInstaller } from "../src/channel_linux";
7+
8+
const cacheFindSpy = vi.spyOn(cache, "find");
9+
const cacheCacheDirSpy = vi.spyOn(cache, "cacheDir");
10+
const tcDownloadToolSpy = vi.spyOn(tc, "downloadTool");
11+
const tcExtractZipSpy = vi.spyOn(tc, "extractZip");
12+
const execSpy = vi.spyOn(exec, "exec");
13+
const fsMkdtempSpy = vi.spyOn(fs.promises, "mkdtemp");
14+
const fsUnlinkSpy = vi.spyOn(fs.promises, "unlink");
15+
16+
afterEach(() => {
17+
vi.resetAllMocks();
18+
});
19+
20+
describe("LinuxChannelInstaller", () => {
21+
const installer = new LinuxChannelInstaller({ os: "linux", arch: "amd64" });
22+
23+
describe("checkInstalledBrowser", () => {
24+
test("return undefined if not installed", async () => {
25+
cacheFindSpy.mockResolvedValue(undefined);
26+
27+
const result = await installer.checkInstalledBrowser("stable");
28+
29+
expect(result).toBeUndefined();
30+
});
31+
32+
test("return install result if installed", async () => {
33+
cacheFindSpy.mockResolvedValue("/path/to/chromium");
34+
35+
const result = await installer.checkInstalledBrowser("stable");
36+
37+
expect(result).toEqual({ root: "/path/to/chromium", bin: "chrome" });
38+
});
39+
});
40+
41+
describe("downloadBrowser", () => {
42+
test("throw error if version is not release channel", async () => {
43+
await expect(installer.downloadBrowser("foo")).rejects.toThrowError(
44+
"Unexpected version: foo",
45+
);
46+
});
47+
48+
test("throw error if version is canary", async () => {
49+
await expect(installer.downloadBrowser("canary")).rejects.toThrowError(
50+
"Chrome canary not supported for platform linux amd64",
51+
);
52+
});
53+
54+
test("download stable version", async () => {
55+
tcDownloadToolSpy.mockResolvedValue("/path/to/downloaded.deb");
56+
57+
const result = await installer.downloadBrowser("stable");
58+
59+
expect(result).toEqual({ archive: "/path/to/downloaded.deb" });
60+
expect(tcDownloadToolSpy).toHaveBeenCalled();
61+
});
62+
});
63+
64+
describe("installBrowser", () => {
65+
test("throw error if version is not release channel", async () => {
66+
await expect(
67+
installer.installBrowser("foo", "/path/to/downloaded.deb"),
68+
).rejects.toThrowError("Unexpected version: foo");
69+
});
70+
71+
test("throw error if version is canary", async () => {
72+
await expect(
73+
installer.installBrowser("canary", "/path/to/downloaded.deb"),
74+
).rejects.toThrowError("Chrome canary not supported for Linux");
75+
});
76+
77+
test("install stable version", async () => {
78+
fsMkdtempSpy.mockResolvedValue("/deb-abcdef");
79+
fsUnlinkSpy.mockResolvedValue(undefined);
80+
execSpy.mockResolvedValue(0);
81+
cacheCacheDirSpy.mockResolvedValue("/path/to/chromium");
82+
83+
const result = await installer.installBrowser(
84+
"stable",
85+
"/path/to/downloaded.deb",
86+
);
87+
88+
expect(result).toEqual({ root: "/path/to/chromium", bin: "chrome" });
89+
expect(cacheCacheDirSpy).toHaveBeenCalledWith(
90+
"/deb-abcdef",
91+
"chromium",
92+
"stable",
93+
);
94+
});
95+
});
96+
97+
describe("checkInstalledDriver", () => {
98+
test("return undefined if not installed", async () => {
99+
cacheFindSpy.mockResolvedValue(undefined);
100+
101+
const result = await installer.checkInstalledDriver("stable");
102+
expect(result).toBeUndefined();
103+
});
104+
105+
test("return install result if installed", async () => {
106+
cacheFindSpy.mockResolvedValue("/path/to/chromedriver");
107+
108+
const result = await installer.checkInstalledDriver("stable");
109+
expect(result).toEqual({
110+
root: "/path/to/chromedriver",
111+
bin: "chromedriver",
112+
});
113+
});
114+
});
115+
116+
describe("downloadDriver", () => {
117+
test("throw error if version is not release channel", async () => {
118+
await expect(installer.downloadDriver("foo")).rejects.toThrowError(
119+
"Invalid version: foo",
120+
);
121+
});
122+
123+
test("download stable version", async () => {
124+
tcDownloadToolSpy.mockResolvedValue("/tmp/chromedirver.zip");
125+
126+
const result = await installer.downloadDriver("stable");
127+
128+
expect(result).toEqual({ archive: "/tmp/chromedirver.zip" });
129+
expect(tcDownloadToolSpy).toHaveBeenCalled();
130+
});
131+
});
132+
133+
describe("installDriver", () => {
134+
test("install stable version", async () => {
135+
tcExtractZipSpy.mockResolvedValue("/tmp/chromedriver");
136+
cacheCacheDirSpy.mockResolvedValue("/path/to/chromedriver");
137+
138+
const result = await installer.installDriver(
139+
"stable",
140+
"/path/to/downloaded.deb",
141+
);
142+
143+
expect(result).toEqual({
144+
root: "/path/to/chromedriver",
145+
bin: "chromedriver",
146+
});
147+
expect(cacheCacheDirSpy).toHaveBeenCalledWith(
148+
"/tmp/chromedriver/chromedriver-linux64",
149+
"chromedriver",
150+
"stable",
151+
);
152+
});
153+
});
154+
});

__test__/channel_macos.test.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import * as fs from "node:fs";
2+
import * as exec from "@actions/exec";
3+
import * as tc from "@actions/tool-cache";
4+
import { afterEach, describe, expect, test, vi } from "vitest";
5+
import * as cache from "../src/cache";
6+
import { MacOSChannelInstaller } from "../src/channel_macos";
7+
8+
const cacheFindSpy = vi.spyOn(cache, "find");
9+
const cacheCacheDirSpy = vi.spyOn(cache, "cacheDir");
10+
const tcDownloadToolSpy = vi.spyOn(tc, "downloadTool");
11+
const tcExtractZipSpy = vi.spyOn(tc, "extractZip");
12+
const fsSymlinkSpy = vi.spyOn(fs.promises, "symlink");
13+
const execSpy = vi.spyOn(exec, "exec");
14+
15+
afterEach(() => {
16+
vi.resetAllMocks();
17+
});
18+
19+
describe("MacOSChannelInstaller", () => {
20+
const installer = new MacOSChannelInstaller({
21+
os: "darwin",
22+
arch: "amd64",
23+
});
24+
25+
describe("checkInstalledBrowser", () => {
26+
test("return undefined if not installed", async () => {
27+
cacheFindSpy.mockResolvedValue(undefined);
28+
29+
const result = await installer.checkInstalledBrowser("stable");
30+
31+
expect(result).toBeUndefined();
32+
});
33+
34+
test("return install result if installed", async () => {
35+
cacheFindSpy.mockResolvedValue("/path/to/Chromium.app");
36+
37+
const result = await installer.checkInstalledBrowser("stable");
38+
39+
expect(result).toEqual({
40+
root: "/path/to/Chromium.app",
41+
bin: "Contents/MacOS/chrome",
42+
});
43+
});
44+
});
45+
46+
describe("downloadBrowser", () => {
47+
test("throw error if version is not release channel", async () => {
48+
await expect(installer.downloadBrowser("foo")).rejects.toThrowError(
49+
"Unexpected version: foo",
50+
);
51+
});
52+
53+
test("download stable version", async () => {
54+
tcDownloadToolSpy.mockResolvedValue("/path/to/downloaded.dmg");
55+
56+
const result = await installer.downloadBrowser("stable");
57+
58+
expect(result).toEqual({ archive: "/path/to/downloaded.dmg" });
59+
});
60+
});
61+
62+
describe("installBrowser", () => {
63+
test("throw error if version is not release channel", async () => {
64+
await expect(
65+
installer.installBrowser("foo", "/path/to/downloaded.dmg"),
66+
).rejects.toThrowError("Unexpected version: foo");
67+
});
68+
69+
test("install stable version", async () => {
70+
execSpy.mockResolvedValue(0);
71+
fsSymlinkSpy.mockResolvedValue();
72+
cacheCacheDirSpy.mockResolvedValue("/path/to/Chromium.app");
73+
74+
const result = await installer.installBrowser(
75+
"stable",
76+
"/path/to/downloaded.dmg",
77+
);
78+
79+
expect(result).toEqual({
80+
root: "/path/to/Chromium.app",
81+
bin: "Contents/MacOS/chrome",
82+
});
83+
expect(cacheCacheDirSpy).toHaveBeenCalledWith(
84+
"/Volumes/downloaded.dmg/Google Chrome.app",
85+
"chromium",
86+
"stable",
87+
);
88+
});
89+
});
90+
91+
describe("checkInstalledDriver", () => {
92+
test("return undefined if not installed", async () => {
93+
cacheFindSpy.mockResolvedValue(undefined);
94+
95+
const result = await installer.checkInstalledDriver("stable");
96+
expect(result).toBeUndefined();
97+
});
98+
99+
test("return install result if installed", async () => {
100+
cacheFindSpy.mockResolvedValue("/path/to/chromedriver");
101+
102+
const result = await installer.checkInstalledDriver("stable");
103+
expect(result).toEqual({
104+
root: "/path/to/chromedriver",
105+
bin: "chromedriver",
106+
});
107+
});
108+
});
109+
110+
describe("downloadDriver", () => {
111+
test("throw error if version is not release channel", async () => {
112+
await expect(installer.downloadDriver("foo")).rejects.toThrowError(
113+
"Invalid version: foo",
114+
);
115+
});
116+
117+
test("download driver", async () => {
118+
tcDownloadToolSpy.mockResolvedValue("/path/to/downloaded.zip");
119+
120+
const result = await installer.downloadDriver("stable");
121+
122+
expect(result).toEqual({ archive: "/path/to/downloaded.zip" });
123+
});
124+
});
125+
126+
describe("installDriver", () => {
127+
test("install driver", async () => {
128+
cacheCacheDirSpy.mockResolvedValue("/path/to/chromedriver");
129+
tcExtractZipSpy.mockResolvedValue("/path/to/chromedriver");
130+
131+
const result = await installer.installDriver(
132+
"stable",
133+
"/path/to/chromedriver.zip",
134+
);
135+
136+
expect(result).toEqual({
137+
root: "/path/to/chromedriver",
138+
bin: "chromedriver",
139+
});
140+
expect(cacheCacheDirSpy).toHaveBeenCalledWith(
141+
"/path/to/chromedriver/chromedriver-mac-x64",
142+
"chromedriver",
143+
"stable",
144+
);
145+
});
146+
});
147+
});

0 commit comments

Comments
 (0)