diff --git a/.github/workflows/dev-workflow.yaml b/.github/workflows/dev-workflow.yaml index 64063b4d5..204f18ed2 100644 --- a/.github/workflows/dev-workflow.yaml +++ b/.github/workflows/dev-workflow.yaml @@ -52,7 +52,9 @@ jobs: ENCRYPTED_GOOGLE_SERVICE_PASSWORD: ${{ secrets.ENCRYPTED_GOOGLE_SERVICE_PASSWORD }} # Get flutter dependencies. - - run: flutter pub get + - run: | + flutter pub get + dart run build_runner build --delete-conflicting-outputs # Check the format of the code and commit the formatted files. - name: Format files in lib and test directories @@ -174,6 +176,7 @@ jobs: if: matrix.target == 'iOS' run: | flutter pub get + dart run build_runner build --delete-conflicting-outputs cd ios pod update flutter clean @@ -197,7 +200,9 @@ jobs: exit 1 # Get dependencies and decrypt needed files. - - run: flutter pub get + - run: | + flutter pub get + dart run build_runner build --delete-conflicting-outputs - name: Decrypt SignETS certificate and Google Services files run: | diff --git a/.github/workflows/master-workflow.yaml b/.github/workflows/master-workflow.yaml index c03f4a72c..d347b70a9 100644 --- a/.github/workflows/master-workflow.yaml +++ b/.github/workflows/master-workflow.yaml @@ -85,7 +85,9 @@ jobs: ENCRYPTED_GOOGLE_SERVICE_PASSWORD: ${{ secrets.ENCRYPTED_GOOGLE_SERVICE_PASSWORD }} # Get flutter dependencies. - - run: flutter pub get + - run : | + flutter pub get + dart run build_runner build --delete-conflicting-outputs # Check if the code has any errors/warnings - name: Analyze code @@ -176,12 +178,15 @@ jobs: if: matrix.target == 'iOS' run: | flutter pub get + dart run build_runner build --delete-conflicting-outputs cd ios pod install flutter clean # Get dependencies and decrypt needed files. - - run: flutter pub get + - run: | + flutter pub get + dart run build_runner build --delete-conflicting-outputs - name: Decrypt SignETS certificate and Google Services files run: | diff --git a/.github/workflows/release-workflow.yaml b/.github/workflows/release-workflow.yaml index be43d384b..08d0bc9da 100644 --- a/.github/workflows/release-workflow.yaml +++ b/.github/workflows/release-workflow.yaml @@ -49,13 +49,16 @@ jobs: if: matrix.target == 'iOS' run: | flutter pub get + dart run build_runner build --delete-conflicting-outputs cd ios pod install flutter clean - run: flutter doctor -v # Get dependencies and decrypt needed files. - - run: flutter pub get + - run: | + flutter pub get + dart run build_runner build --delete-conflicting-outputs - name: Decrypt certificates files run: | diff --git a/.gitignore b/.gitignore index 2875a4e37..a12547a23 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ app_untranslated_messages.yaml # Flutter test test/**/failures/ +test/mock/**/*.mocks.dart # Certificates and secrets assets/certificates/ diff --git a/README.md b/README.md index 038774f23..bee468231 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,11 @@ chmod +x ./env_variables.sh flutter pub get ``` +- To generate the mocks: +``` +dart run build_runner build +``` + ## Add environment variable for API_KEY - To add the Google Maps API TOKEN and the GitHub API TOKEN, you need to rename the file `.env.template` into `.env`. In diff --git a/lib/core/constants/preferences_flags.dart b/lib/core/constants/preferences_flags.dart index 6e9ee9507..67ef95b64 100644 --- a/lib/core/constants/preferences_flags.dart +++ b/lib/core/constants/preferences_flags.dart @@ -1,6 +1,3 @@ -// Flutter imports: -import 'package:flutter/cupertino.dart'; - // Package imports: import 'package:enum_to_string/enum_to_string.dart'; @@ -62,8 +59,8 @@ class DynamicPreferencesFlag { PreferencesFlag groupAssociationFlag; DynamicPreferencesFlag( - {@required this.groupAssociationFlag, - @required this.uniqueKey, + {required this.groupAssociationFlag, + required this.uniqueKey, this.separator = "_"}); String get data => groupAssociationFlag.toString() + separator + uniqueKey; diff --git a/lib/core/constants/widget_helper.dart b/lib/core/constants/widget_helper.dart index 09bdcaa2a..5b47557e4 100644 --- a/lib/core/constants/widget_helper.dart +++ b/lib/core/constants/widget_helper.dart @@ -4,7 +4,7 @@ enum WidgetType { progress, grades } /// The extension corresponding to WidgetType to get associated constants extension WidgetTypeExtension on WidgetType { /// ID used by iOS systems to update the corresponding widget - String get iOSname { + String? get iOSname { switch (this) { case WidgetType.progress: return 'ETSMobile_ProgressWidget'; @@ -22,7 +22,7 @@ extension WidgetTypeExtension on WidgetType { /// /// Another option would be to use qualifiedAndroidName instead of androidName /// (see home_widget's updateWidget method for more info) - String get androidName { + String? get androidName { switch (this) { case WidgetType.progress: return 'ProgressWidgetProvider'; diff --git a/lib/core/managers/cache_manager.dart b/lib/core/managers/cache_manager.dart index 79b529661..87a57860e 100644 --- a/lib/core/managers/cache_manager.dart +++ b/lib/core/managers/cache_manager.dart @@ -28,7 +28,7 @@ class CacheManager { /// Get from the cache the value associated with [key]. /// Throw a [CacheException] if the [key] doesn't exist. Future get(String key) async { - final lib.FileInfo fileInfo = await _cacheManager.getFileFromCache(key); + final lib.FileInfo? fileInfo = await _cacheManager.getFileFromCache(key); if (fileInfo == null) { _analyticsService.logEvent( diff --git a/lib/core/managers/course_repository.dart b/lib/core/managers/course_repository.dart index fc81c31f5..6e7cb68ab 100644 --- a/lib/core/managers/course_repository.dart +++ b/lib/core/managers/course_repository.dart @@ -52,25 +52,25 @@ class CourseRepository { final SignetsAPIClient _signetsApiClient = locator(); /// Student list of courses - List _courses; + List? _courses; - List get courses => _courses; + List? get courses => _courses; /// List of the courses activities for the student - List _coursesActivities; + List? _coursesActivities; - List get coursesActivities => _coursesActivities; + List? get coursesActivities => _coursesActivities; /// List of the schedule activities for the student in the active session - List _scheduleActivities; + List? _scheduleActivities; - List get scheduleActivities => _scheduleActivities; + List? get scheduleActivities => _scheduleActivities; /// List of session where the student has been registered. /// The sessions are organized from oldest to youngest - List _sessions; + List? _sessions; - List get sessions => _sessions; + List? get sessions => _sessions; /// Return the active sessions which mean the sessions that the endDate isn't already passed. List get activeSessions { @@ -81,14 +81,14 @@ class CourseRepository { ?.where((session) => session.endDate.isAfter(now) || session.endDate.isAtSameMomentAs(now)) - ?.toList() ?? + .toList() ?? []; } /// Get and update the list of courses activities for the active sessions. /// After fetching the new activities from the [SignetsApi] the [CacheManager] /// is updated with the latest version of the activities. - Future> getCoursesActivities( + Future?> getCoursesActivities( {bool fromCacheOnly = false}) async { // Force fromCacheOnly mode when user has no connectivity if (!(await _networkingService.hasConnectivity())) { @@ -109,7 +109,7 @@ class CourseRepository { .map((e) => CourseActivity.fromJson(e as Map)) .toList(); _logger.d( - "$tag - getCoursesActivities: ${_coursesActivities.length} activities loaded from cache"); + "$tag - getCoursesActivities: ${_coursesActivities?.length ?? 0} activities loaded from cache"); } on CacheException catch (_) { _logger.e( "$tag - getCoursesActivities: exception raised will trying to load activities from cache."); @@ -128,15 +128,17 @@ class CourseRepository { await getSessions(); } - final String password = await _userRepository.getPassword(); - for (final Session session in activeSessions) { - fetchedCoursesActivities.addAll( - await _signetsApiClient.getCoursesActivities( - username: _userRepository.monETSUser.universalCode, - password: password, - session: session.shortName)); - _logger.d( - "$tag - getCoursesActivities: fetched ${fetchedCoursesActivities.length} activities."); + if (_userRepository.monETSUser != null) { + final String password = await _userRepository.getPassword(); + for (final Session session in activeSessions) { + fetchedCoursesActivities.addAll( + await _signetsApiClient.getCoursesActivities( + username: _userRepository.monETSUser!.universalCode, + password: password, + session: session.shortName)); + _logger.d( + "$tag - getCoursesActivities: fetched ${fetchedCoursesActivities.length} activities."); + } } } on Exception catch (e, stacktrace) { _analyticsService.logError(tag, @@ -152,13 +154,14 @@ class CourseRepository { activeSessionStartDate = element.startDate; } } - _coursesActivities.removeWhere( + _coursesActivities?.removeWhere( (element) => element.startDateTime.isAfter(activeSessionStartDate)); // Update the list of activities to avoid duplicate activities for (final CourseActivity activity in fetchedCoursesActivities) { - if (!_coursesActivities.contains(activity)) { - _coursesActivities.add(activity); + if (_coursesActivities != null && + !_coursesActivities!.contains(activity)) { + _coursesActivities!.add(activity); } } @@ -199,7 +202,7 @@ class CourseRepository { .map((e) => ScheduleActivity.fromJson(e as Map)) .toList(); _logger.d( - "$tag - getScheduleActivities: ${_scheduleActivities.length} activities loaded from cache"); + "$tag - getScheduleActivities: ${_scheduleActivities!.length} activities loaded from cache"); } on CacheException catch (_) { _logger.e( "$tag - getScheduleActivities: exception raised will trying to load activities from cache."); @@ -207,7 +210,7 @@ class CourseRepository { } if (fromCacheOnly) { - return _scheduleActivities; + return _scheduleActivities!; } final List fetchedScheduleActivities = []; @@ -218,16 +221,19 @@ class CourseRepository { await getSessions(); } - final String password = await _userRepository.getPassword(); + if (_userRepository.monETSUser != null) { + final String password = await _userRepository.getPassword(); - for (final Session session in activeSessions) { - fetchedScheduleActivities.addAll( - await _signetsApiClient.getScheduleActivities( - username: _userRepository.monETSUser.universalCode, - password: password, - session: session.shortName)); - _logger.d( - "$tag - getScheduleActivities: fetched ${fetchedScheduleActivities.length} activities."); + for (final Session session in activeSessions) { + fetchedScheduleActivities.addAll( + await _signetsApiClient.getScheduleActivities( + username: _userRepository.monETSUser!.universalCode, + password: password, + session: session.shortName)); + + _logger.d( + "$tag - getScheduleActivities: fetched ${fetchedScheduleActivities.length} activities."); + } } } on Exception catch (e, stacktrace) { _analyticsService.logError(tag, @@ -238,8 +244,9 @@ class CourseRepository { // Update the list of activities to avoid duplicate activities for (final ScheduleActivity activity in fetchedScheduleActivities) { - if (!_scheduleActivities.contains(activity)) { - _scheduleActivities.add(activity); + if (_scheduleActivities != null && + !_scheduleActivities!.contains(activity)) { + _scheduleActivities!.add(activity); } } @@ -253,7 +260,7 @@ class CourseRepository { "$tag - getScheduleActivities: exception raised will trying to update the cache."); } - return _scheduleActivities; + return _scheduleActivities!; } /// Get the list of session on which the student was active. @@ -272,7 +279,7 @@ class CourseRepository { .map((e) => Session.fromJson(e as Map)) .toList(); _logger.d( - "$tag - getSessions: ${_sessions.length} sessions loaded from cache."); + "$tag - getSessions: ${_sessions?.length ?? 0} sessions loaded from cache."); } on CacheException catch (_) { _logger.e( "$tag - getSessions: exception raised will trying to load the sessions from cache."); @@ -281,37 +288,40 @@ class CourseRepository { // Don't try to update cache when offline if (!(await _networkingService.hasConnectivity())) { - return _sessions; + return _sessions!; } try { - // getPassword will try to authenticate the user if not authenticated. - final String password = await _userRepository.getPassword(); - - final List fetchedSession = await _signetsApiClient.getSessions( - username: _userRepository.monETSUser.universalCode, - password: password); - _logger - .d("$tag - getSessions: ${fetchedSession.length} sessions fetched."); - for (final Session session in fetchedSession) { - if (!_sessions.contains(session)) { - _sessions.add(session); + if (_userRepository.monETSUser != null) { + // getPassword will try to authenticate the user if not authenticated. + final String password = await _userRepository.getPassword(); + + final List fetchedSession = + await _signetsApiClient.getSessions( + username: _userRepository.monETSUser!.universalCode, + password: password); + _logger.d( + "$tag - getSessions: ${fetchedSession.length} sessions fetched."); + for (final Session session in fetchedSession) { + if (!_sessions!.contains(session)) { + _sessions!.add(session); + } } - } - // Update cache - _cacheManager.update(sessionsCacheKey, jsonEncode(_sessions)); + // Update cache + _cacheManager.update(sessionsCacheKey, jsonEncode(_sessions)); + } } on CacheException catch (_) { _logger.e( "$tag - getSessions: exception raised will trying to update the cache."); - return _sessions; + return _sessions!; } on Exception catch (e, stacktrace) { _analyticsService.logError( tag, "Exception raised during getSessions: $e", e, stacktrace); rethrow; } - return _sessions; + return _sessions!; } /// Get the student's course list. After fetching the courses from [SignetsApi], @@ -336,7 +346,7 @@ class CourseRepository { .map((e) => Course.fromJson(e as Map)) .toList(); _logger.d( - "$tag - getCourses: ${_courses.length} courses loaded from cache"); + "$tag - getCourses: ${_courses!.length} courses loaded from cache"); } on CacheException catch (_) { _logger.e( "$tag - getCourses: exception raised will trying to load courses from cache."); @@ -344,19 +354,22 @@ class CourseRepository { } if (fromCacheOnly) { - return _courses; + return _courses!; } final List fetchedCourses = []; final Map> fetchedCourseReviews = {}; try { - final String password = await _userRepository.getPassword(); - - fetchedCourses.addAll(await _signetsApiClient.getCourses( - username: _userRepository.monETSUser.universalCode, - password: password)); - _logger.d("$tag - getCourses: fetched ${fetchedCourses.length} courses."); + if (_userRepository.monETSUser != null) { + final String password = await _userRepository.getPassword(); + + fetchedCourses.addAll(await _signetsApiClient.getCourses( + username: _userRepository.monETSUser!.universalCode, + password: password)); + _logger + .d("$tag - getCourses: fetched ${fetchedCourses.length} courses."); + } } on Exception catch (e, stacktrace) { _analyticsService.logError( tag, "Exception raised during getCourses: $e", e, stacktrace); @@ -370,7 +383,7 @@ class CourseRepository { _logger.d("$tag - getCourses: $e during getCoursesEvaluations. Ignored"); } - _courses.clear(); + _courses!.clear(); // If there isn't the grade yet, will fetch the summary. // We don't do this for every course to avoid losing time. @@ -383,10 +396,10 @@ class CourseRepository { _logger.e( "$tag - getCourses: Exception raised while trying to get summary " "of ${course.acronym}."); - _courses.add(course); + _courses!.add(course); } } else { - _courses.add(course); + _courses!.add(course); } } @@ -399,14 +412,14 @@ class CourseRepository { "$tag - getCourses: exception raised will trying to update the cache."); } - return _courses; + return _courses!; } /// Get the summary (detailed evaluations) of [course]. After fetching the /// summary from [SignetsApi], the [CacheManager] is updated with the latest /// version of the course. Return the course with the summary set. Future getCourseSummary(Course course) async { - CourseSummary summary; + CourseSummary? summary; // Don't try to update the summary when user has no connection if (!(await _networkingService.hasConnectivity())) { @@ -414,13 +427,16 @@ class CourseRepository { } try { - final String password = await _userRepository.getPassword(); + if (_userRepository.monETSUser != null) { + final String password = await _userRepository.getPassword(); - summary = await _signetsApiClient.getCourseSummary( - username: _userRepository.monETSUser.universalCode, - password: password, - course: course); - _logger.d("$tag - getCourseSummary: fetched ${course.acronym} summary."); + summary = await _signetsApiClient.getCourseSummary( + username: _userRepository.monETSUser!.universalCode, + password: password, + course: course); + _logger + .d("$tag - getCourseSummary: fetched ${course.acronym} summary."); + } } on Exception catch (e, stacktrace) { if (e is ApiException) { if (e.errorCode == SignetsError.gradesEmpty || @@ -439,10 +455,10 @@ class CourseRepository { _courses ??= []; // Update courses list - _courses.removeWhere((element) => + _courses!.removeWhere((element) => course.acronym == element.acronym && course.session == element.session); course.summary = summary; - _courses.add(course); + _courses!.add(course); try { // Update cache @@ -470,15 +486,17 @@ class CourseRepository { await getSessions(); } - for (final Session session in _sessions) { - sessionReviews = await _signetsApiClient.getCourseReviews( - username: _userRepository.monETSUser.universalCode, - password: password, - session: session); - reviews.putIfAbsent(session.shortName, () => sessionReviews); - _logger.d( - "$tag - getCoursesEvaluations: fetched ${reviews[session.shortName].length} " - "evaluations for session ${session.shortName}."); + if (_userRepository.monETSUser != null) { + for (final Session session in _sessions!) { + sessionReviews = await _signetsApiClient.getCourseReviews( + username: _userRepository.monETSUser!.universalCode, + password: password, + session: session); + reviews.putIfAbsent(session.shortName, () => sessionReviews); + _logger.d( + "$tag - getCoursesEvaluations: fetched ${reviews[session.shortName]?.length ?? 0} " + "evaluations for session ${session.shortName}."); + } } } on Exception catch (e, stacktrace) { _analyticsService.logError(tag, e.toString(), e, stacktrace); @@ -490,15 +508,11 @@ class CourseRepository { } /// Get the evaluation for a course or null if not found. - CourseReview _getReviewForCourse( + CourseReview? _getReviewForCourse( Course course, Map> reviews) { - if (reviews.containsKey(course.session)) { - return reviews[course.session].firstWhere( - (element) => - element.acronym == course.acronym && - element.group == course.group, - orElse: () => null); - } - return null; + // Todo: changer pour firstWhereOrNull après update de Collection + final review = reviews[course.session]?.where((element) => + element.acronym == course.acronym && element.group == course.group); + return review?.isNotEmpty ?? false ? review?.first : null; } } diff --git a/lib/core/managers/settings_manager.dart b/lib/core/managers/settings_manager.dart index 93160f8c6..f414c1d42 100644 --- a/lib/core/managers/settings_manager.dart +++ b/lib/core/managers/settings_manager.dart @@ -30,16 +30,16 @@ class SettingsManager with ChangeNotifier { locator(); /// current ThemeMode - ThemeMode _themeMode; + ThemeMode? _themeMode; /// current Locale - Locale _locale; + Locale? _locale; /// Get current time DateTime get dateTimeNow => DateTime.now(); /// Get ThemeMode - ThemeMode get themeMode { + ThemeMode? get themeMode { _preferencesService.getString(PreferencesFlag.theme).then((value) => { if (value != null) { @@ -72,7 +72,7 @@ class SettingsManager with ChangeNotifier { } /// Get Locale - Locale get locale { + Locale? get locale { _preferencesService.getString(PreferencesFlag.locale).then((value) { if (value != null) { _locale = @@ -136,7 +136,7 @@ class SettingsManager with ChangeNotifier { /// Set ThemeMode void setThemeMode(ThemeMode value) { _preferencesService.setString(PreferencesFlag.theme, value.toString()); - // Log the event + _analyticsService.logEvent( "${tag}_${EnumToString.convertToString(PreferencesFlag.theme)}", EnumToString.convertToString(value)); @@ -144,16 +144,19 @@ class SettingsManager with ChangeNotifier { notifyListeners(); } - /// Set Locale void setLocale(String value) { _locale = AppIntl.supportedLocales.firstWhere((e) => e.toString() == value); - _preferencesService.setString(PreferencesFlag.locale, _locale.languageCode); - // Log the event + _analyticsService.logEvent( "${tag}_${EnumToString.convertToString(PreferencesFlag.locale)}", - _locale.languageCode); - _locale = Locale(_locale.languageCode); - notifyListeners(); + _locale?.languageCode ?? 'Not found'); + + if (_locale != null) { + _preferencesService.setString( + PreferencesFlag.locale, _locale!.languageCode); + _locale = Locale(_locale!.languageCode); + notifyListeners(); + } } /// Get the settings of the schedule, these are loaded from the user preferences. @@ -164,7 +167,8 @@ class SettingsManager with ChangeNotifier { .getString(PreferencesFlag.scheduleCalendarFormat) .then((value) => value == null ? CalendarFormat.week - : EnumToString.fromString(CalendarFormat.values, value)); + : EnumToString.fromString(CalendarFormat.values, value) ?? + CalendarFormat.week); settings.putIfAbsent( PreferencesFlag.scheduleCalendarFormat, () => calendarFormat); @@ -208,10 +212,10 @@ class SettingsManager with ChangeNotifier { } /// Add/update the value of [flag] - Future setString(PreferencesFlag flag, String value) async { + Future setString(PreferencesFlag flag, String? value) async { // Log the event _analyticsService.logEvent( - "${tag}_${EnumToString.convertToString(flag)}", value); + "${tag}_${EnumToString.convertToString(flag)}", value.toString()); if (value == null) { return _preferencesService.removePreferencesFlag(flag); @@ -221,7 +225,7 @@ class SettingsManager with ChangeNotifier { /// Add/update the value of [flag] Future setDynamicString( - PreferencesFlag flag, String key, String value) async { + PreferencesFlag flag, String key, String? value) async { if (value == null) { return _preferencesService.removeDynamicPreferencesFlag(flag, key); } @@ -241,7 +245,7 @@ class SettingsManager with ChangeNotifier { } /// Get the value of [flag] - Future getString(PreferencesFlag flag) async { + Future getString(PreferencesFlag flag) async { // Log the event _analyticsService.logEvent( "${tag}_${EnumToString.convertToString(flag)}", 'getString'); @@ -249,7 +253,7 @@ class SettingsManager with ChangeNotifier { } /// Get the value of [flag] - Future getDynamicString(PreferencesFlag flag, String key) async { + Future getDynamicString(PreferencesFlag flag, String key) async { // Log the event _analyticsService.logEvent("${tag}_${flag.toString()}", 'getString'); return _preferencesService.getDynamicString(flag, key); @@ -265,7 +269,7 @@ class SettingsManager with ChangeNotifier { } /// Get the value of [flag] - Future getBool(PreferencesFlag flag) async { + Future getBool(PreferencesFlag flag) async { // Log the event _analyticsService.logEvent( "${tag}_${EnumToString.convertToString(flag)}", 'getBool'); diff --git a/lib/core/managers/user_repository.dart b/lib/core/managers/user_repository.dart index 0befb05ad..5d9980769 100644 --- a/lib/core/managers/user_repository.dart +++ b/lib/core/managers/user_repository.dart @@ -50,19 +50,19 @@ class UserRepository { final MonETSAPIClient _monEtsApiClient = locator(); /// Mon ETS user for the student - MonETSUser _monETSUser; + MonETSUser? _monETSUser; - MonETSUser get monETSUser => _monETSUser; + MonETSUser? get monETSUser => _monETSUser; /// Information for the student profile - ProfileStudent _info; + ProfileStudent? _info; - ProfileStudent get info => _info; + ProfileStudent? get info => _info; /// List of the programs for the student - List _programs; + List? _programs; - List get programs => _programs; + List? get programs => _programs; /// Authenticate the user using the [username] (for a student should be the /// universal code like AAXXXXX). @@ -70,8 +70,8 @@ class UserRepository { /// will be saved in the secure storage of the device to authorize a silent /// authentication next time. Future authenticate( - {@required String username, - @required String password, + {required String username, + required String password, bool isSilent = false}) async { try { _monETSUser = await _monEtsApiClient.authenticate( @@ -105,7 +105,7 @@ class UserRepository { } await _analyticsService.setUserProperties( - userId: username, domain: _monETSUser.domain); + userId: username, domain: _monETSUser!.domain); // Save the credentials in the secure storage if (!isSilent) { @@ -133,6 +133,12 @@ class UserRepository { final username = await _secureStorage.read(key: usernameSecureKey); if (username != null) { final password = await _secureStorage.read(key: passwordSecureKey); + if (password == null) { + await _secureStorage.deleteAll(); + _analyticsService.logError(tag, + "SilentAuthenticate - PlatformException(Handled) - $passwordSecureKey not found"); + return false; + } return await authenticate( username: username, password: password, isSilent: true); } @@ -178,6 +184,11 @@ class UserRepository { } try { final password = await _secureStorage.read(key: passwordSecureKey); + if (password == null) { + _analyticsService.logEvent( + tag, "Trying to acquire password but not authenticated"); + throw const ApiException(prefix: tag, message: "Not authenticated"); + } return password; } on PlatformException catch (e, stacktrace) { await _secureStorage.deleteAll(); @@ -194,7 +205,7 @@ class UserRepository { // Force fromCacheOnly mode when user has no connectivity if (!(await _networkingService.hasConnectivity())) { // ignore: parameter_assignments - fromCacheOnly = true; + fromCacheOnly = !await _networkingService.hasConnectivity(); } // Load the programs from the cache if the list doesn't exist @@ -211,7 +222,7 @@ class UserRepository { .map((e) => Program.fromJson(e as Map)) .toList(); _logger.d( - "$tag - getPrograms: ${_programs.length} programs loaded from cache."); + "$tag - getPrograms: ${_programs!.length} programs loaded from cache."); } on CacheException catch (_) { _logger.e( "$tag - getPrograms: exception raised while trying to load the programs from cache."); @@ -219,31 +230,33 @@ class UserRepository { } if (fromCacheOnly) { - return _programs; + return _programs!; } try { // getPassword will try to authenticate the user if not authenticated. final String password = await getPassword(); - _programs = await _signetsApiClient.getPrograms( - username: _monETSUser.universalCode, password: password); + if (_monETSUser != null) { + _programs = await _signetsApiClient.getPrograms( + username: _monETSUser!.universalCode, password: password); - _logger.d("$tag - getPrograms: ${_programs.length} programs fetched."); + _logger.d("$tag - getPrograms: ${_programs!.length} programs fetched."); - // Update cache - _cacheManager.update(programsCacheKey, jsonEncode(_programs)); + // Update cache + _cacheManager.update(programsCacheKey, jsonEncode(_programs)); + } } on CacheException catch (_) { _logger.e( "$tag - getPrograms: exception raised while trying to update the cache."); - return _programs; + return _programs!; } on Exception catch (e, stacktrace) { _analyticsService.logError( tag, "Exception raised during getPrograms: $e", e, stacktrace); rethrow; } - return _programs; + return _programs!; } /// Get the profile information. @@ -272,45 +285,46 @@ class UserRepository { } if (fromCacheOnly) { - return _info; + return _info!; } try { // getPassword will try to authenticate the user if not authenticated. final String password = await getPassword(); - final fetchedInfo = await _signetsApiClient.getStudentInfo( - username: _monETSUser.universalCode, password: password); + if (_monETSUser != null) { + final fetchedInfo = await _signetsApiClient.getStudentInfo( + username: _monETSUser!.universalCode, password: password); - _logger.d("$tag - getInfo: $fetchedInfo info fetched."); + _logger.d("$tag - getInfo: $fetchedInfo info fetched."); - if (_info != fetchedInfo) { - _info = fetchedInfo ?? _info; + if (_info != fetchedInfo) { + _info = fetchedInfo; - // Update cache - _cacheManager.update(infoCacheKey, jsonEncode(_info)); + // Update cache + _cacheManager.update(infoCacheKey, jsonEncode(_info)); + } } } on CacheException catch (_) { _logger.e( "$tag - getInfo: exception raised while trying to update the cache."); - return _info; + return _info!; } on Exception catch (e, stacktrace) { _analyticsService.logError( tag, "Exception raised during getInfo: $e", e, stacktrace); rethrow; } - return _info; + return _info!; } /// Check whether the user was previously authenticated. Future wasPreviouslyLoggedIn() async { try { - final String username = await _secureStorage.read(key: passwordSecureKey); + final username = await _secureStorage.read(key: passwordSecureKey); if (username != null) { - final String password = - await _secureStorage.read(key: passwordSecureKey); - return password.isNotEmpty; + final password = await _secureStorage.read(key: passwordSecureKey); + return password != null && password.isNotEmpty; } } on PlatformException catch (e, stacktrace) { await _secureStorage.deleteAll(); diff --git a/lib/core/models/discovery.dart b/lib/core/models/discovery.dart index c2d9949cf..a91385cd6 100644 --- a/lib/core/models/discovery.dart +++ b/lib/core/models/discovery.dart @@ -8,8 +8,8 @@ class Discovery { final Widget details; Discovery( - {@required this.path, - @required this.featureId, - @required this.title, - @required this.details}); + {required this.path, + required this.featureId, + required this.title, + required this.details}); } diff --git a/lib/core/models/emergency_procedure.dart b/lib/core/models/emergency_procedure.dart index 606f32ffd..3c1ee8daa 100644 --- a/lib/core/models/emergency_procedure.dart +++ b/lib/core/models/emergency_procedure.dart @@ -1,9 +1,6 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - class EmergencyProcedure { final String title; final String detail; - EmergencyProcedure({@required this.title, @required this.detail}); + EmergencyProcedure({required this.title, required this.detail}); } diff --git a/lib/core/models/faq_actions.dart b/lib/core/models/faq_actions.dart index 17c268540..251a5f8f6 100644 --- a/lib/core/models/faq_actions.dart +++ b/lib/core/models/faq_actions.dart @@ -11,13 +11,13 @@ class ActionItem { final Color circleColor; ActionItem({ - @required this.title, - @required this.description, - @required this.type, - @required this.link, - @required this.iconName, - @required this.iconColor, - @required this.circleColor, + required this.title, + required this.description, + required this.type, + required this.link, + required this.iconName, + required this.iconColor, + required this.circleColor, }); } diff --git a/lib/core/models/faq_questions.dart b/lib/core/models/faq_questions.dart index d7ea0ba98..c73daf468 100644 --- a/lib/core/models/faq_questions.dart +++ b/lib/core/models/faq_questions.dart @@ -1,12 +1,9 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - class QuestionItem { final Map title; final Map description; QuestionItem({ - @required this.title, - @required this.description, + required this.title, + required this.description, }); } diff --git a/lib/core/models/feedback.dart b/lib/core/models/feedback.dart index 476022c59..50aa08a99 100644 --- a/lib/core/models/feedback.dart +++ b/lib/core/models/feedback.dart @@ -4,8 +4,8 @@ class CustomFeedback { this.feedbackText, }); - String feedbackText; - String feedbackEmail; + String? feedbackText; + String? feedbackEmail; @override String toString() { diff --git a/lib/core/models/feedback_issue.dart b/lib/core/models/feedback_issue.dart index 5dccbb517..4ac56dc7f 100644 --- a/lib/core/models/feedback_issue.dart +++ b/lib/core/models/feedback_issue.dart @@ -2,13 +2,14 @@ import 'package:github/github.dart'; class FeedbackIssue { - int id; - String htmlUrl; - String simpleDescription; - String state; - bool isOpen; - int number; - String createdAt; + late int id; + late String htmlUrl; + late String simpleDescription; + late String state; + late bool isOpen; + late int number; + late String createdAt; + // Constructor FeedbackIssue(Issue issue) { id = issue.id; diff --git a/lib/core/models/group_discovery.dart b/lib/core/models/group_discovery.dart index adfb0d3e0..8a09af894 100644 --- a/lib/core/models/group_discovery.dart +++ b/lib/core/models/group_discovery.dart @@ -1,6 +1,3 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - // Project imports: import 'package:notredame/core/models/discovery.dart'; @@ -9,7 +6,7 @@ class GroupDiscovery { final List discoveries; GroupDiscovery({ - @required this.name, - @required this.discoveries, + required this.name, + required this.discoveries, }); } diff --git a/lib/core/models/quick_link.dart b/lib/core/models/quick_link.dart index 60a28c0e1..635a8fc3f 100644 --- a/lib/core/models/quick_link.dart +++ b/lib/core/models/quick_link.dart @@ -8,8 +8,8 @@ class QuickLink { final String link; QuickLink( - {@required this.id, - @required this.image, - @required this.name, - @required this.link}); + {required this.id, + required this.image, + required this.name, + required this.link}); } diff --git a/lib/core/models/quick_link_data.dart b/lib/core/models/quick_link_data.dart index 7895b9917..487e8c480 100644 --- a/lib/core/models/quick_link_data.dart +++ b/lib/core/models/quick_link_data.dart @@ -1,11 +1,8 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - class QuickLinkData { final int id; final int index; - QuickLinkData({@required this.id, @required this.index}); + QuickLinkData({required this.id, required this.index}); factory QuickLinkData.fromJson(Map json) { return QuickLinkData( diff --git a/lib/core/models/widget_models.dart b/lib/core/models/widget_models.dart index ef447a687..95a2683d3 100644 --- a/lib/core/models/widget_models.dart +++ b/lib/core/models/widget_models.dart @@ -1,6 +1,3 @@ -// Flutter imports: -import 'package:flutter/foundation.dart'; - class GradesWidgetData { static const String keyPrefix = "grade_"; @@ -9,9 +6,9 @@ class GradesWidgetData { String title; GradesWidgetData({ - @required this.title, - @required this.courseAcronyms, - @required this.grades, + required this.title, + required this.courseAcronyms, + required this.grades, }); } @@ -25,9 +22,9 @@ class ProgressWidgetData { String suffix; ProgressWidgetData( - {@required this.title, - @required this.progress, - @required this.elapsedDays, - @required this.totalDays, - @required this.suffix}); + {required this.title, + required this.progress, + required this.elapsedDays, + required this.totalDays, + required this.suffix}); } diff --git a/lib/core/services/analytics_service.dart b/lib/core/services/analytics_service.dart index d98fab89a..a9adea0bf 100644 --- a/lib/core/services/analytics_service.dart +++ b/lib/core/services/analytics_service.dart @@ -1,6 +1,3 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - // Package imports: import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; @@ -18,7 +15,7 @@ class AnalyticsService { /// Log a error. [prefix] should be the service where the error was triggered. Future logError(String prefix, String message, - [Exception error, StackTrace stackTrace]) async { + [Exception? error, StackTrace? stackTrace]) async { final mesTruncated = message.length > 100 ? message.substring(0, 99) : message; await _analytics.logEvent( @@ -38,7 +35,8 @@ class AnalyticsService { } /// Set user properties to identify the user against firebase app. - Future setUserProperties({@required String userId, String domain}) async { + Future setUserProperties( + {required String userId, required String domain}) async { await _analytics.setUserId(id: userId); await _analytics.setUserProperty( name: _userPropertiesDomainKey, value: domain); diff --git a/lib/core/services/app_widget_service.dart b/lib/core/services/app_widget_service.dart index 078ba8449..9c5725f16 100644 --- a/lib/core/services/app_widget_service.dart +++ b/lib/core/services/app_widget_service.dart @@ -24,12 +24,12 @@ class AppWidgetService { final AnalyticsService _analyticsService = locator(); - Future init() async { + Future init() async { return HomeWidget.setAppGroupId('group.ca.etsmtl.applets.ETSMobile'); } /// Update session progress widget with provided data - Future sendProgressData(ProgressWidgetData progressWidgetData) async { + Future sendProgressData(ProgressWidgetData progressWidgetData) async { try { await HomeWidget.saveWidgetData( '${ProgressWidgetData.keyPrefix}progress', @@ -52,7 +52,7 @@ class AppWidgetService { } /// Update grades widget with provided data - Future sendGradesData(GradesWidgetData gradeWidgetData) async { + Future sendGradesData(GradesWidgetData gradeWidgetData) async { try { await HomeWidget.saveWidgetData>( '${GradesWidgetData.keyPrefix}courseAcronyms', @@ -68,7 +68,7 @@ class AppWidgetService { } /// Tell the system to update the given widget type - Future updateWidget(WidgetType type) async { + Future updateWidget(WidgetType type) async { try { return HomeWidget.updateWidget( name: type.androidName, @@ -76,6 +76,7 @@ class AppWidgetService { iOSName: type.iOSname); } on PlatformException { _analyticsService.logError(tag, 'Error updating widget ${type.iOSname}.'); + return false; } } } diff --git a/lib/core/services/github_api.dart b/lib/core/services/github_api.dart index 8b38a2c14..593bc01f3 100644 --- a/lib/core/services/github_api.dart +++ b/lib/core/services/github_api.dart @@ -27,7 +27,7 @@ class GithubApi { static const String _repositorySlug = "ApplETS/Notre-Dame"; static const String _repositoryReportSlug = "ApplETS/Notre-Dame-Bug-report"; - GitHub _github; + late GitHub _github; final Logger _logger = locator(); @@ -48,7 +48,7 @@ class GithubApi { } /// Upload a file to the ApplETS/Notre-Dame-Bug-report repository - void uploadFileToGithub({@required String filePath, @required File file}) { + void uploadFileToGithub({required String filePath, required File file}) { _github.repositories .createFile( RepositorySlug.full(_repositoryReportSlug), @@ -74,10 +74,10 @@ class GithubApi { /// The bug report will contain a file, a description [feedbackText] and also some information about the /// application/device. Future createGithubIssue( - {@required String feedbackText, - @required String fileName, - @required String feedbackType, - String email}) async { + {required String feedbackText, + required String fileName, + required String feedbackType, + String? email}) async { final PackageInfo packageInfo = await PackageInfo.fromPlatform(); return _github.issues .create( diff --git a/lib/core/services/in_app_review_service.dart b/lib/core/services/in_app_review_service.dart index 2e5b03008..f3da402d3 100644 --- a/lib/core/services/in_app_review_service.dart +++ b/lib/core/services/in_app_review_service.dart @@ -22,7 +22,7 @@ class InAppReviewService { /// Opens the Play Store on Android, the App Store with a review Future openStoreListing({ String appStoreId = AppInfo.appStoreId, - String microsoftStoreId, + String? microsoftStoreId, }) => _inAppReview.openStoreListing( appStoreId: appStoreId, diff --git a/lib/core/services/internal_info_service.dart b/lib/core/services/internal_info_service.dart index 5d30cc1af..d5ecbfe79 100644 --- a/lib/core/services/internal_info_service.dart +++ b/lib/core/services/internal_info_service.dart @@ -19,7 +19,7 @@ class InternalInfoService { final PackageInfo packageInfo = await PackageInfo.fromPlatform(); final NetworkingService networkingService = locator(); final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - String deviceName; + String? deviceName; if (Platform.isAndroid) { final AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; @@ -30,7 +30,7 @@ class InternalInfoService { } return "**Device Infos** \n" - "- **Device:** $deviceName \n" + "- **Device:** ${deviceName ?? 'Not Provided'} \n" "- **Version:** ${packageInfo.version} \n" "- **Connectivity:** ${await networkingService.getConnectionType()} \n" "- **Build number:** ${packageInfo.buildNumber} \n" diff --git a/lib/core/services/navigation_service.dart b/lib/core/services/navigation_service.dart index 06ff0599d..18f714a93 100644 --- a/lib/core/services/navigation_service.dart +++ b/lib/core/services/navigation_service.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; // Project imports: +import 'package:notredame/core/services/analytics_service.dart'; import 'package:notredame/core/constants/router_paths.dart'; import 'package:notredame/core/services/remote_config_service.dart'; import 'package:notredame/locator.dart'; @@ -14,16 +15,23 @@ import 'package:notredame/locator.dart'; /// Navigation service who doesn't use the BuildContext which allow us to call it from anywhere. class NavigationService { + static const String tag = "NavigationService"; + final RemoteConfigService remoteConfigService = locator(); final GlobalKey _navigatorKey = GlobalKey(); + /// Will be used to report event and error. + final AnalyticsService _analyticsService = locator(); + GlobalKey get navigatorKey => _navigatorKey; /// Pop the last route of the navigator if possible bool pop() { - if (_navigatorKey.currentState.canPop()) { - _navigatorKey.currentState.pop(); + final currentState = _navigatorKey.currentState; + + if (currentState != null && currentState.canPop()) { + currentState.pop(); return true; } return false; @@ -31,24 +39,36 @@ class NavigationService { /// Push a named route ([routeName] onto the navigator. Future pushNamed(String routeName, {dynamic arguments}) { + final currentState = _navigatorKey.currentState; + + if (currentState == null) { + _analyticsService.logError(tag, "Navigator state is null"); + return Future.error("Navigator state is null"); + } + if (remoteConfigService.outage) { - return _navigatorKey.currentState - .pushNamedAndRemoveUntil(RouterPaths.serviceOutage, (route) => false); + return currentState.pushNamedAndRemoveUntil( + RouterPaths.serviceOutage, (route) => false); } - return _navigatorKey.currentState - .pushNamed(routeName, arguments: arguments); + return currentState.pushNamed(routeName, arguments: arguments); } /// Replace the current route of the navigator by pushing the route named /// [routeName] and then delete the stack of previous routes Future pushNamedAndRemoveUntil(String routeName, [String removeUntilRouteNamed = RouterPaths.dashboard, - Object arguments]) { + Object? arguments]) { + final currentState = _navigatorKey.currentState; + if (currentState == null) { + _analyticsService.logError(tag, "Navigator state is null"); + return Future.error("Navigator state is null"); + } + if (remoteConfigService.outage) { - return _navigatorKey.currentState - .pushNamedAndRemoveUntil(RouterPaths.serviceOutage, (route) => false); + return currentState.pushNamedAndRemoveUntil( + RouterPaths.serviceOutage, (route) => false); } - return _navigatorKey.currentState.pushNamedAndRemoveUntil( + return currentState.pushNamedAndRemoveUntil( routeName, ModalRoute.withName(removeUntilRouteNamed), arguments: arguments); } diff --git a/lib/core/services/preferences_service.dart b/lib/core/services/preferences_service.dart index 110a42b23..6320bfd94 100644 --- a/lib/core/services/preferences_service.dart +++ b/lib/core/services/preferences_service.dart @@ -1,5 +1,4 @@ // Flutter imports: -import 'package:flutter/material.dart'; // Package imports: import 'package:shared_preferences/shared_preferences.dart'; @@ -22,7 +21,7 @@ class PreferencesService { PreferencesFlag.hasRatingBeenRequested ]; - Future setBool(PreferencesFlag flag, {@required bool value}) async { + Future setBool(PreferencesFlag flag, {required bool value}) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.setBool(flag.toString(), value); @@ -73,7 +72,7 @@ class PreferencesService { }); } - Future getPreferencesFlag(PreferencesFlag flag) async { + Future getPreferencesFlag(PreferencesFlag flag) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.get(flag.toString()); } @@ -100,35 +99,37 @@ class PreferencesService { return prefs.setString(flag.toString(), value.toIso8601String()); } - Future getBool(PreferencesFlag flag) async { + Future getBool(PreferencesFlag flag) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.getBool(flag.toString()); } - Future getInt(PreferencesFlag flag) async { + Future getInt(PreferencesFlag flag) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.getInt(flag.toString()); } - Future getString(PreferencesFlag flag) async { + Future getString(PreferencesFlag flag) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.getString(flag.toString()); } - Future getDynamicString(PreferencesFlag flag, String key) async { + Future getDynamicString(PreferencesFlag flag, String key) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.getString('${flag.toString()}_$key'); } - Future getDateTime(PreferencesFlag flag) async { + Future getDateTime(PreferencesFlag flag) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); - try { - return DateTime.parse(prefs.getString(flag.toString())); - } catch (e) { - return null; + final flagPreference = prefs.getString(flag.toString()); + + if (flagPreference != null) { + return DateTime.parse(flagPreference); } + + return null; } } diff --git a/lib/core/services/rive_animation_service.dart b/lib/core/services/rive_animation_service.dart index c1f910b7b..4a8079620 100644 --- a/lib/core/services/rive_animation_service.dart +++ b/lib/core/services/rive_animation_service.dart @@ -1,5 +1,4 @@ // Flutter imports: -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; // Package imports: @@ -10,7 +9,7 @@ import 'package:notredame/core/utils/animation_exception.dart'; /// Manage the rive animation for the application class RiveAnimationService { - Future loadRiveFile({@required String riveFileName}) async { + Future loadRiveFile({required String riveFileName}) async { final bytes = await rootBundle.load("assets/animations/$riveFileName.riv"); try { @@ -27,8 +26,8 @@ class RiveAnimationService { } void addControllerToAnimation( - {@required Artboard artboard, - RiveAnimationController controller}) { + {required Artboard artboard, + RiveAnimationController? controller}) { try { controller ??= SimpleAnimation(artboard.animations[0].name); artboard.addController(controller); diff --git a/lib/core/services/siren_flutter_service.dart b/lib/core/services/siren_flutter_service.dart index 407a2ed7c..238ef9f1c 100644 --- a/lib/core/services/siren_flutter_service.dart +++ b/lib/core/services/siren_flutter_service.dart @@ -8,7 +8,7 @@ import 'package:pub_semver/pub_semver.dart'; // SERVICES class SirenFlutterService { - Siren _siren; + late Siren _siren; SirenFlutterService() { _siren = Siren(); @@ -33,10 +33,10 @@ class SirenFlutterService { // Relay prompt update info to Siren package Future promptUpdate(BuildContext context, - {String title, - String message, - String buttonUpgradeText, - String buttonCancelText, + {required String title, + required String message, + String buttonUpgradeText = 'Upgrade', + String buttonCancelText = 'Cancel', bool forceUpgrade = false}) async { return _siren.promptUpdate(context, title: title, diff --git a/lib/core/utils/animation_exception.dart b/lib/core/utils/animation_exception.dart index cc89e1daf..d82d61faf 100644 --- a/lib/core/utils/animation_exception.dart +++ b/lib/core/utils/animation_exception.dart @@ -1,16 +1,13 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - /// Exception that can be thrown by the rive animation service class AnimationException implements Exception { final String _message; final String _prefix; - final Exception _nestedException; + final Exception? _nestedException; const AnimationException( - {@required String prefix, - @required String message, - Exception nestedException}) + {required String prefix, + required String message, + Exception? nestedException}) : _message = message, _prefix = prefix, _nestedException = nestedException; diff --git a/lib/core/utils/cache_exception.dart b/lib/core/utils/cache_exception.dart index 7f73f8e30..5f6745608 100644 --- a/lib/core/utils/cache_exception.dart +++ b/lib/core/utils/cache_exception.dart @@ -1,12 +1,9 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - /// Exception that can be thrown by the [CacheManager] class CacheException implements Exception { final String _message; final String _prefix; - const CacheException({@required String prefix, @required String message}) + const CacheException({required String prefix, required String message}) : _message = message, _prefix = prefix; diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 2c12245db..9a4654a9e 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -19,7 +19,7 @@ mixin Utils { } } - static double getGradeInPercentage(double grade, double maxGrade) { + static double getGradeInPercentage(double? grade, double? maxGrade) { if (grade == null || maxGrade == null || grade == 0.0 || maxGrade == 0.0) { return 0.0; } diff --git a/lib/core/viewmodels/choose_language_viewmodel.dart b/lib/core/viewmodels/choose_language_viewmodel.dart index 84f7bd280..017b45802 100644 --- a/lib/core/viewmodels/choose_language_viewmodel.dart +++ b/lib/core/viewmodels/choose_language_viewmodel.dart @@ -1,6 +1,3 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - // Package imports: import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:stacked/stacked.dart'; @@ -25,7 +22,7 @@ class ChooseLanguageViewModel extends BaseViewModel { /// Localization class of the application. final AppIntl _appIntl; - ChooseLanguageViewModel({@required AppIntl intl}) : _appIntl = intl; + ChooseLanguageViewModel({required AppIntl intl}) : _appIntl = intl; List get languages { return [_appIntl.settings_english, _appIntl.settings_french]; @@ -44,7 +41,6 @@ class ChooseLanguageViewModel extends BaseViewModel { default: throw Exception( 'No valid language for the index $index passed in parameters'); - break; } _navigationService.pop(); diff --git a/lib/core/viewmodels/contributors_viewmodel.dart b/lib/core/viewmodels/contributors_viewmodel.dart index 2272a88f9..523c4a22b 100644 --- a/lib/core/viewmodels/contributors_viewmodel.dart +++ b/lib/core/viewmodels/contributors_viewmodel.dart @@ -6,10 +6,10 @@ class ContributorsViewModel extends FutureViewModel { /// Create a GitHub Client, with anonymous authentication by default GitHub github = GitHub(); - Future> get contributors => _contributors; + Future>? get contributors => _contributors; /// List of contributors - Future> _contributors; + Future>? _contributors; @override Future futureToRun() { diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index 64b416b6d..46a6183ae 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -44,16 +44,16 @@ class DashboardViewModel extends FutureViewModel> { locator(); /// All dashboard displayable cards - Map _cards; + Map? _cards; /// Localization class of the application. final AppIntl _appIntl; /// Update code that must be used to prompt user for update if necessary. - UpdateCode updateCode; + UpdateCode? updateCode; /// Cards to display on dashboard - List _cardsToDisplay; + List? _cardsToDisplay; /// Percentage of completed days for the session double _progress = 0.0; @@ -91,10 +91,10 @@ class DashboardViewModel extends FutureViewModel> { } /// Get the status of all displayable cards - Map get cards => _cards; + Map? get cards => _cards; /// Get cards to display - List get cardsToDisplay => _cardsToDisplay; + List? get cardsToDisplay => _cardsToDisplay; ProgressBarText _currentProgressBarText = ProgressBarText.daysElapsedWithTotalDays; @@ -114,7 +114,7 @@ class DashboardViewModel extends FutureViewModel> { final PreferencesService preferencesService = locator(); final InAppReviewService inAppReviewService = locator(); - DateTime ratingTimerFlagDate = + DateTime? ratingTimerFlagDate = await preferencesService.getDateTime(PreferencesFlag.ratingTimer); final hasRatingBeenRequested = await preferencesService @@ -192,7 +192,7 @@ class DashboardViewModel extends FutureViewModel> { /// List of courses for the current session final List courses = []; - DashboardViewModel({@required AppIntl intl}) : _appIntl = intl; + DashboardViewModel({required AppIntl intl}) : _appIntl = intl; @override Future> futureToRun() async { @@ -251,19 +251,20 @@ class DashboardViewModel extends FutureViewModel> { final List grades = courses.map((course) { // Code copied from GradeButton.gradeString if (course.grade != null) { - return course.grade; + return course.grade!; } else if (course.summary != null && - course.summary.markOutOf > 0 && - !(course.inReviewPeriod && !course.reviewCompleted)) { + course.summary!.markOutOf > 0 && + !(course.inReviewPeriod && + (course.reviewCompleted != null && !course.reviewCompleted!))) { return _appIntl.grades_grade_in_percentage( - course.summary.currentMarkInPercent.round()); + course.summary!.currentMarkInPercent.round()); } return _appIntl.grades_not_available; }).toList(); await _appWidgetService.sendGradesData(GradesWidgetData( title: - "${_appIntl.grades_title} - ${_courseRepository.activeSessions.first.shortName ?? _appIntl.session_without}", + "${_appIntl.grades_title} - ${_courseRepository.activeSessions.first.shortName.isEmpty ? _appIntl.session_without : ''}", courseAcronyms: acronyms, grades: grades)); await _appWidgetService.updateWidget(WidgetType.grades); @@ -280,8 +281,8 @@ class DashboardViewModel extends FutureViewModel> { /// Change the order of [flag] card from [oldIndex] to [newIndex]. void setOrder(PreferencesFlag flag, int newIndex) { - _cardsToDisplay.remove(flag); - _cardsToDisplay.insert(newIndex, flag); + _cardsToDisplay?.remove(flag); + _cardsToDisplay?.insert(newIndex, flag); updatePreferences(); @@ -292,9 +293,9 @@ class DashboardViewModel extends FutureViewModel> { /// Hide [flag] card from dashboard by setting int value -1 void hideCard(PreferencesFlag flag) { - _cards.update(flag, (value) => -1); + _cards?.update(flag, (value) => -1); - _cardsToDisplay.remove(flag); + _cardsToDisplay?.remove(flag); updatePreferences(); @@ -305,7 +306,7 @@ class DashboardViewModel extends FutureViewModel> { /// Reset all card indexes to their default values void setAllCardsVisible() { - _cards.updateAll((key, value) { + _cards?.updateAll((key, value) { _settingsManager .setInt(key, key.index - PreferencesFlag.broadcastCard.index) .then((value) { @@ -329,11 +330,11 @@ class DashboardViewModel extends FutureViewModel> { if (_cards != null) { final orderedCards = SplayTreeMap.from( - _cards, (a, b) => _cards[a].compareTo(_cards[b])); + _cards!, (a, b) => _cards![a]!.compareTo(_cards![b]!)); orderedCards.forEach((key, value) { if (value >= 0) { - _cardsToDisplay.insert(value, key); + _cardsToDisplay?.insert(value, key); } }); } @@ -349,83 +350,79 @@ class DashboardViewModel extends FutureViewModel> { // Update pref _preferencesService.setString(PreferencesFlag.broadcastChange, remoteConfigService.dashboardMessageEn); - if (_cards[PreferencesFlag.broadcastCard] < 0) { - _cards.updateAll((key, value) { - if (value >= 0) { - return value + 1; - } else { - return value; - } + if (_cards != null && _cards![PreferencesFlag.broadcastCard]! < 0) { + _cards?.updateAll((key, value) { + return value >= 0 ? value + 1 : value; }); - _cards[PreferencesFlag.broadcastCard] = 0; + _cards![PreferencesFlag.broadcastCard] = 0; } } } Future> futureToRunSessionProgressBar() async { - String progressBarText = - await _settingsManager.getString(PreferencesFlag.progressBarText); - - progressBarText ??= ProgressBarText.daysElapsedWithTotalDays.toString(); + try { + final progressBarText = + await _settingsManager.getString(PreferencesFlag.progressBarText) ?? + ProgressBarText.daysElapsedWithTotalDays.toString(); - _currentProgressBarText = ProgressBarText.values - .firstWhere((e) => e.toString() == progressBarText); + _currentProgressBarText = ProgressBarText.values + .firstWhere((e) => e.toString() == progressBarText); - setBusyForObject(progress, true); - return _courseRepository - .getSessions() - // ignore: invalid_return_type_for_catch_error - .catchError(onError) - .whenComplete(() { + setBusyForObject(progress, true); + final sessions = await _courseRepository.getSessions(); _sessionDays = getSessionDays(); _progress = getSessionProgress(); + return sessions; + } catch (error) { + onError(error); + } finally { setBusyForObject(progress, false); - }); + } + return []; } Future> futureToRunSchedule() async { - return _courseRepository - .getCoursesActivities(fromCacheOnly: true) - .then((value) { + try { + var courseActivities = + await _courseRepository.getCoursesActivities(fromCacheOnly: true); setBusyForObject(_todayDateEvents, true); setBusyForObject(_tomorrowDateEvents, true); _todayDateEvents.clear(); _tomorrowDateEvents.clear(); - final todayDate = _settingsManager.dateTimeNow; - _courseRepository - .getCoursesActivities() - // ignore: return_type_invalid_for_catch_error - .catchError(onError) - .whenComplete(() async { - if (_todayDateEvents.isEmpty) { - final DateTime tomorrowDate = todayDate.add(const Duration(days: 1)); - // Build the list - for (final CourseActivity course - in _courseRepository.coursesActivities) { - final DateTime dateOnly = course.startDateTime; - if (isSameDay(todayDate, dateOnly) && - todayDate.compareTo(course.endDateTime) < 0) { - _todayDateEvents.add(course); - } else if (isSameDay(tomorrowDate, dateOnly)) { - _tomorrowDateEvents.add(course); - } + courseActivities = await _courseRepository.getCoursesActivities(); + + if (_todayDateEvents.isEmpty && + _courseRepository.coursesActivities != null) { + final DateTime tomorrowDate = todayDate.add(const Duration(days: 1)); + // Build the list + for (final CourseActivity course + in _courseRepository.coursesActivities!) { + final DateTime dateOnly = course.startDateTime; + if (isSameDay(todayDate, dateOnly) && + todayDate.compareTo(course.endDateTime) < 0) { + _todayDateEvents.add(course); + } else if (isSameDay(tomorrowDate, dateOnly)) { + _tomorrowDateEvents.add(course); } } - _todayDateEvents - .sort((a, b) => a.startDateTime.compareTo(b.startDateTime)); - _tomorrowDateEvents - .sort((a, b) => a.startDateTime.compareTo(b.startDateTime)); - - _todayDateEvents = await removeLaboratoryGroup(_todayDateEvents); - _tomorrowDateEvents = await removeLaboratoryGroup(_tomorrowDateEvents); + } - setBusyForObject(_todayDateEvents, false); - setBusyForObject(_tomorrowDateEvents, false); - }); + _todayDateEvents + .sort((a, b) => a.startDateTime.compareTo(b.startDateTime)); + _tomorrowDateEvents + .sort((a, b) => a.startDateTime.compareTo(b.startDateTime)); - return value; - }); + _todayDateEvents = await removeLaboratoryGroup(_todayDateEvents); + _tomorrowDateEvents = await removeLaboratoryGroup(_tomorrowDateEvents); + return courseActivities ?? []; + } catch (error) { + onError(error); + } finally { + setBusyForObject(_todayDateEvents, false); + setBusyForObject(_tomorrowDateEvents, false); + } + return []; } Future> removeLaboratoryGroup( @@ -435,7 +432,7 @@ class DashboardViewModel extends FutureViewModel> { for (final courseAcronym in todayDateEvents) { final courseKey = courseAcronym.courseGroup.split('-')[0]; - final String activityCodeToUse = await _settingsManager.getDynamicString( + final String? activityCodeToUse = await _settingsManager.getDynamicString( PreferencesFlag.scheduleLaboratoryGroup, courseKey); if (activityCodeToUse == ActivityCode.labGroupA) { @@ -454,10 +451,13 @@ class DashboardViewModel extends FutureViewModel> { /// Update cards order and display status in preferences void updatePreferences() { - for (final MapEntry element in _cards.entries) { - _cards[element.key] = _cardsToDisplay.indexOf(element.key); + if (_cards == null || _cardsToDisplay == null) { + return; + } + for (final MapEntry element in _cards!.entries) { + _cards![element.key] = _cardsToDisplay!.indexOf(element.key); _settingsManager - .setInt(element.key, _cardsToDisplay.indexOf(element.key)) + .setInt(element.key, _cardsToDisplay!.indexOf(element.key)) .then((value) { if (!value) { Fluttertoast.showToast(msg: _appIntl.error); @@ -492,25 +492,23 @@ class DashboardViewModel extends FutureViewModel> { } /// Get the list of courses for the Grades card. - // ignore: missing_return Future> futureToRunGrades() async { if (!busy(courses)) { - setBusyForObject(courses, true); - if (_courseRepository.sessions == null || - _courseRepository.sessions.isEmpty) { - // ignore: return_type_invalid_for_catch_error - await _courseRepository.getSessions().catchError(onError); - } + try { + setBusyForObject(courses, true); + if (_courseRepository.sessions == null || + _courseRepository.sessions!.isEmpty) { + await _courseRepository.getSessions(); + } - // Determine current sessions - if (_courseRepository.activeSessions.isEmpty) { - setBusyForObject(courses, false); - return []; - } - final currentSession = _courseRepository.activeSessions.first; + // Determine current sessions + if (_courseRepository.activeSessions.isEmpty) { + return []; + } + final currentSession = _courseRepository.activeSessions.first; - return _courseRepository.getCourses(fromCacheOnly: true).then( - (coursesCached) { + final coursesCached = + await _courseRepository.getCourses(fromCacheOnly: true); courses.clear(); for (final Course course in coursesCached) { if (course.session == currentSession.shortName) { @@ -518,31 +516,29 @@ class DashboardViewModel extends FutureViewModel> { } } notifyListeners(); - // ignore: return_type_invalid_for_catch_error - _courseRepository.getCourses().catchError(onError).then((value) { - if (value != null) { - // Update the courses list - courses.clear(); - for (final Course course in value) { - if (course.session == currentSession.shortName) { - courses.add(course); - } - } - } - }).whenComplete(() { - setBusyForObject(courses, false); - }); - return courses; - }, onError: onError); + final fetchedCourses = await _courseRepository.getCourses(); + // Update the courses list + courses.clear(); + for (final Course course in fetchedCourses) { + if (course.session == currentSession.shortName) { + courses.add(course); + } + } + } catch (error) { + onError(error); + } finally { + setBusyForObject(courses, false); + } } + return courses; } /// Prompt the update for the app if the navigation service arguments passed /// is not none. When [UpdateCode] is forced, the user will be force to update. /// If [UpdateCode] is not forced, the user will be prompted to update. static Future promptUpdate( - BuildContext context, UpdateCode updateCode) async { + BuildContext context, UpdateCode? updateCode) async { if (updateCode != null && updateCode != UpdateCode.none) { final appIntl = AppIntl.of(context); @@ -551,11 +547,11 @@ class DashboardViewModel extends FutureViewModel> { switch (updateCode) { case UpdateCode.force: isAForcedUpdate = true; - message = appIntl.update_version_message_force; + message = appIntl!.update_version_message_force; break; case UpdateCode.ask: isAForcedUpdate = false; - message = appIntl.update_version_message; + message = appIntl!.update_version_message; break; case UpdateCode.none: return; diff --git a/lib/core/viewmodels/emergency_viewmodel.dart b/lib/core/viewmodels/emergency_viewmodel.dart index aeb410d82..ad412464f 100644 --- a/lib/core/viewmodels/emergency_viewmodel.dart +++ b/lib/core/viewmodels/emergency_viewmodel.dart @@ -2,7 +2,6 @@ import 'dart:convert'; // Flutter imports: -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; // Package imports: @@ -13,12 +12,11 @@ import 'package:webview_flutter/webview_flutter.dart'; import 'package:notredame/core/viewmodels/security_viewmodel.dart'; class EmergencyViewModel extends SecurityViewModel { - WebViewController webViewController; - - EmergencyViewModel({@required AppIntl intl}) : super(intl: intl); + EmergencyViewModel({required AppIntl intl}) : super(intl: intl); /// used to load the emergency procedures html files inside the webView - Future loadHtmlFromAssets(String filename, Brightness brightness) async { + Future loadHtmlFromAssets(String filename, Brightness brightness, + WebViewController webViewController) async { final String fileText = await rootBundle.loadString(filename); final String data = Uri.dataFromString( diff --git a/lib/core/viewmodels/faq_viewmodel.dart b/lib/core/viewmodels/faq_viewmodel.dart index 4381124fb..e82e8275c 100644 --- a/lib/core/viewmodels/faq_viewmodel.dart +++ b/lib/core/viewmodels/faq_viewmodel.dart @@ -17,7 +17,7 @@ class FaqViewModel extends BaseViewModel { final LaunchUrlService _launchUrlService = locator(); - Locale get locale => _settingsManager.locale; + Locale? get locale => _settingsManager.locale; String mailtoStr(String email, String subject) { return 'mailto:$email?subject=$subject'; @@ -30,7 +30,7 @@ class FaqViewModel extends BaseViewModel { Future openMail(String addressEmail, BuildContext context) async { var email = ""; if (addressEmail == AppInfo.email) { - email = mailtoStr(addressEmail, AppIntl.of(context).email_subject); + email = mailtoStr(addressEmail, AppIntl.of(context)!.email_subject); } else { email = mailtoStr(addressEmail, ""); } diff --git a/lib/core/viewmodels/feedback_viewmodel.dart b/lib/core/viewmodels/feedback_viewmodel.dart index a6ca32345..279221e94 100644 --- a/lib/core/viewmodels/feedback_viewmodel.dart +++ b/lib/core/viewmodels/feedback_viewmodel.dart @@ -2,9 +2,6 @@ import 'dart:io'; import 'dart:typed_data'; -// Flutter imports: -import 'package:flutter/material.dart'; - // Package imports: import 'package:feedback/feedback.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -37,7 +34,7 @@ class FeedbackViewModel extends FutureViewModel { // get the list of issues List get myIssues => _myIssues; - FeedbackViewModel({@required AppIntl intl}) : _appIntl = intl; + FeedbackViewModel({required AppIntl intl}) : _appIntl = intl; /// Create a Github issue with [UserFeedback] and the screenshot associated. Future sendFeedback( @@ -55,21 +52,19 @@ class FeedbackViewModel extends FutureViewModel { feedbackText: feedback.text, fileName: fileName, feedbackType: reportType.name, - email: feedback.extra.containsKey('email') - ? feedback.extra['email'].toString() + email: feedback.extra != null && feedback.extra!.containsKey('email') + ? feedback.extra!['email'].toString() : null); - if (issue != null) { - setBusy(true); - _myIssues.add(FeedbackIssue(issue)); - // Sort by state open first and by number descending - _myIssues.sort( - (a, b) => b.state.compareTo(a.state) * 100000 + b.number - a.number); - setBusy(false); - // Save the issue number in the preferences - _preferencesService.setString( - PreferencesFlag.ghIssues, _myIssues.map((e) => e.number).join(',')); - } + setBusy(true); + _myIssues.add(FeedbackIssue(issue)); + // Sort by state open first and by number descending + _myIssues.sort( + (a, b) => b.state.compareTo(a.state) * 100000 + b.number - a.number); + setBusy(false); + // Save the issue number in the preferences + _preferencesService.setString( + PreferencesFlag.ghIssues, _myIssues.map((e) => e.number).join(',')); file.deleteSync(); @@ -80,7 +75,8 @@ class FeedbackViewModel extends FutureViewModel { } List encodeScreenshotForGithub(Uint8List screenshot) { - return image.encodePng(image.copyResize(image.decodeImage(screenshot), + return image.encodePng(image.copyResize( + image.decodeImage(screenshot) ?? image.Image(0, 0), width: _screenshotImageWidth)); } @@ -88,7 +84,7 @@ class FeedbackViewModel extends FutureViewModel { @override Future futureToRun() async { // Get the issues number from the preferences - final String issuesString = + final String? issuesString = await _preferencesService.getString(PreferencesFlag.ghIssues); // If there is no issues, return 0 diff --git a/lib/core/viewmodels/grades_details_viewmodel.dart b/lib/core/viewmodels/grades_details_viewmodel.dart index 69dabe0d1..89988a992 100644 --- a/lib/core/viewmodels/grades_details_viewmodel.dart +++ b/lib/core/viewmodels/grades_details_viewmodel.dart @@ -27,28 +27,20 @@ class GradesDetailsViewModel extends FutureViewModel { /// Used to get the current course selected of the student Course course; - GradesDetailsViewModel({this.course, @required AppIntl intl}) + GradesDetailsViewModel({required this.course, required AppIntl intl}) : _appIntl = intl; @override Future futureToRun() async { - setBusyForObject(course, true); - - // ignore: return_type_invalid_for_catch_error - await _courseRepository - .getCourseSummary(course) - // ignore: return_type_invalid_for_catch_error - .catchError(onError) - ?.then((value) { - if (value != null) { - course = value; - } - })?.whenComplete(() { + try { + setBusyForObject(course, true); + course = await _courseRepository.getCourseSummary(course); + notifyListeners(); + } catch (e) { + onError(e); + } finally { setBusyForObject(course, false); - }); - - notifyListeners(); - + } return course; } @@ -68,18 +60,14 @@ class GradesDetailsViewModel extends FutureViewModel { Future refresh() async { try { setBusyForObject(course, true); - await _courseRepository.getCourseSummary(course)?.then((value) { - if (value != null) { - course = value; - } - }); + course = await _courseRepository.getCourseSummary(course); notifyListeners(); - setBusyForObject(course, false); return true; - } on Exception catch (error) { + } catch (error) { onError(error); - setBusyForObject(course, false); return false; + } finally { + setBusyForObject(course, false); } } diff --git a/lib/core/viewmodels/grades_viewmodel.dart b/lib/core/viewmodels/grades_viewmodel.dart index 1b2fb41d2..1ea14e0c6 100644 --- a/lib/core/viewmodels/grades_viewmodel.dart +++ b/lib/core/viewmodels/grades_viewmodel.dart @@ -30,25 +30,27 @@ class GradesViewModel extends FutureViewModel>> { /// session. final List sessionOrder = []; - GradesViewModel({@required AppIntl intl}) : _appIntl = intl; + GradesViewModel({required AppIntl intl}) : _appIntl = intl; @override - Future>> futureToRun() async => - _courseRepository.getCourses(fromCacheOnly: true).then((coursesCached) { - setBusy(true); - _buildCoursesBySession(coursesCached); - // ignore: return_type_invalid_for_catch_error - _courseRepository.getCourses().catchError(onError).then((value) { - if (value != null) { - // Update the courses list - _buildCoursesBySession(_courseRepository.courses); - } - }).whenComplete(() { - setBusy(false); - }); - - return coursesBySession; - }); + Future>> futureToRun() async { + try { + final coursesCached = + await _courseRepository.getCourses(fromCacheOnly: true); + setBusy(true); + _buildCoursesBySession(coursesCached); + await _courseRepository.getCourses(); + if (_courseRepository.courses != null) { + // Update the courses list + _buildCoursesBySession(_courseRepository.courses!); + } + } catch (error) { + onError(error); + } finally { + setBusy(false); + } + return coursesBySession; + } @override // ignore: type_annotate_public_apis @@ -58,10 +60,11 @@ class GradesViewModel extends FutureViewModel>> { /// Reload the courses from Signets and rebuild the view. Future refresh() async { - // ignore: return_type_invalid_for_catch_error try { await _courseRepository.getCourses(); - _buildCoursesBySession(_courseRepository.courses); + if (_courseRepository.courses != null) { + _buildCoursesBySession(_courseRepository.courses!); + } notifyListeners(); } on Exception catch (error) { onError(error); diff --git a/lib/core/viewmodels/login_viewmodel.dart b/lib/core/viewmodels/login_viewmodel.dart index 749955761..e200a3365 100644 --- a/lib/core/viewmodels/login_viewmodel.dart +++ b/lib/core/viewmodels/login_viewmodel.dart @@ -1,5 +1,4 @@ // Flutter imports: -import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; // Package imports: @@ -39,14 +38,14 @@ class LoginViewModel extends BaseViewModel { /// Used to enable/disable the "log in" button bool get canSubmit => _universalCode.isNotEmpty && _password.isNotEmpty; - LoginViewModel({@required AppIntl intl}) : _appIntl = intl; + LoginViewModel({required AppIntl intl}) : _appIntl = intl; /// Use to get the value associated to each settings key final PreferencesService _preferencesService = locator(); /// Validate the format of the universal code - String validateUniversalCode(String value) { - if (value.isEmpty) { + String? validateUniversalCode(String? value) { + if (value == null || value.isEmpty) { _universalCode = ""; return _appIntl.login_error_field_required; } else if (!_universalCodeMatcher.hasMatch(value)) { @@ -58,8 +57,8 @@ class LoginViewModel extends BaseViewModel { } /// Validate there is a password typed - String validatePassword(String value) { - if (value.isEmpty) { + String? validatePassword(String? value) { + if (value == null || value.isEmpty) { _password = ""; return _appIntl.login_error_field_required; } diff --git a/lib/core/viewmodels/more_viewmodel.dart b/lib/core/viewmodels/more_viewmodel.dart index 8a941098a..0f3112816 100644 --- a/lib/core/viewmodels/more_viewmodel.dart +++ b/lib/core/viewmodels/more_viewmodel.dart @@ -47,24 +47,26 @@ class MoreViewModel extends FutureViewModel { /// Used to redirect on the dashboard. final NavigationService navigationService = locator(); - String _appVersion; + String? _appVersion; final AppIntl _appIntl; /// Get the application version - String get appVersion => _appVersion; + String? get appVersion => _appVersion; - MoreViewModel({@required AppIntl intl}) : _appIntl = intl; + MoreViewModel({required AppIntl intl}) : _appIntl = intl; @override Future futureToRun() async { - setBusy(true); - - await PackageInfo.fromPlatform() - .then((value) => _appVersion = value.version) - .onError((error, stackTrace) => null); - - setBusy(false); + try { + setBusy(true); + final packageInfo = await PackageInfo.fromPlatform(); + _appVersion = packageInfo.version; + } catch (error) { + onError(error); + } finally { + setBusy(false); + } return true; } diff --git a/lib/core/viewmodels/not_found_viewmodel.dart b/lib/core/viewmodels/not_found_viewmodel.dart index 3e4b7945d..98dc1c958 100644 --- a/lib/core/viewmodels/not_found_viewmodel.dart +++ b/lib/core/viewmodels/not_found_viewmodel.dart @@ -1,6 +1,3 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - // Package imports: import 'package:rive/rive.dart'; import 'package:stacked/stacked.dart'; @@ -29,10 +26,10 @@ class NotFoundViewModel extends BaseViewModel { final String notFoundPageName; - Artboard _artboard; - Artboard get artboard => _artboard; + Artboard? _artboard; + Artboard? get artboard => _artboard; - NotFoundViewModel({@required String pageName}) : notFoundPageName = pageName { + NotFoundViewModel({required String pageName}) : notFoundPageName = pageName { _analyticsService.logEvent( tag, "An unknown page ($pageName) has been access from the app."); } @@ -56,7 +53,9 @@ class NotFoundViewModel extends BaseViewModel { void startRiveAnimation() { try { - _riveAnimationService.addControllerToAnimation(artboard: _artboard); + if (artboard != null) { + _riveAnimationService.addControllerToAnimation(artboard: _artboard!); + } } on Exception catch (e, stacktrace) { _analyticsService.logError(tag, "An Error has occured during rive animation start.", e, stacktrace); diff --git a/lib/core/viewmodels/profile_viewmodel.dart b/lib/core/viewmodels/profile_viewmodel.dart index 58139e95f..ecb5cdaca 100644 --- a/lib/core/viewmodels/profile_viewmodel.dart +++ b/lib/core/viewmodels/profile_viewmodel.dart @@ -1,6 +1,3 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - // Package imports: import 'package:ets_api_clients/models.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -36,9 +33,9 @@ class ProfileViewModel extends FutureViewModel> { /// Return the universal access code of the student String get universalAccessCode => - _userRepository?.monETSUser?.universalCode ?? ''; + _userRepository.monETSUser?.universalCode ?? ''; - ProfileViewModel({@required AppIntl intl}) : _appIntl = intl; + ProfileViewModel({required AppIntl intl}) : _appIntl = intl; double get programProgression { final ProgramCredits programCredits = ProgramCredits(); @@ -83,11 +80,11 @@ class ProfileViewModel extends FutureViewModel> { /// Return the list of programs for the student List get programList { - if (_programList == null || _programList.isEmpty) { + if (_programList.isEmpty) { _programList = []; } if (_userRepository.programs != null) { - _programList = _userRepository.programs; + _programList = _userRepository.programs!; } _programList.sort((a, b) => b.status.compareTo(a.status)); @@ -100,22 +97,22 @@ class ProfileViewModel extends FutureViewModel> { bool isLoadingEvents = false; @override - Future> futureToRun() => _userRepository - .getInfo(fromCacheOnly: true) - .then((value) => _userRepository.getPrograms(fromCacheOnly: true)) - .then((value) { - setBusyForObject(isLoadingEvents, true); - _userRepository - .getInfo() - // ignore: return_type_invalid_for_catch_error - .catchError(onError) - // ignore: return_type_invalid_for_catch_error - .then((value) => _userRepository.getPrograms().catchError(onError)) - .whenComplete(() { - setBusyForObject(isLoadingEvents, false); - }); - return value; - }); + Future> futureToRun() async { + try { + await _userRepository.getInfo(fromCacheOnly: true); + await _userRepository.getPrograms(fromCacheOnly: true); + + setBusyForObject(isLoadingEvents, true); + + await _userRepository.getInfo(); + return await _userRepository.getPrograms(); + } catch (error) { + onError(error); + } finally { + setBusyForObject(isLoadingEvents, false); + } + return _userRepository.programs ?? []; + } Future refresh() async { try { diff --git a/lib/core/viewmodels/schedule_settings_viewmodel.dart b/lib/core/viewmodels/schedule_settings_viewmodel.dart index 7f035fa59..d7dac0071 100644 --- a/lib/core/viewmodels/schedule_settings_viewmodel.dart +++ b/lib/core/viewmodels/schedule_settings_viewmodel.dart @@ -1,3 +1,6 @@ +// Dart imports: +import 'package:collection/collection.dart'; + // Package imports: import 'package:calendar_view/calendar_view.dart'; import 'package:enum_to_string/enum_to_string.dart'; @@ -19,11 +22,11 @@ class ScheduleSettingsViewModel final CourseRepository _courseRepository = locator(); /// Current calendar format - CalendarFormat _calendarFormat; + CalendarFormat? _calendarFormat; - CalendarFormat get calendarFormat => _calendarFormat; + CalendarFormat? get calendarFormat => _calendarFormat; - set calendarFormat(CalendarFormat format) { + set calendarFormat(CalendarFormat? format) { setBusy(true); _settingsManager.setString(PreferencesFlag.scheduleCalendarFormat, EnumToString.convertToString(format)); @@ -39,11 +42,11 @@ class ScheduleSettingsViewModel ]; /// Current starting day of week - StartingDayOfWeek _startingDayOfWeek; + StartingDayOfWeek? _startingDayOfWeek; - StartingDayOfWeek get startingDayOfWeek => _startingDayOfWeek; + StartingDayOfWeek? get startingDayOfWeek => _startingDayOfWeek; - set startingDayOfWeek(StartingDayOfWeek day) { + set startingDayOfWeek(StartingDayOfWeek? day) { setBusy(true); _settingsManager.setString(PreferencesFlag.scheduleStartWeekday, EnumToString.convertToString(day)); @@ -59,11 +62,11 @@ class ScheduleSettingsViewModel ]; /// Current weekend day shown - WeekDays _otherDayOfWeek; + WeekDays? _otherDayOfWeek; - WeekDays get otherDayOfWeek => _otherDayOfWeek; + WeekDays? get otherDayOfWeek => _otherDayOfWeek; - set otherDayOfWeek(WeekDays day) { + set otherDayOfWeek(WeekDays? day) { setBusy(true); _settingsManager.setString(PreferencesFlag.scheduleOtherWeekday, EnumToString.convertToString(day)); @@ -135,7 +138,7 @@ class ScheduleSettingsViewModel /// This function is used to save the state of the selected course settings /// for a given course that has different laboratory group Future selectScheduleActivity( - String courseAcronym, ScheduleActivity scheduleActivityToSave) async { + String courseAcronym, ScheduleActivity? scheduleActivityToSave) async { setBusy(true); if (scheduleActivityToSave == null) { await _settingsManager.setDynamicString( @@ -145,8 +148,8 @@ class ScheduleSettingsViewModel PreferencesFlag.scheduleLaboratoryGroup, courseAcronym, scheduleActivityToSave.activityCode); + _selectedScheduleActivity[courseAcronym] = scheduleActivityToSave; } - _selectedScheduleActivity[courseAcronym] = scheduleActivityToSave; setBusy(false); } @@ -173,9 +176,9 @@ class ScheduleSettingsViewModel _scheduleActivitiesByCourse[activity.courseAcronym] = [activity]; } else { // Add the activity to the course. - if (!_scheduleActivitiesByCourse[activity.courseAcronym] - .contains(activity)) { - _scheduleActivitiesByCourse[activity.courseAcronym].add(activity); + final course = _scheduleActivitiesByCourse[activity.courseAcronym]; + if (course != null && !course.contains(activity)) { + course.add(activity); } } } @@ -188,8 +191,8 @@ class ScheduleSettingsViewModel final scheduleActivityCode = await _settingsManager.getDynamicString( PreferencesFlag.scheduleLaboratoryGroup, courseKey); final scheduleActivity = _scheduleActivitiesByCourse[courseKey] - .firstWhere((element) => element.activityCode == scheduleActivityCode, - orElse: () => null); + ?.firstWhereOrNull( + (element) => element.activityCode == scheduleActivityCode); if (scheduleActivity != null) { _selectedScheduleActivity[courseKey] = scheduleActivity; } diff --git a/lib/core/viewmodels/schedule_viewmodel.dart b/lib/core/viewmodels/schedule_viewmodel.dart index 9321a7b28..e9123526f 100644 --- a/lib/core/viewmodels/schedule_viewmodel.dart +++ b/lib/core/viewmodels/schedule_viewmodel.dart @@ -41,7 +41,7 @@ class ScheduleViewModel extends FutureViewModel> { Map> _coursesActivities = {}; /// Courses associated to the student - List courses; + List? courses; /// Day currently selected DateTime selectedDate; @@ -71,9 +71,9 @@ class ScheduleViewModel extends FutureViewModel> { AppTheme.schedulePaletteLight.toList(); /// Get current locale - Locale get locale => _settingsManager.locale; + Locale? get locale => _settingsManager.locale; - ScheduleViewModel({@required AppIntl intl, DateTime initialSelectedDate}) + ScheduleViewModel({required AppIntl intl, DateTime? initialSelectedDate}) : _appIntl = intl, selectedDate = initialSelectedDate ?? DateTime.now(), focusedDate = ValueNotifier(initialSelectedDate ?? DateTime.now()); @@ -110,7 +110,7 @@ class ScheduleViewModel extends FutureViewModel> { List selectedDateCalendarEvents(DateTime date) { return _coursesActivities[DateTime(date.year, date.month, date.day)] ?.map((eventData) => calendarEventData(eventData)) - ?.toList() ?? + .toList() ?? []; } @@ -121,7 +121,7 @@ class ScheduleViewModel extends FutureViewModel> { final associatedCourses = courses?.where( (element) => element.acronym == eventData.courseGroup.split('-')[0]); final associatedCourse = - associatedCourses?.isNotEmpty == true ? associatedCourses.first : null; + associatedCourses?.isNotEmpty == true ? associatedCourses?.first : null; return CalendarEventData( title: "${eventData.courseGroup.split('-')[0]}\n$courseLocation\n${eventData.activityName}", @@ -137,7 +137,7 @@ class ScheduleViewModel extends FutureViewModel> { if (!courseColors.containsKey(courseName)) { courseColors[courseName] = schedulePaletteThemeLight.removeLast(); } - return courseColors[courseName]; + return courseColors[courseName] ?? Colors.red; } List selectedWeekCalendarEvents() { @@ -167,56 +167,56 @@ class ScheduleViewModel extends FutureViewModel> { } bool get showWeekEvents => - settings[PreferencesFlag.scheduleShowWeekEvents] as bool ?? false; + settings[PreferencesFlag.scheduleShowWeekEvents] as bool; bool isLoadingEvents = false; - bool get calendarViewSetting => - settings[PreferencesFlag.scheduleListView] as bool ?? true; + bool get calendarViewSetting { + if (busy(settings)) { + return false; + } + return settings[PreferencesFlag.scheduleListView] as bool; + } @override - Future> futureToRun() => - _courseRepository.getCoursesActivities(fromCacheOnly: true).then((value) { - setBusyForObject(isLoadingEvents, true); - _courseRepository - .getCoursesActivities() - // ignore: return_type_invalid_for_catch_error - .catchError(onError) - .then((value1) async { - if (value1 != null) { - // Reload the list of activities - coursesActivities; - - await _courseRepository - .getCourses(fromCacheOnly: true) - .then((value2) { - courses = value2; - }); - if (_coursesActivities.isNotEmpty) { - if (calendarFormat == CalendarFormat.week) { - calendarEvents = selectedWeekCalendarEvents(); - } else { - calendarEvents = selectedMonthCalendarEvents(); - } - } + Future> futureToRun() async { + loadSettings(); + List? activities = + await _courseRepository.getCoursesActivities(fromCacheOnly: true); + try { + setBusyForObject(isLoadingEvents, true); + + final fetchedCourseActivities = + await _courseRepository.getCoursesActivities(); + if (fetchedCourseActivities != null) { + activities = fetchedCourseActivities; + // Reload the list of activities + coursesActivities; + + courses = await _courseRepository.getCourses(fromCacheOnly: true); + + if (_coursesActivities.isNotEmpty) { + if (calendarFormat == CalendarFormat.week) { + calendarEvents = selectedWeekCalendarEvents(); + } else { + calendarEvents = selectedMonthCalendarEvents(); } - _courseRepository - .getScheduleActivities() - // ignore: return_type_invalid_for_catch_error - .catchError(onError) - .then((value) async { - await assignScheduleActivities(value); - }).whenComplete(() { - setBusyForObject(isLoadingEvents, false); - }); - }); - return value; - }); + } + } + final scheduleActivities = + await _courseRepository.getScheduleActivities(); + await assignScheduleActivities(scheduleActivities); + } catch (e) { + onError(e); + } finally { + setBusyForObject(isLoadingEvents, false); + } + return activities ?? []; + } Future assignScheduleActivities( List listOfSchedules) async { - if (listOfSchedules == null || - listOfSchedules.isEmpty || + if (listOfSchedules.isEmpty || !listOfSchedules.any((element) => element.activityCode == ActivityCode.labGroupA || element.activityCode == ActivityCode.labGroupB)) return; @@ -230,7 +230,7 @@ class ScheduleViewModel extends FutureViewModel> { if (!scheduleActivitiesByCourse.containsKey(activity.courseAcronym)) { scheduleActivitiesByCourse[activity.courseAcronym] = [activity]; } else { - scheduleActivitiesByCourse[activity.courseAcronym].add(activity); + scheduleActivitiesByCourse[activity.courseAcronym]?.add(activity); } } } @@ -245,7 +245,7 @@ class ScheduleViewModel extends FutureViewModel> { } Future loadSettings() async { - setBusy(true); + setBusyForObject(settings, true); settings.clear(); settings.addAll(await _settingsManager.getScheduleSettings()); calendarFormat = @@ -253,16 +253,15 @@ class ScheduleViewModel extends FutureViewModel> { await loadSettingsScheduleActivities(); - setBusy(false); + setBusyForObject(settings, false); } Future loadSettingsScheduleActivities() async { for (final courseAcronym in scheduleActivitiesByCourse.keys) { - final String activityCodeToUse = await _settingsManager.getDynamicString( + final String? activityCodeToUse = await _settingsManager.getDynamicString( PreferencesFlag.scheduleLaboratoryGroup, courseAcronym); final scheduleActivityToSet = scheduleActivitiesByCourse[courseAcronym] - .firstWhere((element) => element.activityCode == activityCodeToUse, - orElse: () => null); + ?.firstWhere((element) => element.activityCode == activityCodeToUse); if (scheduleActivityToSet != null) { settingsScheduleActivities[courseAcronym] = scheduleActivityToSet.name; } else { @@ -281,7 +280,8 @@ class ScheduleViewModel extends FutureViewModel> { // Build the map if (_courseRepository.coursesActivities != null) { - for (final CourseActivity course in _courseRepository.coursesActivities) { + for (final CourseActivity course + in _courseRepository.coursesActivities!) { final DateTime dateOnly = course.startDateTime.subtract(Duration( hours: course.startDateTime.hour, minutes: course.startDateTime.minute)); @@ -335,15 +335,22 @@ class ScheduleViewModel extends FutureViewModel> { coursesActivities; } - // Access the array using the same instance inside the map. This is only required - // since the version 3.0.0 of table_calendar with the eventLoaders argument. - DateTime dateInArray; - return _coursesActivities.keys.any((element) { + // TODO: maybe use containsKey and put the _courseActivities key to a string... + DateTime? dateInArray; + final courseActivitiesContains = _coursesActivities.keys.any((element) { dateInArray = element; return isSameDay(element, date); - }) - ? _coursesActivities[dateInArray] - : []; + }); + if (courseActivitiesContains) { + return _coursesActivities[dateInArray] ?? []; + } + return []; + + // List activities = []; + // if (_coursesActivities.containsKey(date)) { + // activities = _coursesActivities[date] ?? []; + // } + // return activities; } Future setCalendarFormat(CalendarFormat format) async { @@ -357,10 +364,11 @@ class ScheduleViewModel extends FutureViewModel> { try { setBusyForObject(isLoadingEvents, true); await _courseRepository.getCoursesActivities(); - setBusyForObject(isLoadingEvents, false); notifyListeners(); } on Exception catch (error) { onError(error); + } finally { + setBusyForObject(isLoadingEvents, false); } } diff --git a/lib/core/viewmodels/security_viewmodel.dart b/lib/core/viewmodels/security_viewmodel.dart index 32cc23981..dcc01a45f 100644 --- a/lib/core/viewmodels/security_viewmodel.dart +++ b/lib/core/viewmodels/security_viewmodel.dart @@ -13,13 +13,13 @@ import 'package:notredame/core/constants/markers.dart'; import 'package:notredame/core/models/emergency_procedure.dart'; class SecurityViewModel extends BaseViewModel { - GoogleMapController controller; + GoogleMapController? controller; final List emergencyProcedureList; final List markersList; - SecurityViewModel({@required AppIntl intl}) + SecurityViewModel({required AppIntl intl}) : emergencyProcedureList = emergencyProcedures(intl), markersList = markers(intl); @@ -49,6 +49,6 @@ class SecurityViewModel extends BaseViewModel { /// Used to set the color of the map void setMapStyle(String mapStyle) { - controller.setMapStyle(mapStyle); + controller?.setMapStyle(mapStyle); } } diff --git a/lib/core/viewmodels/settings_viewmodel.dart b/lib/core/viewmodels/settings_viewmodel.dart index 20ebed7e2..6d01fbff1 100644 --- a/lib/core/viewmodels/settings_viewmodel.dart +++ b/lib/core/viewmodels/settings_viewmodel.dart @@ -17,17 +17,19 @@ class SettingsViewModel extends FutureViewModel { final AppIntl _appIntl; /// Current locale - String _currentLocale; + String? _currentLocale; /// Current theme - ThemeMode _selectedTheme; + ThemeMode? _selectedTheme; - ThemeMode get selectedTheme => _selectedTheme; + ThemeMode? get selectedTheme => _selectedTheme; /// Set theme - set selectedTheme(ThemeMode value) { - _settingsManager.setThemeMode(value); - _selectedTheme = value; + set selectedTheme(ThemeMode? value) { + if (value != null) { + _settingsManager.setThemeMode(value); + _selectedTheme = value; + } } String get currentLocale { @@ -46,13 +48,13 @@ class SettingsViewModel extends FutureViewModel { _currentLocale = value; } - SettingsViewModel({@required AppIntl intl}) : _appIntl = intl; + SettingsViewModel({required AppIntl intl}) : _appIntl = intl; @override Future futureToRun() async { setBusy(true); await _settingsManager.fetchLanguageAndThemeMode(); - _currentLocale = _settingsManager.locale.languageCode; + _currentLocale = _settingsManager.locale?.languageCode; _selectedTheme = _settingsManager.themeMode; setBusy(false); return true; diff --git a/lib/core/viewmodels/startup_viewmodel.dart b/lib/core/viewmodels/startup_viewmodel.dart index de5525d10..38497eca8 100644 --- a/lib/core/viewmodels/startup_viewmodel.dart +++ b/lib/core/viewmodels/startup_viewmodel.dart @@ -62,7 +62,7 @@ class StartUpViewModel extends BaseViewModel { ]; for (final PreferencesFlag flag in flagsToCheck) { - final Object object = + final Object? object = await _preferencesService.getPreferencesFlag(flag); if (object is String) { diff --git a/lib/main.dart b/lib/main.dart index 6be1ef4db..c04e222b6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -80,7 +80,7 @@ class ETSMobile extends StatelessWidget { feedbackBuilder: (context, onSubmit, scrollController) => CustomFeedbackForm( onSubmit: onSubmit, - scrollController: scrollController, + scrollController: scrollController ?? ScrollController(), ), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, diff --git a/lib/ui/router.dart b/lib/ui/router.dart index 8c857bddc..1ea109b1b 100644 --- a/lib/ui/router.dart +++ b/lib/ui/router.dart @@ -45,9 +45,9 @@ Route generateRoute(RouteSettings routeSettings) { return MaterialPageRoute( settings: RouteSettings(name: routeSettings.name), builder: (_) => - FaqView(backgroundColor: routeSettings.arguments as Color)); + FaqView(backgroundColor: routeSettings.arguments! as Color)); case RouterPaths.dashboard: - final code = (routeSettings.arguments as UpdateCode) ?? UpdateCode.none; + final code = (routeSettings.arguments as UpdateCode?) ?? UpdateCode.none; return PageRouteBuilder( settings: RouteSettings( name: routeSettings.name, arguments: routeSettings.arguments), @@ -78,7 +78,7 @@ Route generateRoute(RouteSettings routeSettings) { }, settings: RouteSettings(name: routeSettings.name), pageBuilder: (_, __, ___) => - GradesDetailsView(course: routeSettings.arguments as Course)); + GradesDetailsView(course: routeSettings.arguments! as Course)); case RouterPaths.ets: return PageRouteBuilder( settings: RouteSettings(name: routeSettings.name), @@ -86,7 +86,7 @@ Route generateRoute(RouteSettings routeSettings) { case RouterPaths.webView: return PageRouteBuilder( pageBuilder: (_, __, ___) => - LinkWebView(routeSettings.arguments as QuickLink)); + LinkWebView(routeSettings.arguments! as QuickLink)); case RouterPaths.security: return PageRouteBuilder( settings: RouteSettings(name: routeSettings.name), diff --git a/lib/ui/utils/discovery_components.dart b/lib/ui/utils/discovery_components.dart index 10196faee..68a99f37e 100644 --- a/lib/ui/utils/discovery_components.dart +++ b/lib/ui/utils/discovery_components.dart @@ -27,16 +27,17 @@ List discoveryComponents(BuildContext context) { maxHeight: MediaQuery.of(context).size.height * 0.6), child: Column( children: [ - _buildHeader(AppIntl.of(context).discovery_navbar_dashboard_title, + _buildHeader( + AppIntl.of(context)!.discovery_navbar_dashboard_title, context), Expanded( child: ListView( padding: EdgeInsets.zero, children: [ - Text( - AppIntl.of(context).discovery_navbar_dashboard_details), + Text(AppIntl.of(context)! + .discovery_navbar_dashboard_details), const Text('\n'), - if (AppIntl.of(context).localeName == "fr") + if (AppIntl.of(context)!.localeName == "fr") Image.asset( 'assets/animations/discovery/fr/dashboard_swipe.gif') else @@ -50,9 +51,9 @@ List discoveryComponents(BuildContext context) { ), ), Discovery( - path: null, + path: '', featureId: DiscoveryIds.bottomBarDashboardRestore, - title: AppIntl.of(context).dashboard_restore_all_cards_title, + title: AppIntl.of(context)!.dashboard_restore_all_cards_title, details: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.4), @@ -64,10 +65,10 @@ List discoveryComponents(BuildContext context) { children: [ _buildSkipDiscoveryButton(context), Text( - AppIntl.of(context).discovery_page_dashboard_restore, + AppIntl.of(context)!.discovery_page_dashboard_restore, ), const Text('\n'), - if (AppIntl.of(context).localeName == "fr") + if (AppIntl.of(context)!.localeName == "fr") Image.asset( 'assets/animations/discovery/fr/dashboard_restore.gif') else @@ -89,16 +90,16 @@ List discoveryComponents(BuildContext context) { maxHeight: MediaQuery.of(context).size.height * 0.6), child: Column( children: [ - _buildHeader( - AppIntl.of(context).discovery_navbar_schedule_title, context), + _buildHeader(AppIntl.of(context)!.discovery_navbar_schedule_title, + context), Expanded( child: ListView( padding: EdgeInsets.zero, children: [ - Text(AppIntl.of(context).discovery_navbar_schedule_details, + Text(AppIntl.of(context)!.discovery_navbar_schedule_details, textAlign: TextAlign.justify), const Text('\n'), - if (AppIntl.of(context).localeName == "fr") + if (AppIntl.of(context)!.localeName == "fr") Image.asset( 'assets/animations/discovery/fr/schedule_calendar.png') else @@ -121,15 +122,15 @@ List discoveryComponents(BuildContext context) { child: Column( children: [ _buildHeader( - AppIntl.of(context).discovery_navbar_student_title, context), + AppIntl.of(context)!.discovery_navbar_student_title, context), Expanded( child: ListView( padding: EdgeInsets.zero, children: [ - Text(AppIntl.of(context).discovery_navbar_student_details, + Text(AppIntl.of(context)!.discovery_navbar_student_details, textAlign: TextAlign.justify), const Text('\n'), - if (AppIntl.of(context).localeName == "fr") + if (AppIntl.of(context)!.localeName == "fr") Image.asset( 'assets/animations/discovery/fr/grade_details.gif') else @@ -152,15 +153,15 @@ List discoveryComponents(BuildContext context) { child: Column( children: [ _buildHeader( - AppIntl.of(context).discovery_navbar_ets_title, context), + AppIntl.of(context)!.discovery_navbar_ets_title, context), Expanded( child: ListView( padding: EdgeInsets.zero, children: [ - Text(AppIntl.of(context).discovery_navbar_ets_details, + Text(AppIntl.of(context)!.discovery_navbar_ets_details, textAlign: TextAlign.justify), const Text('\n'), - if (AppIntl.of(context).localeName == "fr") + if (AppIntl.of(context)!.localeName == "fr") Image.asset('assets/animations/discovery/fr/ets_link.gif') else Image.asset( @@ -182,15 +183,15 @@ List discoveryComponents(BuildContext context) { child: Column( children: [ _buildHeader( - AppIntl.of(context).discovery_navbar_more_title, context), + AppIntl.of(context)!.discovery_navbar_more_title, context), Expanded( child: ListView( padding: EdgeInsets.zero, children: [ - Text(AppIntl.of(context).discovery_navbar_more_details, + Text(AppIntl.of(context)!.discovery_navbar_more_details, textAlign: TextAlign.justify), const Text('\n'), - if (AppIntl.of(context).localeName == "fr") + if (AppIntl.of(context)!.localeName == "fr") Image.asset('assets/animations/discovery/fr/more.jpg') else Image.asset('assets/animations/discovery/en/more.jpg'), @@ -204,9 +205,9 @@ List discoveryComponents(BuildContext context) { ]), GroupDiscovery(name: DiscoveryGroupIds.pageSchedule, discoveries: [ Discovery( - path: null, + path: '', featureId: DiscoveryIds.detailsScheduleSettings, - title: AppIntl.of(context).schedule_settings_title, + title: AppIntl.of(context)!.schedule_settings_title, details: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.6), @@ -217,10 +218,10 @@ List discoveryComponents(BuildContext context) { padding: EdgeInsets.zero, children: [ _buildSkipDiscoveryButton(context), - Text(AppIntl.of(context).discovery_navbar_schedule_details, + Text(AppIntl.of(context)!.discovery_navbar_schedule_details, textAlign: TextAlign.justify), const Text('\n'), - if (AppIntl.of(context).localeName == "fr") + if (AppIntl.of(context)!.localeName == "fr") Image.asset( 'assets/animations/discovery/fr/schedule_settings.gif') else @@ -236,9 +237,9 @@ List discoveryComponents(BuildContext context) { ]), GroupDiscovery(name: DiscoveryGroupIds.pageStudent, discoveries: [ Discovery( - path: null, + path: '', featureId: DiscoveryIds.detailsStudentGradeButton, - title: AppIntl.of(context).grades_title, + title: AppIntl.of(context)!.grades_title, details: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.25), @@ -250,12 +251,12 @@ List discoveryComponents(BuildContext context) { children: [ _buildSkipDiscoveryButton(context), Text( - AppIntl.of(context) + AppIntl.of(context)! .discovery_page_student_grades_session, textAlign: TextAlign.justify), const Text('\n'), Text( - AppIntl.of(context) + AppIntl.of(context)! .discovery_page_student_grades_grade_button, textAlign: TextAlign.justify), ], @@ -266,9 +267,9 @@ List discoveryComponents(BuildContext context) { ), ), Discovery( - path: null, + path: '', featureId: DiscoveryIds.detailsStudentProfile, - title: AppIntl.of(context).profile_title, + title: AppIntl.of(context)!.profile_title, details: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.2), @@ -279,7 +280,7 @@ List discoveryComponents(BuildContext context) { padding: EdgeInsets.zero, children: [ _buildSkipDiscoveryButton(context), - Text(AppIntl.of(context).discovery_page_student_profile, + Text(AppIntl.of(context)!.discovery_page_student_profile, textAlign: TextAlign.justify), ], ), @@ -291,7 +292,7 @@ List discoveryComponents(BuildContext context) { ]), GroupDiscovery(name: DiscoveryGroupIds.pageGradeDetails, discoveries: [ Discovery( - path: null, + path: '', featureId: DiscoveryIds.detailsGradeDetailsEvaluations, title: "", details: ConstrainedBox( @@ -304,7 +305,7 @@ List discoveryComponents(BuildContext context) { padding: EdgeInsets.zero, children: [ _buildSkipDiscoveryButton(context), - Text(AppIntl.of(context).discovery_page_grade_details, + Text(AppIntl.of(context)!.discovery_page_grade_details, textAlign: TextAlign.justify), ], ), @@ -316,9 +317,9 @@ List discoveryComponents(BuildContext context) { ]), GroupDiscovery(name: DiscoveryGroupIds.pageMore, discoveries: [ Discovery( - path: null, + path: '', featureId: DiscoveryIds.detailsMoreBugReport, - title: AppIntl.of(context).more_report_bug, + title: AppIntl.of(context)!.more_report_bug, details: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.2), @@ -329,7 +330,7 @@ List discoveryComponents(BuildContext context) { padding: EdgeInsets.zero, children: [ _buildSkipDiscoveryButton(context), - Text(AppIntl.of(context).discovery_page_more_report_bug, + Text(AppIntl.of(context)!.discovery_page_more_report_bug, textAlign: TextAlign.justify), ], ), @@ -339,9 +340,9 @@ List discoveryComponents(BuildContext context) { ), ), Discovery( - path: null, + path: '', featureId: DiscoveryIds.detailsMoreContributors, - title: AppIntl.of(context).more_contributors, + title: AppIntl.of(context)!.more_contributors, details: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.2), @@ -352,7 +353,7 @@ List discoveryComponents(BuildContext context) { padding: EdgeInsets.zero, children: [ _buildSkipDiscoveryButton(context), - Text(AppIntl.of(context).discovery_page_more_contributors, + Text(AppIntl.of(context)!.discovery_page_more_contributors, textAlign: TextAlign.justify), ], ), @@ -362,9 +363,9 @@ List discoveryComponents(BuildContext context) { ), ), Discovery( - path: null, + path: '', featureId: DiscoveryIds.detailsMoreFaq, - title: AppIntl.of(context).need_help, + title: AppIntl.of(context)!.need_help, details: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.2), @@ -375,7 +376,7 @@ List discoveryComponents(BuildContext context) { padding: EdgeInsets.zero, children: [ _buildSkipDiscoveryButton(context), - Text(AppIntl.of(context).discovery_page_faq, + Text(AppIntl.of(context)!.discovery_page_faq, textAlign: TextAlign.justify), ], ), @@ -385,9 +386,9 @@ List discoveryComponents(BuildContext context) { ), ), Discovery( - path: null, + path: '', featureId: DiscoveryIds.detailsMoreSettings, - title: AppIntl.of(context).more_settings, + title: AppIntl.of(context)!.more_settings, details: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.2), @@ -398,7 +399,7 @@ List discoveryComponents(BuildContext context) { padding: EdgeInsets.zero, children: [ _buildSkipDiscoveryButton(context), - Text(AppIntl.of(context).discovery_page_more_settings, + Text(AppIntl.of(context)!.discovery_page_more_settings, textAlign: TextAlign.justify), ], ), @@ -408,9 +409,9 @@ List discoveryComponents(BuildContext context) { ), ), Discovery( - path: null, + path: '', featureId: DiscoveryIds.detailsMoreThankYou, - title: AppIntl.of(context).title_ets_mobile, + title: AppIntl.of(context)!.title_ets_mobile, details: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.2), @@ -421,7 +422,7 @@ List discoveryComponents(BuildContext context) { padding: EdgeInsets.zero, children: [ _buildSkipDiscoveryButton(context), - Text(AppIntl.of(context).discovery_page_thankyou_message, + Text(AppIntl.of(context)!.discovery_page_thankyou_message, textAlign: TextAlign.justify), ], ), @@ -443,7 +444,7 @@ Padding _buildHeader(String title, BuildContext context) { Text(title, style: Theme.of(context) .textTheme - .headline6 + .headline6! .copyWith(color: Colors.white)), _buildSkipDiscoveryButton(context) ], @@ -456,7 +457,7 @@ Align _buildSkipDiscoveryButton(BuildContext context) { alignment: Alignment.topRight, child: TextButton( onPressed: () => dismissDiscovery(context), - child: Text(AppIntl.of(context).skip_discovery, + child: Text(AppIntl.of(context)!.skip_discovery, style: const TextStyle(color: AppTheme.etsLightRed)), ), ); diff --git a/lib/ui/views/about_view.dart b/lib/ui/views/about_view.dart index 9cb3769cb..1699400a6 100644 --- a/lib/ui/views/about_view.dart +++ b/lib/ui/views/about_view.dart @@ -16,7 +16,7 @@ class AboutView extends StatefulWidget { } class _AboutViewState extends State with TickerProviderStateMixin { - AnimationController _controller; + late AnimationController _controller; bool _completed = false; bool _easterEggTrigger = false; @@ -54,7 +54,7 @@ class _AboutViewState extends State with TickerProviderStateMixin { appBar: AppBar( foregroundColor: Colors.white, title: Text( - AppIntl.of(context).more_about_applets_title, + AppIntl.of(context)!.more_about_applets_title, ), elevation: 0, backgroundColor: Colors.transparent, @@ -112,7 +112,7 @@ class _AboutViewState extends State with TickerProviderStateMixin { Padding( padding: const EdgeInsets.all(16.0), child: Text( - AppIntl.of(context).more_applets_about_details, + AppIntl.of(context)!.more_applets_about_details, style: const TextStyle(color: Colors.white), ), ), @@ -127,42 +127,42 @@ class _AboutViewState extends State with TickerProviderStateMixin { color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubWebsite, AppIntl.of(context))), + Urls.clubWebsite, AppIntl.of(context)!)), IconButton( icon: const FaIcon( FontAwesomeIcons.github, color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubGithub, AppIntl.of(context))), + Urls.clubGithub, AppIntl.of(context)!)), IconButton( icon: const FaIcon( FontAwesomeIcons.facebook, color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubFacebook, AppIntl.of(context))), + Urls.clubFacebook, AppIntl.of(context)!)), IconButton( icon: const FaIcon( FontAwesomeIcons.twitter, color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubTwitter, AppIntl.of(context))), + Urls.clubTwitter, AppIntl.of(context)!)), IconButton( icon: const FaIcon( FontAwesomeIcons.youtube, color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubYoutube, AppIntl.of(context))), + Urls.clubYoutube, AppIntl.of(context)!)), IconButton( icon: const FaIcon( FontAwesomeIcons.discord, color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubDiscord, AppIntl.of(context))), + Urls.clubDiscord, AppIntl.of(context)!)), ], ), ), diff --git a/lib/ui/views/choose_language_view.dart b/lib/ui/views/choose_language_view.dart index 98d77759e..40aa840fc 100644 --- a/lib/ui/views/choose_language_view.dart +++ b/lib/ui/views/choose_language_view.dart @@ -25,7 +25,7 @@ class _ChooseLanguageViewState extends State { itemBuilder: (BuildContext context, int index) { return Card( color: Utils.getColorByBrightness( - context, Colors.white, Colors.grey[900]), + context, Colors.white, Colors.grey[900]!), child: Column(mainAxisSize: MainAxisSize.min, children: [ ListTile( title: Text(model.languages[index]), @@ -44,7 +44,7 @@ class _ChooseLanguageViewState extends State { Widget build(BuildContext context) { return ViewModelBuilder.reactive( viewModelBuilder: () => - ChooseLanguageViewModel(intl: AppIntl.of(context)), + ChooseLanguageViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) => Scaffold( backgroundColor: Utils.getColorByBrightness( context, AppTheme.etsLightRed, AppTheme.primaryDark), @@ -63,7 +63,7 @@ class _ChooseLanguageViewState extends State { child: Align( alignment: Alignment.centerLeft, child: Text( - AppIntl.of(context).choose_language_title, + AppIntl.of(context)!.choose_language_title, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, @@ -77,7 +77,7 @@ class _ChooseLanguageViewState extends State { child: Align( alignment: Alignment.centerLeft, child: Text( - AppIntl.of(context).choose_language_subtitle, + AppIntl.of(context)!.choose_language_subtitle, style: const TextStyle( fontSize: 16, color: Colors.white), ), diff --git a/lib/ui/views/contributors_view.dart b/lib/ui/views/contributors_view.dart index a54d6ce45..1fcca95d5 100644 --- a/lib/ui/views/contributors_view.dart +++ b/lib/ui/views/contributors_view.dart @@ -20,7 +20,7 @@ class ContributorsView extends StatelessWidget { builder: (context, model, child) { return BaseScaffold( appBar: AppBar( - title: Text(AppIntl.of(context).more_contributors), + title: Text(AppIntl.of(context)!.more_contributors), ), showBottomBar: false, body: FutureBuilder>( @@ -30,14 +30,15 @@ class ContributorsView extends StatelessWidget { return buildLoading(); } return ListView.builder( - itemCount: snapshot.data.length, + itemCount: snapshot.data!.length, itemBuilder: (context, index) => ListTile( - title: Text(snapshot.data[index].login), + title: Text(snapshot.data![index].login ?? ''), leading: CircleAvatar( - backgroundImage: - NetworkImage(snapshot.data[index].avatarUrl)), + backgroundImage: NetworkImage( + snapshot.data![index].avatarUrl ?? '')), onTap: () => Utils.launchURL( - snapshot.data[index].htmlUrl, AppIntl.of(context)), + snapshot.data![index].htmlUrl ?? '', + AppIntl.of(context)!), ), ); }, diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index 75a616224..a9e77a21a 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -33,7 +33,7 @@ import 'package:notredame/ui/widgets/haptics_container.dart'; class DashboardView extends StatefulWidget { final UpdateCode updateCode; - const DashboardView({Key key, this.updateCode}) : super(key: key); + const DashboardView({Key? key, required this.updateCode}) : super(key: key); @override _DashboardViewState createState() => _DashboardViewState(); @@ -41,7 +41,7 @@ class DashboardView extends StatefulWidget { class _DashboardViewState extends State with TickerProviderStateMixin { - Text progressBarText; + Text? progressBarText; final NavigationService _navigationService = locator(); final AnalyticsService _analyticsService = locator(); static const String tag = "DashboardView"; @@ -59,12 +59,12 @@ class _DashboardViewState extends State @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( - viewModelBuilder: () => DashboardViewModel(intl: AppIntl.of(context)), + viewModelBuilder: () => DashboardViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) { return BaseScaffold( isInteractionLimitedWhileLoading: false, appBar: AppBar( - title: Text(AppIntl.of(context).title_dashboard), + title: Text(AppIntl.of(context)!.title_dashboard), centerTitle: false, automaticallyImplyLeading: false, actions: [ @@ -98,7 +98,7 @@ class _DashboardViewState extends State // always try to build broadcast cart so the user doesn't miss out on // important info if they dismissed it previously - for (final PreferencesFlag element in model.cardsToDisplay) { + for (final PreferencesFlag element in model.cardsToDisplay ?? []) { switch (element) { case PreferencesFlag.broadcastCard: if (model.remoteConfigService.dashboardMessageActive) { @@ -139,7 +139,7 @@ class _DashboardViewState extends State alignment: Alignment.centerLeft, child: Container( padding: const EdgeInsets.fromLTRB(17, 15, 0, 0), - child: Text(AppIntl.of(context).card_applets_title, + child: Text(AppIntl.of(context)!.card_applets_title, style: Theme.of(context).primaryTextTheme.headline6), )), Column( @@ -147,7 +147,7 @@ class _DashboardViewState extends State children: [ Container( padding: const EdgeInsets.fromLTRB(17, 10, 15, 10), - child: Text(AppIntl.of(context).card_applets_text, + child: Text(AppIntl.of(context)!.card_applets_text, style: Theme.of(context).primaryTextTheme.bodyText2), ), Container( @@ -158,7 +158,8 @@ class _DashboardViewState extends State IconButton( onPressed: () { _analyticsService.logEvent(tag, "Facebook clicked"); - Utils.launchURL(Urls.clubFacebook, AppIntl.of(context)); + Utils.launchURL( + Urls.clubFacebook, AppIntl.of(context)!); }, icon: const FaIcon( FontAwesomeIcons.facebook, @@ -169,7 +170,7 @@ class _DashboardViewState extends State onPressed: () { _analyticsService.logEvent(tag, "Instagram clicked"); Utils.launchURL( - Urls.clubInstagram, AppIntl.of(context)); + Urls.clubInstagram, AppIntl.of(context)!); }, icon: const FaIcon( FontAwesomeIcons.instagram, @@ -179,7 +180,7 @@ class _DashboardViewState extends State IconButton( onPressed: () { _analyticsService.logEvent(tag, "Github clicked"); - Utils.launchURL(Urls.clubGithub, AppIntl.of(context)); + Utils.launchURL(Urls.clubGithub, AppIntl.of(context)!); }, icon: const FaIcon( FontAwesomeIcons.github, @@ -189,7 +190,7 @@ class _DashboardViewState extends State IconButton( onPressed: () { _analyticsService.logEvent(tag, "Email clicked"); - Utils.launchURL(Urls.clubEmail, AppIntl.of(context)); + Utils.launchURL(Urls.clubEmail, AppIntl.of(context)!); }, icon: const FaIcon( FontAwesomeIcons.envelope, @@ -199,7 +200,7 @@ class _DashboardViewState extends State IconButton( onPressed: () { _analyticsService.logEvent(tag, "Discord clicked"); - Utils.launchURL(Urls.clubDiscord, AppIntl.of(context)); + Utils.launchURL(Urls.clubDiscord, AppIntl.of(context)!); }, icon: const FaIcon( FontAwesomeIcons.discord, @@ -227,7 +228,7 @@ class _DashboardViewState extends State alignment: Alignment.centerLeft, child: Container( padding: const EdgeInsets.fromLTRB(17, 15, 0, 0), - child: Text(AppIntl.of(context).progress_bar_title, + child: Text(AppIntl.of(context)!.progress_bar_title, style: Theme.of(context).textTheme.headline6), )), if (model.progress >= 0.0) @@ -263,7 +264,7 @@ class _DashboardViewState extends State child: Center( child: progressBarText ?? Text( - AppIntl.of(context).progress_bar_message( + AppIntl.of(context)!.progress_bar_message( model.sessionDays[0], model.sessionDays[1]), style: const TextStyle(color: Colors.white), ), @@ -275,7 +276,7 @@ class _DashboardViewState extends State Container( padding: const EdgeInsets.all(16), child: Center( - child: Text(AppIntl.of(context).session_without), + child: Text(AppIntl.of(context)!.session_without), ), ), ]), @@ -289,19 +290,19 @@ class _DashboardViewState extends State if (model.currentProgressBarText == ProgressBarText.daysElapsedWithTotalDays) { progressBarText = Text( - AppIntl.of(context) + AppIntl.of(context)! .progress_bar_message(model.sessionDays[0], model.sessionDays[1]), style: const TextStyle(color: Colors.white), ); } else if (model.currentProgressBarText == ProgressBarText.percentage) { progressBarText = Text( - AppIntl.of(context).progress_bar_message_percentage( + AppIntl.of(context)!.progress_bar_message_percentage( ((model.sessionDays[0] / model.sessionDays[1]) * 100).round()), style: const TextStyle(color: Colors.white), ); } else { progressBarText = Text( - AppIntl.of(context).progress_bar_message_remaining_days( + AppIntl.of(context)!.progress_bar_message_remaining_days( model.sessionDays[1] - model.sessionDays[0]), style: const TextStyle(color: Colors.white), ); @@ -309,9 +310,9 @@ class _DashboardViewState extends State } Widget _buildScheduleCard(DashboardViewModel model, PreferencesFlag flag) { - var title = AppIntl.of(context).title_schedule; + var title = AppIntl.of(context)!.title_schedule; if (model.todayDateEvents.isEmpty && model.tomorrowDateEvents.isNotEmpty) { - title = title + AppIntl.of(context).card_schedule_tomorrow; + title = title + AppIntl.of(context)!.card_schedule_tomorrow; } return DismissibleCard( isBusy: model.busy(model.todayDateEvents) || @@ -339,7 +340,7 @@ class _DashboardViewState extends State SizedBox( height: 100, child: Center( - child: Text(AppIntl.of(context).schedule_no_event))) + child: Text(AppIntl.of(context)!.schedule_no_event))) else _buildEventList(model.tomorrowDateEvents) else @@ -381,7 +382,7 @@ class _DashboardViewState extends State child: GestureDetector( onTap: () => _navigationService .pushNamedAndRemoveUntil(RouterPaths.student), - child: Text(AppIntl.of(context).grades_title, + child: Text(AppIntl.of(context)!.grades_title, style: Theme.of(context).textTheme.headline6), ), ), @@ -390,7 +391,7 @@ class _DashboardViewState extends State SizedBox( height: 100, child: Center( - child: Text(AppIntl.of(context) + child: Text(AppIntl.of(context)! .grades_msg_no_grades .split("\n") .first)), @@ -400,8 +401,7 @@ class _DashboardViewState extends State padding: const EdgeInsets.fromLTRB(17, 10, 15, 10), child: Wrap( children: model.courses - .map((course) => - GradeButton(course, showDiscovery: false)) + .map((course) => GradeButton(course)) .toList(), ), ) @@ -442,7 +442,7 @@ class _DashboardViewState extends State ], ), // main text - AutoSizeText(model.broadcastMessage ?? "", + AutoSizeText(model.broadcastMessage, style: Theme.of(context).primaryTextTheme.bodyText2) ]), )); @@ -491,8 +491,14 @@ class _DashboardViewState extends State newIndex -= 1; } - final PreferencesFlag elementMoved = model.cards.keys - .firstWhere((element) => model.cards[element] == oldIndex); + // Should not happen becase dismiss card will not be called if the card is null. + if (model.cards == null) { + _analyticsService.logError(tag, "Cards list is null"); + throw Exception("Cards is null"); + } + + final PreferencesFlag elementMoved = model.cards!.keys + .firstWhere((element) => model.cards![element] == oldIndex); model.setOrder(elementMoved, newIndex); } diff --git a/lib/ui/views/emergency_view.dart b/lib/ui/views/emergency_view.dart index bfa797267..da51d372c 100644 --- a/lib/ui/views/emergency_view.dart +++ b/lib/ui/views/emergency_view.dart @@ -25,7 +25,7 @@ class _EmergencyViewState extends State { @override Widget build(BuildContext context) => ViewModelBuilder.reactive( - viewModelBuilder: () => EmergencyViewModel(intl: AppIntl.of(context)), + viewModelBuilder: () => EmergencyViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) => Scaffold( appBar: AppBar(title: Text(widget.title)), floatingActionButtonLocation: @@ -33,15 +33,15 @@ class _EmergencyViewState extends State { floatingActionButton: FloatingActionButton.extended( onPressed: () { Utils.launchURL( - 'tel:${AppIntl.of(context).security_emergency_number}', - AppIntl.of(context)) + 'tel:${AppIntl.of(context)!.security_emergency_number}', + AppIntl.of(context)!) .catchError((error) { ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text(error.toString()))); }); }, label: Text( - AppIntl.of(context).security_reach_security, + AppIntl.of(context)!.security_reach_security, style: const TextStyle(color: Colors.white, fontSize: 20), ), icon: const Icon(Icons.phone, size: 30, color: Colors.white), @@ -49,9 +49,8 @@ class _EmergencyViewState extends State { ), body: WebView( onWebViewCreated: (WebViewController webViewController) async { - model.webViewController = webViewController; - await model.loadHtmlFromAssets( - widget.description, Theme.of(context).brightness); + await model.loadHtmlFromAssets(widget.description, + Theme.of(context).brightness, webViewController); }, ), ), diff --git a/lib/ui/views/faq_view.dart b/lib/ui/views/faq_view.dart index 632974d4e..f6e377037 100644 --- a/lib/ui/views/faq_view.dart +++ b/lib/ui/views/faq_view.dart @@ -12,7 +12,7 @@ import 'package:notredame/core/models/faq_actions.dart'; import 'package:notredame/core/viewmodels/faq_viewmodel.dart'; class FaqView extends StatefulWidget { - final Color backgroundColor; + final Color? backgroundColor; const FaqView({this.backgroundColor}); @@ -33,7 +33,7 @@ class _FaqViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ getTitle(), - getSubtitle(AppIntl.of(context).questions_and_answers), + getSubtitle(AppIntl.of(context)!.questions_and_answers), Padding( padding: const EdgeInsets.only(left: 15.0, right: 15.0), child: CarouselSlider( @@ -58,8 +58,10 @@ class _FaqViewState extends State { const BorderRadius.all(Radius.circular(8.0)), ), child: getQuestionCard( - question.title[model.locale.languageCode], - question.description[model.locale.languageCode], + question.title[model.locale?.languageCode] ?? '', + question.description[ + model.locale?.languageCode] ?? + '', ), ); }, @@ -67,7 +69,7 @@ class _FaqViewState extends State { }).toList(), ), ), - getSubtitle(AppIntl.of(context).actions), + getSubtitle(AppIntl.of(context)!.actions), Expanded( child: ListView.builder( padding: const EdgeInsets.only(top: 1.0), @@ -76,8 +78,8 @@ class _FaqViewState extends State { final action = faq.actions[index]; return getActionCard( - action.title[model.locale.languageCode], - action.description[model.locale.languageCode], + action.title[model.locale?.languageCode] ?? '', + action.description[model.locale?.languageCode] ?? '', action.type, action.link, action.iconName, @@ -119,9 +121,9 @@ class _FaqViewState extends State { const SizedBox(width: 8.0), Expanded( child: Text( - AppIntl.of(context).need_help, + AppIntl.of(context)!.need_help, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.headline5.copyWith( + style: Theme.of(context).textTheme.headline5!.copyWith( color: widget.backgroundColor == Colors.white ? Colors.black : Colors.white, @@ -138,7 +140,7 @@ class _FaqViewState extends State { padding: const EdgeInsets.only(left: 18.0, top: 18.0, bottom: 10.0), child: Text( subtitle, - style: Theme.of(context).textTheme.headline5.copyWith( + style: Theme.of(context).textTheme.headline5!.copyWith( color: widget.backgroundColor == Colors.white ? Colors.black : Colors.white, @@ -159,7 +161,7 @@ class _FaqViewState extends State { Text( title, textScaleFactor: 1.0, - style: Theme.of(context).textTheme.bodyText2.copyWith( + style: Theme.of(context).textTheme.bodyText2!.copyWith( fontSize: 20, color: Theme.of(context).brightness == Brightness.light ? Colors.black @@ -171,7 +173,7 @@ class _FaqViewState extends State { Text( description, textScaleFactor: 1.0, - style: Theme.of(context).textTheme.bodyText2.copyWith( + style: Theme.of(context).textTheme.bodyText2!.copyWith( fontSize: 16, color: Theme.of(context).brightness == Brightness.light ? Colors.black @@ -246,7 +248,7 @@ class _FaqViewState extends State { children: [ Text( title, - style: Theme.of(context).textTheme.bodyText2.copyWith( + style: Theme.of(context).textTheme.bodyText2!.copyWith( fontSize: 18, color: Theme.of(context).brightness == Brightness.light ? Colors.black @@ -257,7 +259,7 @@ class _FaqViewState extends State { const SizedBox(height: 10.0), Text( description, - style: Theme.of(context).textTheme.bodyText2.copyWith( + style: Theme.of(context).textTheme.bodyText2!.copyWith( fontSize: 16, color: Theme.of(context).brightness == Brightness.light ? Colors.black diff --git a/lib/ui/views/feedback_view.dart b/lib/ui/views/feedback_view.dart index 2ce12be95..470b1e170 100644 --- a/lib/ui/views/feedback_view.dart +++ b/lib/ui/views/feedback_view.dart @@ -22,13 +22,13 @@ class _FeedbackViewState extends State { @override Widget build(BuildContext context) => ViewModelBuilder.reactive( - viewModelBuilder: () => FeedbackViewModel(intl: AppIntl.of(context)), + viewModelBuilder: () => FeedbackViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) { final bool isLightMode = Theme.of(context).brightness == Brightness.light; return Scaffold( appBar: AppBar( - title: Text(AppIntl.of(context).more_report_bug), + title: Text(AppIntl.of(context)!.more_report_bug), ), body: ListView( children: [ @@ -52,8 +52,8 @@ class _FeedbackViewState extends State { )), child: getCardInfo( context, - AppIntl.of(context).more_report_bug_bug, - AppIntl.of(context).more_report_bug_bug_subtitle, + AppIntl.of(context)!.more_report_bug_bug, + AppIntl.of(context)!.more_report_bug_bug_subtitle, Icons.bug_report, const Color.fromRGBO(252, 196, 238, 1), const Color.fromRGBO(153, 78, 174, 1), @@ -80,8 +80,8 @@ class _FeedbackViewState extends State { )), child: getCardInfo( context, - AppIntl.of(context).more_report_bug_feature, - AppIntl.of(context).more_report_bug_feature_subtitle, + AppIntl.of(context)!.more_report_bug_feature, + AppIntl.of(context)!.more_report_bug_feature_subtitle, Icons.design_services, const Color.fromRGBO(63, 219, 251, 1), const Color.fromRGBO(14, 127, 188, 1), @@ -91,8 +91,8 @@ class _FeedbackViewState extends State { const SizedBox(height: 25), Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0), - child: Text(AppIntl.of(context).my_tickets, - style: Theme.of(context).textTheme.headline5.copyWith( + child: Text(AppIntl.of(context)!.my_tickets, + style: Theme.of(context).textTheme.headline5!.copyWith( color: isLightMode ? Colors.black87 : Colors.white))), const Divider( @@ -112,7 +112,7 @@ class _FeedbackViewState extends State { return GestureDetector( onTap: () => { Utils.launchURL(model.myIssues[index].htmlUrl, - AppIntl.of(context)) + AppIntl.of(context)!) }, child: Container( margin: const EdgeInsets.only( @@ -155,9 +155,9 @@ class _FeedbackViewState extends State { const SizedBox(width: 4), createListTag( model.myIssues[index].isOpen - ? AppIntl.of(context) + ? AppIntl.of(context)! .ticket_status_open - : AppIntl.of(context) + : AppIntl.of(context)! .ticket_status_closed, color: model.myIssues[index].state == 'open' @@ -186,11 +186,11 @@ class _FeedbackViewState extends State { ? buildLoading( isInteractionLimitedWhileLoading: false) : Text( - AppIntl.of(context).no_ticket, + AppIntl.of(context)!.no_ticket, overflow: TextOverflow.ellipsis, style: Theme.of(context) .textTheme - .headline6 + .headline6! .copyWith( color: isLightMode ? const Color.fromARGB( @@ -206,8 +206,8 @@ class _FeedbackViewState extends State { const SizedBox(height: 25), Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0), - child: Text(AppIntl.of(context).more_report_tips, - style: Theme.of(context).textTheme.headline5.copyWith( + child: Text(AppIntl.of(context)!.more_report_tips, + style: Theme.of(context).textTheme.headline5!.copyWith( color: isLightMode ? Colors.black87 : Colors.white))), const Divider( @@ -221,16 +221,16 @@ class _FeedbackViewState extends State { text: TextSpan( children: [ TextSpan( - text: AppIntl.of(context).more_report_bug_step1, + text: AppIntl.of(context)!.more_report_bug_step1, style: Theme.of(context) .textTheme - .bodyText2 + .bodyText2! .copyWith(fontSize: 18)), TextSpan( - text: AppIntl.of(context).more_report_bug_step2, + text: AppIntl.of(context)!.more_report_bug_step2, style: Theme.of(context) .textTheme - .bodyText2 + .bodyText2! .copyWith( fontSize: 18, color: Theme.of(context).brightness == @@ -239,16 +239,16 @@ class _FeedbackViewState extends State { : const Color.fromRGBO( 63, 219, 251, 1))), TextSpan( - text: AppIntl.of(context).more_report_bug_step3, + text: AppIntl.of(context)!.more_report_bug_step3, style: Theme.of(context) .textTheme - .bodyText2 + .bodyText2! .copyWith(fontSize: 18)), TextSpan( - text: AppIntl.of(context).more_report_bug_step4, + text: AppIntl.of(context)!.more_report_bug_step4, style: Theme.of(context) .textTheme - .bodyText2 + .bodyText2! .copyWith( fontSize: 18, color: Theme.of(context).brightness == @@ -257,10 +257,10 @@ class _FeedbackViewState extends State { : const Color.fromRGBO( 63, 219, 251, 1))), TextSpan( - text: AppIntl.of(context).more_report_bug_step5, + text: AppIntl.of(context)!.more_report_bug_step5, style: Theme.of(context) .textTheme - .bodyText2 + .bodyText2! .copyWith(fontSize: 18)), ], ), @@ -302,7 +302,7 @@ class _FeedbackViewState extends State { title, style: Theme.of(context) .textTheme - .bodyText2 + .bodyText2! .copyWith(fontSize: 19), textAlign: TextAlign.left, ), @@ -310,7 +310,7 @@ class _FeedbackViewState extends State { subtitle, style: Theme.of(context) .textTheme - .bodyText2 + .bodyText2! .copyWith(fontSize: 16), textAlign: TextAlign.left, ) @@ -322,7 +322,7 @@ class _FeedbackViewState extends State { ); } - Widget createListTag(String text, {Color textColor, Color color}) { + Widget createListTag(String text, {Color? textColor, Color? color}) { return Container( decoration: BoxDecoration( // border radius diff --git a/lib/ui/views/grade_details_view.dart b/lib/ui/views/grade_details_view.dart index 6eed4b880..a8ef76bbd 100644 --- a/lib/ui/views/grade_details_view.dart +++ b/lib/ui/views/grade_details_view.dart @@ -19,7 +19,7 @@ import 'package:notredame/ui/widgets/grade_not_available.dart'; class GradesDetailsView extends StatefulWidget { final Course course; - const GradesDetailsView({this.course}); + const GradesDetailsView({required this.course}); @override _GradesDetailsViewState createState() => _GradesDetailsViewState(); @@ -27,7 +27,7 @@ class GradesDetailsView extends StatefulWidget { class _GradesDetailsViewState extends State with TickerProviderStateMixin { - AnimationController _controller; + late AnimationController _controller; bool _completed = false; @override @@ -53,7 +53,7 @@ class _GradesDetailsViewState extends State Widget build(BuildContext context) => ViewModelBuilder.reactive( viewModelBuilder: () => GradesDetailsViewModel( - course: widget.course, intl: AppIntl.of(context)), + course: widget.course, intl: AppIntl.of(context)!), builder: (context, model, child) => BaseScaffold( showBottomBar: false, body: Material( @@ -77,10 +77,10 @@ class _GradesDetailsViewState extends State tag: 'course_acronym_${model.course.acronym}_${model.course.session}', child: Text( - model.course.acronym ?? "", + model.course.acronym, softWrap: false, overflow: TextOverflow.visible, - style: Theme.of(context).textTheme.bodyText1.copyWith( + style: Theme.of(context).textTheme.bodyText1!.copyWith( color: Colors.white, fontSize: 25, fontWeight: FontWeight.bold), @@ -103,14 +103,14 @@ class _GradesDetailsViewState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildClassInfo(model.course.title ?? ""), + _buildClassInfo(model.course.title), if (model.course.teacherName != null) - _buildClassInfo(AppIntl.of(context) - .grades_teacher(model.course.teacherName)), - _buildClassInfo(AppIntl.of(context) - .grades_group_number(model.course.group ?? "")), - _buildClassInfo(AppIntl.of(context).credits_number( - model.course.numberOfCredits ?? "")), + _buildClassInfo(AppIntl.of(context)! + .grades_teacher(model.course.teacherName!)), + _buildClassInfo(AppIntl.of(context)! + .grades_group_number(model.course.group)), + _buildClassInfo(AppIntl.of(context)! + .credits_number(model.course.numberOfCredits)), ], ), ), @@ -129,7 +129,8 @@ class _GradesDetailsViewState extends State Widget _buildGradeEvaluations(GradesDetailsViewModel model) { if (model.isBusy) { return const Center(child: CircularProgressIndicator()); - } else if (model.course.inReviewPeriod && !model.course.reviewCompleted) { + } else if (model.course.inReviewPeriod && + !(model.course.reviewCompleted ?? true)) { return Center( child: GradeNotAvailable( key: const Key("EvaluationNotCompleted"), @@ -158,12 +159,12 @@ class _GradesDetailsViewState extends State key: const Key("GradeCircularProgress_summary"), finalGrade: model.course.grade, studentGrade: Utils.getGradeInPercentage( - model.course.summary.currentMark, - model.course.summary.markOutOf, + model.course.summary?.currentMark, + model.course.summary?.markOutOf, ), averageGrade: Utils.getGradeInPercentage( - model.course.summary.passMark, - model.course.summary.markOutOf, + model.course.summary?.passMark, + model.course.summary?.markOutOf, ), ), ), @@ -173,18 +174,18 @@ class _GradesDetailsViewState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildGradesSummary( - model.course.summary.currentMark, - model.course.summary.markOutOf, - AppIntl.of(context).grades_current_rating, + model.course.summary?.currentMark, + model.course.summary?.markOutOf, + AppIntl.of(context)!.grades_current_rating, Colors.green, context, ), Padding( padding: const EdgeInsets.only(top: 15.0), child: _buildGradesSummary( - model.course.summary.passMark ?? 0.0, - model.course.summary.markOutOf, - AppIntl.of(context).grades_average, + model.course.summary?.passMark, + model.course.summary?.markOutOf, + AppIntl.of(context)!.grades_average, Colors.red, context, ), @@ -202,48 +203,49 @@ class _GradesDetailsViewState extends State Expanded( flex: 3, child: _buildCourseGradeSummary( - AppIntl.of(context).grades_median, + AppIntl.of(context)!.grades_median, validateGrade( context, - model.course.summary.median.toString(), - AppIntl.of(context).grades_grade_in_percentage( + model.course.summary?.median.toString(), + AppIntl.of(context)!.grades_grade_in_percentage( Utils.getGradeInPercentage( - model.course.summary.median, - model.course.summary.markOutOf)), + model.course.summary?.median, + model.course.summary?.markOutOf)), ), ), ), Expanded( flex: 3, child: _buildCourseGradeSummary( - AppIntl.of(context).grades_standard_deviation, + AppIntl.of(context)!.grades_standard_deviation, validateGrade( context, - model.course.summary.standardDeviation.toString(), - model.course.summary.standardDeviation.toString(), + model.course.summary?.standardDeviation.toString(), + model.course.summary?.standardDeviation.toString(), ), ), ), Expanded( flex: 3, child: _buildCourseGradeSummary( - AppIntl.of(context).grades_percentile_rank, + AppIntl.of(context)!.grades_percentile_rank, validateGrade( context, - model.course.summary.percentileRank.toString(), - model.course.summary.percentileRank.toString(), + model.course.summary?.percentileRank.toString(), + model.course.summary?.percentileRank.toString(), ), ), ), ]), Column(children: [ - for (var evaluation in model.course.summary.evaluations) + for (CourseEvaluation evaluation + in model.course.summary?.evaluations ?? []) GradeEvaluationTile( evaluation, completed: _completed, key: Key("GradeEvaluationTile_${evaluation.title}"), isFirstEvaluation: - evaluation == model.course.summary.evaluations.first, + evaluation == model.course.summary?.evaluations.first, ), ]), ], @@ -267,7 +269,7 @@ class _GradesDetailsViewState extends State info, style: Theme.of(context) .textTheme - .bodyText1 + .bodyText1! .copyWith(color: Colors.white, fontSize: 16), overflow: TextOverflow.ellipsis, ), @@ -275,7 +277,7 @@ class _GradesDetailsViewState extends State ); /// Build the student grade or the average grade with their title - Column _buildGradesSummary(double currentGrade, double maxGrade, + Column _buildGradesSummary(double? currentGrade, double? maxGrade, String recipient, Color color, BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -283,34 +285,36 @@ class _GradesDetailsViewState extends State FittedBox( fit: BoxFit.fitWidth, child: Text( - AppIntl.of(context).grades_grade_with_percentage( - currentGrade, - maxGrade, + AppIntl.of(context)!.grades_grade_with_percentage( + currentGrade ?? 0.0, + maxGrade ?? 0.0, Utils.getGradeInPercentage( currentGrade, maxGrade, ), ), - style: - Theme.of(context).textTheme.headline6.copyWith(color: color)), + style: Theme.of(context) + .textTheme + .headline6! + .copyWith(color: color)), ), Text(recipient, style: - Theme.of(context).textTheme.bodyText1.copyWith(color: color)), + Theme.of(context).textTheme.bodyText1!.copyWith(color: color)), ], ); } - String validateGrade(BuildContext context, String grade, String text) { - if (grade == "null" || grade == null) { - return AppIntl.of(context).grades_not_available; + String validateGrade(BuildContext context, String? grade, String? text) { + if (grade == "null" || grade == null || text == "null" || text == null) { + return AppIntl.of(context)!.grades_not_available; } return text; } /// Build the card of the Medidian, Standart deviation or Percentile Rank - SizedBox _buildCourseGradeSummary(String title, String number) { + SizedBox _buildCourseGradeSummary(String? title, String number) { return SizedBox( height: 110, width: MediaQuery.of(context).size.width / 3.1, diff --git a/lib/ui/views/grades_view.dart b/lib/ui/views/grades_view.dart index e1d6730d6..a0e187b18 100644 --- a/lib/ui/views/grades_view.dart +++ b/lib/ui/views/grades_view.dart @@ -38,7 +38,7 @@ class _GradesViewState extends State { @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( - viewModelBuilder: () => GradesViewModel(intl: AppIntl.of(context)), + viewModelBuilder: () => GradesViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) { return RefreshIndicator( onRefresh: () => model.refresh(), @@ -49,7 +49,7 @@ class _GradesViewState extends State { ListView(), if (model.coursesBySession.isEmpty) Center( - child: Text(AppIntl.of(context).grades_msg_no_grades, + child: Text(AppIntl.of(context)!.grades_msg_no_grades, textAlign: TextAlign.center, style: Theme.of(context).textTheme.headline6)) else @@ -69,9 +69,9 @@ class _GradesViewState extends State { child: _buildSessionCourses( index, _sessionName(model.sessionOrder[index], - AppIntl.of(context)), + AppIntl.of(context)!), model.coursesBySession[ - model.sessionOrder[index]], + model.sessionOrder[index]]!, model), ), ), @@ -105,9 +105,8 @@ class _GradesViewState extends State { const SizedBox(height: 16.0), Wrap( children: courses - .map((course) => index == 0 - ? GradeButton(course, showDiscovery: true) - : GradeButton(course, showDiscovery: false)) + .map((course) => + GradeButton(course, showDiscovery: index == 0)) .toList(), ), ], diff --git a/lib/ui/views/login_view.dart b/lib/ui/views/login_view.dart index c9ca7424b..3c1bd9ce0 100644 --- a/lib/ui/views/login_view.dart +++ b/lib/ui/views/login_view.dart @@ -42,7 +42,7 @@ class _LoginViewState extends State { @override Widget build(BuildContext context) => ViewModelBuilder.reactive( - viewModelBuilder: () => LoginViewModel(intl: AppIntl.of(context)), + viewModelBuilder: () => LoginViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) => Scaffold( backgroundColor: Utils.getColorByBrightness( context, AppTheme.etsLightRed, AppTheme.primaryDark), @@ -57,7 +57,7 @@ class _LoginViewState extends State { key: formKey, onChanged: () { setState(() { - formKey.currentState.validate(); + formKey.currentState?.validate(); }); }, child: AutofillGroup( @@ -103,7 +103,7 @@ class _LoginViewState extends State { borderSide: BorderSide( color: errorTextColor, width: borderRadiusOnFocus)), - labelText: AppIntl.of(context) + labelText: AppIntl.of(context)! .login_prompt_universal_code, labelStyle: const TextStyle(color: Colors.white54), @@ -111,7 +111,7 @@ class _LoginViewState extends State { suffixIcon: Tooltip( key: tooltipkey, triggerMode: TooltipTriggerMode.manual, - message: AppIntl.of(context) + message: AppIntl.of(context)! .universal_code_example, preferBelow: true, child: IconButton( @@ -144,7 +144,7 @@ class _LoginViewState extends State { padding: const EdgeInsets.only(top: 4), child: InkWell( child: Text( - AppIntl.of(context).forgot_password, + AppIntl.of(context)!.forgot_password, style: const TextStyle( decoration: TextDecoration.underline, color: Colors.white), @@ -174,7 +174,7 @@ class _LoginViewState extends State { Fluttertoast.showToast( msg: error); } - formKey.currentState.reset(); + formKey.currentState?.reset(); }); }, style: ButtonStyle( @@ -187,7 +187,7 @@ class _LoginViewState extends State { vertical: 16)), ), child: Text( - AppIntl.of(context).login_action_sign_in, + AppIntl.of(context)!.login_action_sign_in, style: TextStyle( color: model.canSubmit ? submitTextColor @@ -201,7 +201,7 @@ class _LoginViewState extends State { padding: const EdgeInsets.only(top: 24), child: InkWell( child: Text( - AppIntl.of(context).need_help, + AppIntl.of(context)!.need_help, style: const TextStyle( decoration: TextDecoration.underline, color: Colors.white), @@ -228,7 +228,7 @@ class _LoginViewState extends State { spacing: -20, children: [ Text( - AppIntl.of(context).login_applets_logo, + AppIntl.of(context)!.login_applets_logo, style: const TextStyle(color: Colors.white), ), Image.asset( diff --git a/lib/ui/views/more_view.dart b/lib/ui/views/more_view.dart index 04b5e1d35..aa8002300 100644 --- a/lib/ui/views/more_view.dart +++ b/lib/ui/views/more_view.dart @@ -48,21 +48,21 @@ class _MoreViewState extends State { /// License text box List aboutBoxChildren(BuildContext context) { - final textStyle = Theme.of(context).textTheme.bodyText2; + final textStyle = Theme.of(context).textTheme.bodyText2!; return [ const SizedBox(height: 24), RichText( text: TextSpan( children: [ TextSpan( - style: textStyle, text: AppIntl.of(context).flutter_license), + style: textStyle, text: AppIntl.of(context)!.flutter_license), TextSpan( style: textStyle.copyWith(color: Colors.blue), - text: AppIntl.of(context).flutter_website, + text: AppIntl.of(context)!.flutter_website, recognizer: TapGestureRecognizer() ..onTap = () => Utils.launchURL( - AppIntl.of(context).flutter_website, - AppIntl.of(context))), + AppIntl.of(context)!.flutter_website, + AppIntl.of(context)!)), TextSpan(style: textStyle, text: '.'), ], ), @@ -73,17 +73,17 @@ class _MoreViewState extends State { @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( - viewModelBuilder: () => MoreViewModel(intl: AppIntl.of(context)), + viewModelBuilder: () => MoreViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) { return BaseScaffold( appBar: AppBar( - title: Text(AppIntl.of(context).title_more), + title: Text(AppIntl.of(context)!.title_more), automaticallyImplyLeading: false, ), body: ListView( children: [ ListTile( - title: Text(AppIntl.of(context).more_about_applets_title), + title: Text(AppIntl.of(context)!.more_about_applets_title), leading: _buildDiscoveryFeatureDescriptionWidget( context, Hero( @@ -101,7 +101,7 @@ class _MoreViewState extends State { model.navigationService.pushNamed(RouterPaths.about); }), ListTile( - title: Text(AppIntl.of(context).more_report_bug), + title: Text(AppIntl.of(context)!.more_report_bug), leading: _buildDiscoveryFeatureDescriptionWidget( context, getProperIconAccordingToTheme(Icons.bug_report), @@ -112,14 +112,14 @@ class _MoreViewState extends State { model.navigationService.pushNamed(RouterPaths.feedback); }), ListTile( - title: Text(AppIntl.of(context).in_app_review_title), + title: Text(AppIntl.of(context)!.in_app_review_title), leading: const Icon(Icons.rate_review), onTap: () { _analyticsService.logEvent(tag, "Rate us clicked"); MoreViewModel.launchInAppReview(); }), ListTile( - title: Text(AppIntl.of(context).more_contributors), + title: Text(AppIntl.of(context)!.more_contributors), leading: _buildDiscoveryFeatureDescriptionWidget( context, getProperIconAccordingToTheme(Icons.people_outline), @@ -131,7 +131,7 @@ class _MoreViewState extends State { .pushNamed(RouterPaths.contributors); }), ListTile( - title: Text(AppIntl.of(context).more_open_source_licenses), + title: Text(AppIntl.of(context)!.more_open_source_licenses), leading: const Icon(Icons.code), onTap: () { _analyticsService.logEvent(tag, "Rate us clicked"); @@ -145,7 +145,7 @@ class _MoreViewState extends State { 'assets/images/favicon_applets.png')), ), applicationName: - AppIntl.of(context).more_open_source_licenses, + AppIntl.of(context)!.more_open_source_licenses, applicationVersion: model.appVersion, applicationLegalese: '\u{a9} ${DateTime.now().year} App|ETS', @@ -156,7 +156,7 @@ class _MoreViewState extends State { }), if (model.privacyPolicyToggle) ListTile( - title: Text(AppIntl.of(context).privacy_policy), + title: Text(AppIntl.of(context)!.privacy_policy), leading: const Icon(Icons.privacy_tip), onTap: () { _analyticsService.logEvent( @@ -164,7 +164,7 @@ class _MoreViewState extends State { MoreViewModel.launchPrivacyPolicy(); }), ListTile( - title: Text(AppIntl.of(context).need_help), + title: Text(AppIntl.of(context)!.need_help), leading: _buildDiscoveryFeatureDescriptionWidget( context, getProperIconAccordingToTheme(Icons.question_answer), @@ -177,7 +177,7 @@ class _MoreViewState extends State { context, Colors.white, AppTheme.primaryDark)); }), ListTile( - title: Text(AppIntl.of(context).settings_title), + title: Text(AppIntl.of(context)!.settings_title), leading: _buildDiscoveryFeatureDescriptionWidget( context, getProperIconAccordingToTheme(Icons.settings), @@ -188,16 +188,16 @@ class _MoreViewState extends State { model.navigationService.pushNamed(RouterPaths.settings); }), ListTile( - title: Text(AppIntl.of(context).more_log_out), + title: Text(AppIntl.of(context)!.more_log_out), leading: const Icon(Icons.logout), onTap: () => Navigator.of(context).push( PageRouteBuilder( pageBuilder: (context, _, __) => AlertDialog( title: Text( - AppIntl.of(context).more_log_out, + AppIntl.of(context)!.more_log_out, style: const TextStyle(color: Colors.red), ), - content: Text(AppIntl.of(context) + content: Text(AppIntl.of(context)! .more_prompt_log_out_confirmation), actions: [ TextButton( @@ -206,10 +206,10 @@ class _MoreViewState extends State { tag, "Log out clicked"); model.logout(); }, - child: Text(AppIntl.of(context).yes)), + child: Text(AppIntl.of(context)!.yes)), TextButton( onPressed: () => Navigator.of(context).pop(), - child: Text(AppIntl.of(context).no)) + child: Text(AppIntl.of(context)!.no)) ], ), opaque: false, diff --git a/lib/ui/views/not_found_view.dart b/lib/ui/views/not_found_view.dart index 111f7ddd4..7027cc1f7 100644 --- a/lib/ui/views/not_found_view.dart +++ b/lib/ui/views/not_found_view.dart @@ -11,7 +11,7 @@ import 'package:notredame/core/viewmodels/not_found_viewmodel.dart'; import 'package:notredame/ui/utils/app_theme.dart'; class NotFoundView extends StatefulWidget { - final String pageName; + final String? pageName; const NotFoundView({this.pageName}); @@ -20,13 +20,13 @@ class NotFoundView extends StatefulWidget { } class _NotFoundState extends State { - NotFoundViewModel viewModel; + late NotFoundViewModel viewModel; _NotFoundState(); @override void initState() { - viewModel = NotFoundViewModel(pageName: widget.pageName); + viewModel = NotFoundViewModel(pageName: widget.pageName ?? ''); viewModel .loadRiveAnimation() .then((_) => setState(() => viewModel.startRiveAnimation())); @@ -40,7 +40,7 @@ class _NotFoundState extends State { width: 100, height: 80, child: Rive( - artboard: viewModel.artboard, + artboard: viewModel.artboard!, fit: BoxFit.fitWidth, )) : Container(); @@ -68,7 +68,7 @@ class _NotFoundState extends State { bottom: 80, ), child: Text( - AppIntl.of(context).not_found_title, + AppIntl.of(context)!.not_found_title, style: const TextStyle( fontSize: 25, fontWeight: FontWeight.bold), ), @@ -78,7 +78,7 @@ class _NotFoundState extends State { bottom: 70, ), child: Text( - AppIntl.of(context) + AppIntl.of(context)! .not_found_message(model.notFoundPageName), textAlign: TextAlign.center, style: const TextStyle( @@ -94,7 +94,7 @@ class _NotFoundState extends State { onPressed: () { model.navigateToDashboard(); }, - child: Text(AppIntl.of(context).go_to_dashboard), + child: Text(AppIntl.of(context)!.go_to_dashboard), ), ), ], diff --git a/lib/ui/views/outage_view.dart b/lib/ui/views/outage_view.dart index cf21081a0..3fb55e825 100644 --- a/lib/ui/views/outage_view.dart +++ b/lib/ui/views/outage_view.dart @@ -46,7 +46,7 @@ class OutageView extends StatelessWidget { ), SizedBox(height: model.getTextPlacement(context)), Text( - AppIntl.of(context).service_outage, + AppIntl.of(context)!.service_outage, textAlign: TextAlign.center, style: const TextStyle(fontSize: 18, color: Colors.white), @@ -57,7 +57,7 @@ class OutageView extends StatelessWidget { model.tapRefreshButton(context); }, child: Text( - AppIntl.of(context).service_outage_refresh, + AppIntl.of(context)!.service_outage_refresh, style: const TextStyle(fontSize: 17), ), ), @@ -68,7 +68,7 @@ class OutageView extends StatelessWidget { SizedBox( height: model.getContactTextPlacement(context), child: Text( - AppIntl.of(context).service_outage_contact, + AppIntl.of(context)!.service_outage_contact, textAlign: TextAlign.center, style: const TextStyle(color: Colors.white), ), @@ -83,28 +83,30 @@ class OutageView extends StatelessWidget { color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubWebsite, AppIntl.of(context))), + Urls.clubWebsite, + AppIntl.of(context)!)), IconButton( icon: const FaIcon( FontAwesomeIcons.github, color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubGithub, AppIntl.of(context))), + Urls.clubGithub, AppIntl.of(context)!)), IconButton( icon: const FaIcon( Icons.mail_outline, color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubEmail, AppIntl.of(context))), + Urls.clubEmail, AppIntl.of(context)!)), IconButton( icon: const FaIcon( FontAwesomeIcons.discord, color: Colors.white, ), onPressed: () => Utils.launchURL( - Urls.clubDiscord, AppIntl.of(context))), + Urls.clubDiscord, + AppIntl.of(context)!)), ], ), ], diff --git a/lib/ui/views/profile_view.dart b/lib/ui/views/profile_view.dart index 2d3f2088f..45729deef 100644 --- a/lib/ui/views/profile_view.dart +++ b/lib/ui/views/profile_view.dart @@ -34,7 +34,7 @@ class _ProfileViewState extends State { @override Widget build(BuildContext context) => ViewModelBuilder.reactive( - viewModelBuilder: () => ProfileViewModel(intl: AppIntl.of(context)), + viewModelBuilder: () => ProfileViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) { return RefreshIndicator( onRefresh: () => model.refresh(), @@ -112,7 +112,7 @@ Widget buildPage(BuildContext context, ProfileViewModel model) => Column( Padding( padding: const EdgeInsets.only(left: 16.0, top: 8.0, bottom: 8.0), child: Text( - AppIntl.of(context).profile_other_programs, + AppIntl.of(context)!.profile_other_programs, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -179,7 +179,7 @@ Card getMyInfosCard(ProfileViewModel model, BuildContext context) { Clipboard.setData( ClipboardData(text: model.profileStudent.permanentCode)); ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(AppIntl.of(context) + content: Text(AppIntl.of(context)! .profile_permanent_code_copied_to_clipboard), )); }, @@ -189,7 +189,7 @@ Card getMyInfosCard(ProfileViewModel model, BuildContext context) { Padding( padding: const EdgeInsets.only(bottom: 3.0), child: Text( - AppIntl.of(context).profile_permanent_code, + AppIntl.of(context)!.profile_permanent_code, style: const TextStyle( fontSize: 16, ), @@ -208,7 +208,7 @@ Card getMyInfosCard(ProfileViewModel model, BuildContext context) { onTap: () { Clipboard.setData(ClipboardData(text: model.universalAccessCode)); ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(AppIntl.of(context) + content: Text(AppIntl.of(context)! .profile_universal_code_copied_to_clipboard), )); }, @@ -218,7 +218,7 @@ Card getMyInfosCard(ProfileViewModel model, BuildContext context) { Padding( padding: const EdgeInsets.only(top: 16.0, bottom: 3.0), child: Text( - AppIntl.of(context).login_prompt_universal_code, + AppIntl.of(context)!.login_prompt_universal_code, style: const TextStyle(fontSize: 16), ), ), @@ -255,7 +255,7 @@ Card getMyBalanceCard(ProfileViewModel model, BuildContext context) { Padding( padding: const EdgeInsets.only(top: 16.0, left: 16.0, bottom: 3.0), child: Text( - AppIntl.of(context).profile_balance, + AppIntl.of(context)!.profile_balance, style: const TextStyle( fontSize: 16, ), @@ -284,7 +284,7 @@ Card getProgramCompletion(ProfileViewModel model, BuildContext context) { Padding( padding: const EdgeInsets.all(16.0), child: Text( - AppIntl.of(context).profile_program_completion, + AppIntl.of(context)!.profile_program_completion, style: const TextStyle( fontSize: 16, ), @@ -313,7 +313,7 @@ CircularPercentIndicator getLoadingIndicator( center: Text( percentage != 0 ? '$percentage%' - : AppIntl.of(context).profile_program_completion_not_available, + : AppIntl.of(context)!.profile_program_completion_not_available, style: const TextStyle(fontSize: 20), ), progressColor: Colors.green, @@ -325,14 +325,14 @@ Column getCurrentProgramTile(List programList, BuildContext context) { final program = programList.last; final List dataTitles = [ - AppIntl.of(context).profile_code_program, - AppIntl.of(context).profile_average_program, - AppIntl.of(context).profile_number_accumulated_credits_program, - AppIntl.of(context).profile_number_registered_credits_program, - AppIntl.of(context).profile_number_completed_courses_program, - AppIntl.of(context).profile_number_failed_courses_program, - AppIntl.of(context).profile_number_equivalent_courses_program, - AppIntl.of(context).profile_status_program + AppIntl.of(context)!.profile_code_program, + AppIntl.of(context)!.profile_average_program, + AppIntl.of(context)!.profile_number_accumulated_credits_program, + AppIntl.of(context)!.profile_number_registered_credits_program, + AppIntl.of(context)!.profile_number_completed_courses_program, + AppIntl.of(context)!.profile_number_failed_courses_program, + AppIntl.of(context)!.profile_number_equivalent_courses_program, + AppIntl.of(context)!.profile_status_program ]; final List dataFetched = [ diff --git a/lib/ui/views/quick_links_view.dart b/lib/ui/views/quick_links_view.dart index c85182c05..f75541930 100644 --- a/lib/ui/views/quick_links_view.dart +++ b/lib/ui/views/quick_links_view.dart @@ -24,8 +24,8 @@ class _QuickLinksViewState extends State bool _editMode = false; // Animation Controller for Shake Animation - AnimationController _controller; - Animation _animation; + late AnimationController _controller; + late Animation _animation; @override void initState() { @@ -42,7 +42,7 @@ class _QuickLinksViewState extends State @override Widget build(BuildContext context) => ViewModelBuilder.reactive( - viewModelBuilder: () => QuickLinksViewModel(AppIntl.of(context)), + viewModelBuilder: () => QuickLinksViewModel(AppIntl.of(context)!), builder: (context, model, child) => BaseScaffold( isLoading: model.isBusy, appBar: _buildAppBar(context, model), @@ -52,7 +52,7 @@ class _QuickLinksViewState extends State AppBar _buildAppBar(BuildContext context, QuickLinksViewModel model) { return AppBar( - title: Text(AppIntl.of(context).title_ets), + title: Text(AppIntl.of(context)!.title_ets), automaticallyImplyLeading: false, actions: const [], ); @@ -152,7 +152,7 @@ class _QuickLinksViewState extends State }, child: AnimatedBuilder( animation: _animation, - builder: (BuildContext context, Widget child) { + builder: (BuildContext context, Widget? child) { return Transform.rotate( angle: _editMode ? _animation.value : 0, child: child, diff --git a/lib/ui/views/schedule_view.dart b/lib/ui/views/schedule_view.dart index 5d4d740c6..99cf44916 100644 --- a/lib/ui/views/schedule_view.dart +++ b/lib/ui/views/schedule_view.dart @@ -27,9 +27,9 @@ import 'package:notredame/ui/widgets/schedule_settings.dart'; class ScheduleView extends StatefulWidget { @visibleForTesting - final DateTime initialDay; + final DateTime? initialDay; - const ScheduleView({Key key, this.initialDay}) : super(key: key); + const ScheduleView({Key? key, this.initialDay}) : super(key: key); @override _ScheduleViewState createState() => _ScheduleViewState(); @@ -48,7 +48,7 @@ class _ScheduleViewState extends State static const Color _defaultColor = Color(0xff76859B); static final List weekTitles = ["L", "M", "M", "J", "V", "S", "D"]; - AnimationController _animationController; + late AnimationController _animationController; @override void initState() { @@ -75,27 +75,25 @@ class _ScheduleViewState extends State Widget build(BuildContext context) => ViewModelBuilder.reactive( viewModelBuilder: () => ScheduleViewModel( - intl: AppIntl.of(context), initialSelectedDate: widget.initialDay), - onModelReady: (model) { - if (model.settings.isEmpty) { - model.loadSettings(); - } - }, + intl: AppIntl.of(context)!, initialSelectedDate: widget.initialDay), builder: (context, model, child) => BaseScaffold( isLoading: model.busy(model.isLoadingEvents), isInteractionLimitedWhileLoading: false, appBar: AppBar( - title: Text(AppIntl.of(context).title_schedule), + title: Text(AppIntl.of(context)!.title_schedule), centerTitle: false, automaticallyImplyLeading: false, - actions: _buildActionButtons(model), + actions: + model.busy(model.settings) ? [] : _buildActionButtons(model), ), - body: RefreshIndicator( - child: !model.calendarViewSetting - ? _buildCalendarView(model, context) - : _buildListView(model, context), - onRefresh: () => model.refresh(), - )), + body: model.busy(model.settings) + ? const SizedBox() + : RefreshIndicator( + child: !model.calendarViewSetting + ? _buildCalendarView(model, context) + : _buildListView(model, context), + onRefresh: () => model.refresh(), + )), ); Widget _buildListView(ScheduleViewModel model, BuildContext context) { @@ -144,7 +142,7 @@ class _ScheduleViewState extends State Padding( padding: const EdgeInsets.symmetric(vertical: 64.0), child: - Center(child: Text(AppIntl.of(context).schedule_no_event)), + Center(child: Text(AppIntl.of(context)!.schedule_no_event)), ) else if (!model.showWeekEvents) _buildEventList(model.selectedDateEvents(model.selectedDate)), @@ -236,10 +234,10 @@ class _ScheduleViewState extends State return weekTitles[p0]; }, headerStringBuilder: (date, {secondaryDate}) { - final from = AppIntl.of(context).schedule_calendar_from; - final to = AppIntl.of(context).schedule_calendar_to; - final locale = AppIntl.of(context).localeName; - return '$from ${date.day} ${DateFormat.MMMM(locale).format(date)} $to ${secondaryDate.day} ${DateFormat.MMMM(locale).format(secondaryDate)}'; + final from = AppIntl.of(context)!.schedule_calendar_from; + final to = AppIntl.of(context)!.schedule_calendar_to; + final locale = AppIntl.of(context)!.localeName; + return '$from ${date.day} ${DateFormat.MMMM(locale).format(date)} $to ${secondaryDate?.day ?? '00'} ${DateFormat.MMMM(locale).format(secondaryDate ?? date)}'; }, eventTileBuilder: (date, events, boundary, startDuration, endDuration) => @@ -282,7 +280,7 @@ class _ScheduleViewState extends State return weekTitles[p0]; }, headerStringBuilder: (date, {secondaryDate}) { - final locale = AppIntl.of(context).localeName; + final locale = AppIntl.of(context)!.localeName; return '${DateFormat.MMMM(locale).format(date).characters.first.toUpperCase()}${DateFormat.MMMM(locale).format(date).substring(1)} ${date.year}'; }, startDay: calendar_view.WeekDays.sunday, @@ -361,7 +359,7 @@ class _ScheduleViewState extends State } /// Build the square with the number of [events] for the [date] - Widget _buildEventsMarker( + Widget? _buildEventsMarker( ScheduleViewModel model, DateTime date, List events) { if (events.isNotEmpty) { return Positioned( @@ -397,10 +395,11 @@ class _ScheduleViewState extends State valueListenable: model.focusedDate, builder: (context, value, _) { return TableCalendar( + key: const Key("TableCalendar"), startingDayOfWeek: model.settings[PreferencesFlag.scheduleStartWeekday] as StartingDayOfWeek, - locale: model.locale.toLanguageTag(), + locale: model.locale?.toLanguageTag(), selectedDayPredicate: (day) { return isSameDay(model.selectedDate, day); }, diff --git a/lib/ui/views/security_view.dart b/lib/ui/views/security_view.dart index 806fdd7e5..05241a681 100644 --- a/lib/ui/views/security_view.dart +++ b/lib/ui/views/security_view.dart @@ -26,10 +26,10 @@ class _SecurityViewState extends State { @override Widget build(BuildContext context) => ViewModelBuilder.reactive( - viewModelBuilder: () => SecurityViewModel(intl: AppIntl.of(context)), + viewModelBuilder: () => SecurityViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) => Scaffold( appBar: AppBar( - title: Text(AppIntl.of(context).ets_security_title), + title: Text(AppIntl.of(context)!.ets_security_title), ), body: SingleChildScrollView( child: Column( @@ -55,7 +55,7 @@ class _SecurityViewState extends State { Padding( padding: const EdgeInsets.all(8.0), child: Text( - AppIntl.of(context).security_reach_security, + AppIntl.of(context)!.security_reach_security, style: const TextStyle( color: AppTheme.etsLightRed, fontSize: 24), ), @@ -64,17 +64,17 @@ class _SecurityViewState extends State { child: InkWell( splashColor: Colors.red.withAlpha(50), onTap: () => Utils.launchURL( - 'tel:${AppIntl.of(context).security_emergency_number}', - AppIntl.of(context)) + 'tel:${AppIntl.of(context)!.security_emergency_number}', + AppIntl.of(context)!) .catchError((error) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(error.toString()))); }), child: ListTile( leading: const Icon(Icons.phone, size: 30), - title: Text(AppIntl.of(context).security_emergency_call), + title: Text(AppIntl.of(context)!.security_emergency_call), subtitle: - Text(AppIntl.of(context).security_emergency_number), + Text(AppIntl.of(context)!.security_emergency_number), ), ), ), @@ -84,15 +84,15 @@ class _SecurityViewState extends State { child: ListTile( leading: const Icon(Icons.phone, size: 30), title: Text( - AppIntl.of(context).security_emergency_intern_call), + AppIntl.of(context)!.security_emergency_intern_call), subtitle: Text( - AppIntl.of(context).security_emergency_intern_number), + AppIntl.of(context)!.security_emergency_intern_number), ), ), Padding( padding: const EdgeInsets.all(8.0), child: Text( - AppIntl.of(context).security_emergency_procedures, + AppIntl.of(context)!.security_emergency_procedures, style: const TextStyle( color: AppTheme.etsLightRed, fontSize: 24), ), diff --git a/lib/ui/views/settings_view.dart b/lib/ui/views/settings_view.dart index d19d1a31c..7dd0f6122 100644 --- a/lib/ui/views/settings_view.dart +++ b/lib/ui/views/settings_view.dart @@ -19,16 +19,16 @@ class _SettingsViewState extends State { @override Widget build(BuildContext context) => ViewModelBuilder.reactive( - viewModelBuilder: () => SettingsViewModel(intl: AppIntl.of(context)), + viewModelBuilder: () => SettingsViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) => BaseScaffold( appBar: AppBar( - title: Text(AppIntl.of(context).settings_title), + title: Text(AppIntl.of(context)!.settings_title), ), body: ListView( children: [ ListTile( title: Text( - AppIntl.of(context).settings_display_pref_category, + AppIntl.of(context)!.settings_display_pref_category, style: const TextStyle(color: AppTheme.etsLightRed), ), ), @@ -46,21 +46,21 @@ class _SettingsViewState extends State { PopupMenuItem( value: ThemeMode.light, child: ListTile( - title: Text(AppIntl.of(context).light_theme), + title: Text(AppIntl.of(context)!.light_theme), leading: const Icon(Icons.wb_sunny), ), ), PopupMenuItem( value: ThemeMode.dark, child: ListTile( - title: Text(AppIntl.of(context).dark_theme), + title: Text(AppIntl.of(context)!.dark_theme), leading: const Icon(Icons.nightlight_round), ), ), PopupMenuItem( value: ThemeMode.system, child: ListTile( - title: Text(AppIntl.of(context).system_theme), + title: Text(AppIntl.of(context)!.system_theme), leading: const Icon(Icons.brightness_auto), ), ), @@ -71,12 +71,12 @@ class _SettingsViewState extends State { : model.selectedTheme == ThemeMode.dark ? Icons.nightlight_round : Icons.brightness_auto), - title: Text(AppIntl.of(context).settings_dark_theme_pref), + title: Text(AppIntl.of(context)!.settings_dark_theme_pref), subtitle: Text(model.selectedTheme == ThemeMode.light - ? AppIntl.of(context).light_theme + ? AppIntl.of(context)!.light_theme : model.selectedTheme == ThemeMode.dark - ? AppIntl.of(context).dark_theme - : AppIntl.of(context).system_theme), + ? AppIntl.of(context)!.dark_theme + : AppIntl.of(context)!.system_theme), trailing: const Icon(Icons.arrow_drop_down), ), ), @@ -87,7 +87,7 @@ class _SettingsViewState extends State { ), ListTile( title: Text( - AppIntl.of(context).settings_miscellaneous_category, + AppIntl.of(context)!.settings_miscellaneous_category, style: const TextStyle(color: AppTheme.etsLightRed), ), ), @@ -103,16 +103,16 @@ class _SettingsViewState extends State { itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: AppIntl.supportedLocales.first.languageCode, - child: Text(AppIntl.of(context).settings_english), + child: Text(AppIntl.of(context)!.settings_english), ), PopupMenuItem( value: AppIntl.supportedLocales.last.languageCode, - child: Text(AppIntl.of(context).settings_french), + child: Text(AppIntl.of(context)!.settings_french), ), ], child: ListTile( leading: const Icon(Icons.language), - title: Text(AppIntl.of(context).settings_language_pref), + title: Text(AppIntl.of(context)!.settings_language_pref), subtitle: Text(model.currentLocale), trailing: const Icon(Icons.arrow_drop_down), ), diff --git a/lib/ui/views/student_view.dart b/lib/ui/views/student_view.dart index 75cfe9533..2f3e6e73e 100644 --- a/lib/ui/views/student_view.dart +++ b/lib/ui/views/student_view.dart @@ -24,8 +24,8 @@ class _StudentViewState extends State { @override Widget build(BuildContext context) { final List tabs = [ - AppIntl.of(context).grades_title, - AppIntl.of(context).profile_title + AppIntl.of(context)!.grades_title, + AppIntl.of(context)!.profile_title ]; return BaseScaffold( @@ -41,7 +41,7 @@ class _StudentViewState extends State { automaticallyImplyLeading: false, pinned: true, floating: true, - title: Text(AppIntl.of(context).title_student), + title: Text(AppIntl.of(context)!.title_student), forceElevated: innerBoxIsScrolled, bottom: TabBar( indicatorColor: diff --git a/lib/ui/widgets/base_scaffold.dart b/lib/ui/widgets/base_scaffold.dart index 90d585727..a004ebad0 100644 --- a/lib/ui/widgets/base_scaffold.dart +++ b/lib/ui/widgets/base_scaffold.dart @@ -19,13 +19,13 @@ import 'package:notredame/ui/widgets/bottom_bar.dart'; /// Basic Scaffold to avoid boilerplate code in the application. /// Contains a loader controlled by [_isLoading] class BaseScaffold extends StatefulWidget { - final AppBar appBar; + final AppBar? appBar; - final Widget body; + final Widget? body; - final FloatingActionButton fab; + final FloatingActionButton? fab; - final FloatingActionButtonLocation fabPosition; + final FloatingActionButtonLocation? fabPosition; final bool _showBottomBar; @@ -56,7 +56,7 @@ class _BaseScaffoldState extends State { final NetworkingService _networkingService = locator(); - StreamSubscription _subscription; + late StreamSubscription _subscription; @override void initState() { @@ -88,7 +88,7 @@ class _BaseScaffoldState extends State { top: false, child: Stack( children: [ - widget.body, + widget.body ?? const SizedBox(), if (widget._isLoading) buildLoading( isInteractionLimitedWhileLoading: @@ -116,7 +116,7 @@ class _BaseScaffoldState extends State { height: MediaQuery.of(context).size.height / 30, ), Text( - AppIntl.of(context).no_connectivity, + AppIntl.of(context)!.no_connectivity, textAlign: TextAlign.center, ), ], diff --git a/lib/ui/widgets/bottom_bar.dart b/lib/ui/widgets/bottom_bar.dart index 69ff87825..c4427fa94 100644 --- a/lib/ui/widgets/bottom_bar.dart +++ b/lib/ui/widgets/bottom_bar.dart @@ -34,7 +34,7 @@ class _BottomBarState extends State { @override Widget build(BuildContext context) { - _currentView = _defineIndex(ModalRoute.of(context).settings.name); + _currentView = _defineIndex(ModalRoute.of(context)!.settings.name!); return BottomNavigationBar( type: BottomNavigationBarType.fixed, elevation: 0, @@ -104,23 +104,23 @@ class _BottomBarState extends State { BottomNavigationBarItem( icon: _buildDiscoveryFeatureDescriptionWidget( context, RouterPaths.dashboard, Icons.dashboard), - label: AppIntl.of(context).title_dashboard), + label: AppIntl.of(context)!.title_dashboard), BottomNavigationBarItem( icon: _buildDiscoveryFeatureDescriptionWidget( context, RouterPaths.schedule, Icons.schedule), - label: AppIntl.of(context).title_schedule), + label: AppIntl.of(context)!.title_schedule), BottomNavigationBarItem( icon: _buildDiscoveryFeatureDescriptionWidget( context, RouterPaths.student, Icons.school), - label: AppIntl.of(context).title_student), + label: AppIntl.of(context)!.title_student), BottomNavigationBarItem( icon: _buildDiscoveryFeatureDescriptionWidget( context, RouterPaths.ets, Icons.account_balance), - label: AppIntl.of(context).title_ets), + label: AppIntl.of(context)!.title_ets), BottomNavigationBarItem( icon: _buildDiscoveryFeatureDescriptionWidget( context, RouterPaths.more, Icons.dehaze), - label: AppIntl.of(context).title_more), + label: AppIntl.of(context)!.title_more), ]; } diff --git a/lib/ui/widgets/custom_feedback.dart b/lib/ui/widgets/custom_feedback.dart index 4694cbd7a..e0ca50db0 100644 --- a/lib/ui/widgets/custom_feedback.dart +++ b/lib/ui/widgets/custom_feedback.dart @@ -13,13 +13,13 @@ import 'package:notredame/core/models/feedback.dart'; /// The submit button is disabled until the user provides the feedback type and a feedback text. class CustomFeedbackForm extends StatefulWidget { const CustomFeedbackForm({ - Key key, - this.onSubmit, + Key? key, + required this.onSubmit, this.scrollController, }) : super(key: key); final OnSubmit onSubmit; - final ScrollController scrollController; + final ScrollController? scrollController; @override _CustomFeedbackFormState createState() => _CustomFeedbackFormState(); @@ -70,9 +70,9 @@ class _CustomFeedbackFormState extends State { TextButton( // disable this button until the user has specified a feedback onPressed: _customFeedback.feedbackText != null && - _customFeedback.feedbackText.isNotEmpty + _customFeedback.feedbackText!.isNotEmpty ? () => widget.onSubmit( - _customFeedback.feedbackText, + _customFeedback.feedbackText!, extras: { 'email': _customFeedback.feedbackEmail, }, diff --git a/lib/ui/widgets/dismissible_card.dart b/lib/ui/widgets/dismissible_card.dart index 80ba3919b..acd0621ab 100644 --- a/lib/ui/widgets/dismissible_card.dart +++ b/lib/ui/widgets/dismissible_card.dart @@ -6,16 +6,16 @@ class DismissibleCard extends StatelessWidget { final Function(DismissDirection) onDismissed; - final Color cardColor; + final Color? cardColor; final double elevation; final bool isBusy; const DismissibleCard( - {Key key, - @required this.onDismissed, - @required this.child, + {Key? key, + required this.onDismissed, + required this.child, this.elevation = 1, this.cardColor, this.isBusy = false}) diff --git a/lib/ui/widgets/grade_button.dart b/lib/ui/widgets/grade_button.dart index 4772f1884..e8b963d7b 100644 --- a/lib/ui/widgets/grade_button.dart +++ b/lib/ui/widgets/grade_button.dart @@ -26,13 +26,14 @@ class GradeButton extends StatelessWidget { /// Settings manager final SettingsManager _settingsManager = locator(); - GradeButton(this.course, {this.showDiscovery}); + GradeButton(this.course, {this.showDiscovery = false}); @override Widget build(BuildContext context) => Card( child: InkWell( onTap: () async { - if (ModalRoute.of(context).settings.name == RouterPaths.dashboard || + if (ModalRoute.of(context)!.settings.name == + RouterPaths.dashboard || await _settingsManager .getBool(PreferencesFlag.discoveryStudentGrade) == true) { @@ -51,12 +52,12 @@ class GradeButton extends StatelessWidget { /// will return [grades_not_available]. String gradeString(AppIntl intl) { if (course.grade != null) { - return course.grade; + return course.grade!; } else if (course.summary != null && - course.summary.markOutOf > 0 && - !(course.inReviewPeriod && !course.reviewCompleted)) { + course.summary!.markOutOf > 0 && + !(course.inReviewPeriod && !(course.reviewCompleted ?? false))) { return intl.grades_grade_in_percentage( - course.summary.currentMarkInPercent.round()); + course.summary!.currentMarkInPercent.round()); } return intl.grades_not_available; @@ -89,7 +90,7 @@ class GradeButton extends StatelessWidget { course.acronym, style: Theme.of(context) .textTheme - .bodyText1 + .bodyText1! .copyWith(color: Colors.white), ), ), @@ -102,7 +103,7 @@ class GradeButton extends StatelessWidget { ), Expanded( child: Center( - child: Text(gradeString(AppIntl.of(context)), + child: Text(gradeString(AppIntl.of(context)!), style: TextStyle( fontSize: 22, color: Theme.of(context).brightness == Brightness.light diff --git a/lib/ui/widgets/grade_circular_progress.dart b/lib/ui/widgets/grade_circular_progress.dart index 4511a3f0a..3b8064617 100644 --- a/lib/ui/widgets/grade_circular_progress.dart +++ b/lib/ui/widgets/grade_circular_progress.dart @@ -10,14 +10,14 @@ import 'package:notredame/ui/utils/app_theme.dart'; class GradeCircularProgress extends StatefulWidget { final bool completed; - final String finalGrade; - final double studentGrade; - final double averageGrade; + final String? finalGrade; + final double? studentGrade; + final double? averageGrade; final double ratio; const GradeCircularProgress(this.ratio, - {Key key, - this.completed, + {Key? key, + this.completed = false, this.finalGrade, this.studentGrade, this.averageGrade}) @@ -29,8 +29,8 @@ class GradeCircularProgress extends StatefulWidget { class _GradeCircularProgressState extends State with TickerProviderStateMixin { - AnimationController _controller; - Animation animation; + late AnimationController _controller; + late Animation animation; @override void initState() { @@ -92,15 +92,15 @@ class _GradeCircularProgressState extends State String getGrade(BuildContext context) { if (widget.finalGrade != null) { - return widget.finalGrade; + return widget.finalGrade!; } if (widget.studentGrade != null) { - return AppIntl.of(context) - .grades_grade_in_percentage(widget.studentGrade.round()); + return AppIntl.of(context)! + .grades_grade_in_percentage(widget.studentGrade!.round()); } - return AppIntl.of(context).grades_not_available; + return AppIntl.of(context)!.grades_not_available; } Color gradePercentageColor(double gradePercentage) { @@ -134,6 +134,6 @@ class _GradeCircularProgressState extends State } } - return Color.lerp(startColor, endColor, colorProportion); + return Color.lerp(startColor, endColor, colorProportion)!; } } diff --git a/lib/ui/widgets/grade_evaluation_tile.dart b/lib/ui/widgets/grade_evaluation_tile.dart index 41b53206c..f559ca71b 100644 --- a/lib/ui/widgets/grade_evaluation_tile.dart +++ b/lib/ui/widgets/grade_evaluation_tile.dart @@ -25,7 +25,7 @@ class GradeEvaluationTile extends StatefulWidget { final bool isFirstEvaluation; const GradeEvaluationTile(this.evaluation, - {Key key, this.completed, this.isFirstEvaluation}) + {Key? key, this.completed = false, this.isFirstEvaluation = false}) : super(key: key); @override @@ -35,8 +35,8 @@ class GradeEvaluationTile extends StatefulWidget { class _GradeEvaluationTileState extends State with TickerProviderStateMixin { bool showEvaluationDetails = false; - AnimationController controller; - Animation rotateAnimation; + late AnimationController controller; + late Animation rotateAnimation; @override void initState() { @@ -159,7 +159,7 @@ class _GradeEvaluationTileState extends State Padding( padding: const EdgeInsets.only(top: 10.0, bottom: 20.0), child: Text( - AppIntl.of(context) + AppIntl.of(context)! .grades_weight(widget.evaluation.weight), style: TextStyle( fontSize: 14, @@ -174,7 +174,7 @@ class _GradeEvaluationTileState extends State padding: const EdgeInsets.only(top: 25.0, right: 10.0), child: AnimatedBuilder( animation: rotateAnimation, - builder: (BuildContext context, Widget child) { + builder: (BuildContext context, Widget? child) { return Transform.rotate( angle: rotateAnimation.value, child: const Icon( @@ -206,39 +206,39 @@ class _GradeEvaluationTileState extends State child: Column( children: [ _buildSummary( - AppIntl.of(context).grades_grade, - AppIntl.of(context).grades_grade_with_percentage( + AppIntl.of(context)!.grades_grade, + AppIntl.of(context)!.grades_grade_with_percentage( evaluation.mark ?? 0.0, - evaluation.correctedEvaluationOutOf ?? 0.0, + evaluation.correctedEvaluationOutOf, Utils.getGradeInPercentage(evaluation.mark, evaluation.correctedEvaluationOutOfFormatted), ), ), _buildSummary( - AppIntl.of(context).grades_average, - AppIntl.of(context).grades_grade_with_percentage( + AppIntl.of(context)!.grades_average, + AppIntl.of(context)!.grades_grade_with_percentage( evaluation.passMark ?? 0.0, - evaluation.correctedEvaluationOutOf ?? 0.0, + evaluation.correctedEvaluationOutOf, Utils.getGradeInPercentage(evaluation.passMark, evaluation.correctedEvaluationOutOfFormatted), ), ), _buildSummary( - AppIntl.of(context).grades_median, - AppIntl.of(context).grades_grade_with_percentage( + AppIntl.of(context)!.grades_median, + AppIntl.of(context)!.grades_grade_with_percentage( evaluation.median ?? 0.0, - evaluation.correctedEvaluationOutOf ?? 0.0, + evaluation.correctedEvaluationOutOf, Utils.getGradeInPercentage(evaluation.median, evaluation.correctedEvaluationOutOfFormatted), ), ), _buildSummary( - AppIntl.of(context).grades_standard_deviation, + AppIntl.of(context)!.grades_standard_deviation, validateResult( context, evaluation.standardDeviation.toString())), - _buildSummary(AppIntl.of(context).grades_percentile_rank, + _buildSummary(AppIntl.of(context)!.grades_percentile_rank, validateResult(context, evaluation.percentileRank.toString())), - _buildSummary(AppIntl.of(context).grades_target_date, + _buildSummary(AppIntl.of(context)!.grades_target_date, getDate(evaluation.targetDate, context)), ], ), @@ -246,13 +246,13 @@ class _GradeEvaluationTileState extends State ); } - String getDate(DateTime targetDate, BuildContext context) { + String getDate(DateTime? targetDate, BuildContext context) { if (targetDate != null) { - return DateFormat('d MMMM yyyy', AppIntl.of(context).localeName) + return DateFormat('d MMMM yyyy', AppIntl.of(context)!.localeName) .format(targetDate); } - return AppIntl.of(context).grades_not_available; + return AppIntl.of(context)!.grades_not_available; } Padding _buildSummary(String title, String grade) { @@ -262,7 +262,7 @@ class _GradeEvaluationTileState extends State mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(title, style: const TextStyle(fontSize: 14)), - Text(grade ?? '', style: const TextStyle(fontSize: 14)), + Text(grade, style: const TextStyle(fontSize: 14)), ], ), ); @@ -270,12 +270,12 @@ class _GradeEvaluationTileState extends State double getGradeInDecimal(double grade, double maxGrade) => grade / maxGrade; - String validateResult(BuildContext context, String result) { - if (result != "null") { + String validateResult(BuildContext context, String? result) { + if (result != "null" && result != null) { return result; } - return AppIntl.of(context).grades_not_available; + return AppIntl.of(context)!.grades_not_available; } DescribedFeatureOverlay _buildDiscoveryFeatureDescriptionWidget( diff --git a/lib/ui/widgets/grade_not_available.dart b/lib/ui/widgets/grade_not_available.dart index 8b6e1e4ab..a85f68759 100644 --- a/lib/ui/widgets/grade_not_available.dart +++ b/lib/ui/widgets/grade_not_available.dart @@ -8,12 +8,12 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:notredame/ui/utils/app_theme.dart'; class GradeNotAvailable extends StatelessWidget { - final VoidCallback onPressed; + final VoidCallback? onPressed; final bool isEvaluationPeriod; const GradeNotAvailable( - {Key key, this.onPressed, this.isEvaluationPeriod = false}) + {Key? key, this.onPressed, this.isEvaluationPeriod = false}) : super(key: key); @override @@ -30,9 +30,9 @@ class GradeNotAvailable extends StatelessWidget { const SizedBox(height: 25), Text( isEvaluationPeriod - ? AppIntl.of(context) + ? AppIntl.of(context)! .grades_error_course_evaluations_not_completed - : AppIntl.of(context).grades_msg_no_grade, + : AppIntl.of(context)!.grades_msg_no_grade, textAlign: TextAlign.center, softWrap: true, style: isEvaluationPeriod @@ -45,7 +45,7 @@ class GradeNotAvailable extends StatelessWidget { backgroundColor: AppTheme.etsLightRed, foregroundColor: Colors.white), onPressed: onPressed, - child: Text(AppIntl.of(context).retry)) + child: Text(AppIntl.of(context)!.retry)) ], ); } diff --git a/lib/ui/widgets/haptics_container.dart b/lib/ui/widgets/haptics_container.dart index c6fbba0fd..1eb47f3ec 100644 --- a/lib/ui/widgets/haptics_container.dart +++ b/lib/ui/widgets/haptics_container.dart @@ -11,7 +11,7 @@ import 'package:flutter/services.dart'; class HapticsContainer extends StatefulWidget { final Widget child; - const HapticsContainer({Key key, this.child}) : super(key: key); + const HapticsContainer({Key? key, required this.child}) : super(key: key); @override _HapticsContainerState createState() => _HapticsContainerState(); diff --git a/lib/ui/widgets/password_text_field.dart b/lib/ui/widgets/password_text_field.dart index 1fd9fe079..70e472dbd 100644 --- a/lib/ui/widgets/password_text_field.dart +++ b/lib/ui/widgets/password_text_field.dart @@ -8,7 +8,8 @@ class PasswordFormField extends StatefulWidget { final FormFieldValidator validator; final VoidCallback onEditionComplete; - const PasswordFormField({Key key, this.validator, this.onEditionComplete}) + const PasswordFormField( + {Key? key, required this.validator, required this.onEditionComplete}) : super(key: key); @override @@ -38,7 +39,7 @@ class _PasswordFormFieldState extends State { errorBorder: OutlineInputBorder( borderSide: BorderSide( color: errorTextColor, width: borderRadiusOnFocus)), - labelText: AppIntl.of(context).login_prompt_password, + labelText: AppIntl.of(context)!.login_prompt_password, labelStyle: const TextStyle(color: Colors.white54), errorStyle: TextStyle(color: errorTextColor), suffixIcon: IconButton( diff --git a/lib/ui/widgets/schedule_calendar_tile.dart b/lib/ui/widgets/schedule_calendar_tile.dart index 59ef8c6b0..387352f14 100644 --- a/lib/ui/widgets/schedule_calendar_tile.dart +++ b/lib/ui/widgets/schedule_calendar_tile.dart @@ -6,19 +6,19 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class ScheduleCalendarTile extends StatefulWidget { - final String title; - final String description; - final TextStyle titleStyle; - final int totalEvents; - final EdgeInsets padding; - final Color backgroundColor; - final BorderRadius borderRadius; - final DateTime start; - final DateTime end; + final String? title; + final String? description; + final TextStyle? titleStyle; + final int? totalEvents; + final EdgeInsets? padding; + final Color? backgroundColor; + final BorderRadius? borderRadius; + final DateTime? start; + final DateTime? end; final BuildContext buildContext; const ScheduleCalendarTile( - {Key key, + {Key? key, this.title, this.description, this.titleStyle, @@ -28,7 +28,7 @@ class ScheduleCalendarTile extends StatefulWidget { this.borderRadius, this.start, this.end, - this.buildContext}) + required this.buildContext}) : super(key: key); @override @@ -37,15 +37,15 @@ class ScheduleCalendarTile extends StatefulWidget { class _ScheduleCalendarTileState extends State { void _showTileInfo() { - final courseInfos = widget.description.split(";"); + final courseInfos = widget.description?.split(";") ?? []; final courseName = courseInfos[0].split("-")[0]; final courseLocation = courseInfos[1]; final courseType = courseInfos[2]; final teacherName = courseInfos[3]; final startTime = - "${widget.start.hour}:${widget.start.minute.toString().padLeft(2, '0')}"; + "${widget.start?.hour ?? '00'}:${widget.start?.minute.toString().padLeft(2, '0') ?? '00'}"; final endTime = - "${widget.end.hour}:${widget.end.add(const Duration(minutes: 1)).minute.toString().padLeft(2, '0')}"; + "${widget.end?.hour ?? '00'}:${widget.end?.add(const Duration(minutes: 1)).minute.toString().padLeft(2, '0') ?? '00'}"; showDialog( context: context, @@ -73,14 +73,14 @@ class _ScheduleCalendarTileState extends State { ), ), Text( - "${AppIntl.of(widget.buildContext).schedule_calendar_by} $teacherName", + "${AppIntl.of(widget.buildContext)!.schedule_calendar_by} $teacherName", style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w500, ), ), Text( - "${AppIntl.of(widget.buildContext).schedule_calendar_from_time} $startTime ${AppIntl.of(widget.buildContext).schedule_calendar_to_time} $endTime", + "${AppIntl.of(widget.buildContext)!.schedule_calendar_from_time} $startTime ${AppIntl.of(widget.buildContext)!.schedule_calendar_to_time} $endTime", style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w500, @@ -120,7 +120,7 @@ class _ScheduleCalendarTileState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ AutoSizeText( - widget.title, + widget.title ?? "", style: widget.titleStyle, maxLines: 3, ) diff --git a/lib/ui/widgets/schedule_settings.dart b/lib/ui/widgets/schedule_settings.dart index 9086edd89..490b4873f 100644 --- a/lib/ui/widgets/schedule_settings.dart +++ b/lib/ui/widgets/schedule_settings.dart @@ -16,7 +16,7 @@ import 'package:notredame/ui/utils/app_theme.dart'; class ScheduleSettings extends StatefulWidget { final bool showHandle; - const ScheduleSettings({Key key, this.showHandle = true}) : super(key: key); + const ScheduleSettings({Key? key, this.showHandle = true}) : super(key: key); @override _ScheduleSettingsState createState() => _ScheduleSettingsState(); @@ -68,17 +68,17 @@ class _ScheduleSettingsState extends State { child: Center( child: Padding( padding: const EdgeInsets.fromLTRB(15, 20, 20, 20), - child: Text(AppIntl.of(context).schedule_settings_title, + child: Text(AppIntl.of(context)!.schedule_settings_title, style: Theme.of(context).textTheme.headline6)), ), ), Expanded( child: ListTileTheme( - selectedColor: Theme.of(context).textTheme.bodyText1.color, + selectedColor: Theme.of(context).textTheme.bodyText1!.color, child: ListView( key: const ValueKey("SettingsScrollingArea"), children: _buildSettings( - context, model as ScheduleSettingsViewModel), + context, model! as ScheduleSettingsViewModel), ), ), ), @@ -119,7 +119,7 @@ class _ScheduleSettingsState extends State { padding: const EdgeInsets.only( left: 15.0, right: 15.0, top: 15.0, bottom: 2.0), child: Text( - AppIntl.of(context).schedule_select_course_activity, + AppIntl.of(context)!.schedule_select_course_activity, style: TextStyle( color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.bold, @@ -134,7 +134,7 @@ class _ScheduleSettingsState extends State { tiles.add(Padding( padding: const EdgeInsets.fromLTRB(15.0, 8.0, 15.0, 8.0), child: Text( - '${model.scheduleActivitiesByCourse[courseActivitiesAcronym].first.courseAcronym} - ${model.scheduleActivitiesByCourse[courseActivitiesAcronym].first.courseTitle}', + '${model.scheduleActivitiesByCourse[courseActivitiesAcronym]?.first.courseAcronym ?? AppIntl.of(context)!.grades_not_available} - ${model.scheduleActivitiesByCourse[courseActivitiesAcronym]?.first.courseTitle ?? AppIntl.of(context)!.grades_not_available}}', style: const TextStyle( fontWeight: FontWeight.bold, ), @@ -146,19 +146,21 @@ class _ScheduleSettingsState extends State { selectedTileColor: selectedColor, onTap: () => model.selectScheduleActivity(courseActivitiesAcronym, null), - title: Text(AppIntl.of(context).course_activity_group_both), + title: Text(AppIntl.of(context)!.course_activity_group_both), )); - for (final course - in model.scheduleActivitiesByCourse[courseActivitiesAcronym]) { - tiles.add(ListTile( - selected: - model.selectedScheduleActivity[course.courseAcronym] == course, - selectedTileColor: selectedColor, - onTap: () => - model.selectScheduleActivity(course.courseAcronym, course), - title: Text(getActivityTitle(course.activityCode)), - )); + if (model.scheduleActivitiesByCourse[courseActivitiesAcronym] != null) { + for (final course + in model.scheduleActivitiesByCourse[courseActivitiesAcronym]!) { + tiles.add(ListTile( + selected: + model.selectedScheduleActivity[course.courseAcronym] == course, + selectedTileColor: selectedColor, + onTap: () => + model.selectScheduleActivity(course.courseAcronym, course), + title: Text(getActivityTitle(course.activityCode)), + )); + } } if (model.scheduleActivitiesByCourse.values.length > 1) { @@ -171,9 +173,9 @@ class _ScheduleSettingsState extends State { String getActivityTitle(String activityCode) { if (activityCode == ActivityCode.labGroupA) { - return AppIntl.of(context).course_activity_group_a; + return AppIntl.of(context)!.course_activity_group_a; } else if (activityCode == ActivityCode.labGroupB) { - return AppIntl.of(context).course_activity_group_b; + return AppIntl.of(context)!.course_activity_group_b; } return ""; @@ -189,7 +191,7 @@ class _ScheduleSettingsState extends State { activeColor: AppTheme.etsLightRed, ), title: Text( - AppIntl.of(context).schedule_settings_show_week_events_btn_pref), + AppIntl.of(context)!.schedule_settings_show_week_events_btn_pref), ), const Divider(thickness: 1) ]; @@ -201,7 +203,7 @@ class _ScheduleSettingsState extends State { padding: const EdgeInsets.only( left: 15.0, right: 15.0, top: 15.0, bottom: 2.0), child: Text( - AppIntl.of(context).schedule_settings_show_weekend_day, + AppIntl.of(context)!.schedule_settings_show_weekend_day, style: TextStyle( color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.bold, @@ -215,7 +217,7 @@ class _ScheduleSettingsState extends State { selected: model.otherDayOfWeek == WeekDays.monday, selectedTileColor: selectedColor, onTap: () => setState(() => model.otherDayOfWeek = WeekDays.monday), - title: Text(AppIntl.of(context).schedule_settings_show_weekend_day_none), + title: Text(AppIntl.of(context)!.schedule_settings_show_weekend_day_none), )); for (final WeekDays day in model.otherDayPossible) { @@ -242,7 +244,7 @@ class _ScheduleSettingsState extends State { activeColor: AppTheme.etsLightRed, ), title: - Text(AppIntl.of(context).schedule_settings_show_today_btn_pref), + Text(AppIntl.of(context)!.schedule_settings_show_today_btn_pref), ), const Divider(thickness: 1) ]; @@ -256,7 +258,7 @@ class _ScheduleSettingsState extends State { onChanged: (value) => model.toggleCalendarView = value, activeColor: AppTheme.etsLightRed, ), - title: Text(AppIntl.of(context).schedule_settings_list_view), + title: Text(AppIntl.of(context)!.schedule_settings_list_view), ), const Divider(thickness: 1) ]; @@ -267,7 +269,7 @@ class _ScheduleSettingsState extends State { Padding( padding: const EdgeInsets.only(left: 15.0, right: 15.0, bottom: 2.0), child: Text( - AppIntl.of(context).schedule_settings_calendar_format_pref, + AppIntl.of(context)!.schedule_settings_calendar_format_pref, style: TextStyle( color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.bold, @@ -298,7 +300,7 @@ class _ScheduleSettingsState extends State { padding: const EdgeInsets.only( left: 15.0, right: 15.0, top: 15.0, bottom: 2.0), child: Text( - AppIntl.of(context).schedule_settings_starting_weekday_pref, + AppIntl.of(context)!.schedule_settings_starting_weekday_pref, style: TextStyle( color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.bold, @@ -325,38 +327,39 @@ class _ScheduleSettingsState extends State { String getTextForFormat(BuildContext context, CalendarFormat format) { switch (format) { case CalendarFormat.month: - return AppIntl.of(context).schedule_settings_calendar_format_month; + return AppIntl.of(context)!.schedule_settings_calendar_format_month; case CalendarFormat.week: - return AppIntl.of(context).schedule_settings_calendar_format_week; + return AppIntl.of(context)!.schedule_settings_calendar_format_week; case CalendarFormat.twoWeeks: - return AppIntl.of(context).schedule_settings_calendar_format_2_weeks; + return AppIntl.of(context)!.schedule_settings_calendar_format_2_weeks; + default: + return AppIntl.of(context)!.schedule_settings_calendar_format_month; } - return AppIntl.of(context).schedule_settings_calendar_format_month; } String getTextForDay(BuildContext context, StartingDayOfWeek day) { - // ignore: missing_enum_constant_in_switch switch (day) { case StartingDayOfWeek.sunday: - return AppIntl.of(context).schedule_settings_starting_weekday_sunday; + return AppIntl.of(context)!.schedule_settings_starting_weekday_sunday; case StartingDayOfWeek.saturday: - return AppIntl.of(context).schedule_settings_starting_weekday_saturday; + return AppIntl.of(context)!.schedule_settings_starting_weekday_saturday; case StartingDayOfWeek.monday: - return AppIntl.of(context).schedule_settings_starting_weekday_monday; + return AppIntl.of(context)!.schedule_settings_starting_weekday_monday; + default: + return AppIntl.of(context)!.schedule_settings_starting_weekday_monday; } - return AppIntl.of(context).schedule_settings_starting_weekday_monday; } String getTextForWeekDay(BuildContext context, WeekDays day) { - // ignore: missing_enum_constant_in_switch switch (day) { case WeekDays.sunday: - return AppIntl.of(context).schedule_settings_starting_weekday_sunday; + return AppIntl.of(context)!.schedule_settings_starting_weekday_sunday; case WeekDays.saturday: - return AppIntl.of(context).schedule_settings_starting_weekday_saturday; + return AppIntl.of(context)!.schedule_settings_starting_weekday_saturday; case WeekDays.monday: - return AppIntl.of(context).schedule_settings_starting_weekday_monday; + return AppIntl.of(context)!.schedule_settings_starting_weekday_monday; + default: + return AppIntl.of(context)!.schedule_settings_starting_weekday_monday; } - return AppIntl.of(context).schedule_settings_starting_weekday_monday; } } diff --git a/lib/ui/widgets/student_program.dart b/lib/ui/widgets/student_program.dart index d9aefc2de..3e701fd6a 100644 --- a/lib/ui/widgets/student_program.dart +++ b/lib/ui/widgets/student_program.dart @@ -22,8 +22,8 @@ class StudentProgram extends StatefulWidget { class _StudentProgramState extends State with TickerProviderStateMixin { bool showProgramDetails = false; - AnimationController controller; - Animation rotateAnimation; + late AnimationController controller; + late Animation rotateAnimation; @override void initState() { @@ -51,14 +51,14 @@ class _StudentProgramState extends State final bool isLightMode = Theme.of(context).brightness == Brightness.light; final List dataTitles = [ - AppIntl.of(context).profile_code_program, - AppIntl.of(context).profile_average_program, - AppIntl.of(context).profile_number_accumulated_credits_program, - AppIntl.of(context).profile_number_registered_credits_program, - AppIntl.of(context).profile_number_completed_courses_program, - AppIntl.of(context).profile_number_failed_courses_program, - AppIntl.of(context).profile_number_equivalent_courses_program, - AppIntl.of(context).profile_status_program + AppIntl.of(context)!.profile_code_program, + AppIntl.of(context)!.profile_average_program, + AppIntl.of(context)!.profile_number_accumulated_credits_program, + AppIntl.of(context)!.profile_number_registered_credits_program, + AppIntl.of(context)!.profile_number_completed_courses_program, + AppIntl.of(context)!.profile_number_failed_courses_program, + AppIntl.of(context)!.profile_number_equivalent_courses_program, + AppIntl.of(context)!.profile_status_program ]; final List dataFetched = [ @@ -102,7 +102,7 @@ class _StudentProgramState extends State padding: const EdgeInsets.only(top: 5.0), child: AnimatedBuilder( animation: rotateAnimation, - builder: (BuildContext context, Widget child) { + builder: (BuildContext context, Widget? child) { return Transform.rotate( angle: rotateAnimation.value, child: const Icon( diff --git a/pubspec.lock b/pubspec.lock index 17314b6a4..f124c63bd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -64,6 +64,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.3.1" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + build_runner: + dependency: "direct dev" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.3" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.7" built_collection: dependency: transitive description: @@ -534,6 +569,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "10.2.1" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" get_it: dependency: "direct main" description: @@ -625,6 +667,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.13.4" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" http_parser: dependency: transitive description: @@ -667,6 +716,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" js: dependency: transitive description: @@ -737,13 +793,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" mockito: dependency: "direct dev" description: name: mockito url: "https://pub.dartlang.org" source: hosted - version: "5.3.2" + version: "5.4.0" nested: dependency: transitive description: @@ -919,6 +982,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.7.3" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.1" process: dependency: transitive description: @@ -940,6 +1010,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" reorderable_grid_view: dependency: "direct main" description: @@ -1024,6 +1101,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" simple_gesture_detector: dependency: transitive description: @@ -1134,6 +1225,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.12" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" tint: dependency: transitive description: @@ -1239,6 +1337,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.0" webview_flutter: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 2b6401101..c65629c96 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,10 +5,10 @@ description: The 4th generation of ÉTSMobile, the main gateway between the Éco # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 4.33.2+1 +version: 4.34.0+1 environment: - sdk: ">=2.10.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: @@ -80,9 +80,10 @@ dev_dependencies: flutter_test: sdk: flutter lint: ^2.0.1 - mockito: ^5.3.2 + mockito: ^5.3.3 flutter_config: ^2.0.0 flutter_launcher_icons: ^0.11.0 + build_runner: ^2.3.3 flutter_icons: android: "launcher_icon" diff --git a/test/helpers.dart b/test/helpers.dart index 798cb6b61..1dfbd1be7 100644 --- a/test/helpers.dart +++ b/test/helpers.dart @@ -53,7 +53,7 @@ import 'mock/services/siren_flutter_service_mock.dart'; String goldenFilePath(String goldenName) => "./goldenFiles/$goldenName.png"; /// Unregister the service [T] from GetIt -void unregister() { +void unregister() { if (locator.isRegistered()) { locator.unregister(); } @@ -61,7 +61,7 @@ void unregister() { /// Load the l10n classes. Take the [child] widget to test Widget localizedWidget( - {@required Widget child, + {required Widget child, bool useScaffold = true, String locale = 'en', double textScaleFactor = 0.9}) => @@ -79,7 +79,7 @@ Widget localizedWidget( ); /// Load a mock of the [SignetsAPIClient] -SignetsAPIClient setupSignetsApiMock() { +SignetsAPIClientMock setupSignetsApiMock() { unregister(); final service = SignetsAPIClientMock(); @@ -99,7 +99,7 @@ InAppReviewService setupInAppReviewServiceMock() { } /// Load a mock of the [MonETSAPIClient] -MonETSAPIClient setupMonETSApiMock() { +MonETSAPIClientMock setupMonETSApiMock() { unregister(); final service = MonETSAPIClientMock(); @@ -109,7 +109,7 @@ MonETSAPIClient setupMonETSApiMock() { } /// Load a mock of the [AnalyticsService] -AnalyticsService setupAnalyticsServiceMock() { +AnalyticsServiceMock setupAnalyticsServiceMock() { unregister(); final service = AnalyticsServiceMock(); @@ -119,7 +119,7 @@ AnalyticsService setupAnalyticsServiceMock() { } /// Load a mock of the [RiveAnimationService] -RiveAnimationService setupRiveAnimationServiceMock() { +RiveAnimationServiceMock setupRiveAnimationServiceMock() { unregister(); final service = RiveAnimationServiceMock(); @@ -129,7 +129,7 @@ RiveAnimationService setupRiveAnimationServiceMock() { } /// Load a mock of the [InternalInfoService] -InternalInfoService setupInternalInfoServiceMock() { +InternalInfoServiceMock setupInternalInfoServiceMock() { unregister(); final service = InternalInfoServiceMock(); @@ -139,7 +139,7 @@ InternalInfoService setupInternalInfoServiceMock() { } /// Load a mock of the [SirenFlutterService] -SirenFlutterService setupSirenFlutterServiceMock() { +SirenFlutterServiceMock setupSirenFlutterServiceMock() { unregister(); final service = SirenFlutterServiceMock(); @@ -148,7 +148,7 @@ SirenFlutterService setupSirenFlutterServiceMock() { return service; } -void setupFlutterToastMock([WidgetTester tester]) { +void setupFlutterToastMock([WidgetTester? tester]) { const MethodChannel channel = MethodChannel('PonnamKarthik/fluttertoast'); TestDefaultBinaryMessenger messenger; @@ -157,7 +157,7 @@ void setupFlutterToastMock([WidgetTester tester]) { messenger = tester.binding.defaultBinaryMessenger; } else { messenger = - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; + TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger; } messenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { @@ -179,7 +179,7 @@ AppWidgetService setupAppWidgetServiceMock() { } /// Load a mock of the [NavigationService] -NavigationService setupNavigationServiceMock() { +NavigationServiceMock setupNavigationServiceMock() { unregister(); final service = NavigationServiceMock(); @@ -189,7 +189,7 @@ NavigationService setupNavigationServiceMock() { } /// Load a mock of the [GithubApi] -GithubApi setupGithubApiMock() { +GithubApiMock setupGithubApiMock() { unregister(); final service = GithubApiMock(); @@ -199,7 +199,7 @@ GithubApi setupGithubApiMock() { } /// Load a mock of the [FlutterSecureStorage] -FlutterSecureStorage setupFlutterSecureStorageMock() { +FlutterSecureStorageMock setupFlutterSecureStorageMock() { unregister(); final service = FlutterSecureStorageMock(); @@ -209,7 +209,7 @@ FlutterSecureStorage setupFlutterSecureStorageMock() { } /// Load a mock of the [UserRepository] -UserRepository setupUserRepositoryMock() { +UserRepositoryMock setupUserRepositoryMock() { unregister(); final service = UserRepositoryMock(); @@ -224,7 +224,7 @@ Future setupAppIntl() async { } /// Load a mock of the [CacheManager] -CacheManager setupCacheManagerMock() { +CacheManagerMock setupCacheManagerMock() { unregister(); final service = CacheManagerMock(); @@ -245,7 +245,7 @@ Logger setupLogger() { } /// Load a mock of the [PreferencesService] -PreferencesService setupPreferencesServiceMock() { +PreferencesServiceMock setupPreferencesServiceMock() { unregister(); final service = PreferencesServiceMock(); @@ -255,7 +255,7 @@ PreferencesService setupPreferencesServiceMock() { } /// Load a mock of the [SettingsManager] -SettingsManager setupSettingsManagerMock() { +SettingsManagerMock setupSettingsManagerMock() { unregister(); final service = SettingsManagerMock(); @@ -265,7 +265,7 @@ SettingsManager setupSettingsManagerMock() { } /// Load a mock of the [CourseRepository] -CourseRepository setupCourseRepositoryMock() { +CourseRepositoryMock setupCourseRepositoryMock() { unregister(); final service = CourseRepositoryMock(); @@ -276,7 +276,7 @@ CourseRepository setupCourseRepositoryMock() { /// Load a mock of the [NetworkingService] /// Will also stub the first value of changeConnectivityStream -NetworkingService setupNetworkingServiceMock() { +NetworkingServiceMock setupNetworkingServiceMock() { unregister(); final service = NetworkingServiceMock(); @@ -288,7 +288,7 @@ NetworkingService setupNetworkingServiceMock() { return service; } -void setupInAppReviewMock([WidgetTester tester]) { +void setupInAppReviewMock([WidgetTester? tester]) { const MethodChannel channel = MethodChannel('dev.britannio.in_app_review'); TestDefaultBinaryMessenger messenger; @@ -297,7 +297,7 @@ void setupInAppReviewMock([WidgetTester tester]) { messenger = tester.binding.defaultBinaryMessenger; } else { messenger = - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; + TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger; } messenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async { @@ -309,7 +309,7 @@ void setupInAppReviewMock([WidgetTester tester]) { } /// Load a mock of the [LaunchUrlService] -LaunchUrlService setupLaunchUrlServiceMock() { +LaunchUrlServiceMock setupLaunchUrlServiceMock() { unregister(); final service = LaunchUrlServiceMock(); @@ -319,7 +319,7 @@ LaunchUrlService setupLaunchUrlServiceMock() { } /// Load a mock of the [RemoteConfigService] -RemoteConfigService setupRemoteConfigServiceMock() { +RemoteConfigServiceMock setupRemoteConfigServiceMock() { unregister(); final service = RemoteConfigServiceMock(); @@ -329,7 +329,7 @@ RemoteConfigService setupRemoteConfigServiceMock() { } /// Load a mock of the [QuickLinkRepository] -QuickLinkRepository setupQuickLinkRepositoryMock() { +QuickLinkRepositoryMock setupQuickLinkRepositoryMock() { unregister(); final repository = QuickLinkRepositoryMock(); diff --git a/test/managers/course_repository_test.dart b/test/managers/course_repository_test.dart index 9ec3e0da1..b7cebb9e7 100644 --- a/test/managers/course_repository_test.dart +++ b/test/managers/course_repository_test.dart @@ -18,16 +18,17 @@ import 'package:notredame/core/services/analytics_service.dart'; import '../helpers.dart'; import '../mock/managers/cache_manager_mock.dart'; import '../mock/managers/user_repository_mock.dart'; +import '../mock/services/analytics_service_mock.dart'; import '../mock/services/networking_service_mock.dart'; void main() { - AnalyticsService analyticsService; - NetworkingServiceMock networkingService; - UserRepository userRepository; - CacheManager cacheManager; + late AnalyticsServiceMock analyticsServiceMock; + late NetworkingServiceMock networkingServiceMock; + late UserRepositoryMock userRepositoryMock; + late CacheManagerMock cacheManagerMock; + late SignetsAPIClientMock signetsApiMock; - CourseRepository manager; - SignetsAPIClient signetsApi; + late CourseRepository manager; final Session session = Session( shortName: 'NOW', @@ -47,26 +48,26 @@ void main() { group("CourseRepository - ", () { setUp(() { // Setup needed services and managers - analyticsService = setupAnalyticsServiceMock(); - signetsApi = setupSignetsApiMock(); - userRepository = setupUserRepositoryMock(); - cacheManager = setupCacheManagerMock(); - networkingService = setupNetworkingServiceMock() as NetworkingServiceMock; + analyticsServiceMock = setupAnalyticsServiceMock(); + signetsApiMock = setupSignetsApiMock(); + userRepositoryMock = setupUserRepositoryMock(); + cacheManagerMock = setupCacheManagerMock(); + networkingServiceMock = setupNetworkingServiceMock(); setupLogger(); manager = CourseRepository(); }); tearDown(() { - clearInteractions(analyticsService); + clearInteractions(analyticsServiceMock); unregister(); - clearInteractions(signetsApi); + clearInteractions(signetsApiMock); unregister(); - clearInteractions(userRepository); + clearInteractions(userRepositoryMock); unregister(); - clearInteractions(cacheManager); + clearInteractions(cacheManagerMock); unregister(); - clearInteractions(networkingService); + clearInteractions(networkingServiceMock); unregister(); }); @@ -86,32 +87,31 @@ void main() { setUp(() { // Stub a user - UserRepositoryMock.stubMonETSUser(userRepository as UserRepositoryMock, - MonETSUser(domain: null, typeUsagerId: null, username: username)); - UserRepositoryMock.stubGetPassword( - userRepository as UserRepositoryMock, "password"); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); + UserRepositoryMock.stubGetPassword(userRepositoryMock, "password"); // Stub some sessions - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode([])); SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, [session]); + signetsApiMock, username, [session]); // Stub to simulate that the user has an active internet connection - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); }); test("Activities are loaded from cache.", () async { // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)); // Stub the SignetsAPI to return 0 activities SignetsAPIClientMock.stubGetCoursesActivities( - signetsApi as SignetsAPIClientMock, session.shortName, []); + signetsApiMock, session.shortName, []); expect(manager.coursesActivities, isNull); - final List results = + final List? results = await manager.getCoursesActivities(fromCacheOnly: true); expect(results, isInstanceOf>()); @@ -120,16 +120,16 @@ void main() { reason: "The list of activities should not be empty"); verifyInOrder( - [cacheManager.get(CourseRepository.coursesActivitiesCacheKey)]); + [cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey)]); }); test("Activities are only loaded from cache.", () async { // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)); expect(manager.coursesActivities, isNull); - final List results = + final List? results = await manager.getCoursesActivities(fromCacheOnly: true); expect(results, isInstanceOf>()); @@ -138,26 +138,26 @@ void main() { reason: "The list of activities should not be empty"); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), ]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(userRepositoryMock); }); test( "Trying to recover activities from cache but an exception is raised.", () async { // Stub the cache to throw an exception - CacheManagerMock.stubGetException(cacheManager as CacheManagerMock, - CourseRepository.coursesActivitiesCacheKey); + CacheManagerMock.stubGetException( + cacheManagerMock, CourseRepository.coursesActivitiesCacheKey); // Stub the SignetsAPI to return 0 activities SignetsAPIClientMock.stubGetCoursesActivities( - signetsApi as SignetsAPIClientMock, session.shortName, []); + signetsApiMock, session.shortName, []); expect(manager.coursesActivities, isNull); - final List results = + final List? results = await manager.getCoursesActivities(); expect(results, isInstanceOf>()); @@ -166,39 +166,40 @@ void main() { reason: "The list of activities should be empty"); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCoursesActivities( + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCoursesActivities( username: username, password: anyNamed("password"), session: session.shortName), - cacheManager.update(CourseRepository.coursesActivitiesCacheKey, any) + cacheManagerMock.update( + CourseRepository.coursesActivitiesCacheKey, any) ]); - verify(signetsApi.getSessions( + verify(signetsApiMock.getSessions( username: username, password: anyNamed("password"))) .called(1); }); test("Doesn't retrieve sessions if they are already loaded", () async { // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)); // Stub the SignetsAPI to return 1 activities SignetsAPIClientMock.stubGetCoursesActivities( - signetsApi as SignetsAPIClientMock, session.shortName, activities); + signetsApiMock, session.shortName, activities); // Load the sessions await manager.getSessions(); expect(manager.sessions, isNotEmpty); - clearInteractions(cacheManager); - clearInteractions(userRepository); - clearInteractions(signetsApi); + clearInteractions(cacheManagerMock); + clearInteractions(userRepositoryMock); + clearInteractions(signetsApiMock); expect(manager.coursesActivities, isNull); - final List results = + final List? results = await manager.getCoursesActivities(); expect(results, isInstanceOf>()); @@ -207,93 +208,95 @@ void main() { reason: "The list of activities should not be empty"); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCoursesActivities( + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCoursesActivities( username: username, password: anyNamed("password"), session: session.shortName), - cacheManager.update(CourseRepository.coursesActivitiesCacheKey, any) + cacheManagerMock.update( + CourseRepository.coursesActivitiesCacheKey, any) ]); - verifyNoMoreInteractions(signetsApi); + verifyNoMoreInteractions(signetsApiMock); }); test("getSessions fails", () async { // Stub SignetsApi to throw an exception - reset(signetsApi); - SignetsAPIClientMock.stubGetSessionsException( - signetsApi as SignetsAPIClientMock, username); + reset(signetsApiMock); + SignetsAPIClientMock.stubGetSessionsException(signetsApiMock, username); // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)); // Stub the SignetsAPI to return 0 activities SignetsAPIClientMock.stubGetCoursesActivities( - signetsApi as SignetsAPIClientMock, session.shortName, []); + signetsApiMock, session.shortName, []); expect(manager.coursesActivities, isNull); expect(manager.getCoursesActivities(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.coursesActivities, isEmpty, reason: "The list of activities should be empty"); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - analyticsService.logError(CourseRepository.tag, any, any, any) + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); }); test("User authentication fails.", () async { // Stub the cache to return 0 activities - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode([])); // Load the sessions await manager.getSessions(); expect(manager.sessions, isNotEmpty); - clearInteractions(signetsApi); + clearInteractions(signetsApiMock); // Stub an authentication error - reset(userRepository); - UserRepositoryMock.stubGetPasswordException( - userRepository as UserRepositoryMock); + reset(userRepositoryMock); + UserRepositoryMock.stubGetPasswordException(userRepositoryMock); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); expect(manager.getCoursesActivities(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.coursesActivities, isEmpty, reason: "There isn't any activities saved in the cache so the list should be empty"); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), - userRepository.getPassword(), - analyticsService.logError(CourseRepository.tag, any, any, any) + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), + userRepositoryMock.monETSUser, + userRepositoryMock.getPassword(), + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(userRepositoryMock); }); test( "SignetsAPI returns new activities, the old ones should be maintained and the cache updated.", () async { // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)); final CourseActivity courseActivity = CourseActivity( @@ -307,12 +310,10 @@ void main() { // Stub the SignetsAPI to return 2 activities SignetsAPIClientMock.stubGetCoursesActivities( - signetsApi as SignetsAPIClientMock, - session.shortName, - [activity, courseActivity]); + signetsApiMock, session.shortName, [activity, courseActivity]); expect(manager.coursesActivities, isNull); - final List results = + final List? results = await manager.getCoursesActivities(); expect(results, isInstanceOf>()); @@ -321,14 +322,14 @@ void main() { reason: "The list of activities should not be empty"); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCoursesActivities( + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCoursesActivities( username: username, password: anyNamed("password"), session: session.shortName), - cacheManager.update(CourseRepository.coursesActivitiesCacheKey, + cacheManagerMock.update(CourseRepository.coursesActivitiesCacheKey, jsonEncode([activity, courseActivity])) ]); }); @@ -337,15 +338,15 @@ void main() { "SignetsAPI returns activities that already exists, should avoid duplicata.", () async { // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)); // Stub the SignetsAPI to return the same activity as the cache SignetsAPIClientMock.stubGetCoursesActivities( - signetsApi as SignetsAPIClientMock, session.shortName, activities); + signetsApiMock, session.shortName, activities); expect(manager.coursesActivities, isNull); - final List results = + final List? results = await manager.getCoursesActivities(); expect(results, isInstanceOf>()); @@ -354,14 +355,14 @@ void main() { reason: "The list of activities should not have duplicata"); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCoursesActivities( + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCoursesActivities( username: username, password: anyNamed("password"), session: session.shortName), - cacheManager.update(CourseRepository.coursesActivitiesCacheKey, + cacheManagerMock.update(CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)) ]); }); @@ -370,15 +371,15 @@ void main() { "SignetsAPI returns activities that changed (for example class location changed).", () async { // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)); // Load the sessions await manager.getSessions(); expect(manager.sessions, isNotEmpty); - clearInteractions(cacheManager); - clearInteractions(userRepository); - clearInteractions(signetsApi); + clearInteractions(cacheManagerMock); + clearInteractions(userRepositoryMock); + clearInteractions(signetsApiMock); final changedActivity = CourseActivity( courseGroup: activity.courseGroup, @@ -391,12 +392,10 @@ void main() { // Stub the SignetsAPI to return the same activity as the cache SignetsAPIClientMock.stubGetCoursesActivities( - signetsApi as SignetsAPIClientMock, - session.shortName, - [changedActivity]); + signetsApiMock, session.shortName, [changedActivity]); expect(manager.coursesActivities, isNull); - final List results = + final List? results = await manager.getCoursesActivities(); expect(results, isInstanceOf>()); @@ -405,48 +404,48 @@ void main() { reason: "The list of activities should be updated"); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCoursesActivities( + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCoursesActivities( username: username, password: anyNamed("password"), session: session.shortName), - cacheManager.update(CourseRepository.coursesActivitiesCacheKey, + cacheManagerMock.update(CourseRepository.coursesActivitiesCacheKey, jsonEncode([changedActivity])) ]); }); test("SignetsAPI raise a exception.", () async { // Stub the cache to return no activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode([])); // Stub the SignetsAPI to throw an exception SignetsAPIClientMock.stubGetCoursesActivitiesException( - signetsApi as SignetsAPIClientMock, session.shortName, + signetsApiMock, session.shortName, exceptionToThrow: const ApiException(prefix: CourseRepository.tag)); expect(manager.coursesActivities, isNull); expect(manager.getCoursesActivities(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.coursesActivities, isEmpty, reason: "The list of activities should be empty"); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCoursesActivities( + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCoursesActivities( username: username, password: anyNamed("password"), session: session.shortName), - analyticsService.logError(CourseRepository.tag, any, any, any) + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); }); @@ -454,18 +453,18 @@ void main() { "Cache update fails, should still return the updated list of activities.", () async { // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)); // Stub the SignetsAPI to return 1 activity SignetsAPIClientMock.stubGetCoursesActivities( - signetsApi as SignetsAPIClientMock, session.shortName, activities); + signetsApiMock, session.shortName, activities); - CacheManagerMock.stubUpdateException(cacheManager as CacheManagerMock, - CourseRepository.coursesActivitiesCacheKey); + CacheManagerMock.stubUpdateException( + cacheManagerMock, CourseRepository.coursesActivitiesCacheKey); expect(manager.coursesActivities, isNull); - final List results = + final List? results = await manager.getCoursesActivities(); expect(results, isInstanceOf>()); @@ -474,10 +473,10 @@ void main() { reason: "The list of activities should not be empty"); verifyInOrder([ - cacheManager.get(CourseRepository.coursesActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCoursesActivities( + cacheManagerMock.get(CourseRepository.coursesActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCoursesActivities( username: username, password: anyNamed("password"), session: session.shortName) @@ -487,12 +486,12 @@ void main() { test("Should force fromCacheOnly mode when user has no connectivity", () async { // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesActivitiesCacheKey, jsonEncode(activities)); //Stub the networkingService to return no connectivity - reset(networkingService); - NetworkingServiceMock.stubHasConnectivity(networkingService, + reset(networkingServiceMock); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock, hasConnectivity: false); final activitiesCache = await manager.getCoursesActivities(); @@ -535,31 +534,30 @@ void main() { setUp(() { // Stub a user - UserRepositoryMock.stubMonETSUser(userRepository as UserRepositoryMock, - MonETSUser(domain: null, typeUsagerId: null, username: username)); - UserRepositoryMock.stubGetPassword( - userRepository as UserRepositoryMock, "password"); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); + UserRepositoryMock.stubGetPassword(userRepositoryMock, "password"); // Stub some sessions - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode([])); SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, [session]); + signetsApiMock, username, [session]); // Stub to simulate that the user has an active internet connection - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); }); test("Activities are loaded from cache.", () async { // Stub the cache to return 1 activity CacheManagerMock.stubGet( - cacheManager as CacheManagerMock, + cacheManagerMock, CourseRepository.scheduleActivitiesCacheKey, jsonEncode(scheduleActivities)); // Stub the SignetsAPI to return 0 activities SignetsAPIClientMock.stubGetScheduleActivities( - signetsApi as SignetsAPIClientMock, session.shortName, []); + signetsApiMock, session.shortName, []); expect(manager.coursesActivities, isNull); final List results = @@ -571,21 +569,22 @@ void main() { reason: "The list of activities should not be empty"); verifyInOrder([ - cacheManager.get(CourseRepository.scheduleActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getScheduleActivities( + cacheManagerMock.get(CourseRepository.scheduleActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getScheduleActivities( username: username, password: anyNamed("password"), session: session.shortName), - cacheManager.update(CourseRepository.scheduleActivitiesCacheKey, any) + cacheManagerMock.update( + CourseRepository.scheduleActivitiesCacheKey, any) ]); }); test("Activities are only loaded from cache.", () async { // Stub the cache to return 1 activity CacheManagerMock.stubGet( - cacheManager as CacheManagerMock, + cacheManagerMock, CourseRepository.scheduleActivitiesCacheKey, jsonEncode(scheduleActivities)); @@ -599,23 +598,23 @@ void main() { reason: "The list of activities should not be empty"); verifyInOrder([ - cacheManager.get(CourseRepository.scheduleActivitiesCacheKey), + cacheManagerMock.get(CourseRepository.scheduleActivitiesCacheKey), ]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(userRepositoryMock); }); test( "Trying to recover activities from cache but an exception is raised.", () async { // Stub the cache to throw an exception - CacheManagerMock.stubGetException(cacheManager as CacheManagerMock, - CourseRepository.scheduleActivitiesCacheKey); + CacheManagerMock.stubGetException( + cacheManagerMock, CourseRepository.scheduleActivitiesCacheKey); // Stub the SignetsAPI to return 0 activities SignetsAPIClientMock.stubGetScheduleActivities( - signetsApi as SignetsAPIClientMock, session.shortName, []); + signetsApiMock, session.shortName, []); expect(manager.scheduleActivities, isNull); final List results = @@ -627,17 +626,18 @@ void main() { reason: "The list of activities should be empty"); verifyInOrder([ - cacheManager.get(CourseRepository.scheduleActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getScheduleActivities( + cacheManagerMock.get(CourseRepository.scheduleActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getScheduleActivities( username: username, password: anyNamed("password"), session: session.shortName), - cacheManager.update(CourseRepository.scheduleActivitiesCacheKey, any) + cacheManagerMock.update( + CourseRepository.scheduleActivitiesCacheKey, any) ]); - verify(signetsApi.getSessions( + verify(signetsApiMock.getSessions( username: username, password: anyNamed("password"))) .called(1); }); @@ -645,20 +645,20 @@ void main() { test("Doesn't retrieve sessions if they are already loaded", () async { // Stub the cache to return 1 activity CacheManagerMock.stubGet( - cacheManager as CacheManagerMock, + cacheManagerMock, CourseRepository.scheduleActivitiesCacheKey, jsonEncode(scheduleActivities)); // Stub the SignetsAPI to return 0 activities SignetsAPIClientMock.stubGetScheduleActivities( - signetsApi as SignetsAPIClientMock, session.shortName, []); + signetsApiMock, session.shortName, []); // Load the sessions await manager.getSessions(); expect(manager.sessions, isNotEmpty); - clearInteractions(cacheManager); - clearInteractions(userRepository); - clearInteractions(signetsApi); + clearInteractions(cacheManagerMock); + clearInteractions(userRepositoryMock); + clearInteractions(signetsApiMock); expect(manager.scheduleActivities, isNull); final List results = @@ -670,88 +670,90 @@ void main() { reason: "The list of activities should not be empty"); verifyInOrder([ - cacheManager.get(CourseRepository.scheduleActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getScheduleActivities( + cacheManagerMock.get(CourseRepository.scheduleActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getScheduleActivities( username: username, password: anyNamed("password"), session: session.shortName), - cacheManager.update(CourseRepository.scheduleActivitiesCacheKey, any) + cacheManagerMock.update( + CourseRepository.scheduleActivitiesCacheKey, any) ]); - verifyNoMoreInteractions(signetsApi); + verifyNoMoreInteractions(signetsApiMock); }); test("getSessions fails", () async { // Stub SignetsApi to throw an exception - reset(signetsApi); - SignetsAPIClientMock.stubGetSessionsException( - signetsApi as SignetsAPIClientMock, username); + reset(signetsApiMock); + SignetsAPIClientMock.stubGetSessionsException(signetsApiMock, username); // Stub the cache to return 1 activity CacheManagerMock.stubGet( - cacheManager as CacheManagerMock, + cacheManagerMock, CourseRepository.scheduleActivitiesCacheKey, jsonEncode(scheduleActivities)); // Stub the SignetsAPI to return 0 activities SignetsAPIClientMock.stubGetScheduleActivities( - signetsApi as SignetsAPIClientMock, session.shortName, []); + signetsApiMock, session.shortName, []); expect(manager.scheduleActivities, isNull); expect(manager.getScheduleActivities(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.scheduleActivities, isEmpty, reason: "The list of activities should be empty"); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.scheduleActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - analyticsService.logError(CourseRepository.tag, any, any, any) + cacheManagerMock.get(CourseRepository.scheduleActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); }); test("User authentication fails.", () async { // Stub the cache to return 0 activities - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.scheduleActivitiesCacheKey, jsonEncode([])); // Load the sessions await manager.getSessions(); expect(manager.sessions, isNotEmpty); - clearInteractions(signetsApi); + clearInteractions(signetsApiMock); // Stub an authentication error - reset(userRepository); - UserRepositoryMock.stubGetPasswordException( - userRepository as UserRepositoryMock); + reset(userRepositoryMock); + UserRepositoryMock.stubGetPasswordException(userRepositoryMock); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); expect(manager.getScheduleActivities(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.scheduleActivities, isEmpty, reason: "There isn't any activities saved in the cache so the list should be empty"); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.scheduleActivitiesCacheKey), - userRepository.getPassword(), - analyticsService.logError(CourseRepository.tag, any, any, any) + cacheManagerMock.get(CourseRepository.scheduleActivitiesCacheKey), + userRepositoryMock.monETSUser, + userRepositoryMock.getPassword(), + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(userRepositoryMock); }); test( @@ -759,15 +761,13 @@ void main() { () async { // Stub the cache to return 1 activity CacheManagerMock.stubGet( - cacheManager as CacheManagerMock, + cacheManagerMock, CourseRepository.scheduleActivitiesCacheKey, jsonEncode(scheduleActivities)); // Stub the SignetsAPI to return the same activity as the cache SignetsAPIClientMock.stubGetScheduleActivities( - signetsApi as SignetsAPIClientMock, - session.shortName, - scheduleActivities); + signetsApiMock, session.shortName, scheduleActivities); expect(manager.scheduleActivities, isNull); final List results = @@ -779,48 +779,48 @@ void main() { reason: "The list of activities should not have duplicata"); verifyInOrder([ - cacheManager.get(CourseRepository.scheduleActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getScheduleActivities( + cacheManagerMock.get(CourseRepository.scheduleActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getScheduleActivities( username: username, password: anyNamed("password"), session: session.shortName), - cacheManager.update(CourseRepository.scheduleActivitiesCacheKey, + cacheManagerMock.update(CourseRepository.scheduleActivitiesCacheKey, jsonEncode(scheduleActivities)) ]); }); test("SignetsAPI raise a exception.", () async { // Stub the cache to return no activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.scheduleActivitiesCacheKey, jsonEncode([])); // Stub the SignetsAPI to throw an exception SignetsAPIClientMock.stubGetScheduleActivitiesException( - signetsApi as SignetsAPIClientMock, session.shortName, + signetsApiMock, session.shortName, exceptionToThrow: const ApiException(prefix: CourseRepository.tag)); expect(manager.scheduleActivities, isNull); expect(manager.getScheduleActivities(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.scheduleActivities, isEmpty, reason: "The list of activities should be empty"); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.scheduleActivitiesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getScheduleActivities( + cacheManagerMock.get(CourseRepository.scheduleActivitiesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getScheduleActivities( username: username, password: anyNamed("password"), session: session.shortName), - analyticsService.logError(CourseRepository.tag, any, any, any) + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); }); }); @@ -850,19 +850,16 @@ void main() { setUp(() { // Stub to simulate presence of session cache - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode(sessions)); // Stub SignetsApi answer to test only the cache retrieving - SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, []); - UserRepositoryMock.stubMonETSUser( - userRepository as UserRepositoryMock, user); - UserRepositoryMock.stubGetPassword( - userRepository as UserRepositoryMock, password); + SignetsAPIClientMock.stubGetSessions(signetsApiMock, username, []); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, user); + UserRepositoryMock.stubGetPassword(userRepositoryMock, password); // Stub to simulate that the user has an active internet connection - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); }); test("Sessions are loaded from cache", () async { @@ -875,11 +872,11 @@ void main() { reason: 'The sessions list should now be loaded.'); verifyInOrder([ - cacheManager.get(CourseRepository.sessionsCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getSessions(username: username, password: password), - cacheManager.update( + cacheManagerMock.get(CourseRepository.sessionsCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getSessions(username: username, password: password), + cacheManagerMock.update( CourseRepository.sessionsCacheKey, jsonEncode(sessions)) ]); }); @@ -887,9 +884,9 @@ void main() { test("Trying to load sessions from cache but cache doesn't exist", () async { // Stub to simulate an exception when trying to get the sessions from the cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGetException(cacheManager as CacheManagerMock, - CourseRepository.sessionsCacheKey); + reset(cacheManagerMock); + CacheManagerMock.stubGetException( + cacheManagerMock, CourseRepository.sessionsCacheKey); expect(manager.sessions, isNull); final results = await manager.getSessions(); @@ -899,24 +896,25 @@ void main() { expect(manager.sessions, []); verifyInOrder([ - cacheManager.get(CourseRepository.sessionsCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getSessions(username: username, password: password), - cacheManager.update(CourseRepository.sessionsCacheKey, jsonEncode([])) + cacheManagerMock.get(CourseRepository.sessionsCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getSessions(username: username, password: password), + cacheManagerMock.update( + CourseRepository.sessionsCacheKey, jsonEncode([])) ]); }); test("SignetsAPI return another session", () async { // Stub to simulate presence of session cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + reset(cacheManagerMock); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode([])); // Stub SignetsApi answer to test only the cache retrieving - reset(signetsApi as SignetsAPIClientMock); + reset(signetsApiMock); SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, sessions); + signetsApiMock, username, sessions); expect(manager.sessions, isNull); final results = await manager.getSessions(); @@ -927,20 +925,20 @@ void main() { reason: 'The sessions list should now be loaded.'); verifyInOrder([ - cacheManager.get(CourseRepository.sessionsCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getSessions(username: username, password: password), - cacheManager.update( + cacheManagerMock.get(CourseRepository.sessionsCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getSessions(username: username, password: password), + cacheManagerMock.update( CourseRepository.sessionsCacheKey, jsonEncode(sessions)) ]); }); test("SignetsAPI return a session that already exists", () async { // Stub SignetsApi answer to test only the cache retrieving - reset(signetsApi as SignetsAPIClientMock); + reset(signetsApiMock); SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, sessions); + signetsApiMock, username, sessions); expect(manager.sessions, isNull); final results = await manager.getSessions(); @@ -951,24 +949,23 @@ void main() { reason: 'The sessions list should not have any duplicata..'); verifyInOrder([ - cacheManager.get(CourseRepository.sessionsCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getSessions(username: username, password: password), - cacheManager.update( + cacheManagerMock.get(CourseRepository.sessionsCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getSessions(username: username, password: password), + cacheManagerMock.update( CourseRepository.sessionsCacheKey, jsonEncode(sessions)) ]); }); test("SignetsAPI return an exception", () async { // Stub to simulate presence of session cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + reset(cacheManagerMock); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode([])); // Stub SignetsApi answer to test only the cache retrieving - SignetsAPIClientMock.stubGetSessionsException( - signetsApi as SignetsAPIClientMock, username); + SignetsAPIClientMock.stubGetSessionsException(signetsApiMock, username); expect(manager.sessions, isNull); expect(manager.getSessions(), throwsA(isInstanceOf())); @@ -976,33 +973,33 @@ void main() { reason: 'The session list should be empty'); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.sessionsCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getSessions(username: username, password: password), - analyticsService.logError(CourseRepository.tag, any, any, any) + cacheManagerMock.get(CourseRepository.sessionsCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getSessions(username: username, password: password), + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); verifyNever( - cacheManager.update(CourseRepository.sessionsCacheKey, any)); + cacheManagerMock.update(CourseRepository.sessionsCacheKey, any)); }); test("Cache update fail", () async { // Stub to simulate presence of session cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + reset(cacheManagerMock); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode([])); // Stub to simulate exception when updating cache - CacheManagerMock.stubUpdateException(cacheManager as CacheManagerMock, - CourseRepository.sessionsCacheKey); + CacheManagerMock.stubUpdateException( + cacheManagerMock, CourseRepository.sessionsCacheKey); // Stub SignetsApi answer to test only the cache retrieving SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, sessions); + signetsApiMock, username, sessions); expect(manager.sessions, isNull); final results = await manager.getSessions(); @@ -1014,22 +1011,21 @@ void main() { 'The sessions list should now be loaded even if the caching fails.'); verifyInOrder([ - cacheManager.get(CourseRepository.sessionsCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getSessions(username: username, password: password) + cacheManagerMock.get(CourseRepository.sessionsCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getSessions(username: username, password: password) ]); }); test("UserRepository return an exception", () async { // Stub to simulate presence of session cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + reset(cacheManagerMock); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode([])); // Stub UserRepository to throw a exception - UserRepositoryMock.stubGetPasswordException( - userRepository as UserRepositoryMock); + UserRepositoryMock.stubGetPasswordException(userRepositoryMock); expect(manager.sessions, isNull); expect(manager.getSessions(), throwsA(isInstanceOf())); @@ -1037,33 +1033,33 @@ void main() { reason: 'The session list should be empty'); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.sessionsCacheKey), - userRepository.getPassword(), - analyticsService.logError(CourseRepository.tag, any, any, any) + cacheManagerMock.get(CourseRepository.sessionsCacheKey), + userRepositoryMock.getPassword(), + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); - verifyNever(signetsApi.getSessions( + verifyNever(signetsApiMock.getSessions( username: anyNamed("username"), password: anyNamed("password"))); verifyNever( - cacheManager.update(CourseRepository.sessionsCacheKey, any)); + cacheManagerMock.update(CourseRepository.sessionsCacheKey, any)); }); test("Should not try to fetch from signets when offline", () async { - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode(sessions)); //Stub the networkingService to return no connectivity - reset(networkingService); - NetworkingServiceMock.stubHasConnectivity(networkingService, + reset(networkingServiceMock); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock, hasConnectivity: false); final sessionsCache = await manager.getSessions(); expect(sessionsCache, sessions); verifyNever( - signetsApi.getSessions(username: username, password: password)); + signetsApiMock.getSessions(username: username, password: password)); }); }); @@ -1107,14 +1103,13 @@ void main() { final sessions = [oldSession, active]; SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, sessions); - UserRepositoryMock.stubMonETSUser(userRepository as UserRepositoryMock, - MonETSUser(domain: null, typeUsagerId: null, username: username)); - UserRepositoryMock.stubGetPassword( - userRepository as UserRepositoryMock, password); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + signetsApiMock, username, sessions); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); + UserRepositoryMock.stubGetPassword(userRepositoryMock, password); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode(sessions)); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); await manager.getSessions(); @@ -1141,14 +1136,13 @@ void main() { final sessions = [oldSession, old]; SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, sessions); - UserRepositoryMock.stubMonETSUser(userRepository as UserRepositoryMock, - MonETSUser(domain: null, typeUsagerId: null, username: username)); - UserRepositoryMock.stubGetPassword( - userRepository as UserRepositoryMock, password); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + signetsApiMock, username, sessions); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); + UserRepositoryMock.stubGetPassword(userRepositoryMock, password); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode(sessions)); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); await manager.getSessions(); @@ -1175,14 +1169,13 @@ void main() { final sessions = [oldSession, active]; SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, sessions); - UserRepositoryMock.stubMonETSUser(userRepository as UserRepositoryMock, - MonETSUser(domain: null, typeUsagerId: null, username: username)); - UserRepositoryMock.stubGetPassword( - userRepository as UserRepositoryMock, password); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + signetsApiMock, username, sessions); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); + UserRepositoryMock.stubGetPassword(userRepositoryMock, password); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode(sessions)); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); await manager.getSessions(); @@ -1190,15 +1183,13 @@ void main() { }); test("there is no session", () async { - SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, []); - UserRepositoryMock.stubMonETSUser(userRepository as UserRepositoryMock, - MonETSUser(domain: null, typeUsagerId: null, username: username)); - UserRepositoryMock.stubGetPassword( - userRepository as UserRepositoryMock, password); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + SignetsAPIClientMock.stubGetSessions(signetsApiMock, username, []); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); + UserRepositoryMock.stubGetPassword(userRepositoryMock, password); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode([])); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); await manager.getSessions(); @@ -1274,28 +1265,25 @@ void main() { setUp(() { // Stub a user - UserRepositoryMock.stubMonETSUser(userRepository as UserRepositoryMock, - MonETSUser(domain: null, typeUsagerId: null, username: username)); - UserRepositoryMock.stubGetPassword( - userRepository as UserRepositoryMock, "password"); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); + UserRepositoryMock.stubGetPassword(userRepositoryMock, "password"); // Stub some sessions SignetsAPIClientMock.stubGetSessions( - signetsApi as SignetsAPIClientMock, username, [session]); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + signetsApiMock, username, [session]); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.sessionsCacheKey, jsonEncode([])); // Stub to simulate that the user has an active internet connection - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); }); test("Courses are loaded from cache and cache is updated", () async { - SignetsAPIClientMock.stubGetCourses( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourses(signetsApiMock, username, coursesToReturn: [courseWithGrade]); - SignetsAPIClientMock.stubGetCourseReviews( - signetsApi as SignetsAPIClientMock, username); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + SignetsAPIClientMock.stubGetCourseReviews(signetsApiMock, username); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([courseWithGrade])); expect(manager.courses, isNull); @@ -1307,11 +1295,11 @@ void main() { reason: 'The courses list should now be loaded.'); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - cacheManager.update( + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([courseWithGrade])) ]); }); @@ -1319,7 +1307,7 @@ void main() { test("Courses are only loaded from cache", () async { expect(manager.courses, isNull); CacheManagerMock.stubGet( - cacheManager as CacheManagerMock, + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([ courseWithGrade, @@ -1343,11 +1331,11 @@ void main() { ], reason: 'The courses list should now be loaded.'); - verifyInOrder([cacheManager.get(CourseRepository.coursesCacheKey)]); + verifyInOrder([cacheManagerMock.get(CourseRepository.coursesCacheKey)]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(cacheManager); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(cacheManagerMock); + verifyNoMoreInteractions(userRepositoryMock); }); test("Signets return a updated version of a course", () async { @@ -1361,14 +1349,12 @@ void main() { title: 'Cours générique'); CacheManagerMock.stubGet( - cacheManager as CacheManagerMock, + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([courseWithGrade, courseWithGradeDuplicate])); - SignetsAPIClientMock.stubGetCourses( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourses(signetsApiMock, username, coursesToReturn: [courseFetched, courseWithGradeDuplicate]); - SignetsAPIClientMock.stubGetCourseReviews( - signetsApi as SignetsAPIClientMock, username); + SignetsAPIClientMock.stubGetCourseReviews(signetsApiMock, username); expect(manager.courses, isNull); final results = await manager.getCourses(); @@ -1379,11 +1365,11 @@ void main() { reason: 'The courses list should now be loaded.'); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - cacheManager.update(CourseRepository.coursesCacheKey, + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + cacheManagerMock.update(CourseRepository.coursesCacheKey, jsonEncode([courseFetched, courseWithGradeDuplicate])) ]); }); @@ -1391,10 +1377,9 @@ void main() { test("Trying to recover courses from cache failed (exception raised)", () async { expect(manager.courses, isNull); - SignetsAPIClientMock.stubGetCourses( - signetsApi as SignetsAPIClientMock, username); + SignetsAPIClientMock.stubGetCourses(signetsApiMock, username); CacheManagerMock.stubGetException( - cacheManager as CacheManagerMock, CourseRepository.coursesCacheKey); + cacheManagerMock, CourseRepository.coursesCacheKey); final results = await manager.getCourses(fromCacheOnly: true); @@ -1404,71 +1389,71 @@ void main() { reason: 'The courses list should be empty.'); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), + cacheManagerMock.get(CourseRepository.coursesCacheKey), ]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(cacheManager); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(cacheManagerMock); }); test("Signets raised an exception while trying to recover courses", () async { - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - CourseRepository.coursesCacheKey, jsonEncode([])); - SignetsAPIClientMock.stubGetCoursesException( - signetsApi as SignetsAPIClientMock, username); + CacheManagerMock.stubGet( + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([])); + SignetsAPIClientMock.stubGetCoursesException(signetsApiMock, username); expect(manager.courses, isNull); expect(manager.getCourses(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.courses, []); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - analyticsService.logError(CourseRepository.tag, any, any, any) + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.monETSUser, + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(cacheManager); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(cacheManagerMock); + verifyNoMoreInteractions(userRepositoryMock); }); test("Student dropped out of a course, the course should disappear", () async { - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([courseWithoutGrade])); - SignetsAPIClientMock.stubGetCoursesException( - signetsApi as SignetsAPIClientMock, username); + SignetsAPIClientMock.stubGetCoursesException(signetsApiMock, username); expect(manager.courses, isNull); expect(manager.getCourses(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.courses, []); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - analyticsService.logError(CourseRepository.tag, any, any, any) + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.monETSUser, + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(cacheManager); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(cacheManagerMock); + verifyNoMoreInteractions(userRepositoryMock); }); test("Courses don't have grade so getCourseSummary is called", () async { @@ -1497,16 +1482,14 @@ void main() { title: 'Cours générique', summary: summary); - SignetsAPIClientMock.stubGetCourses( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourses(signetsApiMock, username, coursesToReturn: [courseFetched]); - SignetsAPIClientMock.stubGetCourseReviews( - signetsApi as SignetsAPIClientMock, username); + SignetsAPIClientMock.stubGetCourseReviews(signetsApiMock, username); SignetsAPIClientMock.stubGetCourseSummary( - signetsApi as SignetsAPIClientMock, username, courseFetched, + signetsApiMock, username, courseFetched, summaryToReturn: summary); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - CourseRepository.coursesCacheKey, jsonEncode([])); + CacheManagerMock.stubGet( + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([])); expect(manager.courses, isNull); final results = await manager.getCourses(); @@ -1517,13 +1500,13 @@ void main() { reason: 'The courses list should now be loaded.'); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - signetsApi.getCourseSummary( + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + signetsApiMock.getCourseSummary( username: username, password: password, course: courseFetched), - cacheManager.update( + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([courseUpdated])) ]); }); @@ -1537,15 +1520,13 @@ void main() { numberOfCredits: 3, title: 'Cours générique'); - SignetsAPIClientMock.stubGetCourses( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourses(signetsApiMock, username, coursesToReturn: [courseFetched]); - SignetsAPIClientMock.stubGetCourseReviews( - signetsApi as SignetsAPIClientMock, username); + SignetsAPIClientMock.stubGetCourseReviews(signetsApiMock, username); SignetsAPIClientMock.stubGetCourseSummaryException( - signetsApi as SignetsAPIClientMock, username, courseFetched); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - CourseRepository.coursesCacheKey, jsonEncode([])); + signetsApiMock, username, courseFetched); + CacheManagerMock.stubGet( + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([])); expect(manager.courses, isNull); final results = await manager.getCourses(); @@ -1556,28 +1537,26 @@ void main() { reason: 'The courses list should now be loaded.'); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - signetsApi.getCourseSummary( + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + signetsApiMock.getCourseSummary( username: username, password: password, course: courseFetched), - cacheManager.update( + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([courseFetched])) ]); }); test("Cache update fails, should still return the list of courses", () async { - SignetsAPIClientMock.stubGetCourses( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourses(signetsApiMock, username, coursesToReturn: [courseWithGrade]); - SignetsAPIClientMock.stubGetCourseReviews( - signetsApi as SignetsAPIClientMock, username); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + SignetsAPIClientMock.stubGetCourseReviews(signetsApiMock, username); + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([courseWithGrade])); CacheManagerMock.stubUpdateException( - cacheManager as CacheManagerMock, CourseRepository.coursesCacheKey); + cacheManagerMock, CourseRepository.coursesCacheKey); expect(manager.courses, isNull); final results = await manager.getCourses(); @@ -1589,54 +1568,54 @@ void main() { 'The courses list should now be loaded even if the caching fails.'); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - cacheManager.update( + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([courseWithGrade])) ]); }); test("UserRepository return an exception", () async { // Stub to simulate presence of session cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - CourseRepository.coursesCacheKey, jsonEncode([])); + reset(cacheManagerMock); + CacheManagerMock.stubGet( + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([])); // Stub UserRepository to throw a exception - UserRepositoryMock.stubGetPasswordException( - userRepository as UserRepositoryMock); + UserRepositoryMock.stubGetPasswordException(userRepositoryMock); expect(manager.sessions, isNull); expect(manager.getCourses(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.courses, [], reason: 'The courses list should be empty'); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - analyticsService.logError(CourseRepository.tag, any, any, any) + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.getPassword(), + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); - verifyNever(signetsApi.getCourses( + verifyNever(signetsApiMock.getCourses( username: anyNamed("username"), password: anyNamed("password"))); - verifyNever(cacheManager.update(CourseRepository.coursesCacheKey, any)); + verifyNever( + cacheManagerMock.update(CourseRepository.coursesCacheKey, any)); }); test("Should force fromCacheOnly mode when user has no connectivity", () async { // Stub the cache to return 1 activity - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([courseWithGrade])); //Stub the networkingService to return no connectivity - reset(networkingService); - NetworkingServiceMock.stubHasConnectivity(networkingService, + reset(networkingServiceMock); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock, hasConnectivity: false); final coursesCache = await manager.getCourses(); @@ -1653,14 +1632,12 @@ void main() { numberOfCredits: 3, title: 'Cours générique'); - SignetsAPIClientMock.stubGetCourses( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourses(signetsApiMock, username, coursesToReturn: [courseFetched]); - SignetsAPIClientMock.stubGetCourseReviews( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourseReviews(signetsApiMock, username, session: session); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - CourseRepository.coursesCacheKey, jsonEncode([])); + CacheManagerMock.stubGet( + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([])); expect(manager.courses, isNull); final results = await manager.getCourses(); @@ -1671,13 +1648,13 @@ void main() { reason: 'The courses list should now be loaded.'); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - signetsApi.getCourseReviews( + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + signetsApiMock.getCourseReviews( username: username, password: password, session: session), - cacheManager.update( + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([courseFetched])) ]); }); @@ -1712,14 +1689,12 @@ void main() { title: 'Cours générique', review: review); - SignetsAPIClientMock.stubGetCourses( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourses(signetsApiMock, username, coursesToReturn: [courseFetched]); - SignetsAPIClientMock.stubGetCourseReviews( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourseReviews(signetsApiMock, username, session: session, reviewsToReturn: [review]); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - CourseRepository.coursesCacheKey, jsonEncode([])); + CacheManagerMock.stubGet( + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([])); expect(manager.courses, isNull); final results = await manager.getCourses(); @@ -1730,13 +1705,13 @@ void main() { reason: 'The courses list should now be loaded.'); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - signetsApi.getCourseReviews( + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + signetsApiMock.getCourseReviews( username: username, password: password, session: session), - cacheManager.update( + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([updated])) ]); }); @@ -1751,14 +1726,13 @@ void main() { numberOfCredits: 3, title: 'Cours générique'); - SignetsAPIClientMock.stubGetCourses( - signetsApi as SignetsAPIClientMock, username, + SignetsAPIClientMock.stubGetCourses(signetsApiMock, username, coursesToReturn: [courseFetched]); SignetsAPIClientMock.stubGetCourseReviewsException( - signetsApi as SignetsAPIClientMock, username, + signetsApiMock, username, session: session); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - CourseRepository.coursesCacheKey, jsonEncode([])); + CacheManagerMock.stubGet( + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([])); expect(manager.courses, isNull); final results = await manager.getCourses(); @@ -1769,32 +1743,31 @@ void main() { reason: 'The courses list should now be loaded.'); verifyInOrder([ - cacheManager.get(CourseRepository.coursesCacheKey), - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourses(username: username, password: password), - signetsApi.getCourseReviews( + cacheManagerMock.get(CourseRepository.coursesCacheKey), + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourses(username: username, password: password), + signetsApiMock.getCourseReviews( username: username, password: password, session: session), - cacheManager.update( + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([courseFetched])) ]); }); }); group("getCourseSummary - ", () { - Course course; + late Course course; - Course courseUpdated; + late Course courseUpdated; const String username = "username"; const String password = "password"; setUp(() { // Stub a user - UserRepositoryMock.stubMonETSUser(userRepository as UserRepositoryMock, - MonETSUser(domain: null, typeUsagerId: null, username: username)); - UserRepositoryMock.stubGetPassword( - userRepository as UserRepositoryMock, "password"); + UserRepositoryMock.stubMonETSUser(userRepositoryMock, + MonETSUser(domain: '', typeUsagerId: 0, username: username)); + UserRepositoryMock.stubGetPassword(userRepositoryMock, "password"); // Reset models course = Course( @@ -1831,12 +1804,12 @@ void main() { ])); // Stub to simulate that the user has an active internet connection - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); }); test("CourseSummary is fetched and cache is updated", () async { SignetsAPIClientMock.stubGetCourseSummary( - signetsApi as SignetsAPIClientMock, username, course, + signetsApiMock, username, course, summaryToReturn: courseUpdated.summary); expect(manager.courses, isNull); @@ -1848,28 +1821,28 @@ void main() { reason: 'The courses list should now be loaded.'); verifyInOrder([ - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourseSummary( + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourseSummary( username: username, password: password, course: course), - cacheManager.update( + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([courseUpdated])) ]); }); test("Course is updated on the repository", () async { - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([course])); SignetsAPIClientMock.stubGetCourseSummary( - signetsApi as SignetsAPIClientMock, username, course, + signetsApiMock, username, course, summaryToReturn: courseUpdated.summary); // Load a course await manager.getCourses(fromCacheOnly: true); - clearInteractions(cacheManager); - clearInteractions(signetsApi); - clearInteractions(userRepository); + clearInteractions(cacheManagerMock); + clearInteractions(signetsApiMock); + clearInteractions(userRepositoryMock); expect(manager.courses, [course]); @@ -1881,11 +1854,11 @@ void main() { reason: 'The courses list should now be updated.'); verifyInOrder([ - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourseSummary( + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourseSummary( username: username, password: password, course: course), - cacheManager.update( + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([courseUpdated])) ]); }); @@ -1893,7 +1866,7 @@ void main() { test("Signets raised an exception while trying to recover summary", () async { SignetsAPIClientMock.stubGetCourseSummaryException( - signetsApi as SignetsAPIClientMock, username, course); + signetsApiMock, username, course); expect(manager.courses, isNull); @@ -1902,29 +1875,30 @@ void main() { expect(manager.courses, isNull); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourseSummary( + userRepositoryMock.monETSUser, + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourseSummary( username: username, password: password, course: course), - analyticsService.logError(CourseRepository.tag, any, any, any) + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(cacheManager); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(cacheManagerMock); + verifyNoMoreInteractions(userRepositoryMock); }); test( "Cache update fails, should still return the course with its summary", () async { SignetsAPIClientMock.stubGetCourseSummary( - signetsApi as SignetsAPIClientMock, username, course, + signetsApiMock, username, course, summaryToReturn: courseUpdated.summary); CacheManagerMock.stubUpdateException( - cacheManager as CacheManagerMock, CourseRepository.coursesCacheKey); + cacheManagerMock, CourseRepository.coursesCacheKey); expect(manager.courses, isNull); final results = await manager.getCourseSummary(course); @@ -1936,24 +1910,23 @@ void main() { 'The courses list should now be loaded even if the caching fails.'); verifyInOrder([ - userRepository.getPassword(), - userRepository.monETSUser, - signetsApi.getCourseSummary( + userRepositoryMock.getPassword(), + userRepositoryMock.monETSUser, + signetsApiMock.getCourseSummary( username: username, password: password, course: course), - cacheManager.update( + cacheManagerMock.update( CourseRepository.coursesCacheKey, jsonEncode([courseUpdated])) ]); }); test("UserRepository return an exception", () async { // Stub to simulate presence of session cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - CourseRepository.coursesCacheKey, jsonEncode([])); + reset(cacheManagerMock); + CacheManagerMock.stubGet( + cacheManagerMock, CourseRepository.coursesCacheKey, jsonEncode([])); // Stub UserRepository to throw a exception - UserRepositoryMock.stubGetPasswordException( - userRepository as UserRepositoryMock); + UserRepositoryMock.stubGetPasswordException(userRepositoryMock); expect(manager.sessions, isNull); expect(manager.getCourseSummary(course), @@ -1961,26 +1934,26 @@ void main() { expect(manager.courses, isNull); await untilCalled( - analyticsService.logError(CourseRepository.tag, any, any, any)); + analyticsServiceMock.logError(CourseRepository.tag, any, any, any)); verifyInOrder([ - userRepository.getPassword(), - analyticsService.logError(CourseRepository.tag, any, any, any) + userRepositoryMock.getPassword(), + analyticsServiceMock.logError(CourseRepository.tag, any, any, any) ]); - verifyNoMoreInteractions(signetsApi); - verifyNoMoreInteractions(cacheManager); + verifyNoMoreInteractions(signetsApiMock); + verifyNoMoreInteractions(cacheManagerMock); }); test("Should not try to update course summary when offline", () async { //Stub the networkingService to return no connectivity - reset(networkingService); - NetworkingServiceMock.stubHasConnectivity(networkingService, + reset(networkingServiceMock); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock, hasConnectivity: false); final results = await manager.getCourseSummary(course); expect(results, course); - verifyNever(signetsApi.getCourseSummary( + verifyNever(signetsApiMock.getCourseSummary( username: username, password: password, course: course)); }); }); diff --git a/test/managers/quick_link_repository_test.dart b/test/managers/quick_link_repository_test.dart index f3de245f0..b1bc3acdf 100644 --- a/test/managers/quick_link_repository_test.dart +++ b/test/managers/quick_link_repository_test.dart @@ -17,19 +17,20 @@ import '../helpers.dart'; import '../mock/managers/cache_manager_mock.dart'; void main() { - CacheManager cacheManager; - QuickLinkRepository quickLinkRepository; + late CacheManagerMock cacheManagerMock; + + late QuickLinkRepository quickLinkRepository; group("QuickLinkRepository - ", () { setUp(() { // Setup needed services and managers - cacheManager = setupCacheManagerMock(); + cacheManagerMock = setupCacheManagerMock(); quickLinkRepository = QuickLinkRepository(); }); tearDown(() { - clearInteractions(cacheManager); + clearInteractions(cacheManagerMock); unregister(); }); @@ -38,7 +39,7 @@ void main() { // Stub the cache to return some QuickLinkData final quickLinkData = QuickLinkData(id: 1, index: 0); CacheManagerMock.stubGet( - cacheManager as CacheManagerMock, + cacheManagerMock, QuickLinkRepository.quickLinksCacheKey, jsonEncode([quickLinkData])); @@ -49,7 +50,7 @@ void main() { expect(results[0].id, quickLinkData.id); expect(results[0].index, quickLinkData.index); - verify(cacheManager.get(QuickLinkRepository.quickLinksCacheKey)) + verify(cacheManagerMock.get(QuickLinkRepository.quickLinksCacheKey)) .called(1); }); @@ -57,8 +58,8 @@ void main() { "Trying to recover QuickLinkData from cache but an exception is raised.", () async { // Stub the cache to throw an exception - CacheManagerMock.stubGetException(cacheManager as CacheManagerMock, - QuickLinkRepository.quickLinksCacheKey); + CacheManagerMock.stubGetException( + cacheManagerMock, QuickLinkRepository.quickLinksCacheKey); expect(quickLinkRepository.getQuickLinkDataFromCache(), throwsA(isInstanceOf())); @@ -74,7 +75,7 @@ void main() { await quickLinkRepository.updateQuickLinkDataToCache([quickLink]); - verify(cacheManager.update(QuickLinkRepository.quickLinksCacheKey, + verify(cacheManagerMock.update(QuickLinkRepository.quickLinksCacheKey, jsonEncode([quickLinkData]))).called(1); }); @@ -82,8 +83,8 @@ void main() { "Trying to update QuickLinkData to cache but an exception is raised.", () async { // Stub the cache to throw an exception - CacheManagerMock.stubUpdateException(cacheManager as CacheManagerMock, - QuickLinkRepository.quickLinksCacheKey); + CacheManagerMock.stubUpdateException( + cacheManagerMock, QuickLinkRepository.quickLinksCacheKey); final quickLink = QuickLink(id: 1, image: const Text(""), name: 'name', link: 'url'); diff --git a/test/managers/settings_manager_test.dart b/test/managers/settings_manager_test.dart index e3ff48dca..f0a806108 100644 --- a/test/managers/settings_manager_test.dart +++ b/test/managers/settings_manager_test.dart @@ -12,26 +12,26 @@ import 'package:table_calendar/table_calendar.dart'; // Project imports: import 'package:notredame/core/constants/preferences_flags.dart'; import 'package:notredame/core/managers/settings_manager.dart'; -import 'package:notredame/core/services/analytics_service.dart'; import 'package:notredame/core/services/preferences_service.dart'; -import 'package:notredame/core/services/remote_config_service.dart'; import '../helpers.dart'; +import '../mock/services/analytics_service_mock.dart'; import '../mock/services/preferences_service_mock.dart'; import '../mock/services/remote_config_service_mock.dart'; void main() { - AnalyticsService analyticsService; - RemoteConfigService remoteConfigService; - PreferencesService preferencesService; - SettingsManager manager; + late AnalyticsServiceMock analyticsServiceMock; + late RemoteConfigServiceMock remoteConfigServiceMock; + late PreferencesServiceMock preferencesServiceMock; + + late SettingsManager manager; group("SettingsManager - ", () { setUp(() async { // Setting up mocks setupLogger(); - analyticsService = setupAnalyticsServiceMock(); - preferencesService = setupPreferencesServiceMock(); - remoteConfigService = setupRemoteConfigServiceMock(); + analyticsServiceMock = setupAnalyticsServiceMock(); + preferencesServiceMock = setupPreferencesServiceMock(); + remoteConfigServiceMock = setupRemoteConfigServiceMock(); await setupAppIntl(); @@ -46,31 +46,25 @@ void main() { test("validate default behaviour", () async { // Stubs the answer of the preferences services PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleStartWeekday, + preferencesServiceMock, PreferencesFlag.scheduleStartWeekday, toReturn: null); PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleOtherWeekday, + preferencesServiceMock, PreferencesFlag.scheduleOtherWeekday, toReturn: null); PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleCalendarFormat, + preferencesServiceMock, PreferencesFlag.scheduleCalendarFormat, toReturn: null); PreferencesServiceMock.stubGetBool( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleShowTodayBtn, + preferencesServiceMock, PreferencesFlag.scheduleShowTodayBtn, toReturn: null); PreferencesServiceMock.stubGetBool( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleListView, + preferencesServiceMock, PreferencesFlag.scheduleListView, toReturn: null); PreferencesServiceMock.stubGetBool( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleShowWeekEvents, + preferencesServiceMock, PreferencesFlag.scheduleShowWeekEvents, toReturn: null); RemoteConfigServiceMock.stubGetCalendarViewEnabled( - remoteConfigService as RemoteConfigServiceMock); + remoteConfigServiceMock); final expected = { PreferencesFlag.scheduleOtherWeekday: WeekDays.monday, @@ -85,52 +79,47 @@ void main() { expect(result, expected); - verify(preferencesService + verify(preferencesServiceMock .getString(PreferencesFlag.scheduleStartWeekday)) .called(1); - verify(preferencesService + verify(preferencesServiceMock .getString(PreferencesFlag.scheduleOtherWeekday)) .called(1); - verify(preferencesService + verify(preferencesServiceMock .getString(PreferencesFlag.scheduleCalendarFormat)) .called(1); - verify(preferencesService.getBool(PreferencesFlag.scheduleShowTodayBtn)) + verify(preferencesServiceMock + .getBool(PreferencesFlag.scheduleShowTodayBtn)) .called(1); - verify(preferencesService.getBool(PreferencesFlag.scheduleListView)) + verify(preferencesServiceMock.getBool(PreferencesFlag.scheduleListView)) .called(1); - verify(preferencesService + verify(preferencesServiceMock .getBool(PreferencesFlag.scheduleShowWeekEvents)) .called(1); - verifyNoMoreInteractions(preferencesService); - verifyNoMoreInteractions(analyticsService); + verifyNoMoreInteractions(preferencesServiceMock); + verifyNoMoreInteractions(analyticsServiceMock); }); test("validate the loading of the settings", () async { // Stubs the answer of the preferences services PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleStartWeekday, + preferencesServiceMock, PreferencesFlag.scheduleStartWeekday, toReturn: EnumToString.convertToString(StartingDayOfWeek.sunday)); PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleOtherWeekday, + preferencesServiceMock, PreferencesFlag.scheduleOtherWeekday, toReturn: EnumToString.convertToString(WeekDays.monday)); PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleCalendarFormat, + preferencesServiceMock, PreferencesFlag.scheduleCalendarFormat, toReturn: EnumToString.convertToString(CalendarFormat.month)); PreferencesServiceMock.stubGetBool( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleShowTodayBtn, + preferencesServiceMock, PreferencesFlag.scheduleShowTodayBtn, toReturn: false); PreferencesServiceMock.stubGetBool( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleListView, + preferencesServiceMock, PreferencesFlag.scheduleListView, toReturn: false); PreferencesServiceMock.stubGetBool( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleShowWeekEvents, + preferencesServiceMock, PreferencesFlag.scheduleShowWeekEvents, toReturn: false); final expected = { @@ -146,25 +135,26 @@ void main() { expect(result, expected); - verify(preferencesService + verify(preferencesServiceMock .getString(PreferencesFlag.scheduleOtherWeekday)) .called(1); - verify(preferencesService + verify(preferencesServiceMock .getString(PreferencesFlag.scheduleStartWeekday)) .called(1); - verify(preferencesService + verify(preferencesServiceMock .getString(PreferencesFlag.scheduleCalendarFormat)) .called(1); - verify(preferencesService.getBool(PreferencesFlag.scheduleShowTodayBtn)) + verify(preferencesServiceMock + .getBool(PreferencesFlag.scheduleShowTodayBtn)) .called(1); - verify(preferencesService.getBool(PreferencesFlag.scheduleListView)) + verify(preferencesServiceMock.getBool(PreferencesFlag.scheduleListView)) .called(1); - verify(preferencesService + verify(preferencesServiceMock .getBool(PreferencesFlag.scheduleShowWeekEvents)) .called(1); - verifyNoMoreInteractions(preferencesService); - verifyNoMoreInteractions(analyticsService); + verifyNoMoreInteractions(preferencesServiceMock); + verifyNoMoreInteractions(analyticsServiceMock); }); }); @@ -173,55 +163,54 @@ void main() { const flag = PreferencesFlag.theme; manager.setThemeMode(ThemeMode.light); - verify(preferencesService.setString( + verify(preferencesServiceMock.setString( PreferencesFlag.theme, ThemeMode.light.toString())) .called(1); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); manager.setThemeMode(ThemeMode.dark); - verify(preferencesService.setString( + verify(preferencesServiceMock.setString( PreferencesFlag.theme, ThemeMode.dark.toString())) .called(1); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); manager.setThemeMode(ThemeMode.system); - verify(preferencesService.setString( + verify(preferencesServiceMock.setString( PreferencesFlag.theme, ThemeMode.system.toString())) .called(1); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); - verifyNoMoreInteractions(preferencesService); - verifyNoMoreInteractions(analyticsService); + verifyNoMoreInteractions(preferencesServiceMock); + verifyNoMoreInteractions(analyticsServiceMock); }); test("validate default behaviour", () async { const flag = PreferencesFlag.theme; - PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, flag, + PreferencesServiceMock.stubGetString(preferencesServiceMock, flag, toReturn: ThemeMode.light.toString()); manager.themeMode; - await untilCalled(preferencesService.getString(flag)); + await untilCalled(preferencesServiceMock.getString(flag)); expect(manager.themeMode, ThemeMode.light); - verify(preferencesService.getString(flag)).called(2); + verify(preferencesServiceMock.getString(flag)).called(2); - verifyNoMoreInteractions(preferencesService); + verifyNoMoreInteractions(preferencesServiceMock); }); }); @@ -229,98 +218,100 @@ void main() { test("validate default behaviour", () async { const flag = PreferencesFlag.locale; PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, - PreferencesFlag.locale, + preferencesServiceMock, PreferencesFlag.locale, toReturn: const Locale('fr').toString()); manager.setLocale('fr'); manager.locale; - verify(preferencesService.setString(PreferencesFlag.locale, 'fr')) + verify(preferencesServiceMock.setString(PreferencesFlag.locale, 'fr')) + .called(1); + verify(preferencesServiceMock.getString(PreferencesFlag.locale)) .called(1); - verify(preferencesService.getString(PreferencesFlag.locale)).called(1); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); - verifyNoMoreInteractions(preferencesService); - verifyNoMoreInteractions(analyticsService); + verifyNoMoreInteractions(preferencesServiceMock); + verifyNoMoreInteractions(analyticsServiceMock); }); test("set french/english", () async { const flag = PreferencesFlag.locale; manager.setLocale('fr'); - verify(preferencesService.setString(PreferencesFlag.locale, 'fr')) + verify(preferencesServiceMock.setString(PreferencesFlag.locale, 'fr')) .called(1); - untilCalled(analyticsService.logEvent( + untilCalled(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); manager.setLocale('en'); - verify(preferencesService.setString(PreferencesFlag.locale, 'en')) + verify(preferencesServiceMock.setString(PreferencesFlag.locale, 'en')) .called(1); - untilCalled(analyticsService.logEvent( + untilCalled(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); - verifyNoMoreInteractions(preferencesService); - verifyNoMoreInteractions(analyticsService); + verifyNoMoreInteractions(preferencesServiceMock); + verifyNoMoreInteractions(analyticsServiceMock); }); test("default locale isn't set", () { const flag = PreferencesFlag.locale; - when((preferencesService as PreferencesServiceMock).getString(flag)) + when(preferencesServiceMock.getString(flag)) .thenAnswer((_) async => null); expect(manager.locale, const Locale('en')); - verify(preferencesService.getString(PreferencesFlag.locale)).called(1); + verify(preferencesServiceMock.getString(PreferencesFlag.locale)) + .called(1); - verifyNoMoreInteractions(preferencesService); - verifyNoMoreInteractions(analyticsService); + verifyNoMoreInteractions(preferencesServiceMock); + verifyNoMoreInteractions(analyticsServiceMock); }); }); test("fetch theme and locale", () async { PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, PreferencesFlag.locale, + preferencesServiceMock, PreferencesFlag.locale, toReturn: const Locale('fr').toString()); PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, PreferencesFlag.theme, + preferencesServiceMock, PreferencesFlag.theme, toReturn: 'ThemeMode.system'); await manager.fetchLanguageAndThemeMode(); - verify(preferencesService.getString(PreferencesFlag.theme)).called(1); - verify(preferencesService.getString(PreferencesFlag.locale)).called(1); + verify(preferencesServiceMock.getString(PreferencesFlag.theme)).called(1); + verify(preferencesServiceMock.getString(PreferencesFlag.locale)) + .called(1); - verifyNoMoreInteractions(preferencesService); - verifyNoMoreInteractions(analyticsService); + verifyNoMoreInteractions(preferencesServiceMock); + verifyNoMoreInteractions(analyticsServiceMock); }); test("reset language and theme", () async { // Set local and theme PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, PreferencesFlag.locale, + preferencesServiceMock, PreferencesFlag.locale, toReturn: const Locale('fr').toString()); PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, PreferencesFlag.theme, + preferencesServiceMock, PreferencesFlag.theme, toReturn: 'ThemeMode.system'); await manager.fetchLanguageAndThemeMode(); @@ -340,97 +331,89 @@ void main() { test("setString", () async { const flag = PreferencesFlag.scheduleCalendarFormat; - PreferencesServiceMock.stubSetString( - preferencesService as PreferencesServiceMock, flag); + PreferencesServiceMock.stubSetString(preferencesServiceMock, flag); expect(await manager.setString(flag, "test"), true, reason: "setString should return true if the PreferenceService return true"); - untilCalled(analyticsService.logEvent( + untilCalled(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); - verify(preferencesService.setString(flag, any)); + verify(preferencesServiceMock.setString(flag, any)); }); test("setInt", () async { const flag = PreferencesFlag.aboutUsCard; - PreferencesServiceMock.stubSetInt( - preferencesService as PreferencesServiceMock, flag); + PreferencesServiceMock.stubSetInt(preferencesServiceMock, flag); expect(await manager.setInt(flag, 0), true, reason: "setInt should return true if the PreferenceService return true"); - untilCalled(analyticsService.logEvent( + untilCalled(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); - verify(preferencesService.setInt(flag, any)); + verify(preferencesServiceMock.setInt(flag, any)); }); test("getString", () async { const flag = PreferencesFlag.scheduleCalendarFormat; - PreferencesServiceMock.stubGetString( - preferencesService as PreferencesServiceMock, flag); + PreferencesServiceMock.stubGetString(preferencesServiceMock, flag); expect(await manager.getString(flag), 'test', reason: "setString should return true if the PreferenceService return true"); - untilCalled(analyticsService.logEvent( + untilCalled(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); - verify(preferencesService.getString(flag)); + verify(preferencesServiceMock.getString(flag)); }); test("setBool", () async { const flag = PreferencesFlag.scheduleCalendarFormat; - PreferencesServiceMock.stubSetBool( - preferencesService as PreferencesServiceMock, flag); + PreferencesServiceMock.stubSetBool(preferencesServiceMock, flag); expect(await manager.setBool(flag, true), true, reason: "setString should return true if the PreferenceService return true"); - untilCalled(analyticsService.logEvent( + untilCalled(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "${SettingsManager.tag}_${EnumToString.convertToString(flag)}", any)) .called(1); - verify(preferencesService.setBool(flag, value: anyNamed("value"))); + verify(preferencesServiceMock.setBool(flag, value: anyNamed("value"))); }); group("Dashboard - ", () { test("validate default behaviour", () async { PreferencesServiceMock.stubGetInt( - preferencesService as PreferencesServiceMock, - PreferencesFlag.aboutUsCard, + preferencesServiceMock, PreferencesFlag.aboutUsCard, toReturn: null); PreferencesServiceMock.stubGetInt( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleCard, + preferencesServiceMock, PreferencesFlag.scheduleCard, toReturn: null); PreferencesServiceMock.stubGetInt( - preferencesService as PreferencesServiceMock, - PreferencesFlag.progressBarCard, + preferencesServiceMock, PreferencesFlag.progressBarCard, toReturn: null); PreferencesServiceMock.stubGetInt( - preferencesService as PreferencesServiceMock, - PreferencesFlag.gradesCard, + preferencesServiceMock, PreferencesFlag.gradesCard, toReturn: null); // Cards @@ -447,41 +430,37 @@ void main() { expected, ); - verify(preferencesService.getInt(PreferencesFlag.broadcastCard)) + verify(preferencesServiceMock.getInt(PreferencesFlag.broadcastCard)) .called(1); - verify(preferencesService.getInt(PreferencesFlag.aboutUsCard)) + verify(preferencesServiceMock.getInt(PreferencesFlag.aboutUsCard)) .called(1); - verify(preferencesService.getInt(PreferencesFlag.scheduleCard)) + verify(preferencesServiceMock.getInt(PreferencesFlag.scheduleCard)) .called(1); - verify(preferencesService.getInt(PreferencesFlag.progressBarCard)) + verify(preferencesServiceMock.getInt(PreferencesFlag.progressBarCard)) + .called(1); + verify(preferencesServiceMock.getInt(PreferencesFlag.gradesCard)) .called(1); - verify(preferencesService.getInt(PreferencesFlag.gradesCard)).called(1); - verifyNoMoreInteractions(preferencesService); - verifyNoMoreInteractions(analyticsService); + verifyNoMoreInteractions(preferencesServiceMock); + verifyNoMoreInteractions(analyticsServiceMock); }); test("validate the loading of the cards", () async { PreferencesServiceMock.stubGetInt( - preferencesService as PreferencesServiceMock, - PreferencesFlag.broadcastCard, + preferencesServiceMock, PreferencesFlag.broadcastCard, toReturn: 0); PreferencesServiceMock.stubGetInt( - preferencesService as PreferencesServiceMock, - PreferencesFlag.aboutUsCard, + preferencesServiceMock, PreferencesFlag.aboutUsCard, toReturn: 2); PreferencesServiceMock.stubGetInt( - preferencesService as PreferencesServiceMock, - PreferencesFlag.scheduleCard, + preferencesServiceMock, PreferencesFlag.scheduleCard, toReturn: 3); PreferencesServiceMock.stubGetInt( - preferencesService as PreferencesServiceMock, - PreferencesFlag.progressBarCard, + preferencesServiceMock, PreferencesFlag.progressBarCard, // ignore: avoid_redundant_argument_values toReturn: 1); PreferencesServiceMock.stubGetInt( - preferencesService as PreferencesServiceMock, - PreferencesFlag.gradesCard, + preferencesServiceMock, PreferencesFlag.gradesCard, toReturn: 4); // Cards @@ -498,18 +477,19 @@ void main() { expected, ); - verify(preferencesService.getInt(PreferencesFlag.broadcastCard)) + verify(preferencesServiceMock.getInt(PreferencesFlag.broadcastCard)) + .called(1); + verify(preferencesServiceMock.getInt(PreferencesFlag.aboutUsCard)) .called(1); - verify(preferencesService.getInt(PreferencesFlag.aboutUsCard)) + verify(preferencesServiceMock.getInt(PreferencesFlag.scheduleCard)) .called(1); - verify(preferencesService.getInt(PreferencesFlag.scheduleCard)) + verify(preferencesServiceMock.getInt(PreferencesFlag.progressBarCard)) .called(1); - verify(preferencesService.getInt(PreferencesFlag.progressBarCard)) + verify(preferencesServiceMock.getInt(PreferencesFlag.gradesCard)) .called(1); - verify(preferencesService.getInt(PreferencesFlag.gradesCard)).called(1); - verifyNoMoreInteractions(preferencesService); - verifyNoMoreInteractions(analyticsService); + verifyNoMoreInteractions(preferencesServiceMock); + verifyNoMoreInteractions(analyticsServiceMock); }); }); }); diff --git a/test/managers/user_repository_test.dart b/test/managers/user_repository_test.dart index b475990d4..b38974299 100644 --- a/test/managers/user_repository_test.dart +++ b/test/managers/user_repository_test.dart @@ -20,42 +20,42 @@ import 'package:notredame/core/services/analytics_service.dart'; import 'package:notredame/core/services/networking_service.dart'; import '../helpers.dart'; import '../mock/managers/cache_manager_mock.dart'; +import '../mock/services/analytics_service_mock.dart'; import '../mock/services/flutter_secure_storage_mock.dart'; import '../mock/services/networking_service_mock.dart'; void main() { - AnalyticsService analyticsService; - MonETSAPIClient monETSApi; - FlutterSecureStorageMock secureStorage; - CacheManager cacheManager; - SignetsAPIClient signetsApi; - NetworkingServiceMock networkingService; + late AnalyticsServiceMock analyticsServiceMock; + late MonETSAPIClientMock monETSApiMock; + late FlutterSecureStorageMock secureStorageMock; + late CacheManagerMock cacheManagerMock; + late SignetsAPIClientMock signetsApiMock; + late NetworkingServiceMock networkingServiceMock; - UserRepository manager; + late UserRepository manager; group('UserRepository - ', () { setUp(() { // Setup needed service - analyticsService = setupAnalyticsServiceMock(); - monETSApi = setupMonETSApiMock(); - secureStorage = - setupFlutterSecureStorageMock() as FlutterSecureStorageMock; - cacheManager = setupCacheManagerMock(); - signetsApi = setupSignetsApiMock(); - networkingService = setupNetworkingServiceMock() as NetworkingServiceMock; + analyticsServiceMock = setupAnalyticsServiceMock(); + monETSApiMock = setupMonETSApiMock(); + secureStorageMock = setupFlutterSecureStorageMock(); + cacheManagerMock = setupCacheManagerMock(); + signetsApiMock = setupSignetsApiMock(); + networkingServiceMock = setupNetworkingServiceMock(); setupLogger(); manager = UserRepository(); - SignetsAPIClientMock.stubAuthenticate(signetsApi as SignetsAPIClientMock); + SignetsAPIClientMock.stubAuthenticate(signetsApiMock); }); tearDown(() { unregister(); unregister(); unregister(); - clearInteractions(cacheManager); + clearInteractions(cacheManagerMock); unregister(); - clearInteractions(signetsApi); + clearInteractions(signetsApiMock); unregister(); unregister(); }); @@ -65,8 +65,7 @@ void main() { final MonETSUser user = MonETSUser( domain: "ENS", typeUsagerId: 1, username: "right credentials"); - MonETSAPIClientMock.stubAuthenticate( - monETSApi as MonETSAPIClientMock, user); + MonETSAPIClientMock.stubAuthenticate(monETSApiMock, user); // Result is true expect( @@ -75,13 +74,13 @@ void main() { reason: "Check the authentication is successful"); // Verify the secureStorage is used - verify(secureStorage.write( + verify(secureStorageMock.write( key: UserRepository.usernameSecureKey, value: user.username)); - verify(secureStorage.write( + verify(secureStorageMock.write( key: UserRepository.passwordSecureKey, value: "")); // Verify the user id is set in the analytics - verify(analyticsService.setUserProperties( + verify(analyticsServiceMock.setUserProperties( userId: user.username, domain: user.domain)); expect(manager.monETSUser, user, @@ -90,25 +89,24 @@ void main() { test('An exception is throw during the MonETSApi call', () async { const String username = "exceptionUser"; - MonETSAPIClientMock.stubException( - monETSApi as MonETSAPIClientMock, username); + MonETSAPIClientMock.stubException(monETSApiMock, username); expect(await manager.authenticate(username: username, password: ""), isFalse, reason: "The authentication failed so the result should be false"); // Verify the user id isn't set in the analytics - verify(analyticsService.logError(UserRepository.tag, any, any, any)) + verify(analyticsServiceMock.logError(UserRepository.tag, any, any, any)) .called(1); // Verify the secureStorage isn't used - verifyNever(secureStorage.write( + verifyNever(secureStorageMock.write( key: UserRepository.usernameSecureKey, value: username)); - verifyNever(secureStorage.write( + verifyNever(secureStorageMock.write( key: UserRepository.passwordSecureKey, value: "")); // Verify the user id is set in the analytics - verifyNever(analyticsService.setUserProperties( + verifyNever(analyticsServiceMock.setUserProperties( userId: username, domain: anyNamed("domain"))); expect(manager.monETSUser, null, @@ -120,9 +118,8 @@ void main() { final MonETSUser user = MonETSUser( domain: "ENS", typeUsagerId: 1, username: "right credentials"); - MonETSAPIClientMock.stubAuthenticate( - monETSApi as MonETSAPIClientMock, user); - FlutterSecureStorageMock.stubWriteException(secureStorage, + MonETSAPIClientMock.stubAuthenticate(monETSApiMock, user); + FlutterSecureStorageMock.stubWriteException(secureStorageMock, key: UserRepository.usernameSecureKey, exceptionToThrow: PlatformException(code: "bad key")); @@ -133,16 +130,16 @@ void main() { reason: "Check the authentication is successful"); // Verify the secureStorage is used - verify(secureStorage.write( + verify(secureStorageMock.write( key: UserRepository.usernameSecureKey, value: user.username)); // Verify the user id is set in the analytics - verify(analyticsService.setUserProperties( + verify(analyticsServiceMock.setUserProperties( userId: user.username, domain: anyNamed("domain"))); expect(manager.monETSUser, user); // Verify the secureStorage is deleted - verify(secureStorage.deleteAll()); + verify(secureStorageMock.deleteAll()); }); }); @@ -151,16 +148,13 @@ void main() { final MonETSUser user = MonETSUser(domain: "ENS", typeUsagerId: 1, username: "AAXXXXXX"); - MonETSAPIClientMock.stubException( - monETSApi as MonETSAPIClientMock, user.username, + MonETSAPIClientMock.stubException(monETSApiMock, user.username, exception: HttpException( prefix: "MonETSAPI", code: 415, message: "{ \"Message\": \"The request contains an entity body but no Content-Type header. The inferred media type 'application/octet-stream' is not supported for this resource.\"}")); - SignetsAPIClientMock.stubAuthenticate( - signetsApi as SignetsAPIClientMock, - connected: true); + SignetsAPIClientMock.stubAuthenticate(signetsApiMock, connected: true); // Result is true expect( @@ -169,45 +163,43 @@ void main() { reason: "Check the authentication is successful"); // Verify the secureStorage is used - verify(secureStorage.write( + verify(secureStorageMock.write( key: UserRepository.usernameSecureKey, value: user.username)); - verify(secureStorage.write( + verify(secureStorageMock.write( key: UserRepository.passwordSecureKey, value: "")); // Verify the user id is set in the analytics - verify(analyticsService.setUserProperties( + verify(analyticsServiceMock.setUserProperties( userId: user.username, domain: user.domain)); }); test('MonETSAPI failed and SignetsAPI return false', () async { const String username = "exceptionUser"; - MonETSAPIClientMock.stubException( - monETSApi as MonETSAPIClientMock, username, + MonETSAPIClientMock.stubException(monETSApiMock, username, exception: HttpException( prefix: "MonETSAPI", code: 415, message: "{ \"Message\": \"The request contains an entity body but no Content-Type header. The inferred media type 'application/octet-stream' is not supported for this resource.\"}")); - SignetsAPIClientMock.stubAuthenticate( - signetsApi as SignetsAPIClientMock); + SignetsAPIClientMock.stubAuthenticate(signetsApiMock); expect(await manager.authenticate(username: username, password: ""), isFalse, reason: "The authentication failed so the result should be false"); // Verify the user id isn't set in the analytics - verify(analyticsService.logError(UserRepository.tag, any, any, any)) + verify(analyticsServiceMock.logError(UserRepository.tag, any, any, any)) .called(1); // Verify the secureStorage isn't used - verifyNever(secureStorage.write( + verifyNever(secureStorageMock.write( key: UserRepository.usernameSecureKey, value: username)); - verifyNever(secureStorage.write( + verifyNever(secureStorageMock.write( key: UserRepository.passwordSecureKey, value: "")); // Verify the user id is set in the analytics - verifyNever(analyticsService.setUserProperties( + verifyNever(analyticsServiceMock.setUserProperties( userId: username, domain: anyNamed("domain"))); expect(manager.monETSUser, null, @@ -224,22 +216,21 @@ void main() { final MonETSUser user = MonETSUser(domain: "ENS", typeUsagerId: 1, username: username); - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.usernameSecureKey, valueToReturn: username); - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.passwordSecureKey, valueToReturn: password); - MonETSAPIClientMock.stubAuthenticate( - monETSApi as MonETSAPIClientMock, user); + MonETSAPIClientMock.stubAuthenticate(monETSApiMock, user); expect(await manager.silentAuthenticate(), isTrue, reason: "Result should be true"); verifyInOrder([ - secureStorage.read(key: UserRepository.usernameSecureKey), - secureStorage.read(key: UserRepository.passwordSecureKey), - monETSApi.authenticate(username: username, password: password), - analyticsService.setUserProperties( + secureStorageMock.read(key: UserRepository.usernameSecureKey), + secureStorageMock.read(key: UserRepository.passwordSecureKey), + monETSApiMock.authenticate(username: username, password: password), + analyticsServiceMock.setUserProperties( userId: username, domain: user.domain) ]); @@ -251,22 +242,21 @@ void main() { const String username = "username"; const String password = "password"; - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.usernameSecureKey, valueToReturn: username); - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.passwordSecureKey, valueToReturn: password); - MonETSAPIClientMock.stubAuthenticateException( - monETSApi as MonETSAPIClientMock, username); + MonETSAPIClientMock.stubAuthenticateException(monETSApiMock, username); expect(await manager.silentAuthenticate(), isFalse, reason: "Result should be false"); verifyInOrder([ - secureStorage.read(key: UserRepository.usernameSecureKey), - secureStorage.read(key: UserRepository.passwordSecureKey), - monETSApi.authenticate(username: username, password: password), - analyticsService.logError(UserRepository.tag, any, any, any) + secureStorageMock.read(key: UserRepository.usernameSecureKey), + secureStorageMock.read(key: UserRepository.passwordSecureKey), + monETSApiMock.authenticate(username: username, password: password), + analyticsServiceMock.logError(UserRepository.tag, any, any, any) ]); expect(manager.monETSUser, null, @@ -275,20 +265,20 @@ void main() { test('credentials are not saved so the authentication should not be done', () async { - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.usernameSecureKey, valueToReturn: null); - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.passwordSecureKey, valueToReturn: null); expect(await manager.silentAuthenticate(), isFalse, reason: "Result should be false"); verifyInOrder( - [secureStorage.read(key: UserRepository.usernameSecureKey)]); + [secureStorageMock.read(key: UserRepository.usernameSecureKey)]); - verifyNoMoreInteractions(secureStorage); - verifyZeroInteractions(monETSApi); - verifyZeroInteractions(analyticsService); + verifyNoMoreInteractions(secureStorageMock); + verifyZeroInteractions(monETSApiMock); + verifyZeroInteractions(analyticsServiceMock); expect(manager.monETSUser, null, reason: @@ -300,9 +290,8 @@ void main() { final MonETSUser user = MonETSUser( domain: "ENS", typeUsagerId: 1, username: "right credentials"); - MonETSAPIClientMock.stubAuthenticate( - monETSApi as MonETSAPIClientMock, user); - FlutterSecureStorageMock.stubReadException(secureStorage, + MonETSAPIClientMock.stubAuthenticate(monETSApiMock, user); + FlutterSecureStorageMock.stubReadException(secureStorageMock, key: UserRepository.usernameSecureKey, exceptionToThrow: PlatformException(code: "bad key")); @@ -311,9 +300,9 @@ void main() { reason: "Result should be false"); verifyInOrder([ - secureStorage.read(key: UserRepository.usernameSecureKey), - secureStorage.deleteAll(), - analyticsService.logError(UserRepository.tag, any, any, any) + secureStorageMock.read(key: UserRepository.usernameSecureKey), + secureStorageMock.deleteAll(), + analyticsServiceMock.logError(UserRepository.tag, any, any, any) ]); }); }); @@ -325,16 +314,16 @@ void main() { expect(manager.monETSUser, null, reason: "The user shouldn't be available after a logout"); - verify(secureStorage.delete(key: UserRepository.usernameSecureKey)); - verify(secureStorage.delete(key: UserRepository.passwordSecureKey)); + verify(secureStorageMock.delete(key: UserRepository.usernameSecureKey)); + verify(secureStorageMock.delete(key: UserRepository.passwordSecureKey)); verifyNever( - analyticsService.logError(UserRepository.tag, any, any, any)); + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); }); test('Verify that localstorage is safely deleted if an exception occurs', () async { - FlutterSecureStorageMock.stubDeleteException(secureStorage, + FlutterSecureStorageMock.stubDeleteException(secureStorageMock, key: UserRepository.usernameSecureKey, exceptionToThrow: PlatformException(code: "bad key")); @@ -343,9 +332,10 @@ void main() { expect(manager.monETSUser, null, reason: "The user shouldn't be available after a logout"); - verify(secureStorage.delete(key: UserRepository.usernameSecureKey)); - verify(secureStorage.deleteAll()); - verify(analyticsService.logError(UserRepository.tag, any, any, any)); + verify(secureStorageMock.delete(key: UserRepository.usernameSecureKey)); + verify(secureStorageMock.deleteAll()); + verify( + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); }); }); @@ -362,11 +352,10 @@ void main() { final MonETSUser user = MonETSUser(domain: "ENS", typeUsagerId: 1, username: username); - MonETSAPIClientMock.stubAuthenticate( - monETSApi as MonETSAPIClientMock, user); - FlutterSecureStorageMock.stubRead(secureStorage, + MonETSAPIClientMock.stubAuthenticate(monETSApiMock, user); + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.usernameSecureKey, valueToReturn: username); - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.passwordSecureKey, valueToReturn: password); expect(await manager.silentAuthenticate(), isTrue); @@ -375,10 +364,10 @@ void main() { reason: "Result should be 'password'"); verifyInOrder([ - secureStorage.read(key: UserRepository.usernameSecureKey), - secureStorage.read(key: UserRepository.passwordSecureKey), - monETSApi.authenticate(username: username, password: password), - analyticsService.setUserProperties( + secureStorageMock.read(key: UserRepository.usernameSecureKey), + secureStorageMock.read(key: UserRepository.passwordSecureKey), + monETSApiMock.authenticate(username: username, password: password), + analyticsServiceMock.setUserProperties( userId: username, domain: user.domain) ]); }); @@ -392,22 +381,21 @@ void main() { final MonETSUser user = MonETSUser(domain: "ENS", typeUsagerId: 1, username: username); - MonETSAPIClientMock.stubAuthenticate( - monETSApi as MonETSAPIClientMock, user); - FlutterSecureStorageMock.stubRead(secureStorage, + MonETSAPIClientMock.stubAuthenticate(monETSApiMock, user); + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.usernameSecureKey, valueToReturn: username); - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.passwordSecureKey, valueToReturn: password); expect(await manager.getPassword(), password, reason: "Result should be 'password'"); verifyInOrder([ - analyticsService.logEvent(UserRepository.tag, any), - secureStorage.read(key: UserRepository.usernameSecureKey), - secureStorage.read(key: UserRepository.passwordSecureKey), - monETSApi.authenticate(username: username, password: password), - analyticsService.setUserProperties( + analyticsServiceMock.logEvent(UserRepository.tag, any), + secureStorageMock.read(key: UserRepository.usernameSecureKey), + secureStorageMock.read(key: UserRepository.passwordSecureKey), + monETSApiMock.authenticate(username: username, password: password), + analyticsServiceMock.setUserProperties( userId: username, domain: user.domain) ]); }); @@ -418,11 +406,10 @@ void main() { const String username = "username"; const String password = "password"; - MonETSAPIClientMock.stubAuthenticateException( - monETSApi as MonETSAPIClientMock, username); - FlutterSecureStorageMock.stubRead(secureStorage, + MonETSAPIClientMock.stubAuthenticateException(monETSApiMock, username); + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.usernameSecureKey, valueToReturn: username); - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.passwordSecureKey, valueToReturn: password); await manager.silentAuthenticate(); @@ -432,9 +419,9 @@ void main() { 'The authentication failed so an ApiException should be raised.'); await untilCalled( - analyticsService.logError(UserRepository.tag, any, any, any)); + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); - verify(analyticsService.logError(UserRepository.tag, any, any, any)) + verify(analyticsServiceMock.logError(UserRepository.tag, any, any, any)) .called(1); }); @@ -446,9 +433,8 @@ void main() { final MonETSUser user = MonETSUser(domain: "ENS", typeUsagerId: 1, username: username); - MonETSAPIClientMock.stubAuthenticate( - monETSApi as MonETSAPIClientMock, user); - FlutterSecureStorageMock.stubReadException(secureStorage, + MonETSAPIClientMock.stubAuthenticate(monETSApiMock, user); + FlutterSecureStorageMock.stubReadException(secureStorageMock, key: UserRepository.passwordSecureKey, exceptionToThrow: PlatformException(code: "bad key")); @@ -458,10 +444,11 @@ void main() { reason: "localStorage failed, should sent out a custom exception"); await untilCalled( - analyticsService.logError(UserRepository.tag, any, any, any)); + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); - verify(secureStorage.deleteAll()); - verify(analyticsService.logError(UserRepository.tag, any, any, any)); + verify(secureStorageMock.deleteAll()); + verify( + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); }); }); @@ -486,11 +473,10 @@ void main() { setUp(() async { // Stub to simulate presence of programs cache - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, + CacheManagerMock.stubGet(cacheManagerMock, UserRepository.programsCacheKey, jsonEncode(programs)); - MonETSAPIClientMock.stubAuthenticate( - monETSApi as MonETSAPIClientMock, user); + MonETSAPIClientMock.stubAuthenticate(monETSApiMock, user); // Result is true expect( @@ -499,11 +485,10 @@ void main() { reason: "Check the authentication is successful"); // Stub SignetsApi answer to test only the cache retrieving - SignetsAPIClientMock.stubGetPrograms( - signetsApi as SignetsAPIClientMock, username, []); + SignetsAPIClientMock.stubGetPrograms(signetsApiMock, username, []); // Stub to simulate that the user has an active internet connection - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); }); test("Programs are loaded from cache", () async { @@ -515,16 +500,18 @@ void main() { expect(manager.programs, programs, reason: 'The programs list should now be loaded.'); - verify(cacheManager.get(UserRepository.programsCacheKey)); - verifyNoMoreInteractions(cacheManager); + verify(cacheManagerMock.get(UserRepository.programsCacheKey)); + verifyNoMoreInteractions(cacheManagerMock); }); test("Trying to load programs from cache but cache doesn't exist", () async { // Stub to simulate an exception when trying to get the programs from the cache - reset(cacheManager as CacheManagerMock); + reset(cacheManagerMock); CacheManagerMock.stubGetException( - cacheManager as CacheManagerMock, UserRepository.programsCacheKey); + cacheManagerMock, UserRepository.programsCacheKey); + FlutterSecureStorageMock.stubRead(secureStorageMock, + key: UserRepository.passwordSecureKey, valueToReturn: ''); expect(manager.programs, isNull); final results = await manager.getPrograms(); @@ -533,22 +520,24 @@ void main() { expect(results, []); expect(manager.programs, []); - verify(cacheManager.get(UserRepository.programsCacheKey)); - verify(manager.getPassword()); - verifyNever(cacheManager.update( + verify(cacheManagerMock.get(UserRepository.programsCacheKey)); + verify(secureStorageMock.read(key: UserRepository.passwordSecureKey)); + verifyNever(cacheManagerMock.update( UserRepository.programsCacheKey, jsonEncode(programs))); }); test("SignetsAPI return another program", () async { // Stub to simulate presence of program cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - UserRepository.programsCacheKey, jsonEncode([])); + reset(cacheManagerMock); + CacheManagerMock.stubGet( + cacheManagerMock, UserRepository.programsCacheKey, jsonEncode([])); + FlutterSecureStorageMock.stubRead(secureStorageMock, + key: UserRepository.passwordSecureKey, valueToReturn: ''); // Stub SignetsApi answer to test only the cache retrieving - reset(signetsApi as SignetsAPIClientMock); + reset(signetsApiMock); SignetsAPIClientMock.stubGetPrograms( - signetsApi as SignetsAPIClientMock, username, programs); + signetsApiMock, username, programs); expect(manager.programs, isNull); final results = await manager.getPrograms(); @@ -558,52 +547,57 @@ void main() { expect(manager.programs, programs, reason: 'The programs list should now be loaded.'); - verify(cacheManager.get(UserRepository.programsCacheKey)); - verify(manager.getPassword()); - verify(cacheManager.update( + verify(cacheManagerMock.get(UserRepository.programsCacheKey)); + verify(secureStorageMock.read(key: UserRepository.passwordSecureKey)); + verify(cacheManagerMock.update( UserRepository.programsCacheKey, jsonEncode(programs))); }); test("SignetsAPI return an exception", () async { // Stub to simulate presence of program cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - UserRepository.programsCacheKey, jsonEncode([])); + reset(cacheManagerMock); + CacheManagerMock.stubGet( + cacheManagerMock, UserRepository.programsCacheKey, jsonEncode([])); // Stub SignetsApi answer to test only the cache retrieving - SignetsAPIClientMock.stubGetProgramsException( - signetsApi as SignetsAPIClientMock, username); + SignetsAPIClientMock.stubGetProgramsException(signetsApiMock, username); + FlutterSecureStorageMock.stubRead(secureStorageMock, + key: UserRepository.passwordSecureKey, valueToReturn: ''); expect(manager.programs, isNull); expect(manager.getPrograms(), throwsA(isInstanceOf())); - await untilCalled(networkingService.hasConnectivity()); + await untilCalled(networkingServiceMock.hasConnectivity()); expect(manager.programs, [], reason: 'The programs list should be empty'); await untilCalled( - analyticsService.logError(UserRepository.tag, any, any, any)); + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); - verify(cacheManager.get(UserRepository.programsCacheKey)); - verify(manager.getPassword()); - verify(analyticsService.logError(UserRepository.tag, any, any, any)); + verify(cacheManagerMock.get(UserRepository.programsCacheKey)); + verify(secureStorageMock.read(key: UserRepository.passwordSecureKey)); + verify( + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); - verifyNever(cacheManager.update(UserRepository.programsCacheKey, any)); + verifyNever( + cacheManagerMock.update(UserRepository.programsCacheKey, any)); }); test("Cache update fail", () async { // Stub to simulate presence of program cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - UserRepository.programsCacheKey, jsonEncode([])); + reset(cacheManagerMock); + CacheManagerMock.stubGet( + cacheManagerMock, UserRepository.programsCacheKey, jsonEncode([])); // Stub to simulate exception when updating cache CacheManagerMock.stubUpdateException( - cacheManager as CacheManagerMock, UserRepository.programsCacheKey); + cacheManagerMock, UserRepository.programsCacheKey); + FlutterSecureStorageMock.stubRead(secureStorageMock, + key: UserRepository.passwordSecureKey, valueToReturn: ''); // Stub SignetsApi answer to test only the cache retrieving SignetsAPIClientMock.stubGetPrograms( - signetsApi as SignetsAPIClientMock, username, programs); + signetsApiMock, username, programs); expect(manager.programs, isNull); final results = await manager.getPrograms(); @@ -618,8 +612,8 @@ void main() { test("Should force fromCacheOnly mode when user has no connectivity", () async { //Stub the networkingService to return no connectivity - reset(networkingService); - NetworkingServiceMock.stubHasConnectivity(networkingService, + reset(networkingServiceMock); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock, hasConnectivity: false); final programsCache = await manager.getPrograms(); @@ -633,6 +627,8 @@ void main() { firstName: 'John', lastName: 'Doe', permanentCode: 'DOEJ00000000'); + final ProfileStudent defaultInfo = ProfileStudent( + balance: '', firstName: '', lastName: '', permanentCode: ''); const String username = "username"; @@ -641,11 +637,13 @@ void main() { setUp(() async { // Stub to simulate presence of info cache - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - UserRepository.infoCacheKey, jsonEncode(info)); + CacheManagerMock.stubGet( + cacheManagerMock, UserRepository.infoCacheKey, jsonEncode(info)); + + MonETSAPIClientMock.stubAuthenticate(monETSApiMock, user); - MonETSAPIClientMock.stubAuthenticate( - monETSApi as MonETSAPIClientMock, user); + FlutterSecureStorageMock.stubRead(secureStorageMock, + key: UserRepository.passwordSecureKey, valueToReturn: ''); // Result is true expect( @@ -654,11 +652,10 @@ void main() { reason: "Check the authentication is successful"); // Stub SignetsApi answer to test only the cache retrieving - SignetsAPIClientMock.stubGetInfo( - signetsApi as SignetsAPIClientMock, username, null); + SignetsAPIClientMock.stubGetInfo(signetsApiMock, username, defaultInfo); // Stub to simulate that the user has an active internet connection - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); }); test("Info are loaded from cache", () async { @@ -669,34 +666,34 @@ void main() { expect(results, info); expect(manager.info, info, reason: 'The info should now be loaded.'); - verify(cacheManager.get(UserRepository.infoCacheKey)); - verifyNoMoreInteractions(cacheManager as CacheManagerMock); - verifyNoMoreInteractions(signetsApi as SignetsAPIClientMock); + verify(cacheManagerMock.get(UserRepository.infoCacheKey)); + verifyNoMoreInteractions(cacheManagerMock); + verifyNoMoreInteractions(signetsApiMock); }); test("Trying to load info from cache but cache doesn't exist", () async { // Stub to simulate an exception when trying to get the info from the cache - reset(cacheManager as CacheManagerMock); + reset(cacheManagerMock); CacheManagerMock.stubGetException( - cacheManager as CacheManagerMock, UserRepository.infoCacheKey); + cacheManagerMock, UserRepository.infoCacheKey); expect(manager.info, isNull); final results = await manager.getInfo(); - expect(results, isNull); - expect(manager.info, isNull); + expect(results, defaultInfo); + expect(manager.info, defaultInfo); - verify(cacheManager.get(UserRepository.infoCacheKey)); - verify(manager.getPassword()); - verifyNever( - cacheManager.update(UserRepository.infoCacheKey, jsonEncode(info))); + verify(cacheManagerMock.get(UserRepository.infoCacheKey)); + verify(secureStorageMock.read(key: UserRepository.passwordSecureKey)); + verifyNever(cacheManagerMock.update( + UserRepository.infoCacheKey, jsonEncode(info))); }); test("SignetsAPI return another info", () async { // Stub to simulate presence of info cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - UserRepository.infoCacheKey, jsonEncode(info)); + reset(cacheManagerMock); + CacheManagerMock.stubGet( + cacheManagerMock, UserRepository.infoCacheKey, jsonEncode(info)); // Stub SignetsApi answer to test only the cache retrieving final ProfileStudent anotherInfo = ProfileStudent( @@ -704,9 +701,8 @@ void main() { firstName: 'Johnny', lastName: 'Doe', permanentCode: 'DOEJ00000000'); - reset(signetsApi as SignetsAPIClientMock); - SignetsAPIClientMock.stubGetInfo( - signetsApi as SignetsAPIClientMock, username, anotherInfo); + reset(signetsApiMock); + SignetsAPIClientMock.stubGetInfo(signetsApiMock, username, anotherInfo); expect(manager.info, isNull); final results = await manager.getInfo(); @@ -716,17 +712,16 @@ void main() { expect(manager.info, anotherInfo, reason: 'The new info should now be loaded.'); - verify(cacheManager.get(UserRepository.infoCacheKey)); - verify(manager.getPassword()); - verify(cacheManager.update( + verify(cacheManagerMock.get(UserRepository.infoCacheKey)); + verify(secureStorageMock.read(key: UserRepository.passwordSecureKey)); + verify(cacheManagerMock.update( UserRepository.infoCacheKey, jsonEncode(anotherInfo))); }); test("SignetsAPI return a info that already exists", () async { // Stub SignetsApi answer to test only the cache retrieving - reset(signetsApi as SignetsAPIClientMock); - SignetsAPIClientMock.stubGetInfo( - signetsApi as SignetsAPIClientMock, username, info); + reset(signetsApiMock); + SignetsAPIClientMock.stubGetInfo(signetsApiMock, username, info); expect(manager.info, isNull); final results = await manager.getInfo(); @@ -736,49 +731,48 @@ void main() { expect(manager.info, info, reason: 'The info should not have any duplicata..'); - verify(cacheManager.get(UserRepository.infoCacheKey)); - verify(manager.getPassword()); - verifyNever( - cacheManager.update(UserRepository.infoCacheKey, jsonEncode(info))); + verify(cacheManagerMock.get(UserRepository.infoCacheKey)); + verify(secureStorageMock.read(key: UserRepository.passwordSecureKey)); + verifyNever(cacheManagerMock.update( + UserRepository.infoCacheKey, jsonEncode(info))); }); test("SignetsAPI return an exception", () async { // Stub to simulate presence of info cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - UserRepository.infoCacheKey, jsonEncode(info)); + reset(cacheManagerMock); + CacheManagerMock.stubGet( + cacheManagerMock, UserRepository.infoCacheKey, jsonEncode(info)); // Stub SignetsApi answer to test only the cache retrieving - SignetsAPIClientMock.stubGetInfoException( - signetsApi as SignetsAPIClientMock, username); + SignetsAPIClientMock.stubGetInfoException(signetsApiMock, username); expect(manager.info, isNull); expect(manager.getInfo(), throwsA(isInstanceOf())); expect(manager.info, null, reason: 'The info should be empty'); await untilCalled( - analyticsService.logError(UserRepository.tag, any, any, any)); + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); - verify(cacheManager.get(UserRepository.infoCacheKey)); - verify(manager.getPassword()); - verify(analyticsService.logError(UserRepository.tag, any, any, any)); + verify(cacheManagerMock.get(UserRepository.infoCacheKey)); + verify(secureStorageMock.read(key: UserRepository.passwordSecureKey)); + verify( + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); - verifyNever(cacheManager.update(UserRepository.infoCacheKey, any)); + verifyNever(cacheManagerMock.update(UserRepository.infoCacheKey, any)); }); test("Cache update fail", () async { // Stub to simulate presence of session cache - reset(cacheManager as CacheManagerMock); - CacheManagerMock.stubGet(cacheManager as CacheManagerMock, - UserRepository.infoCacheKey, jsonEncode(info)); + reset(cacheManagerMock); + CacheManagerMock.stubGet( + cacheManagerMock, UserRepository.infoCacheKey, jsonEncode(info)); // Stub to simulate exception when updating cache CacheManagerMock.stubUpdateException( - cacheManager as CacheManagerMock, UserRepository.infoCacheKey); + cacheManagerMock, UserRepository.infoCacheKey); // Stub SignetsApi answer to test only the cache retrieving - SignetsAPIClientMock.stubGetInfo( - signetsApi as SignetsAPIClientMock, username, info); + SignetsAPIClientMock.stubGetInfo(signetsApiMock, username, info); expect(manager.info, isNull); final results = await manager.getInfo(); @@ -792,8 +786,8 @@ void main() { test("Should force fromCacheOnly mode when user has no connectivity", () async { //Stub the networkingService to return no connectivity - reset(networkingService); - NetworkingServiceMock.stubHasConnectivity(networkingService, + reset(networkingServiceMock); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock, hasConnectivity: false); final infoCache = await manager.getInfo(); @@ -807,9 +801,9 @@ void main() { const String username = "username"; const String password = "password"; - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.usernameSecureKey, valueToReturn: username); - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.passwordSecureKey, valueToReturn: password); expect(await manager.wasPreviouslyLoggedIn(), isTrue); @@ -819,9 +813,9 @@ void main() { const String username = "username"; const String password = ""; - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.usernameSecureKey, valueToReturn: username); - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.passwordSecureKey, valueToReturn: password); expect(await manager.wasPreviouslyLoggedIn(), isFalse); @@ -831,15 +825,16 @@ void main() { () async { const String username = "username"; - FlutterSecureStorageMock.stubRead(secureStorage, + FlutterSecureStorageMock.stubRead(secureStorageMock, key: UserRepository.usernameSecureKey, valueToReturn: username); - FlutterSecureStorageMock.stubReadException(secureStorage, + FlutterSecureStorageMock.stubReadException(secureStorageMock, key: UserRepository.passwordSecureKey, exceptionToThrow: PlatformException(code: "bad key")); expect(await manager.wasPreviouslyLoggedIn(), isFalse); - verify(secureStorage.deleteAll()); - verify(analyticsService.logError(UserRepository.tag, any, any, any)); + verify(secureStorageMock.deleteAll()); + verify( + analyticsServiceMock.logError(UserRepository.tag, any, any, any)); }); }); }); diff --git a/test/mock/managers/cache_manager_mock.dart b/test/mock/managers/cache_manager_mock.dart index 19bc43c80..33e4923f5 100644 --- a/test/mock/managers/cache_manager_mock.dart +++ b/test/mock/managers/cache_manager_mock.dart @@ -1,12 +1,16 @@ // Package imports: +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/managers/cache_manager.dart'; import 'package:notredame/core/utils/cache_exception.dart'; +import 'cache_manager_mock.mocks.dart'; + /// Mock for the [CacheManager] -class CacheManagerMock extends Mock implements CacheManager { +@GenerateNiceMocks([MockSpec()]) +class CacheManagerMock extends MockCacheManager { /// Stub the get function of [mock], when [key] is used, [valueToReturn] is answered. static void stubGet(CacheManagerMock mock, String key, String valueToReturn) { when(mock.get(key)).thenAnswer((_) async => valueToReturn); diff --git a/test/mock/managers/course_repository_mock.dart b/test/mock/managers/course_repository_mock.dart index 4dfbda627..6dee4cedc 100644 --- a/test/mock/managers/course_repository_mock.dart +++ b/test/mock/managers/course_repository_mock.dart @@ -1,12 +1,16 @@ // Package imports: import 'package:ets_api_clients/exceptions.dart'; import 'package:ets_api_clients/models.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/managers/course_repository.dart'; -class CourseRepositoryMock extends Mock implements CourseRepository { +import 'course_repository_mock.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +class CourseRepositoryMock extends MockCourseRepository { /// Stub the getter [coursesActivities] of [mock] when called will return [toReturn]. static void stubCoursesActivities(CourseRepositoryMock mock, {List toReturn = const []}) { @@ -27,19 +31,17 @@ class CourseRepositoryMock extends Mock implements CourseRepository { /// Stub the function [getCoursesActivities] of [mock] when called will return [toReturn]. static void stubGetCoursesActivities(CourseRepositoryMock mock, - {List toReturn = const [], bool fromCacheOnly}) { - when(mock.getCoursesActivities( - fromCacheOnly: fromCacheOnly ?? anyNamed("fromCacheOnly"))) + {List toReturn = const [], bool fromCacheOnly = false}) { + when(mock.getCoursesActivities(fromCacheOnly: fromCacheOnly)) .thenAnswer((_) async => toReturn); } /// Stub the function [getCoursesActivities] of [mock] when called will throw [toThrow]. static void stubGetCoursesActivitiesException(CourseRepositoryMock mock, {Exception toThrow = const ApiException(prefix: 'ApiException'), - bool fromCacheOnly}) { - when(mock.getCoursesActivities( - fromCacheOnly: fromCacheOnly ?? anyNamed("fromCacheOnly"))) - .thenAnswer((_) => Future.delayed(const Duration(milliseconds: 50)) + bool fromCacheOnly = false}) { + when(mock.getCoursesActivities(fromCacheOnly: fromCacheOnly)).thenAnswer( + (_) => Future.delayed(const Duration(milliseconds: 50)) .then((value) => throw toThrow)); } @@ -59,9 +61,8 @@ class CourseRepositoryMock extends Mock implements CourseRepository { /// Stub the function [getCourses] of [mock] when called will return [toReturn]. static void stubGetCourses(CourseRepositoryMock mock, - {List toReturn = const [], bool fromCacheOnly}) { - when(mock.getCourses( - fromCacheOnly: fromCacheOnly ?? anyNamed("fromCacheOnly"))) + {List toReturn = const [], bool fromCacheOnly = false}) { + when(mock.getCourses(fromCacheOnly: fromCacheOnly)) .thenAnswer((_) async => toReturn); } @@ -74,17 +75,16 @@ class CourseRepositoryMock extends Mock implements CourseRepository { /// Stub the function [getCourses] of [mock] when called will throw [toThrow]. static void stubGetCoursesException(CourseRepositoryMock mock, {Exception toThrow = const ApiException(prefix: 'ApiException'), - bool fromCacheOnly}) { - when(mock.getCourses( - fromCacheOnly: fromCacheOnly ?? anyNamed("fromCacheOnly"))) - .thenAnswer((_) => Future.delayed(const Duration(milliseconds: 50)) + bool fromCacheOnly = false}) { + when(mock.getCourses(fromCacheOnly: fromCacheOnly)).thenAnswer((_) => + Future.delayed(const Duration(milliseconds: 50)) .then((value) => throw toThrow)); } /// Stub the function [getCourseSummary] of [mock] when called will return [toReturn]. static void stubGetCourseSummary( CourseRepositoryMock mock, Course courseCalled, - {Course toReturn}) { + {required Course toReturn}) { when(mock.getCourseSummary(courseCalled)).thenAnswer((_) async => toReturn); } @@ -99,9 +99,9 @@ class CourseRepositoryMock extends Mock implements CourseRepository { /// Stub the function [getScheduleActivities] of [mock] when called will return [toReturn]. static void stubGetScheduleActivities(CourseRepositoryMock mock, - {List toReturn = const [], bool fromCacheOnly}) { - when(mock.getScheduleActivities( - fromCacheOnly: fromCacheOnly ?? anyNamed("fromCacheOnly"))) + {List toReturn = const [], + bool fromCacheOnly = false}) { + when(mock.getScheduleActivities(fromCacheOnly: fromCacheOnly)) .thenAnswer((_) async => toReturn); } diff --git a/test/mock/managers/quick_links_repository_mock.dart b/test/mock/managers/quick_links_repository_mock.dart index 8cde2beb5..485f8fa79 100644 --- a/test/mock/managers/quick_links_repository_mock.dart +++ b/test/mock/managers/quick_links_repository_mock.dart @@ -1,5 +1,6 @@ // Package imports: import 'package:ets_api_clients/exceptions.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: @@ -7,7 +8,10 @@ import 'package:notredame/core/managers/quick_link_repository.dart'; import 'package:notredame/core/models/quick_link.dart'; import 'package:notredame/core/models/quick_link_data.dart'; -class QuickLinkRepositoryMock extends Mock implements QuickLinkRepository { +import 'quick_links_repository_mock.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +class QuickLinkRepositoryMock extends MockQuickLinkRepository { /// Stub the function [getQuickLinkDataFromCache] of [mock] when called will return [toReturn]. static void stubGetQuickLinkDataFromCache(QuickLinkRepositoryMock mock, {List toReturn = const []}) { diff --git a/test/mock/managers/settings_manager_mock.dart b/test/mock/managers/settings_manager_mock.dart index 5a90997cb..a27043f2c 100644 --- a/test/mock/managers/settings_manager_mock.dart +++ b/test/mock/managers/settings_manager_mock.dart @@ -1,5 +1,6 @@ // Flutter imports: import 'package:flutter/material.dart'; +import 'package:mockito/annotations.dart'; // Package imports: import 'package:mockito/mockito.dart'; @@ -8,7 +9,10 @@ import 'package:mockito/mockito.dart'; import 'package:notredame/core/constants/preferences_flags.dart'; import 'package:notredame/core/managers/settings_manager.dart'; -class SettingsManagerMock extends Mock implements SettingsManager { +import 'settings_manager_mock.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +class SettingsManagerMock extends MockSettingsManager { /// Stub the [getScheduleSettings] function of [mock], when called return [toReturn]. static void stubGetScheduleSettings(SettingsManagerMock mock, {Map toReturn = const {}}) { @@ -71,7 +75,9 @@ class SettingsManagerMock extends Mock implements SettingsManager { } /// Stub the [dateTimeNow] function of [mock], when called return [toReturn]. - static void stubDateTimeNow(SettingsManagerMock mock, {DateTime toReturn}) { + static void stubDateTimeNow(SettingsManagerMock mock, + {required DateTime toReturn}) { + // ignore: cast_nullable_to_non_nullable when(mock.dateTimeNow).thenReturn(toReturn); } } diff --git a/test/mock/managers/user_repository_mock.dart b/test/mock/managers/user_repository_mock.dart index 0eb6b9955..4fdddd5d4 100644 --- a/test/mock/managers/user_repository_mock.dart +++ b/test/mock/managers/user_repository_mock.dart @@ -1,13 +1,17 @@ // Package imports: import 'package:ets_api_clients/exceptions.dart'; import 'package:ets_api_clients/models.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/managers/user_repository.dart'; +import 'user_repository_mock.mocks.dart'; + /// Mock for the [UserRepository] -class UserRepositoryMock extends Mock implements UserRepository { +@GenerateNiceMocks([MockSpec()]) +class UserRepositoryMock extends MockUserRepository { /// When [monETSUser] is called will return [userToReturn] static void stubMonETSUser(UserRepositoryMock mock, MonETSUser userToReturn) { when(mock.monETSUser).thenAnswer((_) => userToReturn); @@ -43,22 +47,23 @@ class UserRepositoryMock extends Mock implements UserRepository { /// Stub the getter [ProfileStudent] of [mock] when called will return [toReturn]. static void stubProfileStudent(UserRepositoryMock mock, - {ProfileStudent toReturn}) { + {ProfileStudent? toReturn}) { when(mock.info).thenReturn(toReturn); } /// Stub the function [getInfo] of [mock] when called will return [toReturn]. static void stubGetInfo(UserRepositoryMock mock, - {ProfileStudent toReturn, bool fromCacheOnly}) { + {required ProfileStudent toReturn, bool? fromCacheOnly}) { when(mock.getInfo( fromCacheOnly: fromCacheOnly ?? anyNamed("fromCacheOnly"))) + // ignore: cast_nullable_to_non_nullable .thenAnswer((_) async => toReturn); } /// Stub the function [getInfo] of [mock] when called will throw [toThrow]. static void stubGetInfoException(UserRepositoryMock mock, {Exception toThrow = const ApiException(prefix: 'ApiException'), - bool fromCacheOnly}) { + bool? fromCacheOnly}) { when(mock.getInfo( fromCacheOnly: fromCacheOnly ?? anyNamed("fromCacheOnly"))) .thenAnswer((_) => Future.delayed(const Duration(milliseconds: 50)) @@ -73,7 +78,7 @@ class UserRepositoryMock extends Mock implements UserRepository { /// Stub the function [getPrograms] of [mock] when called will return [toReturn]. static void stubGetPrograms(UserRepositoryMock mock, - {List toReturn = const [], bool fromCacheOnly}) { + {List toReturn = const [], bool? fromCacheOnly}) { when(mock.getPrograms( fromCacheOnly: fromCacheOnly ?? anyNamed("fromCacheOnly"))) .thenAnswer((_) async => toReturn); @@ -82,7 +87,7 @@ class UserRepositoryMock extends Mock implements UserRepository { /// Stub the function [getPrograms] of [mock] when called will throw [toThrow]. static void stubGetProgramsException(UserRepositoryMock mock, {Exception toThrow = const ApiException(prefix: 'ApiException'), - bool fromCacheOnly}) { + bool? fromCacheOnly}) { when(mock.getPrograms( fromCacheOnly: fromCacheOnly ?? anyNamed("fromCacheOnly"))) .thenAnswer((_) => Future.delayed(const Duration(milliseconds: 50)) diff --git a/test/mock/services/analytics_service_mock.dart b/test/mock/services/analytics_service_mock.dart index 78cd8a958..4417498e6 100644 --- a/test/mock/services/analytics_service_mock.dart +++ b/test/mock/services/analytics_service_mock.dart @@ -1,8 +1,11 @@ // Package imports: -import 'package:mockito/mockito.dart'; +import 'package:mockito/annotations.dart'; // Project imports: import 'package:notredame/core/services/analytics_service.dart'; +import 'analytics_service_mock.mocks.dart'; + /// Mock for the [AnalyticsService] -class AnalyticsServiceMock extends Mock implements AnalyticsService {} +@GenerateNiceMocks([MockSpec()]) +class AnalyticsServiceMock extends MockAnalyticsService {} diff --git a/test/mock/services/app_widget_service_mock.dart b/test/mock/services/app_widget_service_mock.dart index f2edbb8b2..d4799802e 100644 --- a/test/mock/services/app_widget_service_mock.dart +++ b/test/mock/services/app_widget_service_mock.dart @@ -1,8 +1,11 @@ // Package imports: -import 'package:mockito/mockito.dart'; +import 'package:mockito/annotations.dart'; // Project imports: import 'package:notredame/core/services/app_widget_service.dart'; +import 'app_widget_service_mock.mocks.dart'; + /// Mock for the [AppWidgetService] -class AppWidgetServiceMock extends Mock implements AppWidgetService {} +@GenerateNiceMocks([MockSpec()]) +class AppWidgetServiceMock extends MockAppWidgetService {} diff --git a/test/mock/services/flutter_secure_storage_mock.dart b/test/mock/services/flutter_secure_storage_mock.dart index 6775d7f80..a3723bf4b 100644 --- a/test/mock/services/flutter_secure_storage_mock.dart +++ b/test/mock/services/flutter_secure_storage_mock.dart @@ -1,34 +1,35 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - // Package imports: import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'flutter_secure_storage_mock.mocks.dart'; + /// Mock for the [FlutterSecureStorage] -class FlutterSecureStorageMock extends Mock implements FlutterSecureStorage { +@GenerateNiceMocks([MockSpec()]) +class FlutterSecureStorageMock extends MockFlutterSecureStorage { /// Stub the read function of [FlutterSecureStorage] static void stubRead(FlutterSecureStorageMock mock, - {@required String key, @required String valueToReturn}) { + {required String key, required String? valueToReturn}) { when(mock.read(key: key)).thenAnswer((_) async => valueToReturn); } /// Stub the read function of [FlutterSecureStorage] with an [Exception] static void stubReadException(FlutterSecureStorageMock mock, - {@required String key, @required Exception exceptionToThrow}) { + {required String key, required Exception exceptionToThrow}) { when(mock.read(key: key)).thenThrow(exceptionToThrow); } /// Stub the write function of [FlutterSecureStorage] with an [Exception] static void stubWriteException(FlutterSecureStorageMock mock, - {@required String key, @required Exception exceptionToThrow}) { + {required String key, required Exception exceptionToThrow}) { when(mock.write(key: key, value: anyNamed("value"))) .thenThrow(exceptionToThrow); } /// Stub the delete function of [FlutterSecureStorage] with an [Exception] static void stubDeleteException(FlutterSecureStorageMock mock, - {@required String key, @required Exception exceptionToThrow}) { + {required String key, required Exception exceptionToThrow}) { when(mock.delete(key: key)).thenThrow(exceptionToThrow); } } diff --git a/test/mock/services/github_api_mock.dart b/test/mock/services/github_api_mock.dart index c70ffcab6..404632d52 100644 --- a/test/mock/services/github_api_mock.dart +++ b/test/mock/services/github_api_mock.dart @@ -3,14 +3,19 @@ import 'dart:io'; // Package imports: import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:github/github.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/models/feedback_issue.dart'; import 'package:notredame/core/services/github_api.dart'; +import 'github_api_mock.mocks.dart'; + /// Mock for the [GithubApi] -class GithubApiMock extends Mock implements GithubApi { +@GenerateNiceMocks([MockSpec()]) +class GithubApiMock extends MockGithubApi { /// Stub the localFile of propertie [localFile] and return [fileToReturn]. static void stubLocalFile(GithubApiMock client, File fileToReturn) { when(client.localFile).thenAnswer((_) async => fileToReturn); @@ -21,4 +26,13 @@ class GithubApiMock extends Mock implements GithubApi { when(client.fetchIssuesByNumbers(any, any)) .thenAnswer((_) async => Future.value(issuesToReturn)); } + + static void stubCreateGithubIssue(GithubApiMock client, Issue toReturn) { + when(client.createGithubIssue( + feedbackText: anyNamed("feedbackText"), + fileName: anyNamed("fileName"), + feedbackType: anyNamed("feedbackType"), + email: anyNamed("email"))) + .thenAnswer((_) async => Future.value(toReturn)); + } } diff --git a/test/mock/services/home_widget_mock.dart b/test/mock/services/home_widget_mock.dart index 71f1f619c..5e9e23373 100644 --- a/test/mock/services/home_widget_mock.dart +++ b/test/mock/services/home_widget_mock.dart @@ -4,21 +4,24 @@ import 'package:flutter/services.dart'; // Package imports: import 'package:flutter_test/flutter_test.dart'; import 'package:home_widget/home_widget.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mockito/annotations.dart'; // Project imports: import 'package:notredame/core/services/app_widget_service.dart'; +import 'home_widget_mock.mocks.dart'; + /// Pseudo-mock for the static [HomeWidget] class (mocks the channel instead) -class HomeWidgetMock extends Mock { - MethodChannel _channel; - TestDefaultBinaryMessenger _messenger; +@GenerateNiceMocks([MockSpec()]) +class HomeWidgetMock extends MockHomeWidget { + late MethodChannel _channel; + late TestDefaultBinaryMessenger _messenger; HomeWidgetMock() { _channel = const MethodChannel('home_widget'); _messenger = - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; + TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger; } /// Overrides [HomeWidget]'s channel messenger behavior on [HomeWidget.setAppGroupId] diff --git a/test/mock/services/http_client_mock.dart b/test/mock/services/http_client_mock.dart deleted file mode 100644 index 085cb1d69..000000000 --- a/test/mock/services/http_client_mock.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Dart imports: -import 'dart:convert'; - -// Package imports: -import 'package:http/http.dart' as http; -import 'package:mockito/mockito.dart'; - -/// Mock for the Http client -class HttpClientMock extends Mock implements http.Client { - /// Stub the next post of [url] and return [jsonResponse] with [statusCode] as http response code. - static void stubJsonPost(HttpClientMock client, String url, - Map jsonResponse, int statusCode) { - when(client.post(Uri.parse(url), - headers: anyNamed('headers'), body: anyNamed('body'))) - .thenAnswer( - (_) async => http.Response(jsonEncode(jsonResponse), statusCode)); - } - - /// Stub the next post request to [url] and return [response] with [statusCode] as http response code. - static void stubPost(HttpClientMock client, String url, String response, - [int statusCode = 200]) { - when(client.post(Uri.parse(url), - headers: anyNamed('headers'), body: anyNamed('body'))) - .thenAnswer((_) async => http.Response(response, statusCode)); - } -} diff --git a/test/mock/services/in_app_review_service_mock.dart b/test/mock/services/in_app_review_service_mock.dart index a0367af1f..825ed9895 100644 --- a/test/mock/services/in_app_review_service_mock.dart +++ b/test/mock/services/in_app_review_service_mock.dart @@ -1,11 +1,15 @@ // Package imports: +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/services/in_app_review_service.dart'; +import 'in_app_review_service_mock.mocks.dart'; + /// Mock for the [AnalyticsService] -class InAppReviewServiceMock extends Mock implements InAppReviewService { +@GenerateNiceMocks([MockSpec()]) +class InAppReviewServiceMock extends MockInAppReviewService { /// Stub the answer of [isAvailable] static void stubIsAvailable(InAppReviewServiceMock mock, {bool toReturn = true}) { diff --git a/test/mock/services/internal_info_service_mock.dart b/test/mock/services/internal_info_service_mock.dart index 570b81ff3..35018fd1b 100644 --- a/test/mock/services/internal_info_service_mock.dart +++ b/test/mock/services/internal_info_service_mock.dart @@ -1,11 +1,15 @@ // Package imports: +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:package_info_plus/package_info_plus.dart'; // Project imports: import 'package:notredame/core/services/internal_info_service.dart'; -class InternalInfoServiceMock extends Mock implements InternalInfoService { +import 'internal_info_service_mock.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +class InternalInfoServiceMock extends MockInternalInfoService { /// Stub the answer of [getDeviceInfoForErrorReporting] static void stubGetDeviceInfoForErrorReporting(InternalInfoServiceMock mock) { when(mock.getDeviceInfoForErrorReporting()) @@ -14,7 +18,7 @@ class InternalInfoServiceMock extends Mock implements InternalInfoService { /// Stub the answer of [getPackageInfo] static void stubGetPackageInfo(InternalInfoServiceMock mock, - {String version}) { + {String version = "0.0.0"}) { when(mock.getPackageInfo()).thenAnswer((_) async => PackageInfo( appName: "ÉTSMobile", packageName: "ca.etsmtl.applets.etsmobile", diff --git a/test/mock/services/launch_url_service_mock.dart b/test/mock/services/launch_url_service_mock.dart index 0a328d3b6..409a8377a 100644 --- a/test/mock/services/launch_url_service_mock.dart +++ b/test/mock/services/launch_url_service_mock.dart @@ -1,11 +1,15 @@ // Package imports: +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/services/launch_url_service.dart'; +import 'launch_url_service_mock.mocks.dart'; + /// Mock for the [LaunchUrlService] -class LaunchUrlServiceMock extends Mock implements LaunchUrlService { +@GenerateNiceMocks([MockSpec()]) +class LaunchUrlServiceMock extends MockLaunchUrlService { static void stubCanLaunchUrl(LaunchUrlServiceMock client, String url, {bool toReturn = true}) { when(client.canLaunch(url)).thenAnswer((_) async => toReturn); diff --git a/test/mock/services/navigation_service_mock.dart b/test/mock/services/navigation_service_mock.dart index 0d53f6aef..ef9bb4f5a 100644 --- a/test/mock/services/navigation_service_mock.dart +++ b/test/mock/services/navigation_service_mock.dart @@ -1,8 +1,11 @@ // Package imports: -import 'package:mockito/mockito.dart'; +import 'package:mockito/annotations.dart'; // Project imports: import 'package:notredame/core/services/navigation_service.dart'; +import 'navigation_service_mock.mocks.dart'; + /// Mock for the [NavigationService] -class NavigationServiceMock extends Mock implements NavigationService {} +@GenerateNiceMocks([MockSpec()]) +class NavigationServiceMock extends MockNavigationService {} diff --git a/test/mock/services/networking_service_mock.dart b/test/mock/services/networking_service_mock.dart index e56f0ffd9..60505d609 100644 --- a/test/mock/services/networking_service_mock.dart +++ b/test/mock/services/networking_service_mock.dart @@ -1,12 +1,16 @@ // Package imports: import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/services/networking_service.dart'; +import 'networking_service_mock.mocks.dart'; + /// Mock for the [NetworkingService] -class NetworkingServiceMock extends Mock implements NetworkingService { +@GenerateNiceMocks([MockSpec()]) +class NetworkingServiceMock extends MockNetworkingService { /// Stub the user connection state static void stubHasConnectivity(NetworkingServiceMock service, {bool hasConnectivity = true}) { diff --git a/test/mock/services/preferences_service_mock.dart b/test/mock/services/preferences_service_mock.dart index dcacca670..c2b7fb0a5 100644 --- a/test/mock/services/preferences_service_mock.dart +++ b/test/mock/services/preferences_service_mock.dart @@ -1,11 +1,15 @@ // Package imports: +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/constants/preferences_flags.dart'; import 'package:notredame/core/services/preferences_service.dart'; -class PreferencesServiceMock extends Mock implements PreferencesService { +import 'preferences_service_mock.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +class PreferencesServiceMock extends MockPreferencesService { /// Stub the answer of [setString] when the [flag] is used. static void stubSetString(PreferencesServiceMock mock, PreferencesFlag flag, {bool toReturn = true}) { @@ -27,39 +31,39 @@ class PreferencesServiceMock extends Mock implements PreferencesService { /// Stub the answer of [getString] when the [flag] is used. static void stubGetString(PreferencesServiceMock mock, PreferencesFlag flag, - {String toReturn = "test"}) { + {String? toReturn = "test"}) { when(mock.getString(flag)).thenAnswer((_) async => toReturn); } /// Stub the answer of [getString] when the [flag] is used. static void stubGetInt(PreferencesServiceMock mock, PreferencesFlag flag, - {int toReturn = 1}) { + {int? toReturn = 1}) { when(mock.getInt(flag)).thenAnswer((_) async => toReturn); } /// Stub the answer of [getBool] when the [flag] is used. static void stubGetBool(PreferencesServiceMock mock, PreferencesFlag flag, - {bool toReturn = true}) { + {bool? toReturn = true}) { when(mock.getBool(flag)).thenAnswer((_) async => toReturn); } /// Stub the answer of [getDateTime] when the [flag] is used. static void stubGetDateTime(PreferencesServiceMock mock, PreferencesFlag flag, - {DateTime toReturn}) { + {DateTime? toReturn}) { when(mock.getDateTime(flag)).thenAnswer((_) async => toReturn); } /// Stub the answer of [getPreferencesFlag] when the [flag] is used. static void stubGetPreferencesFlag( PreferencesServiceMock mock, PreferencesFlag flag, - {Object toReturn}) { + {Object? toReturn}) { when(mock.getPreferencesFlag(flag)).thenAnswer((_) async => toReturn); } /// Stub to throw an [Exception] when the getInt /// will be called with this [flag] static void stubException(PreferencesServiceMock mock, PreferencesFlag flag, - {bool toReturn = true}) { + {bool? toReturn = true}) { when(mock.getInt(flag)).thenThrow(Exception()); } } diff --git a/test/mock/services/remote_config_service_mock.dart b/test/mock/services/remote_config_service_mock.dart index f4eea8f4b..4b01c6bad 100644 --- a/test/mock/services/remote_config_service_mock.dart +++ b/test/mock/services/remote_config_service_mock.dart @@ -1,11 +1,15 @@ // Package imports: +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/services/remote_config_service.dart'; +import 'remote_config_service_mock.mocks.dart'; + /// Mock for the [RemoteConfigService] -class RemoteConfigServiceMock extends Mock implements RemoteConfigService { +@GenerateNiceMocks([MockSpec()]) +class RemoteConfigServiceMock extends MockRemoteConfigService { /// Stub the getter [coursesActivities] of [mock] when called will return [toReturn]. static void stubGetCalendarViewEnabled(RemoteConfigServiceMock mock, {bool toReturn = true}) { diff --git a/test/mock/services/rive_animation_service_mock.dart b/test/mock/services/rive_animation_service_mock.dart index 958573652..cda80059f 100644 --- a/test/mock/services/rive_animation_service_mock.dart +++ b/test/mock/services/rive_animation_service_mock.dart @@ -1,4 +1,5 @@ // Package imports: +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:rive/rive.dart'; @@ -6,8 +7,11 @@ import 'package:rive/rive.dart'; import 'package:notredame/core/services/rive_animation_service.dart'; import 'package:notredame/core/utils/animation_exception.dart'; +import 'rive_animation_service_mock.mocks.dart'; + /// Mock for the [RiveAnimationService] -class RiveAnimationServiceMock extends Mock implements RiveAnimationService { +@GenerateNiceMocks([MockSpec()]) +class RiveAnimationServiceMock extends MockRiveAnimationService { static const startException = AnimationException( prefix: "addControllerToAnimation", message: diff --git a/test/mock/services/siren_flutter_service_mock.dart b/test/mock/services/siren_flutter_service_mock.dart index 36826c96b..7aa9652a0 100644 --- a/test/mock/services/siren_flutter_service_mock.dart +++ b/test/mock/services/siren_flutter_service_mock.dart @@ -1,15 +1,16 @@ -// Flutter imports: -import 'package:flutter/material.dart'; - // Package imports: +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:pub_semver/pub_semver.dart'; // Project imports: import 'package:notredame/core/services/siren_flutter_service.dart'; +import 'siren_flutter_service_mock.mocks.dart'; + /// Mock for the [SirenFlutterService] -class SirenFlutterServiceMock extends Mock implements SirenFlutterService { +@GenerateNiceMocks([MockSpec()]) +class SirenFlutterServiceMock extends MockSirenFlutterService { /// Stub the updateIsAvailable function of [SirenFlutterService] static void stubUpdateIsAvailable(SirenFlutterServiceMock mock, {bool valueToReturn = false}) { @@ -18,13 +19,13 @@ class SirenFlutterServiceMock extends Mock implements SirenFlutterService { /// Stub the localVersion function of [SirenFlutterService] static void stubLocalVersion(SirenFlutterServiceMock mock, - {@required Version valueToReturn}) { + {required Version valueToReturn}) { when(mock.localVersion).thenAnswer((_) async => valueToReturn); } /// Stub the storeVersion function of [SirenFlutterService] static void stubStoreVersion(SirenFlutterServiceMock mock, - {@required Version valueToReturn}) { + {required Version valueToReturn}) { when(mock.storeVersion).thenAnswer((_) async => valueToReturn); } } diff --git a/test/mock/viewmodels/login_viewmodel.dart b/test/mock/viewmodels/login_viewmodel.dart deleted file mode 100644 index f61441693..000000000 --- a/test/mock/viewmodels/login_viewmodel.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Package imports: -import 'package:mockito/mockito.dart'; - -// Project imports: -import 'package:notredame/core/viewmodels/login_viewmodel.dart'; - -class LoginViewModelMock extends Mock implements LoginViewModel {} diff --git a/test/services/app_widget_service_test.dart b/test/services/app_widget_service_test.dart index 5e291b4a3..b458ca0c1 100644 --- a/test/services/app_widget_service_test.dart +++ b/test/services/app_widget_service_test.dart @@ -12,8 +12,9 @@ import '../mock/services/home_widget_mock.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - AppWidgetService service; - HomeWidgetMock homeWidgetMock; + late HomeWidgetMock homeWidgetMock; + + late AppWidgetService service; group("AppWidgetServiceTest - ", () { setUp(() { @@ -133,7 +134,7 @@ void main() { test("progress", () async { const WidgetType type = WidgetType.progress; homeWidgetMock.stubUpdateWidgetMock( - type.androidName, type.androidName, type.iOSname); + type.androidName!, type.androidName!, type.iOSname!); expect( await HomeWidget.updateWidget( name: type.androidName, @@ -145,7 +146,7 @@ void main() { test("grades", () async { const WidgetType type = WidgetType.grades; homeWidgetMock.stubUpdateWidgetMock( - type.androidName, type.androidName, type.iOSname); + type.androidName!, type.androidName!, type.iOSname!); expect( await HomeWidget.updateWidget( name: type.androidName, diff --git a/test/services/preferences_service_test.dart b/test/services/preferences_service_test.dart index b103d5100..547faeaba 100644 --- a/test/services/preferences_service_test.dart +++ b/test/services/preferences_service_test.dart @@ -7,9 +7,9 @@ import 'package:notredame/core/constants/preferences_flags.dart'; import 'package:notredame/core/services/preferences_service.dart'; void main() { - SharedPreferences sharedPreferences; + late SharedPreferences sharedPreferences; - PreferencesService service; + late PreferencesService service; SharedPreferences.setMockInitialValues({}); TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/test/ui/views/choose_language_view_test.dart b/test/ui/views/choose_language_view_test.dart index 23b9b0f14..56242af4a 100644 --- a/test/ui/views/choose_language_view_test.dart +++ b/test/ui/views/choose_language_view_test.dart @@ -15,7 +15,7 @@ import 'package:notredame/ui/views/choose_language_view.dart'; import '../../helpers.dart'; void main() { - AppIntl intl; + late AppIntl intl; group('SettingsView - ', () { setUp(() async { intl = await setupAppIntl(); diff --git a/test/ui/views/dashboard_view_test.dart b/test/ui/views/dashboard_view_test.dart index 211b4f834..9f4b439db 100644 --- a/test/ui/views/dashboard_view_test.dart +++ b/test/ui/views/dashboard_view_test.dart @@ -12,9 +12,7 @@ import 'package:flutter_test/flutter_test.dart'; // Project imports: import 'package:notredame/core/constants/preferences_flags.dart'; -import 'package:notredame/core/managers/course_repository.dart'; -import 'package:notredame/core/managers/settings_manager.dart'; -import 'package:notredame/core/services/remote_config_service.dart'; +import 'package:notredame/core/constants/update_code.dart'; import 'package:notredame/ui/views/dashboard_view.dart'; import 'package:notredame/ui/widgets/course_activity_tile.dart'; import 'package:notredame/ui/widgets/grade_button.dart'; @@ -25,11 +23,12 @@ import '../../mock/services/in_app_review_service_mock.dart'; import '../../mock/services/remote_config_service_mock.dart'; void main() { - SettingsManager settingsManager; - CourseRepository courseRepository; - RemoteConfigService remoteConfigService; - AppIntl intl; - InAppReviewServiceMock inAppReviewServiceMock; + late SettingsManagerMock settingsManagerMock; + late CourseRepositoryMock courseRepositoryMock; + late RemoteConfigServiceMock remoteConfigServiceMock; + late InAppReviewServiceMock inAppReviewServiceMock; + + late AppIntl intl; // Activities for today final gen101 = CourseActivity( @@ -143,25 +142,21 @@ void main() { Future testDashboardSchedule(WidgetTester tester, DateTime now, List courses, int expected) async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: courses); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); - SettingsManagerMock.stubGetDashboard(settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); - SettingsManagerMock.stubDateTimeNow(settingsManager as SettingsManagerMock, - toReturn: now); + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: now); - await tester.pumpWidget( - localizedWidget(child: FeatureDiscovery(child: const DashboardView()))); + await tester.pumpWidget(localizedWidget( + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find schedule card in second position by its title @@ -174,11 +169,11 @@ void main() { group('DashboardView - ', () { setUp(() async { intl = await setupAppIntl(); - settingsManager = setupSettingsManagerMock(); - courseRepository = setupCourseRepositoryMock(); - remoteConfigService = setupRemoteConfigServiceMock(); + settingsManagerMock = setupSettingsManagerMock(); + courseRepositoryMock = setupCourseRepositoryMock(); + remoteConfigServiceMock = setupRemoteConfigServiceMock(); setupNavigationServiceMock(); - courseRepository = setupCourseRepositoryMock(); + courseRepositoryMock = setupCourseRepositoryMock(); setupNetworkingServiceMock(); setupAnalyticsServiceMock(); setupAppWidgetServiceMock(); @@ -189,70 +184,50 @@ void main() { InAppReviewServiceMock.stubIsAvailable(inAppReviewServiceMock, toReturn: false); - CourseRepositoryMock.stubSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubGetSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - - RemoteConfigServiceMock.stubGetBroadcastEnabled( - remoteConfigService as RemoteConfigServiceMock); - RemoteConfigServiceMock.stubGetBroadcastColor( - remoteConfigService as RemoteConfigServiceMock); - RemoteConfigServiceMock.stubGetBroadcastEn( - remoteConfigService as RemoteConfigServiceMock); - RemoteConfigServiceMock.stubGetBroadcastFr( - remoteConfigService as RemoteConfigServiceMock); - RemoteConfigServiceMock.stubGetBroadcastTitleEn( - remoteConfigService as RemoteConfigServiceMock); - RemoteConfigServiceMock.stubGetBroadcastTitleFr( - remoteConfigService as RemoteConfigServiceMock); - RemoteConfigServiceMock.stubGetBroadcastType( - remoteConfigService as RemoteConfigServiceMock); - RemoteConfigServiceMock.stubGetBroadcastUrl( - remoteConfigService as RemoteConfigServiceMock); - - SettingsManagerMock.stubGetBool(settingsManager as SettingsManagerMock, - PreferencesFlag.discoveryDashboard, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + + RemoteConfigServiceMock.stubGetBroadcastEnabled(remoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastColor(remoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastEn(remoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastFr(remoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastTitleEn(remoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastTitleFr(remoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastType(remoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastUrl(remoteConfigServiceMock); + + SettingsManagerMock.stubGetBool( + settingsManagerMock, PreferencesFlag.discoveryDashboard, toReturn: true); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.broadcastCard); SettingsManagerMock.stubSetInt( - settingsManager as SettingsManagerMock, PreferencesFlag.aboutUsCard); + settingsManagerMock, PreferencesFlag.aboutUsCard); SettingsManagerMock.stubSetInt( - settingsManager as SettingsManagerMock, PreferencesFlag.scheduleCard); + settingsManagerMock, PreferencesFlag.scheduleCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.progressBarCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.progressBarCard); SettingsManagerMock.stubSetInt( - settingsManager as SettingsManagerMock, PreferencesFlag.gradesCard); + settingsManagerMock, PreferencesFlag.gradesCard); - SettingsManagerMock.stubDateTimeNow( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: DateTime.now()); }); @@ -261,12 +236,12 @@ void main() { group('UI - ', () { testWidgets('Has view title restore button and cards, displayed', (WidgetTester tester) async { - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find Dashboard Title @@ -287,23 +262,19 @@ void main() { testWidgets('Has card aboutUs displayed properly', (WidgetTester tester) async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find aboutUs card @@ -387,37 +358,33 @@ void main() { group('Interactions - ', () { testWidgets('AboutUsCard is dismissible and can be restored', (WidgetTester tester) async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.broadcastCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.aboutUsCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.aboutUsCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.scheduleCard); SettingsManagerMock.stubSetInt( - settingsManager as SettingsManagerMock, PreferencesFlag.gradesCard); + settingsManagerMock, PreferencesFlag.gradesCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.progressBarCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.progressBarCard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find Dismissible Cards @@ -448,36 +415,32 @@ void main() { testWidgets('AboutUsCard is reorderable and can be restored', (WidgetTester tester) async { - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.broadcastCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.aboutUsCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.aboutUsCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.scheduleCard); SettingsManagerMock.stubSetInt( - settingsManager as SettingsManagerMock, PreferencesFlag.gradesCard); + settingsManagerMock, PreferencesFlag.gradesCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.progressBarCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.progressBarCard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find Dismissible Cards @@ -512,7 +475,7 @@ void main() { of: find.byType(Dismissible, skipOffstage: false).last, matching: find.byType(Text), )); - expect((text as Text).data, intl.card_applets_title); + expect(text.data, intl.card_applets_title); // Tap the restoreCards button await tester.tap(find.byIcon(Icons.restore)); @@ -528,17 +491,17 @@ void main() { findsNWidgets(numberOfCards)); // Check that the first card is now AboutUs - expect((text as Text).data, intl.card_applets_title); + expect(text.data, intl.card_applets_title); }); testWidgets('ScheduleCard is dismissible and can be restored', (WidgetTester tester) async { - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find Dismissible Cards @@ -574,12 +537,12 @@ void main() { group('UI - gradesCard', () { testWidgets('Has card grades displayed - with no courses', (WidgetTester tester) async { - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find grades card @@ -600,24 +563,19 @@ void main() { testWidgets('Has card grades displayed - with courses', (WidgetTester tester) async { - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock, - toReturn: courses); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: true, + CourseRepositoryMock.stubCourses(courseRepositoryMock, toReturn: courses); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, + fromCacheOnly: true, toReturn: courses); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, toReturn: courses); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find grades card @@ -636,26 +594,26 @@ void main() { testWidgets('gradesCard is dismissible and can be restored', (WidgetTester tester) async { - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.broadcastCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.aboutUsCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.aboutUsCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.scheduleCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.progressBarCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.progressBarCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.gradesCard); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.gradesCard); + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find Dismissible Cards @@ -692,12 +650,12 @@ void main() { group("UI - progressBar", () { testWidgets('Has card progressBar displayed', (WidgetTester tester) async { - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find progress card @@ -715,12 +673,12 @@ void main() { testWidgets('progressCard is dismissible and can be restored', (WidgetTester tester) async { - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find Dismissible Cards @@ -753,12 +711,12 @@ void main() { testWidgets('progressBarCard is reorderable and can be restored', (WidgetTester tester) async { InAppReviewServiceMock.stubIsAvailable(inAppReviewServiceMock); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); // Find Dismissible Cards @@ -793,7 +751,7 @@ void main() { of: find.widgetWithText(Dismissible, intl.progress_bar_title).last, matching: find.byType(Text), )); - expect((text as Text).data, intl.progress_bar_title); + expect(text.data, intl.progress_bar_title); // Tap the restoreCards button await tester.tap(find.byIcon(Icons.restore)); @@ -809,7 +767,7 @@ void main() { findsNWidgets(numberOfCards)); // Check that the first card is now AboutUs - expect((text as Text).data, intl.progress_bar_title); + expect(text.data, intl.progress_bar_title); }); }); @@ -819,8 +777,7 @@ void main() { }); testWidgets("Applets Card", (WidgetTester tester) async { - RemoteConfigServiceMock.stubGetBroadcastEnabled( - remoteConfigService as RemoteConfigServiceMock, + RemoteConfigServiceMock.stubGetBroadcastEnabled(remoteConfigServiceMock, toReturn: false); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); @@ -829,12 +786,12 @@ void main() { PreferencesFlag.aboutUsCard: 1, }; - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); await expectLater(find.byType(DashboardView), @@ -842,39 +799,33 @@ void main() { }); testWidgets("Schedule card", (WidgetTester tester) async { - RemoteConfigServiceMock.stubGetBroadcastEnabled( - remoteConfigService as RemoteConfigServiceMock, + RemoteConfigServiceMock.stubGetBroadcastEnabled(remoteConfigServiceMock, toReturn: false); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); dashboard = { PreferencesFlag.broadcastCard: 0, PreferencesFlag.scheduleCard: 1, }; - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); await expectLater(find.byType(DashboardView), matchesGoldenFile(goldenFilePath("dashboardView_scheduleCard_1"))); }); testWidgets("progressBar Card", (WidgetTester tester) async { - RemoteConfigServiceMock.stubGetBroadcastEnabled( - remoteConfigService as RemoteConfigServiceMock, + RemoteConfigServiceMock.stubGetBroadcastEnabled(remoteConfigServiceMock, toReturn: false); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); @@ -883,12 +834,12 @@ void main() { PreferencesFlag.progressBarCard: 1, }; - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const DashboardView()))); + child: FeatureDiscovery( + child: const DashboardView(updateCode: UpdateCode.none)))); await tester.pumpAndSettle(); await expectLater( diff --git a/test/ui/views/faq_view_test.dart b/test/ui/views/faq_view_test.dart index 909c16b67..066a17ba5 100644 --- a/test/ui/views/faq_view_test.dart +++ b/test/ui/views/faq_view_test.dart @@ -8,20 +8,19 @@ import 'package:flutter_test/flutter_test.dart'; // Project imports: import 'package:notredame/core/constants/faq.dart'; -import 'package:notredame/core/managers/settings_manager.dart'; import 'package:notredame/ui/views/faq_view.dart'; import '../../helpers.dart'; import '../../mock/managers/settings_manager_mock.dart'; void main() { group('FaqView - ', () { - AppIntl appIntl; + late AppIntl appIntl; - SettingsManager settingsManager; + late SettingsManagerMock settingsManagerMock; setUp(() async { setupLaunchUrlServiceMock(); - settingsManager = setupSettingsManagerMock(); + settingsManagerMock = setupSettingsManagerMock(); appIntl = await setupAppIntl(); }); @@ -29,7 +28,7 @@ void main() { group('UI - ', () { testWidgets('has x ElevatedButton', (WidgetTester tester) async { - SettingsManagerMock.stubLocale(settingsManager as SettingsManagerMock); + SettingsManagerMock.stubLocale(settingsManagerMock); await tester.pumpWidget(localizedWidget(child: const FaqView())); await tester.pumpAndSettle(); @@ -42,7 +41,7 @@ void main() { }); testWidgets('has 2 subtitles', (WidgetTester tester) async { - SettingsManagerMock.stubLocale(settingsManager as SettingsManagerMock); + SettingsManagerMock.stubLocale(settingsManagerMock); await tester.pumpWidget(localizedWidget(child: const FaqView())); await tester.pumpAndSettle(); @@ -55,7 +54,7 @@ void main() { }); testWidgets('has 1 title', (WidgetTester tester) async { - SettingsManagerMock.stubLocale(settingsManager as SettingsManagerMock); + SettingsManagerMock.stubLocale(settingsManagerMock); await tester.pumpWidget(localizedWidget(child: const FaqView())); await tester.pumpAndSettle(); @@ -68,7 +67,7 @@ void main() { group("golden - ", () { testWidgets("default view", (WidgetTester tester) async { - SettingsManagerMock.stubLocale(settingsManager as SettingsManagerMock); + SettingsManagerMock.stubLocale(settingsManagerMock); tester.binding.window.physicalSizeTestValue = const Size(1800, 2410); await tester.pumpWidget(localizedWidget(child: const FaqView())); diff --git a/test/ui/views/goldenFiles/gradesDetailsView_2.png b/test/ui/views/goldenFiles/gradesDetailsView_2.png index c7db3e6bb..5c3a06e17 100644 Binary files a/test/ui/views/goldenFiles/gradesDetailsView_2.png and b/test/ui/views/goldenFiles/gradesDetailsView_2.png differ diff --git a/test/ui/views/goldenFiles/gradesDetailsView_evaluation_not_completed.png b/test/ui/views/goldenFiles/gradesDetailsView_evaluation_not_completed.png index c7db3e6bb..450e536e1 100644 Binary files a/test/ui/views/goldenFiles/gradesDetailsView_evaluation_not_completed.png and b/test/ui/views/goldenFiles/gradesDetailsView_evaluation_not_completed.png differ diff --git a/test/ui/views/goldenFiles/notFoundView_1.png b/test/ui/views/goldenFiles/notFoundView_1.png index de47ab31f..07b7d9cc8 100644 Binary files a/test/ui/views/goldenFiles/notFoundView_1.png and b/test/ui/views/goldenFiles/notFoundView_1.png differ diff --git a/test/ui/views/grades_details_view_test.dart b/test/ui/views/grades_details_view_test.dart index 56a614bea..25b850973 100644 --- a/test/ui/views/grades_details_view_test.dart +++ b/test/ui/views/grades_details_view_test.dart @@ -19,7 +19,7 @@ import '../../mock/managers/course_repository_mock.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - CourseRepository courseRepository; + late CourseRepositoryMock courseRepositoryMock; final CourseSummary courseSummary = CourseSummary( currentMark: 5, @@ -34,7 +34,7 @@ void main() { courseGroup: "02", title: "Laboratoire 1", weight: 10, - teacherMessage: null, + teacherMessage: '', ignore: false, mark: 24, correctedEvaluationOutOf: "35", @@ -96,7 +96,7 @@ void main() { group('GradesDetailsView - ', () { setUp(() async { setupNavigationServiceMock(); - courseRepository = setupCourseRepositoryMock(); + courseRepositoryMock = setupCourseRepositoryMock(); setupSettingsManagerMock(); setupNetworkingServiceMock(); }); @@ -112,6 +112,8 @@ void main() { 'has a RefreshIndicator, GradeCircularProgress, three cards and evaluation tiles when a course is valid', (WidgetTester tester) async { setupFlutterToastMock(tester); + CourseRepositoryMock.stubGetCourseSummary(courseRepositoryMock, course, + toReturn: course); await tester.pumpWidget(localizedWidget( child: FeatureDiscovery(child: GradesDetailsView(course: course)))); await tester.pumpAndSettle(); @@ -139,11 +141,12 @@ void main() { (WidgetTester tester) async { setupFlutterToastMock(tester); CourseRepositoryMock.stubGetCourseSummary( - courseRepository as CourseRepositoryMock, courseWithoutSummary, + courseRepositoryMock, courseWithoutSummary, toReturn: course); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: GradesDetailsView(course: course)))); + child: FeatureDiscovery( + child: GradesDetailsView(course: courseWithoutSummary)))); await tester.pumpAndSettle(); expect(find.byType(SliverAppBar), findsOneWidget); @@ -160,11 +163,12 @@ void main() { (WidgetTester tester) async { setupFlutterToastMock(tester); CourseRepositoryMock.stubGetCourseSummary( - courseRepository as CourseRepositoryMock, courseWithoutSummary, + courseRepositoryMock, courseWithoutSummary, toReturn: course); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: GradesDetailsView(course: course)))); + child: FeatureDiscovery( + child: GradesDetailsView(course: courseWithoutSummary)))); await tester.pumpAndSettle(); final gesture = await tester @@ -181,7 +185,7 @@ void main() { (WidgetTester tester) async { setupFlutterToastMock(tester); CourseRepositoryMock.stubGetCourseSummary( - courseRepository as CourseRepositoryMock, courseWithoutSummary, + courseRepositoryMock, courseWithoutSummary, toReturn: courseWithoutSummary); await tester.pumpWidget(localizedWidget( @@ -197,8 +201,7 @@ void main() { (WidgetTester tester) async { setupFlutterToastMock(tester); CourseRepositoryMock.stubGetCourseSummary( - courseRepository as CourseRepositoryMock, - courseWithEvaluationNotCompleted, + courseRepositoryMock, courseWithEvaluationNotCompleted, toReturn: courseWithEvaluationNotCompleted); await tester.pumpWidget(localizedWidget( @@ -215,13 +218,14 @@ void main() { testWidgets("default view", (WidgetTester tester) async { setupFlutterToastMock(tester); CourseRepositoryMock.stubGetCourseSummary( - courseRepository as CourseRepositoryMock, courseWithoutSummary, + courseRepositoryMock, courseWithoutSummary, toReturn: course); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: GradesDetailsView(course: course)))); + child: FeatureDiscovery( + child: GradesDetailsView(course: courseWithoutSummary)))); await tester.pumpAndSettle(); await expectLater(find.byType(GradesDetailsView), @@ -231,15 +235,14 @@ void main() { testWidgets("if there is no grades available", (WidgetTester tester) async { setupFlutterToastMock(tester); - CourseRepositoryMock.stubGetCourseSummary( - courseRepository as CourseRepositoryMock, courseWithoutSummary, + CourseRepositoryMock.stubGetCourseSummary(courseRepositoryMock, course, toReturn: courseWithoutSummary); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); await tester.pumpWidget(localizedWidget( child: FeatureDiscovery(child: GradesDetailsView(course: course)))); - await tester.pumpAndSettle(); + await tester.pumpAndSettle(const Duration(seconds: 1)); await expectLater(find.byType(GradesDetailsView), matchesGoldenFile(goldenFilePath("gradesDetailsView_2"))); @@ -248,15 +251,14 @@ void main() { testWidgets("if in the evaluation period and evaluation not completed", (WidgetTester tester) async { setupFlutterToastMock(tester); - CourseRepositoryMock.stubGetCourseSummary( - courseRepository as CourseRepositoryMock, courseWithoutSummary, + CourseRepositoryMock.stubGetCourseSummary(courseRepositoryMock, course, toReturn: courseWithEvaluationNotCompleted); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); await tester.pumpWidget(localizedWidget( child: FeatureDiscovery(child: GradesDetailsView(course: course)))); - await tester.pumpAndSettle(); + await tester.pumpAndSettle(const Duration(seconds: 1)); await expectLater( find.byType(GradesDetailsView), diff --git a/test/ui/views/grades_view_test.dart b/test/ui/views/grades_view_test.dart index a1abdf0bd..d0f354b70 100644 --- a/test/ui/views/grades_view_test.dart +++ b/test/ui/views/grades_view_test.dart @@ -20,8 +20,8 @@ import '../../helpers.dart'; import '../../mock/managers/course_repository_mock.dart'; void main() { - CourseRepository courseRepository; - AppIntl intl; + late CourseRepositoryMock courseRepositoryMock; + late AppIntl intl; final Course courseSummer = Course( acronym: 'GEN101', @@ -65,7 +65,7 @@ void main() { setUp(() async { intl = await setupAppIntl(); setupNavigationServiceMock(); - courseRepository = setupCourseRepositoryMock(); + courseRepositoryMock = setupCourseRepositoryMock(); setupSettingsManagerMock(); setupAnalyticsServiceMock(); }); @@ -79,13 +79,9 @@ void main() { group("golden -", () { testWidgets("No grades available", (WidgetTester tester) async { // Mock the repository to have 0 courses available - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); @@ -101,16 +97,11 @@ void main() { testWidgets("Multiples sessions and grades loaded", (WidgetTester tester) async { // Mock the repository to have 0 courses available - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCourses(courseRepositoryMock, toReturn: courses); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - toReturn: courses, - fromCacheOnly: true); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, + toReturn: courses, fromCacheOnly: true); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); @@ -128,13 +119,9 @@ void main() { "Right message is displayed when there is no grades available", (WidgetTester tester) async { // Mock the repository to have 0 courses available - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); @@ -150,16 +137,11 @@ void main() { "Correct number of grade button and right session name are displayed", (WidgetTester tester) async { // Mock the repository to have 4 courses available - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCourses(courseRepositoryMock, toReturn: courses); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - toReturn: courses, - fromCacheOnly: true); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, + toReturn: courses, fromCacheOnly: true); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); diff --git a/test/ui/views/login_view_test.dart b/test/ui/views/login_view_test.dart index b7a7dda15..75e5c642a 100644 --- a/test/ui/views/login_view_test.dart +++ b/test/ui/views/login_view_test.dart @@ -15,11 +15,9 @@ import 'package:notredame/core/services/preferences_service.dart'; import 'package:notredame/ui/views/login_view.dart'; import 'package:notredame/ui/widgets/password_text_field.dart'; import '../../helpers.dart'; -import '../../mock/services/analytics_service_mock.dart'; -import '../../mock/services/launch_url_service_mock.dart'; void main() { - AppIntl intl; + late AppIntl intl; group('LoginView - ', () { setUp(() async { @@ -29,8 +27,8 @@ void main() { setupSettingsManagerMock(); setupFlutterSecureStorageMock(); setupPreferencesServiceMock(); - setupLaunchUrlServiceMock() as LaunchUrlServiceMock; - setupAnalyticsServiceMock() as AnalyticsServiceMock; + setupLaunchUrlServiceMock(); + setupAnalyticsServiceMock(); }); tearDown(() { diff --git a/test/ui/views/more_view_test.dart b/test/ui/views/more_view_test.dart index ba4fd5b48..46cfedbd8 100644 --- a/test/ui/views/more_view_test.dart +++ b/test/ui/views/more_view_test.dart @@ -13,31 +13,30 @@ import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/constants/preferences_flags.dart'; import 'package:notredame/core/constants/router_paths.dart'; -import 'package:notredame/core/services/navigation_service.dart'; -import 'package:notredame/core/services/remote_config_service.dart'; import 'package:notredame/ui/views/more_view.dart'; import '../../helpers.dart'; import '../../mock/managers/settings_manager_mock.dart'; import '../../mock/services/in_app_review_service_mock.dart'; +import '../../mock/services/navigation_service_mock.dart'; import '../../mock/services/remote_config_service_mock.dart'; void main() { - AppIntl intl; - NavigationService navigation; - RemoteConfigService remoteConfigService; - InAppReviewServiceMock inAppReviewServiceMock; - SettingsManagerMock settingsManagerMock; + late AppIntl intl; + late NavigationServiceMock navigationServiceMock; + late RemoteConfigServiceMock remoteConfigServiceMock; + late InAppReviewServiceMock inAppReviewServiceMock; + late SettingsManagerMock settingsManagerMock; group('MoreView - ', () { setUp(() async { intl = await setupAppIntl(); - navigation = setupNavigationServiceMock(); - remoteConfigService = setupRemoteConfigServiceMock(); + navigationServiceMock = setupNavigationServiceMock(); + remoteConfigServiceMock = setupRemoteConfigServiceMock(); setupCourseRepositoryMock(); setupPreferencesServiceMock(); setupUserRepositoryMock(); setupCacheManagerMock(); - settingsManagerMock = setupSettingsManagerMock() as SettingsManagerMock; + settingsManagerMock = setupSettingsManagerMock(); setupGithubApiMock(); setupNetworkingServiceMock(); setupAnalyticsServiceMock(); @@ -49,14 +48,14 @@ void main() { toReturn: true); RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock); + remoteConfigServiceMock); }); group('UI - ', () { testWidgets('has 1 listView and 8 listTiles when privacy policy disabled', (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); await tester.pumpWidget( localizedWidget(child: FeatureDiscovery(child: MoreView()))); @@ -85,7 +84,7 @@ void main() { group('navigation - ', () { testWidgets('about', (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); await tester.pumpWidget( localizedWidget(child: FeatureDiscovery(child: MoreView()))); @@ -98,12 +97,12 @@ void main() { // Rebuild the widget after the state has changed. await tester.pump(); - verify(navigation.pushNamed(RouterPaths.about)).called(1); + verify(navigationServiceMock.pushNamed(RouterPaths.about)).called(1); }); testWidgets('rate us is not available', (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); InAppReviewServiceMock.stubIsAvailable(inAppReviewServiceMock, toReturn: false); @@ -125,7 +124,7 @@ void main() { testWidgets('rate us is available', (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); InAppReviewServiceMock.stubIsAvailable(inAppReviewServiceMock); @@ -147,7 +146,7 @@ void main() { testWidgets('contributors', (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); await tester.pumpWidget( localizedWidget(child: FeatureDiscovery(child: MoreView()))); @@ -160,12 +159,13 @@ void main() { // Rebuild the widget after the state has changed. await tester.pump(); - verify(navigation.pushNamed(RouterPaths.contributors)).called(1); + verify(navigationServiceMock.pushNamed(RouterPaths.contributors)) + .called(1); }); testWidgets('licenses', (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); await tester.pumpWidget( localizedWidget(child: FeatureDiscovery(child: MoreView()))); @@ -185,7 +185,7 @@ void main() { testWidgets('need help', (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); await tester.pumpWidget( localizedWidget(child: FeatureDiscovery(child: MoreView()))); @@ -197,13 +197,14 @@ void main() { // Rebuild the widget after the state has changed. await tester.pump(); - verify(navigation.pushNamed(RouterPaths.faq, arguments: Colors.white)) + verify(navigationServiceMock.pushNamed(RouterPaths.faq, + arguments: Colors.white)) .called(1); }); testWidgets('settings', (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); await tester.pumpWidget( localizedWidget(child: FeatureDiscovery(child: MoreView()))); @@ -215,12 +216,13 @@ void main() { // Rebuild the widget after the state has changed. await tester.pump(); - verify(navigation.pushNamed(RouterPaths.settings)).called(1); + verify(navigationServiceMock.pushNamed(RouterPaths.settings)) + .called(1); }); testWidgets('logout', (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); await tester.pumpWidget( localizedWidget(child: FeatureDiscovery(child: MoreView()))); @@ -241,7 +243,7 @@ void main() { group("golden - ", () { testWidgets("default view", (WidgetTester tester) async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); diff --git a/test/ui/views/not_found_view_test.dart b/test/ui/views/not_found_view_test.dart index 49236e082..e48bb25d2 100644 --- a/test/ui/views/not_found_view_test.dart +++ b/test/ui/views/not_found_view_test.dart @@ -9,7 +9,9 @@ import 'package:flutter_test/flutter_test.dart'; // Project imports: import 'package:notredame/ui/views/not_found_view.dart'; +import 'package:rive/rive.dart'; import '../../helpers.dart'; +import '../../mock/services/rive_animation_service_mock.dart'; void main() { group('NotFoundView - ', () { @@ -17,7 +19,9 @@ void main() { setUp(() async { setupNavigationServiceMock(); setupAnalyticsServiceMock(); - setupRiveAnimationServiceMock(); + final riveAnimationMock = setupRiveAnimationServiceMock(); + RiveAnimationServiceMock.stubLoadRiveFile( + riveAnimationMock, 'dot_jumping', RuntimeArtboard()); }); tearDown(() {}); diff --git a/test/ui/views/profile_view_test.dart b/test/ui/views/profile_view_test.dart index b1be87ae7..fa98c8efd 100644 --- a/test/ui/views/profile_view_test.dart +++ b/test/ui/views/profile_view_test.dart @@ -10,7 +10,6 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; // Project imports: -import 'package:notredame/core/managers/user_repository.dart'; import 'package:notredame/core/services/networking_service.dart'; import 'package:notredame/ui/views/profile_view.dart'; import '../../helpers.dart'; @@ -18,8 +17,8 @@ import '../../mock/managers/user_repository_mock.dart'; import '../../mock/services/analytics_service_mock.dart'; void main() { - AppIntl intl; - UserRepository userRepository; + late AppIntl intl; + late UserRepositoryMock userRepositoryMock; final profileStudent = ProfileStudent( firstName: "John", @@ -45,19 +44,18 @@ void main() { setUp(() async { intl = await setupAppIntl(); setupNavigationServiceMock(); - userRepository = setupUserRepositoryMock(); + userRepositoryMock = setupUserRepositoryMock(); setupAnalyticsServiceMock(); - UserRepositoryMock.stubGetInfo(userRepository as UserRepositoryMock, + UserRepositoryMock.stubGetInfo(userRepositoryMock, toReturn: profileStudent); - UserRepositoryMock.stubProfileStudent( - userRepository as UserRepositoryMock, + UserRepositoryMock.stubProfileStudent(userRepositoryMock, toReturn: profileStudent); - UserRepositoryMock.stubGetPrograms(userRepository as UserRepositoryMock, + UserRepositoryMock.stubGetPrograms(userRepositoryMock, toReturn: programList); - UserRepositoryMock.stubPrograms(userRepository as UserRepositoryMock, + UserRepositoryMock.stubPrograms(userRepositoryMock, toReturn: programList); }); diff --git a/test/ui/views/quick_links_view_test.dart b/test/ui/views/quick_links_view_test.dart index 9e6a08fb5..31c3d112a 100644 --- a/test/ui/views/quick_links_view_test.dart +++ b/test/ui/views/quick_links_view_test.dart @@ -23,9 +23,9 @@ import '../../mock/services/internal_info_service_mock.dart'; import '../../mock/services/navigation_service_mock.dart'; void main() { - AppIntl intl; + late AppIntl intl; - QuickLinkRepository quickLinkRepository; + QuickLinkRepositoryMock quickLinkRepositoryMock; group('QuickLinksView - ', () { setUp(() async { @@ -35,13 +35,12 @@ void main() { setupInternalInfoServiceMock(); setupNetworkingServiceMock(); setupLaunchUrlServiceMock(); - quickLinkRepository = setupQuickLinkRepositoryMock(); - QuickLinkRepositoryMock.stubGetDefaultQuickLinks( - quickLinkRepository as QuickLinkRepositoryMock, + quickLinkRepositoryMock = setupQuickLinkRepositoryMock(); + QuickLinkRepositoryMock.stubGetDefaultQuickLinks(quickLinkRepositoryMock, toReturn: quickLinks(intl)); QuickLinkRepositoryMock.stubGetQuickLinkDataFromCacheException( - quickLinkRepository as QuickLinkRepositoryMock); + quickLinkRepositoryMock); }); tearDown(() { diff --git a/test/ui/views/schedule_view_test.dart b/test/ui/views/schedule_view_test.dart index 9805ce502..b19aec4ef 100644 --- a/test/ui/views/schedule_view_test.dart +++ b/test/ui/views/schedule_view_test.dart @@ -8,14 +8,10 @@ import 'package:flutter/material.dart'; import 'package:ets_api_clients/models.dart'; import 'package:feature_discovery/feature_discovery.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:intl/intl.dart'; import 'package:table_calendar/table_calendar.dart'; // Project imports: import 'package:notredame/core/constants/preferences_flags.dart'; -import 'package:notredame/core/managers/course_repository.dart'; -import 'package:notredame/core/managers/settings_manager.dart'; -import 'package:notredame/core/services/remote_config_service.dart'; import 'package:notredame/ui/views/schedule_view.dart'; import 'package:notredame/ui/widgets/schedule_settings.dart'; import '../../helpers.dart'; @@ -24,14 +20,14 @@ import '../../mock/managers/settings_manager_mock.dart'; import '../../mock/services/remote_config_service_mock.dart'; void main() { - SettingsManager settingsManager; - CourseRepository courseRepository; - RemoteConfigService remoteConfigService; + late SettingsManagerMock settingsManagerMock; + late CourseRepositoryMock courseRepositoryMock; + late RemoteConfigServiceMock remoteConfigServiceMock; // Some activities - CourseActivity activityYesterday; - CourseActivity activityToday; - CourseActivity activityTomorrow; + late CourseActivity activityYesterday; + late CourseActivity activityToday; + late CourseActivity activityTomorrow; // Some settings Map settings = { @@ -80,13 +76,13 @@ void main() { setUp(() async { setupNavigationServiceMock(); - settingsManager = setupSettingsManagerMock(); - courseRepository = setupCourseRepositoryMock(); - remoteConfigService = setupRemoteConfigServiceMock(); + settingsManagerMock = setupSettingsManagerMock(); + courseRepositoryMock = setupCourseRepositoryMock(); + remoteConfigServiceMock = setupRemoteConfigServiceMock(); setupNetworkingServiceMock(); setupAnalyticsServiceMock(); - SettingsManagerMock.stubLocale(settingsManager as SettingsManagerMock); + SettingsManagerMock.stubLocale(settingsManagerMock); settings = { PreferencesFlag.scheduleCalendarFormat: CalendarFormat.week, @@ -96,33 +92,25 @@ void main() { PreferencesFlag.scheduleListView: true, }; - CourseRepositoryMock.stubGetScheduleActivities( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock); RemoteConfigServiceMock.stubGetCalendarViewEnabled( - remoteConfigService as RemoteConfigServiceMock); + remoteConfigServiceMock); }); group("golden - ", () { + const tableCalendarKey = Key("TableCalendar"); testWidgets("default view (no events), showTodayButton enabled", (WidgetTester tester) async { tester.binding.window.physicalSizeTestValue = const Size(800, 1410); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); await tester.pumpWidget(localizedWidget( @@ -136,28 +124,20 @@ void main() { testWidgets("default view (no events), showTodayButton disabled", (WidgetTester tester) async { - SettingsManagerMock.stubGetBool(settingsManager as SettingsManagerMock, - PreferencesFlag.discoverySchedule); + SettingsManagerMock.stubGetBool( + settingsManagerMock, PreferencesFlag.discoverySchedule); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); settings[PreferencesFlag.scheduleShowTodayBtn] = false; - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); await tester.pumpWidget(localizedWidget( @@ -172,23 +152,15 @@ void main() { testWidgets("view with events, day with events selected", (WidgetTester tester) async { tester.binding.window.physicalSizeTestValue = const Size(800, 1410); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: [activityYesterday, activityToday, activityTomorrow]); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); await tester.pumpWidget(localizedWidget( @@ -206,23 +178,15 @@ void main() { (WidgetTester tester) async { tester.binding.window.physicalSizeTestValue = const Size(800, 1410); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: [activityYesterday, activityTomorrow]); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); await tester.pumpWidget(localizedWidget( @@ -238,23 +202,15 @@ void main() { (WidgetTester tester) async { tester.binding.window.physicalSizeTestValue = const Size(800, 1410); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: [activityYesterday, activityTomorrow]); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); final testingDate = DateTime(2020); @@ -266,10 +222,11 @@ void main() { child: ScheduleView(initialDay: testingDate))))); await tester.pumpAndSettle(const Duration(seconds: 1)); - expect(find.byType(TableCalendar, skipOffstage: false), findsOneWidget); + expect( + find.byKey(tableCalendarKey, skipOffstage: false), findsOneWidget); expect( find.descendant( - of: find.byType(TableCalendar, skipOffstage: false), + of: find.byKey(tableCalendarKey, skipOffstage: false), matching: find.text( "${testingDate.add(const Duration(days: 1)).day}", skipOffstage: false)), @@ -277,7 +234,7 @@ void main() { // Tap on the day after selected day await tester.tap(find.descendant( - of: find.byType(TableCalendar, skipOffstage: false), + of: find.byKey(tableCalendarKey, skipOffstage: false), matching: find.text( "${testingDate.add(const Duration(days: 1)).day}", skipOffstage: false))); @@ -291,82 +248,19 @@ void main() { }, skip: !Platform.isLinux); group("interactions - ", () { - testWidgets("tap on today button to return on today", - (WidgetTester tester) async { - tester.binding.window.physicalSizeTestValue = const Size(800, 1410); - - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, - toReturn: [activityToday]); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, - toReturn: settings); - - await tester.pumpWidget(localizedWidget( - child: FeatureDiscovery(child: const ScheduleView()))); - await tester.pumpAndSettle(const Duration(seconds: 1)); - - // DateFormat has to be after the pumpWidget to correctly load the locale - final dateFormat = DateFormat.MMMMEEEEd(); - final otherDay = DateTime.now().weekday == 7 - ? DateTime.now().subtract(const Duration(days: 1)) - : DateTime.now().add(const Duration(days: 1)); - - expect(find.text(dateFormat.format(DateTime.now())), findsOneWidget); - - // Tap on the day before today if sunday, otherwise tap on the next day - await tester.tap(find.descendant( - of: find.byType(TableCalendar), - matching: find.text("${otherDay.day}"))); - - // Reload view - await tester.pump(); - - expect(find.text(dateFormat.format(otherDay)), findsOneWidget, - reason: "Should be another day than today"); - - // Tap on "today" button. - await tester.tap(find.byIcon(Icons.today)); - await tester.pump(); - - expect(find.text(dateFormat.format(DateTime.now())), findsOneWidget, - reason: - "Should display today date because we tapped on today button."); - }); - testWidgets("tap on settings button to open the schedule settings", (WidgetTester tester) async { tester.binding.window.physicalSizeTestValue = const Size(800, 1410); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: [activityToday]); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); await tester.pumpWidget(localizedWidget( diff --git a/test/ui/views/security_view_test.dart b/test/ui/views/security_view_test.dart index 2afa64f61..4a19a130c 100644 --- a/test/ui/views/security_view_test.dart +++ b/test/ui/views/security_view_test.dart @@ -12,7 +12,7 @@ import 'package:notredame/ui/views/security_view.dart'; import '../../helpers.dart'; void main() { - AppIntl intl; + late AppIntl intl; group('SecurityView - ', () { setUp(() async { diff --git a/test/ui/views/settings_view_test.dart b/test/ui/views/settings_view_test.dart index f90bc9647..e9e8abe2e 100644 --- a/test/ui/views/settings_view_test.dart +++ b/test/ui/views/settings_view_test.dart @@ -16,7 +16,7 @@ import '../../helpers.dart'; import '../../mock/services/analytics_service_mock.dart'; void main() { - AppIntl intl; + late AppIntl intl; group('SettingsView - ', () { setUp(() async { diff --git a/test/ui/views/student_view_test.dart b/test/ui/views/student_view_test.dart index 1cc8c7421..8cb497f38 100644 --- a/test/ui/views/student_view_test.dart +++ b/test/ui/views/student_view_test.dart @@ -19,23 +19,19 @@ import '../../mock/managers/course_repository_mock.dart'; import '../../mock/services/analytics_service_mock.dart'; void main() { - CourseRepository courseRepository; + CourseRepositoryMock courseRepositoryMock; group('StudentView - ', () { setUp(() async { setupNavigationServiceMock(); setupNetworkingServiceMock(); - courseRepository = setupCourseRepositoryMock(); + courseRepositoryMock = setupCourseRepositoryMock(); setupSettingsManagerMock(); setupAnalyticsServiceMock(); - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); }); diff --git a/test/ui/widgets/bottom_bar_test.dart b/test/ui/widgets/bottom_bar_test.dart index b3aa81f7d..dc069a22e 100644 --- a/test/ui/widgets/bottom_bar_test.dart +++ b/test/ui/widgets/bottom_bar_test.dart @@ -13,13 +13,14 @@ import 'package:notredame/core/services/networking_service.dart'; import 'package:notredame/ui/widgets/bottom_bar.dart'; import '../../helpers.dart'; import '../../mock/services/analytics_service_mock.dart'; +import '../../mock/services/navigation_service_mock.dart'; -NavigationService navigationService; +late NavigationServiceMock navigationServiceMock; void main() { group('BottomBar - ', () { setUp(() { - navigationService = setupNavigationServiceMock(); + navigationServiceMock = setupNavigationServiceMock(); setupNetworkingServiceMock(); setupAnalyticsServiceMock(); }); @@ -57,7 +58,7 @@ void main() { await tester.tap(find.byIcon(Icons.school)); await tester.tap(find.byIcon(Icons.school)); - verify(navigationService.pushNamedAndRemoveUntil(RouterPaths.student)) + verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.student)) .called(1); }); @@ -70,8 +71,8 @@ void main() { await tester.tap(find.byIcon(Icons.schedule)); await tester.tap(find.byIcon(Icons.dashboard)); - verify( - navigationService.pushNamedAndRemoveUntil(RouterPaths.dashboard)); + verify(navigationServiceMock + .pushNamedAndRemoveUntil(RouterPaths.dashboard)); }); testWidgets('schedule', (WidgetTester tester) async { @@ -81,7 +82,8 @@ void main() { await tester.tap(find.byIcon(Icons.schedule)); - verify(navigationService.pushNamedAndRemoveUntil(RouterPaths.schedule)); + verify(navigationServiceMock + .pushNamedAndRemoveUntil(RouterPaths.schedule)); }); testWidgets('student', (WidgetTester tester) async { @@ -91,7 +93,8 @@ void main() { await tester.tap(find.byIcon(Icons.school)); - verify(navigationService.pushNamedAndRemoveUntil(RouterPaths.student)); + verify( + navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.student)); }); testWidgets('ets', (WidgetTester tester) async { @@ -101,7 +104,7 @@ void main() { await tester.tap(find.byIcon(Icons.account_balance)); - verify(navigationService.pushNamedAndRemoveUntil(RouterPaths.ets)); + verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.ets)); }); testWidgets('more', (WidgetTester tester) async { @@ -111,7 +114,7 @@ void main() { await tester.tap(find.byIcon(Icons.dehaze)); - verify(navigationService.pushNamedAndRemoveUntil(RouterPaths.more)); + verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.more)); }); }); }); diff --git a/test/ui/widgets/goldenFiles/scheduleSettingsView_1.png b/test/ui/widgets/goldenFiles/scheduleSettingsView_1.png index f4c234b6c..3aae6f547 100644 Binary files a/test/ui/widgets/goldenFiles/scheduleSettingsView_1.png and b/test/ui/widgets/goldenFiles/scheduleSettingsView_1.png differ diff --git a/test/ui/widgets/grade_button_test.dart b/test/ui/widgets/grade_button_test.dart index 8ba846668..e6e1f3deb 100644 --- a/test/ui/widgets/grade_button_test.dart +++ b/test/ui/widgets/grade_button_test.dart @@ -12,11 +12,12 @@ import 'package:notredame/core/services/navigation_service.dart'; import 'package:notredame/ui/widgets/grade_button.dart'; import '../../helpers.dart'; import '../../mock/managers/settings_manager_mock.dart'; +import '../../mock/services/navigation_service_mock.dart'; void main() { - AppIntl intl; - NavigationService navigationService; - SettingsManager settingsManager; + late AppIntl intl; + late NavigationServiceMock navigationServiceMock; + late SettingsManagerMock settingsManagerMock; final Course courseWithGrade = Course( acronym: 'GEN101', @@ -54,10 +55,10 @@ void main() { group("GradeButton -", () { setUp(() async { - settingsManager = setupSettingsManagerMock(); + settingsManagerMock = setupSettingsManagerMock(); intl = await setupAppIntl(); setupNavigationServiceMock(); - navigationService = setupNavigationServiceMock(); + navigationServiceMock = setupNavigationServiceMock(); }); tearDown(() { @@ -69,33 +70,31 @@ void main() { testWidgets("Display acronym of the course and the current grade", (WidgetTester tester) async { SettingsManagerMock.stubGetString( - settingsManager as SettingsManagerMock, - PreferencesFlag.discoveryStudentGrade, + settingsManagerMock, PreferencesFlag.discoveryStudentGrade, toReturn: 'true'); - await tester.pumpWidget(localizedWidget( - child: GradeButton(courseWithGrade, showDiscovery: false))); + await tester + .pumpWidget(localizedWidget(child: GradeButton(courseWithGrade))); await tester.pumpAndSettle(); expect(find.text(courseWithGrade.acronym), findsOneWidget); - expect(find.text(courseWithGrade.grade), findsOneWidget); + expect(find.text(courseWithGrade.grade!), findsOneWidget); }); testWidgets("Grade not available and summary is loaded.", (WidgetTester tester) async { SettingsManagerMock.stubGetString( - settingsManager as SettingsManagerMock, - PreferencesFlag.discoveryStudentGrade, + settingsManagerMock, PreferencesFlag.discoveryStudentGrade, toReturn: 'true'); - await tester.pumpWidget(localizedWidget( - child: GradeButton(courseWithSummary, showDiscovery: false))); + await tester + .pumpWidget(localizedWidget(child: GradeButton(courseWithSummary))); await tester.pumpAndSettle(); expect(find.text(courseWithGrade.acronym), findsOneWidget); expect( find.text(intl.grades_grade_in_percentage( - courseWithSummary.summary.currentMarkInPercent.round())), + courseWithSummary.summary!.currentMarkInPercent.round())), findsOneWidget, reason: 'There is no grade available and the course summary exists so the ' @@ -105,12 +104,11 @@ void main() { testWidgets("Grade and summary not available.", (WidgetTester tester) async { SettingsManagerMock.stubGetString( - settingsManager as SettingsManagerMock, - PreferencesFlag.discoveryStudentGrade, + settingsManagerMock, PreferencesFlag.discoveryStudentGrade, toReturn: 'true'); - await tester.pumpWidget(localizedWidget( - child: GradeButton(gradesNotAvailable, showDiscovery: false))); + await tester.pumpWidget( + localizedWidget(child: GradeButton(gradesNotAvailable))); await tester.pumpAndSettle(); expect(find.text(courseWithGrade.acronym), findsOneWidget); @@ -124,29 +122,29 @@ void main() { group('Interactions - ', () { testWidgets('Grade button redirects to grades view when tapped ', (WidgetTester tester) async { - SettingsManagerMock.stubGetBool(settingsManager as SettingsManagerMock, - PreferencesFlag.discoveryStudentGrade, + SettingsManagerMock.stubGetBool( + settingsManagerMock, PreferencesFlag.discoveryStudentGrade, toReturn: true); - await tester.pumpWidget(localizedWidget( - child: GradeButton(courseWithGrade, showDiscovery: false))); + await tester + .pumpWidget(localizedWidget(child: GradeButton(courseWithGrade))); await tester.pumpAndSettle(); await tester.tap(find.text(courseWithGrade.acronym)); - verify(navigationService.pushNamed(RouterPaths.gradeDetails, + verify(navigationServiceMock.pushNamed(RouterPaths.gradeDetails, arguments: courseWithGrade)); }); testWidgets( 'Grade button does not redirect to grades view if the grades discovery did not already launch', (WidgetTester tester) async { - await tester.pumpWidget(localizedWidget( - child: GradeButton(courseWithGrade, showDiscovery: false))); + await tester + .pumpWidget(localizedWidget(child: GradeButton(courseWithGrade))); await tester.pumpAndSettle(); await tester.tap(find.text(courseWithGrade.acronym)); - verifyNever(navigationService.pushNamed(RouterPaths.gradeDetails, + verifyNever(navigationServiceMock.pushNamed(RouterPaths.gradeDetails, arguments: courseWithGrade)); }); }); diff --git a/test/ui/widgets/grade_circular_progress_test.dart b/test/ui/widgets/grade_circular_progress_test.dart index 2e5f4ff49..94e3322bc 100644 --- a/test/ui/widgets/grade_circular_progress_test.dart +++ b/test/ui/widgets/grade_circular_progress_test.dart @@ -8,7 +8,7 @@ import 'package:notredame/ui/widgets/grade_circular_progress.dart'; import '../../helpers.dart'; void main() { - AppIntl intl; + late AppIntl intl; group("GradeCircularProgress -", () { setUp(() async { diff --git a/test/ui/widgets/grade_evaluation_tile_test.dart b/test/ui/widgets/grade_evaluation_tile_test.dart index 400237fbb..91e56317e 100644 --- a/test/ui/widgets/grade_evaluation_tile_test.dart +++ b/test/ui/widgets/grade_evaluation_tile_test.dart @@ -13,7 +13,7 @@ import 'package:notredame/ui/widgets/grade_evaluation_tile.dart'; import '../../helpers.dart'; void main() { - AppIntl intl; + late AppIntl intl; final CourseSummary courseSummary = CourseSummary( currentMark: 5, @@ -28,7 +28,7 @@ void main() { courseGroup: "02", title: "Laboratoire 1", weight: 10, - teacherMessage: null, + teacherMessage: '', ignore: false, mark: 24, correctedEvaluationOutOf: "35", @@ -43,7 +43,7 @@ void main() { courseGroup: "02", title: "Laboratoire 2", weight: 15, - teacherMessage: null, + teacherMessage: '', ignore: false, correctedEvaluationOutOf: "30", published: true, @@ -65,8 +65,7 @@ void main() { await tester.pumpWidget(localizedWidget( child: FeatureDiscovery( - child: GradeEvaluationTile(evaluation, - completed: true, isFirstEvaluation: false)))); + child: GradeEvaluationTile(evaluation, completed: true)))); await tester.pumpAndSettle(); final circularPercentIndicator = find.byType(GradeCircularProgress); @@ -85,8 +84,7 @@ void main() { final widget = localizedWidget( child: FeatureDiscovery( - child: GradeEvaluationTile(evaluation, - completed: true, isFirstEvaluation: false))); + child: GradeEvaluationTile(evaluation, completed: true))); await tester.pumpWidget(widget); diff --git a/test/ui/widgets/grade_not_available_test.dart b/test/ui/widgets/grade_not_available_test.dart index d24ed5e38..1abd31268 100644 --- a/test/ui/widgets/grade_not_available_test.dart +++ b/test/ui/widgets/grade_not_available_test.dart @@ -10,7 +10,7 @@ import 'package:notredame/ui/widgets/grade_not_available.dart'; import '../../helpers.dart'; void main() { - AppIntl intl; + late AppIntl intl; group("GradeNotAvailableTest -", () { setUp(() async { diff --git a/test/ui/widgets/password_text_field_test.dart b/test/ui/widgets/password_text_field_test.dart index 1378b228f..b6b937e8b 100644 --- a/test/ui/widgets/password_text_field_test.dart +++ b/test/ui/widgets/password_text_field_test.dart @@ -10,17 +10,23 @@ import 'package:notredame/ui/widgets/password_text_field.dart'; import '../../helpers.dart'; void main() { - AppIntl intl; + late AppIntl intl; + late PasswordFormField passwordFormField; group('PasswordFormField - ', () { setUpAll(() async { intl = await setupAppIntl(); + passwordFormField = PasswordFormField( + validator: (value) { + return null; + }, + onEditionComplete: () {}, + ); }); testWidgets('has a label, the visibility icon and obscure text', (WidgetTester tester) async { - await tester - .pumpWidget(localizedWidget(child: const PasswordFormField())); + await tester.pumpWidget(localizedWidget(child: passwordFormField)); await tester.pumpAndSettle(); final icon = find.byIcon(Icons.visibility); @@ -34,8 +40,7 @@ void main() { testWidgets( 'toggling the visibility button should disable the obscureText property', (WidgetTester tester) async { - await tester - .pumpWidget(localizedWidget(child: const PasswordFormField())); + await tester.pumpWidget(localizedWidget(child: passwordFormField)); await tester.pumpAndSettle(); await tester.tap(find.byIcon(Icons.visibility)); @@ -53,8 +58,7 @@ void main() { testWidgets( 'toggling the visibility button two times should enable the obscureText property', (WidgetTester tester) async { - await tester - .pumpWidget(localizedWidget(child: const PasswordFormField())); + await tester.pumpWidget(localizedWidget(child: passwordFormField)); await tester.pumpAndSettle(); await tester.tap(find.byIcon(Icons.visibility)); diff --git a/test/ui/widgets/schedule_settings_test.dart b/test/ui/widgets/schedule_settings_test.dart index 937294fa3..2d855f028 100644 --- a/test/ui/widgets/schedule_settings_test.dart +++ b/test/ui/widgets/schedule_settings_test.dart @@ -14,7 +14,6 @@ import 'package:table_calendar/table_calendar.dart'; // Project imports: import 'package:notredame/core/constants/preferences_flags.dart'; -import 'package:notredame/core/managers/settings_manager.dart'; import 'package:notredame/ui/widgets/schedule_settings.dart'; import '../../helpers.dart'; import '../../mock/managers/course_repository_mock.dart'; @@ -22,17 +21,18 @@ import '../../mock/managers/settings_manager_mock.dart'; import '../../mock/services/remote_config_service_mock.dart'; void main() { - SettingsManager settingsManager; - RemoteConfigServiceMock remoteConfigService; - CourseRepositoryMock courseRepositoryMock; - AppIntl intl; + late SettingsManagerMock settingsManagerMock; + late RemoteConfigServiceMock remoteConfigServiceMock; + late CourseRepositoryMock courseRepositoryMock; + late AppIntl intl; // Some settings final Map settings = { PreferencesFlag.scheduleCalendarFormat: CalendarFormat.week, PreferencesFlag.scheduleStartWeekday: StartingDayOfWeek.monday, PreferencesFlag.scheduleShowTodayBtn: true, - PreferencesFlag.scheduleListView: true + PreferencesFlag.scheduleListView: true, + PreferencesFlag.scheduleShowWeekEvents: true }; final List classOneWithLaboratoryABscheduleActivities = [ @@ -76,21 +76,19 @@ void main() { group("ScheduleSettings - ", () { setUp(() async { - settingsManager = setupSettingsManagerMock(); - courseRepositoryMock = - setupCourseRepositoryMock() as CourseRepositoryMock; - remoteConfigService = - setupRemoteConfigServiceMock() as RemoteConfigServiceMock; + settingsManagerMock = setupSettingsManagerMock(); + courseRepositoryMock = setupCourseRepositoryMock(); + remoteConfigServiceMock = setupRemoteConfigServiceMock(); intl = await setupAppIntl(); CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock); - RemoteConfigServiceMock.stubGetCalendarViewEnabled(remoteConfigService); + RemoteConfigServiceMock.stubGetCalendarViewEnabled( + remoteConfigServiceMock); }); group("ui - ", () { testWidgets("With handle", (WidgetTester tester) async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); await tester .pumpWidget(localizedWidget(child: const ScheduleSettings())); @@ -100,7 +98,7 @@ void main() { expect( find.byWidgetPredicate((widget) => widget is Container && - (widget.decoration as BoxDecoration).color == Colors.grey), + (widget.decoration! as BoxDecoration).color == Colors.grey), findsOneWidget, reason: "The handle should be grey"); @@ -176,8 +174,7 @@ void main() { }); testWidgets("Without handle", (WidgetTester tester) async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); await tester.pumpWidget( @@ -187,7 +184,7 @@ void main() { expect( find.byWidgetPredicate((widget) => widget is Container && - (widget.decoration as BoxDecoration).color == Colors.grey), + (widget.decoration! as BoxDecoration).color == Colors.grey), findsNothing, reason: "There should not have a handle."); @@ -266,8 +263,7 @@ void main() { testWidgets( "Should display activity selection section when a course has activities", (WidgetTester tester) async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock, toReturn: classOneWithLaboratoryABscheduleActivities); @@ -303,16 +299,13 @@ void main() { testWidgets( "When a settings laboratory is already selected, verify that it is in fact preselected", (WidgetTester tester) async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock, toReturn: classOneWithLaboratoryABscheduleActivities); // preselect the laboB - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN101", + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN101", toReturn: ActivityCode.labGroupB); const scheduleSettings = ScheduleSettings(showHandle: false); @@ -340,8 +333,7 @@ void main() { testWidgets( "if there is only a laboA (no labo b) the options should not appear on screen", (WidgetTester tester) async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); final courseWithOnlyLabA = List.from( classOneWithLaboratoryABscheduleActivities); @@ -368,12 +360,10 @@ void main() { group("interactions - ", () { testWidgets("onChange calendarFormat", (WidgetTester tester) async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleCalendarFormat); + settingsManagerMock, PreferencesFlag.scheduleCalendarFormat); await tester.pumpWidget( localizedWidget(child: const ScheduleSettings(showHandle: false))); @@ -383,7 +373,7 @@ void main() { ListTile, intl.schedule_settings_calendar_format_2_weeks)); await tester.pump(); - await untilCalled(settingsManager.setString( + await untilCalled(settingsManagerMock.setString( PreferencesFlag.scheduleCalendarFormat, any)); final formatTile = find.widgetWithText( @@ -397,11 +387,10 @@ void main() { }); testWidgets("onChange showTodayBtn", (WidgetTester tester) async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); - SettingsManagerMock.stubSetBool(settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleShowTodayBtn); + SettingsManagerMock.stubSetBool( + settingsManagerMock, PreferencesFlag.scheduleShowTodayBtn); await tester.pumpWidget( localizedWidget(child: const ScheduleSettings(showHandle: false))); @@ -415,12 +404,12 @@ void main() { // Currently the await tester.tap on a switch in a tile isn't working. Workaround: (find.byType(Switch, skipOffstage: false).evaluate().single.widget as Switch) - .onChanged(false); + .onChanged!(false); await tester.pumpAndSettle(); - await untilCalled( - settingsManager.setBool(PreferencesFlag.scheduleShowTodayBtn, any)); + await untilCalled(settingsManagerMock.setBool( + PreferencesFlag.scheduleShowTodayBtn, any)); expect( tester.widget(find.descendant( @@ -436,8 +425,7 @@ void main() { testWidgets( "Should display activity selection section when a course has activities", (WidgetTester tester) async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock, toReturn: classOneWithLaboratoryABscheduleActivities); diff --git a/test/ui/widgets/student_program_test.dart b/test/ui/widgets/student_program_test.dart index 9e158962e..9143a7f2d 100644 --- a/test/ui/widgets/student_program_test.dart +++ b/test/ui/widgets/student_program_test.dart @@ -22,7 +22,7 @@ final _program = Program( failedCourses: '0'); void main() { - AppIntl intl; + late AppIntl intl; group('Student program - ', () { setUp(() async { diff --git a/test/ui/widgets/web_link_card_test.dart b/test/ui/widgets/web_link_card_test.dart index fe7113a01..a2db92117 100644 --- a/test/ui/widgets/web_link_card_test.dart +++ b/test/ui/widgets/web_link_card_test.dart @@ -9,30 +9,31 @@ import 'package:mockito/mockito.dart'; import 'package:notredame/core/models/quick_link.dart'; import 'package:notredame/core/services/analytics_service.dart'; import 'package:notredame/core/services/internal_info_service.dart'; -import 'package:notredame/core/services/launch_url_service.dart'; import 'package:notredame/core/services/navigation_service.dart'; import 'package:notredame/ui/widgets/web_link_card.dart'; import '../../helpers.dart'; +import '../../mock/services/analytics_service_mock.dart'; +import '../../mock/services/launch_url_service_mock.dart'; final _quickLink = QuickLink( id: 1, image: const Icon(Icons.ac_unit), name: 'test', link: 'testlink'); void main() { - AnalyticsService analyticsService; - LaunchUrlService launchUrlService; + late AnalyticsServiceMock analyticsServiceMock; + late LaunchUrlServiceMock launchUrlServiceMock; group('WebLinkCard - ', () { setUp(() { - analyticsService = setupAnalyticsServiceMock(); - launchUrlService = setupLaunchUrlServiceMock(); + analyticsServiceMock = setupAnalyticsServiceMock(); + launchUrlServiceMock = setupLaunchUrlServiceMock(); setupInternalInfoServiceMock(); setupNavigationServiceMock(); }); tearDown(() { unregister(); - clearInteractions(analyticsService); - clearInteractions(launchUrlService); + clearInteractions(analyticsServiceMock); + clearInteractions(launchUrlServiceMock); unregister(); unregister(); }); diff --git a/test/viewmodels/choose_language_viewmodel_test.dart b/test/viewmodels/choose_language_viewmodel_test.dart index 8873d58a3..1e93a2e52 100644 --- a/test/viewmodels/choose_language_viewmodel_test.dart +++ b/test/viewmodels/choose_language_viewmodel_test.dart @@ -11,18 +11,19 @@ import 'package:notredame/core/services/navigation_service.dart'; import 'package:notredame/core/viewmodels/choose_language_viewmodel.dart'; import '../helpers.dart'; import '../mock/managers/settings_manager_mock.dart'; +import '../mock/services/navigation_service_mock.dart'; -ChooseLanguageViewModel viewModel; +late ChooseLanguageViewModel viewModel; void main() { - NavigationService navigationService; - SettingsManager settingsManager; + late NavigationServiceMock navigationServiceMock; + late SettingsManagerMock settingsManagerMock; group("ChooseLanguageViewModel - ", () { setUp(() async { // Setting up mocks - navigationService = setupNavigationServiceMock(); - settingsManager = setupSettingsManagerMock(); + navigationServiceMock = setupNavigationServiceMock(); + settingsManagerMock = setupSettingsManagerMock(); final AppIntl intl = await setupAppIntl(); viewModel = ChooseLanguageViewModel(intl: intl); @@ -36,31 +37,33 @@ void main() { group("changeLanguage - ", () { test('can set language english', () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, PreferencesFlag.theme); + settingsManagerMock, PreferencesFlag.theme); viewModel.changeLanguage(0); - verify(settingsManager + verify(settingsManagerMock .setLocale(AppIntl.supportedLocales.first.languageCode)); - verify(navigationService.pop()); - verify(navigationService.pushNamedAndRemoveUntil(RouterPaths.login)); + verify(navigationServiceMock.pop()); + verify( + navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.login)); }); test('can set language français', () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, PreferencesFlag.theme); + settingsManagerMock, PreferencesFlag.theme); viewModel.changeLanguage(1); - verify(settingsManager + verify(settingsManagerMock .setLocale(AppIntl.supportedLocales.last.languageCode)); - verify(navigationService.pop()); - verify(navigationService.pushNamedAndRemoveUntil(RouterPaths.login)); + verify(navigationServiceMock.pop()); + verify( + navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.login)); }); test('throws an error when index does not exist', () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, PreferencesFlag.theme); + settingsManagerMock, PreferencesFlag.theme); expect(() => viewModel.changeLanguage(-1), throwsException, reason: "No valid language for the index -1 passed in parameters"); @@ -73,16 +76,6 @@ void main() { expect(['English', 'Français'], languages); }); - - test('returns the languages with an exception', () async { - const AppIntl intlNull = null; - final ChooseLanguageViewModel viewModelWithInvalidIntl = - ChooseLanguageViewModel(intl: intlNull); - - expect( - () => viewModelWithInvalidIntl.languages, throwsNoSuchMethodError, - reason: "The getter 'settings_english' was called on null"); - }); }); }); } diff --git a/test/viewmodels/dashboard_viewmodel_test.dart b/test/viewmodels/dashboard_viewmodel_test.dart index 2154d8c57..21932be10 100644 --- a/test/viewmodels/dashboard_viewmodel_test.dart +++ b/test/viewmodels/dashboard_viewmodel_test.dart @@ -6,28 +6,26 @@ import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/constants/preferences_flags.dart'; import 'package:notredame/core/constants/progress_bar_text_options.dart'; -import 'package:notredame/core/managers/course_repository.dart'; import 'package:notredame/core/managers/settings_manager.dart'; -import 'package:notredame/core/services/analytics_service.dart'; -import 'package:notredame/core/services/preferences_service.dart'; -import 'package:notredame/core/services/remote_config_service.dart'; import 'package:notredame/core/viewmodels/dashboard_viewmodel.dart'; import '../helpers.dart'; import '../mock/managers/course_repository_mock.dart'; import '../mock/managers/settings_manager_mock.dart'; +import '../mock/services/analytics_service_mock.dart'; import '../mock/services/in_app_review_service_mock.dart'; import '../mock/services/preferences_service_mock.dart'; import '../mock/services/remote_config_service_mock.dart'; void main() { - PreferencesService preferenceService; - SettingsManager settingsManager; - DashboardViewModel viewModel; - CourseRepository courseRepository; - RemoteConfigService remoteConfigService; - PreferencesServiceMock preferencesServiceMock; - InAppReviewServiceMock inAppReviewServiceMock; - AnalyticsService analyticsService; + late PreferencesServiceMock preferenceServiceMock; + late SettingsManagerMock settingsManagerMock; + late CourseRepositoryMock courseRepositoryMock; + late RemoteConfigServiceMock remoteConfigServiceMock; + late PreferencesServiceMock preferencesServiceMock; + late InAppReviewServiceMock inAppReviewServiceMock; + late AnalyticsServiceMock analyticsServiceMock; + + late DashboardViewModel viewModel; final gen101 = CourseActivity( courseGroup: "GEN101", @@ -195,39 +193,28 @@ void main() { group("DashboardViewModel - ", () { setUp(() async { // Setting up mocks - courseRepository = setupCourseRepositoryMock(); - remoteConfigService = setupRemoteConfigServiceMock(); - settingsManager = setupSettingsManagerMock(); - preferenceService = setupPreferencesServiceMock(); - analyticsService = setupAnalyticsServiceMock(); + courseRepositoryMock = setupCourseRepositoryMock(); + remoteConfigServiceMock = setupRemoteConfigServiceMock(); + settingsManagerMock = setupSettingsManagerMock(); + preferenceServiceMock = setupPreferencesServiceMock(); + analyticsServiceMock = setupAnalyticsServiceMock(); setupAppWidgetServiceMock(); - courseRepository = setupCourseRepositoryMock(); - preferencesServiceMock = - setupPreferencesServiceMock() as PreferencesServiceMock; + preferencesServiceMock = setupPreferencesServiceMock(); viewModel = DashboardViewModel(intl: await setupAppIntl()); - CourseRepositoryMock.stubGetSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - SettingsManagerMock.stubDateTimeNow( - settingsManager as SettingsManagerMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: DateTime(2020)); - RemoteConfigServiceMock.stubGetBroadcastEnabled( - remoteConfigService as RemoteConfigServiceMock); - RemoteConfigServiceMock.stubGetBroadcastEn( - remoteConfigService as RemoteConfigServiceMock, + RemoteConfigServiceMock.stubGetBroadcastEnabled(remoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastEn(remoteConfigServiceMock, toReturn: ""); inAppReviewServiceMock = @@ -241,137 +228,113 @@ void main() { group('futureToRunGrades -', () { test('first load from cache than call SignetsAPI to get the courses', () async { - CourseRepositoryMock.stubSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubGetSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - toReturn: courses, - fromCacheOnly: true); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, + toReturn: courses, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, toReturn: courses); expect(await viewModel.futureToRunGrades(), courses); - await untilCalled(courseRepository.sessions); - await untilCalled(courseRepository.sessions); + await untilCalled(courseRepositoryMock.sessions); + await untilCalled(courseRepositoryMock.sessions); expect(viewModel.courses, courses); verifyInOrder([ - courseRepository.sessions, - courseRepository.sessions, - courseRepository.activeSessions, - courseRepository.activeSessions, - courseRepository.getCourses(fromCacheOnly: true), - courseRepository.getCourses(), + courseRepositoryMock.sessions, + courseRepositoryMock.sessions, + courseRepositoryMock.activeSessions, + courseRepositoryMock.activeSessions, + courseRepositoryMock.getCourses(fromCacheOnly: true), + courseRepositoryMock.getCourses(), ]); - verifyNoMoreInteractions(courseRepository); + verifyNoMoreInteractions(courseRepositoryMock); }); test('Signets throw an error while trying to get courses', () async { setupFlutterToastMock(); - CourseRepositoryMock.stubSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubGetSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - toReturn: courses, - fromCacheOnly: true); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, + toReturn: courses, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesException( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); + CourseRepositoryMock.stubGetCoursesException(courseRepositoryMock); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, toReturn: courses); expect(await viewModel.futureToRunGrades(), courses, reason: "Even if SignetsAPI call fails, should return the cache contents"); - await untilCalled(courseRepository.sessions); - await untilCalled(courseRepository.sessions); + await untilCalled(courseRepositoryMock.sessions); + await untilCalled(courseRepositoryMock.sessions); expect(viewModel.courses, courses); verifyInOrder([ - courseRepository.sessions, - courseRepository.sessions, - courseRepository.activeSessions, - courseRepository.activeSessions, - courseRepository.getCourses(fromCacheOnly: true), - courseRepository.getCourses(), + courseRepositoryMock.sessions, + courseRepositoryMock.sessions, + courseRepositoryMock.activeSessions, + courseRepositoryMock.activeSessions, + courseRepositoryMock.getCourses(fromCacheOnly: true), + courseRepositoryMock.getCourses(), ]); - verifyNoMoreInteractions(courseRepository); + verifyNoMoreInteractions(courseRepositoryMock); }); test('There is no session active', () async { - CourseRepositoryMock.stubSessions( - courseRepository as CourseRepositoryMock, - toReturn: []); - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubSessions(courseRepositoryMock, toReturn: []); + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock, toReturn: []); expect(await viewModel.futureToRunGrades(), [], reason: "Should return empty if there is no session active."); - await untilCalled(courseRepository.sessions); + await untilCalled(courseRepositoryMock.sessions); expect(viewModel.courses, []); verifyInOrder([ - courseRepository.sessions, - courseRepository.sessions, - courseRepository.getSessions(), - courseRepository.activeSessions, + courseRepositoryMock.sessions, + courseRepositoryMock.sessions, + courseRepositoryMock.getSessions(), + courseRepositoryMock.activeSessions, ]); - verifyNoMoreInteractions(courseRepository); + verifyNoMoreInteractions(courseRepositoryMock); }); }); group("futureToRun - ", () { test("The initial cards are correctly loaded", () async { setupFlutterToastMock(); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesException( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesException(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetSessions( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetSessions(courseRepositoryMock); + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock, toReturn: [session]); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); await viewModel.futureToRun(); @@ -383,175 +346,145 @@ void main() { PreferencesFlag.progressBarCard ]); - verify(settingsManager.getDashboard()).called(1); - verify(settingsManager.getString(PreferencesFlag.progressBarText)) + verify(settingsManagerMock.getDashboard()).called(1); + verify(settingsManagerMock.getString(PreferencesFlag.progressBarText)) .called(1); - verify(settingsManager.dateTimeNow).called(2); - verifyNoMoreInteractions(settingsManager); + verify(settingsManagerMock.dateTimeNow).called(2); + verifyNoMoreInteractions(settingsManagerMock); }); test("build the list todays activities sorted by time", () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, toReturn: courses); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); final now = DateTime.now(); - SettingsManagerMock.stubDateTimeNow( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: DateTime(now.year, now.month, now.day, 8)); await viewModel.futureToRun(); - await untilCalled(courseRepository.getCoursesActivities()); + await untilCalled(courseRepositoryMock.getCoursesActivities()); expect(viewModel.todayDateEvents, todayActivities); - verify(courseRepository.getCoursesActivities()).called(1); + verify(courseRepositoryMock.getCoursesActivities()).called(1); - verify(courseRepository.getCoursesActivities(fromCacheOnly: true)) + verify(courseRepositoryMock.getCoursesActivities(fromCacheOnly: true)) .called(1); - verify(courseRepository.coursesActivities).called(1); + verify(courseRepositoryMock.coursesActivities).called(2); - verify(settingsManager.getDashboard()).called(1); + verify(settingsManagerMock.getDashboard()).called(1); }); test( "build the list todays activities (doesnt remove activity when pending completion)", () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, toReturn: courses); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); final now = DateTime.now(); - SettingsManagerMock.stubDateTimeNow( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: DateTime(now.year, now.month, now.day, 11, 59)); await viewModel.futureToRun(); - await untilCalled(courseRepository.getCoursesActivities()); + await untilCalled(courseRepositoryMock.getCoursesActivities()); expect(viewModel.todayDateEvents, todayActivities); - verify(courseRepository.getCoursesActivities()).called(1); + verify(courseRepositoryMock.getCoursesActivities()).called(1); - verify(courseRepository.getCoursesActivities(fromCacheOnly: true)) + verify(courseRepositoryMock.getCoursesActivities(fromCacheOnly: true)) .called(1); - verify(courseRepository.coursesActivities).called(1); + verify(courseRepositoryMock.coursesActivities).called(2); - verify(settingsManager.getDashboard()).called(1); + verify(settingsManagerMock.getDashboard()).called(1); }); test("build the list todays activities (remove activity when finished)", () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, toReturn: courses); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); final now = DateTime.now(); - SettingsManagerMock.stubDateTimeNow( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: DateTime(now.year, now.month, now.day, 12, 01)); await viewModel.futureToRun(); - await untilCalled(courseRepository.getCoursesActivities()); + await untilCalled(courseRepositoryMock.getCoursesActivities()); final activitiesFinishedCourse = List.from(todayActivities)..remove(gen101); expect(viewModel.todayDateEvents, activitiesFinishedCourse); - verify(courseRepository.getCoursesActivities()).called(1); + verify(courseRepositoryMock.getCoursesActivities()).called(1); - verify(courseRepository.getCoursesActivities(fromCacheOnly: true)) + verify(courseRepositoryMock.getCoursesActivities(fromCacheOnly: true)) .called(1); - verify(courseRepository.coursesActivities).called(1); + verify(courseRepositoryMock.coursesActivities).called(2); - verify(settingsManager.getDashboard()).called(1); + verify(settingsManagerMock.getDashboard()).called(1); }); test("build the list tomorrow activities sorted by time", () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, toReturn: courses); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); final now = DateTime.now(); - SettingsManagerMock.stubDateTimeNow( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: DateTime(now.year, now.month, now.day, 8)); await viewModel.futureToRun(); - await untilCalled(courseRepository.getCoursesActivities()); + await untilCalled(courseRepositoryMock.getCoursesActivities()); expect(viewModel.tomorrowDateEvents, tomorrowActivities); - verify(courseRepository.getCoursesActivities()).called(1); + verify(courseRepositoryMock.getCoursesActivities()).called(1); - verify(courseRepository.getCoursesActivities(fromCacheOnly: true)) + verify(courseRepositoryMock.getCoursesActivities(fromCacheOnly: true)) .called(1); - verify(courseRepository.coursesActivities).called(1); + verify(courseRepositoryMock.coursesActivities).called(2); - verify(settingsManager.getDashboard()).called(1); + verify(settingsManagerMock.getDashboard()).called(1); }); test( "build the list todays activities with the right course activities (should not have labo A)", () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activitiesWithLabs); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN101", - toReturn: null); - - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN102", - toReturn: null); - - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN103", + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN101"); + + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN102"); + + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN103", toReturn: ActivityCode.labGroupB); expect(await viewModel.removeLaboratoryGroup(activitiesWithLabs), [ @@ -565,28 +498,18 @@ void main() { test( "build the list todays activities with the right course activities (should not have labo B)", () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activitiesWithLabs); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN101", - toReturn: null); - - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN102", - toReturn: null); - - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN103", + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN101"); + + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN102"); + + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN103", toReturn: ActivityCode.labGroupA); expect(await viewModel.removeLaboratoryGroup(activitiesWithLabs), [ @@ -600,29 +523,18 @@ void main() { test( "build the list todays activities with the right course activities (should have both labs)", () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activitiesWithLabs); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN101", - toReturn: null); + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN101"); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN102", - toReturn: null); + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN102"); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN103", - toReturn: null); + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN103"); expect(await viewModel.removeLaboratoryGroup(activitiesWithLabs), activitiesWithLabs); @@ -631,43 +543,33 @@ void main() { test("An exception is thrown during the preferenceService call", () async { setupFlutterToastMock(); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); PreferencesServiceMock.stubException( - preferenceService as PreferencesServiceMock, - PreferencesFlag.broadcastCard); + preferenceServiceMock, PreferencesFlag.broadcastCard); PreferencesServiceMock.stubException( - preferenceService as PreferencesServiceMock, - PreferencesFlag.aboutUsCard); + preferenceServiceMock, PreferencesFlag.aboutUsCard); PreferencesServiceMock.stubException( - preferenceService as PreferencesServiceMock, - PreferencesFlag.scheduleCard); + preferenceServiceMock, PreferencesFlag.scheduleCard); PreferencesServiceMock.stubException( - preferenceService as PreferencesServiceMock, - PreferencesFlag.progressBarCard); + preferenceServiceMock, PreferencesFlag.progressBarCard); await viewModel.futureToRun(); expect(viewModel.cardsToDisplay, []); - verify(settingsManager.getDashboard()).called(1); + verify(settingsManagerMock.getDashboard()).called(1); }); }); group("futureToRunSessionProgressBar - ", () { test("There is an active session", () async { - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock, toReturn: [session]); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); - SettingsManagerMock.stubDateTimeNow( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: DateTime(2020)); await viewModel.futureToRunSessionProgressBar(); expect(viewModel.progress, 0.5); @@ -675,14 +577,11 @@ void main() { }); test("Invalid date (Superior limit)", () async { - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock, toReturn: [session]); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); - SettingsManagerMock.stubDateTimeNow( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: DateTime(2020, 1, 20)); await viewModel.futureToRunSessionProgressBar(); expect(viewModel.progress, 1); @@ -690,14 +589,11 @@ void main() { }); test("Invalid date (Lower limit)", () async { - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock, toReturn: [session]); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); - SettingsManagerMock.stubDateTimeNow( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubDateTimeNow(settingsManagerMock, toReturn: DateTime(2019, 12, 31)); await viewModel.futureToRunSessionProgressBar(); expect(viewModel.progress, 0); @@ -705,8 +601,7 @@ void main() { }); test("Active session is null", () async { - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock); await viewModel.futureToRunSessionProgressBar(); expect(viewModel.progress, -1.0); @@ -716,11 +611,10 @@ void main() { test( "currentProgressBarText should be set to ProgressBarText.percentage when it is the first time changeProgressBarText is called", () async { - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock); viewModel.changeProgressBarText(); - verify(settingsManager.setString(PreferencesFlag.progressBarText, + verify(settingsManagerMock.setString(PreferencesFlag.progressBarText, ProgressBarText.values[1].toString())) .called(1); }); @@ -728,12 +622,11 @@ void main() { test( "currentProgressBarText flag should be set to ProgressBarText.remainingDays when it is the second time changeProgressBarText is called", () async { - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock); viewModel.changeProgressBarText(); viewModel.changeProgressBarText(); - verify(settingsManager.setString(PreferencesFlag.progressBarText, + verify(settingsManagerMock.setString(PreferencesFlag.progressBarText, ProgressBarText.values[2].toString())) .called(1); }); @@ -741,14 +634,13 @@ void main() { test( "currentProgressBarText flag should be set to ProgressBarText.daysElapsedWithTotalDays when it is the third time changeProgressBarText is called", () async { - CourseRepositoryMock.stubActiveSessions( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubActiveSessions(courseRepositoryMock); viewModel.changeProgressBarText(); viewModel.changeProgressBarText(); viewModel.changeProgressBarText(); - verify(settingsManager.setString(PreferencesFlag.progressBarText, + verify(settingsManagerMock.setString(PreferencesFlag.progressBarText, ProgressBarText.values[0].toString())) .called(1); }); @@ -756,20 +648,18 @@ void main() { group("interact with cards - ", () { test("can hide a card and reset cards to default layout", () async { - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.broadcastCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.aboutUsCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.progressBarCard); - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.aboutUsCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.scheduleCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.progressBarCard); + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); await viewModel.futureToRun(); @@ -777,7 +667,7 @@ void main() { viewModel.hideCard(PreferencesFlag.scheduleCard); await untilCalled( - settingsManager.setInt(PreferencesFlag.scheduleCard, -1)); + settingsManagerMock.setInt(PreferencesFlag.scheduleCard, -1)); expect(viewModel.cards, hiddenCardDashboard); expect(viewModel.cardsToDisplay, [ @@ -786,22 +676,22 @@ void main() { PreferencesFlag.progressBarCard ]); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "DashboardViewModel", "Deleting scheduleCard")); - verify(settingsManager.setInt(PreferencesFlag.scheduleCard, -1)) + verify(settingsManagerMock.setInt(PreferencesFlag.scheduleCard, -1)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.broadcastCard, 0)) + verify(settingsManagerMock.setInt(PreferencesFlag.broadcastCard, 0)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.aboutUsCard, 1)) + verify(settingsManagerMock.setInt(PreferencesFlag.aboutUsCard, 1)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.progressBarCard, 2)) + verify(settingsManagerMock.setInt(PreferencesFlag.progressBarCard, 2)) .called(1); // Call the setter. viewModel.setAllCardsVisible(); await untilCalled( - settingsManager.setInt(PreferencesFlag.progressBarCard, 3)); + settingsManagerMock.setInt(PreferencesFlag.progressBarCard, 3)); expect(viewModel.cards, dashboard); expect(viewModel.cardsToDisplay, [ @@ -811,43 +701,39 @@ void main() { PreferencesFlag.progressBarCard ]); - verify( - analyticsService.logEvent("DashboardViewModel", "Restoring cards")); - verify(settingsManager.getDashboard()).called(1); - verify(settingsManager.setInt(PreferencesFlag.broadcastCard, 0)) + verify(analyticsServiceMock.logEvent( + "DashboardViewModel", "Restoring cards")); + verify(settingsManagerMock.getDashboard()).called(1); + verify(settingsManagerMock.setInt(PreferencesFlag.broadcastCard, 0)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.aboutUsCard, 1)) + verify(settingsManagerMock.setInt(PreferencesFlag.aboutUsCard, 1)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.scheduleCard, 2)) + verify(settingsManagerMock.setInt(PreferencesFlag.scheduleCard, 2)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.progressBarCard, 3)) + verify(settingsManagerMock.setInt(PreferencesFlag.progressBarCard, 3)) .called(1); - verify(settingsManager.getString(PreferencesFlag.progressBarText)) + verify(settingsManagerMock.getString(PreferencesFlag.progressBarText)) .called(2); - verify(settingsManager.dateTimeNow).called(3); - verifyNoMoreInteractions(settingsManager); + verify(settingsManagerMock.dateTimeNow).called(3); + verifyNoMoreInteractions(settingsManagerMock); }); test("can set new order for cards", () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock); - - SettingsManagerMock.stubGetDashboard( - settingsManager as SettingsManagerMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + + SettingsManagerMock.stubGetDashboard(settingsManagerMock, toReturn: dashboard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.broadcastCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.aboutUsCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleCard); - SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, - PreferencesFlag.progressBarCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.aboutUsCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.scheduleCard); + SettingsManagerMock.stubSetInt( + settingsManagerMock, PreferencesFlag.progressBarCard); await viewModel.futureToRun(); @@ -863,7 +749,7 @@ void main() { viewModel.setOrder(PreferencesFlag.progressBarCard, 0); await untilCalled( - settingsManager.setInt(PreferencesFlag.progressBarCard, 0)); + settingsManagerMock.setInt(PreferencesFlag.progressBarCard, 0)); expect(viewModel.cards, reorderedDashboard); expect(viewModel.cardsToDisplay, [ @@ -873,21 +759,21 @@ void main() { PreferencesFlag.scheduleCard ]); - verify(analyticsService.logEvent( + verify(analyticsServiceMock.logEvent( "DashboardViewModel", "Reordoring progressBarCard")); - verify(settingsManager.getDashboard()).called(1); - verify(settingsManager.setInt(PreferencesFlag.progressBarCard, 0)) + verify(settingsManagerMock.getDashboard()).called(1); + verify(settingsManagerMock.setInt(PreferencesFlag.progressBarCard, 0)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.broadcastCard, 1)) + verify(settingsManagerMock.setInt(PreferencesFlag.broadcastCard, 1)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.aboutUsCard, 2)) + verify(settingsManagerMock.setInt(PreferencesFlag.aboutUsCard, 2)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.scheduleCard, 3)) + verify(settingsManagerMock.setInt(PreferencesFlag.scheduleCard, 3)) .called(1); - verify(settingsManager.getString(PreferencesFlag.progressBarText)) + verify(settingsManagerMock.getString(PreferencesFlag.progressBarText)) .called(1); - verify(settingsManager.dateTimeNow).called(2); - verifyNoMoreInteractions(settingsManager); + verify(settingsManagerMock.dateTimeNow).called(2); + verifyNoMoreInteractions(settingsManagerMock); }); }); @@ -940,7 +826,7 @@ void main() { toReturn: day); PreferencesServiceMock.stubGetBool( preferencesServiceMock, PreferencesFlag.hasRatingBeenRequested, - toReturn: null); + toReturn: false); expect(await DashboardViewModel.launchInAppReview(), true); diff --git a/test/viewmodels/faq_viewmodel_test.dart b/test/viewmodels/faq_viewmodel_test.dart index 219fd8df5..7f39739dc 100644 --- a/test/viewmodels/faq_viewmodel_test.dart +++ b/test/viewmodels/faq_viewmodel_test.dart @@ -13,13 +13,13 @@ import '../helpers.dart'; import '../mock/services/launch_url_service_mock.dart'; void main() { - LaunchUrlServiceMock launchUrlService; + late LaunchUrlServiceMock launchUrlServiceMock; - FaqViewModel viewModel; + late FaqViewModel viewModel; group('FaqViewModel - ', () { setUp(() async { - launchUrlService = setupLaunchUrlServiceMock() as LaunchUrlServiceMock; + launchUrlServiceMock = setupLaunchUrlServiceMock(); setupSettingsManagerMock(); viewModel = FaqViewModel(); @@ -42,7 +42,7 @@ void main() { test('Calls launchInBrowser', () { viewModel.launchWebsite("https://clubapplets.ca/", Brightness.light); - verify(launchUrlService.launchInBrowser( + verify(launchUrlServiceMock.launchInBrowser( "https://clubapplets.ca/", Brightness.light)) .called(1); }); diff --git a/test/viewmodels/feedback_viewmodel_test.dart b/test/viewmodels/feedback_viewmodel_test.dart index 744d9c732..b685b2b9b 100644 --- a/test/viewmodels/feedback_viewmodel_test.dart +++ b/test/viewmodels/feedback_viewmodel_test.dart @@ -27,12 +27,12 @@ void main() { // Needed to support FlutterToast. TestWidgetsFlutterBinding.ensureInitialized(); - GithubApiMock githubApiMock; + late GithubApiMock githubApiMock; + late PreferencesServiceMock preferencesServiceMock; - PreferencesServiceMock preferencesServiceMock; + late FeedbackViewModel viewModel; - AppIntl appIntl; - FeedbackViewModel viewModel; + late AppIntl appIntl; const feedBackText = 'Notre-Dame bug report'; final file = File('bugReportTest.png'); final filePath = file.path.split('/').last; @@ -45,11 +45,11 @@ void main() { group('FeedbackViewModel - ', () { setUp(() async { setupNavigationServiceMock(); - githubApiMock = setupGithubApiMock() as GithubApiMock; - preferencesServiceMock = - setupPreferencesServiceMock() as PreferencesServiceMock; + githubApiMock = setupGithubApiMock(); + preferencesServiceMock = setupPreferencesServiceMock(); appIntl = await setupAppIntl(); setupLogger(); + GithubApiMock.stubCreateGithubIssue(githubApiMock, Issue()); viewModel = FeedbackViewModel(intl: appIntl); }); @@ -60,7 +60,7 @@ void main() { }); group('sendFeedback - ', () { - Uint8List screenshotData; + late Uint8List screenshotData; setUp(() async { final ByteData bytes = await rootBundle @@ -73,7 +73,7 @@ void main() { setupFlutterToastMock(); await file.writeAsBytes(image.encodePng( - image.copyResize(image.decodeImage(screenshotData), width: 307))); + image.copyResize(image.decodeImage(screenshotData)!, width: 307))); await viewModel.sendFeedback( UserFeedback( diff --git a/test/viewmodels/grades_details_viewmodel_test.dart b/test/viewmodels/grades_details_viewmodel_test.dart index d90ec987f..a099f4c79 100644 --- a/test/viewmodels/grades_details_viewmodel_test.dart +++ b/test/viewmodels/grades_details_viewmodel_test.dart @@ -13,9 +13,11 @@ import '../mock/managers/course_repository_mock.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - AppIntl intl; - GradesDetailsViewModel viewModel; - CourseRepository courseRepository; + late AppIntl intl; + + late CourseRepositoryMock courseRepositoryMock; + + late GradesDetailsViewModel viewModel; final CourseSummary courseSummary = CourseSummary( currentMark: 5, @@ -30,7 +32,7 @@ void main() { courseGroup: "02", title: "Laboratoire 1", weight: 10, - teacherMessage: null, + teacherMessage: '', ignore: false, mark: 24, correctedEvaluationOutOf: "35", @@ -45,7 +47,7 @@ void main() { courseGroup: "02", title: "Laboratoire 2", weight: 10, - teacherMessage: null, + teacherMessage: '', ignore: false, mark: 24, correctedEvaluationOutOf: "30", @@ -80,7 +82,7 @@ void main() { group("GradesDetailsViewModel - ", () { setUp(() async { // Setting up mocks - courseRepository = setupCourseRepositoryMock(); + courseRepositoryMock = setupCourseRepositoryMock(); intl = await setupAppIntl(); setupSettingsManagerMock(); @@ -96,7 +98,7 @@ void main() { group('FutureToRun - -', () { test('SignetsAPI gets the summary', () async { CourseRepositoryMock.stubGetCourseSummary( - courseRepository as CourseRepositoryMock, courseWithoutSummary, + courseRepositoryMock, courseWithoutSummary, toReturn: courseWithSummary); await viewModel.futureToRun(); @@ -108,7 +110,7 @@ void main() { () async { setupFlutterToastMock(); CourseRepositoryMock.stubGetCourseSummaryException( - courseRepository as CourseRepositoryMock, courseWithoutSummary); + courseRepositoryMock, courseWithoutSummary); await viewModel.futureToRun(); expect(viewModel.course, courseWithoutSummary); @@ -120,7 +122,7 @@ void main() { () async { setupFlutterToastMock(); CourseRepositoryMock.stubGetCourseSummary( - courseRepository as CourseRepositoryMock, courseWithoutSummary, + courseRepositoryMock, courseWithoutSummary, toReturn: courseWithSummary); await viewModel.refresh(); @@ -130,15 +132,15 @@ void main() { test('Signets throw an error', () async { CourseRepositoryMock.stubGetCourseSummaryException( - courseRepository as CourseRepositoryMock, courseWithoutSummary); + courseRepositoryMock, courseWithoutSummary); setupFlutterToastMock(); await viewModel.refresh(); expect(viewModel.course, courseWithoutSummary); - verify(courseRepository.getCourseSummary(viewModel.course)); + verify(courseRepositoryMock.getCourseSummary(viewModel.course)); - verifyNoMoreInteractions(courseRepository); + verifyNoMoreInteractions(courseRepositoryMock); }); }); }); diff --git a/test/viewmodels/grades_viewmodel_test.dart b/test/viewmodels/grades_viewmodel_test.dart index d1e5df79f..b3be684fb 100644 --- a/test/viewmodels/grades_viewmodel_test.dart +++ b/test/viewmodels/grades_viewmodel_test.dart @@ -14,9 +14,11 @@ import '../mock/managers/course_repository_mock.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - CourseRepository courseRepository; - AppIntl intl; - GradesViewModel viewModel; + late AppIntl intl; + + late CourseRepositoryMock courseRepositoryMock; + + late GradesViewModel viewModel; final Course courseSummer = Course( acronym: 'GEN101', @@ -83,7 +85,7 @@ void main() { group('GradesViewModel -', () { setUp(() async { - courseRepository = setupCourseRepositoryMock(); + courseRepositoryMock = setupCourseRepositoryMock(); intl = await setupAppIntl(); setupSettingsManagerMock(); setupNavigationServiceMock(); @@ -100,43 +102,35 @@ void main() { group('futureToRun -', () { test('first load from cache than call SignetsAPI to get the courses', () async { - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - toReturn: courses, - fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, + toReturn: courses, fromCacheOnly: true); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, toReturn: courses); - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCourses(courseRepositoryMock, toReturn: courses); expect(await viewModel.futureToRun(), coursesBySession); - await untilCalled(courseRepository.courses); + await untilCalled(courseRepositoryMock.courses); expect(viewModel.coursesBySession, coursesBySession); expect(viewModel.sessionOrder, sessionOrder); verifyInOrder([ - courseRepository.getCourses(fromCacheOnly: true), - courseRepository.getCourses(), - courseRepository.courses + courseRepositoryMock.getCourses(fromCacheOnly: true), + courseRepositoryMock.getCourses(), + courseRepositoryMock.courses, + courseRepositoryMock.courses ]); - verifyNoMoreInteractions(courseRepository); + verifyNoMoreInteractions(courseRepositoryMock); }); test('Signets throw an error while trying to get courses', () async { - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - toReturn: courses, - fromCacheOnly: true); - CourseRepositoryMock.stubGetCoursesException( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, + toReturn: courses, fromCacheOnly: true); + CourseRepositoryMock.stubGetCoursesException(courseRepositoryMock); + CourseRepositoryMock.stubCourses(courseRepositoryMock, toReturn: courses); setupFlutterToastMock(); @@ -144,17 +138,17 @@ void main() { reason: "Even if SignetsAPI call fails, should return the cache contents"); - await untilCalled(courseRepository.getCourses()); + await untilCalled(courseRepositoryMock.getCourses()); expect(viewModel.coursesBySession, coursesBySession); expect(viewModel.sessionOrder, sessionOrder); verifyInOrder([ - courseRepository.getCourses(fromCacheOnly: true), - courseRepository.getCourses() + courseRepositoryMock.getCourses(fromCacheOnly: true), + courseRepositoryMock.getCourses() ]); - verifyNoMoreInteractions(courseRepository); + verifyNoMoreInteractions(courseRepositoryMock); }); }); @@ -162,11 +156,9 @@ void main() { test( 'Call SignetsAPI to get the courses than reload the coursesBySession', () async { - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, toReturn: courses); - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCourses(courseRepositoryMock, toReturn: courses); await viewModel.refresh(); @@ -174,22 +166,20 @@ void main() { expect(viewModel.coursesBySession, coursesBySession); expect(viewModel.sessionOrder, sessionOrder); - verifyInOrder( - [courseRepository.getCourses(), courseRepository.courses]); + verifyInOrder([ + courseRepositoryMock.getCourses(), + courseRepositoryMock.courses, + courseRepositoryMock.courses + ]); - verifyNoMoreInteractions(courseRepository); + verifyNoMoreInteractions(courseRepositoryMock); }); test('Signets throw an error', () async { - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - toReturn: courses, - fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, + toReturn: courses, fromCacheOnly: true); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubCourses(courseRepositoryMock, toReturn: courses); setupFlutterToastMock(); @@ -198,13 +188,10 @@ void main() { expect(viewModel.coursesBySession, coursesBySession); expect(viewModel.sessionOrder, sessionOrder); - reset(courseRepository); - CourseRepositoryMock.stubCourses( - courseRepository as CourseRepositoryMock, + reset(courseRepositoryMock); + CourseRepositoryMock.stubCourses(courseRepositoryMock, toReturn: courses); - CourseRepositoryMock.stubGetCoursesException( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); + CourseRepositoryMock.stubGetCoursesException(courseRepositoryMock); await viewModel.refresh(); @@ -213,10 +200,9 @@ void main() { "The list of courses should not change even when an error occurs"); expect(viewModel.sessionOrder, sessionOrder); - verifyInOrder( - [courseRepository.getCourses(), courseRepository.courses]); + verifyInOrder([courseRepositoryMock.getCourses()]); - verifyNoMoreInteractions(courseRepository); + verifyNoMoreInteractions(courseRepositoryMock); }); }); }); diff --git a/test/viewmodels/login_viewmodel_test.dart b/test/viewmodels/login_viewmodel_test.dart index 5391ffbb2..a96be3d0a 100644 --- a/test/viewmodels/login_viewmodel_test.dart +++ b/test/viewmodels/login_viewmodel_test.dart @@ -10,6 +10,7 @@ import 'package:notredame/core/services/navigation_service.dart'; import 'package:notredame/core/viewmodels/login_viewmodel.dart'; import '../helpers.dart'; import '../mock/managers/user_repository_mock.dart'; +import '../mock/services/navigation_service_mock.dart'; void main() { // Needed to support FlutterToast. @@ -20,18 +21,18 @@ void main() { const String passwordCodeValid = "password"; const String passwordCodeInvalid = ""; - NavigationService navigationService; - UserRepositoryMock userRepositoryMock; + late NavigationServiceMock navigationServiceMock; + late UserRepositoryMock userRepositoryMock; - AppIntl appIntl; + late AppIntl appIntl; - LoginViewModel viewModel; + late LoginViewModel viewModel; group('LoginViewModel - ', () { setUp(() async { - navigationService = setupNavigationServiceMock(); + navigationServiceMock = setupNavigationServiceMock(); setupFlutterSecureStorageMock(); - userRepositoryMock = setupUserRepositoryMock() as UserRepositoryMock; + userRepositoryMock = setupUserRepositoryMock(); setupLogger(); setupPreferencesServiceMock(); appIntl = await setupAppIntl(); @@ -109,8 +110,8 @@ void main() { viewModel.validatePassword(passwordCodeValid); await viewModel.authenticate(); - verify( - navigationService.pushNamedAndRemoveUntil(RouterPaths.dashboard)); + verify(navigationServiceMock + .pushNamedAndRemoveUntil(RouterPaths.dashboard)); }); test( diff --git a/test/viewmodels/more_viewmodel_test.dart b/test/viewmodels/more_viewmodel_test.dart index 9d1ed4603..a6a78203a 100644 --- a/test/viewmodels/more_viewmodel_test.dart +++ b/test/viewmodels/more_viewmodel_test.dart @@ -12,45 +12,48 @@ import 'package:notredame/core/managers/settings_manager.dart'; import 'package:notredame/core/managers/user_repository.dart'; import 'package:notredame/core/services/navigation_service.dart'; import 'package:notredame/core/services/preferences_service.dart'; -import 'package:notredame/core/services/remote_config_service.dart'; import 'package:notredame/core/viewmodels/more_viewmodel.dart'; import '../helpers.dart'; import '../mock/managers/cache_manager_mock.dart'; import '../mock/managers/course_repository_mock.dart'; import '../mock/managers/settings_manager_mock.dart'; import '../mock/managers/user_repository_mock.dart'; +import '../mock/services/navigation_service_mock.dart'; +import '../mock/services/preferences_service_mock.dart'; import '../mock/services/remote_config_service_mock.dart'; void main() { // Needed to support FlutterToast. TestWidgetsFlutterBinding.ensureInitialized(); - CacheManagerMock cacheManagerMock; - SettingsManagerMock settingsManagerMock; - CourseRepositoryMock courseRepositoryMock; - PreferencesService preferenceService; - RemoteConfigService remoteConfigService; - UserRepositoryMock userRepositoryMock; - NavigationService navigationService; + late CacheManagerMock cacheManagerMock; + late SettingsManagerMock settingsManagerMock; + late CourseRepositoryMock courseRepositoryMock; + late PreferencesServiceMock preferenceServiceMock; + late RemoteConfigServiceMock remoteConfigServiceMock; + late UserRepositoryMock userRepositoryMock; + late NavigationServiceMock navigationServiceMock; - AppIntl appIntl; - MoreViewModel viewModel; + late AppIntl appIntl; + late MoreViewModel viewModel; + + final DateTime now = DateTime.now(); final List sessions = [ Session( name: 'Hivers 2XXX', shortName: 'H1', - deadlineCancellationASEQ: null, - deadlineCancellationWithoutRefundNewStudent: null, - deadlineCancellationWithRefund: null, - deadlineCancellationWithRefundNewStudent: null, - deadlineRegistration: null, - startDate: null, - startDateCancellationWithoutRefundNewStudent: null, - startDateCancellationWithRefund: null, - startDateRegistration: null, - endDate: null, - endDateCourses: null), + deadlineCancellationASEQ: now, + deadlineCancellationWithoutRefundNewStudent: now, + deadlineCancellationWithRefund: now, + deadlineCancellationWithRefundNewStudent: now, + deadlineRegistration: now, + startDate: now, + startDateCancellationWithoutRefundNewStudent: now, + startDateCancellationWithRefund: now, + startDateRegistration: now, + endDate: now, + endDateCourses: now), ]; final List coursesActivities = [ @@ -60,8 +63,8 @@ void main() { activityName: '', activityDescription: '', activityLocation: '', - startDateTime: null, - endDateTime: null), + startDateTime: now, + endDateTime: now.add(const Duration(hours: 1))), ]; final List courses = [ @@ -80,44 +83,43 @@ void main() { // Check if the cacheManager has been emptied out cacheManagerMock.empty(), // Check if preference manager is clear - preferenceService.clearWithoutPersistentKey(), + preferenceServiceMock.clearWithoutPersistentKey(), // Check if user repository logOut is called userRepositoryMock.logOut(), // Check if the settings manager has reset lang and theme and notified his listener settingsManagerMock.resetLanguageAndThemeMode(), ]); verifyNoMoreInteractions(cacheManagerMock); - verifyNoMoreInteractions(preferenceService); + verifyNoMoreInteractions(preferenceServiceMock); verifyNoMoreInteractions(userRepositoryMock); verifyNoMoreInteractions(settingsManagerMock); // Make sure that the registered cache - expect(courseRepositoryMock.sessions.length, 0, + expect(courseRepositoryMock.sessions!.length, 0, reason: 'has emptied out the sessions list'); - expect(courseRepositoryMock.coursesActivities.length, 0, + expect(courseRepositoryMock.coursesActivities!.length, 0, reason: 'has emptied out the courseActivities list'); - expect(courseRepositoryMock.courses.length, 0, + expect(courseRepositoryMock.courses!.length, 0, reason: 'has emptied out the courses list'); // Check if navigation has been rerouted to login page verifyInOrder([ - navigationService.pushNamedAndRemoveUntil( + navigationServiceMock.pushNamedAndRemoveUntil( RouterPaths.login, RouterPaths.chooseLanguage) ]); - verifyNoMoreInteractions(navigationService); + verifyNoMoreInteractions(navigationServiceMock); } group('MoreViewModel - ', () { setUp(() async { - cacheManagerMock = setupCacheManagerMock() as CacheManagerMock; - settingsManagerMock = setupSettingsManagerMock() as SettingsManagerMock; - courseRepositoryMock = - setupCourseRepositoryMock() as CourseRepositoryMock; - remoteConfigService = setupRemoteConfigServiceMock(); - preferenceService = setupPreferencesServiceMock(); - userRepositoryMock = setupUserRepositoryMock() as UserRepositoryMock; - navigationService = setupNavigationServiceMock(); + cacheManagerMock = setupCacheManagerMock(); + settingsManagerMock = setupSettingsManagerMock(); + courseRepositoryMock = setupCourseRepositoryMock(); + remoteConfigServiceMock = setupRemoteConfigServiceMock(); + preferenceServiceMock = setupPreferencesServiceMock(); + userRepositoryMock = setupUserRepositoryMock(); + navigationServiceMock = setupNavigationServiceMock(); appIntl = await setupAppIntl(); setupLogger(); @@ -129,7 +131,7 @@ void main() { CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: coursesActivities); RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock); + remoteConfigServiceMock); }); tearDown(() { @@ -145,7 +147,7 @@ void main() { test('If the correct function have been called when logout occur', () async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); setupFlutterToastMock(); UserRepositoryMock.stubLogOut(userRepositoryMock); @@ -159,7 +161,7 @@ void main() { 'If an error occur from the cache manager that the logout function finishes out', () async { RemoteConfigServiceMock.stubGetPrivacyPolicyEnabled( - remoteConfigService as RemoteConfigServiceMock, + remoteConfigServiceMock, toReturn: false); setupFlutterToastMock(); CacheManagerMock.stubEmptyException(cacheManagerMock); diff --git a/test/viewmodels/not_found_viewmodel_test.dart b/test/viewmodels/not_found_viewmodel_test.dart index f70b73d14..52e228fe7 100644 --- a/test/viewmodels/not_found_viewmodel_test.dart +++ b/test/viewmodels/not_found_viewmodel_test.dart @@ -10,23 +10,25 @@ import 'package:notredame/core/services/navigation_service.dart'; import 'package:notredame/core/services/rive_animation_service.dart'; import 'package:notredame/core/viewmodels/not_found_viewmodel.dart'; import '../helpers.dart'; +import '../mock/services/analytics_service_mock.dart'; +import '../mock/services/navigation_service_mock.dart'; import '../mock/services/rive_animation_service_mock.dart'; void main() { - NavigationService navigationService; - RiveAnimationService riveAnimationService; - AnalyticsService analyticsService; + late NavigationServiceMock navigationServiceMock; + late RiveAnimationServiceMock riveAnimationServiceMock; + late AnalyticsServiceMock analyticsServiceMock; - NotFoundViewModel viewModel; + late NotFoundViewModel viewModel; group('NotFoundViewModel - ', () { const String pageNotFoundPassed = "/test"; const String riveFileName = 'dot_jumping'; setUp(() async { - navigationService = setupNavigationServiceMock(); - riveAnimationService = setupRiveAnimationServiceMock(); - analyticsService = setupAnalyticsServiceMock(); + navigationServiceMock = setupNavigationServiceMock(); + riveAnimationServiceMock = setupRiveAnimationServiceMock(); + analyticsServiceMock = setupAnalyticsServiceMock(); setupLogger(); viewModel = NotFoundViewModel(pageName: pageNotFoundPassed); @@ -43,7 +45,7 @@ void main() { const String pageTestCtor = "\testctor"; NotFoundViewModel(pageName: pageTestCtor); - verify(analyticsService.logEvent(NotFoundViewModel.tag, + verify(analyticsServiceMock.logEvent(NotFoundViewModel.tag, "An unknown page ($pageTestCtor) has been access from the app.")); }); }); @@ -52,8 +54,8 @@ void main() { test('navigating back worked', () async { viewModel.navigateToDashboard(); - verify( - navigationService.pushNamedAndRemoveUntil(RouterPaths.dashboard)); + verify(navigationServiceMock + .pushNamedAndRemoveUntil(RouterPaths.dashboard)); }); }); @@ -76,9 +78,7 @@ void main() { final expectedArtboard = Artboard(); RiveAnimationServiceMock.stubLoadRiveFile( - riveAnimationService as RiveAnimationServiceMock, - 'dot_jumping', - expectedArtboard); + riveAnimationServiceMock, 'dot_jumping', expectedArtboard); await viewModel.loadRiveAnimation(); final artboard = viewModel.artboard; @@ -91,16 +91,17 @@ void main() { test('load the dot_jumping Rive animation successfuly', () async { await viewModel.loadRiveAnimation(); - verify(riveAnimationService.loadRiveFile(riveFileName: riveFileName)); + verify( + riveAnimationServiceMock.loadRiveFile(riveFileName: riveFileName)); }); test('load file Rive animation with error', () async { RiveAnimationServiceMock.stubLoadRiveFileException( - riveAnimationService as RiveAnimationServiceMock); + riveAnimationServiceMock); await viewModel.loadRiveAnimation(); - verify(analyticsService.logError( + verify(analyticsServiceMock.logError( NotFoundViewModel.tag, "An Error has occurred during rive animation $riveFileName loading.", RiveAnimationServiceMock.loadException, @@ -113,17 +114,15 @@ void main() { final artboard = Artboard(); RiveAnimationServiceMock.stubLoadRiveFile( - riveAnimationService as RiveAnimationServiceMock, - 'dot_jumping', - artboard); + riveAnimationServiceMock, 'dot_jumping', artboard); RiveAnimationServiceMock.stubAddControllerToAnimationException( - riveAnimationService as RiveAnimationServiceMock, artboard); + riveAnimationServiceMock, artboard); await viewModel.loadRiveAnimation(); viewModel.startRiveAnimation(); - verify(analyticsService.logError( + verify(analyticsServiceMock.logError( NotFoundViewModel.tag, "An Error has occured during rive animation start.", RiveAnimationServiceMock.startException, diff --git a/test/viewmodels/profile_viewmodel_test.dart b/test/viewmodels/profile_viewmodel_test.dart index 7dd5faaf0..71a984bb7 100644 --- a/test/viewmodels/profile_viewmodel_test.dart +++ b/test/viewmodels/profile_viewmodel_test.dart @@ -5,15 +5,16 @@ import 'package:mockito/mockito.dart'; // Project imports: import 'package:notredame/core/constants/programs_credits.dart'; -import 'package:notredame/core/managers/settings_manager.dart'; import 'package:notredame/core/managers/user_repository.dart'; import 'package:notredame/core/viewmodels/profile_viewmodel.dart'; import '../helpers.dart'; +import '../mock/managers/settings_manager_mock.dart'; import '../mock/managers/user_repository_mock.dart'; -UserRepository userRepository; -SettingsManager settingsManager; -ProfileViewModel viewModel; +late UserRepositoryMock userRepositoryMock; +late SettingsManagerMock settingsManagerMock; + +late ProfileViewModel viewModel; void main() { // Needed to support FlutterToast. @@ -60,7 +61,7 @@ void main() { group("ProfileViewModel - ", () { setUp(() async { // Setting up mocks - userRepository = setupUserRepositoryMock(); + userRepositoryMock = setupUserRepositoryMock(); setupAnalyticsServiceMock(); viewModel = ProfileViewModel(intl: await setupAppIntl()); @@ -74,71 +75,68 @@ void main() { test( "first load from cache then call SignetsAPI to get the latest events", () async { - UserRepositoryMock.stubGetInfo(userRepository as UserRepositoryMock); - UserRepositoryMock.stubGetPrograms( - userRepository as UserRepositoryMock); + UserRepositoryMock.stubGetInfo(userRepositoryMock, toReturn: info); + UserRepositoryMock.stubGetPrograms(userRepositoryMock); expect(await viewModel.futureToRun(), []); verifyInOrder([ - userRepository.getInfo(fromCacheOnly: true), - userRepository.getPrograms(fromCacheOnly: true), - userRepository.getInfo(), + userRepositoryMock.getInfo(fromCacheOnly: true), + userRepositoryMock.getPrograms(fromCacheOnly: true), + userRepositoryMock.getInfo(), + userRepositoryMock.getPrograms() ]); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(userRepositoryMock); }); test("Signets throw an error while trying to get new events", () async { setupFlutterToastMock(); - UserRepositoryMock.stubGetInfo(userRepository as UserRepositoryMock, - fromCacheOnly: true); - UserRepositoryMock.stubGetInfoException( - userRepository as UserRepositoryMock, + UserRepositoryMock.stubGetInfo(userRepositoryMock, + fromCacheOnly: true, toReturn: info); + UserRepositoryMock.stubGetInfoException(userRepositoryMock, fromCacheOnly: false); - UserRepositoryMock.stubGetPrograms(userRepository as UserRepositoryMock, + UserRepositoryMock.stubGetPrograms(userRepositoryMock, fromCacheOnly: true); - UserRepositoryMock.stubGetProgramsException( - userRepository as UserRepositoryMock, + UserRepositoryMock.stubGetProgramsException(userRepositoryMock, fromCacheOnly: false); expect(await viewModel.futureToRun(), [], reason: "Even if SignetsAPI fails we should receives a list."); verifyInOrder([ - userRepository.getInfo(fromCacheOnly: true), - userRepository.getPrograms(fromCacheOnly: true), - userRepository.getInfo(), + userRepositoryMock.getInfo(fromCacheOnly: true), + userRepositoryMock.getPrograms(fromCacheOnly: true), + userRepositoryMock.getInfo(), + userRepositoryMock.programs ]); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(userRepositoryMock); }); }); group("info - ", () { test("build the info", () async { - UserRepositoryMock.stubProfileStudent( - userRepository as UserRepositoryMock, + UserRepositoryMock.stubProfileStudent(userRepositoryMock, toReturn: info); expect(viewModel.profileStudent, info); - verify(userRepository.info).called(1); + verify(userRepositoryMock.info).called(1); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(userRepositoryMock); }); }); group("programs - ", () { test("build the list of programs", () async { - UserRepositoryMock.stubPrograms(userRepository as UserRepositoryMock, - toReturn: programs); + UserRepositoryMock.stubPrograms(userRepositoryMock, toReturn: programs); expect(viewModel.programList, programs); - verify(userRepository.programs).called(2); + verify(userRepositoryMock.programs).called(2); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(userRepositoryMock); }); }); @@ -170,7 +168,7 @@ void main() { ), ]; - UserRepositoryMock.stubPrograms(userRepository as UserRepositoryMock, + UserRepositoryMock.stubPrograms(userRepositoryMock, toReturn: testPrograms); // Create an instance of ProgramCredits @@ -181,7 +179,8 @@ void main() { // Calculate the expected progression based on the defined ProgramCredits final double expectedProgression = - (45 / programCredits.programsCredits['7694'] * 100).roundToDouble(); + (45 / programCredits.programsCredits['7694']! * 100) + .roundToDouble(); // Verify that the calculated progression matches the expected value expect(progression, expectedProgression); @@ -204,7 +203,7 @@ void main() { ), ]; - UserRepositoryMock.stubPrograms(userRepository as UserRepositoryMock, + UserRepositoryMock.stubPrograms(userRepositoryMock, toReturn: testPrograms); // Calculate the program progression @@ -217,25 +216,22 @@ void main() { group('refresh -', () { test('Call SignetsAPI to get the user info and programs', () async { - UserRepositoryMock.stubProfileStudent( - userRepository as UserRepositoryMock, - toReturn: info); - UserRepositoryMock.stubGetInfo(userRepository as UserRepositoryMock, + UserRepositoryMock.stubProfileStudent(userRepositoryMock, toReturn: info); - UserRepositoryMock.stubGetPrograms( - userRepository as UserRepositoryMock); + UserRepositoryMock.stubGetInfo(userRepositoryMock, toReturn: info); + UserRepositoryMock.stubGetPrograms(userRepositoryMock); await viewModel.refresh(); expect(viewModel.profileStudent, info); verifyInOrder([ - userRepository.getInfo(), - userRepository.getPrograms(), - userRepository.info, + userRepositoryMock.getInfo(), + userRepositoryMock.getPrograms(), + userRepositoryMock.info, ]); - verifyNoMoreInteractions(userRepository); + verifyNoMoreInteractions(userRepositoryMock); }); }); }); diff --git a/test/viewmodels/quick_links_viewmodel_test.dart b/test/viewmodels/quick_links_viewmodel_test.dart index 21ac94f1a..71abbb2b1 100644 --- a/test/viewmodels/quick_links_viewmodel_test.dart +++ b/test/viewmodels/quick_links_viewmodel_test.dart @@ -12,18 +12,19 @@ import '../helpers.dart'; import '../mock/managers/quick_links_repository_mock.dart'; void main() { - AppIntl intl; - QuickLinksViewModel viewModel; - QuickLinkRepository quickLinkRepository; + late QuickLinkRepositoryMock quickLinkRepositoryMock; + + late QuickLinksViewModel viewModel; // Sample data for tests - QuickLinkData quickLinkDataSample; - QuickLink quickLinkSample; + late AppIntl intl; + late QuickLinkData quickLinkDataSample; + late QuickLink quickLinkSample; group("QuickLinksViewModel - ", () { setUp(() async { // Setting up mocks - quickLinkRepository = setupQuickLinkRepositoryMock(); + quickLinkRepositoryMock = setupQuickLinkRepositoryMock(); intl = await setupAppIntl(); viewModel = QuickLinksViewModel(intl); @@ -38,11 +39,11 @@ void main() { group('getQuickLinks -', () { test('Should get quick links from cache', () async { QuickLinkRepositoryMock.stubGetQuickLinkDataFromCache( - quickLinkRepository as QuickLinkRepositoryMock, + quickLinkRepositoryMock, toReturn: [quickLinkDataSample]); QuickLinkRepositoryMock.stubGetDefaultQuickLinks( - quickLinkRepository as QuickLinkRepositoryMock, + quickLinkRepositoryMock, toReturn: [quickLinkSample]); final result = await viewModel.getQuickLinks(); @@ -53,10 +54,10 @@ void main() { test('Should return default quick links if cache is not initialized', () async { QuickLinkRepositoryMock.stubGetQuickLinkDataFromCacheException( - quickLinkRepository as QuickLinkRepositoryMock); + quickLinkRepositoryMock); QuickLinkRepositoryMock.stubGetDefaultQuickLinks( - quickLinkRepository as QuickLinkRepositoryMock, + quickLinkRepositoryMock, toReturn: [quickLinkSample]); final result = await viewModel.getQuickLinks(); @@ -101,11 +102,11 @@ void main() { group('futureToRun -', () { test('Should fetch and set quick links', () async { QuickLinkRepositoryMock.stubGetQuickLinkDataFromCache( - quickLinkRepository as QuickLinkRepositoryMock, + quickLinkRepositoryMock, toReturn: [quickLinkDataSample]); QuickLinkRepositoryMock.stubGetDefaultQuickLinks( - quickLinkRepository as QuickLinkRepositoryMock, + quickLinkRepositoryMock, toReturn: [quickLinkSample]); final result = await viewModel.futureToRun(); diff --git a/test/viewmodels/schedule_settings_viewmodel_test.dart b/test/viewmodels/schedule_settings_viewmodel_test.dart index e49d47f64..0d9b1f70b 100644 --- a/test/viewmodels/schedule_settings_viewmodel_test.dart +++ b/test/viewmodels/schedule_settings_viewmodel_test.dart @@ -7,16 +7,16 @@ import 'package:table_calendar/table_calendar.dart'; // Project imports: import 'package:notredame/core/constants/preferences_flags.dart'; -import 'package:notredame/core/managers/course_repository.dart'; import 'package:notredame/core/managers/settings_manager.dart'; import 'package:notredame/core/viewmodels/schedule_settings_viewmodel.dart'; import '../helpers.dart'; import '../mock/managers/course_repository_mock.dart'; import '../mock/managers/settings_manager_mock.dart'; -SettingsManager settingsManager; -CourseRepository courseRepository; -ScheduleSettingsViewModel viewModel; +late SettingsManagerMock settingsManagerMock; +late CourseRepositoryMock courseRepositoryMock; + +late ScheduleSettingsViewModel viewModel; void main() { // Needed to support FlutterToast. @@ -26,7 +26,9 @@ void main() { PreferencesFlag.scheduleCalendarFormat: CalendarFormat.week, PreferencesFlag.scheduleStartWeekday: StartingDayOfWeek.monday, PreferencesFlag.scheduleShowTodayBtn: true, - PreferencesFlag.scheduleShowWeekendDays: false + PreferencesFlag.scheduleShowWeekendDays: false, + PreferencesFlag.scheduleListView: false, + PreferencesFlag.scheduleShowWeekEvents: false }; final List classOneWithLaboratoryABscheduleActivities = [ @@ -110,8 +112,8 @@ void main() { []; group("ScheduleSettingsViewModel - ", () { setUp(() { - settingsManager = setupSettingsManagerMock(); - courseRepository = setupCourseRepositoryMock(); + settingsManagerMock = setupSettingsManagerMock(); + courseRepositoryMock = setupCourseRepositoryMock(); viewModel = ScheduleSettingsViewModel(); twoClassesWithLaboratoryABscheduleActivities @@ -128,12 +130,10 @@ void main() { test( "The settings are correctly loaded and sets (if no schedule activities present to use)", () async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); - CourseRepositoryMock.stubGetScheduleActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock, toReturn: []); expect(await viewModel.futureToRun(), settings); @@ -144,32 +144,29 @@ void main() { expect(viewModel.showTodayBtn, settings[PreferencesFlag.scheduleShowTodayBtn]); - verify(settingsManager.getScheduleSettings()).called(1); - verifyNoMoreInteractions(settingsManager); + verify(settingsManagerMock.getScheduleSettings()).called(1); + verifyNoMoreInteractions(settingsManagerMock); - verify(courseRepository.getScheduleActivities()).called(1); - verifyNoMoreInteractions(courseRepository); + verify(courseRepositoryMock.getScheduleActivities()).called(1); + verifyNoMoreInteractions(courseRepositoryMock); }); test( "If there is one valid class which has grouped laboratory, we parse it and store it (None selected)", () async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); - CourseRepositoryMock.stubGetScheduleActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock, toReturn: classOneWithLaboratoryABscheduleActivities); final courseAcronymWithLaboratory = classOneWithLaboratoryABscheduleActivities.first.courseAcronym; SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, + settingsManagerMock, PreferencesFlag.scheduleLaboratoryGroup, - courseAcronymWithLaboratory, - toReturn: null); + courseAcronymWithLaboratory); expect(await viewModel.futureToRun(), settings); expect( @@ -177,28 +174,26 @@ void main() { .containsKey(courseAcronymWithLaboratory), true); expect( - viewModel - .scheduleActivitiesByCourse[courseAcronymWithLaboratory].length, + viewModel.scheduleActivitiesByCourse[courseAcronymWithLaboratory]! + .length, 2); expect( viewModel.selectedScheduleActivity .containsKey(courseAcronymWithLaboratory), false); - verify(courseRepository.getScheduleActivities()).called(1); - verifyNoMoreInteractions(courseRepository); + verify(courseRepositoryMock.getScheduleActivities()).called(1); + verifyNoMoreInteractions(courseRepositoryMock); - verify(settingsManager.getDynamicString(any, any)).called(1); + verify(settingsManagerMock.getDynamicString(any, any)).called(1); }); test( "If there is two valid class which has grouped laboratory, we store both (First => none selected, Second => group A selected)", () async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settings); - CourseRepositoryMock.stubGetScheduleActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock, toReturn: twoClassesWithLaboratoryABscheduleActivities); final firstCourseAcronymWithLab = @@ -207,26 +202,21 @@ void main() { final secondCourseAcronymWithLab = classTwoWithLaboratoryABscheduleActivities.first.courseAcronym; - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - firstCourseAcronymWithLab, - toReturn: null); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - secondCourseAcronymWithLab, + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, firstCourseAcronymWithLab); + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, secondCourseAcronymWithLab, toReturn: ActivityCode.labGroupA); expect(await viewModel.futureToRun(), settings); expect(viewModel.scheduleActivitiesByCourse.keys.length, 2); expect( viewModel - .scheduleActivitiesByCourse[firstCourseAcronymWithLab].length, + .scheduleActivitiesByCourse[firstCourseAcronymWithLab]!.length, 2); expect( viewModel - .scheduleActivitiesByCourse[secondCourseAcronymWithLab].length, + .scheduleActivitiesByCourse[secondCourseAcronymWithLab]!.length, 2); expect( viewModel.selectedScheduleActivity @@ -241,40 +231,38 @@ void main() { classTwoWithLaboratoryABscheduleActivities.firstWhere( (element) => element.activityCode == ActivityCode.labGroupA)); - verify(courseRepository.getScheduleActivities()).called(1); - verifyNoMoreInteractions(courseRepository); + verify(courseRepositoryMock.getScheduleActivities()).called(1); + verifyNoMoreInteractions(courseRepositoryMock); - verify(settingsManager.getDynamicString(any, any)).called(2); + verify(settingsManagerMock.getDynamicString(any, any)).called(2); }); }); group("setter calendarFormat - ", () { test("calendarFormat is updated on the settings", () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleCalendarFormat); + settingsManagerMock, PreferencesFlag.scheduleCalendarFormat); // Call the setter. viewModel.calendarFormat = CalendarFormat.twoWeeks; - await untilCalled(settingsManager.setString( + await untilCalled(settingsManagerMock.setString( PreferencesFlag.scheduleCalendarFormat, any)); expect(viewModel.calendarFormat, CalendarFormat.twoWeeks); expect(viewModel.isBusy, false); - verify(settingsManager.setString( + verify(settingsManagerMock.setString( PreferencesFlag.scheduleCalendarFormat, any)) .called(1); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(settingsManagerMock); }); }); group("setter calendarView - ", () { test("calendarView is updated on the settings", () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleListView); + settingsManagerMock, PreferencesFlag.scheduleListView); const expected = true; @@ -282,22 +270,22 @@ void main() { viewModel.toggleCalendarView = expected; await untilCalled( - settingsManager.setBool(PreferencesFlag.scheduleListView, any)); + settingsManagerMock.setBool(PreferencesFlag.scheduleListView, any)); expect(viewModel.toggleCalendarView, true); expect(viewModel.isBusy, false); - verify(settingsManager.setBool(PreferencesFlag.scheduleListView, any)) + verify(settingsManagerMock.setBool( + PreferencesFlag.scheduleListView, any)) .called(1); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(settingsManagerMock); }); }); group("setter scheduleShowWeekendDays - ", () { test("scheduleShowWeekendDays is updated on the settings", () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleShowWeekendDays); + settingsManagerMock, PreferencesFlag.scheduleShowWeekendDays); const expected = true; @@ -305,62 +293,60 @@ void main() { viewModel.showWeekendDays = expected; - await untilCalled(settingsManager.setBool( + await untilCalled(settingsManagerMock.setBool( PreferencesFlag.scheduleShowWeekendDays, any)); expect(viewModel.showWeekendDays, true); expect(viewModel.isBusy, false); - verify(settingsManager.setBool( + verify(settingsManagerMock.setBool( PreferencesFlag.scheduleShowWeekendDays, any)) .called(1); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(settingsManagerMock); }); }); group("setter startingDayOfWeek - ", () { test("startingDayOfWeek is updated on the settings", () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleStartWeekday); + settingsManagerMock, PreferencesFlag.scheduleStartWeekday); // Call the setter. viewModel.startingDayOfWeek = StartingDayOfWeek.friday; - await untilCalled(settingsManager.setString( + await untilCalled(settingsManagerMock.setString( PreferencesFlag.scheduleStartWeekday, any)); expect(viewModel.startingDayOfWeek, StartingDayOfWeek.friday); expect(viewModel.isBusy, false); - verify(settingsManager.setString( + verify(settingsManagerMock.setString( PreferencesFlag.scheduleStartWeekday, any)) .called(1); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(settingsManagerMock); }); }); group("setter showTodayBtn - ", () { test("showTodayBtn is updated on the settings", () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleStartWeekday); + settingsManagerMock, PreferencesFlag.scheduleStartWeekday); const expected = false; // Call the setter. viewModel.showTodayBtn = expected; - await untilCalled( - settingsManager.setBool(PreferencesFlag.scheduleShowTodayBtn, any)); + await untilCalled(settingsManagerMock.setBool( + PreferencesFlag.scheduleShowTodayBtn, any)); expect(viewModel.showTodayBtn, expected); expect(viewModel.isBusy, false); - verify(settingsManager.setBool( + verify(settingsManagerMock.setBool( PreferencesFlag.scheduleShowTodayBtn, any)) .called(1); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(settingsManagerMock); }); }); }); diff --git a/test/viewmodels/schedule_viewmodel_test.dart b/test/viewmodels/schedule_viewmodel_test.dart index 5e599bf82..91f16e108 100644 --- a/test/viewmodels/schedule_viewmodel_test.dart +++ b/test/viewmodels/schedule_viewmodel_test.dart @@ -14,9 +14,10 @@ import '../helpers.dart'; import '../mock/managers/course_repository_mock.dart'; import '../mock/managers/settings_manager_mock.dart'; -CourseRepository courseRepository; -SettingsManager settingsManager; -ScheduleViewModel viewModel; +late CourseRepositoryMock courseRepositoryMock; +late SettingsManagerMock settingsManagerMock; + +late ScheduleViewModel viewModel; void main() { // Needed to support FlutterToast. @@ -239,11 +240,18 @@ void main() { gen110 ]; + // Some settings + final Map settings = { + PreferencesFlag.scheduleCalendarFormat: CalendarFormat.week, + PreferencesFlag.scheduleStartWeekday: StartingDayOfWeek.monday, + PreferencesFlag.scheduleShowTodayBtn: true + }; + group("ScheduleViewModel - ", () { setUp(() async { // Setting up mocks - courseRepository = setupCourseRepositoryMock(); - settingsManager = setupSettingsManagerMock(); + courseRepositoryMock = setupCourseRepositoryMock(); + settingsManagerMock = setupSettingsManagerMock(); viewModel = ScheduleViewModel(intl: await setupAppIntl()); }); @@ -257,69 +265,65 @@ void main() { test( "first load from cache than call SignetsAPI to get the latest events", () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetScheduleActivities( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock); + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, + toReturn: settings); expect(await viewModel.futureToRun(), []); verifyInOrder([ - courseRepository.getCoursesActivities(fromCacheOnly: true), - courseRepository.getCoursesActivities() + settingsManagerMock.getScheduleSettings(), + courseRepositoryMock.getCoursesActivities(fromCacheOnly: true), + courseRepositoryMock.getCoursesActivities(), + courseRepositoryMock.coursesActivities, + courseRepositoryMock.coursesActivities, + courseRepositoryMock.getCourses(fromCacheOnly: true), + courseRepositoryMock.getScheduleActivities(), ]); - verifyNoMoreInteractions(courseRepository); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(courseRepositoryMock); + verifyNoMoreInteractions(settingsManagerMock); }); test("Signets throw an error while trying to get new events", () async { setupFlutterToastMock(); - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, fromCacheOnly: true); CourseRepositoryMock.stubGetCoursesActivitiesException( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, + courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, fromCacheOnly: true); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetScheduleActivities( - courseRepository as CourseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock); + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, + toReturn: settings); expect(await viewModel.futureToRun(), [], reason: "Even if SignetsAPI fails we should receives a list."); // Await until the call to get the activities from signets is sent - await untilCalled(courseRepository.getCoursesActivities()); + await untilCalled(courseRepositoryMock.getCoursesActivities()); verifyInOrder([ - courseRepository.getCoursesActivities(fromCacheOnly: true), - courseRepository.getCoursesActivities() + settingsManagerMock.getScheduleSettings(), + courseRepositoryMock.getCoursesActivities(fromCacheOnly: true), + courseRepositoryMock.getCoursesActivities() ]); - verifyNoMoreInteractions(courseRepository); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(courseRepositoryMock); + verifyNoMoreInteractions(settingsManagerMock); }); }); group("coursesActivities - ", () { test("build the list of activities sort by date", () async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); final expected = { @@ -329,23 +333,20 @@ void main() { expect(viewModel.coursesActivities, expected); - verify(courseRepository.coursesActivities).called(2); + verify(courseRepositoryMock.coursesActivities).called(2); - verifyNoMoreInteractions(courseRepository); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(courseRepositoryMock); + verifyNoMoreInteractions(settingsManagerMock); }); test( 'scheduleActivityIsSelected returns true when activityDescription is not labA or labB', () async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activitiesLabs); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN103", + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN103", toReturn: ActivityCode.labGroupA); await viewModel.assignScheduleActivities([ @@ -369,15 +370,13 @@ void main() { test( 'scheduleActivityIsSelected returns true when the course does not have an activity', () async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activitiesLabs); SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, + settingsManagerMock, PreferencesFlag.scheduleLaboratoryGroup, - classOneWithLaboratoryABscheduleActivities.first.courseAcronym, - toReturn: null); + classOneWithLaboratoryABscheduleActivities.first.courseAcronym); await viewModel.assignScheduleActivities([]); @@ -388,7 +387,7 @@ void main() { 'scheduleActivityIsSelected returns false when there is no activity selected', () async { SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, + settingsManagerMock, PreferencesFlag.scheduleLaboratoryGroup, classOneWithLaboratoryABscheduleActivities.first.courseAcronym, toReturn: ActivityCode.labGroupA); @@ -403,7 +402,7 @@ void main() { 'scheduleActivityIsSelected returns true when the courseGroup has an activity selected', () async { SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, + settingsManagerMock, PreferencesFlag.scheduleLaboratoryGroup, classOneWithLaboratoryABscheduleActivities.first.courseAcronym, toReturn: ActivityCode.labGroupA); @@ -417,39 +416,36 @@ void main() { group("coursesActivitiesFor - ", () { test("Get the correct list of activities for the specified day", () { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); final expected = [gen102, gen103]; expect(viewModel.coursesActivitiesFor(DateTime(2020, 1, 2)), expected); - verify(courseRepository.coursesActivities).called(2); + verify(courseRepositoryMock.coursesActivities).called(2); - verifyNoMoreInteractions(courseRepository); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(courseRepositoryMock); + verifyNoMoreInteractions(settingsManagerMock); }); test("If the day doesn't have any events, return an empty list.", () { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); expect(viewModel.coursesActivitiesFor(DateTime(2020, 1, 3)), isEmpty, reason: "There is no events for the 3rd Jan on activities"); - verify(courseRepository.coursesActivities).called(2); + verify(courseRepositoryMock.coursesActivities).called(2); - verifyNoMoreInteractions(courseRepository); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(courseRepositoryMock); + verifyNoMoreInteractions(settingsManagerMock); }); }); group("selectedDateEvents", () { test("The events of the date currently selected are return", () { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); final expected = [gen102, gen103]; @@ -457,17 +453,16 @@ void main() { // Setting up the viewmodel viewModel.coursesActivities; viewModel.selectedDate = DateTime(2020, 1, 2); - clearInteractions(courseRepository); + clearInteractions(courseRepositoryMock); expect(viewModel.selectedDateEvents(viewModel.selectedDate), expected); - verifyNoMoreInteractions(courseRepository); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(courseRepositoryMock); + verifyNoMoreInteractions(settingsManagerMock); }); test("The events of the date currently selected are return", () { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); final expected = []; @@ -475,27 +470,34 @@ void main() { // Setting up the viewmodel viewModel.coursesActivities; viewModel.selectedDate = DateTime(2020, 1, 3); - clearInteractions(courseRepository); + clearInteractions(courseRepositoryMock); expect(viewModel.selectedDateEvents(viewModel.selectedDate), expected); - verifyNoMoreInteractions(courseRepository); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(courseRepositoryMock); + verifyNoMoreInteractions(settingsManagerMock); }); }); group('selectedWeekEvents', () { final Map settingsStartingDayMonday = { PreferencesFlag.scheduleStartWeekday: StartingDayOfWeek.monday, + PreferencesFlag.scheduleCalendarFormat: CalendarFormat.month }; final Map settingsStartingDaySaturday = { PreferencesFlag.scheduleStartWeekday: StartingDayOfWeek.saturday, + PreferencesFlag.scheduleCalendarFormat: CalendarFormat.month + }; + final Map settingsStartingDaySunday = { + PreferencesFlag.scheduleStartWeekday: StartingDayOfWeek.sunday, + PreferencesFlag.scheduleCalendarFormat: CalendarFormat.month }; - test('selectedWeekEvents for starting day sunday', () { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + test('selectedWeekEvents for starting day sunday', () async { + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: weekOfActivities); + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, + toReturn: settingsStartingDaySunday); final expected = { DateTime(2020, 1, 5): [gen104], @@ -510,17 +512,16 @@ void main() { // Setting up the viewmodel viewModel.coursesActivities; viewModel.selectedDate = DateTime(2020, 1, 8); - clearInteractions(courseRepository); + await viewModel.loadSettings(); + clearInteractions(courseRepositoryMock); expect(viewModel.selectedWeekEvents(), expected); }); test('selectedWeekEvents for starting day monday', () async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settingsStartingDayMonday); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: weekOfActivities); final expected = { @@ -536,17 +537,15 @@ void main() { viewModel.coursesActivities; viewModel.selectedDate = DateTime(2020, 1, 7); await viewModel.loadSettings(); - clearInteractions(courseRepository); + clearInteractions(courseRepositoryMock); expect(viewModel.selectedWeekEvents(), expected); }); test('selectedWeekEvents for starting day saturday', () async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: settingsStartingDaySaturday); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: weekOfActivities); final expected = { @@ -562,7 +561,7 @@ void main() { viewModel.coursesActivities; viewModel.selectedDate = DateTime(2020, 1, 7); await viewModel.loadSettings(); - clearInteractions(courseRepository); + clearInteractions(courseRepositoryMock); expect(viewModel.selectedWeekEvents(), expected); }); @@ -572,8 +571,7 @@ void main() { test( 'Call SignetsAPI to get the coursesActivities than reload the coursesActivities', () async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activities); await viewModel.refresh(); @@ -586,19 +584,18 @@ void main() { expect(viewModel.coursesActivities, expected); verifyInOrder([ - courseRepository.getCoursesActivities(), - courseRepository.coursesActivities, - courseRepository.coursesActivities + courseRepositoryMock.getCoursesActivities(), + courseRepositoryMock.coursesActivities, + courseRepositoryMock.coursesActivities ]); - verifyNoMoreInteractions(courseRepository); + verifyNoMoreInteractions(courseRepositoryMock); }); }); group('loadSettings -', () { test('calendarFormat changing', () async { - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: { PreferencesFlag.scheduleCalendarFormat: CalendarFormat.month }); @@ -606,15 +603,11 @@ void main() { await viewModel.loadSettings(); expect(viewModel.calendarFormat, CalendarFormat.month); - verify(settingsManager.getScheduleSettings()).called(1); - verifyNoMoreInteractions(settingsManager); + verify(settingsManagerMock.getScheduleSettings()).called(1); + verifyNoMoreInteractions(settingsManagerMock); }); test('assignScheduleActivities - format the schedule activities in a map', () async { - // Test if null, return without doing any change to the schedule list - await viewModel.assignScheduleActivities(null); - expect(viewModel.scheduleActivitiesByCourse.entries.length, 0); - // Test if empty list is passed, do nothing await viewModel.assignScheduleActivities([]); expect(viewModel.scheduleActivitiesByCourse.entries.length, 0); @@ -640,7 +633,7 @@ void main() { // Test normal cases, with laboratory SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, + settingsManagerMock, PreferencesFlag.scheduleLaboratoryGroup, classOneWithLaboratoryABscheduleActivities.first.courseAcronym, toReturn: ActivityCode.labGroupA); @@ -653,28 +646,20 @@ void main() { test( 'loadSettingsScheduleActivities - test when one is selected from one group', () async { - CourseRepositoryMock.stubGetCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubGetCoursesActivities(courseRepositoryMock, toReturn: activities); - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: true, - toReturn: courses); - CourseRepositoryMock.stubGetCourses( - courseRepository as CourseRepositoryMock, - fromCacheOnly: false); - CourseRepositoryMock.stubGetScheduleActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock, + fromCacheOnly: true, toReturn: courses); + CourseRepositoryMock.stubGetCourses(courseRepositoryMock); + CourseRepositoryMock.stubGetScheduleActivities(courseRepositoryMock, toReturn: classOneWithLaboratoryABscheduleActivities); - SettingsManagerMock.stubGetScheduleSettings( - settingsManager as SettingsManagerMock, + SettingsManagerMock.stubGetScheduleSettings(settingsManagerMock, toReturn: { PreferencesFlag.scheduleCalendarFormat: CalendarFormat.month }); SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, + settingsManagerMock, PreferencesFlag.scheduleLaboratoryGroup, classOneWithLaboratoryABscheduleActivities.first.courseAcronym, toReturn: ActivityCode.labGroupA); @@ -682,19 +667,19 @@ void main() { expect(await viewModel.futureToRun(), activities, reason: "Even if SignetsAPI fails we should receives a list."); - List listScheduleActivities; - await courseRepository.getScheduleActivities().then((value) { + late List listScheduleActivities; + await courseRepositoryMock.getScheduleActivities().then((value) { listScheduleActivities = value; }); await viewModel.assignScheduleActivities(listScheduleActivities); - await untilCalled(courseRepository.getCoursesActivities()); - await untilCalled(courseRepository.getScheduleActivities()); + await untilCalled(courseRepositoryMock.getCoursesActivities()); + await untilCalled(courseRepositoryMock.getScheduleActivities()); verifyInOrder([ - courseRepository.getCoursesActivities(fromCacheOnly: true), - courseRepository.getCoursesActivities(), - courseRepository.getScheduleActivities( + courseRepositoryMock.getCoursesActivities(fromCacheOnly: true), + courseRepositoryMock.getCoursesActivities(), + courseRepositoryMock.getScheduleActivities( fromCacheOnly: anyNamed("fromCacheOnly")) ]); @@ -707,20 +692,17 @@ void main() { classOneWithLaboratoryABscheduleActivities.first.courseAcronym], "Laboratoire (Groupe A)"); - verify(settingsManager.getDynamicString(any, any)).called(2); + verify(settingsManagerMock.getDynamicString(any, any)).called(2); }); test( 'coursesActivities - should fill coursesActivities with the activities', () async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activitiesLabs); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN103", + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN103", toReturn: ActivityCode.labGroupA); await viewModel.assignScheduleActivities([ @@ -746,14 +728,11 @@ void main() { test( 'coursesActivities - should fill coursesActivities with the activities with no LabB for GEN103', () async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activitiesLabs); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN103", + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN103", toReturn: ActivityCode.labGroupA); await viewModel.assignScheduleActivities([ @@ -779,14 +758,11 @@ void main() { test( 'coursesActivities - should fill coursesActivities with the activities with no LabA for GEN103', () async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activitiesLabs); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN103", + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN103", toReturn: ActivityCode.labGroupB); await viewModel.assignScheduleActivities([ @@ -812,15 +788,11 @@ void main() { test( 'coursesActivities - should fill coursesActivities with all the activities if none are selected', () async { - CourseRepositoryMock.stubCoursesActivities( - courseRepository as CourseRepositoryMock, + CourseRepositoryMock.stubCoursesActivities(courseRepositoryMock, toReturn: activitiesLabs); - SettingsManagerMock.stubGetDynamicString( - settingsManager as SettingsManagerMock, - PreferencesFlag.scheduleLaboratoryGroup, - "GEN103", - toReturn: null); + SettingsManagerMock.stubGetDynamicString(settingsManagerMock, + PreferencesFlag.scheduleLaboratoryGroup, "GEN103"); await viewModel.assignScheduleActivities([]); diff --git a/test/viewmodels/settings_viewmodel_test.dart b/test/viewmodels/settings_viewmodel_test.dart index 6e9f696fb..a3d01d24e 100644 --- a/test/viewmodels/settings_viewmodel_test.dart +++ b/test/viewmodels/settings_viewmodel_test.dart @@ -13,15 +13,15 @@ import 'package:notredame/core/viewmodels/settings_viewmodel.dart'; import '../helpers.dart'; import '../mock/managers/settings_manager_mock.dart'; -SettingsViewModel viewModel; +late SettingsViewModel viewModel; void main() { - SettingsManager settingsManager; + late SettingsManagerMock settingsManagerMock; group("SettingsViewModel - ", () { setUp(() async { // Setting up mocks - settingsManager = setupSettingsManagerMock(); + settingsManagerMock = setupSettingsManagerMock(); final AppIntl intl = await setupAppIntl(); viewModel = SettingsViewModel(intl: intl); @@ -33,71 +33,70 @@ void main() { group("futureToRun - ", () { test("The settings are correctly loaded and sets", () async { - SettingsManagerMock.stubLocale(settingsManager as SettingsManagerMock); + SettingsManagerMock.stubLocale(settingsManagerMock); - SettingsManagerMock.stubThemeMode( - settingsManager as SettingsManagerMock); + SettingsManagerMock.stubThemeMode(settingsManagerMock); await viewModel.futureToRun(); expect(viewModel.currentLocale, 'English'); expect(viewModel.selectedTheme, ThemeMode.system); verifyInOrder([ - settingsManager.fetchLanguageAndThemeMode(), - settingsManager.locale, - settingsManager.themeMode + settingsManagerMock.fetchLanguageAndThemeMode(), + settingsManagerMock.locale, + settingsManagerMock.themeMode ]); - verifyNoMoreInteractions(settingsManager); + verifyNoMoreInteractions(settingsManagerMock); }); }); group("setter theme - ", () { test("can set system theme option", () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, PreferencesFlag.theme); + settingsManagerMock, PreferencesFlag.theme); // Call the setter. viewModel.selectedTheme = ThemeMode.system; - await untilCalled(settingsManager.setThemeMode(ThemeMode.system)); + await untilCalled(settingsManagerMock.setThemeMode(ThemeMode.system)); expect(viewModel.selectedTheme, ThemeMode.system); expect(viewModel.isBusy, false); - verify(settingsManager.setThemeMode(ThemeMode.system)).called(1); - verifyNoMoreInteractions(settingsManager); + verify(settingsManagerMock.setThemeMode(ThemeMode.system)).called(1); + verifyNoMoreInteractions(settingsManagerMock); }); test("can set dark theme option", () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, PreferencesFlag.theme); + settingsManagerMock, PreferencesFlag.theme); // Call the setter. viewModel.selectedTheme = ThemeMode.dark; - await untilCalled(settingsManager.setThemeMode(ThemeMode.dark)); + await untilCalled(settingsManagerMock.setThemeMode(ThemeMode.dark)); expect(viewModel.selectedTheme, ThemeMode.dark); expect(viewModel.isBusy, false); - verify(settingsManager.setThemeMode(ThemeMode.dark)).called(1); - verifyNoMoreInteractions(settingsManager); + verify(settingsManagerMock.setThemeMode(ThemeMode.dark)).called(1); + verifyNoMoreInteractions(settingsManagerMock); }); test("can set light theme option", () async { SettingsManagerMock.stubSetString( - settingsManager as SettingsManagerMock, PreferencesFlag.theme); + settingsManagerMock, PreferencesFlag.theme); // Call the setter. viewModel.selectedTheme = ThemeMode.light; - await untilCalled(settingsManager.setThemeMode(ThemeMode.light)); + await untilCalled(settingsManagerMock.setThemeMode(ThemeMode.light)); expect(viewModel.selectedTheme, ThemeMode.light); expect(viewModel.isBusy, false); - verify(settingsManager.setThemeMode(ThemeMode.light)).called(1); - verifyNoMoreInteractions(settingsManager); + verify(settingsManagerMock.setThemeMode(ThemeMode.light)).called(1); + verifyNoMoreInteractions(settingsManagerMock); }); }); }); diff --git a/test/viewmodels/startup_viewmodel_test.dart b/test/viewmodels/startup_viewmodel_test.dart index c2fc6b5f3..211173905 100644 --- a/test/viewmodels/startup_viewmodel_test.dart +++ b/test/viewmodels/startup_viewmodel_test.dart @@ -17,34 +17,32 @@ import '../helpers.dart'; import '../mock/managers/settings_manager_mock.dart'; import '../mock/managers/user_repository_mock.dart'; import '../mock/services/internal_info_service_mock.dart'; +import '../mock/services/navigation_service_mock.dart'; import '../mock/services/networking_service_mock.dart'; import '../mock/services/preferences_service_mock.dart'; import '../mock/services/siren_flutter_service_mock.dart'; void main() { - NavigationService navigationService; - UserRepositoryMock userRepositoryMock; - SettingsManagerMock settingsManagerMock; - PreferencesServiceMock preferencesServiceMock; - NetworkingServiceMock networkingService; - InternalInfoServiceMock internalInfoServiceMock; - SirenFlutterServiceMock sirenFlutterServiceMock; + late NavigationServiceMock navigationServiceMock; + late UserRepositoryMock userRepositoryMock; + late SettingsManagerMock settingsManagerMock; + late PreferencesServiceMock preferencesServiceMock; + late NetworkingServiceMock networkingServiceMock; + late InternalInfoServiceMock internalInfoServiceMock; + late SirenFlutterServiceMock sirenFlutterServiceMock; - StartUpViewModel viewModel; + late StartUpViewModel viewModel; group('StartupViewModel - ', () { setUp(() async { setupAnalyticsServiceMock(); - navigationService = setupNavigationServiceMock(); - settingsManagerMock = setupSettingsManagerMock() as SettingsManagerMock; - preferencesServiceMock = - setupPreferencesServiceMock() as PreferencesServiceMock; - userRepositoryMock = setupUserRepositoryMock() as UserRepositoryMock; - networkingService = setupNetworkingServiceMock() as NetworkingServiceMock; - internalInfoServiceMock = - setupInternalInfoServiceMock() as InternalInfoServiceMock; - sirenFlutterServiceMock = - setupSirenFlutterServiceMock() as SirenFlutterServiceMock; + navigationServiceMock = setupNavigationServiceMock(); + settingsManagerMock = setupSettingsManagerMock(); + preferencesServiceMock = setupPreferencesServiceMock(); + userRepositoryMock = setupUserRepositoryMock(); + networkingServiceMock = setupNetworkingServiceMock(); + internalInfoServiceMock = setupInternalInfoServiceMock(); + sirenFlutterServiceMock = setupSirenFlutterServiceMock(); setupLogger(); viewModel = StartUpViewModel(); @@ -64,7 +62,7 @@ void main() { test('sign in successful', () async { UserRepositoryMock.stubSilentAuthenticate(userRepositoryMock); UserRepositoryMock.stubWasPreviouslyLoggedIn(userRepositoryMock); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); InternalInfoServiceMock.stubGetPackageInfo(internalInfoServiceMock, version: "4.0.0"); SettingsManagerMock.stubGetString( @@ -73,7 +71,7 @@ void main() { await viewModel.handleStartUp(); - verify(navigationService.pushNamedAndRemoveUntil( + verify(navigationServiceMock.pushNamedAndRemoveUntil( RouterPaths.dashboard, RouterPaths.dashboard, UpdateCode.none)); }); @@ -83,7 +81,7 @@ void main() { UserRepositoryMock.stubSilentAuthenticate(userRepositoryMock, toReturn: false); UserRepositoryMock.stubWasPreviouslyLoggedIn(userRepositoryMock); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); InternalInfoServiceMock.stubGetPackageInfo(internalInfoServiceMock, version: "4.0.0"); SettingsManagerMock.stubGetString( @@ -98,11 +96,11 @@ void main() { verifyInOrder([ settingsManagerMock.getBool(PreferencesFlag.languageChoice), - navigationService.pop(), - navigationService.pushNamed(RouterPaths.login) + navigationServiceMock.pop(), + navigationServiceMock.pushNamed(RouterPaths.login) ]); - verifyNoMoreInteractions(navigationService); + verifyNoMoreInteractions(navigationServiceMock); }); test( @@ -111,7 +109,7 @@ void main() { UserRepositoryMock.stubSilentAuthenticate(userRepositoryMock, toReturn: false); UserRepositoryMock.stubWasPreviouslyLoggedIn(userRepositoryMock); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); InternalInfoServiceMock.stubGetPackageInfo(internalInfoServiceMock, version: "4.0.0"); SettingsManagerMock.stubGetString( @@ -122,18 +120,18 @@ void main() { verifyInOrder([ settingsManagerMock.getBool(PreferencesFlag.languageChoice), - navigationService.pushNamed(RouterPaths.chooseLanguage), + navigationServiceMock.pushNamed(RouterPaths.chooseLanguage), settingsManagerMock.setBool(PreferencesFlag.languageChoice, true) ]); - verifyNoMoreInteractions(navigationService); + verifyNoMoreInteractions(navigationServiceMock); }); test('verify discovery flags are bool if version mismatch', () async { const String versionToSave = "4.1.0"; UserRepositoryMock.stubSilentAuthenticate(userRepositoryMock); UserRepositoryMock.stubWasPreviouslyLoggedIn(userRepositoryMock); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); InternalInfoServiceMock.stubGetPackageInfo(internalInfoServiceMock, version: versionToSave); SettingsManagerMock.stubGetString( @@ -186,7 +184,7 @@ void main() { test('verify discovery flags are not changed for same version', () async { UserRepositoryMock.stubSilentAuthenticate(userRepositoryMock); UserRepositoryMock.stubWasPreviouslyLoggedIn(userRepositoryMock); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); InternalInfoServiceMock.stubGetPackageInfo(internalInfoServiceMock, version: "4.0.0"); SettingsManagerMock.stubGetString( @@ -205,7 +203,7 @@ void main() { () async { UserRepositoryMock.stubSilentAuthenticate(userRepositoryMock); UserRepositoryMock.stubWasPreviouslyLoggedIn(userRepositoryMock); - NetworkingServiceMock.stubHasConnectivity(networkingService); + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); InternalInfoServiceMock.stubGetPackageInfo(internalInfoServiceMock, version: "4.0.0"); SettingsManagerMock.stubSetString( diff --git a/test/viewmodels/web_link_card_viewmodel_test.dart b/test/viewmodels/web_link_card_viewmodel_test.dart index cdd207b5c..290c835dd 100644 --- a/test/viewmodels/web_link_card_viewmodel_test.dart +++ b/test/viewmodels/web_link_card_viewmodel_test.dart @@ -11,20 +11,23 @@ import 'package:notredame/core/managers/settings_manager.dart'; import 'package:notredame/core/models/quick_link.dart'; import 'package:notredame/core/services/analytics_service.dart'; import 'package:notredame/core/services/internal_info_service.dart'; -import 'package:notredame/core/services/launch_url_service.dart'; import 'package:notredame/core/services/navigation_service.dart'; import 'package:notredame/core/viewmodels/web_link_card_viewmodel.dart'; import '../helpers.dart'; +import '../mock/services/analytics_service_mock.dart'; import '../mock/services/internal_info_service_mock.dart'; +import '../mock/services/launch_url_service_mock.dart'; +import '../mock/services/navigation_service_mock.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); - NavigationService navigationService; - AnalyticsService analyticsService; - InternalInfoService internalInfoService; - LaunchUrlService launchUrlService; - WebLinkCardViewModel viewModel; + late NavigationServiceMock navigationServiceMock; + late AnalyticsServiceMock analyticsServiceMock; + late InternalInfoServiceMock internalInfoServiceMock; + late LaunchUrlServiceMock launchUrlServiceMock; + + late WebLinkCardViewModel viewModel; final quickLink = QuickLink( id: 1, image: const Icon(Icons.ac_unit), name: 'test', link: 'testlink'); @@ -33,10 +36,10 @@ void main() { group('WebLinkCardViewModel - ', () { setUp(() async { - navigationService = setupNavigationServiceMock(); - analyticsService = setupAnalyticsServiceMock(); - internalInfoService = setupInternalInfoServiceMock(); - launchUrlService = setupLaunchUrlServiceMock(); + navigationServiceMock = setupNavigationServiceMock(); + analyticsServiceMock = setupAnalyticsServiceMock(); + internalInfoServiceMock = setupInternalInfoServiceMock(); + launchUrlServiceMock = setupLaunchUrlServiceMock(); setupSettingsManagerMock(); setupLogger(); @@ -46,8 +49,8 @@ void main() { tearDown(() { unregister(); - clearInteractions(analyticsService); - clearInteractions(launchUrlService); + clearInteractions(analyticsServiceMock); + clearInteractions(launchUrlServiceMock); unregister(); unregister(); unregister(); @@ -57,21 +60,21 @@ void main() { test('navigate to security', () async { await viewModel.onLinkClicked(securityQuickLink, Brightness.light); - verify( - analyticsService.logEvent("QuickLink", "QuickLink clicked: test")); - verify(navigationService.pushNamed(RouterPaths.security)); - verifyNoMoreInteractions(navigationService); + verify(analyticsServiceMock.logEvent( + "QuickLink", "QuickLink clicked: test")); + verify(navigationServiceMock.pushNamed(RouterPaths.security)); + verifyNoMoreInteractions(navigationServiceMock); }); test('navigate to web view if launchInBrowser throw', () async { InternalInfoServiceMock.stubGetDeviceInfoForErrorReporting( - internalInfoService as InternalInfoServiceMock); + internalInfoServiceMock); await viewModel.onLinkClicked(quickLink, Brightness.light); - verify( - launchUrlService.launchInBrowser(quickLink.link, Brightness.light)); - verifyNoMoreInteractions(navigationService); + verify(launchUrlServiceMock.launchInBrowser( + quickLink.link, Brightness.light)); + verifyNoMoreInteractions(navigationServiceMock); }); }); });