mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2025-12-10 02:05:20 +00:00
- new weather api added
This commit is contained in:
10633
app/src/androidTest/assets/new_response.json
Normal file
10633
app/src/androidTest/assets/new_response.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.utils
|
package com.appttude.h_mal.atlas_weather.utils
|
||||||
|
|
||||||
|
const val baseUrl = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/"
|
||||||
enum class Stubs(
|
enum class Stubs(
|
||||||
val id: String
|
val id: String
|
||||||
) {
|
) {
|
||||||
@@ -7,5 +8,6 @@ enum class Stubs(
|
|||||||
Imperial("valid_response_imperial"),
|
Imperial("valid_response_imperial"),
|
||||||
WrongLocation("wrong_location_response"),
|
WrongLocation("wrong_location_response"),
|
||||||
InvalidKey("invalid_api_key_response"),
|
InvalidKey("invalid_api_key_response"),
|
||||||
Sydney("valid_response_metric_sydney")
|
Sydney("valid_response_metric_sydney"),
|
||||||
|
New("new_response")
|
||||||
}
|
}
|
||||||
@@ -24,10 +24,7 @@ class SettingsScreen : BaseTestRobot() {
|
|||||||
RecyclerViewActions.actionOnItem<ViewHolder>(
|
RecyclerViewActions.actionOnItem<ViewHolder>(
|
||||||
ViewMatchers.hasDescendant(withText(R.string.weather_units)),
|
ViewMatchers.hasDescendant(withText(R.string.weather_units)),
|
||||||
click()))
|
click()))
|
||||||
val label = when (unitType) {
|
val label = unitType.getLabel()
|
||||||
UnitType.METRIC -> "Metric"
|
|
||||||
UnitType.IMPERIAL -> "Imperial"
|
|
||||||
}
|
|
||||||
|
|
||||||
onView(withText(label))
|
onView(withText(label))
|
||||||
.inRoot(isDialog())
|
.inRoot(isDialog())
|
||||||
|
|||||||
@@ -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.LocationProvider
|
||||||
import com.appttude.h_mal.atlas_weather.data.location.MockLocationProvider
|
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.NetworkModule
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.MockingNetworkInterceptor
|
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.NetworkConnectionInterceptor
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
|
import com.appttude.h_mal.atlas_weather.data.network.interceptors.QueryParamsInterceptor
|
||||||
@@ -32,13 +32,13 @@ class TestAppClass : AppClass() {
|
|||||||
IdlingRegistry.getInstance().register(idlingResources)
|
IdlingRegistry.getInstance().register(idlingResources)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createNetworkModule(): WeatherApi {
|
override fun createNetworkModule(): NewWeatherApi {
|
||||||
return NetworkModule().invoke<WeatherApi>(
|
return NetworkModule().invoke<NewWeatherApi>(
|
||||||
mockingNetworkInterceptor,
|
mockingNetworkInterceptor,
|
||||||
NetworkConnectionInterceptor(this),
|
NetworkConnectionInterceptor(this),
|
||||||
QueryParamsInterceptor(),
|
QueryParamsInterceptor(),
|
||||||
loggingInterceptor
|
loggingInterceptor
|
||||||
) as WeatherApi
|
) as NewWeatherApi
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createLocationModule(): LocationProvider {
|
override fun createLocationModule(): LocationProvider {
|
||||||
|
|||||||
@@ -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.R
|
||||||
import com.appttude.h_mal.atlas_weather.helpers.EspressoHelper.waitForView
|
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
|
||||||
|
import com.appttude.h_mal.atlas_weather.model.types.UnitType.Companion.getLabel
|
||||||
|
|
||||||
|
|
||||||
fun settingsScreen(func: SettingsScreen.() -> Unit) = SettingsScreen().apply { func() }
|
fun settingsScreen(func: SettingsScreen.() -> Unit) = SettingsScreen().apply { func() }
|
||||||
@@ -24,10 +25,7 @@ class SettingsScreen : BaseTestRobot() {
|
|||||||
RecyclerViewActions.actionOnItem<ViewHolder>(
|
RecyclerViewActions.actionOnItem<ViewHolder>(
|
||||||
ViewMatchers.hasDescendant(withText(R.string.weather_units)),
|
ViewMatchers.hasDescendant(withText(R.string.weather_units)),
|
||||||
click()))
|
click()))
|
||||||
val label = when (unitType) {
|
val label = unitType.getLabel()
|
||||||
UnitType.METRIC -> "Metric"
|
|
||||||
UnitType.IMPERIAL -> "Imperial"
|
|
||||||
}
|
|
||||||
|
|
||||||
onView(withText(label))
|
onView(withText(label))
|
||||||
.inRoot(isDialog())
|
.inRoot(isDialog())
|
||||||
|
|||||||
@@ -5,17 +5,16 @@ 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.model.types.UnitType
|
||||||
import com.appttude.h_mal.atlas_weather.ui.MainActivity
|
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.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.furtherInfoScreen
|
||||||
import com.appttude.h_mal.monoWeather.robot.settingsScreen
|
import com.appttude.h_mal.monoWeather.robot.settingsScreen
|
||||||
import com.appttude.h_mal.monoWeather.robot.weatherScreen
|
import com.appttude.h_mal.monoWeather.robot.weatherScreen
|
||||||
import okio.IOException
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import tools.fastlane.screengrab.Screengrab
|
|
||||||
|
|
||||||
class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
class HomePageUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
||||||
|
|
||||||
override fun beforeLaunch() {
|
override fun beforeLaunch() {
|
||||||
stubEndpoint("https://api.openweathermap.org/data/2.5/onecall", Stubs.Metric)
|
stubEndpoint(baseUrl, Stubs.New)
|
||||||
clearPrefs()
|
clearPrefs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.LocationProvider
|
||||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
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.NetworkModule
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkConnectionInterceptor
|
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.interceptors.QueryParamsInterceptor
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.networkUtils.loggingInterceptor
|
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() {
|
open class AppClass : BaseAppClass() {
|
||||||
|
|
||||||
override fun createNetworkModule(): WeatherApi {
|
override fun createNetworkModule(): NewWeatherApi {
|
||||||
return NetworkModule().invoke<WeatherApi>(
|
return NetworkModule().invoke<NewWeatherApi>(
|
||||||
NetworkConnectionInterceptor(this),
|
NetworkConnectionInterceptor(this),
|
||||||
QueryParamsInterceptor(),
|
QueryParamsInterceptor(),
|
||||||
loggingInterceptor
|
loggingInterceptor
|
||||||
) as WeatherApi
|
) as NewWeatherApi
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createLocationModule(): LocationProvider = LocationProviderImpl(this)
|
override fun createLocationModule(): LocationProvider = LocationProviderImpl(this)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.appttude.h_mal.atlas_weather.application
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.appttude.h_mal.atlas_weather.data.WeatherSource
|
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.location.LocationProvider
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
import com.appttude.h_mal.atlas_weather.data.network.Api
|
||||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
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.RepositoryImpl
|
||||||
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepositoryImpl
|
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepositoryImpl
|
||||||
@@ -12,7 +12,6 @@ import com.appttude.h_mal.atlas_weather.helper.ServicesHelper
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import org.kodein.di.Kodein
|
import org.kodein.di.Kodein
|
||||||
import org.kodein.di.KodeinAware
|
import org.kodein.di.KodeinAware
|
||||||
import org.kodein.di.KodeinContainer
|
|
||||||
import org.kodein.di.android.x.androidXModule
|
import org.kodein.di.android.x.androidXModule
|
||||||
import org.kodein.di.generic.bind
|
import org.kodein.di.generic.bind
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
@@ -41,7 +40,7 @@ abstract class BaseAppClass : Application(), KodeinAware {
|
|||||||
bind() from singleton { WeatherSource(instance(), instance()) }
|
bind() from singleton { WeatherSource(instance(), instance()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun createNetworkModule(): WeatherApi
|
abstract fun createNetworkModule(): Api
|
||||||
abstract fun createLocationModule(): LocationProvider
|
abstract fun createLocationModule(): LocationProvider
|
||||||
abstract fun createRoomDatabase(): AppDatabase
|
abstract fun createRoomDatabase(): AppDatabase
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
|||||||
import com.appttude.h_mal.atlas_weather.model.types.LocationType
|
import com.appttude.h_mal.atlas_weather.model.types.LocationType
|
||||||
import com.appttude.h_mal.atlas_weather.model.types.UnitType
|
import com.appttude.h_mal.atlas_weather.model.types.UnitType
|
||||||
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
||||||
|
import com.appttude.h_mal.atlas_weather.utils.getSymbol
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class WeatherSource(
|
class WeatherSource(
|
||||||
@@ -38,7 +39,7 @@ class WeatherSource(
|
|||||||
// get data from database
|
// get data from database
|
||||||
val weatherEntity = repository.loadSingleCurrentWeatherFromRoom(CURRENT_LOCATION)
|
val weatherEntity = repository.loadSingleCurrentWeatherFromRoom(CURRENT_LOCATION)
|
||||||
// check unit type - if same do nothing
|
// check unit type - if same do nothing
|
||||||
val units = if (repository.getUnitType() == UnitType.METRIC) "°C" else "°F"
|
val units = repository.getUnitType().getSymbol()
|
||||||
if (weatherEntity.weather.temperatureUnit == units) return weatherEntity.weather
|
if (weatherEntity.weather.temperatureUnit == units) return weatherEntity.weather
|
||||||
// load data for forced
|
// load data for forced
|
||||||
return fetchWeather(
|
return fetchWeather(
|
||||||
@@ -55,11 +56,13 @@ class WeatherSource(
|
|||||||
// Get weather from api
|
// Get weather from api
|
||||||
val weather = repository
|
val weather = repository
|
||||||
.getWeatherFromApi(latLon.first.toString(), latLon.second.toString())
|
.getWeatherFromApi(latLon.first.toString(), latLon.second.toString())
|
||||||
|
val lat = weather.latitude ?: latLon.first
|
||||||
|
val long = weather.longitude ?: latLon.second
|
||||||
val currentLocation =
|
val currentLocation =
|
||||||
locationProvider.getLocationNameFromLatLong(weather.lat, weather.lon, locationType)
|
locationProvider.getLocationNameFromLatLong(lat, long, locationType)
|
||||||
val unit = repository.getUnitType()
|
val unit = repository.getUnitType().getSymbol()
|
||||||
val fullWeather = FullWeather(weather).apply {
|
val fullWeather = weather.mapData().apply {
|
||||||
temperatureUnit = if (unit == UnitType.METRIC) "°C" else "°F"
|
temperatureUnit = unit
|
||||||
locationString = currentLocation
|
locationString = currentLocation
|
||||||
}
|
}
|
||||||
val entityItem = EntityItem(locationName, fullWeather)
|
val entityItem = EntityItem(locationName, fullWeather)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.data.network
|
package com.appttude.h_mal.atlas_weather.data.network
|
||||||
|
|
||||||
class NetworkModule : BaseNetworkModule() {
|
class NetworkModule : BaseNetworkModule() {
|
||||||
override fun baseUrl(): String = "https://api.openweathermap.org/data/2.5/"
|
override fun baseUrl(): String = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.data.network
|
||||||
|
|
||||||
|
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
|
||||||
|
import retrofit2.Response
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Path
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
|
||||||
|
interface NewWeatherApi : Api {
|
||||||
|
|
||||||
|
// Todo: change the location
|
||||||
|
// Todo: add endpoint for lat/long
|
||||||
|
@GET("{location}")
|
||||||
|
suspend fun getFromApi(
|
||||||
|
@Query("contentType") exclude: String = "json",
|
||||||
|
@Query("unitGroup") units: String = "uk",
|
||||||
|
@Path("location") location: String
|
||||||
|
): Response<WeatherApiResponse>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ class QueryParamsInterceptor : Interceptor {
|
|||||||
val original = chain.request()
|
val original = chain.request()
|
||||||
|
|
||||||
val url = original.url.newBuilder()
|
val url = original.url.newBuilder()
|
||||||
.addQueryParameter("appid", id)
|
.addQueryParameter("key", id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
// Request customization: add request headers
|
// Request customization: add request headers
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.data.network.networkUtils
|
package com.appttude.h_mal.atlas_weather.data.network.networkUtils
|
||||||
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkInterceptor
|
import com.appttude.h_mal.atlas_weather.data.network.interceptors.NetworkInterceptor
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
val loggingInterceptor = HttpLoggingInterceptor().apply {
|
val loggingInterceptor = HttpLoggingInterceptor().apply {
|
||||||
@@ -34,6 +36,13 @@ fun buildOkHttpClient(
|
|||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createGsonConverterFactory(): GsonConverterFactory {
|
||||||
|
val gson = GsonBuilder()
|
||||||
|
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
|
||||||
|
.create()
|
||||||
|
return GsonConverterFactory.create(gson)
|
||||||
|
}
|
||||||
|
|
||||||
fun <T> createRetrofit(
|
fun <T> createRetrofit(
|
||||||
baseUrl: String,
|
baseUrl: String,
|
||||||
okHttpClient: OkHttpClient,
|
okHttpClient: OkHttpClient,
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.data.repository
|
package com.appttude.h_mal.atlas_weather.data.repository
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
|
||||||
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
import com.appttude.h_mal.atlas_weather.data.room.entity.EntityItem
|
||||||
import com.appttude.h_mal.atlas_weather.model.types.UnitType
|
import com.appttude.h_mal.atlas_weather.model.types.UnitType
|
||||||
|
|
||||||
interface Repository {
|
interface Repository {
|
||||||
|
|
||||||
suspend fun getWeatherFromApi(lat: String, long: String): WeatherResponse
|
suspend fun getWeatherFromApi(lat: String, long: String): WeatherApiResponse
|
||||||
suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem)
|
suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem)
|
||||||
suspend fun saveWeatherListToRoom(list: List<EntityItem>)
|
suspend fun saveWeatherListToRoom(list: List<EntityItem>)
|
||||||
fun loadRoomWeatherLiveData(): LiveData<List<EntityItem>>
|
fun loadRoomWeatherLiveData(): LiveData<List<EntityItem>>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.data.repository
|
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.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.network.response.forecast.WeatherResponse
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.prefs.LOCATION_CONST
|
import com.appttude.h_mal.atlas_weather.data.prefs.LOCATION_CONST
|
||||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||||
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
|
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
|
||||||
@@ -12,7 +12,7 @@ import com.appttude.h_mal.atlas_weather.utils.FALLBACK_TIME
|
|||||||
|
|
||||||
|
|
||||||
class RepositoryImpl(
|
class RepositoryImpl(
|
||||||
private val api: WeatherApi,
|
private val api: NewWeatherApi,
|
||||||
private val db: AppDatabase,
|
private val db: AppDatabase,
|
||||||
private val prefs: PreferenceProvider
|
private val prefs: PreferenceProvider
|
||||||
) : Repository, ResponseUnwrap() {
|
) : Repository, ResponseUnwrap() {
|
||||||
@@ -20,8 +20,8 @@ class RepositoryImpl(
|
|||||||
override suspend fun getWeatherFromApi(
|
override suspend fun getWeatherFromApi(
|
||||||
lat: String,
|
lat: String,
|
||||||
long: String
|
long: String
|
||||||
): WeatherResponse {
|
): WeatherApiResponse {
|
||||||
return responseUnwrap { api.getFromApi(lat, long, units = prefs.getUnitsType().name.lowercase()) }
|
return responseUnwrap { api.getFromApi(location = lat + long) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem) {
|
override suspend fun saveCurrentWeatherToRoom(entityItem: EntityItem) {
|
||||||
|
|||||||
@@ -11,14 +11,13 @@ import com.appttude.h_mal.atlas_weather.data.repository.Repository
|
|||||||
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
|
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
|
||||||
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
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.data.room.entity.EntityItem
|
||||||
import com.appttude.h_mal.atlas_weather.model.types.UnitType
|
|
||||||
import com.appttude.h_mal.atlas_weather.model.weather.FullWeather
|
|
||||||
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetCellData
|
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetCellData
|
||||||
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetData
|
import com.appttude.h_mal.atlas_weather.model.widget.InnerWidgetData
|
||||||
import com.appttude.h_mal.atlas_weather.model.widget.WidgetData
|
import com.appttude.h_mal.atlas_weather.model.widget.WidgetData
|
||||||
import com.appttude.h_mal.atlas_weather.model.widget.WidgetError
|
import com.appttude.h_mal.atlas_weather.model.widget.WidgetError
|
||||||
import com.appttude.h_mal.atlas_weather.model.widget.WidgetState
|
import com.appttude.h_mal.atlas_weather.model.widget.WidgetState
|
||||||
import com.appttude.h_mal.atlas_weather.model.widget.WidgetWeatherCollection
|
import com.appttude.h_mal.atlas_weather.model.widget.WidgetWeatherCollection
|
||||||
|
import com.appttude.h_mal.atlas_weather.utils.getSymbol
|
||||||
import com.appttude.h_mal.atlas_weather.utils.toSmallDayName
|
import com.appttude.h_mal.atlas_weather.utils.toSmallDayName
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import com.squareup.picasso.Target
|
import com.squareup.picasso.Target
|
||||||
@@ -45,9 +44,11 @@ class ServicesHelper(
|
|||||||
// Get weather from api
|
// Get weather from api
|
||||||
val weather = repository
|
val weather = repository
|
||||||
.getWeatherFromApi(latLong.first.toString(), latLong.second.toString())
|
.getWeatherFromApi(latLong.first.toString(), latLong.second.toString())
|
||||||
|
val lat = weather.latitude ?: latLong.first
|
||||||
|
val long = weather.longitude ?: latLong.second
|
||||||
val currentLocation =
|
val currentLocation =
|
||||||
locationProvider.getLocationNameFromLatLong(weather.lat, weather.lon)
|
locationProvider.getLocationNameFromLatLong(lat, long)
|
||||||
val fullWeather = FullWeather(weather).apply {
|
val fullWeather = weather.mapData().apply {
|
||||||
temperatureUnit = "°C"
|
temperatureUnit = "°C"
|
||||||
locationString = currentLocation
|
locationString = currentLocation
|
||||||
}
|
}
|
||||||
@@ -105,8 +106,11 @@ class ServicesHelper(
|
|||||||
return WidgetState.HasError(error)
|
return WidgetState.HasError(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val lat = weather.latitude ?: latLong.first
|
||||||
|
val long = weather.longitude ?: latLong.second
|
||||||
|
|
||||||
val currentLocation = try {
|
val currentLocation = try {
|
||||||
locationProvider.getLocationNameFromLatLong(weather.lat, weather.lon)
|
locationProvider.getLocationNameFromLatLong(lat, long)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
val data = getWidgetWeatherCollection()
|
val data = getWidgetWeatherCollection()
|
||||||
data?.let {
|
data?.let {
|
||||||
@@ -120,8 +124,8 @@ class ServicesHelper(
|
|||||||
return WidgetState.HasError(error)
|
return WidgetState.HasError(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
val fullWeather = FullWeather(weather).apply {
|
val fullWeather = weather.mapData().apply {
|
||||||
temperatureUnit = if (repository.getUnitType() == UnitType.METRIC) "°C" else "°F"
|
temperatureUnit = repository.getUnitType().getSymbol()
|
||||||
locationString = currentLocation
|
locationString = currentLocation
|
||||||
}
|
}
|
||||||
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
|
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.model
|
||||||
|
|
||||||
|
interface DataMapper <T: Any> {
|
||||||
|
fun mapData(): T
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.model
|
||||||
|
|
||||||
|
enum class IconMapper(val label: String) {
|
||||||
|
snow("13d"),
|
||||||
|
snow_showers_day("13d"),
|
||||||
|
snow_showers_night("13n"),
|
||||||
|
thunder_rain("11d"),
|
||||||
|
thunder_showers_day("11d"),
|
||||||
|
thunder_showers_night("11n"),
|
||||||
|
rain("10d"),
|
||||||
|
showers_day("10d"),
|
||||||
|
showers_night("10n"),
|
||||||
|
fog("50d"),
|
||||||
|
wind("50d"),
|
||||||
|
cloudy("04d"),
|
||||||
|
partly_cloudy_day("03d"),
|
||||||
|
partly_cloudy_night("03n"),
|
||||||
|
clear_day("01d"),
|
||||||
|
clear_night("01n");
|
||||||
|
|
||||||
|
companion object{
|
||||||
|
fun findIconCode(iconId: String?): String? {
|
||||||
|
val label = iconId?.replace("-", "_")
|
||||||
|
val enumName = IconMapper.entries.find { it.name == label }
|
||||||
|
return enumName?.label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ data class WeatherDisplay(
|
|||||||
val averageTemp: Double?,
|
val averageTemp: Double?,
|
||||||
var unit: String?,
|
var unit: String?,
|
||||||
var location: String?,
|
var location: String?,
|
||||||
val iconURL: String?,
|
var iconURL: String?,
|
||||||
val description: String?,
|
val description: String?,
|
||||||
val hourly: List<Hour>?,
|
val hourly: List<Hour>?,
|
||||||
val forecast: List<Forecast>?,
|
val forecast: List<Forecast>?,
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ enum class UnitType {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getByName(name: String?): UnitType? {
|
fun getByName(name: String?): UnitType? {
|
||||||
return values().firstOrNull {
|
return entries.firstOrNull {
|
||||||
it.name.lowercase(Locale.ROOT) == name?.lowercase(
|
it.name.lowercase(Locale.ROOT) == name?.lowercase(
|
||||||
Locale.ROOT
|
Locale.ROOT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun UnitType.getLabel() = name.lowercase().replaceFirstChar {
|
||||||
|
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.model.weather
|
package com.appttude.h_mal.atlas_weather.model.weather
|
||||||
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.Current
|
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.Current
|
||||||
|
import com.appttude.h_mal.atlas_weather.data.network.response.weather.CurrentConditions
|
||||||
|
import com.appttude.h_mal.atlas_weather.model.IconMapper
|
||||||
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
||||||
|
|
||||||
data class Current(
|
data class Current(
|
||||||
@@ -42,4 +44,24 @@ data class Current(
|
|||||||
dailyItem.humidity,
|
dailyItem.humidity,
|
||||||
dailyItem.windSpeed
|
dailyItem.windSpeed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
constructor(currentConditions: CurrentConditions?): this(
|
||||||
|
dt = currentConditions?.datetimeEpoch,
|
||||||
|
sunrise = currentConditions?.sunriseEpoch,
|
||||||
|
sunset = currentConditions?.sunsetEpoch,
|
||||||
|
temp = currentConditions?.temp,
|
||||||
|
visibility = currentConditions?.visibility?.toInt(),
|
||||||
|
uvi = currentConditions?.uvindex?.toDouble(),
|
||||||
|
pressure = currentConditions?.pressure?.toInt(),
|
||||||
|
clouds = currentConditions?.cloudcover?.toInt(),
|
||||||
|
feelsLike = currentConditions?.feelslike,
|
||||||
|
windDeg = currentConditions?.winddir?.toInt(),
|
||||||
|
dewPoint = currentConditions?.dew,
|
||||||
|
icon = generateIconUrlString(IconMapper.findIconCode(currentConditions?.icon)),
|
||||||
|
description = currentConditions?.conditions,
|
||||||
|
main = currentConditions?.conditions,
|
||||||
|
id = currentConditions?.datetimeEpoch,
|
||||||
|
humidity = currentConditions?.humidity?.toInt(),
|
||||||
|
windSpeed = currentConditions?.windspeed
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.model.weather
|
package com.appttude.h_mal.atlas_weather.model.weather
|
||||||
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.DailyItem
|
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.DailyItem
|
||||||
|
import com.appttude.h_mal.atlas_weather.data.network.response.weather.Days
|
||||||
|
import com.appttude.h_mal.atlas_weather.model.IconMapper
|
||||||
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
||||||
|
|
||||||
|
|
||||||
@@ -50,5 +52,30 @@ data class DailyWeather(
|
|||||||
dailyItem.rain
|
dailyItem.rain
|
||||||
)
|
)
|
||||||
|
|
||||||
|
constructor(days: Days) : this(
|
||||||
|
days.datetimeEpoch,
|
||||||
|
days.sunriseEpoch,
|
||||||
|
days.sunsetEpoch,
|
||||||
|
days.tempmin,
|
||||||
|
days.tempmax,
|
||||||
|
days.temp?.toDouble(),
|
||||||
|
days.feelslike,
|
||||||
|
days.pressure?.toInt(),
|
||||||
|
days.humidity?.toInt(),
|
||||||
|
days.dew,
|
||||||
|
days.windspeed,
|
||||||
|
days.winddir?.toInt(),
|
||||||
|
generateIconUrlString(
|
||||||
|
IconMapper.findIconCode(days.icon)
|
||||||
|
),
|
||||||
|
days.description,
|
||||||
|
days.conditions,
|
||||||
|
days.datetimeEpoch,
|
||||||
|
days.cloudcover?.toInt(),
|
||||||
|
days.precipprob?.toDouble(),
|
||||||
|
days.uvindex?.toDouble(),
|
||||||
|
days.precip?.toDouble()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,10 @@ package com.appttude.h_mal.atlas_weather.model.weather
|
|||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import com.appttude.h_mal.atlas_weather.model.IconMapper
|
||||||
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
import com.appttude.h_mal.atlas_weather.utils.generateIconUrlString
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.Hour as ForecastHour
|
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.Hour as ForecastHour
|
||||||
|
import com.appttude.h_mal.atlas_weather.data.network.response.weather.Hours as WeatherHour
|
||||||
|
|
||||||
|
|
||||||
data class Hour(
|
data class Hour(
|
||||||
@@ -24,6 +26,12 @@ data class Hour(
|
|||||||
hour.temp,
|
hour.temp,
|
||||||
generateIconUrlString(hour.weather?.getOrNull(0)?.icon)
|
generateIconUrlString(hour.weather?.getOrNull(0)?.icon)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
constructor(weatherHour: WeatherHour) : this(
|
||||||
|
weatherHour.datetimeEpoch,
|
||||||
|
weatherHour.temp,
|
||||||
|
generateIconUrlString(IconMapper.findIconCode(weatherHour.icon))
|
||||||
|
)
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
parcel.writeValue(dt)
|
parcel.writeValue(dt)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.utils
|
package com.appttude.h_mal.atlas_weather.utils
|
||||||
|
|
||||||
|
import com.appttude.h_mal.atlas_weather.model.types.UnitType
|
||||||
|
|
||||||
|
|
||||||
fun generateIconUrlString(icon: String?): String? {
|
fun generateIconUrlString(icon: String?): String? {
|
||||||
return icon?.let {
|
return icon?.let {
|
||||||
@@ -9,4 +11,6 @@ fun generateIconUrlString(icon: String?): String? {
|
|||||||
.append("@2x.png")
|
.append("@2x.png")
|
||||||
.toString()
|
.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun UnitType.getSymbol(): String = if (this == UnitType.METRIC) "°C" else "°F"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.data
|
package com.appttude.h_mal.atlas_weather.data
|
||||||
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
|
||||||
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
import com.appttude.h_mal.atlas_weather.data.repository.RepositoryImpl
|
||||||
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
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.data.room.entity.EntityItem
|
||||||
@@ -18,6 +18,7 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import kotlin.properties.Delegates
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class WeatherSourceTest : BaseTest() {
|
class WeatherSourceTest : BaseTest() {
|
||||||
@@ -31,41 +32,43 @@ class WeatherSourceTest : BaseTest() {
|
|||||||
@MockK
|
@MockK
|
||||||
lateinit var locationProvider: LocationProviderImpl
|
lateinit var locationProvider: LocationProviderImpl
|
||||||
|
|
||||||
private lateinit var weatherResponse: WeatherResponse
|
private lateinit var weatherResponse: WeatherApiResponse
|
||||||
|
private var lat by Delegates.notNull<Double>()
|
||||||
|
private var long by Delegates.notNull<Double>()
|
||||||
|
private lateinit var latlon: Pair<Double, Double>
|
||||||
|
private lateinit var fullWeather: FullWeather
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockKAnnotations.init(this)
|
MockKAnnotations.init(this)
|
||||||
weatherResponse = getTestData("weather_sample.json", WeatherResponse::class.java)
|
weatherResponse = getTestData("new_response.json", WeatherApiResponse::class.java)
|
||||||
|
lat = weatherResponse.latitude!!
|
||||||
|
long = weatherResponse.longitude!!
|
||||||
|
latlon = Pair(lat, long)
|
||||||
|
fullWeather = weatherResponse.mapData().apply {
|
||||||
|
temperatureUnit = "°C"
|
||||||
|
locationString = CURRENT_LOCATION
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun fetchDataForSingleLocation_validLocation_validReturn() {
|
fun fetchDataForSingleLocation_validLocation_validReturn() {
|
||||||
// Arrange
|
// Arrange
|
||||||
val latlon = Pair(
|
|
||||||
weatherResponse.lat,
|
|
||||||
weatherResponse.lon
|
|
||||||
)
|
|
||||||
val fullWeather = FullWeather(weatherResponse).apply {
|
|
||||||
temperatureUnit = "°C"
|
|
||||||
locationString = CURRENT_LOCATION
|
|
||||||
}
|
|
||||||
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
|
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
|
||||||
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
||||||
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns latlon
|
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns latlon
|
||||||
coEvery {
|
coEvery {
|
||||||
repository.getWeatherFromApi(
|
repository.getWeatherFromApi(
|
||||||
weatherResponse.lat.toString(),
|
lat.toString(),
|
||||||
weatherResponse.lon.toString()
|
long.toString()
|
||||||
)
|
)
|
||||||
}.returns(weatherResponse)
|
}.returns(weatherResponse)
|
||||||
coEvery {
|
coEvery {
|
||||||
locationProvider.getLocationNameFromLatLong(
|
locationProvider.getLocationNameFromLatLong(
|
||||||
weatherResponse.lat,
|
lat,
|
||||||
weatherResponse.lon,
|
long,
|
||||||
LocationType.City
|
LocationType.City
|
||||||
)
|
)
|
||||||
}.returns(CURRENT_LOCATION)
|
}.returns(CURRENT_LOCATION)
|
||||||
@@ -82,17 +85,13 @@ class WeatherSourceTest : BaseTest() {
|
|||||||
@Test(expected = IOException::class)
|
@Test(expected = IOException::class)
|
||||||
fun fetchDataForSingleLocation_failedWeatherApi_invalidReturn() {
|
fun fetchDataForSingleLocation_failedWeatherApi_invalidReturn() {
|
||||||
// Arrange
|
// Arrange
|
||||||
val latlon = Pair(
|
|
||||||
weatherResponse.lat,
|
|
||||||
weatherResponse.lon
|
|
||||||
)
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
||||||
coEvery {
|
coEvery {
|
||||||
repository.getWeatherFromApi(
|
repository.getWeatherFromApi(
|
||||||
weatherResponse.lat.toString(),
|
lat.toString(),
|
||||||
weatherResponse.lon.toString()
|
long.toString()
|
||||||
)
|
)
|
||||||
} throws IOException("Unable fetch data")
|
} throws IOException("Unable fetch data")
|
||||||
|
|
||||||
@@ -103,23 +102,19 @@ class WeatherSourceTest : BaseTest() {
|
|||||||
@Test(expected = IOException::class)
|
@Test(expected = IOException::class)
|
||||||
fun fetchDataForSingleLocation_failedLocation_invalidReturn() {
|
fun fetchDataForSingleLocation_failedLocation_invalidReturn() {
|
||||||
// Arrange
|
// Arrange
|
||||||
val latlon = Pair(
|
|
||||||
weatherResponse.lat,
|
|
||||||
weatherResponse.lon
|
|
||||||
)
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
||||||
coEvery {
|
coEvery {
|
||||||
repository.getWeatherFromApi(
|
repository.getWeatherFromApi(
|
||||||
weatherResponse.lat.toString(),
|
lat.toString(),
|
||||||
weatherResponse.lon.toString()
|
long.toString()
|
||||||
)
|
)
|
||||||
} returns weatherResponse
|
} returns weatherResponse
|
||||||
coEvery {
|
coEvery {
|
||||||
locationProvider.getLocationNameFromLatLong(
|
locationProvider.getLocationNameFromLatLong(
|
||||||
weatherResponse.lat,
|
lat,
|
||||||
weatherResponse.lon
|
long
|
||||||
)
|
)
|
||||||
}.throws(IOException())
|
}.throws(IOException())
|
||||||
|
|
||||||
@@ -130,14 +125,6 @@ class WeatherSourceTest : BaseTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun searchAboveFallbackTime_validLocation_validReturn() {
|
fun searchAboveFallbackTime_validLocation_validReturn() {
|
||||||
// Arrange
|
// Arrange
|
||||||
val latlon = Pair(
|
|
||||||
weatherResponse.lat,
|
|
||||||
weatherResponse.lon
|
|
||||||
)
|
|
||||||
val fullWeather = FullWeather(weatherResponse).apply {
|
|
||||||
temperatureUnit = "°C"
|
|
||||||
locationString = CURRENT_LOCATION
|
|
||||||
}
|
|
||||||
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
|
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -153,14 +140,6 @@ class WeatherSourceTest : BaseTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun forceFetchDataForSingleLocation_validLocation_validReturn() {
|
fun forceFetchDataForSingleLocation_validLocation_validReturn() {
|
||||||
// Arrange
|
// Arrange
|
||||||
val latlon = Pair(
|
|
||||||
weatherResponse.lat,
|
|
||||||
weatherResponse.lon
|
|
||||||
)
|
|
||||||
val fullWeather = FullWeather(weatherResponse).apply {
|
|
||||||
temperatureUnit = "°C"
|
|
||||||
locationString = CURRENT_LOCATION
|
|
||||||
}
|
|
||||||
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
|
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -169,14 +148,14 @@ class WeatherSourceTest : BaseTest() {
|
|||||||
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns latlon
|
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns latlon
|
||||||
coEvery {
|
coEvery {
|
||||||
repository.getWeatherFromApi(
|
repository.getWeatherFromApi(
|
||||||
weatherResponse.lat.toString(),
|
weatherResponse.latitude.toString(),
|
||||||
weatherResponse.lon.toString()
|
weatherResponse.longitude.toString()
|
||||||
)
|
)
|
||||||
}.returns(weatherResponse)
|
}.returns(weatherResponse)
|
||||||
coEvery {
|
coEvery {
|
||||||
locationProvider.getLocationNameFromLatLong(
|
locationProvider.getLocationNameFromLatLong(
|
||||||
weatherResponse.lat,
|
lat,
|
||||||
weatherResponse.lon,
|
long,
|
||||||
LocationType.City
|
LocationType.City
|
||||||
)
|
)
|
||||||
}.returns(CURRENT_LOCATION)
|
}.returns(CURRENT_LOCATION)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.data.repository
|
package com.appttude.h_mal.atlas_weather.data.repository
|
||||||
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.WeatherApi
|
import com.appttude.h_mal.atlas_weather.data.network.NewWeatherApi
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
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.LOCATION_CONST
|
||||||
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
import com.appttude.h_mal.atlas_weather.data.prefs.PreferenceProvider
|
||||||
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
|
import com.appttude.h_mal.atlas_weather.data.room.AppDatabase
|
||||||
@@ -29,8 +29,7 @@ class RepositoryImplTest : BaseTest() {
|
|||||||
|
|
||||||
lateinit var repository: RepositoryImpl
|
lateinit var repository: RepositoryImpl
|
||||||
|
|
||||||
@MockK
|
@MockK lateinit var api: NewWeatherApi
|
||||||
lateinit var api: WeatherApi
|
|
||||||
|
|
||||||
@MockK
|
@MockK
|
||||||
lateinit var db: AppDatabase
|
lateinit var db: AppDatabase
|
||||||
@@ -86,29 +85,29 @@ class RepositoryImplTest : BaseTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun getWeatherFromApi_validLatLong_validSearch() {
|
fun getWeatherFromApi_validLatLong_validSearch() {
|
||||||
//Arrange
|
//Arrange
|
||||||
val mockResponse = createSuccessfulRetrofitMock<WeatherResponse>()
|
val mockResponse = createSuccessfulRetrofitMock<WeatherApiResponse>()
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
//create a successful retrofit response
|
//create a successful retrofit response
|
||||||
every { prefs.getUnitsType() } returns (UnitType.METRIC)
|
every { prefs.getUnitsType() } returns (UnitType.METRIC)
|
||||||
coEvery { api.getFromApi("", "") }.returns(mockResponse)
|
coEvery { api.getFromApi(location = "") }.returns(mockResponse)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val result = repository.getWeatherFromApi("", "")
|
val result = repository.getWeatherFromApi("", "")
|
||||||
assertIs<WeatherResponse>(result)
|
assertIs<WeatherApiResponse>(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getWeatherFromApi_validLatLong_invalidResponse() {
|
fun getWeatherFromApi_validLatLong_invalidResponse() {
|
||||||
//Arrange
|
//Arrange
|
||||||
val mockResponse = createErrorRetrofitMock<WeatherResponse>()
|
val mockResponse = createErrorRetrofitMock<WeatherApiResponse>()
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
//create a successful retrofit response
|
//create a successful retrofit response
|
||||||
every { prefs.getUnitsType() } returns (UnitType.METRIC)
|
every { prefs.getUnitsType() } returns (UnitType.METRIC)
|
||||||
coEvery { api.getFromApi(any(), any()) } returns (mockResponse)
|
coEvery { api.getFromApi(location = any()) } returns (mockResponse)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
val ioExceptionReturned = assertFailsWith<IOException> {
|
val ioExceptionReturned = assertFailsWith<IOException> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.helper
|
package com.appttude.h_mal.atlas_weather.helper
|
||||||
|
|
||||||
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
import com.appttude.h_mal.atlas_weather.data.location.LocationProviderImpl
|
||||||
import com.appttude.h_mal.atlas_weather.data.network.response.forecast.WeatherResponse
|
import com.appttude.h_mal.atlas_weather.data.network.response.weather.WeatherApiResponse
|
||||||
import com.appttude.h_mal.atlas_weather.data.repository.Repository
|
import com.appttude.h_mal.atlas_weather.data.repository.Repository
|
||||||
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
|
import com.appttude.h_mal.atlas_weather.data.repository.SettingsRepository
|
||||||
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
import com.appttude.h_mal.atlas_weather.data.room.entity.CURRENT_LOCATION
|
||||||
@@ -17,6 +17,7 @@ import org.junit.Assert.assertTrue
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
class ServicesHelperTest : BaseTest() {
|
class ServicesHelperTest : BaseTest() {
|
||||||
|
|
||||||
@@ -31,40 +32,45 @@ class ServicesHelperTest : BaseTest() {
|
|||||||
@MockK
|
@MockK
|
||||||
lateinit var locationProvider: LocationProviderImpl
|
lateinit var locationProvider: LocationProviderImpl
|
||||||
|
|
||||||
lateinit var weatherResponse: WeatherResponse
|
lateinit var weatherResponse: WeatherApiResponse
|
||||||
|
private var lat by Delegates.notNull<Double>()
|
||||||
|
private var long by Delegates.notNull<Double>()
|
||||||
|
private lateinit var latlon: Pair<Double, Double>
|
||||||
|
private lateinit var fullWeather: FullWeather
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockKAnnotations.init(this)
|
MockKAnnotations.init(this)
|
||||||
helper = ServicesHelper(repository, settingsRepository, locationProvider)
|
helper = ServicesHelper(repository, settingsRepository, locationProvider)
|
||||||
|
|
||||||
weatherResponse = getTestData("weather_sample.json", WeatherResponse::class.java)
|
weatherResponse = getTestData("new_response.json", WeatherApiResponse::class.java)
|
||||||
|
lat = weatherResponse.latitude!!
|
||||||
|
long = weatherResponse.longitude!!
|
||||||
|
latlon = Pair(lat, long)
|
||||||
|
fullWeather = weatherResponse.mapData().apply {
|
||||||
|
temperatureUnit = "°C"
|
||||||
|
locationString = CURRENT_LOCATION
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testWidgetDataAsync_successfulResponse() = runBlocking {
|
fun testWidgetDataAsync_successfulResponse() = runBlocking {
|
||||||
// Arrange
|
// Arrange
|
||||||
val entityItem = EntityItem(CURRENT_LOCATION, FullWeather(weatherResponse).apply {
|
val entityItem = EntityItem(CURRENT_LOCATION, fullWeather)
|
||||||
temperatureUnit = "°C"
|
|
||||||
locationString = CURRENT_LOCATION
|
|
||||||
})
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
coEvery { locationProvider.getCurrentLatLong() } returns Pair(
|
coEvery { locationProvider.getCurrentLatLong() } returns Pair(lat, long)
|
||||||
weatherResponse.lat,
|
|
||||||
weatherResponse.lon
|
|
||||||
)
|
|
||||||
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
||||||
coEvery {
|
coEvery {
|
||||||
repository.getWeatherFromApi(
|
repository.getWeatherFromApi(
|
||||||
weatherResponse.lat.toString(),
|
lat.toString(),
|
||||||
weatherResponse.lon.toString()
|
long.toString()
|
||||||
)
|
)
|
||||||
}.returns(weatherResponse)
|
}.returns(weatherResponse)
|
||||||
coEvery {
|
coEvery {
|
||||||
locationProvider.getLocationNameFromLatLong(
|
locationProvider.getLocationNameFromLatLong(
|
||||||
weatherResponse.lat,
|
lat,
|
||||||
weatherResponse.lon
|
long
|
||||||
)
|
)
|
||||||
}.returns(CURRENT_LOCATION)
|
}.returns(CURRENT_LOCATION)
|
||||||
every { repository.saveLastSavedAt(CURRENT_LOCATION) } returns Unit
|
every { repository.saveLastSavedAt(CURRENT_LOCATION) } returns Unit
|
||||||
|
|||||||
10633
app/src/test/resources/new_response.json
Normal file
10633
app/src/test/resources/new_response.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user