fix: auth token handling, add tests
- Read bearer token from set-auth-token header - Add mounted checks to prevent setState after dispose - Add mocktail for testing - Add widget tests for login, clients, events screens - Add unit tests for auth provider, API client - 110 tests passing
This commit is contained in:
219
test/features/events/events_screen_test.dart
Normal file
219
test/features/events/events_screen_test.dart
Normal file
@@ -0,0 +1,219 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:network_app/features/events/presentation/events_screen.dart';
|
||||
import 'package:network_app/shared/services/api_client.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockApiClient extends Mock implements ApiClient {}
|
||||
|
||||
void main() {
|
||||
late MockApiClient mockApiClient;
|
||||
|
||||
setUp(() {
|
||||
mockApiClient = MockApiClient();
|
||||
});
|
||||
|
||||
Widget createTestWidget() {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
apiClientProvider.overrideWithValue(mockApiClient),
|
||||
],
|
||||
child: MaterialApp(
|
||||
home: const EventsScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final now = DateTime.now();
|
||||
final testEvents = [
|
||||
{
|
||||
'event': {
|
||||
'id': '1',
|
||||
'type': 'birthday',
|
||||
'title': "John's Birthday",
|
||||
'date': now.add(const Duration(days: 5)).toIso8601String(),
|
||||
'recurring': true,
|
||||
},
|
||||
'client': {
|
||||
'id': 'c1',
|
||||
'firstName': 'John',
|
||||
'lastName': 'Doe',
|
||||
},
|
||||
},
|
||||
{
|
||||
'event': {
|
||||
'id': '2',
|
||||
'type': 'anniversary',
|
||||
'title': "Jane's Anniversary",
|
||||
'date': now.add(const Duration(days: 10)).toIso8601String(),
|
||||
'recurring': true,
|
||||
},
|
||||
'client': {
|
||||
'id': 'c2',
|
||||
'firstName': 'Jane',
|
||||
'lastName': 'Smith',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
group('EventsScreen Widget Tests', () {
|
||||
testWidgets('shows loading indicator initially', (tester) async {
|
||||
final completer = Completer<List<Map<String, dynamic>>>();
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) => completer.future);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pump();
|
||||
|
||||
expect(find.byType(CircularProgressIndicator), findsOneWidget);
|
||||
|
||||
// Complete to cleanup
|
||||
completer.complete(testEvents);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('displays events list', (tester) async {
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) async => testEvents);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text("John's Birthday"), findsOneWidget);
|
||||
expect(find.text("Jane's Anniversary"), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('displays client names', (tester) async {
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) async => testEvents);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('John Doe'), findsOneWidget);
|
||||
expect(find.text('Jane Smith'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows birthday icon for birthday events', (tester) async {
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) async => [testEvents[0]]);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.cake), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows heart icon for anniversary events', (tester) async {
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) async => [testEvents[1]]);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.favorite), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows empty state when no events', (tester) async {
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) async => []);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('No upcoming events'), findsOneWidget);
|
||||
expect(find.text('Add birthdays and anniversaries to clients'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows error state on API failure', (tester) async {
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenThrow(Exception('Network error'));
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Failed to load events'), findsOneWidget);
|
||||
expect(find.text('Retry'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows days until event', (tester) async {
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) async => testEvents);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Should show "In X days" for upcoming events
|
||||
expect(find.textContaining('In'), findsWidgets);
|
||||
expect(find.textContaining('days'), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('shows Today for same-day events', (tester) async {
|
||||
final todayEvent = [
|
||||
{
|
||||
'event': {
|
||||
'id': '1',
|
||||
'type': 'birthday',
|
||||
'title': "John's Birthday",
|
||||
'date': DateTime.now().toIso8601String(),
|
||||
'recurring': true,
|
||||
},
|
||||
'client': {
|
||||
'id': 'c1',
|
||||
'firstName': 'John',
|
||||
'lastName': 'Doe',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) async => todayEvent);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Today!'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows Tomorrow for next-day events', (tester) async {
|
||||
// Use a date that's 36 hours ahead to ensure inDays == 1
|
||||
final tomorrow = DateTime.now().add(const Duration(hours: 36));
|
||||
final tomorrowEvent = [
|
||||
{
|
||||
'event': {
|
||||
'id': '1',
|
||||
'type': 'birthday',
|
||||
'title': "John's Birthday",
|
||||
'date': tomorrow.toIso8601String(),
|
||||
'recurring': true,
|
||||
},
|
||||
'client': {
|
||||
'id': 'c1',
|
||||
'firstName': 'John',
|
||||
'lastName': 'Doe',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) async => tomorrowEvent);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Tomorrow'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('has app bar with title', (tester) async {
|
||||
when(() => mockApiClient.getEvents(upcomingDays: any(named: 'upcomingDays')))
|
||||
.thenAnswer((_) async => testEvents);
|
||||
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Upcoming Events'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user