appium-chromedriver 5.5.3 → 5.6.1

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +14 -0
  3. package/build/lib/chromedriver.d.ts +1 -1
  4. package/build/lib/chromedriver.d.ts.map +1 -1
  5. package/build/lib/chromedriver.js +4 -4
  6. package/build/lib/chromedriver.js.map +1 -1
  7. package/build/lib/constants.d.ts +19 -0
  8. package/build/lib/constants.d.ts.map +1 -0
  9. package/build/lib/constants.js +26 -0
  10. package/build/lib/constants.js.map +1 -0
  11. package/build/lib/index.d.ts +1 -1
  12. package/build/lib/index.d.ts.map +1 -1
  13. package/build/lib/index.js +1 -1
  14. package/build/lib/index.js.map +1 -1
  15. package/build/lib/install.d.ts.map +1 -1
  16. package/build/lib/install.js +23 -8
  17. package/build/lib/install.js.map +1 -1
  18. package/build/lib/storage-client/chromelabs.d.ts +22 -0
  19. package/build/lib/storage-client/chromelabs.d.ts.map +1 -0
  20. package/build/lib/storage-client/chromelabs.js +148 -0
  21. package/build/lib/storage-client/chromelabs.js.map +1 -0
  22. package/build/lib/storage-client/googleapis.d.ts +32 -0
  23. package/build/lib/storage-client/googleapis.d.ts.map +1 -0
  24. package/build/lib/storage-client/googleapis.js +188 -0
  25. package/build/lib/storage-client/googleapis.js.map +1 -0
  26. package/build/lib/{storage-client.d.ts → storage-client/storage-client.d.ts} +14 -36
  27. package/build/lib/storage-client/storage-client.d.ts.map +1 -0
  28. package/build/lib/{storage-client.js → storage-client/storage-client.js} +105 -181
  29. package/build/lib/storage-client/storage-client.js.map +1 -0
  30. package/build/lib/types.d.ts +9 -3
  31. package/build/lib/types.d.ts.map +1 -1
  32. package/build/lib/utils.d.ts +6 -9
  33. package/build/lib/utils.d.ts.map +1 -1
  34. package/build/lib/utils.js +20 -21
  35. package/build/lib/utils.js.map +1 -1
  36. package/config/mapping.json +1 -0
  37. package/lib/chromedriver.js +4 -5
  38. package/lib/constants.js +24 -0
  39. package/lib/index.ts +1 -1
  40. package/lib/install.js +27 -10
  41. package/lib/storage-client/chromelabs.js +141 -0
  42. package/lib/storage-client/googleapis.js +209 -0
  43. package/lib/{storage-client.js → storage-client/storage-client.js} +124 -213
  44. package/lib/types.ts +9 -3
  45. package/lib/utils.js +20 -20
  46. package/package.json +1 -1
  47. package/build/lib/storage-client.d.ts.map +0 -1
  48. package/build/lib/storage-client.js.map +0 -1
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseGoogleapiStorageXml = exports.parseNotes = exports.findChildNode = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const xpath_1 = __importDefault(require("xpath"));
9
+ const support_1 = require("@appium/support");
10
+ const utils_1 = require("../utils");
11
+ const bluebird_1 = __importDefault(require("bluebird"));
12
+ const constants_1 = require("../constants");
13
+ const xmldom_1 = require("@xmldom/xmldom");
14
+ const path_1 = __importDefault(require("path"));
15
+ const log = support_1.logger.getLogger('ChromedriverGoogleapisStorageClient');
16
+ const MAX_PARALLEL_DOWNLOADS = 5;
17
+ /**
18
+ *
19
+ * @param {Node|Attr} parent
20
+ * @param {string?} childName
21
+ * @param {string?} text
22
+ * @returns
23
+ */
24
+ function findChildNode(parent, childName = null, text = null) {
25
+ if (!childName && !text) {
26
+ return null;
27
+ }
28
+ if (!parent.hasChildNodes()) {
29
+ return null;
30
+ }
31
+ for (let childNodeIdx = 0; childNodeIdx < parent.childNodes.length; childNodeIdx++) {
32
+ const childNode = /** @type {Element|Attr} */ (parent.childNodes[childNodeIdx]);
33
+ if (childName && !text && childName === childNode.localName) {
34
+ return childNode;
35
+ }
36
+ if (text) {
37
+ const childText = extractNodeText(childNode);
38
+ if (!childText) {
39
+ continue;
40
+ }
41
+ if (childName && childName === childNode.localName && text === childText) {
42
+ return childNode;
43
+ }
44
+ if (!childName && text === childText) {
45
+ return childNode;
46
+ }
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+ exports.findChildNode = findChildNode;
52
+ /**
53
+ *
54
+ * @param {Node?} node
55
+ * @returns
56
+ */
57
+ function extractNodeText(node) {
58
+ return !node || !node.firstChild || !support_1.util.hasValue(node.firstChild.nodeValue)
59
+ ? null
60
+ : node.firstChild.nodeValue;
61
+ }
62
+ /**
63
+ * Gets additional chromedriver details from chromedriver
64
+ * release notes
65
+ *
66
+ * @param {string} content - Release notes of the corresponding chromedriver
67
+ * @returns {import('../types').AdditionalDriverDetails}
68
+ */
69
+ function parseNotes(content) {
70
+ const result = {};
71
+ const versionMatch = /^\s*[-]+ChromeDriver[\D]+([\d.]+)/im.exec(content);
72
+ if (versionMatch) {
73
+ result.version = versionMatch[1];
74
+ }
75
+ const minBrowserVersionMatch = /^\s*Supports Chrome[\D]+(\d+)/im.exec(content);
76
+ if (minBrowserVersionMatch) {
77
+ result.minBrowserVersion = minBrowserVersionMatch[1];
78
+ }
79
+ return result;
80
+ }
81
+ exports.parseNotes = parseNotes;
82
+ /**
83
+ * Parses chromedriver storage XML and returns
84
+ * the parsed results
85
+ *
86
+ * @param {string} xml - The chromedriver storage XML
87
+ * @param {boolean} shouldParseNotes [true] - If set to `true`
88
+ * then additional drivers information is going to be parsed
89
+ * and assigned to `this.mapping`
90
+ * @returns {Promise<ChromedriverDetailsMapping>}
91
+ */
92
+ async function parseGoogleapiStorageXml(xml, shouldParseNotes = true) {
93
+ const doc = new xmldom_1.DOMParser().parseFromString(xml);
94
+ const driverNodes = /** @type {Array<Node|Attr>} */ (xpath_1.default.select(`//*[local-name(.)='Contents']`, doc));
95
+ log.debug(`Parsed ${driverNodes.length} entries from storage XML`);
96
+ if (lodash_1.default.isEmpty(driverNodes)) {
97
+ throw new Error('Cannot retrieve any valid Chromedriver entries from the storage config');
98
+ }
99
+ const promises = [];
100
+ const chunk = [];
101
+ /** @type {ChromedriverDetailsMapping} */
102
+ const mapping = {};
103
+ for (const driverNode of driverNodes) {
104
+ const k = extractNodeText(findChildNode(driverNode, 'Key'));
105
+ if (!lodash_1.default.includes(k, '/chromedriver_')) {
106
+ continue;
107
+ }
108
+ const key = String(k);
109
+ const etag = extractNodeText(findChildNode(driverNode, 'ETag'));
110
+ if (!etag) {
111
+ log.debug(`The entry '${key}' does not contain the checksum. Skipping it`);
112
+ continue;
113
+ }
114
+ const filename = path_1.default.basename(key);
115
+ const osNameMatch = /_([a-z]+)/i.exec(filename);
116
+ if (!osNameMatch) {
117
+ log.debug(`The entry '${key}' does not contain valid OS name. Skipping it`);
118
+ continue;
119
+ }
120
+ /** @type {ChromedriverDetails} */
121
+ const cdInfo = {
122
+ url: `${constants_1.GOOGLEAPIS_CDN}/${key}`,
123
+ etag: lodash_1.default.trim(etag, '"'),
124
+ version: /** @type {string} */ (lodash_1.default.first(key.split('/'))),
125
+ minBrowserVersion: null,
126
+ os: {
127
+ name: osNameMatch[1],
128
+ arch: filename.includes(constants_1.ARCH.X64) ? constants_1.ARCH.X64 : constants_1.ARCH.X86,
129
+ cpu: constants_1.APPLE_ARM_SUFFIXES.some((suffix) => filename.includes(suffix)) ? constants_1.CPU.ARM : constants_1.CPU.INTEL,
130
+ }
131
+ };
132
+ mapping[key] = cdInfo;
133
+ const notesPath = `${cdInfo.version}/notes.txt`;
134
+ const isNotesPresent = !!driverNodes.reduce((acc, node) => Boolean(acc || findChildNode(node, 'Key', notesPath)), false);
135
+ if (!isNotesPresent) {
136
+ cdInfo.minBrowserVersion = null;
137
+ if (shouldParseNotes) {
138
+ log.info(`The entry '${key}' does not contain any notes. Skipping it`);
139
+ }
140
+ continue;
141
+ }
142
+ else if (!shouldParseNotes) {
143
+ continue;
144
+ }
145
+ const promise = bluebird_1.default.resolve(retrieveAdditionalDriverInfo(key, `${constants_1.GOOGLEAPIS_CDN}/${notesPath}`, cdInfo));
146
+ promises.push(promise);
147
+ chunk.push(promise);
148
+ if (chunk.length >= MAX_PARALLEL_DOWNLOADS) {
149
+ await bluebird_1.default.any(chunk);
150
+ }
151
+ lodash_1.default.remove(chunk, (p) => p.isFulfilled());
152
+ }
153
+ await bluebird_1.default.all(promises);
154
+ log.info(`The total count of entries in the mapping: ${lodash_1.default.size(mapping)}`);
155
+ return mapping;
156
+ }
157
+ exports.parseGoogleapiStorageXml = parseGoogleapiStorageXml;
158
+ /**
159
+ * Downloads chromedriver release notes and puts them
160
+ * into the dictionary argument
161
+ *
162
+ * The method call mutates by merging `AdditionalDriverDetails`
163
+ * @param {string} driverKey - Driver version plus archive name
164
+ * @param {string} notesUrl - The URL of chromedriver notes
165
+ * @param {ChromedriverDetails} infoDict - The dictionary containing driver info.
166
+ * @param {number} timeout
167
+ * @throws {Error} if the release notes cannot be downloaded
168
+ */
169
+ async function retrieveAdditionalDriverInfo(driverKey, notesUrl, infoDict, timeout = constants_1.STORAGE_REQ_TIMEOUT_MS) {
170
+ const notes = await (0, utils_1.retrieveData)(notesUrl, {
171
+ 'user-agent': 'appium',
172
+ accept: '*/*',
173
+ }, { timeout });
174
+ const { minBrowserVersion } = parseNotes(notes);
175
+ if (!minBrowserVersion) {
176
+ log.debug(`The driver '${driverKey}' does not contain valid release notes at ${notesUrl}. ` +
177
+ `Skipping it`);
178
+ return;
179
+ }
180
+ infoDict.minBrowserVersion = minBrowserVersion;
181
+ }
182
+ /**
183
+ * @typedef {import('../types').SyncOptions} SyncOptions
184
+ * @typedef {import('../types').OSInfo} OSInfo
185
+ * @typedef {import('../types').ChromedriverDetails} ChromedriverDetails
186
+ * @typedef {import('../types').ChromedriverDetailsMapping} ChromedriverDetailsMapping
187
+ */
188
+ //# sourceMappingURL=googleapis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"googleapis.js","sourceRoot":"","sources":["../../../lib/storage-client/googleapis.js"],"names":[],"mappings":";;;;;;AAAA,oDAAuB;AACvB,kDAA0B;AAC1B,6CAA6C;AAC7C,oCAAsC;AACtC,wDAAyB;AACzB,4CAMsB;AACtB,2CAAyC;AACzC,gDAAwB;AAGxB,MAAM,GAAG,GAAG,gBAAM,CAAC,SAAS,CAAC,qCAAqC,CAAC,CAAC;AACpE,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAEjC;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,EAAE,IAAI,GAAG,IAAI;IACjE,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,EAAE;QACvB,OAAO,IAAI,CAAC;KACb;IACD,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE;QAC3B,OAAO,IAAI,CAAC;KACb;IAED,KAAK,IAAI,YAAY,GAAG,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE;QAClF,MAAM,SAAS,GAAG,2BAA2B,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;QAChF,IAAI,SAAS,IAAI,CAAC,IAAI,IAAI,SAAS,KAAK,SAAS,CAAC,SAAS,EAAE;YAC3D,OAAO,SAAS,CAAC;SAClB;QACD,IAAI,IAAI,EAAE;YACR,MAAM,SAAS,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;YAC7C,IAAI,CAAC,SAAS,EAAE;gBACd,SAAS;aACV;YACD,IAAI,SAAS,IAAI,SAAS,KAAK,SAAS,CAAC,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE;gBACxE,OAAO,SAAS,CAAC;aAClB;YACD,IAAI,CAAC,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE;gBACpC,OAAO,SAAS,CAAC;aAClB;SACF;KACF;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AA3BD,sCA2BC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,IAAI;IAC3B,OAAO,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,cAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAC3E,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;AAChC,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,UAAU,CAAC,OAAO;IAChC,MAAM,MAAM,GAAG,EAAE,CAAC;IAClB,MAAM,YAAY,GAAG,qCAAqC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzE,IAAI,YAAY,EAAE;QAChB,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;KAClC;IACD,MAAM,sBAAsB,GAAG,iCAAiC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/E,IAAI,sBAAsB,EAAE;QAC1B,MAAM,CAAC,iBAAiB,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;KACtD;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAXD,gCAWC;AAED;;;;;;;;;GASG;AACI,KAAK,UAAU,wBAAwB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IACzE,MAAM,GAAG,GAAG,IAAI,kBAAS,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,+BAA+B,CAAC,CAClD,eAAK,CAAC,MAAM,CAAC,+BAA+B,EAAE,GAAG,CAAC,CACnD,CAAC;IACF,GAAG,CAAC,KAAK,CAAC,UAAU,WAAW,CAAC,MAAM,2BAA2B,CAAC,CAAC;IACnE,IAAI,gBAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;QAC1B,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;KAC3F;IAED,MAAM,QAAQ,GAAG,EAAE,CAAC;IACpB,MAAM,KAAK,GAAG,EAAE,CAAC;IACjB,yCAAyC;IACzC,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;QACpC,MAAM,CAAC,GAAG,eAAe,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,gBAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,EAAE;YACpC,SAAS;SACV;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAEtB,MAAM,IAAI,GAAG,eAAe,CAAC,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,EAAE;YACT,GAAG,CAAC,KAAK,CAAC,cAAc,GAAG,8CAA8C,CAAC,CAAC;YAC3E,SAAS;SACV;QAED,MAAM,QAAQ,GAAG,cAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,CAAC,WAAW,EAAE;YAChB,GAAG,CAAC,KAAK,CAAC,cAAc,GAAG,+CAA+C,CAAC,CAAC;YAC5E,SAAS;SACV;QAED,kCAAkC;QAClC,MAAM,MAAM,GAAG;YACb,GAAG,EAAE,GAAG,0BAAc,IAAI,GAAG,EAAE;YAC/B,IAAI,EAAE,gBAAC,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;YACvB,OAAO,EAAE,qBAAqB,CAAC,CAAC,gBAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YACxD,iBAAiB,EAAE,IAAI;YACvB,EAAE,EAAE;gBACF,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;gBACpB,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,gBAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAI,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAI,CAAC,GAAG;gBACvD,GAAG,EAAE,8BAAkB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,eAAG,CAAC,GAAG,CAAC,CAAC,CAAC,eAAG,CAAC,KAAK;aAC1F;SACF,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QAEtB,MAAM,SAAS,GAAG,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC;QAChD,MAAM,cAAc,GAAG,CAAC,CAAC,WAAW,CAAC,MAAM,CACzC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,EACpE,KAAK,CACN,CAAC;QACF,IAAI,CAAC,cAAc,EAAE;YACnB,MAAM,CAAC,iBAAiB,GAAG,IAAI,CAAC;YAChC,IAAI,gBAAgB,EAAE;gBACpB,GAAG,CAAC,IAAI,CAAC,cAAc,GAAG,2CAA2C,CAAC,CAAC;aACxE;YACD,SAAS;SACV;aAAM,IAAI,CAAC,gBAAgB,EAAE;YAC5B,SAAS;SACV;QAED,MAAM,OAAO,GAAG,kBAAC,CAAC,OAAO,CAAC,4BAA4B,CAAC,GAAG,EAAE,GAAG,0BAAc,IAAI,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QACvG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,KAAK,CAAC,MAAM,IAAI,sBAAsB,EAAE;YAC1C,MAAM,kBAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;SACpB;QACD,gBAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;KACzC;IACD,MAAM,kBAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtB,GAAG,CAAC,IAAI,CAAC,8CAA8C,gBAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1E,OAAO,OAAO,CAAC;AACjB,CAAC;AA1ED,4DA0EC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,4BAA4B,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,GAAG,kCAAsB;IACzG,MAAM,KAAK,GAAG,MAAM,IAAA,oBAAY,EAC9B,QAAQ,EACR;QACE,YAAY,EAAE,QAAQ;QACtB,MAAM,EAAE,KAAK;KACd,EACD,EAAC,OAAO,EAAC,CACV,CAAC;IACF,MAAM,EAAC,iBAAiB,EAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,CAAC,iBAAiB,EAAE;QACtB,GAAG,CAAC,KAAK,CACP,eAAe,SAAS,6CAA6C,QAAQ,IAAI;YAC/E,aAAa,CAChB,CAAC;QACF,OAAO;KACR;IACD,QAAQ,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;AACjD,CAAC;AAED;;;;;GAKG"}
@@ -1,43 +1,13 @@
1
1
  export class ChromedriverStorageClient {
2
2
  /**
3
3
  *
4
- * @param {import('./types').ChromedriverStorageClientOpts} args
4
+ * @param {import('../types').ChromedriverStorageClientOpts} args
5
5
  */
6
- constructor(args?: import('./types').ChromedriverStorageClientOpts);
6
+ constructor(args?: import('../types').ChromedriverStorageClientOpts);
7
7
  chromedriverDir: string;
8
8
  timeout: number;
9
9
  /** @type {ChromedriverDetailsMapping} */
10
10
  mapping: ChromedriverDetailsMapping;
11
- /**
12
- * Gets additional chromedriver details from chromedriver
13
- * release notes
14
- *
15
- * @param {string} content - Release notes of the corresponding chromedriver
16
- * @returns {import('./types').AdditionalDriverDetails}
17
- */
18
- parseNotes(content: string): import('./types').AdditionalDriverDetails;
19
- /**
20
- * Downloads chromedriver release notes and puts them
21
- * into the dictionary argument
22
- *
23
- * The method call mutates by merging `AdditionalDriverDetails`
24
- * @param {string} driverKey - Driver version plus archive name
25
- * @param {string} notesUrl - The URL of chromedriver notes
26
- * @param {ChromedriverDetails} infoDict - The dictionary containing driver info.
27
- * @throws {Error} if the release notes cannot be downloaded
28
- */
29
- retrieveAdditionalDriverInfo(driverKey: string, notesUrl: string, infoDict: ChromedriverDetails): Promise<void>;
30
- /**
31
- * Parses chromedriver storage XML and stores
32
- * the parsed results into `this.mapping`
33
- *
34
- * @param {Document} doc - The DOM representation
35
- * of the chromedriver storage XML
36
- * @param {boolean} shouldParseNotes [true] - If set to `true`
37
- * then additional drivers information is going to be parsed
38
- * and assigned to `this.mapping`
39
- */
40
- parseStorageXml(doc: Document, shouldParseNotes?: boolean): Promise<void>;
41
11
  /**
42
12
  * Retrieves chromedriver mapping from the storage
43
13
  *
@@ -66,6 +36,14 @@ export class ChromedriverStorageClient {
66
36
  * entry names (version/archive name)
67
37
  */
68
38
  selectMatchingDrivers(osInfo: OSInfo, opts?: SyncOptions): Array<string>;
39
+ /**
40
+ * Checks whether the given chromedriver matches the operating system to run on
41
+ *
42
+ * @param {string} cdName
43
+ * @param {OSInfo} osInfo
44
+ * @returns {boolean}
45
+ */
46
+ doesMatchForOsInfo(cdName: string, { name, arch, cpu }: OSInfo): boolean;
69
47
  /**
70
48
  * Retrieves the given chromedriver from the storage
71
49
  * and unpacks it into `this.chromedriverDir` folder
@@ -94,8 +72,8 @@ export class ChromedriverStorageClient {
94
72
  syncDrivers(opts?: SyncOptions): Promise<string[]>;
95
73
  }
96
74
  export default ChromedriverStorageClient;
97
- export type SyncOptions = import('./types').SyncOptions;
98
- export type OSInfo = import('./types').OSInfo;
99
- export type ChromedriverDetails = import('./types').ChromedriverDetails;
100
- export type ChromedriverDetailsMapping = import('./types').ChromedriverDetailsMapping;
75
+ export type SyncOptions = import('../types').SyncOptions;
76
+ export type OSInfo = import('../types').OSInfo;
77
+ export type ChromedriverDetails = import('../types').ChromedriverDetails;
78
+ export type ChromedriverDetailsMapping = import('../types').ChromedriverDetailsMapping;
101
79
  //# sourceMappingURL=storage-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-client.d.ts","sourceRoot":"","sources":["../../../lib/storage-client/storage-client.js"],"names":[],"mappings":"AAwCA;IACE;;;OAGG;IACH,mBAFW,OAAO,UAAU,EAAE,6BAA6B,EAQ1D;IAJC,wBAAsC;IACtC,gBAAsB;IACtB,yCAAyC;IACzC,SADW,0BAA0B,CACpB;IAGnB;;;;;;;OAOG;IACH,mCALW,OAAO,GAGL,QAAQ,0BAA0B,CAAC,CAiB/C;IAED;;;;;;OAMG;IACH,iBAHW,MAAM,OACN,MAAM,iBAwBhB;IAED;;;;;;;;;OASG;IACH,8BALW,MAAM,SACN,WAAW,GACT,aAAa,CAyHzB;IAED;;;;;;OAMG;IACH,2BAJW,MAAM,uBACN,MAAM,GACJ,OAAO,CAgBnB;IAED;;;;;;;;;;;;;;OAcG;IACH,sBAXW,MAAM,aACN,MAAM,gBACN,MAAM,aAEN,OAAO,GAIL,QAAQ,OAAO,CAAC,CA4C5B;IAED;;;;;;;;OAQG;IACH,mBALW,WAAW,GAGT,QAAQ,MAAM,EAAE,CAAC,CAwD7B;CACF;;0BAKY,OAAO,UAAU,EAAE,WAAW;qBAC9B,OAAO,UAAU,EAAE,MAAM;kCACzB,OAAO,UAAU,EAAE,mBAAmB;yCACtC,OAAO,UAAU,EAAE,0BAA0B"}
@@ -4,15 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ChromedriverStorageClient = void 0;
7
- const utils_1 = require("./utils");
7
+ const utils_1 = require("../utils");
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
- const xpath_1 = __importDefault(require("xpath"));
10
- const xmldom_1 = require("@xmldom/xmldom");
11
9
  const bluebird_1 = __importDefault(require("bluebird"));
12
10
  const path_1 = __importDefault(require("path"));
13
- const os_1 = __importDefault(require("os"));
14
11
  const support_1 = require("@appium/support");
15
- const TIMEOUT_MS = 15000;
12
+ const constants_1 = require("../constants");
13
+ const googleapis_1 = require("./googleapis");
14
+ const chromelabs_1 = require("./chromelabs");
15
+ const compare_versions_1 = require("compare-versions");
16
+ const semver_1 = __importDefault(require("semver"));
16
17
  const MAX_PARALLEL_DOWNLOADS = 5;
17
18
  const log = support_1.logger.getLogger('ChromedriverStorageClient');
18
19
  /**
@@ -25,160 +26,18 @@ async function isCrcOk(src, checksum) {
25
26
  const md5 = await support_1.fs.hash(src, 'md5');
26
27
  return lodash_1.default.toLower(md5) === lodash_1.default.toLower(checksum);
27
28
  }
28
- /**
29
- *
30
- * @param {Node|Attr} parent
31
- * @param {string?} childName
32
- * @param {string?} text
33
- * @returns
34
- */
35
- function findChildNode(parent, childName = null, text = null) {
36
- if (!childName && !text) {
37
- return null;
38
- }
39
- if (!parent.hasChildNodes()) {
40
- return null;
41
- }
42
- for (let childNodeIdx = 0; childNodeIdx < parent.childNodes.length; childNodeIdx++) {
43
- const childNode = /** @type {Element|Attr} */ (parent.childNodes[childNodeIdx]);
44
- if (childName && !text && childName === childNode.localName) {
45
- return childNode;
46
- }
47
- if (text) {
48
- const childText = extractNodeText(childNode);
49
- if (!childText) {
50
- continue;
51
- }
52
- if (childName && childName === childNode.localName && text === childText) {
53
- return childNode;
54
- }
55
- if (!childName && text === childText) {
56
- return childNode;
57
- }
58
- }
59
- }
60
- return null;
61
- }
62
- /**
63
- *
64
- * @param {Node?} node
65
- * @returns
66
- */
67
- function extractNodeText(node) {
68
- return !node || !node.firstChild || !support_1.util.hasValue(node.firstChild.nodeValue)
69
- ? null
70
- : node.firstChild.nodeValue;
71
- }
72
29
  class ChromedriverStorageClient {
73
30
  /**
74
31
  *
75
- * @param {import('./types').ChromedriverStorageClientOpts} args
32
+ * @param {import('../types').ChromedriverStorageClientOpts} args
76
33
  */
77
34
  constructor(args = {}) {
78
- const { chromedriverDir = (0, utils_1.getChromedriverDir)(), timeout = TIMEOUT_MS } = args;
35
+ const { chromedriverDir = (0, utils_1.getChromedriverDir)(), timeout = constants_1.STORAGE_REQ_TIMEOUT_MS } = args;
79
36
  this.chromedriverDir = chromedriverDir;
80
37
  this.timeout = timeout;
81
38
  /** @type {ChromedriverDetailsMapping} */
82
39
  this.mapping = {};
83
40
  }
84
- /**
85
- * Gets additional chromedriver details from chromedriver
86
- * release notes
87
- *
88
- * @param {string} content - Release notes of the corresponding chromedriver
89
- * @returns {import('./types').AdditionalDriverDetails}
90
- */
91
- parseNotes(content) {
92
- const result = {};
93
- const versionMatch = /^\s*[-]+ChromeDriver[\D]+([\d.]+)/im.exec(content);
94
- if (versionMatch) {
95
- result.version = versionMatch[1];
96
- }
97
- const minBrowserVersionMatch = /^\s*Supports Chrome[\D]+(\d+)/im.exec(content);
98
- if (minBrowserVersionMatch) {
99
- result.minBrowserVersion = minBrowserVersionMatch[1];
100
- }
101
- return result;
102
- }
103
- /**
104
- * Downloads chromedriver release notes and puts them
105
- * into the dictionary argument
106
- *
107
- * The method call mutates by merging `AdditionalDriverDetails`
108
- * @param {string} driverKey - Driver version plus archive name
109
- * @param {string} notesUrl - The URL of chromedriver notes
110
- * @param {ChromedriverDetails} infoDict - The dictionary containing driver info.
111
- * @throws {Error} if the release notes cannot be downloaded
112
- */
113
- async retrieveAdditionalDriverInfo(driverKey, notesUrl, infoDict) {
114
- const notes = await (0, utils_1.retrieveData)(notesUrl, {
115
- 'user-agent': 'appium',
116
- accept: '*/*',
117
- }, { timeout: this.timeout });
118
- const { minBrowserVersion } = this.parseNotes(notes);
119
- if (!minBrowserVersion) {
120
- log.debug(`The driver '${driverKey}' does not contain valid release notes at ${notesUrl}. ` +
121
- `Skipping it`);
122
- return;
123
- }
124
- infoDict.minBrowserVersion = minBrowserVersion;
125
- }
126
- /**
127
- * Parses chromedriver storage XML and stores
128
- * the parsed results into `this.mapping`
129
- *
130
- * @param {Document} doc - The DOM representation
131
- * of the chromedriver storage XML
132
- * @param {boolean} shouldParseNotes [true] - If set to `true`
133
- * then additional drivers information is going to be parsed
134
- * and assigned to `this.mapping`
135
- */
136
- async parseStorageXml(doc, shouldParseNotes = true) {
137
- const driverNodes = /** @type {Array<Node|Attr>} */ (xpath_1.default.select(`//*[local-name(.)='Contents']`, doc));
138
- log.debug(`Parsed ${driverNodes.length} entries from storage XML`);
139
- if (lodash_1.default.isEmpty(driverNodes)) {
140
- return;
141
- }
142
- const promises = [];
143
- for (const driverNode of driverNodes) {
144
- const k = extractNodeText(findChildNode(driverNode, 'Key'));
145
- if (!lodash_1.default.includes(k, '/chromedriver_')) {
146
- continue;
147
- }
148
- const key = String(k);
149
- const etag = extractNodeText(findChildNode(driverNode, 'ETag'));
150
- if (!etag) {
151
- log.debug(`The entry '${key}' does not contain the checksum. Skipping it`);
152
- continue;
153
- }
154
- /** @type {ChromedriverDetails} */
155
- const cdInfo = {
156
- url: `${utils_1.CD_CDN}/${key}`,
157
- etag: lodash_1.default.trim(etag, '"'),
158
- version: /** @type {string} */ (lodash_1.default.first(key.split('/'))),
159
- minBrowserVersion: null,
160
- };
161
- this.mapping[key] = cdInfo;
162
- const notesPath = `${cdInfo.version}/notes.txt`;
163
- const isNotesPresent = !!driverNodes.reduce((acc, node) => Boolean(acc || findChildNode(node, 'Key', notesPath)), false);
164
- if (!isNotesPresent) {
165
- cdInfo.minBrowserVersion = null;
166
- if (shouldParseNotes) {
167
- log.info(`The entry '${key}' does not contain any notes. Skipping it`);
168
- }
169
- continue;
170
- }
171
- else if (!shouldParseNotes) {
172
- continue;
173
- }
174
- promises.push(this.retrieveAdditionalDriverInfo(key, `${utils_1.CD_CDN}/${notesPath}`, cdInfo));
175
- if (promises.length % MAX_PARALLEL_DOWNLOADS === 0) {
176
- await bluebird_1.default.all(promises);
177
- }
178
- }
179
- await bluebird_1.default.all(promises);
180
- log.info(`The total count of entries in the mapping: ${lodash_1.default.size(this.mapping)}`);
181
- }
182
41
  /**
183
42
  * Retrieves chromedriver mapping from the storage
184
43
  *
@@ -188,13 +47,19 @@ class ChromedriverStorageClient {
188
47
  * @returns {Promise<ChromedriverDetailsMapping>}
189
48
  */
190
49
  async retrieveMapping(shouldParseNotes = true) {
191
- const xml = await (0, utils_1.retrieveData)(utils_1.CD_CDN, {
192
- 'user-agent': 'appium',
193
- accept: 'application/xml, */*',
194
- }, { timeout: this.timeout });
195
- const doc = new xmldom_1.DOMParser().parseFromString(xml);
196
- await this.parseStorageXml(doc, shouldParseNotes);
197
- return lodash_1.default.cloneDeep(this.mapping);
50
+ const [xmlStr, jsonStr] = await bluebird_1.default.all([
51
+ [constants_1.GOOGLEAPIS_CDN, 'application/xml'],
52
+ [`${constants_1.CHROMELABS_URL}/chrome-for-testing/known-good-versions-with-downloads.json`, 'application/json'],
53
+ ]
54
+ .map(([url, contentType]) => url
55
+ ? (0, utils_1.retrieveData)(url, {
56
+ 'user-agent': constants_1.USER_AGENT,
57
+ accept: `${contentType}, */*`,
58
+ }, { timeout: this.timeout })
59
+ : bluebird_1.default.resolve()));
60
+ this.mapping = xmlStr ? await (0, googleapis_1.parseGoogleapiStorageXml)(xmlStr, shouldParseNotes) : {};
61
+ Object.assign(this.mapping, (0, chromelabs_1.parseKnownGoodVersionsWithDownloadsJson)(jsonStr));
62
+ return this.mapping;
198
63
  }
199
64
  /**
200
65
  * Extracts downloaded chromedriver archive
@@ -267,28 +132,83 @@ class ChromedriverStorageClient {
267
132
  }
268
133
  if (!lodash_1.default.isEmpty(osInfo)) {
269
134
  // Filter out drivers for unsupported system architectures
270
- let { name, arch } = osInfo;
271
- if (arch === utils_1.X64 && !driversToSync.some((cdName) => cdName.includes(`_${name}${utils_1.X64}`))) {
272
- // Fall back to x86 build if x64 one is not available for the given OS
273
- arch = utils_1.X86;
135
+ const { name, arch, cpu = (0, utils_1.getCpuType)() } = osInfo;
136
+ log.debug(`Selecting chromedrivers whose platform matches to ${name}:${cpu}${arch}`);
137
+ let result = driversToSync.filter((cdName) => this.doesMatchForOsInfo(cdName, osInfo));
138
+ if (lodash_1.default.isEmpty(result) && arch === constants_1.ARCH.X64 && cpu === constants_1.CPU.INTEL) {
139
+ // Fallback to X86 if X64 architecture is not available for this driver
140
+ result = driversToSync.filter((cdName) => this.doesMatchForOsInfo(cdName, {
141
+ name, arch: constants_1.ARCH.X86, cpu
142
+ }));
274
143
  }
275
- // https://stackoverflow.com/questions/65146751/detecting-apple-silicon-mac-in-javascript
276
- if (name === utils_1.OS.mac && lodash_1.default.includes(lodash_1.default.toLower(os_1.default.cpus()[0].model), 'apple')) {
277
- for (const armSuffix of utils_1.APPLE_ARM_SUFFIXES) {
278
- if (driversToSync.some((cdName) => cdName.includes(armSuffix))) {
279
- // prefer executable for ARM arch if present
280
- arch = armSuffix;
281
- break;
282
- }
283
- }
144
+ if (lodash_1.default.isEmpty(result) && name === constants_1.OS.MAC && cpu === constants_1.CPU.ARM) {
145
+ // Fallback to Intel/Rosetta if ARM architecture is not available for this driver
146
+ result = driversToSync.filter((cdName) => this.doesMatchForOsInfo(cdName, {
147
+ name, arch, cpu: constants_1.CPU.INTEL
148
+ }));
284
149
  }
285
- log.debug(`Selecting chromedrivers whose platform matches to ${name}${arch}`);
286
- const platformRe = new RegExp(`(\\b|_)${name}${arch}\\b`);
287
- driversToSync = driversToSync.filter((cdName) => platformRe.test(cdName));
150
+ driversToSync = result;
288
151
  log.debug(`Got ${support_1.util.pluralize('item', driversToSync.length, true)}`);
289
152
  }
153
+ if (!lodash_1.default.isEmpty(driversToSync)) {
154
+ log.debug('Excluding older patches if present');
155
+ /** @type {{[key: string]: string[]}} */
156
+ const patchesMap = {};
157
+ // Older chromedrivers must not be excluded as they follow a different
158
+ // versioning pattern
159
+ const versionWithPatchPattern = /\d+\.\d+\.\d+\.\d+/;
160
+ const selectedVersions = new Set();
161
+ for (const cdName of driversToSync) {
162
+ const cdVersion = this.mapping[cdName].version;
163
+ if (!versionWithPatchPattern.test(cdVersion)) {
164
+ selectedVersions.add(cdVersion);
165
+ continue;
166
+ }
167
+ const verObj = semver_1.default.parse(cdVersion, { loose: true });
168
+ if (!verObj) {
169
+ continue;
170
+ }
171
+ if (!lodash_1.default.isArray(patchesMap[verObj.major])) {
172
+ patchesMap[verObj.major] = [];
173
+ }
174
+ patchesMap[verObj.major].push(cdVersion);
175
+ }
176
+ for (const majorVersion of lodash_1.default.keys(patchesMap)) {
177
+ if (patchesMap[majorVersion].length <= 1) {
178
+ continue;
179
+ }
180
+ patchesMap[majorVersion].sort((/** @type {string} */ a, /** @type {string}} */ b) => (0, compare_versions_1.compareVersions)(b, a));
181
+ }
182
+ if (!lodash_1.default.isEmpty(patchesMap)) {
183
+ log.debug('Versions mapping: ' + JSON.stringify(patchesMap, null, 2));
184
+ for (const sortedVersions of lodash_1.default.values(patchesMap)) {
185
+ selectedVersions.add(sortedVersions[0]);
186
+ }
187
+ driversToSync = driversToSync.filter((cdName) => selectedVersions.has(this.mapping[cdName].version));
188
+ }
189
+ }
290
190
  return driversToSync;
291
191
  }
192
+ /**
193
+ * Checks whether the given chromedriver matches the operating system to run on
194
+ *
195
+ * @param {string} cdName
196
+ * @param {OSInfo} osInfo
197
+ * @returns {boolean}
198
+ */
199
+ doesMatchForOsInfo(cdName, { name, arch, cpu }) {
200
+ const cdInfo = this.mapping[cdName];
201
+ if (!cdInfo) {
202
+ return false;
203
+ }
204
+ if (cdInfo.os.name !== name || cdInfo.os.arch !== arch) {
205
+ return false;
206
+ }
207
+ if (cpu && cdInfo.os.cpu && this.mapping[cdName].os.cpu !== cpu) {
208
+ return false;
209
+ }
210
+ return true;
211
+ }
292
212
  /**
293
213
  * Retrieves the given chromedriver from the storage
294
214
  * and unpacks it into `this.chromedriverDir` folder
@@ -311,7 +231,7 @@ class ChromedriverStorageClient {
311
231
  try {
312
232
  await support_1.net.downloadFile(url, archivePath, {
313
233
  isMetered: false,
314
- timeout: TIMEOUT_MS,
234
+ timeout: constants_1.STORAGE_REQ_TIMEOUT_MS,
315
235
  });
316
236
  }
317
237
  catch (e) {
@@ -323,7 +243,7 @@ class ChromedriverStorageClient {
323
243
  log.error(msg);
324
244
  return false;
325
245
  }
326
- if (!(await isCrcOk(archivePath, etag))) {
246
+ if (etag && !(await isCrcOk(archivePath, etag))) {
327
247
  const msg = `The checksum for the downloaded chromedriver '${driverKey}' did not match`;
328
248
  if (isStrict) {
329
249
  throw new Error(msg);
@@ -376,17 +296,21 @@ class ChromedriverStorageClient {
376
296
  */
377
297
  const synchronizedDrivers = [];
378
298
  const promises = [];
299
+ const chunk = [];
379
300
  const archivesRoot = await support_1.tempDir.openDir();
380
301
  try {
381
302
  for (const [idx, driverKey] of driversToSync.entries()) {
382
- promises.push((async () => {
303
+ const promise = bluebird_1.default.resolve((async () => {
383
304
  if (await this.retrieveDriver(idx, driverKey, archivesRoot, !lodash_1.default.isEmpty(opts))) {
384
305
  synchronizedDrivers.push(driverKey);
385
306
  }
386
307
  })());
387
- if (promises.length % MAX_PARALLEL_DOWNLOADS === 0) {
388
- await bluebird_1.default.all(promises);
308
+ promises.push(promise);
309
+ chunk.push(promise);
310
+ if (chunk.length >= MAX_PARALLEL_DOWNLOADS) {
311
+ await bluebird_1.default.any(chunk);
389
312
  }
313
+ lodash_1.default.remove(chunk, (p) => p.isFulfilled());
390
314
  }
391
315
  await bluebird_1.default.all(promises);
392
316
  }
@@ -406,9 +330,9 @@ class ChromedriverStorageClient {
406
330
  exports.ChromedriverStorageClient = ChromedriverStorageClient;
407
331
  exports.default = ChromedriverStorageClient;
408
332
  /**
409
- * @typedef {import('./types').SyncOptions} SyncOptions
410
- * @typedef {import('./types').OSInfo} OSInfo
411
- * @typedef {import('./types').ChromedriverDetails} ChromedriverDetails
412
- * @typedef {import('./types').ChromedriverDetailsMapping} ChromedriverDetailsMapping
333
+ * @typedef {import('../types').SyncOptions} SyncOptions
334
+ * @typedef {import('../types').OSInfo} OSInfo
335
+ * @typedef {import('../types').ChromedriverDetails} ChromedriverDetails
336
+ * @typedef {import('../types').ChromedriverDetailsMapping} ChromedriverDetailsMapping
413
337
  */
414
338
  //# sourceMappingURL=storage-client.js.map