test(location): add LocationMapController unit tests (22 cases)
This commit is contained in:
@@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user