mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
fix tests, introduce golden tests
to make golden tests work, we no longer plot the "now time" for "other logs" this was pretty silly anyway...
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,6 +30,8 @@
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
**/failures/*.png
|
||||
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
@@ -64,7 +64,6 @@ class Meal {
|
||||
|
||||
this.mealItems = mealItems ?? [];
|
||||
this.diaryEntries = diaryEntries ?? [];
|
||||
time = time ?? TimeOfDay.fromDateTime(DateTime.now());
|
||||
this.name = name ?? '';
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,6 @@ class NutritionalPlan {
|
||||
id: PSEUDO_MEAL_ID,
|
||||
plan: id,
|
||||
name: name,
|
||||
time: null,
|
||||
diaryEntries: diaryEntries.where((e) => e.mealId == null).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ class FlNutritionalPlanGoalWidget extends StatefulWidget {
|
||||
// even if it did, i doubt it would let us put text between the gauges/bars
|
||||
// * LinearProgressIndicator has no way to visualize going beyond 100%, or
|
||||
// using multiple colors to show multiple components such as surplus, deficit
|
||||
// * here we try drawing our own simple gauges that can go beyond 100%,and we
|
||||
// can use multiple colors...
|
||||
// * here we draw our own simple gauges that can go beyond 100%,
|
||||
// and support multiple segments
|
||||
class FlNutritionalPlanGoalWidgetState extends State<FlNutritionalPlanGoalWidget> {
|
||||
// normWidth is the width representing 100% completion
|
||||
// note that if val > plan, we will draw beyond this width
|
||||
|
||||
@@ -39,7 +39,7 @@ class MealForm extends StatelessWidget {
|
||||
final _nameController = TextEditingController();
|
||||
|
||||
MealForm(this._planId, [meal]) {
|
||||
_meal = meal ?? Meal(plan: _planId);
|
||||
_meal = meal ?? Meal(plan: _planId, time: TimeOfDay.fromDateTime(DateTime.now()));
|
||||
_timeController.text = timeToString(_meal.time)!;
|
||||
_nameController.text = _meal.name;
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ class MealHeader extends StatelessWidget {
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
_meal.time!.format(context),
|
||||
_meal.time != null ? _meal.time!.format(context) : '',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -607,6 +607,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
golden_toolkit:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: golden_toolkit
|
||||
sha256: "8f74adab33154fe7b731395782797021f97d2edc52f7bfb85ff4f1b5c4a215f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.0"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -82,6 +82,7 @@ dev_dependencies:
|
||||
cider: ^0.2.7
|
||||
drift_dev: ^2.17.0
|
||||
freezed: ^2.5.2
|
||||
golden_toolkit: ^0.15.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
BIN
test/nutrition/goldens/nutritional_plan_1_default_view.png
Normal file
BIN
test/nutrition/goldens/nutritional_plan_1_default_view.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 266 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 263 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
@@ -16,22 +16,112 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/models/nutrition/ingredient.dart';
|
||||
import 'package:wger/providers/auth.dart';
|
||||
import 'package:wger/providers/base_provider.dart';
|
||||
import 'package:wger/providers/body_weight.dart';
|
||||
import 'package:wger/providers/nutrition.dart';
|
||||
import 'package:wger/screens/nutritional_plan_screen.dart';
|
||||
import '../../test_data/nutritional_plans.dart';
|
||||
import '../fixtures/fixture_reader.dart';
|
||||
import 'helper.dart';
|
||||
import 'nutritional_plan_screen_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([WgerBaseProvider, AuthProvider, http.Client])
|
||||
void main() {
|
||||
// makeUrl('nutritionplan', {id: 1, objectMethod: null, query: null})
|
||||
// Add a stub for this method using Mockito's 'when' API, or generate the MockWgerBaseProvider mock with the @GenerateNiceMocks annotation (see https://pub.dev/documentation/mockito/latest/annotations/MockSpec-class.html).)
|
||||
|
||||
late NutritionPlansProvider nutritionProvider;
|
||||
late MockWgerBaseProvider mockWgerBaseProvider;
|
||||
|
||||
setUp(() {
|
||||
mockWgerBaseProvider = MockWgerBaseProvider();
|
||||
nutritionProvider = NutritionPlansProvider(mockWgerBaseProvider, []);
|
||||
|
||||
const String planInfoUrl = 'nutritionplaninfo';
|
||||
const String planUrl = 'nutritionplan';
|
||||
const String diaryUrl = 'nutritiondiary';
|
||||
const String ingredientUrl = 'ingredient';
|
||||
|
||||
final Map<String, dynamic> nutritionalPlanInfoResponse = jsonDecode(
|
||||
fixture('nutrition/nutritional_plan_info_detail_response.json'),
|
||||
);
|
||||
final Map<String, dynamic> nutritionalPlanDetailResponse = jsonDecode(
|
||||
fixture('nutrition/nutritional_plan_detail_response.json'),
|
||||
);
|
||||
final List<dynamic> nutritionDiaryResponse = jsonDecode(
|
||||
fixture('nutrition/nutrition_diary_response.json'),
|
||||
)['results'];
|
||||
final Map<String, dynamic> ingredient59887Response = jsonDecode(
|
||||
fixture('nutrition/ingredient_59887_response.json'),
|
||||
);
|
||||
final Map<String, dynamic> ingredient10065Response = jsonDecode(
|
||||
fixture('nutrition/ingredient_10065_response.json'),
|
||||
);
|
||||
final Map<String, dynamic> ingredient58300Response = jsonDecode(
|
||||
fixture('nutrition/ingredient_58300_response.json'),
|
||||
);
|
||||
|
||||
final ingredientList = [
|
||||
Ingredient.fromJson(ingredient59887Response),
|
||||
Ingredient.fromJson(ingredient10065Response),
|
||||
Ingredient.fromJson(ingredient58300Response),
|
||||
];
|
||||
|
||||
nutritionProvider.ingredients = ingredientList;
|
||||
|
||||
final Uri planInfoUri = Uri(
|
||||
scheme: 'http',
|
||||
host: 'localhost',
|
||||
path: 'api/v2/$planInfoUrl/1',
|
||||
);
|
||||
final Uri planUri = Uri(
|
||||
scheme: 'http',
|
||||
host: 'localhost',
|
||||
path: 'api/v2/$planUrl',
|
||||
);
|
||||
final Uri diaryUri = Uri(
|
||||
scheme: 'http',
|
||||
host: 'localhost',
|
||||
path: 'api/v2/$diaryUrl',
|
||||
);
|
||||
when(mockWgerBaseProvider.makeUrl(planInfoUrl, id: anyNamed('id'))).thenReturn(planInfoUri);
|
||||
when(mockWgerBaseProvider.makeUrl(planUrl, id: anyNamed('id'))).thenReturn(planUri);
|
||||
when(mockWgerBaseProvider.makeUrl(diaryUrl, query: anyNamed('query'))).thenReturn(diaryUri);
|
||||
when(mockWgerBaseProvider.fetch(planInfoUri)).thenAnswer(
|
||||
(realInvocation) => Future.value(nutritionalPlanInfoResponse),
|
||||
);
|
||||
when(mockWgerBaseProvider.fetch(planUri)).thenAnswer(
|
||||
(realInvocation) => Future.value(nutritionalPlanDetailResponse),
|
||||
);
|
||||
when(mockWgerBaseProvider.fetchPaginated(diaryUri)).thenAnswer(
|
||||
(realInvocation) => Future.value(nutritionDiaryResponse),
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
late MockWgerBaseProvider mockBaseProvider;
|
||||
// late ExercisesProvider provider;
|
||||
|
||||
const String npPlanUrl = 'nutritionplan';
|
||||
|
||||
final Uri tNpPlanEntriesUri = Uri(
|
||||
scheme: 'http',
|
||||
host: 'localhost',
|
||||
path: 'api/v2/$npPlanUrl/',
|
||||
);
|
||||
*/
|
||||
Widget createNutritionalPlan({locale = 'en'}) {
|
||||
final key = GlobalKey<NavigatorState>();
|
||||
final mockBaseProvider = MockWgerBaseProvider();
|
||||
@@ -65,23 +155,70 @@ void main() {
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('Test the widgets on the nutritional plan screen', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(createNutritionalPlan());
|
||||
await tester.tap(find.byType(TextButton));
|
||||
await tester.pumpAndSettle();
|
||||
testGoldens(
|
||||
'Test the widgets on the nutritional plan screen',
|
||||
(tester) async {
|
||||
await loadAppFonts();
|
||||
await tester.takeScreenshot(name: 'die1');
|
||||
final globalKey = GlobalKey();
|
||||
await tester.pumpWidgetBuilder(
|
||||
Material(
|
||||
key: globalKey,
|
||||
),
|
||||
wrapper: materialAppWrapper(
|
||||
localizations: [
|
||||
AppLocalizations.delegate,
|
||||
],
|
||||
),
|
||||
surfaceSize: const Size(500, 1000),
|
||||
);
|
||||
await tester.pumpWidget(createNutritionalPlan());
|
||||
await tester.tap(find.byType(TextButton));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Plan description
|
||||
expect(find.text('Less fat, more protein'), findsOneWidget);
|
||||
await screenMatchesGolden(tester, 'nutritional_plan_1_default_view');
|
||||
|
||||
// Ingredients
|
||||
expect(find.text('100g Water'), findsOneWidget);
|
||||
expect(find.text('75g Burger soup'), findsOneWidget);
|
||||
// the new goals widget pushes this content down a bit...
|
||||
await tester.scrollUntilVisible(find.text('300g Broccoli cake'), 30);
|
||||
expect(find.text('300g Broccoli cake'), findsOneWidget);
|
||||
// Default view shows plan description, info button, and no ingredients
|
||||
expect(find.text('Less fat, more protein'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.info_outline), findsNWidgets(3)); // 2 meals, 1 "other logs"
|
||||
expect(find.byIcon(Icons.info), findsNothing);
|
||||
expect(find.text('100g Water'), findsNothing);
|
||||
expect(find.text('75g Burger soup'), findsNothing);
|
||||
|
||||
expect(find.byType(Card), findsNWidgets(2));
|
||||
});
|
||||
// tap the first info button changes it and reveals ingredients for the first meal
|
||||
var infoOutlineButtons = find.byIcon(Icons.info_outline);
|
||||
await tester.tap(infoOutlineButtons.first); // 2nd button shows up also, but is off-screen
|
||||
await tester.pumpAndSettle();
|
||||
await tester.takeScreenshot(name: 'die2');
|
||||
await screenMatchesGolden(tester, 'nutritional_plan_2_one_meal_with_ingredients');
|
||||
|
||||
// Ingredients show up now
|
||||
expect(find.text('100g Water'), findsOneWidget);
|
||||
expect(find.text('75g Burger soup'), findsOneWidget);
|
||||
|
||||
// .. and the button icon has changed
|
||||
expect(find.byIcon(Icons.info_outline), findsNWidgets(2));
|
||||
expect(find.byIcon(Icons.info), findsOneWidget);
|
||||
|
||||
// the goals widget pushes this content down a bit.
|
||||
// let's first find our icon (note: the previous icon no longer matches)
|
||||
infoOutlineButtons = find.byIcon(Icons.info_outline);
|
||||
|
||||
await tester.scrollUntilVisible(infoOutlineButtons.first, 30);
|
||||
expect(find.text('300g Broccoli cake'), findsNothing);
|
||||
|
||||
await tester.tap(infoOutlineButtons.first);
|
||||
await tester.pumpAndSettle();
|
||||
await screenMatchesGolden(tester, 'nutritional_plan_3_both_meals_with_ingredients');
|
||||
expect(find.byIcon(Icons.info_outline), findsOneWidget);
|
||||
expect(find.byIcon(Icons.info), findsNWidgets(2));
|
||||
|
||||
await tester.scrollUntilVisible(find.text('300g Broccoli cake'), 30);
|
||||
expect(find.text('300g Broccoli cake'), findsOneWidget);
|
||||
|
||||
expect(find.byType(Card), findsNWidgets(3));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('Tests the localization of times - EN', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(createNutritionalPlan());
|
||||
|
||||
2
test_data/dart_test.yaml
Normal file
2
test_data/dart_test.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
tags:
|
||||
golden:
|
||||
Reference in New Issue
Block a user