Notrufnummern-Datenbasis, Ländererkennung, State-Management und Lokalisierung (AP1-AP7)
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>
This commit is contained in:
+95
-21
@@ -1,30 +1,104 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:emergency/main.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('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(const MyApp());
|
||||
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(),
|
||||
);
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
await tester.pumpWidget(App(repository: repository, locationService: locationService));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
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);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user