From 81888418cae3ce6962c0ea34d993f2dedfae48a4 Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Mon, 9 Aug 2021 09:59:21 +0300 Subject: [PATCH 1/5] test error dialog --- .../ui/myoverview/MyOverviewFragment.kt | 3 - .../ui/myoverview/MyOverviewFragmentTest.kt | 131 ++++++++++++++++++ 2 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt index 2b03f135b..51d4bc261 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt @@ -7,13 +7,11 @@ import android.os.Looper import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.setFragmentResultListener -import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import com.xwray.groupie.GroupAdapter import com.xwray.groupie.GroupieViewHolder import com.xwray.groupie.Section import com.xwray.groupie.viewbinding.BindableItem -import nl.rijksoverheid.ctr.appconfig.usecases.ClockDeviationUseCase import nl.rijksoverheid.ctr.design.utils.DialogUtil import nl.rijksoverheid.ctr.holder.R import nl.rijksoverheid.ctr.holder.databinding.FragmentMyOverviewBinding @@ -29,7 +27,6 @@ import nl.rijksoverheid.ctr.shared.ext.navigateSafety import nl.rijksoverheid.ctr.shared.livedata.EventObserver import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel -import timber.log.Timber import java.util.concurrent.TimeUnit diff --git a/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt b/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt new file mode 100644 index 000000000..8b97fb1d8 --- /dev/null +++ b/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt @@ -0,0 +1,131 @@ +package nl.rijksoverheid.ctr.holder.ui.myoverview + +import android.content.Context +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.core.os.bundleOf +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModelStore +import androidx.navigation.Navigation +import androidx.navigation.testing.TestNavHostController +import androidx.test.core.app.ApplicationProvider +import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertContains +import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed +import com.schibsted.spain.barista.interaction.BaristaDialogInteractions.clickDialogPositiveButton +import io.mockk.mockk +import io.mockk.verify +import nl.rijksoverheid.ctr.appconfig.api.model.HolderConfig +import nl.rijksoverheid.ctr.design.utils.DialogUtil +import nl.rijksoverheid.ctr.design.utils.DialogUtilImpl +import nl.rijksoverheid.ctr.holder.R +import nl.rijksoverheid.ctr.holder.fakeCommercialTestResultViewModel +import nl.rijksoverheid.ctr.holder.persistence.CachedAppConfigUseCase +import nl.rijksoverheid.ctr.holder.persistence.database.DatabaseSyncerResult +import nl.rijksoverheid.ctr.holder.persistence.database.entities.GreenCardType +import nl.rijksoverheid.ctr.holder.ui.create_qr.CommercialTestCodeFragment +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.context.loadKoinModules +import org.koin.dsl.module +import org.koin.test.AutoCloseKoinTest +import org.robolectric.RobolectricTestRunner +import nl.rijksoverheid.ctr.shared.livedata.Event +import org.junit.Rule + +/* + * Copyright (c) 2021 De Staat der Nederlanden, Ministerie van Volksgezondheid, Welzijn en Sport. + * Licensed under the EUROPEAN UNION PUBLIC LICENCE v. 1.2 + * + * SPDX-License-Identifier: EUPL-1.2 + * + */ +@RunWith(RobolectricTestRunner::class) +class MyOverviewFragmentTest : AutoCloseKoinTest() { + + @get:Rule + val rule = InstantTaskExecutorRule() + + private val dialogUtil: DialogUtil = mockk(relaxed = true) + + private val navController = TestNavHostController( + ApplicationProvider.getApplicationContext() + ).also { + it.setViewModelStore(ViewModelStore()) + it.setGraph(R.navigation.holder_nav_graph_main) + it.setCurrentDestination(R.id.nav_my_overview) + } + + @Test + fun `network error triggers dialog with right copy`() { + val context = ApplicationProvider.getApplicationContext() + val viewModel = launchFragment() + triggerNetworkError(viewModel) + + verify { dialogUtil.presentDialog( + context = any(), + title = R.string.dialog_title_no_internet, + message = context.getString(R.string.dialog_credentials_expired_no_internet), + positiveButtonText = R.string.app_status_internet_required_action, + positiveButtonCallback = any(), + negativeButtonText = R.string.dialog_close, + ) } + } + + private fun triggerNetworkError(viewModel: MyOverviewViewModel) = triggerSyncerResult(viewModel, DatabaseSyncerResult.NetworkError(true)) + + private fun triggerSyncerResult(viewModel: MyOverviewViewModel, syncResult: DatabaseSyncerResult) { + ((viewModel.databaseSyncerResultLiveData) as MutableLiveData).postValue(Event( + syncResult)) + } + + private fun launchFragment(selectType: GreenCardType = GreenCardType.Domestic): MyOverviewViewModel { + val viewModel = object: MyOverviewViewModel() { + override fun getSelectedType(): GreenCardType { + return selectType + } + + override fun refreshOverviewItems( + selectType: GreenCardType, + forceSync: Boolean + ) { + // + } + } + loadKoinModules( + module(override = true) { + viewModel { viewModel } + + factory { dialogUtil } + factory { object: CachedAppConfigUseCase { + override fun getCachedAppConfig(): HolderConfig { + return HolderConfig.default() + } + + override fun getProviderName(providerIdentifier: String): String { + return "GGD" + } + } } + } + ) + + launchFragmentInContainer( + // Supply navArgs + bundleOf( + "returnUri" to "", + MyOverviewFragment.GREEN_CARD_TYPE to GreenCardType.Domestic, + ), themeResId = R.style.AppTheme + ) { + MyOverviewFragment().also { + it.viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner -> + if (viewLifecycleOwner != null) { + Navigation.setViewNavController(it.requireView(), navController) + } + } + } + } + + return viewModel + } +} \ No newline at end of file From 6dde64f5a7653e8af567d6614e7afd7ee0fe7f82 Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Mon, 9 Aug 2021 16:41:12 +0300 Subject: [PATCH 2/5] check test on espresso how it looks like --- holder/build.gradle | 10 ++ .../rijksoverheid/ctr/holder/EspressoTest.kt | 166 ++++++++++++++++++ .../ui/myoverview/MyOverviewFragmentTest.kt | 150 ++++++++++++---- 3 files changed, 295 insertions(+), 31 deletions(-) diff --git a/holder/build.gradle b/holder/build.gradle index 2e728ad33..3e9cc187a 100644 --- a/holder/build.gradle +++ b/holder/build.gradle @@ -169,4 +169,14 @@ dependencies { kapt "org.xerial:sqlite-jdbc:$sqlite_jdbc" androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version" + androidTestImplementation "androidx.test:runner:$androix_test_version" + androidTestImplementation("com.schibsted.spain:barista:$barista_version") { + exclude group: "org.jetbrains.kotlin" + } + androidTestImplementation "androidx.navigation:navigation-testing:$navigation_version" + androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + androidTestImplementation "androidx.room:room-testing:$room_version" + androidTestImplementation "androidx.work:work-testing:$work_manager_version" + androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" + androidTestImplementation "androidx.arch.core:core-testing:$androidx_arch_core_version" } diff --git a/holder/src/androidTest/java/nl/rijksoverheid/ctr/holder/EspressoTest.kt b/holder/src/androidTest/java/nl/rijksoverheid/ctr/holder/EspressoTest.kt index 3322b07de..c71e1736e 100644 --- a/holder/src/androidTest/java/nl/rijksoverheid/ctr/holder/EspressoTest.kt +++ b/holder/src/androidTest/java/nl/rijksoverheid/ctr/holder/EspressoTest.kt @@ -1,13 +1,179 @@ package nl.rijksoverheid.ctr.holder import android.content.Context +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.core.os.bundleOf +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModelStore +import androidx.navigation.Navigation +import androidx.navigation.testing.TestNavHostController +import androidx.test.core.app.ApplicationProvider import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 +import io.mockk.mockk +import io.mockk.verify +import nl.rijksoverheid.ctr.appconfig.api.model.HolderConfig +import nl.rijksoverheid.ctr.design.utils.DialogUtil +import nl.rijksoverheid.ctr.holder.persistence.CachedAppConfigUseCase +import nl.rijksoverheid.ctr.holder.persistence.database.DatabaseSyncerResult +import nl.rijksoverheid.ctr.holder.persistence.database.entities.* +import nl.rijksoverheid.ctr.holder.persistence.database.models.GreenCard +import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.MyOverviewItem +import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.MyOverviewItems +import nl.rijksoverheid.ctr.holder.ui.create_qr.util.OriginState +import nl.rijksoverheid.ctr.holder.ui.myoverview.MyOverviewFragment +import nl.rijksoverheid.ctr.holder.ui.myoverview.MyOverviewViewModel +import nl.rijksoverheid.ctr.shared.livedata.Event +import org.junit.Rule +import org.junit.Test import org.junit.runner.RunWith +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.context.loadKoinModules +import org.koin.dsl.module +import java.time.Clock +import java.time.Instant +import java.time.OffsetDateTime +import java.time.ZoneId @RunWith(AndroidJUnit4::class) class EspressoTest { private val context: Context by lazy { InstrumentationRegistry.getInstrumentation().targetContext } + @get:Rule + val rule = InstantTaskExecutorRule() + + private val dialogUtil: DialogUtil = mockk(relaxed = true) + + private val eventTimeClock = Clock.fixed(Instant.parse("2021-06-01T00:00:00.00Z"), ZoneId.of("UTC")) + private val expireTimeClock = Clock.fixed(Instant.parse("2021-07-01T00:00:00.00Z"), ZoneId.of("UTC")) + + private val navController = TestNavHostController( + ApplicationProvider.getApplicationContext() + ).also { + it.setViewModelStore(ViewModelStore()) + it.setGraph(R.navigation.holder_nav_graph_main) + it.setCurrentDestination(R.id.nav_my_overview) + } + + @Test + fun test() { + val viewModel = launchFragment() + + (viewModel.myOverviewItemsLiveData as MutableLiveData).postValue( + Event( + myOverViewItemsWithValidDomesticGreenCard() + ) + ) + } + + private fun myOverViewItemsWithValidDomesticGreenCard() = MyOverviewItems( + items = listOf( + MyOverviewItem.GreenCardItem( + greenCard = GreenCard( + GreenCardEntity( + id = 1, + walletId = 1, + GreenCardType.Domestic + ), + origins = listOf( + originEntity() + ), + credentialEntities = listOf( + credentialEntity() + ) + ), + originStates = listOf( + OriginState.Valid(originEntity()) + ), + credentialState = MyOverviewItem.GreenCardItem.CredentialState.HasCredential(credentialEntity()), + databaseSyncerResult = DatabaseSyncerResult.Success + ) + ), + selectedType = GreenCardType.Domestic + ) + + private fun originEntity() = OriginEntity( + id = 1, + greenCardId = 1, + type = OriginType.Vaccination, + eventTime = OffsetDateTime.now(eventTimeClock), + validFrom = OffsetDateTime.now(eventTimeClock), + expirationTime = OffsetDateTime.now(expireTimeClock), + ) + + private fun credentialEntity() = CredentialEntity( + id = 1, + greenCardId = 1, + data = "".toByteArray(), + credentialVersion = 2, + validFrom = OffsetDateTime.now(eventTimeClock), + expirationTime = OffsetDateTime.now(expireTimeClock), + ) + + private fun triggerNetworkError(viewModel: MyOverviewViewModel, noCredentialsLeft: Boolean = true) = + triggerSyncerResult(viewModel, DatabaseSyncerResult.NetworkError(noCredentialsLeft)) + + private fun triggerSyncerResult( + viewModel: MyOverviewViewModel, + syncResult: DatabaseSyncerResult + ) { + ((viewModel.databaseSyncerResultLiveData) as MutableLiveData).postValue( + Event( + syncResult + ) + ) + } + + private fun launchFragment(selectType: GreenCardType = GreenCardType.Domestic): MyOverviewViewModel { + val viewModel = object : MyOverviewViewModel() { + override fun getSelectedType(): GreenCardType { + return selectType + } + + override fun refreshOverviewItems( + selectType: GreenCardType, + forceSync: Boolean + ) { + // + } + } + loadKoinModules( + module(override = true) { + viewModel { viewModel } + + factory { dialogUtil } + factory { + object : CachedAppConfigUseCase { + override fun getCachedAppConfig(): HolderConfig { + return HolderConfig.default() + } + + override fun getProviderName(providerIdentifier: String): String { + return "GGD" + } + } + } + } + ) + + launchFragmentInContainer( + // Supply navArgs + bundleOf( + "returnUri" to "", + MyOverviewFragment.GREEN_CARD_TYPE to GreenCardType.Domestic, + ), themeResId = R.style.AppTheme + ) { + MyOverviewFragment().also { + it.viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner -> + if (viewLifecycleOwner != null) { + Navigation.setViewNavController(it.requireView(), navController) + } + } + } + } + + return viewModel + } } \ No newline at end of file diff --git a/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt b/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt index 8b97fb1d8..59ea449f3 100644 --- a/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt +++ b/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt @@ -9,21 +9,20 @@ import androidx.lifecycle.ViewModelStore import androidx.navigation.Navigation import androidx.navigation.testing.TestNavHostController import androidx.test.core.app.ApplicationProvider -import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertContains -import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed -import com.schibsted.spain.barista.interaction.BaristaDialogInteractions.clickDialogPositiveButton import io.mockk.mockk import io.mockk.verify import nl.rijksoverheid.ctr.appconfig.api.model.HolderConfig import nl.rijksoverheid.ctr.design.utils.DialogUtil -import nl.rijksoverheid.ctr.design.utils.DialogUtilImpl import nl.rijksoverheid.ctr.holder.R -import nl.rijksoverheid.ctr.holder.fakeCommercialTestResultViewModel import nl.rijksoverheid.ctr.holder.persistence.CachedAppConfigUseCase import nl.rijksoverheid.ctr.holder.persistence.database.DatabaseSyncerResult -import nl.rijksoverheid.ctr.holder.persistence.database.entities.GreenCardType -import nl.rijksoverheid.ctr.holder.ui.create_qr.CommercialTestCodeFragment -import org.junit.Assert.* +import nl.rijksoverheid.ctr.holder.persistence.database.entities.* +import nl.rijksoverheid.ctr.holder.persistence.database.models.GreenCard +import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.MyOverviewItem +import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.MyOverviewItems +import nl.rijksoverheid.ctr.holder.ui.create_qr.util.OriginState +import nl.rijksoverheid.ctr.shared.livedata.Event +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.koin.androidx.viewmodel.dsl.viewModel @@ -31,8 +30,10 @@ import org.koin.core.context.loadKoinModules import org.koin.dsl.module import org.koin.test.AutoCloseKoinTest import org.robolectric.RobolectricTestRunner -import nl.rijksoverheid.ctr.shared.livedata.Event -import org.junit.Rule +import java.time.Clock +import java.time.Instant +import java.time.OffsetDateTime +import java.time.ZoneId /* * Copyright (c) 2021 De Staat der Nederlanden, Ministerie van Volksgezondheid, Welzijn en Sport. @@ -49,6 +50,9 @@ class MyOverviewFragmentTest : AutoCloseKoinTest() { private val dialogUtil: DialogUtil = mockk(relaxed = true) + private val eventTimeClock = Clock.fixed(Instant.parse("2021-06-01T00:00:00.00Z"), ZoneId.of("UTC")) + private val expireTimeClock = Clock.fixed(Instant.parse("2021-07-01T00:00:00.00Z"), ZoneId.of("UTC")) + private val navController = TestNavHostController( ApplicationProvider.getApplicationContext() ).also { @@ -58,30 +62,112 @@ class MyOverviewFragmentTest : AutoCloseKoinTest() { } @Test - fun `network error triggers dialog with right copy`() { + fun `network error with no credentials left triggers dialog with right copy`() { val context = ApplicationProvider.getApplicationContext() val viewModel = launchFragment() + triggerNetworkError(viewModel) - verify { dialogUtil.presentDialog( - context = any(), - title = R.string.dialog_title_no_internet, - message = context.getString(R.string.dialog_credentials_expired_no_internet), - positiveButtonText = R.string.app_status_internet_required_action, - positiveButtonCallback = any(), - negativeButtonText = R.string.dialog_close, - ) } + verify { + dialogUtil.presentDialog( + context = any(), + title = R.string.dialog_title_no_internet, + message = context.getString(R.string.dialog_credentials_expired_no_internet), + positiveButtonText = R.string.app_status_internet_required_action, + positiveButtonCallback = any(), + negativeButtonText = R.string.dialog_close, + ) + } + } + + @Test + fun `network error with credentials left triggers dialog with right copy`() { + val context = ApplicationProvider.getApplicationContext() + val viewModel = launchFragment() + + triggerNetworkError(viewModel, false) + + verify { + dialogUtil.presentDialog( + context = any(), + title = R.string.dialog_title_no_internet, + message = context.getString(R.string.dialog_update_credentials_no_internet), + positiveButtonText = R.string.app_status_internet_required_action, + positiveButtonCallback = any(), + negativeButtonText = R.string.dialog_close, + ) + } } - private fun triggerNetworkError(viewModel: MyOverviewViewModel) = triggerSyncerResult(viewModel, DatabaseSyncerResult.NetworkError(true)) + @Test + fun `overview with one green card item shows properly`() { + val viewModel = launchFragment() - private fun triggerSyncerResult(viewModel: MyOverviewViewModel, syncResult: DatabaseSyncerResult) { - ((viewModel.databaseSyncerResultLiveData) as MutableLiveData).postValue(Event( - syncResult)) + (viewModel.myOverviewItemsLiveData as MutableLiveData).postValue(Event( + myOverViewItemsWithValidDomesticGreenCard() + )) + } + + private fun myOverViewItemsWithValidDomesticGreenCard() = MyOverviewItems( + items = listOf( + MyOverviewItem.GreenCardItem( + greenCard = GreenCard( + GreenCardEntity( + id = 1, + walletId = 1, + GreenCardType.Domestic + ), + origins = listOf( + originEntity() + ), + credentialEntities = listOf( + credentialEntity() + ) + ), + originStates = listOf( + OriginState.Valid(originEntity()) + ), + credentialState = MyOverviewItem.GreenCardItem.CredentialState.HasCredential(credentialEntity()), + databaseSyncerResult = DatabaseSyncerResult.Success + ) + ), + selectedType = GreenCardType.Domestic + ) + + private fun originEntity() = OriginEntity( + id = 1, + greenCardId = 1, + type = OriginType.Vaccination, + eventTime = OffsetDateTime.now(eventTimeClock), + validFrom = OffsetDateTime.now(eventTimeClock), + expirationTime = OffsetDateTime.now(expireTimeClock), + ) + + private fun credentialEntity() = CredentialEntity( + id = 1, + greenCardId = 1, + data = "".toByteArray(), + credentialVersion = 2, + validFrom = OffsetDateTime.now(eventTimeClock), + expirationTime = OffsetDateTime.now(expireTimeClock), + ) + + private fun triggerNetworkError(viewModel: MyOverviewViewModel, noCredentialsLeft: Boolean = true) = + triggerSyncerResult(viewModel, DatabaseSyncerResult.NetworkError(noCredentialsLeft)) + + private fun triggerSyncerResult( + viewModel: MyOverviewViewModel, + syncResult: DatabaseSyncerResult + ) { + ((viewModel.databaseSyncerResultLiveData) as MutableLiveData).postValue( + Event( + syncResult + ) + ) } private fun launchFragment(selectType: GreenCardType = GreenCardType.Domestic): MyOverviewViewModel { - val viewModel = object: MyOverviewViewModel() { + val viewModel = object : MyOverviewViewModel() { override fun getSelectedType(): GreenCardType { return selectType } @@ -98,15 +184,17 @@ class MyOverviewFragmentTest : AutoCloseKoinTest() { viewModel { viewModel } factory { dialogUtil } - factory { object: CachedAppConfigUseCase { - override fun getCachedAppConfig(): HolderConfig { - return HolderConfig.default() - } + factory { + object : CachedAppConfigUseCase { + override fun getCachedAppConfig(): HolderConfig { + return HolderConfig.default() + } - override fun getProviderName(providerIdentifier: String): String { - return "GGD" + override fun getProviderName(providerIdentifier: String): String { + return "GGD" + } } - } } + } } ) From 421bfdac7b83df9fb3eb6b9337c8cdb0928e52fa Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Tue, 10 Aug 2021 12:21:06 +0300 Subject: [PATCH 3/5] test for green card item --- .../ui/myoverview/MyOverviewFragmentTest.kt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt b/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt index 59ea449f3..8a6412ef3 100644 --- a/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt +++ b/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt @@ -9,6 +9,9 @@ import androidx.lifecycle.ViewModelStore import androidx.navigation.Navigation import androidx.navigation.testing.TestNavHostController import androidx.test.core.app.ApplicationProvider +import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions +import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed +import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertNotDisplayed import io.mockk.mockk import io.mockk.verify import nl.rijksoverheid.ctr.appconfig.api.model.HolderConfig @@ -100,12 +103,21 @@ class MyOverviewFragmentTest : AutoCloseKoinTest() { } @Test - fun `overview with one green card item shows properly`() { + fun `overview with one green card item (vaccination) shows properly`() { val viewModel = launchFragment() (viewModel.myOverviewItemsLiveData as MutableLiveData).postValue(Event( myOverViewItemsWithValidDomesticGreenCard() )) + + assertDisplayed(R.id.type_title, R.string.validity_type_dutch_title) + assertDisplayed(R.id.title, R.string.my_overview_test_result_title) + assertDisplayed(R.id.proof1_title, R.string.qr_card_vaccination_title_domestic) + assertDisplayed(R.id.proof1_subtitle) + assertNotDisplayed(R.id.proof2_title) + assertNotDisplayed(R.id.proof3_title) + assertNotDisplayed(R.id.proof2_subtitle) + assertNotDisplayed(R.id.proof3_subtitle) } private fun myOverViewItemsWithValidDomesticGreenCard() = MyOverviewItems( @@ -134,10 +146,10 @@ class MyOverviewFragmentTest : AutoCloseKoinTest() { selectedType = GreenCardType.Domestic ) - private fun originEntity() = OriginEntity( + private fun originEntity(type: OriginType = OriginType.Vaccination) = OriginEntity( id = 1, greenCardId = 1, - type = OriginType.Vaccination, + type = type, eventTime = OffsetDateTime.now(eventTimeClock), validFrom = OffsetDateTime.now(eventTimeClock), expirationTime = OffsetDateTime.now(expireTimeClock), From 6c686c76f33ae68b986a58da7cf5a39718ae9867 Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Tue, 10 Aug 2021 12:38:10 +0300 Subject: [PATCH 4/5] some more tests --- .../ui/myoverview/MyOverviewFragmentTest.kt | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt b/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt index 8a6412ef3..e5edd1276 100644 --- a/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt +++ b/holder/src/test/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragmentTest.kt @@ -12,6 +12,7 @@ import androidx.test.core.app.ApplicationProvider import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertNotDisplayed +import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertNotExist import io.mockk.mockk import io.mockk.verify import nl.rijksoverheid.ctr.appconfig.api.model.HolderConfig @@ -33,6 +34,7 @@ import org.koin.core.context.loadKoinModules import org.koin.dsl.module import org.koin.test.AutoCloseKoinTest import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config import java.time.Clock import java.time.Instant import java.time.OffsetDateTime @@ -46,6 +48,7 @@ import java.time.ZoneId * */ @RunWith(RobolectricTestRunner::class) +@Config(qualifiers = "nl-land") class MyOverviewFragmentTest : AutoCloseKoinTest() { @get:Rule @@ -113,13 +116,75 @@ class MyOverviewFragmentTest : AutoCloseKoinTest() { assertDisplayed(R.id.type_title, R.string.validity_type_dutch_title) assertDisplayed(R.id.title, R.string.my_overview_test_result_title) assertDisplayed(R.id.proof1_title, R.string.qr_card_vaccination_title_domestic) - assertDisplayed(R.id.proof1_subtitle) + assertDisplayed(R.id.proof1_subtitle, "geldig vanaf 1 juni 2021 ") assertNotDisplayed(R.id.proof2_title) assertNotDisplayed(R.id.proof3_title) assertNotDisplayed(R.id.proof2_subtitle) assertNotDisplayed(R.id.proof3_subtitle) } + @Test + fun `overview with one expired green card item shows properly`() { + val viewModel = launchFragment() + + (viewModel.myOverviewItemsLiveData as MutableLiveData).postValue(Event( + expiredItem() + )) + + assertDisplayed(R.id.text, R.string.qr_card_expired) + assertNotExist(R.id.test_result) + } + + @Test + fun `overview with one origin info item shows properly`() { + val viewModel = launchFragment() + + (viewModel.myOverviewItemsLiveData as MutableLiveData).postValue(Event( + originInfoItem() + )) + + assertDisplayed(R.id.text, "Je vaccinatiebewijs is niet geldig in Nederland. Je hebt wel een internationaal bewijs.") + assertNotExist(R.id.test_result) + } + + @Test + fun `overview with one clock deviation item shows properly`() { + val viewModel = launchFragment() + + (viewModel.myOverviewItemsLiveData as MutableLiveData).postValue(Event( + clockDeviationItem() + )) + + assertDisplayed(R.id.text, R.string.my_overview_clock_deviation_description) + assertNotExist(R.id.test_result) + } + + private fun clockDeviationItem() = MyOverviewItems( + items = listOf( + MyOverviewItem.ClockDeviationItem + ), + selectedType = GreenCardType.Domestic + ) + + private fun originInfoItem() = MyOverviewItems( + items = listOf( + MyOverviewItem.OriginInfoItem( + greenCardType = GreenCardType.Domestic, + originType = OriginType.Vaccination, + ) + ), + selectedType = GreenCardType.Domestic + ) + + private fun expiredItem() = MyOverviewItems( + items = listOf( + MyOverviewItem.GreenCardExpiredItem( + greenCardType = GreenCardType.Domestic + ) + ), + selectedType = GreenCardType.Domestic + ) + private fun myOverViewItemsWithValidDomesticGreenCard() = MyOverviewItems( items = listOf( MyOverviewItem.GreenCardItem( From ada541fccaf7c91f5a7e55b077d279e63d28f657 Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Tue, 10 Aug 2021 12:42:02 +0300 Subject: [PATCH 5/5] cleanup --- holder/build.gradle | 10 -- .../rijksoverheid/ctr/holder/EspressoTest.kt | 166 ------------------ .../ui/myoverview/MyOverviewFragment.kt | 4 + 3 files changed, 4 insertions(+), 176 deletions(-) diff --git a/holder/build.gradle b/holder/build.gradle index 3e9cc187a..2e728ad33 100644 --- a/holder/build.gradle +++ b/holder/build.gradle @@ -169,14 +169,4 @@ dependencies { kapt "org.xerial:sqlite-jdbc:$sqlite_jdbc" androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version" - androidTestImplementation "androidx.test:runner:$androix_test_version" - androidTestImplementation("com.schibsted.spain:barista:$barista_version") { - exclude group: "org.jetbrains.kotlin" - } - androidTestImplementation "androidx.navigation:navigation-testing:$navigation_version" - androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" - androidTestImplementation "androidx.room:room-testing:$room_version" - androidTestImplementation "androidx.work:work-testing:$work_manager_version" - androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" - androidTestImplementation "androidx.arch.core:core-testing:$androidx_arch_core_version" } diff --git a/holder/src/androidTest/java/nl/rijksoverheid/ctr/holder/EspressoTest.kt b/holder/src/androidTest/java/nl/rijksoverheid/ctr/holder/EspressoTest.kt index c71e1736e..3322b07de 100644 --- a/holder/src/androidTest/java/nl/rijksoverheid/ctr/holder/EspressoTest.kt +++ b/holder/src/androidTest/java/nl/rijksoverheid/ctr/holder/EspressoTest.kt @@ -1,179 +1,13 @@ package nl.rijksoverheid.ctr.holder import android.content.Context -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.core.os.bundleOf -import androidx.fragment.app.testing.launchFragmentInContainer -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModelStore -import androidx.navigation.Navigation -import androidx.navigation.testing.TestNavHostController -import androidx.test.core.app.ApplicationProvider import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 -import io.mockk.mockk -import io.mockk.verify -import nl.rijksoverheid.ctr.appconfig.api.model.HolderConfig -import nl.rijksoverheid.ctr.design.utils.DialogUtil -import nl.rijksoverheid.ctr.holder.persistence.CachedAppConfigUseCase -import nl.rijksoverheid.ctr.holder.persistence.database.DatabaseSyncerResult -import nl.rijksoverheid.ctr.holder.persistence.database.entities.* -import nl.rijksoverheid.ctr.holder.persistence.database.models.GreenCard -import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.MyOverviewItem -import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.MyOverviewItems -import nl.rijksoverheid.ctr.holder.ui.create_qr.util.OriginState -import nl.rijksoverheid.ctr.holder.ui.myoverview.MyOverviewFragment -import nl.rijksoverheid.ctr.holder.ui.myoverview.MyOverviewViewModel -import nl.rijksoverheid.ctr.shared.livedata.Event -import org.junit.Rule -import org.junit.Test import org.junit.runner.RunWith -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.core.context.loadKoinModules -import org.koin.dsl.module -import java.time.Clock -import java.time.Instant -import java.time.OffsetDateTime -import java.time.ZoneId @RunWith(AndroidJUnit4::class) class EspressoTest { private val context: Context by lazy { InstrumentationRegistry.getInstrumentation().targetContext } - @get:Rule - val rule = InstantTaskExecutorRule() - - private val dialogUtil: DialogUtil = mockk(relaxed = true) - - private val eventTimeClock = Clock.fixed(Instant.parse("2021-06-01T00:00:00.00Z"), ZoneId.of("UTC")) - private val expireTimeClock = Clock.fixed(Instant.parse("2021-07-01T00:00:00.00Z"), ZoneId.of("UTC")) - - private val navController = TestNavHostController( - ApplicationProvider.getApplicationContext() - ).also { - it.setViewModelStore(ViewModelStore()) - it.setGraph(R.navigation.holder_nav_graph_main) - it.setCurrentDestination(R.id.nav_my_overview) - } - - @Test - fun test() { - val viewModel = launchFragment() - - (viewModel.myOverviewItemsLiveData as MutableLiveData).postValue( - Event( - myOverViewItemsWithValidDomesticGreenCard() - ) - ) - } - - private fun myOverViewItemsWithValidDomesticGreenCard() = MyOverviewItems( - items = listOf( - MyOverviewItem.GreenCardItem( - greenCard = GreenCard( - GreenCardEntity( - id = 1, - walletId = 1, - GreenCardType.Domestic - ), - origins = listOf( - originEntity() - ), - credentialEntities = listOf( - credentialEntity() - ) - ), - originStates = listOf( - OriginState.Valid(originEntity()) - ), - credentialState = MyOverviewItem.GreenCardItem.CredentialState.HasCredential(credentialEntity()), - databaseSyncerResult = DatabaseSyncerResult.Success - ) - ), - selectedType = GreenCardType.Domestic - ) - - private fun originEntity() = OriginEntity( - id = 1, - greenCardId = 1, - type = OriginType.Vaccination, - eventTime = OffsetDateTime.now(eventTimeClock), - validFrom = OffsetDateTime.now(eventTimeClock), - expirationTime = OffsetDateTime.now(expireTimeClock), - ) - - private fun credentialEntity() = CredentialEntity( - id = 1, - greenCardId = 1, - data = "".toByteArray(), - credentialVersion = 2, - validFrom = OffsetDateTime.now(eventTimeClock), - expirationTime = OffsetDateTime.now(expireTimeClock), - ) - - private fun triggerNetworkError(viewModel: MyOverviewViewModel, noCredentialsLeft: Boolean = true) = - triggerSyncerResult(viewModel, DatabaseSyncerResult.NetworkError(noCredentialsLeft)) - - private fun triggerSyncerResult( - viewModel: MyOverviewViewModel, - syncResult: DatabaseSyncerResult - ) { - ((viewModel.databaseSyncerResultLiveData) as MutableLiveData).postValue( - Event( - syncResult - ) - ) - } - - private fun launchFragment(selectType: GreenCardType = GreenCardType.Domestic): MyOverviewViewModel { - val viewModel = object : MyOverviewViewModel() { - override fun getSelectedType(): GreenCardType { - return selectType - } - - override fun refreshOverviewItems( - selectType: GreenCardType, - forceSync: Boolean - ) { - // - } - } - loadKoinModules( - module(override = true) { - viewModel { viewModel } - - factory { dialogUtil } - factory { - object : CachedAppConfigUseCase { - override fun getCachedAppConfig(): HolderConfig { - return HolderConfig.default() - } - - override fun getProviderName(providerIdentifier: String): String { - return "GGD" - } - } - } - } - ) - - launchFragmentInContainer( - // Supply navArgs - bundleOf( - "returnUri" to "", - MyOverviewFragment.GREEN_CARD_TYPE to GreenCardType.Domestic, - ), themeResId = R.style.AppTheme - ) { - MyOverviewFragment().also { - it.viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner -> - if (viewLifecycleOwner != null) { - Navigation.setViewNavController(it.requireView(), navController) - } - } - } - } - - return viewModel - } } \ No newline at end of file diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt index 51d4bc261..4f77703f8 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt @@ -82,6 +82,10 @@ class MyOverviewFragment : Fragment(R.layout.fragment_my_overview) { ) }) + observeSyncErrors() + } + + private fun observeSyncErrors() { myOverviewViewModel.databaseSyncerResultLiveData.observe(viewLifecycleOwner, EventObserver { if (it is DatabaseSyncerResult.NetworkError) {