From ed9f97212a4ed302c590aa3e99e55975ac7969e1 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 12:09:00 +0530 Subject: [PATCH 01/22] Feature request: Implement User Profile Modification Feature --- .../add_post_view_model.dart | 5 + .../edit_profile_view_model.dart | 135 ++++++- .../select_organization_view_model.dart | 4 +- .../after_auth_screens/add_post_page.dart | 9 +- .../profile/edit_profile_page.dart | 16 +- .../profile/profile_page.dart | 348 +++++++++--------- lib/widgets/post_widget.dart | 4 +- .../edit_profile_view_model_test.dart | 275 ++++++++++++-- 8 files changed, 589 insertions(+), 207 deletions(-) diff --git a/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart b/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart index 0bcc7ea1a..cfc415b1c 100644 --- a/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart +++ b/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart @@ -78,6 +78,11 @@ class AddPostViewModel extends BaseModel { String get userName => userConfig.currentUser.firstName! + userConfig.currentUser.lastName!; + /// User profile picture. + /// + /// more_info_if_required + String get userPic => userConfig.currentUser.image!; + /// The organisation name. /// /// params: diff --git a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart index be1a52fdb..2705bcb56 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart @@ -1,8 +1,14 @@ +// ignore_for_file: avoid_dynamic_calls + import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:talawa/enums/enums.dart'; import 'package:talawa/locator.dart'; +import 'package:talawa/models/user/user_info.dart'; +import 'package:talawa/services/size_config.dart'; import 'package:talawa/services/third_party_service/multi_media_pick_service.dart'; import 'package:talawa/view_model/base_view_model.dart'; @@ -18,6 +24,9 @@ class EditProfilePageViewModel extends BaseModel { /// profile image. late File? imageFile; + /// profile image in base64. + String? base64Image; + /// first name controller. TextEditingController firstNameTextController = TextEditingController(); @@ -63,6 +72,58 @@ class EditProfilePageViewModel extends BaseModel { } } + /// modal sheet to choose image. + /// + /// more_info_if_required + /// + /// **params**: + /// * `context`: define_the_param + /// + /// **returns**: + /// None + void showImagePickerIcons(BuildContext context) { + showModalBottomSheet( + context: context, + builder: (BuildContext bc) { + return Container( + height: SizeConfig.screenHeight! * 0.135, + padding: const EdgeInsets.all(17), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + GestureDetector( + onTap: () { + Navigator.of(context).pop(); + getImageFromGallery(camera: true); + }, + child: const Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.camera_alt, size: 37), + Text("Camera"), + ], + ), + ), + GestureDetector( + onTap: () { + Navigator.of(context).pop(); + getImageFromGallery(); + }, + child: const Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.photo_library, size: 37), + Text("Gallery"), + ], + ), + ), + ], + ), + ); + }, + ); + } + /// This function is used to convert the image into Base64 format. /// /// **params**: @@ -75,13 +136,85 @@ class EditProfilePageViewModel extends BaseModel { final List bytes = await file.readAsBytes(); final String base64String = base64Encode(bytes); print(base64String); - imageFile = base64String as File?; return base64String; } catch (error) { return ''; } } + /// a_line_ending_with_end_punctuation. + /// + /// more_info_if_required + /// + /// **params**: + /// * `firstName`: define_the_param + /// * `lastName`: define_the_param + /// * `newImage`: define_the_param + /// + /// **returns**: + /// * `Future`: define_the_return + Future updateUserProfile({ + String? firstName, + String? lastName, + File? newImage, + }) async { + if (firstName == user.firstName && + newImage == null && + lastName == user.lastName) { + print("No updates to perform"); + return; + } + try { + final Map variables = {}; + if (firstName != null) { + variables["firstName"] = firstName; + } + if (lastName != null) { + variables["lastName"] = lastName; + } + if (newImage != null) { + final String imageAsString = await convertToBase64(newImage); + variables["file"] = 'data:image/png;base64,$imageAsString'; + } + if (variables.isNotEmpty) { + await databaseService.gqlAuthMutation( + queries.updateUserProfile(), + variables: variables, + ); + // Fetch updated user info from the database and save it in hivebox. + final QueryResult result1 = await databaseFunctions.gqlAuthQuery( + queries.fetchUserInfo, + variables: {'id': user.id}, + ) as QueryResult; + final User userInfo = User.fromJson( + result1.data!['users'][0] as Map, + fromOrg: true, + ); + userInfo.authToken = userConfig.currentUser.authToken; + userInfo.refreshToken = userConfig.currentUser.refreshToken; + userConfig.updateUser(userInfo); + notifyListeners(); + + user.firstName = firstName ?? user.firstName; + user.lastName = lastName ?? user.lastName; + firstNameTextController.text = user.firstName!; + lastNameTextController.text = user.lastName!; + + navigationService.showTalawaErrorSnackBar( + "Profile updated successfully", + MessageType.info, + ); + notifyListeners(); + } + } on Exception catch (e) { + print('error:$e'); + navigationService.showTalawaErrorSnackBar( + "Something went wrong", + MessageType.error, + ); + } + } + /// This function remove the selected image. /// /// **params**: diff --git a/lib/view_model/pre_auth_view_models/select_organization_view_model.dart b/lib/view_model/pre_auth_view_models/select_organization_view_model.dart index 170da8b86..688b6878a 100644 --- a/lib/view_model/pre_auth_view_models/select_organization_view_model.dart +++ b/lib/view_model/pre_auth_view_models/select_organization_view_model.dart @@ -259,9 +259,9 @@ class SelectOrganizationViewModel extends BaseModel { return { 'organizationsConnection': (existingOrganizations!["organizationsConnection"] - as List>) + + as List) + (newOrganizations!['organizationsConnection'] - as List>), + as List), }; }, ), diff --git a/lib/views/after_auth_screens/add_post_page.dart b/lib/views/after_auth_screens/add_post_page.dart index 7ed9fba9e..ab4d9608e 100644 --- a/lib/views/after_auth_screens/add_post_page.dart +++ b/lib/views/after_auth_screens/add_post_page.dart @@ -4,6 +4,7 @@ import 'package:talawa/locator.dart'; import 'package:talawa/utils/app_localization.dart'; import 'package:talawa/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart'; import 'package:talawa/views/base_view.dart'; +import 'package:talawa/widgets/custom_avatar.dart'; /// Add Post View Model. late AddPostViewModel model; @@ -81,7 +82,13 @@ class _AddPostState extends State { child: Column( children: [ ListTile( - leading: const CircleAvatar(radius: 25), + leading: CustomAvatar( + // ignore: unnecessary_null_comparison + isImageNull: model.userPic == null, + firstAlphabet: model.userName.substring(0, 1).toUpperCase(), + imageUrl: model.userPic, + fontSize: 20, + ), title: Text(model.userName), subtitle: Text( AppLocalizations.of(context)! diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index 05b74a1fc..c11d0199d 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -59,8 +59,9 @@ class _EditProfilePageState extends State { ? CircleAvatar( key: const Key('UserImageInDb'), radius: SizeConfig.screenHeight! * 0.082, - backgroundImage: - NetworkImage(model.user.image!), + backgroundImage: NetworkImage( + model.user.image!, + ), ) : CircleAvatar( key: const Key('UserImageNotInDb'), @@ -88,7 +89,7 @@ class _EditProfilePageState extends State { // if image is null the function will be get getImageFromGallery() // else removeImage() model.imageFile == null - ? model.getImageFromGallery(camera: true) + ? model.showImagePickerIcons(context) : model.removeImage(); }, child: model.imageFile == null @@ -229,7 +230,14 @@ class _EditProfilePageState extends State { const Divider(), // button to update the profile. TextButton( - onPressed: () {}, + onPressed: () { + model.updateUserProfile( + firstName: model.firstNameTextController.text, + newImage: model.imageFile, + lastName: model.lastNameTextController.text, + ); + FocusScope.of(context).unfocus(); + }, child: Text( AppLocalizations.of(context)!.strictTranslate('Update'), ), diff --git a/lib/views/after_auth_screens/profile/profile_page.dart b/lib/views/after_auth_screens/profile/profile_page.dart index 5f8f2864e..42354b0f5 100644 --- a/lib/views/after_auth_screens/profile/profile_page.dart +++ b/lib/views/after_auth_screens/profile/profile_page.dart @@ -63,9 +63,8 @@ class ProfilePage extends StatelessWidget { context: context, builder: (BuildContext context) { return Container( - height: 200, + height: 150, decoration: const BoxDecoration( - color: Colors.white, borderRadius: BorderRadius.only( bottomLeft: Radius.zero, bottomRight: Radius.zero, @@ -86,11 +85,11 @@ class ProfilePage extends StatelessWidget { child: const Text( "Edit Profile", style: TextStyle( - color: Colors.black38, fontFamily: 'open-sans', ), ), ), + const Divider(endIndent: 20, indent: 20), TextButton( onPressed: () { model.logout(context); @@ -98,7 +97,6 @@ class ProfilePage extends StatelessWidget { child: const Text( "Log Out", style: TextStyle( - color: Colors.black38, fontFamily: 'open-sans', ), ), @@ -118,191 +116,195 @@ class ProfilePage extends StatelessWidget { // else renders the widget. body: model.isBusy ? const CircularProgressIndicator() - : SingleChildScrollView( - child: Column( - children: [ - SizedBox( - height: SizeConfig.screenHeight! * 0.01, - ), - Row( - children: [ - Expanded( - flex: 1, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: CustomAvatar( - isImageNull: model.currentUser.image == null, - firstAlphabet: model.currentUser.firstName! - .substring(0, 1), - imageUrl: model.currentUser.image, - fontSize: Theme.of(context) - .textTheme - .titleLarge! - .fontSize, - maxRadius: 30, - ), - ), - ), - Expanded( - flex: 3, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - '${model.currentUser.firstName!} ${model.currentUser.lastName!}', - style: const TextStyle( - color: Colors.white, - fontSize: 20, - fontFamily: 'open-sans', + : RefreshIndicator( + onRefresh: () async => model.initialize(), + child: SingleChildScrollView( + child: Column( + children: [ + SizedBox( + height: SizeConfig.screenHeight! * 0.01, + ), + Row( + children: [ + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: CustomAvatar( + isImageNull: model.currentUser.image == null, + firstAlphabet: model.currentUser.firstName! + .substring(0, 1), + imageUrl: model.currentUser.image, + fontSize: Theme.of(context) + .textTheme + .titleLarge! + .fontSize, + maxRadius: 60, ), ), ), - ), - Expanded( - flex: 1, - child: IconButton( - icon: Icon( - Icons.share, - color: Theme.of(context).colorScheme.secondary, + Expanded( + flex: 3, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + '${model.currentUser.firstName!} ${model.currentUser.lastName!}', + style: const TextStyle( + color: Colors.white, + fontSize: 20, + fontFamily: 'open-sans', + ), + ), ), - onPressed: () => model.invite(context), ), - ), - ], - ), - const SizedBox( - height: 20, - ), - TalawaPluginProvider( - pluginName: "Donation", - visible: true, - child: Column( - children: [ - RaisedRoundedButton( - key: homeModel!.keySPDonateUs, - buttonLabel: - AppLocalizations.of(context)!.strictTranslate( - 'Donate to the Community', + Expanded( + flex: 1, + child: IconButton( + icon: Icon( + Icons.share, + color: + Theme.of(context).colorScheme.secondary, + ), + onPressed: () => model.invite(context), ), - onTap: () => donate(context, model), - textColor: Theme.of(context) - .inputDecorationTheme - .focusedBorder! - .borderSide - .color, - backgroundColor: Theme.of(context) - .colorScheme - .secondaryContainer, ), ], ), - ), - SizedBox( - height: 600, - width: double.infinity, - child: ContainedTabBarView( - tabs: [ - const Tab(text: 'Posts'), - const Tab(text: 'Events'), - const Tab(text: 'Tasks'), - ], - views: [ - ColoredBox( - color: Theme.of(context).colorScheme.background, - child: GridView.count( - mainAxisSpacing: 5, - crossAxisCount: 3, - children: [ - Image.asset('assets/images/pfp2.png'), - Image.asset('assets/images/pfp2.png'), - Image.asset('assets/images/pfp2.png'), - Image.asset('assets/images/pfp2.png'), - Image.asset('assets/images/pfp2.png'), - ], + const SizedBox( + height: 20, + ), + TalawaPluginProvider( + pluginName: "Donation", + visible: true, + child: Column( + children: [ + RaisedRoundedButton( + key: homeModel!.keySPDonateUs, + buttonLabel: AppLocalizations.of(context)! + .strictTranslate( + 'Donate to the Community', + ), + onTap: () => donate(context, model), + textColor: Theme.of(context) + .inputDecorationTheme + .focusedBorder! + .borderSide + .color, + backgroundColor: Theme.of(context) + .colorScheme + .secondaryContainer, ), - ), - Container( - color: Theme.of(context).colorScheme.background, - ), - ColoredBox( - color: Theme.of(context).colorScheme.onPrimary, - child: GestureDetector( - onTap: () { - navigationService - .pushScreen(Routes.userTasks); - }, + ], + ), + ), + SizedBox( + height: 600, + width: double.infinity, + child: ContainedTabBarView( + tabs: [ + const Tab(text: 'Posts'), + const Tab(text: 'Events'), + const Tab(text: 'Tasks'), + ], + views: [ + ColoredBox( + color: Theme.of(context).colorScheme.background, + child: GridView.count( + mainAxisSpacing: 5, + crossAxisCount: 3, + children: [ + Image.asset('assets/images/pfp2.png'), + Image.asset('assets/images/pfp2.png'), + Image.asset('assets/images/pfp2.png'), + Image.asset('assets/images/pfp2.png'), + Image.asset('assets/images/pfp2.png'), + ], + ), ), - ), - ], + Container( + color: Theme.of(context).colorScheme.background, + ), + ColoredBox( + color: Theme.of(context).colorScheme.onPrimary, + child: GestureDetector( + onTap: () { + navigationService + .pushScreen(Routes.userTasks); + }, + ), + ), + ], + ), ), - ), - SizedBox( - height: SizeConfig.screenHeight! * 0.67, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - height: SizeConfig.screenHeight! * 0.01, - ), - SizedBox( - height: SizeConfig.screenHeight! * 0.05, - ), + SizedBox( + height: SizeConfig.screenHeight! * 0.67, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + height: SizeConfig.screenHeight! * 0.01, + ), + SizedBox( + height: SizeConfig.screenHeight! * 0.05, + ), - /// `Donation` acts as plugin. If visible is true the it will be always visible. - /// even if it's uninstalled by the admin (for development purposes) - //TODO: custom tile for Invitation. - CustomListTile( - key: homeModel!.keySPInvite, - index: 3, - type: TileType.option, - option: Options( - icon: Icon( - Icons.share, - color: - Theme.of(context).colorScheme.secondary, - size: 30, + /// `Donation` acts as plugin. If visible is true the it will be always visible. + /// even if it's uninstalled by the admin (for development purposes) + //TODO: custom tile for Invitation. + CustomListTile( + key: homeModel!.keySPInvite, + index: 3, + type: TileType.option, + option: Options( + icon: Icon( + Icons.share, + color: + Theme.of(context).colorScheme.secondary, + size: 30, + ), + // title + title: AppLocalizations.of(context)! + .strictTranslate('Invite'), + // subtitle + subtitle: AppLocalizations.of(context)! + .strictTranslate('Invite to org'), ), - // title - title: AppLocalizations.of(context)! - .strictTranslate('Invite'), - // subtitle - subtitle: AppLocalizations.of(context)! - .strictTranslate('Invite to org'), + // on tap call the invite function + onTapOption: () => model.invite(context), ), - // on tap call the invite function - onTapOption: () => model.invite(context), - ), - SizedBox( - height: SizeConfig.screenHeight! * 0.05, - ), - // Custom tile for Logout option. - //TODO: logout - // CustomListTile( - // key: homeModel!.keySPLogout, - // index: 3, - // type: TileType.option, - // option: Options( - // icon: Icon( - // Icons.logout, - // color: - // Theme.of(context).colorScheme.secondary, - // size: 30, - // ), - // title: AppLocalizations.of(context)! - // .strictTranslate('Log out'), - // subtitle: AppLocalizations.of(context)! - // .strictTranslate('Log out from Talawa'), - // ), - // // on tap calls the logout function - // onTapOption: () => model.logout(context), - // ), - SizedBox( - height: SizeConfig.screenHeight! * 0.05, - ), - FromPalisadoes(key: homeModel!.keySPPalisadoes), - ], + SizedBox( + height: SizeConfig.screenHeight! * 0.05, + ), + // Custom tile for Logout option. + //TODO: logout + // CustomListTile( + // key: homeModel!.keySPLogout, + // index: 3, + // type: TileType.option, + // option: Options( + // icon: Icon( + // Icons.logout, + // color: + // Theme.of(context).colorScheme.secondary, + // size: 30, + // ), + // title: AppLocalizations.of(context)! + // .strictTranslate('Log out'), + // subtitle: AppLocalizations.of(context)! + // .strictTranslate('Log out from Talawa'), + // ), + // // on tap calls the logout function + // onTapOption: () => model.logout(context), + // ), + SizedBox( + height: SizeConfig.screenHeight! * 0.05, + ), + FromPalisadoes(key: homeModel!.keySPPalisadoes), + ], + ), ), - ), - ], + ], + ), ), ), ); diff --git a/lib/widgets/post_widget.dart b/lib/widgets/post_widget.dart index 10cd2681d..7ec420a3a 100644 --- a/lib/widgets/post_widget.dart +++ b/lib/widgets/post_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:talawa/models/post/post_model.dart'; +import 'package:talawa/services/graphql_config.dart'; import 'package:talawa/utils/app_localization.dart'; import 'package:talawa/view_model/widgets_view_models/like_button_view_model.dart'; import 'package:talawa/views/base_view.dart'; @@ -53,7 +54,8 @@ class NewsPost extends StatelessWidget { isImageNull: post.creator!.image == null, firstAlphabet: post.creator!.firstName!.substring(0, 1).toUpperCase(), - imageUrl: post.creator!.image, + imageUrl: + "${'${GraphqlConfig.orgURI}'.replaceFirst('/graphql', '')}/${post.creator!.image}", fontSize: 20, ), title: Row( diff --git a/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart b/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart index 112be39da..64cd58d21 100644 --- a/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart +++ b/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart @@ -3,67 +3,292 @@ import 'dart:io'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:mockito/mockito.dart'; -import 'package:talawa/locator.dart'; +import 'package:talawa/enums/enums.dart'; +import 'package:talawa/services/size_config.dart'; import 'package:talawa/services/third_party_service/multi_media_pick_service.dart'; import 'package:talawa/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart'; import '../../../helpers/test_helpers.dart'; +import '../../../helpers/test_locator.dart'; class MockCallbackFunction extends Mock { void call(); } void main() { - setUp(() { + testSetupLocator(); + SizeConfig().test(); + setUpAll(() { registerServices(); + graphqlConfig.test(); + sizeConfig.test(); + }); + + tearDownAll(() { + unregisterServices(); }); group('EditProfilePageViewModel Test -', () { test("Check if it's initialized correctly", () { final model = EditProfilePageViewModel(); model.initialize(); - expect(model.imageFile, null); }); + test('Profile shoud be edited if new values are given', () async { + final model = EditProfilePageViewModel(); + model.initialize(); + final Map mockData = { + 'updateUserProfile': { + '_id': '64378abd85008f171cf2990d', + }, + }; + final String a = await model.convertToBase64(File('path/to/newImage')); + final Map data = { + 'users': [ + { + '_id': '1234567890', + 'firstName': 'John', + 'lastName': 'Doe', + 'email': 'johndoe@example.com', + 'image': 'https://example.com/profile.jpg', + 'accessToken': 'exampleAccessToken', + 'refreshToken': 'exampleRefreshToken', + } + ], + }; + when( + databaseFunctions.gqlAuthMutation( + queries.updateUserProfile(), + variables: { + 'firstName': 'NewFirstName', + 'lastName': 'NewLastName', + 'newImage': 'data:image/png;base64,$a', + }, + ), + ).thenAnswer( + (_) async => QueryResult( + data: mockData, + source: QueryResultSource.network, + options: QueryOptions(document: gql(queries.updateUserProfile())), + ), + ); + when( + databaseFunctions.gqlAuthQuery( + queries.fetchUserInfo, + variables: {'id': model.user.id}, + ), + ).thenAnswer((_) async { + return QueryResult( + source: QueryResultSource.network, + data: data, + options: QueryOptions(document: gql(queries.fetchUserInfo)), + ); + }); + await model.updateUserProfile( + firstName: 'NewFirstName', + lastName: 'NewLastName', + newImage: File('path/to/newImage'), + ); - test( - 'Check if getImageFromGallery() is working fine when no image is return', - () async { - final notifyListenerCallback = MockCallbackFunction(); - final model = EditProfilePageViewModel() - ..addListener(notifyListenerCallback); + verify( + databaseFunctions.gqlAuthMutation( + queries.updateUserProfile(), + variables: { + "firstName": "NewFirstName", + "lastName": "NewLastName", + "file": 'data:image/png;base64,$a', + }, + ), + ).called(1); + verify( + navigationService.showTalawaErrorSnackBar( + "Profile updated successfully", + MessageType.info, + ), + ); + }); - when(locator().getPhotoFromGallery()) + test('Test UpdateUserProfile when throwing exception', () async { + final model = EditProfilePageViewModel(); + model.initialize(); + final String b = await model.convertToBase64(File('path/to/newIma')); + when( + databaseFunctions.gqlAuthMutation( + queries.updateUserProfile(), + variables: { + 'firstName': 'NewFirstNa', + 'lastName': 'NewLastNa', + 'newImage': 'data:image/png;base64,$b', + }, + ), + ).thenThrow(Exception()); + when( + databaseFunctions.gqlAuthQuery( + queries.fetchUserInfo, + variables: {'id': model.user.id}, + ), + ).thenThrow(Exception()); + await model.updateUserProfile( + firstName: 'NewFirstNa', + lastName: 'NewLastNa', + newImage: File('path/to/newIma'), + ); + verify( + navigationService.showTalawaErrorSnackBar( + "Something went wrong", + MessageType.error, + ), + ); + }); + testWidgets('Test if modal sheet appear if showimageicker method is called', + (WidgetTester tester) async { + final model = EditProfilePageViewModel(); + model.initialize(); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Builder( + builder: (BuildContext context) { + return ElevatedButton( + key: const Key('btn1'), + onPressed: () => model.showImagePickerIcons(context), + child: const Text('listner'), + ); + }, + ), + ), + ), + ); + await tester.tap(find.byKey(const Key('btn1'))); + await tester.pumpAndSettle(); + expect(find.text('Camera'), findsOneWidget); + expect(find.text('Gallery'), findsOneWidget); + expect(find.byIcon(Icons.camera_alt), findsOneWidget); + expect(find.byIcon(Icons.photo_library), findsOneWidget); + }); + + testWidgets('Test if image picker from camera works if image is returned', + (WidgetTester tester) async { + final model = EditProfilePageViewModel(); + model.initialize(); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Builder( + builder: (BuildContext context) { + return ElevatedButton( + key: const Key('btn1'), + onPressed: () => model.showImagePickerIcons(context), + child: const Text('listner'), + ); + }, + ), + ), + ), + ); + await tester.tap(find.byKey(const Key('btn1'))); + await tester.pumpAndSettle(); + expect(find.text('Camera'), findsOneWidget); + expect(find.text('Gallery'), findsOneWidget); + expect(find.byIcon(Icons.camera_alt), findsOneWidget); + expect(find.byIcon(Icons.photo_library), findsOneWidget); + final file = File('fakePath'); + when(locator().getPhotoFromGallery(camera: true)) .thenAnswer((realInvocation) async { - return null; + return file; }); - + await tester.tap(find.byIcon(Icons.camera_alt)); + await tester.pumpAndSettle(); + expect(model.imageFile, file); + }); + testWidgets('Test if image picker from camera works if nul is returned', + (WidgetTester tester) async { + final model = EditProfilePageViewModel(); model.initialize(); - await model.getImageFromGallery(); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Builder( + builder: (BuildContext context) { + return ElevatedButton( + key: const Key('btn1'), + onPressed: () => model.showImagePickerIcons(context), + child: const Text('listner'), + ); + }, + ), + ), + ), + ); + await tester.tap(find.byKey(const Key('btn1'))); + await tester.pumpAndSettle(); + expect(find.text('Camera'), findsOneWidget); + expect(find.text('Gallery'), findsOneWidget); + expect(find.byIcon(Icons.camera_alt), findsOneWidget); + expect(find.byIcon(Icons.photo_library), findsOneWidget); + when(locator().getPhotoFromGallery(camera: true)) + .thenAnswer((realInvocation) async { + return null; + }); + await tester.tap(find.byIcon(Icons.camera_alt)); + await tester.pumpAndSettle(); expect(model.imageFile, null); - verifyNever(notifyListenerCallback()); }); - - test('Check if getImageFromGallery() is working fine when iamge is return', - () async { - final notifyListenerCallback = MockCallbackFunction(); - final model = EditProfilePageViewModel() - ..addListener(notifyListenerCallback); - + testWidgets('Test if image picker from gallery works if image is returned', + (WidgetTester tester) async { + final model = EditProfilePageViewModel(); + model.initialize(); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Builder( + builder: (BuildContext context) { + return ElevatedButton( + key: const Key('btn1'), + onPressed: () => model.showImagePickerIcons(context), + child: const Text('listner'), + ); + }, + ), + ), + ), + ); + await tester.tap(find.byKey(const Key('btn1'))); + await tester.pumpAndSettle(); + expect(find.text('Camera'), findsOneWidget); + expect(find.text('Gallery'), findsOneWidget); + expect(find.byIcon(Icons.camera_alt), findsOneWidget); + expect(find.byIcon(Icons.photo_library), findsOneWidget); final file = File('fakePath'); when(locator().getPhotoFromGallery()) .thenAnswer((realInvocation) async { return file; }); + await tester.tap(find.byIcon(Icons.photo_library)); + await tester.pumpAndSettle(); + expect(model.imageFile, file); + }); + test('No update performed if inputs are the same as existing data', + () async { + final model = EditProfilePageViewModel(); model.initialize(); - await model.getImageFromGallery(); - - expect(model.imageFile, file); - verify(notifyListenerCallback()).called(1); + await model.updateUserProfile( + firstName: model.user.firstName, + lastName: model.user.lastName, + newImage: null, + ); + verifyNever( + databaseFunctions.gqlAuthMutation( + queries.updateUserProfile(), + variables: {'id': 'xzy1'}, + ), + ); }); test('Check if removeImage() is working fine', () async { From c39218237f9b0eccd66f7ec9a6183ee7d1ce7696 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 12:43:03 +0530 Subject: [PATCH 02/22] changes --- .../profile_view_models/edit_profile_view_model.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart index 2705bcb56..ad8d96dd3 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart @@ -142,9 +142,7 @@ class EditProfilePageViewModel extends BaseModel { } } - /// a_line_ending_with_end_punctuation. - /// - /// more_info_if_required + /// Method to update user profile. /// /// **params**: /// * `firstName`: define_the_param From db5417b646e4f8504b06abcaf1b5030c727b76c3 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 12:55:27 +0530 Subject: [PATCH 03/22] changes --- .../profile_view_models/edit_profile_view_model.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart index e08724bee..fb29f9a9f 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart @@ -1,6 +1,3 @@ - -// ignore_for_file: avoid_dynamic_calls - import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -72,7 +69,6 @@ class EditProfilePageViewModel extends BaseModel { } } - /// modal sheet to choose image. /// /// more_info_if_required From 4f0224b9594120ba5e91bb82453f2aef3b8bfb12 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 13:39:38 +0530 Subject: [PATCH 04/22] proper dynamic call --- .../profile_view_models/edit_profile_view_model.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart index fb29f9a9f..012ea2672 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart @@ -182,7 +182,8 @@ class EditProfilePageViewModel extends BaseModel { variables: {'id': user.id}, ) as QueryResult; final User userInfo = User.fromJson( - result1.data!['users'][0] as Map, + ((result1.data!['users'] as List)[0]) + as Map, fromOrg: true, ); userInfo.authToken = userConfig.currentUser.authToken; From 68ab1be2d8180242bbd799acd37a132eb5d954b9 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 15:33:05 +0530 Subject: [PATCH 05/22] fixing tests --- .../add_post_view_models/add_post_view_model.dart | 4 ++-- lib/views/after_auth_screens/add_post_page.dart | 6 +++--- .../widget_tests/after_auth_screens/add_post_page_test.dart | 3 +++ test/widget_tests/widgets/post_widget_test.dart | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart b/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart index cfc415b1c..6d123ee96 100644 --- a/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart +++ b/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart @@ -75,13 +75,13 @@ class AddPostViewModel extends BaseModel { /// None /// returns: /// * `String`: The username of the currentUser - String get userName => + String? get userName => userConfig.currentUser.firstName! + userConfig.currentUser.lastName!; /// User profile picture. /// /// more_info_if_required - String get userPic => userConfig.currentUser.image!; + String? get userPic => userConfig.currentUser.image; /// The organisation name. /// diff --git a/lib/views/after_auth_screens/add_post_page.dart b/lib/views/after_auth_screens/add_post_page.dart index ab4d9608e..1a36adfab 100644 --- a/lib/views/after_auth_screens/add_post_page.dart +++ b/lib/views/after_auth_screens/add_post_page.dart @@ -83,13 +83,13 @@ class _AddPostState extends State { children: [ ListTile( leading: CustomAvatar( - // ignore: unnecessary_null_comparison isImageNull: model.userPic == null, - firstAlphabet: model.userName.substring(0, 1).toUpperCase(), + firstAlphabet: + model.userName!.substring(0, 1).toUpperCase(), imageUrl: model.userPic, fontSize: 20, ), - title: Text(model.userName), + title: Text(model.userName!), subtitle: Text( AppLocalizations.of(context)! .strictTranslate(model.orgName), diff --git a/test/widget_tests/after_auth_screens/add_post_page_test.dart b/test/widget_tests/after_auth_screens/add_post_page_test.dart index f9f15117c..4888dde9d 100644 --- a/test/widget_tests/after_auth_screens/add_post_page_test.dart +++ b/test/widget_tests/after_auth_screens/add_post_page_test.dart @@ -32,6 +32,9 @@ class MockAddPostViewModel extends Mock implements AddPostViewModel { @override String get userName => 'UserName'; + @override + String? get userPic => userConfig.currentUser.image; + @override String get orgName => 'orgName'; diff --git a/test/widget_tests/widgets/post_widget_test.dart b/test/widget_tests/widgets/post_widget_test.dart index 15d0af208..20f266928 100644 --- a/test/widget_tests/widgets/post_widget_test.dart +++ b/test/widget_tests/widgets/post_widget_test.dart @@ -273,7 +273,7 @@ void main() { // Testing props of Custom Avatar Widget expect(customAvatarWidget.isImageNull, true); - expect(customAvatarWidget.imageUrl, null); + expect(customAvatarWidget.imageUrl, ' /null'); expect(customAvatarWidget.fontSize, 20); expect(customAvatarWidget.firstAlphabet, 'T'); From 43af07dafb9db0718f220ee5a3155147b71aaf12 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 15:56:26 +0530 Subject: [PATCH 06/22] fixing coverage --- .../profile_view_models/edit_profile_view_model.dart | 5 ++--- .../edit_profile_view_model_test.dart | 9 +++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart index 012ea2672..0a20e5ad8 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart @@ -131,9 +131,8 @@ class EditProfilePageViewModel extends BaseModel { Future convertToBase64(File file) async { try { final List bytes = await file.readAsBytes(); - final String base64String = base64Encode(bytes); - print(base64String); - return base64String; + base64Image = base64Encode(bytes); + return base64Image!; } catch (error) { return ''; } diff --git a/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart b/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart index 64cd58d21..9ccf17102 100644 --- a/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart +++ b/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart @@ -291,6 +291,15 @@ void main() { ); }); + test('convertToBase64 converts file to base64 string', () async { + final model = EditProfilePageViewModel(); + model.initialize(); + //using this asset as the test asset + final file = File('assets/images/Group 8948.png'); + final fileString = await model.convertToBase64(file); + expect(model.base64Image, fileString); + }); + test('Check if removeImage() is working fine', () async { final notifyListenerCallback = MockCallbackFunction(); final model = EditProfilePageViewModel() From 90f780595fb0fb7ba914c5524bb9c10724c336b3 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 20:24:49 +0530 Subject: [PATCH 07/22] writing test for missing lines --- .../profile/edit_profile_page.dart | 1 + .../profile/edit_profile_page_test.dart | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index c11d0199d..c34bfa9d2 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -230,6 +230,7 @@ class _EditProfilePageState extends State { const Divider(), // button to update the profile. TextButton( + key: const Key('updatebtn'), onPressed: () { model.updateUserProfile( firstName: model.firstNameTextController.text, diff --git a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart index 24698a81a..40700b3e1 100644 --- a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart +++ b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart @@ -27,6 +27,9 @@ import '../../../helpers/test_locator.dart'; class MockBuildContext extends Mock implements BuildContext {} +class MockEditProfilePageViewModel extends Mock + implements EditProfilePageViewModel {} + class MockCallbackFunction extends Mock { void call(); } @@ -295,6 +298,32 @@ Future main() async { tester.tap(imageAvatar); }); }); + testWidgets("Testing Update butoon", (tester) async { + await mockNetworkImages(() async { + final mockctx = MockBuildContext(); + userConfig.updateUser(User()); + userConfig.updateUser( + User(firstName: 'Test', lastName: 'Test', email: 'test@test.com'), + ); + await tester.pumpWidget(createChangePassScreenDark()); + await tester.pumpAndSettle(); + final screenScaffoldWidget = find.byKey( + const Key('EditProfileScreenScaffold'), + ); + expect(screenScaffoldWidget, findsOneWidget); + expect( + (tester.firstWidget(find.byKey(const Key('Root'))) as MaterialApp) + .theme! + .scaffoldBackgroundColor, + TalawaTheme.darkTheme.scaffoldBackgroundColor, + ); + final updateButtonFinder = find.byKey(const Key('updatebtn')); + expect(updateButtonFinder, findsOneWidget); + await tester.tap(updateButtonFinder); + await tester.pumpAndSettle(); + // verify(FocusScope.of(mockctx).unfocus()).called(1); + }); + }); }); group('Testing image selection and removal in Edit Profile Screen', () { setUp(() { From b3beb3d2ddfda3aea0e38bdda75e56f7b8950144 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 20:28:08 +0530 Subject: [PATCH 08/22] writing test for missing lines --- .../after_auth_screens/profile/edit_profile_page_test.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart index 40700b3e1..5ab2bc176 100644 --- a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart +++ b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart @@ -300,7 +300,6 @@ Future main() async { }); testWidgets("Testing Update butoon", (tester) async { await mockNetworkImages(() async { - final mockctx = MockBuildContext(); userConfig.updateUser(User()); userConfig.updateUser( User(firstName: 'Test', lastName: 'Test', email: 'test@test.com'), @@ -321,7 +320,6 @@ Future main() async { expect(updateButtonFinder, findsOneWidget); await tester.tap(updateButtonFinder); await tester.pumpAndSettle(); - // verify(FocusScope.of(mockctx).unfocus()).called(1); }); }); }); From 678ab169dde1052f419be91d841e463ba2da01d9 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 23:28:40 +0530 Subject: [PATCH 09/22] writing test for missing lines --- .../profile_page_view_model.dart | 3 +- .../profile/profile_page.dart | 2 + .../profile_page_view_model_test.dart | 2 +- .../profile/profile_page_test.dart | 51 +++++++++++++++++-- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/lib/view_model/after_auth_view_models/profile_view_models/profile_page_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/profile_page_view_model.dart index d17dadb47..428f4ffc5 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/profile_page_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/profile_page_view_model.dart @@ -249,8 +249,9 @@ class ProfilePageViewModel extends BaseModel { BuildContext context, void Function(void Function()) setter, ) { + print('domBtn_$amount'); return InkWell( - key: const Key('dombtn1'), + key: Key('domBtn_$amount'), onTap: () { setter(() { donationAmount.text = amount; diff --git a/lib/views/after_auth_screens/profile/profile_page.dart b/lib/views/after_auth_screens/profile/profile_page.dart index 42354b0f5..ad868608b 100644 --- a/lib/views/after_auth_screens/profile/profile_page.dart +++ b/lib/views/after_auth_screens/profile/profile_page.dart @@ -160,6 +160,7 @@ class ProfilePage extends StatelessWidget { Expanded( flex: 1, child: IconButton( + key: const Key('inv1'), icon: Icon( Icons.share, color: @@ -227,6 +228,7 @@ class ProfilePage extends StatelessWidget { ColoredBox( color: Theme.of(context).colorScheme.onPrimary, child: GestureDetector( + key: const Key('tastscrn'), onTap: () { navigationService .pushScreen(Routes.userTasks); diff --git a/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/profile_page_view_model_test.dart b/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/profile_page_view_model_test.dart index b4931c9df..b67434290 100644 --- a/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/profile_page_view_model_test.dart +++ b/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/profile_page_view_model_test.dart @@ -224,7 +224,7 @@ void main() async { ), ), ); - await tester.tap(find.byKey(const Key('dombtn1'))); + await tester.tap(find.byKey(const Key('domBtn_$amt'))); expect(setterCalled, true); final containerFinder = find.byType(Container); final Container container = tester.firstWidget(containerFinder); diff --git a/test/views/after_auth_screens/profile/profile_page_test.dart b/test/views/after_auth_screens/profile/profile_page_test.dart index 8a4e4861b..4888f1538 100644 --- a/test/views/after_auth_screens/profile/profile_page_test.dart +++ b/test/views/after_auth_screens/profile/profile_page_test.dart @@ -59,8 +59,6 @@ void main() async { await Hive.openBox('currentOrg'); final pbox = await Hive.openBox('pluginBox'); print(pbox.get('plugins')); - // locator.unregister(); - // locator.registerFactory(() => ProfilePageViewModel()); }); tearDownAll(() { @@ -69,14 +67,59 @@ void main() async { File('test/fixtures/core/currentuser.hive').delete(); File('test/fixtures/core/currentuser.lock').delete(); }); - testWidgets('check if profilePage shows up', (tester) async { - // print(); + testWidgets('check if profilePage shows up and refreshIndicator work', + (tester) async { await tester.pumpWidget( createProfilePage( mainScreenViewModel: locator(), ), ); await tester.pumpAndSettle(); + await tester.fling( + find.byType(SingleChildScrollView), const Offset(0, 300), 1000); + await tester.pumpAndSettle(); + }); + testWidgets('check if invitebutton work', (tester) async { + await tester.pumpWidget( + createProfilePage( + mainScreenViewModel: locator(), + ), + ); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('inv1'))); + await tester.pumpAndSettle(); + }); + testWidgets('check if Donate button work', (tester) async { + await tester.pumpWidget( + createProfilePage( + mainScreenViewModel: locator(), + ), + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('Donate to the Community')); + await tester.pumpAndSettle(); + }); + testWidgets('check if naviagte to task screen work', (tester) async { + await tester.pumpWidget( + createProfilePage( + mainScreenViewModel: locator(), + ), + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('Tasks')); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('tastscrn'))); + await tester.pumpAndSettle(); + }); + testWidgets('check if naviagte to task screen work', (tester) async { + await tester.pumpWidget( + createProfilePage( + mainScreenViewModel: locator(), + ), + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('Invite')); + await tester.pumpAndSettle(); }); }); } From 41e3a57dd0b39c1b6d463e75d9aede7953dd91c3 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Sun, 24 Dec 2023 23:35:51 +0530 Subject: [PATCH 10/22] writing test for missing lines --- test/views/after_auth_screens/profile/profile_page_test.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/views/after_auth_screens/profile/profile_page_test.dart b/test/views/after_auth_screens/profile/profile_page_test.dart index 4888f1538..9fbe68edc 100644 --- a/test/views/after_auth_screens/profile/profile_page_test.dart +++ b/test/views/after_auth_screens/profile/profile_page_test.dart @@ -76,7 +76,10 @@ void main() async { ); await tester.pumpAndSettle(); await tester.fling( - find.byType(SingleChildScrollView), const Offset(0, 300), 1000); + find.byType(SingleChildScrollView), + const Offset(0, 300), + 1000, + ); await tester.pumpAndSettle(); }); testWidgets('check if invitebutton work', (tester) async { From 7fd4d895161cdb33f6fa4171f38b672e69d35a50 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Mon, 25 Dec 2023 00:50:40 +0530 Subject: [PATCH 11/22] writing test for missing lines --- .../after_auth_screens/profile/profile_page.dart | 1 + .../after_auth_screens/profile/profile_page_test.dart | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/views/after_auth_screens/profile/profile_page.dart b/lib/views/after_auth_screens/profile/profile_page.dart index ad868608b..b9ce6ac50 100644 --- a/lib/views/after_auth_screens/profile/profile_page.dart +++ b/lib/views/after_auth_screens/profile/profile_page.dart @@ -131,6 +131,7 @@ class ProfilePage extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(8.0), child: CustomAvatar( + key: const Key('profilepic'), isImageNull: model.currentUser.image == null, firstAlphabet: model.currentUser.firstName! .substring(0, 1), diff --git a/test/views/after_auth_screens/profile/profile_page_test.dart b/test/views/after_auth_screens/profile/profile_page_test.dart index 9fbe68edc..92d4dca20 100644 --- a/test/views/after_auth_screens/profile/profile_page_test.dart +++ b/test/views/after_auth_screens/profile/profile_page_test.dart @@ -74,11 +74,11 @@ void main() async { mainScreenViewModel: locator(), ), ); - await tester.pumpAndSettle(); - await tester.fling( - find.byType(SingleChildScrollView), + await tester.pump(); + expect(find.byType(RefreshIndicator), findsOneWidget); + await tester.drag( + find.byKey(const Key('profilepic')), const Offset(0, 300), - 1000, ); await tester.pumpAndSettle(); }); @@ -114,13 +114,14 @@ void main() async { await tester.tap(find.byKey(const Key('tastscrn'))); await tester.pumpAndSettle(); }); - testWidgets('check if naviagte to task screen work', (tester) async { + testWidgets('check if Invite customListTile work', (tester) async { await tester.pumpWidget( createProfilePage( mainScreenViewModel: locator(), ), ); await tester.pumpAndSettle(); + await tester.ensureVisible(find.text('Invite')); await tester.tap(find.text('Invite')); await tester.pumpAndSettle(); }); From 61385263eb7a02c34a9c9aaff5c389fee500cf03 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Mon, 25 Dec 2023 22:45:04 +0530 Subject: [PATCH 12/22] adding requested changes --- .../add_post_view_model.dart | 4 +- .../edit_profile_view_model.dart | 64 ++++--------------- .../after_auth_screens/add_post_page.dart | 5 +- .../profile/edit_profile_page.dart | 49 +++++++++++++- 4 files changed, 64 insertions(+), 58 deletions(-) diff --git a/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart b/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart index 6d123ee96..eaed4aac3 100644 --- a/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart +++ b/lib/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart @@ -75,12 +75,10 @@ class AddPostViewModel extends BaseModel { /// None /// returns: /// * `String`: The username of the currentUser - String? get userName => + String get userName => userConfig.currentUser.firstName! + userConfig.currentUser.lastName!; /// User profile picture. - /// - /// more_info_if_required String? get userPic => userConfig.currentUser.image; /// The organisation name. diff --git a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart index 0a20e5ad8..cc94d797f 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart @@ -5,7 +5,6 @@ import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:talawa/enums/enums.dart'; import 'package:talawa/locator.dart'; import 'package:talawa/models/user/user_info.dart'; -import 'package:talawa/services/size_config.dart'; import 'package:talawa/services/third_party_service/multi_media_pick_service.dart'; import 'package:talawa/view_model/base_view_model.dart'; @@ -60,7 +59,7 @@ class EditProfilePageViewModel extends BaseModel { /// /// **returns**: /// * `Future`: None - Future getImageFromGallery({bool camera = false}) async { + Future getImage({bool camera = false}) async { final image = await _multiMediaPickerService.getPhotoFromGallery(camera: camera); if (image != null) { @@ -69,56 +68,19 @@ class EditProfilePageViewModel extends BaseModel { } } - /// modal sheet to choose image. - /// - /// more_info_if_required + /// Method to select image from gallery or camera. /// /// **params**: - /// * `context`: define_the_param + /// * `camera`: for true it will select from camera otherwise gallery /// /// **returns**: - /// None - void showImagePickerIcons(BuildContext context) { - showModalBottomSheet( - context: context, - builder: (BuildContext bc) { - return Container( - height: SizeConfig.screenHeight! * 0.135, - padding: const EdgeInsets.all(17), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - GestureDetector( - onTap: () { - Navigator.of(context).pop(); - getImageFromGallery(camera: true); - }, - child: const Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.camera_alt, size: 37), - Text("Camera"), - ], - ), - ), - GestureDetector( - onTap: () { - Navigator.of(context).pop(); - getImageFromGallery(); - }, - child: const Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.photo_library, size: 37), - Text("Gallery"), - ], - ), - ), - ], - ), - ); - }, - ); + /// * `Future`: define_the_return + Future selectImage({bool camera = false}) async { + if (camera) { + getImage(camera: true); + } else { + getImage(); + } } /// This function is used to convert the image into Base64 format. @@ -141,9 +103,9 @@ class EditProfilePageViewModel extends BaseModel { /// Method to update user profile. /// /// **params**: - /// * `firstName`: define_the_param - /// * `lastName`: define_the_param - /// * `newImage`: define_the_param + /// * `firstName`: updated first name. + /// * `lastName`: updated last name. + /// * `newImage`: New profile picture that is to be updated. /// /// **returns**: /// * `Future`: define_the_return diff --git a/lib/views/after_auth_screens/add_post_page.dart b/lib/views/after_auth_screens/add_post_page.dart index 1a36adfab..36b9d4af7 100644 --- a/lib/views/after_auth_screens/add_post_page.dart +++ b/lib/views/after_auth_screens/add_post_page.dart @@ -84,12 +84,11 @@ class _AddPostState extends State { ListTile( leading: CustomAvatar( isImageNull: model.userPic == null, - firstAlphabet: - model.userName!.substring(0, 1).toUpperCase(), + firstAlphabet: model.userName.substring(0, 1).toUpperCase(), imageUrl: model.userPic, fontSize: 20, ), - title: Text(model.userName!), + title: Text(model.userName), subtitle: Text( AppLocalizations.of(context)! .strictTranslate(model.orgName), diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index c34bfa9d2..94a5ff8d1 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -89,7 +89,54 @@ class _EditProfilePageState extends State { // if image is null the function will be get getImageFromGallery() // else removeImage() model.imageFile == null - ? model.showImagePickerIcons(context) + ? showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return Container( + height: + SizeConfig.screenHeight! * 0.135, + padding: const EdgeInsets.all(17), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + GestureDetector( + onTap: () { + Navigator.of(context).pop(); + model.selectImage(camera: true); + }, + child: const Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.camera_alt, + size: 37, + ), + Text("Camera"), + ], + ), + ), + GestureDetector( + onTap: () { + Navigator.of(context).pop(); + model.selectImage(); + }, + child: const Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.photo_library, + size: 37, + ), + Text("Gallery"), + ], + ), + ), + ], + ), + ); + }, + ) : model.removeImage(); }, child: model.imageFile == null From 906e17bb55f0ad53d1be5a8fda868f869d49219b Mon Sep 17 00:00:00 2001 From: Dante291 Date: Mon, 25 Dec 2023 23:50:02 +0530 Subject: [PATCH 13/22] adding requested changes --- lang/de.json | 4 +- lang/en.json | 4 +- lang/es.json | 4 +- lang/fr.json | 4 +- lang/hi.json | 4 +- lang/ja.json | 4 +- lang/pt.json | 4 +- lang/zh.json | 4 +- .../edit_profile_view_model.dart | 4 +- .../profile/edit_profile_page.dart | 29 +++++-- .../edit_profile_view_model_test.dart | 65 +++++++--------- .../profile/edit_profile_page_test.dart | 75 ++++++++++++++++++- 12 files changed, 147 insertions(+), 58 deletions(-) diff --git a/lang/de.json b/lang/de.json index 7ff5a6eea..d903796ca 100644 --- a/lang/de.json +++ b/lang/de.json @@ -162,5 +162,7 @@ "Notification Feature is not installed": "Meddelelsesfunktionen er ikke installeret", "For complete access, please": "Für vollständigen Zugriff bitte", " join an organization.": " einer Organisation beitreten.", - "JOIN":"BEITRETEN" + "JOIN":"BEITRETEN", + "Camera": "Kamera", + "Gallery": "Galerie" } diff --git a/lang/en.json b/lang/en.json index bed1b4a65..09bdd9211 100644 --- a/lang/en.json +++ b/lang/en.json @@ -168,5 +168,7 @@ "No organizations found Please contact your admin": "No organizations found ! Please contact your admin", "For complete access, please": "For complete access, please", " join an organization.": " join an organization.", - "JOIN":"JOIN" + "JOIN":"JOIN", + "Camera": "Camera", + "Gallery": "Gallery" } diff --git a/lang/es.json b/lang/es.json index 5a8ffb18a..d1060a09b 100644 --- a/lang/es.json +++ b/lang/es.json @@ -163,5 +163,7 @@ "No organizations found Please contact your admin": "Neniuj organizoj trovitaj! Bonvolu kontakti vian administranton", "For complete access, please": "Para acceso completo, por favor", " join an organization.": " unirse a una organización.", - "JOIN":"UNIRSE" + "JOIN":"UNIRSE", + "Camera": "Cámara", + "Gallery": "Galería" } diff --git a/lang/fr.json b/lang/fr.json index 5070b7deb..6db60cd06 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -165,5 +165,7 @@ "No organizations found Please contact your admin": "Aucune organisation trouvée ! Veuillez contacter votre administrateur", "For complete access, please": "Pour un accès complet, veuillez", " join an organization.": " rejoindre une organisation.", - "JOIN":"REJOINDRE" + "JOIN":"REJOINDRE", + "Camera": "Caméra", + "Gallery": "Galerie" } diff --git a/lang/hi.json b/lang/hi.json index db3328742..3e3ca23c4 100644 --- a/lang/hi.json +++ b/lang/hi.json @@ -161,5 +161,7 @@ "No organizations found Please contact your admin": "कोई संगठन नहीं मिला! कृपया अपने व्यवस्थापक से संपर्क करें", "For complete access, please": "पूर्ण पहुंच के लिए, कृपया", " join an organization.": " किसी संगठन से जुड़ें.", - "JOIN":"जोड़ना" + "JOIN":"जोड़ना", + "Camera": "कैमरा", + "Gallery": "गैलरी" } diff --git a/lang/ja.json b/lang/ja.json index cf17112a0..98b00fe3f 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -164,5 +164,7 @@ "No organizations found Please contact your admin": "組織が見つかりません!管理者に連絡してください", "For complete access, please": "完全にアクセスするには、", " join an organization.": " 組織に参加します。", - "JOIN": "参加する" + "JOIN": "参加する", + "Camera": "カメラ", + "Gallery": "ギャラリー" } diff --git a/lang/pt.json b/lang/pt.json index 6948f0e91..1bd61aad6 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -163,5 +163,7 @@ "No organizations found Please contact your admin": "Neniuj organizoj trovitaj! Bonvolu kontakti vian administranton", "For complete access, please": "Para acesso completo, por favor", " join an organization.": " ingressar em uma organização.", - "JOIN":"ENTRAR" + "JOIN":"ENTRAR", + "Camera": "Câmera", + "Gallery": "Galeria" } diff --git a/lang/zh.json b/lang/zh.json index 8d96d4789..ef0a20487 100644 --- a/lang/zh.json +++ b/lang/zh.json @@ -162,5 +162,7 @@ "No organizations found Please contact your admin": "Neniuj organizoj trovitaj! Bonvolu kontakti vian administranton", "For complete access, please": "如需完整访问,请", " join an organization.": " 加入一个组织。", - "JOIN":"加入" + "JOIN":"加入", + "Camera": "相机", + "Gallery": "画廊" } diff --git a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart index cc94d797f..c54c4378c 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart @@ -117,7 +117,6 @@ class EditProfilePageViewModel extends BaseModel { if (firstName == user.firstName && newImage == null && lastName == user.lastName) { - print("No updates to perform"); return; } try { @@ -163,8 +162,7 @@ class EditProfilePageViewModel extends BaseModel { ); notifyListeners(); } - } on Exception catch (e) { - print('error:$e'); + } on Exception catch (_) { navigationService.showTalawaErrorSnackBar( "Something went wrong", MessageType.error, diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index 94a5ff8d1..16019dd01 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -26,7 +26,6 @@ class _EditProfilePageState extends State { backgroundColor: Theme.of(context).primaryColor, elevation: 0.0, title: Text( - // Title of the app bar(header). AppLocalizations.of(context)!.strictTranslate('Profile'), key: const Key('ProfileText'), style: Theme.of(context).textTheme.titleLarge!.copyWith( @@ -101,34 +100,50 @@ class _EditProfilePageState extends State { MainAxisAlignment.spaceEvenly, children: [ GestureDetector( + key: const Key('selectcamera'), onTap: () { Navigator.of(context).pop(); model.selectImage(camera: true); }, - child: const Column( + child: Column( mainAxisSize: MainAxisSize.min, children: [ - Icon( + const Icon( Icons.camera_alt, size: 37, ), - Text("Camera"), + Text( + AppLocalizations.of( + context, + )! + .strictTranslate( + 'Camera', + ), + ), ], ), ), GestureDetector( + key: const Key('selectgallery'), onTap: () { Navigator.of(context).pop(); model.selectImage(); }, - child: const Column( + child: Column( mainAxisSize: MainAxisSize.min, children: [ - Icon( + const Icon( Icons.photo_library, size: 37, ), - Text("Gallery"), + Text( + AppLocalizations.of( + context, + )! + .strictTranslate( + 'Gallery', + ), + ), ], ), ), diff --git a/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart b/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart index 9ccf17102..d08d21490 100644 --- a/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart +++ b/test/view_model_tests/after_auth_view_model_tests/profile_view_model_tests/edit_profile_view_model_test.dart @@ -144,7 +144,7 @@ void main() { ), ); }); - testWidgets('Test if modal sheet appear if showimageicker method is called', + testWidgets('Test if SelectImage from camera method works', (WidgetTester tester) async { final model = EditProfilePageViewModel(); model.initialize(); @@ -155,7 +155,7 @@ void main() { builder: (BuildContext context) { return ElevatedButton( key: const Key('btn1'), - onPressed: () => model.showImagePickerIcons(context), + onPressed: () => model.selectImage(camera: true), child: const Text('listner'), ); }, @@ -163,15 +163,19 @@ void main() { ), ), ); + final file = File('fakePath'); + when(locator().getPhotoFromGallery(camera: true)) + .thenAnswer((realInvocation) async { + return file; + }); await tester.tap(find.byKey(const Key('btn1'))); await tester.pumpAndSettle(); - expect(find.text('Camera'), findsOneWidget); - expect(find.text('Gallery'), findsOneWidget); - expect(find.byIcon(Icons.camera_alt), findsOneWidget); - expect(find.byIcon(Icons.photo_library), findsOneWidget); + verify(multimediaPickerService.getPhotoFromGallery(camera: true)) + .called(1); + expect(model.imageFile, file); }); - testWidgets('Test if image picker from camera works if image is returned', + testWidgets('Test if selectImage from gallery method works', (WidgetTester tester) async { final model = EditProfilePageViewModel(); model.initialize(); @@ -182,7 +186,7 @@ void main() { builder: (BuildContext context) { return ElevatedButton( key: const Key('btn1'), - onPressed: () => model.showImagePickerIcons(context), + onPressed: () => model.selectImage(), child: const Text('listner'), ); }, @@ -190,22 +194,18 @@ void main() { ), ), ); - await tester.tap(find.byKey(const Key('btn1'))); - await tester.pumpAndSettle(); - expect(find.text('Camera'), findsOneWidget); - expect(find.text('Gallery'), findsOneWidget); - expect(find.byIcon(Icons.camera_alt), findsOneWidget); - expect(find.byIcon(Icons.photo_library), findsOneWidget); final file = File('fakePath'); - when(locator().getPhotoFromGallery(camera: true)) + when(locator().getPhotoFromGallery()) .thenAnswer((realInvocation) async { return file; }); - await tester.tap(find.byIcon(Icons.camera_alt)); + await tester.tap(find.byKey(const Key('btn1'))); await tester.pumpAndSettle(); + expect(model.imageFile, file); }); - testWidgets('Test if image picker from camera works if nul is returned', + testWidgets( + 'Test if SelectImage from camera method works if null is returned', (WidgetTester tester) async { final model = EditProfilePageViewModel(); model.initialize(); @@ -216,7 +216,7 @@ void main() { builder: (BuildContext context) { return ElevatedButton( key: const Key('btn1'), - onPressed: () => model.showImagePickerIcons(context), + onPressed: () => model.selectImage(camera: true), child: const Text('listner'), ); }, @@ -224,22 +224,18 @@ void main() { ), ), ); - await tester.tap(find.byKey(const Key('btn1'))); - await tester.pumpAndSettle(); - expect(find.text('Camera'), findsOneWidget); - expect(find.text('Gallery'), findsOneWidget); - expect(find.byIcon(Icons.camera_alt), findsOneWidget); - expect(find.byIcon(Icons.photo_library), findsOneWidget); - when(locator().getPhotoFromGallery(camera: true)) .thenAnswer((realInvocation) async { return null; }); - await tester.tap(find.byIcon(Icons.camera_alt)); + await tester.tap(find.byKey(const Key('btn1'))); await tester.pumpAndSettle(); + verify(multimediaPickerService.getPhotoFromGallery(camera: true)) + .called(1); expect(model.imageFile, null); }); - testWidgets('Test if image picker from gallery works if image is returned', + testWidgets( + 'Test if selectImage from gallery method works when null is returned', (WidgetTester tester) async { final model = EditProfilePageViewModel(); model.initialize(); @@ -250,7 +246,7 @@ void main() { builder: (BuildContext context) { return ElevatedButton( key: const Key('btn1'), - onPressed: () => model.showImagePickerIcons(context), + onPressed: () => model.selectImage(), child: const Text('listner'), ); }, @@ -258,20 +254,13 @@ void main() { ), ), ); - await tester.tap(find.byKey(const Key('btn1'))); - await tester.pumpAndSettle(); - expect(find.text('Camera'), findsOneWidget); - expect(find.text('Gallery'), findsOneWidget); - expect(find.byIcon(Icons.camera_alt), findsOneWidget); - expect(find.byIcon(Icons.photo_library), findsOneWidget); - final file = File('fakePath'); when(locator().getPhotoFromGallery()) .thenAnswer((realInvocation) async { - return file; + return null; }); - await tester.tap(find.byIcon(Icons.photo_library)); + await tester.tap(find.byKey(const Key('btn1'))); await tester.pumpAndSettle(); - expect(model.imageFile, file); + expect(model.imageFile, null); }); test('No update performed if inputs are the same as existing data', diff --git a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart index 5ab2bc176..2e52fe882 100644 --- a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart +++ b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart @@ -273,6 +273,77 @@ Future main() async { expect(imageWidgetWithPicture, findsOneWidget); }); }); + testWidgets("Testing if modalSheet appears when changing profile picture", + (tester) async { + await mockNetworkImages(() async { + userConfig.updateUser(User()); + + userConfig.updateUser( + User( + firstName: 'Test', + lastName: 'Test', + email: 'test@test.com', + image: 'https://via.placeholder.com/150', + ), + ); + await tester.pumpWidget(createChangePassScreenDark()); + await tester.pumpAndSettle(); + final screenScaffoldWidget = find.byKey( + const Key('EditProfileScreenScaffold'), + ); + expect(screenScaffoldWidget, findsOneWidget); + expect( + (tester.firstWidget(find.byKey(const Key('Root'))) as MaterialApp) + .theme! + .scaffoldBackgroundColor, + TalawaTheme.darkTheme.scaffoldBackgroundColor, + ); + await tester.tap(find.byKey(const Key('AddRemoveImageButton'))); + await tester.pumpAndSettle(); + expect(find.text('Camera'), findsOneWidget); + expect(find.text('Gallery'), findsOneWidget); + expect(find.byIcon(Icons.camera_alt), findsOneWidget); + expect(find.byIcon(Icons.photo_library), findsOneWidget); + }); + }); + testWidgets("Testing if image selection from camera work fine", + (tester) async { + await mockNetworkImages(() async { + userConfig.updateUser(User()); + + userConfig.updateUser( + User( + firstName: 'Test', + lastName: 'Test', + email: 'test@test.com', + image: 'https://via.placeholder.com/150', + ), + ); + await tester.pumpWidget(createChangePassScreenDark()); + await tester.pumpAndSettle(); + final screenScaffoldWidget = find.byKey( + const Key('EditProfileScreenScaffold'), + ); + expect(screenScaffoldWidget, findsOneWidget); + expect( + (tester.firstWidget(find.byKey(const Key('Root'))) as MaterialApp) + .theme! + .scaffoldBackgroundColor, + TalawaTheme.darkTheme.scaffoldBackgroundColor, + ); + await tester.tap(find.byKey(const Key('AddRemoveImageButton'))); + await tester.pumpAndSettle(); + expect(find.text('Camera'), findsOneWidget); + expect(find.text('Gallery'), findsOneWidget); + expect(find.byIcon(Icons.camera_alt), findsOneWidget); + expect(find.byIcon(Icons.photo_library), findsOneWidget); + + await tester.ensureVisible(find.byKey(const Key('selectcamera'))); + await tester.tap(find.byKey(const Key('selectcamera'))); + await tester.ensureVisible(find.byKey(const Key('selectgallery'))); + await tester.tap(find.byKey(const Key('selectgallery'))); + }); + }); testWidgets("Testing if image selection and removal works", (tester) async { await mockNetworkImages(() async { userConfig.updateUser(User()); @@ -346,7 +417,7 @@ Future main() async { return null; }); - await model.getImageFromGallery(); + await model.getImage(); verify(multimediaPickerService.getPhotoFromGallery(camera: false)); expect(model.imageFile, null); @@ -356,7 +427,7 @@ Future main() async { .thenAnswer((_) async { return file; }); - await model.getImageFromGallery(camera: true); + await model.getImage(camera: true); verify(multimediaPickerService.getPhotoFromGallery(camera: true)); expect(model.imageFile, file); verify(notifyListenerCallback()); From f24b95b7092111f309aa4dab492db0f2ac91d84f Mon Sep 17 00:00:00 2001 From: Dante291 Date: Tue, 26 Dec 2023 00:48:52 +0530 Subject: [PATCH 14/22] adding requested changes --- .../edit_profile_view_model.dart | 4 +-- .../profile_page_view_model.dart | 1 - .../profile/edit_profile_page.dart | 1 + .../profile/edit_profile_page_test.dart | 36 ++++++++++++++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart index c54c4378c..20e83f6f0 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/edit_profile_view_model.dart @@ -74,7 +74,7 @@ class EditProfilePageViewModel extends BaseModel { /// * `camera`: for true it will select from camera otherwise gallery /// /// **returns**: - /// * `Future`: define_the_return + /// * `Future`: none Future selectImage({bool camera = false}) async { if (camera) { getImage(camera: true); @@ -108,7 +108,7 @@ class EditProfilePageViewModel extends BaseModel { /// * `newImage`: New profile picture that is to be updated. /// /// **returns**: - /// * `Future`: define_the_return + /// * `Future`: none Future updateUserProfile({ String? firstName, String? lastName, diff --git a/lib/view_model/after_auth_view_models/profile_view_models/profile_page_view_model.dart b/lib/view_model/after_auth_view_models/profile_view_models/profile_page_view_model.dart index 428f4ffc5..9bbdb9330 100644 --- a/lib/view_model/after_auth_view_models/profile_view_models/profile_page_view_model.dart +++ b/lib/view_model/after_auth_view_models/profile_view_models/profile_page_view_model.dart @@ -249,7 +249,6 @@ class ProfilePageViewModel extends BaseModel { BuildContext context, void Function(void Function()) setter, ) { - print('domBtn_$amount'); return InkWell( key: Key('domBtn_$amount'), onTap: () { diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index 16019dd01..e5c9505a2 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -26,6 +26,7 @@ class _EditProfilePageState extends State { backgroundColor: Theme.of(context).primaryColor, elevation: 0.0, title: Text( + // Title of the app bar(header). AppLocalizations.of(context)!.strictTranslate('Profile'), key: const Key('ProfileText'), style: Theme.of(context).textTheme.titleLarge!.copyWith( diff --git a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart index 2e52fe882..8c51fa0f9 100644 --- a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart +++ b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart @@ -340,8 +340,42 @@ Future main() async { await tester.ensureVisible(find.byKey(const Key('selectcamera'))); await tester.tap(find.byKey(const Key('selectcamera'))); + }); + }); + testWidgets("Testing if image selection from gallery work fine", + (tester) async { + await mockNetworkImages(() async { + userConfig.updateUser(User()); + + userConfig.updateUser( + User( + firstName: 'Test', + lastName: 'Test', + email: 'test@test.com', + image: 'https://via.placeholder.com/150', + ), + ); + await tester.pumpWidget(createChangePassScreenDark()); + await tester.pumpAndSettle(); + final screenScaffoldWidget = find.byKey( + const Key('EditProfileScreenScaffold'), + ); + expect(screenScaffoldWidget, findsOneWidget); + expect( + (tester.firstWidget(find.byKey(const Key('Root'))) as MaterialApp) + .theme! + .scaffoldBackgroundColor, + TalawaTheme.darkTheme.scaffoldBackgroundColor, + ); + await tester.tap(find.byKey(const Key('AddRemoveImageButton'))); + await tester.pumpAndSettle(); + expect(find.text('Camera'), findsOneWidget); + expect(find.text('Gallery'), findsOneWidget); + expect(find.byIcon(Icons.camera_alt), findsOneWidget); + expect(find.byIcon(Icons.photo_library), findsOneWidget); + await tester.ensureVisible(find.byKey(const Key('selectgallery'))); - await tester.tap(find.byKey(const Key('selectgallery'))); + await tester.tap(find.byIcon(Icons.photo_library)); }); }); testWidgets("Testing if image selection and removal works", (tester) async { From 87be0f814a5c918f1e104397e04e563e1e857dd6 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Tue, 26 Dec 2023 01:41:21 +0530 Subject: [PATCH 15/22] adding requested changes --- lib/views/after_auth_screens/add_post_page.dart | 3 ++- .../profile/edit_profile_page.dart | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/views/after_auth_screens/add_post_page.dart b/lib/views/after_auth_screens/add_post_page.dart index 36b9d4af7..028f751a9 100644 --- a/lib/views/after_auth_screens/add_post_page.dart +++ b/lib/views/after_auth_screens/add_post_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:talawa/enums/enums.dart'; import 'package:talawa/locator.dart'; +import 'package:talawa/services/size_config.dart'; import 'package:talawa/utils/app_localization.dart'; import 'package:talawa/view_model/after_auth_view_models/add_post_view_models/add_post_view_model.dart'; import 'package:talawa/views/base_view.dart'; @@ -86,7 +87,7 @@ class _AddPostState extends State { isImageNull: model.userPic == null, firstAlphabet: model.userName.substring(0, 1).toUpperCase(), imageUrl: model.userPic, - fontSize: 20, + fontSize: SizeConfig.screenHeight! * 0.018, ), title: Text(model.userName), subtitle: Text( diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index e5c9505a2..082180b6c 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -31,7 +31,7 @@ class _EditProfilePageState extends State { key: const Key('ProfileText'), style: Theme.of(context).textTheme.titleLarge!.copyWith( fontWeight: FontWeight.w600, - fontSize: 20, + fontSize: SizeConfig.screenHeight! * 0.03, ), ), ), @@ -109,9 +109,11 @@ class _EditProfilePageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - const Icon( + Icon( Icons.camera_alt, - size: 37, + size: SizeConfig + .screenHeight! * + 0.05, ), Text( AppLocalizations.of( @@ -133,9 +135,11 @@ class _EditProfilePageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - const Icon( + Icon( Icons.photo_library, - size: 37, + size: SizeConfig + .screenHeight! * + 0.05, ), Text( AppLocalizations.of( From ff9d6519f2955dec9844621d4deaa960a6b85058 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Tue, 26 Dec 2023 10:37:18 +0530 Subject: [PATCH 16/22] adding requested changes --- .../profile/edit_profile_page.dart | 80 ++++++++----------- 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index 082180b6c..79e7cafc3 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -36,7 +36,6 @@ class _EditProfilePageState extends State { ), ), body: SingleChildScrollView( - // SingleChildScrollView is a box in which a single widget can be scrolled. child: Column( children: [ SizedBox( @@ -100,57 +99,23 @@ class _EditProfilePageState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - GestureDetector( - key: const Key('selectcamera'), - onTap: () { + _createModalSheetButton( + context, + Icons.camera_alt, + 'Camera', + () { Navigator.of(context).pop(); model.selectImage(camera: true); }, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.camera_alt, - size: SizeConfig - .screenHeight! * - 0.05, - ), - Text( - AppLocalizations.of( - context, - )! - .strictTranslate( - 'Camera', - ), - ), - ], - ), ), - GestureDetector( - key: const Key('selectgallery'), - onTap: () { + _createModalSheetButton( + context, + Icons.photo_library, + 'Gallery', + () { Navigator.of(context).pop(); model.selectImage(); }, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.photo_library, - size: SizeConfig - .screenHeight! * - 0.05, - ), - Text( - AppLocalizations.of( - context, - )! - .strictTranslate( - 'Gallery', - ), - ), - ], - ), ), ], ), @@ -317,4 +282,29 @@ class _EditProfilePageState extends State { }, ); } + + /// Button for the different image selection methods. + /// + /// **params**: + /// * `context`:context for the sheet + /// * `icon`: icon for the method + /// * `label`: label for the method + /// * `onTap`: onTap funtionality for the method + /// + /// **returns**: + /// * `Widget`: Icon Button for selecting different image selection method. + Widget _createModalSheetButton( + BuildContext context, IconData icon, String label, VoidCallback onTap) { + return GestureDetector( + key: Key('select$label'), + onTap: onTap, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: SizeConfig.screenHeight! * 0.05), + Text(AppLocalizations.of(context)!.strictTranslate(label)), + ], + ), + ); + } } From 5273b2edb75ae8ffb2e959ec41357c90732ead96 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Tue, 26 Dec 2023 11:11:25 +0530 Subject: [PATCH 17/22] adding requested changes --- .../profile/edit_profile_page.dart | 88 ++++++++----------- 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index 79e7cafc3..184d5d9e5 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -22,11 +22,9 @@ class _EditProfilePageState extends State { return Scaffold( key: const Key('EditProfileScreenScaffold'), appBar: AppBar( - // returns a header for the page. backgroundColor: Theme.of(context).primaryColor, elevation: 0.0, title: Text( - // Title of the app bar(header). AppLocalizations.of(context)!.strictTranslate('Profile'), key: const Key('ProfileText'), style: Theme.of(context).textTheme.titleLarge!.copyWith( @@ -46,47 +44,38 @@ class _EditProfilePageState extends State { children: [ // if the profile pic is not empty then render Circle Avatar with image as background image // else render Circle Avatar with grey background color. - model.imageFile != null - ? CircleAvatar( - radius: SizeConfig.screenHeight! * 0.082, - backgroundImage: Image.file( + CircleAvatar( + radius: SizeConfig.screenHeight! * 0.082, + backgroundImage: model.imageFile != null + ? Image.file( model.imageFile!, fit: BoxFit.fitWidth, - ).image, - ) - : model.user.image != null - ? CircleAvatar( - key: const Key('UserImageInDb'), - radius: SizeConfig.screenHeight! * 0.082, - backgroundImage: NetworkImage( - model.user.image!, - ), - ) - : CircleAvatar( - key: const Key('UserImageNotInDb'), - radius: SizeConfig.screenHeight! * 0.082, - backgroundColor: Colors.grey.withOpacity(0.2), - child: Text( - model.user.firstName! - .substring(0, 1) - .toUpperCase() + - model.user.lastName! - .substring(0, 1) - .toUpperCase(), + ).image + : model.user.image != null + ? NetworkImage(model.user.image!) + : null, + backgroundColor: + model.imageFile == null && model.user.image == null + ? Colors.grey.withOpacity(0.2) + : null, + child: model.imageFile == null + ? model.user.image == null + ? Text( + '${model.user.firstName![0].toUpperCase()}${model.user.lastName![0].toUpperCase()}', style: Theme.of(context) .textTheme .headlineMedium, - ), - ), + ) + : null + : null, + ), Positioned( bottom: 0, right: 0, child: InkWell( - // button to remove or set the profile image. key: const Key('AddRemoveImageButton'), onTap: () { - // if image is null the function will be get getImageFromGallery() - // else removeImage() + // modal sheet for image selection from camera or gallery. model.imageFile == null ? showModalBottomSheet( context: context, @@ -105,7 +94,9 @@ class _EditProfilePageState extends State { 'Camera', () { Navigator.of(context).pop(); - model.selectImage(camera: true); + model.selectImage( + camera: true, + ); }, ), _createModalSheetButton( @@ -124,25 +115,21 @@ class _EditProfilePageState extends State { ) : model.removeImage(); }, - child: model.imageFile == null - ? CircleAvatar( - radius: SizeConfig.screenHeight! * 0.034, - backgroundColor: - Theme.of(context).colorScheme.secondary, - child: const Icon( + child: CircleAvatar( + radius: SizeConfig.screenHeight! * 0.034, + backgroundColor: model.imageFile == null + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.secondary, + child: model.imageFile == null + ? const Icon( Icons.photo_camera, color: Colors.white, - ), - ) - : CircleAvatar( - radius: SizeConfig.screenHeight! * 0.02, - backgroundColor: - Theme.of(context).colorScheme.secondary, - child: const Icon( + ) + : const Icon( Icons.close, color: Colors.white, ), - ), + ), ), ), ], @@ -260,7 +247,6 @@ class _EditProfilePageState extends State { ), ), const Divider(), - // button to update the profile. TextButton( key: const Key('updatebtn'), onPressed: () { @@ -294,7 +280,11 @@ class _EditProfilePageState extends State { /// **returns**: /// * `Widget`: Icon Button for selecting different image selection method. Widget _createModalSheetButton( - BuildContext context, IconData icon, String label, VoidCallback onTap) { + BuildContext context, + IconData icon, + String label, + VoidCallback onTap, + ) { return GestureDetector( key: Key('select$label'), onTap: onTap, From f41bc0d36fce5c79bb829bbd8959dcc500b52a1d Mon Sep 17 00:00:00 2001 From: Dante291 Date: Tue, 26 Dec 2023 11:38:04 +0530 Subject: [PATCH 18/22] adding requested changes --- .../profile/edit_profile_page.dart | 1 + .../add_post_page_test.dart | 1 + .../profile/edit_profile_page_test.dart | 38 ++++--------------- 3 files changed, 9 insertions(+), 31 deletions(-) diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index 184d5d9e5..249315ec2 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -45,6 +45,7 @@ class _EditProfilePageState extends State { // if the profile pic is not empty then render Circle Avatar with image as background image // else render Circle Avatar with grey background color. CircleAvatar( + key: const Key('profilepic'), radius: SizeConfig.screenHeight! * 0.082, backgroundImage: model.imageFile != null ? Image.file( diff --git a/test/widget_tests/after_auth_screens/add_post_page_test.dart b/test/widget_tests/after_auth_screens/add_post_page_test.dart index 4888dde9d..b9984bcb7 100644 --- a/test/widget_tests/after_auth_screens/add_post_page_test.dart +++ b/test/widget_tests/after_auth_screens/add_post_page_test.dart @@ -91,6 +91,7 @@ void main() { // SizeConfig().test(); testSetupLocator(); // locator.registerSingleton(LikeButtonViewModel()); + sizeConfig.test(); setUp(() { registerServices(); diff --git a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart index 8c51fa0f9..01a6171ae 100644 --- a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart +++ b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart @@ -132,7 +132,7 @@ Future main() async { TalawaTheme.lightTheme.scaffoldBackgroundColor, ); final imageWidgetWithPicture = find.byKey( - const Key('UserImageNotInDb'), + const Key('profilepic'), ); expect(imageWidgetWithPicture, findsOneWidget); }); @@ -163,7 +163,7 @@ Future main() async { TalawaTheme.lightTheme.scaffoldBackgroundColor, ); final imageWidgetWithPicture = find.byKey( - const Key('UserImageInDb'), + const Key('profilepic'), ); expect(imageWidgetWithPicture, findsOneWidget); }); @@ -216,31 +216,7 @@ Future main() async { ); expect(appBarText, findsOneWidget); }); - testWidgets("Testing if Edit Screen shows image when not exist in database", - (tester) async { - await mockNetworkImages(() async { - userConfig.updateUser(User()); - userConfig.updateUser( - User(firstName: 'Test', lastName: 'Test', email: 'test@test.com'), - ); - await tester.pumpWidget(createChangePassScreenDark()); - await tester.pumpAndSettle(); - final screenScaffoldWidget = find.byKey( - const Key('EditProfileScreenScaffold'), - ); - expect(screenScaffoldWidget, findsOneWidget); - expect( - (tester.firstWidget(find.byKey(const Key('Root'))) as MaterialApp) - .theme! - .scaffoldBackgroundColor, - TalawaTheme.darkTheme.scaffoldBackgroundColor, - ); - final imageWidgetWithPicture = find.byKey( - const Key('UserImageNotInDb'), - ); - expect(imageWidgetWithPicture, findsOneWidget); - }); - }); + const Key('profilepic'); testWidgets( "Testing if Edit Screen shows image when already exist in database", (tester) async { @@ -268,7 +244,7 @@ Future main() async { TalawaTheme.darkTheme.scaffoldBackgroundColor, ); final imageWidgetWithPicture = find.byKey( - const Key('UserImageInDb'), + const Key('profilepic'), ); expect(imageWidgetWithPicture, findsOneWidget); }); @@ -338,8 +314,8 @@ Future main() async { expect(find.byIcon(Icons.camera_alt), findsOneWidget); expect(find.byIcon(Icons.photo_library), findsOneWidget); - await tester.ensureVisible(find.byKey(const Key('selectcamera'))); - await tester.tap(find.byKey(const Key('selectcamera'))); + await tester.ensureVisible(find.byIcon(Icons.camera_alt)); + await tester.tap(find.byIcon(Icons.camera_alt)); }); }); testWidgets("Testing if image selection from gallery work fine", @@ -374,7 +350,7 @@ Future main() async { expect(find.byIcon(Icons.camera_alt), findsOneWidget); expect(find.byIcon(Icons.photo_library), findsOneWidget); - await tester.ensureVisible(find.byKey(const Key('selectgallery'))); + await tester.ensureVisible(find.byIcon(Icons.photo_library)); await tester.tap(find.byIcon(Icons.photo_library)); }); }); From e2a0129062c5f88e804621c426a80a193412036a Mon Sep 17 00:00:00 2001 From: Dante291 Date: Tue, 26 Dec 2023 13:52:58 +0530 Subject: [PATCH 19/22] test for missing lines --- .../profile/edit_profile_page_test.dart | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart index 01a6171ae..fb3284606 100644 --- a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart +++ b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart @@ -413,24 +413,15 @@ Future main() async { tearDown(() { unregisterServices(); }); - testWidgets('Testing image selection and removal in Edit Profile Screen', + + testWidgets( + 'Testing image selection when user is selecting image from device', (tester) async { final notifyListenerCallback = MockCallbackFunction(); final model = EditProfilePageViewModel() ..addListener(notifyListenerCallback); model.initialize(); - // testing getImageFromGallery - // with camera false - when(multimediaPickerService.getPhotoFromGallery(camera: false)) - .thenAnswer((realInvocation) async { - return null; - }); - - await model.getImage(); - verify(multimediaPickerService.getPhotoFromGallery(camera: false)); - expect(model.imageFile, null); - // with camera true final file = File('fakePath'); when(multimediaPickerService.getPhotoFromGallery(camera: true)) @@ -441,6 +432,20 @@ Future main() async { verify(multimediaPickerService.getPhotoFromGallery(camera: true)); expect(model.imageFile, file); verify(notifyListenerCallback()); + await tester.pumpWidget(createChangePassScreenDark()); + await tester.pumpAndSettle(); + expect(find.byKey(const Key('AddRemoveImageButton')), findsOneWidget); + await tester.tap(find.byKey(const Key('AddRemoveImageButton'))); + await tester.pumpAndSettle(); + await tester.tap(find.byIcon(Icons.camera_alt)); + await tester.pumpAndSettle(); + expect(model.imageFile, isNotNull); + }); + testWidgets('Testing if image removal work properly', (tester) async { + final notifyListenerCallback = MockCallbackFunction(); + final model = EditProfilePageViewModel() + ..addListener(notifyListenerCallback); + model.initialize(); // testing removeImage model.removeImage(); From e9694363699859669b71cba5324a792eca8e3405 Mon Sep 17 00:00:00 2001 From: Dante291 Date: Tue, 26 Dec 2023 19:33:17 +0530 Subject: [PATCH 20/22] adding requested changes --- lib/views/after_auth_screens/profile/edit_profile_page.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index 249315ec2..32de5599d 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -292,7 +292,10 @@ class _EditProfilePageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Icon(icon, size: SizeConfig.screenHeight! * 0.05), + Icon( + icon, + size: SizeConfig.screenHeight! * 0.05, + ), Text(AppLocalizations.of(context)!.strictTranslate(label)), ], ), From 114e3c7f0e8c54aeb3f43180907653ae362d449a Mon Sep 17 00:00:00 2001 From: Dante291 Date: Tue, 26 Dec 2023 20:16:14 +0530 Subject: [PATCH 21/22] adding requested changes --- .../profile/profile_page.dart | 25 +++++++++++-------- .../profile/profile_page_test.dart | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/views/after_auth_screens/profile/profile_page.dart b/lib/views/after_auth_screens/profile/profile_page.dart index b9ce6ac50..f2d4c74e9 100644 --- a/lib/views/after_auth_screens/profile/profile_page.dart +++ b/lib/views/after_auth_screens/profile/profile_page.dart @@ -51,7 +51,7 @@ class ProfilePage extends StatelessWidget { AppLocalizations.of(context)!.strictTranslate('Profile'), style: Theme.of(context).textTheme.titleLarge!.copyWith( // fontWeight: FontWeight.w600, - fontSize: 20, + fontSize: SizeConfig.screenHeight! * 0.03, fontFamily: 'open-sans', color: Colors.white, ), @@ -63,7 +63,7 @@ class ProfilePage extends StatelessWidget { context: context, builder: (BuildContext context) { return Container( - height: 150, + height: SizeConfig.screenHeight! * 0.15, decoration: const BoxDecoration( borderRadius: BorderRadius.only( bottomLeft: Radius.zero, @@ -89,7 +89,10 @@ class ProfilePage extends StatelessWidget { ), ), ), - const Divider(endIndent: 20, indent: 20), + Divider( + endIndent: SizeConfig.screenHeight! * 0.03, + indent: SizeConfig.screenHeight! * 0.03, + ), TextButton( onPressed: () { model.logout(context); @@ -140,7 +143,7 @@ class ProfilePage extends StatelessWidget { .textTheme .titleLarge! .fontSize, - maxRadius: 60, + maxRadius: SizeConfig.screenHeight! * 0.02, ), ), ), @@ -150,9 +153,9 @@ class ProfilePage extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: Text( '${model.currentUser.firstName!} ${model.currentUser.lastName!}', - style: const TextStyle( + style: TextStyle( color: Colors.white, - fontSize: 20, + fontSize: SizeConfig.screenHeight! * 0.025, fontFamily: 'open-sans', ), ), @@ -161,7 +164,7 @@ class ProfilePage extends StatelessWidget { Expanded( flex: 1, child: IconButton( - key: const Key('inv1'), + key: const Key('inviteicon'), icon: Icon( Icons.share, color: @@ -172,8 +175,8 @@ class ProfilePage extends StatelessWidget { ), ], ), - const SizedBox( - height: 20, + SizedBox( + height: SizeConfig.screenHeight! * 0.02, ), TalawaPluginProvider( pluginName: "Donation", @@ -200,7 +203,7 @@ class ProfilePage extends StatelessWidget { ), ), SizedBox( - height: 600, + height: SizeConfig.screenHeight! * 0.6, width: double.infinity, child: ContainedTabBarView( tabs: [ @@ -263,7 +266,7 @@ class ProfilePage extends StatelessWidget { Icons.share, color: Theme.of(context).colorScheme.secondary, - size: 30, + size: SizeConfig.screenHeight! * 0.025, ), // title title: AppLocalizations.of(context)! diff --git a/test/views/after_auth_screens/profile/profile_page_test.dart b/test/views/after_auth_screens/profile/profile_page_test.dart index 92d4dca20..487f5e9a7 100644 --- a/test/views/after_auth_screens/profile/profile_page_test.dart +++ b/test/views/after_auth_screens/profile/profile_page_test.dart @@ -89,7 +89,7 @@ void main() async { ), ); await tester.pumpAndSettle(); - await tester.tap(find.byKey(const Key('inv1'))); + await tester.tap(find.byKey(const Key('inviteicon'))); await tester.pumpAndSettle(); }); testWidgets('check if Donate button work', (tester) async { From a459cb55c1d30a815a81b619e18ddd5dcf83ec5a Mon Sep 17 00:00:00 2001 From: Dante291 Date: Tue, 26 Dec 2023 20:31:33 +0530 Subject: [PATCH 22/22] test for missing lines --- .../after_auth_screens/profile/profile_page.dart | 4 +++- .../profile/profile_page_test.dart | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/views/after_auth_screens/profile/profile_page.dart b/lib/views/after_auth_screens/profile/profile_page.dart index f2d4c74e9..ab058478d 100644 --- a/lib/views/after_auth_screens/profile/profile_page.dart +++ b/lib/views/after_auth_screens/profile/profile_page.dart @@ -58,12 +58,14 @@ class ProfilePage extends StatelessWidget { ), actions: [ IconButton( + key: const Key('settingIcon'), onPressed: () { showModalBottomSheet( context: context, builder: (BuildContext context) { return Container( - height: SizeConfig.screenHeight! * 0.15, + key: const Key('sheetContainer'), + height: SizeConfig.screenHeight! * 0.17, decoration: const BoxDecoration( borderRadius: BorderRadius.only( bottomLeft: Radius.zero, diff --git a/test/views/after_auth_screens/profile/profile_page_test.dart b/test/views/after_auth_screens/profile/profile_page_test.dart index 487f5e9a7..64a7ced12 100644 --- a/test/views/after_auth_screens/profile/profile_page_test.dart +++ b/test/views/after_auth_screens/profile/profile_page_test.dart @@ -125,5 +125,17 @@ void main() async { await tester.tap(find.text('Invite')); await tester.pumpAndSettle(); }); + testWidgets('check if modal sheet for settings shows up', (tester) async { + await tester.pumpWidget( + createProfilePage( + mainScreenViewModel: locator(), + ), + ); + await tester.pumpAndSettle(); + await tester.ensureVisible(find.byKey(const Key('settingIcon'))); + await tester.tap(find.byKey(const Key('settingIcon'))); + await tester.pumpAndSettle(); + expect(find.byKey(const Key('sheetContainer')), findsOneWidget); + }); }); }