import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:tasq/models/office.dart'; import 'package:tasq/models/profile.dart'; import 'package:tasq/models/user_office.dart'; import 'package:tasq/providers/profile_provider.dart'; import 'package:tasq/providers/tickets_provider.dart'; import 'package:tasq/providers/user_offices_provider.dart'; import 'package:tasq/providers/auth_provider.dart'; import 'package:tasq/screens/profile/profile_screen.dart'; import 'package:tasq/widgets/multi_select_picker.dart'; import 'package:awesome_snackbar_content/awesome_snackbar_content.dart'; class _FakeProfileController implements ProfileController { String? lastFullName; String? lastPassword; @override Future updateFullName({ required String userId, required String fullName, }) async { lastFullName = fullName; } @override Future updatePassword(String password) async { lastPassword = password; } @override Future uploadAvatar({ required String userId, required Uint8List bytes, required String fileName, }) async { return 'https://example.com/avatar.jpg'; } @override Future uploadFacePhoto({ required String userId, required Uint8List bytes, required String fileName, }) async { return 'https://example.com/face.jpg'; } @override Future downloadFacePhoto(String userId) async { return null; } } class _FakeUserOfficesController implements UserOfficesController { final List> assigned = []; @override Future assignUserOffice({ required String userId, required String officeId, }) async { assigned.add({'userId': userId, 'officeId': officeId}); } @override Future removeUserOffice({ required String userId, required String officeId, }) async { assigned.removeWhere( (e) => e['userId'] == userId && e['officeId'] == officeId, ); } } void main() { final officeA = Office(id: 'office-1', name: 'HQ'); final officeB = Office(id: 'office-2', name: 'Branch'); final profile = Profile(id: 'user-1', role: 'standard', fullName: 'Paola'); ProviderScope buildApp({required List overrides}) { return ProviderScope( overrides: [ currentUserIdProvider.overrideWithValue(profile.id), sessionProvider.overrideWithValue(null), ...overrides, ], child: const MaterialApp(home: Scaffold(body: ProfileScreen())), ); } testWidgets('renders fields and sections', (tester) async { await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), officesProvider.overrideWith( (ref) => Stream.value([officeA, officeB]), ), userOfficesProvider.overrideWith( (ref) => Stream.value(const []), ), ], ), ); await tester.pumpAndSettle(); expect(find.text('My Profile'), findsOneWidget); expect(find.text('Account details'), findsOneWidget); expect(find.widgetWithText(TextFormField, 'Full name'), findsOneWidget); expect( find.byWidgetPredicate( (w) => w is MultiSelectPicker && w.label == 'Offices', ), findsOneWidget, ); }); testWidgets('save details calls profile controller', (tester) async { final fake = _FakeProfileController(); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), officesProvider.overrideWith((ref) => Stream.value([officeA])), userOfficesProvider.overrideWith( (ref) => Stream.value(const []), ), profileControllerProvider.overrideWithValue(fake), ], ), ); await tester.pumpAndSettle(); await tester.enterText( find.widgetWithText(TextFormField, 'Full name'), 'New Name', ); await tester.ensureVisible(find.text('Save details')); await tester.tap(find.text('Save details')); await tester.pumpAndSettle(); expect(fake.lastFullName, equals('New Name')); // should show a success snackbar using the awesome_snackbar_content package // (AwesomeSnackbarContent uses its own SVG icons, not Material Icons) expect(find.byType(AwesomeSnackbarContent), findsOneWidget); }); testWidgets('save offices assigns selected office', (tester) async { final fakeOffices = _FakeUserOfficesController(); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), officesProvider.overrideWith( (ref) => Stream.value([officeA, officeB]), ), userOfficesProvider.overrideWith( (ref) => Stream.value(const []), ), userOfficesControllerProvider.overrideWithValue(fakeOffices), ], ), ); await tester.pumpAndSettle(); // Open MultiSelect picker final selectChip = find.byWidgetPredicate( (w) => w is ActionChip && (w.label as Text).data == 'Select', ); expect(selectChip, findsOneWidget); await tester.ensureVisible(selectChip); await tester.tap(selectChip); await tester.pumpAndSettle(); // In the dialog, tap the first office checkbox and press Done await tester.tap(find.text('HQ')); await tester.pumpAndSettle(); await tester.tap(find.text('Done')); await tester.pumpAndSettle(); // Save offices await tester.tap(find.text('Save offices')); await tester.pumpAndSettle(); expect(fakeOffices.assigned, isNotEmpty); expect(fakeOffices.assigned.first['officeId'], equals('office-1')); }); testWidgets('change password calls controller', (tester) async { final fake = _FakeProfileController(); await tester.pumpWidget( buildApp( overrides: [ currentProfileProvider.overrideWith((ref) => Stream.value(profile)), officesProvider.overrideWith((ref) => Stream.value([officeA])), userOfficesProvider.overrideWith( (ref) => Stream.value(const []), ), profileControllerProvider.overrideWithValue(fake), ], ), ); await tester.pumpAndSettle(); await tester.enterText( find.widgetWithText(TextFormField, 'New password'), 'new-pass-123', ); await tester.enterText( find.widgetWithText(TextFormField, 'Confirm password'), 'new-pass-123', ); await tester.ensureVisible(find.text('Change password')); await tester.tap(find.text('Change password')); await tester.pumpAndSettle(); expect(fake.lastPassword, equals('new-pass-123')); }); }