tasq/test/user_management_screen_test.dart

216 lines
7.3 KiB
Dart

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<void> setPassword({
required String userId,
required String password,
}) async {
lastSetPasswordUserId = userId;
lastSetPasswordValue = password;
return Future.value();
}
@override
Future<void> 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<Override> 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 <TicketMessage>[])),
isAdminProvider.overrideWith((ref) => true),
// Provide an AdminUserStatus so the dialog can show email/status immediately.
adminUserStatusProvider.overrideWithProvider(
FutureProvider.autoDispose.family<AdminUserStatus, String>(
(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<EditableText>(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 <TicketMessage>[])),
isAdminProvider.overrideWith((ref) => true),
adminUserStatusProvider.overrideWithProvider(
FutureProvider.autoDispose.family<AdminUserStatus, String>(
(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);
});
}