Skip to content

Commit 42b8eb8

Browse files
authored
chrome for testing feature (#817)
* chrome for testing feature * linter fixes * reverted to 8.3.0
1 parent 0537531 commit 42b8eb8

File tree

13 files changed

+10436
-132
lines changed

13 files changed

+10436
-132
lines changed

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
fetch-depth: 0
4242
- uses: actions/setup-node@v1
4343
with:
44-
node-version: 14.x
44+
node-version: 16.x
4545
- name: NPM Setup
4646
run: |
4747
npm set registry "https://registry.npmjs.org/"
@@ -90,4 +90,4 @@ jobs:
9090
context: .
9191
# always push given this workflow is triggered manually
9292
push: true
93-
tags: webdriverio/selenium-standalone:v${{ steps.version.outputs.TAG_NAME }}, webdriverio/selenium-standalone:latest
93+
tags: webdriverio/selenium-standalone:v${{ steps.version.outputs.TAG_NAME }}, webdriverio/selenium-standalone:latest

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
runs-on: ubuntu-latest
88
strategy:
99
matrix:
10-
node-version: ['12.x', '14.x', '16.x']
10+
node-version: ['16.x', '18.x']
1111
java: ['11']
1212
steps:
1313
- uses: actions/checkout@v2
@@ -31,4 +31,4 @@ jobs:
3131
- name: Test
3232
run: npm run test
3333
env:
34-
CI: true
34+
CI: true

.github/workflows/update.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ jobs:
4444
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4545
# The "auto" flag will only merge once all of the target branch's required checks
4646
# are met. Configure those in the "branch protection" settings for each repo.
47-
run: gh pr merge --auto --squash "$PR_URL"
47+
run: gh pr merge --auto --squash "$PR_URL"

lib/compute-download-urls.js

Lines changed: 142 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
module.exports = computeDownloadUrls;
1+
module.exports = { computeDownloadUrls, resolveDownloadPath, getLastChromedriverVersionFromMajor, getLatestChromium };
22

33
const got = require('got');
44
const util = require('util');
55
const { isSelenium4 } = require('./isSelenium4');
6+
const { detectBrowserPlatform, resolveBuildId } = require('@puppeteer/browsers');
7+
const { validateMajorVersionPrefix, getVersionWithZeroedPatchPart } = require('./validation');
8+
const axios = require('axios');
69

710
const urls = {
811
selenium: '%s/%s/selenium-server-standalone-%s.jar',
@@ -37,7 +40,9 @@ const urls = {
3740
* @param {string} opts.seleniumBaseURL - Base URL for where to download selenium.jar from
3841
* @param {Object} opts.drivers - Object containing options for various drivers. See comment for object format.
3942
*/
40-
async function computeDownloadUrls(opts) {
43+
async function computeDownloadUrls(options) {
44+
const opts = Object.assign({}, options);
45+
4146
const downloadUrls = {
4247
selenium:
4348
opts.seleniumFullURL ||
@@ -51,16 +56,83 @@ async function computeDownloadUrls(opts) {
5156
),
5257
};
5358
if (opts.drivers.chrome) {
54-
await resolveLatestVersion(opts, 'chrome', opts.drivers.chrome.baseURL + '/LATEST_RELEASE');
55-
56-
downloadUrls.chrome =
57-
opts.drivers.chrome.fullURL ||
58-
util.format(
59-
urls.chrome,
60-
opts.drivers.chrome.baseURL,
59+
if (opts.drivers.chrome.version === 'latest' && !opts.drivers.chrome.fullURL) {
60+
opts.drivers.chrome.version = await resolveBuildId(
61+
'chromedriver',
62+
detectBrowserPlatform(),
63+
opts.drivers.chrome.channel ? opts.drivers.chrome.channel : 'stable'
64+
);
65+
downloadUrls.chrome = resolveDownloadUrl(
66+
detectBrowserPlatform(),
6167
opts.drivers.chrome.version,
62-
getChromeDriverArchitecture(opts.drivers.chrome.arch, opts.drivers.chrome.version)
68+
opts.drivers.chrome.baseURL
6369
);
70+
} else if (
71+
validateMajorVersionPrefix(opts.drivers.chrome.version) &&
72+
Number(validateMajorVersionPrefix(opts.drivers.chrome.version)) > 114 &&
73+
!opts.drivers.chrome.fullURL
74+
) {
75+
const lastVersionFromMajor = opts.drivers.chrome.major
76+
? opts.drivers.chrome.major
77+
: await getLastChromedriverVersionFromMajor(opts.drivers.chrome.version);
78+
79+
if (lastVersionFromMajor) {
80+
const url = lastVersionFromMajor.downloads.chrome
81+
.map((m) => {
82+
if (m.platform === detectBrowserPlatform()) {
83+
return m.url;
84+
}
85+
})
86+
.filter((f) => f !== undefined);
87+
88+
if (url.length) {
89+
downloadUrls.chrome = url[0];
90+
opts.drivers.chrome.version = lastVersionFromMajor.version;
91+
} else {
92+
console.log(`Url for the latest for major:${url} fallback to latest`);
93+
94+
opts.drivers.chrome.version = await resolveBuildId(
95+
'chromedriver',
96+
detectBrowserPlatform(),
97+
opts.drivers.chrome.channel ? opts.drivers.chrome.channel : 'stable'
98+
);
99+
downloadUrls.chrome = resolveDownloadUrl(
100+
detectBrowserPlatform(),
101+
opts.drivers.chrome.version,
102+
opts.drivers.chrome.baseURL
103+
);
104+
}
105+
} else {
106+
console.log(`The latest for major ${opts.drivers.chrome.version}:${lastVersionFromMajor} fallback to latest`);
107+
108+
opts.drivers.chrome.version = await resolveBuildId(
109+
'chromedriver',
110+
detectBrowserPlatform(),
111+
opts.drivers.chrome.channel ? opts.drivers.chrome.channel : 'stable'
112+
);
113+
downloadUrls.chrome = resolveDownloadUrl(
114+
detectBrowserPlatform(),
115+
opts.drivers.chrome.version,
116+
opts.drivers.chrome.baseURL
117+
);
118+
}
119+
} else if (opts.drivers.chrome.fullURL) {
120+
downloadUrls.chrome = opts.drivers.chrome.fullURL;
121+
} else {
122+
opts.drivers.chrome.baseURL = opts.drivers.chrome.baseURL
123+
? opts.drivers.chrome.baseURL
124+
: 'https://chromedriver.storage.googleapis.com';
125+
await resolveLatestVersion(opts, 'chrome', opts.drivers.chrome.baseURL + '/LATEST_RELEASE');
126+
127+
downloadUrls.chrome =
128+
opts.drivers.chrome.fullURL ||
129+
util.format(
130+
urls.chrome,
131+
opts.drivers.chrome.baseURL,
132+
opts.drivers.chrome.version,
133+
getChromeDriverArchitecture(opts.drivers.chrome.arch, opts.drivers.chrome.version)
134+
);
135+
}
64136
}
65137
if (opts.drivers.ie) {
66138
downloadUrls.ie =
@@ -104,7 +176,6 @@ async function computeDownloadUrls(opts) {
104176
getChromiumEdgeDriverArchitecture(opts.drivers.chromiumedge.arch, opts.drivers.chromiumedge.version)
105177
);
106178
}
107-
108179
return downloadUrls;
109180
}
110181

@@ -219,7 +290,7 @@ async function chromiumEdgeBundleAvailable(opts) {
219290
}
220291

221292
async function resolveLatestVersion(opts, browserDriver, url) {
222-
if (opts.drivers[browserDriver].version === 'latest') {
293+
if (opts.drivers[browserDriver].version === 'latest' && browserDriver !== 'chrome') {
223294
try {
224295
if (browserDriver === 'firefox') {
225296
if (await getLatestGeckodriver(opts, browserDriver, url)) {
@@ -234,27 +305,31 @@ async function resolveLatestVersion(opts, browserDriver, url) {
234305
return true;
235306
}
236307
}
237-
} else if (await getLatestChromium(opts, browserDriver, url)) {
238-
return true;
239308
}
240309
} catch (_) {
241310
// eslint-disable-next-line no-empty
242311
}
243312
// eslint-disable-next-line no-param-reassign
244313
opts.drivers[browserDriver].version = opts.drivers[browserDriver].fallbackVersion;
314+
} else if (browserDriver === 'chrome' && /^(\d{3})\.\d+\.\d+/.test(opts.drivers.chrome.version)) {
315+
if (await getLatestChromium(opts, browserDriver, url)) {
316+
return true;
317+
}
245318
}
246319
}
247320

248321
async function getLatestChromium(opts, browserDriver, url) {
249-
const response = await got(url, { timeout: 10000 });
250-
// edgewebdriver latest version file contains invalid characters
251-
const version = response.body.replace(/\r|\n/g, '').replace(/[^\d|.]/g, '');
252-
if (!version) {
322+
try {
323+
const response = await got(url, { timeout: 10000 });
324+
// edgewebdriver latest version file contains invalid characters
325+
const version = response.body.replace(/\r|\n/g, '').replace(/[^\d|.]/g, '');
326+
327+
// eslint-disable-next-line no-param-reassign
328+
opts.drivers[browserDriver].version = version;
329+
return true;
330+
} catch {
253331
return false;
254332
}
255-
// eslint-disable-next-line no-param-reassign
256-
opts.drivers[browserDriver].version = version;
257-
return true;
258333
}
259334

260335
async function getLatestGeckodriver(opts, browserDriver, url) {
@@ -266,12 +341,51 @@ async function getLatestGeckodriver(opts, browserDriver, url) {
266341
}
267342
}
268343

269-
function getVersionWithZeroedPatchPart(fullVersion) {
270-
if (!/^\d+\.\d+\.\d+$/i.test(fullVersion)) {
271-
// If version longer than just 3 numbers, like '4.0.0-beta-1', do nothing
272-
return fullVersion;
344+
function resolveDownloadUrl(
345+
platform,
346+
buildId,
347+
baseUrl = 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing'
348+
) {
349+
return `${baseUrl}/${resolveDownloadPath(platform, buildId).join('/')}`;
350+
}
351+
352+
function folder(platform) {
353+
switch (platform) {
354+
case 'linux':
355+
return 'linux64';
356+
case 'mac_arm':
357+
return 'mac-arm64';
358+
case 'mac':
359+
return 'mac-x64';
360+
case 'win32':
361+
return 'win32';
362+
case 'win64':
363+
return 'win64';
273364
}
274-
// else make version patch part zero: '4.1.1' => '4.1.0'
275-
const [major, minor] = fullVersion.split('.');
276-
return `${major}.${minor}.0`;
365+
}
366+
367+
function resolveDownloadPath(platform, buildId) {
368+
return [buildId, folder(platform), `chromedriver-${folder(platform)}.zip`];
369+
}
370+
371+
async function getLastChromedriverVersionFromMajor(version) {
372+
const response = await axios({
373+
method: 'get',
374+
url: 'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json',
375+
responseType: 'json',
376+
headers: {
377+
'Content-Type': 'application/json',
378+
},
379+
});
380+
const versionsWithMajor = response.data.versions.filter(
381+
(f) =>
382+
validateMajorVersionPrefix(f.version) === validateMajorVersionPrefix(version) && 'chromedriver' in f.downloads
383+
);
384+
versionsWithMajor
385+
.sort((version1, version2) => {
386+
return version1.version.localeCompare(version2.version, undefined, { numeric: true, sensitivity: 'base' });
387+
})
388+
.reverse();
389+
390+
return versionsWithMajor.length ? versionsWithMajor[0] : null;
277391
}

lib/compute-fs-paths.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
11
module.exports = computeFsPaths;
22

33
const path = require('path');
4+
const { getLastChromedriverVersionFromMajor, getLatestChromium } = require('./compute-download-urls');
5+
const { validateMajorVersionPrefix } = require('./validation');
46

57
const basePath = path.join(__dirname, '..', '.selenium');
68

7-
function computeFsPaths(options) {
9+
async function computeFsPaths(options) {
810
let fsPaths = {};
911
const opts = Object.assign({}, options);
1012
opts.basePath = opts.basePath || basePath;
1113
if (opts.drivers.chrome) {
14+
if (
15+
opts.drivers.chrome.version !== 'latest' &&
16+
Number(validateMajorVersionPrefix(opts.drivers.chrome.version)) > 114
17+
) {
18+
const version = await getLastChromedriverVersionFromMajor(opts.drivers.chrome.version);
19+
opts.drivers.chrome.major = version;
20+
opts.drivers.chrome.version = version ? version.version : 'latest';
21+
} else if (
22+
opts.drivers.chrome.version !== 'latest' &&
23+
Number(validateMajorVersionPrefix(opts.drivers.chrome.version)) <= 114
24+
) {
25+
await getLatestChromium(opts, 'chrome', 'https://chromedriver.storage.googleapis.com/LATEST_RELEASE');
26+
}
1227
fsPaths.chrome = {
1328
installPath: path.join(
1429
opts.basePath,

lib/default-config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ module.exports = () => {
55
drivers: {
66
chrome: {
77
version: 'latest',
8-
fallbackVersion: '94.0.4606.61',
8+
channel: 'stable',
99
arch: process.arch,
10-
baseURL: 'https://chromedriver.storage.googleapis.com',
10+
baseURL: 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing',
1111
},
1212
ie: {
1313
version: '3.150.1',

lib/install.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const got = require('got');
88
const merge = require('lodash.merge');
99
const mapValues = require('lodash.mapvalues');
1010

11-
const computeDownloadUrls = require('./compute-download-urls');
11+
const { computeDownloadUrls } = require('./compute-download-urls');
1212
const computeFsPaths = require('./compute-fs-paths');
1313
const defaultConfig = require('./default-config')();
1414
const noop = require('./noop');
@@ -67,7 +67,7 @@ async function install(_opts) {
6767
delete opts.drivers.edge;
6868
}
6969

70-
const requestOpts = Object.assign({ timeout: 90000 }, opts.requestOpts);
70+
const requestOpts = Object.assign({ timeout: 320000 }, opts.requestOpts);
7171
if (opts.proxy) {
7272
requestOpts.proxy = opts.proxy;
7373
}
@@ -79,7 +79,7 @@ async function install(_opts) {
7979
logger('----------');
8080
logger('');
8181

82-
const fsPaths = computeFsPaths({
82+
const fsPaths = await computeFsPaths({
8383
seleniumVersion: opts.version,
8484
drivers: opts.drivers,
8585
basePath: opts.basePath,

lib/start.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ async function start(_opts) {
6262
}
6363
}
6464

65-
const fsPaths = computeFsPaths({
65+
const fsPaths = await computeFsPaths({
6666
seleniumVersion: opts.version,
6767
drivers: opts.drivers,
6868
basePath: opts.basePath,

lib/validation.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = { validateMajorVersionPrefix, getVersionWithZeroedPatchPart };
2+
3+
function validateMajorVersionPrefix(possibleMajorPrefix) {
4+
let prefix;
5+
6+
if (possibleMajorPrefix) {
7+
prefix = possibleMajorPrefix.match(/^[+-]?([0-9]+)/);
8+
}
9+
return prefix && prefix.length > 0 ? prefix[0] : '';
10+
}
11+
12+
function getVersionWithZeroedPatchPart(fullVersion) {
13+
if (!/^\d+\.\d+\.\d+$/i.test(fullVersion)) {
14+
// If version longer than just 3 numbers, like '4.0.0-beta-1', do nothing
15+
return fullVersion;
16+
}
17+
// else make version patch part zero: '4.1.1' => '4.1.0'
18+
const [major, minor] = fullVersion.split('.');
19+
return `${major}.${minor}.0`;
20+
}

0 commit comments

Comments
 (0)