Files
emergency/test/features/home/country_picker_screen_test.dart
T
rootandClaude Sonnet 5 e3aa088bc2 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>
2026-07-24 11:29:38 +02:00

147 lines
5.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:emergency/features/home/country_picker_screen.dart';
import 'package:emergency/l10n/app_localizations.dart';
import 'package:emergency/models/country.dart';
import 'package:emergency/models/emergency_number.dart';
import 'package:emergency/models/emergency_type.dart';
import 'package:emergency/providers/emergency_provider.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';
/// Liefert eine feste Länderliste, ohne das echte JSON-Asset laden zu
/// müssen (analog zum Fake-Repository-Muster in `emergency_provider_test.dart`).
class _FakeRepository extends EmergencyNumberRepository {
@override
List<Country> get allCountries => const [
Country(
isoCode: 'DE',
nameDe: 'Deutschland',
nameEn: 'Germany',
flag: '🇩🇪',
numbers: [
EmergencyNumber(type: EmergencyType.general, number: '112', labelDe: 'Notruf', labelEn: 'Emergency'),
],
),
Country(
isoCode: 'FR',
nameDe: 'Frankreich',
nameEn: 'France',
flag: '🇫🇷',
numbers: [
EmergencyNumber(type: EmergencyType.general, number: '112', labelDe: 'Notruf', labelEn: 'Emergency'),
],
),
];
}
class _NoopSource implements CountryCodeSource {
const _NoopSource();
@override
Future<String?> resolve() async => null;
}
/// Fake-Provider: überschreibt `selectCountryManually`, damit der Test ohne
/// echte `LocationService`-Ermittlungskette prüfen kann, mit welchem
/// ISO-Code der Screen die Auswahl auslöst.
class _FakeProvider extends EmergencyProvider {
_FakeProvider({required super.repository})
: super(
locationService: LocationService(
simSource: const _NoopSource(),
gpsSource: const _NoopSource(),
),
);
String? selectedIsoCode;
int selectCallCount = 0;
@override
Future<void> selectCountryManually(String isoCode) async {
selectCallCount++;
selectedIsoCode = isoCode;
}
}
void main() {
Future<void> pumpPickerBehindAButton(WidgetTester tester, EmergencyProvider provider) async {
// CountryPickerScreen ruft `Navigator.pop()` beim Auswählen auf dafür
// muss eine vorherige Route existieren, sonst wäre der Pop ein No-op.
await tester.pumpWidget(
ChangeNotifierProvider<EmergencyProvider>.value(
value: provider,
child: MaterialApp(
// Feste Locale, damit die (unveränderten) deutschen Text-Assertions
// dieser Tests unabhängig von der Test-Umgebungs-/Systemsprache
// funktionieren; das eigentliche Umschalten der App-Sprache wird
// gezielt in `test/core/country_locale_map_test.dart` bzw.
// `test/app_locale_test.dart` (AP7) geprüft.
locale: const Locale('de'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Builder(
builder: (context) => ElevatedButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(builder: (_) => const CountryPickerScreen()),
),
child: const Text('Öffnen'),
),
),
),
),
);
await tester.tap(find.text('Öffnen'));
await tester.pumpAndSettle();
}
testWidgets('zeigt die Länderliste an', (tester) async {
final provider = _FakeProvider(repository: _FakeRepository());
await pumpPickerBehindAButton(tester, provider);
expect(find.text('Deutschland'), findsOneWidget);
expect(find.text('Frankreich'), findsOneWidget);
});
testWidgets('Suchfeld filtert die Länderliste', (tester) async {
final provider = _FakeProvider(repository: _FakeRepository());
await pumpPickerBehindAButton(tester, provider);
await tester.enterText(find.byType(TextField), 'Deutsch');
await tester.pump();
expect(find.text('Deutschland'), findsOneWidget);
expect(find.text('Frankreich'), findsNothing);
});
testWidgets('zeigt einen Hinweis, wenn kein Land zum Filter passt', (tester) async {
final provider = _FakeProvider(repository: _FakeRepository());
await pumpPickerBehindAButton(tester, provider);
await tester.enterText(find.byType(TextField), 'Nichtland');
await tester.pump();
expect(find.text('Kein Land gefunden.'), findsOneWidget);
});
testWidgets('Tap auf ein Land löst selectCountryManually aus und schließt den Screen', (
tester,
) async {
final provider = _FakeProvider(repository: _FakeRepository());
await pumpPickerBehindAButton(tester, provider);
await tester.tap(find.text('Deutschland'));
await tester.pumpAndSettle();
expect(provider.selectCallCount, 1);
expect(provider.selectedIsoCode, 'DE');
// Screen wurde geschlossen (zurück zum "Öffnen"-Button).
expect(find.text('Öffnen'), findsOneWidget);
expect(find.text('Deutschland'), findsNothing);
});
}