appium-chromedriver 5.2.16 → 5.3.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/CHANGELOG.md +14 -0
- package/build/index.d.ts +11 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +18 -18
- package/build/index.js.map +1 -0
- package/build/lib/chromedriver.d.ts +108 -0
- package/build/lib/chromedriver.d.ts.map +1 -0
- package/build/lib/chromedriver.js +685 -559
- package/build/lib/chromedriver.js.map +1 -1
- package/build/lib/install.d.ts +3 -0
- package/build/lib/install.d.ts.map +1 -0
- package/build/lib/install.js +39 -32
- package/build/lib/install.js.map +1 -1
- package/build/lib/protocol-helpers.d.ts +15 -0
- package/build/lib/protocol-helpers.d.ts.map +1 -0
- package/build/lib/protocol-helpers.js +38 -21
- package/build/lib/protocol-helpers.js.map +1 -1
- package/build/lib/storage-client.d.ts +119 -0
- package/build/lib/storage-client.d.ts.map +1 -0
- package/build/lib/storage-client.js +393 -274
- package/build/lib/storage-client.js.map +1 -1
- package/build/lib/types.d.ts +101 -0
- package/build/lib/types.d.ts.map +1 -0
- package/build/lib/types.js +3 -0
- package/build/lib/types.js.map +1 -0
- package/build/lib/utils.d.ts +52 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +117 -78
- package/build/lib/utils.js.map +1 -1
- package/config/mapping.json +1 -0
- package/index.js +9 -0
- package/lib/chromedriver.js +300 -162
- package/lib/install.js +8 -1
- package/lib/protocol-helpers.js +17 -1
- package/lib/storage-client.js +165 -129
- package/lib/types.ts +101 -0
- package/lib/utils.js +86 -42
- package/package.json +34 -30
- package/tsconfig.json +11 -0
package/lib/install.js
CHANGED
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
|
|
8
8
|
const DOWNLOAD_TIMEOUT_MS = 15 * 1000;
|
|
9
9
|
const LATEST_VERSION = 'LATEST';
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param {string} ver
|
|
13
|
+
*/
|
|
11
14
|
async function formatCdVersion (ver) {
|
|
12
15
|
return ver === LATEST_VERSION
|
|
13
16
|
? (await retrieveData(`${CD_CDN}/LATEST_RELEASE`, {
|
|
@@ -17,6 +20,10 @@ async function formatCdVersion (ver) {
|
|
|
17
20
|
: ver;
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* @param {string} platformName
|
|
26
|
+
*/
|
|
20
27
|
async function prepareChromedriverDir (platformName) {
|
|
21
28
|
const chromedriverDir = getChromedriverDir(platformName);
|
|
22
29
|
if (!await fs.exists(chromedriverDir)) {
|
package/lib/protocol-helpers.js
CHANGED
|
@@ -3,12 +3,23 @@ import { isStandardCap } from '@appium/base-driver';
|
|
|
3
3
|
|
|
4
4
|
const W3C_PREFIX = 'goog:';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {string} capName
|
|
9
|
+
*/
|
|
6
10
|
function toW3cCapName (capName) {
|
|
7
11
|
return (_.isString(capName) && !capName.includes(':') && !isStandardCap(capName))
|
|
8
12
|
? `${W3C_PREFIX}${capName}`
|
|
9
13
|
: capName;
|
|
10
14
|
}
|
|
11
15
|
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {Record<string,any>} allCaps
|
|
19
|
+
* @param {string} rawCapName
|
|
20
|
+
* @param {any} defaultValue
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
12
23
|
function getCapValue (allCaps = {}, rawCapName, defaultValue) {
|
|
13
24
|
for (const [capName, capValue] of _.toPairs(allCaps)) {
|
|
14
25
|
if (toW3cCapName(capName) === toW3cCapName(rawCapName)) {
|
|
@@ -18,11 +29,16 @@ function getCapValue (allCaps = {}, rawCapName, defaultValue) {
|
|
|
18
29
|
return defaultValue;
|
|
19
30
|
}
|
|
20
31
|
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* @param {any} originalCaps
|
|
35
|
+
* @returns {Record<string,any>}
|
|
36
|
+
*/
|
|
21
37
|
function toW3cCapNames (originalCaps = {}) {
|
|
22
38
|
return _.reduce(originalCaps, (acc, value, key) => {
|
|
23
39
|
acc[toW3cCapName(key)] = value;
|
|
24
40
|
return acc;
|
|
25
|
-
}, {});
|
|
41
|
+
}, /** @type {Record<string,any>} */({}));
|
|
26
42
|
}
|
|
27
43
|
|
|
28
44
|
export { toW3cCapNames, getCapValue };
|
package/lib/storage-client.js
CHANGED
|
@@ -1,28 +1,45 @@
|
|
|
1
1
|
import {
|
|
2
|
-
getChromedriverDir,
|
|
3
|
-
|
|
2
|
+
getChromedriverDir,
|
|
3
|
+
CD_CDN,
|
|
4
|
+
retrieveData,
|
|
5
|
+
getOsInfo,
|
|
6
|
+
OS,
|
|
7
|
+
X64,
|
|
8
|
+
X86,
|
|
9
|
+
APPLE_ARM_SUFFIXES,
|
|
4
10
|
} from './utils';
|
|
5
11
|
import _ from 'lodash';
|
|
6
12
|
import xpath from 'xpath';
|
|
7
|
-
import {
|
|
13
|
+
import {DOMParser} from '@xmldom/xmldom';
|
|
8
14
|
import B from 'bluebird';
|
|
9
15
|
import path from 'path';
|
|
10
16
|
import os from 'os';
|
|
11
|
-
import {
|
|
12
|
-
|
|
17
|
+
import {system, fs, logger, tempDir, zip, util, net} from '@appium/support';
|
|
13
18
|
|
|
14
19
|
const TIMEOUT_MS = 15000;
|
|
15
20
|
const MAX_PARALLEL_DOWNLOADS = 5;
|
|
16
21
|
|
|
17
22
|
const log = logger.getLogger('ChromedriverStorageClient');
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
/**
|
|
25
|
+
*
|
|
26
|
+
* @param {string} src
|
|
27
|
+
* @param {string} checksum
|
|
28
|
+
* @returns {Promise<boolean>}
|
|
29
|
+
*/
|
|
30
|
+
async function isCrcOk(src, checksum) {
|
|
21
31
|
const md5 = await fs.hash(src, 'md5');
|
|
22
32
|
return _.toLower(md5) === _.toLower(checksum);
|
|
23
33
|
}
|
|
24
34
|
|
|
25
|
-
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {Node|Attr} parent
|
|
38
|
+
* @param {string?} childName
|
|
39
|
+
* @param {string?} text
|
|
40
|
+
* @returns
|
|
41
|
+
*/
|
|
42
|
+
function findChildNode(parent, childName = null, text = null) {
|
|
26
43
|
if (!childName && !text) {
|
|
27
44
|
return null;
|
|
28
45
|
}
|
|
@@ -31,7 +48,7 @@ function findChildNode (parent, childName = null, text = null) {
|
|
|
31
48
|
}
|
|
32
49
|
|
|
33
50
|
for (let childNodeIdx = 0; childNodeIdx < parent.childNodes.length; childNodeIdx++) {
|
|
34
|
-
const childNode = parent.childNodes[childNodeIdx];
|
|
51
|
+
const childNode = /** @type {Element|Attr} */ (parent.childNodes[childNodeIdx]);
|
|
35
52
|
if (childName && !text && childName === childNode.localName) {
|
|
36
53
|
return childNode;
|
|
37
54
|
}
|
|
@@ -51,29 +68,35 @@ function findChildNode (parent, childName = null, text = null) {
|
|
|
51
68
|
return null;
|
|
52
69
|
}
|
|
53
70
|
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
/**
|
|
72
|
+
*
|
|
73
|
+
* @param {Node?} node
|
|
74
|
+
* @returns
|
|
75
|
+
*/
|
|
76
|
+
function extractNodeText(node) {
|
|
77
|
+
return !node || !node.firstChild || !util.hasValue(node.firstChild.nodeValue)
|
|
56
78
|
? null
|
|
57
79
|
: node.firstChild.nodeValue;
|
|
58
80
|
}
|
|
59
81
|
|
|
60
|
-
|
|
61
82
|
class ChromedriverStorageClient {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @param {import('./types').ChromedriverStorageClientOpts} args
|
|
86
|
+
*/
|
|
87
|
+
constructor(args = {}) {
|
|
88
|
+
const {chromedriverDir = getChromedriverDir(), timeout = TIMEOUT_MS} = args;
|
|
67
89
|
this.chromedriverDir = chromedriverDir;
|
|
68
90
|
this.timeout = timeout;
|
|
91
|
+
/** @type {ChromedriverDetailsMapping} */
|
|
69
92
|
this.mapping = {};
|
|
70
93
|
}
|
|
71
94
|
|
|
72
95
|
/**
|
|
73
96
|
* @typedef {Object} AdditionalDriverDetails
|
|
74
|
-
* @property {?
|
|
97
|
+
* @property {string?} version - Chromedriver version
|
|
75
98
|
* or `null` if it cannot be found
|
|
76
|
-
* @property {?
|
|
99
|
+
* @property {string?} minBrowserVersion - The minimum browser version
|
|
77
100
|
* supported by chromedriver or `null` if it cannot be found
|
|
78
101
|
*/
|
|
79
102
|
|
|
@@ -84,7 +107,7 @@ class ChromedriverStorageClient {
|
|
|
84
107
|
* @param {string} content - Release notes of the corresponding chromedriver
|
|
85
108
|
* @returns {AdditionalDriverDetails}
|
|
86
109
|
*/
|
|
87
|
-
parseNotes
|
|
110
|
+
parseNotes(content) {
|
|
88
111
|
const result = {};
|
|
89
112
|
const versionMatch = /^\s*[-]+ChromeDriver[\D]+([\d.]+)/im.exec(content);
|
|
90
113
|
if (versionMatch) {
|
|
@@ -101,21 +124,27 @@ class ChromedriverStorageClient {
|
|
|
101
124
|
* Downloads chromedriver release notes and puts them
|
|
102
125
|
* into the dictionary argument
|
|
103
126
|
*
|
|
127
|
+
* The method call mutates by merging `AdditionalDriverDetails`
|
|
104
128
|
* @param {string} driverKey - Driver version plus archive name
|
|
105
129
|
* @param {string} notesUrl - The URL of chromedriver notes
|
|
106
|
-
* @param {
|
|
107
|
-
* The method call mutates by merging `AdditionalDriverDetails`
|
|
130
|
+
* @param {ChromedriverDetails} infoDict - The dictionary containing driver info.
|
|
108
131
|
* @throws {Error} if the release notes cannot be downloaded
|
|
109
132
|
*/
|
|
110
|
-
async retrieveAdditionalDriverInfo
|
|
111
|
-
const notes = await retrieveData(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
133
|
+
async retrieveAdditionalDriverInfo(driverKey, notesUrl, infoDict) {
|
|
134
|
+
const notes = await retrieveData(
|
|
135
|
+
notesUrl,
|
|
136
|
+
{
|
|
137
|
+
'user-agent': 'appium',
|
|
138
|
+
accept: '*/*',
|
|
139
|
+
},
|
|
140
|
+
{timeout: this.timeout}
|
|
141
|
+
);
|
|
142
|
+
const {minBrowserVersion} = this.parseNotes(notes);
|
|
116
143
|
if (!minBrowserVersion) {
|
|
117
|
-
log.debug(
|
|
118
|
-
`
|
|
144
|
+
log.debug(
|
|
145
|
+
`The driver '${driverKey}' does not contain valid release notes at ${notesUrl}. ` +
|
|
146
|
+
`Skipping it`
|
|
147
|
+
);
|
|
119
148
|
return;
|
|
120
149
|
}
|
|
121
150
|
infoDict.minBrowserVersion = minBrowserVersion;
|
|
@@ -125,14 +154,16 @@ class ChromedriverStorageClient {
|
|
|
125
154
|
* Parses chromedriver storage XML and stores
|
|
126
155
|
* the parsed results into `this.mapping`
|
|
127
156
|
*
|
|
128
|
-
* @param {
|
|
157
|
+
* @param {Document} doc - The DOM representation
|
|
129
158
|
* of the chromedriver storage XML
|
|
130
159
|
* @param {boolean} shouldParseNotes [true] - If set to `true`
|
|
131
160
|
* then additional drivers information is going to be parsed
|
|
132
161
|
* and assigned to `this.mapping`
|
|
133
162
|
*/
|
|
134
|
-
async parseStorageXml
|
|
135
|
-
const driverNodes =
|
|
163
|
+
async parseStorageXml(doc, shouldParseNotes = true) {
|
|
164
|
+
const driverNodes = /** @type {Array<Node|Attr>} */ (
|
|
165
|
+
xpath.select(`//*[local-name(.)='Contents']`, doc)
|
|
166
|
+
);
|
|
136
167
|
log.debug(`Parsed ${driverNodes.length} entries from storage XML`);
|
|
137
168
|
if (_.isEmpty(driverNodes)) {
|
|
138
169
|
return;
|
|
@@ -140,10 +171,11 @@ class ChromedriverStorageClient {
|
|
|
140
171
|
|
|
141
172
|
const promises = [];
|
|
142
173
|
for (const driverNode of driverNodes) {
|
|
143
|
-
const
|
|
144
|
-
if (!_.includes(
|
|
174
|
+
const k = extractNodeText(findChildNode(driverNode, 'Key'));
|
|
175
|
+
if (!_.includes(k, '/chromedriver_')) {
|
|
145
176
|
continue;
|
|
146
177
|
}
|
|
178
|
+
const key = String(k);
|
|
147
179
|
|
|
148
180
|
const etag = extractNodeText(findChildNode(driverNode, 'ETag'));
|
|
149
181
|
if (!etag) {
|
|
@@ -151,16 +183,20 @@ class ChromedriverStorageClient {
|
|
|
151
183
|
continue;
|
|
152
184
|
}
|
|
153
185
|
|
|
186
|
+
/** @type {ChromedriverDetails} */
|
|
154
187
|
const cdInfo = {
|
|
155
188
|
url: `${CD_CDN}/${key}`,
|
|
156
189
|
etag: _.trim(etag, '"'),
|
|
157
|
-
version: _.first(key.split('/')),
|
|
190
|
+
version: /** @type {string} */ (_.first(key.split('/'))),
|
|
191
|
+
minBrowserVersion: null,
|
|
158
192
|
};
|
|
159
193
|
this.mapping[key] = cdInfo;
|
|
160
194
|
|
|
161
195
|
const notesPath = `${cdInfo.version}/notes.txt`;
|
|
162
|
-
const isNotesPresent = !!driverNodes
|
|
163
|
-
|
|
196
|
+
const isNotesPresent = !!driverNodes.reduce(
|
|
197
|
+
(acc, node) => Boolean(acc || findChildNode(node, 'Key', notesPath)),
|
|
198
|
+
false
|
|
199
|
+
);
|
|
164
200
|
if (!isNotesPresent) {
|
|
165
201
|
cdInfo.minBrowserVersion = null;
|
|
166
202
|
if (shouldParseNotes) {
|
|
@@ -180,34 +216,23 @@ class ChromedriverStorageClient {
|
|
|
180
216
|
log.info(`The total count of entries in the mapping: ${_.size(this.mapping)}`);
|
|
181
217
|
}
|
|
182
218
|
|
|
183
|
-
/**
|
|
184
|
-
* @typedef {Object} DriverDetails
|
|
185
|
-
* @property {string} url - The full url to the corresponding driver in
|
|
186
|
-
* the remote storage
|
|
187
|
-
* @property {string} etag - The CRC of the driver archive
|
|
188
|
-
* @property {string} version - Chromedriver version
|
|
189
|
-
*/
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* @typedef {Object} ChromedriversMapping
|
|
193
|
-
* @property {DriverDetails} - The keys are unique driver identifiers
|
|
194
|
-
* (version/archive name). The corresponding values have `DriverDetails`
|
|
195
|
-
* containing chromedriver details
|
|
196
|
-
*/
|
|
197
|
-
|
|
198
219
|
/**
|
|
199
220
|
* Retrieves chromedriver mapping from the storage
|
|
200
221
|
*
|
|
201
222
|
* @param {boolean} shouldParseNotes [true] - if set to `true`
|
|
202
223
|
* then additional chromedrivers info is going to be retrieved and
|
|
203
224
|
* parsed from release notes
|
|
204
|
-
* @returns {
|
|
225
|
+
* @returns {Promise<ChromedriverDetailsMapping>}
|
|
205
226
|
*/
|
|
206
|
-
async retrieveMapping
|
|
207
|
-
const xml = await retrieveData(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
227
|
+
async retrieveMapping(shouldParseNotes = true) {
|
|
228
|
+
const xml = await retrieveData(
|
|
229
|
+
CD_CDN,
|
|
230
|
+
{
|
|
231
|
+
'user-agent': 'appium',
|
|
232
|
+
accept: 'application/xml, */*',
|
|
233
|
+
},
|
|
234
|
+
{timeout: this.timeout}
|
|
235
|
+
);
|
|
211
236
|
const doc = new DOMParser().parseFromString(xml);
|
|
212
237
|
await this.parseStorageXml(doc, shouldParseNotes);
|
|
213
238
|
return _.cloneDeep(this.mapping);
|
|
@@ -220,54 +245,50 @@ class ChromedriverStorageClient {
|
|
|
220
245
|
* @param {string} src - The source archive path
|
|
221
246
|
* @param {string} dst - The destination chromedriver path
|
|
222
247
|
*/
|
|
223
|
-
async unzipDriver
|
|
248
|
+
async unzipDriver(src, dst) {
|
|
224
249
|
const tmpRoot = await tempDir.openDir();
|
|
225
250
|
try {
|
|
226
251
|
await zip.extractAllTo(src, tmpRoot);
|
|
227
|
-
const chromedriverPath = await fs.walkDir(
|
|
228
|
-
|
|
252
|
+
const chromedriverPath = await fs.walkDir(
|
|
253
|
+
tmpRoot,
|
|
254
|
+
true,
|
|
255
|
+
(itemPath, isDirectory) =>
|
|
256
|
+
!isDirectory && _.toLower(path.parse(itemPath).name) === 'chromedriver'
|
|
257
|
+
);
|
|
229
258
|
if (!chromedriverPath) {
|
|
230
|
-
throw new Error(
|
|
259
|
+
throw new Error(
|
|
260
|
+
'The archive was unzipped properly, but we could not find any chromedriver executable'
|
|
261
|
+
);
|
|
231
262
|
}
|
|
232
263
|
log.debug(`Moving the extracted '${path.basename(chromedriverPath)}' to '${dst}'`);
|
|
233
264
|
await fs.mv(chromedriverPath, dst, {
|
|
234
|
-
mkdirp: true
|
|
265
|
+
mkdirp: true,
|
|
235
266
|
});
|
|
236
267
|
} finally {
|
|
237
268
|
await fs.rimraf(tmpRoot);
|
|
238
269
|
}
|
|
239
270
|
}
|
|
240
271
|
|
|
241
|
-
/**
|
|
242
|
-
* @typedef {Object} OSInfo
|
|
243
|
-
* @property {string} name - The name of the host OS
|
|
244
|
-
* Can be either `mac`, `windows` or `linux`
|
|
245
|
-
* @property {string} arch - The architecture of the host OS.
|
|
246
|
-
* Can be either `32` or `64`
|
|
247
|
-
*/
|
|
248
|
-
|
|
249
272
|
/**
|
|
250
273
|
* Filters `this.mapping` to only select matching
|
|
251
274
|
* chromedriver entries by operating system information
|
|
252
275
|
* and/or additional synchronization options (if provided)
|
|
253
276
|
*
|
|
254
|
-
* @param {
|
|
255
|
-
* @param {
|
|
277
|
+
* @param {OSInfo} osInfo
|
|
278
|
+
* @param {SyncOptions} opts
|
|
256
279
|
* @returns {Array<String>} The list of filtered chromedriver
|
|
257
280
|
* entry names (version/archive name)
|
|
258
281
|
*/
|
|
259
|
-
selectMatchingDrivers
|
|
260
|
-
const {
|
|
261
|
-
minBrowserVersion,
|
|
262
|
-
versions = [],
|
|
263
|
-
} = opts;
|
|
282
|
+
selectMatchingDrivers(osInfo, opts = {}) {
|
|
283
|
+
const {minBrowserVersion, versions = []} = opts;
|
|
264
284
|
let driversToSync = _.keys(this.mapping);
|
|
265
285
|
|
|
266
286
|
if (!_.isEmpty(versions)) {
|
|
267
287
|
// Handle only selected versions if requested
|
|
268
288
|
log.debug(`Selecting chromedrivers whose versions match to ${versions}`);
|
|
269
|
-
driversToSync = driversToSync
|
|
270
|
-
|
|
289
|
+
driversToSync = driversToSync.filter((cdName) =>
|
|
290
|
+
versions.includes(`${this.mapping[cdName].version}`)
|
|
291
|
+
);
|
|
271
292
|
|
|
272
293
|
log.debug(`Got ${util.pluralize('item', driversToSync.length, true)}`);
|
|
273
294
|
if (_.isEmpty(driversToSync)) {
|
|
@@ -275,29 +296,41 @@ class ChromedriverStorageClient {
|
|
|
275
296
|
}
|
|
276
297
|
}
|
|
277
298
|
|
|
278
|
-
if (!isNaN(minBrowserVersion)) {
|
|
299
|
+
if (_.isString(minBrowserVersion) && !Number.isNaN(minBrowserVersion)) {
|
|
279
300
|
// Only select drivers that support the current browser whose major version number equals to `minBrowserVersion`
|
|
280
301
|
const minBrowserVersionInt = parseInt(minBrowserVersion, 10);
|
|
281
|
-
log.debug(
|
|
302
|
+
log.debug(
|
|
303
|
+
`Selecting chromedrivers whose minimum supported browser version matches to ${minBrowserVersionInt}`
|
|
304
|
+
);
|
|
282
305
|
let closestMatchedVersionNumber = 0;
|
|
283
306
|
// Select the newest available and compatible chromedriver
|
|
284
307
|
for (const cdName of driversToSync) {
|
|
285
|
-
const currentMinBrowserVersion = parseInt(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
308
|
+
const currentMinBrowserVersion = parseInt(
|
|
309
|
+
String(this.mapping[cdName].minBrowserVersion),
|
|
310
|
+
10
|
|
311
|
+
);
|
|
312
|
+
if (
|
|
313
|
+
!Number.isNaN(currentMinBrowserVersion) &&
|
|
314
|
+
currentMinBrowserVersion <= minBrowserVersionInt &&
|
|
315
|
+
closestMatchedVersionNumber < currentMinBrowserVersion
|
|
316
|
+
) {
|
|
289
317
|
closestMatchedVersionNumber = currentMinBrowserVersion;
|
|
290
318
|
}
|
|
291
319
|
}
|
|
292
|
-
driversToSync = driversToSync.filter(
|
|
293
|
-
|
|
320
|
+
driversToSync = driversToSync.filter(
|
|
321
|
+
(cdName) =>
|
|
322
|
+
`${this.mapping[cdName].minBrowserVersion}` ===
|
|
323
|
+
`${closestMatchedVersionNumber > 0 ? closestMatchedVersionNumber : minBrowserVersionInt}`
|
|
324
|
+
);
|
|
294
325
|
|
|
295
326
|
log.debug(`Got ${util.pluralize('item', driversToSync.length, true)}`);
|
|
296
327
|
if (_.isEmpty(driversToSync)) {
|
|
297
328
|
return [];
|
|
298
329
|
}
|
|
299
|
-
log.debug(
|
|
300
|
-
`
|
|
330
|
+
log.debug(
|
|
331
|
+
`Will select candidate ${util.pluralize('driver', driversToSync.length)} ` +
|
|
332
|
+
`versioned as '${_.uniq(driversToSync.map((cdName) => this.mapping[cdName].version))}'`
|
|
333
|
+
);
|
|
301
334
|
}
|
|
302
335
|
|
|
303
336
|
if (!_.isEmpty(osInfo)) {
|
|
@@ -338,27 +371,28 @@ class ChromedriverStorageClient {
|
|
|
338
371
|
* or return a boolean result if the driver retrieval process fails
|
|
339
372
|
* @throws {Error} if there was a failure while retrieving the driver
|
|
340
373
|
* and `isStrict` is set to `true`
|
|
341
|
-
* @returns {boolean} if `true` then the chromedriver is successfully
|
|
374
|
+
* @returns {Promise<boolean>} if `true` then the chromedriver is successfully
|
|
342
375
|
* downloaded and extracted.
|
|
343
376
|
*/
|
|
344
|
-
async retrieveDriver
|
|
345
|
-
const {
|
|
377
|
+
async retrieveDriver(index, driverKey, archivesRoot, isStrict = false) {
|
|
378
|
+
const {url, etag, version} = this.mapping[driverKey];
|
|
346
379
|
const archivePath = path.resolve(archivesRoot, `${index}.zip`);
|
|
347
380
|
log.debug(`Retrieving '${url}' to '${archivePath}'`);
|
|
348
381
|
try {
|
|
349
382
|
await net.downloadFile(url, archivePath, {
|
|
350
383
|
isMetered: false,
|
|
351
|
-
timeout: TIMEOUT_MS
|
|
384
|
+
timeout: TIMEOUT_MS,
|
|
352
385
|
});
|
|
353
386
|
} catch (e) {
|
|
354
|
-
const
|
|
387
|
+
const err = /** @type {Error} */ (e);
|
|
388
|
+
const msg = `Cannot download chromedriver archive. Original error: ${err.message}`;
|
|
355
389
|
if (isStrict) {
|
|
356
390
|
throw new Error(msg);
|
|
357
391
|
}
|
|
358
392
|
log.error(msg);
|
|
359
393
|
return false;
|
|
360
394
|
}
|
|
361
|
-
if (!await isCrcOk(archivePath, etag)) {
|
|
395
|
+
if (!(await isCrcOk(archivePath, etag))) {
|
|
362
396
|
const msg = `The checksum for the downloaded chromedriver '${driverKey}' did not match`;
|
|
363
397
|
if (isStrict) {
|
|
364
398
|
throw new Error(msg);
|
|
@@ -366,46 +400,33 @@ class ChromedriverStorageClient {
|
|
|
366
400
|
log.error(msg);
|
|
367
401
|
return false;
|
|
368
402
|
}
|
|
369
|
-
const fileName = `${path.parse(url).name}_v${version}` +
|
|
370
|
-
(system.isWindows() ? '.exe' : '');
|
|
403
|
+
const fileName = `${path.parse(url).name}_v${version}` + (system.isWindows() ? '.exe' : '');
|
|
371
404
|
const targetPath = path.resolve(this.chromedriverDir, fileName);
|
|
372
405
|
try {
|
|
373
406
|
await this.unzipDriver(archivePath, targetPath);
|
|
374
407
|
await fs.chmod(targetPath, 0o755);
|
|
375
408
|
log.debug(`Permissions of the file '${targetPath}' have been changed to 755`);
|
|
376
409
|
} catch (e) {
|
|
410
|
+
const err = /** @type {Error} */ (e);
|
|
377
411
|
if (isStrict) {
|
|
378
|
-
throw
|
|
412
|
+
throw err;
|
|
379
413
|
}
|
|
380
|
-
log.error(
|
|
414
|
+
log.error(err.message);
|
|
381
415
|
return false;
|
|
382
416
|
}
|
|
383
417
|
return true;
|
|
384
418
|
}
|
|
385
419
|
|
|
386
|
-
/**
|
|
387
|
-
* @typedef {Object} SyncOptions
|
|
388
|
-
* @property {Array<String>} versions - The list of chromedriver
|
|
389
|
-
* versions to sync. If empty (the default value) then all available
|
|
390
|
-
* chromedrivers are going to be downloaded and extracted
|
|
391
|
-
* @property {string|number} minBrowserVersion - The minumum supported
|
|
392
|
-
* Chrome version that downloaded chromedrivers should support. Can match
|
|
393
|
-
* multiple drivers.
|
|
394
|
-
* @property {?OSInfo} osInfo - System information used to filter out
|
|
395
|
-
* the list of the retrieved drivers. If not provided then the script
|
|
396
|
-
* will try to retrieve it.
|
|
397
|
-
*/
|
|
398
|
-
|
|
399
420
|
/**
|
|
400
421
|
* Retrieves chromedrivers from the remote storage
|
|
401
422
|
* to the local file system
|
|
402
423
|
*
|
|
403
|
-
* @param {
|
|
424
|
+
* @param {SyncOptions} opts
|
|
404
425
|
* @throws {Error} if there was a problem while retrieving
|
|
405
426
|
* the drivers
|
|
406
|
-
* @returns {
|
|
427
|
+
* @returns {Promise<string[]>} The list of successfully synchronized driver keys
|
|
407
428
|
*/
|
|
408
|
-
async syncDrivers
|
|
429
|
+
async syncDrivers(opts = {}) {
|
|
409
430
|
if (_.isEmpty(this.mapping)) {
|
|
410
431
|
await this.retrieveMapping(!!opts.minBrowserVersion);
|
|
411
432
|
}
|
|
@@ -413,24 +434,31 @@ class ChromedriverStorageClient {
|
|
|
413
434
|
throw new Error('Cannot retrieve chromedrivers mapping from Google storage');
|
|
414
435
|
}
|
|
415
436
|
|
|
416
|
-
const driversToSync = this.selectMatchingDrivers(opts.osInfo ?? await getOsInfo(), opts);
|
|
437
|
+
const driversToSync = this.selectMatchingDrivers(opts.osInfo ?? (await getOsInfo()), opts);
|
|
417
438
|
if (_.isEmpty(driversToSync)) {
|
|
418
439
|
log.debug(`There are no drivers to sync. Exiting`);
|
|
419
440
|
return [];
|
|
420
441
|
}
|
|
421
|
-
log.debug(
|
|
422
|
-
|
|
423
|
-
|
|
442
|
+
log.debug(
|
|
443
|
+
`Got ${util.pluralize('driver', driversToSync.length, true)} to sync: ` +
|
|
444
|
+
JSON.stringify(driversToSync, null, 2)
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* @type {string[]}
|
|
449
|
+
*/
|
|
424
450
|
const synchronizedDrivers = [];
|
|
425
451
|
const promises = [];
|
|
426
452
|
const archivesRoot = await tempDir.openDir();
|
|
427
453
|
try {
|
|
428
454
|
for (const [idx, driverKey] of driversToSync.entries()) {
|
|
429
|
-
promises.push(
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
455
|
+
promises.push(
|
|
456
|
+
(async () => {
|
|
457
|
+
if (await this.retrieveDriver(idx, driverKey, archivesRoot, !_.isEmpty(opts))) {
|
|
458
|
+
synchronizedDrivers.push(driverKey);
|
|
459
|
+
}
|
|
460
|
+
})()
|
|
461
|
+
);
|
|
434
462
|
|
|
435
463
|
if (promises.length % MAX_PARALLEL_DOWNLOADS === 0) {
|
|
436
464
|
await B.all(promises);
|
|
@@ -441,8 +469,10 @@ class ChromedriverStorageClient {
|
|
|
441
469
|
await fs.rimraf(archivesRoot);
|
|
442
470
|
}
|
|
443
471
|
if (!_.isEmpty(synchronizedDrivers)) {
|
|
444
|
-
log.info(
|
|
445
|
-
|
|
472
|
+
log.info(
|
|
473
|
+
`Successfully synchronized ` +
|
|
474
|
+
`${util.pluralize('chromedriver', synchronizedDrivers.length, true)}`
|
|
475
|
+
);
|
|
446
476
|
} else {
|
|
447
477
|
log.info(`No chromedrivers were synchronized`);
|
|
448
478
|
}
|
|
@@ -450,5 +480,11 @@ class ChromedriverStorageClient {
|
|
|
450
480
|
}
|
|
451
481
|
}
|
|
452
482
|
|
|
453
|
-
|
|
454
483
|
export default ChromedriverStorageClient;
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* @typedef {import('./types').SyncOptions} SyncOptions
|
|
487
|
+
* @typedef {import('./types').OSInfo} OSInfo
|
|
488
|
+
* @typedef {import('./types').ChromedriverDetails} ChromedriverDetails
|
|
489
|
+
* @typedef {import('./types').ChromedriverDetailsMapping} ChromedriverDetailsMapping
|
|
490
|
+
*/
|