wix / Detox

Gray box end-to-end testing and automation framework for mobile apps
https://wix.github.io/Detox/
MIT License
11.05k stars 1.91k forks source link

Support for Android permission flows #2184

Open d4vidi opened 4 years ago

d4vidi commented 4 years ago

Is your feature request related to a problem? Please describe.

Currently, Detox launches all apps with all runtime permissions requested by the app (in the manifest xml) enabled in advance - as we use pm install -g, (i.e. -g: grant all runtime permissions). Need to allow for more user-side control over this, so as to allow for the testing of permission flows.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you believe the issue is still relevant, please test on the latest Detox and report back.

Thank you for your contributions!

For more information on bots in this reporsitory, read this discussion.

Andarius commented 3 years ago

In the meantime, is there a way to execute something like adb -e shell pm reset-permissions with the detox -c android.emu.debug test command ?

arcellaa commented 3 years ago

@Andarius You can write something like that in your init.ts

import { execSync } from 'child_process';
//...
beforeEach(async function () {
  execSync(`adb -e -s ${device.id} shell pm reset-permissions`);
  await device.launchApp({    newInstance: true  });
});
rajithaswoop commented 3 years ago

Permission Issue with Android : I have this issue on Detox 18.13.0, React Native 0.63.4, API 30 for my Android emulator. It works perfectly for camera permission on iOS but on Android, whatever I set NO or YES on permissions, they are always enabled.

If I change theses permissions on the app before launching detox test, all permissions are enabled again. @LeoNatan @noomorph

d4vidi commented 3 years ago

@rajithaswoop please read the issue's original description 😄

Myrfion commented 1 year ago

@arcellaa @d4vidi Hi, I am trying to make the android permissions flow work with the instructions that you provided, but the app seems to stuck on launch and I am getting something like that, even tho app launches and I can see native location permission popup:

23:46:23.177 detox[506] i The app is busy with the following tasks:
• UI elements are busy:
  - Reason: Animations running on screen.
• There are enqueued timers.
23:46:33.199 detox[506] i The app is busy with the following tasks:
• UI elements are busy:
  - Reason: Animations running on screen.
• There are enqueued timers.
23:46:43.247 detox[506] i The app is busy with the following tasks:
• UI elements are busy:
  - Reason: Animations running on screen.
• There are enqueued timers.

Disabling synchronization doesn't really seem to work if run it like await device.disableSynchronization() or if I disable it in launchApp

await device.launchApp({
  newInstance: true,
  launchArgs: { detoxEnableSynchronization: 0 }
});

If I do that, instead of the first message I get The app seems to be idle and after showing it several times it says:

✕ Go through permissions flow with manually selectign permissions (34383 ms)

  ● Permissions Flow › Go through permissions flow with manually selectign permissions

    Test Failed: No activities in stage RESUMED. Did you forget to launch the activity. (test.getActivity() or similar)?

      35 |
      36 |   static async selectLocationPermissionDialogOption(option: LocationPermissionnNativeDialogOptions) {
    > 37 |     await expect(element(by.text('Allow'))).toBeVisible()
         |                                             ^
      38 |
      39 |     await element(by.text(locationPermissionsNativeDialogOptionsText[option])).tap()
      40 |

      at Function.selectLocationPermissionDialogOption (src/actions/Permissions.ts:37:45)
      at Object.<anonymous> (src/tests/permissions-flow.e2e.ts:14:23)

I would really appreciate any thoughts on what might be the problem

Neeal20 commented 5 months ago

@Myrfion Hello, did you manage to find a solution ?

SarjuHansaliya commented 1 month ago

For anyone looking for a workaround, currently I am using adb command to grant camera and other permissions.

Here is code I am using

export const grantAndroidCameraPermission = async (
  bundleIdentifier: string
) => {
  execSync(`adb shell pm grant ${bundleIdentifier} android.permission.CAMERA`);
};

And I run this as soon as launch the app

beforeEach(async () => {
  await launchApp();
  await grantAndroidCameraPermission("com.xyz.abc");
});
rubenwardy commented 1 month ago

I'd love this feature, it would have saved me a lot of time.

Instead, I found two different solutions that allow interacting with the Android permissions dialog:

Would appreciate any thoughts on this. See https://blog.rubenwardy.com/2024/05/24/detox-permissions/

ace-n commented 2 weeks ago

Hey folks!

I did some digging on this; here's the implementation I had in mind (with a few clarifying questions at the bottom).

@noomorph @gosha212 LMK if I should create a PR for this! 🙂

High-level approach

  1. AndroidDriver.js: Add a pass-through setPermissions method that calls out to ADB.js.

  2. ADB.js: Add a setPermissions method that directly invokes pm {grant/revoke} calls based on its permissions argument:

// Example `permissions` value
const permissions = { ACCESS_FINE_LOCATION: 'grant' };

...

// Pseudocode
async setPermissions(permissions) {
    for (const [permission, verb] of Object.entries(permissions)) {
        await this.adb.shell(`pm ${verb} android.permission.${permission}`)
    }
}

Lower-level questions

1) Will we need a separate install() function/argument for permissions-based installs (to avoid/disable the -g flag on pm install respectively), or can we update permissions dynamically post-initial-installation? (My current design assumes the latter approach will suffice.) 2) How should the permissions argument be structured? I assume we should have explicit grant and revoke string-values, to match iOS' style for yes/no/etc?