From 024488406c6e18eb9e5996f84d1565885059e60b Mon Sep 17 00:00:00 2001 From: "h.malik144@gmail.com" Date: Wed, 3 Jul 2024 17:24:35 +0100 Subject: [PATCH] - Modularised the app to share more setup - Fixed failing UI tests --- .../appttude/h_mal/atlas_weather/BaseTest.kt | 10 +++ .../atlas_weather/application/TestAppClass.kt | 17 ++++- .../data/location/MockLocationProvider.kt | 12 +++- .../snapshot/SnapshotCaptureTest.kt | 7 ++ .../atlas_weather/application/TestAppClass.kt | 67 ------------------- .../snapshot/SnapshotCaptureTest.kt | 7 ++ .../h_mal/monoWeather/tests/HomePageUITest.kt | 2 +- app/src/atlasWeather/AndroidManifest.xml | 2 +- .../{AtlasApp.kt => FlavourModule.kt} | 25 +++---- .../atlas_weather/ui/home/HomeFragment.kt | 16 +++-- .../application/{atlasApp.kt => AppClass.kt} | 0 .../atlas_weather/application/BaseAppClass.kt | 7 +- .../atlas_weather/data/room/WeatherDao.kt | 5 ++ app/src/monoWeather/AndroidManifest.xml | 2 +- .../{MonoApp.kt => FlavourModule.kt} | 11 +-- 15 files changed, 87 insertions(+), 103 deletions(-) rename app/src/{androidTestAtlasWeather => androidTest}/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt (86%) delete mode 100644 app/src/androidTestMonoWeather/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt rename app/src/atlasWeather/java/com/appttude/h_mal/atlas_weather/application/{AtlasApp.kt => FlavourModule.kt} (57%) rename app/src/main/java/com/appttude/h_mal/atlas_weather/application/{atlasApp.kt => AppClass.kt} (100%) rename app/src/monoWeather/java/com/appttude/h_mal/atlas_weather/application/{MonoApp.kt => FlavourModule.kt} (56%) diff --git a/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/BaseTest.kt b/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/BaseTest.kt index 3069fd6..1e3822e 100644 --- a/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/BaseTest.kt +++ b/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/BaseTest.kt @@ -92,8 +92,18 @@ open class BaseTest( testApp.stubLocation(location, lat, long) } + fun clearLocation(location: String) { + testApp.removeLocation(location) + } + + fun clearLocation(lat: Double, long: Double) { + testApp.removeLocation(lat, long) + } + fun clearPrefs() = prefs.clearPrefs() + fun clearDatabase() = testApp.clearDatabase() + fun getActivity() = testActivity @After diff --git a/app/src/androidTestAtlasWeather/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt b/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt similarity index 86% rename from app/src/androidTestAtlasWeather/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt rename to app/src/androidTest/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt index 2a3d363..d29f4f7 100644 --- a/app/src/androidTestAtlasWeather/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt +++ b/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt @@ -14,9 +14,13 @@ import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInt import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor import com.appttude.h_mal.atlas_weather.data.room.AppDatabase import com.appttude.h_mal.atlas_weather.data.room.Converter +import org.kodein.di.LazyKodein import java.io.BufferedReader -class TestAppClass : AtlasApp() { +class TestAppClass : AppClass() { + + override val kodein: LazyKodein = super.kodein + private val idlingResources = CountingIdlingResource("Data_loader") private val mockingNetworkInterceptor = MockingNetworkInterceptor(idlingResources) @@ -64,4 +68,15 @@ class TestAppClass : AtlasApp() { locationProvider.addLocationToList(location, lat, long) } + fun removeLocation(location: String) { + locationProvider.removeLocationFromList(location) + } + + fun removeLocation(lat: Double, long: Double) { + locationProvider.removeLocationFromList(lat, long) + } + + fun clearDatabase() { + database.getWeatherDao().deleteAll() + } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/data/location/MockLocationProvider.kt b/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/data/location/MockLocationProvider.kt index ea101a8..0ffeca6 100644 --- a/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/data/location/MockLocationProvider.kt +++ b/app/src/androidTest/java/com/appttude/h_mal/atlas_weather/data/location/MockLocationProvider.kt @@ -22,6 +22,16 @@ class MockLocationProvider : LocationProvider { fun addLocationToList(name: String, lat: Double, long: Double) { val latLong = Pair(lat, long) - feedMap.put(name, latLong) + feedMap[name] = latLong + } + + fun removeLocationFromList(name: String) { + feedMap.remove(name) + } + + fun removeLocationFromList(lat: Double, long: Double) { + feedMap.filterValues { it.first == lat && it.second == long }.keys.firstOrNull()?.let { + feedMap.remove(it) + } } } \ No newline at end of file diff --git a/app/src/androidTestAtlasWeather/java/com/appttude/h_mal/atlas_weather/snapshot/SnapshotCaptureTest.kt b/app/src/androidTestAtlasWeather/java/com/appttude/h_mal/atlas_weather/snapshot/SnapshotCaptureTest.kt index 4eff728..d457f41 100644 --- a/app/src/androidTestAtlasWeather/java/com/appttude/h_mal/atlas_weather/snapshot/SnapshotCaptureTest.kt +++ b/app/src/androidTestAtlasWeather/java/com/appttude/h_mal/atlas_weather/snapshot/SnapshotCaptureTest.kt @@ -22,6 +22,13 @@ class SnapshotCaptureTest : BaseTest(MainActivity::class.java) { clearPrefs() } + override fun testFinished() { + super.testFinished() + clearLocation("London") + clearDatabase() + clearPrefs() + } + @Test fun homeAndFurtherInfoPageCapture() { weatherScreen { diff --git a/app/src/androidTestMonoWeather/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt b/app/src/androidTestMonoWeather/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt deleted file mode 100644 index 9d796e1..0000000 --- a/app/src/androidTestMonoWeather/java/com/appttude/h_mal/atlas_weather/application/TestAppClass.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.appttude.h_mal.atlas_weather.application - -import androidx.room.Room -import androidx.test.espresso.IdlingRegistry -import androidx.test.espresso.idling.CountingIdlingResource -import androidx.test.platform.app.InstrumentationRegistry -import com.appttude.h_mal.atlas_weather.data.location.LocationProvider -import com.appttude.h_mal.atlas_weather.data.location.MockLocationProvider -import com.appttude.h_mal.atlas_weather.data.network.NetworkModule -import com.appttude.h_mal.atlas_weather.data.network.WeatherApi -import com.appttude.h_mal.atlas_weather.data.network.interceptors.MockingNetworkInterceptor -import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor -import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor -import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor -import com.appttude.h_mal.atlas_weather.data.room.AppDatabase -import com.appttude.h_mal.atlas_weather.data.room.Converter -import java.io.BufferedReader - -class TestAppClass : MonoApp() { - private val idlingResources = CountingIdlingResource("Data_loader") - private val mockingNetworkInterceptor = MockingNetworkInterceptor(idlingResources) - - lateinit var database: AppDatabase - private val locationProvider: MockLocationProvider = MockLocationProvider() - - override fun onCreate() { - super.onCreate() - IdlingRegistry.getInstance().register(idlingResources) - } - - override fun createNetworkModule(): WeatherApi { - return NetworkModule().invoke( - mockingNetworkInterceptor, - NetworkConnectionInterceptor(this), - QueryParamsInterceptor(), - loggingInterceptor - ) as WeatherApi - } - - override fun createLocationModule(): LocationProvider { - return locationProvider - } - - override fun createRoomDatabase(): AppDatabase { - database = Room.inMemoryDatabaseBuilder(applicationContext, AppDatabase::class.java) - .allowMainThreadQueries() - .addTypeConverter(Converter(this)) - .build() - return database - } - - fun stubUrl(url: String, rawPath: String, code: Int = 200) { - val iStream = - InstrumentationRegistry.getInstrumentation().context.assets.open("$rawPath.json") - val data = iStream.bufferedReader().use(BufferedReader::readText) - mockingNetworkInterceptor.addUrlStub(url = url, data = data, code = code) - } - - fun removeUrlStub(url: String) { - mockingNetworkInterceptor.removeUrlStub(url = url) - } - - fun stubLocation(location: String, lat: Double = 0.00, long: Double = 0.00) { - locationProvider.addLocationToList(location, lat, long) - } - -} \ No newline at end of file diff --git a/app/src/androidTestMonoWeather/java/com/appttude/h_mal/atlas_weather/snapshot/SnapshotCaptureTest.kt b/app/src/androidTestMonoWeather/java/com/appttude/h_mal/atlas_weather/snapshot/SnapshotCaptureTest.kt index a5e17bc..c40c435 100644 --- a/app/src/androidTestMonoWeather/java/com/appttude/h_mal/atlas_weather/snapshot/SnapshotCaptureTest.kt +++ b/app/src/androidTestMonoWeather/java/com/appttude/h_mal/atlas_weather/snapshot/SnapshotCaptureTest.kt @@ -9,6 +9,7 @@ import com.appttude.h_mal.atlas_weather.utils.Stubs import com.appttude.h_mal.monoWeather.robot.furtherInfoScreen import com.appttude.h_mal.monoWeather.robot.settingsScreen import com.appttude.h_mal.monoWeather.robot.weatherScreen +import org.junit.Ignore import org.junit.Test import tools.fastlane.screengrab.Screengrab @@ -22,6 +23,12 @@ class SnapshotCaptureTest : BaseTest(MainActivity::class.java) { clearPrefs() } + override fun testFinished() { + super.testFinished() + clearLocation("London") + clearDatabase() + } + @Test fun homeAndFurtherInfoPageCapture() { diff --git a/app/src/androidTestMonoWeather/java/com/appttude/h_mal/monoWeather/tests/HomePageUITest.kt b/app/src/androidTestMonoWeather/java/com/appttude/h_mal/monoWeather/tests/HomePageUITest.kt index fbe8e39..8b84247 100644 --- a/app/src/androidTestMonoWeather/java/com/appttude/h_mal/monoWeather/tests/HomePageUITest.kt +++ b/app/src/androidTestMonoWeather/java/com/appttude/h_mal/monoWeather/tests/HomePageUITest.kt @@ -8,6 +8,7 @@ import com.appttude.h_mal.atlas_weather.utils.Stubs import com.appttude.h_mal.monoWeather.robot.furtherInfoScreen import com.appttude.h_mal.monoWeather.robot.settingsScreen import com.appttude.h_mal.monoWeather.robot.weatherScreen +import okio.IOException import org.junit.Test import tools.fastlane.screengrab.Screengrab @@ -40,7 +41,6 @@ class HomePageUITest : BaseTest(MainActivity::class.java) { verifyMaxTemperature(12) verifyAverageTemperature(9) } - } @Test diff --git a/app/src/atlasWeather/AndroidManifest.xml b/app/src/atlasWeather/AndroidManifest.xml index bb3fd3e..e9829e4 100644 --- a/app/src/atlasWeather/AndroidManifest.xml +++ b/app/src/atlasWeather/AndroidManifest.xml @@ -5,7 +5,7 @@ (R.layout.fragment_home) { + private val notificationService by instance() + private lateinit var recyclerAdapter: WeatherRecyclerAdapter private lateinit var swipeRefresh: SwipeRefreshLayout @@ -100,7 +102,11 @@ class HomeFragment : BaseFragment(R.layout.fragment_home) { } @Deprecated("Deprecated in Java") - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) // NOTE: delegate the permission handling to generated method onRequestPermissionsResult(requestCode, grantResults) @@ -110,7 +116,7 @@ class HomeFragment : BaseFragment(R.layout.fragment_home) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { sendNotification() } else { - (requireActivity().application as AtlasApp).scheduleNotifications() + notificationService.schedulePushNotifications() } } @@ -142,7 +148,7 @@ class HomeFragment : BaseFragment(R.layout.fragment_home) { @SuppressLint("MissingPermission") @NeedsPermission(POST_NOTIFICATIONS) fun sendNotification() { - (requireActivity().application as AtlasApp).scheduleNotifications() + notificationService.schedulePushNotifications() } @OnShowRationale(POST_NOTIFICATIONS) diff --git a/app/src/main/java/com/appttude/h_mal/atlas_weather/application/atlasApp.kt b/app/src/main/java/com/appttude/h_mal/atlas_weather/application/AppClass.kt similarity index 100% rename from app/src/main/java/com/appttude/h_mal/atlas_weather/application/atlasApp.kt rename to app/src/main/java/com/appttude/h_mal/atlas_weather/application/AppClass.kt diff --git a/app/src/main/java/com/appttude/h_mal/atlas_weather/application/BaseAppClass.kt b/app/src/main/java/com/appttude/h_mal/atlas_weather/application/BaseAppClass.kt index 3411a10..5f36fb2 100644 --- a/app/src/main/java/com/appttude/h_mal/atlas_weather/application/BaseAppClass.kt +++ b/app/src/main/java/com/appttude/h_mal/atlas_weather/application/BaseAppClass.kt @@ -12,6 +12,7 @@ import com.appttude.h_mal.atlas_weather.helper.ServicesHelper import com.google.gson.Gson import org.kodein.di.Kodein import org.kodein.di.KodeinAware +import org.kodein.di.KodeinContainer import org.kodein.di.android.x.androidXModule import org.kodein.di.generic.bind import org.kodein.di.generic.instance @@ -22,7 +23,7 @@ abstract class BaseAppClass : Application(), KodeinAware { // Kodein aware to initialise the classes used for DI override val kodein = Kodein.lazy { import(parentModule) - import(flavourModule) + import(getFlavourModule(application = this@BaseAppClass)) } val parentModule = Kodein.Module("Parent Module", allowSilentOverride = true) { @@ -40,10 +41,6 @@ abstract class BaseAppClass : Application(), KodeinAware { bind() from singleton { WeatherSource(instance(), instance()) } } - open val flavourModule = Kodein.Module("Flavour") { - import(parentModule) - } - abstract fun createNetworkModule(): WeatherApi abstract fun createLocationModule(): LocationProvider abstract fun createRoomDatabase(): AppDatabase diff --git a/app/src/main/java/com/appttude/h_mal/atlas_weather/data/room/WeatherDao.kt b/app/src/main/java/com/appttude/h_mal/atlas_weather/data/room/WeatherDao.kt index 9c96e0d..9f286b7 100644 --- a/app/src/main/java/com/appttude/h_mal/atlas_weather/data/room/WeatherDao.kt +++ b/app/src/main/java/com/appttude/h_mal/atlas_weather/data/room/WeatherDao.kt @@ -1,7 +1,9 @@ package com.appttude.h_mal.atlas_weather.data.room +import androidx.annotation.VisibleForTesting import androidx.lifecycle.LiveData import androidx.room.Dao +import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query @@ -32,4 +34,7 @@ interface WeatherDao { @Query("DELETE FROM EntityItem WHERE id = :userId") fun deleteEntry(userId: String): Int + @VisibleForTesting + @Query("DELETE FROM EntityItem") + fun deleteAll(): Int } \ No newline at end of file diff --git a/app/src/monoWeather/AndroidManifest.xml b/app/src/monoWeather/AndroidManifest.xml index 88f1301..3d0dc3f 100644 --- a/app/src/monoWeather/AndroidManifest.xml +++ b/app/src/monoWeather/AndroidManifest.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools">