Browse Source

test(neon): test Option

pull/480/head
Nikolas Rimikis 1 year ago
parent
commit
783123ca05
No known key found for this signature in database
GPG Key ID: 85ED1DE9786A4FF2
  1. 4
      packages/neon/neon/lib/src/settings/models/option.dart
  2. 6
      packages/neon/neon/lib/src/settings/models/select_option.dart
  3. 4
      packages/neon/neon/lib/src/settings/models/toggle_option.dart
  4. 6
      packages/neon/neon/lib/src/utils/settings_export_helper.dart
  5. 1
      packages/neon/neon/pubspec.yaml
  6. 251
      packages/neon/neon/test/option_test.dart

4
packages/neon/neon/lib/src/settings/models/option.dart

@ -84,10 +84,10 @@ abstract class Option<T> extends ChangeNotifier implements ValueListenable<T> {
} }
/// Deserializes the data. /// Deserializes the data.
T? deserialize(final Object? data); T deserialize(final Object data);
/// Serializes the [value]. /// Serializes the [value].
Object? serialize(); Object serialize();
BehaviorSubject<T>? _stream; BehaviorSubject<T>? _stream;

6
packages/neon/neon/lib/src/settings/models/select_option.dart

@ -41,8 +41,8 @@ class SelectOption<T> extends Option<T> {
@override @override
set value(final T value) { set value(final T value) {
unawaited(storage.setString(key, value.toString()));
super.value = value; super.value = value;
unawaited(storage.setString(key, serialize()));
} }
/// A collection of different values this can have. /// A collection of different values this can have.
@ -60,8 +60,8 @@ class SelectOption<T> extends Option<T> {
} }
@override @override
String? serialize() => value?.toString(); String serialize() => value.toString();
@override @override
T? deserialize(final dynamic data) => _fromString(_values, data as String?); T deserialize(final Object data) => _fromString(_values, data as String)!;
} }

4
packages/neon/neon/lib/src/settings/models/toggle_option.dart

@ -27,13 +27,13 @@ class ToggleOption extends Option<bool> {
@override @override
set value(final bool value) { set value(final bool value) {
unawaited(storage.setBool(key, value));
super.value = value; super.value = value;
unawaited(storage.setBool(key, serialize()));
} }
@override @override
bool serialize() => value; bool serialize() => value;
@override @override
bool deserialize(final dynamic data) => data as bool; bool deserialize(final Object data) => data as bool;
} }

6
packages/neon/neon/lib/src/utils/settings_export_helper.dart

@ -54,7 +54,11 @@ class SettingsExportHelper {
for (final optionKey in data.keys) { for (final optionKey in data.keys) {
for (final option in options) { for (final option in options) {
if (option.key == optionKey) { if (option.key == optionKey) {
option.value = await option.deserialize(data[optionKey]); final Object? value = data[optionKey];
if (value != null) {
option.value = await option.deserialize(value);
}
} }
} }
} }

1
packages/neon/neon/pubspec.yaml

@ -60,6 +60,7 @@ dev_dependencies:
build_runner: ^2.4.4 build_runner: ^2.4.4
go_router_builder: ^2.0.1 go_router_builder: ^2.0.1
json_serializable: ^6.6.2 json_serializable: ^6.6.2
mocktail: ^0.3.0
nit_picking: nit_picking:
git: git:
url: https://github.com/stack11/dart_nit_picking url: https://github.com/stack11/dart_nit_picking

251
packages/neon/neon/test/option_test.dart

@ -0,0 +1,251 @@
// ignore_for_file: discarded_futures
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:mocktail/mocktail.dart';
import 'package:neon/src/settings/models/select_option.dart';
import 'package:neon/src/settings/models/storage.dart';
import 'package:neon/src/settings/models/toggle_option.dart';
import 'package:test/test.dart';
class MockStorage extends Mock implements SettingsStorage {}
class MockCallbackFunction extends Mock {
FutureOr<void> call();
}
enum SelectValues {
first,
second,
third,
}
void main() {
final storage = MockStorage();
const key = 'storage-key';
String labelBuilder(final _) => 'label';
group('SelectOption', () {
final valuesLabel = {
SelectValues.first: (final _) => 'first',
SelectValues.second: (final _) => 'second',
SelectValues.third: (final _) => 'third',
};
late SelectOption<SelectValues> option;
setUp(() {
when(() => storage.setString(key, any())).thenAnswer((final _) async {});
option = SelectOption<SelectValues>(
storage: storage,
key: key,
label: labelBuilder,
defaultValue: SelectValues.first,
values: valuesLabel,
);
});
tearDown(() {
reset(storage);
option.dispose();
});
test('Create', () {
expect(option.value, option.defaultValue, reason: 'Should default to defaultValue.');
when(() => storage.getString(key)).thenReturn('SelectValues.second');
option = SelectOption<SelectValues>(
storage: storage,
key: key,
label: labelBuilder,
defaultValue: SelectValues.first,
values: valuesLabel,
);
expect(option.value, SelectValues.second, reason: 'Should load value from storage when available.');
});
test('Depend', () {
final enabled = ValueNotifier(false);
final callback = MockCallbackFunction();
option = SelectOption<SelectValues>.depend(
storage: storage,
key: key,
label: labelBuilder,
defaultValue: SelectValues.first,
values: valuesLabel,
enabled: enabled,
)..addListener(callback.call);
expect(option.enabled, enabled.value, reason: 'Should initialize with enabled value.');
enabled.value = true;
verify(callback.call).called(1);
expect(option.enabled, enabled.value, reason: 'Should update the enabled state.');
});
test('Update', () {
final callback = MockCallbackFunction();
option
..addListener(callback.call)
..value = SelectValues.third;
verify(callback.call).called(1);
verify(() => storage.setString(key, 'SelectValues.third')).called(1);
expect(option.value, SelectValues.third, reason: 'Should update the value.');
option.value = SelectValues.third;
verifyNever(callback.call); // Don't notify with the same value
expect(option.value, SelectValues.third, reason: 'Should keep the value.');
});
test('Disable', () {
final callback = MockCallbackFunction();
option.addListener(callback.call);
expect(option.enabled, true, reason: 'Should default to enabled');
option.enabled = false;
verify(callback.call).called(1);
expect(option.enabled, false, reason: 'Should disable option.');
option.enabled = false;
verifyNever(callback.call); // Don't notify with the same value
expect(option.enabled, false, reason: 'Should keep the value.');
});
test('Change values', () {
final callback = MockCallbackFunction();
option.addListener(callback.call);
expect(option.values, equals(valuesLabel));
final newValues = {
SelectValues.second: (final _) => 'second',
SelectValues.third: (final _) => 'third',
};
option.values = newValues;
verify(callback.call).called(1);
expect(option.values, equals(newValues), reason: 'Should change values.');
option.values = newValues;
verifyNever(callback.call); // Don't notify with the same value
expect(option.values, newValues, reason: 'Should keep the values.');
});
test('Reset', () {
final callback = MockCallbackFunction();
option
..value = SelectValues.third
..addListener(callback.call);
expect(option.value, SelectValues.third);
option.reset();
verify(callback.call).called(1);
expect(option.value, option.defaultValue, reason: 'Should reset the value.');
});
});
group('ToggleOption', () {
late ToggleOption option;
setUp(() {
when(() => storage.setBool(key, any())).thenAnswer((final _) async {});
option = ToggleOption(
storage: storage,
key: key,
label: labelBuilder,
defaultValue: true,
);
});
tearDown(() {
reset(storage);
option.dispose();
});
test('Create', () {
expect(option.value, option.defaultValue, reason: 'Should default to defaultValue.');
when(() => storage.getBool(key)).thenReturn(true);
option = ToggleOption(
storage: storage,
key: key,
label: labelBuilder,
defaultValue: false,
);
expect(option.value, true, reason: 'Should load value from storage when available.');
});
test('Depend', () {
final enabled = ValueNotifier(false);
final callback = MockCallbackFunction();
option = ToggleOption.depend(
storage: storage,
key: key,
label: labelBuilder,
defaultValue: true,
enabled: enabled,
)..addListener(callback.call);
expect(option.enabled, enabled.value, reason: 'Should initialize with enabled value.');
enabled.value = true;
verify(callback.call).called(1);
expect(option.enabled, enabled.value, reason: 'Should update the enabled state.');
});
test('Update', () {
final callback = MockCallbackFunction();
option
..addListener(callback.call)
..value = false;
verify(callback.call).called(1);
verify(() => storage.setBool(key, false)).called(1);
expect(option.value, false, reason: 'Should update the value.');
option.value = false;
verifyNever(callback.call); // Don't notify with the same value
expect(option.value, false, reason: 'Should keep the value.');
});
test('Disable', () {
final callback = MockCallbackFunction();
option.addListener(callback.call);
expect(option.enabled, true, reason: 'Should default to enabled');
option.enabled = false;
verify(callback.call).called(1);
expect(option.enabled, false, reason: 'Should disable option.');
option.enabled = false;
verifyNever(callback.call); // Don't notify with the same value
expect(option.enabled, false, reason: 'Should keep the value.');
});
test('Reset', () {
final callback = MockCallbackFunction();
option
..value = false
..addListener(callback.call);
expect(option.value, false);
option.reset();
verify(callback.call).called(1);
expect(option.value, option.defaultValue, reason: 'Should reset the value.');
});
});
}
Loading…
Cancel
Save