mirror of
https://github.com/immich-app/immich.git
synced 2025-06-03 05:34:32 -04:00
chore(mobile): Favorite provider unit test (#1874)
* Favorite provider tests * Remove unused mock * Add setUp function to avoid duplicate code
This commit is contained in:
parent
807bdfeda9
commit
c9a6820de7
@ -3,14 +3,15 @@ import 'package:immich_mobile/shared/models/asset.dart';
|
|||||||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||||
|
|
||||||
class FavoriteSelectionNotifier extends StateNotifier<Set<String>> {
|
class FavoriteSelectionNotifier extends StateNotifier<Set<String>> {
|
||||||
FavoriteSelectionNotifier(this.ref) : super({}) {
|
FavoriteSelectionNotifier(this.assetsState, this.assetNotifier) : super({}) {
|
||||||
state = ref.watch(assetProvider).allAssets
|
state = assetsState.allAssets
|
||||||
.where((asset) => asset.isFavorite)
|
.where((asset) => asset.isFavorite)
|
||||||
.map((asset) => asset.id)
|
.map((asset) => asset.id)
|
||||||
.toSet();
|
.toSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Ref ref;
|
final AssetsState assetsState;
|
||||||
|
final AssetNotifier assetNotifier;
|
||||||
|
|
||||||
void _setFavoriteForAssetId(String id, bool favorite) {
|
void _setFavoriteForAssetId(String id, bool favorite) {
|
||||||
if (!favorite) {
|
if (!favorite) {
|
||||||
@ -29,7 +30,7 @@ class FavoriteSelectionNotifier extends StateNotifier<Set<String>> {
|
|||||||
|
|
||||||
_setFavoriteForAssetId(asset.id, !_isFavorite(asset.id));
|
_setFavoriteForAssetId(asset.id, !_isFavorite(asset.id));
|
||||||
|
|
||||||
await ref.watch(assetProvider.notifier).toggleFavorite(
|
await assetNotifier.toggleFavorite(
|
||||||
asset,
|
asset,
|
||||||
state.contains(asset.id),
|
state.contains(asset.id),
|
||||||
);
|
);
|
||||||
@ -37,8 +38,8 @@ class FavoriteSelectionNotifier extends StateNotifier<Set<String>> {
|
|||||||
|
|
||||||
Future<void> addToFavorites(Iterable<Asset> assets) {
|
Future<void> addToFavorites(Iterable<Asset> assets) {
|
||||||
state = state.union(assets.map((a) => a.id).toSet());
|
state = state.union(assets.map((a) => a.id).toSet());
|
||||||
final futures = assets.map((a) =>
|
final futures = assets.map((a) =>
|
||||||
ref.watch(assetProvider.notifier).toggleFavorite(
|
assetNotifier.toggleFavorite(
|
||||||
a,
|
a,
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
@ -50,7 +51,10 @@ class FavoriteSelectionNotifier extends StateNotifier<Set<String>> {
|
|||||||
|
|
||||||
final favoriteProvider =
|
final favoriteProvider =
|
||||||
StateNotifierProvider<FavoriteSelectionNotifier, Set<String>>((ref) {
|
StateNotifierProvider<FavoriteSelectionNotifier, Set<String>>((ref) {
|
||||||
return FavoriteSelectionNotifier(ref);
|
return FavoriteSelectionNotifier(
|
||||||
|
ref.watch(assetProvider),
|
||||||
|
ref.watch(assetProvider.notifier),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
final favoriteAssetProvider = StateProvider((ref) {
|
final favoriteAssetProvider = StateProvider((ref) {
|
||||||
|
@ -740,6 +740,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
|
mockito:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: mockito
|
||||||
|
sha256: "2a8a17b82b1bde04d514e75d90d634a0ac23f6cb4991f6098009dd56836aeafe"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.3.2"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -64,6 +64,7 @@ dev_dependencies:
|
|||||||
flutter_launcher_icons: "^0.9.2"
|
flutter_launcher_icons: "^0.9.2"
|
||||||
flutter_native_splash: ^2.2.16
|
flutter_native_splash: ^2.2.16
|
||||||
isar_generator: *isar_version
|
isar_generator: *isar_version
|
||||||
|
mockito: ^5.3.2
|
||||||
integration_test:
|
integration_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
104
mobile/test/favorite_provider_test.dart
Normal file
104
mobile/test/favorite_provider_test.dart
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/modules/favorite/providers/favorite_provider.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/asset.dart';
|
||||||
|
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||||
|
import 'package:mockito/annotations.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
|
||||||
|
@GenerateNiceMocks([
|
||||||
|
MockSpec<AssetsState>(),
|
||||||
|
MockSpec<AssetNotifier>(),
|
||||||
|
])
|
||||||
|
import 'favorite_provider_test.mocks.dart';
|
||||||
|
|
||||||
|
Asset _getTestAsset(String id, bool favorite) {
|
||||||
|
return Asset(
|
||||||
|
remoteId: id,
|
||||||
|
deviceAssetId: '',
|
||||||
|
deviceId: '',
|
||||||
|
ownerId: '',
|
||||||
|
fileCreatedAt: DateTime.now(),
|
||||||
|
fileModifiedAt: DateTime.now(),
|
||||||
|
durationInSeconds: 0,
|
||||||
|
fileName: '',
|
||||||
|
isFavorite: favorite,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group("Test favoriteProvider", () {
|
||||||
|
|
||||||
|
late MockAssetsState assetsState;
|
||||||
|
late MockAssetNotifier assetNotifier;
|
||||||
|
late ProviderContainer container;
|
||||||
|
late StateNotifierProvider<FavoriteSelectionNotifier, Set<String>> testFavoritesProvider;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
assetsState = MockAssetsState();
|
||||||
|
assetNotifier = MockAssetNotifier();
|
||||||
|
container = ProviderContainer();
|
||||||
|
|
||||||
|
testFavoritesProvider =
|
||||||
|
StateNotifierProvider<FavoriteSelectionNotifier, Set<String>>((ref) {
|
||||||
|
return FavoriteSelectionNotifier(
|
||||||
|
assetsState,
|
||||||
|
assetNotifier,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},);
|
||||||
|
|
||||||
|
test("Empty favorites provider", () {
|
||||||
|
when(assetsState.allAssets).thenReturn([]);
|
||||||
|
expect(<String>{}, container.read(testFavoritesProvider));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Non-empty favorites provider", () {
|
||||||
|
when(assetsState.allAssets).thenReturn([
|
||||||
|
_getTestAsset("001", false),
|
||||||
|
_getTestAsset("002", true),
|
||||||
|
_getTestAsset("003", false),
|
||||||
|
_getTestAsset("004", false),
|
||||||
|
_getTestAsset("005", true),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(<String>{"002", "005"}, container.read(testFavoritesProvider));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Toggle favorite", () {
|
||||||
|
when(assetNotifier.toggleFavorite(null, false))
|
||||||
|
.thenAnswer((_) async => false);
|
||||||
|
|
||||||
|
final testAsset1 = _getTestAsset("001", false);
|
||||||
|
final testAsset2 = _getTestAsset("002", true);
|
||||||
|
|
||||||
|
when(assetsState.allAssets).thenReturn([testAsset1, testAsset2]);
|
||||||
|
|
||||||
|
expect(<String>{"002"}, container.read(testFavoritesProvider));
|
||||||
|
|
||||||
|
container.read(testFavoritesProvider.notifier).toggleFavorite(testAsset2);
|
||||||
|
expect(<String>{}, container.read(testFavoritesProvider));
|
||||||
|
|
||||||
|
container.read(testFavoritesProvider.notifier).toggleFavorite(testAsset1);
|
||||||
|
expect(<String>{"001"}, container.read(testFavoritesProvider));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Add favorites", () {
|
||||||
|
when(assetNotifier.toggleFavorite(null, false))
|
||||||
|
.thenAnswer((_) async => false);
|
||||||
|
|
||||||
|
when(assetsState.allAssets).thenReturn([]);
|
||||||
|
|
||||||
|
expect(<String>{}, container.read(testFavoritesProvider));
|
||||||
|
|
||||||
|
container.read(testFavoritesProvider.notifier).addToFavorites(
|
||||||
|
[
|
||||||
|
_getTestAsset("001", false),
|
||||||
|
_getTestAsset("002", false),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(<String>{"001", "002"}, container.read(testFavoritesProvider));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
259
mobile/test/favorite_provider_test.mocks.dart
Normal file
259
mobile/test/favorite_provider_test.mocks.dart
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
// Mocks generated by Mockito 5.3.2 from annotations
|
||||||
|
// in immich_mobile/test/favorite_provider_test.dart.
|
||||||
|
// Do not manually edit this file.
|
||||||
|
|
||||||
|
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||||
|
import 'dart:async' as _i5;
|
||||||
|
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart' as _i7;
|
||||||
|
import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'
|
||||||
|
as _i6;
|
||||||
|
import 'package:immich_mobile/shared/models/asset.dart' as _i4;
|
||||||
|
import 'package:immich_mobile/shared/providers/asset.provider.dart' as _i2;
|
||||||
|
import 'package:logging/logging.dart' as _i3;
|
||||||
|
import 'package:mockito/mockito.dart' as _i1;
|
||||||
|
import 'package:state_notifier/state_notifier.dart' as _i8;
|
||||||
|
|
||||||
|
// 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: implementation_imports
|
||||||
|
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
||||||
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
// ignore_for_file: unnecessary_parenthesis
|
||||||
|
// ignore_for_file: camel_case_types
|
||||||
|
// ignore_for_file: subtype_of_sealed_class
|
||||||
|
|
||||||
|
class _FakeAssetsState_0 extends _i1.SmartFake implements _i2.AssetsState {
|
||||||
|
_FakeAssetsState_0(
|
||||||
|
Object parent,
|
||||||
|
Invocation parentInvocation,
|
||||||
|
) : super(
|
||||||
|
parent,
|
||||||
|
parentInvocation,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FakeLogger_1 extends _i1.SmartFake implements _i3.Logger {
|
||||||
|
_FakeLogger_1(
|
||||||
|
Object parent,
|
||||||
|
Invocation parentInvocation,
|
||||||
|
) : super(
|
||||||
|
parent,
|
||||||
|
parentInvocation,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A class which mocks [AssetsState].
|
||||||
|
///
|
||||||
|
/// See the documentation for Mockito's code generation for more information.
|
||||||
|
class MockAssetsState extends _i1.Mock implements _i2.AssetsState {
|
||||||
|
@override
|
||||||
|
List<_i4.Asset> get allAssets => (super.noSuchMethod(
|
||||||
|
Invocation.getter(#allAssets),
|
||||||
|
returnValue: <_i4.Asset>[],
|
||||||
|
returnValueForMissingStub: <_i4.Asset>[],
|
||||||
|
) as List<_i4.Asset>);
|
||||||
|
@override
|
||||||
|
_i5.Future<_i2.AssetsState> withRenderDataStructure(
|
||||||
|
_i6.AssetGridLayoutParameters? layout) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#withRenderDataStructure,
|
||||||
|
[layout],
|
||||||
|
),
|
||||||
|
returnValue: _i5.Future<_i2.AssetsState>.value(_FakeAssetsState_0(
|
||||||
|
this,
|
||||||
|
Invocation.method(
|
||||||
|
#withRenderDataStructure,
|
||||||
|
[layout],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
returnValueForMissingStub:
|
||||||
|
_i5.Future<_i2.AssetsState>.value(_FakeAssetsState_0(
|
||||||
|
this,
|
||||||
|
Invocation.method(
|
||||||
|
#withRenderDataStructure,
|
||||||
|
[layout],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
) as _i5.Future<_i2.AssetsState>);
|
||||||
|
@override
|
||||||
|
_i2.AssetsState withAdditionalAssets(List<_i4.Asset>? toAdd) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#withAdditionalAssets,
|
||||||
|
[toAdd],
|
||||||
|
),
|
||||||
|
returnValue: _FakeAssetsState_0(
|
||||||
|
this,
|
||||||
|
Invocation.method(
|
||||||
|
#withAdditionalAssets,
|
||||||
|
[toAdd],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: _FakeAssetsState_0(
|
||||||
|
this,
|
||||||
|
Invocation.method(
|
||||||
|
#withAdditionalAssets,
|
||||||
|
[toAdd],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) as _i2.AssetsState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A class which mocks [AssetNotifier].
|
||||||
|
///
|
||||||
|
/// See the documentation for Mockito's code generation for more information.
|
||||||
|
class MockAssetNotifier extends _i1.Mock implements _i2.AssetNotifier {
|
||||||
|
@override
|
||||||
|
_i3.Logger get log => (super.noSuchMethod(
|
||||||
|
Invocation.getter(#log),
|
||||||
|
returnValue: _FakeLogger_1(
|
||||||
|
this,
|
||||||
|
Invocation.getter(#log),
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: _FakeLogger_1(
|
||||||
|
this,
|
||||||
|
Invocation.getter(#log),
|
||||||
|
),
|
||||||
|
) as _i3.Logger);
|
||||||
|
@override
|
||||||
|
set onError(_i7.ErrorListener? _onError) => super.noSuchMethod(
|
||||||
|
Invocation.setter(
|
||||||
|
#onError,
|
||||||
|
_onError,
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: null,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
bool get mounted => (super.noSuchMethod(
|
||||||
|
Invocation.getter(#mounted),
|
||||||
|
returnValue: false,
|
||||||
|
returnValueForMissingStub: false,
|
||||||
|
) as bool);
|
||||||
|
@override
|
||||||
|
_i5.Stream<_i2.AssetsState> get stream => (super.noSuchMethod(
|
||||||
|
Invocation.getter(#stream),
|
||||||
|
returnValue: _i5.Stream<_i2.AssetsState>.empty(),
|
||||||
|
returnValueForMissingStub: _i5.Stream<_i2.AssetsState>.empty(),
|
||||||
|
) as _i5.Stream<_i2.AssetsState>);
|
||||||
|
@override
|
||||||
|
_i2.AssetsState get state => (super.noSuchMethod(
|
||||||
|
Invocation.getter(#state),
|
||||||
|
returnValue: _FakeAssetsState_0(
|
||||||
|
this,
|
||||||
|
Invocation.getter(#state),
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: _FakeAssetsState_0(
|
||||||
|
this,
|
||||||
|
Invocation.getter(#state),
|
||||||
|
),
|
||||||
|
) as _i2.AssetsState);
|
||||||
|
@override
|
||||||
|
set state(_i2.AssetsState? value) => super.noSuchMethod(
|
||||||
|
Invocation.setter(
|
||||||
|
#state,
|
||||||
|
value,
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: null,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
_i2.AssetsState get debugState => (super.noSuchMethod(
|
||||||
|
Invocation.getter(#debugState),
|
||||||
|
returnValue: _FakeAssetsState_0(
|
||||||
|
this,
|
||||||
|
Invocation.getter(#debugState),
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: _FakeAssetsState_0(
|
||||||
|
this,
|
||||||
|
Invocation.getter(#debugState),
|
||||||
|
),
|
||||||
|
) as _i2.AssetsState);
|
||||||
|
@override
|
||||||
|
bool get hasListeners => (super.noSuchMethod(
|
||||||
|
Invocation.getter(#hasListeners),
|
||||||
|
returnValue: false,
|
||||||
|
returnValueForMissingStub: false,
|
||||||
|
) as bool);
|
||||||
|
@override
|
||||||
|
_i5.Future<void> rebuildAssetGridDataStructure() => (super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#rebuildAssetGridDataStructure,
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
returnValue: _i5.Future<void>.value(),
|
||||||
|
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||||
|
) as _i5.Future<void>);
|
||||||
|
@override
|
||||||
|
void onNewAssetUploaded(_i4.Asset? newAsset) => super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#onNewAssetUploaded,
|
||||||
|
[newAsset],
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: null,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
dynamic deleteAssets(Set<_i4.Asset>? deleteAssets) => super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#deleteAssets,
|
||||||
|
[deleteAssets],
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: null,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
_i5.Future<bool> toggleFavorite(
|
||||||
|
_i4.Asset? asset,
|
||||||
|
bool? status,
|
||||||
|
) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#toggleFavorite,
|
||||||
|
[
|
||||||
|
asset,
|
||||||
|
status,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
returnValue: _i5.Future<bool>.value(false),
|
||||||
|
returnValueForMissingStub: _i5.Future<bool>.value(false),
|
||||||
|
) as _i5.Future<bool>);
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(
|
||||||
|
_i2.AssetsState? old,
|
||||||
|
_i2.AssetsState? current,
|
||||||
|
) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#updateShouldNotify,
|
||||||
|
[
|
||||||
|
old,
|
||||||
|
current,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
returnValue: false,
|
||||||
|
returnValueForMissingStub: false,
|
||||||
|
) as bool);
|
||||||
|
@override
|
||||||
|
_i7.RemoveListener addListener(
|
||||||
|
_i8.Listener<_i2.AssetsState>? listener, {
|
||||||
|
bool? fireImmediately = true,
|
||||||
|
}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#addListener,
|
||||||
|
[listener],
|
||||||
|
{#fireImmediately: fireImmediately},
|
||||||
|
),
|
||||||
|
returnValue: () {},
|
||||||
|
returnValueForMissingStub: () {},
|
||||||
|
) as _i7.RemoveListener);
|
||||||
|
@override
|
||||||
|
void dispose() => super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#dispose,
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
returnValueForMissingStub: null,
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user