appium-chromedriver 8.0.30 → 8.1.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.
@@ -3,24 +3,27 @@ import path from 'path';
3
3
  import {logger} from '@appium/support';
4
4
  import * as semver from 'semver';
5
5
  import {ARCH, CPU} from '../constants';
6
+ import type {ChromedriverDetailsMapping} from '../types';
6
7
 
7
8
  const log = logger.getLogger('ChromedriverChromelabsStorageClient');
8
9
 
9
10
  /**
10
- * Parses The output of the corresponding JSON API
11
- * that retrieves Chromedriver versions. See
12
- * https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints
13
- * for more details.
11
+ * Parses the output of the JSON API that retrieves Chromedriver versions
14
12
  *
15
- * @param {string} jsonStr
16
- * @returns {ChromedriverDetailsMapping}
13
+ * See https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints for more details.
14
+ *
15
+ * @param jsonStr - The JSON string from the known-good-versions-with-downloads API
16
+ * @returns A mapping of chromedriver entry keys to their details
17
+ * @throws {Error} if the JSON cannot be parsed or has an unsupported format
17
18
  */
18
- export function parseKnownGoodVersionsWithDownloadsJson(jsonStr) {
19
- let json;
19
+ export function parseKnownGoodVersionsWithDownloadsJson(
20
+ jsonStr: string
21
+ ): ChromedriverDetailsMapping {
22
+ let json: KnownGoodVersionsJson;
20
23
  try {
21
24
  json = JSON.parse(jsonStr);
22
25
  } catch (e) {
23
- const err = /** @type {Error} */ (e);
26
+ const err = e as Error;
24
27
  throw new Error(`Storage JSON cannot be parsed. Original error: ${err.message}`);
25
28
  }
26
29
  /**
@@ -60,8 +63,7 @@ export function parseKnownGoodVersionsWithDownloadsJson(jsonStr) {
60
63
  * "version":"113.0.5672.35",
61
64
  * ...
62
65
  */
63
- /** @type {ChromedriverDetailsMapping} */
64
- const mapping = {};
66
+ const mapping: ChromedriverDetailsMapping = {};
65
67
  if (!_.isArray(json?.versions)) {
66
68
  log.debug(jsonStr);
67
69
  throw new Error('The format of the storage JSON is not supported');
@@ -80,10 +82,13 @@ export function parseKnownGoodVersionsWithDownloadsJson(jsonStr) {
80
82
  }
81
83
  const osNameMatch = /^[a-z]+/i.exec(downloadEntry.platform);
82
84
  if (!osNameMatch) {
83
- log.debug(`The entry '${downloadEntry.url}' does not contain valid platform name. Skipping it`);
85
+ log.debug(
86
+ `The entry '${downloadEntry.url}' does not contain valid platform name. Skipping it`
87
+ );
84
88
  continue;
85
89
  }
86
- const key = `${path.basename(path.dirname(path.dirname(downloadEntry.url)))}/` +
90
+ const key =
91
+ `${path.basename(path.dirname(path.dirname(downloadEntry.url)))}/` +
87
92
  `${path.basename(downloadEntry.url)}`;
88
93
  mapping[key] = {
89
94
  url: downloadEntry.url,
@@ -94,7 +99,7 @@ export function parseKnownGoodVersionsWithDownloadsJson(jsonStr) {
94
99
  name: osNameMatch[0],
95
100
  arch: downloadEntry.platform.includes(ARCH.X64) ? ARCH.X64 : ARCH.X86,
96
101
  cpu: downloadEntry.platform.includes(CPU.ARM) ? CPU.ARM : CPU.INTEL,
97
- }
102
+ },
98
103
  };
99
104
  }
100
105
  }
@@ -103,20 +108,20 @@ export function parseKnownGoodVersionsWithDownloadsJson(jsonStr) {
103
108
  }
104
109
 
