diff --git a/detox/local-cli/detox-test.js b/detox/local-cli/detox-test.js index 11a38ece82..164f31b0b9 100644 --- a/detox/local-cli/detox-test.js +++ b/detox/local-cli/detox-test.js @@ -4,7 +4,7 @@ const program = require('commander'); const path = require('path'); const cp = require('child_process'); program - .option('-r, --runner [runner]', 'Test runner (currently supports mocha)', 'mocha') + .option('-r, --runner [runner]', 'Test runner (currently supports mocha)') .option('-o, --runner-config [config]', 'Test runner config file', 'mocha.opts') .option('-l, --loglevel [value]', 'info, debug, verbose, silly, wss') .option('-c, --configuration [device configuration]', 'Select a device configuration from your defined configurations,' @@ -21,26 +21,54 @@ program const config = require(path.join(process.cwd(), 'package.json')).detox; const testFolder = config.specs || 'e2e'; -const loglevel = program.loglevel ? `--loglevel ${program.loglevel}` : ''; -const configuration = program.configuration ? `--configuration ${program.configuration}` : ''; -const cleanup = program.cleanup ? `--cleanup` : ''; -const reuse = program.reuse ? `--reuse` : ''; -const artifactsLocation = program.artifactsLocation ? `--artifacts-location ${program.artifactsLocation}` : ''; +let runner = config.runner || 'mocha'; + +if (program.runner) { + runner = program.runner; +} + +function runMocha() { + const loglevel = program.loglevel ? `--loglevel ${program.loglevel}` : ''; + const configuration = program.configuration ? `--configuration ${program.configuration}` : ''; + const cleanup = program.cleanup ? `--cleanup` : ''; + const reuse = program.reuse ? `--reuse` : ''; + const artifactsLocation = program.artifactsLocation ? `--artifacts-location ${program.artifactsLocation}` : ''; + + const debugSynchronization = program.debugSynchronization ? `--debug-synchronization ${program.debugSynchronization}` : ''; + const command = `node_modules/.bin/mocha ${testFolder} --opts ${testFolder}/${program.runnerConfig} ${configuration} ${loglevel} ${cleanup} ${reuse} ${debugSynchronization} ${artifactsLocation}`; + + console.log(command); + cp.execSync(command, {stdio: 'inherit'}); +} + +function runJest() { + const command = `node_modules/.bin/jest ${testFolder} --runInBand`; + console.log(command); + cp.execSync(command, { + stdio: 'inherit', + env: { + ...process.env, + configuration: program.configuration, + loglevel: program.loglevel, + cleanup: program.cleanup, + reuse: program.reuse, + debugSynchronization: program.debugSynchronization, + artifactsLocation: program.artifactsLocation + } + }); +} if (typeof program.debugSynchronization === "boolean") { program.debugSynchronization = 3000; } -let debugSynchronization = program.debugSynchronization ? `--debug-synchronization ${program.debugSynchronization}` : ''; - -let command; -switch (program.runner) { +switch (runner) { case 'mocha': - command = `node_modules/.bin/${program.runner} ${testFolder} --opts ${testFolder}/${program.runnerConfig} ${configuration} ${loglevel} ${cleanup} ${reuse} ${debugSynchronization} ${artifactsLocation}`; + runMocha(); + break; + case 'jest': + runJest(); break; default: - throw new Error(`${program.runner} is not supported in detox cli tools. You can still run your tests with the runner's own cli tool`); + throw new Error(`${runner} is not supported in detox cli tools. You can still run your tests with the runner's own cli tool`); } - -console.log(command); -cp.execSync(command, {stdio: 'inherit'}); diff --git a/detox/src/Detox.js b/detox/src/Detox.js index 593de63683..89d383edf6 100644 --- a/detox/src/Detox.js +++ b/detox/src/Detox.js @@ -22,7 +22,6 @@ const DEVICE_CLASSES = { }; class Detox { - constructor(userConfig) { if (!userConfig) { throw new Error(`No configuration was passed to detox, make sure you pass a config when calling 'detox.init(config)'`); @@ -33,10 +32,10 @@ class Detox { this.device = null; this._currentTestNumber = 0; const artifactsLocation = argparse.getArgValue('artifacts-location'); - if(artifactsLocation !== undefined) { + if (artifactsLocation !== undefined) { try { this._artifactsPathsProvider = new ArtifactsPathsProvider(artifactsLocation); - } catch(ex) { + } catch (ex) { log.warn(ex); } } @@ -130,7 +129,7 @@ class Detox { } if (!deviceConfig) { - throw new Error(`Cannot determine which configuration to use. use --configuration to choose one of the following: + throw new Error(`Cannot determine which configuration to use. use --configuration to choose one of the following: ${Object.keys(configurations)}`); } diff --git a/detox/src/Detox.test.js b/detox/src/Detox.test.js index ce8528d3dc..1d35fb3c00 100644 --- a/detox/src/Detox.test.js +++ b/detox/src/Detox.test.js @@ -4,17 +4,16 @@ describe('Detox', () => { let fs; let Detox; let detox; - let minimist; - let clientMockData = {lastConstructorArguments: null}; - let deviceMockData = {lastConstructorArguments: null}; + const clientMockData = {lastConstructorArguments: null}; + const deviceMockData = {lastConstructorArguments: null}; beforeEach(async () => { function setCustomMock(modulePath, dataObject) { const JestMock = jest.genMockFromModule(modulePath); class FinalMock extends JestMock { - constructor() { - super(...arguments); - dataObject.lastConstructorArguments = arguments; + constructor(...rest) { + super(rest); + dataObject.lastConstructorArguments = rest; } } jest.setMock(modulePath, FinalMock); @@ -23,12 +22,12 @@ describe('Detox', () => { jest.mock('npmlog'); jest.mock('fs'); fs = require('fs'); - jest.mock('minimist'); - minimist = require('minimist'); jest.mock('./ios/expect'); setCustomMock('./client/Client', clientMockData); setCustomMock('./devices/Device', deviceMockData); + process.env = {}; + jest.mock('./devices/IosDriver'); jest.mock('./devices/SimulatorDriver'); jest.mock('./devices/Device'); @@ -66,7 +65,7 @@ describe('Detox', () => { }); it(`Passing --cleanup should shutdown the currently running device`, async () => { - mockCommandLineArgs({cleanup: true}); + process.env.cleanup = true; Detox = require('./Detox'); detox = new Detox(schemes.validOneDeviceNoSession); @@ -99,7 +98,7 @@ describe('Detox', () => { }); it(`Two valid devices, detox should init with the device passed in '--configuration' cli option`, async () => { - mockCommandLineArgs({configuration: 'ios.sim.debug'}); + process.env.configuration = 'ios.sim.debug'; Detox = require('./Detox'); detox = new Detox(schemes.validTwoDevicesNoSession); @@ -108,7 +107,7 @@ describe('Detox', () => { }); it(`Two valid devices, detox should throw if device passed in '--configuration' cli option doesn't exist`, async () => { - mockCommandLineArgs({configuration: 'nonexistent'}); + process.env.configuration = 'nonexistent'; Detox = require('./Detox'); detox = new Detox(schemes.validTwoDevicesNoSession); @@ -121,7 +120,7 @@ describe('Detox', () => { }); it(`Two valid devices, detox should throw if device passed in '--configuration' cli option doesn't exist`, async () => { - mockCommandLineArgs({configuration: 'nonexistent'}); + process.env.configuration = 'nonexistent'; Detox = require('./Detox'); detox = new Detox(schemes.validTwoDevicesNoSession); @@ -165,22 +164,22 @@ describe('Detox', () => { }); it(`Detox should use session defined per configuration - none`, async () => { - mockCommandLineArgs({configuration: 'ios.sim.none'}); + process.env.configuration = 'ios.sim.none'; Detox = require('./Detox'); detox = new Detox(schemes.sessionPerConfiguration); await detox.init(); - let expectedSession = schemes.sessionPerConfiguration.configurations['ios.sim.none'].session; + const expectedSession = schemes.sessionPerConfiguration.configurations['ios.sim.none'].session; expect(clientMockData.lastConstructorArguments[0]).toBe(expectedSession); }); it(`Detox should use session defined per configuration - release`, async () => { - mockCommandLineArgs({configuration: 'ios.sim.release'}); + process.env.configuration = 'ios.sim.release'; Detox = require('./Detox'); detox = new Detox(schemes.sessionPerConfiguration); await detox.init(); - let expectedSession = schemes.sessionPerConfiguration.configurations['ios.sim.release'].session; + const expectedSession = schemes.sessionPerConfiguration.configurations['ios.sim.release'].session; expect(clientMockData.lastConstructorArguments[0]).toBe(expectedSession); }); @@ -189,12 +188,12 @@ describe('Detox', () => { detox = new Detox(schemes.sessionInCommonAndInConfiguration); await detox.init(); - let expectedSession = schemes.sessionInCommonAndInConfiguration.configurations['ios.sim.none'].session; + const expectedSession = schemes.sessionInCommonAndInConfiguration.configurations['ios.sim.none'].session; expect(clientMockData.lastConstructorArguments[0]).toBe(expectedSession); }); it(`beforeEach() - should set device artifacts destination`, async () => { - mockCommandLineArgs({'artifacts-location': '/tmp'}); + process.env.artifactsLocation = '/tmp'; Detox = require('./Detox'); detox = new Detox(schemes.validOneDeviceAndSession); await detox.init(); @@ -211,7 +210,7 @@ describe('Detox', () => { }); it(`afterEach() - should call device.finalizeArtifacts`, async () => { - mockCommandLineArgs({'artifacts-location': '/tmp'}); + process.env.artifactsLocation = '/tmp'; Detox = require('./Detox'); detox = new Detox(schemes.validOneDeviceAndSession); await detox.init(); @@ -228,13 +227,11 @@ describe('Detox', () => { }); it(`the constructor should catch exception from ArtifactsPathsProvider`, async () => { - mockCommandLineArgs({'artifacts-location': '/tmp'}); - fs.mkdirSync = jest.fn(() => {throw 'Could not create artifacts root dir'}); + process.env.artifactsLocation = '/tmp'; + fs.mkdirSync = jest.fn(() => { + throw Error('Could not create artifacts root dir'); + }); Detox = require('./Detox'); detox = new Detox(schemes.validOneDeviceAndSession); }); - - function mockCommandLineArgs(args) { - minimist.mockReturnValue(args); - } }); diff --git a/detox/src/utils/argparse.js b/detox/src/utils/argparse.js index 4196e77515..a2f1408d51 100644 --- a/detox/src/utils/argparse.js +++ b/detox/src/utils/argparse.js @@ -1,7 +1,15 @@ const argv = require('minimist')(process.argv.slice(2)); function getArgValue(key) { - const value = argv ? argv[key] : undefined; + let value; + + if (argv && argv[key]) { + value = argv[key]; + } else { + const camelCasedKey = key.replace(/(\-\w)/g, (m) => m[1].toUpperCase()); + value = process.env[camelCasedKey]; + } + return value; } diff --git a/detox/src/utils/argparse.test.js b/detox/src/utils/argparse.test.js index 32b9809e28..b230418279 100644 --- a/detox/src/utils/argparse.test.js +++ b/detox/src/utils/argparse.test.js @@ -1,20 +1,39 @@ -const _ = require('lodash'); +jest.unmock('process'); describe('argparse', () => { - let argparse; + describe('using env variables', () => { + let argparse; - beforeEach(() => { - jest.mock('minimist'); - const minimist = require('minimist'); - minimist.mockReturnValue({test: 'a value'}); - argparse = require('./argparse'); - }); + beforeEach(() => { + process.env.fooBar = 'a value'; + argparse = require('./argparse'); + }); + + it(`nonexistent key should return undefined result`, () => { + expect(argparse.getArgValue('blah')).not.toBeDefined(); + }); - it(`nonexistent key should return undefined result`, () => { - expect(argparse.getArgValue('blah')).not.toBeDefined(); + it(`existing key should return a result`, () => { + expect(argparse.getArgValue('foo-bar')).toBe('a value'); + }); }); - it(`existing key should return a result`, () => { - expect(argparse.getArgValue('test')).toBe('a value'); + describe('using arguments', () => { + let argparse; + + beforeEach(() => { + jest.mock('minimist'); + const minimist = require('minimist'); + minimist.mockReturnValue({'kebab-case-key': 'a value'}); + argparse = require('./argparse'); + }); + + it(`nonexistent key should return undefined result`, () => { + expect(argparse.getArgValue('blah')).not.toBeDefined(); + }); + + it(`existing key should return a result`, () => { + expect(argparse.getArgValue('kebab-case-key')).toBe('a value'); + }); }); }); diff --git a/docs/Guide.Jest.md b/docs/Guide.Jest.md index 7253f67225..b1bc2fe3fe 100644 --- a/docs/Guide.Jest.md +++ b/docs/Guide.Jest.md @@ -16,10 +16,9 @@ npm install --save-dev jest You should remove `e2e/mocha.opts`, you no longer need it. -### 3. Write a detox setup file +### 3. Replace generated detox setup file (e2e/init.js) ```js -// ./jest/setup/e2e.js const detox = require('detox'); const config = require('../package.json').detox; @@ -44,15 +43,15 @@ beforeEach(async () => { Add this part to your `package.json`: ```json "jest": { - "setupTestFrameworkScriptFile": "/jest/setup.js" + "setupTestFrameworkScriptFile": "./e2e/init.js" }, "scripts": { - "test:e2e": "jest __e2e__ --setupTestFrameworkScriptFile=./jest/setup/e2e-tests.js --runInBand", + "test:e2e": "detox test", "test:e2e:build": "detox build" } ``` -We need the `--runInBand` as detox doesn't support parallelism yet. +In the `detox` part of your `package.json`, add `"runner": "jest"` to tell detox that you want to use jest runner instead of mocha. ### Writing Tests