From 35677da6950c3c468b1c14e0e851bb483dcecf90 Mon Sep 17 00:00:00 2001 From: Asaf Korem <55082339+asafkorem@users.noreply.github.com> Date: Wed, 13 Apr 2022 14:17:24 +0300 Subject: [PATCH] feat(iOS): support --headless config (#3304) BREAKING: opens Simulator app by default unless the headless mode is enabled --- .../src/configuration/composeDeviceConfig.js | 61 ++++++++++--------- .../configuration/composeDeviceConfig.test.js | 35 +++++++---- .../drivers/ios/SimulatorLauncher.js | 6 +- .../drivers/ios/SimulatorLauncher.test.js | 21 ++++--- .../devices/common/drivers/DeviceLauncher.js | 4 +- .../common/drivers/ios/tools/AppleSimUtils.js | 43 ++++++++++--- .../runtime/drivers/ios/SimulatorDriver.js | 5 +- .../drivers/ios/SimulatorDriver.test.js | 8 ++- detox/src/devices/runtime/factories/ios.js | 1 + detox/test/package.json | 2 +- docs/APIRef.Configuration.md | 16 ++--- examples/demo-react-native-jest/package.json | 2 +- examples/demo-react-native/package.json | 2 +- 13 files changed, 128 insertions(+), 78 deletions(-) diff --git a/detox/src/configuration/composeDeviceConfig.js b/detox/src/configuration/composeDeviceConfig.js index d3f853d7bd..1822af41f1 100644 --- a/detox/src/configuration/composeDeviceConfig.js +++ b/detox/src/configuration/composeDeviceConfig.js @@ -159,7 +159,7 @@ function validateDeviceConfig({ deviceConfig, errorComposer, deviceAlias }) { throw errorComposer.malformedDeviceProperty(deviceAlias, 'headless'); } - if (deviceConfig.type !== 'android.emulator') { + if (deviceConfig.type !== 'ios.simulator' && deviceConfig.type !== 'android.emulator') { throw errorComposer.unsupportedDeviceProperty(deviceAlias, 'headless'); } } @@ -187,41 +187,44 @@ function validateDeviceConfig({ deviceConfig, errorComposer, deviceAlias }) { } function applyCLIOverrides(deviceConfig, cliConfig) { - if (cliConfig.deviceName) { - deviceConfig.device = cliConfig.deviceName; + _assignCLIConfigIfSupported('device-name', cliConfig.deviceName, deviceConfig, 'device'); + _assignCLIConfigIfSupported('device-boot-args', cliConfig.deviceBootArgs, deviceConfig, 'bootArgs'); + _assignCLIConfigIfSupported('headless', cliConfig.headless, deviceConfig, 'headless'); + _assignCLIConfigIfSupported('force-adb-install', cliConfig.forceAdbInstall, deviceConfig, 'forceAdbInstall'); + _assignCLIConfigIfSupported('gpu', cliConfig.gpu, deviceConfig, 'gpuMode'); + _assignCLIConfigIfSupported('readonly-emu', cliConfig.readonlyEmu, deviceConfig, 'readonly'); +} + +function _assignCLIConfigIfSupported(argName, argValue, deviceConfig, propertyName) { + if (argValue === undefined) { + return; } const deviceType = deviceConfig.type; - if (cliConfig.deviceBootArgs) { - if ((deviceType === 'ios.simulator') || (deviceType === 'android.emulator')) { - deviceConfig.bootArgs = cliConfig.deviceBootArgs; - } else { - log.warn(`--device-boot-args CLI override is not supported by device type = "${deviceType}" and will be ignored`); - } + const supportedDeviceTypesPrefixes = _supportedDeviceTypesPrefixes(argName); + if (!supportedDeviceTypesPrefixes.some((prefix) => deviceType.startsWith(prefix))) { + log.warn(`--${argName} CLI override is not supported by device type = "${deviceType}" and will be ignored`); + return; } - if (cliConfig.forceAdbInstall !== undefined) { - if (deviceType.startsWith('android.')) { - deviceConfig.forceAdbInstall = cliConfig.forceAdbInstall; - } else { - log.warn(`--force-adb-install CLI override is not supported by device type = "${deviceType}" and will be ignored`); - } - } + deviceConfig[propertyName] = argValue; +} - const emulatorCLIConfig = _.pick(cliConfig, ['headless', 'gpu', 'readonlyEmu']); - const emulatorOverrides = _.omitBy({ - headless: cliConfig.headless, - gpuMode: cliConfig.gpu, - readonly: cliConfig.readonlyEmu, - }, _.isUndefined); +function _supportedDeviceTypesPrefixes(argName) { + switch (argName) { + case 'device-name': + return ['']; - if (!_.isEmpty(emulatorOverrides)) { - if (deviceType === 'android.emulator') { - Object.assign(deviceConfig, emulatorOverrides); - } else { - const flags = Object.keys(emulatorCLIConfig).map(key => '--' + _.kebabCase(key)).join(', '); - log.warn(`${flags} CLI overriding is not supported by device type = "${deviceType}" and will be ignored`); - } + case 'force-adb-install': + return ['android.']; + + case 'gpu': + case 'readonly-emu': + return ['android.emulator']; + + case 'device-boot-args': + case 'headless': + return ['ios.simulator', 'android.emulator']; } } diff --git a/detox/src/configuration/composeDeviceConfig.test.js b/detox/src/configuration/composeDeviceConfig.test.js index 16c099461d..72cec03acf 100644 --- a/detox/src/configuration/composeDeviceConfig.test.js +++ b/detox/src/configuration/composeDeviceConfig.test.js @@ -363,13 +363,16 @@ describe('composeDeviceConfig', () => { }); describe('--headless', () => { - describe('given android.emulator device', () => { - beforeEach(() => setConfig('android.emulator', configType)); + describe.each([ + ['ios.simulator'], + ['android.emulator'] + ])('given a supported device type (%j)', (deviceType) => { + beforeEach(() => setConfig(deviceType, configType)); it('should override .headless without warnings', () => { - cliConfig.headless = true; + cliConfig.headless = false; expect(compose()).toEqual(expect.objectContaining({ - headless: true, + headless: false, })); expect(logger.warn).not.toHaveBeenCalled(); @@ -378,17 +381,16 @@ describe('composeDeviceConfig', () => { describe.each([ ['ios.none'], - ['ios.simulator'], ['android.attached'], ['android.genycloud'], - ['./customDriver'], + ['./customDriver'] ])('given a non-supported device (%j)', (deviceType) => { beforeEach(() => setConfig(deviceType, configType)); it('should print a warning and refuse to override .headless', () => { - cliConfig.headless = true; + cliConfig.headless = false; expect(compose()).not.toEqual(expect.objectContaining({ - headless: true, + headless: false, })); expect(logger.warn).toHaveBeenCalledWith(expect.stringMatching(/--headless.*not supported/)); @@ -608,22 +610,29 @@ describe('composeDeviceConfig', () => { describe('.headless validation', () => { test.each([ 'ios.none', - 'ios.simulator', 'android.attached', - 'android.genycloud', + 'android.genycloud' ])('cannot be used for a non-emulator device (%j)', (deviceType) => { setConfig(deviceType, configType); deviceConfig.headless = true; expect(compose).toThrow(errorComposer.unsupportedDeviceProperty(alias(), 'headless')); }); - describe('given android.emulator device', () => { - beforeEach(() => setConfig('android.emulator', configType)); + describe.each([ + 'ios.simulator', + 'android.emulator' + ])('given supporting device type (%j)', (deviceType) => { + beforeEach(() => setConfig(deviceType, configType)); test(`should throw if value is not a boolean (e.g., string)`, () => { - deviceConfig.headless = `${Math.random() > 0.5}`; // string + deviceConfig.headless = `${Math.random() > 0.5}`; expect(compose).toThrowError(errorComposer.malformedDeviceProperty(alias(), 'headless')); }); + + test('should not throw if value is a boolean', () => { + deviceConfig.headless = false; + expect(compose).not.toThrowError(); + }); }); test('should be disabled for custom devices', () => { diff --git a/detox/src/devices/allocation/drivers/ios/SimulatorLauncher.js b/detox/src/devices/allocation/drivers/ios/SimulatorLauncher.js index 9cb4c19d8c..4ca9f4d072 100644 --- a/detox/src/devices/allocation/drivers/ios/SimulatorLauncher.js +++ b/detox/src/devices/allocation/drivers/ios/SimulatorLauncher.js @@ -6,9 +6,9 @@ class SimulatorLauncher extends DeviceLauncher { this._applesimutils = applesimutils; } - async launch(udid, type, bootArgs) { - const coldBoot = await this._applesimutils.boot(udid, bootArgs); - await this._notifyBootEvent(udid, type, coldBoot); + async launch(udid, type, bootArgs, headless) { + const coldBoot = await this._applesimutils.boot(udid, bootArgs, headless); + await this._notifyBootEvent(udid, type, coldBoot, headless); } async shutdown(udid) { diff --git a/detox/src/devices/allocation/drivers/ios/SimulatorLauncher.test.js b/detox/src/devices/allocation/drivers/ios/SimulatorLauncher.test.js index 1226cba3be..cfdb1978f6 100644 --- a/detox/src/devices/allocation/drivers/ios/SimulatorLauncher.test.js +++ b/detox/src/devices/allocation/drivers/ios/SimulatorLauncher.test.js @@ -18,13 +18,14 @@ describe('Simulator launcher (helper)', () => { describe('launch', () => { const type = 'mockType'; const bootArgs = { mock: 'boot-args' }; + const headless = true; const givenBootResultCold = () => applesimutils.boot.mockResolvedValue(true); const givenBootResultWarm = () => applesimutils.boot.mockResolvedValue(false); it('should boot using apple-sim-utils', async () => { - await uut.launch(udid, '', bootArgs); - expect(applesimutils.boot).toHaveBeenCalledWith(udid, bootArgs); + await uut.launch(udid, '', bootArgs, headless); + expect(applesimutils.boot).toHaveBeenCalledWith(udid, bootArgs, headless); }); it('should fail if apple-sim-utils fails', async () => { @@ -35,20 +36,26 @@ describe('Simulator launcher (helper)', () => { it('should emit boot event', async () => { givenBootResultWarm(); - await uut.launch(udid, type, {}); - expect(eventEmitter.emit).toHaveBeenCalledWith('bootDevice', expect.objectContaining({ deviceId: udid, type, coldBoot: false })); + await uut.launch(udid, type, {}, headless); + expect(eventEmitter.emit).toHaveBeenCalledWith( + 'bootDevice', + expect.objectContaining({ deviceId: udid, type, coldBoot: false, headless }) + ); }); it('should emit cold-boot status in boot event', async () => { givenBootResultCold(); - await uut.launch(udid, type, {}); - expect(eventEmitter.emit).toHaveBeenCalledWith('bootDevice', expect.objectContaining({ deviceId: udid, type, coldBoot: true })); + await uut.launch(udid, type, {}, headless); + expect(eventEmitter.emit).toHaveBeenCalledWith( + 'bootDevice', + expect.objectContaining({ deviceId: udid, type, coldBoot: true, headless }) + ); }); it('should fail if emission fails', async () => { const error = new Error('mock error'); eventEmitter.emit.mockRejectedValue(error); - await expect(uut.launch(udid, '', bootArgs)).rejects.toThrowError(error); + await expect(uut.launch(udid, '', bootArgs, headless)).rejects.toThrowError(error); }); }); diff --git a/detox/src/devices/common/drivers/DeviceLauncher.js b/detox/src/devices/common/drivers/DeviceLauncher.js index 27641e0500..a7b998d6b3 100644 --- a/detox/src/devices/common/drivers/DeviceLauncher.js +++ b/detox/src/devices/common/drivers/DeviceLauncher.js @@ -11,8 +11,8 @@ class DeviceLauncher { return this._eventEmitter.emit('shutdownDevice', { deviceId }); } - async _notifyBootEvent(deviceId, type, coldBoot) { - return this._eventEmitter.emit('bootDevice', { deviceId, type, coldBoot }); + async _notifyBootEvent(deviceId, type, coldBoot, headless) { + return this._eventEmitter.emit('bootDevice', { deviceId, type, coldBoot, headless }); } } diff --git a/detox/src/devices/common/drivers/ios/tools/AppleSimUtils.js b/detox/src/devices/common/drivers/ios/tools/AppleSimUtils.js index afedcf6c3c..68f1b2a9f4 100644 --- a/detox/src/devices/common/drivers/ios/tools/AppleSimUtils.js +++ b/detox/src/devices/common/drivers/ios/tools/AppleSimUtils.js @@ -42,20 +42,27 @@ class AppleSimUtils { /*** * Boots the simulator if it is not booted already. * - * @param {String} udid - device id - * @returns {Promise} true, if device has been booted up from the shutdown state + * @param {String} udid iOS Simulator UDID. + * @param {String} deviceBootArgs simctl boot command arguments. + * @param {Boolean} headless If false, opens the Simulator app after the Simulator has booted. + * @returns {Promise} true, if device has been booted up from the shutdown state. */ - async boot(udid, deviceBootArgs = '') { + async boot(udid, deviceBootArgs = '', headless = false) { const isBooted = await this.isBooted(udid); - if (!isBooted) { - const statusLogs = { trying: `Booting device ${udid}...` }; - await this._execSimctl({ cmd: `boot ${udid} ${deviceBootArgs}`, statusLogs, retries: 10 }); - await this._execSimctl({ cmd: `bootstatus ${udid}`, retries: 1 }); - return true; + if (isBooted) { + return false; } - return false; + const statusLogs = { trying: `Booting device ${udid}...` }; + await this._execSimctl({ cmd: `boot ${udid} ${deviceBootArgs}`, statusLogs, retries: 10 }); + await this._execSimctl({ cmd: `bootstatus ${udid}`, retries: 1 }); + + if (!headless) { + await this._openSimulatorApp(udid); + } + + return true; } async isBooted(udid) { @@ -72,6 +79,24 @@ class AppleSimUtils { return device; } + async _openSimulatorApp(udid) { + try { + await childProcess.execWithRetriesAndLogs(`open -a Simulator --args -CurrentDeviceUDID ${udid}`, { retries: 0 }); + } catch (error) { + this._logUnableToOpenSimulator(); + } + } + + _logUnableToOpenSimulator() { + log.warn( + `Unable to open the Simulator app. Please make sure you have Xcode and iOS Simulator installed ` + + `(https://developer.apple.com/xcode/). In case you already have the latest Xcode version installed, ` + + `try run the command: \`sudo xcode-select -s /Applications/Xcode.app\`. If you are running tests from CI, ` + + `we recommend running them with "--headless" device configuration (see: ` + + `https://wix.github.io/Detox/docs/next/api/configuration/#device-configurations).` + ); + } + /*** * @param deviceInfo - an item in output of `applesimutils --list` * @returns {Promise} UDID of a new device diff --git a/detox/src/devices/runtime/drivers/ios/SimulatorDriver.js b/detox/src/devices/runtime/drivers/ios/SimulatorDriver.js index 878d483869..fef35a1d01 100644 --- a/detox/src/devices/runtime/drivers/ios/SimulatorDriver.js +++ b/detox/src/devices/runtime/drivers/ios/SimulatorDriver.js @@ -30,12 +30,13 @@ class SimulatorDriver extends IosDriver { * @param deps { SimulatorDriverDeps } * @param props { SimulatorDriverProps } */ - constructor(deps, { udid, type, bootArgs }) { + constructor(deps, { udid, type, bootArgs, headless }) { super(deps); this.udid = udid; this._type = type; this._bootArgs = bootArgs; + this._headless = headless; this._deviceName = `${udid} (${this._type})`; this._simulatorLauncher = deps.simulatorLauncher; this._applesimutils = deps.applesimutils; @@ -151,7 +152,7 @@ class SimulatorDriver extends IosDriver { async resetContentAndSettings() { await this._simulatorLauncher.shutdown(this.udid); await this._applesimutils.resetContentAndSettings(this.udid); - await this._simulatorLauncher.launch(this.udid, this._type, this._bootArgs); + await this._simulatorLauncher.launch(this.udid, this._type, this._bootArgs, this._headless); } getLogsPaths() { diff --git a/detox/src/devices/runtime/drivers/ios/SimulatorDriver.test.js b/detox/src/devices/runtime/drivers/ios/SimulatorDriver.test.js index 4050e3fef7..24a555a5e5 100644 --- a/detox/src/devices/runtime/drivers/ios/SimulatorDriver.test.js +++ b/detox/src/devices/runtime/drivers/ios/SimulatorDriver.test.js @@ -4,6 +4,7 @@ describe('IOS simulator driver', () => { const type = 'Chika'; const bundleId = 'bundle-id-mock'; const bootArgs = { boot: 'args' }; + const headless = true; let client; let eventEmitter; @@ -27,7 +28,10 @@ describe('IOS simulator driver', () => { simulatorLauncher = new SimulatorLauncher(); const SimulatorDriver = require('./SimulatorDriver'); - uut = new SimulatorDriver({ simulatorLauncher, applesimutils, client, eventEmitter }, { udid, type, bootArgs }); + uut = new SimulatorDriver( + { simulatorLauncher, applesimutils, client, eventEmitter }, + { udid, type, bootArgs, headless } + ); }); it('should return the UDID as the external ID', () => { @@ -80,7 +84,7 @@ describe('IOS simulator driver', () => { it('should relaunch the simulator', async () => { await uut.resetContentAndSettings(); - expect(simulatorLauncher.launch).toHaveBeenCalledWith(udid, type, bootArgs); + expect(simulatorLauncher.launch).toHaveBeenCalledWith(udid, type, bootArgs, true); }); }); diff --git a/detox/src/devices/runtime/factories/ios.js b/detox/src/devices/runtime/factories/ios.js index d606c94d92..764b939ac2 100644 --- a/detox/src/devices/runtime/factories/ios.js +++ b/detox/src/devices/runtime/factories/ios.js @@ -28,6 +28,7 @@ class IosSimulator extends RuntimeDriverFactoryIos { udid: deviceCookie.udid, type: deviceConfig.device.type, bootArgs: deviceConfig.bootArgs, + headless: deviceConfig.headless }; const { IosSimulatorRuntimeDriver } = require('../drivers'); diff --git a/detox/test/package.json b/detox/test/package.json index 57807a125b..52dcc94c0a 100644 --- a/detox/test/package.json +++ b/detox/test/package.json @@ -14,7 +14,7 @@ "packager": "react-native start", "detox-server": "detox run-server", "e2e:ios": "detox test -c ios.sim.release", - "e2e:ios-ci": "npm run e2e:ios -- --workers 2 --retries 1 --jest-report-specs -l verbose", + "e2e:ios-ci": "npm run e2e:ios -- --workers 2 --retries 1 --jest-report-specs -l verbose --headless", "e2e:android": "detox test -c android.emu.release", "e2e:android-ci-genycloud": "detox test -c android.genycloud.release --workers 5 --retries 1 --jest-report-specs --loglevel verbose", "e2e:android-ci-google": "detox test -c android.emu.release --workers 3 --retries 1 --jest-report-specs --loglevel verbose --headless --gpu off", diff --git a/docs/APIRef.Configuration.md b/docs/APIRef.Configuration.md index 195ee19db7..0ca75a80d5 100644 --- a/docs/APIRef.Configuration.md +++ b/docs/APIRef.Configuration.md @@ -161,14 +161,14 @@ A device config can have the following params: | Configuration Params | Details | | | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `type` | _**Required.** String Literal_. Mandatory property to discern device types: `ios.simulator`, `android.emulator`, `android.attached`, `android.genycloud`, `ios.none`, etc. | | -| `device` | _**Required.** Object._ Device query, e.g. `{ "byType": "iPhone 11 Pro" }` for iOS simulator, `{ "avdName": "Pixel_2_API_29" }` for Android emulator or `{ "adbName": "" }` for attached Android device with name matching the regex. | | -| `bootArgs` | _Optional. String. Supported by `ios.simulator` and `android.emulator` only._
Supply an extra **string** of arguments to `xcrun simctl boot ...` or `emulator -verbose ... @AVD_Name`. | | -| `forceAdbInstall` | _Optional. Boolean. Supported for Android devices only._
A **boolean** value, **false** by default. When set **true**, it tells `device.installApp()` to use `adb install`. Otherwise, it would use the combination of `adb push ` and `adb shell pm install`. | | -| `utilBinaryPaths` | _Optional. Array of strings. Supported for Android devices only._
An array of relative paths of _utility_ app (APK) binary-files to preinstall on the tested devices - once before the test execution begins.
**Note**: these are not affected by various install-lifecycle events, such as launching an app with `device.launchApp({delete: true})`, which reinstalls the app. A good example of why this might come in handy is [Test Butler](https://github.com/linkedin/test-butler). | | -| `gpuMode` | \_Optional. String Literal (auto \\ | host \| swiftshader\_indirect \| angle\_indirect \| guest). Supported by `android.emulator` only.\_
A fixed **string** , which tells [in which GPU mode](https://developer.android.com/studio/run/emulator-acceleration#command-gpu) the emulator should be booted. | -| `headless` | _Optional. Boolean. Supported by `android.emulator` only._
_False_ by default. When set to _true_, it tells Detox to boot an Android emulator with `-no-window` option. | | -| `readonly` | _Optional. Boolean. Supported by `android.emulator` only._
_True_ by default. When set to _false_, it forces Detox to boot emulators in the writable mode, where filesystem changes are persisted even after rebooting the emulator. **Note**: you cannot use multiple workers with the same writable AVD instance, which is why this option is not recommended to be customized. | | +| `type` | _**Required.** String Literal_. Mandatory property to discern device types: `ios.simulator`, `android.emulator`, `android.attached`, `android.genycloud`, `ios.none`, etc. | +| `device` | _**Required.** Object._ Device query, e.g. `{ "byType": "iPhone 11 Pro" }` for iOS simulator, `{ "avdName": "Pixel_2_API_29" }` for Android emulator or `{ "adbName": "" }` for attached Android device with name matching the regex. | +| `bootArgs` | _Optional. String. Supported by `ios.simulator` and `android.emulator` only._
Supply an extra _String_ of arguments to `xcrun simctl boot ...` or `emulator -verbose ... @AVD_Name`. | +| `forceAdbInstall` | _Optional. Boolean. Supported for Android devices only._
A _Boolean_ value, `false` by default. When set to `true`, it tells `device.installApp()` to use `adb install`. Otherwise, it would use the combination of `adb push ` and `adb shell pm install`. | +| `utilBinaryPaths` | _Optional. Array of strings. Supported for Android devices only._
An array of relative paths of _utility_ app (APK) binary-files to preinstall on the tested devices - once before the test execution begins.
**Note**: these are not affected by various install-lifecycle events, such as launching an app with `device.launchApp({delete: true})`, which reinstalls the app. A good example of why this might come in handy is [Test Butler](https://github.com/linkedin/test-butler). | +| `gpuMode` | _Optional. String Literal (auto \| host \| swiftshader\_indirect \| angle\_indirect \| guest). Supported by `android.emulator` only._
A fixed **string** , which tells [in which GPU mode](https://developer.android.com/studio/run/emulator-acceleration#command-gpu) the emulator should be booted. | +| `headless` | _Optional. Boolean._ `false` by default. When set to `true`, it tells Detox to boot an Android emulator with `-no-window` option, or to not open the iOS Simulator app when running with Android or iOS respectively. | +| `readonly` | _Optional. Boolean. Supported by `android.emulator` only._
`false` by default. When set to `true`, it forces Detox to boot even a single emulator with `-read-only` option.
**Note**: when used with multiple workers, this setting has no effect — emulators will be booted always with `-read-only`. | Also, in the Detox `configurations` you can use the device configs as-is, without aliasing: diff --git a/examples/demo-react-native-jest/package.json b/examples/demo-react-native-jest/package.json index 8172564046..e521dc857d 100644 --- a/examples/demo-react-native-jest/package.json +++ b/examples/demo-react-native-jest/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "test:ios-release": "detox test --configuration ios.sim.release -l verbose", - "test:ios-release-ci": "detox test --configuration ios.sim.release -l verbose --workers 2", + "test:ios-release-ci": "detox test --configuration ios.sim.release -l verbose --workers 2 --headless", "test:android-release": "detox test --configuration android.emu.release", "test:android-release-ci": "detox test --configuration android.emu.release -l verbose --workers 2 --headless --gpu off --record-logs all --take-screenshots all", "test:android-genycloud-release": "detox test --configuration android.genycloud.release", diff --git a/examples/demo-react-native/package.json b/examples/demo-react-native/package.json index c1c9798f9e..9d2a562b52 100644 --- a/examples/demo-react-native/package.json +++ b/examples/demo-react-native/package.json @@ -13,7 +13,7 @@ "test:ios": "detox test --configuration ios.sim.debug", "test:ios-debug": "detox test --configuration ios.sim.debug", "test:ios-release": "detox test --configuration ios.sim.release", - "test:ios-release-ci": "detox test --configuration ios.sim.release -l verbose --record-logs all --take-screenshots all", + "test:ios-release-ci": "detox test --configuration ios.sim.release -l verbose --record-logs all --take-screenshots all --headless", "test:android-debug": "detox test --configuration android.emu.debug", "test:android-release": "detox test --configuration android.emu.release", "test:android-release-ci": "detox test --configuration android.emu.release -l verbose --headless --gpu off --record-logs all --take-screenshots all",