From b11f76a6771bcd0fb9b45d75a57543618f250efe Mon Sep 17 00:00:00 2001 From: Ephraim Kigamba Date: Thu, 4 May 2023 11:10:07 +0300 Subject: [PATCH 1/4] Add Geowidget Fragment tests --- .../screens/GeoWidgetFragmentTest.kt | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt index 4e963a325d..d9e8f61a0b 100644 --- a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt +++ b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt @@ -17,8 +17,10 @@ package org.smartregister.fhircore.geowidget.screens import android.os.Build +import android.os.Bundle import android.os.Looper import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.lifecycle.MutableLiveData import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection import com.mapbox.geojson.Point @@ -31,6 +33,7 @@ import io.mockk.every import io.mockk.just import io.mockk.mockk import io.mockk.runs +import io.mockk.spyk import io.mockk.verify import io.ona.kujaku.views.KujakuMapView import org.junit.Assert @@ -42,6 +45,7 @@ import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config +import org.robolectric.util.ReflectionHelpers import org.smartregister.fhircore.engine.configuration.geowidget.GeoWidgetConfiguration import org.smartregister.fhircore.geowidget.model.GeoWidgetEvent import org.smartregister.fhircore.geowidget.shadows.ShadowConnectivityReceiver @@ -75,6 +79,10 @@ class GeoWidgetFragmentTest { every { kujakuMapView.onResume() } answers { kujakuMapViewLifecycle = "onResume" } every { kujakuMapView.onDestroy() } answers { kujakuMapViewLifecycle = "onDestroy" } every { kujakuMapView.onStop() } answers { kujakuMapViewLifecycle = "onStop" } + every { kujakuMapView.onSaveInstanceState(any()) } answers + { + kujakuMapViewLifecycle = "onSaveInstanceState" + } } @Test @@ -107,6 +115,62 @@ class GeoWidgetFragmentTest { Assert.assertEquals("onLowMemory", kujakuMapViewLifecycle) } + @Test + fun onSaveInstanceStateSetsKujakuOnSaveInstanceState() { + geowidgetFragment.onSaveInstanceState(Bundle()) + Assert.assertEquals("onSaveInstanceState", kujakuMapViewLifecycle) + } + + @Test + fun onResumeSetsKujakuOnResume() { + val geoWidgetViewModel = mockk(relaxed = true) + val familyFeaturesLiveData = spyk>() + + val mockedGeoWidgetFragment = spyk(geowidgetFragment) + + every { geoWidgetViewModel.getFamiliesFeatureCollectionStream(any()) } returns + familyFeaturesLiveData + every { mockedGeoWidgetFragment.requireContext() } returns mockk() + every { mockedGeoWidgetFragment.viewLifecycleOwner } returns mockk() + every { familyFeaturesLiveData.observe(any(), any()) } just runs + + // Set our mocked view model + ReflectionHelpers.setField( + mockedGeoWidgetFragment, + "geoWidgetViewModel\$delegate", + lazy { geoWidgetViewModel } + ) + + mockedGeoWidgetFragment.onResume() + Assert.assertEquals("onResume", kujakuMapViewLifecycle) + } + + @Test + fun onResumeShouldObserveFamilyFeaturesCollectionStream() { + val geoWidgetViewModel = mockk(relaxed = true) + val familyFeaturesLiveData = spyk>() + + val mockedGeoWidgetFragment = spyk(geowidgetFragment) + + every { geoWidgetViewModel.getFamiliesFeatureCollectionStream(any()) } returns + familyFeaturesLiveData + every { mockedGeoWidgetFragment.requireContext() } returns mockk() + every { mockedGeoWidgetFragment.viewLifecycleOwner } returns mockk() + every { familyFeaturesLiveData.observe(any(), any()) } just runs + + // Set our mocked view model + ReflectionHelpers.setField( + mockedGeoWidgetFragment, + "geoWidgetViewModel\$delegate", + lazy { geoWidgetViewModel } + ) + + mockedGeoWidgetFragment.onResume() + + verify { geoWidgetViewModel.getFamiliesFeatureCollectionStream(any()) } + verify { familyFeaturesLiveData.observe(any(), mockedGeoWidgetFragment) } + } + @Test fun renderResourcesOnMapShouldSetGeoJsonAndCallZoomToPointsOnMap() { val featureCollection = FeatureCollection.fromFeatures(emptyList()) From 1991db9c5364326a10ee7a2a888d5ec7f95aed94 Mon Sep 17 00:00:00 2001 From: Ephraim Kigamba Date: Thu, 6 Jul 2023 13:13:34 +0300 Subject: [PATCH 2/4] Add Geowidget Fragment test --- .../geowidget/screens/GeoWidgetFragment.kt | 12 ++- .../screens/GeoWidgetFragmentTest.kt | 91 ++++++++++++++++++- .../geowidget/shadows/ShadowMapbox.java | 62 +++++++++++++ 3 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/shadows/ShadowMapbox.java diff --git a/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragment.kt b/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragment.kt index c4e58b11f3..953213b47b 100644 --- a/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragment.kt +++ b/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragment.kt @@ -72,15 +72,17 @@ open class GeoWidgetFragment : Fragment(), Observer { savedInstanceState: Bundle? ): View? { Mapbox.getInstance(requireContext(), BuildConfig.MAPBOX_SDK_TOKEN) - geoWidgetConfiguration = - configurationRegistry.retrieveConfiguration( - ConfigType.GeoWidget, - geoWidgetActivityArgs.configId - ) + geoWidgetConfiguration = geoWidgetConfiguration() return setupViews() } + private fun geoWidgetConfiguration(): GeoWidgetConfiguration = + configurationRegistry.retrieveConfiguration( + ConfigType.GeoWidget, + geoWidgetActivityArgs.configId + ) + /** Create the fragment views. Add the toolbar and KujakuMapView to a LinearLayout */ private fun setupViews(): LinearLayout { val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 168) diff --git a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt index d9e8f61a0b..a6a9e74281 100644 --- a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt +++ b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt @@ -16,11 +16,14 @@ package org.smartregister.fhircore.geowidget.screens +import android.content.Context import android.os.Build import android.os.Bundle import android.os.Looper +import android.widget.LinearLayout import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.MutableLiveData +import androidx.navigation.NavArgsLazy import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection import com.mapbox.geojson.Point @@ -50,11 +53,12 @@ import org.smartregister.fhircore.engine.configuration.geowidget.GeoWidgetConfig import org.smartregister.fhircore.geowidget.model.GeoWidgetEvent import org.smartregister.fhircore.geowidget.shadows.ShadowConnectivityReceiver import org.smartregister.fhircore.geowidget.shadows.ShadowKujakuMapView +import org.smartregister.fhircore.geowidget.shadows.ShadowMapbox @RunWith(RobolectricTestRunner::class) @Config( sdk = [Build.VERSION_CODES.O_MR1], - shadows = [ShadowConnectivityReceiver::class, ShadowKujakuMapView::class], + shadows = [ShadowConnectivityReceiver::class, ShadowKujakuMapView::class, ShadowMapbox::class], application = HiltTestApplication::class ) @HiltAndroidTest @@ -68,6 +72,7 @@ class GeoWidgetFragmentTest { @Before fun setup() { hiltRule.inject() + Robolectric.buildActivity(GeoWidgetTestActivity::class.java).create().resume().get() geowidgetFragment = GeoWidgetFragment() @@ -243,4 +248,88 @@ class GeoWidgetFragmentTest { verify { kujakuMapView.getMapAsync(any()) } } + + @Test + fun `onCreateView should call setupViews()`() { + val mockedGeoWidgetFragment = spyk(geowidgetFragment, recordPrivateCalls = true) + val geowidgetActivityArgs = spyk() + every { mockedGeoWidgetFragment.requireContext() } returns mockk() + + val geowidgetActivityArgs2 = spyk(NavArgsLazy(GeoWidgetFragmentArgs::class) { mockk() }) + + every { geowidgetActivityArgs.configId } returns "geowidget-config-id" + every { geowidgetActivityArgs2.value } returns geowidgetActivityArgs + + ReflectionHelpers.setField( + mockedGeoWidgetFragment, + "geoWidgetActivityArgs\$delegate", + geowidgetActivityArgs2 + ) + + every { mockedGeoWidgetFragment["setupViews"]() } returns mockk() + every { mockedGeoWidgetFragment["geoWidgetConfiguration"]() } returns + mockk() + + mockedGeoWidgetFragment.onCreateView(mockk(), mockk(), mockk()) + + verify { mockedGeoWidgetFragment["setupViews"]() } + } + + @Test + fun `onCreateView should initialise Mapbox()`() { + ShadowMapbox.allowMethodRecording() + + val mockedGeoWidgetFragment = spyk(geowidgetFragment) + val geowidgetActivityArgs = spyk() + val mockContext = mockk() + every { mockedGeoWidgetFragment.requireContext() } returns mockContext + + val geowidgetActivityArgs2 = spyk(NavArgsLazy(GeoWidgetFragmentArgs::class) { mockk() }) + + every { geowidgetActivityArgs.configId } returns "geowidget-config-id" + every { geowidgetActivityArgs2.value } returns geowidgetActivityArgs + + ReflectionHelpers.setField( + mockedGeoWidgetFragment, + "geoWidgetActivityArgs\$delegate", + geowidgetActivityArgs2 + ) + + every { mockedGeoWidgetFragment["setupViews"]() } returns mockk() + every { mockedGeoWidgetFragment["geoWidgetConfiguration"]() } returns + mockk() + + mockedGeoWidgetFragment.onCreateView(mockk(), mockk(), mockk()) + + val methodCallParams = ShadowMapbox.getLastMethodCall() + Assert.assertNotNull(methodCallParams["getInstance"]) + Assert.assertEquals(mockContext, methodCallParams["getInstance"]!![0]) + Assert.assertEquals("sample", methodCallParams["getInstance"]!![1]) + } + + @Test + fun `onCreateView should fetch geowidget configuration`() { + val mockedGeoWidgetFragment = spyk(geowidgetFragment, recordPrivateCalls = true) + val geowidgetActivityArgs = spyk() + every { mockedGeoWidgetFragment.requireContext() } returns mockk() + + val geowidgetActivityArgs2 = spyk(NavArgsLazy(GeoWidgetFragmentArgs::class) { mockk() }) + + every { geowidgetActivityArgs.configId } returns "geowidget-config-id" + every { geowidgetActivityArgs2.value } returns geowidgetActivityArgs + + ReflectionHelpers.setField( + mockedGeoWidgetFragment, + "geoWidgetActivityArgs\$delegate", + geowidgetActivityArgs2 + ) + + every { mockedGeoWidgetFragment["setupViews"]() } returns mockk() + every { mockedGeoWidgetFragment["geoWidgetConfiguration"]() } returns + mockk() + + mockedGeoWidgetFragment.onCreateView(mockk(), mockk(), mockk()) + + verify { mockedGeoWidgetFragment["geoWidgetConfiguration"]() } + } } diff --git a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/shadows/ShadowMapbox.java b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/shadows/ShadowMapbox.java new file mode 100644 index 0000000000..fddc3f7ed2 --- /dev/null +++ b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/shadows/ShadowMapbox.java @@ -0,0 +1,62 @@ +package org.smartregister.fhircore.geowidget.shadows; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.mapbox.mapboxsdk.Mapbox; +import com.mapbox.mapboxsdk.net.ConnectivityReceiver; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by Ephraim Kigamba - ekigamba@ona.io on 28/12/2017. + */ + +@Implements(Mapbox.class) +public class ShadowMapbox { + + private static String lastMethodCall = null; + private static List lastMethodParams = null; + + @Implementation + public static synchronized Mapbox getInstance(@NonNull Context context, @Nullable String accessToken) { + + if ("YES".equals(lastMethodCall)) { + lastMethodCall = "getInstance"; + lastMethodParams = new ArrayList<>(); + lastMethodParams.add(context); + lastMethodParams.add(accessToken); + } + + return null; + } + + public static void allowMethodRecording() { + lastMethodCall = "YES"; + } + + /** + * Once this method is called, it resets + * @return + */ + public static Map> getLastMethodCall() { + String methodCall = lastMethodCall; + List params = lastMethodParams; + + lastMethodCall = null; + lastMethodParams = null; + + HashMap> map = new HashMap<>(); + map.put(methodCall, params); + + return map; + } +} From 1feac0ce2368cac6e685a469fd6c57df3403d0bd Mon Sep 17 00:00:00 2001 From: Ephraim Kigamba Date: Thu, 27 Jul 2023 12:41:36 +0300 Subject: [PATCH 3/4] Fix failing GeowidgetFragment test due to wrong Mapbox SDK token --- .../fhircore/geowidget/screens/GeoWidgetFragmentTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt index a6a9e74281..f709199096 100644 --- a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt +++ b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt @@ -50,6 +50,7 @@ import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config import org.robolectric.util.ReflectionHelpers import org.smartregister.fhircore.engine.configuration.geowidget.GeoWidgetConfiguration +import org.smartregister.fhircore.geowidget.BuildConfig import org.smartregister.fhircore.geowidget.model.GeoWidgetEvent import org.smartregister.fhircore.geowidget.shadows.ShadowConnectivityReceiver import org.smartregister.fhircore.geowidget.shadows.ShadowKujakuMapView @@ -304,7 +305,7 @@ class GeoWidgetFragmentTest { val methodCallParams = ShadowMapbox.getLastMethodCall() Assert.assertNotNull(methodCallParams["getInstance"]) Assert.assertEquals(mockContext, methodCallParams["getInstance"]!![0]) - Assert.assertEquals("sample", methodCallParams["getInstance"]!![1]) + Assert.assertEquals(BuildConfig.MAPBOX_SDK_TOKEN, methodCallParams["getInstance"]!![1]) } @Test From 16420b9fad78a1847b4535502aee59d420d431a3 Mon Sep 17 00:00:00 2001 From: Ephraim Kigamba Date: Thu, 27 Jul 2023 15:41:09 +0300 Subject: [PATCH 4/4] Code cleanup with spotlessApply --- .../geowidget/screens/GeoWidgetFragment.kt | 16 ++++++++-------- .../geowidget/screens/GeoWidgetFragmentTest.kt | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragment.kt b/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragment.kt index 22dbe85dd1..42b39ec7f2 100644 --- a/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragment.kt +++ b/android/geowidget/src/main/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragment.kt @@ -69,7 +69,7 @@ open class GeoWidgetFragment : Fragment(), Observer { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View? { Mapbox.getInstance(requireContext(), BuildConfig.MAPBOX_SDK_TOKEN) geoWidgetConfiguration = geoWidgetConfiguration() @@ -80,7 +80,7 @@ open class GeoWidgetFragment : Fragment(), Observer { private fun geoWidgetConfiguration(): GeoWidgetConfiguration = configurationRegistry.retrieveConfiguration( ConfigType.GeoWidget, - geoWidgetActivityArgs.configId + geoWidgetActivityArgs.configId, ) /** Create the fragment views. Add the toolbar and KujakuMapView to a LinearLayout */ @@ -157,15 +157,15 @@ open class GeoWidgetFragment : Fragment(), Observer { geoWidgetViewModel.geoWidgetEventLiveData.postValue( GeoWidgetEvent.RegisterClient( location.idElement.value, - geoWidgetConfiguration.registrationQuestionnaire - ) + geoWidgetConfiguration.registrationQuestionnaire, + ), ) } } } override fun onCancel() {} - } + }, ) } @@ -182,7 +182,7 @@ open class GeoWidgetFragment : Fragment(), Observer { } } }, - "quest-data-points" + "quest-data-points", ) } @@ -222,8 +222,8 @@ open class GeoWidgetFragment : Fragment(), Observer { mapboxMap.easeCamera( CameraUpdateFactory.newLatLngBounds( LatLngBounds.from(paddedBbox[3], paddedBbox[2], paddedBbox[1], paddedBbox[0]), - 50 - ) + 50, + ), ) } } diff --git a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt index 6e17ca32cc..26c9328c3b 100644 --- a/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt +++ b/android/geowidget/src/test/java/org/smartregister/fhircore/geowidget/screens/GeoWidgetFragmentTest.kt @@ -60,7 +60,7 @@ import org.smartregister.fhircore.geowidget.shadows.ShadowMapbox @Config( sdk = [Build.VERSION_CODES.O_MR1], shadows = [ShadowConnectivityReceiver::class, ShadowKujakuMapView::class, ShadowMapbox::class], - application = HiltTestApplication::class + application = HiltTestApplication::class, ) @HiltAndroidTest class GeoWidgetFragmentTest { @@ -146,7 +146,7 @@ class GeoWidgetFragmentTest { ReflectionHelpers.setField( mockedGeoWidgetFragment, "geoWidgetViewModel\$delegate", - lazy { geoWidgetViewModel } + lazy { geoWidgetViewModel }, ) mockedGeoWidgetFragment.onResume() @@ -170,7 +170,7 @@ class GeoWidgetFragmentTest { ReflectionHelpers.setField( mockedGeoWidgetFragment, "geoWidgetViewModel\$delegate", - lazy { geoWidgetViewModel } + lazy { geoWidgetViewModel }, ) mockedGeoWidgetFragment.onResume() @@ -214,11 +214,11 @@ class GeoWidgetFragmentTest { every { geoWidgetFragment.setFeatureClickListener() } just runs every { geoWidgetViewModel.geoWidgetEventLiveData.postValue( - GeoWidgetEvent.OpenProfile(familyId, geoWidgetConfiguration) + GeoWidgetEvent.OpenProfile(familyId, geoWidgetConfiguration), ) } just runs geoWidgetViewModel.geoWidgetEventLiveData.postValue( - GeoWidgetEvent.OpenProfile(familyId, geoWidgetConfiguration) + GeoWidgetEvent.OpenProfile(familyId, geoWidgetConfiguration), ) GeoWidgetEvent.OpenProfile(familyId, mockk()) @@ -226,7 +226,7 @@ class GeoWidgetFragmentTest { verify { geoWidgetViewModel.geoWidgetEventLiveData.postValue( - GeoWidgetEvent.OpenProfile(familyId, geoWidgetConfiguration) + GeoWidgetEvent.OpenProfile(familyId, geoWidgetConfiguration), ) } } @@ -276,7 +276,7 @@ class GeoWidgetFragmentTest { ReflectionHelpers.setField( mockedGeoWidgetFragment, "geoWidgetActivityArgs\$delegate", - geowidgetActivityArgs2 + geowidgetActivityArgs2, ) every { mockedGeoWidgetFragment["setupViews"]() } returns mockk() @@ -305,7 +305,7 @@ class GeoWidgetFragmentTest { ReflectionHelpers.setField( mockedGeoWidgetFragment, "geoWidgetActivityArgs\$delegate", - geowidgetActivityArgs2 + geowidgetActivityArgs2, ) every { mockedGeoWidgetFragment["setupViews"]() } returns mockk() @@ -334,7 +334,7 @@ class GeoWidgetFragmentTest { ReflectionHelpers.setField( mockedGeoWidgetFragment, "geoWidgetActivityArgs\$delegate", - geowidgetActivityArgs2 + geowidgetActivityArgs2, ) every { mockedGeoWidgetFragment["setupViews"]() } returns mockk()