diff --git a/lib/models/nutrition/ingredient.dart b/lib/models/nutrition/ingredient.dart index be64ae5db..d4ee39d59 100644 --- a/lib/models/nutrition/ingredient.dart +++ b/lib/models/nutrition/ingredient.dart @@ -67,6 +67,10 @@ class Ingredient { @JsonKey(required: true) final String name; + /// Brand of the product + @JsonKey(name: 'brand') + final String? brand; + @JsonKey(required: true, name: 'created') final DateTime created; @@ -123,6 +127,7 @@ class Ingredient { required this.id, required this.code, required this.name, + this.brand, required this.created, required this.energy, required this.carbohydrates, diff --git a/lib/models/nutrition/ingredient.g.dart b/lib/models/nutrition/ingredient.g.dart index 39c74cad6..97bae6933 100644 --- a/lib/models/nutrition/ingredient.g.dart +++ b/lib/models/nutrition/ingredient.g.dart @@ -36,6 +36,7 @@ Ingredient _$IngredientFromJson(Map json) { id: (json['id'] as num).toInt(), code: json['code'] as String?, name: json['name'] as String, + brand: json['brand'] as String?, created: DateTime.parse(json['created'] as String), energy: (json['energy'] as num).toInt(), carbohydrates: stringToNum(json['carbohydrates'] as String?), @@ -67,6 +68,7 @@ Map _$IngredientToJson(Ingredient instance) => { : const CircleIconAvatar( Icon(Icons.image, color: Colors.grey), ), - title: Text( - ingredient.name, + title: Text.rich( + TextSpan( + children: [ + TextSpan(text: ingredient.name), + if (ingredient.brand != null && ingredient.brand!.isNotEmpty) + TextSpan( + text: ' ${ingredient.brand}', + style: TextStyle( + color: DefaultTextStyle.of(context).style.color?.withValues(alpha: 0.6), + ), + ), + ], + ), maxLines: 2, overflow: TextOverflow.ellipsis, ), diff --git a/test/nutrition/ingredient_typeahead_test.dart b/test/nutrition/ingredient_typeahead_test.dart index 5c9194a8c..37b023ecf 100644 --- a/test/nutrition/ingredient_typeahead_test.dart +++ b/test/nutrition/ingredient_typeahead_test.dart @@ -174,6 +174,27 @@ void main() { expect(find.text('Vegan'), findsNothing); }); + testWidgets('Shows brand inline in search result tile', (WidgetTester tester) async { + // ingredient3 (Broccoli cake) has brand 'Weightwatchers' + when( + mockNutrition.searchIngredient( + any, + languageCode: anyNamed('languageCode'), + searchLanguage: anyNamed('searchLanguage'), + isVegan: anyNamed('isVegan'), + isVegetarian: anyNamed('isVegetarian'), + nutriscoreMax: anyNamed('nutriscoreMax'), + ), + ).thenAnswer((_) => Future.value([ingredient3])); + + await tester.pumpWidget(createWidgetUnderTest()); + await tester.enterText(find.byType(TextFormField), 'Broccoli'); + await tester.pump(const Duration(milliseconds: 600)); + await tester.pumpAndSettle(); + + expect(find.textContaining('Weightwatchers'), findsOneWidget); + }); + testWidgets('Shows no dietary chips when ingredient has no info', (WidgetTester tester) async { // ingredient2 (Burger soup) has no dietary info when( diff --git a/test/widgets/nutrition/ingredient_dialogs_test.dart b/test/widgets/nutrition/ingredient_dialogs_test.dart index 5a51220e4..91f65a582 100644 --- a/test/widgets/nutrition/ingredient_dialogs_test.dart +++ b/test/widgets/nutrition/ingredient_dialogs_test.dart @@ -22,6 +22,34 @@ import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/ingredient.dart'; import 'package:wger/widgets/nutrition/ingredient_dialogs.dart'; +import '../../../test_data/nutritional_plans.dart'; + +Future pumpIngredientDetailsDialog( + WidgetTester tester, { + required AsyncSnapshot snapshot, +}) async { + await tester.pumpWidget( + MaterialApp( + locale: const Locale('en'), + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: Scaffold( + body: Builder( + builder: (context) => ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (_) => IngredientDetails(snapshot), + ); + }, + child: const Text('Show Dialog'), + ), + ), + ), + ), + ); +} + Future pumpIngredientScanDialog( WidgetTester tester, { required AsyncSnapshot snapshot, @@ -54,6 +82,39 @@ Future pumpIngredientScanDialog( } void main() { + group('IngredientDetails tests', () { + testWidgets('shows brand below name in title when ingredient has a brand', ( + WidgetTester tester, + ) async { + // ingredient3 (Broccoli cake, brand: 'Weightwatchers') + final snapshot = AsyncSnapshot.withData(ConnectionState.done, ingredient3); + + await pumpIngredientDetailsDialog(tester, snapshot: snapshot); + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.byType(AlertDialog), findsOneWidget); + expect(find.text('Broccoli cake'), findsOneWidget); + expect(find.text('Weightwatchers'), findsOneWidget); + }); + + testWidgets('does not show brand in title when ingredient has no brand', ( + WidgetTester tester, + ) async { + // ingredient1 (Water, brand: null) + final snapshot = AsyncSnapshot.withData(ConnectionState.done, ingredient1); + + await pumpIngredientDetailsDialog(tester, snapshot: snapshot); + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.byType(AlertDialog), findsOneWidget); + expect(find.text('Water'), findsOneWidget); + // brand: null must not render as the literal string "null" + expect(find.text('null'), findsNothing); + }); + }); + group('IngredientScanResultDialog tests', () { const testBarcode = '1234567890123'; diff --git a/test_data/nutritional_plans.dart b/test_data/nutritional_plans.dart index 771bbb9cd..2e9fd68ba 100644 --- a/test_data/nutritional_plans.dart +++ b/test_data/nutritional_plans.dart @@ -32,6 +32,7 @@ final ingredient1 = Ingredient( id: 1, code: '123456787', name: 'Water', + brand: null, created: DateTime(2021, 5, 1), energy: 500, carbohydrates: 10, @@ -52,6 +53,7 @@ final ingredient2 = Ingredient( id: 2, code: '123456788', name: 'Burger soup', + brand: null, created: DateTime(2021, 5, 10), energy: 25, carbohydrates: 10, @@ -69,6 +71,7 @@ final ingredient3 = Ingredient( id: 3, code: '123456789', name: 'Broccoli cake', + brand: 'Weightwatchers', created: DateTime(2021, 5, 2), energy: 1200, carbohydrates: 110, @@ -86,6 +89,7 @@ final muesli = Ingredient( id: 1, code: '123456787', name: 'Müsli', + brand: 'Spar Gourmet', created: DateTime(2021, 5, 1), energy: 500, carbohydrates: 10, @@ -106,6 +110,7 @@ final milk = Ingredient( id: 1, code: '123456787', name: 'Milk', + brand: null, created: DateTime(2021, 5, 1), energy: 500, carbohydrates: 10,