i18n and other cleanup

This commit is contained in:
Roland Geider
2026-01-16 17:09:05 +01:00
parent dce1b79683
commit ec153c51df
29 changed files with 330 additions and 114 deletions

View File

@@ -233,6 +233,16 @@
"@comment": {
"description": "Comment, additional information"
},
"topSet": "Top set: {value}",
"@topSet": {
"description": "Value is a representation of the set, like '10 x 80kg'",
"type": "text",
"placeholders": {
"value": {
"type": "String"
}
}
},
"impressionGood": "Good",
"impressionNeutral": "Neutral",
"impressionBad": "Bad",
@@ -592,6 +602,7 @@
"@newEntry": {
"description": "Title when adding a new entry such as a weight or log entry"
},
"noTrophies": "You have no trophies yet",
"noWeightEntries": "You have no weight entries",
"@noWeightEntries": {
"description": "Message shown when the user has no logged weight entries"

View File

@@ -1,21 +1,3 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* Copyright (c) 2025 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/>.
*/
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'trophy.dart';

View File

@@ -73,7 +73,7 @@ class WgerBaseProvider {
Uri uri, {
int maxRetries = 3,
Duration initialDelay = const Duration(milliseconds: 250),
String? language
String? language,
}) async {
int attempt = 0;
final random = math.Random();
@@ -90,7 +90,7 @@ class WgerBaseProvider {
while (true) {
try {
final response = await client
.get(uri, headers: getDefaultHeaders(includeAuth: true))
.get(uri, headers: getDefaultHeaders(includeAuth: true, language: language))
.timeout(const Duration(seconds: 5));
if (response.statusCode >= 400) {

View File

@@ -10,10 +10,10 @@ part of 'gym_state.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(GymStateNotifier)
const gymStateProvider = GymStateNotifierProvider._();
final gymStateProvider = GymStateNotifierProvider._();
final class GymStateNotifierProvider extends $NotifierProvider<GymStateNotifier, GymModeState> {
const GymStateNotifierProvider._()
GymStateNotifierProvider._()
: super(
from: null,
argument: null,
@@ -40,14 +40,13 @@ final class GymStateNotifierProvider extends $NotifierProvider<GymStateNotifier,
}
}
String _$gymStateNotifierHash() => r'8474afce33638bf67570fd64b64e9b5d171804d3';
String _$gymStateNotifierHash() => r'3a0bb78e9f7e682ba93a40a73b170126b5eb5ca9';
abstract class _$GymStateNotifier extends $Notifier<GymModeState> {
GymModeState build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<GymModeState, GymModeState>;
final element =
ref.element
@@ -57,6 +56,6 @@ abstract class _$GymStateNotifier extends $Notifier<GymModeState> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, build);
}
}

View File

@@ -10,12 +10,12 @@ part of 'trophies.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(trophyRepository)
const trophyRepositoryProvider = TrophyRepositoryProvider._();
final trophyRepositoryProvider = TrophyRepositoryProvider._();
final class TrophyRepositoryProvider
extends $FunctionalProvider<TrophyRepository, TrophyRepository, TrophyRepository>
with $Provider<TrophyRepository> {
const TrophyRepositoryProvider._()
TrophyRepositoryProvider._()
: super(
from: null,
argument: null,
@@ -51,11 +51,11 @@ final class TrophyRepositoryProvider
String _$trophyRepositoryHash() => r'0699f0c0f7f324f3ba9b21420d9845a3e3096b61';
@ProviderFor(TrophyStateNotifier)
const trophyStateProvider = TrophyStateNotifierProvider._();
final trophyStateProvider = TrophyStateNotifierProvider._();
final class TrophyStateNotifierProvider
extends $NotifierProvider<TrophyStateNotifier, TrophyState> {
const TrophyStateNotifierProvider._()
TrophyStateNotifierProvider._()
: super(
from: null,
argument: null,
@@ -89,7 +89,6 @@ abstract class _$TrophyStateNotifier extends $Notifier<TrophyState> {
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<TrophyState, TrophyState>;
final element =
ref.element
@@ -99,6 +98,6 @@ abstract class _$TrophyStateNotifier extends $Notifier<TrophyState> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, build);
}
}

View File

@@ -31,9 +31,7 @@ class TrophyScreen extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: EmptyAppBar(AppLocalizations.of(context).trophies),
body: WidescreenWrapper(
child: TrophiesOverview(),
),
body: const WidescreenWrapper(child: TrophiesOverview()),
);
}
}

View File

@@ -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.
@@ -22,10 +22,10 @@ import 'package:wger/theme/theme.dart';
class NothingFound extends StatelessWidget {
final String _title;
final String _titleForm;
final Widget _form;
final String? _titleForm;
final Widget? _form;
const NothingFound(this._title, this._titleForm, this._form);
const NothingFound(this._title, [this._titleForm, this._form]);
@override
Widget build(BuildContext context) {
@@ -35,21 +35,22 @@ class NothingFound extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_title),
IconButton(
iconSize: 30,
icon: const Icon(Icons.add_box, color: wgerPrimaryButtonColor),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
_titleForm,
hasListView: true,
_form,
),
);
},
),
if (_titleForm != null && _form != null)
IconButton(
iconSize: 30,
icon: const Icon(Icons.add_box, color: wgerPrimaryButtonColor),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
_titleForm,
hasListView: true,
_form,
),
);
},
),
],
),
);

