Add some tests

This commit is contained in:
Roland Geider
2025-05-23 18:08:19 +02:00
parent 0d36fe4bc3
commit f5decaf6a5
5 changed files with 341 additions and 17 deletions

View File

@@ -13,11 +13,12 @@ const DEFAULT_LB_PLATES = [2.5, 5, 10, 25, 35, 45];
const PREFS_KEY_PLATES = 'selectedPlates';
final plateWeightsProvider = StateNotifierProvider<PlateWeightsNotifier, PlateWeightsState>((ref) {
return PlateWeightsNotifier();
final plateCalculatorProvider =
StateNotifierProvider<PlateCalculatorNotifier, PlateCalculatorState>((ref) {
return PlateCalculatorNotifier();
});
class PlateWeightsState {
class PlateCalculatorState {
final _logger = Logger('PlateWeightsState');
final barWeightKg = 20;
@@ -53,7 +54,7 @@ class PlateWeightsState {
final List<num> availablePlatesKg = const [0.5, 1, 1.25, 2, 2.5, 5, 10, 15, 20, 25];
final List<num> availablePlatesLb = const [2.5, 5, 10, 25, 35, 45];
PlateWeightsState({
PlateCalculatorState({
this.isMetric = true,
this.totalWeight = 0,
List<num>? selectedPlates,
@@ -90,12 +91,12 @@ class PlateWeightsState {
return {'isMetric': isMetric, 'selectedPlates': selectedPlates};
}
PlateWeightsState copyWith({
PlateCalculatorState copyWith({
bool? isMetric,
num? totalWeight,
List<num>? selectedPlates,
}) {
return PlateWeightsState(
return PlateCalculatorState(
isMetric: isMetric ?? this.isMetric,
totalWeight: totalWeight ?? this.totalWeight,
selectedPlates: selectedPlates ?? this.selectedPlates,
@@ -103,21 +104,23 @@ class PlateWeightsState {
}
}
class PlateWeightsNotifier extends StateNotifier<PlateWeightsState> {
final _logger = Logger('PlateWeightsNotifier');
class PlateCalculatorNotifier extends StateNotifier<PlateCalculatorState> {
final _logger = Logger('PlateCalculatorNotifier');
late SharedPreferencesAsync prefs;
PlateWeightsNotifier({SharedPreferencesAsync? prefs}) : super(PlateWeightsState()) {
PlateCalculatorNotifier({SharedPreferencesAsync? prefs}) : super(PlateCalculatorState()) {
this.prefs = prefs ?? PreferenceHelper.asyncPref;
_readDataFromSharedPrefs();
}
Future<void> saveToSharedPrefs() async {
_logger.fine('Saving plate data to SharedPreferences');
await prefs.setString(PREFS_KEY_PLATES, jsonEncode(state.toJson()));
}
Future<void> _readDataFromSharedPrefs() async {
_logger.fine('Reading plate data from SharedPreferences');
final prefsData = await prefs.getString(PREFS_KEY_PLATES);
if (prefsData != null) {

View File

@@ -22,7 +22,7 @@ class _AddPlateWeightsState extends ConsumerState<AddPlateWeights>
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(plateWeightsProvider.notifier);
ref.read(plateCalculatorProvider.notifier);
});
}
@@ -36,8 +36,8 @@ class _AddPlateWeightsState extends ConsumerState<AddPlateWeights>
Widget build(BuildContext context) {
final i18n = AppLocalizations.of(context);
final plateWeightsState = ref.watch(plateWeightsProvider);
final plateWeightsNotifier = ref.read(plateWeightsProvider.notifier);
final plateWeightsState = ref.watch(plateCalculatorProvider);
final plateWeightsNotifier = ref.read(plateCalculatorProvider.notifier);
final userProvider = provider.Provider.of<UserProvider>(context);
return Scaffold(

View File

@@ -166,7 +166,7 @@ class _LogPageState extends ConsumerState<LogPage> {
setState(() {
widget._log.weight = newValue;
_weightController.text = newValue.toString();
ref.read(plateWeightsProvider.notifier).setWeight(
ref.read(plateCalculatorProvider.notifier).setWeight(
_weightController.text == '' ? 0 : double.parse(_weightController.text),
);
});
@@ -189,7 +189,7 @@ class _LogPageState extends ConsumerState<LogPage> {
num.parse(value);
setState(() {
widget._log.weight = num.parse(value);
ref.read(plateWeightsProvider.notifier).setWeight(
ref.read(plateCalculatorProvider.notifier).setWeight(
_weightController.text == '' ? 0 : double.parse(_weightController.text),
);
});
@@ -218,7 +218,7 @@ class _LogPageState extends ConsumerState<LogPage> {
setState(() {
widget._log.weight = newValue;
_weightController.text = newValue.toString();
ref.read(plateWeightsProvider.notifier).setWeight(
ref.read(plateCalculatorProvider.notifier).setWeight(
_weightController.text == '' ? 0 : double.parse(_weightController.text),
);
});
@@ -379,7 +379,7 @@ class _LogPageState extends ConsumerState<LogPage> {
}
Widget getPlates() {
final plateWeightsState = ref.watch(plateWeightsProvider);
final plateWeightsState = ref.watch(plateCalculatorProvider);
return Column(
children: [
@@ -414,7 +414,7 @@ class _LogPageState extends ConsumerState<LogPage> {
size: 37,
padding: 2,
margin: 0,
color: ref.read(plateWeightsProvider).getColor(entry.key),
color: ref.read(plateCalculatorProvider).getColor(entry.key),
),
const SizedBox(width: 10),
],

View File

@@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart'; // Added for annotations
import 'package:mockito/mockito.dart'; // Added for mockito
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/providers/plate_weights.dart';
import 'plate_calculator_test.mocks.dart';
@GenerateMocks([SharedPreferencesAsync])
void main() {
group('PlateWeightsNotifier', () {
late PlateCalculatorNotifier notifier;
late MockSharedPreferencesAsync mockPrefs;
setUp(() {
mockPrefs = MockSharedPreferencesAsync();
when(mockPrefs.getString(PREFS_KEY_PLATES)).thenAnswer((_) async => null);
when(mockPrefs.setString(any, any)).thenAnswer((_) async => true);
notifier = PlateCalculatorNotifier(prefs: mockPrefs);
});
test('toggleSelection adds and removes plates', () async {
// Test adding a plate
await notifier.toggleSelection(0.5);
expect(notifier.state.selectedPlates.contains(0.5), true);
// Test removing a plate
await notifier.toggleSelection(0.5);
expect(notifier.state.selectedPlates.contains(0.5), false);
});
test('unitChange updates state correctly', () {
// Change to imperial
notifier.setWeight(123);
notifier.unitChange(unit: WeightUnitEnum.lb);
expect(notifier.state.isMetric, false);
expect(notifier.state.totalWeight, 0);
expect(notifier.state.selectedPlates, DEFAULT_LB_PLATES);
expect(notifier.state.barWeight, notifier.state.barWeightLb);
expect(notifier.state.availablePlates, notifier.state.availablePlatesLb);
// Change back to metric
notifier.setWeight(123);
notifier.unitChange(unit: WeightUnitEnum.kg);
expect(notifier.state.isMetric, true);
expect(notifier.state.totalWeight, 0);
expect(notifier.state.selectedPlates, DEFAULT_KG_PLATES);
expect(notifier.state.barWeight, notifier.state.barWeightKg);
expect(notifier.state.availablePlates, notifier.state.availablePlatesKg);
});
test('setWeight updates totalWeight', () {
notifier.setWeight(100);
expect(notifier.state.totalWeight, 100);
});
});
group('PlateWeightsState', () {
test('copyWith creates a new instance with updated values', () {
final initialState = PlateCalculatorState();
final updatedState = initialState.copyWith(
isMetric: false,
totalWeight: 100,
selectedPlates: [1, 2, 3],
);
expect(updatedState.isMetric, false);
expect(updatedState.totalWeight, 100);
expect(updatedState.selectedPlates, [1, 2, 3]);
});
test('toJson returns correct map', () {
final state = PlateCalculatorState(isMetric: false, selectedPlates: [10, 20]);
final json = state.toJson();
expect(json['isMetric'], false);
expect(json['selectedPlates'], [10, 20]);
});
test('barWeight returns correct value based on isMetric', () {
final metricState = PlateCalculatorState(isMetric: true);
expect(metricState.barWeight, metricState.barWeightKg);
final imperialState = PlateCalculatorState(isMetric: false);
expect(imperialState.barWeight, imperialState.barWeightLb);
});
test('availablePlates returns correct list based on isMetric', () {
final metricState = PlateCalculatorState(isMetric: true);
expect(metricState.availablePlates, metricState.availablePlatesKg);
final imperialState = PlateCalculatorState(isMetric: false);
expect(imperialState.availablePlates, metricState.availablePlatesLb);
});
test('getColor returns correct color', () {
final metricState = PlateCalculatorState(isMetric: true);
expect(metricState.getColor(20), Colors.blue);
expect(metricState.getColor(0.1), Colors.grey, reason: 'Fallback color');
final imperialState = PlateCalculatorState(isMetric: false);
expect(imperialState.getColor(45), Colors.blue);
expect(imperialState.getColor(0.1), Colors.grey, reason: 'Fallback color');
});
});
}

View File

@@ -0,0 +1,213 @@
// Mocks generated by Mockito 5.4.6 from annotations
// in wger/test/providers/plate_weights_test.dart.
// Do not manually edit this file.
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i3;
import 'package:mockito/mockito.dart' as _i1;
import 'package:shared_preferences/src/shared_preferences_async.dart' as _i2;
// 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
/// A class which mocks [SharedPreferencesAsync].
///
/// See the documentation for Mockito's code generation for more information.
// ignore: must_be_immutable
class MockSharedPreferencesAsync extends _i1.Mock implements _i2.SharedPreferencesAsync {
MockSharedPreferencesAsync() {
_i1.throwOnMissingStub(this);
}
@override
_i3.Future<Set<String>> getKeys({Set<String>? allowList}) => (super.noSuchMethod(
Invocation.method(
#getKeys,
[],
{#allowList: allowList},
),
returnValue: _i3.Future<Set<String>>.value(<String>{}),
) as _i3.Future<Set<String>>);
@override
_i3.Future<Map<String, Object?>> getAll({Set<String>? allowList}) => (super.noSuchMethod(
Invocation.method(
#getAll,
[],
{#allowList: allowList},
),
returnValue: _i3.Future<Map<String, Object?>>.value(<String, Object?>{}),
) as _i3.Future<Map<String, Object?>>);
@override
_i3.Future<bool?> getBool(String? key) => (super.noSuchMethod(
Invocation.method(
#getBool,
[key],
),
returnValue: _i3.Future<bool?>.value(),
) as _i3.Future<bool?>);
@override
_i3.Future<int?> getInt(String? key) => (super.noSuchMethod(
Invocation.method(
#getInt,
[key],
),
returnValue: _i3.Future<int?>.value(),
) as _i3.Future<int?>);
@override
_i3.Future<double?> getDouble(String? key) => (super.noSuchMethod(
Invocation.method(
#getDouble,
[key],
),
returnValue: _i3.Future<double?>.value(),
) as _i3.Future<double?>);
@override
_i3.Future<String?> getString(String? key) => (super.noSuchMethod(
Invocation.method(
#getString,
[key],
),
returnValue: _i3.Future<String?>.value(),
) as _i3.Future<String?>);
@override
_i3.Future<List<String>?> getStringList(String? key) => (super.noSuchMethod(
Invocation.method(
#getStringList,
[key],
),
returnValue: _i3.Future<List<String>?>.value(),
) as _i3.Future<List<String>?>);
@override
_i3.Future<bool> containsKey(String? key) => (super.noSuchMethod(
Invocation.method(
#containsKey,
[key],
),
returnValue: _i3.Future<bool>.value(false),
) as _i3.Future<bool>);
@override
_i3.Future<void> setBool(
String? key,
bool? value,
) =>
(super.noSuchMethod(
Invocation.method(
#setBool,
[
key,
value,
],
),
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
@override
_i3.Future<void> setInt(
String? key,
int? value,
) =>
(super.noSuchMethod(
Invocation.method(
#setInt,
[
key,
value,
],
),
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
@override
_i3.Future<void> setDouble(
String? key,
double? value,
) =>
(super.noSuchMethod(
Invocation.method(
#setDouble,
[
key,
value,
],
),
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
@override
_i3.Future<void> setString(
String? key,
String? value,
) =>
(super.noSuchMethod(
Invocation.method(
#setString,
[
key,
value,
],
),
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
@override
_i3.Future<void> setStringList(
String? key,
List<String>? value,
) =>
(super.noSuchMethod(
Invocation.method(
#setStringList,
[
key,
value,
],
),
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
@override
_i3.Future<void> remove(String? key) => (super.noSuchMethod(
Invocation.method(
#remove,
[key],
),
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
@override
_i3.Future<void> clear({Set<String>? allowList}) => (super.noSuchMethod(
Invocation.method(
#clear,
[],
{#allowList: allowList},
),
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
}