-
Notifications
You must be signed in to change notification settings - Fork 280
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add remaining tests for shopping cart feature
# Conflicts: # ecommerce_app/test/src/goldens/products_list_1000x1000.png # ecommerce_app/test/src/goldens/products_list_300x600.png # ecommerce_app/test/src/goldens/products_list_600x800.png
- Loading branch information
Showing
9 changed files
with
460 additions
and
37 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
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,35 @@ | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:integration_test/integration_test.dart'; | ||
|
||
import '../test/src/robot.dart'; | ||
|
||
void main() { | ||
IntegrationTestWidgetsFlutterBinding.ensureInitialized(); | ||
testWidgets('Full purchase flow', (tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
r.products.expectFindAllProductCards(); | ||
// add to cart flows | ||
await r.products.selectProduct(); | ||
await r.products.setProductQuantity(3); | ||
await r.cart.addToCart(); | ||
await r.cart.openCart(); | ||
r.cart.expectFindNCartItems(1); | ||
await r.closePage(); | ||
// sign in | ||
await r.openPopupMenu(); | ||
await r.auth.openEmailPasswordSignInScreen(); | ||
await r.auth.signInWithEmailAndPassword(); | ||
r.products.expectFindAllProductCards(); | ||
// check cart again (to verify cart synchronization) | ||
await r.cart.openCart(); | ||
r.cart.expectFindNCartItems(1); | ||
await r.closePage(); | ||
// sign out | ||
await r.openPopupMenu(); | ||
await r.auth.openAccountScreen(); | ||
await r.auth.tapLogoutButton(); | ||
await r.auth.tapDialogLogoutButton(); | ||
r.products.expectFindAllProductCards(); | ||
}); | ||
} |
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,93 @@ | ||
import 'package:ecommerce_app/src/common_widgets/item_quantity_selector.dart'; | ||
import 'package:ecommerce_app/src/features/cart/presentation/shopping_cart/shopping_cart_item.dart'; | ||
import 'package:ecommerce_app/src/features/products/presentation/home_app_bar/shopping_cart_icon.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
|
||
class CartRobot { | ||
CartRobot(this.tester); | ||
final WidgetTester tester; | ||
|
||
Future<void> addToCart() async { | ||
final finder = find.text('Add to Cart'); | ||
expect(finder, findsOneWidget); | ||
await tester.tap(finder); | ||
await tester.pumpAndSettle(); | ||
} | ||
|
||
// shopping cart | ||
Future<void> openCart() async { | ||
final finder = find.byKey(ShoppingCartIcon.shoppingCartIconKey); | ||
expect(finder, findsOneWidget); | ||
await tester.tap(finder); | ||
await tester.pumpAndSettle(); | ||
} | ||
|
||
void expectProductIsOutOfStock() async { | ||
final finder = find.text('Out of Stock'); | ||
expect(finder, findsOneWidget); | ||
} | ||
|
||
Future<void> incrementCartItemQuantity( | ||
{required int quantity, required int atIndex}) async { | ||
final finder = find.byKey(ItemQuantitySelector.incrementKey(atIndex)); | ||
expect(finder, findsOneWidget); | ||
for (var i = 0; i < quantity; i++) { | ||
await tester.tap(finder); | ||
await tester.pumpAndSettle(); | ||
} | ||
} | ||
|
||
Future<void> decrementCartItemQuantity( | ||
{required int quantity, required int atIndex}) async { | ||
final finder = find.byKey(ItemQuantitySelector.decrementKey(atIndex)); | ||
expect(finder, findsOneWidget); | ||
for (var i = 0; i < quantity; i++) { | ||
await tester.tap(finder); | ||
await tester.pumpAndSettle(); | ||
} | ||
} | ||
|
||
Future<void> deleteCartItem({required int atIndex}) async { | ||
final finder = find.byKey(ShoppingCartItemContents.deleteKey(atIndex)); | ||
expect(finder, findsOneWidget); | ||
await tester.tap(finder); | ||
await tester.pumpAndSettle(); | ||
} | ||
|
||
void expectShoppingCartIsLoading() { | ||
final finder = find.byType(CircularProgressIndicator); | ||
expect(finder, findsOneWidget); | ||
} | ||
|
||
void expectShoppingCartIsEmpty() { | ||
final finder = find.text('Your shopping cart is empty'); | ||
expect(finder, findsOneWidget); | ||
} | ||
|
||
void expectFindZeroCartItems() { | ||
final finder = find.byType(ShoppingCartItem); | ||
expect(finder, findsNothing); | ||
} | ||
|
||
void expectFindNCartItems(int count) { | ||
final finder = find.byType(ShoppingCartItem); | ||
expect(finder, findsNWidgets(count)); | ||
} | ||
|
||
Text getItemQuantityWidget({int? atIndex}) { | ||
final finder = find.byKey(ItemQuantitySelector.quantityKey(atIndex)); | ||
expect(finder, findsOneWidget); | ||
return finder.evaluate().single.widget as Text; | ||
} | ||
|
||
void expectItemQuantity({required int quantity, int? atIndex}) { | ||
final text = getItemQuantityWidget(atIndex: atIndex); | ||
expect(text.data, '$quantity'); | ||
} | ||
|
||
void expectShoppingCartTotalIs(String text) { | ||
final finder = find.text(text); | ||
expect(finder, findsOneWidget); | ||
} | ||
} |
98 changes: 98 additions & 0 deletions
98
...st/src/features/cart/presentation/shopping_cart/shopping_cart_screen_controller_test.dart
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,98 @@ | ||
import 'package:ecommerce_app/src/features/cart/domain/item.dart'; | ||
import 'package:ecommerce_app/src/features/cart/presentation/shopping_cart/shopping_cart_screen_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() { | ||
const productId = '1'; | ||
group('updateItemQuantity', () { | ||
test('update quantity, success', () async { | ||
// setup | ||
const item = Item(productId: productId, quantity: 3); | ||
final cartService = MockCartService(); | ||
when(() => cartService.setItem(item)).thenAnswer( | ||
(_) => Future.value(null), | ||
); | ||
final controller = ShoppingCartScreenController(cartService: cartService); | ||
// run & verify | ||
expectLater( | ||
controller.stream, | ||
emitsInOrder([ | ||
const AsyncLoading<void>(), | ||
const AsyncData<void>(null), | ||
]), | ||
); | ||
await controller.updateItemQuantity(productId, 3); | ||
}); | ||
|
||
test('update quantity, failure', () async { | ||
// setup | ||
const item = Item(productId: productId, quantity: 3); | ||
final cartService = MockCartService(); | ||
when(() => cartService.setItem(item)).thenThrow( | ||
(_) => Exception('Connection failed'), | ||
); | ||
final controller = ShoppingCartScreenController(cartService: cartService); | ||
// run & verify | ||
expectLater( | ||
controller.stream, | ||
emitsInOrder([ | ||
const AsyncLoading<void>(), | ||
predicate<AsyncValue<void>>( | ||
(value) { | ||
expect(value.hasError, true); | ||
return true; | ||
}, | ||
), | ||
]), | ||
); | ||
await controller.updateItemQuantity(productId, 3); | ||
// verify | ||
verify(() => cartService.setItem(item)).called(1); | ||
}); | ||
}); | ||
group('removeItemById', () { | ||
test('remove item, success', () async { | ||
// setup | ||
final cartService = MockCartService(); | ||
when(() => cartService.removeItemById(productId)).thenAnswer( | ||
(_) => Future.value(null), | ||
); | ||
final controller = ShoppingCartScreenController(cartService: cartService); | ||
// run & verify | ||
expectLater( | ||
controller.stream, | ||
emitsInOrder([ | ||
const AsyncLoading<void>(), | ||
const AsyncData<void>(null), | ||
]), | ||
); | ||
await controller.removeItemById(productId); | ||
}); | ||
test('remove item, failure', () async { | ||
// setup | ||
final cartService = MockCartService(); | ||
when(() => cartService.removeItemById(productId)).thenThrow( | ||
(_) => Exception('Connection failed'), | ||
); | ||
final controller = ShoppingCartScreenController(cartService: cartService); | ||
// run & verify | ||
expectLater( | ||
controller.stream, | ||
emitsInOrder([ | ||
const AsyncLoading<void>(), | ||
predicate<AsyncValue<void>>( | ||
(value) { | ||
expect(value.hasError, true); | ||
return true; | ||
}, | ||
), | ||
]), | ||
); | ||
await controller.removeItemById(productId); | ||
}); | ||
}); | ||
} |
120 changes: 120 additions & 0 deletions
120
...erce_app/test/src/features/cart/presentation/shopping_cart/shopping_cart_screen_test.dart
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,120 @@ | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import '../../../../robot.dart'; | ||
|
||
void main() { | ||
group('shopping cart', () { | ||
testWidgets('Empty shopping cart', (tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
r.products.expectFindNProductCards(14); // check all products are found | ||
await r.cart.openCart(); | ||
r.cart.expectShoppingCartIsEmpty(); | ||
}); | ||
|
||
testWidgets('Add product with quantity = 1', (tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
await r.products.selectProduct(); | ||
await r.cart.addToCart(); | ||
await r.cart.openCart(); | ||
r.cart.expectItemQuantity(quantity: 1, atIndex: 0); | ||
r.cart.expectShoppingCartTotalIs('Total: \$15.00'); | ||
}); | ||
|
||
testWidgets('Add product with quantity = 5', (tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
await r.products.selectProduct(); | ||
await r.products.setProductQuantity(5); | ||
await r.cart.addToCart(); | ||
await r.cart.openCart(); | ||
r.cart.expectItemQuantity(quantity: 5, atIndex: 0); | ||
r.cart.expectShoppingCartTotalIs('Total: \$75.00'); | ||
}); | ||
|
||
testWidgets('Add product with quantity = 6', (tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
await r.products.selectProduct(); | ||
await r.products.setProductQuantity(6); | ||
await r.cart.addToCart(); | ||
await r.cart.openCart(); | ||
r.cart.expectItemQuantity(quantity: 5, atIndex: 0); | ||
r.cart.expectShoppingCartTotalIs('Total: \$75.00'); | ||
}); | ||
|
||
testWidgets('Add product with quantity = 2, then increment by 2', | ||
(tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
await r.products.selectProduct(); | ||
await r.products.setProductQuantity(2); | ||
await r.cart.addToCart(); | ||
await r.cart.openCart(); | ||
await r.cart.incrementCartItemQuantity(quantity: 2, atIndex: 0); | ||
r.cart.expectItemQuantity(quantity: 4, atIndex: 0); | ||
r.cart.expectShoppingCartTotalIs('Total: \$60.00'); | ||
}); | ||
|
||
testWidgets('Add product with quantity = 5, then decrement by 2', | ||
(tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
await r.products.selectProduct(); | ||
await r.products.setProductQuantity(5); | ||
await r.cart.addToCart(); | ||
await r.cart.openCart(); | ||
await r.cart.decrementCartItemQuantity(quantity: 2, atIndex: 0); | ||
r.cart.expectItemQuantity(quantity: 3, atIndex: 0); | ||
r.cart.expectShoppingCartTotalIs('Total: \$45.00'); | ||
}); | ||
|
||
testWidgets('Add two products', (tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
// add first product | ||
await r.products.selectProduct(atIndex: 0); | ||
await r.cart.addToCart(); | ||
await r.goBack(); | ||
// add second product | ||
await r.products.selectProduct(atIndex: 1); | ||
await r.cart.addToCart(); | ||
await r.cart.openCart(); | ||
r.cart.expectFindNCartItems(2); | ||
r.cart.expectShoppingCartTotalIs('Total: \$28.00'); | ||
}); | ||
|
||
testWidgets('Add product, then delete it', (tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
await r.products.selectProduct(); | ||
await r.cart.addToCart(); | ||
await r.cart.openCart(); | ||
await r.cart.deleteCartItem(atIndex: 0); | ||
r.cart.expectShoppingCartIsEmpty(); | ||
}); | ||
|
||
testWidgets('Add product with quantity = 5, goes out of stock', | ||
(tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
await r.products.selectProduct(); | ||
await r.products.setProductQuantity(5); | ||
await r.cart.addToCart(); | ||
r.cart.expectProductIsOutOfStock(); | ||
}); | ||
|
||
testWidgets( | ||
'Add product with quantity = 5, remains out of stock when opened again', | ||
(tester) async { | ||
final r = Robot(tester); | ||
await r.pumpMyApp(); | ||
await r.products.selectProduct(); | ||
await r.products.setProductQuantity(5); | ||
await r.cart.addToCart(); | ||
await r.goBack(); | ||
await r.products.selectProduct(); | ||
r.cart.expectProductIsOutOfStock(); | ||
}); | ||
}); | ||
} |
Oops, something went wrong.