Skip to content

feat(config): default app permissions (iOS) #3493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions detox/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ declare global {
bundleId?: string;
build?: string;
launchArgs?: Record<string, any>;
permissions?: DevicePermissions;
}

interface DetoxAndroidAppConfig {
Expand All @@ -201,6 +202,7 @@ declare global {
build?: string;
testBinaryPath?: string;
launchArgs?: Record<string, any>;
permissions?: DevicePermissions;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant?

}

type DetoxBuiltInDeviceConfig =
Expand Down
1 change: 1 addition & 0 deletions detox/src/configuration/composeAppsConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function composeAppsConfigFromPlain(opts) {
bundleId: localConfig.bundleId,
build: localConfig.build,
launchArgs: localConfig.launchArgs,
permissions: localConfig.permissions,
};
break;
default:
Expand Down
16 changes: 16 additions & 0 deletions detox/src/configuration/composeAppsConfig.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ describe('composeAppsConfig', () => {
binaryPath: 'path/to/app',
bundleId: 'com.example.app',
build: 'echo OK',
permissions: {
calendar: 'YES',
},
launchArgs: {
hello: 'world',
}
Expand All @@ -65,6 +68,18 @@ describe('composeAppsConfig', () => {
it.each([
['ios.none', 'ios.app'],
['ios.simulator', 'ios.app'],
])('should infer type and app properties for %j', (deviceType, appType) => {
deviceConfig.type = deviceType;
expect(compose()).toEqual({
default: {
...localConfig,
type: appType,
device: undefined,
},
});
});

it.each([
['android.attached', 'android.apk'],
['android.emulator', 'android.apk'],
['android.genycloud', 'android.apk'],
Expand All @@ -75,6 +90,7 @@ describe('composeAppsConfig', () => {
...localConfig,
type: appType,
device: undefined,
permissions: undefined,
},
});
});
Expand Down
11 changes: 9 additions & 2 deletions detox/src/devices/runtime/RuntimeDevice.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const _ = require('lodash');

const DetoxRuntimeError = require('../../errors/DetoxRuntimeError');
const debug = require('../../utils/debug'); // debug utils, leave here even if unused
const { traceCall } = require('../../utils/trace');
Expand Down Expand Up @@ -56,6 +58,7 @@ class RuntimeDevice {
this._emitter = eventEmitter;
this._errorComposer = runtimeErrorComposer;

/** @type {Detox.DetoxAppConfig | null} */
this._currentApp = null;
this._currentAppLaunchArgs = new LaunchArgsEditor();
this._processes = {};
Expand Down Expand Up @@ -189,9 +192,13 @@ class RuntimeDevice {
}

async installApp(binaryPath, testBinaryPath) {
await traceCall('appInstall', () => {
await traceCall('appInstall', async () => {
/** @type {*} */
const currentApp = binaryPath ? { binaryPath, testBinaryPath } : this._getCurrentApp();
return this.deviceDriver.installApp(currentApp.binaryPath, currentApp.testBinaryPath);
await this.deviceDriver.installApp(currentApp.binaryPath, currentApp.testBinaryPath);
if (!_.isEmpty(currentApp.permissions)) {
await this.deviceDriver.setPermissions(currentApp.bundleId, currentApp.permissions);
}
Copy link
Collaborator

@d4vidi d4vidi Jul 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I didn't go deeply into this but something about this approach feels a bit off. It assumes that the permissions can be set in such a global way - before an instance of the app is launched. While it works on iOS, I can't 100% say the same about Android / a theoretical future platform.

What about applying a similar solution to the launch-args technique, which is set in advance and handled on-site (i.e. in launch app)?

});
}

Expand Down
26 changes: 26 additions & 0 deletions detox/src/devices/runtime/RuntimeDevice.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -687,9 +687,35 @@ describe('Device', () => {
expect(driverMock.driver.installApp).toHaveBeenCalledWith('newAppPath', device._deviceConfig.testBinaryPath);
});

it(`with a custom app permissions should setPermissions after installing`, async () => {
const device = await aValidDevice({
appsConfig: {
default: {
permissions: {
calendar: 'NO',
camera: 'YES',
},
},
},
});

driverMock.driver.setPermissions.mockRejectedValue(new Error('Should be called last!'));
driverMock.driver.installApp.mockImplementation(() => {
driverMock.driver.setPermissions.mockReset();
});

await device.installApp();
expect(driverMock.driver.setPermissions).toHaveBeenCalledWith('test.bundle', {
calendar: 'NO',
camera: 'YES',
});
});

it(`with no args should use the default path given in configuration`, async () => {
const device = await aValidDevice();
await device.installApp();

expect(driverMock.driver.setPermissions).not.toHaveBeenCalled();
expect(driverMock.driver.installApp).toHaveBeenCalledWith(device._currentApp.binaryPath, device._currentApp.testBinaryPath);
});
});
Expand Down
9 changes: 9 additions & 0 deletions detox/test/e2e/detox.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ const launchArgs = {
micro: 'soft',
};

const defaultPermissions = {
calendar: 'YES',
notifications: 'YES',
camera: 'YES',
photos: 'YES',
};

/** @type {Detox.DetoxConfig} */
const config = {
testRunner: 'nyc jest',
Expand Down Expand Up @@ -57,13 +64,15 @@ const config = {
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/example.app',
build: 'set -o pipefail && xcodebuild -workspace ios/example.xcworkspace -UseNewBuildSystem=YES -scheme example_ci -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build -quiet',
bundleId: 'com.wix.detox-example',
permissions: defaultPermissions,
},

'ios.release': {
type: 'ios.app',
name: 'example',
binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/example.app',
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',
permissions: defaultPermissions,
},

'android.debug': {
Expand Down
23 changes: 14 additions & 9 deletions docs/APIRef.Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,11 @@ The format of Detox config allows you to define inside it multiple app configs i
"ios.debug": {
"type": "ios.app",
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/example.app",
"build": "xcodebuild -project ios/example.xcodeproj -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build"
"build": "xcodebuild -project ios/example.xcodeproj -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"permissions": {
/** @see {Detox.DevicePermissions} */
notifications: 'YES',
},
},
"android.release": {
"type": "android.apk",
Expand All @@ -218,14 +222,15 @@ The format of Detox config allows you to define inside it multiple app configs i

An app config can have the following params:

| Configuration Params | Details |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | Mandatory property to discern app types: `ios.app`, `android.apk`. |
| `name` | Use only when working with multiple apps within the same configuration. See an example below. |
| `binaryPath` | Relative path to the ipa/app/apk due to be tested (make sure you build the app in a project relative path) |
| `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. |
| `testBinaryPath` | (optional, Android only): relative path to the test app (apk) |
| `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). |
| Configuration Params | Details |
|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | Mandatory property to discern app types: `ios.app`, `android.apk`. |
| `name` | Use only when working with multiple apps within the same configuration. See an example below. |
| `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. |
| `binaryPath` | Relative path to the ipa/app/apk due to be tested (make sure you build the app in a project relative path) |
| `testBinaryPath` | **\[optional, Android only]**: relative path to the test app (apk) |
| `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). |
| `permissions` | **\[optional, iOS only]** An object specifying default [runtime permissions](APIRef.DeviceObjectAPI.md#permissions-ios-only) to give to the app after installing. |

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

Expand Down
31 changes: 31 additions & 0 deletions docs/APIRef.DeviceObjectAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ await device.launchApp({permissions: {calendar: 'YES'}});

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).

As a reference, you can also use this [table of possible permissions](#permissions-ios-only).

##### 3. `url`—Launching with URL

Launches the app with the specified URL to test your app’s deep link handling mechanism.
Expand Down Expand Up @@ -528,3 +530,32 @@ Exposes [`UiAutomator`’s `UiDevice` API](https://developer.android.com/referen
**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.

[`UiDevice`’s autogenerated code](https://github.com/wix/Detox/tree/a9a09246c05733f6b91cfcc0dba05a4714abca92/detox/src/android/espressoapi/UIDevice.js)

### Permissions (iOS only)
Copy link
Collaborator

@d4vidi d4vidi Jul 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add link in ToC


When you are [configuring the apps](APIRef.Configuration.md#apps-configurations) list in your
Detox config file or [launching the app](#2-permissionsset-runtime-permissions-ios-only),
you can give certain permissions beforehand to it to avoid this inconvenient modal:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A simple and elegant explanation


![iOS permissions modal](img/ios-permissions.png)

So, here is the exhaustive permissions list that Detox currently supports for iOS:

| Permission | Values |
|---------------|---------------------------------------------------------|
| calendar | `'YES' &#124; 'NO' &#124; 'unset'` |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&124; doesn't seem to render well

| camera | `'YES' &#124; 'NO' &#124; 'unset'` |
| contacts | `'YES' &#124; 'NO' &#124; 'unset'` |
| faceid | `'YES' &#124; 'NO' &#124; 'unset'` |
| health | `'YES' &#124; 'NO' &#124; 'unset'` |
| homekit | `'YES' &#124; 'NO' &#124; 'unset'` |
| location | `'always' &#124; 'inuse' &#124; 'never' &#124; 'unset'` |
| medialibrary | `'YES' &#124; 'NO' &#124; 'unset'` |
| microphone | `'YES' &#124; 'NO' &#124; 'unset'` |
| motion | `'YES' &#124; 'NO' &#124; 'unset'` |
| notification | `'YES' &#124; 'NO' &#124; 'unset'` |
| photos | `'YES' &#124; 'NO' &#124; 'unset'` |
| reminders | `'YES' &#124; 'NO' &#124; 'unset'` |
| siri | `'YES' &#124; 'NO' &#124; 'unset'` |
| speech | `'YES' &#124; 'NO' &#124; 'unset'` |
| userTracking | `'YES' &#124; 'NO' &#124; 'unset'` |
Binary file added docs/img/ios-permissions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.