Hi, when I call await device.setOrientation('landscape'); on an iPad simulator in portrait mode, nothing happens.
^17.9.0)Rotate Device Automatically for the simulatorHowever, it works properly on iPhone simulators; I've tried both the iPhone 11 and iPhone 8; using the same code.
For me it happens on a iPad Pro (9.7-inch) simulator with iOS 14.
iPad Pro (9.7-inch) and OS version iOS 14.0await device.setOrientation('landscape');The same test will make the device rotate on my iPhone simulators.
^17.9.00.61.5v10.21.0iPad Pro (9.7-inch)12.0.114.010.15.7--loglevel trace argument and am providing the verbose log below:With this simple test:
it(`rotates to landscape`, async () => {
await device.setOrientation("landscape");
})
The iPad did not rotate and this is the verbose logs:
Show verbose logs
detox[338] INFO: [test.js] configuration="ios.sim.debug" loglevel="trace" deviceName="iPad Pro (9.7-inch)" useCustomLogger=true DETOX_START_TIMESTAMP=1602858516867 reportSpecs=true jest --config e2e/screenshots/detox-runner-config.json --testNamePattern '^((?!:android:).)*$' --maxWorkers 1 e2e/screenshots/screenshots.e2e.ts
detox[339] TRACE: [Detox.js/DETOX_CREATE] created a Detox instance with config:
{"artifactsConfig":{"rootDir":"e2e/artifacts/ios.sim.debug.2020-10-16 14-28-36Z","plugins":{"log":{"enabled":false,"keepOnlyFailedTestsArtifacts":false},"screenshot":{"enabled":true,"shouldTakeAutomaticSnapshots":false,"keepOnlyFailedTestsArtifacts":false},"video":{"enabled":false,"keepOnlyFailedTestsArtifacts":false},"instruments":{"enabled":false,"keepOnlyFailedTestsArtifacts":false},"timeline":{"enabled":false}},"pathBuilder":{"_rootDir":"e2e/artifacts/ios.sim.debug.2020-10-16 14-28-36Z"}},"behaviorConfig":{"init":{"reinstallApp":true,"exposeGlobals":true,"launchApp":true},"cleanup":{"shutdownDevice":false}},"cliConfig":{"configuration":"ios.sim.debug","deviceName":"iPad Pro (9.7-inch)","loglevel":"trace","useCustomLogger":"true"},"deviceConfig":{"type":"ios.simulator","binaryPath":"ios/build/Build/Products/Debug-iphonesimulator/example.app","build":"ENVFILE=.env.test xcodebuild -workspace ios/example.xcworkspace -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build","device":"iPad Pro (9.7-inch)"},"runnerConfig":{"testRunner":"jest","runnerConfig":"e2e/detox-runner-config.json","specs":"e2e"},"sessionConfig":{"autoStart":true,"server":"ws://localhost:55703","sessionId":"8475fab5-80f8-7ed3-d22b-56e874375ca4"},"errorBuilder":{"filepath":"/Users/developer/Projects/example/.detoxrc.json","contents":{"testRunner":"jest","runnerConfig":"e2e/detox-runner-config.json","artifacts":{"rootDir":"e2e/artifacts"},"configurations":{"ios.none.debug":{"type":"ios.none","binaryPath":"ios/build/Build/Products/Debug-iphonesimulator/example.app","build":"ENVFILE=.env.test xcodebuild -workspace ios/example.xcworkspace -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build","device":{"type":"Unspecified. Use `detox test --device-name <your_device>` instead."},"session":{"server":"ws://localhost:8099","sessionId":"com.wix.demo.react.native"}},"ios.sim.debug":{"type":"ios.simulator","binaryPath":"ios/build/Build/Products/Debug-iphonesimulator/example.app","build":"ENVFILE=.env.test xcodebuild -workspace ios/example.xcworkspace -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build","device":"iPad Pro (9.7-inch)"},"ios.sim.release":{"type":"ios.simulator","binaryPath":"ios/build/Build/Products/Release-iphonesimulator/example.app","build":"ENVFILE=.env.test RCT_NO_LAUNCH_PACKAGER=true xcodebuild -workspace ios/example.xcworkspace -scheme example -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet","device":{"type":"Unspecified. Use `detox test --device-name <your_device>` instead."}},"android.emu.debug":{"type":"android.emulator","binaryPath":"android/app/build/outputs/apk/debug/app-debug.apk","build":"cd android && ENVFILE=.env.test ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..","device":{"avdName":"Unspecified. Use `detox test --device-name <your_device>` instead."}},"android.emu.release":{"type":"android.emulator","binaryPath":"android/app/build/outputs/apk/release/app-release.apk","build":"cd android && ENVFILE=.env.test ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..","device":{"avdName":"Unspecified. Use `detox test --device-name <your_device>` instead."}},"android.attach.debug":{"type":"android.attached","binaryPath":"android/app/build/outputs/apk/debug/app-debug.apk","build":"cd android && ENVFILE=.env.test ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..","device":{"adbName":"Unspecified. Use `detox test --device-name <your_device>` instead."}},"android.attach.release":{"type":"android.attached","binaryPath":"android/app/build/outputs/apk/release/app-release.apk","build":"cd android && ENVFILE=.env.test ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..","device":{"adbName":"Unspecified. Use `detox test --device-name <your_device>` instead."}}}},"configurationName":"ios.sim.debug"}}
detox[339] INFO: [DetoxServer.js] server listening on localhost:55703...
detox[339] DEBUG: [AsyncWebSocket.js/WEBSOCKET_OPEN] opened web socket to: ws://localhost:55703
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_SEND] {"type":"login","params":{"sessionId":"8475fab5-80f8-7ed3-d22b-56e874375ca4","role":"tester"},"messageId":0}
detox[339] DEBUG: [DetoxServer.js/LOGIN] role=tester, sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4
detox[339] DEBUG: [DetoxServer.js/LOGIN_SUCCESS] role=tester, sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_MESSAGE] {"type":"loginSuccess","params":{"sessionId":"8475fab5-80f8-7ed3-d22b-56e874375ca4","role":"tester"},"messageId":0}
detox[339] DEBUG: [exec.js/EXEC_CMD, #0] applesimutils --list --byType "iPad Pro (9.7-inch)"
detox[339] TRACE: [exec.js/EXEC_SUCCESS, #0] [
{
"os" : {
"bundlePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime",
"buildversion" : "18A372",
"runtimeRoot" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot",
"identifier" : "com.apple.CoreSimulator.SimRuntime.iOS-14-0",
"version" : "14.0",
"isAvailable" : true,
"name" : "iOS 14.0"
},
"dataPath" : "\/Users\/developer\/Library\/Developer\/CoreSimulator\/Devices\/8B143808-BAEB-4918-A88B-0C466F714379\/data",
"logPath" : "\/Users\/developer\/Library\/Logs\/CoreSimulator\/8B143808-BAEB-4918-A88B-0C466F714379",
"udid" : "8B143808-BAEB-4918-A88B-0C466F714379",
"isAvailable" : true,
"deviceType" : {
"minRuntimeVersion" : 655360,
"bundlePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/DeviceTypes\/iPad Pro (9.7-inch).simdevicetype",
"maxRuntimeVersion" : 4294967295,
"name" : "iPad Pro (9.7-inch)",
"identifier" : "com.apple.CoreSimulator.SimDeviceType.iPad-Pro--9-7-inch-",
"productFamily" : "iPad"
},
"deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPad-Pro--9-7-inch-",
"state" : "Booted",
"name" : "iPad Pro (9.7-inch)"
}
]
detox[339] DEBUG: [exec.js/EXEC_CMD, #1] applesimutils --list --byId 8B143808-BAEB-4918-A88B-0C466F714379 --maxResults 1
detox[339] TRACE: [exec.js/EXEC_SUCCESS, #1] [
{
"os" : {
"bundlePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime",
"buildversion" : "18A372",
"runtimeRoot" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot",
"identifier" : "com.apple.CoreSimulator.SimRuntime.iOS-14-0",
"version" : "14.0",
"isAvailable" : true,
"name" : "iOS 14.0"
},
"dataPath" : "\/Users\/developer\/Library\/Developer\/CoreSimulator\/Devices\/8B143808-BAEB-4918-A88B-0C466F714379\/data",
"logPath" : "\/Users\/developer\/Library\/Logs\/CoreSimulator\/8B143808-BAEB-4918-A88B-0C466F714379",
"udid" : "8B143808-BAEB-4918-A88B-0C466F714379",
"isAvailable" : true,
"deviceType" : {
"minRuntimeVersion" : 655360,
"bundlePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/DeviceTypes\/iPad Pro (9.7-inch).simdevicetype",
"maxRuntimeVersion" : 4294967295,
"name" : "iPad Pro (9.7-inch)",
"identifier" : "com.apple.CoreSimulator.SimDeviceType.iPad-Pro--9-7-inch-",
"productFamily" : "iPad"
},
"deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPad-Pro--9-7-inch-",
"state" : "Booted",
"name" : "iPad Pro (9.7-inch)"
}
]
detox[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onBootDevice({ coldBoot: false,
deviceId: '8B143808-BAEB-4918-A88B-0C466F714379',
type: 'iPad Pro (9.7-inch)' })
detox[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onBeforeUninstallApp({ deviceId: '8B143808-BAEB-4918-A88B-0C466F714379',
bundleId: 'com.my.example' })
detox[339] DEBUG: [exec.js/EXEC_CMD, #2] /usr/bin/xcrun simctl uninstall 8B143808-BAEB-4918-A88B-0C466F714379 com.my.example
detox[339] DEBUG: [exec.js/EXEC_TRY, #2] Uninstalling com.my.example...
detox[339] TRACE: [exec.js/EXEC_SUCCESS, #2]
detox[339] DEBUG: [exec.js/EXEC_SUCCESS, #2] com.my.example uninstalled
detox[339] DEBUG: [exec.js/EXEC_CMD, #3] /usr/bin/xcrun simctl install 8B143808-BAEB-4918-A88B-0C466F714379 "/Users/developer/Projects/example/ios/build/Build/Products/Debug-iphonesimulator/example.app"
detox[339] DEBUG: [exec.js/EXEC_TRY, #3] Installing /Users/developer/Projects/example/ios/build/Build/Products/Debug-iphonesimulator/example.app...
detox[339] TRACE: [exec.js/EXEC_SUCCESS, #3]
detox[339] DEBUG: [exec.js/EXEC_SUCCESS, #3] /Users/developer/Projects/example/ios/build/Build/Products/Debug-iphonesimulator/example.app installed
detox[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onBeforeTerminateApp({ deviceId: '8B143808-BAEB-4918-A88B-0C466F714379',
bundleId: 'com.my.example' })
detox[339] DEBUG: [exec.js/EXEC_CMD, #4] /usr/bin/xcrun simctl terminate 8B143808-BAEB-4918-A88B-0C466F714379 com.my.example
detox[339] DEBUG: [exec.js/EXEC_TRY, #4] Terminating com.my.example...
detox[339] TRACE: [exec.js/EXEC_SUCCESS, #4]
detox[339] DEBUG: [exec.js/EXEC_SUCCESS, #4] com.my.example terminated
detox[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onTerminateApp({ deviceId: '8B143808-BAEB-4918-A88B-0C466F714379',
bundleId: 'com.my.example' })
detox[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onBeforeLaunchApp({ bundleId: 'com.my.example',
deviceId: '8B143808-BAEB-4918-A88B-0C466F714379',
launchArgs:
{ detoxServer: 'ws://localhost:55703',
detoxSessionId: '8475fab5-80f8-7ed3-d22b-56e874375ca4' } })
detox[339] DEBUG: [exec.js/EXEC_CMD, #5] SIMCTL_CHILD_DYLD_INSERT_LIBRARIES="/Users/developer/Library/Detox/ios/a451d7d3b3551a7a98863cacab4292ad755ec9af/Detox.framework/Detox" /usr/bin/xcrun simctl launch 8B143808-BAEB-4918-A88B-0C466F714379 com.my.example --args -detoxServer "ws://localhost:55703" -detoxSessionId "8475fab5-80f8-7ed3-d22b-56e874375ca4" -detoxDisableHierarchyDump "true"
detox[339] DEBUG: [exec.js/EXEC_TRY, #5] Launching com.my.example...
detox[339] TRACE: [exec.js/EXEC_SUCCESS, #5] com.my.example: 423
detox[339] DEBUG: [exec.js/EXEC_CMD, #6] /usr/bin/xcrun simctl get_app_container 8B143808-BAEB-4918-A88B-0C466F714379 com.my.example
detox[339] TRACE: [exec.js/EXEC_SUCCESS, #6] /Users/developer/Library/Developer/CoreSimulator/Devices/8B143808-BAEB-4918-A88B-0C466F714379/data/Containers/Bundle/Application/D93E5AFB-AA3C-4746-B0F1-545B28821D8B/example.app
detox[339] INFO: [AppleSimUtils.js] com.my.example launched. To watch simulator logs, run:
/usr/bin/xcrun simctl spawn 8B143808-BAEB-4918-A88B-0C466F714379 log stream --level debug --style compact --predicate 'process == "example"'
detox[423] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onLaunchApp({ bundleId: 'com.my.example',
deviceId: '8B143808-BAEB-4918-A88B-0C466F714379',
launchArgs:
{ detoxServer: 'ws://localhost:55703',
detoxSessionId: '8475fab5-80f8-7ed3-d22b-56e874375ca4',
detoxDisableHierarchyDump: true },
pid: 423 })
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_SEND] {"type":"isReady","params":{},"messageId":-1000}
detox[339] TRACE: [DetoxServer.js/MESSAGE] role=tester action=isReady (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] DEBUG: [DetoxServer.js/CANNOT_FORWARD] role=testee not connected, cannot fw action (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] DEBUG: [DetoxServer.js/LOGIN] role=testee, sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4
detox[339] DEBUG: [DetoxServer.js/LOGIN_SUCCESS] role=testee, sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4
detox[339] TRACE: [DetoxServer.js/MESSAGE] role=testee action=ready (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] TRACE: [DetoxServer.js/MESSAGE] role=testee action=ready (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_MESSAGE] {"params":{},"messageId":-1000,"type":"ready"}
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_MESSAGE] {"params":{},"messageId":-1000,"type":"ready"}
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_SEND] {"type":"waitForActive","params":{},"messageId":1}
detox[339] TRACE: [DetoxServer.js/MESSAGE] role=tester action=waitForActive (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] TRACE: [DetoxServer.js/MESSAGE] role=testee action=waitForActiveDone (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_MESSAGE] {"messageId":1,"type":"waitForActiveDone","params":{}}
detox[423] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onAppReady({ deviceId: '8B143808-BAEB-4918-A88B-0C466F714379',
bundleId: 'com.my.example',
pid: 423 })
ROOT_DESCRIBE_BLOCK[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onRunDescribeStart({ name: 'ROOT_DESCRIBE_BLOCK' })
detox[339] INFO: rotates to landscape
detox[339] TRACE: [Detox.js/DETOX_BEFORE_EACH] running test: "rotates to landscape"
detox[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onTestStart({ title: 'rotates to landscape',
fullName: 'rotates to landscape',
status: 'running',
invocations: 1 })
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_SEND] {"type":"setOrientation","params":{"orientation":"landscape"},"messageId":2}
detox[339] TRACE: [DetoxServer.js/MESSAGE] role=tester action=setOrientation (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] TRACE: [DetoxServer.js/MESSAGE] role=testee action=setOrientationDone (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_MESSAGE] {"type":"setOrientationDone","messageId":2,"params":{}}
detox[339] TRACE: [Detox.js/DETOX_AFTER_EACH] passed test: "rotates to landscape"
detox[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onTestDone({ title: 'rotates to landscape',
fullName: 'rotates to landscape',
status: 'passed',
invocations: 1,
timedOut: false })
detox[339] INFO: rotates to landscape [OK]
ROOT_DESCRIBE_BLOCK[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onRunDescribeFinish({ name: 'ROOT_DESCRIBE_BLOCK' })
detox[339] TRACE: [ArtifactsManager.js/LIFECYCLE] artifactsManager.onBeforeCleanup()
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_SEND] {"type":"cleanup","params":{"stopRunner":true},"messageId":-49642}
detox[339] TRACE: [DetoxServer.js/MESSAGE] role=tester action=cleanup (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] TRACE: [DetoxServer.js/MESSAGE] role=testee action=cleanupDone (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] TRACE: [AsyncWebSocket.js/WEBSOCKET_MESSAGE] {"type":"cleanupDone","messageId":-49642,"params":{}}
detox[339] DEBUG: [DetoxServer.js/DISCONNECT] role=tester, sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4
detox[339] DEBUG: [DetoxServer.js/DISCONNECT] role=testee, sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4
detox[339] DEBUG: [DetoxServer.js/CANNOT_FORWARD] role=tester not connected, cannot fw action (sessionId=8475fab5-80f8-7ed3-d22b-56e874375ca4)
detox[339] DEBUG: [DetoxServer.js/WS_CLOSE] Detox server connections terminated gracefully
PASS e2e/screenshots/screenshots.e2e.ts (9.34 s)
✓ rotates to landscape (11 ms)
Check that your project’s iPad settings allow for different orientations. If it works for iPhone, it should work for iPad.
I have set full device orientation for both iPad and iPhone in my project’s settings:

I can manually rotate my iPad using the keyboard shortcuts and my application is properly rotating as well: https://share.getcloudapp.com/E0urjGGx
But, this code snippet only works on non iPad devices:
import { device } from 'detox';
const sleep = async (delay) => new Promise((r) => setTimeout(r, delay));
it(`rotates to landscape`, async () => {
await device.setOrientation('landscape');
await sleep(2000);
await device.setOrientation('portrait');
await sleep(2000);
await device.setOrientation('landscape');
});
I tried toggling that setting:

Apart from this, I am not aware of any other settings that might affect the rotation behaviour? 🤔
I've also tried the iPad mini 4 and the iPad Pro (12.9-inch) (4th generation) without any luck.
Ok, thanks for verifying. I will take a look soon.
Reproduced. Investigating.
It seems related to multiple app support. So far, I have not found a solution. As a workaround, you can enable the "Require full screen" check box in Xcode. You probably shouldn't commit this change, as your users won't be able to use iPad multitasking features with your app, but for testing, that's fine.
OK, so it seems like this isn't possible. The system orientation, in apps which are not full screen, is fully controlled by the SpringBoard process, and we currently do not have a way to signal that to do the rotation. Once we add support for XCUITest, this will be possible.
For the time being, I am introducing an assertion into the setOrientation() API that will check for the right conditions, and fail the test if setting an orientation is not supported.
In order to use the workaround in production, you can use PlistBuddy to inject UIRequiresFullScreen=true in your app's Info.plist.
Ok, I understand.
When turning on the Require full screen setting, It is indeed working. Good idea to use PlistBuddy.
For my use case, I've made a temporary fix in the detox.config.js config:
"ios.sim.debug": {
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/example.app",
- "build": "xcodebuild -project ios/example.xcodeproj -UseNewBuildSystem=NO -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
+ "build": "xcodebuild -project ios/example.xcodeproj -UseNewBuildSystem=NO -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build && /usr/libexec/PlistBuddy -c 'Add :UIRequiresFullScreen bool true' ios/build/Build/Products/Debug-iphonesimulator/example.app/Info.plist",
"type": "ios.simulator",
"device": {
"type": "iPhone 11 Pro"
}
},
Thank you for you fast replies and for taking the time to investigate this issue!
Cheers. We will eventually support this when we move to XCUITest.
One suggestion, it’s probably a better idea to have the script modify the Info.plist of the build product, not your source.
Good point; the build folder is untracked so we don't have any chances to commit that change by mistake.
I've updated my https://github.com/wix/Detox/issues/2418#issuecomment-711421149 to edit the Info.plist of the build product.