Initial commit: CookIT recipe collection and week planner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CedricandClaude Opus 5.5 committed 2026-10-28 22:55:09 +01:00
commit 7d7a0bc60b
173 files changed
+22803

No files matched your search

+232
View File
@@ -0,0 +1,232 @@
import 'package:cookit/src/recipes/models/recipe.dart';
import 'package:cookit/src/recipes/views/widgets/recipe_card.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../support/fixtures.dart';
import '../support/test_app.dart';
/// A Recipe name as shown on a grid card (the suggestion card may show it too).
Finder inGrid(String text) => find.descendant(of: find.byType(RecipeCard), matching: find.text(text));
void main() {
testWidgets('home grid shows Recipes and filters them by search, Course and review', (tester) async {
final app = await TestApp.create(tester);
final dessert = (await tester.runAsync(() => app.courses.findOrCreate('Dessert')))!;
await tester.runAsync(() async {
await app.recipes.saveRecipe(recipe(id: 'a', name: 'Käsespätzle', tags: ['schwäbisch']));
await app.recipes.saveRecipe(
recipe(id: 'b', name: 'Tiramisu', courseId: dessert.id, reviewReasons: ['Portionen fehlen.']),
);
});
await tester.pumpWidget(app.build());
await app.settle(tester);
expect(inGrid('Käsespätzle'), findsOneWidget);
expect(inGrid('Tiramisu'), findsOneWidget);
expect(find.text('IDEE FÜR HEUTE'), findsOneWidget);
await tester.enterText(find.byType(TextField), 'kase');
await tester.pumpAndSettle();
expect(inGrid('Käsespätzle'), findsOneWidget);
expect(inGrid('Tiramisu'), findsNothing);
await tester.enterText(find.byType(TextField), '');
await tester.tap(find.text('1 Rezept wartet auf deinen Blick'));
await tester.pumpAndSettle();
expect(inGrid('Käsespätzle'), findsNothing);
expect(inGrid('Tiramisu'), findsOneWidget);
await tester.tap(find.text('Nur Rezepte zum Prüfen – alle zeigen'));
await tester.pumpAndSettle();
final chips = find.byWidgetPredicate((w) => w is ListView && w.scrollDirection == Axis.horizontal);
final dessertChip = find.descendant(of: chips, matching: find.text('Dessert'));
await tester.scrollUntilVisible(
dessertChip,
200,
scrollable: find.descendant(of: chips, matching: find.byType(Scrollable)),
);
await tester.tap(dessertChip);
await tester.pumpAndSettle();
expect(inGrid('Tiramisu'), findsOneWidget);
expect(inGrid('Käsespätzle'), findsNothing);
await app.dispose(tester);
});
testWidgets('Cooking Mode walks through the ingredients and every step', (tester) async {
final app = await TestApp.create(tester);
await tester.runAsync(() => app.recipes.saveRecipe(recipe()));
await tester.pumpWidget(app.build());
await app.settle(tester);
await tester.tap(inGrid('Focaccia'));
await app.settle(tester);
await tester.tap(find.text('Kochmodus starten'));
await app.settle(tester);
expect(find.text('Bereit zum Kochen?'), findsOneWidget);
expect(find.text('300 g'), findsOneWidget);
await tester.tap(find.byTooltip('Mehr Portionen'));
await tester.pumpAndSettle();
expect(find.text('5'), findsOneWidget);
expect(find.text('375 g'), findsOneWidget);
await tester.tap(find.text('Los geht’s'));
await tester.pumpAndSettle();
expect(find.text('Schritt 1 von 2'), findsOneWidget);
expect(find.text('Kneten.'), findsOneWidget);
expect(find.text('Teig'), findsOneWidget);
await tester.tap(find.text('Weiter'));
await tester.pumpAndSettle();
expect(find.text('Gehen lassen.'), findsOneWidget);
await tester.tap(find.text('Fertig'));
await tester.pumpAndSettle();
expect(find.text('Guten Appetit!'), findsOneWidget);
await tester.tap(find.text('Zurück zum Rezept'));
await app.settle(tester);
expect(find.text('Kochmodus starten'), findsOneWidget);
await app.dispose(tester);
});
testWidgets('the heart on a card toggles the favourite', (tester) async {
final app = await TestApp.create(tester);
await tester.runAsync(() => app.recipes.saveRecipe(recipe()));
await tester.pumpWidget(app.build());
await app.settle(tester);
await tester.tap(find.descendant(of: find.byType(RecipeCard), matching: find.byTooltip('Favorit')));
await app.settle(tester);
expect((await tester.runAsync(app.recipes.allRecipes))!.single.favourite, isTrue);
await app.dispose(tester);
});
testWidgets('empty collection explains how to start', (tester) async {
final app = await TestApp.create(tester);
await tester.pumpWidget(app.build());
await app.settle(tester);
expect(find.text('Noch keine Rezepte'), findsOneWidget);
await app.dispose(tester);
});
testWidgets('detail shows the Recipe, and "Erledigt" clears its Review Flag', (tester) async {
final app = await TestApp.create(tester);
await tester.runAsync(() => app.recipes.saveRecipe(recipe(reviewReasons: ['Portionen fehlen.'])));
await tester.pumpWidget(app.build());
await app.settle(tester);
await tester.tap(inGrid('Focaccia'));
await app.settle(tester);
expect(find.text('Bitte prüfen'), findsOneWidget);
expect(find.text('• Portionen fehlen.'), findsOneWidget);
expect(find.text('300 g'), findsOneWidget);
expect(find.text('2–3 EL'), findsOneWidget);
expect(find.text('Topping'), findsOneWidget);
expect(find.text('775 kcal'), findsOneWidget);
expect(find.text('pro Portion'), findsOneWidget);
expect(find.text('50 Min.'), findsOneWidget);
expect(find.text('22x22 cm Form'), findsOneWidget, reason: 'Notes show under the Zutaten tab too');
await tester.tap(find.text('Zubereitung · 2'));
await tester.pumpAndSettle();
expect(find.text('Teig'), findsOneWidget);
expect(find.text('Kneten.'), findsOneWidget);
expect(find.text('22x22 cm Form'), findsOneWidget);
await tester.tap(find.text('Erledigt'));
await app.settle(tester);
expect(find.text('Bitte prüfen'), findsNothing);
await app.dispose(tester);
});
testWidgets('a new Recipe can be created with ingredients and steps', (tester) async {
final app = await TestApp.create(tester);
await tester.pumpWidget(app.build());
await app.settle(tester);
await tester.tap(find.byTooltip('Neues Rezept'));
await app.settle(tester);
await tester.tap(find.text('Speichern'));
await tester.pumpAndSettle();
expect(find.text('Bitte gib dem Rezept einen Namen.'), findsOneWidget);
await tester.enterText(find.widgetWithText(TextFormField, 'Name'), 'Pfannkuchen');
await tester.enterText(find.widgetWithText(TextFormField, 'Portionen'), '2');
final ingredientName = find.widgetWithText(TextFormField, 'Zutat');
await tester.enterText(find.widgetWithText(TextFormField, 'Menge'), '1,5');
await tester.enterText(ingredientName, 'Mehl');
await tester.tap(find.text('–'));
await tester.pumpAndSettle();
expect(find.text('Bund'), findsNothing);
await tester.tap(find.text('Weitere Einheiten …'));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(ChoiceChip, 'Bund'));
await tester.pumpAndSettle();
expect(find.text('Bund'), findsOneWidget);
await tester.enterText(find.widgetWithText(TextFormField, 'Schritt 1'), 'Alles verrühren.');
await tester.tap(find.text('Speichern'));
await app.settle(tester);
expect(inGrid('Pfannkuchen'), findsOneWidget);
final saved = (await tester.runAsync(app.recipes.allRecipes))!.single;
expect(saved.servings, 2);
expect(saved.ingredientGroups.single.lines.single.amount, 1.5);
expect(saved.ingredientGroups.single.lines.single.name, 'Mehl');
expect(saved.ingredientGroups.single.lines.single.unitKey, 'bunch');
expect(saved.stepSections.single.steps, ['Alles verrühren.']);
expect(saved.calories, isNull);
expect(saved.needsReview, isFalse);
await app.dispose(tester);
});
testWidgets('an invalid amount blocks saving', (tester) async {
final app = await TestApp.create(tester);
await tester.pumpWidget(app.build());
await app.settle(tester);
await tester.tap(find.byTooltip('Neues Rezept'));
await app.settle(tester);
await tester.enterText(find.widgetWithText(TextFormField, 'Name'), 'Test');
await tester.enterText(find.widgetWithText(TextFormField, 'Menge'), 'viel');
await tester.tap(find.text('Speichern'));
await app.settle(tester);
expect(find.text('z. B. 2, 1,5, ½ oder 2-3'), findsOneWidget);
expect(await tester.runAsync(app.recipes.allRecipes), isEmpty);
await app.dispose(tester);
});
testWidgets('editing keeps favourite and Review Flag', (tester) async {
final app = await TestApp.create(tester);
await tester.runAsync(() => app.recipes.saveRecipe(recipe(favourite: true, reviewReasons: ['x'])));
await tester.pumpWidget(app.build());
await app.settle(tester);
await tester.tap(inGrid('Focaccia'));
await app.settle(tester);
await tester.tap(find.byTooltip('Bearbeiten'));
await app.settle(tester);
await tester.enterText(find.widgetWithText(TextFormField, 'Name'), 'Focaccia mit Rosmarin');
await tester.tap(find.text('Speichern'));
await app.settle(tester);
final saved = (await tester.runAsync(app.recipes.allRecipes))!.single;
expect(saved.name, 'Focaccia mit Rosmarin');
expect(saved.favourite, isTrue);
expect(saved.reviewReasons, ['x']);
expect(saved.calories!.basis, CaloriesBasis.perServing);
expect(saved.ingredientGroups.map((g) => g.name), [null, 'Topping']);
expect(saved.stepSections.single.title, 'Teig');
await app.dispose(tester);
});
}