Skip to content

Commit

Permalink
feat(config): launch app manually (wix#2540)
Browse files Browse the repository at this point in the history
  • Loading branch information
noomorph authored Dec 24, 2020
1 parent 4f107c3 commit 5c5e573
Show file tree
Hide file tree
Showing 23 changed files with 530 additions and 78 deletions.
7 changes: 6 additions & 1 deletion detox/local-cli/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const _ = require('lodash');
const cp = require('child_process');
const path = require('path');
const unparse = require('yargs-unparser');
const whichSync = require('which').sync;
const { parse, quote } = require('./utils/shellQuote');
const splitArgv = require('./utils/splitArgv');
const DetoxRuntimeError = require('../src/errors/DetoxRuntimeError');
Expand Down Expand Up @@ -37,7 +38,11 @@ module.exports.handler = async function test(argv) {
});

if (detoxArgs['inspect-brk']) {
forwardedArgs.argv.$0 = `node --inspect-brk ${runnerConfig.testRunner}`;
const runnerBinary = whichSync(runner, {
path: prependNodeModulesBinToPATH({ ...process.env }),
});

forwardedArgs.argv.$0 = `node --inspect-brk ${require.resolve(runnerBinary)}`;
} else {
forwardedArgs.argv.$0 = runnerConfig.testRunner;
}
Expand Down
5 changes: 3 additions & 2 deletions detox/local-cli/test.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ describe('CLI', () => {
});

