Skip to content

Commit

Permalink
Completed code and tests for section 11
Browse files Browse the repository at this point in the history
# Conflicts:
#	ecommerce_app/lib/src/features/products/presentation/product_screen/leave_review_action.dart
#	ecommerce_app/lib/src/features/reviews/data/fake_purchases_repository.dart
  • Loading branch information
bizz84 committed May 15, 2024
1 parent 3080eb7 commit 2af9fde
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 17 deletions.
10 changes: 5 additions & 5 deletions ecommerce_app/integration_test/purchase_flow_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ void main() {
r.cart.expectFindZeroCartItems();
await r.closePage();
// reviews flow
// await r.products.selectProduct();
// r.reviews.expectFindLeaveReview();
// await r.reviews.tapLeaveReviewButton();
// await r.reviews.createAndSubmitReview();
// r.reviews.expectFindOneReview();
await r.products.selectProduct();
r.reviews.expectFindLeaveReview();
await r.reviews.tapLeaveReviewButton();
await r.reviews.createAndSubmitReview('Love it!');
r.reviews.expectFindOneReview();
// sign out
await r.openPopupMenu();
await r.auth.openAccountScreen();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:ecommerce_app/src/features/authentication/data/fake_auth_repository.dart';
import 'package:ecommerce_app/src/features/products/data/fake_products_repository.dart';
import 'package:ecommerce_app/src/features/products/domain/product.dart';
import 'package:ecommerce_app/src/features/reviews/data/fake_reviews_repository.dart';
import 'package:ecommerce_app/src/features/reviews/domain/review.dart';
Expand All @@ -25,6 +26,37 @@ class ReviewsService {
uid: user.uid,
review: review,
);
// * Note: this should be done on the backend
// * At this stage the review is already submitted
// * and we don't need to await for the product rating to also be updated
_updateProductRating(productId);
}

Future<void> _updateProductRating(ProductID productId) async {
final reviews =
await ref.read(reviewsRepositoryProvider).fetchReviews(productId);
final avgRating = _avgReviewScore(reviews);
final product = ref.read(productsRepositoryProvider).getProduct(productId);
if (product == null) {
throw StateError('Product not found with id: $productId.'.hardcoded);
}
final updated = product.copyWith(
avgRating: avgRating,
numRatings: reviews.length,
);
await ref.read(productsRepositoryProvider).setProduct(updated);
}

double _avgReviewScore(List<Review> reviews) {
if (reviews.isNotEmpty) {
var total = 0.0;
for (var review in reviews) {
total += review.rating;
}
return total / reviews.length;
} else {
return 0.0;
}
}
}

