From a3548e38e02312544d7e7141c6aacf3f6353c347 Mon Sep 17 00:00:00 2001 From: Christoph Loy Date: Sun, 14 Jan 2024 00:10:57 +0100 Subject: [PATCH] Add testing for recipe_cubit --- lib/cubits/item_search_cubit.dart | 28 +++++++----- lib/cubits/recipe_cubit.dart | 36 ++++++++------- lib/cubits/recipe_list_cubit.dart | 6 +-- lib/pages/recipe_page.dart | 6 +-- lib/services/transaction_handler.dart | 5 ++- .../local_only_transaction_handler.dart | 12 +++++ test/cubits/recipe_cubit_test.dart | 45 +++++++++++++++++++ 7 files changed, 102 insertions(+), 36 deletions(-) create mode 100644 test/cubits/local_only_transaction_handler.dart create mode 100644 test/cubits/recipe_cubit_test.dart diff --git a/lib/cubits/item_search_cubit.dart b/lib/cubits/item_search_cubit.dart index b3666dae..ccf96149 100644 --- a/lib/cubits/item_search_cubit.dart +++ b/lib/cubits/item_search_cubit.dart @@ -13,10 +13,7 @@ class ItemSearchCubit extends Cubit { void itemSelected(Item item) { final List selectedItems = List.from(state.selectedItems); - final bool containsItem = state.selectedItems.contains(item); - if (containsItem) { - selectedItems.removeWhere((e) => e == item); - } else { + if(!selectedItems.remove(item)) { selectedItems.add(item); } emit(ItemSearchState(selectedItems, '', state.searchResults)); @@ -24,13 +21,7 @@ class ItemSearchCubit extends Cubit { Future search(String query) async { if (query.isNotEmpty) { - final splitIndex = query.indexOf(','); - String queryName = query; - String? queryDescription; - if (splitIndex >= 0) { - queryName = query.substring(0, splitIndex).trim(); - queryDescription = query.substring(splitIndex + 1).trim(); - } + final (queryName, queryDescription) = parseQuery(query); List items = []; for (Item item @@ -58,6 +49,21 @@ class ItemSearchCubit extends Cubit { emit(ItemSearchState(state.selectedItems, query, state.searchResults)); } } + + /// Scans the [query] for a comma and if found, + /// returns a tuple consisting of the part before + /// the comma and the remaining string. + /// Otherwise returns the [query]. + (String, String?) parseQuery(String query) { + final splitIndex = query.indexOf(","); + if (splitIndex >= 0) { + return ( + query.substring(0, splitIndex).trim(), + query.substring(splitIndex + 1).trim() + ); + } + return (query, null); + } } class ItemSearchState extends Equatable { diff --git a/lib/cubits/recipe_cubit.dart b/lib/cubits/recipe_cubit.dart index 7e4407b8..41048d98 100644 --- a/lib/cubits/recipe_cubit.dart +++ b/lib/cubits/recipe_cubit.dart @@ -13,17 +13,24 @@ import 'package:kitchenowl/services/transactions/shoppinglist.dart'; class RecipeCubit extends Cubit { final Household? household; + final TransactionHandler _transactionHandler; - RecipeCubit({this.household, required Recipe recipe, int? selectedYields}) - : super(RecipeState(recipe: recipe, selectedYields: selectedYields)) { + RecipeCubit(Household? household, Recipe recipe, int? selectedYields) + : this.forTesting(TransactionHandler.getInstance(), household, recipe, selectedYields); + + RecipeCubit.forTesting( + TransactionHandler transactionHandler, + this.household, + Recipe recipe, + int? selectedYields) + : _transactionHandler = transactionHandler, + super(RecipeState(recipe: recipe, selectedYields: selectedYields)) { refresh(); } void itemSelected(RecipeItem item) { - final List selectedItems = List.from(state.selectedItems); - if (selectedItems.contains(item.name)) { - selectedItems.remove(item.name); - } else { + final Set selectedItems = Set.from(state.selectedItems); + if (!selectedItems.remove(item.name)) { selectedItems.add(item.name); } emit(state.copyWith(selectedItems: selectedItems)); @@ -40,11 +47,11 @@ class RecipeCubit extends Cubit { } Future refresh() async { - final recipe = TransactionHandler.getInstance() + final recipe = _transactionHandler .runTransaction(TransactionRecipeGetRecipe(recipe: state.recipe)); Future>? shoppingLists; if (household != null) { - shoppingLists = TransactionHandler.getInstance().runTransaction( + shoppingLists = _transactionHandler.runTransaction( TransactionShoppingListGet(household: household!), forceOffline: true, ); @@ -60,7 +67,7 @@ class RecipeCubit extends Cubit { Future addItemsToList([ShoppingList? shoppingList]) async { shoppingList ??= household?.defaultShoppingList; if (shoppingList != null) { - await TransactionHandler.getInstance() + await _transactionHandler .runTransaction(TransactionShoppingListAddRecipeItems( shoppinglist: shoppingList, items: state.dynamicRecipe.items @@ -72,8 +79,7 @@ class RecipeCubit extends Cubit { Future addRecipeToPlanner({int? day, bool updateOnAdd = false}) async { if (household != null) { - await TransactionHandler.getInstance() - .runTransaction(TransactionPlannerAddRecipe( + await _transactionHandler.runTransaction(TransactionPlannerAddRecipe( household: household!, recipePlan: RecipePlan( recipe: state.recipe, @@ -89,8 +95,8 @@ class RecipeCubit extends Cubit { } } -class RecipeState extends Equatable { - final List selectedItems; +final class RecipeState extends Equatable { + final Set selectedItems; final Recipe recipe; final Recipe dynamicRecipe; final int selectedYields; @@ -113,11 +119,11 @@ class RecipeState extends Equatable { }) : selectedYields = selectedYields ?? recipe.yields, dynamicRecipe = recipe.withYields(selectedYields ?? recipe.yields), selectedItems = - recipe.items.where((e) => !e.optional).map((e) => e.name).toList(); + recipe.items.where((e) => !e.optional).map((e) => e.name).toSet(); RecipeState copyWith({ Recipe? recipe, - List? selectedItems, + Set? selectedItems, int? selectedYields, UpdateEnum? updateState, List? shoppingLists, diff --git a/lib/cubits/recipe_list_cubit.dart b/lib/cubits/recipe_list_cubit.dart index 128da3de..af13020b 100644 --- a/lib/cubits/recipe_list_cubit.dart +++ b/lib/cubits/recipe_list_cubit.dart @@ -71,11 +71,7 @@ class RecipeListCubit extends Cubit { if (state is SearchRecipeListState) { query = query ?? state.query; } - if (_refreshThread != null && query != _refreshCurrentQuery) { - _refreshCurrentQuery = query; - _refreshThread = _refresh(query); - } - if (_refreshThread == null) { + if (_refreshThread == null || query != _refreshCurrentQuery) { _refreshCurrentQuery = query; _refreshThread = _refresh(query); } diff --git a/lib/pages/recipe_page.dart b/lib/pages/recipe_page.dart index 2b105dff..6d0b8eca 100644 --- a/lib/pages/recipe_page.dart +++ b/lib/pages/recipe_page.dart @@ -45,9 +45,9 @@ class _RecipePageState extends State { void initState() { super.initState(); cubit = RecipeCubit( - household: widget.household, - recipe: widget.recipe, - selectedYields: widget.selectedYields, + widget.household, + widget.recipe, + widget.selectedYields, ); } diff --git a/lib/services/transaction_handler.dart b/lib/services/transaction_handler.dart index 40ad3d4c..4a80f6a1 100644 --- a/lib/services/transaction_handler.dart +++ b/lib/services/transaction_handler.dart @@ -6,9 +6,10 @@ import 'package:kitchenowl/services/storage/transaction_storage.dart'; class TransactionHandler { static TransactionHandler? _instance; - TransactionHandler._internal(); + TransactionHandler.internal(); + static TransactionHandler getInstance() { - _instance ??= TransactionHandler._internal(); + _instance ??= TransactionHandler.internal(); return _instance!; } diff --git a/test/cubits/local_only_transaction_handler.dart b/test/cubits/local_only_transaction_handler.dart new file mode 100644 index 00000000..03546f21 --- /dev/null +++ b/test/cubits/local_only_transaction_handler.dart @@ -0,0 +1,12 @@ +import 'package:kitchenowl/services/transaction.dart'; +import 'package:kitchenowl/services/transaction_handler.dart'; + +class LocalOnlyTransactionHandler extends TransactionHandler { + LocalOnlyTransactionHandler() : super.internal(); + + @override + Future runTransaction(Transaction t, + {bool forceOffline = false, bool saveTransaction = true}) { + return t.runLocal(); + } +} \ No newline at end of file diff --git a/test/cubits/recipe_cubit_test.dart b/test/cubits/recipe_cubit_test.dart new file mode 100644 index 00000000..d9b27583 --- /dev/null +++ b/test/cubits/recipe_cubit_test.dart @@ -0,0 +1,45 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:kitchenowl/cubits/recipe_cubit.dart'; +import 'package:kitchenowl/models/item.dart'; +import 'package:kitchenowl/models/recipe.dart'; + +import 'local_only_transaction_handler.dart'; + +const flour = RecipeItem(name: "Flour"); +const salt = RecipeItem(name: "Salt"); +const recipe = Recipe( + id: 100, + name: "foo", + description: "The desc", + items: [flour, salt], + yields: 1); + +RecipeCubit createSampleCubit() => + RecipeCubit.forTesting(LocalOnlyTransactionHandler(), null, recipe, 1); + +void main() { + test("Item selection works", () { + final cubit = createSampleCubit(); + final allItems = recipe.mandatoryItems.map((e) => e.name).toSet(); + + expect(cubit.state.selectedItems, allItems); + + cubit.itemSelected(flour); + expect(cubit.state.selectedItems, {salt.name}); + + cubit.itemSelected(flour); + expect(cubit.state.selectedItems, allItems); + + cubit.itemSelected(flour); + cubit.itemSelected(salt); + expect(cubit.state.selectedItems, isEmpty); + }); + + test("Changing the yield works", () { + final cubit = createSampleCubit(); + expect(cubit.state.selectedYields, 1); + + cubit.setSelectedYields(4); + expect(cubit.state.selectedYields, 4); + }); +}