105
110
  /**
106
- * Parses The output of the corresponding JSON API
107
- * that retrieves the most recent stable Chromedriver version. See
108
- * https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints
109
- * for more details.
111
+ * Parses the output of the JSON API that retrieves the most recent stable Chromedriver version
112
+ *
113
+ * See https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints for more details.
110
114
  *
111
- * @param {string} jsonStr
112
- * @returns {string} The most recent available chromedriver version
115
+ * @param jsonStr - The JSON string from the last-known-good-versions API
116
+ * @returns The most recent available chromedriver version string
117
+ * @throws {Error} if the JSON cannot be parsed or has an unsupported format
113
118
  */
114
- export function parseLatestKnownGoodVersionsJson(jsonStr) {
115
- let json;
119
+ export function parseLatestKnownGoodVersionsJson(jsonStr: string): string {
120
+ let json: LatestKnownGoodVersionsJson;
116
121
  try {
117
122
  json = JSON.parse(jsonStr);
118
123
  } catch (e) {
119
- const err = /** @type {Error} */ (e);
124
+ const err = e as Error;
120
125
  throw new Error(`Storage JSON cannot be parsed. Original error: ${err.message}`);
121
126
  }
122
127
  /**
@@ -136,6 +141,29 @@ export function parseLatestKnownGoodVersionsJson(jsonStr) {
136
141
  return json.channels.Stable.version;
137
142
  }
138
143
 
139
- /**
140
- * @typedef {import('../types').ChromedriverDetailsMapping} ChromedriverDetailsMapping
141
- */
144
+ interface VersionEntry {
145
+ version: string;
146
+ revision?: string;
147
+ downloads?: {
148
+ chromedriver?: Array<{
149
+ platform: string;
150
+ url: string;
151
+ }>;
152
+ };
153
+ }
154
+
155
+ interface KnownGoodVersionsJson {
156
+ timestamp?: string;
157
+ versions?: VersionEntry[];
158
+ }
159
+
160
+ interface LatestKnownGoodVersionsJson {
161
+ timestamp?: string;
162
+ channels?: {
163
+ Stable?: {
164
+ channel?: string;
165
+ version?: string;
166
+ revision?: string;
167
+ };
168
+ };
169
+ }
@@ -12,18 +12,28 @@ import {
12
12
  } from '../constants';
13
13
  import {DOMParser} from '@xmldom/xmldom';
14
14
  import path from 'node:path';
15
+ import type {
16
+ AdditionalDriverDetails,
17
+ ChromedriverDetails,
18
+ ChromedriverDetailsMapping,
19
+ } from '../types';
15
20
 
16
21
  const log = logger.getLogger('ChromedriverGoogleapisStorageClient');
17
22
  const MAX_PARALLEL_DOWNLOADS = 5;
18
23
 
19
24
  /**
25
+ * Finds a child node in an XML node by name and/or text content
20
26
  *
21
- * @param {Node|Attr} parent
22
- * @param {string?} childName
23
- * @param {string?} text
24
- * @returns
27
+ * @param parent - The parent XML node to search in
28
+ * @param childName - Optional child node name to match
29
+ * @param text - Optional text content to match
30
+ * @returns The matching child node or null if not found
25
31
  */
26
- export function findChildNode(parent, childName = null, text = null) {
32
+ export function findChildNode(
33
+ parent: Node | Attr,
34
+ childName: string | null = null,
35
+ text: string | null = null
36
+ ): Node | Attr | null {
27
37
  if (!childName && !text) {
28
38
  return null;
29
39
  }
@@ -32,7 +42,7 @@ export function findChildNode(parent, childName = null, text = null) {
32
42
  }
33
43
 
34
44
  for (let childNodeIdx = 0; childNodeIdx < parent.childNodes.length; childNodeIdx++) {
35
- const childNode = /** @type {Element|Attr} */ (parent.childNodes[childNodeIdx]);
45
+ const childNode = parent.childNodes[childNodeIdx] as Element | Attr;
36
46
  if (childName && !text && childName === childNode.localName) {
37
47
  return childNode;
38
48
  }
@@ -52,26 +62,15 @@ export function findChildNode(parent, childName = null, text = null) {
52
62
  return null;
53
63
  }
54
64
 
55
- /**
56
- *
57
- * @param {Node?} node
58
- * @returns
59
- */
60
- function extractNodeText(node) {
61
- return !node?.firstChild || !util.hasValue(node.firstChild.nodeValue)
62
- ? null
63
- : node.firstChild.nodeValue;
64
- }
65
-
66
65
  /**
67
66
  * Gets additional chromedriver details from chromedriver
68
67
  * release notes
69
68
  *
70
- * @param {string} content - Release notes of the corresponding chromedriver
71
- * @returns {import('../types').AdditionalDriverDetails}
69
+ * @param content - Release notes of the corresponding chromedriver
70
+ * @returns AdditionalDriverDetails
72
71
  */
73
- export function parseNotes(content) {
74
- const result = {};
72
+ export function parseNotes(content: string): AdditionalDriverDetails {
73
+ const result: AdditionalDriverDetails = {};
75
74
  const versionMatch = /^\s*[-]+ChromeDriver[\D]+([\d.]+)/im.exec(content);
76
75
  if (versionMatch) {
77
76
  result.version = versionMatch[1];
@@ -87,27 +86,26 @@ export function parseNotes(content) {
87
86
  * Parses chromedriver storage XML and returns
88
87
  * the parsed results
89
88
  *
90
- * @param {string} xml - The chromedriver storage XML
91
- * @param {boolean} shouldParseNotes [true] - If set to `true`
89
+ * @param xml - The chromedriver storage XML
90
+ * @param shouldParseNotes [true] - If set to `true`
92
91
  * then additional drivers information is going to be parsed
93
92
  * and assigned to `this.mapping`
94
- * @returns {Promise<ChromedriverDetailsMapping>}
93
+ * @returns Promise<ChromedriverDetailsMapping>
95
94
  */
96
- export async function parseGoogleapiStorageXml(xml, shouldParseNotes = true) {
95
+ export async function parseGoogleapiStorageXml(
96
+ xml: string,
97
+ shouldParseNotes = true
98
+ ): Promise<ChromedriverDetailsMapping> {
97
99
  const doc = new DOMParser().parseFromString(xml, 'text/xml');
98
- const driverNodes = /** @type {Array<Node|Attr>} */ (
99
- // https://github.com/xmldom/xmldom/issues/724
100
- xpathSelect(`//*[local-name(.)='Contents']`, doc)
101
- );
100
+ const driverNodes = xpathSelect(`//*[local-name(.)='Contents']`, doc) as Array<Node | Attr>;
102
101
  log.debug(`Parsed ${driverNodes.length} entries from storage XML`);
103
102
  if (_.isEmpty(driverNodes)) {
104
103
  throw new Error('Cannot retrieve any valid Chromedriver entries from the storage config');
105
104
  }
106
105
 
107
- const promises = [];
108
- const chunk = [];
109
- /** @type {ChromedriverDetailsMapping} */
110
- const mapping = {};
106
+ const promises: Promise<void>[] = [];
107
+ const chunk: Promise<void>[] = [];
108
+ const mapping: ChromedriverDetailsMapping = {};
111
109
  for (const driverNode of driverNodes) {
112
110
  const k = extractNodeText(findChildNode(driverNode, 'Key'));
113
111
  if (!_.includes(k, '/chromedriver_')) {
@@ -128,17 +126,16 @@ export async function parseGoogleapiStorageXml(xml, shouldParseNotes = true) {
128
126
  continue;
129
127
  }
130
128
 
131
- /** @type {ChromedriverDetails} */
132
- const cdInfo = {
129
+ const cdInfo: ChromedriverDetails = {
133
130
  url: `${GOOGLEAPIS_CDN}/${key}`,
134
131
  etag: _.trim(etag, '"'),
135
- version: /** @type {string} */ (_.first(key.split('/'))),
132
+ version: _.first(key.split('/')) as string,
136
133
  minBrowserVersion: null,
137
134
  os: {
138
135
  name: osNameMatch[1],
139
136
  arch: filename.includes(ARCH.X64) ? ARCH.X64 : ARCH.X86,
140
137
  cpu: APPLE_ARM_SUFFIXES.some((suffix) => filename.includes(suffix)) ? CPU.ARM : CPU.INTEL,
141
- }
138
+ },
142
139
  };
143
140
  mapping[key] = cdInfo;
144
141
 
@@ -157,13 +154,15 @@ export async function parseGoogleapiStorageXml(xml, shouldParseNotes = true) {
157
154
  continue;
158
155
  }
159
156
 
160
- const promise = B.resolve(retrieveAdditionalDriverInfo(key, `${GOOGLEAPIS_CDN}/${notesPath}`, cdInfo));
157
+ const promise = B.resolve(
158
+ retrieveAdditionalDriverInfo(key, `${GOOGLEAPIS_CDN}/${notesPath}`, cdInfo)
159
+ );
161
160
  promises.push(promise);
162
161
  chunk.push(promise);
163
162
  if (chunk.length >= MAX_PARALLEL_DOWNLOADS) {
164
163
  await B.any(chunk);
165
164
  }
166
- _.remove(chunk, (p) => p.isFulfilled());
165
+ _.remove(chunk, (p) => (p as B<void>).isFulfilled());
167
166
  }
168
167
  await B.all(promises);
169
168
  log.info(`The total count of entries in the mapping: ${_.size(mapping)}`);
@@ -171,17 +170,20 @@ export async function parseGoogleapiStorageXml(xml, shouldParseNotes = true) {
171
170
  }
172
171
 
173
172
  /**
174
- * Downloads chromedriver release notes and puts them
175
- * into the dictionary argument
173
+ * Downloads chromedriver release notes and updates the driver info dictionary
176
174
  *
177
- * The method call mutates by merging `AdditionalDriverDetails`
178
- * @param {string} driverKey - Driver version plus archive name
179
- * @param {string} notesUrl - The URL of chromedriver notes
180
- * @param {ChromedriverDetails} infoDict - The dictionary containing driver info.
181
- * @param {number} timeout
182
- * @throws {Error} if the release notes cannot be downloaded
175
+ * Mutates `infoDict` by setting `minBrowserVersion` if found in notes
176
+ * @param driverKey - Driver version plus archive name
177
+ * @param notesUrl - The URL of chromedriver notes
178
+ * @param infoDict - The dictionary containing driver info (will be mutated)
179
+ * @param timeout - Request timeout in milliseconds
183
180
  */
184
- async function retrieveAdditionalDriverInfo(driverKey, notesUrl, infoDict, timeout = STORAGE_REQ_TIMEOUT_MS) {
181
+ async function retrieveAdditionalDriverInfo(
182
+ driverKey: string,
183
+ notesUrl: string,
184
+ infoDict: ChromedriverDetails,
185
+ timeout = STORAGE_REQ_TIMEOUT_MS
186
+ ): Promise<void> {
185
187
  const notes = await retrieveData(
186
188
  notesUrl,
187
189
  {
@@ -201,9 +203,8 @@ async function retrieveAdditionalDriverInfo(driverKey, notesUrl, infoDict, timeo
201
203
  infoDict.minBrowserVersion = minBrowserVersion;
202
204
  }
203
205
 
204
- /**
205
- * @typedef {import('../types').SyncOptions} SyncOptions
206
- * @typedef {import('../types').OSInfo} OSInfo
207
- * @typedef {import('../types').ChromedriverDetails} ChromedriverDetails
208
- * @typedef {import('../types').ChromedriverDetailsMapping} ChromedriverDetailsMapping
209
- */
206
+ function extractNodeText(node: Node | null | undefined): string | null {
207
+ return !node?.firstChild || !util.hasValue(node.firstChild.nodeValue)
208
+ ? null
209
+ : node.firstChild.nodeValue;
210
+ }