mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2025-12-10 02:05:20 +00:00
- Tests passed
- Weather API successfully replaces
This commit is contained in:
@@ -18,8 +18,8 @@ android {
|
||||
applicationId "com.appttude.h_mal.atlas_weather"
|
||||
minSdkVersion MIN_SDK_VERSION
|
||||
targetSdkVersion TARGET_SDK_VERSION
|
||||
versionCode 5
|
||||
versionName "3.0"
|
||||
versionCode 6
|
||||
versionName "3.1"
|
||||
testInstrumentationRunner "com.appttude.h_mal.atlas_weather.application.TestRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
@@ -32,6 +32,10 @@ android {
|
||||
buildConfigField "String", "ParamOne", System.getenv('WEATHER_API')
|
||||
buildConfigField "String", "ParamTwo", System.getenv('SEARCH_API')
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
resources.excludes.add("META-INF/*")
|
||||
}
|
||||
}
|
||||
android {
|
||||
sourceSets {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"cod": 401,
|
||||
"message": "Invalid API key. Please see http://openweathermap.org/faq#error401 for more info."
|
||||
}
|
||||
1
app/src/androidTest/assets/invalid_api_key_response.txt
Normal file
1
app/src/androidTest/assets/invalid_api_key_response.txt
Normal file
@@ -0,0 +1 @@
|
||||
No account found with API key 'wrong api key'
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
10603
app/src/androidTest/assets/valid_response_with_alert.json
Normal file
10603
app/src/androidTest/assets/valid_response_with_alert.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -67,7 +67,7 @@ open class BaseTest<A : Activity>(
|
||||
}
|
||||
|
||||
testApp =
|
||||
InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
|
||||
InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestAppClass
|
||||
runBlocking {
|
||||
beforeLaunch()
|
||||
}
|
||||
@@ -80,8 +80,8 @@ open class BaseTest<A : Activity>(
|
||||
afterLaunch()
|
||||
}
|
||||
|
||||
fun stubEndpoint(url: String, stub: Stubs, code: Int = 200) {
|
||||
testApp.stubUrl(url, stub.id, code)
|
||||
fun stubEndpoint(url: String, stub: Stubs, code: Int = 200, extension: String = ".json") {
|
||||
testApp.stubUrl(url, stub.id, code, extension)
|
||||
}
|
||||
|
||||
fun unstubEndpoint(url: String) {
|
||||
@@ -125,7 +125,6 @@ open class BaseTest<A : Activity>(
|
||||
})
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
fun checkToastMessage(message: String) {
|
||||
Espresso.onView(ViewMatchers.withText(message)).inRoot(withDecorView(Matchers.not(decorView)))
|
||||
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
|
||||
|
||||
@@ -17,7 +17,12 @@ import androidx.test.espresso.action.ViewActions.swipeDown
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.contrib.PickerActions
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withClassName
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import com.appttude.h_mal.atlas_weather.helpers.EspressoHelper.waitForView
|
||||
import com.appttude.h_mal.atlas_weather.helpers.checkErrorMessage
|
||||
import com.appttude.h_mal.atlas_weather.helpers.checkImage
|
||||
|
||||
@@ -16,9 +16,9 @@ class MockingNetworkInterceptor(
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
idlingResource.increment()
|
||||
val original = chain.request()
|
||||
val originalHttpUrl = original.url.toString().split("?")[0]
|
||||
val originalHttpUrl = original.url.toString()
|
||||
|
||||
feedMap[originalHttpUrl]?.let { responsePair ->
|
||||
feedMap[feedMap.keys.first { originalHttpUrl.contains(it) }]?.let { responsePair ->
|
||||
val code = responsePair.second
|
||||
val jsonBody = responsePair.first
|
||||
|
||||
|
||||
@@ -9,5 +9,4 @@ enum class Stubs(
|
||||
WrongLocation("wrong_location_response"),
|
||||
InvalidKey("invalid_api_key_response"),
|
||||
Sydney("valid_response_metric_sydney"),
|
||||
New("new_response")
|
||||
}
|
||||
@@ -7,7 +7,7 @@ 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.NewWeatherApi
|
||||
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
|
||||
@@ -28,8 +28,8 @@ class TestAppClass : AppClass() {
|
||||
IdlingRegistry.getInstance().register(idlingResources)
|
||||
}
|
||||
|
||||
override fun createNetworkModule(): NewWeatherApi {
|
||||
return NetworkModule().invoke<NewWeatherApi>(
|
||||
override fun createNetworkModule(): WeatherApi {
|
||||
return NetworkModule().invoke<WeatherApi>(
|
||||
mockingNetworkInterceptor,
|
||||
NetworkConnectionInterceptor(this),
|
||||
QueryParamsInterceptor(),
|
||||
@@ -49,9 +49,9 @@ class TestAppClass : AppClass() {
|
||||
return database
|
||||
}
|
||||
|
||||
fun stubUrl(url: String, rawPath: String, code: Int = 200) {
|
||||
fun stubUrl(url: String, rawPath: String, code: Int = 200, extension: String = ".json") {
|
||||
val iStream =
|
||||
InstrumentationRegistry.getInstrumentation().context.assets.open("$rawPath.json")
|
||||
InstrumentationRegistry.getInstrumentation().context.assets.open("$rawPath$extension")
|
||||
val data = iStream.bufferedReader().use(BufferedReader::readText)
|
||||
mockingNetworkInterceptor.addUrlStub(url = url, data = data, code = code)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ 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.waitForView
|
||||
import com.appttude.h_mal.atlas_weather.model.types.UnitType
|
||||
import com.appttude.h_mal.atlas_weather.model.types.UnitType.Companion.getLabel
|
||||
|
||||
|
||||
fun settingsScreen(func: SettingsScreen.() -> Unit) = SettingsScreen().apply { func() }
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import com.appttude.h_mal.atlas_weather.robot.furtherInfoScreen
|
||||
import com.appttude.h_mal.atlas_weather.robot.settingsScreen
|
||||
import com.appttude.h_mal.atlas_weather.robot.weatherScreen
|
||||
import com.appttude.h_mal.atlas_weather.utils.baseUrl
|
||||
import org.junit.Test
|
||||
import tools.fastlane.screengrab.Screengrab
|
||||
|
||||
@@ -17,7 +18,7 @@ import tools.fastlane.screengrab.Screengrab
|
||||
class SnapshotCaptureTest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
|
||||
override fun beforeLaunch() {
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
|
||||
stubEndpoint(baseUrl, Stubs.Metric)
|
||||
stubLocation("London", 51.51, -0.13)
|
||||
clearPrefs()
|
||||
}
|
||||
@@ -50,7 +51,7 @@ class SnapshotCaptureTest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
openMenuItem()
|
||||
}
|
||||
settingsScreen {
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Imperial)
|
||||
stubEndpoint(baseUrl, Stubs.Imperial)
|
||||
Screengrab.screenshot("SettingsScreen")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,27 @@
|
||||
package com.appttude.h_mal.atlas_weather.tests
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.BaseTest
|
||||
import com.appttude.h_mal.atlas_weather.robot.homeScreen
|
||||
import com.appttude.h_mal.atlas_weather.ui.MainActivity
|
||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import com.appttude.h_mal.atlas_weather.robot.homeScreen
|
||||
import com.appttude.h_mal.atlas_weather.utils.baseUrl
|
||||
import org.junit.Test
|
||||
|
||||
class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
|
||||
override fun beforeLaunch() {
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.InvalidKey, 400)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApp_invalidKeyWeatherResponse_returnsEmptyViewPage() {
|
||||
homeScreen {
|
||||
waitFor(2000)
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
}
|
||||
stubEndpoint(baseUrl, Stubs.InvalidKey, 400, ".txt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalidKeyWeatherResponse_swipeToRefresh_returnsValidPage() {
|
||||
homeScreen {
|
||||
waitFor(2000)
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
|
||||
stubEndpoint(baseUrl, Stubs.Metric)
|
||||
refresh()
|
||||
verifyCurrentTemperature(2)
|
||||
verifyCurrentTemperature(13)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,19 +5,20 @@ import com.appttude.h_mal.atlas_weather.BaseTest
|
||||
import com.appttude.h_mal.atlas_weather.robot.homeScreen
|
||||
import com.appttude.h_mal.atlas_weather.ui.MainActivity
|
||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import com.appttude.h_mal.atlas_weather.utils.baseUrl
|
||||
import org.junit.Test
|
||||
|
||||
class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
|
||||
override fun beforeLaunch() {
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
|
||||
stubEndpoint(baseUrl, Stubs.Metric)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApp_validWeatherResponse_returnsValidPage() {
|
||||
homeScreen {
|
||||
isDisplayed()
|
||||
verifyCurrentTemperature(2)
|
||||
verifyCurrentTemperature(13)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ 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.NewWeatherApi
|
||||
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
|
||||
@@ -18,7 +18,6 @@ import org.kodein.di.LazyKodein
|
||||
import java.io.BufferedReader
|
||||
|
||||
class TestAppClass : AppClass() {
|
||||
|
||||
override val kodein: LazyKodein = super.kodein
|
||||
|
||||
private val idlingResources = CountingIdlingResource("Data_loader")
|
||||
@@ -32,13 +31,13 @@ class TestAppClass : AppClass() {
|
||||
IdlingRegistry.getInstance().register(idlingResources)
|
||||
}
|
||||
|
||||
override fun createNetworkModule(): NewWeatherApi {
|
||||
return NetworkModule().invoke<NewWeatherApi>(
|
||||
override fun createNetworkModule(): WeatherApi {
|
||||
return NetworkModule().invoke<WeatherApi>(
|
||||
mockingNetworkInterceptor,
|
||||
NetworkConnectionInterceptor(this),
|
||||
QueryParamsInterceptor(),
|
||||
loggingInterceptor
|
||||
) as NewWeatherApi
|
||||
) as WeatherApi
|
||||
}
|
||||
|
||||
override fun createLocationModule(): LocationProvider {
|
||||
@@ -53,9 +52,9 @@ class TestAppClass : AppClass() {
|
||||
return database
|
||||
}
|
||||
|
||||
fun stubUrl(url: String, rawPath: String, code: Int = 200) {
|
||||
fun stubUrl(url: String, rawPath: String, code: Int = 200, extension: String = ".json") {
|
||||
val iStream =
|
||||
InstrumentationRegistry.getInstrumentation().context.assets.open("$rawPath.json")
|
||||
InstrumentationRegistry.getInstrumentation().context.assets.open("$rawPath$extension")
|
||||
val data = iStream.bufferedReader().use(BufferedReader::readText)
|
||||
mockingNetworkInterceptor.addUrlStub(url = url, data = data, code = code)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.appttude.h_mal.atlas_weather.utils.baseUrl
|
||||
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
|
||||
|
||||
@@ -19,7 +18,7 @@ import tools.fastlane.screengrab.Screengrab
|
||||
class SnapshotCaptureTest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
|
||||
override fun beforeLaunch() {
|
||||
stubEndpoint(baseUrl, Stubs.New)
|
||||
stubEndpoint(baseUrl, Stubs.Metric)
|
||||
stubLocation("London", 51.5064, -0.12721)
|
||||
clearPrefs()
|
||||
}
|
||||
@@ -53,7 +52,7 @@ class SnapshotCaptureTest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
openMenuItem()
|
||||
}
|
||||
settingsScreen {
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Imperial)
|
||||
stubEndpoint(baseUrl, Stubs.Imperial)
|
||||
Screengrab.screenshot("SettingsScreen")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.appttude.h_mal.monoWeather.robot
|
||||
import com.appttude.h_mal.atlas_weather.BaseTestRobot
|
||||
import com.appttude.h_mal.atlas_weather.R
|
||||
import com.appttude.h_mal.monoWeather.ui.home.adapter.forecastDaily.ViewHolderForecastDaily
|
||||
import com.appttude.h_mal.monoWeather.ui.home.adapter.further.ViewHolderFurtherDetails
|
||||
|
||||
fun weatherScreen(func: WeatherScreen.() -> Unit) = WeatherScreen().apply { func() }
|
||||
class WeatherScreen : BaseTestRobot() {
|
||||
|
||||
@@ -4,36 +4,30 @@ package com.appttude.h_mal.monoWeather.tests
|
||||
import com.appttude.h_mal.atlas_weather.BaseTest
|
||||
import com.appttude.h_mal.atlas_weather.ui.MainActivity
|
||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import com.appttude.h_mal.atlas_weather.utils.baseUrl
|
||||
import com.appttude.h_mal.monoWeather.robot.weatherScreen
|
||||
import org.junit.Ignore
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runners.MethodSorters
|
||||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
|
||||
override fun beforeLaunch() {
|
||||
// Todo: change this
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.InvalidKey, 400)
|
||||
stubEndpoint(baseUrl, Stubs.InvalidKey, 400, ".txt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApp_invalidKeyWeatherResponse_returnsEmptyViewPage() {
|
||||
weatherScreen {
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test is flakey - must investigate")
|
||||
@Test
|
||||
fun invalidKeyWeatherResponse_swipeToRefresh_returnsValidPage() {
|
||||
weatherScreen {
|
||||
// verify empty
|
||||
verifyUnableToRetrieve()
|
||||
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
|
||||
stubEndpoint(baseUrl, Stubs.Metric)
|
||||
refresh()
|
||||
verifyCurrentTemperature(2)
|
||||
verifyCurrentTemperature(13)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,19 +2,17 @@ package com.appttude.h_mal.monoWeather.tests
|
||||
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.BaseTest
|
||||
import com.appttude.h_mal.atlas_weather.model.types.UnitType
|
||||
import com.appttude.h_mal.atlas_weather.ui.MainActivity
|
||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import com.appttude.h_mal.atlas_weather.utils.baseUrl
|
||||
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.Test
|
||||
|
||||
class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
|
||||
override fun beforeLaunch() {
|
||||
stubEndpoint(baseUrl, Stubs.New)
|
||||
stubEndpoint(baseUrl, Stubs.Metric)
|
||||
clearPrefs()
|
||||
}
|
||||
|
||||
@@ -22,7 +20,7 @@ class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
fun loadApp_validWeatherResponse_returnsValidPage() {
|
||||
weatherScreen {
|
||||
isDisplayed()
|
||||
verifyCurrentTemperature(2)
|
||||
verifyCurrentTemperature(13)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
}
|
||||
}
|
||||
@@ -31,35 +29,15 @@ class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
fun loadApp_validWeatherResponse_viewFurtherDetailsPage() {
|
||||
weatherScreen {
|
||||
isDisplayed()
|
||||
verifyCurrentTemperature(2)
|
||||
verifyCurrentTemperature(13)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
tapDayInformationByPosition(4)
|
||||
}
|
||||
furtherInfoScreen {
|
||||
isDisplayed()
|
||||
verifyMaxTemperature(12)
|
||||
verifyAverageTemperature(9)
|
||||
verifyMaxTemperature(15)
|
||||
verifyAverageTemperature(11)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApp_changeToImperial_returnsValidPage() {
|
||||
weatherScreen {
|
||||
isDisplayed()
|
||||
verifyCurrentTemperature(2)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Imperial)
|
||||
openMenuItem()
|
||||
}
|
||||
settingsScreen {
|
||||
selectWeatherUnits(UnitType.IMPERIAL)
|
||||
goBack()
|
||||
}
|
||||
weatherScreen {
|
||||
isDisplayed()
|
||||
refresh()
|
||||
verifyCurrentTemperature(58)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.appttude.h_mal.monoWeather.tests
|
||||
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.BaseTest
|
||||
import com.appttude.h_mal.atlas_weather.model.types.UnitType
|
||||
import com.appttude.h_mal.atlas_weather.ui.MainActivity
|
||||
import com.appttude.h_mal.atlas_weather.utils.Stubs
|
||||
import com.appttude.h_mal.atlas_weather.utils.baseUrl
|
||||
import com.appttude.h_mal.monoWeather.robot.settingsScreen
|
||||
import com.appttude.h_mal.monoWeather.robot.weatherScreen
|
||||
import org.junit.Test
|
||||
|
||||
class SettingsPageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
|
||||
override fun beforeLaunch() {
|
||||
stubEndpoint(baseUrl, Stubs.Metric)
|
||||
clearPrefs()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadApp_changeToImperial_returnsValidPage() {
|
||||
weatherScreen {
|
||||
isDisplayed()
|
||||
verifyCurrentTemperature(13)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
stubEndpoint(baseUrl, Stubs.Imperial)
|
||||
openMenuItem()
|
||||
}
|
||||
settingsScreen {
|
||||
selectWeatherUnits(UnitType.IMPERIAL)
|
||||
goBack()
|
||||
}
|
||||
weatherScreen {
|
||||
isDisplayed()
|
||||
refresh()
|
||||
verifyCurrentTemperature(56)
|
||||
verifyCurrentLocation("Mock Location")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ import org.junit.Test
|
||||
class WorldPageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
|
||||
override fun beforeLaunch() {
|
||||
stubEndpoint(baseUrl, Stubs.New)
|
||||
stubLocation("London", 51.5064, -0.12721)
|
||||
stubEndpoint(baseUrl, Stubs.Metric)
|
||||
stubLocation("London", 51.5064,-0.12721)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -28,9 +28,8 @@ class WorldPageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
clickFab()
|
||||
}
|
||||
addLocation {
|
||||
// Todo: change this
|
||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Sydney)
|
||||
stubLocation("Sydney", -33.89, -151.12)
|
||||
stubEndpoint(baseUrl, Stubs.Sydney)
|
||||
stubLocation("Sydney",-33.8696,151.207)
|
||||
setLocation("Sydney")
|
||||
submit()
|
||||
}
|
||||
@@ -39,7 +38,7 @@ class WorldPageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||
}
|
||||
weatherScreen {
|
||||
isDisplayed()
|
||||
verifyCurrentTemperature(12)
|
||||
verifyCurrentTemperature(16)
|
||||
verifyCurrentLocation("Sydney")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.appttude.h_mal.atlas_weather.application
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
||||
import com.appttude.h_mal.atlas_weather.data.network.NetworkModule
|
||||
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
|
||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
||||
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
|
||||
@@ -11,12 +11,12 @@ import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
|
||||
|
||||
open class AppClass : BaseAppClass() {
|
||||
|
||||
override fun createNetworkModule(): NewWeatherApi {
|
||||
return NetworkModule().invoke<NewWeatherApi>(
|
||||
override fun createNetworkModule(): WeatherApi {
|
||||
return NetworkModule().invoke<WeatherApi>(
|
||||
NetworkConnectionInterceptor(this),
|
||||
QueryParamsInterceptor(),
|
||||
loggingInterceptor
|
||||
) as NewWeatherApi
|
||||
) as WeatherApi
|
||||
}
|
||||
|
||||
override fun createLocationModule(): LocationProvider = LocationProviderImpl(this)
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Application
|
||||
import com.appttude.h_mal.atlas_weather.data.WeatherSource
|
||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.network.Api
|
||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepositoryImpl
|
||||
@@ -28,7 +29,7 @@ abstract class BaseAppClass : Application(), KodeinAware {
|
||||
val parentModule = Kodein.Module("Parent Module", allowSilentOverride = true) {
|
||||
import(androidXModule(this@BaseAppClass))
|
||||
|
||||
bind() from singleton { createNetworkModule() }
|
||||
bind() from singleton { createNetworkModule() as WeatherApi }
|
||||
bind() from singleton { createLocationModule() }
|
||||
|
||||
bind() from singleton { Gson() }
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network
|
||||
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import retrofit2.HttpException
|
||||
import retrofit2.Response
|
||||
import java.io.IOException
|
||||
|
||||
abstract class ResponseUnwrap {
|
||||
|
||||
@@ -15,18 +13,7 @@ abstract class ResponseUnwrap {
|
||||
if (response.isSuccessful) {
|
||||
return response.body()!!
|
||||
} else {
|
||||
val error = response.errorBody()?.string()
|
||||
|
||||
val errorMessage = error?.let {
|
||||
try {
|
||||
JSONObject(it).getString("message")
|
||||
} catch (e: JSONException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
} ?: "Error Code: ${response.code()}"
|
||||
|
||||
throw IOException(errorMessage)
|
||||
throw HttpException(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,13 @@ import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
||||
interface NewWeatherApi : Api {
|
||||
interface WeatherApi : Api {
|
||||
|
||||
@GET("{location}")
|
||||
suspend fun getFromApi(
|
||||
@Path("location") location: String,
|
||||
@Query("contentType") exclude: String = "json",
|
||||
@Query("unitGroup") units: String = "uk",
|
||||
@Path("location") location: String
|
||||
@Query("unitGroup") units: String = "metric"
|
||||
): Response<WeatherApiResponse>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.network.response.weather
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class Alerts(
|
||||
@SerializedName("event") var event: String? = null,
|
||||
@SerializedName("headline") var headline: String? = null,
|
||||
@SerializedName("ends") var ends: String? = null,
|
||||
@SerializedName("endsEpoch") var endsEpoch: Int? = null,
|
||||
@SerializedName("onset") var onset: String? = null,
|
||||
@SerializedName("onsetEpoch") var onsetEpoch: Int? = null,
|
||||
@SerializedName("id") var id: String? = null,
|
||||
@SerializedName("language") var language: String? = null,
|
||||
@SerializedName("link") var link: String? = null,
|
||||
@SerializedName("description") var description: String? = null,
|
||||
)
|
||||
@@ -18,7 +18,7 @@ data class WeatherApiResponse(
|
||||
@SerializedName("tzoffset") var tzoffset: Int? = null,
|
||||
@SerializedName("description") var description: String? = null,
|
||||
@SerializedName("days") var days: ArrayList<Days> = arrayListOf(),
|
||||
@SerializedName("alerts") var alerts: ArrayList<String> = arrayListOf(),
|
||||
@SerializedName("alerts") var alerts: ArrayList<Alerts> = arrayListOf(),
|
||||
@SerializedName("currentConditions") var currentConditions: CurrentConditions? = CurrentConditions()
|
||||
): DataMapper<FullWeather> {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.repository
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
|
||||
import com.appttude.h_mal.atlas_weather.data.network.ResponseUnwrap
|
||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.LOCATION_CONST
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||
@@ -12,7 +12,7 @@ import com.appttude.h_mal.atlas_weather.utils.FALLBACK_TIME
|
||||
|
||||
|
||||
class RepositoryImpl(
|
||||
private val api: NewWeatherApi,
|
||||
private val api: WeatherApi,
|
||||
private val db: AppDatabase,
|
||||
private val prefs: PreferenceProvider
|
||||
) : Repository, ResponseUnwrap() {
|
||||
@@ -21,7 +21,8 @@ class RepositoryImpl(
|
||||
lat: String,
|
||||
long: String
|
||||
): WeatherApiResponse {
|
||||
return responseUnwrap { api.getFromApi(location = lat + long) }
|
||||
val unit = if (prefs.getUnitsType() == UnitType.METRIC) "metric" else "us"
|
||||
return responseUnwrap { api.getFromApi(location = "$lat,$long", units = unit) }
|
||||
}
|
||||
|
||||
override suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem) {
|
||||
|
||||
@@ -190,7 +190,7 @@ class ServicesHelper(
|
||||
|
||||
val list = mutableListOf<InnerWidgetCellData>()
|
||||
|
||||
result.weather.daily?.drop(1)?.dropLast(2)?.forEach { dailyWeather ->
|
||||
result.weather.daily?.drop(1)?.dropLast(1)?.forEach { dailyWeather ->
|
||||
val day = dailyWeather.dt?.toSmallDayName()
|
||||
val icon = dailyWeather.icon
|
||||
val temp = dailyWeather.max?.toInt().toString()
|
||||
@@ -220,7 +220,7 @@ class ServicesHelper(
|
||||
|
||||
val list = mutableListOf<InnerWidgetCellData>()
|
||||
|
||||
result.weather.daily?.drop(1)?.dropLast(2)?.forEach { dailyWeather ->
|
||||
result.weather.daily?.drop(1)?.dropLast(1)?.forEach { dailyWeather ->
|
||||
val day = dailyWeather.dt?.toSmallDayName()
|
||||
val icon = dailyWeather.icon
|
||||
val temp = dailyWeather.max?.toInt().toString()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.appttude.h_mal.atlas_weather.data.repository
|
||||
|
||||
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
|
||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
||||
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.LOCATION_CONST
|
||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||
@@ -18,7 +18,8 @@ import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers.anyDouble
|
||||
import java.io.IOException
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import retrofit2.HttpException
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertIs
|
||||
@@ -30,7 +31,7 @@ class RepositoryImplTest : BaseTest() {
|
||||
|
||||
lateinit var repository: RepositoryImpl
|
||||
|
||||
@MockK lateinit var api: NewWeatherApi
|
||||
@MockK lateinit var api: WeatherApi
|
||||
|
||||
@MockK
|
||||
lateinit var db: AppDatabase
|
||||
@@ -93,7 +94,7 @@ class RepositoryImplTest : BaseTest() {
|
||||
//Act
|
||||
//create a successful retrofit response
|
||||
every { prefs.getUnitsType() } returns (UnitType.METRIC)
|
||||
coEvery { api.getFromApi(location = lat + long) }.returns(mockResponse)
|
||||
coEvery { api.getFromApi(location = "$lat,$long") }.returns(mockResponse)
|
||||
|
||||
// Assert
|
||||
runBlocking {
|
||||
@@ -105,20 +106,25 @@ class RepositoryImplTest : BaseTest() {
|
||||
@Test
|
||||
fun getWeatherFromApi_validLatLong_invalidResponse() {
|
||||
//Arrange
|
||||
val mockResponse = createErrorRetrofitMock<WeatherApiResponse>()
|
||||
val errorMessage = "Why dont you have a valid api key?"
|
||||
val mockResponse = createErrorRetrofitMock<WeatherApiResponse>(errorMessage)
|
||||
val lat = anyString()
|
||||
val long = anyString()
|
||||
|
||||
//Act
|
||||
//create a successful retrofit response
|
||||
every { prefs.getUnitsType() } returns (UnitType.METRIC)
|
||||
coEvery { api.getFromApi(location = any()) } returns (mockResponse)
|
||||
coEvery { api.getFromApi(location = "$lat,$long") } returns (mockResponse)
|
||||
|
||||
// Assert
|
||||
val ioExceptionReturned = assertFailsWith<IOException> {
|
||||
val ioExceptionReturned = assertFailsWith<HttpException> {
|
||||
runBlocking {
|
||||
repository.getWeatherFromApi("", "")
|
||||
repository.getWeatherFromApi(lat, long)
|
||||
}
|
||||
}
|
||||
assertEquals(ioExceptionReturned.message, "Error Code: 400")
|
||||
|
||||
assertEquals(ioExceptionReturned.code(), 400)
|
||||
assertEquals(ioExceptionReturned.message(), "Why dont you have a valid api key?")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -4,7 +4,9 @@ import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.ResponseBody
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import retrofit2.Response
|
||||
|
||||
|
||||
@@ -29,9 +31,17 @@ open class BaseTest {
|
||||
|
||||
fun <T: Any> createErrorRetrofitMock(code: Int = 400): Response<T> {
|
||||
val responseBody = mockk<ResponseBody>(relaxed = true)
|
||||
val rawResponse = mockk<okhttp3.Response>().also {
|
||||
every { it.code } returns code
|
||||
}
|
||||
return Response.error<T>(code, responseBody)
|
||||
}
|
||||
|
||||
fun <T: Any> createErrorRetrofitMock(errorMessage: String, code: Int = 400): Response<T> {
|
||||
val responseBody = errorMessage.toResponseBody("application/json".toMediaType())
|
||||
val rawResponse = mockk<okhttp3.Response>(relaxed = true).also {
|
||||
every { it.code } returns code
|
||||
every { it.isSuccessful } returns false
|
||||
every { it.body } returns responseBody
|
||||
every { it.message } returns errorMessage
|
||||
}
|
||||
return Response.error<T>(responseBody, rawResponse)
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ RETROFIT_VERSION = 2.9.0
|
||||
OKHTTP_VERSION = 4.9.0
|
||||
MOKITO_INLINE_VERSION = 2.13.0
|
||||
CORE_TEST_VERSION = 2.2.0
|
||||
MOCKK_VERSION = 1.10.5
|
||||
MOCKK_VERSION = 1.13.12
|
||||
TEST_JUNIT_VERSION = 1.2.0
|
||||
TEST_RUNNER_VERSION = 1.5.2
|
||||
ESPRESSO_VERSION = 3.6.0
|
||||
|
||||
Reference in New Issue
Block a user