Skip to content

Commit 1cc796e

Browse files
committed
Added logic search news in bloc
1 parent d045a95 commit 1cc796e

File tree

6 files changed

+216
-3
lines changed

6 files changed

+216
-3
lines changed

lib/feature/presentation/bloc/topheadlinesnews/top_headlines_news_bloc.dart

+25-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ import 'dart:async';
33
import 'package:bloc/bloc.dart';
44
import 'package:flutter_news_app/core/error/failure.dart';
55
import 'package:flutter_news_app/feature/domain/usecase/gettopheadlinesnews/get_top_headlines_news.dart';
6+
import 'package:flutter_news_app/feature/domain/usecase/searchtopheadlinesnews/search_top_headlines_news.dart';
67
import 'package:meta/meta.dart';
78

89
import './bloc.dart';
910

1011
class TopHeadlinesNewsBloc extends Bloc<TopHeadlinesNewsEvent, TopHeadlinesNewsState> {
1112
final GetTopHeadlinesNews getTopHeadlinesNews;
13+
final SearchTopHeadlinesNews searchTopHeadlinesNews;
1214

13-
TopHeadlinesNewsBloc({@required this.getTopHeadlinesNews}) : assert(getTopHeadlinesNews != null);
15+
TopHeadlinesNewsBloc({
16+
@required this.getTopHeadlinesNews,
17+
@required this.searchTopHeadlinesNews,
18+
}) : assert(getTopHeadlinesNews != null),
19+
assert(searchTopHeadlinesNews != null);
1420

1521
@override
1622
TopHeadlinesNewsState get initialState => InitialTopHeadlinesNewsState();
@@ -23,6 +29,8 @@ class TopHeadlinesNewsBloc extends Bloc<TopHeadlinesNewsEvent, TopHeadlinesNewsS
2329
yield* _mapLoadTopHeadlinesNewsEventToState(event);
2430
} else if (event is ChangeCategoryTopHeadlinesNewsEvent) {
2531
yield* _mapChangeCategoryTopHeadlinesNewsEventToState(event);
32+
} else if (event is SearchTopHeadlinesNewsEvent) {
33+
yield* _mapSearchTopHeadlinesNewsEventToState(event);
2634
}
2735
}
2836

@@ -47,4 +55,20 @@ class TopHeadlinesNewsBloc extends Bloc<TopHeadlinesNewsEvent, TopHeadlinesNewsS
4755
) async* {
4856
yield ChangedCategoryTopHeadlinesNewsState(indexCategorySelected: event.indexCategorySelected);
4957
}
58+
59+
Stream<TopHeadlinesNewsState> _mapSearchTopHeadlinesNewsEventToState(SearchTopHeadlinesNewsEvent event) async* {
60+
yield LoadingTopHeadlinesNewsState();
61+
var result = await searchTopHeadlinesNews(ParamsSearchTopHeadlinesNews(keyword: event.keyword));
62+
yield result.fold(
63+
// ignore: missing_return
64+
(failure) {
65+
if (failure is ServerFailure) {
66+
return FailureTopHeadlinesNewsState(errorMessage: failure.errorMessage);
67+
} else if (failure is ConnectionFailure) {
68+
return FailureTopHeadlinesNewsState(errorMessage: failure.errorMessage);
69+
}
70+
},
71+
(response) => SearchSuccessTopHeadlinesNewsState(listArticles: response.articles),
72+
);
73+
}
5074
}

lib/feature/presentation/bloc/topheadlinesnews/top_headlines_news_event.dart

+14
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,18 @@ class ChangeCategoryTopHeadlinesNewsEvent extends TopHeadlinesNewsEvent {
3131
String toString() {
3232
return 'ChangeCategoryTopHeadlinesNewsEvent{indexCategorySelected: $indexCategorySelected}';
3333
}
34+
}
35+
36+
class SearchTopHeadlinesNewsEvent extends TopHeadlinesNewsEvent {
37+
final String keyword;
38+
39+
SearchTopHeadlinesNewsEvent({@required this.keyword});
40+
41+
@override
42+
List<Object> get props => [keyword];
43+
44+
@override
45+
String toString() {
46+
return 'SearchTopHeadlinesNewsEvent{keyword: $keyword}';
47+
}
3448
}

lib/feature/presentation/bloc/topheadlinesnews/top_headlines_news_state.dart

+14
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,18 @@ class ChangedCategoryTopHeadlinesNewsState extends TopHeadlinesNewsState {
5252
String toString() {
5353
return 'ChangedCategoryTopHeadlinesNewsState{indexCategorySelected: $indexCategorySelected}';
5454
}
55+
}
56+
57+
class SearchSuccessTopHeadlinesNewsState extends TopHeadlinesNewsState {
58+
final List<ItemArticleTopHeadlinesNewsResponseModel> listArticles;
59+
60+
SearchSuccessTopHeadlinesNewsState({this.listArticles});
61+
62+
@override
63+
List<Object> get props => [listArticles];
64+
65+
@override
66+
String toString() {
67+
return 'SearchSuccessTopHeadlinesNewsState{listArticles: $listArticles}';
68+
}
5569
}

