From fdc37b0d77713f20b9c5f912b83f2063695ecc9b Mon Sep 17 00:00:00 2001 From: trofima Date: Fri, 27 Oct 2017 00:04:06 +0300 Subject: [PATCH 01/13] export platform specific objects throw proxy --- detox/src/Detox.js | 8 ++++++-- detox/src/devices/IosDriver.js | 9 ++++++--- detox/src/exportWrapper.js | 35 ++++++++++++++++++++++++++++++++++ detox/src/index.js | 6 +++++- detox/src/platform.js | 17 +++++++++++++++++ 5 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 detox/src/exportWrapper.js create mode 100644 detox/src/platform.js diff --git a/detox/src/Detox.js b/detox/src/Detox.js index 89d383edf6..5e5b219dc1 100644 --- a/detox/src/Detox.js +++ b/detox/src/Detox.js @@ -41,7 +41,7 @@ class Detox { } } - async init(params = {launchApp: true}) { + async init(params = {launchApp: true, initGlobals: true}) { if (!(this.userConfig.configurations && _.size(this.userConfig.configurations) >= 1)) { throw new Error(`No configured devices`); } @@ -68,7 +68,11 @@ class Detox { const deviceDriver = new deviceClass(this.client); this.device = new Device(deviceConfig, sessionConfig, deviceDriver); await this.device.prepare(params); - global.device = this.device; + + if (params.initGlobals) { + deviceDriver.exportGlobals(); + global.device = this.device; + } } async cleanup() { diff --git a/detox/src/devices/IosDriver.js b/detox/src/devices/IosDriver.js index 5600b9a417..b1c0a7a783 100644 --- a/detox/src/devices/IosDriver.js +++ b/detox/src/devices/IosDriver.js @@ -13,9 +13,12 @@ class IosDriver extends DeviceDriverBase { constructor(client) { super(client); - const expect = require('../ios/expect'); - expect.exportGlobals(); - expect.setInvocationManager(new InvocationManager(client)); + this.expect = require('../ios/expect'); + this.expect.setInvocationManager(new InvocationManager(client)); + } + + exportGlobals() { + this.expect.exportGlobals(); } createPushNotificationJson(notification) { diff --git a/detox/src/exportWrapper.js b/detox/src/exportWrapper.js new file mode 100644 index 0000000000..f81fdac81b --- /dev/null +++ b/detox/src/exportWrapper.js @@ -0,0 +1,35 @@ +const platform = require('./platform'); +const iosExports = require('./ios/expect'); +const androidExports = require('./android/expect'); + +const exportMap = { + expect: { + ios: iosExports.expect, + android: androidExports.expect, + }, + element: { + ios: iosExports.element, + android: androidExports.element, + }, + waitFor: { + ios: iosExports.waitFor, + android: androidExports.waitFor, + }, + by: { + ios: iosExports.by, + android: androidExports.by, + } +}; + +const exports = new Proxy(exportMap, { + get(map, funcName) { + return (...args) => map[funcName][platform.get('name')](...args); + } +}); + +module.exports = { + ...exports, + get device() { + return platform.get('device'); + } +}; diff --git a/detox/src/index.js b/detox/src/index.js index bb4b091255..dcdc2c2c2d 100644 --- a/detox/src/index.js +++ b/detox/src/index.js @@ -1,9 +1,12 @@ const Detox = require('./Detox'); +const platform = require('./platform'); +const exportWrapper = require('./exportWrapper'); let detox; async function init(config, params) { detox = new Detox(config); + platform.set(config.type, detox.device); await detox.init(params); } @@ -39,5 +42,6 @@ module.exports = { init, cleanup, beforeEach, - afterEach + afterEach, + ...exportWrapper, }; diff --git a/detox/src/platform.js b/detox/src/platform.js new file mode 100644 index 0000000000..98d1b2277a --- /dev/null +++ b/detox/src/platform.js @@ -0,0 +1,17 @@ +class Platform { + constructor() { + this.name = null; + this.device = null; + } + + set(type, device) { + this.name = type.split('.')[0]; + this.device = device; + } + + get(entry) { + return this[entry]; + } +} + +module.exports = {platform: new Platform()}; \ No newline at end of file From a5051c12500137515a3ec21b5340744f75859b20 Mon Sep 17 00:00:00 2001 From: trofima Date: Thu, 23 Nov 2017 16:23:58 +0200 Subject: [PATCH 02/13] Fixed spread errors. Fixed existing tests. Moved out deviceConfig generation and related refactor. --- detox/src/Detox.js | 64 ++------- detox/src/Detox.test.js | 157 +++++++--------------- detox/src/exportWrapper.js | 7 +- detox/src/index.js | 44 +++++- detox/src/index.test.js | 130 ++++++++++++++++++ detox/src/ios/earlgreyapi/GREYMatchers.js | 16 +-- detox/src/platform.js | 2 +- 7 files changed, 238 insertions(+), 182 deletions(-) diff --git a/detox/src/Detox.js b/detox/src/Detox.js index 3aa254ab03..3e3dc30540 100644 --- a/detox/src/Detox.js +++ b/detox/src/Detox.js @@ -24,12 +24,9 @@ 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)'`); - } - - this.userConfig = userConfig; + constructor({deviceConfig, session}) { + this.deviceConfig = deviceConfig; + this.userSession = deviceConfig.session || session; this.client = null; this.device = null; this._currentTestNumber = 0; @@ -44,31 +41,23 @@ class Detox { } async init(params = {launchApp: true, initGlobals: true}) { - if (!(this.userConfig.configurations && _.size(this.userConfig.configurations) >= 1)) { - throw new Error(`No configured devices`); - } - - const deviceConfig = await this._getDeviceConfig(); - if (!deviceConfig.type) { - configuration.throwOnEmptyType(); - } + const sessionConfig = await this._getSessionConfig(); - const [sessionConfig, shouldStartServer] = await this._chooseSession(deviceConfig); - - if (shouldStartServer) { + if (!this.userSession) { this.server = new DetoxServer(new URL(sessionConfig.server).port); } this.client = new Client(sessionConfig); await this.client.connect(); - const deviceClass = DEVICE_CLASSES[deviceConfig.type]; + const deviceClass = DEVICE_CLASSES[this.deviceConfig.type]; + if (!deviceClass) { - throw new Error(`'${deviceConfig.type}' is not supported`); + throw new Error(`'${this.deviceConfig.type}' is not supported`); } const deviceDriver = new deviceClass(this.client); - this.device = new Device(deviceConfig, sessionConfig, deviceDriver); + this.device = new Device(this.deviceConfig, sessionConfig, deviceDriver); await this.device.prepare(params); if (params.initGlobals) { @@ -109,41 +98,12 @@ class Detox { } } - async _chooseSession(deviceConfig) { - let session = deviceConfig.session; - let shouldStartServer = false; - - if (!session) { - session = this.userConfig.session; - } - - if (!session) { - session = await configuration.defaultSession(); - shouldStartServer = true; - } + async _getSessionConfig() { + const session = this.userSession || await configuration.defaultSession(); configuration.validateSession(session); - return [session, shouldStartServer]; - } - - async _getDeviceConfig() { - const configurationName = argparse.getArgValue('configuration'); - const configurations = this.userConfig.configurations; - - let deviceConfig; - if (!configurationName && _.size(configurations) === 1) { - deviceConfig = _.values(configurations)[0]; - } else { - deviceConfig = configurations[configurationName]; - } - - if (!deviceConfig) { - throw new Error(`Cannot determine which configuration to use. use --configuration to choose one of the following: - ${Object.keys(configurations)}`); - } - - return deviceConfig; + return session; } } diff --git a/detox/src/Detox.test.js b/detox/src/Detox.test.js index 1d35fb3c00..467960cf10 100644 --- a/detox/src/Detox.test.js +++ b/detox/src/Detox.test.js @@ -4,6 +4,11 @@ describe('Detox', () => { let fs; let Detox; let detox; + const validDeviceConfig = schemes.validOneDeviceNoSession.configurations['ios.sim.release']; + const validDeviceConfigWithSession = schemes.sessionPerConfiguration.configurations['ios.sim.none']; + const invalidDeviceConfig = schemes.invalidDeviceNoDeviceType.configurations['ios.sim.release']; + const invalidDeviceTypeConfig = schemes.invalidOneDeviceTypeEmulatorNoSession.configurations['ios.sim.release']; + const validSession = schemes.validOneDeviceAndSession.session; const clientMockData = {lastConstructorArguments: null}; const deviceMockData = {lastConstructorArguments: null}; @@ -27,6 +32,7 @@ describe('Detox', () => { setCustomMock('./devices/Device', deviceMockData); process.env = {}; + global.device = undefined; jest.mock('./devices/IosDriver'); jest.mock('./devices/SimulatorDriver'); @@ -35,40 +41,11 @@ describe('Detox', () => { jest.mock('./client/Client'); }); - it(`No config is passed to init, should throw`, async () => { - Detox = require('./Detox'); - try { - detox = new Detox(); - } catch (ex) { - expect(ex).toBeDefined(); - } - }); - - it(`Config with no devices, should throw`, async () => { - Detox = require('./Detox'); - try { - detox = new Detox(schemes.invalidNoDevice); - await detox.init(); - } catch (ex) { - expect(ex).toBeDefined(); - } - }); - - it(`Config with emulator, should throw`, async () => { - Detox = require('./Detox'); - try { - detox = new Detox(schemes.invalidOneDeviceTypeEmulatorNoSession); - await detox.init(); - } catch (ex) { - expect(ex).toBeDefined(); - } - }); - it(`Passing --cleanup should shutdown the currently running device`, async () => { process.env.cleanup = true; Detox = require('./Detox'); - detox = new Detox(schemes.validOneDeviceNoSession); + detox = new Detox({deviceConfig: validDeviceConfig}); await detox.init(); await detox.cleanup(); expect(detox.device.shutdown).toHaveBeenCalledTimes(1); @@ -76,7 +53,7 @@ describe('Detox', () => { it(`Not passing --cleanup should keep the currently running device up`, async () => { Detox = require('./Detox'); - detox = new Detox(schemes.validOneDeviceNoSession); + detox = new Detox({deviceConfig: validDeviceConfig}); await detox.init(); await detox.cleanup(); expect(detox.device.shutdown).toHaveBeenCalledTimes(0); @@ -84,118 +61,62 @@ describe('Detox', () => { it(`One valid device, detox should init with generated session config and default to this device`, async () => { Detox = require('./Detox'); - detox = new Detox(schemes.validOneDeviceNoSession); + detox = new Detox({deviceConfig: validDeviceConfig}); await detox.init(); expect(clientMockData.lastConstructorArguments[0]).toBeDefined(); }); - it(`One valid device, detox should use session config and default to this device`, async () => { - Detox = require('./Detox'); - detox = new Detox(schemes.validOneDeviceAndSession); - await detox.init(); - - expect(clientMockData.lastConstructorArguments[0]).toBe(schemes.validOneDeviceAndSession.session); - }); - - it(`Two valid devices, detox should init with the device passed in '--configuration' cli option`, async () => { - process.env.configuration = 'ios.sim.debug'; - Detox = require('./Detox'); - - detox = new Detox(schemes.validTwoDevicesNoSession); - await detox.init(); - expect(deviceMockData.lastConstructorArguments[0]).toEqual(schemes.validTwoDevicesNoSession.configurations['ios.sim.debug']); - }); - - it(`Two valid devices, detox should throw if device passed in '--configuration' cli option doesn't exist`, async () => { - process.env.configuration = 'nonexistent'; - Detox = require('./Detox'); - - detox = new Detox(schemes.validTwoDevicesNoSession); + it(`throws if device type is not supported`, async () => { + let exception = undefined; try { - await detox.init(); - } catch (ex) { - expect(ex).toBeDefined(); + Detox = require('./Detox'); + detox = new Detox({deviceConfig: invalidDeviceTypeConfig}); + detox.init(); + } catch (e) { + exception = e; } - }); - it(`Two valid devices, detox should throw if device passed in '--configuration' cli option doesn't exist`, async () => { - process.env.configuration = 'nonexistent'; - Detox = require('./Detox'); - - detox = new Detox(schemes.validTwoDevicesNoSession); - - try { - await detox.init(); - } catch (ex) { - expect(ex).toBeDefined(); - } + expect(exception).toBeDefined(); }); - it(`One invalid device (no device name), detox should throw`, async () => { + it(`One valid device, detox should use session config and default to this device`, async () => { Detox = require('./Detox'); + detox = new Detox({deviceConfig: validDeviceConfig, session: validSession}); + await detox.init(); - detox = new Detox(schemes.invalidDeviceNoDeviceName); - - try { - await detox.init(); - } catch (ex) { - expect(ex).toBeDefined(); - } - }); - - it(`One invalid device, detox should throw`, async () => { - Detox = require('./Detox'); - - detox = new Detox(schemes.invalidDeviceNoDeviceType); - - try { - await detox.init(); - fail('should have thrown'); - } catch (ex) { - expect(ex).toBeDefined(); - } + expect(clientMockData.lastConstructorArguments[0]).toBe(validSession); }); it(`cleanup on a non initialized detox should not throw`, async () => { Detox = require('./Detox'); - detox = new Detox(schemes.invalidDeviceNoDeviceType); + detox = new Detox({deviceConfig: invalidDeviceConfig}); detox.cleanup(); }); - it(`Detox should use session defined per configuration - none`, async () => { + it(`Detox should use session defined per configuration `, async () => { process.env.configuration = 'ios.sim.none'; Detox = require('./Detox'); - detox = new Detox(schemes.sessionPerConfiguration); - await detox.init(); - - 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 () => { - process.env.configuration = 'ios.sim.release'; - Detox = require('./Detox'); - detox = new Detox(schemes.sessionPerConfiguration); + detox = new Detox({deviceConfig: validDeviceConfigWithSession}); await detox.init(); - const expectedSession = schemes.sessionPerConfiguration.configurations['ios.sim.release'].session; + const expectedSession = validDeviceConfigWithSession.session; expect(clientMockData.lastConstructorArguments[0]).toBe(expectedSession); }); it(`Detox should prefer session defined per configuration over common session`, async () => { Detox = require('./Detox'); - detox = new Detox(schemes.sessionInCommonAndInConfiguration); + detox = new Detox({deviceConfig: validDeviceConfigWithSession, session: {}}); await detox.init(); - const expectedSession = schemes.sessionInCommonAndInConfiguration.configurations['ios.sim.none'].session; + const expectedSession = validDeviceConfigWithSession.session; expect(clientMockData.lastConstructorArguments[0]).toBe(expectedSession); }); it(`beforeEach() - should set device artifacts destination`, async () => { process.env.artifactsLocation = '/tmp'; Detox = require('./Detox'); - detox = new Detox(schemes.validOneDeviceAndSession); + detox = new Detox({deviceConfig: validDeviceConfigWithSession}); await detox.init(); await detox.beforeEach('a', 'b', 'c'); expect(device.setArtifactsDestination).toHaveBeenCalledTimes(1); @@ -203,7 +124,7 @@ describe('Detox', () => { it(`beforeEach() - should not set device artifacts destination if artifacts not set in cli args`, async () => { Detox = require('./Detox'); - detox = new Detox(schemes.validOneDeviceAndSession); + detox = new Detox({deviceConfig: validDeviceConfigWithSession}); await detox.init(); await detox.beforeEach('a', 'b', 'c'); expect(device.setArtifactsDestination).toHaveBeenCalledTimes(0); @@ -212,7 +133,7 @@ describe('Detox', () => { it(`afterEach() - should call device.finalizeArtifacts`, async () => { process.env.artifactsLocation = '/tmp'; Detox = require('./Detox'); - detox = new Detox(schemes.validOneDeviceAndSession); + detox = new Detox({deviceConfig: validDeviceConfigWithSession}); await detox.init(); await detox.afterEach(); expect(device.finalizeArtifacts).toHaveBeenCalledTimes(1); @@ -220,7 +141,7 @@ describe('Detox', () => { it(`afterEach() - should not call device.finalizeArtifacts if artifacts not set in cli arg`, async () => { Detox = require('./Detox'); - detox = new Detox(schemes.validOneDeviceAndSession); + detox = new Detox({deviceConfig: validDeviceConfigWithSession}); await detox.init(); await detox.afterEach(); expect(device.finalizeArtifacts).toHaveBeenCalledTimes(0); @@ -232,6 +153,20 @@ describe('Detox', () => { throw Error('Could not create artifacts root dir'); }); Detox = require('./Detox'); - detox = new Detox(schemes.validOneDeviceAndSession); + detox = new Detox({deviceConfig: validDeviceConfigWithSession}); + }); + + it('exports globals by default', async () => { + Detox = require('./Detox'); + detox = new Detox({deviceConfig: validDeviceConfigWithSession}); + await detox.init(); + expect(global.device).toBeDefined(); + }); + + it(`doesn't exports globals if requested`, async () => { + Detox = require('./Detox'); + detox = new Detox({deviceConfig: validDeviceConfigWithSession}); + await detox.init({initGlobals: false}); + expect(global.device).not.toBeDefined(); }); }); diff --git a/detox/src/exportWrapper.js b/detox/src/exportWrapper.js index f81fdac81b..ccf07eae2d 100644 --- a/detox/src/exportWrapper.js +++ b/detox/src/exportWrapper.js @@ -21,15 +21,14 @@ const exportMap = { } }; -const exports = new Proxy(exportMap, { +const localExports = new Proxy(exportMap, { get(map, funcName) { return (...args) => map[funcName][platform.get('name')](...args); } }); -module.exports = { - ...exports, +module.exports = Object.assign(localExports, { get device() { return platform.get('device'); } -}; +}); diff --git a/detox/src/index.js b/detox/src/index.js index dcdc2c2c2d..d3922c6e73 100644 --- a/detox/src/index.js +++ b/detox/src/index.js @@ -1,12 +1,47 @@ const Detox = require('./Detox'); const platform = require('./platform'); const exportWrapper = require('./exportWrapper'); +const argparse = require('./utils/argparse'); +const configuration = require('./configuration'); +const _ = require('lodash'); let detox; +function getDeviceConfig(configurations) { + const configurationName = argparse.getArgValue('configuration'); + + const deviceConfig = (!configurationName && _.size(configurations) === 1) + ? _.values(configurations)[0] + : configurations[configurationName]; + + if (!deviceConfig) { + throw new Error(`Cannot determine which configuration to use. use --configuration to choose one of the following: + ${Object.keys(configurations)}`); + } + if (!deviceConfig.type) { + configuration.throwOnEmptyType(); + } + + if (!deviceConfig.name) { + configuration.throwOnEmptyName(); + } + + return deviceConfig; +} + async function init(config, params) { - detox = new Detox(config); - platform.set(config.type, detox.device); + if (!config) { + throw new Error(`No configuration was passed to detox, make sure you pass a config when calling 'detox.init(config)'`); + } + + if (!(config.configurations && _.size(config.configurations) >= 1)) { + throw new Error(`No configured devices`); + } + + const deviceConfig = getDeviceConfig(config.configurations); + + detox = new Detox({deviceConfig, session: config.session}); + platform.set(deviceConfig.type, detox.device); await detox.init(params); } @@ -38,10 +73,9 @@ async function afterEach() { // throw reason; //}); -module.exports = { +module.exports = Object.assign({ init, cleanup, beforeEach, afterEach, - ...exportWrapper, -}; +}, exportWrapper); diff --git a/detox/src/index.test.js b/detox/src/index.test.js index a4cbd36a42..e28a2aa337 100644 --- a/detox/src/index.test.js +++ b/detox/src/index.test.js @@ -1,13 +1,143 @@ const schemes = require('./configurations.mock'); describe('index', () => { let detox; + const mockDevice = {}; + const mockDetox = { + device: mockDevice, + init: jest.fn(), + cleanup: jest.fn(), + beforeEach: jest.fn(), + afterEach: jest.fn(), + }; + beforeEach(() => { jest.mock('detox-server'); jest.mock('./devices/Device'); jest.mock('./client/Client'); + jest.mock('./Detox', () => jest.fn(() => mockDetox)); + jest.mock('./platform'); detox = require('./index'); }); + afterEach(() => { + process.env = {}; + }); + + it(`throws if there was no config passed`, async () => { + let exception = undefined; + + try { + await detox.init(); + } catch (e) { + exception = e; + } + + expect(exception).toBeDefined(); + }); + + it(`throws if there is no devices in config`, async () => { + let exception = undefined; + + try { + await detox.init(schemes.invalidNoDevice); + } catch (e) { + exception = e; + } + + expect(exception).toBeDefined(); + }); + + it(`constructs detox with device config`, async () => { + const Detox = require('./Detox'); + + await detox.init(schemes.validOneDeviceNoSession); + + expect(Detox).toHaveBeenCalledWith({ + deviceConfig: schemes.validOneDeviceNoSession.configurations['ios.sim.release'], + session: undefined, + }); + }); + + it(`constructs detox with device config passed in '--configuration' cli value`, async () => { + process.env.configuration = 'ios.sim.debug'; + const Detox = require('./Detox'); + + await detox.init(schemes.validTwoDevicesNoSession); + + expect(Detox).toHaveBeenCalledWith({ + deviceConfig: schemes.validTwoDevicesNoSession.configurations['ios.sim.debug'], + session: undefined, + }); + }); + + it(`throws if device passed in '--configuration' cli option doesn't exist`, async () => { + let exception = undefined; + process.env.configuration = 'nonexistent'; + + try { + await detox.init(schemes.validTwoDevicesNoSession); + } catch (e) { + exception = e; + } + + expect(exception).toBeDefined(); + }); + + it(`throws if a device has no name`, async () => { + let exception = undefined; + + try { + await detox.init(schemes.invalidDeviceNoDeviceName); + } catch (e) { + exception = e; + } + + expect(exception).toBeDefined(); + }); + + it(`throws if a device is invalid`, async () => { + let exception = undefined; + + try { + await detox.init(schemes.invalidDeviceNoDeviceType); + } catch (e) { + exception = e; + } + + expect(exception).toBeDefined(); + }); + + it(`throws if a device is invalid`, async () => { + let exception = undefined; + + try { + await detox.init(schemes.invalidDeviceNoDeviceType); + } catch (e) { + exception = e; + } + + expect(exception).toBeDefined(); + }); + + it(`sets platform`, async () => { + const platform = require('./platform'); + + await detox.init(schemes.validOneDeviceNoSession); + + expect(platform.set).toHaveBeenCalledWith( + 'ios.simulator', + mockDevice, + ); + }); + + it(`initializes detox`, async () => { + const params = {}; + + await detox.init(schemes.validOneDeviceNoSession, params); + + expect(mockDetox.init).toHaveBeenCalledWith(params); + }); + it(`Basic usage`, async() => { await detox.init(schemes.validOneDeviceNoSession); await detox.cleanup(); diff --git a/detox/src/ios/earlgreyapi/GREYMatchers.js b/detox/src/ios/earlgreyapi/GREYMatchers.js index 2b8ab723e5..5786930b81 100644 --- a/detox/src/ios/earlgreyapi/GREYMatchers.js +++ b/detox/src/ios/earlgreyapi/GREYMatchers.js @@ -274,8 +274,8 @@ to a minimum value. } /*Matcher for UI element that is sufficiently visible to the user. EarlGrey considers elements -with visible area percentage greater than @c kElementSufficientlyVisiblePercentage (0.75) -to be sufficiently visible. +that are more than @c kElementSufficientlyVisiblePercentage (75 %) visible areawise to be +sufficiently visible. @return A matcher intialized with a visibility percentage that confirms an element is sufficiently visible. @@ -290,7 +290,7 @@ sufficiently visible. }; } - /*Matcher for UI element that are not visible to the user i.e. has a zero visible area. + /*Matcher for UI element that is not visible to the user at all i.e. it has a zero visible area. @return A matcher for verifying if an element is not visible. */static matcherForNotVisible() { @@ -304,13 +304,11 @@ sufficiently visible. }; } - /*Matcher for UI element that matches EarlGrey's criteria for user interaction currently it must + /*Matcher for UI element that matches EarlGrey's criteria for user interaction. Currently it must satisfy at least the following criteria: - +1) At least a few pixels of the element are visible to the user. +2) The element's accessibility activation point OR the center of the element's visible area +is completely visible. @return A matcher that checks if a UI element is interactable. */static matcherForInteractable() { diff --git a/detox/src/platform.js b/detox/src/platform.js index 98d1b2277a..cc279809c3 100644 --- a/detox/src/platform.js +++ b/detox/src/platform.js @@ -14,4 +14,4 @@ class Platform { } } -module.exports = {platform: new Platform()}; \ No newline at end of file +module.exports = new Platform(); \ No newline at end of file From 3467079f63dda9788e64137b3a31a83c0f9839ae Mon Sep 17 00:00:00 2001 From: trofima Date: Thu, 23 Nov 2017 17:15:22 +0200 Subject: [PATCH 03/13] exportWrapper tests, Detox test fixed. --- detox/src/Detox.test.js | 2 +- detox/src/exportWrapper.js | 14 +++++--------- detox/src/exportWrapper.test.js | 28 ++++++++++++++++++++++++++++ detox/src/index.test.js | 17 ++++++++++++----- 4 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 detox/src/exportWrapper.test.js diff --git a/detox/src/Detox.test.js b/detox/src/Detox.test.js index 467960cf10..fd4080b996 100644 --- a/detox/src/Detox.test.js +++ b/detox/src/Detox.test.js @@ -72,7 +72,7 @@ describe('Detox', () => { try { Detox = require('./Detox'); detox = new Detox({deviceConfig: invalidDeviceTypeConfig}); - detox.init(); + await detox.init(); } catch (e) { exception = e; } diff --git a/detox/src/exportWrapper.js b/detox/src/exportWrapper.js index ccf07eae2d..224a451e44 100644 --- a/detox/src/exportWrapper.js +++ b/detox/src/exportWrapper.js @@ -21,14 +21,10 @@ const exportMap = { } }; -const localExports = new Proxy(exportMap, { - get(map, funcName) { - return (...args) => map[funcName][platform.get('name')](...args); - } -}); - -module.exports = Object.assign(localExports, { - get device() { - return platform.get('device'); +module.exports = new Proxy(exportMap, { + get(map, name) { + return (name === 'device') + ? platform.get('device') + : map[name][platform.get('name')]; } }); diff --git a/detox/src/exportWrapper.test.js b/detox/src/exportWrapper.test.js new file mode 100644 index 0000000000..33a938fd7f --- /dev/null +++ b/detox/src/exportWrapper.test.js @@ -0,0 +1,28 @@ +const iosExports = require('./ios/expect'); +const androidExports = require('./android/expect'); +const exportWrapper = require('./exportWrapper'); +const platform = require('./platform'); + +describe('exportWrapper', () => { + const mockDevice = {}; + + it(`exports ios specific objects`, async () => { + platform.set('ios.none', mockDevice); + + expect(exportWrapper.device).toBe(mockDevice); + expect(exportWrapper.expect).toBe(iosExports.expect); + expect(exportWrapper.element).toBe(iosExports.element); + expect(exportWrapper.waitFor).toBe(iosExports.waitFor); + expect(exportWrapper.by).toBe(iosExports.by); + }); + + it(`exports android specific objects`, async () => { + platform.set('android.attached', mockDevice); + + expect(exportWrapper.device).toBe(mockDevice); + expect(exportWrapper.expect).toBe(androidExports.expect); + expect(exportWrapper.element).toBe(androidExports.element); + expect(exportWrapper.waitFor).toBe(androidExports.waitFor); + expect(exportWrapper.by).toBe(androidExports.by); + }); +}); diff --git a/detox/src/index.test.js b/detox/src/index.test.js index e28a2aa337..415e794310 100644 --- a/detox/src/index.test.js +++ b/detox/src/index.test.js @@ -11,16 +11,23 @@ describe('index', () => { }; beforeEach(() => { - jest.mock('detox-server'); - jest.mock('./devices/Device'); - jest.mock('./client/Client'); - jest.mock('./Detox', () => jest.fn(() => mockDetox)); - jest.mock('./platform'); + jest + .mock('detox-server') + .mock('./devices/Device') + .mock('./client/Client') + .mock('./Detox', () => jest.fn(() => mockDetox)) + .mock('./platform'); detox = require('./index'); }); afterEach(() => { process.env = {}; + jest + .unmock('detox-server') + .unmock('./devices/Device') + .unmock('./client/Client') + .unmock('./Detox') + .unmock('./platform'); }); it(`throws if there was no config passed`, async () => { From 26dcd9f5d1be6c4e23fb53a2df5cd7d772947e95 Mon Sep 17 00:00:00 2001 From: trofima Date: Thu, 23 Nov 2017 17:26:51 +0200 Subject: [PATCH 04/13] platform tests --- detox/src/platform.js | 8 ++++---- detox/src/platform.test.js | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 detox/src/platform.test.js diff --git a/detox/src/platform.js b/detox/src/platform.js index cc279809c3..d49e450340 100644 --- a/detox/src/platform.js +++ b/detox/src/platform.js @@ -4,14 +4,14 @@ class Platform { this.device = null; } + get(entry) { + return this[entry]; + } + set(type, device) { this.name = type.split('.')[0]; this.device = device; } - - get(entry) { - return this[entry]; - } } module.exports = new Platform(); \ No newline at end of file diff --git a/detox/src/platform.test.js b/detox/src/platform.test.js new file mode 100644 index 0000000000..ec03c64d17 --- /dev/null +++ b/detox/src/platform.test.js @@ -0,0 +1,17 @@ +const platform = require('./platform'); + +describe('exportWrapper', () => { + const mockDevice = {}; + + it(`stores platform specific device`, async () => { + platform.set('ios.none', mockDevice); + + expect(platform.get('device')).toBe(mockDevice); + }); + + it(`stores platform name`, async () => { + platform.set('ios.none', mockDevice); + + expect(platform.get('name')).toBe('ios'); + }); +}); From e497d74751e99e73afcd81ae0bb6e5de01ebedf2 Mon Sep 17 00:00:00 2001 From: trofima Date: Thu, 23 Nov 2017 17:35:25 +0200 Subject: [PATCH 05/13] minor refactor --- detox/src/index.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/detox/src/index.js b/detox/src/index.js index d3922c6e73..be843b82c2 100644 --- a/detox/src/index.js +++ b/detox/src/index.js @@ -29,7 +29,7 @@ function getDeviceConfig(configurations) { return deviceConfig; } -async function init(config, params) { +function validateConfig(config) { if (!config) { throw new Error(`No configuration was passed to detox, make sure you pass a config when calling 'detox.init(config)'`); } @@ -37,14 +37,21 @@ async function init(config, params) { if (!(config.configurations && _.size(config.configurations) >= 1)) { throw new Error(`No configured devices`); } +} - const deviceConfig = getDeviceConfig(config.configurations); +async function initializeDetox({configurations, session}, params) { + const deviceConfig = getDeviceConfig(configurations); - detox = new Detox({deviceConfig, session: config.session}); + detox = new Detox({deviceConfig, session}); platform.set(deviceConfig.type, detox.device); await detox.init(params); } +async function init(config, params) { + validateConfig(config); + await initializeDetox(config, params); +} + async function cleanup() { if (detox) { await detox.cleanup(); From e61d411df35755e9ec9e479825b6964de37a8763 Mon Sep 17 00:00:00 2001 From: trofima Date: Thu, 7 Dec 2017 18:26:57 +0200 Subject: [PATCH 06/13] trying to make it work --- detox/src/exportWrapper.js | 1 + detox/src/index.js | 94 +++++++++++++++++-- detox/src/platform.js | 5 +- .../e2eExportProxy/example.spec.js | 34 +++++++ .../demo-react-native/e2eExportProxy/init.js | 11 +++ .../e2eExportProxy/mocha.opts | 3 + examples/demo-react-native/package.json | 2 +- 7 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 examples/demo-react-native/e2eExportProxy/example.spec.js create mode 100644 examples/demo-react-native/e2eExportProxy/init.js create mode 100644 examples/demo-react-native/e2eExportProxy/mocha.opts diff --git a/detox/src/exportWrapper.js b/detox/src/exportWrapper.js index 224a451e44..8b838c7770 100644 --- a/detox/src/exportWrapper.js +++ b/detox/src/exportWrapper.js @@ -23,6 +23,7 @@ const exportMap = { module.exports = new Proxy(exportMap, { get(map, name) { + console.log(name); return (name === 'device') ? platform.get('device') : map[name][platform.get('name')]; diff --git a/detox/src/index.js b/detox/src/index.js index be843b82c2..0fa706e8c3 100644 --- a/detox/src/index.js +++ b/detox/src/index.js @@ -1,6 +1,6 @@ const Detox = require('./Detox'); -const platform = require('./platform'); -const exportWrapper = require('./exportWrapper'); +// const platform = require('./platform'); +// const exportWrapper = require('./exportWrapper'); const argparse = require('./utils/argparse'); const configuration = require('./configuration'); const _ = require('lodash'); @@ -43,8 +43,8 @@ async function initializeDetox({configurations, session}, params) { const deviceConfig = getDeviceConfig(configurations); detox = new Detox({deviceConfig, session}); - platform.set(deviceConfig.type, detox.device); await detox.init(params); + platform.set(deviceConfig.type, detox.device); } async function init(config, params) { @@ -80,9 +80,85 @@ async function afterEach() { // throw reason; //}); -module.exports = Object.assign({ - init, - cleanup, - beforeEach, - afterEach, -}, exportWrapper); +// console.log(exportWrapper); + +const platform = require('./platform'); +const iosExports = require('./ios/expect'); +const androidExports = require('./android/expect'); + +const exportMap = { + expect: { + ios: iosExports.expect, + android: androidExports.expect, + }, + element: { + ios: iosExports.element, + android: androidExports.element, + }, + waitFor: { + ios: iosExports.waitFor, + android: androidExports.waitFor, + }, + by: { + ios: iosExports.by, + android: androidExports.by, + } +}; + +const a = { + async init(config, params) { + validateConfig(config); + await initializeDetox(config, params); + }, + + async cleanup() { + if (detox) { + await detox.cleanup(); + } + }, + + async beforeEach() { + if (detox) { + await detox.beforeEach.apply(detox, arguments); + } + }, + + async afterEach() { + if (detox) { + await detox.afterEach.apply(detox, arguments); + } + }, + + element() { + return exportMap.element[platform.get('name')].apply(null, arguments); + }, + + expect() { + return exportMap.expect[platform.get('name')].apply(null, arguments); + }, + + waitFor() { + return exportMap.waitFor[platform.get('name')].apply(null, arguments); + }, +}; + +module.exports = new Proxy(a, { + get(map, name) { + console.log(name); + + if (name === 'by') + return exportMap.by[platform.get('name')]; + if (name === 'device') + return platform.get('device'); + + return map[name]; + } +}); + + +// module.exports = Object.assign({ +// init, +// cleanup, +// beforeEach, +// afterEach, +// }, exportWrapper); diff --git a/detox/src/platform.js b/detox/src/platform.js index d49e450340..3e7e1ece51 100644 --- a/detox/src/platform.js +++ b/detox/src/platform.js @@ -9,9 +9,12 @@ class Platform { } set(type, device) { + console.log('set', type.split('.')[0]); this.name = type.split('.')[0]; this.device = device; } } -module.exports = new Platform(); \ No newline at end of file +const platform = new Platform(); + +module.exports = platform; \ No newline at end of file diff --git a/examples/demo-react-native/e2eExportProxy/example.spec.js b/examples/demo-react-native/e2eExportProxy/example.spec.js new file mode 100644 index 0000000000..9cf0bb9b27 --- /dev/null +++ b/examples/demo-react-native/e2eExportProxy/example.spec.js @@ -0,0 +1,34 @@ +const detox = require('detox'); + +// console.log(detox); +// console.log(detox.element); +// console.log(detox.by); +// console.log(detox.expect); +// console.log(detox.device); + + +describe('Example', () => { + beforeEach(async () => { + // await device.reloadReactNative(); + await detox.device.reloadReactNative(); + }); + + it('should have welcome screen', async () => { + // await expect(element(by.id('welcome'))).toBeVisible(); + await detox.expect(detox.element(detox.by.id('welcome'))).toBeVisible(); + }); + + it('should show hello screen after tap', async () => { + // await element(by.id('hello_button')).tap(); + await detox.element(detox.by.id('hello_button')).tap(); + // await expect(element(by.text('Hello!!!'))).toBeVisible(); + await detox.expect(detox.element(detox.by.text('Hello!!!'))).toBeVisible(); + }); + + it('should show world screen after tap', async () => { + // await element(by.id('world_button')).tap(); + await detox.element(detox.by.id('world_button')).tap(); + // await expect(element(by.text('World!!!'))).toBeVisible(); + await detox.expect(detox.element(detox.by.text('World!!!'))).toBeVisible(); + }); +}); \ No newline at end of file diff --git a/examples/demo-react-native/e2eExportProxy/init.js b/examples/demo-react-native/e2eExportProxy/init.js new file mode 100644 index 0000000000..e0764d88a2 --- /dev/null +++ b/examples/demo-react-native/e2eExportProxy/init.js @@ -0,0 +1,11 @@ +const detox = require('detox'); +const config = require('../package.json').detox; + +before(async () => { + await detox.init(config, {initGlobals: false}); + // await detox.init(config); +}); + +after(async () => { + await detox.cleanup(); +}); diff --git a/examples/demo-react-native/e2eExportProxy/mocha.opts b/examples/demo-react-native/e2eExportProxy/mocha.opts new file mode 100644 index 0000000000..99b1114b77 --- /dev/null +++ b/examples/demo-react-native/e2eExportProxy/mocha.opts @@ -0,0 +1,3 @@ +--recursive +--timeout 120000 +--bail \ No newline at end of file diff --git a/examples/demo-react-native/package.json b/examples/demo-react-native/package.json index 1df6c5d470..75699db3d8 100644 --- a/examples/demo-react-native/package.json +++ b/examples/demo-react-native/package.json @@ -15,7 +15,7 @@ }, "detox": { "test-runner": "mocha", - "specs": "e2e", + "specs": "e2eExportProxy", "runner-config": "e2e/mocha.opts", "configurations": { "ios.sim.release": { From e9f768ce30cf7a8d1705c5bf6055b9eee23fe5c2 Mon Sep 17 00:00:00 2001 From: trofima Date: Thu, 7 Dec 2017 18:58:03 +0200 Subject: [PATCH 07/13] fixed default params --- detox/src/Detox.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/detox/src/Detox.js b/detox/src/Detox.js index 3e3dc30540..3006f3687b 100644 --- a/detox/src/Detox.js +++ b/detox/src/Detox.js @@ -40,8 +40,10 @@ class Detox { } } - async init(params = {launchApp: true, initGlobals: true}) { + async init(userParams) { const sessionConfig = await this._getSessionConfig(); + const defaultParams = {launchApp: true, initGlobals: true}; + const params = Object.assign(userParams, defaultParams); if (!this.userSession) { this.server = new DetoxServer(new URL(sessionConfig.server).port); From 4d010847b8e3d55fbf4283ccafaa772e205e01bc Mon Sep 17 00:00:00 2001 From: trofima Date: Thu, 7 Dec 2017 19:03:03 +0200 Subject: [PATCH 08/13] comments cleaned --- detox/src/index.js | 2 ++ detox/src/platform.js | 1 - .../e2eExportProxy/example.spec.js | 24 +++++-------------- .../demo-react-native/e2eExportProxy/init.js | 1 - 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/detox/src/index.js b/detox/src/index.js index 0fa706e8c3..63b7a74841 100644 --- a/detox/src/index.js +++ b/detox/src/index.js @@ -129,6 +129,8 @@ const a = { } }, + //TODO trofima: the same should be done for each method of by and device. + element() { return exportMap.element[platform.get('name')].apply(null, arguments); }, diff --git a/detox/src/platform.js b/detox/src/platform.js index 3e7e1ece51..151215ec4e 100644 --- a/detox/src/platform.js +++ b/detox/src/platform.js @@ -9,7 +9,6 @@ class Platform { } set(type, device) { - console.log('set', type.split('.')[0]); this.name = type.split('.')[0]; this.device = device; } diff --git a/examples/demo-react-native/e2eExportProxy/example.spec.js b/examples/demo-react-native/e2eExportProxy/example.spec.js index 9cf0bb9b27..8aaca0f883 100644 --- a/examples/demo-react-native/e2eExportProxy/example.spec.js +++ b/examples/demo-react-native/e2eExportProxy/example.spec.js @@ -1,34 +1,22 @@ const detox = require('detox'); - -// console.log(detox); -// console.log(detox.element); -// console.log(detox.by); -// console.log(detox.expect); -// console.log(detox.device); - +const {expect, element} = detox; describe('Example', () => { beforeEach(async () => { - // await device.reloadReactNative(); await detox.device.reloadReactNative(); }); it('should have welcome screen', async () => { - // await expect(element(by.id('welcome'))).toBeVisible(); - await detox.expect(detox.element(detox.by.id('welcome'))).toBeVisible(); + await expect(element(detox.by.id('welcome'))).toBeVisible(); }); it('should show hello screen after tap', async () => { - // await element(by.id('hello_button')).tap(); - await detox.element(detox.by.id('hello_button')).tap(); - // await expect(element(by.text('Hello!!!'))).toBeVisible(); - await detox.expect(detox.element(detox.by.text('Hello!!!'))).toBeVisible(); + await element(detox.by.id('hello_button')).tap(); + await expect(element(detox.by.text('Hello!!!'))).toBeVisible(); }); it('should show world screen after tap', async () => { - // await element(by.id('world_button')).tap(); - await detox.element(detox.by.id('world_button')).tap(); - // await expect(element(by.text('World!!!'))).toBeVisible(); - await detox.expect(detox.element(detox.by.text('World!!!'))).toBeVisible(); + await element(detox.by.id('world_button')).tap(); + await expect(element(detox.by.text('World!!!'))).toBeVisible(); }); }); \ No newline at end of file diff --git a/examples/demo-react-native/e2eExportProxy/init.js b/examples/demo-react-native/e2eExportProxy/init.js index e0764d88a2..3638d936a5 100644 --- a/examples/demo-react-native/e2eExportProxy/init.js +++ b/examples/demo-react-native/e2eExportProxy/init.js @@ -3,7 +3,6 @@ const config = require('../package.json').detox; before(async () => { await detox.init(config, {initGlobals: false}); - // await detox.init(config); }); after(async () => { From da315b4c4d6d6691ac00cb8fc9844c9668ed758f Mon Sep 17 00:00:00 2001 From: trofima Date: Fri, 8 Dec 2017 19:23:12 +0200 Subject: [PATCH 09/13] improved proxying of the 'by' and 'device' objects --- detox/src/exportWrapper.js | 37 +++++-- detox/src/index.js | 104 ++---------------- .../e2eExportProxy/example.spec.js | 16 +-- 3 files changed, 45 insertions(+), 112 deletions(-) diff --git a/detox/src/exportWrapper.js b/detox/src/exportWrapper.js index 8b838c7770..8d7fda36ef 100644 --- a/detox/src/exportWrapper.js +++ b/detox/src/exportWrapper.js @@ -21,11 +21,32 @@ const exportMap = { } }; -module.exports = new Proxy(exportMap, { - get(map, name) { - console.log(name); - return (name === 'device') - ? platform.get('device') - : map[name][platform.get('name')]; - } -}); +function applyToPlatformSpecific(name, args) { + return exportMap[name][platform.get('name')].apply(null, args); +} + +module.exports = { + element() { + return applyToPlatformSpecific('element', arguments); + }, + + expect() { + return applyToPlatformSpecific('expect', arguments); + }, + + waitFor() { + return applyToPlatformSpecific('waitFor', arguments); + }, + + by: new Proxy({}, { + get(target, name) { + return exportMap.by[platform.get('name')][name]; + } + }), + + device: new Proxy({}, { + get(target, name) { + return platform.get('device')[name]; + } + }), +}; diff --git a/detox/src/index.js b/detox/src/index.js index 63b7a74841..486de8199e 100644 --- a/detox/src/index.js +++ b/detox/src/index.js @@ -1,6 +1,6 @@ const Detox = require('./Detox'); -// const platform = require('./platform'); -// const exportWrapper = require('./exportWrapper'); +const platform = require('./platform'); +const exportWrapper = require('./exportWrapper'); const argparse = require('./utils/argparse'); const configuration = require('./configuration'); const _ = require('lodash'); @@ -70,97 +70,9 @@ async function afterEach() { } } -//process.on('uncaughtException', (err) => { -// //client.close(); -// -// throw err; -//}); -// -//process.on('unhandledRejection', (reason, p) => { -// throw reason; -//}); - -// console.log(exportWrapper); - -const platform = require('./platform'); -const iosExports = require('./ios/expect'); -const androidExports = require('./android/expect'); - -const exportMap = { - expect: { - ios: iosExports.expect, - android: androidExports.expect, - }, - element: { - ios: iosExports.element, - android: androidExports.element, - }, - waitFor: { - ios: iosExports.waitFor, - android: androidExports.waitFor, - }, - by: { - ios: iosExports.by, - android: androidExports.by, - } -}; - -const a = { - async init(config, params) { - validateConfig(config); - await initializeDetox(config, params); - }, - - async cleanup() { - if (detox) { - await detox.cleanup(); - } - }, - - async beforeEach() { - if (detox) { - await detox.beforeEach.apply(detox, arguments); - } - }, - - async afterEach() { - if (detox) { - await detox.afterEach.apply(detox, arguments); - } - }, - - //TODO trofima: the same should be done for each method of by and device. - - element() { - return exportMap.element[platform.get('name')].apply(null, arguments); - }, - - expect() { - return exportMap.expect[platform.get('name')].apply(null, arguments); - }, - - waitFor() { - return exportMap.waitFor[platform.get('name')].apply(null, arguments); - }, -}; - -module.exports = new Proxy(a, { - get(map, name) { - console.log(name); - - if (name === 'by') - return exportMap.by[platform.get('name')]; - if (name === 'device') - return platform.get('device'); - - return map[name]; - } -}); - - -// module.exports = Object.assign({ -// init, -// cleanup, -// beforeEach, -// afterEach, -// }, exportWrapper); +module.exports = Object.assign({ + init, + cleanup, + beforeEach, + afterEach, +}, exportWrapper); diff --git a/examples/demo-react-native/e2eExportProxy/example.spec.js b/examples/demo-react-native/e2eExportProxy/example.spec.js index 8aaca0f883..6851984429 100644 --- a/examples/demo-react-native/e2eExportProxy/example.spec.js +++ b/examples/demo-react-native/e2eExportProxy/example.spec.js @@ -1,22 +1,22 @@ -const detox = require('detox'); -const {expect, element} = detox; +const {device, expect, element, by, waitFor} = require('detox'); describe('Example', () => { beforeEach(async () => { - await detox.device.reloadReactNative(); + await device.reloadReactNative(); }); it('should have welcome screen', async () => { - await expect(element(detox.by.id('welcome'))).toBeVisible(); + await waitFor(element(by.id('welcome'))).toExist().withTimeout(2000); + await expect(element(by.id('welcome'))).toBeVisible(); }); it('should show hello screen after tap', async () => { - await element(detox.by.id('hello_button')).tap(); - await expect(element(detox.by.text('Hello!!!'))).toBeVisible(); + await element(by.id('hello_button')).tap(); + await expect(element(by.text('Hello!!!'))).toBeVisible(); }); it('should show world screen after tap', async () => { - await element(detox.by.id('world_button')).tap(); - await expect(element(detox.by.text('World!!!'))).toBeVisible(); + await element(by.id('world_button')).tap(); + await expect(element(by.text('World!!!'))).toBeVisible(); }); }); \ No newline at end of file From 8325aff84d5e33e05e16bf0b24e91dfe691d44cb Mon Sep 17 00:00:00 2001 From: trofima Date: Fri, 8 Dec 2017 20:36:02 +0200 Subject: [PATCH 10/13] Detox default params extension fixed, tests for exportWrapper added --- detox/src/Detox.js | 2 +- detox/src/exportWrapper.test.js | 58 ++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/detox/src/Detox.js b/detox/src/Detox.js index 3006f3687b..14a02ad50b 100644 --- a/detox/src/Detox.js +++ b/detox/src/Detox.js @@ -43,7 +43,7 @@ class Detox { async init(userParams) { const sessionConfig = await this._getSessionConfig(); const defaultParams = {launchApp: true, initGlobals: true}; - const params = Object.assign(userParams, defaultParams); + const params = Object.assign(defaultParams, userParams || {}); if (!this.userSession) { this.server = new DetoxServer(new URL(sessionConfig.server).port); diff --git a/detox/src/exportWrapper.test.js b/detox/src/exportWrapper.test.js index 33a938fd7f..5c8b24e7ca 100644 --- a/detox/src/exportWrapper.test.js +++ b/detox/src/exportWrapper.test.js @@ -1,28 +1,56 @@ -const iosExports = require('./ios/expect'); -const androidExports = require('./android/expect'); const exportWrapper = require('./exportWrapper'); const platform = require('./platform'); +jest.mock('./ios/expect'); +jest.mock('./android/expect'); +const iosExports = require('./ios/expect'); +const androidExports = require('./android/expect'); describe('exportWrapper', () => { - const mockDevice = {}; + const mockDevice = {method: jest.fn()}; + + it(`proxies ios specific objects`, async () => { + const arg1 = 1; + const arg2 = 'test'; - it(`exports ios specific objects`, async () => { platform.set('ios.none', mockDevice); + iosExports.by.method = jest.fn(); + + exportWrapper.device.method(arg1, arg2); + expect(mockDevice.method).toHaveBeenCalledWith(arg1, arg2); + + exportWrapper.expect(arg1, arg2); + expect(iosExports.expect).toHaveBeenCalledWith(arg1, arg2); - expect(exportWrapper.device).toBe(mockDevice); - expect(exportWrapper.expect).toBe(iosExports.expect); - expect(exportWrapper.element).toBe(iosExports.element); - expect(exportWrapper.waitFor).toBe(iosExports.waitFor); - expect(exportWrapper.by).toBe(iosExports.by); + exportWrapper.element(arg1, arg2); + expect(iosExports.element).toHaveBeenCalledWith(arg1, arg2); + + exportWrapper.waitFor(arg1, arg2); + expect(iosExports.waitFor).toHaveBeenCalledWith(arg1, arg2); + + exportWrapper.by.method(arg1, arg2); + expect(iosExports.by.method).toHaveBeenCalledWith(arg1, arg2); }); - it(`exports android specific objects`, async () => { + it(`proxies android specific objects`, async () => { + const arg1 = 1; + const arg2 = 'test'; + platform.set('android.attached', mockDevice); + androidExports.by.method = jest.fn(); + + exportWrapper.device.method(arg1, arg2); + expect(mockDevice.method).toHaveBeenCalledWith(arg1, arg2); + + exportWrapper.expect(arg1, arg2); + expect(androidExports.expect).toHaveBeenCalledWith(arg1, arg2); + + exportWrapper.element(arg1, arg2); + expect(androidExports.element).toHaveBeenCalledWith(arg1, arg2); + + exportWrapper.waitFor(arg1, arg2); + expect(androidExports.waitFor).toHaveBeenCalledWith(arg1, arg2); - expect(exportWrapper.device).toBe(mockDevice); - expect(exportWrapper.expect).toBe(androidExports.expect); - expect(exportWrapper.element).toBe(androidExports.element); - expect(exportWrapper.waitFor).toBe(androidExports.waitFor); - expect(exportWrapper.by).toBe(androidExports.by); + exportWrapper.by.method(arg1, arg2); + expect(androidExports.by.method).toHaveBeenCalledWith(arg1, arg2); }); }); From c091a9caebbb19f058c59c337443864375f64d0a Mon Sep 17 00:00:00 2001 From: Rotem M Date: Mon, 11 Dec 2017 12:37:26 +0200 Subject: [PATCH 11/13] - Added support for custom specs with cli tool - Updated e2e tests for demo-react-native --- detox/local-cli/detox-test.js | 17 ++++++++++++++--- .../example.spec.js | 8 ++++++-- .../init.js | 4 ++++ .../mocha.opts | 0 examples/demo-react-native/package.json | 2 +- scripts/demo-projects.ios.sh | 1 + 6 files changed, 26 insertions(+), 6 deletions(-) rename examples/demo-react-native/{e2eExportProxy => e2eExplicitRequire}/example.spec.js (86%) rename examples/demo-react-native/{e2eExportProxy => e2eExplicitRequire}/init.js (55%) rename examples/demo-react-native/{e2eExportProxy => e2eExplicitRequire}/mocha.opts (100%) diff --git a/detox/local-cli/detox-test.js b/detox/local-cli/detox-test.js index 7396d7d500..fac9b7cb0f 100644 --- a/detox/local-cli/detox-test.js +++ b/detox/local-cli/detox-test.js @@ -6,6 +6,8 @@ const cp = require('child_process'); program .option('-o, --runner-config [config]', `Test runner config file, defaults to e2e/mocha.opts for mocha and e2e/config.json' for jest`) + .option('-s, --specs [relativePath]', + `Root of test folder`) .option('-l, --loglevel [value]', 'info, debug, verbose, silly, wss') .option('-c, --configuration [device configuration]', @@ -26,14 +28,23 @@ program const config = require(path.join(process.cwd(), 'package.json')).detox; -const testFolder = config.specs || 'e2e'; -const runner = config['test-runner'] || 'mocha'; -const runnerConfig = program.runnerConfig || config['runner-config'] || getDefaultRunnerConfig(); +const testFolder = getConfigFor('specs', 'e2e'); +const runner = getConfigFor('testRunner', 'mocha'); +const runnerConfig = getConfigFor('runnerConfig', getDefaultRunnerConfig()); if (typeof program.debugSynchronization === "boolean") { program.debugSynchronization = 3000; } +function getConfigFor(key, defaults) { + const keyKebabCase = camelToKebabCase(key); + return program[key] || config[key] || config[keyKebabCase] || defaults; +} + +function camelToKebabCase(string) { + return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); +} + switch (runner) { case 'mocha': runMocha(); diff --git a/examples/demo-react-native/e2eExportProxy/example.spec.js b/examples/demo-react-native/e2eExplicitRequire/example.spec.js similarity index 86% rename from examples/demo-react-native/e2eExportProxy/example.spec.js rename to examples/demo-react-native/e2eExplicitRequire/example.spec.js index 6851984429..c822777fa3 100644 --- a/examples/demo-react-native/e2eExportProxy/example.spec.js +++ b/examples/demo-react-native/e2eExplicitRequire/example.spec.js @@ -6,7 +6,6 @@ describe('Example', () => { }); it('should have welcome screen', async () => { - await waitFor(element(by.id('welcome'))).toExist().withTimeout(2000); await expect(element(by.id('welcome'))).toBeVisible(); }); @@ -19,4 +18,9 @@ describe('Example', () => { await element(by.id('world_button')).tap(); await expect(element(by.text('World!!!'))).toBeVisible(); }); -}); \ No newline at end of file + + it('waitFor should be exported', async () => { + await waitFor(element(by.id('welcome'))).toExist().withTimeout(2000); + await expect(element(by.id('welcome'))).toExist(); + }); +}); diff --git a/examples/demo-react-native/e2eExportProxy/init.js b/examples/demo-react-native/e2eExplicitRequire/init.js similarity index 55% rename from examples/demo-react-native/e2eExportProxy/init.js rename to examples/demo-react-native/e2eExplicitRequire/init.js index 3638d936a5..9ff09dc1e3 100644 --- a/examples/demo-react-native/e2eExportProxy/init.js +++ b/examples/demo-react-native/e2eExplicitRequire/init.js @@ -1,6 +1,10 @@ const detox = require('detox'); const config = require('../package.json').detox; +/* +Example showing how to use Detox with required objects rather than globally exported. +e.g `const {device, expect, element, by, waitFor} = require('detox');` + */ before(async () => { await detox.init(config, {initGlobals: false}); }); diff --git a/examples/demo-react-native/e2eExportProxy/mocha.opts b/examples/demo-react-native/e2eExplicitRequire/mocha.opts similarity index 100% rename from examples/demo-react-native/e2eExportProxy/mocha.opts rename to examples/demo-react-native/e2eExplicitRequire/mocha.opts diff --git a/examples/demo-react-native/package.json b/examples/demo-react-native/package.json index 75699db3d8..1df6c5d470 100644 --- a/examples/demo-react-native/package.json +++ b/examples/demo-react-native/package.json @@ -15,7 +15,7 @@ }, "detox": { "test-runner": "mocha", - "specs": "e2eExportProxy", + "specs": "e2e", "runner-config": "e2e/mocha.opts", "configurations": { "ios.sim.release": { diff --git a/scripts/demo-projects.ios.sh b/scripts/demo-projects.ios.sh index e5b4409007..5518ab7399 100755 --- a/scripts/demo-projects.ios.sh +++ b/scripts/demo-projects.ios.sh @@ -7,6 +7,7 @@ run_f "$(dirname "$0")/bootstrap.sh" pushd examples/demo-react-native run_f "detox build -c ios.sim.release" run_f "detox test -c ios.sim.release" +run_f "detox test -c ios.sim.release -specs e2eExplicitRequire --runner-config e2eExplicitRequire/mocha.opts" popd pushd examples/demo-react-native-jest From 85f45c0181cb233c2d752e00dcf12fb31237b353 Mon Sep 17 00:00:00 2001 From: trofima Date: Thu, 11 Jan 2018 18:06:22 +0200 Subject: [PATCH 12/13] exportWrapper tests, mock fixed. --- detox/src/exportWrapper.test.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/detox/src/exportWrapper.test.js b/detox/src/exportWrapper.test.js index 5c8b24e7ca..8d1193d4db 100644 --- a/detox/src/exportWrapper.test.js +++ b/detox/src/exportWrapper.test.js @@ -1,13 +1,25 @@ -const exportWrapper = require('./exportWrapper'); -const platform = require('./platform'); -jest.mock('./ios/expect'); -jest.mock('./android/expect'); -const iosExports = require('./ios/expect'); -const androidExports = require('./android/expect'); +let exportWrapper; +let platform; +let iosExports; +let androidExports; describe('exportWrapper', () => { const mockDevice = {method: jest.fn()}; + beforeAll(async() => { + jest.mock('./ios/expect'); + jest.mock('./android/expect'); + exportWrapper = require('./exportWrapper'); + platform = require('./platform'); + iosExports = require('./ios/expect'); + androidExports = require('./android/expect'); + }); + + afterAll(async() => { + jest.unmock('./ios/expect'); + jest.unmock('./android/expect'); + }); + it(`proxies ios specific objects`, async () => { const arg1 = 1; const arg2 = 'test'; From 2e4132d7f26e75d9f797655a7bb865ad57bd3f1a Mon Sep 17 00:00:00 2001 From: Rotem M Date: Fri, 26 Jan 2018 01:28:10 +0200 Subject: [PATCH 13/13] fixed broken command --- scripts/demo-projects.ios.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/demo-projects.ios.sh b/scripts/demo-projects.ios.sh index 5518ab7399..d455cca735 100755 --- a/scripts/demo-projects.ios.sh +++ b/scripts/demo-projects.ios.sh @@ -7,7 +7,7 @@ run_f "$(dirname "$0")/bootstrap.sh" pushd examples/demo-react-native run_f "detox build -c ios.sim.release" run_f "detox test -c ios.sim.release" -run_f "detox test -c ios.sim.release -specs e2eExplicitRequire --runner-config e2eExplicitRequire/mocha.opts" +run_f "detox test -c ios.sim.release --specs e2eExplicitRequire --runner-config e2eExplicitRequire/mocha.opts" popd pushd examples/demo-react-native-jest