appium-chromedriver
Version:
Node.js wrapper around chromedriver.
173 lines • 6.81 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findChildNode = findChildNode;
exports.parseNotes = parseNotes;
exports.parseGoogleapiStorageXml = parseGoogleapiStorageXml;
const xpath_1 = require("xpath");
const support_1 = require("@appium/support");
const utils_1 = require("../utils");
const asyncbox_1 = require("asyncbox");
const constants_1 = require("../constants");
const xmldom_1 = require("@xmldom/xmldom");
const node_path_1 = __importDefault(require("node:path"));
const log = support_1.logger.getLogger('ChromedriverGoogleapisStorageClient');
const MAX_PARALLEL_DOWNLOADS = 5;
/**
* Finds a child node in an XML node by name and/or text content
*
* @param parent - The parent XML node to search in
* @param childName - Optional child node name to match
* @param text - Optional text content to match
* @returns The matching child node or null if not found
*/
function findChildNode(parent, childName = null, text = null) {
if (!childName && !text) {
return null;
}
if (!parent.hasChildNodes()) {
return null;
}
for (let childNodeIdx = 0; childNodeIdx < parent.childNodes.length; childNodeIdx++) {
const childNode = parent.childNodes[childNodeIdx];
if (childName && !text && childName === childNode.localName) {
return childNode;
}
if (text) {
const childText = extractNodeText(childNode);
if (!childText) {
continue;
}
if (childName && childName === childNode.localName && text === childText) {
return childNode;
}
if (!childName && text === childText) {
return childNode;
}
}
}
return null;
}
/**
* Gets additional chromedriver details from chromedriver
* release notes
*
* @param content - Release notes of the corresponding chromedriver
* @returns AdditionalDriverDetails
*/
function parseNotes(content) {
const result = {};
const versionMatch = /^\s*[-]+ChromeDriver[\D]+([\d.]+)/im.exec(content);
if (versionMatch) {
result.version = versionMatch[1];
}
const minBrowserVersionMatch = /^\s*Supports Chrome[\D]+(\d+)/im.exec(content);
if (minBrowserVersionMatch) {
result.minBrowserVersion = minBrowserVersionMatch[1];
}
return result;
}
/**
* Parses chromedriver storage XML and returns
* the parsed results
*
* @param xml - The chromedriver storage XML
* @param shouldParseNotes [true] - If set to `true`
* then additional drivers information is going to be parsed
* and assigned to `this.mapping`
* @returns Promise<ChromedriverDetailsMapping>
*/
async function parseGoogleapiStorageXml(xml, shouldParseNotes = true) {
const doc = new xmldom_1.DOMParser().parseFromString(xml, 'text/xml');
const driverNodes = (0, xpath_1.select)(`//*[local-name(.)='Contents']`, doc);
log.debug(`Parsed ${driverNodes.length} entries from storage XML`);
if (driverNodes.length === 0) {
throw new Error('Cannot retrieve any valid Chromedriver entries from the storage config');
}
const infoParsers = [];
const mapping = {};
for (const driverNode of driverNodes) {
const k = extractNodeText(findChildNode(driverNode, 'Key'));
if (!String(k).includes('/chromedriver_')) {
continue;
}
const key = String(k);
const versionSegment = key.split('/')[0];
if (!versionSegment) {
continue;
}
const etag = extractNodeText(findChildNode(driverNode, 'ETag'));
if (!etag) {
log.debug(`The entry '${key}' does not contain the checksum. Skipping it`);
continue;
}
const filename = node_path_1.default.basename(key);
const osNameMatch = /_([a-z]+)/i.exec(filename);
if (!osNameMatch) {
log.debug(`The entry '${key}' does not contain valid OS name. Skipping it`);
continue;
}
const cdInfo = {
url: `${constants_1.GOOGLEAPIS_CDN}/${key}`,
etag: etag.replace(/^"+|"+$/g, ''),
version: versionSegment,
minBrowserVersion: null,
os: {
name: osNameMatch[1],
arch: filename.includes(constants_1.ARCH.X64) ? constants_1.ARCH.X64 : constants_1.ARCH.X86,
cpu: constants_1.APPLE_ARM_SUFFIXES.some((suffix) => filename.includes(suffix)) ? constants_1.CPU.ARM : constants_1.CPU.INTEL,
},
};
mapping[key] = cdInfo;
const notesPath = `${cdInfo.version}/notes.txt`;
const isNotesPresent = !!driverNodes.reduce((acc, node) => Boolean(acc || findChildNode(node, 'Key', notesPath)), false);
if (!isNotesPresent) {
cdInfo.minBrowserVersion = null;
if (shouldParseNotes) {
log.info(`The entry '${key}' does not contain any notes. Skipping it`);
}
continue;
}
else if (!shouldParseNotes) {
continue;
}
infoParsers.push(async () => {
await retrieveAdditionalDriverInfo(key, `${constants_1.GOOGLEAPIS_CDN}/${notesPath}`, cdInfo);
});
}
await (0, asyncbox_1.asyncmap)(infoParsers, async (parseInfo) => {
await parseInfo();
}, { concurrency: MAX_PARALLEL_DOWNLOADS });
log.info(`The total count of entries in the mapping: ${Object.keys(mapping).length}`);
return mapping;
}
/**
* Downloads chromedriver release notes and updates the driver info dictionary
*
* Mutates `infoDict` by setting `minBrowserVersion` if found in notes
* @param driverKey - Driver version plus archive name
* @param notesUrl - The URL of chromedriver notes
* @param infoDict - The dictionary containing driver info (will be mutated)
* @param timeout - Request timeout in milliseconds
*/
async function retrieveAdditionalDriverInfo(driverKey, notesUrl, infoDict, timeout = constants_1.STORAGE_REQ_TIMEOUT_MS) {
const notes = await (0, utils_1.retrieveData)(notesUrl, {
'user-agent': 'appium',
accept: '*/*',
}, { timeout });
const { minBrowserVersion } = parseNotes(notes);
if (!minBrowserVersion) {
log.debug(`The driver '${driverKey}' does not contain valid release notes at ${notesUrl}. ` +
`Skipping it`);
return;
}
infoDict.minBrowserVersion = minBrowserVersion;
}
function extractNodeText(node) {
return !node?.firstChild || !support_1.util.hasValue(node.firstChild.nodeValue)
? null
: node.firstChild.nodeValue;
}
//# sourceMappingURL=googleapis.js.map