test/feature/presentation/bloc/topheadlinesnews/top_headlines_news_bloc_test.dart

+104-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:dartz/dartz.dart';
55
import 'package:flutter_news_app/core/error/failure.dart';
66
import 'package:flutter_news_app/feature/data/model/topheadlinesnews/top_headlines_news_response_model.dart';
77
import 'package:flutter_news_app/feature/domain/usecase/gettopheadlinesnews/get_top_headlines_news.dart';
8+
import 'package:flutter_news_app/feature/domain/usecase/searchtopheadlinesnews/search_top_headlines_news.dart';
89
import 'package:flutter_news_app/feature/presentation/bloc/topheadlinesnews/bloc.dart';
910
import 'package:flutter_test/flutter_test.dart';
1011
import 'package:mockito/mockito.dart';
@@ -13,13 +14,20 @@ import '../../../../fixture/fixture_reader.dart';
1314

1415
class MockGetTopHeadlinesNews extends Mock implements GetTopHeadlinesNews {}
1516

17+
class MockSearchTopHeadlinesNews extends Mock implements SearchTopHeadlinesNews {}
18+
1619
void main() {
1720
MockGetTopHeadlinesNews mockGetTopHeadlinesNews;
21+
MockSearchTopHeadlinesNews mockSearchTopHeadlinesNews;
1822
TopHeadlinesNewsBloc topHeadlinesNewsBloc;
1923

2024
setUp(() {
2125
mockGetTopHeadlinesNews = MockGetTopHeadlinesNews();
22-
topHeadlinesNewsBloc = TopHeadlinesNewsBloc(getTopHeadlinesNews: mockGetTopHeadlinesNews);
26+
mockSearchTopHeadlinesNews = MockSearchTopHeadlinesNews();
27+
topHeadlinesNewsBloc = TopHeadlinesNewsBloc(
28+
getTopHeadlinesNews: mockGetTopHeadlinesNews,
29+
searchTopHeadlinesNews: mockSearchTopHeadlinesNews,
30+
);
2331
});
2432

2533
tearDown(() {
@@ -30,7 +38,20 @@ void main() {
3038
'make sure that AssertionError will be called when accepting null arguments',
3139
() async {
3240
// assert
33-
expect(() => TopHeadlinesNewsBloc(getTopHeadlinesNews: null), throwsAssertionError);
41+
expect(
42+
() => TopHeadlinesNewsBloc(
43+
getTopHeadlinesNews: null,
44+
searchTopHeadlinesNews: mockSearchTopHeadlinesNews,
45+
),
46+
throwsAssertionError,
47+
);
48+
expect(
49+
() => TopHeadlinesNewsBloc(
50+
getTopHeadlinesNews: mockGetTopHeadlinesNews,
51+
searchTopHeadlinesNews: null,
52+
),
53+
throwsAssertionError,
54+
);
3455
},
3556
);
3657

@@ -156,4 +177,85 @@ void main() {
156177
],
157178
);
158179
});
180+
181+
group('SearchTopHeadlinesNews', () {
182+
final tKeyword = 'testKeyword';
183+
final tTopHeadlinesNewsResponseModel = TopHeadlinesNewsResponseModel.fromJson(
184+
json.decode(
185+
fixture('top_headlines_news_response_model.json'),
186+
),
187+
);
188+
189+
test(
190+
'make sure that the SearchTopHeadlinesNews use case is really called',
191+
() async {
192+
// arrange
193+
when(mockSearchTopHeadlinesNews(any)).thenAnswer((_) async => Right(tTopHeadlinesNewsResponseModel));
194+
195+
// act
196+
topHeadlinesNewsBloc.add(SearchTopHeadlinesNewsEvent(keyword: tKeyword));
197+
await untilCalled(mockSearchTopHeadlinesNews(any));
198+
199+
// assert
200+
verify(mockSearchTopHeadlinesNews(ParamsSearchTopHeadlinesNews(keyword: tKeyword)));
201+
},
202+
);
203+
204+
blocTest(
205+
'make sure to emit [LoadingTopHeadlinesNewsState, SearchSuccessTopHeadlinesNewsState] when receive '
206+
'SearchTopHeadlinesNewsEvent with a successful process',
207+
build: () async {
208+
when(mockSearchTopHeadlinesNews(any)).thenAnswer((_) async => Right(tTopHeadlinesNewsResponseModel));
209+
return topHeadlinesNewsBloc;
210+
},
211+
act: (bloc) {
212+
return bloc.add(SearchTopHeadlinesNewsEvent(keyword: tKeyword));
213+
},
214+
expect: [
215+
LoadingTopHeadlinesNewsState(),
216+
SearchSuccessTopHeadlinesNewsState(listArticles: tTopHeadlinesNewsResponseModel.articles),
217+
],
218+
verify: (_) async {
219+
verify(mockSearchTopHeadlinesNews(ParamsSearchTopHeadlinesNews(keyword: tKeyword)));
220+
},
221+
);
222+
223+
blocTest(
224+
'make sure to emit [LoadingTopHeadlinesNewsState, FailureTopHeadlinesNewsState] when receive '
225+
'SearchTopHeadlinesNewsEvent with a failed process from endpoint',
226+
build: () async {
227+
when(mockSearchTopHeadlinesNews(any)).thenAnswer((_) async => Left(ServerFailure('testErrorMessage')));
228+
return topHeadlinesNewsBloc;
229+
},
230+
act: (bloc) {
231+
return bloc.add(SearchTopHeadlinesNewsEvent(keyword: tKeyword));
232+
},
233+
expect: [
234+
LoadingTopHeadlinesNewsState(),
235+
FailureTopHeadlinesNewsState(errorMessage: 'testErrorMessage'),
236+
],
237+
verify: (_) async {
238+
verify(mockSearchTopHeadlinesNews(ParamsSearchTopHeadlinesNews(keyword: tKeyword)));
239+
},
240+
);
241+
242+
blocTest(
243+
'make sure to emit [LoadingTopHeadlinesNewsState, FailureTopHeadlinesNewsState] when the internet '
244+
'connection has a problem',
245+
build: () async {
246+
when(mockSearchTopHeadlinesNews(any)).thenAnswer((_) async => Left(ConnectionFailure()));
247+
return topHeadlinesNewsBloc;
248+
},
249+
act: (bloc) {
250+
return bloc.add(SearchTopHeadlinesNewsEvent(keyword: tKeyword));
251+
},
252+
expect: [
253+
LoadingTopHeadlinesNewsState(),
254+
FailureTopHeadlinesNewsState(errorMessage: messageConnectionFailure),
255+
],
256+
verify: (_) async {
257+
verify(mockSearchTopHeadlinesNews(ParamsSearchTopHeadlinesNews(keyword: tKeyword)));
258+
},
259+
);
260+
});
159261
}

