Skip to content

Commit

Permalink
Update controller to avoid submitting the review if the data hasn't c…
Browse files Browse the repository at this point in the history
…hanged
  • Loading branch information
bizz84 committed Nov 16, 2023
1 parent fa3238a commit b7db6d0
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,33 @@ class LeaveReviewController extends StateNotifier<AsyncValue<void>> {
final DateTime Function() currentDateBuilder;

Future<void> submitReview({
Review? previousReview,
required ProductID productId,
required double rating,
required String comment,
required void Function() onSuccess,
}) async {
final review = Review(
rating: rating,
comment: comment,
date: currentDateBuilder(),
);
state = const AsyncLoading();
final newState = await AsyncValue.guard(() =>
reviewsService.submitReview(productId: productId, review: review));
if (mounted) {
// * only set the state if the controller hasn't been disposed
state = newState;
if (state.hasError == false) {
onSuccess();
// * only submit if the rating is new or it has changed
if (previousReview == null ||
rating != previousReview.rating ||
comment != previousReview.comment) {
final review = Review(
rating: rating,
comment: comment,
date: currentDateBuilder(),
);
state = const AsyncLoading();
final newState = await AsyncValue.guard(() =>
reviewsService.submitReview(productId: productId, review: review));
if (mounted) {
// * only set the state if the controller hasn't been disposed
state = newState;
if (state.hasError == false) {
onSuccess();
}
}
} else {
onSuccess();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class _LeaveReviewFormState extends ConsumerState<LeaveReviewForm> {
? null
: () =>
ref.read(leaveReviewControllerProvider.notifier).submitReview(
previousReview: widget.review,
productId: widget.productId,
rating: _rating,
comment: _controller.text,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import 'package:ecommerce_app/src/features/reviews/domain/review.dart';
import 'package:ecommerce_app/src/features/reviews/presentation/leave_review_screen/leave_review_controller.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

import '../../../../mocks.dart';

void main() {
final testDate = DateTime(2022, 7, 31);
const testRating = 5.0;
const testComment = 'love it!';
final testReview = Review(
rating: testRating,
comment: testComment,
date: testDate,
);
const testProductId = '1';

late MockReviewsService reviewsService;
setUp(() {
reviewsService = MockReviewsService();
});

group('submitReview', () {
test('success', () async {
// setup
when(() => reviewsService.submitReview(
productId: testProductId,
review: testReview,
)).thenAnswer((_) => Future.value());
final controller = LeaveReviewController(
reviewsService: reviewsService,
currentDateBuilder: () => testDate,
);
// run & verify
var didSucceed = false;
expectLater(
controller.stream,
emitsInOrder([
const AsyncLoading<void>(),
const AsyncData<void>(null),
]),
);
await controller.submitReview(
previousReview: null,
rating: testRating,
comment: testComment,
productId: testProductId,
onSuccess: () => didSucceed = true,
);
verify(() => reviewsService.submitReview(
productId: testProductId,
review: testReview,
)).called(1);
expect(didSucceed, true);
});

test('failure', () async {
// setup
when(() => reviewsService.submitReview(
productId: testProductId,
review: testReview,
)).thenThrow(Exception('Connection failed'));
final controller = LeaveReviewController(
reviewsService: reviewsService,
currentDateBuilder: () => testDate,
);
// run & verify
var didSucceed = false;
expectLater(
controller.stream,
emitsInOrder([
const AsyncLoading<void>(),
predicate<AsyncValue<void>>(
(value) {
expect(value.hasError, true);
return true;
},
)
]),
);
await controller.submitReview(
previousReview: null,
rating: testRating,
comment: testComment,
productId: testProductId,
onSuccess: () => didSucceed = true,
);
verify(() => reviewsService.submitReview(
productId: testProductId,
review: testReview,
)).called(1);
expect(didSucceed, false);
});

test('same data as before', () async {
// setup
when(() => reviewsService.submitReview(
productId: testProductId,
review: testReview,
)).thenThrow(Exception('Connection failed'));
final controller = LeaveReviewController(
reviewsService: reviewsService,
currentDateBuilder: () => testDate,
);
// run & verify
var didSucceed = false;
expectLater(
controller.stream,
emitsInOrder([]), // no state changes
);
await controller.submitReview(
previousReview: testReview,
rating: testRating,
comment: testComment,
productId: testProductId,
onSuccess: () => didSucceed = true,
);
verifyNever(() => reviewsService.submitReview(
productId: testProductId,
review: testReview,
));
expect(didSucceed, true);
});
});
}

0 comments on commit b7db6d0

Please sign in to comment.