Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): skipLegacyWorkersInjection #2872

Merged
merged 5 commits into from
Jul 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions detox/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ declare global {
* @example testRunner: 'mocha'
*/
testRunner?: string;
/**
* Stops passing default `--maxWorkers 1` to the test runner,
* presuming that from now on you have that already configured
* in your test runner config as a default.
*/
skipLegacyWorkersInjection?: boolean;
/**
* @example runnerConfig: 'e2e/config.js'
*/
Expand Down
1 change: 1 addition & 0 deletions detox/local-cli/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ function createJestFolderE2E() {
createFile('.detoxrc.json', JSON.stringify({
testRunner: 'jest',
runnerConfig: 'e2e/config.json',
skipLegacyWorkersInjection: true,
...createDefaultConfigurations(),
}, null, 2));
}
Expand Down
1 change: 1 addition & 0 deletions detox/local-cli/templates/jest.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const firstTestContent = require('./firstTestContent');

const runnerConfig = `{
"maxWorkers": 1,
"testEnvironment": "./environment",
"testRunner": "jest-circus/runner",
"testTimeout": 120000,
Expand Down
37 changes: 20 additions & 17 deletions detox/local-cli/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const { loadLastFailedTests, resetLastFailedTests } = require('../src/utils/last
const log = require('../src/utils/logger').child({ __filename });
const { parse, quote } = require('../src/utils/shellQuote');

const { readJestConfig } = require('./utils/jestInternals');
const { getPlatformSpecificString, printEnvironmentVariables } = require('./utils/misc');
const { prependNodeModulesBinToPATH } = require('./utils/misc');
const splitArgv = require('./utils/splitArgv');
Expand All @@ -33,7 +34,7 @@ module.exports.handler = async function test(argv) {
detoxArgs,
});

const forwardedArgs = prepareArgs({
const forwardedArgs = await prepareArgs({
cliConfig,
runnerConfig,
runnerArgs,
Expand Down Expand Up @@ -75,7 +76,7 @@ module.exports.middlewares = [

function choosePrepareArgs({ cliConfig, detoxArgs, runner }) {
if (runner === 'mocha') {
if (hasMultipleWorkers(cliConfig)) {
if (cliConfig.workers) {
log.warn('Cannot use -w, --workers. Parallel test execution is only supported with Jest');
}

Expand Down Expand Up @@ -155,19 +156,25 @@ function prepareMochaArgs({ cliConfig, runnerArgs, runnerConfig, platform }) {
};
}

function prepareJestArgs({ cliConfig, runnerArgs, runnerConfig, platform }) {
async function prepareJestArgs({ cliConfig, runnerArgs, runnerConfig, platform }) {
const { specs, passthrough } = splitArgv.jest(runnerArgs);
const platformFilter = getPlatformSpecificString(platform);

return {
argv: {
color: !cliConfig.noColor && undefined,
config: runnerConfig.runnerConfig /* istanbul ignore next */ || undefined,
testNamePattern: platformFilter ? `^((?!${platformFilter}).)*$` : undefined,
maxWorkers: cliConfig.workers,
const argv = _.omitBy({
color: !cliConfig.noColor && undefined,
config: runnerConfig.runnerConfig /* istanbul ignore next */ || undefined,
testNamePattern: platformFilter ? `^((?!${platformFilter}).)*$` : undefined,
maxWorkers: cliConfig.workers || (runnerConfig.skipLegacyWorkersInjection ? undefined : 1),

...passthrough,
},
...passthrough,
}, _.isUndefined);

const hasMultipleWorkers = runnerConfig.skipLegacyWorkersInjection
? (await readJestConfig(argv)).globalConfig.maxWorkers > 1
: cliConfig.workers > 1;

return {
argv,

env: _.omitBy({
DETOX_APP_LAUNCH_ARGS: cliConfig.appLaunchArgs,
Expand All @@ -183,13 +190,13 @@ function prepareJestArgs({ cliConfig, runnerArgs, runnerConfig, platform }) {
DETOX_GPU: cliConfig.gpu,
DETOX_HEADLESS: cliConfig.headless,
DETOX_LOGLEVEL: cliConfig.loglevel,
DETOX_READ_ONLY_EMU: platform === 'android' ? hasMultipleWorkers(cliConfig) : undefined,
DETOX_READ_ONLY_EMU: platform === 'android' ? hasMultipleWorkers : undefined,
DETOX_RECORD_LOGS: cliConfig.recordLogs,
DETOX_RECORD_PERFORMANCE: cliConfig.recordPerformance,
DETOX_RECORD_TIMELINE: cliConfig.recordTimeline,
DETOX_RECORD_VIDEOS: cliConfig.recordVideos,
DETOX_REPORT_SPECS: _.isUndefined(cliConfig.jestReportSpecs)
? !hasMultipleWorkers(cliConfig)
? !hasMultipleWorkers
: `${cliConfig.jestReportSpecs}` === 'true',
DETOX_REUSE: cliConfig.reuse,
DETOX_START_TIMESTAMP: Date.now(),
Expand Down Expand Up @@ -233,10 +240,6 @@ function launchTestRunner({ argv, env, specs }) {
});
}

function hasMultipleWorkers(cliConfig) {
return cliConfig.workers != 1;
}

async function runTestRunnerWithRetries(forwardedArgs, { keepLockFile, platform, retries }) {
let runsLeft = 1 + retries;
let launchError;
Expand Down
101 changes: 66 additions & 35 deletions detox/local-cli/test.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,23 @@ describe('CLI', () => {
});
});

describe('given skipLegacyWorkersInjection: true', () => {
describe.each([
['ios.simulator'],
['android.emulator'],
])('for %s', (configurationType) => {
it('should omit --maxWorkers CLI arg', async () => {
singleConfig().type = configurationType;
detoxConfig.skipLegacyWorkersInjection = true;
detoxConfig.runnerConfig = 'package.json';

await run();

expect(cliCall().command).not.toMatch(/--maxWorkers/);
});
});
});

test.each([['-c'], ['--configuration']])(
'%s <configuration> should provide inverted --testNamePattern that configuration (jest)',
async (__configuration) => {
Expand Down Expand Up @@ -447,49 +464,63 @@ describe('CLI', () => {
expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_CAPTURE_VIEW_HIERARCHY: 'enabled' }));
});

test.each([['-w'], ['--workers']])('%s <value> should be passed as CLI argument', async (__workers) => {
await run(`${__workers} 2`);
expect(cliCall().command).toContain('--maxWorkers 2');
});
describe.each([
[false],
[true],
])('when skipLegacyWorkersInjection is %j', (skipLegacyWorkersInjection) => {
beforeEach(() => {
Object.assign(detoxConfig, {
skipLegacyWorkersInjection,
runnerConfig: tempfile('.json', JSON.stringify({
maxWorkers: 1,
})),
});
});

test.each([['-w'], ['--workers']])('%s <value> should be replaced with --maxWorkers <value>', async (__workers) => {
await run(`${__workers} 2 --maxWorkers 3`);
test.each([['-w'], ['--workers']])('%s <value> should be passed as CLI argument', async (__workers) => {
await run(`${__workers} 2`);
expect(cliCall().command).toContain('--maxWorkers 2');
});

const { command } = cliCall();
expect(command).toContain('--maxWorkers 3');
expect(command).not.toContain('--maxWorkers 2');
});
test.each([['-w'], ['--workers']])('%s <value> should be replaced with --maxWorkers <value>', async (__workers) => {
await run(`${__workers} 2 --maxWorkers 3`);

test.each([['-w'], ['--workers']])('%s <value> can be overriden by a later value', async (__workers) => {
await run(`${__workers} 2 ${__workers} 3`);
const { command } = cliCall();
expect(command).toContain('--maxWorkers 3');
expect(command).not.toContain('--maxWorkers 2');
});

const { command } = cliCall();
expect(command).toContain('--maxWorkers 3');
expect(command).not.toContain('--maxWorkers 2');
});
test.each([['-w'], ['--workers']])('%s <value> can be overriden by a later value', async (__workers) => {
await run(`${__workers} 2 ${__workers} 3`);

test.each([['-w'], ['--workers']])('%s <value> should not warn anything for iOS', async (__workers) => {
singleConfig().type = 'ios.simulator';
await run(`${__workers} 2`);
expect(logger.warn).not.toHaveBeenCalled();
});
const { command } = cliCall();
expect(command).toContain('--maxWorkers 3');
expect(command).not.toContain('--maxWorkers 2');
});

test.each([['-w'], ['--workers']])('%s <value> should not put readOnlyEmu environment variable for iOS', async (__workers) => {
singleConfig().type = 'ios.simulator';
await run(`${__workers} 2`);
expect(cliCall().env).not.toHaveProperty('DETOX_READ_ONLY_EMU');
});
test.each([['-w'], ['--workers']])('%s <value> should not warn anything for iOS', async (__workers) => {
singleConfig().type = 'ios.simulator';
await run(`${__workers} 2`);
expect(logger.warn).not.toHaveBeenCalled();
});

test.each([['-w'], ['--workers']])('%s <value> should put readOnlyEmu environment variable for Android if there is a single worker', async (__workers) => {
singleConfig().type = 'android.emulator';
await run(`${__workers} 1`);
expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_READ_ONLY_EMU: false }));
});
test.each([['-w'], ['--workers']])('%s <value> should not put readOnlyEmu environment variable for iOS', async (__workers) => {
singleConfig().type = 'ios.simulator';
await run(`${__workers} 2`);
expect(cliCall().env).not.toHaveProperty('DETOX_READ_ONLY_EMU');
});

test.each([['-w'], ['--workers']])('%s <value> should put readOnlyEmu environment variable for Android if there are multiple workers', async (__workers) => {
singleConfig().type = 'android.emulator';
await run(`${__workers} 2`);
expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_READ_ONLY_EMU: true }));
test.each([['-w'], ['--workers']])('%s <value> should put readOnlyEmu environment variable for Android if there is a single worker', async (__workers) => {
singleConfig().type = 'android.emulator';
await run(`${__workers} 1`);
expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_READ_ONLY_EMU: false }));
});

test.each([['-w'], ['--workers']])('%s <value> should put readOnlyEmu environment variable for Android if there are multiple workers', async (__workers) => {
singleConfig().type = 'android.emulator';
await run(`${__workers} 2`);
expect(cliCall().env).toEqual(expect.objectContaining({ DETOX_READ_ONLY_EMU: true }));
});
});

test('should omit --testNamePattern for custom platforms', async () => {
Expand Down
70 changes: 70 additions & 0 deletions detox/local-cli/utils/jestInternals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const Module = require('module');
const path = require('path');

const resolveFrom = require('resolve-from');

const DetoxRuntimeError = require('../../src/errors/DetoxRuntimeError');

const getNodeModulePaths = (dir) => Module._nodeModulePaths(dir);

function getJestLocation() {
const cwd = process.cwd();

if (!resolveFrom.silent(cwd, 'jest')) {
throw new DetoxRuntimeError({
message: 'Could not resolve "jest" package from the current working directory.\n\n' +
'This means that Detox could not find it in any of the following locations:\n' +
getNodeModulePaths(cwd).map(p => `* ${p}`).join('\n'),
hint: `Try installing "jest": npm install jest --save-dev`,
});
}

return path.dirname(resolveFrom(cwd, 'jest/package.json'));
}

function resolveJestDependency(jestLocation, dependencyName) {
const result = resolveFrom.silent(jestLocation, dependencyName);
if (!result) {
throw new DetoxRuntimeError({
message: `Could not resolve "${dependencyName}" package from the "jest" npm package directory.\n\n` +
'This means that Detox could not find it in any of the following locations:\n' +
getNodeModulePaths(jestLocation).map(p => `* ${p}`).join('\n'),
hint: 'Consider reporting this as an issue at: https://github.com/wix/Detox/issues'
});
}

return result;
}

function requireJestDependency(jestLocation, dependencyName) {
return require(resolveJestDependency(jestLocation, dependencyName));
}

function resolveJestCliArgs() {
const jestLocation = getJestLocation();
resolveJestDependency(jestLocation, 'jest-cli');

try {
const jestCliManifest = resolveJestDependency(jestLocation, 'jest-cli/package.json');
const argsJsFile = path.join(path.dirname(jestCliManifest), 'build/cli/args.js');

return require(argsJsFile);
} catch (e) {
throw new DetoxRuntimeError({
message: 'Could not parse CLI arguments supported by "jest-cli" package, see the error below.',
hint: 'Consider reporting this as an issue at: https://github.com/wix/Detox/issues',
debugInfo: e,
});
}
}

async function readJestConfig(argv) {
const jestLocation = getJestLocation();
const { readConfig } = requireJestDependency(jestLocation, 'jest-config');
return readConfig(argv, process.cwd(), false);
}

module.exports = {
resolveJestCliArgs,
readJestConfig,
};
45 changes: 1 addition & 44 deletions detox/local-cli/utils/splitArgv.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
const Module = require('module');
const path = require('path');

const _ = require('lodash');
const resolveFrom = require('resolve-from');

const DetoxRuntimeError = require('../../src/errors/DetoxRuntimeError');

const { resolveJestCliArgs } = require('./jestInternals');
const testCommandArgs = require('./testCommandArgs');

function extractKnownKeys(yargsBuilder) {
Expand Down Expand Up @@ -72,44 +67,6 @@ function getMochaBooleanArgs() {
.value();
}

function resolveJestCliArgs() {
const cwd = process.cwd();
const getNodeModulePaths = (dir) => Module._nodeModulePaths(dir);

if (!resolveFrom.silent(cwd, 'jest')) {
throw new DetoxRuntimeError({
message: 'Could not resolve "jest" package from the current working directory.\n\n' +
'This means that Detox could not find it in any of the following locations:\n' +
getNodeModulePaths(cwd).map(p => `* ${p}`).join('\n'),
hint: `Try installing "jest": npm install jest --save-dev`,
});
}

const jestLocation = path.dirname(resolveFrom(cwd, 'jest/package.json'));

if (!resolveFrom.silent(jestLocation, 'jest-cli')) {
throw new DetoxRuntimeError({
message: 'Could not resolve "jest-cli" package from the "jest" npm package directory.\n\n' +
'This means that Detox could not find it in any of the following locations:\n' +
getNodeModulePaths(jestLocation).map(p => `* ${p}`).join('\n'),
hint: 'Consider reporting this as an issue at: https://github.com/wix/Detox/issues'
});
}

try {
const jestCliManifest = resolveFrom(jestLocation, 'jest-cli/package.json');
const argsJsFile = path.join(path.dirname(jestCliManifest), 'build/cli/args.js');

return require(argsJsFile);
} catch (e) {
throw new DetoxRuntimeError({
message: 'Could not parse CLI arguments supported by "jest-cli" package, see the error below.',
hint: 'Consider reporting this as an issue at: https://github.com/wix/Detox/issues',
debugInfo: e,
});
}
}

function splitDetoxArgv(argv) {
const aliases = extractKnownKeys(testCommandArgs);
const isDetoxArg = (_value, key) => aliases.has(key);
Expand Down
1 change: 0 additions & 1 deletion detox/local-cli/utils/testCommandArgs.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ module.exports = {
group: 'Execution:',
describe: 'Specifies the number of workers the test runner should spawn. Requires a test runner with parallel execution support (e.g. Jest)',
number: true,
default: 1,
},
'jest-report-specs': {
group: 'Execution:',
Expand Down
2 changes: 1 addition & 1 deletion detox/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "detox",
"description": "E2E tests and automation for mobile",
"version": "18.18.2-smoke.0",
"version": "18.19.0-smoke.0",
"bin": {
"detox": "local-cli/cli.js"
},
Expand Down
1 change: 1 addition & 0 deletions detox/src/configuration/composeRunnerConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function composeRunnerConfig({ globalConfig, cliConfig }) {
testRunner,
runnerConfig: customRunnerConfig || defaultRunnerConfig,
specs: globalConfig.specs || 'e2e',
skipLegacyWorkersInjection: Boolean(globalConfig.skipLegacyWorkersInjection),
};
}

Expand Down
Loading