import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:tasq/models/office.dart'; import 'package:tasq/models/profile.dart'; import 'package:tasq/models/ticket_message.dart'; import 'package:tasq/models/user_office.dart'; import 'package:tasq/providers/profile_provider.dart'; import 'package:tasq/providers/user_offices_provider.dart'; import 'package:tasq/providers/admin_user_provider.dart'; import 'package:tasq/providers/tickets_provider.dart'; import 'package:tasq/screens/admin/user_management_screen.dart'; class _FakeAdminController extends AdminUserController { _FakeAdminController() : super(SupabaseClient('http://localhost', 'test-key', authOptions: const AuthClientOptions(autoRefreshToken: false))); String? lastSetPasswordUserId; String? lastSetPasswordValue; String? lastSetLockUserId; bool? lastSetLockedValue; @override Future setPassword({ required String userId, required String password, }) async { lastSetPasswordUserId = userId; lastSetPasswordValue = password; return Future.value(); } @override Future setLock({required String userId, required bool locked}) async { lastSetLockUserId = userId; lastSetLockedValue = locked; return Future.value(); } } void main() { final office = Office(id: 'office-1', name: 'HQ'); final profile = Profile(id: 'user-1', role: 'admin', fullName: 'Alice Admin'); ProviderScope buildApp({required List overrides}) { return ProviderScope( overrides: overrides, child: const MaterialApp(home: Scaffold(body: UserManagementScreen())), ); } testWidgets('sanity - basic Text widgets render', (tester) async { await tester.pumpWidget( const MaterialApp(home: Scaffold(body: Text('SANITY'))), ); await tester.pump(); expect(find.text('SANITY'), findsOneWidget); }); testWidgets('Edit dialog pre-fills fields and shows actions', (tester) async { await tester.binding.setSurfaceSize(const Size(1280, 900)); addTearDown(() async => await tester.binding.setSurfaceSize(null)); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), profilesProvider.overrideWith((ref) => Stream.value([profile])), officesProvider.overrideWith((ref) => Stream.value([office])), userOfficesProvider.overrideWith( (ref) => Stream.value([ UserOffice(userId: 'user-1', officeId: 'office-1'), ]), ), ticketMessagesAllProvider.overrideWith((ref) => Stream.value(const [])), isAdminProvider.overrideWith((ref) => true), // Provide an AdminUserStatus so the dialog can show email/status immediately. adminUserStatusProvider.overrideWithProvider( FutureProvider.autoDispose.family( (ref, id) async => AdminUserStatus( email: 'alice@example.com', bannedUntil: null, ), ), ), ], ), ); await tester.pump(); await tester.pump(const Duration(milliseconds: 16)); for (var i = 0; i < 20; i++) { if (find.text('Alice Admin').evaluate().isNotEmpty) break; await tester.pump(const Duration(milliseconds: 50)); } expect(find.text('Alice Admin'), findsOneWidget); // Open edit dialog await tester.tap(find.text('Alice Admin')); await tester.pump(); await tester.pump(const Duration(milliseconds: 16)); // Dialog should show and the Full name TextField should contain the profile name final alert = find.byType(AlertDialog); expect(alert, findsOneWidget); final editable = find.descendant( of: alert, matching: find.byType(EditableText), ); expect(editable, findsWidgets); // The first EditableText inside the dialog is the Full name field final fullNameEditable = editable.first; final et = tester.widget(fullNameEditable); expect(et.controller.text, equals('Alice Admin')); // Role should show selected value expect( find.descendant(of: alert, matching: find.text('admin')), findsWidgets, ); // Reset password and Lock buttons must be visible expect( find.descendant( of: alert, matching: find.widgetWithText(OutlinedButton, 'Reset password'), ), findsOneWidget, ); expect( find.descendant( of: alert, matching: find.widgetWithText(OutlinedButton, 'Lock'), ), findsOneWidget, ); }); testWidgets('Reset password and lock call admin controller', (tester) async { final fake = _FakeAdminController(); await tester.binding.setSurfaceSize(const Size(1280, 900)); addTearDown(() async => await tester.binding.setSurfaceSize(null)); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), profilesProvider.overrideWith((ref) => Stream.value([profile])), officesProvider.overrideWith((ref) => Stream.value([office])), userOfficesProvider.overrideWith( (ref) => Stream.value([ UserOffice(userId: 'user-1', officeId: 'office-1'), ]), ), ticketMessagesAllProvider.overrideWith((ref) => Stream.value(const [])), isAdminProvider.overrideWith((ref) => true), adminUserStatusProvider.overrideWithProvider( FutureProvider.autoDispose.family( (ref, id) async => AdminUserStatus( email: 'alice@example.com', bannedUntil: null, ), ), ), adminUserControllerProvider.overrideWithValue(fake), ], ), ); await tester.pump(); await tester.pump(const Duration(milliseconds: 16)); for (var i = 0; i < 20; i++) { if (find.text('Alice Admin').evaluate().isNotEmpty) break; await tester.pump(const Duration(milliseconds: 50)); } // Open edit dialog (again) await tester.tap(find.text('Alice Admin')); await tester.pump(); await tester.pump(const Duration(milliseconds: 16)); // Tap Reset password await tester.tap(find.widgetWithText(OutlinedButton, 'Reset password')); await tester.pump(); await tester.pump(const Duration(milliseconds: 16)); // Enter new password in the nested dialog final pwdField = find.byType(TextFormField).last; await tester.enterText(pwdField, 'new-pass-123'); await tester.pump(); await tester.pump(const Duration(milliseconds: 16)); // Confirm update await tester.tap(find.text('Update password')); await tester.pump(); await tester.pump(const Duration(milliseconds: 16)); expect(fake.lastSetPasswordUserId, equals('user-1')); expect(fake.lastSetPasswordValue, equals('new-pass-123')); // Back in the main dialog - press Lock await tester.tap(find.widgetWithText(OutlinedButton, 'Lock')); await tester.pumpAndSettle(); expect(fake.lastSetLockUserId, equals('user-1')); expect(fake.lastSetLockedValue, isTrue); }); }