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:
2026-05-16 17:41:38 +02:00
parent eb7912421c
commit 9a865a1479
9 changed files with 809 additions and 0 deletions
@@ -0,0 +1,46 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:biteplan/features/shopping_list/models/shopping_item.dart';
void main() {
group('ShoppingItem', () {
test('checked è false di default', () {
final item = ShoppingItem(id: '1', name: 'Pollo');
expect(item.checked, false);
});
test('copyWith() aggiorna solo il campo specificato', () {
final item = ShoppingItem(id: '1', name: 'Pollo');
final checked = item.copyWith(checked: true);
expect(checked.checked, true);
expect(checked.name, 'Pollo');
expect(checked.id, '1');
});
test('copyWith() non modifica lorigine', () {
final item = ShoppingItem(id: '1', name: 'Pollo');
item.copyWith(checked: true);
expect(item.checked, false);
});
test('JSON round-trip preserva tutti i campi', () {
const item = ShoppingItem(id: '42', name: 'Riso', checked: true);
final restored = ShoppingItem.fromJson(item.toJson());
expect(restored.id, '42');
expect(restored.name, 'Riso');
expect(restored.checked, true);
});
test('fromJson gestisce campo checked assente (default false)', () {
final item = ShoppingItem.fromJson({'id': '1', 'name': 'Pasta'});
expect(item.checked, false);
});
test('toJson produce la mappa corretta', () {
const item = ShoppingItem(id: '5', name: 'Zucchine', checked: false);
final json = item.toJson();
expect(json['id'], '5');
expect(json['name'], 'Zucchine');
expect(json['checked'], false);
});
});
}
@@ -0,0 +1,109 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:biteplan/features/shopping_list/providers/shopping_list_provider.dart';
void main() {
setUp(() {
SharedPreferences.setMockInitialValues({});
});
test('inizia vuota', () async {
final provider = ShoppingListProvider();
await provider.load();
expect(provider.items, isEmpty);
});
test('add() aggiunge un elemento', () async {
final provider = ShoppingListProvider();
await provider.load();
provider.add('Pollo');
expect(provider.items.length, 1);
expect(provider.items.first.name, 'Pollo');
expect(provider.items.first.checked, false);
});
test('add() ignora stringa vuota o solo spazi', () async {
final provider = ShoppingListProvider();
await provider.load();
provider.add(' ');
provider.add('');
expect(provider.items, isEmpty);
});
test('add() esegue trim del testo', () async {
final provider = ShoppingListProvider();
await provider.load();
provider.add(' Riso ');
expect(provider.items.first.name, 'Riso');
});
test('toggle() inverte lo stato checked', () async {
final provider = ShoppingListProvider();
await provider.load();
provider.add('Riso');
final id = provider.items.first.id;
provider.toggle(id);
expect(provider.items.first.checked, true);
provider.toggle(id);
expect(provider.items.first.checked, false);
});
test('pendingItems e checkedItems separano correttamente', () async {
final provider = ShoppingListProvider();
await provider.load();
provider.add('Riso');
provider.add('Pasta');
provider.add('Pollo');
provider.toggle(provider.items[0].id);
provider.toggle(provider.items[2].id);
expect(provider.pendingItems.length, 1);
expect(provider.checkedItems.length, 2);
});
test('remove() elimina per id', () async {
final provider = ShoppingListProvider();
await provider.load();
provider.add('Pollo');
provider.add('Riso');
final id = provider.items.first.id;
provider.remove(id);
expect(provider.items.length, 1);
expect(provider.items.first.name, 'Riso');
});
test('addAll() non aggiunge duplicati (case-insensitive)', () async {
final provider = ShoppingListProvider();
await provider.load();
provider.add('Pollo');
provider.addAll(['pollo', 'Riso', 'RISO', 'Pasta']);
expect(provider.items.length, 3); // Pollo, Riso, Pasta
});
test('addAll() ignora elementi già presenti', () async {
final provider = ShoppingListProvider();
await provider.load();
provider.add('Riso');
provider.addAll(['Riso', 'Pasta']);
expect(provider.items.length, 2);
});
test('clearAll() svuota la lista', () async {
final provider = ShoppingListProvider();
await provider.load();
provider.add('Pollo');
provider.add('Riso');
provider.clearAll();
expect(provider.items, isEmpty);
});
test('persiste e ricarica i dati', () async {
final provider1 = ShoppingListProvider();
await provider1.load();
provider1.add('Merluzzo');
await Future.delayed(const Duration(milliseconds: 50));
final provider2 = ShoppingListProvider();
await provider2.load();
expect(provider2.items.first.name, 'Merluzzo');
});
}
@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:biteplan/features/shopping_list/presentation/widgets/shopping_item_tile.dart';
import 'package:biteplan/features/shopping_list/models/shopping_item.dart';
import '../../../helpers/pump_app.dart';
Widget _tile({
required ShoppingItem item,
VoidCallback? onToggle,
VoidCallback? onRemove,
}) =>
Scaffold(
body: ShoppingItemTile(
item: item,
onToggle: onToggle ?? () {},
onRemove: onRemove ?? () {},
),
);
void main() {
testWidgets('mostra il nome dell\'elemento', (tester) async {
await tester.pumpApp(_tile(item: const ShoppingItem(id: '1', name: 'Pollo')));
expect(find.text('Pollo'), findsOneWidget);
});
testWidgets('checkbox non spuntato quando checked è false', (tester) async {
await tester.pumpApp(_tile(item: const ShoppingItem(id: '1', name: 'Riso')));
final checkbox = tester.widget<Checkbox>(find.byType(Checkbox));
expect(checkbox.value, false);
});
testWidgets('checkbox spuntato quando checked è true', (tester) async {
await tester.pumpApp(
_tile(item: const ShoppingItem(id: '1', name: 'Riso', checked: true)));
final checkbox = tester.widget<Checkbox>(find.byType(Checkbox));
expect(checkbox.value, true);
});
testWidgets('chiama onToggle al tap sul checkbox', (tester) async {
var toggled = false;
await tester.pumpApp(_tile(
item: const ShoppingItem(id: '1', name: 'Pollo'),
onToggle: () => toggled = true,
));
await tester.tap(find.byType(Checkbox));
expect(toggled, true);
});
testWidgets('chiama onRemove al tap sul pulsante chiudi', (tester) async {
var removed = false;
await tester.pumpApp(_tile(
item: const ShoppingItem(id: '1', name: 'Pasta'),
onRemove: () => removed = true,
));
await tester.tap(find.byIcon(Icons.close));
expect(removed, true);
});
testWidgets('testo barrato quando elemento è completato', (tester) async {
await tester.pumpApp(
_tile(item: const ShoppingItem(id: '1', name: 'Tonno', checked: true)));
final textWidget = tester.widget<Text>(find.text('Tonno'));
expect(textWidget.style?.decoration, TextDecoration.lineThrough);
});
}