View File

@@ -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.
@@ -58,7 +58,7 @@ class _DashboardRoutineWidgetState extends State<DashboardRoutineWidget> {
),
subtitle: Text(
_hasContent
? '${dateFormat.format(routine!.start)} - ${dateFormat.format(routine!.end)}'
? '${dateFormat.format(routine!.start)} - ${dateFormat.format(routine.end)}'
: '',
),
leading: Icon(

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/trophies/trophy.dart';
import 'package:wger/providers/trophies.dart';
import 'package:wger/screens/trophy_screen.dart';
@@ -28,6 +29,7 @@ class DashboardTrophiesWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final trophiesState = ref.read(trophyStateProvider);
final i18n = AppLocalizations.of(context);
return Card(
color: Colors.transparent,
@@ -36,9 +38,25 @@ class DashboardTrophiesWidget extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (trophiesState.nonPrTrophies.isEmpty)
Padding(
padding: const EdgeInsets.all(16.0),
child: Text('No trophies yet', style: Theme.of(context).textTheme.bodyMedium),
Card(
child: Column(
children: [
ListTile(
title: Text(
i18n.trophies,
style: Theme.of(context).textTheme.headlineSmall,
),
// leading: Icon(Icons.widgets_outlined),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
i18n.noTrophies,
style: Theme.of(context).textTheme.bodyMedium,
),
),
],
),
)
else
SizedBox(

View File

@@ -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.
@@ -42,7 +42,7 @@ class DashboardWeightWidget extends StatelessWidget {
);
return Consumer<BodyWeightProvider>(
builder: (context, _, __) => Card(
builder: (context, _, _) => Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [

View File

@@ -18,7 +18,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/helpers/errors.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
@@ -33,9 +32,8 @@ import 'session_info.dart';
class DayLogWidget extends ConsumerWidget {
final DateTime _date;
final Routine _routine;
final _logger = Logger('DayLogWidget');
DayLogWidget(this._date, this._routine);
const DayLogWidget(this._date, this._routine);
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -53,9 +51,6 @@ class DayLogWidget extends ConsumerWidget {
.where((t) => t.contextData?.sessionId == sessionApi.session.id)
.toList();
_logger.info(trophyState.prTrophies);
_logger.info(prTrophies);
return Column(
spacing: 10,
children: [

View File

@@ -1,6 +1,6 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* Copyright (c) 2020 - 2025 wger Team
* Copyright (c) 2020 - 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
@@ -74,6 +74,7 @@ class _ExerciseExpansionTile extends StatelessWidget {
Widget build(BuildContext context) {
final languageCode = Localizations.localeOf(context).languageCode;
final theme = Theme.of(context);
final i18n = AppLocalizations.of(context);
final topSet = logs.isEmpty
? null
@@ -85,7 +86,7 @@ class _ExerciseExpansionTile extends StatelessWidget {
return ExpansionTile(
// leading: const Icon(Icons.fitness_center),
title: Text(exercise.getTranslation(languageCode).name, style: theme.textTheme.titleMedium),
subtitle: Text('Top set: $topSetWeight $topSetWeightUnit'),
subtitle: Text(i18n.topSet('$topSetWeight $topSetWeightUnit')),
children: logs.map((log) => _SetDataRow(log: log, userPrTrophies: userPrTrophies)).toList(),
);
}

View File

@@ -19,6 +19,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:wger/helpers/material.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/trophies/user_trophy_progression.dart';
import 'package:wger/providers/trophies.dart';
@@ -28,6 +29,7 @@ class TrophiesOverview extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final trophyState = ref.watch(trophyStateProvider);
final i18n = AppLocalizations.of(context);
// Responsive grid: determine columns based on screen width
final width = MediaQuery.widthOf(context);
@@ -48,7 +50,7 @@ class TrophiesOverview extends ConsumerWidget {
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'No trophies yet',
i18n.noTrophies,
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
),

View File

@@ -1104,9 +1104,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i2.WgerBaseProvider {
as Uri);
@override
_i18.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i18.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i18.Future<dynamic>.value(),
)
as _i18.Future<dynamic>);

View File

@@ -406,9 +406,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i2.WgerBaseProvider {
as Uri);
@override
_i14.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i14.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i14.Future<dynamic>.value(),
)
as _i14.Future<dynamic>);

View File

@@ -179,9 +179,22 @@ class MockGalleryProvider extends _i1.Mock implements _i4.GalleryProvider {
as Uri);
@override
_i6.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i6.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i6.Future<dynamic>.value(),
)
as _i6.Future<dynamic>);

View File

@@ -179,9 +179,22 @@ class MockGalleryProvider extends _i1.Mock implements _i4.GalleryProvider {
as Uri);
@override
_i6.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i6.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i6.Future<dynamic>.value(),
)
as _i6.Future<dynamic>);

View File

@@ -116,9 +116,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider {
as Uri);
@override
_i5.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i5.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i5.Future<dynamic>.value(),
)
as _i5.Future<dynamic>);

View File

@@ -126,9 +126,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider {
as Uri);
@override
_i5.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i5.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i5.Future<dynamic>.value(),
)
as _i5.Future<dynamic>);

