diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4bb6e7e..52497a7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,7 @@ image: cirrusci/flutter:3.7.5 stages: - analyze + - test before_script: - flutter pub get @@ -13,3 +14,10 @@ analyze: - flutter analyze only: - merge_requests + +test: + stage: test + script: + - flutter test + only: + - merge_requests diff --git a/test/unit_tests/date_service_test.dart b/test/unit_tests/date_service_test.dart index 9afba5e..2791f7d 100644 --- a/test/unit_tests/date_service_test.dart +++ b/test/unit_tests/date_service_test.dart @@ -1,37 +1,112 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:timezone/data/latest.dart' as tz; import 'package:smoke_cess_app/services/date_service.dart'; +import 'package:smoke_cess_app/services/pages_service.dart'; +import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/timezone.dart'; void main() { - tz.initializeTimeZones(); - test('IsSameDay: false', () { - bool result = - isSameDay(DateTime.now(), DateTime.now().add(const Duration(days: 1))); - expect(result, false); + group('Helpers', () { + group('isSameDay', () { + test('returns true when dates are the same day', () { + DateTime dateA = DateTime.now(); + DateTime dateB = DateTime(dateA.year, dateA.month, dateA.day, 10, 0); + expect(isSameDay(dateA, dateB), true); + }); + + test('returns false when dates are different days', () { + DateTime dateA = DateTime.now(); + DateTime dateB = + DateTime(dateA.year, dateA.month, dateA.day + 1, 10, 0); + expect(isSameDay(dateA, dateB), false); + }); + + test('returns false when one date is null', () { + DateTime? dateA = DateTime.now(); + DateTime? dateB; + expect(isSameDay(dateA, dateB), false); + }); + }); + + group('createTZDateTimes', () { + test('returns empty list when selectedDays is null', () { + List tzDateTimes = createTZDateTimes(null, 10, 0); + expect(tzDateTimes, []); + }); + + test('returns empty list when selectedHours is null', () { + List tzDateTimes = createTZDateTimes(['Montag'], null, 0); + expect(tzDateTimes, []); + }); + + test('returns empty list when selectedMinutes is null', () { + List tzDateTimes = createTZDateTimes(['Montag'], 10, null); + expect(tzDateTimes, []); + }); + + test('returns empty list when no valid dates are found', () { + List tzDateTimes = createTZDateTimes(['Montag'], 10, 0); + expect(tzDateTimes, []); + }); + + test('returns list of valid dates', () { + List tzDateTimes = createTZDateTimes(['Montag'], 10, 0); + expect(tzDateTimes.length, greaterThan(0)); + }); + }); + + group('getTimeTill', () { + test('returns time till next mood', () async { + Duration duration = await getTimeTill(Pages.mood); + expect(duration, isNotNull); + }); + + test('returns time till next sleep', () async { + Duration duration = await getTimeTill(Pages.sleep); + expect(duration, isNotNull); + }); + //? + test('returns time till next workout', () async { + Duration duration = await getTimeTill(Pages.relapse); + expect(duration, isNotNull); + }); + //? + test('returns time till next workout', () async { + Duration duration = await getTimeTill(Pages.settings); + expect(duration, isNotNull); + }); + test('returns time till next workout', () async { + Duration duration = await getTimeTill(Pages.timer); + expect(duration, isNotNull); + }); + }); }); - test('IsSameDay: true', () { - bool result = isSameDay(DateTime.now(), DateTime.now()); - expect(result, true); - }); - test('CreateTZDateTimes: all the same days as today in next 6 weeks at 23:59', - () { - List selectedDays = [ - weekDays.keys.elementAt(DateTime.now().weekday - 1) - ]; - List expected = []; - DateTime now = DateTime.now(); - final Duration offset = now.timeZoneOffset; - final DateTime date = - DateTime(now.year, now.month, now.day, 23, 59, 0, 0, 0); - for (int i = 0; i <= trainingTime; i = i + 7) { - expected.add( - TZDateTime.local(date.year, date.month, date.day, 23, 59, 0, 0, 0) - .add(Duration(days: i)) - // subtract offset since TZDateTime uses the UTC Timezone - .subtract(offset)); - } - List result = createTZDateTimes(selectedDays, 23, 59); - expect(result, expected); + + group('Helper functions', () { + setUp(() async { + tz.initializeTimeZones(); + }); + + test('isSameDay returns true if two dates are the same day', () { + final date1 = DateTime(2022, 3, 6); + final date2 = DateTime(2022, 3, 6, 15, 30); + final result = isSameDay(date1, date2); + expect(result, true); + }); + + test('isSameDay returns false if two dates are not the same day', () { + final date1 = DateTime(2022, 3, 6); + final date2 = DateTime(2022, 3, 7); + final result = isSameDay(date1, date2); + expect(result, false); + }); + + test('createTZDateTimes returns a list of TZDateTime objects', () async { + final selectedDays = ['Montag', 'Dienstag']; + final selectedHours = 12; + final selectedMinutes = 30; + final result = + await createTZDateTimes(selectedDays, selectedHours, selectedMinutes); + expect(result, isA>()); + }); }); } diff --git a/test/unit_tests/settings_provider_test.dart b/test/unit_tests/settings_provider_test.dart new file mode 100644 index 0000000..4d43184 --- /dev/null +++ b/test/unit_tests/settings_provider_test.dart @@ -0,0 +1,28 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:smoke_cess_app/providers/settings_provider.dart'; + +void main() { + group('SettingsProvider', () { + test('initial state', () { + final provider = SettingsProvider(); + expect(provider.settings, isNull); + expect(provider.initialized, isFalse); + expect(provider.scanning, isFalse); + }); + + test('initialize settings', () async { + final provider = SettingsProvider(); + provider.initSettings(); + expect(provider.settings, isNotNull); + expect(provider.initialized, isTrue); + }); + + test('set scanning', () { + final provider = SettingsProvider(); + provider.scanning = true; + expect(provider.scanning, isTrue); + provider.scanning = false; + expect(provider.scanning, isFalse); + }); + }); +} diff --git a/test/unit_tests/workout_provider_test.dart b/test/unit_tests/workout_provider_test.dart new file mode 100644 index 0000000..31d7784 --- /dev/null +++ b/test/unit_tests/workout_provider_test.dart @@ -0,0 +1,75 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:smoke_cess_app/providers/audio_provider.dart'; +import 'package:smoke_cess_app/providers/timer_provider.dart'; +import 'package:smoke_cess_app/providers/workout_provider.dart'; + +void main() { + group('WorkoutProvider', () { + late WorkoutProvider workoutProvider; + late TimerProvider timerProvider; + late AudioProvider audioProvider; + + setUp(() { + timerProvider = TimerProvider(); + audioProvider = AudioProvider(); + workoutProvider = WorkoutProvider(timerProvider, audioProvider); + }); + + test('initial values', () { + expect(workoutProvider.isWorkoutStarted, false); + expect(workoutProvider.isWorkoutComplete, false); + expect(workoutProvider.motivationBefore, 50); + expect(workoutProvider.motivationAfter, 50); + expect(workoutProvider.currentPhase, WorkoutPhases.warmUp); + expect(workoutProvider.currentPhaseDuration, const Duration(seconds: 5)); + expect(workoutProvider.isPhaseComplete, false); + expect(workoutProvider.currentPhaseColor, Colors.green); + expect(workoutProvider.currentPhaseTitle, 'Warm Up'); + }); + + test('next phase', () { + workoutProvider.nextPhase(); + + expect(workoutProvider.currentPhase, WorkoutPhases.highIntensity); + expect(workoutProvider.currentPhaseDuration, const Duration(seconds: 4)); + expect(workoutProvider.isPhaseComplete, false); + expect(workoutProvider.currentPhaseColor, Colors.red); + expect(workoutProvider.currentPhaseTitle, 'High Intensity'); + }); + + test('start workout', () { + workoutProvider.startWorkout(); + + expect(workoutProvider.isWorkoutStarted, true); + expect(workoutProvider.isWorkoutComplete, false); + expect(workoutProvider.currentPhase, WorkoutPhases.warmUp); + expect(workoutProvider.currentPhaseDuration, const Duration(seconds: 5)); + expect(workoutProvider.isPhaseComplete, false); + }); + + test('stop workout', () { + workoutProvider.startWorkout(); + workoutProvider.stopWorkout(); + + expect(workoutProvider.isWorkoutStarted, false); + expect(workoutProvider.isWorkoutComplete, true); + }); + + test('interrupt workout', () { + workoutProvider.startWorkout(); + workoutProvider.interruptWorkout(); + + expect(workoutProvider.isWorkoutStarted, false); + expect(workoutProvider.isWorkoutComplete, false); + }); + + // test('save workout', () { + // workoutProvider.motivationBefore = 70; + // workoutProvider.motivationAfter = 80; + // workoutProvider.saveWorkout(); + + // // -> hier in datenbank nachschauen obs geklappt hat + // }); + }); +} diff --git a/test/widget_tests/history_list_test.dart b/test/widget_tests/history_list_test.dart new file mode 100644 index 0000000..7354473 --- /dev/null +++ b/test/widget_tests/history_list_test.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'package:intl/intl.dart'; +import 'package:smoke_cess_app/widgets/entry_detail_widget.dart'; +import 'package:smoke_cess_app/widgets/history_list_widget.dart'; + +void main() { + group('HistoryList', () { + initializeDateFormatting('de'); + final List history = ['1', '2', '3']; + dateSelector(String p0) => DateTime.now(); + entryDataSelector(String p0) => p0; + + testWidgets('should produce #(history.length) EntryDetail Widgets', + (widgetTester) async { + await widgetTester.pumpWidget(WrappedHistoryList( + history: history, + dateSelector: dateSelector, + entryDataSelector: entryDataSelector)); + final widgetFinder = find.byType(EntryDetail); + final dateFinder = + find.text(DateFormat.MMMd('de').format(DateTime.now())); + //HistoryList uses Icons.circle as default if there is no IconData provided + final iconFinder = find.byIcon(Icons.circle); + expect(widgetFinder, findsNWidgets(history.length)); + expect(dateFinder, findsNWidgets(history.length)); + expect(iconFinder, findsNWidgets(history.length)); + }); + + testWidgets( + 'should render correct icon if specified with icon selection function', + (widgetTester) async { + IconData iconDataSelector(String p0) => Icons.email; + + await widgetTester.pumpWidget(WrappedHistoryList( + history: history, + dateSelector: dateSelector, + entryDataSelector: entryDataSelector, + iconDataSelector: iconDataSelector, + )); + final circleIconFinder = find.byIcon(Icons.circle); + final correctIconFinder = find.byIcon(Icons.email); + + expect(circleIconFinder, findsNothing); + expect(correctIconFinder, findsNWidgets(history.length)); + }); + + testWidgets('icon field should overwrite iconSelector', + (widgetTester) async { + const IconData icon = Icons.abc; + IconData iconDataSelector(String p0) => Icons.email; + await widgetTester.pumpWidget(WrappedHistoryList( + history: history, + dateSelector: dateSelector, + entryDataSelector: entryDataSelector, + iconDataSelector: iconDataSelector, + icon: Icons.abc, + )); + final circleIconFinder = find.byIcon(Icons.circle); + final emailIconFinder = find.byIcon(Icons.email); + final correctIconFinder = find.byIcon(icon); + + expect(circleIconFinder, findsNothing); + expect(emailIconFinder, findsNothing); + expect(correctIconFinder, findsNWidgets(history.length)); + }); + + testWidgets( + 'EntryDetails should display ExpansionTiles if entryComment is given', + (widgetTester) async { + String entryCommentSelector(String p0) => ""; + await widgetTester.pumpWidget(WrappedHistoryList( + history: history, + dateSelector: dateSelector, + entryDataSelector: entryDataSelector, + entryCommentSelector: entryCommentSelector, + )); + final expansionTileFinder = find.byType(ExpansionTile); + expect(expansionTileFinder, findsNWidgets(history.length)); + }); + + testWidgets( + 'EntryDetails should display ExpansionTiles if entryComment is not given', + (widgetTester) async { + String entryCommentSelector(String p0) => ""; + await widgetTester.pumpWidget(WrappedHistoryList( + history: history, + dateSelector: dateSelector, + entryDataSelector: entryDataSelector, + entryCommentSelector: entryCommentSelector, + )); + final listTileFinder = find.byType(ListTile); + expect(listTileFinder, findsNWidgets(history.length)); + }); + }); +} + +class WrappedHistoryList extends StatelessWidget { + final List history; + final DateTime Function(T) dateSelector; + final String Function(T) entryDataSelector; + final IconData Function(T)? iconDataSelector; + final String Function(T)? entryCommentSelector; + final IconData? icon; + const WrappedHistoryList( + {super.key, + required this.history, + required this.dateSelector, + required this.entryDataSelector, + this.iconDataSelector, + this.icon, + this.entryCommentSelector}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + body: Column(children: [ + HistoryList( + history: history, + dateSelector: dateSelector, + entryDataSelector: entryDataSelector, + icon: icon, + iconDataSelector: iconDataSelector, + entryCommentSelector: entryCommentSelector, + ) + ]))); + } +}