import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:kooltap/l10n/app_localizations.dart'; import 'package:kooltap/models/bar_tab.dart'; import 'package:kooltap/models/tab_item.dart'; import 'package:kooltap/services/admin_pin_service.dart'; import 'package:kooltap/services/bar_tab_service.dart'; import 'package:kooltap/services/pin_lock_service.dart'; import 'package:kooltap/viewmodels/admin_pin_view_model.dart'; import 'package:kooltap/viewmodels/bar_screen_view_model.dart'; import 'package:kooltap/views/dialogs/edit_tab_dialog.dart'; import 'package:mocktail/mocktail.dart'; import 'package:provider/provider.dart'; class MockBarTabService extends Mock implements BarTabService {} class MockAdminPinService extends Mock implements AdminPinService {} BarTab _tab({required bool withItems}) { return BarTab( id: 'tab-1', customerName: 'John', openedAt: DateTime(2026), items: [ if (withItems) const TabItem( id: 'item-1', tabId: 'tab-1', productId: 'beer', productName: 'Beer', quantity: 2, unitPriceInCents: 250, ), ], ); } void main() { late MockBarTabService barTabService; late MockAdminPinService adminPinService; late AdminPinViewModel adminPinViewModel; setUp(() { barTabService = MockBarTabService(); adminPinService = MockAdminPinService(); adminPinViewModel = AdminPinViewModel(adminPinService: adminPinService); when(() => barTabService.deleteTab(any())).thenAnswer((_) async {}); when(() => adminPinService.hasPin()).thenAnswer((_) async => true); when(() => adminPinService.verifyPin('1234')).thenAnswer((_) async => true); when( () => adminPinService.verifyPin('0000'), ).thenAnswer((_) async => false); }); Future openDialog(WidgetTester tester, BarTab tab) async { await adminPinViewModel.load(); await tester.pumpWidget( MultiProvider( providers: [ ChangeNotifierProvider( create: (_) => BarScreenViewModel(barTabService: barTabService), ), ChangeNotifierProvider.value( value: adminPinViewModel, ), ], child: MaterialApp( locale: const Locale('en'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: Builder( builder: (context) => Scaffold( body: ElevatedButton( onPressed: () => confirmDeleteTab(context, tab), child: const Text('Open'), ), ), ), ), ), ); await tester.tap(find.text('Open')); await tester.pumpAndSettle(); } Future confirmWithPin(WidgetTester tester, String? pin) async { if (pin != null) await tester.enterText(find.byType(TextField), pin); await tester.tap(find.widgetWithText(FilledButton, 'Delete')); await tester.pumpAndSettle(); } testWidgets('deletes an empty tab without asking for a PIN', (tester) async { await openDialog(tester, _tab(withItems: false)); expect(find.byType(TextField), findsNothing); await confirmWithPin(tester, null); verify(() => barTabService.deleteTab('tab-1')).called(1); }); testWidgets('deletes a tab with drinks after the right admin PIN', ( tester, ) async { await openDialog(tester, _tab(withItems: true)); await confirmWithPin(tester, '1234'); verify(() => barTabService.deleteTab('tab-1')).called(1); expect(adminPinViewModel.isUnlocked, false); }); testWidgets('keeps a tab with drinks after a wrong admin PIN', ( tester, ) async { await openDialog(tester, _tab(withItems: true)); await confirmWithPin(tester, '0000'); verifyNever(() => barTabService.deleteTab(any())); }); testWidgets('keeps a tab with drinks while the PIN is locked out', ( tester, ) async { when( () => adminPinService.verifyPin(any()), ).thenThrow(const PinLockedOutException(Duration(minutes: 1))); await openDialog(tester, _tab(withItems: true)); await confirmWithPin(tester, '1234'); verifyNever(() => barTabService.deleteTab(any())); }); testWidgets('cancelling deletes nothing', (tester) async { await openDialog(tester, _tab(withItems: true)); await tester.tap(find.text('Cancel')); await tester.pumpAndSettle(); verifyNever(() => barTabService.deleteTab(any())); }); testWidgets('does not ask for a PIN when no admin PIN is set', ( tester, ) async { when(() => adminPinService.hasPin()).thenAnswer((_) async => false); await openDialog(tester, _tab(withItems: true)); expect(find.byType(TextField), findsNothing); await confirmWithPin(tester, null); verify(() => barTabService.deleteTab('tab-1')).called(1); }); }