glp-companion/test/features/auth/dev_auth_bypass_test.dart

107 lines
3.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:glp1_companion/core/platform/notification_service.dart';
import 'package:glp1_companion/features/auth/presentation/controllers/dev_auth_bypass.dart';
import 'package:glp1_companion/features/auth/presentation/screens/login_screen.dart';
import 'package:glp1_companion/features/medication/domain/entities/dose_entry.dart';
import 'package:glp1_companion/features/medication/domain/repositories/medication_repository.dart';
import 'package:glp1_companion/features/medication/presentation/controllers/medication_controller.dart';
import 'package:glp1_companion/main.dart';
/// Empty history so the medication shell renders without touching drift.
class _EmptyMedicationRepository implements IMedicationRepository {
@override
Stream<List<DoseEntry>> watchDoseEntries({int limit = 500}) =>
Stream.value(const []);
@override
Future<List<DoseEntry>> getDoseEntries({int limit = 500}) async => const [];
@override
Future<DoseEntry> saveDoseEntry(DoseEntry entry) async => entry;
@override
Future<void> deleteDoseEntry(int id) async {}
@override
Future<DoseEntry?> getLatestAdministeredDose() async => null;
}
class _NoopNotificationService implements INotificationService {
@override
Future<void> initialize() async {}
@override
Future<bool> requestPermissions() async => false;
@override
Future<bool> scheduleDoseReminder({
required DateTime scheduledAt,
required String title,
required String body,
int id = kDoseReminderNotificationId,
String? payload,
}) async =>
false;
@override
Future<void> cancelReminder(int id) async {}
@override
Future<void> cancelAllReminders() async {}
}
Widget _buildApp() => ProviderScope(
overrides: [
medicationRepositoryProvider
.overrideWithValue(_EmptyMedicationRepository()),
notificationServiceProvider
.overrideWithValue(_NoopNotificationService()),
],
child: const GLP1CompanionApp(),
);
void main() {
testWidgets('unauthenticated sessions land on the login screen', (tester) async {
await tester.pumpWidget(_buildApp());
await tester.pumpAndSettle();
expect(find.byType(LoginScreen), findsOneWidget);
});
testWidgets('dev bypass button skips sign-in and opens the medication shell',
(tester) async {
// A tall phone-like viewport keeps the whole login column on-screen.
tester.view.physicalSize = const Size(1080, 2400);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.reset);
await tester.pumpWidget(_buildApp());
await tester.pumpAndSettle();
// Debug builds expose the bypass affordance on the login screen.
final bypassButton = find.text('Continue Without Signing In');
expect(bypassButton, findsOneWidget);
await tester.tap(bypassButton);
await tester.pumpAndSettle();
// The guard admits the session and routes into the protected shell.
expect(find.byType(LoginScreen), findsNothing);
expect(find.text('Medication Tracker'), findsOneWidget);
// The dev banner marks the session as not signed in.
expect(find.text('Development mode - not signed in'), findsOneWidget);
});
testWidgets('bypass resets to disabled on every cold start', (tester) async {
await tester.pumpWidget(
const ProviderScope(child: SizedBox()),
);
final container = ProviderScope.containerOf(
tester.element(find.byType(SizedBox)),
);
expect(container.read(devAuthBypassProvider), isFalse);
});
}