From 4f9d94d5ccd20f957e9810bcb0f837ac7d6610a4 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Sun, 21 May 2017 08:53:59 +0200 Subject: [PATCH 1/9] add orientation change screen to test app --- detox/test/index.ios.js | 1 + detox/test/src/Screens/Orientation.js | 33 +++++++++++++++++++++++++++ detox/test/src/Screens/index.js | 4 +++- 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 detox/test/src/Screens/Orientation.js diff --git a/detox/test/index.ios.js b/detox/test/index.ios.js index f779e37f25..812eeb6b38 100644 --- a/detox/test/index.ios.js +++ b/detox/test/index.ios.js @@ -69,6 +69,7 @@ class example extends Component { {this.renderScreenButton('Stress', Screens.StressScreen)} {this.renderScreenButton('Switch Root', Screens.SwitchRootScreen)} {this.renderScreenButton('Timeouts', Screens.TimeoutsScreen)} + {this.renderScreenButton('Orientation', Screens.Orientation)} ); } diff --git a/detox/test/src/Screens/Orientation.js b/detox/test/src/Screens/Orientation.js new file mode 100644 index 0000000000..9d91402679 --- /dev/null +++ b/detox/test/src/Screens/Orientation.js @@ -0,0 +1,33 @@ +import React, { Component } from 'react'; +import { + Text, + View, + TouchableOpacity +} from 'react-native'; + +export default class Orientation extends Component { + + constructor(props) { + super(props); + this.state = { + horizontal: false + }; + console.log('Orientation react component constructed (console.log test)'); + } + + detectHorizontal({nativeEvent: {layout: {width, height,x,y}}}) { + this.setState({ + horizontal: width > height + }); + } + + render() { + return ( + + + {this.state.horizontal ? 'Landscape' : 'Portrait'} + + + ); + } +} diff --git a/detox/test/src/Screens/index.js b/detox/test/src/Screens/index.js index 1d41a38755..a7989d6cff 100644 --- a/detox/test/src/Screens/index.js +++ b/detox/test/src/Screens/index.js @@ -6,6 +6,7 @@ import WaitForScreen from './WaitForScreen'; import StressScreen from './StressScreen'; import SwitchRootScreen from './SwitchRootScreen'; import TimeoutsScreen from './TimeoutsScreen'; +import Orientation from './Orientation'; export { SanityScreen, @@ -15,5 +16,6 @@ export { WaitForScreen, StressScreen, SwitchRootScreen, - TimeoutsScreen + TimeoutsScreen, + Orientation }; From 1cfc199a1ee51d1ab9016efb687df8da61255cde Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Sun, 21 May 2017 08:56:29 +0200 Subject: [PATCH 2/9] add orientation change e2e test --- detox/test/e2e/f-simulator.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/detox/test/e2e/f-simulator.js b/detox/test/e2e/f-simulator.js index 51066bbeb7..b150fbc99b 100644 --- a/detox/test/e2e/f-simulator.js +++ b/detox/test/e2e/f-simulator.js @@ -28,4 +28,28 @@ describe('Simulator', () => { await element(by.label('Say Hello')).tap(); await expect(element(by.label('Hello!!!'))).toBeVisible(); }); + + describe('device orientation', () => { + beforeEach(async() => { + await device.reloadReactNative(); + await element(by.label('Orientation')).tap(); + + // Check if the element whichs input we will test actually exists + await expect(element(by.id('currentOrientation'))).toExist(); + }); + + it('OrientationLandscape', async () => { + await device.setOrientation('landscape'); + + await expect(element(by.id('currentOrientation'))).toHaveText('Landscape'); + }); + + it('OrientationPortrait', async() => { + // As default is portrait we need to set it otherwise + await device.setOrientation('landscape'); + await device.setOrientation('portrait'); + + await expect(element(by.id('currentOrientation'))).toHaveText('Portrait'); + }); + }); }); From e37a4296d77260e5ddbda2eba76341c5f8bd22a7 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Wed, 24 May 2017 00:28:16 +0200 Subject: [PATCH 3/9] add UIDeviceOrientation type for method invocation --- detox/ios/Detox/MethodInvocation.m | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/detox/ios/Detox/MethodInvocation.m b/detox/ios/Detox/MethodInvocation.m index 6eb828c512..6ff920f2a7 100644 --- a/detox/ios/Detox/MethodInvocation.m +++ b/detox/ios/Detox/MethodInvocation.m @@ -80,6 +80,24 @@ + (id) getValue:(id)value withType:(id)type onError:(void (^)(NSString*))onError if (![value isKindOfClass:[NSDictionary class]]) return nil; return [MethodInvocation invoke:value onError:onError]; } + if ([type isEqualToString:@"UIDeviceOrientation"]) + { + if (value == nil) { + return nil; + } + + if ([value isEqualToString:@"landscape"]) + { + return [NSNumber numberWithLong:UIDeviceOrientationLandscapeLeft]; + } + + if ([value isEqualToString:@"portrait"]) + { + return [NSNumber numberWithLong:UIDeviceOrientationPortrait]; + } + + return nil; + } return nil; } From 78111e45273dab50746ef7e90e0363adde53bf6c Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Wed, 24 May 2017 00:31:51 +0200 Subject: [PATCH 4/9] add setOrientation to device Closes #131 --- detox/src/devices/Device.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/detox/src/devices/Device.js b/detox/src/devices/Device.js index a094a35076..69c077863b 100644 --- a/detox/src/devices/Device.js +++ b/detox/src/devices/Device.js @@ -1,6 +1,7 @@ const fs = require('fs'); const _ = require('lodash'); const argparse = require('../utils/argparse'); +const invoke = require('../invoke'); class Device { constructor(client, session, deviceConfig) { @@ -42,6 +43,16 @@ class Device { await this.client.sendUserNotification(params); } + async setOrientation(orientation) { + // orientation is 'landscape' (meaning left side portrait) or 'portrait' (non-reversed) + const call = invoke.call( + invoke.IOS.EarlGrey(''), + 'rotateDeviceToOrientation:errorOrNil:', + invoke.IOS.UIDeviceOrientation(orientation) + ); + await new invoke.InvocationManager(this.client).execute(call); + } + async shutdown() { return await Promise.resolve(''); } From 7bc9b52ef172ca0f68326f216c6e52ec0032214e Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Thu, 25 May 2017 20:03:01 +0200 Subject: [PATCH 5/9] refactor earl grey call and orientation constants --- detox/ios/Detox/MethodInvocation.m | 18 ------------------ detox/src/devices/Device.js | 15 +++++++++++---- detox/src/invoke/EarlGrey.js | 14 +++++++++++++- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/detox/ios/Detox/MethodInvocation.m b/detox/ios/Detox/MethodInvocation.m index 6ff920f2a7..6eb828c512 100644 --- a/detox/ios/Detox/MethodInvocation.m +++ b/detox/ios/Detox/MethodInvocation.m @@ -80,24 +80,6 @@ + (id) getValue:(id)value withType:(id)type onError:(void (^)(NSString*))onError if (![value isKindOfClass:[NSDictionary class]]) return nil; return [MethodInvocation invoke:value onError:onError]; } - if ([type isEqualToString:@"UIDeviceOrientation"]) - { - if (value == nil) { - return nil; - } - - if ([value isEqualToString:@"landscape"]) - { - return [NSNumber numberWithLong:UIDeviceOrientationLandscapeLeft]; - } - - if ([value isEqualToString:@"portrait"]) - { - return [NSNumber numberWithLong:UIDeviceOrientationPortrait]; - } - - return nil; - } return nil; } diff --git a/detox/src/devices/Device.js b/detox/src/devices/Device.js index 69c077863b..3841e7828e 100644 --- a/detox/src/devices/Device.js +++ b/detox/src/devices/Device.js @@ -44,11 +44,18 @@ class Device { } async setOrientation(orientation) { - // orientation is 'landscape' (meaning left side portrait) or 'portrait' (non-reversed) - const call = invoke.call( - invoke.IOS.EarlGrey(''), + // keys are possible orientations + const orientationMapping = { + landscape: 3, // top at left side landscape + portrait: 1 // non-reversed portrait + }; + if (!Object.keys(orientationMapping).includes(orientation)) { + throw new Error(`setOrientation failed: provided orientation ${orientation} is not part of supported orientations: ${Object.keys(orientationMapping)}`) + } + + const call = invoke.EarlGrey.call( 'rotateDeviceToOrientation:errorOrNil:', - invoke.IOS.UIDeviceOrientation(orientation) + invoke.IOS.NSInteger(orientationMapping[orientation]) ); await new invoke.InvocationManager(this.client).execute(call); } diff --git a/detox/src/invoke/EarlGrey.js b/detox/src/invoke/EarlGrey.js index ac893145fc..afef185146 100644 --- a/detox/src/invoke/EarlGrey.js +++ b/detox/src/invoke/EarlGrey.js @@ -3,6 +3,18 @@ const instance = { value: 'instance' }; +function call(method, ...args) { + return { + target: { + type: 'EarlGrey', + value: true, + }, + method: method, + args: args + }; +}; + module.exports = { - instance + instance, + call }; From d0c6f3bbe846c332a02c2f2b50924bbfcc182928 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Thu, 25 May 2017 20:42:15 +0200 Subject: [PATCH 6/9] move setOrientation implementation to simulator --- detox/src/devices/Device.js | 18 ------------------ detox/src/devices/Simulator.js | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/detox/src/devices/Device.js b/detox/src/devices/Device.js index 3841e7828e..a094a35076 100644 --- a/detox/src/devices/Device.js +++ b/detox/src/devices/Device.js @@ -1,7 +1,6 @@ const fs = require('fs'); const _ = require('lodash'); const argparse = require('../utils/argparse'); -const invoke = require('../invoke'); class Device { constructor(client, session, deviceConfig) { @@ -43,23 +42,6 @@ class Device { await this.client.sendUserNotification(params); } - async setOrientation(orientation) { - // keys are possible orientations - const orientationMapping = { - landscape: 3, // top at left side landscape - portrait: 1 // non-reversed portrait - }; - if (!Object.keys(orientationMapping).includes(orientation)) { - throw new Error(`setOrientation failed: provided orientation ${orientation} is not part of supported orientations: ${Object.keys(orientationMapping)}`) - } - - const call = invoke.EarlGrey.call( - 'rotateDeviceToOrientation:errorOrNil:', - invoke.IOS.NSInteger(orientationMapping[orientation]) - ); - await new invoke.InvocationManager(this.client).execute(call); - } - async shutdown() { return await Promise.resolve(''); } diff --git a/detox/src/devices/Simulator.js b/detox/src/devices/Simulator.js index 4d1b2f7aef..718c38e82c 100644 --- a/detox/src/devices/Simulator.js +++ b/detox/src/devices/Simulator.js @@ -7,6 +7,7 @@ const IosNoneDevice = require('./IosNoneDevice'); const FBsimctl = require('./Fbsimctl'); const configuration = require('../configuration'); const argparse = require('../utils/argparse'); +const invoke = require('../invoke'); class Simulator extends IosNoneDevice { @@ -97,6 +98,23 @@ class Simulator extends IosNoneDevice { async shutdown() { await this._fbsimctl.shutdown(this._simulatorUdid); } + + async setOrientation(orientation) { + // keys are possible orientations + const orientationMapping = { + landscape: 3, // top at left side landscape + portrait: 1 // non-reversed portrait + }; + if (!Object.keys(orientationMapping).includes(orientation)) { + throw new Error(`setOrientation failed: provided orientation ${orientation} is not part of supported orientations: ${Object.keys(orientationMapping)}`) + } + + const call = invoke.EarlGrey.call( + 'rotateDeviceToOrientation:errorOrNil:', + invoke.IOS.NSInteger(orientationMapping[orientation]) + ); + await new invoke.InvocationManager(this.client).execute(call); + } } module.exports = Simulator; From 41b24d93bf78ba171b728598128f32ba36389290 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Thu, 25 May 2017 21:36:48 +0200 Subject: [PATCH 7/9] add unit tests for orientation change --- detox/src/devices/Simulator.test.js | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/detox/src/devices/Simulator.test.js b/detox/src/devices/Simulator.test.js index aa8d4a9f2a..88d4313d4b 100644 --- a/detox/src/devices/Simulator.test.js +++ b/detox/src/devices/Simulator.test.js @@ -187,6 +187,39 @@ describe('Simulator', () => { await simulator.openURL(url); expect(simulator._fbsimctl.open).toHaveBeenCalledWith(simulator._simulatorUdid, url); }); + + it(`setOrientation() should throw an error if give wrong input `, async() => { + expect.assertions(1); + simulator = validSimulator(); + + try { + await simulator.setOrientation('UpsideDown'); + } catch(e) { + expect(e.message).toMatch('setOrientation failed: provided orientation UpsideDown is not part of supported orientations: landscape,portrait'); + } + }); + + it(`setOrientation() should set the orientation to portrait`, async() => { + simulator = validSimulator(); + + await simulator.setOrientation('portrait'); + expect(client.execute).toHaveBeenCalled(); + const call = client.execute.mock.calls[client.execute.mock.calls.length - 1][0] + expect(call.target.type).toBe('EarlGrey'); + expect(call.method).toBe('rotateDeviceToOrientation:errorOrNil:'); + expect(call.args[0].value).toBe(1); + }); + + it(`setOrientation() should set the orientation to landscape`, async() => { + simulator = validSimulator(); + + await simulator.setOrientation('landscape'); + expect(client.execute).toHaveBeenCalled(); + const call = client.execute.mock.calls[client.execute.mock.calls.length - 1][0] + expect(call.target.type).toBe('EarlGrey'); + expect(call.method).toBe('rotateDeviceToOrientation:errorOrNil:'); + expect(call.args[0].value).toBe(3); + }); }); const notification = { From 582103fd7cc524f5e554df120dd01a9896aa6d77 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Thu, 25 May 2017 22:36:34 +0200 Subject: [PATCH 8/9] refactor call to EarlGrey --- detox/src/devices/Simulator.js | 2 +- detox/src/devices/Simulator.test.js | 4 ++-- detox/src/invoke/EarlGrey.js | 14 +------------- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/detox/src/devices/Simulator.js b/detox/src/devices/Simulator.js index 718c38e82c..e7ac3b6b4f 100644 --- a/detox/src/devices/Simulator.js +++ b/detox/src/devices/Simulator.js @@ -109,7 +109,7 @@ class Simulator extends IosNoneDevice { throw new Error(`setOrientation failed: provided orientation ${orientation} is not part of supported orientations: ${Object.keys(orientationMapping)}`) } - const call = invoke.EarlGrey.call( + const call = invoke.call(invoke.EarlGrey.instance, 'rotateDeviceToOrientation:errorOrNil:', invoke.IOS.NSInteger(orientationMapping[orientation]) ); diff --git a/detox/src/devices/Simulator.test.js b/detox/src/devices/Simulator.test.js index 88d4313d4b..e47015a558 100644 --- a/detox/src/devices/Simulator.test.js +++ b/detox/src/devices/Simulator.test.js @@ -204,7 +204,7 @@ describe('Simulator', () => { await simulator.setOrientation('portrait'); expect(client.execute).toHaveBeenCalled(); - const call = client.execute.mock.calls[client.execute.mock.calls.length - 1][0] + const call = client.execute.mock.calls[client.execute.mock.calls.length - 1][0](); expect(call.target.type).toBe('EarlGrey'); expect(call.method).toBe('rotateDeviceToOrientation:errorOrNil:'); expect(call.args[0].value).toBe(1); @@ -215,7 +215,7 @@ describe('Simulator', () => { await simulator.setOrientation('landscape'); expect(client.execute).toHaveBeenCalled(); - const call = client.execute.mock.calls[client.execute.mock.calls.length - 1][0] + const call = client.execute.mock.calls[client.execute.mock.calls.length - 1][0](); expect(call.target.type).toBe('EarlGrey'); expect(call.method).toBe('rotateDeviceToOrientation:errorOrNil:'); expect(call.args[0].value).toBe(3); diff --git a/detox/src/invoke/EarlGrey.js b/detox/src/invoke/EarlGrey.js index afef185146..ac893145fc 100644 --- a/detox/src/invoke/EarlGrey.js +++ b/detox/src/invoke/EarlGrey.js @@ -3,18 +3,6 @@ const instance = { value: 'instance' }; -function call(method, ...args) { - return { - target: { - type: 'EarlGrey', - value: true, - }, - method: method, - args: args - }; -}; - module.exports = { - instance, - call + instance }; From 8590f44761548d849ac130ccdbf9f471270014c2 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Thu, 25 May 2017 22:36:53 +0200 Subject: [PATCH 9/9] document new api surface for orientation change --- docs/APIRef.DeviceObjectAPI.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/APIRef.DeviceObjectAPI.md b/docs/APIRef.DeviceObjectAPI.md index 91ef485b6b..9d312e45e1 100644 --- a/docs/APIRef.DeviceObjectAPI.md +++ b/docs/APIRef.DeviceObjectAPI.md @@ -29,3 +29,7 @@ Uninstall the app defined in the current [`configuration`](APIRef.Configuration. ### `device.sendUserNotification(params)` + +### `device.setOrientation(orientation)` +Takes `"portrait"` or `"landscape"` and rotates the device to the given orientation. +Currently only available in the iOS Simulator.