View File

@@ -361,9 +361,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i8.WgerBaseProvider {
as Uri);
@override
_i5.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i5.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i5.Future<dynamic>.value(),
)
as _i5.Future<dynamic>);

View File

@@ -205,9 +205,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider {
as Uri);
@override
_i20.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i20.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i20.Future<dynamic>.value(),
)
as _i20.Future<dynamic>);

View File

@@ -116,9 +116,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider {
as Uri);
@override
_i5.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i5.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i5.Future<dynamic>.value(),
)
as _i5.Future<dynamic>);

View File

@@ -155,9 +155,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider {
as Uri);
@override
_i11.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i11.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i11.Future<dynamic>.value(),
)
as _i11.Future<dynamic>);

View File

@@ -116,9 +116,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider {
as Uri);
@override
_i5.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i5.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i5.Future<dynamic>.value(),
)
as _i5.Future<dynamic>);

View File

@@ -116,9 +116,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider {
as Uri);
@override
_i5.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i5.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i5.Future<dynamic>.value(),
)
as _i5.Future<dynamic>);

View File

@@ -18,20 +18,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod/misc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:network_image_mock/network_image_mock.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/trophies.dart';
import 'package:wger/widgets/dashboard/widgets/trophies.dart';
import '../../test_data/trophies.dart';
void main() {
testWidgets('DashboardTrophiesWidget shows trophies', (WidgetTester tester) async {
// Act
await mockNetworkImagesFor(() async {
await tester.pumpWidget(
ProviderScope(
overrides: [
Future<void> pumpOverview(WidgetTester tester, [List<Override> overrides = const []]) async {
await tester.pumpWidget(
ProviderScope(
overrides: overrides,
child: const MaterialApp(
locale: Locale('en'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold(
body: DashboardTrophiesWidget(),
),
),
),
);
}
group('DashboardTrophiesWidget tests', () {
testWidgets('shows trophies', (WidgetTester tester) async {
// Act
await mockNetworkImagesFor(() async {
await pumpOverview(
tester,
[
trophyStateProvider.overrideWithValue(
TrophyState(
userTrophies: getUserTrophies(),
@@ -39,17 +58,22 @@ void main() {
),
),
],
child: const MaterialApp(
home: Scaffold(
body: DashboardTrophiesWidget(),
),
),
),
);
);
await tester.pumpAndSettle();
// Assert
expect(find.text('New Year, New Me'), findsOneWidget);
});
});
testWidgets('handles empty results', (WidgetTester tester) async {
// Act
await pumpOverview(tester);
await tester.pumpAndSettle();
// Assert
expect(find.text('New Year, New Me'), findsOneWidget);
expect(find.text('Trophies'), findsOneWidget);
expect(find.text('You have no trophies yet'), findsOneWidget);
});
});
}

View File

@@ -20,6 +20,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:network_image_mock/network_image_mock.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/trophies.dart';
import 'package:wger/widgets/trophies/trophies_overview.dart';
@@ -41,6 +42,9 @@ void main() {
),
],
child: const MaterialApp(
locale: Locale('en'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold(body: TrophiesOverview()),
),
),

View File

@@ -116,9 +116,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider {
as Uri);
@override
_i5.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i5.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i5.Future<dynamic>.value(),
)
as _i5.Future<dynamic>);

View File

@@ -116,9 +116,22 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider {
as Uri);
@override
_i5.Future<dynamic> fetch(Uri? uri, {String? language}) =>
_i5.Future<dynamic> fetch(
Uri? uri, {
int? maxRetries = 3,
Duration? initialDelay = const Duration(milliseconds: 250),
String? language,
}) =>
(super.noSuchMethod(
Invocation.method(#fetch, [uri], {#language: language}),
Invocation.method(
#fetch,
[uri],
{
#maxRetries: maxRetries,
#initialDelay: initialDelay,
#language: language,
},
),
returnValue: _i5.Future<dynamic>.value(),
)
as _i5.Future<dynamic>);