mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Merge branch 'master' into feature/trophies
# Conflicts: # lib/main.dart # lib/screens/dashboard.dart
This commit is contained in:
121
test/core/settings_dashboard_visibility_test.dart
Normal file
121
test/core/settings_dashboard_visibility_test.dart
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (c) 2026 wger Team
|
||||
*
|
||||
* wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
|
||||
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
|
||||
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/providers/base_provider.dart';
|
||||
import 'package:wger/providers/user.dart';
|
||||
import 'package:wger/widgets/core/settings/dashboard_visibility.dart';
|
||||
|
||||
import 'settings_dashboard_visibility_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([
|
||||
UserProvider,
|
||||
WgerBaseProvider,
|
||||
])
|
||||
void main() {
|
||||
late UserProvider userProvider;
|
||||
late MockWgerBaseProvider mockBaseProvider;
|
||||
|
||||
setUp(() {
|
||||
SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty();
|
||||
mockBaseProvider = MockWgerBaseProvider();
|
||||
userProvider = UserProvider(mockBaseProvider);
|
||||
});
|
||||
|
||||
Widget createWidget() {
|
||||
return ChangeNotifierProvider<UserProvider>.value(
|
||||
value: userProvider,
|
||||
child: const MaterialApp(
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: Scaffold(
|
||||
body: SettingsDashboardVisibility(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('renders list of dashboard widgets', (tester) async {
|
||||
await tester.pumpWidget(createWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify all items are present
|
||||
expect(find.byType(ListTile), findsNWidgets(DashboardWidget.values.length));
|
||||
expect(find.text('Routines'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('toggle visibility updates provider', (tester) async {
|
||||
await tester.pumpWidget(createWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Routines should be visible initally (default is true)
|
||||
expect(userProvider.isDashboardWidgetVisible(DashboardWidget.routines), true);
|
||||
|
||||
// Find the visibility icon for Routines
|
||||
final routineTile = find.byKey(const ValueKey(DashboardWidget.routines));
|
||||
final iconBtn = find.descendant(of: routineTile, matching: find.byType(IconButton));
|
||||
|
||||
// Check icon is 'visibility'
|
||||
expect(find.descendant(of: iconBtn, matching: find.byIcon(Icons.visibility)), findsOneWidget);
|
||||
|
||||
// Tap to toggle
|
||||
await tester.tap(iconBtn);
|
||||
await tester.pump(); // re-render
|
||||
|
||||
// Check provider state
|
||||
expect(userProvider.isDashboardWidgetVisible(DashboardWidget.routines), false);
|
||||
|
||||
// Check icon is 'visibility_off'
|
||||
expect(
|
||||
find.descendant(of: iconBtn, matching: find.byIcon(Icons.visibility_off)),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
// Reordering test is a bit flaky without full drag setup, but we can try
|
||||
testWidgets('dragging reorders items', (tester) async {
|
||||
await tester.pumpWidget(createWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Initial order: routines, nutrition, weight...
|
||||
expect(userProvider.dashboardOrder[0], DashboardWidget.routines);
|
||||
expect(userProvider.dashboardOrder[1], DashboardWidget.nutrition);
|
||||
|
||||
// Find drag handle for Routines (index 0)
|
||||
final handleFinder = find.byIcon(Icons.drag_handle);
|
||||
final firstHandle = handleFinder.at(0);
|
||||
// final secondHandle = handleFinder.at(1);
|
||||
|
||||
// Drag first item down
|
||||
await tester.drag(firstHandle, const Offset(0, 100)); // Drag down enough to swap
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify order changed
|
||||
// If swapped with second item (nutrition) and maybe third (weight) depending on height
|
||||
// Based on running test: index 0 is nutrition, index 1 is weight.
|
||||
expect(userProvider.dashboardOrder[0], DashboardWidget.nutrition);
|
||||
expect(userProvider.dashboardOrder[1], DashboardWidget.weight);
|
||||
expect(userProvider.dashboardOrder[2], DashboardWidget.routines);
|
||||
});
|
||||
}
|
||||
349
test/core/settings_dashboard_visibility_test.mocks.dart
Normal file
349
test/core/settings_dashboard_visibility_test.mocks.dart
Normal file
@@ -0,0 +1,349 @@
|
||||
// Mocks generated by Mockito 5.4.6 from annotations
|
||||
// in wger/test/core/settings_dashboard_visibility_test.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:async' as _i9;
|
||||
import 'dart:ui' as _i10;
|
||||
|
||||
import 'package:flutter/material.dart' as _i7;
|
||||
import 'package:http/http.dart' as _i5;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:shared_preferences/shared_preferences.dart' as _i3;
|
||||
import 'package:wger/models/user/profile.dart' as _i8;
|
||||
import 'package:wger/providers/auth.dart' as _i4;
|
||||
import 'package:wger/providers/base_provider.dart' as _i2;
|
||||
import 'package:wger/providers/user.dart' as _i6;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
// ignore_for_file: avoid_setters_without_getters
|
||||
// ignore_for_file: comment_references
|
||||
// ignore_for_file: deprecated_member_use
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
||||
// ignore_for_file: must_be_immutable
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
// ignore_for_file: unnecessary_parenthesis
|
||||
// ignore_for_file: camel_case_types
|
||||
// ignore_for_file: subtype_of_sealed_class
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
class _FakeWgerBaseProvider_0 extends _i1.SmartFake implements _i2.WgerBaseProvider {
|
||||
_FakeWgerBaseProvider_0(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
class _FakeSharedPreferencesAsync_1 extends _i1.SmartFake implements _i3.SharedPreferencesAsync {
|
||||
_FakeSharedPreferencesAsync_1(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
class _FakeAuthProvider_2 extends _i1.SmartFake implements _i4.AuthProvider {
|
||||
_FakeAuthProvider_2(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
class _FakeClient_3 extends _i1.SmartFake implements _i5.Client {
|
||||
_FakeClient_3(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
class _FakeUri_4 extends _i1.SmartFake implements Uri {
|
||||
_FakeUri_4(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
class _FakeResponse_5 extends _i1.SmartFake implements _i5.Response {
|
||||
_FakeResponse_5(Object parent, Invocation parentInvocation) : super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
/// A class which mocks [UserProvider].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockUserProvider extends _i1.Mock implements _i6.UserProvider {
|
||||
MockUserProvider() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i7.ThemeMode get themeMode =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#themeMode),
|
||||
returnValue: _i7.ThemeMode.system,
|
||||
)
|
||||
as _i7.ThemeMode);
|
||||
|
||||
@override
|
||||
_i2.WgerBaseProvider get baseProvider =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#baseProvider),
|
||||
returnValue: _FakeWgerBaseProvider_0(
|
||||
this,
|
||||
Invocation.getter(#baseProvider),
|
||||
),
|
||||
)
|
||||
as _i2.WgerBaseProvider);
|
||||
|
||||
@override
|
||||
_i3.SharedPreferencesAsync get prefs =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#prefs),
|
||||
returnValue: _FakeSharedPreferencesAsync_1(
|
||||
this,
|
||||
Invocation.getter(#prefs),
|
||||
),
|
||||
)
|
||||
as _i3.SharedPreferencesAsync);
|
||||
|
||||
@override
|
||||
List<_i6.DashboardWidget> get dashboardOrder =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#dashboardOrder),
|
||||
returnValue: <_i6.DashboardWidget>[],
|
||||
)
|
||||
as List<_i6.DashboardWidget>);
|
||||
|
||||
@override
|
||||
set themeMode(_i7.ThemeMode? value) => super.noSuchMethod(
|
||||
Invocation.setter(#themeMode, value),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
set prefs(_i3.SharedPreferencesAsync? value) => super.noSuchMethod(
|
||||
Invocation.setter(#prefs, value),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
set profile(_i8.Profile? value) => super.noSuchMethod(
|
||||
Invocation.setter(#profile, value),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get hasListeners =>
|
||||
(super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) as bool);
|
||||
|
||||
@override
|
||||
void clear() => super.noSuchMethod(
|
||||
Invocation.method(#clear, []),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
bool isDashboardWidgetVisible(_i6.DashboardWidget? key) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#isDashboardWidgetVisible, [key]),
|
||||
returnValue: false,
|
||||
)
|
||||
as bool);
|
||||
|
||||
@override
|
||||
_i9.Future<void> setDashboardWidgetVisible(
|
||||
_i6.DashboardWidget? key,
|
||||
bool? visible,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#setDashboardWidgetVisible, [key, visible]),
|
||||
returnValue: _i9.Future<void>.value(),
|
||||
returnValueForMissingStub: _i9.Future<void>.value(),
|
||||
)
|
||||
as _i9.Future<void>);
|
||||
|
||||
@override
|
||||
_i9.Future<void> setDashboardOrder(int? oldIndex, int? newIndex) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#setDashboardOrder, [oldIndex, newIndex]),
|
||||
returnValue: _i9.Future<void>.value(),
|
||||
returnValueForMissingStub: _i9.Future<void>.value(),
|
||||
)
|
||||
as _i9.Future<void>);
|
||||
|
||||
@override
|
||||
void setThemeMode(_i7.ThemeMode? mode) => super.noSuchMethod(
|
||||
Invocation.method(#setThemeMode, [mode]),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
_i9.Future<void> fetchAndSetProfile() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchAndSetProfile, []),
|
||||
returnValue: _i9.Future<void>.value(),
|
||||
returnValueForMissingStub: _i9.Future<void>.value(),
|
||||
)
|
||||
as _i9.Future<void>);
|
||||
|
||||
@override
|
||||
_i9.Future<void> saveProfile() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#saveProfile, []),
|
||||
returnValue: _i9.Future<void>.value(),
|
||||
returnValueForMissingStub: _i9.Future<void>.value(),
|
||||
)
|
||||
as _i9.Future<void>);
|
||||
|
||||
@override
|
||||
_i9.Future<void> verifyEmail() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#verifyEmail, []),
|
||||
returnValue: _i9.Future<void>.value(),
|
||||
returnValueForMissingStub: _i9.Future<void>.value(),
|
||||
)
|
||||
as _i9.Future<void>);
|
||||
|
||||
@override
|
||||
void addListener(_i10.VoidCallback? listener) => super.noSuchMethod(
|
||||
Invocation.method(#addListener, [listener]),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void removeListener(_i10.VoidCallback? listener) => super.noSuchMethod(
|
||||
Invocation.method(#removeListener, [listener]),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void dispose() => super.noSuchMethod(
|
||||
Invocation.method(#dispose, []),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void notifyListeners() => super.noSuchMethod(
|
||||
Invocation.method(#notifyListeners, []),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class which mocks [WgerBaseProvider].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockWgerBaseProvider extends _i1.Mock implements _i2.WgerBaseProvider {
|
||||
MockWgerBaseProvider() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i4.AuthProvider get auth =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#auth),
|
||||
returnValue: _FakeAuthProvider_2(this, Invocation.getter(#auth)),
|
||||
)
|
||||
as _i4.AuthProvider);
|
||||
|
||||
@override
|
||||
_i5.Client get client =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#client),
|
||||
returnValue: _FakeClient_3(this, Invocation.getter(#client)),
|
||||
)
|
||||
as _i5.Client);
|
||||
|
||||
@override
|
||||
set auth(_i4.AuthProvider? value) => super.noSuchMethod(
|
||||
Invocation.setter(#auth, value),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
set client(_i5.Client? value) => super.noSuchMethod(
|
||||
Invocation.setter(#client, value),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
Map<String, String> getDefaultHeaders({bool? includeAuth = false}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#getDefaultHeaders, [], {
|
||||
#includeAuth: includeAuth,
|
||||
}),
|
||||
returnValue: <String, String>{},
|
||||
)
|
||||
as Map<String, String>);
|
||||
|
||||
@override
|
||||
Uri makeUrl(
|
||||
String? path, {
|
||||
int? id,
|
||||
String? objectMethod,
|
||||
Map<String, dynamic>? query,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#makeUrl,
|
||||
[path],
|
||||
{#id: id, #objectMethod: objectMethod, #query: query},
|
||||
),
|
||||
returnValue: _FakeUri_4(
|
||||
this,
|
||||
Invocation.method(
|
||||
#makeUrl,
|
||||
[path],
|
||||
{#id: id, #objectMethod: objectMethod, #query: query},
|
||||
),
|
||||
),
|
||||
)
|
||||
as Uri);
|
||||
|
||||
@override
|
||||
_i9.Future<dynamic> fetch(
|
||||
Uri? uri, {
|
||||
int? maxRetries = 3,
|
||||
Duration? initialDelay = const Duration(milliseconds: 250),
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#fetch,
|
||||
[uri],
|
||||
{#maxRetries: maxRetries, #initialDelay: initialDelay},
|
||||
),
|
||||
returnValue: _i9.Future<dynamic>.value(),
|
||||
)
|
||||
as _i9.Future<dynamic>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<dynamic>> fetchPaginated(Uri? uri) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchPaginated, [uri]),
|
||||
returnValue: _i9.Future<List<dynamic>>.value(<dynamic>[]),
|
||||
)
|
||||
as _i9.Future<List<dynamic>>);
|
||||
|
||||
@override
|
||||
_i9.Future<Map<String, dynamic>> post(Map<String, dynamic>? data, Uri? uri) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#post, [data, uri]),
|
||||
returnValue: _i9.Future<Map<String, dynamic>>.value(
|
||||
<String, dynamic>{},
|
||||
),
|
||||
)
|
||||
as _i9.Future<Map<String, dynamic>>);
|
||||
|
||||
@override
|
||||
_i9.Future<Map<String, dynamic>> patch(
|
||||
Map<String, dynamic>? data,
|
||||
Uri? uri,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#patch, [data, uri]),
|
||||
returnValue: _i9.Future<Map<String, dynamic>>.value(
|
||||
<String, dynamic>{},
|
||||
),
|
||||
)
|
||||
as _i9.Future<Map<String, dynamic>>);
|
||||
|
||||
@override
|
||||
_i9.Future<_i5.Response> deleteRequest(String? url, int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#deleteRequest, [url, id]),
|
||||
returnValue: _i9.Future<_i5.Response>.value(
|
||||
_FakeResponse_5(
|
||||
this,
|
||||
Invocation.method(#deleteRequest, [url, id]),
|
||||
),
|
||||
),
|
||||
)
|
||||
as _i9.Future<_i5.Response>);
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (C) 2020, 2021 wger Team
|
||||
* Copyright (c) 2026 wger Team
|
||||
*
|
||||
* wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wger Workout Manager is distributed in the hope that it will be useful,
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
@@ -49,6 +49,9 @@ void main() {
|
||||
|
||||
setUp(() {
|
||||
when(mockUserProvider.themeMode).thenReturn(ThemeMode.system);
|
||||
when(
|
||||
mockSharedPreferences.getString(UserProvider.PREFS_DASHBOARD_CONFIG),
|
||||
).thenAnswer((_) async => null);
|
||||
when(mockExerciseProvider.exercises).thenReturn(getTestExercises());
|
||||
when(mockNutritionProvider.ingredients).thenReturn([ingredient1, ingredient2]);
|
||||
});
|
||||
@@ -100,18 +103,23 @@ void main() {
|
||||
group('Theme settings', () {
|
||||
test('Default theme is system', () async {
|
||||
when(mockSharedPreferences.getBool(PREFS_USER_DARK_THEME)).thenAnswer((_) async => null);
|
||||
final userProvider = await UserProvider(MockWgerBaseProvider(), prefs: mockSharedPreferences);
|
||||
final userProvider = UserProvider(MockWgerBaseProvider(), prefs: mockSharedPreferences);
|
||||
await Future.delayed(const Duration(milliseconds: 50)); // wait for async prefs load
|
||||
expect(userProvider.themeMode, ThemeMode.system);
|
||||
});
|
||||
|
||||
test('Loads light theme', () async {
|
||||
when(mockSharedPreferences.getBool(PREFS_USER_DARK_THEME)).thenAnswer((_) async => false);
|
||||
final userProvider = await UserProvider(MockWgerBaseProvider(), prefs: mockSharedPreferences);
|
||||
final userProvider = UserProvider(MockWgerBaseProvider(), prefs: mockSharedPreferences);
|
||||
await Future.delayed(const Duration(milliseconds: 50)); // wait for async prefs load
|
||||
expect(userProvider.themeMode, ThemeMode.light);
|
||||
});
|
||||
|
||||
test('Saves theme to prefs', () {
|
||||
when(mockSharedPreferences.getBool(any)).thenAnswer((_) async => null);
|
||||
when(
|
||||
mockSharedPreferences.getString('dashboardWidgetVisibility'),
|
||||
).thenAnswer((_) async => null);
|
||||
final userProvider = UserProvider(MockWgerBaseProvider(), prefs: mockSharedPreferences);
|
||||
userProvider.setThemeMode(ThemeMode.dark);
|
||||
verify(mockSharedPreferences.setBool(PREFS_USER_DARK_THEME, true)).called(1);
|
||||
|
||||
@@ -943,6 +943,14 @@ class MockUserProvider extends _i1.Mock implements _i21.UserProvider {
|
||||
)
|
||||
as _i14.SharedPreferencesAsync);
|
||||
|
||||
@override
|
||||
List<_i21.DashboardWidget> get dashboardOrder =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#dashboardOrder),
|
||||
returnValue: <_i21.DashboardWidget>[],
|
||||
)
|
||||
as List<_i21.DashboardWidget>);
|
||||
|
||||
@override
|
||||
set themeMode(_i22.ThemeMode? value) => super.noSuchMethod(
|
||||
Invocation.setter(#themeMode, value),
|
||||
@@ -971,6 +979,35 @@ class MockUserProvider extends _i1.Mock implements _i21.UserProvider {
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
bool isDashboardWidgetVisible(_i21.DashboardWidget? key) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#isDashboardWidgetVisible, [key]),
|
||||
returnValue: false,
|
||||
)
|
||||
as bool);
|
||||
|
||||
@override
|
||||
_i18.Future<void> setDashboardWidgetVisible(
|
||||
_i21.DashboardWidget? key,
|
||||
bool? visible,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#setDashboardWidgetVisible, [key, visible]),
|
||||
returnValue: _i18.Future<void>.value(),
|
||||
returnValueForMissingStub: _i18.Future<void>.value(),
|
||||
)
|
||||
as _i18.Future<void>);
|
||||
|
||||
@override
|
||||
_i18.Future<void> setDashboardOrder(int? oldIndex, int? newIndex) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#setDashboardOrder, [oldIndex, newIndex]),
|
||||
returnValue: _i18.Future<void>.value(),
|
||||
returnValueForMissingStub: _i18.Future<void>.value(),
|
||||
)
|
||||
as _i18.Future<void>);
|
||||
|
||||
@override
|
||||
void setThemeMode(_i22.ThemeMode? mode) => super.noSuchMethod(
|
||||
Invocation.method(#setThemeMode, [mode]),
|
||||
|
||||
@@ -404,6 +404,17 @@ class MockAppLocalizations extends _i1.Mock implements _i2.AppLocalizations {
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
String get dashboardWidgets =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#dashboardWidgets),
|
||||
returnValue: _i3.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#dashboardWidgets),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
String get labelDashboard =>
|
||||
(super.noSuchMethod(
|
||||
|
||||
@@ -389,6 +389,14 @@ class MockUserProvider extends _i1.Mock implements _i17.UserProvider {
|
||||
)
|
||||
as _i4.SharedPreferencesAsync);
|
||||
|
||||
@override
|
||||
List<_i17.DashboardWidget> get dashboardOrder =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#dashboardOrder),
|
||||
returnValue: <_i17.DashboardWidget>[],
|
||||
)
|
||||
as List<_i17.DashboardWidget>);
|
||||
|
||||
@override
|
||||
set themeMode(_i18.ThemeMode? value) => super.noSuchMethod(
|
||||
Invocation.setter(#themeMode, value),
|
||||
@@ -417,6 +425,35 @@ class MockUserProvider extends _i1.Mock implements _i17.UserProvider {
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
bool isDashboardWidgetVisible(_i17.DashboardWidget? key) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#isDashboardWidgetVisible, [key]),
|
||||
returnValue: false,
|
||||
)
|
||||
as bool);
|
||||
|
||||
@override
|
||||
_i15.Future<void> setDashboardWidgetVisible(
|
||||
_i17.DashboardWidget? key,
|
||||
bool? visible,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#setDashboardWidgetVisible, [key, visible]),
|
||||
returnValue: _i15.Future<void>.value(),
|
||||
returnValueForMissingStub: _i15.Future<void>.value(),
|
||||
)
|
||||
as _i15.Future<void>);
|
||||
|
||||
@override
|
||||
_i15.Future<void> setDashboardOrder(int? oldIndex, int? newIndex) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#setDashboardOrder, [oldIndex, newIndex]),
|
||||
returnValue: _i15.Future<void>.value(),
|
||||
returnValueForMissingStub: _i15.Future<void>.value(),
|
||||
)
|
||||
as _i15.Future<void>);
|
||||
|
||||
@override
|
||||
void setThemeMode(_i18.ThemeMode? mode) => super.noSuchMethod(
|
||||
Invocation.method(#setThemeMode, [mode]),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (C) 2020, 2021 wger Team
|
||||
* Copyright (c) 2026 wger Team
|
||||
*
|
||||
* wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wger Workout Manager is distributed in the hope that it will be useful,
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
@@ -21,6 +21,7 @@ import 'dart:convert';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
|
||||
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
|
||||
import 'package:wger/providers/base_provider.dart';
|
||||
@@ -51,7 +52,6 @@ void main() {
|
||||
);
|
||||
|
||||
setUp(() {
|
||||
/// Replacement for SharedPreferences.setMockInitialValues()
|
||||
SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty();
|
||||
mockWgerBaseProvider = MockWgerBaseProvider();
|
||||
userProvider = UserProvider(mockWgerBaseProvider);
|
||||
@@ -103,4 +103,80 @@ void main() {
|
||||
verify(userProvider.baseProvider.fetch(tEmailVerifyUri));
|
||||
});
|
||||
});
|
||||
|
||||
group('dashboard config', () {
|
||||
test('initial config should be default (all visible, default order)', () {
|
||||
expect(userProvider.dashboardOrder.length, 5);
|
||||
|
||||
expect(
|
||||
userProvider.dashboardOrder,
|
||||
orderedEquals([
|
||||
DashboardWidget.routines,
|
||||
DashboardWidget.nutrition,
|
||||
DashboardWidget.weight,
|
||||
DashboardWidget.measurements,
|
||||
DashboardWidget.calendar,
|
||||
]),
|
||||
);
|
||||
expect(userProvider.isDashboardWidgetVisible(DashboardWidget.routines), true);
|
||||
});
|
||||
|
||||
test('toggling visibility should update state', () async {
|
||||
// act
|
||||
await userProvider.setDashboardWidgetVisible(DashboardWidget.routines, false);
|
||||
|
||||
// assert
|
||||
expect(userProvider.isDashboardWidgetVisible(DashboardWidget.routines), false);
|
||||
|
||||
// re-enable
|
||||
await userProvider.setDashboardWidgetVisible(DashboardWidget.routines, true);
|
||||
expect(userProvider.isDashboardWidgetVisible(DashboardWidget.routines), true);
|
||||
});
|
||||
|
||||
test('reordering should update order', () async {
|
||||
// arrange
|
||||
final initialFirst = userProvider.dashboardOrder[0];
|
||||
final initialSecond = userProvider.dashboardOrder[1];
|
||||
|
||||
// act: move first to second position
|
||||
// oldIndex: 0, newIndex: 2 (because insert is before index)
|
||||
await userProvider.setDashboardOrder(0, 2);
|
||||
|
||||
// assert
|
||||
expect(userProvider.dashboardOrder[0], initialSecond);
|
||||
expect(userProvider.dashboardOrder[1], initialFirst);
|
||||
});
|
||||
|
||||
test('should load config from prefs', () async {
|
||||
// arrange
|
||||
final prefs = SharedPreferencesAsync();
|
||||
final customConfig = [
|
||||
{'widget': 'nutrition', 'visible': true},
|
||||
{'widget': 'routines', 'visible': false},
|
||||
];
|
||||
await prefs.setString(
|
||||
UserProvider.PREFS_DASHBOARD_CONFIG,
|
||||
jsonEncode(customConfig),
|
||||
);
|
||||
|
||||
// act
|
||||
final newProvider = UserProvider(mockWgerBaseProvider, prefs: prefs);
|
||||
await Future.delayed(const Duration(milliseconds: 50)); // wait for async prefs load
|
||||
|
||||
// assert
|
||||
// The loaded ones come first
|
||||
expect(newProvider.dashboardOrder[0], DashboardWidget.nutrition);
|
||||
expect(newProvider.dashboardOrder[1], DashboardWidget.routines);
|
||||
|
||||
// Check visibility
|
||||
expect(newProvider.isDashboardWidgetVisible(DashboardWidget.nutrition), true);
|
||||
expect(newProvider.isDashboardWidgetVisible(DashboardWidget.routines), false);
|
||||
|
||||
// Remaining items are added after
|
||||
expect(newProvider.dashboardOrder.length, 5);
|
||||
|
||||
// Items not in the prefs are visible by default
|
||||
expect(newProvider.isDashboardWidgetVisible(DashboardWidget.weight), true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -231,6 +231,14 @@ class MockUserProvider extends _i1.Mock implements _i13.UserProvider {
|
||||
)
|
||||
as _i4.SharedPreferencesAsync);
|
||||
|
||||
@override
|
||||
List<_i13.DashboardWidget> get dashboardOrder =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#dashboardOrder),
|
||||
returnValue: <_i13.DashboardWidget>[],
|
||||
)
|
||||
as List<_i13.DashboardWidget>);
|
||||
|
||||
@override
|
||||
set themeMode(_i14.ThemeMode? value) => super.noSuchMethod(
|
||||
Invocation.setter(#themeMode, value),
|
||||
@@ -259,6 +267,35 @@ class MockUserProvider extends _i1.Mock implements _i13.UserProvider {
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
bool isDashboardWidgetVisible(_i13.DashboardWidget? key) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#isDashboardWidgetVisible, [key]),
|
||||
returnValue: false,
|
||||
)
|
||||
as bool);
|
||||
|
||||
@override
|
||||
_i11.Future<void> setDashboardWidgetVisible(
|
||||
_i13.DashboardWidget? key,
|
||||
bool? visible,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#setDashboardWidgetVisible, [key, visible]),
|
||||
returnValue: _i11.Future<void>.value(),
|
||||
returnValueForMissingStub: _i11.Future<void>.value(),
|
||||
)
|
||||
as _i11.Future<void>);
|
||||
|
||||
@override
|
||||
_i11.Future<void> setDashboardOrder(int? oldIndex, int? newIndex) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#setDashboardOrder, [oldIndex, newIndex]),
|
||||
returnValue: _i11.Future<void>.value(),
|
||||
returnValueForMissingStub: _i11.Future<void>.value(),
|
||||
)
|
||||
as _i11.Future<void>);
|
||||
|
||||
@override
|
||||
void setThemeMode(_i14.ThemeMode? mode) => super.noSuchMethod(
|
||||
Invocation.method(#setThemeMode, [mode]),
|
||||
|
||||
Reference in New Issue
Block a user