Implementiert den kompletten Kern-Flow der Emergency App gemäß docs/plan.md: - Notrufnummern-Datenbasis für 199 Länder + Modelle/Repository (AP1) - DialerService für tel:-Notrufe ohne Auto-Dial (AP2) - Statisches UI mit Panik-Button-Fallback (AP3) - Priorisierte Ländererkennung: SIM/Netz -> GPS/Geocoding -> Cache (AP4) - EmergencyProvider verdrahtet Repository/LocationService mit der UI (AP5) - Manuelle Länderauswahl als Fallback samt eigener Priorität (AP6) - UI-Lokalisierung (de/en/fr/es), an erkanntes Land gekoppelt (AP7) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
105 lines
4.2 KiB
Dart
105 lines
4.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:emergency/app.dart';
|
|
import 'package:emergency/features/home/widgets/emergency_button.dart';
|
|
import 'package:emergency/repositories/emergency_number_repository.dart';
|
|
import 'package:emergency/services/country_code_sources/country_code_source.dart';
|
|
import 'package:emergency/services/location_service.dart';
|
|
|
|
/// Einfache manuelle Fake-Quelle statt Mocking-Package (siehe AP4-Vorgabe:
|
|
/// kein neues Mocking-Package einführen), analog zu
|
|
/// `test/services/location_service_test.dart`.
|
|
class _FakeSource implements CountryCodeSource {
|
|
_FakeSource(this._result);
|
|
|
|
final String? _result;
|
|
|
|
@override
|
|
Future<String?> resolve() async => _result;
|
|
}
|
|
|
|
/// Fake-Cache: Verhindert, dass `LocationService` beim Cachen eines
|
|
/// SIM/GPS-Treffers den echten `shared_preferences`-Plugin-Kanal anspricht
|
|
/// (der ohne Mock-Setup in reinen Widget-Tests nicht antwortet).
|
|
class _FakeCache implements CountryCodeCache {
|
|
@override
|
|
Future<String?> resolve() async => null;
|
|
|
|
@override
|
|
Future<void> save(String isoCode) async {}
|
|
}
|
|
|
|
/// Analog zu `_FakeCache`, aber für die manuelle Auswahl (AP6): verhindert
|
|
/// den echten `shared_preferences`-Zugriff, den `LocationService` seit AP6
|
|
/// als vorgeschaltete Prioritätsstufe abfragt.
|
|
class _FakeManualSource implements ManualCountryCodeStore {
|
|
@override
|
|
Future<String?> resolve() async => null;
|
|
|
|
@override
|
|
Future<void> save(String? isoCode) async {}
|
|
}
|
|
|
|
void main() {
|
|
testWidgets('HomeScreen zeigt nach dem Laden mindestens einen EmergencyButton für DE', (
|
|
WidgetTester tester,
|
|
) async {
|
|
// Asset-Laden über rootBundle ist echte I/O und läuft nicht innerhalb des
|
|
// FakeAsync-Zeitrahmens von pump()/pumpAndSettle() durch. Deshalb wird
|
|
// die Datenbasis hier vorab über runAsync geladen (Repository.load() ist
|
|
// danach idempotent, s. Repository). Die Ländererkennung selbst wird
|
|
// über eine gefakte SIM-Quelle deterministisch auf "DE" gesetzt, damit
|
|
// der Test nicht von echten Plattform-Kanälen (SIM/GPS/Cache) abhängt.
|
|
final repository = EmergencyNumberRepository();
|
|
await tester.runAsync(() => repository.load());
|
|
// gpsSource muss explizit gesetzt werden: Ohne eigenen Wert würde
|
|
// LocationService() intern ein echtes GpsCountryCodeSource (und damit
|
|
// Geocoding()) konstruieren, was ohne registriertes Platform-Interface
|
|
// sofort wirft (siehe gps_country_code_source_test.dart). cacheSource
|
|
// wird ebenfalls gefaked, siehe _FakeCache.
|
|
final locationService = LocationService(
|
|
simSource: _FakeSource('DE'),
|
|
gpsSource: _FakeSource(null),
|
|
cacheSource: _FakeCache(),
|
|
manualSource: _FakeManualSource(),
|
|
);
|
|
|
|
await tester.pumpWidget(App(repository: repository, locationService: locationService));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(EmergencyButton), findsWidgets);
|
|
// DE führt "112" bereits als general-Nummer, daher darf der globale
|
|
// Panik-Button hier nicht zusätzlich dieselbe Nummer anzeigen (Dedup).
|
|
expect(find.text('112'), findsOneWidget);
|
|
expect(find.byKey(const Key('panicButton')), findsNothing);
|
|
});
|
|
|
|
testWidgets(
|
|
'HomeScreen zeigt den globalen Panik-Button bereits während des Ladens der Ländererkennung',
|
|
(WidgetTester tester) async {
|
|
// Bewusst kein runAsync/Vorladen des Repositories: Der Ladezustand
|
|
// (EmergencyStatus.initializing) soll direkt nach pumpWidget
|
|
// beobachtbar sein, ohne auf das Ende des echten Asset-Ladens zu warten.
|
|
// Die Ländererkennung wird ohnehin nie erreicht (blockiert beim
|
|
// Repository-Laden), locationService wird trotzdem explizit gefaked,
|
|
// siehe Kommentar im Test oben.
|
|
await tester.pumpWidget(
|
|
App(
|
|
repository: EmergencyNumberRepository(),
|
|
locationService: LocationService(
|
|
simSource: _FakeSource(null),
|
|
gpsSource: _FakeSource(null),
|
|
cacheSource: _FakeCache(),
|
|
manualSource: _FakeManualSource(),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byType(CircularProgressIndicator), findsOneWidget);
|
|
expect(find.byKey(const Key('panicButton')), findsOneWidget);
|
|
expect(find.text('112'), findsOneWidget);
|
|
},
|
|
);
|
|
}
|