From be9eaa562bb27f707f6204cf24a20808e7126588 Mon Sep 17 00:00:00 2001 From: Peter Harrison <16875803+palisadoes@users.noreply.github.com> Date: Sat, 4 May 2024 13:53:23 -0700 Subject: [PATCH] Merge develop - 20240504 (#2503) * Deleted all files in the main branch in anticipation of merging develop into main cleanly * Merge develop into main --- lang/de.json | 2 + lang/en.json | 2 + lang/es.json | 2 + lang/fr.json | 2 + lang/hi.json | 2 + lang/ja.json | 2 + lang/pt.json | 2 + lang/zh.json | 2 + lib/constants/custom_theme.dart | 20 +- lib/constants/recurrence_values.dart | 290 ++++++++- .../chats/chat_list_tile_data_model.g.dart | 7 +- lib/models/chats/chat_message.g.dart | 7 +- lib/models/chats/chat_user.g.dart | 7 +- lib/models/events/event_model.dart | 15 - lib/models/user/user_info.g.dart | 3 - .../create_event_view_model.dart | 123 ++-- .../explore_events_view_model.dart | 42 +- .../app_settings/app_settings_page.dart | 10 +- .../events/create_custom_recurring_event.dart | 196 ++++-- .../events/create_event_page.dart | 252 ++++---- .../events/edit_event_page.dart | 18 +- .../events/event_info_body.dart | 37 +- .../events/explore_events.dart | 92 ++- .../feed/organization_feed.dart | 11 +- .../profile/profile_page.dart | 23 +- lib/views/pre_auth_screens/login.dart | 2 +- lib/views/pre_auth_screens/set_url.dart | 3 +- ...create_recurring_event_helper_widgets.dart | 25 +- lib/widgets/custom_alert_dialog.dart | 3 +- lib/widgets/custom_avatar.dart | 9 +- lib/widgets/custom_weekday_selector.dart | 17 +- lib/widgets/event_card.dart | 58 +- lib/widgets/event_date_time_tile.dart | 42 +- lib/widgets/post_detailed_page.dart | 34 +- lib/widgets/post_list_widget.dart | 6 +- lib/widgets/post_modal.dart | 43 +- lib/widgets/post_widget.dart | 132 ++-- lib/widgets/recurrence_dialog.dart | 207 ++++-- pubspec.lock | 36 +- pubspec.yaml | 10 +- test/helpers/test_helpers.mocks.dart | 285 ++++++--- .../chat/chat_list_tile_data_model_test.dart | 43 +- test/model_tests/chat/chat_user_test.dart | 5 +- test/model_tests/events/event_model_test.dart | 9 - test/service_tests/event_service_test.dart | 3 - .../create_event_view_model_test.dart | 301 ++++++++- .../explore_events_view_model_test.dart | 1 - .../create_custom_recurring_event_test.dart | 284 ++++++++- .../events/create_event_page_test.dart | 234 ++++--- .../events/event_info_body_test.dart | 3 - .../profile/user_event_test.dart | 3 - .../app_settings/app_setting_page_test.dart | 6 +- .../events/create_event_page_test.dart | 15 +- .../events/event_info_page_test.dart | 23 +- .../pre_auth_screens/set_url_page_test.dart | 218 +++++-- .../widgets/event_date_time_tile_test.dart | 22 +- .../widgets/post_widget_test.dart | 603 +++--------------- .../widgets/recurrence_dialog_test.dart | 257 ++++++++ 58 files changed, 2486 insertions(+), 1625 deletions(-) create mode 100644 test/widget_tests/widgets/recurrence_dialog_test.dart diff --git a/lang/de.json b/lang/de.json index 679e807a5..d1e979820 100644 --- a/lang/de.json +++ b/lang/de.json @@ -70,6 +70,8 @@ "Add Event": "Ereignis hinzufügen", "Add": "Anzeige", "Add Image": "Bild hinzufügen", + "Select Start Date": "Startdatum auswählen", + "Select End Date": "Enddatum auswählen", "Select Start Date and Time": "Wählen Sie Startdatum und -uhrzeit", "Select End Date and Time": "Wählen Sie Enddatum und -uhrzeit", "Does not repeat": "Wiederholt sich nicht", diff --git a/lang/en.json b/lang/en.json index aa970061d..a110b9ae4 100644 --- a/lang/en.json +++ b/lang/en.json @@ -76,6 +76,8 @@ "Add Event": "Add Event", "Add": "Add", "Add Image": "Add Image", + "Select Start Date": "Select Start Date", + "Select End Date": "Select End Date", "Select Start Date and Time": "Select Start Date and Time", "Select End Date and Time": "Select End Date and Time", "Does not repeat": "Does not repeat", diff --git a/lang/es.json b/lang/es.json index df4d6ea1c..9885c9af3 100644 --- a/lang/es.json +++ b/lang/es.json @@ -73,6 +73,8 @@ "Add Image": "Añadir imagen", "Select Start Date and Time": "Seleccione la fecha y hora de inicio", "Select End Date and Time": "Seleccione la fecha y hora de finalización", + "Select Start Date": "Seleccionar fecha de inicio", + "Select End Date": "Seleccionar fecha de finalización", "Does not repeat": "No se repite", "Keep Public": "Mantener pública", "Keep Registerable": "Mantener registrable", diff --git a/lang/fr.json b/lang/fr.json index 9c241bbb1..c6535f724 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -76,6 +76,8 @@ "Add Event": "Ajouter un évènement", "Add": "Ajoutez-le", "Add Image": "Ajouter une image", + "Select Start Date": "Sélectionner la date de début", + "Select End Date": "Sélectionner la date de fin", "Select Start Date and Time": "Sélectionnez la date et l'heure de début", "Select End Date and Time": "Sélectionnez la date et l'heure de fin", "Does not repeat": "ça ne se répète pas", diff --git a/lang/hi.json b/lang/hi.json index 75d702e97..4cc46c1cc 100644 --- a/lang/hi.json +++ b/lang/hi.json @@ -71,6 +71,8 @@ "Add Event": "कार्यक्रम जोड़ें", "Add": "इसे जोड़ें", "Add Image": "छवि जोड़ें", + "Select Start Date": "शुरूआत की तिथि का चयन करें", + "Select End Date": "समाप्ति की तिथि का चयन करें", "Select Start Date and Time": "प्रारंभ दिनांक और समय चुनें", "Select End Date and Time": "समाप्ति तिथि और समय चुनें", "Does not repeat": "यह दोहराता नहीं है", diff --git a/lang/ja.json b/lang/ja.json index 7c802fe1e..75dd81dee 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -71,6 +71,8 @@ "Add Event": "イベントを追加", "Add": "追加", "Add Image": "画像を追加", + "Select Start Date": "開始日を選択", + "Select End Date": "終了日を選択", "Select Start Date and Time": "開始日時を選択します", "Select End Date and Time": "終了日時を選択します", "Does not repeat": "繰り返さない", diff --git a/lang/pt.json b/lang/pt.json index f83c80a06..861139a63 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -71,6 +71,8 @@ "Add Event": "Adicionar Evento", "Add": "Adicionar", "Add Image": "Adicionar imagem", + "Select Start Date": "Selecionar data de início", + "Select End Date": "Selecionar data de término", "Select Start Date and Time": "Selecione a data e hora de início", "Select End Date and Time": "Selecione a data e hora de término", "Does not repeat": "Não repete", diff --git a/lang/zh.json b/lang/zh.json index 8d473194c..68e3dc825 100644 --- a/lang/zh.json +++ b/lang/zh.json @@ -71,6 +71,8 @@ "Add Event": "添加事件", "Add": "添加", "Add Image": "添加图片", + "Select Start Date": "选择开始日期", + "Select End Date": "选择结束日期", "Select Start Date and Time": "选择开始日期和时间", "Select End Date and Time": "选择结束日期和时间", "Does not repeat": "不重复", diff --git a/lib/constants/custom_theme.dart b/lib/constants/custom_theme.dart index 15909e59d..8f01eee81 100644 --- a/lib/constants/custom_theme.dart +++ b/lib/constants/custom_theme.dart @@ -1,9 +1,7 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - import 'package:flutter/material.dart'; ///This file contains various custom themes. +/// ///For instance, lightTheme, darkTheme, _lightTextTheme, _darkTextTheme, etc. ///These are imported to other files/widgets to apply the required themes. class TalawaTheme { @@ -25,6 +23,7 @@ class TalawaTheme { static const Color _darkInWhite = Color(0xffffffff); static const Color _darkColorSchemePrimary = Color(0xfffabc57); + /// Theme for light mode. static final lightTheme = ThemeData( scaffoldBackgroundColor: _lightScaffoldColor, textSelectionTheme: const TextSelectionThemeData( @@ -41,10 +40,14 @@ class TalawaTheme { primaryContainer: _lightPrimaryVariantColor, primary: _lightColorSchemePrimary, secondary: Color(0xffF5F5F5), + onSecondary: Colors.black, secondaryContainer: _darkScaffoldColor, + tertiary: Colors.black26, + tertiaryContainer: Color.fromARGB(255, 231, 231, 231), ).copyWith(secondary: _lightAccentColor), ); + /// Theme for dark mode. static final darkTheme = ThemeData( textSelectionTheme: const TextSelectionThemeData( cursorColor: _darkCursorColor, @@ -61,7 +64,10 @@ class TalawaTheme { primaryContainer: _darkPrimaryVariantColor, primary: _darkColorSchemePrimary, secondary: Colors.black, + onSecondary: Colors.white, secondaryContainer: _lightScaffoldColor, + tertiary: Colors.white70, + tertiaryContainer: Color.fromARGB(255, 61, 61, 61), ).copyWith(secondary: _darkAccentColor), ); @@ -78,12 +84,8 @@ class TalawaTheme { fontSize: 16, color: Color(0xFF737373), ), - bodyLarge: TextStyle( - fontSize: 14, - ), - bodyMedium: TextStyle( - fontSize: 14, - ), + bodyLarge: TextStyle(fontSize: 14, color: Color(0xFF737373)), + bodyMedium: TextStyle(fontSize: 14), bodySmall: TextStyle( fontWeight: FontWeight.w400, color: Color(0xFF737373), diff --git a/lib/constants/recurrence_values.dart b/lib/constants/recurrence_values.dart index c6393aa06..ff55d17c7 100644 --- a/lib/constants/recurrence_values.dart +++ b/lib/constants/recurrence_values.dart @@ -1,53 +1,279 @@ -/// Class containing constants for recurrence options. -class Recurrance { - /// Constant representing an event that does not repeat. - static const once = 'Does not repeat'; +import 'dart:core'; +import 'package:intl/intl.dart'; - /// Constant representing daily recurrence. +/// Class containing constants for event intervals. +class EventIntervals { + /// Constant representing an event that occurs every day. static const daily = 'day'; - /// Constant representing weekly recurrence. + /// Constant representing an event that occurs every week. static const weekly = 'week'; - /// Constant representing monthly recurrence. + /// Constant representing an event that occurs every month. static const monthly = 'month'; - /// Constant representing yearly recurrence. + /// Constant representing an event that occurs every year. static const yearly = 'year'; +} - /// Constant representing a custom recurrence. - static const custom = 'custom...'; +/// Class containing constants for event end types. +class EventEndTypes { + /// Constant representing an event that never ends. + static const never = 'never'; - /// Constant representing Monday in weekdays. - static const weekdayMonday = 'MO'; + /// Constant representing an event that ends on a specific date. + static const on = 'on'; - /// Constant representing Tuesday in weekdays. - static const weekdayTuesday = 'TU'; + /// Constant representing an event that ends after a specified number of occurrences. + static const after = 'after'; +} + +/// Class containing constants for frequency options. +class Frequency { + /// Constant representing an event that occurs every day. + static const daily = 'DAILY'; + + /// Constant representing an event that occurs every week. + static const weekly = 'WEEKLY'; - /// Constant representing Wednesday in weekdays. - static const weekdayWednesday = 'WE'; + /// Constant representing an event that occurs every month. + static const monthly = 'MONTHLY'; + + /// Constant representing an event that occurs every year. + static const yearly = 'YEARLY'; +} - /// Constant representing Thursday in weekdays. - static const weekdayThursday = 'TH'; +/// Class containing constants for week days. +class WeekDays { + /// Constant representing Sunday. + static const sunday = 'SUNDAY'; - /// Constant representing Friday in weekdays. - static const weekdayFriday = 'FR'; + /// Constant representing Monday. + static const monday = 'MONDAY'; - /// Constant representing Saturday in weekdays. - static const weekdaySaturday = 'SA'; + /// Constant representing Tuesday. + static const tuesday = 'TUESDAY'; - /// Constant representing Sunday in weekdays. - static const weekdaySunday = 'SU'; + /// Constant representing Wednesday. + static const wednesday = 'WEDNESDAY'; + + /// Constant representing Thursday. + static const thursday = 'THURSDAY'; + + /// Constant representing Friday. + static const friday = 'FRIDAY'; + + /// Constant representing Saturday. + static const saturday = 'SATURDAY'; } -/// Class containing constants for event end types. -class EventEndTypes { - /// Constant representing an event that never ends. - static const never = 'Never'; +/// Class containing constants for recurrence end types. +class RecurringEventMutationType { + /// Constant representing mutation of current instance only. + static const thisInstance = 'ThisInstance'; - /// Constant representing an event that ends on a specific date. - static const on = 'On'; + /// constant representing mutation of current and following instances. + static const thisAndFollowingInstances = 'ThisAndFollowingInstances'; - /// Constant representing an event that ends after a specified number of occurrences. - static const after = 'After'; + /// constant representing mutation of all instances. + static const allInstances = 'AllInstances'; +} + +/// represents all the days in a week. +List days = [ + WeekDays.monday, + WeekDays.tuesday, + WeekDays.wednesday, + WeekDays.thursday, + WeekDays.friday, + WeekDays.saturday, + WeekDays.sunday, +]; + +/// conatins all neccessary utils. +class RecurrenceUtils { + /// represents all the months. + static const List monthNames = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', + ]; + + /// represents all the week day occurences. + static const List weekDayOccurences = [ + 'First', + 'Second', + 'Third', + 'Fourth', + 'Last', + ]; + + /// represents all the week days. + static const List weekDays = [ + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', + ]; + + /// 'getRecurrenceRuleText' return text for various recurrence rules. + /// + /// **params**: + /// * `frequency`: Frequency of the event. + /// * `weekDays`: List of week days. + /// * `interval`: Interval of the event. + /// * `count`: Count of the event. + /// * `weekDayOccurenceInMonth`: Week day occurence in month. + /// * `startDate`: Start date of the event. + /// * `endDate`: End date of the event. + /// + /// **returns**: + /// * `String`: Recurrence rule text. + static String getRecurrenceRuleText( + String frequency, + Set? weekDays, + int? interval, + int? count, + int? weekDayOccurenceInMonth, + DateTime startDate, + DateTime? endDate, + ) { + String recurrenceRuleText = ''; + + switch (frequency) { + case Frequency.daily: + if (interval != null && interval > 1) { + recurrenceRuleText = 'Every $interval days'; + } else { + recurrenceRuleText = 'Daily'; + } + break; + case Frequency.weekly: + if (weekDays == null) { + break; + } + if (interval != null && interval > 1) { + recurrenceRuleText = 'Every $interval weeks on '; + } else { + recurrenceRuleText = 'Weekly on '; + } + recurrenceRuleText += getWeekDaysString(weekDays.toList()); + break; + case Frequency.monthly: + if (interval != null && interval > 1) { + recurrenceRuleText = 'Every $interval months on '; + } else { + recurrenceRuleText = 'Monthly on '; + } + + if (weekDayOccurenceInMonth != null) { + final getOccurence = + weekDayOccurenceInMonth != -1 ? weekDayOccurenceInMonth - 1 : 4; + recurrenceRuleText += + '${weekDayOccurences[getOccurence]} ${RecurrenceUtils.weekDays[startDate.weekday - 1]}'; + } else { + recurrenceRuleText += 'Day ${startDate.day}'; + } + break; + case Frequency.yearly: + if (interval != null && interval > 1) { + recurrenceRuleText = 'Every $interval years on '; + } else { + recurrenceRuleText = 'Annually on '; + } + recurrenceRuleText += + '${monthNames[startDate.month - 1]} ${startDate.day}'; + break; + } + + if (endDate != null) { + final formatter = DateFormat('MMMM dd, yyyy'); + recurrenceRuleText += ', until ${formatter.format(endDate)}'; + } + + if (count != null) { + recurrenceRuleText += ', $count ${count > 1 ? 'times' : 'time'}'; + } + + return recurrenceRuleText; + } + + /// 'getWeekDaysString' returns string for weekDays. + /// + /// **params**: + /// * `weekDays`: List of Weekdays + /// + /// **returns**: + /// * `String`: returns string for weekDays. + static String getWeekDaysString(List weekDays) { + final fullDayNames = weekDays + .map((day) => RecurrenceUtils.weekDays[weekDays.indexOf(day)]) + .toList(); + + String weekDaysString = fullDayNames.join(', '); + + final lastCommaIndex = weekDaysString.lastIndexOf(','); + if (lastCommaIndex != -1) { + weekDaysString = + '${weekDaysString.substring(0, lastCommaIndex)} &${weekDaysString.substring(lastCommaIndex + 1)}'; + } + + return weekDaysString; + } + + /// 'getWeekDayOccurenceInMonth' returns the week day occurence in month. + /// + /// **params**: + /// * `date`: Date of the event. + /// + /// **returns**: + /// * `int`: Week day occurence in month. + static int getWeekDayOccurenceInMonth(DateTime date) { + final dayOfMonth = date.day; + + // Calculate the current occurrence + final occurrence = (dayOfMonth / 7).ceil(); + + return occurrence; + } + + /// Function to check whether cuurent event instance is Last occurence of Week Day. + /// + /// **params**: + /// * `date`: Date of the event. + /// + /// **returns**: + /// * `bool`: Returns true if the current event instance is the last occurence of the week day. + static bool isLastOccurenceOfWeekDay(DateTime date) { + final currentDay = date.weekday; + + DateTime lastOccurenceInMonth = DateTime( + date.year, + date.month + 1, + 0, + ); + + // set the lastOccurenceInMonth to that day's last occurence + while (lastOccurenceInMonth.weekday != currentDay) { + lastOccurenceInMonth = DateTime( + lastOccurenceInMonth.year, + lastOccurenceInMonth.month, + lastOccurenceInMonth.day - 1, + ); + } + + return date.day == lastOccurenceInMonth.day; + } } diff --git a/lib/models/chats/chat_list_tile_data_model.g.dart b/lib/models/chats/chat_list_tile_data_model.g.dart index b7d137027..200e51549 100644 --- a/lib/models/chats/chat_list_tile_data_model.g.dart +++ b/lib/models/chats/chat_list_tile_data_model.g.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'chat_list_tile_data_model.dart'; @@ -16,7 +13,7 @@ ChatListTileDataModel _$ChatListTileDataModelFromJson( (json['users'] as List?) ?.map((e) => ChatUser.fromJson(e as Map)) .toList(), - json['_id'] as String?, + json['id'] as String?, ); Map _$ChatListTileDataModelToJson( @@ -24,5 +21,5 @@ Map _$ChatListTileDataModelToJson( ) => { 'users': instance.users, - '_id': instance.id, + 'id': instance.id, }; diff --git a/lib/models/chats/chat_message.g.dart b/lib/models/chats/chat_message.g.dart index 4ac1f3456..24dedabc9 100644 --- a/lib/models/chats/chat_message.g.dart +++ b/lib/models/chats/chat_message.g.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'chat_message.dart'; @@ -10,7 +7,7 @@ part of 'chat_message.dart'; // ************************************************************************** ChatMessage _$ChatMessageFromJson(Map json) => ChatMessage( - json['_id'] as String?, + json['id'] as String?, json['sender'] == null ? null : ChatUser.fromJson(json['sender'] as Map), @@ -22,7 +19,7 @@ ChatMessage _$ChatMessageFromJson(Map json) => ChatMessage( Map _$ChatMessageToJson(ChatMessage instance) => { - '_id': instance.id, + 'id': instance.id, 'sender': instance.sender?.toJson(), 'receiver': instance.receiver?.toJson(), 'messageContent': instance.messageContent, diff --git a/lib/models/chats/chat_user.g.dart b/lib/models/chats/chat_user.g.dart index 421737da7..beeef4f36 100644 --- a/lib/models/chats/chat_user.g.dart +++ b/lib/models/chats/chat_user.g.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'chat_user.dart'; @@ -11,12 +8,12 @@ part of 'chat_user.dart'; ChatUser _$ChatUserFromJson(Map json) => ChatUser( firstName: json['firstName'] as String?, - id: json['_id'] as String?, + id: json['id'] as String?, image: json['image'] as String?, ); Map _$ChatUserToJson(ChatUser instance) => { 'firstName': instance.firstName, - '_id': instance.id, + 'id': instance.id, 'image': instance.image, }; diff --git a/lib/models/events/event_model.dart b/lib/models/events/event_model.dart index 4c4bbb727..a15db5925 100644 --- a/lib/models/events/event_model.dart +++ b/lib/models/events/event_model.dart @@ -9,15 +9,12 @@ class Event { this.description, this.attendees, this.location, - this.latitude, - this.longitude, this.recurring, this.allDay, this.startDate, this.endDate, this.startTime, this.endTime, - this.recurrence, this.isPublic, this.isRegistered, this.isRegisterable, @@ -34,15 +31,12 @@ class Event { title: json['title'] as String?, description: json['description'] as String?, location: json['location'] as String?, - longitude: json['longitude'] as double?, - latitude: json['latitude'] as double?, recurring: json['recurring'] as bool?, allDay: json['allDay'] as bool?, startDate: json['startDate'] as String?, endDate: json['endDate'] as String?, startTime: json['startTime'] as String?, endTime: json['endTime'] as String?, - recurrence: json['recurrence'] as String?, isPublic: json['isPublic'] as bool?, isRegistered: json['isRegistered'] as bool?, isRegisterable: json['isRegisterable'] as bool?, @@ -84,12 +78,6 @@ class Event { /// The location of the event. String? location; - /// The latitude of the event. - double? latitude; - - /// The longitude of the event. - double? longitude; - /// A boolean value that indicates if the event is recurring. bool? recurring; @@ -108,9 +96,6 @@ class Event { /// The end time of the event. String? endTime; - /// The recurrence of the event. - String? recurrence; - /// A boolean value that indicates if the event is public. bool? isPublic; diff --git a/lib/models/user/user_info.g.dart b/lib/models/user/user_info.g.dart index 7059bec4c..3b89a5ff1 100644 --- a/lib/models/user/user_info.g.dart +++ b/lib/models/user/user_info.g.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'user_info.dart'; diff --git a/lib/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart b/lib/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart index 3baf49052..dd2ec28dd 100644 --- a/lib/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart +++ b/lib/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart @@ -41,7 +41,8 @@ class CreateEventViewModel extends BaseModel { TextEditingController(text: '1'); /// Event ends After n occurences controller. - TextEditingController endOccurenceController = TextEditingController(); + TextEditingController endOccurenceController = + TextEditingController(text: '1'); /// Event Start Time. TimeOfDay eventStartTime = TimeOfDay.now(); @@ -56,7 +57,13 @@ class CreateEventViewModel extends BaseModel { DateTime eventStartDate = DateTime.now(); /// Event End Date. - DateTime? eventEndDate = DateTime.now(); + DateTime eventEndDate = DateTime.now(); + + /// Event Start Date for recurrence. + DateTime recurrenceStartDate = DateTime.now(); + + /// Event End Date for recurrence. + DateTime? recurrenceEndDate; /// Public Event or Not. bool isPublicSwitch = true; @@ -73,38 +80,37 @@ class CreateEventViewModel extends BaseModel { /// DescriptionFocus FocusNode. FocusNode descriptionFocus = FocusNode(); - /// Latitude store. - double? latitude; - - /// Longitude store. - double? longitude; - /// is an allday event. bool isAllDay = true; /// Is a recurring event. bool isRecurring = false; - /// recurrence count. - int recurranceCount = 1; + /// RecurrenceRuleData frequency. + String recurrenceInterval = EventIntervals.weekly; + + /// Event end type. + String eventEndType = EventEndTypes.never; - /// recurrence. - String? recurrance; + /// represents the frequency of the event. + String frequency = Frequency.weekly; - /// RecurranceRuleData frequency. - String recurranceFrequency = Recurrance.weekly; + /// represents the week days of the event. + Set weekDays = { + days[DateTime.now().weekday - 1], + }; - /// Monthly recurrence. - String monthlyRecurrence = 'Monthly on day 3'; + /// represents the interval of the event. + int interval = 1; - /// weekdays. - Set weekdays = {Recurrance.weekdayTuesday}; + /// represents the count of the event. + int? count; - /// Event end type. - String eventEndType = EventEndTypes.never; + /// represents the week day occurence in Month. + int? weekDayOccurenceInMonth; - /// Custom recurrance event end date. - DateTime? eventEndOnEndDate = DateTime.now(); + /// represents the recurrence label. + String recurrenceLabel = 'Does not repeat'; //late OrganizationService _organizationService; late final Map _memberCheckedMap = {}; @@ -193,41 +199,47 @@ class CreateEventViewModel extends BaseModel { eventStartTime.minute, ); final DateTime endTime = DateTime( - eventEndDate?.year ?? DateTime.now().year, - eventEndDate?.month ?? DateTime.now().month, - eventEndDate?.day ?? DateTime.now().day, + eventEndDate.year, + eventEndDate.month, + eventEndDate.day, eventEndTime.hour, eventEndTime.minute, ); - - final String? frequency = getRecurrance(recurranceFrequency); - // all required data for creating an event final Map variables = { "data": { - 'startDate': DateFormat('yyyy-MM-dd').format(eventStartDate), - if (eventEndDate != null) - 'endDate': DateFormat('yyyy-MM-dd').format(eventEndDate!), - 'organizationId': _currentOrg.id, 'title': eventTitleTextController.text, 'description': eventDescriptionTextController.text, 'location': eventLocationTextController.text, 'isPublic': isPublicSwitch, 'isRegisterable': isRegisterableSwitch, 'recurring': isRecurring, - 'recurrance': recurrance ?? frequency, 'allDay': isAllDay, - 'startTime': '${DateFormat('HH:mm:ss').format(startTime)}Z', - if (eventEndDate != null) - 'endTime': '${DateFormat('HH:mm:ss').format(endTime)}Z', - if (latitude != null) 'latitude': latitude, - if (longitude != null) 'longitude': longitude, + 'organizationId': _currentOrg.id, + 'startDate': DateFormat('yyyy-MM-dd').format(eventStartDate), + 'endDate': DateFormat('yyyy-MM-dd').format(eventEndDate), + 'startTime': + isAllDay ? null : '${DateFormat('HH:mm:ss').format(startTime)}Z', + 'endTime': + isAllDay ? null : '${DateFormat('HH:mm:ss').format(endTime)}Z', }, - if (recurrance == null) + if (isRecurring) 'recurrenceRuleData': { - if (eventEndType != EventEndTypes.never) 'count': recurranceCount, + 'recurrenceStartDate': DateFormat('yyyy-MM-dd').format( + recurrenceStartDate, + ), + 'recurrenceEndDate': recurrenceEndDate != null + ? DateFormat('yyyy-MM-dd').format(recurrenceEndDate!) + : null, 'frequency': frequency, - if (frequency == 'WEEKLY') 'weekDays': List.from(weekdays), + 'weekDays': (frequency == Frequency.weekly || + (frequency == Frequency.monthly && + weekDayOccurenceInMonth != null)) + ? weekDays.toList() + : null, + 'interval': interval, + 'count': count, + 'weekDayOccurenceInMonth': weekDayOccurenceInMonth, }, }; @@ -240,11 +252,9 @@ class CreateEventViewModel extends BaseModel { EventQueries().addEvent(), variables: variables, ); - print(result); navigationService.pop(); if (result != null) { navigationService.pop(); - await _eventService.getEvents(); } } @@ -343,33 +353,8 @@ class CreateEventViewModel extends BaseModel { /// /// **returns**: /// None - void setEventEndDate(DateTime? selectedEndDate) { + void setEventEndDate(DateTime selectedEndDate) { eventEndDate = selectedEndDate; notifyListeners(); } - - /// Returns corresponding recurrence value based on frequency. - /// - /// **params**: - /// * `frequency`: Recurrence frequency selected by user. - /// - /// **returns**: - /// * `String?`: Recurrence value. - String? getRecurrance(String frequency) { - isRecurring = true; - if (frequency == Recurrance.daily || frequency == 'Every day') { - return 'DAILY'; - } else if (frequency == Recurrance.weekly || frequency == 'Every week') { - return 'WEEKLY'; - } else if (frequency == Recurrance.monthly || frequency == 'Every month') { - return 'MONTHLY'; - } else if (frequency == Recurrance.yearly || frequency == 'Every year') { - return 'YEARLY'; - } else if (frequency == Recurrance.once) { - isRecurring = false; - return null; - } else { - return null; - } - } } diff --git a/lib/view_model/after_auth_view_models/event_view_models/explore_events_view_model.dart b/lib/view_model/after_auth_view_models/event_view_models/explore_events_view_model.dart index 4740452f6..7b792d7be 100644 --- a/lib/view_model/after_auth_view_models/event_view_models/explore_events_view_model.dart +++ b/lib/view_model/after_auth_view_models/event_view_models/explore_events_view_model.dart @@ -1,6 +1,4 @@ import 'dart:async'; - -import 'package:intl/intl.dart'; import 'package:talawa/enums/enums.dart'; import 'package:talawa/locator.dart'; import 'package:talawa/models/events/event_model.dart'; @@ -117,7 +115,7 @@ class ExploreEventsViewModel extends BaseModel { if (!_uniqueEventIds.contains(newEvent.id) && newEvent.organization!.id == userConfig.currentOrg.id) { _uniqueEventIds.add(newEvent.id!); - _parseEventDateTime(newEvent); + _events.insert(0, newEvent); } if (!_userEvents.any((event) => event.id == newEvent.id) && newEvent.creator!.id == userConfig.currentUser.id) { @@ -126,43 +124,6 @@ class ExploreEventsViewModel extends BaseModel { notifyListeners(); } - /// The helper function that used to parse the date and time. - /// - /// **params**: - /// * `newEvent`: `Event` type variable containing data to create a new event. - /// - /// **returns**: - /// None - void _parseEventDateTime(Event newEvent) { - // Maybe needed for the tests. Can be further discussed. - if (newEvent.startDate == null || - newEvent.endDate == null || - newEvent.startTime == null || - newEvent.endTime == null) { - newEvent.startDate = DateFormat('yyyy-MM-dd').format(DateTime.now()); - newEvent.endDate = DateFormat('yyyy-MM-dd').format(DateTime.now()); - newEvent.startTime = DateFormat('HH:mm:ss').format(DateTime.now()); - newEvent.endTime = DateFormat('HH:mm:ss').format(DateTime.now()); - } - - if (RegExp(r'^\d{4}-\d{2}-\d{2}$').hasMatch(newEvent.startDate!) && - RegExp(r'^\d{2}:\d{2}:\d{2}.\d{3}Z$').hasMatch(newEvent.startTime!) && - RegExp(r'^\d{4}-\d{2}-\d{2}$').hasMatch(newEvent.endDate!) && - RegExp(r'^\d{2}:\d{2}:\d{2}.\d{3}Z$').hasMatch(newEvent.endTime!)) { - final startMoment = - DateTime.parse('${newEvent.startDate} ${newEvent.startTime}') - .toLocal(); - final endMoment = - DateTime.parse('${newEvent.endDate} ${newEvent.endTime}').toLocal(); - - newEvent.startDate = DateFormat('yMd').format(startMoment); - newEvent.endDate = DateFormat('yMd').format(endMoment); - newEvent.startTime = DateFormat('h:mm a').format(startMoment); - newEvent.endTime = DateFormat('h:mm a').format(endMoment); - } - _events.insert(0, newEvent); - } - /// This function deletes the event. /// /// **params**: @@ -184,7 +145,6 @@ class ExploreEventsViewModel extends BaseModel { if (result != null) { navigationService.pop(); setState(ViewState.busy); - print(result); _uniqueEventIds.remove(eventId); _events.removeWhere((element) => element.id == eventId); _userEvents.removeWhere((element) => element.id == eventId); diff --git a/lib/views/after_auth_screens/app_settings/app_settings_page.dart b/lib/views/after_auth_screens/app_settings/app_settings_page.dart index 3398a51e1..c718f5cb8 100644 --- a/lib/views/after_auth_screens/app_settings/app_settings_page.dart +++ b/lib/views/after_auth_screens/app_settings/app_settings_page.dart @@ -22,10 +22,6 @@ class AppSettingsPage extends StatelessWidget { @override Widget build(BuildContext context) { - // Scaffold implements the basic Material Design visual layout structure. - //Learn more about Scaffold class - //[here](https://api.flutter.dev/flutter/material/Scaffold-class.html). - // const String _talawaWebsite = ''; const String talawaDocs = 'https://docs.talawa.io'; const String talawaGithub = 'https://github.com/PalisadoesFoundation/talawa'; @@ -59,10 +55,6 @@ class AppSettingsPage extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // SizedBox( - // height: SizeConfig.screenHeight! * 0.77, - // child: - // ), Flexible( child: SingleChildScrollView( scrollDirection: Axis.vertical, @@ -299,7 +291,7 @@ class AppSettingsPage extends StatelessWidget { return CustomAlertDialog( reverse: true, dialogSubTitle: 'Are you sure you want to logout?', - successText: 'LogOut', + successText: 'Logout', success: () async { try { final bool isLogoutSuccessful = diff --git a/lib/views/after_auth_screens/events/create_custom_recurring_event.dart b/lib/views/after_auth_screens/events/create_custom_recurring_event.dart index 5edd07ab2..0667482d9 100644 --- a/lib/views/after_auth_screens/events/create_custom_recurring_event.dart +++ b/lib/views/after_auth_screens/events/create_custom_recurring_event.dart @@ -26,8 +26,13 @@ class _CustomRecurrencePageState extends State { late CreateEventViewModel viewModel; @override - Widget build(BuildContext context) { + void initState() { + super.initState(); viewModel = widget.model; + } + + @override + Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).primaryColor, @@ -43,14 +48,26 @@ class _CustomRecurrencePageState extends State { actions: [ TextButton( onPressed: () { - if (viewModel.eventEndType == EventEndTypes.never) { - viewModel.setEventEndDate(null); - } else if (viewModel.eventEndType == EventEndTypes.on) { - viewModel.eventEndDate = viewModel.eventEndOnEndDate; - } else if (viewModel.eventEndType == EventEndTypes.after) { - viewModel.recurranceCount = - int.parse(viewModel.endOccurenceController.text); - } + setState(() { + if (viewModel.eventEndType == EventEndTypes.never) { + viewModel.recurrenceEndDate = null; + } else if (viewModel.eventEndType == EventEndTypes.after) { + viewModel.count = + int.parse(viewModel.endOccurenceController.text); + } + viewModel.interval = + int.parse(viewModel.repeatsEveryCountController.text); + viewModel.recurrenceLabel = + RecurrenceUtils.getRecurrenceRuleText( + viewModel.frequency, + viewModel.weekDays, + viewModel.interval, + viewModel.count, + viewModel.weekDayOccurenceInMonth, + viewModel.recurrenceStartDate, + viewModel.recurrenceEndDate, + ); + }); navigationService.pop(); }, child: Text(AppLocalizations.of(context)!.strictTranslate('Done')), @@ -72,6 +89,43 @@ class _CustomRecurrencePageState extends State { /// **returns**: /// * `Column`: Column of recurrence options widgets. Column _buildRecurrenceOptions(BuildContext context) { + final List monthlyOptions = [ + RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + null, + int.parse(viewModel.repeatsEveryCountController.text), + int.parse(viewModel.endOccurenceController.text), + null, + viewModel.recurrenceStartDate, + viewModel.recurrenceEndDate, + ), + RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + { + RecurrenceUtils + .weekDays[widget.model.recurrenceStartDate.weekday - 1], + }, + int.parse(viewModel.repeatsEveryCountController.text), + int.parse(viewModel.endOccurenceController.text), + RecurrenceUtils.getWeekDayOccurenceInMonth( + widget.model.recurrenceStartDate, + ), + viewModel.recurrenceStartDate, + viewModel.recurrenceEndDate, + ), + RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + { + RecurrenceUtils + .weekDays[widget.model.recurrenceStartDate.weekday - 1], + }, + int.parse(viewModel.repeatsEveryCountController.text), + int.parse(viewModel.endOccurenceController.text), + -1, + viewModel.recurrenceStartDate, + viewModel.recurrenceEndDate, + ), + ]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -85,11 +139,12 @@ class _CustomRecurrencePageState extends State { ], ), ), - recurrenceisDailyorYearly() - ? buildCustomDivider(context) + (viewModel.recurrenceInterval == EventIntervals.daily || + viewModel.recurrenceInterval == EventIntervals.yearly) + ? Divider(color: Theme.of(context).hintColor) : Column( children: [ - buildCustomDivider(context), + Divider(color: Theme.of(context).hintColor), Container( padding: EdgeInsets.fromLTRB( _sectionPadding, @@ -97,19 +152,57 @@ class _CustomRecurrencePageState extends State { 90, _sectionPadding, ), - child: viewModel.recurranceFrequency == Recurrance.monthly + child: viewModel.recurrenceInterval == + EventIntervals.monthly ? Row( children: [ RecurrenceFrequencyDropdown( model: viewModel, options: [ - 'Monthly on day 3', - 'Monthly on the first Sunday', + monthlyOptions[0], + if (RecurrenceUtils + .getWeekDayOccurenceInMonth( + widget.model.recurrenceStartDate, + ) != + 5) + monthlyOptions[1], + if (RecurrenceUtils.isLastOccurenceOfWeekDay( + widget.model.recurrenceStartDate, + )) + monthlyOptions[2], ], - selectedOption: viewModel.monthlyRecurrence, + selectedOption: monthlyOptions[0], onSelected: (String value) { + Set tempWeekDays; + int? tempWeekDayOccurenceInMonth; + if (value == monthlyOptions[0]) { + tempWeekDays = {}; + tempWeekDayOccurenceInMonth = null; + } else if (value == monthlyOptions[1]) { + tempWeekDays = { + RecurrenceUtils.weekDays[widget.model + .recurrenceStartDate.weekday - + 1], + }; + tempWeekDayOccurenceInMonth = + RecurrenceUtils + .getWeekDayOccurenceInMonth( + widget.model.recurrenceStartDate, + ); + } else { + tempWeekDays = { + RecurrenceUtils.weekDays[widget.model + .recurrenceStartDate.weekday - + 1], + }; + tempWeekDayOccurenceInMonth = -1; + } setState(() { - viewModel.monthlyRecurrence = value; + viewModel.frequency = Frequency.monthly; + viewModel.recurrenceLabel = value; + viewModel.weekDays = tempWeekDays; + viewModel.weekDayOccurenceInMonth = + tempWeekDayOccurenceInMonth; }); }, ), @@ -125,7 +218,7 @@ class _CustomRecurrencePageState extends State { ], ), ), - buildCustomDivider(context), + Divider(color: Theme.of(context).hintColor), ], ), Column( @@ -136,9 +229,7 @@ class _CustomRecurrencePageState extends State { EdgeInsets.only(top: _sectionPadding, left: _sectionPadding), child: inputFieldHeading('Ends'), ), - EventEndOptions( - model: viewModel, - ), + EventEndOptions(model: viewModel), ], ), ], @@ -169,16 +260,27 @@ class _CustomRecurrencePageState extends State { RecurrenceFrequencyDropdown( model: viewModel, options: [ - Recurrance.daily, - Recurrance.weekly, - Recurrance.monthly, - Recurrance.yearly, + EventIntervals.daily, + EventIntervals.weekly, + EventIntervals.monthly, + EventIntervals.yearly, ], - selectedOption: viewModel.recurranceFrequency, + selectedOption: viewModel.recurrenceInterval, onSelected: (String value) { - setState(() { - viewModel.recurranceFrequency = value; - }); + switch (value) { + case EventIntervals.daily: + updateModel(Frequency.daily, value, null, null); + break; + case EventIntervals.weekly: + updateModel(Frequency.weekly, value, null, null); + break; + case EventIntervals.monthly: + updateModel(Frequency.monthly, value, null, null); + break; + case EventIntervals.yearly: + updateModel(Frequency.yearly, value, null, null); + break; + } }, ), ], @@ -204,29 +306,27 @@ class _CustomRecurrencePageState extends State { ), ); - /// Divider with custom properties. + /// Returns the updated model with the selected recurrence options. /// /// **params**: - /// * `context`: BuildContext of the widget. + /// * `frequency`: represent the frequency of the event. + /// * `value`: represent the interval of the event. + /// * `weekDayOccurenceInMonth`: represent the week day occurence in month. + /// * `weekDays`: represent the list of week days. /// /// **returns**: - /// * `Divider`: Custom divider. - Divider buildCustomDivider(BuildContext context) { - return Divider( - color: Theme.of(context).hintColor, - ); - } - - /// Determines whether the recurrence frequency is daily or yearly. - /// - /// **params**: /// None - /// - /// **returns**: - /// * `bool`: A boolean value indicating whether the recurrence frequency is daily or yearly. - bool recurrenceisDailyorYearly() { - final widgetModel = viewModel; - return widgetModel.recurranceFrequency == Recurrance.daily || - widgetModel.recurranceFrequency == Recurrance.yearly; + void updateModel( + String frequency, + String value, + int? weekDayOccurenceInMonth, + Set? weekDays, + ) { + setState(() { + widget.model.frequency = frequency; + widget.model.recurrenceInterval = value; + widget.model.weekDayOccurenceInMonth = weekDayOccurenceInMonth; + widget.model.weekDays = weekDays ?? {}; + }); } } diff --git a/lib/views/after_auth_screens/events/create_event_page.dart b/lib/views/after_auth_screens/events/create_event_page.dart index ba5f2a770..99cd94457 100644 --- a/lib/views/after_auth_screens/events/create_event_page.dart +++ b/lib/views/after_auth_screens/events/create_event_page.dart @@ -26,8 +26,6 @@ class CreateEventPage extends StatefulWidget { /// _CreateEventPageState returns a widget for a Page to Creatxe the Event in the Organization. class _CreateEventPageState extends State { - /// Recurrence label of recurrence selection button. - String recurrenceLabel = Recurrance.once; @override Widget build(BuildContext context) { final TextStyle subtitleTextStyle = @@ -36,7 +34,6 @@ class _CreateEventPageState extends State { return BaseView( onModelReady: (model) => model.initialize(), builder: (context, model, child) { - print(model.recurrance); return Scaffold( // AppBar is the header of the page appBar: AppBar( @@ -44,14 +41,10 @@ class _CreateEventPageState extends State { elevation: 1, centerTitle: true, leading: GestureDetector( - onTap: () { - // ignore: undefined_method - navigationServiceLocal.pop(); - }, + onTap: () => navigationServiceLocal.pop(), child: const Icon(Icons.close), ), title: Text( - // text translation to the app language. AppLocalizations.of(context)!.strictTranslate('Add Event'), style: Theme.of(context).textTheme.titleLarge!.copyWith( fontWeight: FontWeight.w600, @@ -82,7 +75,6 @@ class _CreateEventPageState extends State { body: Scrollbar( thickness: 2, child: SingleChildScrollView( - // SingleChildScrollView is a box in which a single widget can be scrolled. child: Padding( padding: const EdgeInsets.all(15), child: Column( @@ -109,10 +101,8 @@ class _CreateEventPageState extends State { ), ], ), - // If the image for the event is selected or not null. model.imageFile != null ? Container( - // Container for rendering the selected image height: 300, padding: const EdgeInsets.all(8.0), child: Stack( @@ -139,27 +129,109 @@ class _CreateEventPageState extends State { : Container(), const Divider(), CreateEventForm( - // CreateEventForm returns a widget of a Form for creating events. - // This widget is exported from `lib/views/after_auth_screens/events/create_event_form.dart`. model: model, ), + SizedBox(height: SizeConfig.screenHeight! * 0.013), + const Divider(), + SizedBox( + width: SizeConfig.screenWidth, + child: Wrap( + alignment: WrapAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppLocalizations.of(context)! + .strictTranslate('Keep Registerable'), + style: subtitleTextStyle, + ), + SizedBox( + width: SizeConfig.screenWidth! * 0.005, + ), + Switch( + value: model.isRegisterableSwitch, + onChanged: (value) { + setState(() { + model.isRegisterableSwitch = value; + }); + }, + activeColor: + Theme.of(context).colorScheme.primary, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppLocalizations.of(context)! + .strictTranslate('All day'), + style: subtitleTextStyle, + ), + SizedBox( + width: SizeConfig.screenWidth! * 0.005, + ), + Switch( + // Switch to select the visibility of the event. + value: model.isAllDay, + onChanged: (value) { + setState(() { + model.isAllDay = value; + }); + }, + activeColor: + Theme.of(context).colorScheme.primary, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppLocalizations.of(context)! + .strictTranslate('Keep Public'), + style: subtitleTextStyle, + ), + SizedBox( + width: SizeConfig.screenWidth! * 0.005, + ), + Switch( + value: model.isPublicSwitch, + onChanged: (value) { + setState(() { + model.isPublicSwitch = value; + }); + }, + activeColor: + Theme.of(context).colorScheme.primary, + ), + ], + ), + ], + ), + ), SizedBox( height: SizeConfig.screenHeight! * 0.013, ), const Divider(), + SizedBox( + height: SizeConfig.screenHeight! * 0.013, + ), Text( - // translation of the text to app language. - AppLocalizations.of(context)! - .strictTranslate('Select Start Date and Time'), + AppLocalizations.of(context)!.strictTranslate( + model.isAllDay + ? 'Select Start Date' + : 'Select Start Date and Time', + ), style: subtitleTextStyle, ), SizedBox( height: SizeConfig.screenHeight! * 0.013, ), // DateTimeTile is custom widget that returns a tile to select date and time. - // You can learn more about DateTimeTile from [here](lib/widgets/date_time_picker.dart). DateTimeTile( - // variables and member functions initialisation. + isAllDay: model.isAllDay, date: "${model.eventStartDate.toLocal()}".split(' ')[0], time: model.eventStartTime.format(context), setDate: () async { @@ -172,25 +244,32 @@ class _CreateEventPageState extends State { ); } setState(() { - model.eventStartDate = date; + if (model.eventStartDate != date) { + model.eventStartDate = date; + model.recurrenceStartDate = date; + model.recurrenceLabel = 'Does not repeat'; + model.isRecurring = false; + model.frequency = Frequency.weekly; + model.weekDays = {}; + model.weekDayOccurenceInMonth = null; + } }); }, setTime: () async { - print(model.eventStartDate); final time = await customTimePicker( initialTime: model.eventStartTime, ); - // print(model.eventStartTime); final validationError = Validator.validateEventTime( time, model.eventEndTime, ); - print('hi'); if (validationError != null) { + // coverage:ignore-start navigationService.showTalawaErrorSnackBar( 'Start time must be before end time', MessageType.error, ); + // coverage:ignore-end } else { setState(() { model.eventStartTime = time; @@ -202,8 +281,11 @@ class _CreateEventPageState extends State { height: SizeConfig.screenHeight! * 0.026, ), Text( - AppLocalizations.of(context)! - .strictTranslate('Select End Date and Time'), + AppLocalizations.of(context)!.strictTranslate( + model.isAllDay + ? 'Select End Date' + : 'Select End Date and Time', + ), style: Theme.of(context) .textTheme .headlineSmall! @@ -214,20 +296,29 @@ class _CreateEventPageState extends State { ), DateTimeTile( key: const Key('key for test cep'), - date: "${model.eventEndDate?.toLocal() ?? DateTime.now()}" - .split(' ')[0], + isAllDay: model.isAllDay, + date: "${model.eventEndDate.toLocal()}".split(' ')[0], time: model.eventEndTime.format(context), setDate: () async { final date = await customDatePicker( - initialDate: model.eventEndDate ?? DateTime.now(), + initialDate: model.eventEndDate, ); final startDate = model.eventStartDate; + // coverage:ignore-start if (startDate.compareTo(date) < 0) { setState(() { - model.eventEndDate = date; + if (model.eventEndDate != date) { + model.eventEndDate = date; + model.recurrenceLabel = 'Does not repeat'; + model.recurrenceEndDate = null; + model.isRecurring = false; + model.frequency = Frequency.weekly; + model.weekDays = {}; + model.weekDayOccurenceInMonth = null; + } }); + // coverage:ignore-end } else { - // ignore: undefined_method navigationServiceLocal.showSnackBar( "End Date cannot be after start date ", ); @@ -244,10 +335,12 @@ class _CreateEventPageState extends State { final showSnackBar = navigationService.showTalawaErrorSnackBar; if (validationError != null) { + // coverage:ignore-start showSnackBar( 'Start time must be before end time', MessageType.error, ); + // coverage:ignore-end } else { setState(() { model.eventEndTime = time; @@ -259,16 +352,20 @@ class _CreateEventPageState extends State { height: SizeConfig.screenHeight! * 0.026, ), InkWell( + key: const Key('inkwell_recurrLabel'), child: Row( children: [ const Icon(Icons.restore), SizedBox( width: SizeConfig.screenWidth! * 0.045, ), - Text( - AppLocalizations.of(context)! - .strictTranslate(recurrenceLabel), - style: subtitleTextStyle, + Expanded( + child: Text( + AppLocalizations.of(context)! + .strictTranslate(model.recurrenceLabel), + style: subtitleTextStyle, + overflow: TextOverflow.ellipsis, + ), ), ], ), @@ -279,101 +376,18 @@ class _CreateEventPageState extends State { builder: (context) { return ShowRecurrenceDialog( model: model, - initialRecurrence: recurrenceLabel, ); }, ); setState(() { - recurrenceLabel = selectedReccurence!; + if (selectedReccurence != null) { + model.recurrenceLabel = selectedReccurence; + } }); }, ), SizedBox(height: SizeConfig.screenHeight! * 0.026), const Divider(), - SizedBox( - width: SizeConfig.screenWidth, - child: Wrap( - alignment: WrapAlignment.spaceBetween, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - AppLocalizations.of(context)! - .strictTranslate('Keep Registerable'), - style: subtitleTextStyle, - ), - SizedBox( - width: SizeConfig.screenWidth! * 0.005, - ), - Switch( - value: model.isRegisterableSwitch, - onChanged: (value) { - setState(() { - model.isRegisterableSwitch = value; - print(model.isRegisterableSwitch); - }); - }, - activeColor: - Theme.of(context).colorScheme.primary, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - AppLocalizations.of(context)! - .strictTranslate('All day'), - style: subtitleTextStyle, - ), - SizedBox( - width: SizeConfig.screenWidth! * 0.005, - ), - Switch( - // Switch to select the visibility of the event. - value: model.isAllDay, - onChanged: (value) { - setState(() { - model.isAllDay = value; - print(model.isAllDay); - }); - }, - activeColor: - Theme.of(context).colorScheme.primary, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - AppLocalizations.of(context)! - .strictTranslate('Keep Public'), - style: subtitleTextStyle, - ), - SizedBox( - width: SizeConfig.screenWidth! * 0.005, - ), - Switch( - // Switch to select the visibility of the event. - value: model.isPublicSwitch, - onChanged: (value) { - setState(() { - model.isPublicSwitch = value; - print(model.isPublicSwitch); - }); - }, - activeColor: - Theme.of(context).colorScheme.primary, - ), - ], - ), - ], - ), - ), - SizedBox(height: SizeConfig.screenHeight! * 0.026), - const Divider(), InkWell( key: const Key('inwell_cep2'), onTap: () { diff --git a/lib/views/after_auth_screens/events/edit_event_page.dart b/lib/views/after_auth_screens/events/edit_event_page.dart index 6a87ceb03..8ea4d2a6d 100644 --- a/lib/views/after_auth_screens/events/edit_event_page.dart +++ b/lib/views/after_auth_screens/events/edit_event_page.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - import 'package:flutter/material.dart'; import 'package:talawa/locator.dart'; import 'package:talawa/models/events/event_model.dart'; @@ -15,14 +12,15 @@ import 'package:talawa/widgets/event_date_time_tile.dart'; /// EditEventPage returns a widget that has mutable state _EditEventPageState. class EditEventPage extends StatefulWidget { const EditEventPage({super.key, required this.event}); + + /// Represents event instance to be edited. final Event event; @override _EditEventPageState createState() => _EditEventPageState(); } -/// _EditEventPageState returns a widget to edit the -/// event that is being posted by the current user. +/// _EditEventPageState returns a widget to edit the event that is being posted by the current user. class _EditEventPageState extends State { @override Widget build(BuildContext context) { @@ -33,7 +31,6 @@ class _EditEventPageState extends State { builder: (context, model, child) { return Scaffold( appBar: AppBar( - // returns a header for the page. backgroundColor: Theme.of(context).primaryColor, elevation: 1, centerTitle: true, @@ -90,8 +87,6 @@ class _EditEventPageState extends State { ), const Divider(), EditEventForm( - // EditEventForm returns a widget of a Form for editing the event. - // This widget is exported from `lib/views/after_auth_screens/events/edit_events_form.dart`. model: model, ), SizedBox( @@ -102,9 +97,8 @@ class _EditEventPageState extends State { SizedBox( height: SizeConfig.screenHeight! * 0.013, ), - // DateTimeTile is custom widget that returns a tile to select date and time. - // You can learn more about DateTimeTile from [here](lib/widgets/date_time_picker.dart). DateTimeTile( + isAllDay: false, // variables and member functions initialisation. date: "${model.eventStartDate.toLocal()}".split(' ')[0], time: model.eventStartTime.format(context), @@ -120,7 +114,6 @@ class _EditEventPageState extends State { final time = await customTimePicker( initialTime: model.eventStartTime, ); - setState(() { model.eventStartTime = time; }); @@ -140,6 +133,7 @@ class _EditEventPageState extends State { height: SizeConfig.screenHeight! * 0.013, ), DateTimeTile( + isAllDay: false, date: "${model.eventEndDate.toLocal()}".split(' ')[0], time: model.eventEndTime.format(context), setDate: () async { @@ -193,7 +187,6 @@ class _EditEventPageState extends State { onChanged: (value) { setState(() { model.isPublicSwitch = value; - print(model.isPublicSwitch); }); }, activeColor: @@ -216,7 +209,6 @@ class _EditEventPageState extends State { onChanged: (value) { setState(() { model.isRegisterableSwitch = value; - print(model.isRegisterableSwitch); }); }, activeColor: diff --git a/lib/views/after_auth_screens/events/event_info_body.dart b/lib/views/after_auth_screens/events/event_info_body.dart index c47745a1c..65f516282 100644 --- a/lib/views/after_auth_screens/events/event_info_body.dart +++ b/lib/views/after_auth_screens/events/event_info_body.dart @@ -89,7 +89,7 @@ class EventInfoBody extends StatelessWidget { ), Text( // Display start and end date of the Event. - "${event.startDate!} - ${event.endDate!}", + "${event.startDate} - ${event.endDate}", style: Theme.of(context).textTheme.bodySmall, ), const Spacer(), @@ -125,23 +125,24 @@ class EventInfoBody extends StatelessWidget { SizedBox( height: SizeConfig.screenHeight! * 0.011, ), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // display schedule icon with the start and end date of the time. - const Icon( - Icons.schedule, - size: 12, - ), - SizedBox( - width: SizeConfig.screenWidth! * 0.025, - ), - Text( - "${event.startTime!} - ${event.endTime!}", - style: Theme.of(context).textTheme.bodySmall, - ), - ], - ), + if (event.startTime != null && event.endTime != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // display schedule icon with the start and end date of the time. + const Icon( + Icons.schedule, + size: 12, + ), + SizedBox( + width: SizeConfig.screenWidth! * 0.025, + ), + Text( + "${event.startTime} - ${event.endTime}", + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), SizedBox( height: SizeConfig.screenHeight! * 0.011, ), diff --git a/lib/views/after_auth_screens/events/explore_events.dart b/lib/views/after_auth_screens/events/explore_events.dart index 119b31553..16fe3561d 100644 --- a/lib/views/after_auth_screens/events/explore_events.dart +++ b/lib/views/after_auth_screens/events/explore_events.dart @@ -18,7 +18,7 @@ class ExploreEvents extends StatelessWidget { this.homeModel, }) : super(key: key); - /// [homeModal] is a type of [MainScreenViewModel] which provides methods to handle the data for this component. + /// Represents the view model for the home screen. final MainScreenViewModel? homeModel; @override @@ -28,8 +28,7 @@ class ExploreEvents extends StatelessWidget { builder: (context, model, child) { return Scaffold( appBar: AppBar( - // AppBar returns widget for the header. - backgroundColor: Theme.of(context).primaryColor, + backgroundColor: Colors.green, key: const Key( "ExploreEventsAppBar", ), @@ -43,13 +42,11 @@ class ExploreEvents extends StatelessWidget { style: Theme.of(context).textTheme.titleLarge!.copyWith( fontWeight: FontWeight.w600, fontSize: 20, + color: Colors.white, ), ), leading: IconButton( - // returns a button of menu icon to redirect to home. - color: Theme.of( - context, - ).iconTheme.color, + color: Colors.white, icon: const Icon( Icons.menu, ), @@ -61,7 +58,6 @@ class ExploreEvents extends StatelessWidget { padding: EdgeInsets.only( right: SizeConfig.screenWidth! * 0.027, ), - // if the events is not empty then renders button for searching the events else renders just a box. child: model.events.isNotEmpty ? IconButton( onPressed: () { @@ -73,17 +69,15 @@ class ExploreEvents extends StatelessWidget { ), ); }, - icon: Icon( + icon: const Icon( Icons.search, - size: (SizeConfig.safeBlockHorizontal ?? 4) * 5, + color: Colors.white, ), ) : const SizedBox(), ), ], ), - // if the model is still fetching the events list then renders the Circular Progress Indicator - // else render refresh icon along with the list of searched events for exploration. body: model.isBusy ? const Center( child: CircularProgressIndicator(), @@ -100,6 +94,9 @@ class ExploreEvents extends StatelessWidget { ), child: Column( children: [ + SizedBox( + height: SizeConfig.screenHeight! * 0.013, + ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -107,27 +104,24 @@ class ExploreEvents extends StatelessWidget { Expanded( flex: 2, child: GestureDetector( - onTap: () { - showModalBottomSheet( - context: context, - builder: (_) { - return dropDownList( - model, - context, - ); - }, - ); - }, + onTap: () => showModalBottomSheet( + backgroundColor: Colors.white, + context: context, + builder: (_) => dropDownList( + model, + context, + ), + ), child: Card( + surfaceTintColor: Theme.of(context) + .colorScheme + .secondaryContainer, color: Theme.of(context) .colorScheme .onPrimary, child: Container( - padding: EdgeInsets.symmetric( - vertical: (SizeConfig - .safeBlockHorizontal ?? - 4) * - 3, + padding: const EdgeInsets.symmetric( + vertical: 12, ), child: Row( mainAxisAlignment: @@ -156,30 +150,25 @@ class ExploreEvents extends StatelessWidget { Expanded( flex: 3, child: GestureDetector( - onTap: () { - showDialog( - // on tap open the Explore Event Dialog. - context: context, - builder: (_) { - return const ExploreEventDialog( - key: Key('ExploreEvents'), - ); - }, - ); - }, + onTap: () => showDialog( + context: context, + builder: (_) => + const ExploreEventDialog( + key: Key('ExploreEvents'), + ), + ), child: Card( + surfaceTintColor: Theme.of(context) + .colorScheme + .secondaryContainer, key: homeModel?.keySEDateFilter, color: Theme.of(context) .colorScheme .onPrimary, child: Container( - padding: EdgeInsets.symmetric( - vertical: (SizeConfig - .safeBlockHorizontal ?? - 4) * - 3, + padding: const EdgeInsets.symmetric( + vertical: 12, ), - // width: SizeConfig.screenWidth! * 0.30, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -215,6 +204,9 @@ class ExploreEvents extends StatelessWidget { Expanded( flex: 1, child: Card( + surfaceTintColor: Theme.of(context) + .colorScheme + .secondaryContainer, color: Theme.of(context) .colorScheme .onPrimary, @@ -234,10 +226,8 @@ class ExploreEvents extends StatelessWidget { ], ), SizedBox( - height: SizeConfig.screenHeight! * 0.027, + height: SizeConfig.screenHeight! * 0.020, ), - // if the events model is empty then renders a box with text as "Empty List" - // else renders lists of the all event tile. model.events.isEmpty ? SizedBox( height: SizeConfig.screenHeight! * 0.5, @@ -285,11 +275,7 @@ class ExploreEvents extends StatelessWidget { key: homeModel?.keySEAdd, heroTag: "AddEventFab", backgroundColor: Theme.of(context).colorScheme.background, - onPressed: () { - navigationService.pushScreen( - "/createEventPage", - ); - }, + onPressed: () => navigationService.pushScreen("/createEventPage"), icon: Icon( Icons.add, color: Theme.of(context).colorScheme.secondary, diff --git a/lib/views/after_auth_screens/feed/organization_feed.dart b/lib/views/after_auth_screens/feed/organization_feed.dart index 1aabe61af..5913f121e 100644 --- a/lib/views/after_auth_screens/feed/organization_feed.dart +++ b/lib/views/after_auth_screens/feed/organization_feed.dart @@ -60,24 +60,23 @@ class _OrganizationFeedState extends State { ), ), appBar: AppBar( - // AppBar returns a widget for the header of the page. backgroundColor: Colors.green, - // Theme.of(context).primaryColor, elevation: 0.0, centerTitle: true, title: Text( model.currentOrgName, key: widget.homeModel?.keySHOrgName, style: Theme.of(context).textTheme.titleLarge!.copyWith( + fontWeight: FontWeight.w600, fontSize: 20, color: Colors.white, ), ), leading: IconButton( key: widget.homeModel?.keySHMenuIcon, - icon: Icon( + icon: const Icon( Icons.menu, - color: Theme.of(context).iconTheme.color, + color: Colors.white, ), onPressed: () { MainScreenViewModel.scaffoldKey.currentState!.openDrawer(); @@ -132,7 +131,9 @@ class _OrganizationFeedState extends State { pinnedPost: model.pinnedPosts, model: widget.homeModel!, ), - // Show PostListWidget if there are posts, otherwise show the 'no posts' message + SizedBox( + height: SizeConfig.screenHeight! * 0.01, + ), model.posts.isNotEmpty ? PostListWidget( key: widget.homeModel?.keySHPost, diff --git a/lib/views/after_auth_screens/profile/profile_page.dart b/lib/views/after_auth_screens/profile/profile_page.dart index 9accd7066..fa43731b2 100644 --- a/lib/views/after_auth_screens/profile/profile_page.dart +++ b/lib/views/after_auth_screens/profile/profile_page.dart @@ -22,8 +22,7 @@ class ProfilePage extends StatelessWidget { this.homeModel, }) : super(key: key); - /// MainScreenViewModel. - /// + /// represents MainScreenViewModel. final MainScreenViewModel? homeModel; @override @@ -35,11 +34,10 @@ class ProfilePage extends StatelessWidget { key: model.scaffoldKey, appBar: AppBar( backgroundColor: Colors.green, - // Theme.of(context).primaryColor, elevation: 0.0, centerTitle: true, leading: IconButton( - color: Theme.of(context).iconTheme.color, + color: Colors.white, icon: const Icon(Icons.menu), onPressed: () => MainScreenViewModel.scaffoldKey.currentState!.openDrawer(), @@ -48,9 +46,8 @@ class ProfilePage extends StatelessWidget { title: Text( AppLocalizations.of(context)!.strictTranslate('Profile'), style: Theme.of(context).textTheme.titleLarge!.copyWith( - // fontWeight: FontWeight.w600, - fontSize: SizeConfig.screenHeight! * 0.03, - fontFamily: 'open-sans', + fontWeight: FontWeight.w600, + fontSize: 20, color: Colors.white, ), ), @@ -60,7 +57,10 @@ class ProfilePage extends StatelessWidget { onPressed: () { navigationService.pushScreen(Routes.appSettings); }, - icon: const Icon(Icons.settings), + icon: const Icon( + Icons.settings, + color: Colors.white, + ), ), ], ), @@ -103,9 +103,7 @@ class ProfilePage extends StatelessWidget { child: Text( '${model.currentUser.firstName!} ${model.currentUser.lastName!}', style: TextStyle( - color: Colors.white, fontSize: SizeConfig.screenHeight! * 0.025, - fontFamily: 'open-sans', ), ), ), @@ -144,9 +142,8 @@ class ProfilePage extends StatelessWidget { .focusedBorder! .borderSide .color, - backgroundColor: Theme.of(context) - .colorScheme - .secondaryContainer, + backgroundColor: + Theme.of(context).colorScheme.tertiary, ), ], ), diff --git a/lib/views/pre_auth_screens/login.dart b/lib/views/pre_auth_screens/login.dart index 2fc9b39fa..6495c6cd4 100644 --- a/lib/views/pre_auth_screens/login.dart +++ b/lib/views/pre_auth_screens/login.dart @@ -183,7 +183,7 @@ class _LoginState extends State { onTap: () => model.login(), textColor: const Color(0xFF008A37), key: const Key('LoginButton'), - backgroundColor: Colors.white, + backgroundColor: Theme.of(context).colorScheme.tertiary, ), SizedBox(height: SizeConfig.screenHeight! * 0.0215), ], diff --git a/lib/views/pre_auth_screens/set_url.dart b/lib/views/pre_auth_screens/set_url.dart index 3e123aaa6..520811c85 100644 --- a/lib/views/pre_auth_screens/set_url.dart +++ b/lib/views/pre_auth_screens/set_url.dart @@ -156,8 +156,7 @@ class _SetUrlState extends State { .focusedBorder! .borderSide .color, - backgroundColor: - Theme.of(context).colorScheme.secondaryContainer, + backgroundColor: Theme.of(context).colorScheme.tertiary, ), SizedBox( height: SizeConfig.screenHeight! * 0.0215, diff --git a/lib/widgets/create_recurring_event_helper_widgets.dart b/lib/widgets/create_recurring_event_helper_widgets.dart index 16c31b1bc..df3d0240e 100644 --- a/lib/widgets/create_recurring_event_helper_widgets.dart +++ b/lib/widgets/create_recurring_event_helper_widgets.dart @@ -137,7 +137,10 @@ class _RecurrenceFrequencyDropdownState child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(widget.selectedOption), + Text( + widget.selectedOption, + overflow: TextOverflow.ellipsis, + ), const Icon(Icons.arrow_drop_down), ], ), @@ -177,16 +180,13 @@ class _EventEndOptionsState extends State { @override void initState() { if (widget.model.eventEndType == EventEndTypes.never) { - widget.model.eventEndDate = null; + widget.model.recurrenceEndDate = null; } super.initState(); } @override Widget build(BuildContext context) { - print(widget.model.eventEndType); - print(widget.model.recurrance); - print(widget.model.recurranceFrequency); return Column( children: [ radioButton( @@ -194,8 +194,9 @@ class _EventEndOptionsState extends State { child: const Text(EventEndTypes.never), index: 0, inputAction: () { - widget.model.setEventEndDate(null); setState(() { + widget.model.count = null; + widget.model.recurrenceEndDate = null; widget.model.eventEndType = EventEndTypes.never; }); }, @@ -216,17 +217,18 @@ class _EventEndOptionsState extends State { onPressed: () async { // initially pickedDate is initialised with current end time. final pickedDate = await customDatePicker( - initialDate: - widget.model.eventEndOnEndDate ?? DateTime.now(), + initialDate: DateTime.now(), ); setState(() { - widget.model.eventEndOnEndDate = pickedDate; + widget.model.recurrenceEndDate = pickedDate; widget.model.eventEndType = EventEndTypes.on; }); }, icon: Text( formatDate( - widget.model.eventEndOnEndDate.toString().split(" ")[0], + (widget.model.recurrenceEndDate ?? DateTime.now()) + .toString() + .split(" ")[0], ), style: widget.model.eventEndType == EventEndTypes.on ? TextStyle(color: Theme.of(context).dividerColor) @@ -244,7 +246,9 @@ class _EventEndOptionsState extends State { index: 1, inputAction: () { setState(() { + widget.model.recurrenceEndDate = DateTime.now(); widget.model.eventEndType = EventEndTypes.on; + widget.model.count = null; }); }, ), @@ -266,6 +270,7 @@ class _EventEndOptionsState extends State { index: 2, inputAction: () { setState(() { + widget.model.recurrenceEndDate = null; widget.model.eventEndType = EventEndTypes.after; }); }, diff --git a/lib/widgets/custom_alert_dialog.dart b/lib/widgets/custom_alert_dialog.dart index 312c3d47d..7b434365e 100644 --- a/lib/widgets/custom_alert_dialog.dart +++ b/lib/widgets/custom_alert_dialog.dart @@ -74,12 +74,13 @@ class CustomAlertDialog extends StatelessWidget { onTap: () => success(), buttonLabel: AppLocalizations.of(context)!.strictTranslate(successText), textColor: const Color(0xFF008A37), - backgroundColor: Colors.white, + backgroundColor: Theme.of(context).colorScheme.tertiary, width: SizeConfig.screenWidth! * 0.2, height: SizeConfig.screenHeight! * 0.06, ), ]; return AlertDialog( + surfaceTintColor: Theme.of(context).colorScheme.secondaryContainer, title: Text( AppLocalizations.of(context)!.strictTranslate(dialogTitle), style: Theme.of(context) diff --git a/lib/widgets/custom_avatar.dart b/lib/widgets/custom_avatar.dart index 9126cd49b..7d8ce4343 100644 --- a/lib/widgets/custom_avatar.dart +++ b/lib/widgets/custom_avatar.dart @@ -1,4 +1,3 @@ -// ignore_for_file: talawa_good_doc_comments, talawa_api_doc import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; @@ -48,10 +47,10 @@ class CustomAvatar extends StatelessWidget { child: Center( child: Text( firstAlphabet!, - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(fontSize: fontSize), + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontSize: fontSize, + color: Theme.of(context).colorScheme.onSecondary, + ), ), ), ) diff --git a/lib/widgets/custom_weekday_selector.dart b/lib/widgets/custom_weekday_selector.dart index 6f6069664..f46ef92d1 100644 --- a/lib/widgets/custom_weekday_selector.dart +++ b/lib/widgets/custom_weekday_selector.dart @@ -16,7 +16,7 @@ class CustomWeekDaySelector extends StatefulWidget { class _CustomWeekDaySelectorState extends State { @override Widget build(BuildContext context) { - final Set weekdays = widget.model.weekdays; + final Set weekdays = widget.model.weekDays; return Container( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -30,6 +30,7 @@ class _CustomWeekDaySelectorState extends State { } else { weekdays.add(weekday); } + widget.model.weekDays = weekdays; }); }, child: Container( @@ -73,19 +74,19 @@ class _CustomWeekDaySelectorState extends State { String _getWeekdayAbbreviation(int index) { switch (index) { case 0: - return Recurrance.weekdaySunday; + return WeekDays.sunday; case 1: - return Recurrance.weekdayMonday; + return WeekDays.monday; case 2: - return Recurrance.weekdayTuesday; + return WeekDays.tuesday; case 3: - return Recurrance.weekdayWednesday; + return WeekDays.wednesday; case 4: - return Recurrance.weekdayThursday; + return WeekDays.thursday; case 5: - return Recurrance.weekdayFriday; + return WeekDays.friday; case 6: - return Recurrance.weekdaySaturday; + return WeekDays.saturday; default: return ''; } diff --git a/lib/widgets/event_card.dart b/lib/widgets/event_card.dart index 676e24b6a..52480d78b 100644 --- a/lib/widgets/event_card.dart +++ b/lib/widgets/event_card.dart @@ -36,6 +36,7 @@ class EventCard extends StatelessWidget { child: Stack( children: [ Card( + surfaceTintColor: Theme.of(context).colorScheme.secondaryContainer, shape: RoundedRectangleBorder( side: isRegistered && userConfig.currentUser.id != event.creator!.id @@ -122,7 +123,7 @@ class EventCard extends StatelessWidget { const Spacer(), Expanded( child: Text( - "${event.startDate!} - ${event.endDate!}", + "${event.startDate} - ${event.endDate}", style: Theme.of(context).textTheme.bodySmall, ), ), @@ -131,35 +132,36 @@ class EventCard extends StatelessWidget { SizedBox( height: SizeConfig.screenHeight! * 0.006, ), - Row( - children: [ - const Icon( - Icons.schedule, - size: 12, - ), - SizedBox( - width: SizeConfig.screenWidth! * 0.015, - ), - Text( - "${event.startTime!} - ${event.endTime!}", - style: Theme.of(context).textTheme.bodySmall, - ), - const Spacer(), - const Icon( - Icons.place, - size: 12, - ), - SizedBox( - child: Text( - event.location!.substring( - 0, - min(event.location!.length, 20), - ), + if (event.startTime != null && event.endTime != null) + Row( + children: [ + const Icon( + Icons.schedule, + size: 12, + ), + SizedBox( + width: SizeConfig.screenWidth! * 0.015, + ), + Text( + "${event.startTime} - ${event.endTime!}", style: Theme.of(context).textTheme.bodySmall, ), - ), - ], - ), + const Spacer(), + const Icon( + Icons.place, + size: 12, + ), + SizedBox( + child: Text( + event.location!.substring( + 0, + min(event.location!.length, 20), + ), + style: Theme.of(context).textTheme.bodySmall, + ), + ), + ], + ), SizedBox( height: SizeConfig.screenHeight! * 0.013, ), diff --git a/lib/widgets/event_date_time_tile.dart b/lib/widgets/event_date_time_tile.dart index 09a86b220..459765823 100644 --- a/lib/widgets/event_date_time_tile.dart +++ b/lib/widgets/event_date_time_tile.dart @@ -9,6 +9,7 @@ class DateTimeTile extends StatelessWidget { required this.time, required this.setDate, required this.setTime, + required this.isAllDay, }); /// Represents a date in string format. @@ -17,6 +18,9 @@ class DateTimeTile extends StatelessWidget { /// Represents a time in string format. final String time; + /// Represents a boolean value indicating if the event is all day. + final bool isAllDay; + /// Function that sets the date. final Function() setDate; @@ -52,25 +56,27 @@ class DateTimeTile extends StatelessWidget { style: const TextStyle(fontSize: 16), ), ), - const Spacer(), - const Icon( - Icons.schedule, - color: Color(0xff524F4F), - size: 19, - ), - SizedBox( - width: SizeConfig.screenWidth! * 0.045, - ), - InkWell( - key: const Key('EventDateTimeTileTime'), - onTap: () async { - setTime(); - }, - child: Text( - time, - style: const TextStyle(fontSize: 16), + if (!isAllDay) ...[ + const Spacer(), + const Icon( + Icons.schedule, + color: Color(0xff524F4F), + size: 19, ), - ), + SizedBox( + width: SizeConfig.screenWidth! * 0.045, + ), + InkWell( + key: const Key('EventDateTimeTileTime'), + onTap: () async { + setTime(); + }, + child: Text( + time, + style: const TextStyle(fontSize: 16), + ), + ), + ], ], ), ), diff --git a/lib/widgets/post_detailed_page.dart b/lib/widgets/post_detailed_page.dart index aad904c8d..42dbf68eb 100644 --- a/lib/widgets/post_detailed_page.dart +++ b/lib/widgets/post_detailed_page.dart @@ -1,4 +1,3 @@ -// ignore_for_file: talawa_good_doc_comments, talawa_api_doc import 'package:flutter/material.dart'; import 'package:talawa/utils/app_localization.dart'; @@ -11,7 +10,6 @@ class DescriptionTextWidget extends StatefulWidget { const DescriptionTextWidget({required this.text}); /// actual description to be displayed. - /// final String text; @override @@ -20,23 +18,15 @@ class DescriptionTextWidget extends StatefulWidget { class _DescriptionTextWidgetState extends State { /// before clicking show more. - /// - /// late String firstHalf; /// After the show more. - /// - /// late String secondHalf; /// tags in the post. - /// late String tag; - //setting the flag to true initially - /// is show more turned on. - /// - /// + /// setting the flag to true initially (is show more turned on). bool flag = true; @override @@ -65,23 +55,24 @@ class _DescriptionTextWidgetState extends State { @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 5.0), child: secondHalf.isEmpty ? Column( children: [ Text( firstHalf, - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(fontFamily: 'open-sans', color: Colors.black38), + textAlign: TextAlign.justify, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontFamily: 'open-sans', + color: Theme.of(context).colorScheme.onSecondary, + ), ), tag != "" ? Text( "# $tag", style: Theme.of(context).textTheme.bodyMedium!.copyWith( fontFamily: 'open-sans', - color: Colors.black38, + color: Theme.of(context).colorScheme.onSecondary, ), ) : Container(), @@ -93,10 +84,11 @@ class _DescriptionTextWidgetState extends State { // If the flag is true, we only display the firstHalf. // If it is false, we display the entire text. flag ? ("$firstHalf...") : (firstHalf + secondHalf), - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(fontFamily: 'open-sans'), + textAlign: TextAlign.justify, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontFamily: 'open-sans', + color: Theme.of(context).colorScheme.onSecondary, + ), ), Row( mainAxisAlignment: MainAxisAlignment.end, diff --git a/lib/widgets/post_list_widget.dart b/lib/widgets/post_list_widget.dart index 5f0cd08b7..2364e16a6 100644 --- a/lib/widgets/post_list_widget.dart +++ b/lib/widgets/post_list_widget.dart @@ -11,17 +11,13 @@ class PostListWidget extends StatelessWidget { this.deletePost, }); - /// lis of all the post. - /// + /// list of all the post. final List posts; /// This function is passed for the handling the action to be performed when the comment button is clicked. - /// - /// to see the function check the place where the widget is called. final Function(Post)? function; /// Function the deleting the post. - /// final Function(Post)? deletePost; @override diff --git a/lib/widgets/post_modal.dart b/lib/widgets/post_modal.dart index 6a47c9e00..d26a8c226 100644 --- a/lib/widgets/post_modal.dart +++ b/lib/widgets/post_modal.dart @@ -4,8 +4,6 @@ import 'package:talawa/locator.dart'; import 'package:talawa/models/post/post_model.dart'; /// To add options to the bottom nav bar, increase the height too. -/// -/// class PostBottomModal extends StatelessWidget { const PostBottomModal({ super.key, @@ -15,18 +13,12 @@ class PostBottomModal extends StatelessWidget { }); /// This function is passed for the handling the action to be performed when the comment button is clicked. - /// - /// to see the function check the place where the widget is called. final Function(Post)? function; - /// To delete the post if user can. - /// - /// only work if the post is made by the user + /// To delete the post if user can (only work if the post is made by the user). final Function(Post)? deletePost; /// Post object containing all the data related to the post. - /// - /// see the post model to get more information regarding this final Post post; @override @@ -36,11 +28,11 @@ class PostBottomModal extends StatelessWidget { Center( child: Row( children: [ - const Padding( - padding: EdgeInsets.fromLTRB(20, 0, 0, 0), + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), child: Icon( Icons.report_gmailerrorred_outlined, - color: Colors.black38, + color: Theme.of(context).colorScheme.tertiary, ), ), TextButton( @@ -52,13 +44,9 @@ class PostBottomModal extends StatelessWidget { ); Navigator.pop(context); }, - child: const Text( + child: Text( 'Report the post to the Admin', - style: TextStyle( - color: Colors.black38, - fontSize: 20, - fontFamily: 'open-sans', - ), + style: Theme.of(context).textTheme.bodyLarge, ), ), ], @@ -67,20 +55,17 @@ class PostBottomModal extends StatelessWidget { Center( child: Row( children: [ - const Padding( - padding: EdgeInsets.fromLTRB(20, 0, 0, 0), + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), child: Icon( Icons.delete, - color: Colors.black38, + color: Theme.of(context).colorScheme.tertiary, ), ), TextButton( key: const Key('deletePost'), onPressed: () { - deletePost != null - ? deletePost!(post) - // ignore: unnecessary_statements - : {}; + deletePost?.call(post); showDialog( context: context, builder: (BuildContext builder) { @@ -113,13 +98,9 @@ class PostBottomModal extends StatelessWidget { }, ); }, - child: const Text( + child: Text( 'The post was deleted', - style: TextStyle( - color: Colors.black38, - fontSize: 20, - fontFamily: 'open-sans', - ), + style: Theme.of(context).textTheme.bodyLarge, ), ), ], diff --git a/lib/widgets/post_widget.dart b/lib/widgets/post_widget.dart index 7ec420a3a..807f49037 100644 --- a/lib/widgets/post_widget.dart +++ b/lib/widgets/post_widget.dart @@ -12,8 +12,6 @@ import 'package:talawa/widgets/post_detailed_page.dart'; import 'package:talawa/widgets/post_modal.dart'; /// Stateless class to show the fetched post. -/// -/// entirely ui based widget class NewsPost extends StatelessWidget { const NewsPost({ super.key, @@ -23,29 +21,25 @@ class NewsPost extends StatelessWidget { }); /// Post object containing all the data related to the post. - /// - /// see the post model to get more information regarding this final Post post; /// This function is passed for the handling the action to be performed when the comment button is clicked. - /// - /// to see the function check the place where the widget is called. final Function(Post)? function; - /// To delete the post if user can. - /// - /// only work if the post is made by the user + /// To delete the post if user can (only work if the post is made by the user). final Function(Post)? deletePost; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), - child: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(16)), + child: Card( + elevation: 5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), ), + surfaceTintColor: Theme.of(context).colorScheme.secondaryContainer, + color: Theme.of(context).colorScheme.tertiaryContainer, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -58,58 +52,49 @@ class NewsPost extends StatelessWidget { "${'${GraphqlConfig.orgURI}'.replaceFirst('/graphql', '')}/${post.creator!.image}", fontSize: 20, ), - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "${post.creator!.firstName} ${post.creator!.lastName}", - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.black38, + title: Text( + "${post.creator!.firstName} ${post.creator!.lastName}", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Theme.of(context).colorScheme.onSecondary, + ), + ), + trailing: IconButton( + key: const Key('reportButton'), + onPressed: () => showModalBottomSheet( + context: context, + builder: (BuildContext context1) => Container( + key: const Key('reportPost'), + height: 120, + decoration: BoxDecoration( + color: Theme.of(context).primaryColor, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(16), + topLeft: Radius.circular(16), + ), ), - ), - IconButton( - onPressed: () { - showModalBottomSheet( - context: context, - builder: (BuildContext context1) { - return Container( - height: 120, - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topRight: Radius.circular(16), - topLeft: Radius.circular(16), - ), - ), - child: PostBottomModal( - post: post, - deletePost: deletePost, - function: function, - ), - ); - }, - ); - }, - icon: const Icon( - Icons.report_gmailerrorred_outlined, - color: Colors.black38, + child: PostBottomModal( + post: post, + deletePost: deletePost, + function: function, ), ), - ], + ), + icon: Icon( + Icons.report_gmailerrorred_outlined, + color: Theme.of(context).colorScheme.onSecondary, + ), ), ), - post.imageUrl == null - ? DescriptionTextWidget(text: post.description!) - : Container(), post.imageUrl != null ? Container( - height: 380, + key: const Key('postParentContainer'), + height: 340, color: Colors.white, child: PostContainer(photoUrl: post.imageUrl), ) - : Container(), + : DescriptionTextWidget(text: post.description!), BaseView( onModelReady: (model) { model.initialize(post.likedBy ?? [], post.sId); @@ -126,29 +111,25 @@ class NewsPost extends StatelessWidget { child: Column( children: [ MultiReactButton( - toggle: () { - model.toggleIsLiked(); - }, + toggle: () => model.toggleIsLiked(), ), Text( "${model.likedBy.length}", - style: const TextStyle( + style: TextStyle( fontFamily: 'open-sans', - color: Colors.black38, + color: + Theme.of(context).colorScheme.onSecondary, ), ), ], ), ), - const SizedBox( - width: 20, - ), Container( child: Column( children: [ GestureDetector( - onTap: () => - function != null ? function!(post) : {}, + key: const Key('commentButton'), + onTap: () => function?.call(post), child: Padding( padding: const EdgeInsets.all(8.0), child: SizedBox( @@ -166,8 +147,9 @@ class NewsPost extends StatelessWidget { ), Text( "${post.comments!.length}", - style: const TextStyle( - color: Colors.black38, + style: TextStyle( + color: + Theme.of(context).colorScheme.onSecondary, ), ), ], @@ -177,8 +159,8 @@ class NewsPost extends StatelessWidget { padding: const EdgeInsets.fromLTRB(120, 0, 0, 0), child: Text( ' ${post.getPostCreatedDuration()}', - style: const TextStyle( - color: Colors.black38, + style: TextStyle( + color: Theme.of(context).colorScheme.onSecondary, fontSize: 12, ), ), @@ -195,13 +177,12 @@ class NewsPost extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ GestureDetector( - onTap: () => function != null ? function!(post) : {}, + onTap: () => function?.call(post), child: Text( - //TODO: Currently the Liked Model contain on SID of USER who liked the post, thus my name here "${AppLocalizations.of(context)!.strictTranslate("Liked")} by ...", - style: const TextStyle( + style: TextStyle( fontFamily: 'open-sans', - color: Colors.black38, + color: Theme.of(context).colorScheme.onSecondary, ), ), ), @@ -216,9 +197,8 @@ class NewsPost extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - post.imageUrl != null - ? DescriptionTextWidget(text: post.description!) - : Container(), + if (post.imageUrl != null) + DescriptionTextWidget(text: post.description!), ], ), ), diff --git a/lib/widgets/recurrence_dialog.dart b/lib/widgets/recurrence_dialog.dart index 348460a9f..2d35c103c 100644 --- a/lib/widgets/recurrence_dialog.dart +++ b/lib/widgets/recurrence_dialog.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:talawa/constants/recurrence_values.dart'; import 'package:talawa/constants/routing_constants.dart'; import 'package:talawa/locator.dart'; import 'package:talawa/services/size_config.dart'; @@ -9,78 +11,191 @@ class ShowRecurrenceDialog extends StatefulWidget { const ShowRecurrenceDialog({ super.key, required this.model, - required this.initialRecurrence, }); + /// Instance of create event view model. final CreateEventViewModel model; - final String initialRecurrence; @override State createState() => _ShowRecurrenceDialogState(); } class _ShowRecurrenceDialogState extends State { - static const donotRepeat = "Does not repeat"; - static const dialy = "Every day"; - static const weekly = "Every week"; - static const monthly = "Every month"; - static const yearly = "Every year"; - static const custom = "Custom..."; - - static const List _frequencies = [ - donotRepeat, - dialy, - weekly, - monthly, - yearly, - custom, - ]; - - late String _frequency; @override Widget build(BuildContext context) { - _frequency = widget.initialRecurrence; - print('widget built'); return Dialog( child: SizedBox( - height: SizeConfig.screenHeight! * 0.5, + height: SizeConfig.screenHeight! * 0.74, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - radioButton(_frequencies[0]), - radioButton(_frequencies[1]), - radioButton(_frequencies[2]), - radioButton(_frequencies[3]), - radioButton(_frequencies[4]), - radioButton(_frequencies[5]), + radioButtonFixText( + "Does not repeat", + // coverage:ignore-start + (value) => updateModel(value!, false, null, null, null), + // coverage:ignore-end + ), + radioButton( + Frequency.daily, + widget.model.interval, + widget.model.count, + null, + null, + ), + radioButton(Frequency.weekly, widget.model.interval, + widget.model.count, null, [ + RecurrenceUtils + .weekDays[widget.model.recurrenceStartDate.weekday - 1], + ]), + radioButton( + Frequency.monthly, + widget.model.interval, + widget.model.count, + null, + null, + ), + if (RecurrenceUtils.getWeekDayOccurenceInMonth( + widget.model.recurrenceStartDate, + ) != + 5) + radioButton( + Frequency.monthly, + widget.model.interval, + widget.model.count, + RecurrenceUtils.getWeekDayOccurenceInMonth( + widget.model.recurrenceStartDate, + ), + [ + RecurrenceUtils + .weekDays[widget.model.recurrenceStartDate.weekday - 1], + ]), + if (RecurrenceUtils.isLastOccurenceOfWeekDay( + widget.model.recurrenceStartDate, + )) + radioButton(Frequency.monthly, widget.model.interval, + widget.model.count, -1, [ + RecurrenceUtils + .weekDays[widget.model.recurrenceStartDate.weekday], + ]), + radioButton( + Frequency.yearly, + widget.model.interval, + widget.model.count, + null, + null, + ), + radioButtonFixText( + 'Monday to Friday ${widget.model.recurrenceEndDate != null ? "until ${DateFormat('MMMM d, y').format(widget.model.recurrenceEndDate!)}" : ""}', + (value) => updateModel(value!, true, Frequency.weekly, null, { + 'MONDAY', + 'TUESDAY', + 'WEDNESDAY', + 'THURSDAY', + 'FRIDAY', + }), + ), + radioButtonFixText("Custom...", (value) async { + widget.model.isRecurring = true; + await navigationService.pushScreen( + Routes.customRecurrencePage, + arguments: widget.model, + ); + setState(() { + Navigator.pop(context, widget.model.recurrenceLabel); + }); + }), ], ), ), ); } - // custom radio list tile. + /// custom radio list tile. + /// + /// **params**: + /// * `frequency`: represent the frequency of the event. + /// * `interval`: represent the interval of the event. + /// * `count`: represent the count of the event. + /// * `weekDayOccurenceInMonth`: represent the week day occurence in month. + /// * `weekDays`: represent the list of week days. + /// + /// **returns**: + /// * `RadioListTile`: returns radio list tile. RadioListTile radioButton( String frequency, + int? interval, + int? count, + int? weekDayOccurenceInMonth, + List? weekDays, + ) { + final String text = RecurrenceUtils.getRecurrenceRuleText( + frequency, + weekDays?.toSet(), + interval, + count, + weekDayOccurenceInMonth, + widget.model.recurrenceStartDate, + widget.model.recurrenceEndDate, + ); + return RadioListTile( + title: Text(text), + value: text, + groupValue: widget.model.recurrenceLabel, + onChanged: (value) => updateModel( + value!, + true, + frequency, + weekDayOccurenceInMonth, + weekDays?.map((day) => day.toUpperCase()).toSet(), + ), + ); + } + + /// custom radio list tile with fixed the text. + /// + /// **params**: + /// * `text`: represents the text of the radio button. + /// * `onChanged`: represents the function to be called when the radio button is pressed. + /// + /// **returns**: + /// * `RadioListTile`: returns radio list tile. + RadioListTile radioButtonFixText( + String text, + Function(String?)? onChanged, ) { return RadioListTile( - title: Text(frequency), - value: frequency, - groupValue: _frequency, - onChanged: (value) async { - // navigate to custom recurrence page when pressed custom... button. - widget.model.recurrance = widget.model.getRecurrance(value!); - if (value == _frequencies[5]) { - await navigationService.pushScreen( - Routes.customRecurrencePage, - arguments: widget.model, - ); - } - setState(() { - _frequency = value; - Navigator.pop(context, _frequency); - }); - }, + title: Text(text), + value: text, + groupValue: widget.model.recurrenceLabel, + onChanged: onChanged, ); } + + /// Returns the updated model with the selected recurrence options. + /// + /// **params**: + /// * `value`: represents text of the selected recurrence option. + /// * `isRecurring`: represent whether the event is recurring or not. + /// * `frequency`: represent the frequency of the event. + /// * `weekDayOccurenceInMonth`: represent the week day occurence in month. + /// * `weekDays`: represent the list of week days. + /// + /// **returns**: + /// None + void updateModel( + String value, + bool isRecurring, + String? frequency, + int? weekDayOccurenceInMonth, + Set? weekDays, + ) { + setState(() { + widget.model.isRecurring = isRecurring; + widget.model.recurrenceLabel = value; + widget.model.weekDays = weekDays ?? {}; + widget.model.weekDayOccurenceInMonth = weekDayOccurenceInMonth; + widget.model.frequency = frequency ?? Frequency.weekly; + Navigator.pop(context, value); + }); + } } diff --git a/pubspec.lock b/pubspec.lock index 93c585333..4088cf5d8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -269,10 +269,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" currency_picker: dependency: "direct main" description: @@ -418,10 +418,10 @@ packages: dependency: "direct main" description: name: flutter_cache_manager - sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" + sha256: "395d6b7831f21f3b989ebedbb785545932adb9afe2622c1ffacf7f4b53a7e544" url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "3.3.2" flutter_hooks: dependency: transitive description: @@ -793,18 +793,18 @@ packages: dependency: "direct main" description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" json_serializable: dependency: "direct dev" description: name: json_serializable - sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b url: "https://pub.dev" source: hosted - version: "6.7.1" + version: "6.8.0" leak_tracker: dependency: transitive description: @@ -1318,10 +1318,10 @@ packages: dependency: transitive description: name: sqflite - sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + sha256: "5ce2e1a15e822c3b4bfb5400455775e421da7098eed8adc8f26298ada7c9308c" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.3" sqflite_common: dependency: transitive description: @@ -1366,26 +1366,26 @@ packages: dependency: "direct main" description: name: syncfusion_flutter_calendar - sha256: "24070f4ca1558d994e8232b250a2ce3edc920845c8b0f3597895afc8338c901e" + sha256: e06aff78dd2040a3239d59b8543020afd4bfcfde5d66d028d6ac04a84ed0e4b0 url: "https://pub.dev" source: hosted - version: "25.1.40" + version: "25.1.41" syncfusion_flutter_core: dependency: transitive description: name: syncfusion_flutter_core - sha256: "8e19260d292ed77e1cbd4ee1baafb3c9486079cad856e7891478131d25076039" + sha256: "708425f534ef04253334763c8b75acc79eaec2d9b325bcd862575ea0a2207d87" url: "https://pub.dev" source: hosted - version: "25.1.40" + version: "25.1.41" syncfusion_flutter_datepicker: dependency: "direct main" description: name: syncfusion_flutter_datepicker - sha256: "04e7cc46b2c0ebfcd76e6082284f7daaf4731df37ddcc876d3073159a6e55d25" + sha256: b6abaa80a6dff16552a5b7a7b3879094d285ae5a49a33466d4d1a62041e250dd url: "https://pub.dev" source: hosted - version: "25.1.40" + version: "25.1.41" synchronized: dependency: transitive description: @@ -1549,10 +1549,10 @@ packages: dependency: transitive description: name: uuid - sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 + sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" url: "https://pub.dev" source: hosted - version: "4.3.3" + version: "4.4.0" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fcdf5540d..dd898036e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: contained_tab_bar_view: ^0.8.0 crypto: ^3.0.3 - cupertino_icons: ^1.0.3 + cupertino_icons: ^1.0.8 currency_picker: ^2.0.21 ############## Remove ########## @@ -36,7 +36,7 @@ dependencies: flutter: sdk: flutter flutter_braintree: ^4.0.0 - flutter_cache_manager: ^3.3.1 + flutter_cache_manager: ^3.3.2 flutter_local_notifications: ^17.1.0 flutter_localizations: sdk: flutter @@ -49,7 +49,7 @@ dependencies: hive: ^2.2.3 http: ^1.2.1 image_cropper: ^5.0.1 - image_picker: ^1.0.6 + image_picker: ^1.1.0 intl: ^0.18.1 json_annotation: ^4.7.0 mockito: ^5.4.4 @@ -65,7 +65,7 @@ dependencies: shared_preferences: ^2.2.3 shimmer: ^3.0.0 social_share: ^2.2.1 - syncfusion_flutter_calendar: ^25.1.40 + syncfusion_flutter_calendar: ^25.1.41 syncfusion_flutter_datepicker: ^25.1.39 timelines: ^0.1.0 tutorial_coach_mark: ^1.2.11 @@ -84,7 +84,7 @@ dev_dependencies: sdk: flutter hive_generator: ^2.0.1 - json_serializable: ^6.7.1 + json_serializable: ^6.8.0 lint: ^2.3.0 mocktail_image_network: ^1.1.0 diff --git a/test/helpers/test_helpers.mocks.dart b/test/helpers/test_helpers.mocks.dart index a62b80318..017b54648 100644 --- a/test/helpers/test_helpers.mocks.dart +++ b/test/helpers/test_helpers.mocks.dart @@ -978,6 +978,51 @@ class MockGraphQLClient extends _i2.Mock implements _i3.GraphQLClient { /// /// See the documentation for Mockito's code generation for more information. class MockPostService extends _i2.Mock implements _i16.PostService { + @override + set postInfo(Map? _postInfo) => super.noSuchMethod( + Invocation.setter( + #postInfo, + _postInfo, + ), + returnValueForMissingStub: null, + ); + + @override + set after(String? _after) => super.noSuchMethod( + Invocation.setter( + #after, + _after, + ), + returnValueForMissingStub: null, + ); + + @override + set before(String? _before) => super.noSuchMethod( + Invocation.setter( + #before, + _before, + ), + returnValueForMissingStub: null, + ); + + @override + set first(int? _first) => super.noSuchMethod( + Invocation.setter( + #first, + _first, + ), + returnValueForMissingStub: null, + ); + + @override + set last(int? _last) => super.noSuchMethod( + Invocation.setter( + #last, + _last, + ), + returnValueForMissingStub: null, + ); + @override _i5.Stream> get postStream => (super.noSuchMethod( Invocation.getter(#postStream), @@ -1060,24 +1105,24 @@ class MockPostService extends _i2.Mock implements _i16.PostService { ); @override - _i5.Future nextPage() => super.noSuchMethod( + _i5.Future nextPage() => (super.noSuchMethod( Invocation.method( #nextPage, [], ), returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), - ); + ) as _i5.Future); @override - _i5.Future previousPage() => super.noSuchMethod( + _i5.Future previousPage() => (super.noSuchMethod( Invocation.method( #previousPage, [], ), returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), - ); + ) as _i5.Future); } /// A class which mocks [MultiMediaPickerService]. @@ -2831,24 +2876,8 @@ class MockOrganizationFeedViewModel extends _i2.Mock ), returnValueForMissingStub: null, ); - @override - void nextPage() => super.noSuchMethod( - Invocation.method( - #nextPage, - [], - ), - returnValueForMissingStub: null, - ); @override - void previousPage() => super.noSuchMethod( - Invocation.method( - #previousPage, - [], - ), - returnValueForMissingStub: null, - ); - @override void initialise({bool? isTest = false}) => super.noSuchMethod( Invocation.method( #initialise, @@ -2922,6 +2951,24 @@ class MockOrganizationFeedViewModel extends _i2.Mock returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override + void nextPage() => super.noSuchMethod( + Invocation.method( + #nextPage, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void previousPage() => super.noSuchMethod( + Invocation.method( + #previousPage, + [], + ), + returnValueForMissingStub: null, + ); + @override void setState(_i14.ViewState? viewState) => super.noSuchMethod( Invocation.method( @@ -3447,6 +3494,19 @@ class MockCreateEventViewModel extends _i2.Mock returnValueForMissingStub: null, ); + @override + DateTime get eventEndDate => (super.noSuchMethod( + Invocation.getter(#eventEndDate), + returnValue: _FakeDateTime_23( + this, + Invocation.getter(#eventEndDate), + ), + returnValueForMissingStub: _FakeDateTime_23( + this, + Invocation.getter(#eventEndDate), + ), + ) as DateTime); + @override set eventEndDate(DateTime? _eventEndDate) => super.noSuchMethod( Invocation.setter( @@ -3456,6 +3516,37 @@ class MockCreateEventViewModel extends _i2.Mock returnValueForMissingStub: null, ); + @override + DateTime get recurrenceStartDate => (super.noSuchMethod( + Invocation.getter(#recurrenceStartDate), + returnValue: _FakeDateTime_23( + this, + Invocation.getter(#recurrenceStartDate), + ), + returnValueForMissingStub: _FakeDateTime_23( + this, + Invocation.getter(#recurrenceStartDate), + ), + ) as DateTime); + + @override + set recurrenceStartDate(DateTime? _recurrenceStartDate) => super.noSuchMethod( + Invocation.setter( + #recurrenceStartDate, + _recurrenceStartDate, + ), + returnValueForMissingStub: null, + ); + + @override + set recurrenceEndDate(DateTime? _recurrenceEndDate) => super.noSuchMethod( + Invocation.setter( + #recurrenceEndDate, + _recurrenceEndDate, + ), + returnValueForMissingStub: null, + ); + @override bool get isPublicSwitch => (super.noSuchMethod( Invocation.getter(#isPublicSwitch), @@ -3554,24 +3645,6 @@ class MockCreateEventViewModel extends _i2.Mock returnValueForMissingStub: null, ); - @override - set latitude(double? _latitude) => super.noSuchMethod( - Invocation.setter( - #latitude, - _latitude, - ), - returnValueForMissingStub: null, - ); - - @override - set longitude(double? _longitude) => super.noSuchMethod( - Invocation.setter( - #longitude, - _longitude, - ), - returnValueForMissingStub: null, - ); - @override bool get isAllDay => (super.noSuchMethod( Invocation.getter(#isAllDay), @@ -3605,117 +3678,140 @@ class MockCreateEventViewModel extends _i2.Mock ); @override - int get recurranceCount => (super.noSuchMethod( - Invocation.getter(#recurranceCount), - returnValue: 0, - returnValueForMissingStub: 0, - ) as int); - - @override - set recurranceCount(int? _recurranceCount) => super.noSuchMethod( - Invocation.setter( - #recurranceCount, - _recurranceCount, + String get recurrenceInterval => (super.noSuchMethod( + Invocation.getter(#recurrenceInterval), + returnValue: _i25.dummyValue( + this, + Invocation.getter(#recurrenceInterval), ), - returnValueForMissingStub: null, - ); + returnValueForMissingStub: _i25.dummyValue( + this, + Invocation.getter(#recurrenceInterval), + ), + ) as String); @override - set recurrance(String? _recurrance) => super.noSuchMethod( + set recurrenceInterval(String? _recurrenceInterval) => super.noSuchMethod( Invocation.setter( - #recurrance, - _recurrance, + #recurrenceInterval, + _recurrenceInterval, ), returnValueForMissingStub: null, ); @override - String get recurranceFrequency => (super.noSuchMethod( - Invocation.getter(#recurranceFrequency), + String get eventEndType => (super.noSuchMethod( + Invocation.getter(#eventEndType), returnValue: _i25.dummyValue( this, - Invocation.getter(#recurranceFrequency), + Invocation.getter(#eventEndType), ), returnValueForMissingStub: _i25.dummyValue( this, - Invocation.getter(#recurranceFrequency), + Invocation.getter(#eventEndType), ), ) as String); @override - set recurranceFrequency(String? _recurranceFrequency) => super.noSuchMethod( + set eventEndType(String? _eventEndType) => super.noSuchMethod( Invocation.setter( - #recurranceFrequency, - _recurranceFrequency, + #eventEndType, + _eventEndType, ), returnValueForMissingStub: null, ); @override - String get monthlyRecurrence => (super.noSuchMethod( - Invocation.getter(#monthlyRecurrence), + String get frequency => (super.noSuchMethod( + Invocation.getter(#frequency), returnValue: _i25.dummyValue( this, - Invocation.getter(#monthlyRecurrence), + Invocation.getter(#frequency), ), returnValueForMissingStub: _i25.dummyValue( this, - Invocation.getter(#monthlyRecurrence), + Invocation.getter(#frequency), ), ) as String); @override - set monthlyRecurrence(String? _monthlyRecurrence) => super.noSuchMethod( + set frequency(String? _frequency) => super.noSuchMethod( Invocation.setter( - #monthlyRecurrence, - _monthlyRecurrence, + #frequency, + _frequency, ), returnValueForMissingStub: null, ); @override - Set get weekdays => (super.noSuchMethod( - Invocation.getter(#weekdays), + Set get weekDays => (super.noSuchMethod( + Invocation.getter(#weekDays), returnValue: {}, returnValueForMissingStub: {}, ) as Set); @override - set weekdays(Set? _weekdays) => super.noSuchMethod( + set weekDays(Set? _weekDays) => super.noSuchMethod( Invocation.setter( - #weekdays, - _weekdays, + #weekDays, + _weekDays, ), returnValueForMissingStub: null, ); @override - String get eventEndType => (super.noSuchMethod( - Invocation.getter(#eventEndType), - returnValue: _i25.dummyValue( - this, - Invocation.getter(#eventEndType), + int get interval => (super.noSuchMethod( + Invocation.getter(#interval), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + + @override + set interval(int? _interval) => super.noSuchMethod( + Invocation.setter( + #interval, + _interval, ), - returnValueForMissingStub: _i25.dummyValue( - this, - Invocation.getter(#eventEndType), + returnValueForMissingStub: null, + ); + + @override + set count(int? _count) => super.noSuchMethod( + Invocation.setter( + #count, + _count, ), - ) as String); + returnValueForMissingStub: null, + ); @override - set eventEndType(String? _eventEndType) => super.noSuchMethod( + set weekDayOccurenceInMonth(int? _weekDayOccurenceInMonth) => + super.noSuchMethod( Invocation.setter( - #eventEndType, - _eventEndType, + #weekDayOccurenceInMonth, + _weekDayOccurenceInMonth, ), returnValueForMissingStub: null, ); @override - set eventEndOnEndDate(DateTime? _eventEndOnEndDate) => super.noSuchMethod( + String get recurrenceLabel => (super.noSuchMethod( + Invocation.getter(#recurrenceLabel), + returnValue: _i25.dummyValue( + this, + Invocation.getter(#recurrenceLabel), + ), + returnValueForMissingStub: _i25.dummyValue( + this, + Invocation.getter(#recurrenceLabel), + ), + ) as String); + + @override + set recurrenceLabel(String? _recurrenceLabel) => super.noSuchMethod( Invocation.setter( - #eventEndOnEndDate, - _eventEndOnEndDate, + #recurrenceLabel, + _recurrenceLabel, ), returnValueForMissingStub: null, ); @@ -3879,15 +3975,6 @@ class MockCreateEventViewModel extends _i2.Mock returnValueForMissingStub: null, ); - @override - String? getRecurrance(String? frequency) => (super.noSuchMethod( - Invocation.method( - #getRecurrance, - [frequency], - ), - returnValueForMissingStub: null, - ) as String?); - @override void setState(_i14.ViewState? viewState) => super.noSuchMethod( Invocation.method( @@ -4203,6 +4290,7 @@ class MockImagePicker extends _i2.Mock implements _i13.ImagePicker { double? maxHeight, int? imageQuality, bool? requestFullMetadata = true, + int? limit, }) => (super.noSuchMethod( Invocation.method( @@ -4213,6 +4301,7 @@ class MockImagePicker extends _i2.Mock implements _i13.ImagePicker { #maxHeight: maxHeight, #imageQuality: imageQuality, #requestFullMetadata: requestFullMetadata, + #limit: limit, }, ), returnValue: _i5.Future>.value(<_i13.XFile>[]), @@ -4248,6 +4337,7 @@ class MockImagePicker extends _i2.Mock implements _i13.ImagePicker { double? maxHeight, int? imageQuality, bool? requestFullMetadata = true, + int? limit, }) => (super.noSuchMethod( Invocation.method( @@ -4258,6 +4348,7 @@ class MockImagePicker extends _i2.Mock implements _i13.ImagePicker { #maxHeight: maxHeight, #imageQuality: imageQuality, #requestFullMetadata: requestFullMetadata, + #limit: limit, }, ), returnValue: _i5.Future>.value(<_i13.XFile>[]), diff --git a/test/model_tests/chat/chat_list_tile_data_model_test.dart b/test/model_tests/chat/chat_list_tile_data_model_test.dart index 0bda6029e..5571f7aec 100644 --- a/test/model_tests/chat/chat_list_tile_data_model_test.dart +++ b/test/model_tests/chat/chat_list_tile_data_model_test.dart @@ -1,10 +1,28 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - import 'package:flutter_test/flutter_test.dart'; import 'package:talawa/models/chats/chat_list_tile_data_model.dart'; import 'package:talawa/models/chats/chat_user.dart'; +/// function to check if the users are equal or not. +/// +/// **params**: +/// * `users1`: list of ChatUser +/// * `users2`: list of ChatUser +/// +/// **returns**: +/// None +void checkUsers(List? users1, List? users2) { + if (users1 == null || users2 == null) { + expect(users1, users2); + return; + } + + for (int index = 0; index < users1.length; index++) { + expect(users1[index].id, users2[index].id); + expect(users1[index].firstName, users2[index].firstName); + expect(users1[index].image, users2[index].image); + } +} + void main() { group('Test ChatListTileDataModel', () { const int listLength = 4; @@ -23,29 +41,16 @@ void main() { ); final Map json = { - '_id': '123', + 'id': '123', 'users': List.generate(listLength, (index) { return { - '_id': '$index', + 'id': '$index', 'firstName': 'First$index', 'image': 'Image$index', }; }), }; - void checkUsers(List? users1, List? users2) { - if (users1 == null || users2 == null) { - expect(users1, users2); - return; - } - - for (int index = 0; index < users1.length; index++) { - expect(users1[index].id, users2[index].id); - expect(users1[index].firstName, users2[index].firstName); - expect(users1[index].image, users2[index].image); - } - } - test('Test constructor', () { expect(chatListTileDataModel.id, '123'); checkUsers(chatListTileDataModel.users, users); @@ -65,7 +70,7 @@ void main() { test('Test toJson', () { final Map json = chatListTileDataModel.toJson(); - expect(json['_id'], chatListTileDataModel.id); + expect(json['id'], chatListTileDataModel.id); checkUsers(json['users'] as List, chatListTileDataModel.users); }); }); diff --git a/test/model_tests/chat/chat_user_test.dart b/test/model_tests/chat/chat_user_test.dart index f6a4793a1..50430f830 100644 --- a/test/model_tests/chat/chat_user_test.dart +++ b/test/model_tests/chat/chat_user_test.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - import 'package:flutter_test/flutter_test.dart'; import 'package:talawa/models/chats/chat_user.dart'; @@ -13,7 +10,7 @@ void main() { ); final chatUserJson = { 'firstName': 'Ayush', - '_id': null, + 'id': null, 'image': 'random image url', }; test('Test json to model', () { diff --git a/test/model_tests/events/event_model_test.dart b/test/model_tests/events/event_model_test.dart index 689472c0c..2557fa085 100644 --- a/test/model_tests/events/event_model_test.dart +++ b/test/model_tests/events/event_model_test.dart @@ -21,15 +21,12 @@ void main() { title: 'for test only', description: 'for test only', location: 'for test only', - latitude: 1233, - longitude: 123, recurring: false, allDay: false, startDate: 'for test only', endDate: 'for test only', startTime: 'for test only', endTime: 'for test only', - recurrence: 'for test only', isPublic: true, isRegistered: true, isRegisterable: true, @@ -56,15 +53,12 @@ void main() { 'title': 'for test only', 'description': 'for test only', 'location': 'for test only', - 'latitude': 1233.00, - 'longitude': 123.00, 'recurring': false, 'allDay': false, 'startDate': 'for test only', 'endDate': 'for test only', 'startTime': 'for test only', 'endTime': 'for test only', - 'recurrence': 'for test only', 'isPublic': true, 'isRegistered': true, 'isRegisterable': true, @@ -119,14 +113,11 @@ void main() { ); expect(event.attendees?[0].image, eventFromJson.attendees?[0].image); expect(event.location, eventFromJson.location); - expect(event.latitude, eventFromJson.latitude); - expect(event.longitude, eventFromJson.longitude); expect(event.recurring, eventFromJson.recurring); expect(event.allDay, eventFromJson.allDay); expect(event.startDate, eventFromJson.startDate); expect(event.startTime, eventFromJson.startTime); expect(event.endTime, eventFromJson.endTime); - expect(event.recurrence, eventFromJson.recurrence); expect(event.isPublic, eventFromJson.isPublic); expect(event.isRegistered, eventFromJson.isRegistered); expect(event.isRegisterable, eventFromJson.isRegisterable); diff --git a/test/service_tests/event_service_test.dart b/test/service_tests/event_service_test.dart index 76c461bbd..9da3f0935 100644 --- a/test/service_tests/event_service_test.dart +++ b/test/service_tests/event_service_test.dart @@ -136,15 +136,12 @@ void main() { "title": "Sample Event", "description": "This is a sample event description.", "location": "Sample Location", - "longitude": -73.935242, - "latitude": 40.73061, "recurring": true, "allDay": false, "startDate": "2024-01-15", "endDate": "2024-01-16", "startTime": "10:00 AM", "endTime": "4:00 PM", - "recurrence": "Weekly", "isPublic": true, "isRegistered": true, "isRegisterable": true, diff --git a/test/view_model_tests/after_auth_view_model_tests/event_view_model_tests/create_event_view_model_test.dart b/test/view_model_tests/after_auth_view_model_tests/event_view_model_tests/create_event_view_model_test.dart index 7c444d6c2..cd683b6f8 100644 --- a/test/view_model_tests/after_auth_view_model_tests/event_view_model_tests/create_event_view_model_test.dart +++ b/test/view_model_tests/after_auth_view_model_tests/event_view_model_tests/create_event_view_model_test.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - import 'dart:io'; import 'package:flutter/material.dart'; @@ -21,12 +18,33 @@ import 'package:talawa/view_model/after_auth_view_models/event_view_models/creat import '../../../helpers/test_helpers.dart'; import '../../../helpers/test_locator.dart'; +/// `MockBuildContext` class represents mock instance of Build context. class MockBuildContext extends Mock implements BuildContext {} +/// Instance of callback function. class MockCallbackFunction extends Mock { + /// Callback function. + /// + /// **params**: + /// None + /// + /// **returns**: + /// None void call(); } +/// a_line_ending_with_end_punctuation. +/// +/// more_info_if_required +/// +/// **params**: +/// * `formKey`: define_the_param +/// * `eventTitleTextController`: define_the_param +/// * `eventLocationTextController`: define_the_param +/// * `eventDescriptionTextController`: define_the_param +/// +/// **returns**: +/// * `Widget`: define_the_return Widget createApp( GlobalKey formKey, TextEditingController eventTitleTextController, @@ -127,9 +145,9 @@ void main() { model.eventStartTime.minute, ); final DateTime endMoment = DateTime( - model.eventEndDate!.year, - model.eventEndDate!.month, - model.eventEndDate!.day, + model.eventEndDate.year, + model.eventEndDate.month, + model.eventEndDate.day, model.eventEndTime.hour, model.eventEndTime.minute, ); @@ -169,16 +187,177 @@ void main() { 'location': model.eventLocationTextController.text, 'isPublic': model.isPublicSwitch, 'isRegisterable': model.isRegisterableSwitch, - 'recurring': true, - 'recurrance': 'WEEKLY', + 'recurring': model.isRecurring, 'allDay': true, - 'startTime': '${DateFormat('HH:mm:ss').format(startMoment)}Z', - 'endTime': '${DateFormat('HH:mm:ss').format(endMoment)}Z', + 'startTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(startMoment)}Z', + 'endTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(endMoment)}Z', }, - 'recurrenceRuleData': { - 'frequency': 'WEEKLY', - 'weekDays': ['TU'], + if (model.isRecurring) + 'recurrenceRuleData': { + 'recurrenceStartDate': + DateFormat('yyyy-MM-dd').format(model.recurrenceStartDate), + 'recurrenceEndDate': model.recurrenceEndDate != null + ? DateFormat('yyyy-MM-dd').format(model.recurrenceEndDate!) + : null, + 'frequency': model.frequency, + 'weekDays': (model.frequency == Frequency.weekly || + (model.frequency == Frequency.monthly && + model.weekDayOccurenceInMonth != null)) + ? model.weekDays.toList() + : null, + 'interval': model.interval, + 'count': model.count, + 'weekDayOccurenceInMonth': model.weekDayOccurenceInMonth, + }, + }, + ), + ).thenAnswer((_) async { + return true; + }); + + await model.createEvent(); + + verify( + databaseFunctions.gqlAuthMutation( + EventQueries().addEvent(), + variables: { + 'data': { + 'startDate': DateFormat('yyyy-MM-dd').format(startMoment), + 'endDate': DateFormat('yyyy-MM-dd').format(endMoment), + 'organizationId': 'XYZ', + 'title': model.eventTitleTextController.text, + 'description': model.eventDescriptionTextController.text, + 'location': model.eventLocationTextController.text, + 'isPublic': model.isPublicSwitch, + 'isRegisterable': model.isRegisterableSwitch, + 'recurring': model.isRecurring, + 'allDay': true, + 'startTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(startMoment)}Z', + 'endTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(endMoment)}Z', }, + if (model.isRecurring) + 'recurrenceRuleData': { + 'recurrenceStartDate': + DateFormat('yyyy-MM-dd').format(model.recurrenceStartDate), + 'recurrenceEndDate': model.recurrenceEndDate != null + ? DateFormat('yyyy-MM-dd').format(model.recurrenceEndDate!) + : null, + 'frequency': model.frequency, + 'weekDays': (model.frequency == Frequency.weekly || + (model.frequency == Frequency.monthly && + model.weekDayOccurenceInMonth != null)) + ? model.weekDays.toList() + : null, + 'interval': model.interval, + 'count': model.count, + 'weekDayOccurenceInMonth': model.weekDayOccurenceInMonth, + }, + }, + ), + ); + + verify(navigationService.pop()); + }); + + testWidgets("testing createEvent function (Recurring)", (tester) async { + final model = CreateEventViewModel(); + model.initialize(); + await tester.pumpWidget( + createApp( + model.formKey, + model.eventTitleTextController, + model.eventLocationTextController, + model.eventDescriptionTextController, + ), + ); + + final DateTime startMoment = DateTime( + model.eventStartDate.year, + model.eventStartDate.month, + model.eventStartDate.day, + model.eventStartTime.hour, + model.eventStartTime.minute, + ); + + final DateTime endMoment = DateTime( + model.eventEndDate.year, + model.eventEndDate.month, + model.eventEndDate.day, + model.eventEndTime.hour, + model.eventEndTime.minute, + ); + + model.isRecurring = true; + + await tester.pump(); + await tester.pumpAndSettle(); + + await tester.enterText( + find.byType(TextFormField).first, + 'fakeEventTitle', + ); + await tester.enterText( + find.byType(TextFormField).last, + 'fakeEventDescription', + ); + await tester.enterText( + find.byType(TextFormField).at(1), + 'fakeEventLocation', + ); + databaseFunctions.init(); + + when(databaseFunctions.refreshAccessToken("testtoken")) + .thenAnswer((realInvocation) async { + return true; + }); + + when( + databaseFunctions.gqlAuthMutation( + EventQueries().addEvent(), + variables: { + 'data': { + 'startDate': DateFormat('yyyy-MM-dd').format(startMoment), + 'endDate': DateFormat('yyyy-MM-dd').format(endMoment), + 'organizationId': 'XYZ', + 'title': model.eventTitleTextController.text, + 'description': model.eventDescriptionTextController.text, + 'location': model.eventLocationTextController.text, + 'isPublic': model.isPublicSwitch, + 'isRegisterable': model.isRegisterableSwitch, + 'recurring': model.isRecurring, + 'allDay': true, + 'startTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(startMoment)}Z', + 'endTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(endMoment)}Z', + }, + if (model.isRecurring) + 'recurrenceRuleData': { + 'recurrenceStartDate': + DateFormat('yyyy-MM-dd').format(model.recurrenceStartDate), + 'recurrenceEndDate': model.recurrenceEndDate != null + ? DateFormat('yyyy-MM-dd').format(model.recurrenceEndDate!) + : null, + 'frequency': model.frequency, + 'weekDays': (model.frequency == Frequency.weekly || + (model.frequency == Frequency.monthly && + model.weekDayOccurenceInMonth != null)) + ? model.weekDays.toList() + : null, + 'interval': model.interval, + 'count': model.count, + 'weekDayOccurenceInMonth': model.weekDayOccurenceInMonth, + }, }, ), ).thenAnswer((_) async { @@ -200,16 +379,83 @@ void main() { 'location': model.eventLocationTextController.text, 'isPublic': model.isPublicSwitch, 'isRegisterable': model.isRegisterableSwitch, - 'recurring': true, - 'recurrance': 'WEEKLY', + 'recurring': model.isRecurring, 'allDay': true, - 'startTime': '${DateFormat('HH:mm:ss').format(startMoment)}Z', - 'endTime': '${DateFormat('HH:mm:ss').format(endMoment)}Z', + 'startTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(startMoment)}Z', + 'endTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(endMoment)}Z', }, - 'recurrenceRuleData': { - 'frequency': 'WEEKLY', - 'weekDays': ['TU'], + if (model.isRecurring) + 'recurrenceRuleData': { + 'recurrenceStartDate': + DateFormat('yyyy-MM-dd').format(model.recurrenceStartDate), + 'recurrenceEndDate': model.recurrenceEndDate != null + ? DateFormat('yyyy-MM-dd').format(model.recurrenceEndDate!) + : null, + 'frequency': model.frequency, + 'weekDays': (model.frequency == Frequency.weekly || + (model.frequency == Frequency.monthly && + model.weekDayOccurenceInMonth != null)) + ? model.weekDays.toList() + : null, + 'interval': model.interval, + 'count': model.count, + 'weekDayOccurenceInMonth': model.weekDayOccurenceInMonth, + }, + }, + ), + ); + + verify(navigationService.pop()); + + model.recurrenceEndDate = DateTime.now(); + model.frequency = Frequency.monthly; + model.weekDayOccurenceInMonth = 1; + + await model.createEvent(); + + verify( + databaseFunctions.gqlAuthMutation( + EventQueries().addEvent(), + variables: { + 'data': { + 'startDate': DateFormat('yyyy-MM-dd').format(startMoment), + 'endDate': DateFormat('yyyy-MM-dd').format(endMoment), + 'organizationId': 'XYZ', + 'title': model.eventTitleTextController.text, + 'description': model.eventDescriptionTextController.text, + 'location': model.eventLocationTextController.text, + 'isPublic': model.isPublicSwitch, + 'isRegisterable': model.isRegisterableSwitch, + 'recurring': model.isRecurring, + 'allDay': true, + 'startTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(startMoment)}Z', + 'endTime': model.isAllDay + ? null + : '${DateFormat('HH:mm:ss').format(endMoment)}Z', }, + if (model.isRecurring) + 'recurrenceRuleData': { + 'recurrenceStartDate': + DateFormat('yyyy-MM-dd').format(model.recurrenceStartDate), + 'recurrenceEndDate': model.recurrenceEndDate != null + ? DateFormat('yyyy-MM-dd').format(model.recurrenceEndDate!) + : null, + 'frequency': model.frequency, + 'weekDays': (model.frequency == Frequency.weekly || + (model.frequency == Frequency.monthly && + model.weekDayOccurenceInMonth != null)) + ? model.weekDays.toList() + : null, + 'interval': model.interval, + 'count': model.count, + 'weekDayOccurenceInMonth': model.weekDayOccurenceInMonth, + }, }, ), ); @@ -313,12 +559,19 @@ void main() { expect(isMemberFound, false); }); - test('getRecurrence method', () { + test('setEventEndDate should set the event end date and notify listeners', + () { final model = CreateEventViewModel(); model.initialize(); - expect(model.getRecurrance(Recurrance.monthly), 'MONTHLY'); - expect(model.getRecurrance(Recurrance.yearly), 'YEARLY'); - expect(model.getRecurrance(Recurrance.once), null); + + final newDate = DateTime.now().add(const Duration(days: 1)); + final notifyListenerCallback = MockCallbackFunction(); + model.addListener(notifyListenerCallback); + + model.setEventEndDate(newDate); + + expect(model.eventEndDate, newDate); + verify(notifyListenerCallback()).called(1); }); }); } diff --git a/test/view_model_tests/after_auth_view_model_tests/event_view_model_tests/explore_events_view_model_test.dart b/test/view_model_tests/after_auth_view_model_tests/event_view_model_tests/explore_events_view_model_test.dart index 7c157e70b..294e7e79a 100644 --- a/test/view_model_tests/after_auth_view_model_tests/event_view_model_tests/explore_events_view_model_test.dart +++ b/test/view_model_tests/after_auth_view_model_tests/event_view_model_tests/explore_events_view_model_test.dart @@ -75,7 +75,6 @@ void main() { endDate: '2024-01-14', startTime: '08:01:00.000Z', endTime: '08:50:00.000Z', - recurrence: "none", creator: User(id: 'Test Id'), isPublic: true, isRegistered: true, diff --git a/test/views/after_auth_screens/events/create_custom_recurring_event_test.dart b/test/views/after_auth_screens/events/create_custom_recurring_event_test.dart index b4997ad42..9043883d4 100644 --- a/test/views/after_auth_screens/events/create_custom_recurring_event_test.dart +++ b/test/views/after_auth_screens/events/create_custom_recurring_event_test.dart @@ -21,6 +21,7 @@ import '../../../helpers/test_locator.dart'; /// **params**: /// * `themeMode`: ThemeMode /// * `theme`: ThemeData of App +/// * `model`: CreateEventViewModel /// /// **returns**: /// * `Widget`: Event Screen Widget @@ -65,6 +66,125 @@ void main() { // tearDown(() { // unregisterServices(); // }); + + group("RecurrenceUtils", () { + test("getRecurrenceRuleText for Frequency Daily", () { + final ruleText = RecurrenceUtils.getRecurrenceRuleText( + Frequency.daily, + null, + 2, + null, + null, + DateTime(2024, 1, 1), + null, + ); + expect(ruleText, 'Every 2 days'); + + final ruleText2 = RecurrenceUtils.getRecurrenceRuleText( + Frequency.daily, + null, + 1, + null, + null, + DateTime(2024, 1, 1), + null, + ); + expect(ruleText2, 'Daily'); + }); + + test("getRecurrenceRuleText for Frequency Weekly", () { + final ruleText = RecurrenceUtils.getRecurrenceRuleText( + Frequency.weekly, + {'MONDAY'}, + 2, + null, + null, + DateTime(2024, 1, 1), + null, + ); + expect(ruleText, 'Every 2 weeks on Monday'); + + final ruleText2 = RecurrenceUtils.getRecurrenceRuleText( + Frequency.weekly, + {'MONDAY'}, + 1, + null, + null, + DateTime(2024, 1, 1), + null, + ); + expect(ruleText2, 'Weekly on Monday'); + }); + + test("getRecurrenceRuleText for Frequency Monthly", () { + final ruleText = RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + null, + 2, + null, + 2, + DateTime(2024, 1, 1), + null, + ); + expect(ruleText, 'Every 2 months on Second Monday'); + + final ruleText2 = RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + null, + 1, + null, + null, + DateTime(2024, 1, 1), + null, + ); + expect(ruleText2, 'Monthly on Day 1'); + }); + + test("getRecurrenceRuleText for Frequency Yearly", () { + final ruleText = RecurrenceUtils.getRecurrenceRuleText( + Frequency.yearly, + null, + 3, + null, + null, + DateTime(2024, 1, 1), + null, + ); + expect(ruleText, 'Every 3 years on January 1'); + + final ruleText2 = RecurrenceUtils.getRecurrenceRuleText( + Frequency.yearly, + null, + 1, + null, + null, + DateTime(2024, 1, 1), + null, + ); + expect(ruleText2, 'Annually on January 1'); + }); + + test('getWeekDaysString formats string correctly', () { + final weekDaysString = RecurrenceUtils.getWeekDaysString([ + WeekDays.monday, + WeekDays.tuesday, + WeekDays.wednesday, + ]); + expect(weekDaysString, 'Monday, Tuesday & Wednesday'); + }); + + test('isLastOccurenceOfWeekDay returns true for last occurrence', () { + final date = DateTime(2022, 1, 31); // Last Monday of January 2022 + final isLastOccurrence = RecurrenceUtils.isLastOccurenceOfWeekDay(date); + expect(isLastOccurrence, true); + }); + + test('isLastOccurenceOfWeekDay returns false for not last occurrence', () { + final date = DateTime(2022, 1, 24); // Not the last Monday of January 2022 + final isLastOccurrence = RecurrenceUtils.isLastOccurenceOfWeekDay(date); + expect(isLastOccurrence, false); + }); + }); group('Test custom recurrence page.', () { testWidgets('Appbar is being rendered as expected.', (tester) async { await tester.pumpWidget( @@ -72,17 +192,11 @@ void main() { theme: TalawaTheme.darkTheme, ), ); - await tester.pump(); - + await tester.pumpAndSettle(); final appBarFinder = find.byType(AppBar); - // verify if AppBar renders. expect(appBarFinder, findsOne); - - // Verify if the AppBar renders with the correct title expect(find.text('Custom recurrence'), findsOneWidget); - - // Verify if the Done button is present expect(find.text('Done'), findsOneWidget); }); @@ -93,9 +207,7 @@ void main() { ), ); await tester.pump(); - final customDividerFinder = find.byType(Divider); - expect(customDividerFinder, findsNWidgets(2)); }); @@ -127,10 +239,12 @@ void main() { expect(customTextFieldFinder, findsNWidgets(2)); }); - testWidgets('section1InputFields', (tester) async { + testWidgets('Check functionality of Interval Frequency', (tester) async { + final model = CreateEventViewModel(); await tester.pumpWidget( createCustomRecurrenceScreen( theme: TalawaTheme.darkTheme, + model: model, ), ); await tester.pump(); @@ -153,7 +267,7 @@ void main() { // Test RecurrenceFrequencyDropdown expect(find.text("week"), findsOne); - final popupMenuButton = find.byType(PopupMenuButton); + final popupMenuButton = find.byType(PopupMenuButton).first; await tester.tap(popupMenuButton); await tester.pumpAndSettle(); @@ -162,26 +276,138 @@ void main() { expect(find.text("month"), findsOne); expect(find.text("year"), findsOne); - // Test dropdown selection working. - await tester.tap(find.text("month")); + // check functionality of interval frequency day + await tester.tap(find.text(EventIntervals.daily)); + await tester.pumpAndSettle(); + + expect(model.frequency, Frequency.daily); + expect(model.recurrenceInterval, EventIntervals.daily); + expect(model.weekDayOccurenceInMonth, null); + expect(model.weekDays, []); + + // check functionality of interval frequency week + await tester.tap(popupMenuButton); + await tester.pumpAndSettle(); + await tester.tap(find.text(EventIntervals.weekly)); + await tester.pumpAndSettle(); + + expect(model.frequency, Frequency.weekly); + expect(model.recurrenceInterval, EventIntervals.weekly); + expect(model.weekDayOccurenceInMonth, null); + expect(model.weekDays, []); + + // check functionality of interval frequency week + await tester.tap(popupMenuButton); await tester.pumpAndSettle(); + await tester.tap(find.text(EventIntervals.monthly)); + await tester.pumpAndSettle(); + + expect(model.frequency, Frequency.monthly); + expect(model.recurrenceInterval, EventIntervals.monthly); + expect(model.weekDayOccurenceInMonth, null); + expect(model.weekDays, []); + + // check functionality of interval frequency year + await tester.tap(popupMenuButton); + await tester.pumpAndSettle(); + await tester.tap(find.text(EventIntervals.yearly)); + await tester.pumpAndSettle(); + + expect(model.frequency, Frequency.yearly); + expect(model.recurrenceInterval, EventIntervals.yearly); + expect(model.weekDayOccurenceInMonth, null); + expect(model.weekDays, []); + }); + + testWidgets("Dropdown for Monthly frequency", (tester) async { + final model = CreateEventViewModel(); + model.recurrenceStartDate = DateTime(2023, 02, 28); + await tester.pumpWidget( + createCustomRecurrenceScreen( + theme: TalawaTheme.darkTheme, + model: model, + ), + ); + await tester.pump(); + final frequencyDropdown = find.byType(PopupMenuButton).first; + await tester.tap(frequencyDropdown); + await tester.pumpAndSettle(); + + expect(find.text("day"), findsOne); expect(find.text("month"), findsOne); + expect(find.text("year"), findsOne); + + // select interval frequency as month + await tester.tap(find.text("month")); + await tester.pumpAndSettle(); - // check if monthly recurrence dropdown shows up. - await tester.tap(find.text('Monthly on day 3')); + await tester.tap(find.byType(PopupMenuButton).last); await tester.pumpAndSettle(); - await tester.tap(find.text('Monthly on day 3').last); + + // check all the functionality of dropdown option shown on isLastDayOccurence is true (monthlyOption[2]) + final monthlyRecurrenceLabel1 = RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + { + RecurrenceUtils.weekDays[model.recurrenceStartDate.weekday - 1], + }, + int.parse(model.repeatsEveryCountController.text), + int.parse(model.endOccurenceController.text), + -1, + model.recurrenceStartDate, + model.recurrenceEndDate, + ); + expect( + find.text(monthlyRecurrenceLabel1), + findsOne, + ); + + await tester.tap(find.text(monthlyRecurrenceLabel1)); await tester.pumpAndSettle(); - // check if interval UI's middle part dissappears - // when clicked on day/year. - await tester.tap(find.text('month')); + expect(model.frequency, Frequency.monthly); + expect(model.recurrenceLabel, monthlyRecurrenceLabel1); + expect(model.weekDayOccurenceInMonth, -1); + expect(model.weekDays, { + RecurrenceUtils.weekDays[model.recurrenceStartDate.weekday - 1], + }); + + // check all the functionality of dropdown option shown when weekDayOccurence~=5 (monthlyOption[1]) + await tester.tap(find.byType(PopupMenuButton).last); await tester.pumpAndSettle(); - await tester.tap(find.text('year')); + + final monthlyRecurrenceLabel2 = RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + { + RecurrenceUtils.weekDays[model.recurrenceStartDate.weekday - 1], + }, + int.parse(model.repeatsEveryCountController.text), + int.parse(model.endOccurenceController.text), + RecurrenceUtils.getWeekDayOccurenceInMonth( + model.recurrenceStartDate, + ), + model.recurrenceStartDate, + model.recurrenceEndDate, + ); + expect( + find.text(monthlyRecurrenceLabel2), + findsOne, + ); + + await tester.tap(find.text(monthlyRecurrenceLabel2)); await tester.pumpAndSettle(); - expect(find.text('Monthly on day 3'), findsNothing); + expect(model.frequency, Frequency.monthly); + expect(model.recurrenceLabel, monthlyRecurrenceLabel2); + expect( + model.weekDayOccurenceInMonth, + RecurrenceUtils.getWeekDayOccurenceInMonth( + model.recurrenceStartDate, + ), + ); + expect(model.weekDays, { + RecurrenceUtils.weekDays[model.recurrenceStartDate.weekday - 1], + }); }); testWidgets('CustomWeekDaySelector', (tester) async { @@ -191,10 +417,8 @@ void main() { await tester.pumpWidget(widget); await tester.pump(); - // Test widget rendering. expect(find.byType(CustomWeekDaySelector), findsOne); - // Test widget functionality. await tester.tap(find.text("M")); await tester.pumpAndSettle(); await tester.tap(find.text("W")); @@ -250,13 +474,13 @@ void main() { expect(find.byType(DatePickerDialog), findsNothing); - await tester.tap(find.text('Never')); + await tester.tap(find.text('never')); await tester.pumpAndSettle(); expect(model.eventEndType, EventEndTypes.never); - expect(model.eventEndDate, null); + expect(model.recurrenceEndDate, null); - await tester.tap(find.text('On')); + await tester.tap(find.text('on')); await tester.pumpAndSettle(); expect(model.eventEndType, EventEndTypes.on); @@ -274,17 +498,15 @@ void main() { await tester.tap(find.text('Done')); await tester.pumpAndSettle(); - expect(model.eventEndDate, null); + expect(model.recurrenceEndDate, null); - await tester.tap(find.text('On')); + await tester.tap(find.text('on')); await tester.pumpAndSettle(); await tester.tap(find.text('Done')); await tester.pumpAndSettle(); - expect(model.eventEndDate, model.eventEndOnEndDate); - - await tester.tap(find.text('After')); + await tester.tap(find.text('after')); await tester.pumpAndSettle(); final afterTextField = find.byType(CustomTextField).last; diff --git a/test/views/after_auth_screens/events/create_event_page_test.dart b/test/views/after_auth_screens/events/create_event_page_test.dart index 5d5f5eb7a..c947afb14 100644 --- a/test/views/after_auth_screens/events/create_event_page_test.dart +++ b/test/views/after_auth_screens/events/create_event_page_test.dart @@ -1,5 +1,4 @@ import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -21,11 +20,22 @@ import '../../../helpers/test_helpers.dart'; import '../../../helpers/test_locator.dart'; import '../../../widget_tests/after_auth_screens/events/create_event_form_test.dart'; +/// Mock class instance of CallbackFunction. class MockCallbackFunction extends Mock { + /// Mock function call. + /// + /// **params**: + /// None + /// + /// **returns**: + /// None void call(); } +/// mock instance for setDateCallback. final setDateCallback = MockCallbackFunction(); + +/// mock instance for setTimeCallback. final setTimeCallback = MockCallbackFunction(); /// Creates a EventScreen for tests. @@ -79,7 +89,7 @@ void main() { tearDown(() { unregisterServices(); }); - group('testing', () { + group('testing -> CreateEventPage', () { testWidgets("Checking tap Inkwell for setDate 1 datetime", (tester) async { await tester.pumpWidget( createEventScreen( @@ -87,25 +97,26 @@ void main() { theme: TalawaTheme.darkTheme, ), ); + + await tester.pumpAndSettle(); + + final switches = find.descendant( + of: find.byType(Row), + matching: find.byType(Switch), + ); + expect(switches, findsNWidgets(3)); + expect((tester.widgetList(switches).toList()[0] as Switch).value, true); + await tester.ensureVisible(switches.at(0)); + await tester.tap(switches.at(1)); + await tester.pump(); final inkwellFinder = find.byType(InkWell); expect(inkwellFinder, findsNWidgets(8)); - // tester.allElements.forEach((element) { - // print(element); - // }); - ///returning the file variable to the - ///result of function multimediaPickerService.getPhotoFromGallery - ///when this function is called in the - ///view model of add_post_page. final file = File('fakePath'); - /// using the new instance of multimediaPickerService - /// so that when statement can be used again, - /// else it gives null point exception final multimediaPickerService = locator(); - /// when is function provided by mockito lib when(multimediaPickerService.getPhotoFromGallery(camera: false)) .thenAnswer((_) async { return file; @@ -122,22 +133,25 @@ void main() { theme: TalawaTheme.darkTheme, ), ); + await tester.pumpAndSettle(); + + final switches = find.descendant( + of: find.byType(Row), + matching: find.byType(Switch), + ); + expect(switches, findsNWidgets(3)); + expect((tester.widgetList(switches).toList()[0] as Switch).value, true); + await tester.ensureVisible(switches.at(0)); + await tester.tap(switches.at(1)); + await tester.pump(); final inkwellFinder = find.byType(InkWell); expect(inkwellFinder, findsNWidgets(8)); - ///returning the file variable to the - ///result of function multimediaPickerService.getPhotoFromGallery - ///when this function is called in the - ///view model of add_post_page. final file = File('fakePath'); - /// using the new instance of multimediaPickerService - /// so that when statement can be used again, - /// else it gives null point exception final multimediaPickerService = locator(); - /// when is function provided by mockito lib when(multimediaPickerService.getPhotoFromGallery(camera: false)) .thenAnswer((_) async { return file; @@ -174,31 +188,13 @@ void main() { ); await tester.pumpAndSettle(); - const List frequencies = [ - "Does not repeat", - "Every day", - "Every week", - "Every month", - "Every year", - "Custom...", - ]; - - String prev = frequencies[0]; - for (int i = 1; i < 5; i++) { - expect(find.text(prev), findsAny); - await tester.tap(find.text(prev)); - await tester.pumpAndSettle(); - await tester.tap(find.text(frequencies[i]).last); - await tester.pumpAndSettle(); - expect(find.byType(ShowRecurrenceDialog), findsNothing); - expect(find.text(frequencies[i]), findsOneWidget); - prev = frequencies[i]; - } + const String prev = "Does not repeat"; expect(find.text(prev), findsAny); + await tester.ensureVisible(find.byKey(const Key("inkwell_recurrLabel"))); await tester.tap(find.text(prev)); await tester.pumpAndSettle(); - await tester.tap(find.text(frequencies[5])); + await tester.tap(find.text("Custom...")); await tester.pumpAndSettle(); expect(find.byType(ShowRecurrenceDialog), findsNothing); @@ -211,22 +207,26 @@ void main() { theme: TalawaTheme.darkTheme, ), ); + + await tester.pumpAndSettle(); + + final switches = find.descendant( + of: find.byType(Row), + matching: find.byType(Switch), + ); + expect(switches, findsNWidgets(3)); + expect((tester.widgetList(switches).toList()[0] as Switch).value, true); + await tester.ensureVisible(switches.at(0)); + await tester.tap(switches.at(1)); + await tester.pump(); final inkwellFinder = find.byType(InkWell); expect(inkwellFinder, findsNWidgets(8)); - ///returning the file variable to the - ///result of function multimediaPickerService.getPhotoFromGallery - ///when this function is called in the - ///view model of add_post_page. final file = File('fakePath'); - /// using the new instance of multimediaPickerService - /// so that when statement can be used again, - /// else it gives null point exception final multimediaPickerService = locator(); - /// when is function provided by mockito lib when(multimediaPickerService.getPhotoFromGallery(camera: false)) .thenAnswer((_) async { return file; @@ -243,25 +243,26 @@ void main() { theme: TalawaTheme.darkTheme, ), ); + + await tester.pumpAndSettle(); + + final switches = find.descendant( + of: find.byType(Row), + matching: find.byType(Switch), + ); + expect(switches, findsNWidgets(3)); + expect((tester.widgetList(switches).toList()[0] as Switch).value, true); + await tester.ensureVisible(switches.at(0)); + await tester.tap(switches.at(1)); + await tester.pump(); final inkwellFinder = find.byType(InkWell); expect(inkwellFinder, findsNWidgets(8)); - // tester.allElements.forEach((element) { - // print(element); - // }); - ///returning the file variable to the - ///result of function multimediaPickerService.getPhotoFromGallery - ///when this function is called in the - ///view model of add_post_page. final file = File('fakePath'); - /// using the new instance of multimediaPickerService - /// so that when statement can be used again, - /// else it gives null point exception final multimediaPickerService = locator(); - /// when is function provided by mockito lib when(multimediaPickerService.getPhotoFromGallery(camera: false)) .thenAnswer((_) async { return file; @@ -281,25 +282,12 @@ void main() { ); await tester.pump(); - /// using the key of icon button - /// because their are many icon button - final finder = find.byKey(const Key('txt_btn_cep')); expect(finder, findsOneWidget); - - ///returning the file variable to the - ///result of function multimediaPickerService.getPhotoFromGallery - ///when this function is called in the - ///view model of add_post_page. final file = File('fakePath'); - - /// using the new instance of multimediaPickerService - /// so that when statement can be used again, - /// else it gives null point exception final multimediaPickerServices = locator(); - /// when is function provided by mockito lib when(multimediaPickerServices.getPhotoFromGallery()) .thenAnswer((_) async { return file; @@ -331,18 +319,10 @@ void main() { expect(finder, findsOneWidget); - ///returning the file variable to the - ///result of function multimediaPickerService.getPhotoFromGallery - ///when this function is called in the - ///view model of add_post_page. final file = File('fakePath'); - /// using the new instance of multimediaPickerService - /// so that when statement can be used again, - /// else it gives null point exception final multimediaPickerService = locator(); - /// when is function provided by mockito lib when(multimediaPickerService.getPhotoFromGallery(camera: false)) .thenAnswer((_) async { return file; @@ -509,6 +489,18 @@ void main() { theme: TalawaTheme.darkTheme, ), ); + + await tester.pumpAndSettle(); + + final switches = find.descendant( + of: find.byType(Row), + matching: find.byType(Switch), + ); + expect(switches, findsNWidgets(3)); + expect((tester.widgetList(switches).toList()[0] as Switch).value, true); + await tester.ensureVisible(switches.at(0)); + await tester.tap(switches.at(1)); + await tester.pump(); final inkwellFinder = find.byType(InkWell); expect(inkwellFinder, findsNWidgets(8)); @@ -558,6 +550,36 @@ void main() { findsNWidgets(2), ); }); + + testWidgets('Test end date selection', (tester) async { + await tester.pumpWidget( + createEventScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); + + await tester.pumpAndSettle(); + + final switches = find.descendant( + of: find.byType(Row), + matching: find.byType(Switch), + ); + expect(switches, findsNWidgets(3)); + expect((tester.widgetList(switches).toList()[0] as Switch).value, true); + await tester.ensureVisible(switches.at(0)); + await tester.tap(switches.at(1)); + + await tester.pump(); + + await tester.tap(find.byKey(const Key('EventDateTimeTileDate')).last); + await tester.pump(); + + await tester.tap(find.text('OK')); + await tester.pumpAndSettle(); + expect(find.text('Does not repeat'), findsOneWidget); + }); + testWidgets('Tap on DateTimeTile time', (tester) async { final currentTime = DateTime.now(); final futureTime = currentTime.add(const Duration(minutes: 30)); @@ -569,6 +591,16 @@ void main() { ); await tester.pumpAndSettle(); + final switches = find.descendant( + of: find.byType(Row), + matching: find.byType(Switch), + ); + expect(switches, findsNWidgets(3)); + expect((tester.widgetList(switches).toList()[0] as Switch).value, true); + await tester.ensureVisible(switches.at(0)); + await tester.tap(switches.at(1)); + + await tester.pump(); await tester.tap(find.byKey(const Key('EventDateTimeTileTime')).first); await tester.pump(); @@ -593,6 +625,18 @@ void main() { theme: TalawaTheme.darkTheme, ), ); + + await tester.pumpAndSettle(); + + final switches = find.descendant( + of: find.byType(Row), + matching: find.byType(Switch), + ); + expect(switches, findsNWidgets(3)); + expect((tester.widgetList(switches).toList()[0] as Switch).value, true); + await tester.ensureVisible(switches.at(0)); + await tester.tap(switches.at(1)); + await tester.pump(); await tester.tap(find.byKey(const Key('EventDateTimeTileDate')).last); @@ -622,6 +666,16 @@ void main() { ); await tester.pumpAndSettle(); + final switches = find.descendant( + of: find.byType(Row), + matching: find.byType(Switch), + ); + expect(switches, findsNWidgets(3)); + expect((tester.widgetList(switches).toList()[0] as Switch).value, true); + await tester.ensureVisible(switches.at(0)); + await tester.tap(switches.at(1)); + + await tester.pump(); await tester.tap(find.byKey(const Key('EventDateTimeTileTime')).last); await tester.pump(); @@ -642,11 +696,12 @@ void main() { }); group("Tests for integration with view model and services", () { - late final CreateEventViewModel cachedViewModel; + late final CreateEventViewModel cachedViewModel = + getAndRegisterCreateEventModel(); - testWidgets("setup MockCreateEventViewModel", (tester) async { - cachedViewModel = getAndRegisterCreateEventModel(); - }); + // testWidgets("setup MockCreateEventViewModel", (tester) async { + // cachedViewModel = getAndRegisterCreateEventModel(); + // }); testWidgets("Check if AppBar buttons work", (tester) async { mockNetworkImages(() async { @@ -709,12 +764,9 @@ void main() { await tester.ensureVisible(find.byKey(const Key('text_btn_ambs1'))); await tester.pumpAndSettle(); - await tester.tap(find.byType(CheckboxListTile)); - expect(cachedViewModel.memberCheckedMap['fakeUser1'], false); - await tester.pumpAndSettle(); - await tester.tap(find.byType(CheckboxListTile)); + await tester.tap(find.byType(CheckboxListTile).first); expect(cachedViewModel.memberCheckedMap['fakeUser1'], true); - await tester.pump(); + await tester.pumpAndSettle(); }); testWidgets( diff --git a/test/views/after_auth_screens/events/event_info_body_test.dart b/test/views/after_auth_screens/events/event_info_body_test.dart index e1804b6cf..b7b8b5bba 100644 --- a/test/views/after_auth_screens/events/event_info_body_test.dart +++ b/test/views/after_auth_screens/events/event_info_body_test.dart @@ -36,8 +36,6 @@ Event getTestEvent({ startTime: "00:00", endTime: "24:00", location: "iitbhu, varanasi", - latitude: viewOnMap ? 40.730610 : null, - longitude: viewOnMap ? -73.935242 : null, description: "test_event_description", admins: [ User( @@ -57,7 +55,6 @@ Event getTestEvent({ ), ], isRegisterable: true, - isRegistered: false, ); } diff --git a/test/views/after_auth_screens/profile/user_event_test.dart b/test/views/after_auth_screens/profile/user_event_test.dart index 0d39be40d..c210b26eb 100644 --- a/test/views/after_auth_screens/profile/user_event_test.dart +++ b/test/views/after_auth_screens/profile/user_event_test.dart @@ -139,15 +139,12 @@ void main() { title: 'Sample Event', description: 'This is a fake event description.', location: 'City Park', - latitude: 40.7128, - longitude: -74.0060, recurring: false, allDay: false, startDate: '2022-01-01', endDate: '2022-01-02', startTime: '12:00 PM', endTime: '3:00 PM', - recurrence: 'Weekly', isPublic: true, isRegistered: false, isRegisterable: true, diff --git a/test/widget_tests/after_auth_screens/app_settings/app_setting_page_test.dart b/test/widget_tests/after_auth_screens/app_settings/app_setting_page_test.dart index 482866e45..9fb34de38 100644 --- a/test/widget_tests/after_auth_screens/app_settings/app_setting_page_test.dart +++ b/test/widget_tests/after_auth_screens/app_settings/app_setting_page_test.dart @@ -267,7 +267,7 @@ Future main() async { await tester.tap(find.byKey(const Key('Logout'))); await tester.pumpAndSettle(); - final logoutButton = find.textContaining('LogOut'); + final logoutButton = find.textContaining('Logout').last; await tester.tap(logoutButton); unregisterServices(); @@ -299,7 +299,7 @@ Future main() async { await tester.tap(find.byKey(const Key('Logout'))); await tester.pumpAndSettle(); - final logoutButton = find.textContaining('LogOut'); + final logoutButton = find.textContaining('Logout').last; await tester.tap(logoutButton); }); @@ -314,7 +314,7 @@ Future main() async { await tester.tap(find.byKey(const Key('Logout'))); await tester.pumpAndSettle(); - final logoutButton = find.textContaining('LogOut'); + final logoutButton = find.textContaining('Logout').last; await tester.tap(logoutButton); verify(navigationService.pop()); diff --git a/test/widget_tests/after_auth_screens/events/create_event_page_test.dart b/test/widget_tests/after_auth_screens/events/create_event_page_test.dart index 55e8f5d6f..c3a33cbf5 100644 --- a/test/widget_tests/after_auth_screens/events/create_event_page_test.dart +++ b/test/widget_tests/after_auth_screens/events/create_event_page_test.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -16,6 +13,14 @@ import 'package:talawa/views/base_view.dart'; import 'package:talawa/widgets/event_date_time_tile.dart'; import 'package:talawa/widgets/member_name_tile.dart'; +/// Creates an instance of CreateEventScreen with the given parameters. +/// +/// **params**: +/// * `themeMode`: ThemeMode of the app +/// * `theme`: ThemeData of the app +/// +/// **returns**: +/// * `Widget`: return the CreateEventScreen widget Widget createEventScreen({ ThemeMode themeMode = ThemeMode.light, required ThemeData theme, @@ -277,7 +282,7 @@ void main() { navigationService.navigatorKey.currentContext!, ); final textDesc = find.text( - appLocalization!.strictTranslate('Select Start Date and Time'), + appLocalization!.strictTranslate('Select Start Date'), ); final dateTimeTiles = find.byType(DateTimeTile); expect(textDesc, findsOneWidget); @@ -299,7 +304,7 @@ void main() { navigationService.navigatorKey.currentContext!, ); final textDesc = find.text( - appLocalization!.strictTranslate('Select End Date and Time'), + appLocalization!.strictTranslate('Select End Date'), ); final dateTimeTiles = find.byType(DateTimeTile); expect(textDesc, findsOneWidget); diff --git a/test/widget_tests/after_auth_screens/events/event_info_page_test.dart b/test/widget_tests/after_auth_screens/events/event_info_page_test.dart index 752b2506c..054f21ce9 100644 --- a/test/widget_tests/after_auth_screens/events/event_info_page_test.dart +++ b/test/widget_tests/after_auth_screens/events/event_info_page_test.dart @@ -1,8 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - -// ignore_for_file: avoid_positional_boolean_parameters - import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -19,6 +14,14 @@ import 'package:talawa/views/after_auth_screens/events/event_info_page.dart'; import '../../../helpers/test_helpers.dart'; +/// Creates an instance of Event with the given parameters. +/// +/// **params**: +/// * `isPublic`: event is public or not +/// * `isCreator`: user is creator of the event or not +/// +/// **returns**: +/// * `Event`: return the Event instance Event getEvent(bool isPublic, bool isCreator) { return Event( id: '1', @@ -37,8 +40,6 @@ Event getEvent(bool isPublic, bool isCreator) { ), startDate: '10000', endDate: '20000', - latitude: 23.45, - longitude: -23.45, admins: [ User( id: isCreator ? "xzy1" : "abc1", @@ -51,6 +52,14 @@ Event getEvent(bool isPublic, bool isCreator) { ); } +/// Creates an instance of EventInfoPage with the given parameters. +/// +/// **params**: +/// * `isPublic`: whether the event is public or not +/// * `isCreator`: whether the user is the creator of the event or not +/// +/// **returns**: +/// * `Widget`: return the EventInfoPage widget Widget createEventInfoPage(bool isPublic, bool isCreator) { return MaterialApp( localizationsDelegates: [ diff --git a/test/widget_tests/pre_auth_screens/set_url_page_test.dart b/test/widget_tests/pre_auth_screens/set_url_page_test.dart index 65db66041..fc9b6d16f 100644 --- a/test/widget_tests/pre_auth_screens/set_url_page_test.dart +++ b/test/widget_tests/pre_auth_screens/set_url_page_test.dart @@ -1,15 +1,8 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - -// import 'dart:io'; - import 'package:flutter/material.dart'; -// import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hive/hive.dart'; import 'package:mockito/mockito.dart'; -// import 'package:path_provider/path_provider.dart' as path; import 'package:provider/provider.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:talawa/constants/custom_theme.dart'; @@ -27,32 +20,18 @@ import 'package:talawa/widgets/rich_text.dart'; import '../../helpers/test_helpers.dart'; -Widget createSetUrlScreenLight({ThemeMode themeMode = ThemeMode.light}) => - MultiProvider( - providers: [ - ChangeNotifierProvider( - create: (_) => AppLanguage(isTest: true), - ), - ], - child: MaterialApp( - localizationsDelegates: [ - const AppLocalizationsDelegate(isTest: true), - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - key: const Key('Root'), - themeMode: themeMode, - theme: TalawaTheme.lightTheme, - home: const SetUrl( - key: Key('SetUrl'), - uri: 'null', - ), - navigatorKey: navigationService.navigatorKey, - onGenerateRoute: router.generateRoute, - ), - ); - -Widget createSetUrlScreenDark({ThemeMode themeMode = ThemeMode.dark}) => +/// Returns the SetUrlScreen widget. +/// +/// **params**: +/// * `themeMode`: ThemeMode of the app. +/// * `theme`: ThemeData of the app. +/// +/// **returns**: +/// * `Widget`: SetUrlScreen widget. +Widget createSetUrlScreen({ + ThemeMode themeMode = ThemeMode.light, + ThemeData? theme, +}) => MultiProvider( providers: [ ChangeNotifierProvider( @@ -67,6 +46,7 @@ Widget createSetUrlScreenDark({ThemeMode themeMode = ThemeMode.dark}) => ], key: const Key('Root'), themeMode: themeMode, + theme: theme ?? TalawaTheme.lightTheme, darkTheme: TalawaTheme.darkTheme, home: const SetUrl( key: Key('SetUrl'), @@ -101,7 +81,12 @@ Future main() async { group('Select Language Screen Widget Test in light mode', () { testWidgets("Testing if Select Language Screen shows up", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); //initializing the screenScaffold Finder @@ -121,7 +106,12 @@ Future main() async { testWidgets("Testing if icon button shows up", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); //initializing the logo Finder @@ -141,7 +131,12 @@ Future main() async { }); testWidgets("Testing if app logo shows up", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); //initializing the logo Finder @@ -161,7 +156,12 @@ Future main() async { testWidgets("Testing if custom rich text shows up", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); //initializing the custom rich text widget Finder @@ -214,7 +214,12 @@ Future main() async { }); testWidgets("Testing the Url Input text form field", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); //initializing the url input field widget Finder @@ -260,7 +265,12 @@ Future main() async { }); testWidgets("Testing change language button", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); //initializing the change language widget Finder @@ -283,7 +293,12 @@ Future main() async { }); testWidgets("Testing if login button works", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); //initializing the login button Finder @@ -295,7 +310,7 @@ Future main() async { expect( (tester.firstWidget(loginButtonWidget) as RaisedRoundedButton) .backgroundColor, - TalawaTheme.lightTheme.colorScheme.secondaryContainer, + TalawaTheme.lightTheme.colorScheme.tertiary, ); expect( (tester.firstWidget(loginButtonWidget) as RaisedRoundedButton) @@ -315,7 +330,12 @@ Future main() async { testWidgets("Check navigation to Login page when Login button is pressed.", (tester) async { - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pump(); expect(find.byKey(const Key('SetUrlScreenScaffold')), findsOneWidget); @@ -332,7 +352,12 @@ Future main() async { }); testWidgets("Testing if signup button works", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); //initializing the signup button Finder @@ -364,7 +389,12 @@ Future main() async { "Testing onFieldSubmitted in TextFormField by simulating keyboard hits", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); final formFinder = find.ancestor( @@ -382,7 +412,12 @@ Future main() async { }); testWidgets("Testing onTap in sign up button", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); final btnFinder = find.byKey(const Key('SignUpButton')); @@ -398,7 +433,12 @@ Future main() async { testWidgets("Testing onTap in Change Language Gesture Detector", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); final gestureDetectorFinder = find.byKey(const Key('ChangeLanguage')); @@ -425,7 +465,12 @@ Future main() async { group('Select Language Screen Widget Test in dark mode', () { testWidgets("Testing if Select Language Screen shows up", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); //initializing the screenScaffold Finder @@ -445,7 +490,12 @@ Future main() async { testWidgets("Testing if icon button shows up", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenLight()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.light, + theme: TalawaTheme.lightTheme, + ), + ); await tester.pumpAndSettle(); //initializing the logo Finder @@ -465,7 +515,12 @@ Future main() async { }); testWidgets("Check if QR button works", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); final iconButton = find.byIcon(Icons.qr_code_scanner); @@ -479,7 +534,12 @@ Future main() async { }); testWidgets("Testing if app logo shows up", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); //initializing the logo Finder @@ -498,7 +558,12 @@ Future main() async { }); testWidgets("Testing if custom rich text shows up", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); //initializing the custom rich text widget Finder @@ -551,7 +616,12 @@ Future main() async { }); testWidgets("Testing the Url Input text form field", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); //initializing the url input field widget Finder @@ -597,7 +667,12 @@ Future main() async { }); testWidgets("Testing change language button", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); //initializing the change language widget Finder @@ -620,7 +695,12 @@ Future main() async { }); testWidgets("Testing if login button works", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); //initializing the login button Finder @@ -632,7 +712,7 @@ Future main() async { expect( (tester.firstWidget(loginButtonWidget) as RaisedRoundedButton) .backgroundColor, - TalawaTheme.darkTheme.colorScheme.secondaryContainer, + TalawaTheme.darkTheme.colorScheme.tertiary, ); expect( (tester.firstWidget(loginButtonWidget) as RaisedRoundedButton) @@ -652,7 +732,12 @@ Future main() async { }); testWidgets("Testing if signup button works", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); //initializing the signup button Finder @@ -684,7 +769,12 @@ Future main() async { "Testing onFieldSubmitted in TextFormField by simulating keyboard hits", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); final formFinder = find.ancestor( @@ -702,7 +792,12 @@ Future main() async { }); testWidgets("Testing onTap in sign up button", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); final btnFinder = find.byKey(const Key('SignUpButton')); @@ -718,7 +813,12 @@ Future main() async { testWidgets("Testing onTap in Change Language Gesture Detector", (tester) async { //pushing setUrlScreen - await tester.pumpWidget(createSetUrlScreenDark()); + await tester.pumpWidget( + createSetUrlScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); await tester.pumpAndSettle(); final gestureDetectorFinder = find.byKey(const Key('ChangeLanguage')); diff --git a/test/widget_tests/widgets/event_date_time_tile_test.dart b/test/widget_tests/widgets/event_date_time_tile_test.dart index 7e7291f70..4fb0c6e0d 100644 --- a/test/widget_tests/widgets/event_date_time_tile_test.dart +++ b/test/widget_tests/widgets/event_date_time_tile_test.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -10,17 +7,36 @@ import 'package:talawa/widgets/event_date_time_tile.dart'; import '../../helpers/test_helpers.dart'; import '../../helpers/test_locator.dart'; +/// Instance of callback function. class MockCallbackFunction extends Mock { + /// Callback function. + /// + /// **params**: + /// None + /// + /// **returns**: + /// None void call(); } +/// A callback function to set date. final setDateCallback = MockCallbackFunction(); + +/// A callback function to set time. final setTimeCallback = MockCallbackFunction(); +/// Creates a widget for testing. +/// +/// **params**: +/// None +/// +/// **returns**: +/// * `Widget`: Returns mocked Material App Widget Widget createWidget() { return MaterialApp( home: Scaffold( body: DateTimeTile( + isAllDay: false, date: "fakeDate", time: "fakeTime", setDate: setDateCallback, diff --git a/test/widget_tests/widgets/post_widget_test.dart b/test/widget_tests/widgets/post_widget_test.dart index 20f266928..5064449a9 100644 --- a/test/widget_tests/widgets/post_widget_test.dart +++ b/test/widget_tests/widgets/post_widget_test.dart @@ -1,12 +1,8 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - -// import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; +import 'package:network_image_mock/network_image_mock.dart'; import 'package:talawa/constants/custom_theme.dart'; import 'package:talawa/locator.dart'; import 'package:talawa/models/post/post_model.dart'; @@ -19,15 +15,27 @@ import 'package:talawa/widgets/custom_avatar.dart'; import 'package:talawa/widgets/multi_reaction.dart'; import 'package:talawa/widgets/post_container.dart'; import 'package:talawa/widgets/post_detailed_page.dart'; +import 'package:talawa/widgets/post_modal.dart'; import 'package:talawa/widgets/post_widget.dart'; // import 'package:talawa/widgets/video_widget.dart'; // import 'package:visibility_detector/visibility_detector.dart'; import '../../helpers/test_helpers.dart'; +/// Key for NewsPost widget. const Key newsPostKey = Key("newsPostKey"); + +/// Key for PostContainer widget. const Key postContainerKey = Key("postContainerKey"); +/// [createNewsPostWidget] is a method that returns a NewsPost widget. +/// +/// **params**: +/// * `function`: Function(Post)? - function to be called when the post comment is clicked. +/// * `post`: Post object containing all the data related to the post. +/// +/// **returns**: +/// * `Widget`: NewsPost widget. Widget createNewsPostWidget([Function(Post)? function, Post? post]) { return MaterialApp( locale: const Locale('en'), @@ -48,26 +56,6 @@ Widget createNewsPostWidget([Function(Post)? function, Post? post]) { ); } -Widget createPostContainerWidget() { - return MaterialApp( - locale: const Locale('en'), - localizationsDelegates: [ - const AppLocalizationsDelegate(isTest: true), - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - themeMode: ThemeMode.light, - theme: TalawaTheme.lightTheme, - home: const Scaffold( - body: PostContainer( - key: postContainerKey, - photoUrl: - "https://dcblog.b-cdn.net/wp-content/uploads/2021/02/Full-form-of-URL-1-1024x824.jpg", - ), - ), - ); -} - void main() { SizeConfig().test(); locator.registerSingleton(NavigationService()); @@ -130,78 +118,21 @@ void main() { // ); }); }); - testWidgets("Test if onTap is functional", (WidgetTester tester) async { + + testWidgets("test onTap of GestureDetector's", (tester) async { await tester.runAsync(() async { int clicked = 0; - void func(Post post) { - clicked++; - } - - await tester.pumpWidget(createNewsPostWidget(func)); + await tester + .pumpWidget(createNewsPostWidget((Post post) => clicked++)); await tester.pump(); - final postFinder = find.byKey(newsPostKey); - final columnFinder = - find.descendant(of: postFinder, matching: find.byType(Column)); - final column2Finder = columnFinder.at(2); - final secondColumnWidget = - tester.firstWidget(column2Finder) as Column; - print(secondColumnWidget); - // final firstPaddingWidget = secondColumnWidget.children[0] as Padding; - // final firstGestureDetectorFinder = find.descendant( - // of: find.byWidget(firstPaddingWidget), - // matching: find.byType(GestureDetector), - // ); - // final firstGestureDetectorWidget = - // tester.firstWidget(firstGestureDetectorFinder) as GestureDetector; - // await tester.tap(find.byWidget(firstGestureDetectorWidget).first); + await tester.tap(find.byKey(const Key('commentButton'))); await tester.pump(); - expect(clicked, 0); - - // final secondGestureDetectorFinder = find.descendant( - // of: find.byWidget(firstPaddingWidget), - // matching: find.byType(GestureDetector), - // ); - // final secondGestureDetectorWidget = tester - // .firstWidget(secondGestureDetectorFinder.last) as GestureDetector; - // await tester.tap(find.byWidget(secondGestureDetectorWidget).first); - // await tester.pump(); - // expect(clicked, 0); + expect(clicked, 1); - // final thirdPaddingWidget = secondColumnWidget.children[2] as Padding; - // - // final second3GestureDetectorFinder = find.descendant( - // of: find.byWidget(thirdPaddingWidget), - // matching: find.byType(GestureDetector), - // ); - // final second3GestureDetectorWidget = - // tester.firstWidget(second3GestureDetectorFinder.last) - // as GestureDetector; - // await tester.tap(find.byWidget(second3GestureDetectorWidget)); - // await tester.pump(); - // expect(clicked, 3); - // - // final first3GestureDetectorFinder = find.descendant( - // of: find.byWidget(thirdPaddingWidget), - // matching: find.byType(GestureDetector), - // ); - // final first3GestureDetectorWidget = tester - // .firstWidget(first3GestureDetectorFinder) as GestureDetector; - // await tester.tap(find.byWidget(first3GestureDetectorWidget)); - // await tester.pump(); - - // expect( - // first3GestureDetectorWidget.child, - // isA() - // .having((icon) => icon.icon, "icon", Icons.thumb_up) - // .having( - // (icon) => icon.color, - // "color", - // equals( - // const Color(0xff737373), - // ), - // ), - // ); + await tester.tap(find.byType(GestureDetector).last); + await tester.pump(); + expect(clicked, 2); }); }); }); @@ -231,9 +162,9 @@ void main() { // Testing if all direct children of column are there expect(firstColumnWidget.children[0], isA()); expect(firstColumnWidget.children[1], isA()); - expect(firstColumnWidget.children[2], isA()); + // expect(firstColumnWidget.children[2], isA()); expect( - firstColumnWidget.children[3], + firstColumnWidget.children[2], isA>(), ); }); @@ -278,7 +209,7 @@ void main() { expect(customAvatarWidget.firstAlphabet, 'T'); // Tests if leading of list tile is custom avatar - expect(listTileWidget.title.runtimeType, Row); + expect(listTileWidget.title.runtimeType, Text); final textsOfListTileFinder = find.descendant( of: listTileFinder.first, @@ -330,35 +261,7 @@ void main() { expect(descriptionTextWidget.text, "TestDescription"); }); }); - testWidgets("Test props of Container containing the Post Container", - (WidgetTester tester) async { - await tester.runAsync(() async { - await tester.pumpWidget(createNewsPostWidget()); - await tester.pump(); - final postFinder = find.byKey(newsPostKey); - final columnFinder = find - .descendant(of: postFinder, matching: find.byType(Column)) - .first; - final containerWidget = (tester.firstWidget(columnFinder) as Column) - .children[2] as Container; - - // Testing if the text description is correct - // expect(containerWidget.constraints!.maxHeight, 400); - // expect(containerWidget.constraints!.minHeight, 400); - - expect( - containerWidget.color, - null, - ); - - final postContainerFinder = find.descendant( - of: find.byWidget(containerWidget), - matching: find.byType(PostContainer), - ); - expect(postContainerFinder, findsNothing); - }); - }); testWidgets("Test props of Base view", (WidgetTester tester) async { await tester.runAsync(() async { await tester.pumpWidget(createNewsPostWidget()); @@ -368,7 +271,7 @@ void main() { find.descendant(of: postFinder, matching: find.byType(Column)); final baseViewWidget = (tester.firstWidget(columnFinder) as Column) - .children[3] as BaseView; + .children[2] as BaseView; // Testing if the text description is correct expect(baseViewWidget.onModelReady, isNotNull); @@ -379,10 +282,6 @@ void main() { final secondColumnWidget = tester.firstWidget(column2Finder) as Column; print(secondColumnWidget); - // Testing if all direct children of column are there - // expect(secondColumnWidget.children[0], isA()); - // expect(secondColumnWidget.children[1], isA()); - // expect(secondColumnWidget.children[2], isA()); }); }); @@ -420,7 +319,6 @@ void main() { expect(reactionChangedCalled, true); }); }); - testWidgets('Test props first padding widget', (WidgetTester tester) async { await tester.runAsync(() async { @@ -435,62 +333,17 @@ void main() { final firstPaddingWidget = secondColumnWidget.children[0] as Padding; - // expect( - // // firstPaddingWidget.padding, - // const EdgeInsets.symmetric(horizontal: 16, vertical: 10), - // ); - // expect( - // firstPaddingWidget.child, - // isA() - // .having( - // (row) => row.mainAxisAlignment, - // 'mainAxisAlignment', - // MainAxisAlignment.spaceBetween, - // ) - // .having( - // (row) => row.children, - // "children", - // [ - // isA(), - // isA(), - // ], - // ), - // ); final firstGestureDetectorFinder = find.descendant( of: find.byWidget(firstPaddingWidget), matching: find.byType(GestureDetector), ); print(firstGestureDetectorFinder); - // final firstGestureDetectorWidget = tester - // .firstWidget(firstGestureDetectorFinder) as GestureDetector; - // expect(firstGestureDetectorWidget.onTap, isNotNull); - // expect(firstGestureDetectorWidget.onTap, isA()); - // expect( - // firstGestureDetectorWidget.child, - // isA().having((text) => text.data, "data", "0 Likes").having( - // (text) => text.style, - // "style", - // const TextStyle( - // fontFamily: 'open-sans', - // fontWeight: FontWeight.w800, - // ), - // ), - // ); final secondGestureDetectorFinder = find.descendant( of: find.byWidget(firstPaddingWidget), matching: find.byType(GestureDetector), ); print(secondGestureDetectorFinder); - // final secondGestureDetectorWidget = - // tester.firstWidget(secondGestureDetectorFinder.last) - // as GestureDetector; - // expect(secondGestureDetectorWidget.onTap, isNotNull); - // expect(secondGestureDetectorWidget.onTap, isA()); - // expect( - // secondGestureDetectorWidget.child, - // isA().having((text) => text.data, "data", "0 comments"), - // ); }); }); @@ -523,362 +376,60 @@ void main() { final secondColumnWidget = tester.firstWidget(column2Finder) as Column; print(secondColumnWidget); - // final thirdPaddingWidget = - // secondColumnWidget.children[2] as Padding; - // expect( - // thirdPaddingWidget.padding, - // const EdgeInsets.symmetric(horizontal: 16, vertical: 5), - // ); - // expect( - // thirdPaddingWidget.child, - // isA().having( - // (row) => row.children, - // "children", - // [ - // isA(), - // isA(), - // ], - // ), - // ); - // final first3GestureDetectorFinder = find.descendant( - // of: find.byWidget(thirdPaddingWidget), - // matching: find.byType(GestureDetector), - // ); - // final first3GestureDetectorWidget = tester - // .firstWidget(first3GestureDetectorFinder) as GestureDetector; - // expect(first3GestureDetectorWidget.onTap, isNotNull); - // expect(first3GestureDetectorWidget.onTap, isA()); - // expect( - // first3GestureDetectorWidget.child, - // isA() - // .having((icon) => icon.icon, "icon", Icons.thumb_up) - // .having( - // (icon) => icon.color, - // "color", - // const Color(0xff737373), - // ), - // ); - - // final second3GestureDetectorFinder = find.descendant( - // of: find.byWidget(thirdPaddingWidget), - // matching: find.byType(GestureDetector), - // ); - // final second3GestureDetectorWidget = - // tester.firstWidget(second3GestureDetectorFinder.last) - // as GestureDetector; - // expect(second3GestureDetectorWidget.onTap, isNotNull); - // expect(second3GestureDetectorWidget.onTap, isA()); - // expect( - // second3GestureDetectorWidget.child, - // isA() - // .having( - // (padding) => padding.padding, - // "padding", - // const EdgeInsets.only(left: 18.0), - // ) - // .having( - // (padding) => padding.child, - // "child", - // isA() - // .having((icon) => icon.icon, "icon", Icons.comment) - // .having( - // (icon) => icon.color, - // "color", - // const Color(0xff737373), - // ), - // ), - // ); + }); + }); + + testWidgets('Test if report button opens modal bottom sheet', + (WidgetTester tester) async { + await tester.pumpWidget(createNewsPostWidget()); + await tester.pump(); + + final reportButtonFinder = find.byKey(const Key('reportButton')); + expect(reportButtonFinder, findsOneWidget); + + await tester.tap(reportButtonFinder); + await tester.pumpAndSettle(); + + final modalBottomSheetFinder = + find.byKey(const Key('reportPost')).first; + expect(modalBottomSheetFinder, findsOneWidget); + + final postBottomModalFinder = find.byType(PostBottomModal); + expect(postBottomModalFinder, findsOneWidget); + }); + + testWidgets("Test post image", (tester) async { + await tester.runAsync(() async { + await mockNetworkImagesFor(() async { + final Post post = getPostMockModel(); + when(post.imageUrl).thenReturn( + "testImageUrl", + ); + + await tester.pumpWidget( + createNewsPostWidget( + null, + post, + ), + ); + + await tester.pumpAndSettle(); + + final postParentContainer = + find.byKey(const Key('postParentContainer')); + expect(postParentContainer, findsOneWidget); + final postContainer = find + .descendant( + of: postParentContainer, + matching: find.byType(PostContainer), + ) + .first; + + expect(postContainer, findsOneWidget); + }); }); }); }); }); }); - // - // group('Testing Post Container Widget -', () { - // testWidgets('Test if Post Container Widget shows', - // (WidgetTester tester) async { - // await tester.runAsync(() async { - // // https://dcblog.b-cdn.net/wp-content/uploads/2021/02/Full-form-of-URL-1-1024x824.jpg - // nock('https://dcblog.b-cdn.net') - // .get('wp-content/uploads/2021/02/Full-form-of-URL-1-1024x824.jpg') - // .reply(200, json.encode('{"id": "49c23ebc-c107-4dae-b1c6-5d325b8f8b58", "name": "Example campus" }')); - // await tester.pumpWidget(createPostContainerWidget()); - // await tester.pump(); - // final postContainerFinder = find.byKey(postContainerKey); - // expect(postContainerFinder, findsOneWidget); - // }); - // }); - // testWidgets('Test props of Visibility Detector', - // (WidgetTester tester) async { - // await tester.runAsync(() async { - // await tester.pumpWidget(createPostContainerWidget()); - // await tester.pump(); - // final postContainerFinder = find.byKey(postContainerKey); - // final visibilityDetectorFinder = find.descendant( - // of: postContainerFinder, - // matching: find.byType(VisibilityDetector), - // ); - // final visibilityDetectorWidget = - // tester.firstWidget(visibilityDetectorFinder) as VisibilityDetector; - // - // expect(visibilityDetectorFinder, findsOneWidget); - // expect(visibilityDetectorWidget.key, const Key('Post Id')); - // expect(visibilityDetectorWidget.onVisibilityChanged, isNotNull); - // expect(visibilityDetectorWidget.onVisibilityChanged, isA()); - // expect( - // visibilityDetectorWidget.child, - // isA().having( - // (stack) => stack.children, - // "children", - // [ - // isA(), - // isA(), - // ], - // ), - // ); - // }); - // }); - // testWidgets('Test props of PageView', (WidgetTester tester) async { - // await tester.runAsync(() async { - // await tester.pumpWidget(createPostContainerWidget()); - // await tester.pump(); - // final postContainerFinder = find.byKey(postContainerKey); - // final pageViewFinder = find.descendant( - // of: postContainerFinder, - // matching: find.byType(PageView), - // ); - // final pageViewWidget = tester.firstWidget(pageViewFinder) as PageView; - // expect(pageViewFinder, findsOneWidget); - // expect(pageViewWidget.scrollDirection, Axis.horizontal); - // expect( - // pageViewWidget.controller, - // isA().having( - // (pageController) => pageController.initialPage, - // "initial page", - // 0, - // ), - // ); - // expect(pageViewWidget.onPageChanged, isA()); - // }); - // }); - // testWidgets('Test children of PageView', (WidgetTester tester) async { - // await tester.runAsync(() async { - // await mockNetworkImagesFor(() async { - // await tester.pumpWidget(createPostContainerWidget()); - // await tester.pump(); - // final postContainerFinder = find.byKey(postContainerKey); - // final pageViewFinder = find.descendant( - // of: postContainerFinder, - // matching: find.byType(PageView), - // ); - // final centerFinder = find.ancestor( - // of: find.byType(VideoWidget), - // matching: find.descendant( - // of: pageViewFinder, - // matching: find.byType(Center), - // ), - // ); - // final imageFinder = find.descendant( - // of: pageViewFinder, - // matching: find.byType(Image), - // ); - // - // expect(centerFinder, findsOneWidget); - // expect(imageFinder, findsNothing); - // - // final centerWidget = tester.firstWidget(centerFinder) as Center; - // expect( - // centerWidget.child, - // isA() - // .having( - // (video) => video.url, - // "url", - // 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', - // ) - // .having((video) => video.play, "play", true), - // ); - // - // await tester.dragFrom( - // Offset( - // SizeConfig.screenWidth!, - // tester.getCenter(pageViewFinder).dy, - // ), - // Offset(-SizeConfig.screenWidth! * 2, 0), - // ); - // await tester.pump(); - // - // expect(centerFinder, findsOneWidget); - // expect(imageFinder, findsOneWidget); - // expect( - // tester.firstWidget(imageFinder), - // isA().having( - // (image) => image.image, - // "image", - // const NetworkImage( - // 'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg', - // ), - // ), - // ); - // - // final paddingFinder = find.descendant( - // of: postContainerFinder, - // matching: find.byType(Padding), - // ); - // final paddingFinders = find.descendant( - // of: paddingFinder.at(1), - // matching: find.byType(Padding), - // ); - // final padding1Widgets = - // tester.firstWidget(paddingFinders.at(0)) as Padding; - // final padding2Widgets = - // tester.firstWidget(paddingFinders.at(3)) as Padding; - // await tester.pump(); - // expect((padding1Widgets.child! as Divider).color, Colors.grey); - // expect( - // (padding2Widgets.child! as Divider).color, - // TalawaTheme.lightTheme.colorScheme.primary, - // ); - // expect( - // (tester.firstWidget(pageViewFinder) as PageView).controller.page, - // 0.9, - // ); - // }); - // }); - // }); - // testWidgets('Test props of Padding', (WidgetTester tester) async { - // await tester.runAsync(() async { - // await tester.pumpWidget(createPostContainerWidget()); - // await tester.pump(); - // final postContainerFinder = find.byKey(postContainerKey); - // final paddingFinder = find.descendant( - // of: postContainerFinder, - // matching: find.byType(Padding), - // ); - // final paddingWidget = tester.firstWidget(paddingFinder) as Padding; - // expect( - // paddingWidget.padding, - // const EdgeInsets.symmetric(horizontal: 8.0), - // ); - // expect( - // paddingWidget.child, - // isA() - // .having( - // (column) => column.crossAxisAlignment, - // "cross axis alignment", - // CrossAxisAlignment.center, - // ) - // .having( - // (column) => column.mainAxisAlignment, - // "main axis alignment", - // MainAxisAlignment.end, - // ) - // .having( - // (column) => column.mainAxisSize, - // "main axis size", - // MainAxisSize.max, - // ) - // .having( - // (column) => column.children, - // "children", - // [isA()], - // ), - // ); - // }); - // }); - // testWidgets('Test props of second Padding', (WidgetTester tester) async { - // await tester.runAsync(() async { - // await tester.pumpWidget(createPostContainerWidget()); - // await tester.pump(); - // final postContainerFinder = find.byKey(postContainerKey); - // final paddingFinder = find.descendant( - // of: postContainerFinder, - // matching: find.byType(Padding), - // ); - // final paddingWidget = - // tester.firstWidget(paddingFinder.at(1)) as Padding; - // expect( - // paddingWidget.padding, - // const EdgeInsets.symmetric( - // horizontal: 100.0, - // vertical: 10.0, - // ), - // ); - // expect( - // paddingWidget.child, - // isA().having( - // (row) => row.children, - // "children", - // [ - // isA().having( - // (expanded) => expanded.child, - // "child", - // isA(), - // ), - // isA().having( - // (expanded) => expanded.child, - // "child", - // isA(), - // ), - // isA().having( - // (expanded) => expanded.child, - // "child", - // isA(), - // ), - // isA().having( - // (expanded) => expanded.child, - // "child", - // isA(), - // ), - // ], - // ), - // ); - // final paddingFinders = find.descendant( - // of: paddingFinder.at(1), - // matching: find.byType(Padding), - // ); - // final padding1Widgets = - // tester.firstWidget(paddingFinders.at(0)) as Padding; - // final padding2Widgets = - // tester.firstWidget(paddingFinders.at(3)) as Padding; - // - // expect( - // padding1Widgets.padding, - // const EdgeInsets.symmetric(horizontal: 5.0), - // ); - // expect( - // padding1Widgets.child, - // isA() - // .having( - // (divider) => divider.thickness, - // "thickness", - // 3.0, - // ) - // .having( - // (divider) => divider.color, - // "color", - // TalawaTheme.lightTheme.colorScheme.primary, - // ), - // ); - // - // expect( - // padding2Widgets.padding, - // const EdgeInsets.symmetric(horizontal: 5.0), - // ); - // expect( - // padding2Widgets.child, - // isA() - // .having( - // (divider) => divider.thickness, - // "thickness", - // 3.0, - // ) - // .having( - // (divider) => divider.color, - // "color", - // Colors.grey, - // ), - // ); - // }); - // }); - // }); } diff --git a/test/widget_tests/widgets/recurrence_dialog_test.dart b/test/widget_tests/widgets/recurrence_dialog_test.dart new file mode 100644 index 000000000..6909cf2bc --- /dev/null +++ b/test/widget_tests/widgets/recurrence_dialog_test.dart @@ -0,0 +1,257 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:intl/intl.dart'; +import 'package:talawa/constants/recurrence_values.dart'; +import 'package:talawa/services/size_config.dart'; +import 'package:talawa/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart'; +import 'package:talawa/widgets/recurrence_dialog.dart'; + +import '../../helpers/test_helpers.dart'; +import '../../helpers/test_locator.dart'; + +void main() { + group('ShowRecurrenceDialog', () { + TestWidgetsFlutterBinding.ensureInitialized(); + testSetupLocator(); + locator().test(); + late CreateEventViewModel model; + + setUp(() { + locator().test(); + registerServices(); + model = CreateEventViewModel(); + model.initialize(); + }); + + tearDown(() => unregisterServices()); + + testWidgets('shows correct initial values', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: ShowRecurrenceDialog(model: model), + ), + ); + + expect(find.text('Does not repeat'), findsOneWidget); + expect(find.text('Daily'), findsOneWidget); + expect( + find.text( + RecurrenceUtils.getRecurrenceRuleText( + Frequency.weekly, + {RecurrenceUtils.weekDays[model.recurrenceStartDate.weekday - 1]}, + model.interval, + model.count, + model.weekDayOccurenceInMonth, + model.recurrenceStartDate, + model.recurrenceEndDate, + ), + ), + findsOneWidget, + ); + expect( + find.text( + RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + null, + model.interval, + model.count, + model.weekDayOccurenceInMonth, + model.recurrenceStartDate, + model.recurrenceEndDate, + ), + ), + findsOneWidget, + ); + expect( + find.text( + RecurrenceUtils.getRecurrenceRuleText( + Frequency.yearly, + null, + model.interval, + model.count, + model.weekDayOccurenceInMonth, + model.recurrenceStartDate, + model.recurrenceEndDate, + ), + ), + findsOneWidget, + ); + expect( + find.text( + 'Monday to Friday ${model.recurrenceEndDate != null ? "until ${DateFormat('MMMM d, y').format(model.recurrenceEndDate!)}" : ""}', + ), + findsOneWidget, + ); + expect(find.text('Custom...'), findsOneWidget); + }); + + testWidgets('shows correct initial values for last weekday occurrence', + (tester) async { + model.recurrenceStartDate = DateTime(2023, 02, 28); + await tester.pumpWidget( + MaterialApp( + home: ShowRecurrenceDialog(model: model), + ), + ); + + expect( + find.text( + RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + {'TUESDAY'}, + model.interval, + model.count, + -1, + model.recurrenceStartDate, + model.recurrenceEndDate, + ), + ), + findsOneWidget, + ); + }); + + testWidgets('updates model when Does not repeat is selected', + (tester) async { + await tester.pumpWidget( + MaterialApp( + home: ShowRecurrenceDialog(model: model), + ), + ); + + await tester.tap(find.text('Does not repeat')); + await tester.pump(); + + expect(model.isRecurring, false); + expect(model.recurrenceLabel, 'Does not repeat'); + expect(model.frequency, Frequency.weekly); + expect(model.weekDays, { + days[DateTime.now().weekday - 1], + }); + expect(model.weekDayOccurenceInMonth, null); + }); + + testWidgets('updates model when Daily is selected', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: ShowRecurrenceDialog(model: model), + ), + ); + + await tester.tap(find.text('Daily')); + await tester.pump(); + + expect(model.isRecurring, true); + expect(model.recurrenceLabel, 'Daily'); + expect(model.frequency, Frequency.daily); + }); + + testWidgets('updates model when Weekly is selected', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: ShowRecurrenceDialog(model: model), + ), + ); + final label = RecurrenceUtils.getRecurrenceRuleText( + Frequency.weekly, + {RecurrenceUtils.weekDays[model.recurrenceStartDate.weekday - 1]}, + model.interval, + model.count, + model.weekDayOccurenceInMonth, + model.recurrenceStartDate, + model.recurrenceEndDate, + ); + await tester.tap(find.text(label)); + await tester.pump(); + + expect(model.isRecurring, true); + expect(model.recurrenceLabel, label); + expect(model.frequency, Frequency.weekly); + expect( + model.weekDays, + { + RecurrenceUtils.weekDays[model.recurrenceStartDate.weekday - 1] + .toUpperCase(), + }, + ); + }); + + testWidgets('updates model when Monthly is selected', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: ShowRecurrenceDialog(model: model), + ), + ); + + final label = RecurrenceUtils.getRecurrenceRuleText( + Frequency.monthly, + null, + model.interval, + model.count, + model.weekDayOccurenceInMonth, + model.recurrenceStartDate, + model.recurrenceEndDate, + ); + + await tester.tap(find.text(label)); + await tester.pump(); + + expect(model.isRecurring, true); + expect(model.recurrenceLabel, label); + expect(model.frequency, Frequency.monthly); + }); + + testWidgets('updates model when Yearly is selected', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: ShowRecurrenceDialog(model: model), + ), + ); + + final label = RecurrenceUtils.getRecurrenceRuleText( + Frequency.yearly, + null, + model.interval, + model.count, + model.weekDayOccurenceInMonth, + model.recurrenceStartDate, + model.recurrenceEndDate, + ); + + await tester.tap(find.text(label)); + await tester.pump(); + + expect(model.isRecurring, true); + expect(model.recurrenceLabel, label); + expect(model.frequency, Frequency.yearly); + }); + + testWidgets('updates model when Monday to Friday is selected', + (tester) async { + await tester.pumpWidget( + MaterialApp( + home: ShowRecurrenceDialog(model: model), + ), + ); + + final label = + 'Monday to Friday ${model.recurrenceEndDate != null ? "until ${DateFormat('MMMM d, y').format(model.recurrenceEndDate!)}" : ""}'; + + await tester.tap(find.text(label)); + await tester.pump(); + + expect(model.isRecurring, true); + expect(model.recurrenceLabel, label); + expect(model.frequency, Frequency.weekly); + expect( + model.weekDays, + { + 'MONDAY', + 'TUESDAY', + 'WEDNESDAY', + 'THURSDAY', + 'FRIDAY', + }, + ); + }); + }); +}