Expand Down
10 changes: 5 additions & 5 deletions ecommerce_app/test/src/features/purchase_flow_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ void main() {
r.cart.expectFindZeroCartItems();
await r.closePage();
// reviews flow
// await r.products.selectProduct();
// r.reviews.expectFindLeaveReview();
// await r.reviews.tapLeaveReviewButton();
// await r.reviews.createAndSubmitReview();
// r.reviews.expectFindOneReview();
await r.products.selectProduct();
r.reviews.expectFindLeaveReview();
await r.reviews.tapLeaveReviewButton();
await r.reviews.createAndSubmitReview('Love it!');
r.reviews.expectFindOneReview();
// sign out
await r.openPopupMenu();
await r.auth.openAccountScreen();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:ecommerce_app/src/constants/test_products.dart';
import 'package:ecommerce_app/src/features/authentication/data/fake_auth_repository.dart';
import 'package:ecommerce_app/src/features/authentication/domain/app_user.dart';
import 'package:ecommerce_app/src/features/products/data/fake_products_repository.dart';
import 'package:ecommerce_app/src/features/reviews/application/reviews_service.dart';
import 'package:ecommerce_app/src/features/reviews/data/fake_reviews_repository.dart';
import 'package:ecommerce_app/src/features/reviews/domain/review.dart';
Expand All @@ -11,21 +13,25 @@ import '../../../mocks.dart';

void main() {
const testUser = AppUser(uid: 'abc', email: '[email protected]');
const testProductId = '1';
final testProductId = kTestProducts[0].id;
final testReview =
Review(rating: 5, comment: '', date: DateTime(2022, 7, 31));
late MockAuthRepository authRepository;
late MockReviewsRepository reviewsRepository;
late MockProductsRepository productsRepository;

setUp(() {
authRepository = MockAuthRepository();
reviewsRepository = MockReviewsRepository();
productsRepository = MockProductsRepository();
});

ReviewsService makeReviewsService() {
final container = ProviderContainer(
overrides: [
authRepositoryProvider.overrideWithValue(authRepository),
reviewsRepositoryProvider.overrideWithValue(reviewsRepository),
productsRepositoryProvider.overrideWithValue(productsRepository),
],
);
addTearDown(container.dispose);
Expand Down Expand Up @@ -54,6 +60,14 @@ void main() {
uid: testUser.uid,
review: testReview,
)).thenAnswer((_) => Future.value());
when(() => reviewsRepository.fetchReviews(testProductId))
.thenAnswer((_) => Future.value([]));
when(() => productsRepository.getProduct(testProductId)).thenReturn(
kTestProducts[0],
);
when(() => productsRepository.setProduct(
kTestProducts[0],
)).thenAnswer((_) => Future.value());
final reviewsService = makeReviewsService();
// run
await reviewsService.submitReview(productId: '1', review: testReview);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:flutter_test/flutter_test.dart';

import '../../../../robot.dart';

void main() {
Future<void> purchaseOneProduct(Robot r) async {
// add to cart
await r.products.selectProduct();
await r.products.setProductQuantity(3);
await r.cart.addToCart();
await r.cart.openCart();
r.cart.expectFindNCartItems(1);
// checkout
await r.checkout.startCheckout();
await r.auth.enterAndSubmitEmailAndPassword();
r.cart.expectFindNCartItems(1);
await r.checkout.startPayment();
// when a payment is complete, user is taken to the orders page
r.orders.expectFindNOrders(1);
await r.closePage(); // close orders page
}

testWidgets('purchase product, leave review, update it', (tester) async {
final r = Robot(tester);
await r.pumpMyApp();
await purchaseOneProduct(r);
await r.products.selectProduct();
// leave review
r.reviews.expectFindLeaveReview();
await r.reviews.tapLeaveReviewButton();
await r.reviews.createAndSubmitReview('Love it!');
r.reviews.expectFindOneReview();
r.reviews.expectFindText('Love it!');
// update review
r.reviews.expectFindUpdateReview();
await r.reviews.tapUpdateReviewButton();
await r.reviews.updateAndSubmitReview('Great!');
r.reviews.expectFindOneReview();
r.reviews.expectFindText('Great!');
});
}
34 changes: 28 additions & 6 deletions ecommerce_app/test/src/features/reviews/reviews_robot.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,48 @@ class ReviewsRobot {
expect(finder, findsOneWidget);
}

void expectFindUpdateReview() {
final finder = find.text('Update review');
expect(finder, findsOneWidget);
}

Future<void> tapLeaveReviewButton() async {
final finder = find.text('Leave a review');
expect(finder, findsOneWidget);
await tester.tap(finder);
await tester.pumpAndSettle();
}

Future<void> tapUpdateReviewButton() async {
final finder = find.text('Update review');
expect(finder, findsOneWidget);
await tester.tap(finder);
await tester.pumpAndSettle();
}

void expectFindOneReview() {
// don't skip offstage widgets as the product reviews may appear below
// the visible area on screen
final finder = find.byType(ProductReviewCard, skipOffstage: false);
expect(finder, findsOneWidget);
}

void expectFindText(String text) {
final finder = find.text(text);
expect(finder, findsOneWidget);
}

// leave a review
Future<void> selectReviewScore() async {
Future<void> enterReviewRating() async {
final finder = find.byKey(const Key('stars-4'));
expect(finder, findsOneWidget);
await tester.tap(finder);
}

Future<void> enterReviewComment() async {
Future<void> enterReviewComment(String comment) async {
final finder = find.byKey(LeaveReviewForm.reviewCommentKey);
expect(finder, findsOneWidget);
await tester.enterText(finder, 'Love it!');
await tester.enterText(finder, comment);
}

Future<void> submitReview() async {
Expand All @@ -46,9 +63,14 @@ class ReviewsRobot {
await tester.pumpAndSettle();
}

Future<void> createAndSubmitReview() async {
await selectReviewScore();
await enterReviewComment();
Future<void> createAndSubmitReview(String comment) async {
await enterReviewRating();
await enterReviewComment(comment);
await submitReview();
}

Future<void> updateAndSubmitReview(String comment) async {
await enterReviewComment(comment);
await submitReview();
}
}

0 comments on commit 2af9fde

Please sign in to comment.