diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt index 5f537314a..5f63ee4e3 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt @@ -45,7 +45,7 @@ class LibrarySubMenusMultipleSelectionToolbarRobot { mDevice.waitNotNull( Until.findObject( - By.text("SHARE A LINK") + By.text("ALL ACTIONS") ), waitingTime ) } @@ -55,7 +55,7 @@ class LibrarySubMenusMultipleSelectionToolbarRobot { mDevice.waitNotNull( Until.findObject( - By.text("SHARE A LINK") + By.text("ALL ACTIONS") ), waitingTime ) } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt index 89e279f76..474d27c2d 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt @@ -48,7 +48,7 @@ class ThreeDotMenuMainRobot { fun verifyReaderViewAppearance(visible: Boolean) = assertReaderViewAppearanceButton(visible) fun clickShareButton() { shareButton().click() - mDevice.waitNotNull(Until.findObject(By.text("SHARE A LINK")), waitingTime) + mDevice.waitNotNull(Until.findObject(By.text("ALL ACTIONS")), waitingTime) } fun verifyShareTabButton() = assertShareTabButton() @@ -293,7 +293,7 @@ private fun assertSendToDeviceTitle() = SendToDeviceTitle() .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) private fun ShareALinkTitle() = - onView(allOf(withText(R.string.share_link_all_apps_subheader), withResourceName("apps_link_header"))) + onView(allOf(withText("ALL ACTIONS"), withResourceName("apps_link_header"))) private fun assertShareALinkTitle() = ShareALinkTitle() diff --git a/app/src/main/java/org/mozilla/fenix/share/ShareViewModel.kt b/app/src/main/java/org/mozilla/fenix/share/ShareViewModel.kt index 3228abc98..bd7d55837 100644 --- a/app/src/main/java/org/mozilla/fenix/share/ShareViewModel.kt +++ b/app/src/main/java/org/mozilla/fenix/share/ShareViewModel.kt @@ -31,12 +31,13 @@ import org.mozilla.fenix.share.listadapters.SyncShareOption class ShareViewModel(application: Application) : AndroidViewModel(application) { companion object { - private const val RECENT_APPS_LIMIT = 6 + internal const val RECENT_APPS_LIMIT = 6 } private val connectivityManager by lazy { application.getSystemService() } private val fxaAccountManager = application.components.backgroundServices.accountManager - private val recentAppsStorage = RecentAppsStorage(application.applicationContext) + @VisibleForTesting + internal var recentAppsStorage = RecentAppsStorage(application.applicationContext) private val devicesListLiveData = MutableLiveData>(emptyList()) private val appsListLiveData = MutableLiveData>(emptyList()) @@ -131,8 +132,9 @@ class ShareViewModel(application: Application) : AndroidViewModel(application) { connectivityManager?.unregisterNetworkCallback(networkCallback) } + @VisibleForTesting @WorkerThread - private fun getIntentActivities(shareIntent: Intent, context: Context): List? { + fun getIntentActivities(shareIntent: Intent, context: Context): List? { return context.packageManager.queryIntentActivities(shareIntent, 0) } diff --git a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/DefaultQuickSettingsControllerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/DefaultQuickSettingsControllerTest.kt index 6812a2f7c..098e04329 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/DefaultQuickSettingsControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/DefaultQuickSettingsControllerTest.kt @@ -22,9 +22,9 @@ import io.mockk.slot import io.mockk.verify import io.mockk.verifyOrder import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.ObsoleteCoroutinesApi import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestCoroutineScope import mozilla.components.browser.session.Session import mozilla.components.feature.session.SessionUseCases import mozilla.components.feature.sitepermissions.SitePermissions @@ -42,16 +42,17 @@ import org.mozilla.fenix.utils.Settings import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config +@ExperimentalCoroutinesApi @UseExperimental(ObsoleteCoroutinesApi::class) @RunWith(RobolectricTestRunner::class) @Config(application = TestApplication::class) class DefaultQuickSettingsControllerTest { private val context = testContext private val store = mockk() - private val coroutinesScope = GlobalScope + private val coroutinesScope = TestCoroutineScope() private val navController = mockk(relaxed = true) private val browserSession = mockk() - private val sitePermissions = SitePermissions(origin = "", savedAt = 123) + private val sitePermissions: SitePermissions = SitePermissions(origin = "", savedAt = 123) private val appSettings = mockk(relaxed = true) private val permissionStorage = mockk(relaxed = true) private val reload = mockk(relaxed = true) @@ -121,9 +122,8 @@ class DefaultQuickSettingsControllerTest { // We want to verify that the Status is toggled and this event is passed to Controller also. assertThat(sitePermissions.camera).isSameAs(NO_DECISION) verifyOrder { - sitePermissions.toggle(capture(toggledFeature)).also { - controller.handlePermissionsChange(it) - } + val permission = sitePermissions.toggle(capture(toggledFeature)) + controller.handlePermissionsChange(permission) } // We should also modify View's state. Not necessarily as the last operation. verify { diff --git a/app/src/test/java/org/mozilla/fenix/share/ShareControllerTest.kt b/app/src/test/java/org/mozilla/fenix/share/ShareControllerTest.kt index 9a261ef09..5c76aa497 100644 --- a/app/src/test/java/org/mozilla/fenix/share/ShareControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/share/ShareControllerTest.kt @@ -22,11 +22,15 @@ import io.mockk.slot import io.mockk.spyk import io.mockk.verify import io.mockk.verifyOrder +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestCoroutineScope import mozilla.components.concept.engine.prompt.ShareData import mozilla.components.concept.sync.Device import mozilla.components.concept.sync.DeviceType import mozilla.components.concept.sync.TabData import mozilla.components.feature.accounts.push.SendTabUseCases +import mozilla.components.feature.share.RecentAppsStorage import mozilla.components.support.test.robolectric.testContext import org.junit.Before import org.junit.Test @@ -44,6 +48,7 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(application = TestApplication::class) +@ExperimentalCoroutinesApi class ShareControllerTest { // Need a valid context to retrieve Strings for example, but we also need it to return our "metrics" private val context: Context = spyk(testContext) @@ -58,12 +63,15 @@ class ShareControllerTest { TabData("title1", "url1") ) private val textToShare = "${shareData[0].title} ${shareData[0].url}\n\n${shareData[1].title} ${shareData[1].url}" + private val testCoroutineScope = TestCoroutineScope() private val sendTabUseCases = mockk(relaxed = true) private val snackbar = mockk(relaxed = true) private val navController = mockk(relaxed = true) private val dismiss = mockk<(ShareController.Result) -> Unit>(relaxed = true) + private val recentAppStorage = mockk(relaxed = true) private val controller = DefaultShareController( - context, shareData, sendTabUseCases, snackbar, navController, dismiss + context, shareData, sendTabUseCases, snackbar, navController, + recentAppStorage, testCoroutineScope, dismiss ) @Before @@ -79,7 +87,7 @@ class ShareControllerTest { } @Test - fun `handleShareToApp should start a new sharing activity and close this`() { + fun `handleShareToApp should start a new sharing activity and close this`() = runBlocking { val appPackageName = "package" val appClassName = "activity" val appShareOption = AppShareOption("app", mockk(), appPackageName, appClassName) @@ -88,8 +96,10 @@ class ShareControllerTest { // needed for capturing the actual Intent used the `slot` one doesn't have this flag so we // need to use an Activity Context. val activityContext: Context = mockk() - val testController = DefaultShareController(activityContext, shareData, mockk(), mockk(), mockk(), dismiss) + val testController = DefaultShareController(activityContext, shareData, mockk(), mockk(), mockk(), + recentAppStorage, testCoroutineScope, dismiss) every { activityContext.startActivity(capture(shareIntent)) } just Runs + every { recentAppStorage.updateRecentApp(appShareOption.packageName) } just Runs testController.handleShareToApp(appShareOption) @@ -104,6 +114,7 @@ class ShareControllerTest { assertThat(shareIntent.captured.component!!.className).isEqualTo(appClassName) } verifyOrder { + recentAppStorage.updateRecentApp(appShareOption.packageName) activityContext.startActivity(shareIntent.captured) dismiss(ShareController.Result.SUCCESS) } @@ -119,7 +130,8 @@ class ShareControllerTest { // needed for capturing the actual Intent used the `slot` one doesn't have this flag so we // need to use an Activity Context. val activityContext: Context = mockk() - val testController = DefaultShareController(activityContext, shareData, mockk(), snackbar, mockk(), dismiss) + val testController = DefaultShareController(activityContext, shareData, mockk(), snackbar, + mockk(), mockk(), testCoroutineScope, dismiss) every { activityContext.startActivity(capture(shareIntent)) } throws SecurityException() every { activityContext.getString(R.string.share_error_snackbar) } returns "Cannot share to this app" @@ -242,7 +254,14 @@ class ShareControllerTest { @Test fun `getSuccessMessage should return different strings depending on the number of shared tabs`() { val controllerWithOneSharedTab = DefaultShareController( - context, listOf(ShareData(url = "url0", title = "title0")), mockk(), mockk(), mockk(), mockk() + context, + listOf(ShareData(url = "url0", title = "title0")), + mockk(), + mockk(), + mockk(), + mockk(), + mockk(), + mockk() ) val controllerWithMoreSharedTabs = controller val expectedTabSharedMessage = context.getString(R.string.sync_sent_tab_snackbar) diff --git a/app/src/test/java/org/mozilla/fenix/share/ShareViewModelTest.kt b/app/src/test/java/org/mozilla/fenix/share/ShareViewModelTest.kt index 3a4a56fd4..44e0f553a 100644 --- a/app/src/test/java/org/mozilla/fenix/share/ShareViewModelTest.kt +++ b/app/src/test/java/org/mozilla/fenix/share/ShareViewModelTest.kt @@ -11,12 +11,19 @@ import android.content.pm.ResolveInfo import android.graphics.drawable.Drawable import android.net.ConnectivityManager import androidx.core.content.getSystemService +import io.mockk.Runs import io.mockk.every +import io.mockk.just import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.spyk import io.mockk.verify -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.runBlockingTest +import mozilla.components.feature.share.RecentApp +import mozilla.components.feature.share.RecentAppsStorage import mozilla.components.service.fxa.manager.FxaAccountManager import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals @@ -27,6 +34,7 @@ import org.mozilla.fenix.TestApplication import org.mozilla.fenix.ext.application import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.isOnline +import org.mozilla.fenix.share.ShareViewModel.Companion.RECENT_APPS_LIMIT import org.mozilla.fenix.share.listadapters.AppShareOption import org.mozilla.fenix.share.listadapters.SyncShareOption import org.robolectric.RobolectricTestRunner @@ -34,6 +42,7 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(application = TestApplication::class) +@ExperimentalCoroutinesApi class ShareViewModelTest { private val packageName = "org.mozilla.fenix" @@ -67,10 +76,38 @@ class ShareViewModelTest { } @Test - fun `loadDevicesAndApps registers networkCallback`() = runBlocking { + fun `loadDevicesAndApps`() = runBlockingTest { + mockkStatic(Dispatchers::class) + every { + Dispatchers.IO + } returns TestCoroutineDispatcher() + viewModel = spyk(viewModel) + val drawable: Drawable = mockk() + val appOptions = ArrayList() + val appElement = AppShareOption("Label", drawable, "Package", "Activity") + appOptions.add(appElement) + + val recentAppOptions = ArrayList() + val appEntity: RecentApp = mockk() + every { appEntity.packageName } returns "Package" + recentAppOptions.add(appEntity) + val storage: RecentAppsStorage = mockk(relaxed = true) + viewModel.recentAppsStorage = storage + + every { viewModel.buildAppsList(any(), any()) } returns appOptions + every { storage.updateDatabaseWithNewApps(appOptions.map { app -> app.packageName }) } just Runs + every { storage.getRecentAppsUpTo(RECENT_APPS_LIMIT) } returns recentAppOptions + viewModel.loadDevicesAndApps() - verify { connectivityManager.registerNetworkCallback(any(), eq(viewModel.networkCallback)) } + verify { + connectivityManager.registerNetworkCallback( + any(), + any() + ) + } + assertEquals(1, viewModel.recentAppsList.value?.size) + assertEquals(0, viewModel.appsList.value?.size) } @Test @@ -115,7 +152,10 @@ class ShareViewModelTest { every { fxaAccountManager.authenticatedAccount() } returns mockk() every { fxaAccountManager.accountNeedsReauth() } returns true - assertEquals(listOf(SyncShareOption.Reconnect), viewModel.buildDeviceList(fxaAccountManager)) + assertEquals( + listOf(SyncShareOption.Reconnect), + viewModel.buildDeviceList(fxaAccountManager) + ) } private fun createResolveInfo(