- mid commit

This commit is contained in:
2023-08-12 13:21:23 +01:00
parent 9aaf98a655
commit 3d7997f623
25 changed files with 541 additions and 149 deletions

View File

@@ -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&#10; Android SDK built for x86" value="120" />
<entry key="Google&#10; 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&#10; 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&#10; Pixel 7 Pro" value="120" />
<entry key="Pixel_2_API_27" value="120" />
<entry key="Tests" value="360" />
</map>

View 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
}
]
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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")
}

View File

@@ -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()

View File

@@ -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)
}

View File

@@ -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())
}

View File

@@ -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())

View File

@@ -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)
}
}

View File

@@ -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()

View File

@@ -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")

View File

@@ -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")
}
}
}

View File

@@ -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
}
}

View File

@@ -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 -> {

View File

@@ -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>

View File

@@ -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!!)
}

View File

@@ -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>

View File

@@ -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?) {

View File

@@ -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

View File

@@ -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)

View File

@@ -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>

View File

@@ -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>