diff --git a/lang/de.json b/lang/de.json index 7ff5a6eea1..d903796ca1 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 bed1b4a658..09bdd9211f 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 5a8ffb18a3..d1060a09b5 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 5070b7deb7..6db60cd069 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 db33287420..3e3ca23c4c 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 cf17112a0d..98b00fe3f8 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 6948f0e918..1bd61aad6b 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 8d96d47899..ef0a204878 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/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 0bcc7ea1ad..eaed4aac3f 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,9 @@ class AddPostViewModel extends BaseModel { String get userName => userConfig.currentUser.firstName! + userConfig.currentUser.lastName!; + /// User profile picture. + 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 b65a258320..20e83f6f03 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,10 @@ +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/third_party_service/multi_media_pick_service.dart'; import 'package:talawa/view_model/base_view_model.dart'; @@ -16,6 +20,9 @@ class EditProfilePageViewModel extends BaseModel { /// profile image. late File? imageFile; + /// profile image in base64. + String? base64Image; + /// first name controller. TextEditingController firstNameTextController = TextEditingController(); @@ -52,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) { @@ -61,6 +68,108 @@ class EditProfilePageViewModel extends BaseModel { } } + /// Method to select image from gallery or camera. + /// + /// **params**: + /// * `camera`: for true it will select from camera otherwise gallery + /// + /// **returns**: + /// * `Future`: none + Future selectImage({bool camera = false}) async { + if (camera) { + getImage(camera: true); + } else { + getImage(); + } + } + + /// This function is used to convert the image into Base64 format. + /// + /// **params**: + /// * `file`: Takes the image in format of file. + /// + /// **returns**: + /// * `Future`: image in string format + Future convertToBase64(File file) async { + try { + final List bytes = await file.readAsBytes(); + base64Image = base64Encode(bytes); + return base64Image!; + } catch (error) { + return ''; + } + } + + /// Method to update user profile. + /// + /// **params**: + /// * `firstName`: updated first name. + /// * `lastName`: updated last name. + /// * `newImage`: New profile picture that is to be updated. + /// + /// **returns**: + /// * `Future`: none + Future updateUserProfile({ + String? firstName, + String? lastName, + File? newImage, + }) async { + if (firstName == user.firstName && + newImage == null && + lastName == user.lastName) { + 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'] as List)[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 (_) { + navigationService.showTalawaErrorSnackBar( + "Something went wrong", + MessageType.error, + ); + } + } + /// This function remove the selected image. /// /// **params**: 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 d17dadb471..9bbdb9330e 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 @@ -250,7 +250,7 @@ class ProfilePageViewModel extends BaseModel { void Function(void Function()) setter, ) { return InkWell( - key: const Key('dombtn1'), + key: Key('domBtn_$amount'), onTap: () { setter(() { donationAmount.text = amount; 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 170da8b868..688b6878a0 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 7ed9fba9e8..028f751a9d 100644 --- a/lib/views/after_auth_screens/add_post_page.dart +++ b/lib/views/after_auth_screens/add_post_page.dart @@ -1,9 +1,11 @@ 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'; +import 'package:talawa/widgets/custom_avatar.dart'; /// Add Post View Model. late AddPostViewModel model; @@ -81,7 +83,12 @@ class _AddPostState extends State { child: Column( children: [ ListTile( - leading: const CircleAvatar(radius: 25), + leading: CustomAvatar( + isImageNull: model.userPic == null, + firstAlphabet: model.userName.substring(0, 1).toUpperCase(), + imageUrl: model.userPic, + fontSize: SizeConfig.screenHeight! * 0.018, + ), 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 05b74a1fc5..32de5599d7 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -22,21 +22,18 @@ 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( fontWeight: FontWeight.w600, - fontSize: 20, + fontSize: SizeConfig.screenHeight! * 0.03, ), ), ), body: SingleChildScrollView( - // SingleChildScrollView is a box in which a single widget can be scrolled. child: Column( children: [ SizedBox( @@ -47,69 +44,93 @@ 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( + key: const Key('profilepic'), + 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 - ? model.getImageFromGallery(camera: true) + ? showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return Container( + height: + SizeConfig.screenHeight! * 0.135, + padding: const EdgeInsets.all(17), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + _createModalSheetButton( + context, + Icons.camera_alt, + 'Camera', + () { + Navigator.of(context).pop(); + model.selectImage( + camera: true, + ); + }, + ), + _createModalSheetButton( + context, + Icons.photo_library, + 'Gallery', + () { + Navigator.of(context).pop(); + model.selectImage(); + }, + ), + ], + ), + ); + }, + ) : 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, ), - ), + ), ), ), ], @@ -227,9 +248,16 @@ class _EditProfilePageState extends State { ), ), const Divider(), - // button to update the profile. TextButton( - onPressed: () {}, + key: const Key('updatebtn'), + 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'), ), @@ -241,4 +269,36 @@ 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)), + ], + ), + ); + } } diff --git a/lib/views/after_auth_screens/profile/profile_page.dart b/lib/views/after_auth_screens/profile/profile_page.dart index 5f8f2864ed..ab058478d8 100644 --- a/lib/views/after_auth_screens/profile/profile_page.dart +++ b/lib/views/after_auth_screens/profile/profile_page.dart @@ -51,21 +51,22 @@ 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, ), ), actions: [ IconButton( + key: const Key('settingIcon'), onPressed: () { showModalBottomSheet( context: context, builder: (BuildContext context) { return Container( - height: 200, + key: const Key('sheetContainer'), + height: SizeConfig.screenHeight! * 0.17, decoration: const BoxDecoration( - color: Colors.white, borderRadius: BorderRadius.only( bottomLeft: Radius.zero, bottomRight: Radius.zero, @@ -86,11 +87,14 @@ class ProfilePage extends StatelessWidget { child: const Text( "Edit Profile", style: TextStyle( - color: Colors.black38, fontFamily: 'open-sans', ), ), ), + Divider( + endIndent: SizeConfig.screenHeight! * 0.03, + indent: SizeConfig.screenHeight! * 0.03, + ), TextButton( onPressed: () { model.logout(context); @@ -98,7 +102,6 @@ class ProfilePage extends StatelessWidget { child: const Text( "Log Out", style: TextStyle( - color: Colors.black38, fontFamily: 'open-sans', ), ), @@ -118,191 +121,198 @@ 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( + key: const Key('profilepic'), + isImageNull: model.currentUser.image == null, + firstAlphabet: model.currentUser.firstName! + .substring(0, 1), + imageUrl: model.currentUser.image, + fontSize: Theme.of(context) + .textTheme + .titleLarge! + .fontSize, + maxRadius: SizeConfig.screenHeight! * 0.02, ), ), ), - ), - 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: TextStyle( + color: Colors.white, + fontSize: SizeConfig.screenHeight! * 0.025, + 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( + key: const Key('inviteicon'), + 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'), - ], + SizedBox( + height: SizeConfig.screenHeight! * 0.02, + ), + 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: SizeConfig.screenHeight! * 0.6, + 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( + key: const Key('tastscrn'), + 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: SizeConfig.screenHeight! * 0.025, + ), + // 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 10cd2681dd..7ec420a3ad 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 112be39daa..d08d21490d 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,290 @@ 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 SelectImage from camera method works', + (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.selectImage(camera: true), + child: const Text('listner'), + ); + }, + ), + ), + ), + ); + final file = File('fakePath'); + when(locator().getPhotoFromGallery(camera: true)) .thenAnswer((realInvocation) async { - return null; + return file; }); + await tester.tap(find.byKey(const Key('btn1'))); + await tester.pumpAndSettle(); + verify(multimediaPickerService.getPhotoFromGallery(camera: true)) + .called(1); + expect(model.imageFile, file); + }); + testWidgets('Test if selectImage from gallery method works', + (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.selectImage(), + child: const Text('listner'), + ); + }, + ), + ), + ), + ); + final file = File('fakePath'); + when(locator().getPhotoFromGallery()) + .thenAnswer((realInvocation) async { + return file; + }); + await tester.tap(find.byKey(const Key('btn1'))); + await tester.pumpAndSettle(); + expect(model.imageFile, file); + }); + testWidgets( + 'Test if SelectImage from camera method works if null 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.selectImage(camera: true), + child: const Text('listner'), + ); + }, + ), + ), + ), + ); + when(locator().getPhotoFromGallery(camera: true)) + .thenAnswer((realInvocation) async { + return null; + }); + await tester.tap(find.byKey(const Key('btn1'))); + await tester.pumpAndSettle(); + verify(multimediaPickerService.getPhotoFromGallery(camera: true)) + .called(1); 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); - - final file = File('fakePath'); + testWidgets( + 'Test if selectImage from gallery method works when null 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.selectImage(), + child: const Text('listner'), + ); + }, + ), + ), + ), + ); when(locator().getPhotoFromGallery()) .thenAnswer((realInvocation) async { - return file; + return null; }); + await tester.tap(find.byKey(const Key('btn1'))); + await tester.pumpAndSettle(); + expect(model.imageFile, null); + }); + test('No update performed if inputs are the same as existing data', + () async { + final model = EditProfilePageViewModel(); model.initialize(); - await model.getImageFromGallery(); + await model.updateUserProfile( + firstName: model.user.firstName, + lastName: model.user.lastName, + newImage: null, + ); + verifyNever( + databaseFunctions.gqlAuthMutation( + queries.updateUserProfile(), + variables: {'id': 'xzy1'}, + ), + ); + }); - expect(model.imageFile, file); - verify(notifyListenerCallback()).called(1); + 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 { 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 b4931c9df9..b674342905 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 8a4e4861b9..64a7ced120 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,75 @@ 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.pump(); + expect(find.byType(RefreshIndicator), findsOneWidget); + await tester.drag( + find.byKey(const Key('profilepic')), + const Offset(0, 300), + ); + 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('inviteicon'))); + 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 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(); + }); + 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); }); }); } 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 f9f15117c2..b9984bcb71 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'; @@ -88,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 24698a81ae..fb32846060 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(); } @@ -129,7 +132,7 @@ Future main() async { TalawaTheme.lightTheme.scaffoldBackgroundColor, ); final imageWidgetWithPicture = find.byKey( - const Key('UserImageNotInDb'), + const Key('profilepic'), ); expect(imageWidgetWithPicture, findsOneWidget); }); @@ -160,7 +163,7 @@ Future main() async { TalawaTheme.lightTheme.scaffoldBackgroundColor, ); final imageWidgetWithPicture = find.byKey( - const Key('UserImageInDb'), + const Key('profilepic'), ); expect(imageWidgetWithPicture, findsOneWidget); }); @@ -213,12 +216,20 @@ Future main() async { ); expect(appBarText, findsOneWidget); }); - testWidgets("Testing if Edit Screen shows image when not exist in database", + const Key('profilepic'); + testWidgets( + "Testing if Edit Screen shows image when already exist in database", (tester) async { await mockNetworkImages(() async { userConfig.updateUser(User()); + userConfig.updateUser( - User(firstName: 'Test', lastName: 'Test', email: 'test@test.com'), + User( + firstName: 'Test', + lastName: 'Test', + email: 'test@test.com', + image: 'https://via.placeholder.com/150', + ), ); await tester.pumpWidget(createChangePassScreenDark()); await tester.pumpAndSettle(); @@ -233,13 +244,12 @@ Future main() async { TalawaTheme.darkTheme.scaffoldBackgroundColor, ); final imageWidgetWithPicture = find.byKey( - const Key('UserImageNotInDb'), + const Key('profilepic'), ); expect(imageWidgetWithPicture, findsOneWidget); }); }); - testWidgets( - "Testing if Edit Screen shows image when already exist in database", + testWidgets("Testing if modalSheet appears when changing profile picture", (tester) async { await mockNetworkImages(() async { userConfig.updateUser(User()); @@ -264,10 +274,84 @@ Future main() async { .scaffoldBackgroundColor, TalawaTheme.darkTheme.scaffoldBackgroundColor, ); - final imageWidgetWithPicture = find.byKey( - const Key('UserImageInDb'), + 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', + ), ); - expect(imageWidgetWithPicture, findsOneWidget); + 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.byIcon(Icons.camera_alt)); + await tester.tap(find.byIcon(Icons.camera_alt)); + }); + }); + 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.byIcon(Icons.photo_library)); + await tester.tap(find.byIcon(Icons.photo_library)); }); }); testWidgets("Testing if image selection and removal works", (tester) async { @@ -295,6 +379,30 @@ Future main() async { tester.tap(imageAvatar); }); }); + testWidgets("Testing Update butoon", (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 updateButtonFinder = find.byKey(const Key('updatebtn')); + expect(updateButtonFinder, findsOneWidget); + await tester.tap(updateButtonFinder); + await tester.pumpAndSettle(); + }); + }); }); group('Testing image selection and removal in Edit Profile Screen', () { setUp(() { @@ -305,34 +413,39 @@ 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.getImageFromGallery(); - verify(multimediaPickerService.getPhotoFromGallery(camera: false)); - expect(model.imageFile, null); - // with camera true final file = File('fakePath'); when(multimediaPickerService.getPhotoFromGallery(camera: true)) .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()); + 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(); diff --git a/test/widget_tests/widgets/post_widget_test.dart b/test/widget_tests/widgets/post_widget_test.dart index 15d0af208d..20f2669281 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');