test(location): add LocationMapController unit tests (22 cases)

This commit is contained in:
2026-04-26 08:25:18 +02:00
parent 04b8d1609c
commit a9a34fbbc5

View File

@@ -0,0 +1,311 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:latlong2/latlong.dart';
import 'package:location/src/core/domain/entities/geofence_entity.dart';
import 'package:location/src/core/domain/entities/frequent_place_entity.dart';
import 'package:location/src/features/location/presentation/providers/location_map_controller.dart';
import 'package:location/src/features/location/presentation/providers/location_map_state.dart';
import 'package:sf_shared/testing.dart';
import 'package:sf_tracking/sf_tracking.dart';
const _geofence = GeofenceEntity(
id: 'g1',
name: 'Home',
latitude: 40.0,
longitude: -3.0,
radius: 200,
isActive: true,
createdAt: 0,
);
const _frequentPlace = FrequentPlaceEntity(
id: 'f1',
name: 'School',
lat: 41.0,
lng: -3.5,
createdAt: 0,
);
ProviderContainer buildContainer() {
return makeContainer(
overrides: [
sfTrackingProvider.overrideWithValue(
SfTrackingRepository(clients: const []),
),
],
);
}
LocationMapState readState(ProviderContainer container) =>
container.read(locationMapControllerProvider);
LocationMapController readNotifier(ProviderContainer container) =>
container.read(locationMapControllerProvider.notifier);
void main() {
group('history navigation', () {
test('startHistoryNavigation sets index to 0', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
expect(readState(container).historyNavigationIndex, 0);
expect(readState(container).historyPlaying, false);
});
test('stopHistoryNavigation resets index to -1', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).stopHistoryNavigation();
expect(readState(container).historyNavigationIndex, -1);
expect(readState(container).historyPlaying, false);
});
test('nextHistoryPosition increments index', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).nextHistoryPosition(10);
expect(readState(container).historyNavigationIndex, 1);
});
test('nextHistoryPosition at end sets historyPlaying false', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).nextHistoryPosition(2);
readNotifier(container).nextHistoryPosition(2);
expect(readState(container).historyNavigationIndex, 1);
expect(readState(container).historyPlaying, false);
});
test('previousHistoryPosition decrements index', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).nextHistoryPosition(10);
readNotifier(container).nextHistoryPosition(10);
readNotifier(container).previousHistoryPosition();
expect(readState(container).historyNavigationIndex, 1);
});
test('previousHistoryPosition at 0 stays at 0', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).previousHistoryPosition();
expect(readState(container).historyNavigationIndex, 0);
});
});
group('history playback', () {
test('toggleHistoryPlayback activates playback', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).toggleHistoryPlayback(10);
expect(readState(container).historyPlaying, true);
});
test('toggleHistoryPlayback deactivates playback', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).toggleHistoryPlayback(10);
readNotifier(container).toggleHistoryPlayback(10);
expect(readState(container).historyPlaying, false);
});
test('toggleHistoryPlayback at end restarts from 0', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).nextHistoryPosition(3);
readNotifier(container).nextHistoryPosition(3);
readNotifier(container).toggleHistoryPlayback(3);
expect(readState(container).historyNavigationIndex, 0);
expect(readState(container).historyPlaying, true);
});
test('stopPlaybackTimer cancels timer', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).toggleHistoryPlayback(10);
readNotifier(container).stopPlaybackTimer();
expect(readState(container).historyPlaying, true);
});
test('startPlaybackTimer sets up timer and stopPlaybackTimer clears it', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startHistoryNavigation();
readNotifier(container).toggleHistoryPlayback(10);
expect(readState(container).historyPlaying, true);
readNotifier(container).stopPlaybackTimer();
readNotifier(container).stopPlaybackTimer();
});
});
group('reveal animation', () {
test('startReveal sets animating true and count 1', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startReveal();
expect(readState(container).isRevealAnimating, true);
expect(readState(container).historyRevealCount, 1);
});
test('updateRevealCount updates count', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startReveal();
readNotifier(container).updateRevealCount(5);
expect(readState(container).historyRevealCount, 5);
});
test('finishReveal clears animation state', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startReveal();
readNotifier(container).finishReveal();
expect(readState(container).isRevealAnimating, false);
expect(readState(container).historyRevealCount, isNull);
});
});
group('geofence editing', () {
test('startEditingGeofence enters placement mode', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startEditingGeofence(_geofence);
final state = readState(container);
expect(state.placingMode, PlacingMode.geofence);
expect(state.editingGeofence, _geofence);
expect(state.previewPoint, LatLng(40.0, -3.0));
expect(state.previewRadius, 200);
expect(state.selectedGeofence, isNull);
});
test('confirmGeofencePlacement preserves editing radius', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startEditingGeofence(_geofence);
readNotifier(container).confirmGeofencePlacement(LatLng(41.0, -2.0));
final state = readState(container);
expect(state.placingMode, PlacingMode.none);
expect(state.adjustingRadius, true);
expect(state.previewPoint, LatLng(41.0, -2.0));
expect(state.previewRadius, 200);
});
test('confirmGeofencePlacement defaults radius 200 for new geofence', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startPlacing(PlacingMode.geofence);
readNotifier(container).confirmGeofencePlacement(LatLng(41.0, -2.0));
expect(readState(container).previewRadius, 200);
});
test('cancelPlacing clears editing state', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startEditingGeofence(_geofence);
readNotifier(container).cancelPlacing();
final state = readState(container);
expect(state.placingMode, PlacingMode.none);
expect(state.editingGeofence, isNull);
expect(state.editingFrequentPlace, isNull);
expect(state.previewPoint, isNull);
});
});
group('frequent place editing', () {
test('startEditingFrequentPlace enters placement mode', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startEditingFrequentPlace(_frequentPlace);
final state = readState(container);
expect(state.placingMode, PlacingMode.frequentPlace);
expect(state.editingFrequentPlace, _frequentPlace);
expect(state.previewPoint, LatLng(41.0, -3.5));
expect(state.selectedFrequentPlace, isNull);
});
test('confirmFrequentPlacePlacement clears editing state', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).startEditingFrequentPlace(_frequentPlace);
readNotifier(container).confirmFrequentPlacePlacement();
final state = readState(container);
expect(state.placingMode, PlacingMode.none);
expect(state.editingFrequentPlace, isNull);
expect(state.previewPoint, isNull);
});
});
group('recenter', () {
test('scheduleRecenter does nothing when not following', () {
final container = buildContainer();
addTearDown(container.dispose);
expect(readState(container).isFollowing, false);
var called = false;
readNotifier(container).scheduleRecenter(() => called = true);
expect(called, false);
});
test('cancelRecenter does not throw', () {
final container = buildContainer();
addTearDown(container.dispose);
readNotifier(container).toggleFollowing();
readNotifier(container).scheduleRecenter(() {});
readNotifier(container).cancelRecenter();
readNotifier(container).cancelRecenter();
});
});
}