-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1ccadf0
commit c73c95a
Showing
14 changed files
with
467 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
import 'package:weather_tdd/apikey.dart'; | ||
|
||
sealed class Urls { | ||
static const String baseUrl = 'https://api.openweathermap.org/data/2.5'; | ||
static const String apiKey = MYAPIKEY; | ||
sealed class URLS { | ||
static const String BASEURL = 'https://api.openweathermap.org/data/2.5'; | ||
|
||
static const String APIKEY = MYAPIKEY; | ||
|
||
static String currentWeatherByName(String city) => | ||
'$baseUrl/weather?q=$city&appid=$apiKey'; | ||
'$BASEURL/weather?q=$city&appid=$APIKEY'; | ||
|
||
static String weatherIcon(String iconCode) => | ||
'http://openweathermap.org/img/wn/$iconCode@2x.png'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
sealed class ServerException implements Exception {} | ||
interface class ServerException implements Exception {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
|
||
|
||
import 'dart:convert'; | ||
|
||
import '../../core/consts/consts.dart'; | ||
import '../../core/error/exception.dart'; | ||
import '../models/weather_model.dart'; | ||
import 'package:http/http.dart' as http; | ||
|
||
abstract class WeatherRemoteDataSource { | ||
|
||
Future<WeatherModel> getWeather(String cityName); | ||
} | ||
|
||
|
||
interface class WeatherRemoteDataSourceImpl extends WeatherRemoteDataSource { | ||
final http.Client client; | ||
WeatherRemoteDataSourceImpl({required this.client}); | ||
|
||
@override | ||
Future < WeatherModel > getWeather(String cityName) async { | ||
final response = | ||
await client.get(Uri.parse(URLS.currentWeatherByName(cityName))); | ||
|
||
if (response.statusCode == 200) { | ||
return WeatherModel.fromJson(json.decode(response.body)); | ||
} else { | ||
throw ServerException(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:dartz/dartz.dart'; | ||
|
||
import '../../core/error/exception.dart'; | ||
import '../../core/error/failure.dart'; | ||
import '../../domain/entities/weather.dart'; | ||
import '../../domain/repos/weather_repo.dart'; | ||
import '../data_sources/remote_data_source.dart'; | ||
|
||
interface class WeatherRepoImpl extends WeatherRepo { | ||
|
||
final WeatherRemoteDataSource weatherRemoteDataSource; | ||
|
||
WeatherRepoImpl({required this.weatherRemoteDataSource}); | ||
|
||
@override | ||
Future < Either < Failure, WeatherEntity >> getWeather(String cityName) async { | ||
try { | ||
final result = await weatherRemoteDataSource.getWeather(cityName); | ||
return Right(result.toEntity()); | ||
} on ServerException { | ||
return const Left(ServerFailure('A server error has occurred, server error')); | ||
} on SocketException { | ||
return const Left(ConnectionFailure('Failed to connect to the network, connection error')); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import 'package:flutter_bloc/flutter_bloc.dart'; | ||
|
||
import 'package:rxdart/rxdart.dart'; | ||
import 'package:equatable/equatable.dart'; | ||
import 'package:meta/meta.dart'; | ||
import '../../domain/entities/weather.dart'; | ||
import '../../domain/usecases/get_weather.dart'; | ||
part 'weather_event.dart'; | ||
|
||
part 'weather_state.dart'; | ||
|
||
class WeatherBloc extends Bloc<WeatherEvent,WeatherState> { | ||
|
||
final GetWeatherUseCase _getWeatherUseCase; | ||
WeatherBloc(this._getWeatherUseCase) : super(WeatherEmpty()) { | ||
on<OnCityChangedEvent>( | ||
(event, emit) async { | ||
|
||
emit(WeatherLoading()); | ||
final result = await _getWeatherUseCase.execute(event.cityName); | ||
result.fold( | ||
(failure) { | ||
emit(WeatherFailure(failure.message)); | ||
}, | ||
(data) { | ||
emit(WeatherLoaded(data)); | ||
}, | ||
); | ||
}, | ||
transformer: debounce(const Duration(milliseconds: 500)), | ||
); | ||
} | ||
} | ||
|
||
EventTransformer<T> debounce<T>(Duration duration) { | ||
return (events, mapper) => events.debounceTime(duration).flatMap(mapper); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
part of 'weather_bloc.dart'; | ||
|
||
|
||
@immutable | ||
abstract class WeatherEvent extends Equatable { | ||
const WeatherEvent(); | ||
|
||
@override | ||
List<Object?> get props => []; | ||
} | ||
|
||
class OnCityChangedEvent extends WeatherEvent { | ||
final String cityName; | ||
|
||
const OnCityChangedEvent(this.cityName); | ||
|
||
@override | ||
List<Object?> get props => [cityName]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
part of 'weather_bloc.dart'; | ||
|
||
|
||
@immutable | ||
abstract class WeatherState extends Equatable { | ||
const WeatherState(); | ||
|
||
@override | ||
List<Object?> get props => []; | ||
} | ||
|
||
class WeatherEmpty extends WeatherState {} | ||
|
||
class WeatherLoading extends WeatherState {} | ||
|
||
class WeatherLoaded extends WeatherState { | ||
final WeatherEntity result; | ||
|
||
const WeatherLoaded(this.result); | ||
|
||
@override | ||
List<Object?> get props => [result]; | ||
} | ||
|
||
class WeatherFailure extends WeatherState { | ||
final String message; | ||
|
||
const WeatherFailure(this.message); | ||
|
||
@override | ||
List<Object?> get props => [message]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:http/http.dart' as http; | ||
import 'package:mockito/mockito.dart'; | ||
import 'package:weather_tdd/core/consts/consts.dart'; | ||
import 'package:weather_tdd/core/error/exception.dart'; | ||
import 'package:weather_tdd/data/data_sources/remote_data_source.dart'; | ||
import 'package:weather_tdd/data/models/weather_model.dart'; | ||
|
||
import '../../helpers/json_reader.dart'; | ||
import '../../helpers/test_helper.mocks.dart'; | ||
|
||
void main() { | ||
late MockHttpClient mockHttpClient; | ||
late WeatherRemoteDataSourceImpl weatherRemoteDataSourceImpl; | ||
|
||
setUp(() { | ||
mockHttpClient = MockHttpClient(); | ||
weatherRemoteDataSourceImpl = | ||
WeatherRemoteDataSourceImpl(client: mockHttpClient); | ||
}); | ||
|
||
const testCityName = 'Cairo'; | ||
|
||
group('get weather testing group', () { | ||
test( | ||
'should return weather model when the response code is 200, It is a successful test', | ||
() async { | ||
//arrange | ||
when(mockHttpClient | ||
.get(Uri.parse(URLS.currentWeatherByName(testCityName)))) | ||
.thenAnswer((_) async => http.Response( | ||
readJson('helpers/dummy_data/dummy_weather_response.json'), 200)); | ||
|
||
//act | ||
final result = await weatherRemoteDataSourceImpl.getWeather(testCityName); | ||
|
||
//assert | ||
expect(result, isA<WeatherModel>()); | ||
}); | ||
|
||
test( | ||
'should throw a server exception when the response code is 404 or other, It is a successful test', | ||
() async { | ||
//arrange | ||
when( | ||
mockHttpClient | ||
.get(Uri.parse(URLS.currentWeatherByName(testCityName))), | ||
).thenAnswer((_) async => http.Response('Not found', 404)); | ||
|
||
//act | ||
final result = weatherRemoteDataSourceImpl.getWeather(testCityName); | ||
|
||
//assert | ||
expect(result, throwsA(isA<ServerException>())); | ||
}, | ||
); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:dartz/dartz.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:mockito/mockito.dart'; | ||
import 'package:weather_tdd/core/error/exception.dart'; | ||
import 'package:weather_tdd/core/error/failure.dart'; | ||
import 'package:weather_tdd/data/models/weather_model.dart'; | ||
import 'package:weather_tdd/data/repos/weather_repo_impl.dart'; | ||
import 'package:weather_tdd/domain/entities/weather.dart'; | ||
|
||
import '../../helpers/test_helper.mocks.dart'; | ||
|
||
void main() { | ||
|
||
late MockWeatherRemoteDataSource mockWeatherRemoteDataSource; | ||
late WeatherRepoImpl weatherRepoImpl; | ||
|
||
setUp(() { | ||
mockWeatherRemoteDataSource = MockWeatherRemoteDataSource(); | ||
weatherRepoImpl = WeatherRepoImpl( | ||
weatherRemoteDataSource: mockWeatherRemoteDataSource, | ||
); | ||
}); | ||
|
||
const testWeatherModel = WeatherModel( | ||
cityName: 'Cairo', | ||
main: 'Clouds', | ||
description: 'few clouds', | ||
iconCode: '02d', | ||
temperature: 304.28, | ||
pressure: 1007, | ||
humidity: 72, | ||
); | ||
|
||
const testWeatherEntity = WeatherEntity( | ||
cityName: 'Cairo', | ||
main: 'Clouds', | ||
description: 'few clouds', | ||
iconCode: '02d', | ||
temperature: 304.28, | ||
pressure: 1007, | ||
humidity: 72, | ||
); | ||
|
||
const testCityName = 'Cairo'; | ||
|
||
|
||
group('get weather testing group, reop impl test', () { | ||
|
||
test( | ||
'should return weather when a call to data source is successful, It is a successful test', | ||
() async { | ||
// arrange | ||
when(mockWeatherRemoteDataSource.getWeather(testCityName)) | ||
.thenAnswer((_) async => testWeatherModel); | ||
|
||
// act | ||
final result = await weatherRepoImpl.getWeather(testCityName); | ||
|
||
// assert | ||
expect(result, equals(const Right(testWeatherEntity))); | ||
}, | ||
); | ||
|
||
test( | ||
'should return server failure when a call to data source is unsuccessful', | ||
() async { | ||
// arrange | ||
when(mockWeatherRemoteDataSource.getWeather(testCityName)) | ||
.thenThrow(ServerException()); | ||
|
||
// act | ||
final result = await weatherRepoImpl.getWeather(testCityName); | ||
|
||
// assert | ||
expect(result, equals(const Left(ServerFailure('A server error has occurred, server error')))); | ||
}, | ||
); | ||
|
||
test( | ||
'should return connection failure when the device has no internet, connection error, this is a successful test', | ||
() async { | ||
// arrange | ||
when(mockWeatherRemoteDataSource.getWeather(testCityName)) | ||
.thenThrow(const SocketException('Failed to connect to the network, socket error')); | ||
|
||
// act | ||
final result = await weatherRepoImpl.getWeather(testCityName); | ||
|
||
// assert | ||
expect(result, equals(const Left(ConnectionFailure('Failed to connect to the network, connection error')))); | ||
}, | ||
); | ||
|
||
}); | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.