import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:geolocator/geolocator.dart'; import 'package:tasq/models/app_settings.dart'; import 'package:tasq/models/profile.dart'; import 'package:tasq/providers/profile_provider.dart'; import 'package:tasq/providers/workforce_provider.dart'; import 'package:tasq/providers/location_provider.dart'; import 'package:tasq/screens/admin/geofence_test_screen.dart'; void main() { // Polygon from existing unit test (CRMC) final polygonJson = [ {"lat": 7.2025231, "lng": 124.2339774}, {"lat": 7.2018791, "lng": 124.2334463}, {"lat": 7.2013362, "lng": 124.2332049}, {"lat": 7.2011393, "lng": 124.2332907}, {"lat": 7.2009531, "lng": 124.2334195}, {"lat": 7.200655, "lng": 124.2339344}, {"lat": 7.2000749, "lng": 124.2348465}, {"lat": 7.199501, "lng": 124.2357645}, {"lat": 7.1990597, "lng": 124.2364595}, {"lat": 7.1986557, "lng": 124.2371331}, {"lat": 7.1992252, "lng": 124.237168}, {"lat": 7.199494, "lng": 124.2372713}, {"lat": 7.1997415, "lng": 124.2374604}, {"lat": 7.1999383, "lng": 124.2377071}, {"lat": 7.2001938, "lng": 124.2380934}, {"lat": 7.2011411, "lng": 124.2364357}, {"lat": 7.2025231, "lng": 124.2339774}, ]; ProviderScope buildApp({required List overrides}) { return ProviderScope( overrides: overrides, child: const MaterialApp(home: GeofenceTestScreen()), ); } testWidgets('non-admin cannot access', (tester) async { await tester.pumpWidget( buildApp(overrides: [isAdminProvider.overrideWith((ref) => false)]), ); await tester.pump(); expect(find.text('Admin access required.'), findsOneWidget); }); testWidgets('polygon geofence - inside shown', (tester) async { final profile = Profile(id: 'p1', role: 'admin', fullName: 'Admin'); final insidePos = Position( latitude: 7.2009, longitude: 124.2360, timestamp: DateTime.now(), accuracy: 1.0, altitude: 0.0, altitudeAccuracy: 0.0, heading: 0.0, speed: 0.0, speedAccuracy: 0.0, headingAccuracy: 0.0, ); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), isAdminProvider.overrideWith((ref) => true), geofenceProvider.overrideWith( (ref) => Future.value(GeofenceConfig.fromJson({'polygon': polygonJson})), ), currentPositionProvider.overrideWith( (ref) => Future.value(insidePos), ), ], ), ); await tester.pumpAndSettle(); expect(find.text('Inside geofence'), findsOneWidget); expect(find.byIcon(Icons.check_circle), findsOneWidget); }); testWidgets('polygon geofence - outside shown', (tester) async { final profile = Profile(id: 'p1', role: 'admin', fullName: 'Admin'); final outsidePos = Position( latitude: 7.2060, longitude: 124.2360, timestamp: DateTime.now(), accuracy: 1.0, altitude: 0.0, altitudeAccuracy: 0.0, heading: 0.0, speed: 0.0, speedAccuracy: 0.0, headingAccuracy: 0.0, ); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), isAdminProvider.overrideWith((ref) => true), geofenceProvider.overrideWith( (ref) => Future.value(GeofenceConfig.fromJson({'polygon': polygonJson})), ), currentPositionProvider.overrideWith( (ref) => Future.value(outsidePos), ), ], ), ); await tester.pumpAndSettle(); expect(find.text('Outside geofence'), findsOneWidget); expect(find.byIcon(Icons.cancel), findsOneWidget); }); testWidgets('circle geofence - inside shown', (tester) async { final profile = Profile(id: 'p1', role: 'admin', fullName: 'Admin'); final cfg = GeofenceConfig(lat: 0.0, lng: 0.0, radiusMeters: 1000.0); final insidePos = Position( latitude: 0.005, longitude: 0.0, timestamp: DateTime.now(), accuracy: 1.0, altitude: 0.0, altitudeAccuracy: 0.0, heading: 0.0, speed: 0.0, speedAccuracy: 0.0, headingAccuracy: 0.0, ); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), isAdminProvider.overrideWith((ref) => true), geofenceProvider.overrideWith((ref) => Future.value(cfg)), currentPositionProvider.overrideWith( (ref) => Future.value(insidePos), ), ], ), ); await tester.pumpAndSettle(); expect(find.text('Inside geofence'), findsOneWidget); expect(find.byIcon(Icons.check_circle), findsOneWidget); }); testWidgets('circle geofence - outside shown', (tester) async { final profile = Profile(id: 'p1', role: 'admin', fullName: 'Admin'); final cfg = GeofenceConfig(lat: 0.0, lng: 0.0, radiusMeters: 1000.0); final outsidePos = Position( latitude: 0.02, longitude: 0.0, timestamp: DateTime.now(), accuracy: 1.0, altitude: 0.0, altitudeAccuracy: 0.0, heading: 0.0, speed: 0.0, speedAccuracy: 0.0, headingAccuracy: 0.0, ); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), isAdminProvider.overrideWith((ref) => true), geofenceProvider.overrideWith((ref) => Future.value(cfg)), currentPositionProvider.overrideWith( (ref) => Future.value(outsidePos), ), ], ), ); await tester.pumpAndSettle(); expect(find.text('Outside geofence'), findsOneWidget); expect(find.byIcon(Icons.cancel), findsOneWidget); }); testWidgets('live tracking toggle follows stream', (tester) async { final profile = Profile(id: 'p1', role: 'admin', fullName: 'Admin'); final cfg = GeofenceConfig(lat: 0.0, lng: 0.0, radiusMeters: 1000.0); final initialPos = Position( latitude: 0.02, longitude: 0.0, timestamp: DateTime.now(), accuracy: 1.0, altitude: 0.0, altitudeAccuracy: 0.0, heading: 0.0, speed: 0.0, speedAccuracy: 0.0, headingAccuracy: 0.0, ); final livePos = Position( latitude: 0.005, longitude: 0.0, timestamp: DateTime.now(), accuracy: 1.0, altitude: 0.0, altitudeAccuracy: 0.0, heading: 0.0, speed: 0.0, speedAccuracy: 0.0, headingAccuracy: 0.0, ); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), isAdminProvider.overrideWith((ref) => true), geofenceProvider.overrideWith((ref) => Future.value(cfg)), currentPositionProvider.overrideWith( (ref) => Future.value(initialPos), ), currentPositionStreamProvider.overrideWith( (ref) => Stream.value(livePos), ), ], ), ); await tester.pumpAndSettle(); // initial state is the one-shot position (outside) expect(find.text('Outside geofence'), findsOneWidget); // enable live tracking await tester.tap(find.byKey(const Key('live-switch'))); await tester.pumpAndSettle(); // stream value should now be used and indicate inside expect(find.text('Inside geofence'), findsOneWidget); expect(find.byIcon(Icons.check_circle), findsOneWidget); }); }