233 lines
6.8 KiB
Dart
233 lines
6.8 KiB
Dart
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<void> updateFullName({
|
|
required String userId,
|
|
required String fullName,
|
|
}) async {
|
|
lastFullName = fullName;
|
|
}
|
|
|
|
@override
|
|
Future<void> updatePassword(String password) async {
|
|
lastPassword = password;
|
|
}
|
|
|
|
@override
|
|
Future<String> uploadAvatar({
|
|
required String userId,
|
|
required Uint8List bytes,
|
|
required String fileName,
|
|
}) async {
|
|
return 'https://example.com/avatar.jpg';
|
|
}
|
|
|
|
@override
|
|
Future<String> uploadFacePhoto({
|
|
required String userId,
|
|
required Uint8List bytes,
|
|
required String fileName,
|
|
}) async {
|
|
return 'https://example.com/face.jpg';
|
|
}
|
|
|
|
@override
|
|
Future<Uint8List?> downloadFacePhoto(String userId) async {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
class _FakeUserOfficesController implements UserOfficesController {
|
|
final List<Map<String, String>> assigned = [];
|
|
|
|
@override
|
|
Future<void> assignUserOffice({
|
|
required String userId,
|
|
required String officeId,
|
|
}) async {
|
|
assigned.add({'userId': userId, 'officeId': officeId});
|
|
}
|
|
|
|
@override
|
|
Future<void> 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<Override> 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 <UserOffice>[]),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
|
|
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<Office> && 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 <UserOffice>[]),
|
|
),
|
|
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 <UserOffice>[]),
|
|
),
|
|
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 <UserOffice>[]),
|
|
),
|
|
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'));
|
|
});
|
|
}
|