Skip to content

Commit

Permalink
Add remaining tests for shopping cart feature
Browse files Browse the repository at this point in the history
# 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
bizz84 committed Dec 20, 2023
1 parent 2663b2a commit b2aa8bd
Show file tree
Hide file tree
Showing 9 changed files with 460 additions and 37 deletions.
22 changes: 0 additions & 22 deletions ecommerce_app/integration_test/auth_flow_test.dart

This file was deleted.

35 changes: 35 additions & 0 deletions ecommerce_app/integration_test/purchase_flow_test.dart
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();
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ void main() {
testWidgets('Sign in and sign out flow', (tester) async {
final r = Robot(tester);
await r.pumpMyApp();
r.expectFindAllProductCards();
r.products.expectFindAllProductCards();
await r.openPopupMenu();
await r.auth.openEmailPasswordSignInScreen();
await r.auth.signInWithEmailAndPassword();
r.expectFindAllProductCards();
r.products.expectFindAllProductCards();
await r.openPopupMenu();
await r.auth.openAccountScreen();
await r.auth.tapLogoutButton();
await r.auth.tapDialogLogoutButton();
r.expectFindAllProductCards();
r.products.expectFindAllProductCards();
});
}
93 changes: 93 additions & 0 deletions ecommerce_app/test/src/features/cart/cart_robot.dart
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);
}
}
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);
});
});
}
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();
});
});
}
Loading

0 comments on commit b2aa8bd

Please sign in to comment.