Skip to content

Commit 056704b

Browse files
committed
feat(config): default app permissions (iOS)
1 parent 60150de commit 056704b

9 files changed

+108
-11
lines changed

detox/index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ declare global {
184184
bundleId?: string;
185185
build?: string;
186186
launchArgs?: Record<string, any>;
187+
permissions?: DevicePermissions;
187188
}
188189

189190
interface DetoxAndroidAppConfig {
@@ -201,6 +202,7 @@ declare global {
201202
build?: string;
202203
testBinaryPath?: string;
203204
launchArgs?: Record<string, any>;
205+
permissions?: DevicePermissions;
204206
}
205207

206208
type DetoxBuiltInDeviceConfig =

detox/src/configuration/composeAppsConfig.js

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ function composeAppsConfigFromPlain(opts) {
6868
bundleId: localConfig.bundleId,
6969
build: localConfig.build,
7070
launchArgs: localConfig.launchArgs,
71+
permissions: localConfig.permissions,
7172
};
7273
break;
7374
default:

detox/src/configuration/composeAppsConfig.test.js

+16
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ describe('composeAppsConfig', () => {
5656
binaryPath: 'path/to/app',
5757
bundleId: 'com.example.app',
5858
build: 'echo OK',
59+
permissions: {
60+
calendar: 'YES',
61+
},
5962
launchArgs: {
6063
hello: 'world',
6164
}
@@ -65,6 +68,18 @@ describe('composeAppsConfig', () => {
6568
it.each([
6669
['ios.none', 'ios.app'],
6770
['ios.simulator', 'ios.app'],
71+
])('should infer type and app properties for %j', (deviceType, appType) => {
72+
deviceConfig.type = deviceType;
73+
expect(compose()).toEqual({
74+
default: {
75+
...localConfig,
76+
type: appType,
77+
device: undefined,
78+
},
79+
});
80+
});
81+
82+
it.each([
6883
['android.attached', 'android.apk'],
6984
['android.emulator', 'android.apk'],
7085
['android.genycloud', 'android.apk'],
@@ -75,6 +90,7 @@ describe('composeAppsConfig', () => {
7590
...localConfig,
7691
type: appType,
7792
device: undefined,
93+
permissions: undefined,
7894
},
7995
});
8096
});

detox/src/devices/runtime/RuntimeDevice.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const _ = require('lodash');
2+
13
const DetoxRuntimeError = require('../../errors/DetoxRuntimeError');
24
const debug = require('../../utils/debug'); // debug utils, leave here even if unused
35
const { traceCall } = require('../../utils/trace');
@@ -56,6 +58,7 @@ class RuntimeDevice {
5658
this._emitter = eventEmitter;
5759
this._errorComposer = runtimeErrorComposer;
5860

61+
/** @type {Detox.DetoxAppConfig | null} */
5962
this._currentApp = null;
6063
this._currentAppLaunchArgs = new LaunchArgsEditor();
6164
this._processes = {};
@@ -189,9 +192,13 @@ class RuntimeDevice {
189192
}
190193

191194
async installApp(binaryPath, testBinaryPath) {
192-
await traceCall('appInstall', () => {
195+
await traceCall('appInstall', async () => {
196+
/** @type {*} */
193197
const currentApp = binaryPath ? { binaryPath, testBinaryPath } : this._getCurrentApp();
194-
return this.deviceDriver.installApp(currentApp.binaryPath, currentApp.testBinaryPath);
198+
await this.deviceDriver.installApp(currentApp.binaryPath, currentApp.testBinaryPath);
199+
if (!_.isEmpty(currentApp.permissions)) {
200+
await this.deviceDriver.setPermissions(currentApp.bundleId, currentApp.permissions);
201+
}
195202
});
196203
}
197204

detox/src/devices/runtime/RuntimeDevice.test.js

+26
Original file line numberDiff line numberDiff line change
@@ -687,9 +687,35 @@ describe('Device', () => {
687687
expect(driverMock.driver.installApp).toHaveBeenCalledWith('newAppPath', device._deviceConfig.testBinaryPath);
688688
});
689689

690+
it(`with a custom app permissions should setPermissions after installing`, async () => {
691+
const device = await aValidDevice({
692+
appsConfig: {
693+
default: {
694+
permissions: {
695+
calendar: 'NO',
696+
camera: 'YES',
697+
},
698+
},
699+
},
700+
});
701+
702+
driverMock.driver.setPermissions.mockRejectedValue(new Error('Should be called last!'));
703+
driverMock.driver.installApp.mockImplementation(() => {
704+
driverMock.driver.setPermissions.mockReset();
705+
});
706+
707+
await device.installApp();
708+
expect(driverMock.driver.setPermissions).toHaveBeenCalledWith('test.bundle', {
709+
calendar: 'NO',
710+
camera: 'YES',
711+
});
712+
});
713+
690714
it(`with no args should use the default path given in configuration`, async () => {
691715
const device = await aValidDevice();
692716
await device.installApp();
717+
718+
expect(driverMock.driver.setPermissions).not.toHaveBeenCalled();
693719
expect(driverMock.driver.installApp).toHaveBeenCalledWith(device._currentApp.binaryPath, device._currentApp.testBinaryPath);
694720
});
695721
});

detox/test/e2e/detox.config.js

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ const launchArgs = {
1616
micro: 'soft',
1717
};
1818

19+
const defaultPermissions = {
20+
calendar: 'YES',
21+
notifications: 'YES',
22+
camera: 'YES',
23+
photos: 'YES',
24+
};
25+
1926
/** @type {Detox.DetoxConfig} */
2027
const config = {
2128
testRunner: 'nyc jest',
@@ -57,13 +64,15 @@ const config = {
5764
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/example.app',
5865
build: 'set -o pipefail && xcodebuild -workspace ios/example.xcworkspace -UseNewBuildSystem=YES -scheme example_ci -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build -quiet',
5966
bundleId: 'com.wix.detox-example',
67+
permissions: defaultPermissions,
6068
},
6169

6270
'ios.release': {
6371
type: 'ios.app',
6472
name: 'example',
6573
binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/example.app',
6674
build: 'set -o pipefail && export CODE_SIGNING_REQUIRED=NO && export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/example.xcworkspace -UseNewBuildSystem=YES -scheme example_ci -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet',
75+
permissions: defaultPermissions,
6776
},
6877

6978
'android.debug': {

docs/APIRef.Configuration.md

+14-9
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,11 @@ The format of Detox config allows you to define inside it multiple app configs i
202202
"ios.debug": {
203203
"type": "ios.app",
204204
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/example.app",
205-
"build": "xcodebuild -project ios/example.xcodeproj -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build"
205+
"build": "xcodebuild -project ios/example.xcodeproj -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
206+
"permissions": {
207+
/** @see {Detox.DevicePermissions} */
208+
notifications: 'YES',
209+
},
206210
},
207211
"android.release": {
208212
"type": "android.apk",
@@ -218,14 +222,15 @@ The format of Detox config allows you to define inside it multiple app configs i
218222

219223
An app config can have the following params:
220224

221-
| Configuration Params | Details |
222-
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
223-
| `type` | Mandatory property to discern app types: `ios.app`, `android.apk`. |
224-
| `name` | Use only when working with multiple apps within the same configuration. See an example below. |
225-
| `binaryPath` | Relative path to the ipa/app/apk due to be tested (make sure you build the app in a project relative path) |
226-
| `build` | **\[optional]** Build command (normally an `xcodebuild` command you use to build your app), which can be called later using Detox CLI tool as a convenience. |
227-
| `testBinaryPath` | (optional, Android only): relative path to the test app (apk) |
228-
| `launchArgs` | **\[optional]** An object specifying arguments (key-values pairs) to pass through into the app, upon launching on the device. For more info, refer to the dedicated [launch-arguments guide](APIRef.LaunchArgs.md). |
225+
| Configuration Params | Details |
226+
|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
227+
| `type` | Mandatory property to discern app types: `ios.app`, `android.apk`. |
228+
| `name` | Use only when working with multiple apps within the same configuration. See an example below. |
229+
| `build` | **\[optional]** Build command (normally an `xcodebuild` command you use to build your app), which can be called later using Detox CLI tool as a convenience. |
230+
| `binaryPath` | Relative path to the ipa/app/apk due to be tested (make sure you build the app in a project relative path) |
231+
| `testBinaryPath` | **\[optional, Android only]**: relative path to the test app (apk) |
232+
| `launchArgs` | **\[optional]** An object specifying arguments (key-values pairs) to pass through into the app, upon launching on the device. For more info, refer to the dedicated [launch-arguments guide](APIRef.LaunchArgs.md). |
233+
| `permissions` | **\[optional, iOS only]** An object specifying default [runtime permissions](APIRef.DeviceObjectAPI.md#permissions-ios-only) to give before launching the app. |
229234

230235
To work with multiple apps within the same configuration you should be giving each app its name, e.g.:
231236

docs/APIRef.DeviceObjectAPI.md

+31
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ await device.launchApp({permissions: {calendar: 'YES'}});
137137
138138
Detox uses [AppleSimUtils](https://github.com/wix/AppleSimulatorUtils) to implement this functionality for iOS simulators. Read about the different types of permissions and how to set them in AppleSimUtils' documentation and by checking out Detox’s [own test suite](https://github.com/wix/Detox/tree/a9a09246c05733f6b91cfcc0dba05a4714abca92/detox/test/e2e/13.permissions.test.js).
139139
140+
As a reference, you can also use this [table of possible permissions](#permissions-ios-only).
141+
140142
##### 3. `url`—Launching with URL
141143
142144
Launches the app with the specified URL to test your app’s deep link handling mechanism.
@@ -528,3 +530,32 @@ Exposes [`UiAutomator`’s `UiDevice` API](https://developer.android.com/referen
528530
**This is not a part of the official Detox API**, it may break and change whenever an update to `UiDevice` or `UiAutomator` Gradle dependencies (`androidx.test.uiautomator:uiautomator`) is introduced.
529531
530532
[`UiDevice`’s autogenerated code](https://github.com/wix/Detox/tree/a9a09246c05733f6b91cfcc0dba05a4714abca92/detox/src/android/espressoapi/UIDevice.js)
533+
534+
### Permissions (iOS only)
535+
536+
When you are [configuring the apps](APIRef.Configuration.md#apps-configurations) list in your
537+
Detox config file or [launching the app](#2-permissionsset-runtime-permissions-ios-only),
538+
you can give certain permissions beforehand to it to avoid this inconvenient modal:
539+
540+
![iOS permissions modal](img/ios-permissions.png)
541+
542+
So, here is the exhaustive permissions list that Detox currently supports for iOS:
543+
544+
| Permission | Values |
545+
|---------------|---------------------------------------------------------|
546+
| calendar | `'YES' &#124; 'NO' &#124; 'unset'` |
547+
| camera | `'YES' &#124; 'NO' &#124; 'unset'` |
548+
| contacts | `'YES' &#124; 'NO' &#124; 'unset'` |
549+
| faceid | `'YES' &#124; 'NO' &#124; 'unset'` |
550+
| health | `'YES' &#124; 'NO' &#124; 'unset'` |
551+
| homekit | `'YES' &#124; 'NO' &#124; 'unset'` |
552+
| location | `'always' &#124; 'inuse' &#124; 'never' &#124; 'unset'` |
553+
| medialibrary | `'YES' &#124; 'NO' &#124; 'unset'` |
554+
| microphone | `'YES' &#124; 'NO' &#124; 'unset'` |
555+
| motion | `'YES' &#124; 'NO' &#124; 'unset'` |
556+
| notification | `'YES' &#124; 'NO' &#124; 'unset'` |
557+
| photos | `'YES' &#124; 'NO' &#124; 'unset'` |
558+
| reminders | `'YES' &#124; 'NO' &#124; 'unset'` |
559+
| siri | `'YES' &#124; 'NO' &#124; 'unset'` |
560+
| speech | `'YES' &#124; 'NO' &#124; 'unset'` |
561+
| userTracking | `'YES' &#124; 'NO' &#124; 'unset'` |

docs/img/ios-permissions.png

85.7 KB
Loading

0 commit comments

Comments
 (0)