test.each([
['--inspect-brk e2eFolder', /^node --inspect-brk jest .* e2eFolder$/, {}],
['--inspect-brk e2eFolder', /^node --inspect-brk .*jest\.js .* e2eFolder$/, {}],
['-d e2eFolder', / e2eFolder$/, { debugSynchronization: 3000 }],
['--debug-synchronization e2eFolder', / e2eFolder$/, { debugSynchronization: 3000 }],
['-r e2eFolder', / e2eFolder$/, { reuse: true }],
Expand Down Expand Up @@ -660,7 +660,8 @@ describe('CLI', () => {

test('--inspect-brk should prepend "node --inspect-brk" to the command', async () => {
await run('--inspect-brk');
expect(cliCall().command).toMatch(RegExp(`^node --inspect-brk ${testRunner}`));
const absolutePathToTestRunnerJs = require.resolve(`.bin/${testRunner}`);
expect(cliCall().command).toMatch(RegExp(`^node --inspect-brk ${absolutePathToTestRunnerJs}`));
});

test('should append $DETOX_ARGV_OVERRIDE to detox test ... command and print a warning', async () => {
Expand Down
2 changes: 2 additions & 0 deletions detox/local-cli/utils/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ function prependNodeModulesBinToPATH(env) {
if (!env[PATH].startsWith(nodeBinariesPath)) {
env[PATH] = nodeBinariesPath + env[PATH];
}

return env[PATH];
}

module.exports = {
Expand Down
1 change: 1 addition & 0 deletions detox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
"src/utils/logger.js",
"src/utils/onTerminate.js",
"src/utils/pipeCommands.js",
"src/utils/pressAnyKey.js",
"src/utils/sleep.js",
"AAPT.js",
"ADB.js",
Expand Down
1 change: 1 addition & 0 deletions detox/src/Detox.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ class Detox {
});

this.device = new Device({
behaviorConfig: this._behaviorConfig,
deviceConfig: this._deviceConfig,
emitter: this._eventEmitter,
deviceDriver,
Expand Down
1 change: 1 addition & 0 deletions detox/src/Detox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ describe('Detox', () => {

it('should instantiate Device', () =>
expect(Device).toHaveBeenCalledWith({
behaviorConfig: detoxConfig.behaviorConfig,
deviceConfig: detoxConfig.deviceConfig,
emitter: expect.anything(),
deviceDriver: expect.anything(),
Expand Down
26 changes: 15 additions & 11 deletions detox/src/configuration/composeArtifactsConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,7 @@ function composeArtifactsConfig({
}),
extendArtifactsConfig(deviceConfig.artifacts),
extendArtifactsConfig(detoxConfig.artifacts),
extendArtifactsConfig({
rootDir: 'artifacts',
pathBuilder: null,
plugins: {
log: 'none',
screenshot: 'manual',
video: 'none',
instruments: 'none',
timeline: 'none',
},
}),
extendArtifactsConfig(false),
);

artifactsConfig.rootDir = buildDefaultArtifactsRootDirpath(
Expand All @@ -53,6 +43,20 @@ function composeArtifactsConfig({
}

function extendArtifactsConfig(config) {
if (config === false) {
return extendArtifactsConfig({
rootDir: 'artifacts',
pathBuilder: null,
plugins: {
log: 'none',
screenshot: 'manual',
video: 'none',
instruments: 'none',
timeline: 'none',
},
});
}

const p = config && config.plugins;
if (!p) {
return config;
Expand Down
22 changes: 22 additions & 0 deletions detox/src/configuration/composeArtifactsConfig.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,28 @@ describe('composeArtifactsConfig', () => {
});
});

it('should disable global artifacts config if deviceConfig.artifacts = false', () => {
expect(composeArtifactsConfig({
configurationName: 'abracadabra',
deviceConfig: {
artifacts: false,
},
detoxConfig: {
artifacts: {
...schemes.allArtifactsConfiguration,
rootDir: 'otherPlace',
pathBuilder: _.noop,
}
},
cliConfig: {},
})).toMatchObject({
pathBuilder: expect.objectContaining({
rootDir: expect.stringMatching(/^artifacts[\\\/]abracadabra\.\d{4}/),
}),
plugins: schemes.pluginsDefaultsResolved,
});
});

it('should use CLI config', () => {
expect(composeArtifactsConfig({
configurationName: 'abracadabra',
Expand Down
62 changes: 35 additions & 27 deletions detox/src/configuration/composeBehaviorConfig.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
const _ = require('lodash');

function composeBehaviorConfig({ cliConfig, detoxConfig, deviceConfig, userParams }) {
return _.defaultsDeep(
{
init: {
reinstallApp: cliConfig.reuse ? false : undefined,
return _.chain({})
.defaultsDeep(
{
init: {
reinstallApp: cliConfig.reuse ? false : undefined,
},
cleanup: {
shutdownDevice: cliConfig.cleanup ? true : undefined,
},
},
cleanup: {
shutdownDevice: cliConfig.cleanup ? true : undefined,
userParams && {
init: {
exposeGlobals: userParams.initGlobals,
launchApp: userParams.launchApp,
reinstallApp: negateDefined(userParams.reuse),
},
},
},
userParams && {
init: {
exposeGlobals: userParams.initGlobals,
launchApp: userParams.launchApp,
reinstallApp: negateDefined(userParams.reuse),
},
},
deviceConfig.behavior,
detoxConfig.behavior,
{
init: {
exposeGlobals: true,
reinstallApp: true,
launchApp: true,
},
cleanup: {
shutdownDevice: false,
},
}
);
deviceConfig.behavior,
detoxConfig.behavior,
{
init: {
exposeGlobals: true,
reinstallApp: undefined,
launchApp: true,
},
launchApp: 'auto',
cleanup: {
shutdownDevice: false,
},
}
)
.tap(config => {
if (config.init.reinstallApp === undefined) {
config.init.reinstallApp = config.launchApp !== 'manual';
}
})
.value();
}

function negateDefined(x) {
Expand Down
21 changes: 20 additions & 1 deletion detox/src/configuration/composeBehaviorConfig.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,28 @@ describe('composeBehaviorConfig', () => {
reinstallApp: true,
launchApp: true,
},
launchApp: 'auto',
cleanup: {
shutdownDevice: false,
},
})
});

describe('if a custom config has only .launchApp = "manual" override', () => {
beforeEach(() => {
detoxConfig = {
behavior: { launchApp: 'manual' }
};
});

it('should implicitly override behavior.init.reinstallApp = false', () => {
const expected = _.cloneDeep(detoxConfig.behavior);
const actual = composed();

expect(actual.init.reinstallApp).toBe(false);
});
});

describe('if detox config is set', () => {
beforeEach(() => {
detoxConfig = {
Expand All @@ -42,6 +58,7 @@ describe('composeBehaviorConfig', () => {
reinstallApp: false,
launchApp: false,
},
launchApp: 'manual',
cleanup: {
shutdownDevice: true,
},
Expand All @@ -65,6 +82,7 @@ describe('composeBehaviorConfig', () => {
reinstallApp: true,
launchApp: true,
},
launchApp: 'auto',
cleanup: {
shutdownDevice: false,
},
Expand Down Expand Up @@ -95,6 +113,7 @@ describe('composeBehaviorConfig', () => {
reinstallApp: true,
launchApp: false,
},
launchApp: 'auto',
cleanup: {
shutdownDevice: false,
}
Expand All @@ -114,6 +133,7 @@ describe('composeBehaviorConfig', () => {
reinstallApp: false,
launchApp: false,
},
launchApp: 'auto',
cleanup: {
shutdownDevice: true,
}
Expand All @@ -124,4 +144,3 @@ describe('composeBehaviorConfig', () => {
});
});
});

25 changes: 19 additions & 6 deletions detox/src/devices/Device.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
const _ = require('lodash');
const debug = require('../utils/debug'); // debug utils, leave here even if unused
const { traceCall } = require('../utils/trace');
const log = require('../utils/logger').child({ __filename });

class Device {
constructor({ deviceConfig, deviceDriver, emitter, sessionConfig }) {
constructor({
behaviorConfig,
deviceConfig,
deviceDriver,
emitter,
sessionConfig
}) {
this._behaviorConfig = behaviorConfig;
this._deviceConfig = deviceConfig;
this._sessionConfig = sessionConfig;
this._emitter = emitter;
Expand All @@ -22,9 +30,9 @@ class Device {
}

async launchApp(params = {newInstance: false}, bundleId) {
return traceCall('launchApp', () =>
this._doLaunchApp(params, bundleId));
return traceCall('launchApp', () => this._doLaunchApp(params, bundleId));
}

async _doLaunchApp(params, bundleId) {
const payloadParams = ['url', 'userNotification', 'userActivity'];
const hasPayload = this._assertHasSingleParam(payloadParams, params);
Expand Down Expand Up @@ -67,11 +75,16 @@ class Device {
}
}

const processId = await this.deviceDriver.launchApp(this._deviceId, _bundleId, this._prepareLaunchArgs(baseLaunchArgs), params.languageAndLocale);
let processId;
if (this._behaviorConfig.launchApp === 'manual') {
processId = await this.deviceDriver.waitForAppLaunch(this._deviceId, _bundleId, this._prepareLaunchArgs(baseLaunchArgs), params.languageAndLocale);
} else {
processId = await this.deviceDriver.launchApp(this._deviceId, _bundleId, this._prepareLaunchArgs(baseLaunchArgs), params.languageAndLocale);
await this.deviceDriver.waitUntilReady();
await this.deviceDriver.waitForActive();
}
this._processes[_bundleId] = processId;

await this.deviceDriver.waitUntilReady();
await this.deviceDriver.waitForActive();
await this._emitter.emit('appReady', {
deviceId: this._deviceId,
bundleId: _bundleId,
Expand Down
16 changes: 16 additions & 0 deletions detox/src/devices/Device.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ describe('Device', () => {
expect(this.driver.launchApp).toHaveBeenCalledWith(device._deviceId, device._bundleId, expectedArgs, languageAndLocale);
}

expectWaitForLaunchCalled(device, expectedArgs, languageAndLocale) {
expect(this.driver.waitForAppLaunch).toHaveBeenCalledWith(device._deviceId, device._bundleId, expectedArgs, languageAndLocale);
}

expectReinstallCalled() {
expect(this.driver.uninstallApp).toHaveBeenCalled();
expect(this.driver.installApp).toHaveBeenCalled();
Expand Down Expand Up @@ -90,6 +94,7 @@ describe('Device', () => {

function schemeDevice(scheme, configuration, overrides) {
const device = new Device(_.merge({
behaviorConfig: {},
deviceConfig: scheme.configurations[configuration],
deviceDriver: driverMock.driver,
sessionConfig: scheme.session,
Expand Down Expand Up @@ -142,6 +147,17 @@ describe('Device', () => {
driverMock.expectLaunchCalled(device, expectedArgs);
});

it(`given behaviorConfig.launchApp == 'manual' should wait for the app launch`, async () => {
const expectedArgs = expectedDriverArgs;
const device = validDevice({
behaviorConfig: { launchApp: 'manual' }
});
await device.launchApp();

expect(driverMock.driver.launchApp).not.toHaveBeenCalled();
driverMock.expectWaitForLaunchCalled(device, expectedArgs);
});

it(`args should launch app and emit appReady`, async () => {
driverMock.driver.launchApp = async () => 42;

Expand Down
6 changes: 5 additions & 1 deletion detox/src/devices/drivers/DeviceDriverBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ class DeviceDriverBase {
}

async launchApp() {
return await Promise.resolve('');
return await Promise.resolve(NaN);
}

async waitForAppLaunch() {
return await Promise.resolve(NaN);
}

async takeScreenshot(deviceId, screenshotName) {
Expand Down
Loading

0 comments on commit 5c5e573

Please sign in to comment.