test/feature/presentation/bloc/topheadlinesnews/top_headlines_news_event_test.dart

+27
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,31 @@ void main() {
5050
},
5151
);
5252
});
53+
54+
group('SearchTopHeadlinesNews', () {
55+
final tKeyword = 'testKeyword';
56+
final tSearchTopHeadlinesNewsEvent = SearchTopHeadlinesNewsEvent(keyword: tKeyword);
57+
58+
test(
59+
'make sure the props value is [keyword]',
60+
() async {
61+
// assert
62+
expect(
63+
tSearchTopHeadlinesNewsEvent.props,
64+
[tSearchTopHeadlinesNewsEvent.keyword],
65+
);
66+
},
67+
);
68+
69+
test(
70+
'make sure the output of the toString function',
71+
() async {
72+
// assert
73+
expect(
74+
tSearchTopHeadlinesNewsEvent.toString(),
75+
'SearchTopHeadlinesNewsEvent{keyword: ${tSearchTopHeadlinesNewsEvent.keyword}}',
76+
);
77+
},
78+
);
79+
});
5380
}

test/feature/presentation/bloc/topheadlinesnews/top_headlines_news_state_test.dart

+32
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,36 @@ void main() {
115115
},
116116
);
117117
});
118+
119+
group('SearchSuccessTopHeadlinesNewsState', () {
120+
final tTopHeadlinesNewsResponseModel = TopHeadlinesNewsResponseModel.fromJson(
121+
json.decode(
122+
fixture('top_headlines_news_response_model.json'),
123+
),
124+
);
125+
final tListArticles = tTopHeadlinesNewsResponseModel.articles;
126+
final tSearchSuccessTopHeadlinesNewsState = SearchSuccessTopHeadlinesNewsState(listArticles: tListArticles);
127+
128+
test(
129+
'make sure the props value is [listArticles]',
130+
() async {
131+
// assert
132+
expect(
133+
tSearchSuccessTopHeadlinesNewsState.props,
134+
[tSearchSuccessTopHeadlinesNewsState.listArticles],
135+
);
136+
},
137+
);
138+
139+
test(
140+
'make sure the output of the toString function',
141+
() async {
142+
// assert
143+
expect(
144+
tSearchSuccessTopHeadlinesNewsState.toString(),
145+
'SearchSuccessTopHeadlinesNewsState{listArticles: ${tSearchSuccessTopHeadlinesNewsState.listArticles}}',
146+
);
147+
},
148+
);
149+
});
118150
}

0 commit comments

Comments
 (0)