mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2025-12-10 02:05:20 +00:00
- mid commit
This commit is contained in:
33
.idea/androidTestResultsUserPreferences.xml
generated
33
.idea/androidTestResultsUserPreferences.xml
generated
@@ -16,6 +16,23 @@
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-2113309033">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="2C121FDH300122" value="120" />
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Google Android SDK built for x86" value="120" />
|
||||
<entry key="Google Pixel 7 Pro" value="120" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
<entry key="emulator-5554" value="120" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-2008434490">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
@@ -68,6 +85,20 @@
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-860247611">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="2C121FDH300122" value="120" />
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Google Pixel 7 Pro" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
</option>
|
||||
</AndroidTestResultsTableState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="-409920851">
|
||||
<value>
|
||||
<AndroidTestResultsTableState>
|
||||
@@ -86,7 +117,9 @@
|
||||
<AndroidTestResultsTableState>
|
||||
<option name="preferredColumnWidths">
|
||||
<map>
|
||||
<entry key="2C121FDH300122" value="120" />
|
||||
<entry key="Duration" value="90" />
|
||||
<entry key="Google Pixel 7 Pro" value="120" />
|
||||
<entry key="Pixel_2_API_27" value="120" />
|
||||
<entry key="Tests" value="360" />
|
||||
</map>
|
||||
|
||||
350
app/src/androidTest/assets/valid_response_metric_sydney.json
Normal file
350
app/src/androidTest/assets/valid_response_metric_sydney.json
Normal file
@@ -0,0 +1,350 @@
|
||||
{
|
||||
"lat": -33.89,
|
||||
"lon": -151.12,
|
||||
"timezone": "Etc/GMT+10",
|
||||
"timezone_offset": -36000,
|
||||
"current": {
|
||||
"dt": 1691771435,
|
||||
"sunrise": 1691772463,
|
||||
"sunset": 1691811108,
|
||||
"temp": 12.48,
|
||||
"feels_like": 11.25,
|
||||
"pressure": 1025,
|
||||
"humidity": 56,
|
||||
"dew_point": 3.95,
|
||||
"uvi": 0,
|
||||
"clouds": 100,
|
||||
"visibility": 10000,
|
||||
"wind_speed": 2.3,
|
||||
"wind_deg": 66,
|
||||
"wind_gust": 2.68,
|
||||
"weather": [
|
||||
{
|
||||
"id": 804,
|
||||
"main": "Clouds",
|
||||
"description": "overcast clouds",
|
||||
"icon": "04n"
|
||||
}
|
||||
]
|
||||
},
|
||||
"daily": [
|
||||
{
|
||||
"dt": 1691791200,
|
||||
"sunrise": 1691772463,
|
||||
"sunset": 1691811108,
|
||||
"moonrise": 1691761680,
|
||||
"moonset": 1691796060,
|
||||
"moon_phase": 0.86,
|
||||
"temp": {
|
||||
"day": 12.94,
|
||||
"min": 12.26,
|
||||
"max": 13.59,
|
||||
"night": 13.59,
|
||||
"eve": 12.46,
|
||||
"morn": 12.46
|
||||
},
|
||||
"feels_like": {
|
||||
"day": 11.91,
|
||||
"night": 13.38,
|
||||
"eve": 11.8,
|
||||
"morn": 11.2
|
||||
},
|
||||
"pressure": 1022,
|
||||
"humidity": 62,
|
||||
"dew_point": 5.89,
|
||||
"wind_speed": 17.33,
|
||||
"wind_deg": 36,
|
||||
"wind_gust": 21.77,
|
||||
"weather": [
|
||||
{
|
||||
"id": 501,
|
||||
"main": "Rain",
|
||||
"description": "moderate rain",
|
||||
"icon": "10d"
|
||||
}
|
||||
],
|
||||
"clouds": 100,
|
||||
"pop": 1,
|
||||
"rain": 5.93,
|
||||
"uvi": 2.13
|
||||
},
|
||||
{
|
||||
"dt": 1691877600,
|
||||
"sunrise": 1691858802,
|
||||
"sunset": 1691897551,
|
||||
"moonrise": 1691851260,
|
||||
"moonset": 1691885640,
|
||||
"moon_phase": 0.89,
|
||||
"temp": {
|
||||
"day": 15.41,
|
||||
"min": 14.02,
|
||||
"max": 16.03,
|
||||
"night": 15.48,
|
||||
"eve": 15.27,
|
||||
"morn": 15.67
|
||||
},
|
||||
"feels_like": {
|
||||
"day": 15.23,
|
||||
"night": 15.25,
|
||||
"eve": 14.92,
|
||||
"morn": 15.77
|
||||
},
|
||||
"pressure": 1008,
|
||||
"humidity": 85,
|
||||
"dew_point": 12.76,
|
||||
"wind_speed": 16.89,
|
||||
"wind_deg": 26,
|
||||
"wind_gust": 22.91,
|
||||
"weather": [
|
||||
{
|
||||
"id": 501,
|
||||
"main": "Rain",
|
||||
"description": "moderate rain",
|
||||
"icon": "10d"
|
||||
}
|
||||
],
|
||||
"clouds": 7,
|
||||
"pop": 1,
|
||||
"rain": 11.8,
|
||||
"uvi": 4.27
|
||||
},
|
||||
{
|
||||
"dt": 1691964000,
|
||||
"sunrise": 1691945139,
|
||||
"sunset": 1691983994,
|
||||
"moonrise": 1691940540,
|
||||
"moonset": 1691975520,
|
||||
"moon_phase": 0.92,
|
||||
"temp": {
|
||||
"day": 14.49,
|
||||
"min": 13.32,
|
||||
"max": 15.51,
|
||||
"night": 13.32,
|
||||
"eve": 13.92,
|
||||
"morn": 15.02
|
||||
},
|
||||
"feels_like": {
|
||||
"day": 14.11,
|
||||
"night": 12.67,
|
||||
"eve": 13.56,
|
||||
"morn": 14.59
|
||||
},
|
||||
"pressure": 1005,
|
||||
"humidity": 81,
|
||||
"dew_point": 11.26,
|
||||
"wind_speed": 16.29,
|
||||
"wind_deg": 281,
|
||||
"wind_gust": 21.74,
|
||||
"weather": [
|
||||
{
|
||||
"id": 500,
|
||||
"main": "Rain",
|
||||
"description": "light rain",
|
||||
"icon": "10d"
|
||||
}
|
||||
],
|
||||
"clouds": 84,
|
||||
"pop": 0.63,
|
||||
"rain": 1.42,
|
||||
"uvi": 3.5
|
||||
},
|
||||
{
|
||||
"dt": 1692050400,
|
||||
"sunrise": 1692031476,
|
||||
"sunset": 1692070437,
|
||||
"moonrise": 1692029400,
|
||||
"moonset": 1692065400,
|
||||
"moon_phase": 0.95,
|
||||
"temp": {
|
||||
"day": 12.81,
|
||||
"min": 12.63,
|
||||
"max": 13.09,
|
||||
"night": 12.63,
|
||||
"eve": 12.74,
|
||||
"morn": 12.84
|
||||
},
|
||||
"feels_like": {
|
||||
"day": 11.71,
|
||||
"night": 11.36,
|
||||
"eve": 11.51,
|
||||
"morn": 11.9
|
||||
},
|
||||
"pressure": 1019,
|
||||
"humidity": 60,
|
||||
"dew_point": 5.36,
|
||||
"wind_speed": 15.72,
|
||||
"wind_deg": 218,
|
||||
"wind_gust": 18.13,
|
||||
"weather": [
|
||||
{
|
||||
"id": 500,
|
||||
"main": "Rain",
|
||||
"description": "light rain",
|
||||
"icon": "10d"
|
||||
}
|
||||
],
|
||||
"clouds": 74,
|
||||
"pop": 0.47,
|
||||
"rain": 0.46,
|
||||
"uvi": 2.37
|
||||
},
|
||||
{
|
||||
"dt": 1692136800,
|
||||
"sunrise": 1692117812,
|
||||
"sunset": 1692156879,
|
||||
"moonrise": 1692117900,
|
||||
"moonset": 1692155340,
|
||||
"moon_phase": 0,
|
||||
"temp": {
|
||||
"day": 12.21,
|
||||
"min": 12.21,
|
||||
"max": 13.31,
|
||||
"night": 13.3,
|
||||
"eve": 13.14,
|
||||
"morn": 12.29
|
||||
},
|
||||
"feels_like": {
|
||||
"day": 11,
|
||||
"night": 12.28,
|
||||
"eve": 11.97,
|
||||
"morn": 10.98
|
||||
},
|
||||
"pressure": 1023,
|
||||
"humidity": 58,
|
||||
"dew_point": 4.12,
|
||||
"wind_speed": 6.03,
|
||||
"wind_deg": 241,
|
||||
"wind_gust": 7.73,
|
||||
"weather": [
|
||||
{
|
||||
"id": 803,
|
||||
"main": "Clouds",
|
||||
"description": "broken clouds",
|
||||
"icon": "04d"
|
||||
}
|
||||
],
|
||||
"clouds": 80,
|
||||
"pop": 0,
|
||||
"uvi": 3
|
||||
},
|
||||
{
|
||||
"dt": 1692223200,
|
||||
"sunrise": 1692204146,
|
||||
"sunset": 1692243322,
|
||||
"moonrise": 1692206100,
|
||||
"moonset": 1692245160,
|
||||
"moon_phase": 0.02,
|
||||
"temp": {
|
||||
"day": 12.69,
|
||||
"min": 12.33,
|
||||
"max": 13.65,
|
||||
"night": 12.97,
|
||||
"eve": 13.1,
|
||||
"morn": 13.58
|
||||
},
|
||||
"feels_like": {
|
||||
"day": 12.05,
|
||||
"night": 11.84,
|
||||
"eve": 12.14,
|
||||
"morn": 12.93
|
||||
},
|
||||
"pressure": 1024,
|
||||
"humidity": 78,
|
||||
"dew_point": 8.88,
|
||||
"wind_speed": 7.54,
|
||||
"wind_deg": 130,
|
||||
"wind_gust": 7.83,
|
||||
"weather": [
|
||||
{
|
||||
"id": 501,
|
||||
"main": "Rain",
|
||||
"description": "moderate rain",
|
||||
"icon": "10d"
|
||||
}
|
||||
],
|
||||
"clouds": 86,
|
||||
"pop": 0.8,
|
||||
"rain": 10.37,
|
||||
"uvi": 3
|
||||
},
|
||||
{
|
||||
"dt": 1692309600,
|
||||
"sunrise": 1692290480,
|
||||
"sunset": 1692329764,
|
||||
"moonrise": 1692294120,
|
||||
"moonset": 1692334980,
|
||||
"moon_phase": 0.05,
|
||||
"temp": {
|
||||
"day": 12.81,
|
||||
"min": 12.44,
|
||||
"max": 13.25,
|
||||
"night": 13.25,
|
||||
"eve": 13.05,
|
||||
"morn": 12.79
|
||||
},
|
||||
"feels_like": {
|
||||
"day": 11.58,
|
||||
"night": 12.28,
|
||||
"eve": 11.93,
|
||||
"morn": 11.61
|
||||
},
|
||||
"pressure": 1030,
|
||||
"humidity": 55,
|
||||
"dew_point": 3.97,
|
||||
"wind_speed": 7.57,
|
||||
"wind_deg": 145,
|
||||
"wind_gust": 7.26,
|
||||
"weather": [
|
||||
{
|
||||
"id": 800,
|
||||
"main": "Clear",
|
||||
"description": "clear sky",
|
||||
"icon": "01d"
|
||||
}
|
||||
],
|
||||
"clouds": 5,
|
||||
"pop": 0,
|
||||
"uvi": 3
|
||||
},
|
||||
{
|
||||
"dt": 1692396000,
|
||||
"sunrise": 1692376812,
|
||||
"sunset": 1692416207,
|
||||
"moonrise": 1692382020,
|
||||
"moonset": 1692424680,
|
||||
"moon_phase": 0.08,
|
||||
"temp": {
|
||||
"day": 13.99,
|
||||
"min": 13.52,
|
||||
"max": 14.8,
|
||||
"night": 14.8,
|
||||
"eve": 14.4,
|
||||
"morn": 13.52
|
||||
},
|
||||
"feels_like": {
|
||||
"day": 13.14,
|
||||
"night": 14.29,
|
||||
"eve": 13.62,
|
||||
"morn": 12.73
|
||||
},
|
||||
"pressure": 1027,
|
||||
"humidity": 65,
|
||||
"dew_point": 7.62,
|
||||
"wind_speed": 12.53,
|
||||
"wind_deg": 47,
|
||||
"wind_gust": 14.21,
|
||||
"weather": [
|
||||
{
|
||||
"id": 500,
|
||||
"main": "Rain",
|
||||
"description": "light rain",
|
||||
"icon": "10d"
|
||||
}
|
||||
],
|
||||
"clouds": 99,
|
||||
"pop": 0.31,
|
||||
"rain": 0.31,
|
||||
"uvi": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import androidx.test.espresso.Root
|
||||
import androidx.test.espresso.UiController
|
||||
import androidx.test.espresso.ViewAction
|
||||
import androidx.test.espresso.assertion.ViewAssertions
|
||||
import androidx.test.espresso.matcher.RootMatchers.withDecorView
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
@@ -26,6 +27,7 @@ import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.hamcrest.Description
|
||||
import org.hamcrest.Matcher
|
||||
import org.hamcrest.Matchers
|
||||
import org.hamcrest.TypeSafeMatcher
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@@ -42,6 +44,7 @@ open class BaseTest<A : Activity>(
|
||||
lateinit var scenario: ActivityScenario<A>
|
||||
private lateinit var testApp: TestAppClass
|
||||
private lateinit var testActivity: Activity
|
||||
private lateinit var decorView: View
|
||||
|
||||
@get:Rule
|
||||
var permissionRule = GrantPermissionRule.grant(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
@@ -66,6 +69,7 @@ open class BaseTest<A : Activity>(
|
||||
|
||||
scenario = ActivityScenario.launch(startIntent)
|
||||
scenario.onActivity {
|
||||
decorView = it.window.decorView
|
||||
testActivity = it
|
||||
}
|
||||
afterLaunch()
|
||||
@@ -79,6 +83,10 @@ open class BaseTest<A : Activity>(
|
||||
testApp.removeUrlStub(url)
|
||||
}
|
||||
|
||||
fun stubLocation(location: String, lat: Double = 0.00, long: Double = 0.00) {
|
||||
testApp.stubLocation(location, lat, long)
|
||||
}
|
||||
|
||||
fun getActivity() = testActivity
|
||||
|
||||
@After
|
||||
@@ -102,27 +110,8 @@ open class BaseTest<A : Activity>(
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
fun checkToastMessage(message: String) {
|
||||
Espresso.onView(ViewMatchers.withText(message)).inRoot(object : BaseCustomMatcher<Root>() {
|
||||
override fun describe(description: Description?) {
|
||||
description?.appendText("is toast")
|
||||
}
|
||||
|
||||
override fun matchesSafely(root: Root): Boolean {
|
||||
root.run {
|
||||
if (windowLayoutParams.get().type == WindowManager.LayoutParams.TYPE_TOAST) {
|
||||
decorView.run {
|
||||
if (windowToken === applicationWindowToken) {
|
||||
// windowToken == appToken means this window isn't contained by any other windows.
|
||||
// if it was a window for an activity, it would have TYPE_BASE_APPLICATION.
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
).check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
|
||||
Espresso.onView(ViewMatchers.withText(message)).inRoot(withDecorView(Matchers.not(decorView)))
|
||||
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
waitFor(3500)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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
|
||||
@@ -13,14 +14,15 @@ 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 com.appttude.h_mal.atlas_weather.test.BuildConfig
|
||||
import com.appttude.h_mal.atlas_weather.test.BuildConfig.APPLICATION_ID
|
||||
import java.io.BufferedReader
|
||||
|
||||
class TestAppClass : BaseAppClass() {
|
||||
private val idlingResources = CountingIdlingResource("Data_loader")
|
||||
private val mockingNetworkInterceptor = MockingNetworkInterceptor(idlingResources)
|
||||
|
||||
lateinit var database: AppDatabase
|
||||
lateinit var locationProvider: MockLocationProvider
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
IdlingRegistry.getInstance().register(idlingResources)
|
||||
@@ -35,16 +37,21 @@ class TestAppClass : BaseAppClass() {
|
||||
) as WeatherApi
|
||||
}
|
||||
|
||||
override fun createLocationModule() = MockLocationProvider()
|
||||
override fun createLocationModule(): LocationProvider {
|
||||
locationProvider = MockLocationProvider()
|
||||
return locationProvider
|
||||
}
|
||||
|
||||
override fun createRoomDatabase(): AppDatabase {
|
||||
return Room.inMemoryDatabaseBuilder(this, AppDatabase::class.java)
|
||||
database = Room.inMemoryDatabaseBuilder(this, AppDatabase::class.java)
|
||||
.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 iStream =
|
||||
InstrumentationRegistry.getInstrumentation().context.assets.open("$rawPath.json")
|
||||
val data = iStream.bufferedReader().use(BufferedReader::readText)
|
||||
mockingNetworkInterceptor.addUrlStub(url = url, data = data, code = code)
|
||||
}
|
||||
@@ -53,4 +60,8 @@ class TestAppClass : BaseAppClass() {
|
||||
mockingNetworkInterceptor.removeUrlStub(url = url)
|
||||
}
|
||||
|
||||
fun stubLocation(location: String, lat: Double = 0.00, long: Double = 0.00) {
|
||||
locationProvider.addLocationToList(location, lat, long)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,17 +3,25 @@ package com.appttude.h_mal.atlas_weather.data.location
|
||||
import com.appttude.h_mal.atlas_weather.model.types.LocationType
|
||||
|
||||
class MockLocationProvider : LocationProvider {
|
||||
private val latLong = Pair(0.00, 0.00)
|
||||
private var feedMap: MutableMap<String, Pair<Double, Double>> = mutableMapOf()
|
||||
|
||||
private var latLong = Pair(0.00, 0.00)
|
||||
|
||||
override suspend fun getCurrentLatLong() = latLong
|
||||
override fun getLatLongFromLocationName(location: String) = latLong
|
||||
override fun getLatLongFromLocationName(location: String): Pair<Double, Double> {
|
||||
return feedMap[location] ?: Pair(0.00, 0.00)
|
||||
}
|
||||
|
||||
override suspend fun getLocationNameFromLatLong(
|
||||
lat: Double,
|
||||
long: Double,
|
||||
type: LocationType
|
||||
): String {
|
||||
return "Mock Location"
|
||||
return feedMap.filterValues { it.first == lat && it.second == long }.keys.firstOrNull() ?: "Mock Location"
|
||||
}
|
||||
|
||||
fun addLocationToList(name: String, lat: Double, long: Double) {
|
||||
val latLong = Pair(lat, long)
|
||||
feedMap.put(name, latLong)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.appttude.h_mal.atlas_weather.testSuite
|
||||
|
||||
import android.os.Build
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.room.Room
|
||||
import androidx.room.util.UUIDUtil
|
||||
@@ -10,6 +11,7 @@ import com.appttude.h_mal.atlas_weather.data.room.WeatherDao
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
||||
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
||||
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
||||
import com.appttude.h_mal.atlas_weather.test.BuildConfig
|
||||
import com.appttude.h_mal.atlas_weather.utils.getOrAwaitValue
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
@@ -20,6 +22,7 @@ import org.junit.Test
|
||||
import org.junit.Assert.*
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.Mockito.mock
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
@@ -96,7 +99,11 @@ class RoomDatabaseTests {
|
||||
}
|
||||
|
||||
private fun createEntity(id: String = CURRENT_LOCATION): EntityItem {
|
||||
val weather = mockk<FullWeather>()
|
||||
val weather = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
FullWeather()
|
||||
} else {
|
||||
mockk<FullWeather>()
|
||||
}
|
||||
return EntityItem(id, weather)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,5 +6,6 @@ enum class Stubs(
|
||||
Metric("valid_response_metric"),
|
||||
Imperial("valid_response_imperial"),
|
||||
WrongLocation("wrong_location_response"),
|
||||
InvalidKey("invalid_api_key_response")
|
||||
InvalidKey("invalid_api_key_response"),
|
||||
Sydney("valid_response_metric_sydney")
|
||||
}
|
||||
@@ -17,8 +17,6 @@ class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
homeScreen {
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
// verify toast
|
||||
checkToastMessage("Invalid API key. Please see http://openweathermap.org/faq#error401 for more info.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +25,6 @@ class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
homeScreen {
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
// verify toast
|
||||
checkToastMessage("Invalid API key. Please see http://openweathermap.org/faq#error401 for more info.")
|
||||
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
|
||||
refresh()
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.appttude.h_mal.monoWeather.robot
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.BaseTestRobot
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
|
||||
fun addLocation(func: AddLocationScreenRobot.() -> Unit) = AddLocationScreenRobot().apply { func() }
|
||||
class AddLocationScreenRobot : BaseTestRobot() {
|
||||
fun setLocation(location: String) =
|
||||
fillEditText(R.id.location_name_tv, location)
|
||||
|
||||
fun submit() = clickButton(R.id.submit)
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
package com.appttude.h_mal.monoWeather.robot
|
||||
|
||||
import androidx.test.espresso.Espresso
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import com.appttude.h_mal.atlas_weather.BaseTestRobot
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.helpers.EspressoHelper
|
||||
|
||||
fun container(func: ContainerRobot.() -> Unit) = ContainerRobot().apply { func() }
|
||||
class ContainerRobot : BaseTestRobot() {
|
||||
|
||||
fun tapTabInBottomBar(tab: Tab) {
|
||||
when (tab) {
|
||||
Tab.HOME -> Espresso.onView(withId(R.id.nav_world))
|
||||
Tab.WORLD -> Espresso.onView(withId(R.id.nav_home))
|
||||
Tab.WORLD -> EspressoHelper.waitForView(withId(R.id.nav_world))
|
||||
Tab.HOME -> EspressoHelper.waitForView(withId(R.id.nav_home))
|
||||
}.perform(click())
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.appttude.h_mal.monoWeather.robot
|
||||
import com.appttude.h_mal.atlas_weather.BaseTestRobot
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
|
||||
fun homeScreen(func: HomeScreenRobot.() -> Unit) = HomeScreenRobot().apply { func() }
|
||||
class HomeScreenRobot : BaseTestRobot() {
|
||||
fun weatherScreen(func: WeatherScreen.() -> Unit) = WeatherScreen().apply { func() }
|
||||
class WeatherScreen : BaseTestRobot() {
|
||||
fun verifyCurrentTemperature(temperature: Int) =
|
||||
matchText(R.id.temp_main_4, temperature.toString())
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.appttude.h_mal.monoWeather.robot
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import com.appttude.h_mal.atlas_weather.BaseTestRobot
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.helpers.EspressoHelper
|
||||
|
||||
fun world(func: WorldScreenRobot.() -> Unit) = WorldScreenRobot().apply { func() }
|
||||
class WorldScreenRobot : BaseTestRobot() {
|
||||
fun clickFab() = clickButton(R.id.floatingActionButton)
|
||||
fun clickItemInList(location: String) {
|
||||
EspressoHelper.waitForView(withId(R.id.world_recycler))
|
||||
clickViewInRecycler<RecyclerView.ViewHolder>(R.id.world_recycler, location)
|
||||
}
|
||||
|
||||
fun clickItemInListByPosition(position: Int) =
|
||||
clickViewInRecycler<RecyclerView.ViewHolder>(R.id.world_recycler, position)
|
||||
|
||||
fun emptyViewDisplayed() {
|
||||
matchText(R.id.body_text, R.string.retrieve_warning)
|
||||
matchText(R.id.header_text, R.string.empty_retrieve_warning)
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package com.appttude.h_mal.monoWeather.tests
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import com.appttude.h_mal.monoWeather.MonoBaseTest
|
||||
import com.appttude.h_mal.monoWeather.robot.homeScreen
|
||||
import com.appttude.h_mal.monoWeather.robot.weatherScreen
|
||||
import org.junit.Test
|
||||
|
||||
class HomePageNoDataUITest : MonoBaseTest() {
|
||||
@@ -14,21 +14,17 @@ class HomePageNoDataUITest : MonoBaseTest() {
|
||||
|
||||
@Test
|
||||
fun loadApp_invalidKeyWeatherResponse_returnsEmptyViewPage() {
|
||||
homeScreen {
|
||||
weatherScreen {
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
// verify toast
|
||||
checkToastMessage("Invalid API key. Please see http://openweathermap.org/faq#error401 for more info.")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalidKeyWeatherResponse_swipeToRefresh_returnsValidPage() {
|
||||
homeScreen {
|
||||
weatherScreen {
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
// verify toast
|
||||
checkToastMessage("Invalid API key. Please see http://openweathermap.org/faq#error401 for more info.")
|
||||
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
|
||||
refresh()
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.appttude.h_mal.monoWeather.tests
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import com.appttude.h_mal.monoWeather.MonoBaseTest
|
||||
import com.appttude.h_mal.monoWeather.robot.homeScreen
|
||||
import com.appttude.h_mal.monoWeather.robot.weatherScreen
|
||||
import org.junit.Test
|
||||
|
||||
class HomePageUITest : MonoBaseTest() {
|
||||
@@ -14,7 +14,7 @@ class HomePageUITest : MonoBaseTest() {
|
||||
|
||||
@Test
|
||||
fun loadApp_validWeatherResponse_returnsValidPage() {
|
||||
homeScreen {
|
||||
weatherScreen {
|
||||
isDisplayed()
|
||||
verifyCurrentTemperature(2)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
|
||||
@@ -3,27 +3,40 @@ package com.appttude.h_mal.monoWeather.tests
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import com.appttude.h_mal.monoWeather.MonoBaseTest
|
||||
import com.appttude.h_mal.monoWeather.robot.ContainerRobot
|
||||
import com.appttude.h_mal.monoWeather.robot.ContainerRobot.Tab.WORLD
|
||||
import com.appttude.h_mal.monoWeather.robot.addLocation
|
||||
import com.appttude.h_mal.monoWeather.robot.container
|
||||
import com.appttude.h_mal.monoWeather.robot.homeScreen
|
||||
import com.appttude.h_mal.monoWeather.robot.weatherScreen
|
||||
import com.appttude.h_mal.monoWeather.robot.world
|
||||
import org.junit.Test
|
||||
|
||||
class WorldPageUITest : MonoBaseTest() {
|
||||
|
||||
// override fun beforeLaunch() {
|
||||
// stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun loadApp_addNewLocation_returnsValidPage() {
|
||||
// container {
|
||||
// tapTabInBottomBar(WORLD)
|
||||
// }
|
||||
// homeScreen {
|
||||
// isDisplayed()
|
||||
// verifyCurrentTemperature(2)
|
||||
// verifyCurrentLocation("Mock Location")
|
||||
// }
|
||||
// }
|
||||
override fun beforeLaunch() {
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApp_addNewLocation_returnsValidPage() {
|
||||
container {
|
||||
tapTabInBottomBar(WORLD)
|
||||
}
|
||||
world {
|
||||
clickFab()
|
||||
}
|
||||
addLocation {
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Sydney)
|
||||
stubLocation("Sydney", -33.89, -151.12)
|
||||
setLocation("Sydney")
|
||||
submit()
|
||||
}
|
||||
world {
|
||||
clickItemInList("Sydney")
|
||||
}
|
||||
weatherScreen {
|
||||
isDisplayed()
|
||||
verifyCurrentTemperature(12)
|
||||
verifyCurrentLocation("Sydney")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
|
||||
class EmptyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||
class EmptyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val icon: ImageView = itemView.findViewById(R.id.icon)
|
||||
var bodyTV: TextView = itemView.findViewById(R.id.body_text)
|
||||
var headerTV: TextView = itemView.findViewById(R.id.header_text)
|
||||
|
||||
fun bindData(
|
||||
@DrawableRes imageRes: Int? = R.drawable.ic_baseline_cloud_off_24,
|
||||
header: String = itemView.resources.getString(R.string.retrieve_warning),
|
||||
body: String = itemView.resources.getString(R.string.empty_retrieve_warning) ){
|
||||
imageRes?.let { icon.setImageResource(it) }
|
||||
headerTV.text = header
|
||||
bodyTV.text = body
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ class WeatherRecyclerAdapter(
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (getDataType(viewType)) {
|
||||
is ViewType.Empty -> {
|
||||
val emptyViewHolder = View(parent.context)
|
||||
val emptyViewHolder = parent.generateView(R.layout.empty_state_layout)
|
||||
EmptyViewHolder(emptyViewHolder)
|
||||
}
|
||||
|
||||
@@ -75,7 +75,8 @@ class WeatherRecyclerAdapter(
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (getDataType(getItemViewType(position))) {
|
||||
is ViewType.Empty -> {
|
||||
holder as EmptyViewHolder
|
||||
val emptyViewHolder = holder as EmptyViewHolder
|
||||
emptyViewHolder.bindData()
|
||||
}
|
||||
|
||||
is ViewType.Current -> {
|
||||
|
||||
@@ -31,15 +31,4 @@
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:srcCompat="@android:drawable/ic_input_add" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar2"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -18,6 +18,7 @@ class WorldViewModel(
|
||||
private val locationProvider: LocationProvider,
|
||||
private val repository: Repository
|
||||
) : WeatherViewModel() {
|
||||
private var currentLocation: String? = null
|
||||
|
||||
private val weatherListLiveData = repository.loadRoomWeatherLiveData()
|
||||
|
||||
@@ -27,9 +28,13 @@ class WorldViewModel(
|
||||
WeatherDisplay(data)
|
||||
}
|
||||
onSuccess(list)
|
||||
currentLocation?.let { i -> list.first { j -> j.location == i } }
|
||||
?.let { k -> onSuccess(k) }
|
||||
}
|
||||
}
|
||||
|
||||
fun setLocation(location: String) = run { currentLocation = location }
|
||||
|
||||
fun getSingleLocation(locationName: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val entity = repository.getSingleWeather(locationName)
|
||||
@@ -48,8 +53,7 @@ class WorldViewModel(
|
||||
repository.getSingleWeather(locationName)
|
||||
}
|
||||
repository.saveCurrentWeatherToRoom(weatherEntity)
|
||||
repository.saveLastSavedAt(locationName)
|
||||
onSuccess(Unit)
|
||||
repository.saveLastSavedAt(weatherEntity.id)
|
||||
} catch (e: IOException) {
|
||||
onError(e.message!!)
|
||||
}
|
||||
|
||||
@@ -37,20 +37,4 @@
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
android:visibility="gone">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -23,6 +23,7 @@ class WorldItemFragment : BaseFragment<WorldViewModel>(R.layout.fragment_home) {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
param1 = WorldItemFragmentArgs.fromBundle(requireArguments()).locationName
|
||||
param1?.let { viewModel.setLocation(it) }
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@@ -52,12 +53,11 @@ class WorldItemFragment : BaseFragment<WorldViewModel>(R.layout.fragment_home) {
|
||||
}
|
||||
|
||||
override fun onSuccess(data: Any?) {
|
||||
super.onSuccess(data)
|
||||
swipe_refresh.isRefreshing = false
|
||||
|
||||
if (data is WeatherDisplay) {
|
||||
recyclerAdapter.addCurrent(data)
|
||||
}
|
||||
super.onSuccess(data)
|
||||
swipe_refresh.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun onFailure(error: Any?) {
|
||||
|
||||
@@ -2,15 +2,13 @@ package com.appttude.h_mal.monoWeather.ui.world
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.observe
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
|
||||
import com.appttude.h_mal.atlas_weather.utils.displayToast
|
||||
import com.appttude.h_mal.atlas_weather.utils.goBack
|
||||
import com.appttude.h_mal.atlas_weather.utils.hideKeyboard
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
|
||||
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
|
||||
import kotlinx.android.synthetic.main.activity_add_forecast.location_name_tv
|
||||
import kotlinx.android.synthetic.main.activity_add_forecast.progressBar
|
||||
import kotlinx.android.synthetic.main.activity_add_forecast.submit
|
||||
|
||||
|
||||
|
||||
@@ -3,18 +3,16 @@ package com.appttude.h_mal.monoWeather.ui.world
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.observe
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
||||
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
|
||||
import com.appttude.h_mal.atlas_weather.utils.navigateTo
|
||||
import com.appttude.h_mal.atlas_weather.viewmodel.WorldViewModel
|
||||
import com.appttude.h_mal.atlas_weather.ui.BaseFragment
|
||||
import com.appttude.h_mal.monoWeather.ui.world.WorldFragmentDirections.actionWorldFragmentToWorldItemFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.android.synthetic.main.fragment__two.floatingActionButton
|
||||
import kotlinx.android.synthetic.main.fragment__two.progressBar
|
||||
import kotlinx.android.synthetic.main.fragment__two.world_recycler
|
||||
import kotlinx.android.synthetic.monoWeather.fragment__two.floatingActionButton
|
||||
import kotlinx.android.synthetic.monoWeather.fragment__two.world_recycler
|
||||
|
||||
|
||||
/**
|
||||
@@ -27,7 +25,6 @@ class WorldFragment : BaseFragment<WorldViewModel>(R.layout.fragment__two) {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel.fetchAllLocations()
|
||||
}
|
||||
|
||||
@@ -61,6 +58,7 @@ class WorldFragment : BaseFragment<WorldViewModel>(R.layout.fragment__two) {
|
||||
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun onSuccess(data: Any?) {
|
||||
super.onSuccess(data)
|
||||
|
||||
|
||||
@@ -27,18 +27,4 @@
|
||||
android:contentDescription="@string/image_string"
|
||||
app:srcCompat="@drawable/ic_baseline_add_24" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</FrameLayout>
|
||||
</RelativeLayout>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:src="@drawable/ic_baseline_cloud_off_24"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/message"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_weather_to_display"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Reference in New Issue
Block a user