test: espandi copertura a 110 test e organizza in gruppi
- Aggiunto converter_provider_test.dart (nuovo): stato iniziale, select, setGrams, result, swapDirection, reset, search - Aggiunto qr_test.dart (nuovo): buildQrPayload e parseMealPlanFromQr con round-trip e casi di errore - meal_planner_provider_test: group(), importPlan(), fallback JSON corrotto - shopping_list_provider_test: group(), addAll con aggregazione quantity, fallback JSON corrotto - shopping_item_test: campi quantity (default, copyWith, JSON round-trip) - shopping_item_tile_test: group(), visualizzazione (xN) - conversion_entry_test: caso limite yieldFactor=0 - meal_card_test: group() - Rimosso widget_test.dart (boilerplate obsoleto) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -61,5 +61,14 @@ void main() {
|
|||||||
expect(entry.rawToCooked(0), 0.0);
|
expect(entry.rawToCooked(0), 0.0);
|
||||||
expect(entry.cookedToRaw(0), 0.0);
|
expect(entry.cookedToRaw(0), 0.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('yield = 0: rawToCooked restituisce 0', () {
|
||||||
|
const entry = ConversionEntry(
|
||||||
|
food: 'test',
|
||||||
|
method: 'test',
|
||||||
|
yieldFactor: 0.0,
|
||||||
|
);
|
||||||
|
expect(entry.rawToCooked(100), 0.0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:biteplan/features/converter/providers/converter_provider.dart';
|
||||||
|
import 'package:biteplan/features/converter/models/conversion_entry.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ConverterProvider', () {
|
||||||
|
late ConverterProvider provider;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
provider = ConverterProvider();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('stato iniziale', () {
|
||||||
|
test('results è vuoto', () {
|
||||||
|
expect(provider.results, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('selected è null', () {
|
||||||
|
expect(provider.selected, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('grams è null', () {
|
||||||
|
expect(provider.grams, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rawToCooked è true', () {
|
||||||
|
expect(provider.rawToCooked, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('result è null', () {
|
||||||
|
expect(provider.result, isNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('select()', () {
|
||||||
|
const entry = ConversionEntry(food: 'riso', method: 'bollitura', yieldFactor: 3.0);
|
||||||
|
|
||||||
|
test('imposta l\'alimento selezionato', () {
|
||||||
|
provider.select(entry);
|
||||||
|
expect(provider.selected, entry);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('azzera i grammi', () {
|
||||||
|
provider.setGrams(100);
|
||||||
|
provider.select(entry);
|
||||||
|
expect(provider.grams, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('svuota i risultati', () {
|
||||||
|
provider.select(entry);
|
||||||
|
expect(provider.results, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notifica i listener', () {
|
||||||
|
var notified = false;
|
||||||
|
provider.addListener(() => notified = true);
|
||||||
|
provider.select(entry);
|
||||||
|
expect(notified, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('setGrams()', () {
|
||||||
|
test('aggiorna il valore', () {
|
||||||
|
provider.setGrams(150.0);
|
||||||
|
expect(provider.grams, 150.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('accetta null', () {
|
||||||
|
provider.setGrams(100.0);
|
||||||
|
provider.setGrams(null);
|
||||||
|
expect(provider.grams, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notifica i listener', () {
|
||||||
|
var notified = false;
|
||||||
|
provider.addListener(() => notified = true);
|
||||||
|
provider.setGrams(100.0);
|
||||||
|
expect(notified, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('result', () {
|
||||||
|
const entry = ConversionEntry(food: 'riso', method: 'bollitura', yieldFactor: 3.0);
|
||||||
|
|
||||||
|
test('null se nessun alimento selezionato', () {
|
||||||
|
provider.setGrams(100.0);
|
||||||
|
expect(provider.result, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null se grammi non impostati', () {
|
||||||
|
provider.select(entry);
|
||||||
|
expect(provider.result, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null se grammi sono 0', () {
|
||||||
|
provider.select(entry);
|
||||||
|
provider.setGrams(0.0);
|
||||||
|
expect(provider.result, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null se grammi sono negativi', () {
|
||||||
|
provider.select(entry);
|
||||||
|
provider.setGrams(-10.0);
|
||||||
|
expect(provider.result, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('calcola crudo→cotto', () {
|
||||||
|
provider.select(entry);
|
||||||
|
provider.setGrams(100.0);
|
||||||
|
expect(provider.result, closeTo(300.0, 0.05));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('calcola cotto→crudo dopo swapDirection', () {
|
||||||
|
provider.select(entry);
|
||||||
|
provider.swapDirection();
|
||||||
|
provider.setGrams(300.0);
|
||||||
|
expect(provider.result, closeTo(100.0, 0.05));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('arrotonda a 1 decimale', () {
|
||||||
|
const e = ConversionEntry(food: 'pollo', method: 'forno', yieldFactor: 0.67);
|
||||||
|
provider.select(e);
|
||||||
|
provider.setGrams(33.0);
|
||||||
|
// 33 * 0.67 = 22.11 → arrotondato a 22.1
|
||||||
|
expect(provider.result, closeTo(22.1, 0.05));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('swapDirection()', () {
|
||||||
|
test('inverte la direzione', () {
|
||||||
|
expect(provider.rawToCooked, true);
|
||||||
|
provider.swapDirection();
|
||||||
|
expect(provider.rawToCooked, false);
|
||||||
|
provider.swapDirection();
|
||||||
|
expect(provider.rawToCooked, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('azzera i grammi', () {
|
||||||
|
provider.setGrams(100.0);
|
||||||
|
provider.swapDirection();
|
||||||
|
expect(provider.grams, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notifica i listener', () {
|
||||||
|
var notified = false;
|
||||||
|
provider.addListener(() => notified = true);
|
||||||
|
provider.swapDirection();
|
||||||
|
expect(notified, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('reset()', () {
|
||||||
|
test('azzera tutti i campi', () {
|
||||||
|
provider.select(const ConversionEntry(food: 'riso', method: 'bollitura', yieldFactor: 3.0));
|
||||||
|
provider.setGrams(100.0);
|
||||||
|
provider.swapDirection();
|
||||||
|
provider.reset();
|
||||||
|
expect(provider.selected, isNull);
|
||||||
|
expect(provider.grams, isNull);
|
||||||
|
expect(provider.rawToCooked, true);
|
||||||
|
expect(provider.results, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notifica i listener', () {
|
||||||
|
var notified = false;
|
||||||
|
provider.addListener(() => notified = true);
|
||||||
|
provider.reset();
|
||||||
|
expect(notified, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('search()', () {
|
||||||
|
test('risultati vuoti con query vuota', () {
|
||||||
|
provider.search('');
|
||||||
|
expect(provider.results, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('risultati vuoti con solo spazi', () {
|
||||||
|
provider.search(' ');
|
||||||
|
expect(provider.results, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notifica i listener', () {
|
||||||
|
var notified = false;
|
||||||
|
provider.addListener(() => notified = true);
|
||||||
|
provider.search('riso');
|
||||||
|
expect(notified, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:biteplan/features/meal_planner/providers/meal_planner_provider.dart';
|
import 'package:biteplan/features/meal_planner/providers/meal_planner_provider.dart';
|
||||||
|
import 'package:biteplan/features/meal_planner/models/meal_plan.dart';
|
||||||
|
import 'package:biteplan/core/constants/app_constants.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
setUp(() {
|
setUp(() {
|
||||||
SharedPreferences.setMockInitialValues({});
|
SharedPreferences.setMockInitialValues({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('MealPlannerProvider', () {
|
||||||
test('inizia con piano vuoto', () async {
|
test('inizia con piano vuoto', () async {
|
||||||
final provider = MealPlannerProvider();
|
final provider = MealPlannerProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
@@ -85,4 +88,53 @@ void main() {
|
|||||||
provider.addItem('lunedi', 'colazione', 'latte');
|
provider.addItem('lunedi', 'colazione', 'latte');
|
||||||
expect(notified, true);
|
expect(notified, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('importPlan()', () {
|
||||||
|
test('sovrascrive il piano corrente', () async {
|
||||||
|
final provider = MealPlannerProvider();
|
||||||
|
await provider.load();
|
||||||
|
provider.addItem('lunedi', 'colazione', 'latte');
|
||||||
|
|
||||||
|
final newPlan = MealPlan({
|
||||||
|
...Map.fromEntries(kDayIds.map((d) => MapEntry(d, DayPlan()))),
|
||||||
|
'martedi': DayPlan(pranzo: ['pasta']),
|
||||||
|
});
|
||||||
|
provider.importPlan(newPlan);
|
||||||
|
|
||||||
|
expect(provider.plan.days['lunedi']!.colazione, isEmpty);
|
||||||
|
expect(provider.plan.days['martedi']!.pranzo, ['pasta']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('persiste il piano importato', () async {
|
||||||
|
final provider1 = MealPlannerProvider();
|
||||||
|
await provider1.load();
|
||||||
|
final newPlan = MealPlan({
|
||||||
|
...Map.fromEntries(kDayIds.map((d) => MapEntry(d, DayPlan()))),
|
||||||
|
'mercoledi': DayPlan(cena: ['pesce']),
|
||||||
|
});
|
||||||
|
provider1.importPlan(newPlan);
|
||||||
|
await Future.delayed(const Duration(milliseconds: 50));
|
||||||
|
|
||||||
|
final provider2 = MealPlannerProvider();
|
||||||
|
await provider2.load();
|
||||||
|
expect(provider2.plan.days['mercoledi']!.cena, ['pesce']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notifica i listener', () async {
|
||||||
|
final provider = MealPlannerProvider();
|
||||||
|
await provider.load();
|
||||||
|
var notified = false;
|
||||||
|
provider.addListener(() => notified = true);
|
||||||
|
provider.importPlan(MealPlan.empty(kDayIds));
|
||||||
|
expect(notified, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('load gestisce JSON corrotto con piano vuoto', () async {
|
||||||
|
SharedPreferences.setMockInitialValues({'meals': 'json non valido {'});
|
||||||
|
final provider = MealPlannerProvider();
|
||||||
|
await provider.load();
|
||||||
|
expect(provider.plan.hasAnyMeal, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:biteplan/features/meal_planner/qr_codec.dart';
|
||||||
|
import 'package:biteplan/features/meal_planner/models/meal_plan.dart';
|
||||||
|
import 'package:biteplan/core/constants/app_constants.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('buildQrPayload', () {
|
||||||
|
test('genera payload non nullo per piano vuoto', () {
|
||||||
|
final plan = MealPlan.empty(kDayIds);
|
||||||
|
expect(buildQrPayload(plan), isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('il payload contiene versione e dati pasti', () {
|
||||||
|
final plan = MealPlan.empty(kDayIds);
|
||||||
|
final payload = buildQrPayload(plan)!;
|
||||||
|
expect(payload, contains('"v":1'));
|
||||||
|
expect(payload, contains('"meals"'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('il payload include i dati inseriti', () {
|
||||||
|
final plan = MealPlan({
|
||||||
|
...Map.fromEntries(kDayIds.map((d) => MapEntry(d, DayPlan()))),
|
||||||
|
'lunedi': DayPlan(colazione: ['latte', 'biscotti'], pranzo: ['pasta']),
|
||||||
|
});
|
||||||
|
final payload = buildQrPayload(plan)!;
|
||||||
|
expect(payload, contains('latte'));
|
||||||
|
expect(payload, contains('biscotti'));
|
||||||
|
expect(payload, contains('pasta'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('restituisce null se payload supera 2953 byte', () {
|
||||||
|
final longName = 'a' * 400;
|
||||||
|
final plan = MealPlan(Map.fromEntries(kDayIds.map((d) => MapEntry(
|
||||||
|
d,
|
||||||
|
DayPlan(
|
||||||
|
colazione: List.generate(5, (_) => longName),
|
||||||
|
pranzo: List.generate(5, (_) => longName),
|
||||||
|
cena: List.generate(5, (_) => longName),
|
||||||
|
),
|
||||||
|
))));
|
||||||
|
expect(buildQrPayload(plan), isNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('parseMealPlanFromQr', () {
|
||||||
|
test('round-trip: parse di payload generato da buildQrPayload', () {
|
||||||
|
final plan = MealPlan.empty(kDayIds);
|
||||||
|
final payload = buildQrPayload(plan)!;
|
||||||
|
final (parsed, error) = parseMealPlanFromQr(payload);
|
||||||
|
expect(error, isNull);
|
||||||
|
expect(parsed, isNotNull);
|
||||||
|
for (final day in kDayIds) {
|
||||||
|
expect(parsed!.days.containsKey(day), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('round-trip preserva tutti i pasti', () {
|
||||||
|
final original = MealPlan({
|
||||||
|
...Map.fromEntries(kDayIds.map((d) => MapEntry(d, DayPlan()))),
|
||||||
|
'lunedi': DayPlan(colazione: ['latte'], pranzo: ['pasta'], cena: ['pollo']),
|
||||||
|
'martedi': DayPlan(cena: ['pesce']),
|
||||||
|
});
|
||||||
|
final payload = buildQrPayload(original)!;
|
||||||
|
final (parsed, error) = parseMealPlanFromQr(payload);
|
||||||
|
|
||||||
|
expect(error, isNull);
|
||||||
|
expect(parsed!.days['lunedi']!.colazione, ['latte']);
|
||||||
|
expect(parsed.days['lunedi']!.pranzo, ['pasta']);
|
||||||
|
expect(parsed.days['lunedi']!.cena, ['pollo']);
|
||||||
|
expect(parsed.days['martedi']!.cena, ['pesce']);
|
||||||
|
expect(parsed.days['mercoledi']!.colazione, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('errore su stringa non JSON', () {
|
||||||
|
final (plan, error) = parseMealPlanFromQr('testo non json');
|
||||||
|
expect(plan, isNull);
|
||||||
|
expect(error, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('errore se versione non è 1', () {
|
||||||
|
final (plan, error) = parseMealPlanFromQr('{"v":2,"meals":{}}');
|
||||||
|
expect(plan, isNull);
|
||||||
|
expect(error, contains('non valido'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('errore se meals è una lista invece di una mappa', () {
|
||||||
|
final (plan, error) = parseMealPlanFromQr('{"v":1,"meals":[]}');
|
||||||
|
expect(plan, isNull);
|
||||||
|
expect(error, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('errore se struttura di un giorno non è valida', () {
|
||||||
|
final (plan, error) = parseMealPlanFromQr('{"v":1,"meals":{"lunedi":"stringa"}}');
|
||||||
|
expect(plan, isNull);
|
||||||
|
expect(error, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('errore se campo v è assente', () {
|
||||||
|
final (plan, error) = parseMealPlanFromQr('{"meals":{}}');
|
||||||
|
expect(plan, isNull);
|
||||||
|
expect(error, isNotNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ Widget _card({
|
|||||||
);
|
);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
group('MealCard', () {
|
||||||
testWidgets('mostra il nome del giorno', (tester) async {
|
testWidgets('mostra il nome del giorno', (tester) async {
|
||||||
await tester.pumpApp(_card());
|
await tester.pumpApp(_card());
|
||||||
expect(find.text('Lunedì'), findsOneWidget);
|
expect(find.text('Lunedì'), findsOneWidget);
|
||||||
@@ -99,4 +100,5 @@ void main() {
|
|||||||
expect(removedSlot, 'colazione');
|
expect(removedSlot, 'colazione');
|
||||||
expect(removedIndex, 0);
|
expect(removedIndex, 0);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ void main() {
|
|||||||
expect(item.checked, false);
|
expect(item.checked, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('quantity è 1 di default', () {
|
||||||
|
final item = ShoppingItem(id: '1', name: 'Pollo');
|
||||||
|
expect(item.quantity, 1);
|
||||||
|
});
|
||||||
|
|
||||||
test('copyWith() aggiorna solo il campo specificato', () {
|
test('copyWith() aggiorna solo il campo specificato', () {
|
||||||
final item = ShoppingItem(id: '1', name: 'Pollo');
|
final item = ShoppingItem(id: '1', name: 'Pollo');
|
||||||
final checked = item.copyWith(checked: true);
|
final checked = item.copyWith(checked: true);
|
||||||
@@ -16,18 +21,26 @@ void main() {
|
|||||||
expect(checked.id, '1');
|
expect(checked.id, '1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('copyWith() aggiorna quantity', () {
|
||||||
|
final item = ShoppingItem(id: '1', name: 'Zucchine');
|
||||||
|
final updated = item.copyWith(quantity: 3);
|
||||||
|
expect(updated.quantity, 3);
|
||||||
|
expect(updated.name, 'Zucchine');
|
||||||
|
});
|
||||||
|
|
||||||
test('copyWith() non modifica lorigine', () {
|
test('copyWith() non modifica lorigine', () {
|
||||||
final item = ShoppingItem(id: '1', name: 'Pollo');
|
final item = ShoppingItem(id: '1', name: 'Pollo');
|
||||||
item.copyWith(checked: true);
|
item.copyWith(checked: true);
|
||||||
expect(item.checked, false);
|
expect(item.checked, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('JSON round-trip preserva tutti i campi', () {
|
test('JSON round-trip preserva tutti i campi incluso quantity', () {
|
||||||
const item = ShoppingItem(id: '42', name: 'Riso', checked: true);
|
const item = ShoppingItem(id: '42', name: 'Riso', checked: true, quantity: 3);
|
||||||
final restored = ShoppingItem.fromJson(item.toJson());
|
final restored = ShoppingItem.fromJson(item.toJson());
|
||||||
expect(restored.id, '42');
|
expect(restored.id, '42');
|
||||||
expect(restored.name, 'Riso');
|
expect(restored.name, 'Riso');
|
||||||
expect(restored.checked, true);
|
expect(restored.checked, true);
|
||||||
|
expect(restored.quantity, 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fromJson gestisce campo checked assente (default false)', () {
|
test('fromJson gestisce campo checked assente (default false)', () {
|
||||||
@@ -35,12 +48,18 @@ void main() {
|
|||||||
expect(item.checked, false);
|
expect(item.checked, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('toJson produce la mappa corretta', () {
|
test('fromJson gestisce campo quantity assente (default 1)', () {
|
||||||
const item = ShoppingItem(id: '5', name: 'Zucchine', checked: false);
|
final item = ShoppingItem.fromJson({'id': '1', 'name': 'Pasta', 'checked': false});
|
||||||
|
expect(item.quantity, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toJson produce la mappa corretta con quantity', () {
|
||||||
|
const item = ShoppingItem(id: '5', name: 'Zucchine', checked: false, quantity: 2);
|
||||||
final json = item.toJson();
|
final json = item.toJson();
|
||||||
expect(json['id'], '5');
|
expect(json['id'], '5');
|
||||||
expect(json['name'], 'Zucchine');
|
expect(json['name'], 'Zucchine');
|
||||||
expect(json['checked'], false);
|
expect(json['checked'], false);
|
||||||
|
expect(json['quantity'], 2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ void main() {
|
|||||||
SharedPreferences.setMockInitialValues({});
|
SharedPreferences.setMockInitialValues({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('ShoppingListProvider', () {
|
||||||
test('inizia vuota', () async {
|
test('inizia vuota', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
expect(provider.items, isEmpty);
|
expect(provider.items, isEmpty);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('add() aggiunge un elemento', () async {
|
group('add()', () {
|
||||||
|
test('aggiunge un elemento', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
provider.add('Pollo');
|
provider.add('Pollo');
|
||||||
@@ -22,7 +24,7 @@ void main() {
|
|||||||
expect(provider.items.first.checked, false);
|
expect(provider.items.first.checked, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('add() ignora stringa vuota o solo spazi', () async {
|
test('ignora stringa vuota o solo spazi', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
provider.add(' ');
|
provider.add(' ');
|
||||||
@@ -30,14 +32,16 @@ void main() {
|
|||||||
expect(provider.items, isEmpty);
|
expect(provider.items, isEmpty);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('add() esegue trim del testo', () async {
|
test('esegue trim del testo', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
provider.add(' Riso ');
|
provider.add(' Riso ');
|
||||||
expect(provider.items.first.name, 'Riso');
|
expect(provider.items.first.name, 'Riso');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('toggle() inverte lo stato checked', () async {
|
group('toggle()', () {
|
||||||
|
test('inverte lo stato checked', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
provider.add('Riso');
|
provider.add('Riso');
|
||||||
@@ -47,8 +51,10 @@ void main() {
|
|||||||
provider.toggle(id);
|
provider.toggle(id);
|
||||||
expect(provider.items.first.checked, false);
|
expect(provider.items.first.checked, false);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('pendingItems e checkedItems separano correttamente', () async {
|
group('pendingItems / checkedItems', () {
|
||||||
|
test('separano correttamente', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
provider.add('Riso');
|
provider.add('Riso');
|
||||||
@@ -59,8 +65,10 @@ void main() {
|
|||||||
expect(provider.pendingItems.length, 1);
|
expect(provider.pendingItems.length, 1);
|
||||||
expect(provider.checkedItems.length, 2);
|
expect(provider.checkedItems.length, 2);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('remove() elimina per id', () async {
|
group('remove()', () {
|
||||||
|
test('elimina per id', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
provider.add('Pollo');
|
provider.add('Pollo');
|
||||||
@@ -70,8 +78,10 @@ void main() {
|
|||||||
expect(provider.items.length, 1);
|
expect(provider.items.length, 1);
|
||||||
expect(provider.items.first.name, 'Riso');
|
expect(provider.items.first.name, 'Riso');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('addAll() non aggiunge duplicati (case-insensitive)', () async {
|
group('addAll()', () {
|
||||||
|
test('non aggiunge duplicati (case-insensitive)', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
provider.add('Pollo');
|
provider.add('Pollo');
|
||||||
@@ -79,7 +89,7 @@ void main() {
|
|||||||
expect(provider.items.length, 3); // Pollo, Riso, Pasta
|
expect(provider.items.length, 3); // Pollo, Riso, Pasta
|
||||||
});
|
});
|
||||||
|
|
||||||
test('addAll() ignora elementi già presenti', () async {
|
test('ignora elementi già presenti', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
provider.add('Riso');
|
provider.add('Riso');
|
||||||
@@ -87,7 +97,36 @@ void main() {
|
|||||||
expect(provider.items.length, 2);
|
expect(provider.items.length, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clearAll() svuota la lista', () async {
|
test('aggrega duplicati con quantity', () async {
|
||||||
|
final provider = ShoppingListProvider();
|
||||||
|
await provider.load();
|
||||||
|
provider.addAll(['Zucchine', 'Pollo', 'Zucchine', 'Pollo', 'Pollo']);
|
||||||
|
final zucchine = provider.items.firstWhere((i) => i.name.toLowerCase() == 'zucchine');
|
||||||
|
final pollo = provider.items.firstWhere((i) => i.name.toLowerCase() == 'pollo');
|
||||||
|
expect(zucchine.quantity, 2);
|
||||||
|
expect(pollo.quantity, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('imposta quantity=1 per elementi non duplicati', () async {
|
||||||
|
final provider = ShoppingListProvider();
|
||||||
|
await provider.load();
|
||||||
|
provider.addAll(['Pasta', 'Riso']);
|
||||||
|
for (final item in provider.items) {
|
||||||
|
expect(item.quantity, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('non aggiunge se tutti già presenti', () async {
|
||||||
|
final provider = ShoppingListProvider();
|
||||||
|
await provider.load();
|
||||||
|
provider.add('Riso');
|
||||||
|
provider.addAll(['riso']);
|
||||||
|
expect(provider.items.length, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('clearAll()', () {
|
||||||
|
test('svuota la lista', () async {
|
||||||
final provider = ShoppingListProvider();
|
final provider = ShoppingListProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
provider.add('Pollo');
|
provider.add('Pollo');
|
||||||
@@ -95,6 +134,7 @@ void main() {
|
|||||||
provider.clearAll();
|
provider.clearAll();
|
||||||
expect(provider.items, isEmpty);
|
expect(provider.items, isEmpty);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('persiste e ricarica i dati', () async {
|
test('persiste e ricarica i dati', () async {
|
||||||
final provider1 = ShoppingListProvider();
|
final provider1 = ShoppingListProvider();
|
||||||
@@ -106,4 +146,12 @@ void main() {
|
|||||||
await provider2.load();
|
await provider2.load();
|
||||||
expect(provider2.items.first.name, 'Merluzzo');
|
expect(provider2.items.first.name, 'Merluzzo');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('load gestisce JSON corrotto con lista vuota', () async {
|
||||||
|
SharedPreferences.setMockInitialValues({'shopping_list': 'json non valido {'});
|
||||||
|
final provider = ShoppingListProvider();
|
||||||
|
await provider.load();
|
||||||
|
expect(provider.items, isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ Widget _tile({
|
|||||||
);
|
);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
group('ShoppingItemTile', () {
|
||||||
testWidgets('mostra il nome dell\'elemento', (tester) async {
|
testWidgets('mostra il nome dell\'elemento', (tester) async {
|
||||||
await tester.pumpApp(_tile(item: const ShoppingItem(id: '1', name: 'Pollo')));
|
await tester.pumpApp(_tile(item: const ShoppingItem(id: '1', name: 'Pollo')));
|
||||||
expect(find.text('Pollo'), findsOneWidget);
|
expect(find.text('Pollo'), findsOneWidget);
|
||||||
@@ -62,4 +63,22 @@ void main() {
|
|||||||
final textWidget = tester.widget<Text>(find.text('Tonno'));
|
final textWidget = tester.widget<Text>(find.text('Tonno'));
|
||||||
expect(textWidget.style?.decoration, TextDecoration.lineThrough);
|
expect(textWidget.style?.decoration, TextDecoration.lineThrough);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('non mostra indicatore quantità quando quantity è 1', (tester) async {
|
||||||
|
await tester.pumpApp(_tile(item: const ShoppingItem(id: '1', name: 'Riso')));
|
||||||
|
expect(find.text('(x1)'), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('mostra (x2) quando quantity è 2', (tester) async {
|
||||||
|
await tester.pumpApp(
|
||||||
|
_tile(item: const ShoppingItem(id: '1', name: 'Zucchine', quantity: 2)));
|
||||||
|
expect(find.text('(x2)'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('mostra (xN) per quantità maggiori', (tester) async {
|
||||||
|
await tester.pumpApp(
|
||||||
|
_tile(item: const ShoppingItem(id: '1', name: 'Pollo', quantity: 5)));
|
||||||
|
expect(find.text('(x5)'), findsOneWidget);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user