- Einladen per einmaligem 6-stelligem Code (7 Tage gültig), wahlweise als Mitglied oder Pflanzen-Sitter - Beitritt über Cloud Function joinHousehold (Transaktion, Admin-Rechte) - Security Rules: Sitter dürfen an Pflanzen nur Bestätigungs-Felder ändern; invites nur erstellen, nie lesen - Haushalts-Screen: Mitgliederliste mit Rollen, Einladen, Beitreten mit Wechsel-Warnung - UI-Gating: Sitter sehen keine Anlegen-/Bearbeiten-/Löschen-Aktionen - Bestätigungen speichern den Namen (lastWateredBy/lastFertilizedBy), Anzeige im Profil - Tests: Members-Map im Seed, neuer Sitter-Test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
110 lines
3.7 KiB
Dart
110 lines
3.7 KiB
Dart
import 'package:cloud_firestore/cloud_firestore.dart';
|
||
import 'package:fake_cloud_firestore/fake_cloud_firestore.dart';
|
||
import 'package:firebase_auth_mocks/firebase_auth_mocks.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:firebase_auth/firebase_auth.dart';
|
||
import 'package:leafittome/app.dart';
|
||
import 'package:leafittome/core/firebase/firebase_providers.dart';
|
||
import 'package:leafittome/core/settings/settings_provider.dart';
|
||
import 'package:leafittome/features/notifications/data/push_registration_service.dart';
|
||
import 'package:shared_preferences/shared_preferences.dart';
|
||
|
||
/// Tests brauchen kein echtes FCM – Push-Registrierung ist ein No-Op.
|
||
class _FakePushRegistrationService implements PushRegistrationService {
|
||
@override
|
||
Future<void> registerDevice(User user) async {}
|
||
|
||
@override
|
||
Future<void> syncReminderTime(TimeOfDay time) async {}
|
||
}
|
||
|
||
/// Baut die App mit gemocktem Firebase: angemeldeter Nutzer 'u1' im Haushalt
|
||
/// 'h1' mit einer Monstera, deren Gießen seit einem Tag überfällig ist.
|
||
Future<Widget> buildTestApp({String role = 'member'}) async {
|
||
SharedPreferences.setMockInitialValues({});
|
||
final prefs = await SharedPreferences.getInstance();
|
||
|
||
final auth = MockFirebaseAuth(
|
||
signedIn: true,
|
||
mockUser: MockUser(uid: 'u1', email: 'test@example.com'),
|
||
);
|
||
|
||
final firestore = FakeFirebaseFirestore();
|
||
await firestore.collection('users').doc('u1').set({
|
||
'email': 'test@example.com',
|
||
'householdId': 'h1',
|
||
});
|
||
await firestore.collection('households').doc('h1').set({
|
||
'name': 'Mein Haushalt',
|
||
'memberUids': ['u1'],
|
||
'members': {
|
||
'u1': {'role': role, 'email': 'test@example.com'},
|
||
},
|
||
});
|
||
final now = DateTime.now();
|
||
await firestore
|
||
.collection('households')
|
||
.doc('h1')
|
||
.collection('plants')
|
||
.add({
|
||
'nickname': 'Monstera',
|
||
'species': 'Monstera deliciosa',
|
||
'locationId': null,
|
||
'description': '',
|
||
'careNotes': '',
|
||
'wateringIntervalDays': 7,
|
||
'fertilizingIntervalDays': 28,
|
||
'lastWatered':
|
||
Timestamp.fromDate(now.subtract(const Duration(days: 8))),
|
||
'lastFertilized':
|
||
Timestamp.fromDate(now.subtract(const Duration(days: 3))),
|
||
});
|
||
|
||
return ProviderScope(
|
||
overrides: [
|
||
sharedPreferencesProvider.overrideWithValue(prefs),
|
||
firebaseAuthProvider.overrideWithValue(auth),
|
||
firestoreProvider.overrideWithValue(firestore),
|
||
pushRegistrationServiceProvider
|
||
.overrideWithValue(_FakePushRegistrationService()),
|
||
],
|
||
child: const LeafItToMeApp(),
|
||
);
|
||
}
|
||
|
||
void main() {
|
||
testWidgets('Angemeldeter Nutzer sieht die Heute-Checkliste mit fälliger Aufgabe',
|
||
(tester) async {
|
||
await tester.pumpWidget(await buildTestApp());
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(find.text('Heute'), findsOneWidget);
|
||
expect(find.textContaining('offene Aufgabe'), findsOneWidget);
|
||
expect(find.textContaining('Monstera gießen'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('Aufgabe bestätigen schreibt nach Firestore und leert die Liste',
|
||
(tester) async {
|
||
await tester.pumpWidget(await buildTestApp());
|
||
await tester.pumpAndSettle();
|
||
|
||
await tester.tap(find.text('Erledigt').first);
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(find.text('Erledigt'), findsNothing);
|
||
expect(find.text('Alles versorgt!'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('Sitter kann abhaken, sieht aber keinen Anlegen-Button',
|
||
(tester) async {
|
||
await tester.pumpWidget(await buildTestApp(role: 'sitter'));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Abhaken ist erlaubt …
|
||
expect(find.text('Erledigt'), findsWidgets);
|
||
// … aber Pflanzen anlegen nicht.
|
||
expect(find.text('Pflanze hinzufügen'), findsNothing);
|
||
});
|
||
}
|