From 7ce3fca31d32ad37e71adaa1d7ef9e531c733dd6 Mon Sep 17 00:00:00 2001 From: Ishaan <68785503+EmeraldWither@users.noreply.github.com> Date: Tue, 25 Jun 2024 21:23:04 -0400 Subject: [PATCH 1/5] create no connection test --- .../robot_notifications_listener_test.dart | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/services/robot_notifications_listener_test.dart diff --git a/test/services/robot_notifications_listener_test.dart b/test/services/robot_notifications_listener_test.dart new file mode 100644 index 00000000..ae03a94f --- /dev/null +++ b/test/services/robot_notifications_listener_test.dart @@ -0,0 +1,42 @@ +import 'package:elastic_dashboard/services/robot_notifications_listener.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +import '../test_util.dart'; +import '../test_util.mocks.dart'; + +// Create a mock class for the onNotification callback +class MockNotificationCallback extends Mock { + void call(String? title, String? description, Icon? icon); +} + +void main() { + test("Robot Notifications (No Connection) ", () { + MockNTConnection mockConnection = createMockOfflineNT4(); + MockNT4Subscription mockSub = MockNT4Subscription(); + + when(mockConnection.subscribeAll(any, any)).thenReturn(mockSub); + + // Create a mock for the onNotification callback + MockNotificationCallback mockOnNotification = MockNotificationCallback(); + + RobotNotificationsListener notifications = RobotNotificationsListener( + ntConnection: mockConnection, + onNotification: mockOnNotification.call, + ); + + notifications.listen(); + + // Verify that subscribeAll was called with the specific parameters + verify(mockConnection.subscribeAll('/Elastic/robotnotifications', 0.2)) + .called(1); + verify(mockConnection.addDisconnectedListener(any)).called(1); + + // Verify that no other interactions have been made with the mockConnection + verifyNoMoreInteractions(mockConnection); + + // Verify that the onNotification callback was never called + verifyNever(mockOnNotification.call(any, any, any)); + }); +} From 951c83b75639d792e9feb9c6f39bd48f0e8b93a4 Mon Sep 17 00:00:00 2001 From: Ishaan <68785503+EmeraldWither@users.noreply.github.com> Date: Tue, 25 Jun 2024 22:05:58 -0400 Subject: [PATCH 2/5] add inital connections without notifications --- .../robot_notifications_listener_test.dart | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/test/services/robot_notifications_listener_test.dart b/test/services/robot_notifications_listener_test.dart index ae03a94f..08c49e55 100644 --- a/test/services/robot_notifications_listener_test.dart +++ b/test/services/robot_notifications_listener_test.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; + +import 'package:elastic_dashboard/services/nt4_client.dart'; import 'package:elastic_dashboard/services/robot_notifications_listener.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -39,4 +42,122 @@ void main() { // Verify that the onNotification callback was never called verifyNever(mockOnNotification.call(any, any, any)); }); + test( + "Robot Notifications (Initial Connection | No Notifications)", + () { + MockNTConnection mockConnection = createMockOnlineNT4( + virtualTopics: [ + NT4Topic( + name: '/Elastic/robotnotifications', + type: NT4TypeStr.kString, + properties: {}) + ], + ); + MockNT4Subscription mockSub = MockNT4Subscription(); + + when(mockConnection.subscribeAll(any, any)).thenReturn(mockSub); + + // Create a mock for the onNotification callback + MockNotificationCallback mockOnNotification = MockNotificationCallback(); + + RobotNotificationsListener notifications = RobotNotificationsListener( + ntConnection: mockConnection, + onNotification: mockOnNotification.call, + ); + + notifications.listen(); + + // Verify that subscribeAll was called with the specific parameters + verify(mockConnection.subscribeAll('/Elastic/robotnotifications', 0.2)) + .called(1); + verify(mockConnection.addDisconnectedListener(any)).called(1); + + // Verify that no other interactions have been made with the mockConnection + verifyNoMoreInteractions(mockConnection); + + // Verify that the onNotification callback was never called + verifyNever(mockOnNotification.call(any, any, any)); + }, + ); + + test( + "Robot Notifications (Initial Connection | No Notifications)", + () { + MockNTConnection mockConnection = createMockOnlineNT4( + virtualTopics: [ + NT4Topic( + name: '/Elastic/robotnotifications', + type: NT4TypeStr.kString, + properties: {}) + ], + ); + MockNT4Subscription mockSub = MockNT4Subscription(); + + when(mockConnection.subscribeAll(any, any)).thenReturn(mockSub); + + // Create a mock for the onNotification callback + MockNotificationCallback mockOnNotification = MockNotificationCallback(); + + RobotNotificationsListener notifications = RobotNotificationsListener( + ntConnection: mockConnection, + onNotification: mockOnNotification.call, + ); + + notifications.listen(); + + // Verify that subscribeAll was called with the specific parameters + verify(mockConnection.subscribeAll('/Elastic/robotnotifications', 0.2)) + .called(1); + verify(mockConnection.addDisconnectedListener(any)).called(1); + + // Verify that no other interactions have been made with the mockConnection + verifyNoMoreInteractions(mockConnection); + + // Verify that the onNotification callback was never called + verifyNever(mockOnNotification.call(any, any, any)); + }, + ); + test( + "Robot Notifications (Initial Connection | Existing Notifications)", + () { + Map initData = { + 'title': 'Title1', + 'description': 'Description1', + 'level': 'INFO' + }; + print('${jsonEncode(initData)}'); + MockNTConnection mockConnection = createMockOnlineNT4(virtualTopics: [ + NT4Topic( + name: '/Elastic/robotnotifications', + type: NT4TypeStr.kString, + properties: {}) + ]); + + MockNT4Subscription mockSub = MockNT4Subscription(); + + when(mockConnection.subscribeAll(any, any)).thenReturn(mockSub); + + // Create a mock for the onNotification callback + MockNotificationCallback mockOnNotification = MockNotificationCallback(); + + RobotNotificationsListener notifications = RobotNotificationsListener( + ntConnection: mockConnection, + onNotification: mockOnNotification.call, + ); + + notifications.listen(); + //TODO add stuff + + // Verify that subscribeAll was called with the specific parameters + verify(mockConnection.subscribeAll('/Elastic/robotnotifications', 0.2)) + .called(1); + verify(mockConnection.addDisconnectedListener(any)).called(1); + + // Verify that no other interactions have been made with the mockConnection + verifyNoMoreInteractions(mockConnection); + + // Verify that the onNotification callback was never called + verifyNever(mockOnNotification.call(any, any, any)); + }, + ); } From befcf11d616d92af31263cd371011e25093db712 Mon Sep 17 00:00:00 2001 From: Ishaan <68785503+EmeraldWither@users.noreply.github.com> Date: Thu, 27 Jun 2024 20:18:20 -0400 Subject: [PATCH 3/5] fix tests --- .../robot_notifications_listener_test.dart | 223 ++++++++---------- 1 file changed, 99 insertions(+), 124 deletions(-) diff --git a/test/services/robot_notifications_listener_test.dart b/test/services/robot_notifications_listener_test.dart index 08c49e55..4c90c373 100644 --- a/test/services/robot_notifications_listener_test.dart +++ b/test/services/robot_notifications_listener_test.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:elastic_dashboard/services/nt4_client.dart'; import 'package:elastic_dashboard/services/robot_notifications_listener.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -9,17 +8,36 @@ import 'package:mockito/mockito.dart'; import '../test_util.dart'; import '../test_util.mocks.dart'; -// Create a mock class for the onNotification callback class MockNotificationCallback extends Mock { void call(String? title, String? description, Icon? icon); } void main() { - test("Robot Notifications (No Connection) ", () { - MockNTConnection mockConnection = createMockOfflineNT4(); + test("Robot Notifications (Initial Connection | No Existing Data) ", () { + MockNTConnection mockConnection = createMockOnlineNT4(); MockNT4Subscription mockSub = MockNT4Subscription(); - when(mockConnection.subscribeAll(any, any)).thenReturn(mockSub); + List listeners = []; + when(mockSub.listen(any)).thenAnswer( + (realInvocation) { + listeners.add(realInvocation.positionalArguments[0]); + }, + ); + + when(mockSub.updateValue(any, any)).thenAnswer( + (invoc) { + for (var value in listeners) { + value.call( + invoc.positionalArguments[0], invoc.positionalArguments[1]); + } + }, + ); + + when(mockConnection.subscribeAll(any, any)).thenAnswer( + (realInvocation) { + return mockSub; + }, + ); // Create a mock for the onNotification callback MockNotificationCallback mockOnNotification = MockNotificationCallback(); @@ -42,122 +60,79 @@ void main() { // Verify that the onNotification callback was never called verifyNever(mockOnNotification.call(any, any, any)); }); - test( - "Robot Notifications (Initial Connection | No Notifications)", - () { - MockNTConnection mockConnection = createMockOnlineNT4( - virtualTopics: [ - NT4Topic( - name: '/Elastic/robotnotifications', - type: NT4TypeStr.kString, - properties: {}) - ], - ); - MockNT4Subscription mockSub = MockNT4Subscription(); - - when(mockConnection.subscribeAll(any, any)).thenReturn(mockSub); - - // Create a mock for the onNotification callback - MockNotificationCallback mockOnNotification = MockNotificationCallback(); - - RobotNotificationsListener notifications = RobotNotificationsListener( - ntConnection: mockConnection, - onNotification: mockOnNotification.call, - ); - - notifications.listen(); - - // Verify that subscribeAll was called with the specific parameters - verify(mockConnection.subscribeAll('/Elastic/robotnotifications', 0.2)) - .called(1); - verify(mockConnection.addDisconnectedListener(any)).called(1); - - // Verify that no other interactions have been made with the mockConnection - verifyNoMoreInteractions(mockConnection); - - // Verify that the onNotification callback was never called - verifyNever(mockOnNotification.call(any, any, any)); - }, - ); - - test( - "Robot Notifications (Initial Connection | No Notifications)", - () { - MockNTConnection mockConnection = createMockOnlineNT4( - virtualTopics: [ - NT4Topic( - name: '/Elastic/robotnotifications', - type: NT4TypeStr.kString, - properties: {}) - ], - ); - MockNT4Subscription mockSub = MockNT4Subscription(); - - when(mockConnection.subscribeAll(any, any)).thenReturn(mockSub); - - // Create a mock for the onNotification callback - MockNotificationCallback mockOnNotification = MockNotificationCallback(); - - RobotNotificationsListener notifications = RobotNotificationsListener( - ntConnection: mockConnection, - onNotification: mockOnNotification.call, - ); - - notifications.listen(); - - // Verify that subscribeAll was called with the specific parameters - verify(mockConnection.subscribeAll('/Elastic/robotnotifications', 0.2)) - .called(1); - verify(mockConnection.addDisconnectedListener(any)).called(1); - - // Verify that no other interactions have been made with the mockConnection - verifyNoMoreInteractions(mockConnection); - - // Verify that the onNotification callback was never called - verifyNever(mockOnNotification.call(any, any, any)); - }, - ); - test( - "Robot Notifications (Initial Connection | Existing Notifications)", - () { - Map initData = { - 'title': 'Title1', - 'description': 'Description1', - 'level': 'INFO' - }; - print('${jsonEncode(initData)}'); - MockNTConnection mockConnection = createMockOnlineNT4(virtualTopics: [ - NT4Topic( - name: '/Elastic/robotnotifications', - type: NT4TypeStr.kString, - properties: {}) - ]); - - MockNT4Subscription mockSub = MockNT4Subscription(); - - when(mockConnection.subscribeAll(any, any)).thenReturn(mockSub); - - // Create a mock for the onNotification callback - MockNotificationCallback mockOnNotification = MockNotificationCallback(); - - RobotNotificationsListener notifications = RobotNotificationsListener( - ntConnection: mockConnection, - onNotification: mockOnNotification.call, - ); - - notifications.listen(); - //TODO add stuff - - // Verify that subscribeAll was called with the specific parameters - verify(mockConnection.subscribeAll('/Elastic/robotnotifications', 0.2)) - .called(1); - verify(mockConnection.addDisconnectedListener(any)).called(1); - - // Verify that no other interactions have been made with the mockConnection - verifyNoMoreInteractions(mockConnection); - - // Verify that the onNotification callback was never called - verifyNever(mockOnNotification.call(any, any, any)); - }, - ); -} + + test("Robot Notifications (Initial Connection | Existing Data) ", () { + MockNTConnection mockConnection = createMockOnlineNT4(); + MockNT4Subscription mockSub = MockNT4Subscription(); + + Map data = { + 'title': 'Title1', + 'description': 'Description1', + 'level': 'Info' + }; + + List listeners = []; + when(mockSub.listen(any)).thenAnswer( + (realInvocation) { + listeners.add(realInvocation.positionalArguments[0]); + mockSub.updateValue(jsonEncode(data), 0); + }, + ); + + when(mockSub.updateValue(any, any)).thenAnswer( + (invoc) { + for (var value in listeners) { + value.call( + invoc.positionalArguments[0], invoc.positionalArguments[1]); + } + }, + ); + + when(mockConnection.subscribeAll(any, any)).thenAnswer( + (realInvocation) { + mockSub.updateValue(jsonEncode(data), 0); + return mockSub; + }, + ); + + // Create a mock for the onNotification callback + MockNotificationCallback mockOnNotification = MockNotificationCallback(); + + RobotNotificationsListener notifications = RobotNotificationsListener( + ntConnection: mockConnection, + onNotification: mockOnNotification.call, + ); + + notifications.listen(); + + // Verify that subscribeAll was called with the specific parameters + verify(mockConnection.subscribeAll('/Elastic/robotnotifications', 0.2)) + .called(1); + verify(mockConnection.addDisconnectedListener(any)).called(1); + + // Verify that no other interactions have been made with the mockConnection + verifyNoMoreInteractions(mockConnection); + + // Verify that the onNotification callback was never called + verifyNever(mockOnNotification(any, any, any)); + + //publish some data and expect an update + data['title'] = 'Title2'; + data['description'] = 'Description2'; + data['level'] = 'INFO'; + mockSub.updateValue(jsonEncode(data), 2); + + verify(mockOnNotification(data['title'], data['description'], any)); + + //try malformed data + data['title'] = null; + data['description'] = null; + data['level'] = 'malformedlevel'; + + mockSub.updateValue(jsonEncode(data), 3); + reset(mockOnNotification); + verifyNever(mockOnNotification(any, any, any)); + }); + + +} \ No newline at end of file From bfcf5a919a1140b5c458fc04339d9adcc8b3fbfb Mon Sep 17 00:00:00 2001 From: Ishaan <68785503+EmeraldWither@users.noreply.github.com> Date: Sat, 29 Jun 2024 20:38:33 -0400 Subject: [PATCH 4/5] in progress: start testing notification widget --- test/pages/dashboard_page_test.dart | 93 ++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 8 deletions(-) diff --git a/test/pages/dashboard_page_test.dart b/test/pages/dashboard_page_test.dart index 532e0933..5772a30e 100644 --- a/test/pages/dashboard_page_test.dart +++ b/test/pages/dashboard_page_test.dart @@ -1,14 +1,6 @@ import 'dart:convert'; import 'dart:io'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:titlebar_buttons/titlebar_buttons.dart'; - import 'package:elastic_dashboard/pages/dashboard_page.dart'; import 'package:elastic_dashboard/services/field_images.dart'; import 'package:elastic_dashboard/services/nt4_client.dart'; @@ -25,6 +17,14 @@ import 'package:elastic_dashboard/widgets/nt_widgets/multi-topic/combo_box_choos import 'package:elastic_dashboard/widgets/nt_widgets/single_topic/boolean_box.dart'; import 'package:elastic_dashboard/widgets/settings_dialog.dart'; import 'package:elastic_dashboard/widgets/tab_grid.dart'; +import 'package:elegant_notification/elegant_notification.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:titlebar_buttons/titlebar_buttons.dart'; + import '../test_util.dart'; import '../test_util.mocks.dart'; @@ -880,4 +880,81 @@ void main() { expect(find.byType(SettingsDialog), findsOneWidget); }); + + testWidgets( + 'Robot Notifications', + (widgetTester) async { + FlutterError.onError = ignoreOverflowErrors; + final Map data = { + 'title': 'Robot Notification Title', + 'description': 'Robot Notification Description', + 'level': 'INFO' + }; + + MockNTConnection connection = createMockOnlineNT4(virtualTopics: [ + NT4Topic( + name: '/Elastic/RobotNotifications', + type: NT4TypeStr.kString, + properties: {}, + ) + ], virtualValues: { + '/Elastic/RobotNotifications': jsonEncode(data) + }); + MockNT4Subscription mockSub = MockNT4Subscription(); + + List listeners = []; + when(mockSub.listen(any)).thenAnswer( + (realInvocation) { + listeners.add(realInvocation.positionalArguments[0]); + mockSub.updateValue(jsonEncode(data), 0); + }, + ); + + when(mockSub.updateValue(any, any)).thenAnswer( + (invoc) { + for (var value in listeners) { + value.call( + invoc.positionalArguments[0], invoc.positionalArguments[1]); + } + }, + ); + + when(connection.subscribeAll(any, any)).thenAnswer( + (realInvocation) { + return mockSub; + }, + ); + + final notificationWidget = + find.widgetWithText(ElegantNotification, data['title']); + + await widgetTester.pumpWidget( + MaterialApp( + home: DashboardPage( + ntConnection: connection, + preferences: preferences, + version: '0.0.0.0', + ), + ), + ); + expect(notificationWidget, findsNothing); + + await widgetTester.pumpAndSettle(); + connection + .subscribeAll('/Elastic/robotnotifications', 0.2) + .updateValue(jsonEncode(data), 1); + + await widgetTester.pump(); + + expect(notificationWidget, findsOneWidget); + + await widgetTester.pumpAndSettle(); + + expect(notificationWidget, findsNothing); + + connection + .subscribeAll('/Elastic/robotnotifications', 0.2) + .updateValue(jsonEncode(data), 1); + }, + ); } From 0d5f7e4e059323538803e3cab73da207e2e0d120 Mon Sep 17 00:00:00 2001 From: Gold87 <91761103+Gold872@users.noreply.github.com> Date: Fri, 12 Jul 2024 16:47:52 -0400 Subject: [PATCH 5/5] Simplified robot notification tests --- test/pages/dashboard_page_test.dart | 17 +++++----- .../robot_notifications_listener_test.dart | 34 +++---------------- test/test_util.dart | 15 +++++++- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/test/pages/dashboard_page_test.dart b/test/pages/dashboard_page_test.dart index 5772a30e..7b1136c4 100644 --- a/test/pages/dashboard_page_test.dart +++ b/test/pages/dashboard_page_test.dart @@ -1,6 +1,15 @@ import 'dart:convert'; import 'dart:io'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +import 'package:elegant_notification/elegant_notification.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:titlebar_buttons/titlebar_buttons.dart'; + import 'package:elastic_dashboard/pages/dashboard_page.dart'; import 'package:elastic_dashboard/services/field_images.dart'; import 'package:elastic_dashboard/services/nt4_client.dart'; @@ -17,14 +26,6 @@ import 'package:elastic_dashboard/widgets/nt_widgets/multi-topic/combo_box_choos import 'package:elastic_dashboard/widgets/nt_widgets/single_topic/boolean_box.dart'; import 'package:elastic_dashboard/widgets/settings_dialog.dart'; import 'package:elastic_dashboard/widgets/tab_grid.dart'; -import 'package:elegant_notification/elegant_notification.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:titlebar_buttons/titlebar_buttons.dart'; - import '../test_util.dart'; import '../test_util.mocks.dart'; diff --git a/test/services/robot_notifications_listener_test.dart b/test/services/robot_notifications_listener_test.dart index 4c90c373..0c0905bc 100644 --- a/test/services/robot_notifications_listener_test.dart +++ b/test/services/robot_notifications_listener_test.dart @@ -1,10 +1,11 @@ import 'dart:convert'; -import 'package:elastic_dashboard/services/robot_notifications_listener.dart'; import 'package:flutter/material.dart'; + import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; +import 'package:elastic_dashboard/services/robot_notifications_listener.dart'; import '../test_util.dart'; import '../test_util.mocks.dart'; @@ -15,29 +16,6 @@ class MockNotificationCallback extends Mock { void main() { test("Robot Notifications (Initial Connection | No Existing Data) ", () { MockNTConnection mockConnection = createMockOnlineNT4(); - MockNT4Subscription mockSub = MockNT4Subscription(); - - List listeners = []; - when(mockSub.listen(any)).thenAnswer( - (realInvocation) { - listeners.add(realInvocation.positionalArguments[0]); - }, - ); - - when(mockSub.updateValue(any, any)).thenAnswer( - (invoc) { - for (var value in listeners) { - value.call( - invoc.positionalArguments[0], invoc.positionalArguments[1]); - } - }, - ); - - when(mockConnection.subscribeAll(any, any)).thenAnswer( - (realInvocation) { - return mockSub; - }, - ); // Create a mock for the onNotification callback MockNotificationCallback mockOnNotification = MockNotificationCallback(); @@ -116,7 +94,7 @@ void main() { // Verify that the onNotification callback was never called verifyNever(mockOnNotification(any, any, any)); - //publish some data and expect an update + // Publish some data and expect an update data['title'] = 'Title2'; data['description'] = 'Description2'; data['level'] = 'INFO'; @@ -124,7 +102,7 @@ void main() { verify(mockOnNotification(data['title'], data['description'], any)); - //try malformed data + // Try malformed data data['title'] = null; data['description'] = null; data['level'] = 'malformedlevel'; @@ -133,6 +111,4 @@ void main() { reset(mockOnNotification); verifyNever(mockOnNotification(any, any, any)); }); - - -} \ No newline at end of file +} diff --git a/test/test_util.dart b/test/test_util.dart index ad58f335..891b39b8 100644 --- a/test/test_util.dart +++ b/test/test_util.dart @@ -75,6 +75,8 @@ MockNTConnection createMockOnlineNT4({ Map virtualTopicsMap = {}; + List subscriptionListeners = []; + for (int i = 0; i < virtualTopics.length; i++) { virtualTopicsMap.addAll({i + 1: virtualTopics[i]}); } @@ -139,7 +141,18 @@ MockNTConnection createMockOnlineNT4({ when(topicSubscription.periodicStream(yieldAll: anyNamed('yieldAll'))) .thenAnswer((_) => Stream.value(virtualValues?[topic.name])); - when(topicSubscription.listen(any)).thenAnswer((realInvocation) {}); + when(topicSubscription.listen(any)).thenAnswer((realInvocation) { + subscriptionListeners.add(realInvocation.positionalArguments[0]); + }); + + when(topicSubscription.updateValue(any, any)).thenAnswer( + (invoc) { + for (var value in subscriptionListeners) { + value.call( + invoc.positionalArguments[0], invoc.positionalArguments[1]); + } + }, + ); when(mockNT4Connection.getLastAnnouncedValue(topic.name)) .thenAnswer((_) => virtualValues?[topic.name]);