1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.jetpackcamera 17 18 import androidx.compose.ui.test.isDisplayed 19 import androidx.compose.ui.test.isNotDisplayed 20 import androidx.compose.ui.test.junit4.createEmptyComposeRule 21 import androidx.compose.ui.test.onNodeWithTag 22 import androidx.compose.ui.test.performClick 23 import androidx.test.ext.junit.runners.AndroidJUnit4 24 import androidx.test.filters.SdkSuppress 25 import androidx.test.platform.app.InstrumentationRegistry 26 import androidx.test.uiautomator.UiDevice 27 import com.google.jetpackcamera.feature.preview.ui.CAPTURE_BUTTON 28 import com.google.jetpackcamera.permissions.ui.CAMERA_PERMISSION_BUTTON 29 import com.google.jetpackcamera.permissions.ui.RECORD_AUDIO_PERMISSION_BUTTON 30 import com.google.jetpackcamera.permissions.ui.REQUEST_PERMISSION_BUTTON 31 import com.google.jetpackcamera.utils.APP_REQUIRED_PERMISSIONS 32 import com.google.jetpackcamera.utils.APP_START_TIMEOUT_MILLIS 33 import com.google.jetpackcamera.utils.IndividualTestGrantPermissionRule 34 import com.google.jetpackcamera.utils.askEveryTimeDialog 35 import com.google.jetpackcamera.utils.denyPermissionDialog 36 import com.google.jetpackcamera.utils.grantPermissionDialog 37 import com.google.jetpackcamera.utils.onNodeWithText 38 import com.google.jetpackcamera.utils.runScenarioTest 39 import org.junit.Rule 40 import org.junit.Test 41 import org.junit.runner.RunWith 42 43 const val CAMERA_PERMISSION = "android.permission.CAMERA" 44 45 @RunWith(AndroidJUnit4::class) 46 class PermissionsTest { 47 @get:Rule 48 val composeTestRule = createEmptyComposeRule() 49 50 @get:Rule 51 val allPermissionsRule = IndividualTestGrantPermissionRule( 52 permissions = APP_REQUIRED_PERMISSIONS.toTypedArray(), 53 targetTestNames = arrayOf( 54 "allPermissions_alreadyGranted_screenNotShown" 55 ) 56 ) 57 58 @get:Rule 59 val cameraPermissionRule = IndividualTestGrantPermissionRule( 60 permissions = arrayOf(CAMERA_PERMISSION), 61 targetTestNames = arrayOf( 62 "recordAudioPermission_granted_closesPage", 63 "recordAudioPermission_denied_closesPage" 64 ) 65 ) 66 67 private val instrumentation = InstrumentationRegistry.getInstrumentation() 68 private val uiDevice = UiDevice.getInstance(instrumentation) 69 70 @Test allPermissions_alreadyGranted_screenNotShownnull71 fun allPermissions_alreadyGranted_screenNotShown() { 72 runScenarioTest<MainActivity> { 73 composeTestRule.waitUntil(timeoutMillis = APP_START_TIMEOUT_MILLIS) { 74 composeTestRule.onNodeWithTag(CAPTURE_BUTTON).isDisplayed() 75 } 76 } 77 } 78 79 @Test <lambda>null80 fun cameraPermission_granted_closesPage() = runScenarioTest<MainActivity> { 81 // Wait for the camera permission screen to be displayed 82 composeTestRule.waitUntil(timeoutMillis = APP_START_TIMEOUT_MILLIS) { 83 composeTestRule.onNodeWithTag(CAMERA_PERMISSION_BUTTON).isDisplayed() 84 } 85 86 // Click button to request permission 87 composeTestRule.onNodeWithTag(REQUEST_PERMISSION_BUTTON) 88 .assertExists() 89 .performClick() 90 91 uiDevice.waitForIdle() 92 // grant permission 93 uiDevice.grantPermissionDialog() 94 95 // Assert we're no longer on camera permission screen 96 composeTestRule.onNodeWithTag(CAMERA_PERMISSION_BUTTON).assertDoesNotExist() 97 } 98 99 @SdkSuppress(minSdkVersion = 30) 100 @Test cameraPermission_askEveryTime_closesPagenull101 fun cameraPermission_askEveryTime_closesPage() { 102 uiDevice.waitForIdle() 103 runScenarioTest<MainActivity> { 104 // Wait for the camera permission screen to be displayed 105 composeTestRule.waitUntil(timeoutMillis = APP_START_TIMEOUT_MILLIS) { 106 composeTestRule.onNodeWithTag(CAMERA_PERMISSION_BUTTON).isDisplayed() 107 } 108 109 // Click button to request permission 110 composeTestRule.onNodeWithTag(REQUEST_PERMISSION_BUTTON) 111 .assertExists() 112 .performClick() 113 114 // set permission to ask every time 115 uiDevice.askEveryTimeDialog() 116 117 // Assert we're no longer on camera permission screen 118 composeTestRule.onNodeWithTag(CAMERA_PERMISSION_BUTTON).assertDoesNotExist() 119 } 120 } 121 122 @Test cameraPermission_declined_staysOnScreennull123 fun cameraPermission_declined_staysOnScreen() { 124 // required permissions should persist on screen 125 // Wait for the permission screen to be displayed 126 runScenarioTest<MainActivity> { 127 composeTestRule.waitUntil(timeoutMillis = APP_START_TIMEOUT_MILLIS) { 128 composeTestRule.onNodeWithTag(CAMERA_PERMISSION_BUTTON).isDisplayed() 129 } 130 131 // Click button to request permission 132 composeTestRule.onNodeWithTag(REQUEST_PERMISSION_BUTTON) 133 .assertExists() 134 .performClick() 135 136 // deny permission 137 uiDevice.denyPermissionDialog() 138 139 uiDevice.waitForIdle() 140 141 // Assert we're still on camera permission screen 142 composeTestRule.onNodeWithTag(CAMERA_PERMISSION_BUTTON).isDisplayed() 143 144 // text changed after permission denied 145 composeTestRule.waitUntil(timeoutMillis = APP_START_TIMEOUT_MILLIS) { 146 composeTestRule.onNodeWithText( 147 com.google.jetpackcamera.permissions.R.string 148 .camera_permission_declined_rationale 149 ) 150 .isDisplayed() 151 } 152 // request permissions button should now say to navigate to settings 153 composeTestRule.onNodeWithText( 154 com.google.jetpackcamera.permissions 155 .R.string.navigate_to_settings 156 ).assertExists() 157 } 158 } 159 160 @Test recordAudioPermission_granted_closesPagenull161 fun recordAudioPermission_granted_closesPage() { 162 // optional permissions should close the screen after declining 163 runScenarioTest<MainActivity> { 164 composeTestRule.waitUntil(timeoutMillis = APP_START_TIMEOUT_MILLIS) { 165 composeTestRule.onNodeWithTag(RECORD_AUDIO_PERMISSION_BUTTON).isDisplayed() 166 } 167 168 // Click button to request permission 169 composeTestRule.onNodeWithTag(REQUEST_PERMISSION_BUTTON) 170 .assertExists() 171 .performClick() 172 173 // grant permission 174 uiDevice.grantPermissionDialog() 175 uiDevice.waitForIdle() 176 177 // Assert we're on a different page 178 composeTestRule.waitUntil(timeoutMillis = APP_START_TIMEOUT_MILLIS) { 179 composeTestRule.onNodeWithTag(RECORD_AUDIO_PERMISSION_BUTTON).isNotDisplayed() 180 } 181 } 182 } 183 184 @Test recordAudioPermission_denied_closesPagenull185 fun recordAudioPermission_denied_closesPage() { 186 // optional permissions should close the screen after declining 187 runScenarioTest<MainActivity> { 188 composeTestRule.waitUntil(timeoutMillis = APP_START_TIMEOUT_MILLIS) { 189 composeTestRule.onNodeWithTag(RECORD_AUDIO_PERMISSION_BUTTON).isDisplayed() 190 } 191 192 // Click button to request permission 193 composeTestRule.onNodeWithTag(REQUEST_PERMISSION_BUTTON) 194 .assertExists() 195 .performClick() 196 197 // deny permission 198 uiDevice.denyPermissionDialog() 199 uiDevice.waitForIdle() 200 201 // Assert we're on a different page 202 composeTestRule.waitUntil(timeoutMillis = APP_START_TIMEOUT_MILLIS) { 203 composeTestRule.onNodeWithTag(RECORD_AUDIO_PERMISSION_BUTTON).isNotDisplayed() 204 } 205 } 206 } 207 } 208