test: aggiungi suite di test Flutter
Struttura specchia lib/features/: - test/features/meal_planner/ — unit (DayPlan, MealPlan, provider) + widget (MealCard) - test/features/converter/ — unit (ConversionEntry, formule rawToCooked/cookedToRaw) - test/features/shopping_list/ — unit (ShoppingItem, provider) + widget (ShoppingItemTile) - test/helpers/pump_app.dart — estensione PumpApp condivisa tra widget test - integration_test/app_test.dart — e2e navigazione, shopping list, convertitore, meal planner Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:biteplan/features/meal_planner/models/meal_plan.dart';
|
||||
|
||||
void main() {
|
||||
group('DayPlan', () {
|
||||
test('crea con liste vuote di default', () {
|
||||
final day = DayPlan();
|
||||
expect(day.colazione, isEmpty);
|
||||
expect(day.pranzo, isEmpty);
|
||||
expect(day.cena, isEmpty);
|
||||
});
|
||||
|
||||
test('slot() ritorna la lista corretta', () {
|
||||
final day = DayPlan(
|
||||
colazione: ['latte'],
|
||||
pranzo: ['pasta'],
|
||||
cena: ['pollo'],
|
||||
);
|
||||
expect(day.slot('colazione'), ['latte']);
|
||||
expect(day.slot('pranzo'), ['pasta']);
|
||||
expect(day.slot('cena'), ['pollo']);
|
||||
expect(day.slot('unknown'), isEmpty);
|
||||
});
|
||||
|
||||
test('copyWith() non modifica lorigine', () {
|
||||
final day = DayPlan(colazione: ['latte']);
|
||||
final updated = day.copyWith(colazione: ['latte', 'caffè']);
|
||||
expect(updated.colazione, ['latte', 'caffè']);
|
||||
expect(day.colazione, ['latte']);
|
||||
});
|
||||
|
||||
test('copyWith() preserva i campi non aggiornati', () {
|
||||
final day = DayPlan(colazione: ['latte'], pranzo: ['pasta']);
|
||||
final updated = day.copyWith(colazione: ['caffè']);
|
||||
expect(updated.pranzo, ['pasta']);
|
||||
});
|
||||
|
||||
test('JSON round-trip preserva i dati', () {
|
||||
final day = DayPlan(colazione: ['latte', 'biscotti'], cena: ['pollo']);
|
||||
final restored = DayPlan.fromJson(day.toJson());
|
||||
expect(restored.colazione, ['latte', 'biscotti']);
|
||||
expect(restored.cena, ['pollo']);
|
||||
expect(restored.pranzo, isEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
group('MealPlan', () {
|
||||
test('empty() crea piano con tutti i giorni', () {
|
||||
const days = ['lunedi', 'martedi', 'mercoledi'];
|
||||
final plan = MealPlan.empty(days);
|
||||
expect(plan.days.keys, containsAll(days));
|
||||
for (final d in days) {
|
||||
expect(plan.days[d]!.colazione, isEmpty);
|
||||
}
|
||||
});
|
||||
|
||||
test('hasAnyMeal è false con piano vuoto', () {
|
||||
final plan = MealPlan.empty(['lunedi', 'martedi']);
|
||||
expect(plan.hasAnyMeal, false);
|
||||
});
|
||||
|
||||
test('hasAnyMeal è true con almeno un elemento', () {
|
||||
final plan = MealPlan({'lunedi': DayPlan(colazione: ['latte'])});
|
||||
expect(plan.hasAnyMeal, true);
|
||||
});
|
||||
|
||||
test('allItems ritorna lista appiattita', () {
|
||||
final plan = MealPlan({
|
||||
'lunedi': DayPlan(colazione: ['latte'], pranzo: ['pasta']),
|
||||
'martedi': DayPlan(cena: ['pollo']),
|
||||
});
|
||||
expect(plan.allItems, containsAll(['latte', 'pasta', 'pollo']));
|
||||
expect(plan.allItems.length, 3);
|
||||
});
|
||||
|
||||
test('allItems è vuota con piano vuoto', () {
|
||||
final plan = MealPlan.empty(['lunedi']);
|
||||
expect(plan.allItems, isEmpty);
|
||||
});
|
||||
|
||||
test('withUpdatedDay() aggiorna solo il giorno specificato', () {
|
||||
final plan = MealPlan.empty(['lunedi', 'martedi']);
|
||||
final updated =
|
||||
plan.withUpdatedDay('lunedi', DayPlan(colazione: ['latte']));
|
||||
expect(updated.days['lunedi']!.colazione, ['latte']);
|
||||
expect(updated.days['martedi']!.colazione, isEmpty);
|
||||
});
|
||||
|
||||
test('JSON round-trip preserva tutti i dati', () {
|
||||
final original = MealPlan({
|
||||
'lunedi': DayPlan(colazione: ['latte', 'biscotti'], cena: ['pollo']),
|
||||
'martedi': DayPlan(pranzo: ['pasta al pomodoro']),
|
||||
});
|
||||
final restored = MealPlan.fromJsonString(original.toJsonString());
|
||||
expect(restored.days['lunedi']!.colazione, ['latte', 'biscotti']);
|
||||
expect(restored.days['lunedi']!.cena, ['pollo']);
|
||||
expect(restored.days['martedi']!.pranzo, ['pasta al pomodoro']);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:biteplan/features/meal_planner/providers/meal_planner_provider.dart';
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
});
|
||||
|
||||
test('inizia con piano vuoto', () async {
|
||||
final provider = MealPlannerProvider();
|
||||
await provider.load();
|
||||
expect(provider.plan.hasAnyMeal, false);
|
||||
});
|
||||
|
||||
test('addItem aggiunge nello slot corretto', () async {
|
||||
final provider = MealPlannerProvider();
|
||||
await provider.load();
|
||||
provider.addItem('lunedi', 'colazione', 'latte');
|
||||
expect(provider.plan.days['lunedi']!.colazione, ['latte']);
|
||||
expect(provider.plan.days['lunedi']!.pranzo, isEmpty);
|
||||
});
|
||||
|
||||
test('addItem aggiunge in tutti e tre gli slot', () async {
|
||||
final provider = MealPlannerProvider();
|
||||
await provider.load();
|
||||
provider.addItem('martedi', 'colazione', 'yogurt');
|
||||
provider.addItem('martedi', 'pranzo', 'pasta');
|
||||
provider.addItem('martedi', 'cena', 'pollo');
|
||||
expect(provider.plan.days['martedi']!.colazione, ['yogurt']);
|
||||
expect(provider.plan.days['martedi']!.pranzo, ['pasta']);
|
||||
expect(provider.plan.days['martedi']!.cena, ['pollo']);
|
||||
});
|
||||
|
||||
test('addItem ignora testo vuoto o solo spazi', () async {
|
||||
final provider = MealPlannerProvider();
|
||||
await provider.load();
|
||||
provider.addItem('lunedi', 'colazione', ' ');
|
||||
provider.addItem('lunedi', 'colazione', '');
|
||||
expect(provider.plan.days['lunedi']!.colazione, isEmpty);
|
||||
});
|
||||
|
||||
test('addItem esegue trim del testo', () async {
|
||||
final provider = MealPlannerProvider();
|
||||
await provider.load();
|
||||
provider.addItem('lunedi', 'pranzo', ' pasta ');
|
||||
expect(provider.plan.days['lunedi']!.pranzo, ['pasta']);
|
||||
});
|
||||
|
||||
test('removeItem rimuove dalla posizione corretta', () async {
|
||||
final provider = MealPlannerProvider();
|
||||
await provider.load();
|
||||
provider.addItem('lunedi', 'pranzo', 'pasta');
|
||||
provider.addItem('lunedi', 'pranzo', 'insalata');
|
||||
provider.addItem('lunedi', 'pranzo', 'frutta');
|
||||
provider.removeItem('lunedi', 'pranzo', 1);
|
||||
expect(provider.plan.days['lunedi']!.pranzo, ['pasta', 'frutta']);
|
||||
});
|
||||
|
||||
test('clearAll svuota tutti i giorni', () async {
|
||||
final provider = MealPlannerProvider();
|
||||
await provider.load();
|
||||
provider.addItem('lunedi', 'colazione', 'latte');
|
||||
provider.addItem('venerdi', 'cena', 'pesce');
|
||||
provider.clearAll();
|
||||
expect(provider.plan.hasAnyMeal, false);
|
||||
});
|
||||
|
||||
test('persiste e ricarica i dati', () async {
|
||||
final provider1 = MealPlannerProvider();
|
||||
await provider1.load();
|
||||
provider1.addItem('lunedi', 'cena', 'pollo');
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
final provider2 = MealPlannerProvider();
|
||||
await provider2.load();
|
||||
expect(provider2.plan.days['lunedi']!.cena, ['pollo']);
|
||||
});
|
||||
|
||||
test('notifica i listener dopo addItem', () async {
|
||||
final provider = MealPlannerProvider();
|
||||
await provider.load();
|
||||
var notified = false;
|
||||
provider.addListener(() => notified = true);
|
||||
provider.addItem('lunedi', 'colazione', 'latte');
|
||||
expect(notified, true);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:biteplan/features/meal_planner/presentation/widgets/meal_card.dart';
|
||||
import 'package:biteplan/features/meal_planner/models/meal_plan.dart';
|
||||
import '../../../helpers/pump_app.dart';
|
||||
|
||||
Widget _card({
|
||||
DayPlan? dayPlan,
|
||||
bool initiallyExpanded = false,
|
||||
void Function(String, String)? onAdd,
|
||||
void Function(String, int)? onRemove,
|
||||
}) =>
|
||||
Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: MealCard(
|
||||
dayId: 'lunedi',
|
||||
dayLabel: 'Lunedì',
|
||||
dayPlan: dayPlan ?? DayPlan(),
|
||||
initiallyExpanded: initiallyExpanded,
|
||||
onAdd: onAdd ?? (_, __) {},
|
||||
onRemove: onRemove ?? (_, __) {},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
void main() {
|
||||
testWidgets('mostra il nome del giorno', (tester) async {
|
||||
await tester.pumpApp(_card());
|
||||
expect(find.text('Lunedì'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('mostra il conteggio voci nel sottotitolo', (tester) async {
|
||||
await tester.pumpApp(_card(
|
||||
dayPlan: DayPlan(colazione: ['latte', 'caffè'], pranzo: ['pasta']),
|
||||
));
|
||||
expect(find.text('3 voci'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('usa forma singolare per 1 voce', (tester) async {
|
||||
await tester.pumpApp(_card(dayPlan: DayPlan(colazione: ['latte'])));
|
||||
expect(find.text('1 voce'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('nessun sottotitolo quando il giorno è vuoto', (tester) async {
|
||||
await tester.pumpApp(_card());
|
||||
expect(find.text('0 voci'), findsNothing);
|
||||
expect(find.text('voce'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('mostra gli elementi quando espanso', (tester) async {
|
||||
await tester.pumpApp(_card(
|
||||
dayPlan: DayPlan(colazione: ['latte'], cena: ['pollo al forno']),
|
||||
initiallyExpanded: true,
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('latte'), findsOneWidget);
|
||||
expect(find.text('pollo al forno'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('chiama onAdd con slot e testo corretti', (tester) async {
|
||||
String? capturedSlot;
|
||||
String? capturedText;
|
||||
|
||||
await tester.pumpApp(_card(
|
||||
initiallyExpanded: true,
|
||||
onAdd: (slot, text) {
|
||||
capturedSlot = slot;
|
||||
capturedText = text;
|
||||
},
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byType(TextField).first, 'yogurt');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
|
||||
expect(capturedSlot, 'colazione');
|
||||
expect(capturedText, 'yogurt');
|
||||
});
|
||||
|
||||
testWidgets('chiama onRemove quando si preme il pulsante elimina',
|
||||
(tester) async {
|
||||
String? removedSlot;
|
||||
int? removedIndex;
|
||||
|
||||
await tester.pumpApp(_card(
|
||||
dayPlan: DayPlan(colazione: ['latte']),
|
||||
initiallyExpanded: true,
|
||||
onRemove: (slot, index) {
|
||||
removedSlot = slot;
|
||||
removedIndex = index;
|
||||
},
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byIcon(Icons.close).first);
|
||||
await tester.pump();
|
||||
|
||||
expect(removedSlot, 'colazione');
|
||||
expect(removedIndex, 0);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user