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 17 package com.android.photopicker.features.cloudmedia 18 19 import android.app.Instrumentation.ActivityMonitor 20 import android.content.ContentResolver 21 import android.content.Context 22 import android.content.Intent 23 import android.content.IntentFilter 24 import android.content.pm.PackageManager 25 import android.os.UserManager 26 import android.provider.MediaStore 27 import android.test.mock.MockContentResolver 28 import androidx.compose.ui.test.ExperimentalTestApi 29 import androidx.compose.ui.test.assertIsDisplayed 30 import androidx.compose.ui.test.assertIsNotDisplayed 31 import androidx.compose.ui.test.hasContentDescription 32 import androidx.compose.ui.test.hasText 33 import androidx.compose.ui.test.junit4.createAndroidComposeRule 34 import androidx.compose.ui.test.performClick 35 import androidx.test.platform.app.InstrumentationRegistry 36 import com.android.photopicker.R 37 import com.android.photopicker.core.ActivityModule 38 import com.android.photopicker.core.ApplicationModule 39 import com.android.photopicker.core.ApplicationOwned 40 import com.android.photopicker.core.Background 41 import com.android.photopicker.core.ConcurrencyModule 42 import com.android.photopicker.core.EmbeddedServiceModule 43 import com.android.photopicker.core.Main 44 import com.android.photopicker.core.banners.BannerDefinitions 45 import com.android.photopicker.core.banners.BannerManager 46 import com.android.photopicker.core.banners.BannerState 47 import com.android.photopicker.core.banners.BannerStateDao 48 import com.android.photopicker.core.configuration.ConfigurationManager 49 import com.android.photopicker.core.configuration.DeviceConfigProxy 50 import com.android.photopicker.core.configuration.FEATURE_CLOUD_ENFORCE_PROVIDER_ALLOWLIST 51 import com.android.photopicker.core.configuration.FEATURE_CLOUD_MEDIA_FEATURE_ENABLED 52 import com.android.photopicker.core.configuration.FEATURE_CLOUD_MEDIA_PROVIDER_ALLOWLIST 53 import com.android.photopicker.core.configuration.NAMESPACE_MEDIAPROVIDER 54 import com.android.photopicker.core.configuration.PhotopickerFlags 55 import com.android.photopicker.core.configuration.TestDeviceConfigProxyImpl 56 import com.android.photopicker.core.configuration.TestPhotopickerConfiguration 57 import com.android.photopicker.core.database.DatabaseManager 58 import com.android.photopicker.core.events.Events 59 import com.android.photopicker.core.features.FeatureManager 60 import com.android.photopicker.core.glide.GlideTestRule 61 import com.android.photopicker.core.selection.Selection 62 import com.android.photopicker.data.DataService 63 import com.android.photopicker.data.TestDataServiceImpl 64 import com.android.photopicker.data.model.CollectionInfo 65 import com.android.photopicker.data.model.Media 66 import com.android.photopicker.data.model.MediaSource 67 import com.android.photopicker.data.model.Provider 68 import com.android.photopicker.features.PhotopickerFeatureBaseTest 69 import com.android.photopicker.features.overflowmenu.OverflowMenuFeature 70 import com.android.photopicker.inject.PhotopickerTestModule 71 import com.android.photopicker.tests.HiltTestActivity 72 import com.android.photopicker.util.test.nonNullableEq 73 import com.android.photopicker.util.test.whenever 74 import com.google.common.truth.Truth.assertWithMessage 75 import dagger.Lazy 76 import dagger.Module 77 import dagger.hilt.InstallIn 78 import dagger.hilt.android.testing.BindValue 79 import dagger.hilt.android.testing.HiltAndroidRule 80 import dagger.hilt.android.testing.HiltAndroidTest 81 import dagger.hilt.android.testing.UninstallModules 82 import dagger.hilt.components.SingletonComponent 83 import javax.inject.Inject 84 import kotlinx.coroutines.CoroutineDispatcher 85 import kotlinx.coroutines.CoroutineScope 86 import kotlinx.coroutines.ExperimentalCoroutinesApi 87 import kotlinx.coroutines.test.StandardTestDispatcher 88 import kotlinx.coroutines.test.TestScope 89 import kotlinx.coroutines.test.advanceTimeBy 90 import kotlinx.coroutines.test.runTest 91 import org.junit.Before 92 import org.junit.Rule 93 import org.junit.Test 94 import org.mockito.Mock 95 import org.mockito.Mockito.anyInt 96 import org.mockito.MockitoAnnotations 97 98 @UninstallModules( 99 ActivityModule::class, 100 ApplicationModule::class, 101 ConcurrencyModule::class, 102 EmbeddedServiceModule::class, 103 ) 104 @HiltAndroidTest 105 @OptIn(ExperimentalCoroutinesApi::class, ExperimentalTestApi::class) 106 class CloudMediaFeatureTest : PhotopickerFeatureBaseTest() { 107 108 /* Hilt's rule needs to come first to ensure the DI container is setup for the test. */ 109 @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) 110 @get:Rule(order = 1) 111 val composeTestRule = createAndroidComposeRule(activityClass = HiltTestActivity::class.java) 112 @get:Rule(order = 2) val glideRule = GlideTestRule() 113 114 /* Setup dependencies for the UninstallModules for the test class. */ 115 @Module @InstallIn(SingletonComponent::class) class TestModule : PhotopickerTestModule() 116 117 val testDispatcher = StandardTestDispatcher() 118 119 /* Overrides for ActivityModule */ 120 val testScope: TestScope = TestScope(testDispatcher) 121 @BindValue @Main val mainScope: CoroutineScope = testScope 122 @BindValue @Background var testBackgroundScope: CoroutineScope = testScope.backgroundScope 123 124 /* Overrides for the ConcurrencyModule */ 125 @BindValue @Main val mainDispatcher: CoroutineDispatcher = testDispatcher 126 @BindValue @Background val backgroundDispatcher: CoroutineDispatcher = testDispatcher 127 128 @BindValue @ApplicationOwned val contentResolver: ContentResolver = MockContentResolver() 129 130 @Inject lateinit var selection: Lazy<Selection<Media>> 131 @Inject lateinit var featureManager: Lazy<FeatureManager> 132 @Inject lateinit var bannerManager: Lazy<BannerManager> 133 @Inject lateinit var events: Lazy<Events> 134 @Inject lateinit var dataService: Lazy<DataService> 135 @Inject lateinit var databaseManager: DatabaseManager 136 @Inject override lateinit var configurationManager: Lazy<ConfigurationManager> 137 138 // Needed for UserMonitor 139 @Inject lateinit var mockContext: Context 140 @Inject lateinit var deviceConfig: DeviceConfigProxy 141 @Mock lateinit var mockUserManager: UserManager 142 @Mock lateinit var mockPackageManager: PackageManager 143 144 private val localProvider = 145 Provider( 146 authority = "local_authority", 147 mediaSource = MediaSource.LOCAL, 148 uid = 1, 149 displayName = "Local Provider", 150 ) 151 private val cloudProvider = 152 Provider( 153 authority = "clout_authority", 154 mediaSource = MediaSource.REMOTE, 155 uid = 2, 156 displayName = "Cloud Provider", 157 ) 158 159 @Before setupnull160 fun setup() { 161 MockitoAnnotations.initMocks(this) 162 hiltRule.inject() 163 164 val testDeviceConfigProxy = 165 checkNotNull(deviceConfig as? TestDeviceConfigProxyImpl) { 166 "Expected a TestDeviceConfigProxy" 167 } 168 169 testDeviceConfigProxy.setFlag( 170 NAMESPACE_MEDIAPROVIDER, 171 FEATURE_CLOUD_MEDIA_FEATURE_ENABLED.first, 172 true, 173 ) 174 testDeviceConfigProxy.setFlag( 175 NAMESPACE_MEDIAPROVIDER, 176 FEATURE_CLOUD_ENFORCE_PROVIDER_ALLOWLIST.first, 177 true, 178 ) 179 testDeviceConfigProxy.setFlag( 180 NAMESPACE_MEDIAPROVIDER, 181 FEATURE_CLOUD_MEDIA_PROVIDER_ALLOWLIST.first, 182 "com.android.test.cloudpicker", 183 ) 184 185 configurationManager 186 .get() 187 .setCaller( 188 callingPackage = "com.android.test.package", 189 callingPackageUid = 12345, 190 callingPackageLabel = "Test Package", 191 ) 192 // Stub for MockContentResolver constructor 193 whenever(mockContext.getApplicationInfo()) { getTestableContext().getApplicationInfo() } 194 195 setupTestForUserMonitor(mockContext, mockUserManager, contentResolver, mockPackageManager) 196 } 197 198 @Test testCloudMediaEnabledInConfigurationsnull199 fun testCloudMediaEnabledInConfigurations() { 200 assertWithMessage("CloudMediaFeature is not always enabled (ACTION_PICK_IMAGES)") 201 .that( 202 CloudMediaFeature.Registration.isEnabled( 203 TestPhotopickerConfiguration.build { 204 action(MediaStore.ACTION_PICK_IMAGES) 205 intent(Intent(MediaStore.ACTION_PICK_IMAGES)) 206 flags( 207 PhotopickerFlags( 208 CLOUD_MEDIA_ENABLED = true, 209 CLOUD_ALLOWED_PROVIDERS = arrayOf("cloud_authority"), 210 ) 211 ) 212 } 213 ) 214 ) 215 .isEqualTo(true) 216 217 assertWithMessage("CloudMediaFeature is enabled with invalid flags (ACTION_PICK_IMAGES)") 218 .that( 219 CloudMediaFeature.Registration.isEnabled( 220 TestPhotopickerConfiguration.build { 221 action(MediaStore.ACTION_PICK_IMAGES) 222 intent(Intent(MediaStore.ACTION_PICK_IMAGES)) 223 } 224 ) 225 ) 226 .isEqualTo(false) 227 228 assertWithMessage("CloudMediaFeature is not always enabled (ACTION_GET_CONTENT)") 229 .that( 230 CloudMediaFeature.Registration.isEnabled( 231 TestPhotopickerConfiguration.build { 232 action(Intent.ACTION_GET_CONTENT) 233 intent(Intent(Intent.ACTION_GET_CONTENT)) 234 flags( 235 PhotopickerFlags( 236 CLOUD_MEDIA_ENABLED = true, 237 CLOUD_ALLOWED_PROVIDERS = arrayOf("cloud_authority"), 238 ) 239 ) 240 } 241 ) 242 ) 243 .isEqualTo(true) 244 245 assertWithMessage("CloudMediaFeature is enabled with invalid flags (ACTION_GET_CONTENT)") 246 .that( 247 CloudMediaFeature.Registration.isEnabled( 248 TestPhotopickerConfiguration.build { 249 action(Intent.ACTION_GET_CONTENT) 250 intent(Intent(Intent.ACTION_GET_CONTENT)) 251 } 252 ) 253 ) 254 .isEqualTo(false) 255 256 assertWithMessage("CloudMediaFeature is not always disabled (USER_SELECT_FOR_APP)") 257 .that( 258 CloudMediaFeature.Registration.isEnabled( 259 TestPhotopickerConfiguration.build { 260 action(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) 261 intent(Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP)) 262 callingPackage("com.example.test") 263 callingPackageUid(1234) 264 callingPackageLabel("test_app") 265 flags( 266 PhotopickerFlags( 267 CLOUD_MEDIA_ENABLED = true, 268 CLOUD_ALLOWED_PROVIDERS = arrayOf("cloud_authority"), 269 ) 270 ) 271 } 272 ) 273 ) 274 .isEqualTo(false) 275 276 assertWithMessage( 277 "CloudMediaFeature is not always disabled (default flags) (USER_SELECT_FOR_APP)" 278 ) 279 .that( 280 CloudMediaFeature.Registration.isEnabled( 281 TestPhotopickerConfiguration.build { 282 action(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) 283 intent(Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP)) 284 callingPackage("com.example.test") 285 callingPackageUid(1234) 286 callingPackageLabel("test_app") 287 } 288 ) 289 ) 290 .isEqualTo(false) 291 } 292 293 @Test testCloudMediaOverflowMenuItemIsDisplayednull294 fun testCloudMediaOverflowMenuItemIsDisplayed() = 295 testScope.runTest { 296 composeTestRule.setContent { 297 callPhotopickerMain( 298 featureManager = featureManager.get(), 299 selection = selection.get(), 300 events = events.get(), 301 ) 302 } 303 304 assertWithMessage("OverflowMenuFeature is not enabled") 305 .that(featureManager.get().isFeatureEnabled(OverflowMenuFeature::class.java)) 306 .isTrue() 307 308 composeTestRule 309 .onNode( 310 hasContentDescription( 311 getTestableContext() 312 .getResources() 313 .getString(R.string.photopicker_overflow_menu_description) 314 ) 315 ) 316 .performClick() 317 318 composeTestRule 319 .onNode( 320 hasText( 321 getTestableContext() 322 .getResources() 323 .getString(R.string.photopicker_overflow_cloud_media_app) 324 ) 325 ) 326 .assertIsDisplayed() 327 } 328 329 @Test testCloudMediaOverflowMenuItemLaunchesCloudSettingsnull330 fun testCloudMediaOverflowMenuItemLaunchesCloudSettings() = 331 testScope.runTest { 332 333 // Setup an intentFilter that matches the settings action 334 val intentFilter = 335 IntentFilter().apply { addAction(MediaStore.ACTION_PICK_IMAGES_SETTINGS) } 336 337 // Setup an activityMonitor to catch any launched intents to settings 338 val activityMonitor = 339 ActivityMonitor(intentFilter, /* result= */ null, /* block= */ true) 340 InstrumentationRegistry.getInstrumentation().addMonitor(activityMonitor) 341 342 composeTestRule.setContent { 343 callPhotopickerMain( 344 featureManager = featureManager.get(), 345 selection = selection.get(), 346 events = events.get(), 347 ) 348 } 349 350 assertWithMessage("OverflowMenuFeature is not enabled") 351 .that(featureManager.get().isFeatureEnabled(OverflowMenuFeature::class.java)) 352 .isTrue() 353 354 composeTestRule 355 .onNode( 356 hasContentDescription( 357 getTestableContext() 358 .getResources() 359 .getString(R.string.photopicker_overflow_menu_description) 360 ) 361 ) 362 .performClick() 363 364 composeTestRule 365 .onNode( 366 hasText( 367 getTestableContext() 368 .getResources() 369 .getString(R.string.photopicker_overflow_cloud_media_app) 370 ) 371 ) 372 .assertIsDisplayed() 373 .performClick() 374 375 activityMonitor.waitForActivityWithTimeout(5000L) 376 assertWithMessage("Settings activity wasn't launched") 377 .that(activityMonitor.getHits()) 378 .isEqualTo(1) 379 } 380 381 @Test testCloudMediaAvailableBannernull382 fun testCloudMediaAvailableBanner() = 383 testScope.runTest { 384 val bannerStateDao = databaseManager.acquireDao(BannerStateDao::class.java) 385 386 // Treat privacy explainer as already dismissed since it's a higher priority. 387 whenever( 388 bannerStateDao.getBannerState( 389 nonNullableEq(BannerDefinitions.PRIVACY_EXPLAINER.id), 390 anyInt(), 391 ) 392 ) { 393 BannerState( 394 bannerId = BannerDefinitions.PRIVACY_EXPLAINER.id, 395 dismissed = true, 396 uid = 12345, 397 ) 398 } 399 whenever( 400 bannerStateDao.getBannerState( 401 nonNullableEq(BannerDefinitions.CLOUD_MEDIA_AVAILABLE.id), 402 anyInt(), 403 ) 404 ) { 405 null 406 } 407 408 val testDataService = dataService.get() as? TestDataServiceImpl 409 checkNotNull(testDataService) { "Expected a TestDataServiceImpl" } 410 testDataService.setAvailableProviders(listOf(localProvider, cloudProvider)) 411 testDataService.collectionInfo.put( 412 cloudProvider, 413 CollectionInfo( 414 authority = cloudProvider.authority, 415 collectionId = "collection-id", 416 accountName = "[email protected]", 417 accountConfigurationIntent = Intent(), 418 ), 419 ) 420 421 val resources = getTestableContext().getResources() 422 val expectedTitle = 423 resources.getString(R.string.photopicker_banner_cloud_media_available_title) 424 val expectedMessage = 425 resources.getString( 426 R.string.photopicker_banner_cloud_media_available_message, 427 cloudProvider.displayName, 428 "[email protected]", 429 ) 430 431 bannerManager.get().refreshBanners() 432 advanceTimeBy(100) 433 composeTestRule.setContent { 434 callPhotopickerMain( 435 featureManager = featureManager.get(), 436 selection = selection.get(), 437 events = events.get(), 438 ) 439 } 440 composeTestRule.waitForIdle() 441 composeTestRule.onNode(hasText(expectedTitle)).assertIsDisplayed() 442 composeTestRule.onNode(hasText(expectedMessage)).assertIsDisplayed() 443 } 444 445 @Test testCloudMediaAvailableBannerAsDismissednull446 fun testCloudMediaAvailableBannerAsDismissed() = 447 testScope.runTest { 448 val bannerStateDao = databaseManager.acquireDao(BannerStateDao::class.java) 449 450 // Treat privacy explainer as already dismissed since it's a higher priority. 451 whenever( 452 bannerStateDao.getBannerState( 453 nonNullableEq(BannerDefinitions.PRIVACY_EXPLAINER.id), 454 anyInt(), 455 ) 456 ) { 457 BannerState( 458 bannerId = BannerDefinitions.PRIVACY_EXPLAINER.id, 459 dismissed = true, 460 uid = 12345, 461 ) 462 } 463 whenever( 464 bannerStateDao.getBannerState( 465 nonNullableEq(BannerDefinitions.CLOUD_MEDIA_AVAILABLE.id), 466 anyInt(), 467 ) 468 ) { 469 BannerState( 470 bannerId = BannerDefinitions.CLOUD_MEDIA_AVAILABLE.id, 471 dismissed = true, 472 uid = 12345, 473 ) 474 } 475 476 val testDataService = dataService.get() as? TestDataServiceImpl 477 checkNotNull(testDataService) { "Expected a TestDataServiceImpl" } 478 testDataService.setAvailableProviders(listOf(localProvider, cloudProvider)) 479 testDataService.collectionInfo.put( 480 cloudProvider, 481 CollectionInfo( 482 authority = cloudProvider.authority, 483 collectionId = "collection-id", 484 accountName = "[email protected]", 485 accountConfigurationIntent = Intent(), 486 ), 487 ) 488 489 val resources = getTestableContext().getResources() 490 val expectedTitle = 491 resources.getString(R.string.photopicker_banner_cloud_media_available_title) 492 val expectedMessage = 493 resources.getString( 494 R.string.photopicker_banner_cloud_media_available_message, 495 cloudProvider.displayName, 496 "[email protected]", 497 ) 498 499 bannerManager.get().refreshBanners() 500 advanceTimeBy(100) 501 composeTestRule.setContent { 502 callPhotopickerMain( 503 featureManager = featureManager.get(), 504 selection = selection.get(), 505 events = events.get(), 506 ) 507 } 508 composeTestRule.waitForIdle() 509 composeTestRule.onNode(hasText(expectedTitle)).assertIsNotDisplayed() 510 composeTestRule.onNode(hasText(expectedMessage)).assertIsNotDisplayed() 511 } 512 513 @Test testCloudChooseAccountBannernull514 fun testCloudChooseAccountBanner() = 515 testScope.runTest { 516 val bannerStateDao = databaseManager.acquireDao(BannerStateDao::class.java) 517 518 // Treat privacy explainer as already dismissed since it's a higher priority. 519 whenever( 520 bannerStateDao.getBannerState( 521 nonNullableEq(BannerDefinitions.PRIVACY_EXPLAINER.id), 522 anyInt(), 523 ) 524 ) { 525 BannerState( 526 bannerId = BannerDefinitions.PRIVACY_EXPLAINER.id, 527 dismissed = true, 528 uid = 12345, 529 ) 530 } 531 532 val testDataService = dataService.get() as? TestDataServiceImpl 533 checkNotNull(testDataService) { "Expected a TestDataServiceImpl" } 534 testDataService.setAvailableProviders(listOf(localProvider, cloudProvider)) 535 testDataService.collectionInfo.put( 536 cloudProvider, 537 CollectionInfo( 538 authority = cloudProvider.authority, 539 collectionId = null, 540 accountName = null, 541 accountConfigurationIntent = Intent(), 542 ), 543 ) 544 545 val resources = getTestableContext().getResources() 546 val expectedTitle = 547 resources.getString(R.string.photopicker_banner_cloud_choose_account_title) 548 val expectedMessage = 549 resources.getString( 550 R.string.photopicker_banner_cloud_choose_account_message, 551 cloudProvider.displayName, 552 ) 553 554 bannerManager.get().refreshBanners() 555 advanceTimeBy(100) 556 composeTestRule.setContent { 557 callPhotopickerMain( 558 featureManager = featureManager.get(), 559 selection = selection.get(), 560 events = events.get(), 561 ) 562 } 563 composeTestRule.waitForIdle() 564 composeTestRule.onNode(hasText(expectedTitle)).assertIsDisplayed() 565 composeTestRule.onNode(hasText(expectedMessage)).assertIsDisplayed() 566 } 567 568 @Test testCloudChooseAccountBannerAsDismissednull569 fun testCloudChooseAccountBannerAsDismissed() = 570 testScope.runTest { 571 val bannerStateDao = databaseManager.acquireDao(BannerStateDao::class.java) 572 573 // Treat privacy explainer as already dismissed since it's a higher priority. 574 whenever( 575 bannerStateDao.getBannerState( 576 nonNullableEq(BannerDefinitions.PRIVACY_EXPLAINER.id), 577 anyInt(), 578 ) 579 ) { 580 BannerState( 581 bannerId = BannerDefinitions.PRIVACY_EXPLAINER.id, 582 dismissed = true, 583 uid = 12345, 584 ) 585 } 586 587 whenever( 588 bannerStateDao.getBannerState( 589 nonNullableEq(BannerDefinitions.CLOUD_CHOOSE_ACCOUNT.id), 590 anyInt(), 591 ) 592 ) { 593 BannerState( 594 bannerId = BannerDefinitions.CLOUD_CHOOSE_ACCOUNT.id, 595 dismissed = true, 596 uid = 12345, 597 ) 598 } 599 600 val testDataService = dataService.get() as? TestDataServiceImpl 601 checkNotNull(testDataService) { "Expected a TestDataServiceImpl" } 602 testDataService.setAvailableProviders(listOf(localProvider, cloudProvider)) 603 testDataService.collectionInfo.put( 604 cloudProvider, 605 CollectionInfo( 606 authority = cloudProvider.authority, 607 collectionId = null, 608 accountName = null, 609 accountConfigurationIntent = Intent(), 610 ), 611 ) 612 613 val resources = getTestableContext().getResources() 614 val expectedTitle = 615 resources.getString(R.string.photopicker_banner_cloud_choose_account_title) 616 val expectedMessage = 617 resources.getString( 618 R.string.photopicker_banner_cloud_choose_account_message, 619 cloudProvider.displayName, 620 ) 621 622 bannerManager.get().refreshBanners() 623 advanceTimeBy(100) 624 composeTestRule.setContent { 625 callPhotopickerMain( 626 featureManager = featureManager.get(), 627 selection = selection.get(), 628 events = events.get(), 629 ) 630 } 631 composeTestRule.waitForIdle() 632 composeTestRule.onNode(hasText(expectedTitle)).assertIsNotDisplayed() 633 composeTestRule.onNode(hasText(expectedMessage)).assertIsNotDisplayed() 634 } 635 636 @Test testCloudChooseProviderBannernull637 fun testCloudChooseProviderBanner() = 638 testScope.runTest { 639 val bannerStateDao = databaseManager.acquireDao(BannerStateDao::class.java) 640 641 // Treat privacy explainer as already dismissed since it's a higher priority. 642 whenever( 643 bannerStateDao.getBannerState( 644 nonNullableEq(BannerDefinitions.PRIVACY_EXPLAINER.id), 645 anyInt(), 646 ) 647 ) { 648 BannerState( 649 bannerId = BannerDefinitions.PRIVACY_EXPLAINER.id, 650 dismissed = true, 651 uid = 12345, 652 ) 653 } 654 655 val testDataService = dataService.get() as? TestDataServiceImpl 656 checkNotNull(testDataService) { "Expected a TestDataServiceImpl" } 657 testDataService.allowedProviders = listOf(cloudProvider) 658 testDataService.setAvailableProviders(listOf(localProvider)) 659 660 val resources = getTestableContext().getResources() 661 val expectedTitle = 662 resources.getString(R.string.photopicker_banner_cloud_choose_provider_title) 663 val expectedMessage = 664 resources.getString(R.string.photopicker_banner_cloud_choose_provider_message) 665 666 bannerManager.get().refreshBanners() 667 advanceTimeBy(100) 668 composeTestRule.setContent { 669 callPhotopickerMain( 670 featureManager = featureManager.get(), 671 selection = selection.get(), 672 events = events.get(), 673 ) 674 } 675 composeTestRule.waitForIdle() 676 composeTestRule.onNode(hasText(expectedTitle)).assertIsDisplayed() 677 composeTestRule.onNode(hasText(expectedMessage)).assertIsDisplayed() 678 } 679 680 @Test testCloudChooseProviderBannerAsDismissednull681 fun testCloudChooseProviderBannerAsDismissed() = 682 testScope.runTest { 683 val bannerStateDao = databaseManager.acquireDao(BannerStateDao::class.java) 684 685 // Treat privacy explainer as already dismissed since it's a higher priority. 686 whenever( 687 bannerStateDao.getBannerState( 688 nonNullableEq(BannerDefinitions.PRIVACY_EXPLAINER.id), 689 anyInt(), 690 ) 691 ) { 692 BannerState( 693 bannerId = BannerDefinitions.PRIVACY_EXPLAINER.id, 694 dismissed = true, 695 uid = 12345, 696 ) 697 } 698 699 whenever( 700 bannerStateDao.getBannerState( 701 nonNullableEq(BannerDefinitions.CLOUD_CHOOSE_PROVIDER.id), 702 anyInt(), 703 ) 704 ) { 705 BannerState( 706 bannerId = BannerDefinitions.CLOUD_CHOOSE_PROVIDER.id, 707 dismissed = true, 708 uid = 12345, 709 ) 710 } 711 712 val testDataService = dataService.get() as? TestDataServiceImpl 713 checkNotNull(testDataService) { "Expected a TestDataServiceImpl" } 714 testDataService.allowedProviders = listOf(cloudProvider) 715 testDataService.setAvailableProviders(listOf(localProvider)) 716 717 val resources = getTestableContext().getResources() 718 val expectedTitle = 719 resources.getString(R.string.photopicker_banner_cloud_choose_provider_title) 720 val expectedMessage = 721 resources.getString(R.string.photopicker_banner_cloud_choose_provider_message) 722 723 bannerManager.get().refreshBanners() 724 advanceTimeBy(100) 725 composeTestRule.setContent { 726 callPhotopickerMain( 727 featureManager = featureManager.get(), 728 selection = selection.get(), 729 events = events.get(), 730 ) 731 } 732 composeTestRule.waitForIdle() 733 composeTestRule.onNode(hasText(expectedTitle)).assertIsNotDisplayed() 734 composeTestRule.onNode(hasText(expectedMessage)).assertIsNotDisplayed() 735 } 736 } 737