detox
Version:
E2E tests and automation for mobile
240 lines (195 loc) • 7.52 kB
JavaScript
// @ts-nocheck
const _ = require('lodash');
const environmentFactory = require('../environmentFactory');
const log = require('../utils/logger').child({ cat: 'config' });
/**
* @param {DetoxConfigErrorComposer} opts.errorComposer
* @param {Detox.DetoxConfig} opts.globalConfig
* @param {Detox.DetoxConfiguration} opts.localConfig
* @param {*} opts.cliConfig
* @returns {Detox.DetoxDeviceConfig}
*/
function composeDeviceConfig(opts) {
const deviceConfig = composeDeviceConfigFromAliased(opts);
applyCLIOverrides(deviceConfig, opts.cliConfig);
deviceConfig.device = unpackDeviceQuery(deviceConfig);
return deviceConfig;
}
/**
* @param {DetoxConfigErrorComposer} opts.errorComposer
* @param {Detox.DetoxConfig} opts.globalConfig
* @param {Detox.DetoxConfiguration} opts.localConfig
* @returns {Detox.DetoxDeviceConfig}
*/
function composeDeviceConfigFromAliased(opts) {
const { errorComposer, globalConfig, localConfig } = opts;
/** @type {Detox.DetoxDeviceConfig} */
let deviceConfig;
const isAliased = typeof localConfig.device === 'string';
if (isAliased) {
if (_.isEmpty(globalConfig.devices)) {
throw errorComposer.thereAreNoDeviceConfigs(localConfig.device);
} else {
deviceConfig = globalConfig.devices[localConfig.device];
}
if (!deviceConfig) {
throw errorComposer.cantResolveDeviceAlias(localConfig.device);
}
} else {
if (!localConfig.device) {
throw errorComposer.deviceConfigIsUndefined();
}
deviceConfig = localConfig.device;
}
validateDeviceConfig({
deviceConfig,
errorComposer,
deviceAlias: isAliased ? localConfig.device : undefined
});
return { ...deviceConfig };
}
/**
* @param {DetoxConfigErrorComposer} errorComposer
* @param {Detox.DetoxDeviceConfig} deviceConfig
* @param {String | undefined} deviceAlias
*/
function validateDeviceConfig({ deviceConfig, errorComposer, deviceAlias }) {
if (!deviceConfig.type) {
throw errorComposer.missingDeviceType(deviceAlias);
}
const maybeError = _.attempt(() => environmentFactory.validateConfig(deviceConfig));
if (_.isError(maybeError)) {
throw errorComposer.invalidDeviceType(deviceAlias, deviceConfig, maybeError);
}
if (!KNOWN_TYPES.has(deviceConfig.type)) {
return;
}
if (deviceConfig.bootArgs != null) {
if (!_.isString(deviceConfig.bootArgs)) {
throw errorComposer.malformedDeviceProperty(deviceAlias, 'bootArgs');
}
if (deviceConfig.type !== 'ios.simulator' && deviceConfig.type !== 'android.emulator') {
throw errorComposer.unsupportedDeviceProperty(deviceAlias, 'bootArgs');
}
}
if (deviceConfig.utilBinaryPaths != null) {
if (!Array.isArray(deviceConfig.utilBinaryPaths)) {
throw errorComposer.malformedDeviceProperty(deviceAlias, 'utilBinaryPaths');
}
if (deviceConfig.utilBinaryPaths.some(s => !_.isString(s))) {
throw errorComposer.malformedDeviceProperty(deviceAlias, 'utilBinaryPaths');
}
if (!deviceConfig.type.match(/^android\.(attached|emulator|genycloud)$/)) {
throw errorComposer.unsupportedDeviceProperty(deviceAlias, 'utilBinaryPaths');
}
}
if (deviceConfig.forceAdbInstall !== undefined) {
if (!_.isBoolean(deviceConfig.forceAdbInstall)) {
throw errorComposer.malformedDeviceProperty(deviceAlias, 'forceAdbInstall');
}
if (!deviceConfig.type.match(/^android\.(attached|emulator|genycloud)$/)) {
throw errorComposer.unsupportedDeviceProperty(deviceAlias, 'forceAdbInstall');
}
}
if (deviceConfig.gpuMode !== undefined) {
if (!_.isString(deviceConfig.gpuMode)) {
throw errorComposer.malformedDeviceProperty(deviceAlias, 'gpuMode');
}
if (!deviceConfig.gpuMode.match(/^(auto|host|swiftshader_indirect|angle_indirect|guest|off)$/)) {
throw errorComposer.malformedDeviceProperty(deviceAlias, 'gpuMode');
}
if (deviceConfig.type !== 'android.emulator') {
throw errorComposer.unsupportedDeviceProperty(deviceAlias, 'gpuMode');
}
}
if (deviceConfig.headless !== undefined) {
if (!_.isBoolean(deviceConfig.headless)) {
throw errorComposer.malformedDeviceProperty(deviceAlias, 'headless');
}
if (deviceConfig.type !== 'ios.simulator' && deviceConfig.type !== 'android.emulator') {
throw errorComposer.unsupportedDeviceProperty(deviceAlias, 'headless');
}
}
if (deviceConfig.readonly !== undefined) {
if (!_.isBoolean(deviceConfig.readonly)) {
throw errorComposer.malformedDeviceProperty(deviceAlias, 'readonly');
}
if (deviceConfig.type !== 'android.emulator') {
throw errorComposer.unsupportedDeviceProperty(deviceAlias, 'readonly');
}
}
if (_.isObject(deviceConfig.device)) {
const expectedProperties = EXPECTED_DEVICE_MATCHER_PROPS[deviceConfig.type];
/* istanbul ignore else */
if (!_.isEmpty(expectedProperties)) {
const minimalShape = _.pick(deviceConfig.device, expectedProperties);
if (_.isEmpty(minimalShape)) {
throw errorComposer.missingDeviceMatcherProperties(deviceAlias, expectedProperties);
}
}
}
}
function applyCLIOverrides(deviceConfig, cliConfig) {
_assignCLIConfigIfSupported('device-name', cliConfig.deviceName, deviceConfig, 'device');
_assignCLIConfigIfSupported('device-boot-args', cliConfig.deviceBootArgs, deviceConfig, 'bootArgs');
_assignCLIConfigIfSupported('headless', cliConfig.headless, deviceConfig, 'headless');
_assignCLIConfigIfSupported('force-adb-install', cliConfig.forceAdbInstall, deviceConfig, 'forceAdbInstall');
_assignCLIConfigIfSupported('gpu', cliConfig.gpu, deviceConfig, 'gpuMode');
_assignCLIConfigIfSupported('readonly-emu', cliConfig.readonlyEmu, deviceConfig, 'readonly');
}
function _assignCLIConfigIfSupported(argName, argValue, deviceConfig, propertyName) {
if (argValue === undefined) {
return;
}
const deviceType = deviceConfig.type;
const supportedDeviceTypesPrefixes = _supportedDeviceTypesPrefixes(argName);
if (!supportedDeviceTypesPrefixes.some((prefix) => deviceType.startsWith(prefix))) {
log.warn(`--${argName} CLI override is not supported by device type = "${deviceType}" and will be ignored`);
return;
}
deviceConfig[propertyName] = argValue;
}
function _supportedDeviceTypesPrefixes(argName) {
switch (argName) {
case 'device-name':
return [''];
case 'force-adb-install':
return ['android.'];
case 'gpu':
case 'readonly-emu':
return ['android.emulator'];
case 'device-boot-args':
case 'headless':
return ['ios.simulator', 'android.emulator'];
}
}
function unpackDeviceQuery(deviceConfig) {
const query = deviceConfig.device;
if (!_.isString(query)) {
return query;
}
switch (deviceConfig.type) {
case 'ios.simulator':
if (_.includes(query, ',')) {
const [type, os] = _.split(query, /\s*,\s*/);
return { type, os };
}
return { type: query };
case 'android.attached':
return { adbName: query };
case 'android.emulator':
return { avdName: query };
case 'android.genycloud':
return { recipeName: query };
default:
return query;
}
}
const EXPECTED_DEVICE_MATCHER_PROPS = {
'ios.simulator': ['type', 'name', 'id'],
'android.attached': ['adbName'],
'android.emulator': ['avdName'],
'android.genycloud': ['recipeUUID', 'recipeName'],
};
const KNOWN_TYPES = new Set(Object.keys(EXPECTED_DEVICE_MATCHER_PROPS));
module.exports = composeDeviceConfig;