From d2b1967718b33c6c7cf64bbb7c59103e1881c23a Mon Sep 17 00:00:00 2001 From: tsutsu3 Date: Sat, 25 Jan 2025 01:13:20 +0900 Subject: [PATCH] test(widget): add widget tests to improve coverage (#107) --- LICENSE.md | 2 +- analysis_options.yaml | 2 +- codecov.yml | 2 + pubspec.lock | 8 + pubspec.yaml | 1 + sonar-project.properties | 2 +- test/widgets/app_logs/app_logs_test.dart | 108 + test/widgets/helpers.dart | 972 +++++++++ test/widgets/helpers.mocks.dart | 1842 +++++++++++++++++ test/widgets/home/home_test.dart | 70 + .../auto_refresh_time_screen_test.dart | 162 ++ test/widgets/settings/settings_test.dart | 457 ++++ test/widgets/statistics/statistics_test.dart | 144 ++ test/widgets/unlock_test.dart | 52 + test/widgets/utils.dart | 10 + 15 files changed, 3831 insertions(+), 3 deletions(-) create mode 100644 codecov.yml create mode 100644 test/widgets/app_logs/app_logs_test.dart create mode 100644 test/widgets/helpers.dart create mode 100644 test/widgets/helpers.mocks.dart create mode 100644 test/widgets/home/home_test.dart create mode 100644 test/widgets/settings/app_settings/auto_refresh_time_screen_test.dart create mode 100644 test/widgets/settings/settings_test.dart create mode 100644 test/widgets/statistics/statistics_test.dart create mode 100644 test/widgets/unlock_test.dart create mode 100644 test/widgets/utils.dart diff --git a/LICENSE.md b/LICENSE.md index e1c460a1..ebab855e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -438,4 +438,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### Included Files -- `lib/screens/settings/about.dart` +- `lib/screens/settings/about/about.dart` diff --git a/analysis_options.yaml b/analysis_options.yaml index d5f10f6e..3452a132 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -56,7 +56,7 @@ dart_code_linter: - avoid-unused-parameters - prefer-trailing-comma - check-unused-files - - check-unused-code + # - check-unused-code dart_code_metrics: extends: diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..6a8b6302 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,2 @@ +ignore: + - "lib/screens/settings/about/about.dart" diff --git a/pubspec.lock b/pubspec.lock index ae9b96a0..b89d1305 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -518,6 +518,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.16" + flutter_svg_test: + dependency: "direct dev" + description: + name: flutter_svg_test + sha256: bfc0114ef604770d5c5dfd993c0ce7e8a5234c894361adee52dad98f6db53185 + url: "https://pub.dev" + source: hosted + version: "1.0.3" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 93893978..998918aa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -105,6 +105,7 @@ dev_dependencies: dart_code_metrics_presets: ^2.19.0 dart_code_linter: ^1.2.1 full_coverage: ^1.0.0 + flutter_svg_test: ^1.0.3 dependency_overrides: analyzer: ^6.5.0 diff --git a/sonar-project.properties b/sonar-project.properties index 94afaeed..09dbe070 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -14,7 +14,7 @@ sonar.tests=test sonar.sourceEncoding=UTF-8 # exclude generated files -sonar.exclusions=test/**/*_test.mocks.dart,lib/**/*.g.dart +sonar.exclusions=test/**/*_test.mocks.dart,lib/**/*.g.dart,lib/screens/settings/about/about.dart # coverage sonar.dart.lcov.reportPaths=coverage/lcov.info \ No newline at end of file diff --git a/test/widgets/app_logs/app_logs_test.dart b/test/widgets/app_logs/app_logs_test.dart new file mode 100644 index 00000000..a38718c0 --- /dev/null +++ b/test/widgets/app_logs/app_logs_test.dart @@ -0,0 +1,108 @@ +import 'package:mockito/mockito.dart'; +import 'package:pi_hole_client/screens/app_logs/app_log_details_modal.dart'; +import 'package:pi_hole_client/screens/app_logs/app_logs.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import '../helpers.dart'; + +void main() async { + initializeApp(); + + group( + 'App Log Screen Widget Tests', + () { + late TestSetupHelper testSetup; + + setUp(() async { + testSetup = TestSetupHelper(); + testSetup.initializeMock(useApiGatewayVersion: 'v6'); + }); + + testWidgets( + 'should show AppLogs screen with one log', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 1.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const AppLogs(), + ), + ); + + expect(find.byType(AppLogs), findsOneWidget); + await tester.pump(); + + expect(find.text('Logs'), findsOneWidget); + expect(find.byIcon(Icons.share), findsOneWidget); + + expect(find.text('sample'), findsOneWidget); + expect(find.text('message'), findsOneWidget); + expect(find.text('2025-01-22 10:50:31.000'), findsOneWidget); + + // TODO: show snackbar + // TODO: check clipboard data + // await tester.tap(find.byIcon(Icons.share)); + // await tester.pump(const Duration(seconds: 1)); + // await tester.pumpAndSettle(const Duration(seconds: 1)); + // expect(find.text('Logs copied to clipboard'), findsOneWidget); + + await tester.tap(find.text('sample')); + await tester.pumpAndSettle(); + expect(find.byType(AppLogDetailsModal), findsOneWidget); + expect(find.text('Log details'), findsOneWidget); + + // Message tab is selected by default + expect(find.text('sample'), findsOneWidget); + + await tester.tap(find.text('Status code')); + await tester.pumpAndSettle(); + expect(find.text('200'), findsOneWidget); + + await tester.tap(find.text('Body')); + await tester.pumpAndSettle(); + expect(find.text('body'), findsOneWidget); + }, + ); + + testWidgets( + 'should show blank screen (no app logs)', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 1.0; + + when(testSetup.mockConfigProvider.logs).thenReturn([]); + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const AppLogs(), + ), + ); + + expect(find.byType(AppLogs), findsOneWidget); + await tester.pump(); + + expect(find.text('Logs'), findsOneWidget); + + // check log message + expect(find.text('sample'), findsNothing); + + // can't tap on share icon because there are no logs + await tester.tap(find.byIcon(Icons.share)); + await tester.pumpAndSettle(); + expect(find.text('Logs copied to clipboard'), findsNothing); + }, + ); + }, + ); +} diff --git a/test/widgets/helpers.dart b/test/widgets/helpers.dart new file mode 100644 index 00000000..03b1148a --- /dev/null +++ b/test/widgets/helpers.dart @@ -0,0 +1,972 @@ +import 'package:dynamic_color/dynamic_color.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:pi_hole_client/config/globals.dart'; +import 'package:pi_hole_client/config/theme.dart'; +import 'package:pi_hole_client/constants/enums.dart'; +import 'package:pi_hole_client/constants/query_types.dart'; +import 'package:pi_hole_client/gateways/api_gateway_interface.dart'; +import 'package:pi_hole_client/gateways/v5/api_gateway_v5.dart'; +import 'package:pi_hole_client/gateways/v6/api_gateway_v6.dart'; +import 'package:pi_hole_client/models/api/v6/metrics/query.dart'; +import 'package:pi_hole_client/models/app_log.dart'; +import 'package:pi_hole_client/models/gateways.dart'; +import 'package:pi_hole_client/models/log.dart'; +import 'package:pi_hole_client/models/overtime_data.dart'; +import 'package:pi_hole_client/models/realtime_status.dart'; +import 'package:pi_hole_client/models/server.dart'; +import 'package:pi_hole_client/providers/app_config_provider.dart'; +import 'package:pi_hole_client/providers/domains_list_provider.dart'; +import 'package:pi_hole_client/providers/filters_provider.dart'; +import 'package:pi_hole_client/providers/servers_provider.dart'; +import 'package:pi_hole_client/providers/status_provider.dart'; +import 'package:pi_hole_client/models/domain.dart'; +import 'package:provider/provider.dart'; +import 'package:mockito/annotations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import './helpers.mocks.dart'; + +final serverV5 = Server( + address: 'http://localhost:8080', + alias: 'test v5', + defaultServer: false, + apiVersion: 'v5', +); + +final serverV6 = Server( + address: 'http://localhost:8081', + alias: 'test v6', + defaultServer: false, + apiVersion: 'v6', +); + +final domains = [ + Domain( + id: 1, + type: 0, + domain: 'example.com', + enabled: 1, + dateAdded: DateTime.now(), + dateModified: DateTime.now(), + comment: null, + groups: [0], + ), +]; + +final blackDomains = [ + Domain( + id: 3, + type: 1, + domain: 'black01.example.com', + enabled: 1, + dateAdded: DateTime.now(), + dateModified: DateTime.now(), + comment: null, + groups: [0], + ), +]; + +final whiteDomains = [ + Domain( + id: 1, + type: 1, + domain: 'white01.example.com', + enabled: 1, + dateAdded: DateTime.now(), + dateModified: DateTime.now(), + comment: null, + groups: [0], + ), + Domain( + id: 2, + type: 1, + domain: 'white02.example.com', + enabled: 1, + dateAdded: DateTime.now(), + dateModified: DateTime.now(), + comment: null, + groups: [0], + ), +]; + +final queries = Queries.fromJson({ + 'queries': [ + { + 'id': 1, + 'time': DateTime.now().millisecondsSinceEpoch / 1000 - 600, + 'type': 'A', + 'domain': 'white.example.com', + 'cname': null, + 'status': 'FORWARDED', + 'client': {'ip': '192.168.100.2'}, + 'dnssec': 'INSECURE', + 'reply': {'type': 'IP', 'time': 19}, + 'list_id': null, + 'upstream': 'localhost#5353', + 'dbid': 112421354, + }, + ], + 'cursor': 175881, + 'recordsTotal': 1234, + 'recordsFiltered': 1234, + 'draw': 1, + 'took': 0.003, +}); + +final overtimeData = OverTimeData.fromJson( + { + 'domains_over_time': { + '1733391300': 0, + '1733391900': 0, + '1733392500': 0, + '1733393100': 0, + '1733393700': 0, + '1733394300': 0, + '1733394900': 0, + '1733395500': 0, + '1733396100': 0, + '1733396700': 3, + '1733397300': 0, + '1733397900': 0, + '1733398500': 0, + '1733399100': 0, + '1733399700': 0, + '1733400300': 2, + '1733400900': 7, + '1733401500': 0, + '1733402100': 0, + '1733402700': 0, + '1733403300': 0, + '1733403900': 2, + '1733404500': 0, + '1733405100': 0, + '1733405700': 0, + '1733406300': 0, + '1733406900': 0, + '1733407500': 2, + '1733408100': 0, + '1733408700': 0, + '1733409300': 0, + '1733409900': 0, + '1733410500': 0, + '1733411100': 0, + '1733411700': 0, + '1733412300': 0, + '1733412900': 0, + '1733413500': 0, + '1733414100': 0, + '1733414700': 0, + '1733415300': 0, + '1733415900': 0, + '1733416500': 0, + '1733417100': 0, + '1733417700': 0, + '1733418300': 0, + '1733418900': 0, + '1733419500': 0, + '1733420100': 0, + '1733420700': 0, + '1733421300': 0, + '1733421900': 0, + '1733422500': 0, + '1733423100': 0, + '1733423700': 0, + '1733424300': 0, + '1733424900': 0, + '1733425500': 0, + '1733426100': 0, + '1733426700': 0, + '1733427300': 0, + '1733427900': 0, + '1733428500': 0, + '1733429100': 0, + '1733429700': 0, + '1733430300': 0, + '1733430900': 0, + '1733431500': 0, + '1733432100': 0, + '1733432700': 0, + '1733433300': 0, + '1733433900': 0, + '1733434500': 0, + '1733435100': 0, + '1733435700': 0, + '1733436300': 0, + '1733436900': 0, + '1733437500': 0, + '1733438100': 0, + '1733438700': 0, + '1733439300': 0, + '1733439900': 0, + '1733440500': 0, + '1733441100': 0, + '1733441700': 0, + '1733442300': 0, + '1733442900': 0, + '1733443500': 0, + '1733444100': 0, + '1733444700': 0, + '1733445300': 0, + '1733445900': 0, + '1733446500': 0, + '1733447100': 0, + '1733447700': 0, + '1733448300': 0, + '1733448900': 0, + '1733449500': 0, + '1733450100': 0, + '1733450700': 0, + '1733451300': 0, + '1733451900': 0, + '1733452500': 0, + '1733453100': 0, + '1733453700': 0, + '1733454300': 0, + '1733454900': 0, + '1733455500': 0, + '1733456100': 0, + '1733456700': 0, + '1733457300': 0, + '1733457900': 0, + '1733458500': 0, + '1733459100': 0, + '1733459700': 0, + '1733460300': 0, + '1733460900': 0, + '1733461500': 0, + '1733462100': 0, + '1733462700': 0, + '1733463300': 0, + '1733463900': 0, + '1733464500': 0, + '1733465100': 0, + '1733465700': 0, + '1733466300': 0, + '1733466900': 0, + '1733467500': 0, + '1733468100': 0, + '1733468700': 0, + '1733469300': 0, + '1733469900': 0, + '1733470500': 0, + '1733471100': 0, + '1733471700': 0, + '1733472300': 0, + '1733472900': 0, + '1733473500': 0, + '1733474100': 0, + '1733474700': 0, + '1733475300': 0, + '1733475900': 0, + '1733476500': 0, + '1733477100': 0, + }, + 'ads_over_time': { + '1733391300': 0, + '1733391900': 0, + '1733392500': 0, + '1733393100': 0, + '1733393700': 0, + '1733394300': 0, + '1733394900': 0, + '1733395500': 0, + '1733396100': 0, + '1733396700': 0, + '1733397300': 0, + '1733397900': 0, + '1733398500': 0, + '1733399100': 0, + '1733399700': 0, + '1733400300': 0, + '1733400900': 1, + '1733401500': 0, + '1733402100': 0, + '1733402700': 0, + '1733403300': 0, + '1733403900': 0, + '1733404500': 0, + '1733405100': 0, + '1733405700': 0, + '1733406300': 0, + '1733406900': 0, + '1733407500': 0, + '1733408100': 0, + '1733408700': 0, + '1733409300': 0, + '1733409900': 0, + '1733410500': 0, + '1733411100': 0, + '1733411700': 0, + '1733412300': 0, + '1733412900': 0, + '1733413500': 0, + '1733414100': 0, + '1733414700': 0, + '1733415300': 0, + '1733415900': 0, + '1733416500': 0, + '1733417100': 0, + '1733417700': 0, + '1733418300': 0, + '1733418900': 0, + '1733419500': 0, + '1733420100': 0, + '1733420700': 0, + '1733421300': 0, + '1733421900': 0, + '1733422500': 0, + '1733423100': 0, + '1733423700': 0, + '1733424300': 0, + '1733424900': 0, + '1733425500': 0, + '1733426100': 0, + '1733426700': 0, + '1733427300': 0, + '1733427900': 0, + '1733428500': 0, + '1733429100': 0, + '1733429700': 0, + '1733430300': 0, + '1733430900': 0, + '1733431500': 0, + '1733432100': 0, + '1733432700': 0, + '1733433300': 0, + '1733433900': 0, + '1733434500': 0, + '1733435100': 0, + '1733435700': 0, + '1733436300': 0, + '1733436900': 0, + '1733437500': 0, + '1733438100': 0, + '1733438700': 0, + '1733439300': 0, + '1733439900': 0, + '1733440500': 0, + '1733441100': 0, + '1733441700': 0, + '1733442300': 0, + '1733442900': 0, + '1733443500': 0, + '1733444100': 0, + '1733444700': 0, + '1733445300': 0, + '1733445900': 0, + '1733446500': 0, + '1733447100': 0, + '1733447700': 0, + '1733448300': 0, + '1733448900': 0, + '1733449500': 0, + '1733450100': 0, + '1733450700': 0, + '1733451300': 0, + '1733451900': 0, + '1733452500': 0, + '1733453100': 0, + '1733453700': 0, + '1733454300': 0, + '1733454900': 0, + '1733455500': 0, + '1733456100': 0, + '1733456700': 0, + '1733457300': 0, + '1733457900': 0, + '1733458500': 0, + '1733459100': 0, + '1733459700': 0, + '1733460300': 0, + '1733460900': 0, + '1733461500': 0, + '1733462100': 0, + '1733462700': 0, + '1733463300': 0, + '1733463900': 0, + '1733464500': 0, + '1733465100': 0, + '1733465700': 0, + '1733466300': 0, + '1733466900': 0, + '1733467500': 0, + '1733468100': 0, + '1733468700': 0, + '1733469300': 0, + '1733469900': 0, + '1733470500': 0, + '1733471100': 0, + '1733471700': 0, + '1733472300': 0, + '1733472900': 0, + '1733473500': 0, + '1733474100': 0, + '1733474700': 0, + '1733475300': 0, + '1733475900': 0, + '1733476500': 0, + '1733477100': 0, + }, + 'clients': [ + {'name': '', 'ip': '172.26.0.1'}, + {'name': 'localhost', 'ip': '127.0.0.1'}, + ], + 'over_time': { + '1733391300': [0, 0], + '1733391900': [0, 0], + '1733392500': [0, 0], + '1733393100': [0, 0], + '1733393700': [0, 0], + '1733394300': [0, 0], + '1733394900': [0, 0], + '1733395500': [0, 0], + '1733396100': [0, 0], + '1733396700': [3, 0], + '1733397300': [0, 0], + '1733397900': [0, 0], + '1733398500': [0, 0], + '1733399100': [0, 0], + '1733399700': [0, 0], + '1733400300': [0, 2], + '1733400900': [7, 0], + '1733401500': [0, 0], + '1733402100': [0, 0], + '1733402700': [0, 0], + '1733403300': [0, 0], + '1733403900': [0, 2], + '1733404500': [0, 0], + '1733405100': [0, 0], + '1733405700': [0, 0], + '1733406300': [0, 0], + '1733406900': [0, 0], + '1733407500': [0, 2], + '1733408100': [0, 0], + '1733408700': [0, 0], + '1733409300': [0, 0], + '1733409900': [0, 0], + '1733410500': [0, 0], + '1733411100': [0, 0], + '1733411700': [0, 0], + '1733412300': [0, 0], + '1733412900': [0, 0], + '1733413500': [0, 0], + '1733414100': [0, 0], + '1733414700': [0, 0], + '1733415300': [0, 0], + '1733415900': [0, 0], + '1733416500': [0, 0], + '1733417100': [0, 0], + '1733417700': [0, 0], + '1733418300': [0, 0], + '1733418900': [0, 0], + '1733419500': [0, 0], + '1733420100': [0, 0], + '1733420700': [0, 0], + '1733421300': [0, 0], + '1733421900': [0, 0], + '1733422500': [0, 0], + '1733423100': [0, 0], + '1733423700': [0, 0], + '1733424300': [0, 0], + '1733424900': [0, 0], + '1733425500': [0, 0], + '1733426100': [0, 0], + '1733426700': [0, 0], + '1733427300': [0, 0], + '1733427900': [0, 0], + '1733428500': [0, 0], + '1733429100': [0, 0], + '1733429700': [0, 0], + '1733430300': [0, 0], + '1733430900': [0, 0], + '1733431500': [0, 0], + '1733432100': [0, 0], + '1733432700': [0, 0], + '1733433300': [0, 0], + '1733433900': [0, 0], + '1733434500': [0, 0], + '1733435100': [0, 0], + '1733435700': [0, 0], + '1733436300': [0, 0], + '1733436900': [0, 0], + '1733437500': [0, 0], + '1733438100': [0, 0], + '1733438700': [0, 0], + '1733439300': [0, 0], + '1733439900': [0, 0], + '1733440500': [0, 0], + '1733441100': [0, 0], + '1733441700': [0, 0], + '1733442300': [0, 0], + '1733442900': [0, 0], + '1733443500': [0, 0], + '1733444100': [0, 0], + '1733444700': [0, 0], + '1733445300': [0, 0], + '1733445900': [0, 0], + '1733446500': [0, 0], + '1733447100': [0, 0], + '1733447700': [0, 0], + '1733448300': [0, 0], + '1733448900': [0, 0], + '1733449500': [0, 0], + '1733450100': [0, 0], + '1733450700': [0, 0], + '1733451300': [0, 0], + '1733451900': [0, 0], + '1733452500': [0, 0], + '1733453100': [0, 0], + '1733453700': [0, 0], + '1733454300': [0, 0], + '1733454900': [0, 0], + '1733455500': [0, 0], + '1733456100': [0, 0], + '1733456700': [0, 0], + '1733457300': [0, 0], + '1733457900': [0, 0], + '1733458500': [0, 0], + '1733459100': [0, 0], + '1733459700': [0, 0], + '1733460300': [0, 0], + '1733460900': [0, 0], + '1733461500': [0, 0], + '1733462100': [0, 0], + '1733462700': [0, 0], + '1733463300': [0, 0], + '1733463900': [0, 0], + '1733464500': [0, 0], + '1733465100': [0, 0], + '1733465700': [0, 0], + '1733466300': [0, 0], + '1733466900': [0, 0], + '1733467500': [0, 0], + '1733468100': [0, 0], + '1733468700': [0, 0], + '1733469300': [0, 0], + '1733469900': [0, 0], + '1733470500': [0, 0], + '1733471100': [0, 0], + '1733471700': [0, 0], + '1733472300': [0, 0], + '1733472900': [0, 0], + '1733473500': [0, 0], + '1733474100': [0, 0], + '1733474700': [0, 0], + '1733475300': [0, 0], + '1733475900': [0, 0], + '1733476500': [0, 0], + '1733477100': [0, 0], + }, + }, +); + +final realtimeStatus = RealtimeStatus.fromJson( + { + 'domains_being_blocked': 121860, + 'dns_queries_today': 16, + 'ads_blocked_today': 1, + 'ads_percentage_today': 6.25, + 'unique_domains': 11, + 'queries_forwarded': 9, + 'queries_cached': 6, + 'clients_ever_seen': 2, + 'unique_clients': 2, + 'dns_queries_all_types': 16, + 'reply_UNKNOWN': 0, + 'reply_NODATA': 0, + 'reply_NXDOMAIN': 3, + 'reply_CNAME': 0, + 'reply_IP': 10, + 'reply_DOMAIN': 3, + 'reply_RRNAME': 0, + 'reply_SERVFAIL': 0, + 'reply_REFUSED': 0, + 'reply_NOTIMP': 0, + 'reply_OTHER': 0, + 'reply_DNSSEC': 0, + 'reply_NONE': 0, + 'reply_BLOB': 0, + 'dns_queries_all_replies': 16, + 'privacy_level': 0, + 'status': 'enabled', + 'gravity_last_updated': { + 'file_exists': true, + 'absolute': 1732972589, + 'relative': {'days': 5, 'hours': 18, 'minutes': 14}, + }, + 'top_queries': { + '1.0.26.172.in-addr.arpa': 3, + '8.8.8.8.in-addr.arpa': 3, + 'github.com': 2, + 'gitlab.com': 1, + 'sample.com': 1, + 'test.com': 1, + 'google.com': 1, + 'google.co.jp': 1, + 'yahoo.co.jp': 1, + 'fix.test.com': 1, + }, + 'top_ads': {'test.com': 1}, + 'top_sources': {'172.26.0.1': 10, 'localhost|127.0.0.1': 6}, + 'top_sources_blocked': {'172.26.0.1': 1}, + 'forward_destinations': { + 'blocked|blocked': 6.25, + 'cached|cached': 37.5, + 'other|other': 0, + 'dns.google#53|8.8.8.8#53': 56.25, + }, + 'querytypes': { + 'A (IPv4)': 62.5, + 'AAAA (IPv6)': 0, + 'ANY': 0, + 'SRV': 0, + 'SOA': 0, + 'PTR': 37.5, + 'TXT': 0, + 'NAPTR': 0, + 'MX': 0, + 'DS': 0, + 'RRSIG': 0, + 'DNSKEY': 0, + 'NS': 0, + 'OTHER': 0, + 'SVCB': 0, + 'HTTPS': 0, + }, + }, +); + +/// Initialize the app with the given environment file. +/// +/// This function should be called before any other setup. +/// Call this function in the `main` function of the test file. +/// +/// Parameters: +/// - `envFileName`: The name of the environment file. +/// +/// Example: +/// ```dart +/// void main() async { +/// await initializeApp(); +/// // ... +/// ``` +Future initializeApp() async { + TestWidgetsFlutterBinding.ensureInitialized(); + await dotenv.load(fileName: '.env'); + + FlutterSecureStorage.setMockInitialValues({ + 'http://localhost:8080_token': 'token123', + 'http://localhost:8081_password': 'password123', + }); +} + +/// Helper class to setup the providers for the test. +/// +/// This class should be used in the `setUp` method of the test. +/// +/// Example: +/// ```dart +/// setUp(() { +/// // If you need to override the default mock behavior +/// final customApiGateway = MockApiGatewayV6(); +/// +/// testSetup = TestSetupHelper( +/// server: server, +/// queries: queries, +/// customApiGatewayV6: customApiGateway, +/// ); +/// +/// // Initialize the mocks +/// testSetup.initializeMocks(); +/// +/// // Override the default mock behavior +/// when(customApiGateway.fetchLogs(any, any)).thenAnswer((_) async { +/// return FetchLogsResponse( +/// result: APiResponseType.success, +/// data: [], +/// ); +/// }); +/// }); +/// }); +@GenerateMocks([ + AppConfigProvider, + ServersProvider, + FiltersProvider, + StatusProvider, + DomainsListProvider, + ApiGatewayV5, + ApiGatewayV6, +]) +class TestSetupHelper { + late MockAppConfigProvider mockConfigProvider; + late MockServersProvider mockServersProvider; + late MockFiltersProvider mockFiltersProvider; + late MockStatusProvider mockStatusProvider; + late MockDomainsListProvider mockDomainsListProvider; + + late MockApiGatewayV5 mockApiGatewayV5; + late MockApiGatewayV6 mockApiGatewayV6; + + TestSetupHelper({ + MockAppConfigProvider? customConfigProvider, + MockServersProvider? customServersProvider, + MockFiltersProvider? customFiltersProvider, + MockStatusProvider? customStatusProvider, + MockDomainsListProvider? customDomainsListProvider, + MockApiGatewayV5? customApiGatewayV5, + MockApiGatewayV6? customApiGatewayV6, + }) { + mockConfigProvider = customConfigProvider ?? MockAppConfigProvider(); + mockServersProvider = customServersProvider ?? MockServersProvider(); + mockFiltersProvider = customFiltersProvider ?? MockFiltersProvider(); + mockStatusProvider = customStatusProvider ?? MockStatusProvider(); + mockDomainsListProvider = + customDomainsListProvider ?? MockDomainsListProvider(); + + mockApiGatewayV5 = customApiGatewayV5 ?? MockApiGatewayV5(); + mockApiGatewayV6 = customApiGatewayV6 ?? MockApiGatewayV6(); + } + + void initializeMock({String useApiGatewayVersion = 'v5'}) { + _initConfiProviderMock(useApiGatewayVersion); + _initServerProviderMock(useApiGatewayVersion); + _initFiltersProviderMock(useApiGatewayVersion); + _initStatusProviderMock(useApiGatewayVersion); + _initDomainListProviderMock(useApiGatewayVersion); + _initApiGatewayV5Mock(); + _initApiGatewayV6Mock(); + } + + /// Build the test widget with the given setup helper. + /// + /// Example: + /// ```dart + /// testWidgets( + /// 'should show the successful snackbar when adding a server', + /// (WidgetTester tester) async { + /// await tester.pumpWidget( + /// testSetup.buildTestWidget( + /// YourTestWidget(), // Your test widget + /// ), + /// ); + /// }, + /// ); + /// ``` + Widget buildTestWidget(Widget child) { + return DynamicColorBuilder( + builder: (lightDynamic, darkDynamic) { + return MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (context) => mockServersProvider, + ), + ChangeNotifierProvider( + create: (context) => mockConfigProvider, + ), + ChangeNotifierProvider( + create: (context) => mockStatusProvider, + ), + ChangeNotifierProxyProvider( + create: (context) => mockDomainsListProvider, + update: (context, serverConfig, servers) => + servers!..update(serverConfig), + ), + ChangeNotifierProxyProvider( + create: (context) => mockFiltersProvider, + update: (context, serverConfig, servers) => + servers!..update(serverConfig), + ), + ChangeNotifierProxyProvider( + create: (context) => mockServersProvider, + update: (context, appConfig, servers) => + servers!..update(appConfig), + ), + ], + child: MaterialApp( + theme: lightTheme(lightDynamic), + darkTheme: darkTheme(darkDynamic), + themeMode: mockConfigProvider.selectedTheme, + home: Scaffold( + body: child, + ), + locale: Locale(mockConfigProvider.selectedLanguage), + supportedLocales: const [ + Locale('en', ''), + Locale('es', ''), + Locale('de', ''), + Locale('pl', ''), + Locale('ja', ''), + ], + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + AppLocalizations.delegate, + ], + scaffoldMessengerKey: scaffoldMessengerKey, + ), + ); + }, + ); + } + + void _initConfiProviderMock(String useApiGatewayVersion) { + when(mockConfigProvider.showingSnackbar).thenReturn(false); + when(mockConfigProvider.logsPerQuery).thenReturn(2); + when(mockConfigProvider.passCode).thenReturn(null); + when(mockConfigProvider.useBiometrics).thenReturn(false); + when(mockConfigProvider.overrideSslCheck).thenReturn(false); + when(mockConfigProvider.reducedDataCharts).thenReturn(false); + when(mockConfigProvider.hideZeroValues).thenReturn(false); + when(mockConfigProvider.selectedTheme).thenReturn(ThemeMode.light); + when(mockConfigProvider.statisticsVisualizationMode).thenReturn(0); + when(mockConfigProvider.setShowingSnackbar(any)).thenReturn(null); + when(mockConfigProvider.setOverrideSslCheck(any)) + .thenAnswer((_) async => true); + when(mockConfigProvider.setReducedDataCharts(any)) + .thenAnswer((_) async => true); + when(mockConfigProvider.setHideZeroValues(any)) + .thenAnswer((_) async => true); + when(mockConfigProvider.restoreAppConfig()).thenAnswer((_) async => true); + when(mockConfigProvider.logs).thenReturn([ + AppLog( + type: 'sample', + dateTime: DateTime(2025, 01, 22, 10, 50, 31), + message: 'message', + statusCode: '200', + resBody: 'body', + ), + ]); + when(mockConfigProvider.selectedThemeNumber).thenReturn(0); + when(mockConfigProvider.selectedLanguage).thenReturn('en'); + when(mockConfigProvider.getAutoRefreshTime).thenReturn(5); + when(mockConfigProvider.selectedSettingsScreen).thenReturn(null); + when(mockConfigProvider.selectedLanguageNumber).thenReturn(0); + when(mockConfigProvider.getAppInfo).thenReturn( + PackageInfo( + appName: 'pi-hole client', + packageName: 'io.github.tsutsu3.pi_hole_clien', + version: '1.0.0', + buildNumber: '1', + ), + ); + when(mockConfigProvider.setAutoRefreshTime(any)) + .thenAnswer((_) async => true); + } + + void _initServerProviderMock(String useApiGatewayVersion) { + when(mockServersProvider.selectedApiGateway).thenReturn( + useApiGatewayVersion == 'v5' + ? mockApiGatewayV5 as ApiGateway + : mockApiGatewayV6 as ApiGateway, + ); + when(mockServersProvider.selectedServer).thenReturn( + useApiGatewayVersion == 'v5' ? serverV5 : serverV6, + ); + when(mockServersProvider.colors).thenReturn(lightAppColors); + when(mockServersProvider.numShown).thenReturn( + useApiGatewayVersion == 'v5' ? 12 : 14, + ); + when(mockServersProvider.getQueryStatus(any)).thenReturn( + // forwarded + useApiGatewayVersion == 'v5' ? queryStatusesV5[1] : queryStatusesV6[2], + ); + when(mockServersProvider.checkUrlExists(any)) + .thenAnswer((_) async => {'result': 'success', 'exists': false}); + when(mockServersProvider.addServer(any)).thenAnswer((_) async => true); + when(mockServersProvider.editServer(any)).thenAnswer((_) async => true); + when(mockServersProvider.loadApiGateway(any)).thenReturn( + useApiGatewayVersion == 'v5' + ? mockApiGatewayV5 as ApiGateway + : mockApiGatewayV6 as ApiGateway, + ); + when(mockServersProvider.deleteDbData()).thenAnswer((_) async => true); + when(mockServersProvider.getServersList).thenReturn([serverV6]); + } + + void _initFiltersProviderMock(String useApiGatewayVersion) { + when(mockFiltersProvider.statusSelected).thenReturn( + useApiGatewayVersion == 'v5' + ? [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14] + : [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ); + when(mockFiltersProvider.selectedClients).thenReturn(['192.168.100.2']); + when(mockFiltersProvider.selectedDomain).thenReturn('white.example.com'); + when(mockFiltersProvider.startTime).thenReturn(DateTime.now()); + when(mockFiltersProvider.endTime) + .thenReturn(DateTime.now().add(const Duration(hours: 2))); + when(mockFiltersProvider.resetFilters()).thenReturn(null); + when(mockFiltersProvider.totalClients) + .thenReturn(['localhost', '192.168.100.2']); + when(mockFiltersProvider.resetTime()).thenReturn(null); + when(mockFiltersProvider.resetStatus()).thenReturn(null); + when(mockFiltersProvider.resetClients()).thenReturn(null); + when(mockFiltersProvider.setSelectedDomain(null)).thenReturn(null); + when(mockFiltersProvider.statusAllowedAndRetried).thenReturn( + useApiGatewayVersion == 'v5' ? [2, 3, 12, 13, 14] : [3, 4, 13, 14, 15], + ); + } + + void _initStatusProviderMock(String useApiGatewayVersion) { + when(mockStatusProvider.getStatusLoading).thenReturn(LoadStatus.loaded); + when(mockStatusProvider.isServerConnected).thenReturn(true); + when(mockStatusProvider.getOvertimeData).thenReturn(overtimeData); + when(mockStatusProvider.getOvertimeDataLoadStatus).thenReturn(1); + when(mockStatusProvider.getOvertimeDataJson) + .thenReturn(overtimeData.toJson()); + when(mockStatusProvider.getRealtimeStatus).thenReturn(realtimeStatus); + } + + void _initDomainListProviderMock(String useApiGatewayVersion) {} + + void _initApiGatewayV5Mock() { + when(mockApiGatewayV5.loginQuery()).thenAnswer( + (_) async => LoginQueryResponse( + result: APiResponseType.success, + status: 'enabled', + sid: 'sid123', + ), + ); + } + + void _initApiGatewayV6Mock() { + when(mockApiGatewayV6.getDomainLists()).thenAnswer( + (_) async => GetDomainLists( + result: APiResponseType.success, + data: DomainListResult( + whitelist: domains, + whitelistRegex: [], + blacklist: domains, + blacklistRegex: [], + ), + ), + ); + + when(mockApiGatewayV6.removeDomainFromList(any)).thenAnswer((_) async { + return RemoveDomainFromListResponse(result: APiResponseType.success); + }); + + when(mockApiGatewayV6.addDomainToList(any)).thenAnswer((_) async { + return AddDomainToListResponse(result: APiResponseType.success); + }); + + when(mockApiGatewayV6.fetchLogs(any, any)).thenAnswer((_) async { + return FetchLogsResponse( + result: APiResponseType.success, + data: queries.queries.map((query) => Log.fromV6(query)).toList(), + ); + }); + + when(mockApiGatewayV6.setWhiteBlacklist(any, any)).thenAnswer((_) async { + return SetWhiteBlacklistResponse( + result: APiResponseType.success, + data: DomainResult(success: true, message: 'Added white.example.com'), + ); + }); + } +} diff --git a/test/widgets/helpers.mocks.dart b/test/widgets/helpers.mocks.dart new file mode 100644 index 00000000..bfd09a3e --- /dev/null +++ b/test/widgets/helpers.mocks.dart @@ -0,0 +1,1842 @@ +// Mocks generated by Mockito 5.4.5 from annotations +// in pi_hole_client/test/widgets/helpers.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i12; +import 'dart:ui' as _i14; + +import 'package:device_info_plus/device_info_plus.dart' as _i11; +import 'package:flutter/material.dart' as _i7; +import 'package:http/http.dart' as _i4; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i8; +import 'package:package_info_plus/package_info_plus.dart' as _i10; +import 'package:pi_hole_client/config/theme.dart' as _i2; +import 'package:pi_hole_client/constants/enums.dart' as _i21; +import 'package:pi_hole_client/gateways/api_gateway_interface.dart' as _i17; +import 'package:pi_hole_client/gateways/v5/api_gateway_v5.dart' as _i26; +import 'package:pi_hole_client/gateways/v6/api_gateway_v6.dart' as _i27; +import 'package:pi_hole_client/models/app_log.dart' as _i9; +import 'package:pi_hole_client/models/domain.dart' as _i25; +import 'package:pi_hole_client/models/gateways.dart' as _i5; +import 'package:pi_hole_client/models/overtime_data.dart' as _i23; +import 'package:pi_hole_client/models/query_status.dart' as _i16; +import 'package:pi_hole_client/models/realtime_status.dart' as _i22; +import 'package:pi_hole_client/models/repository/database.dart' as _i13; +import 'package:pi_hole_client/models/server.dart' as _i3; +import 'package:pi_hole_client/providers/app_config_provider.dart' as _i6; +import 'package:pi_hole_client/providers/domains_list_provider.dart' as _i24; +import 'package:pi_hole_client/providers/filters_provider.dart' as _i18; +import 'package:pi_hole_client/providers/servers_provider.dart' as _i15; +import 'package:pi_hole_client/providers/status_provider.dart' as _i20; +import 'package:pi_hole_client/screens/logs/logs_filters_modal.dart' as _i19; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeAppColors_0 extends _i1.SmartFake implements _i2.AppColors { + _FakeAppColors_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeServer_1 extends _i1.SmartFake implements _i3.Server { + _FakeServer_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeResponse_2 extends _i1.SmartFake implements _i4.Response { + _FakeResponse_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeLoginQueryResponse_3 extends _i1.SmartFake + implements _i5.LoginQueryResponse { + _FakeLoginQueryResponse_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRealtimeStatusResponse_4 extends _i1.SmartFake + implements _i5.RealtimeStatusResponse { + _FakeRealtimeStatusResponse_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDisableServerResponse_5 extends _i1.SmartFake + implements _i5.DisableServerResponse { + _FakeDisableServerResponse_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeEnableServerResponse_6 extends _i1.SmartFake + implements _i5.EnableServerResponse { + _FakeEnableServerResponse_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFetchOverTimeDataResponse_7 extends _i1.SmartFake + implements _i5.FetchOverTimeDataResponse { + _FakeFetchOverTimeDataResponse_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFetchLogsResponse_8 extends _i1.SmartFake + implements _i5.FetchLogsResponse { + _FakeFetchLogsResponse_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSetWhiteBlacklistResponse_9 extends _i1.SmartFake + implements _i5.SetWhiteBlacklistResponse { + _FakeSetWhiteBlacklistResponse_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeGetDomainLists_10 extends _i1.SmartFake + implements _i5.GetDomainLists { + _FakeGetDomainLists_10( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRemoveDomainFromListResponse_11 extends _i1.SmartFake + implements _i5.RemoveDomainFromListResponse { + _FakeRemoveDomainFromListResponse_11( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeAddDomainToListResponse_12 extends _i1.SmartFake + implements _i5.AddDomainToListResponse { + _FakeAddDomainToListResponse_12( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [AppConfigProvider]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockAppConfigProvider extends _i1.Mock implements _i6.AppConfigProvider { + MockAppConfigProvider() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.AppColors get colors => (super.noSuchMethod( + Invocation.getter(#colors), + returnValue: _FakeAppColors_0( + this, + Invocation.getter(#colors), + ), + ) as _i2.AppColors); + + @override + bool get showingSnackbar => (super.noSuchMethod( + Invocation.getter(#showingSnackbar), + returnValue: false, + ) as bool); + + @override + int get selectedTab => (super.noSuchMethod( + Invocation.getter(#selectedTab), + returnValue: 0, + ) as int); + + @override + _i7.ThemeMode get selectedTheme => (super.noSuchMethod( + Invocation.getter(#selectedTheme), + returnValue: _i7.ThemeMode.system, + ) as _i7.ThemeMode); + + @override + String get selectedLanguage => (super.noSuchMethod( + Invocation.getter(#selectedLanguage), + returnValue: _i8.dummyValue( + this, + Invocation.getter(#selectedLanguage), + ), + ) as String); + + @override + int get selectedThemeNumber => (super.noSuchMethod( + Invocation.getter(#selectedThemeNumber), + returnValue: 0, + ) as int); + + @override + int get selectedLanguageNumber => (super.noSuchMethod( + Invocation.getter(#selectedLanguageNumber), + returnValue: 0, + ) as int); + + @override + bool get overrideSslCheck => (super.noSuchMethod( + Invocation.getter(#overrideSslCheck), + returnValue: false, + ) as bool); + + @override + bool get reducedDataCharts => (super.noSuchMethod( + Invocation.getter(#reducedDataCharts), + returnValue: false, + ) as bool); + + @override + double get logsPerQuery => (super.noSuchMethod( + Invocation.getter(#logsPerQuery), + returnValue: 0.0, + ) as double); + + @override + bool get biometricsSupport => (super.noSuchMethod( + Invocation.getter(#biometricsSupport), + returnValue: false, + ) as bool); + + @override + bool get useBiometrics => (super.noSuchMethod( + Invocation.getter(#useBiometrics), + returnValue: false, + ) as bool); + + @override + bool get appUnlocked => (super.noSuchMethod( + Invocation.getter(#appUnlocked), + returnValue: false, + ) as bool); + + @override + bool get validVibrator => (super.noSuchMethod( + Invocation.getter(#validVibrator), + returnValue: false, + ) as bool); + + @override + bool get importantInfoReaden => (super.noSuchMethod( + Invocation.getter(#importantInfoReaden), + returnValue: false, + ) as bool); + + @override + bool get hideZeroValues => (super.noSuchMethod( + Invocation.getter(#hideZeroValues), + returnValue: false, + ) as bool); + + @override + int get statisticsVisualizationMode => (super.noSuchMethod( + Invocation.getter(#statisticsVisualizationMode), + returnValue: 0, + ) as int); + + @override + bool get sendCrashReports => (super.noSuchMethod( + Invocation.getter(#sendCrashReports), + returnValue: false, + ) as bool); + + @override + List<_i9.AppLog> get logs => (super.noSuchMethod( + Invocation.getter(#logs), + returnValue: <_i9.AppLog>[], + ) as List<_i9.AppLog>); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + + @override + void setShowingSnackbar(bool? status) => super.noSuchMethod( + Invocation.method( + #setShowingSnackbar, + [status], + ), + returnValueForMissingStub: null, + ); + + @override + void setSelectedTab(int? selectedTab) => super.noSuchMethod( + Invocation.method( + #setSelectedTab, + [selectedTab], + ), + returnValueForMissingStub: null, + ); + + @override + void setAppInfo(_i10.PackageInfo? appInfo) => super.noSuchMethod( + Invocation.method( + #setAppInfo, + [appInfo], + ), + returnValueForMissingStub: null, + ); + + @override + void setAndroidInfo(_i11.AndroidDeviceInfo? deviceInfo) => super.noSuchMethod( + Invocation.method( + #setAndroidInfo, + [deviceInfo], + ), + returnValueForMissingStub: null, + ); + + @override + void setIosInfo(_i11.IosDeviceInfo? deviceInfo) => super.noSuchMethod( + Invocation.method( + #setIosInfo, + [deviceInfo], + ), + returnValueForMissingStub: null, + ); + + @override + void setBiometricsSupport(bool? isSupported) => super.noSuchMethod( + Invocation.method( + #setBiometricsSupport, + [isSupported], + ), + returnValueForMissingStub: null, + ); + + @override + void setAppUnlocked(bool? status) => super.noSuchMethod( + Invocation.method( + #setAppUnlocked, + [status], + ), + returnValueForMissingStub: null, + ); + + @override + void setValidVibrator(bool? valid) => super.noSuchMethod( + Invocation.method( + #setValidVibrator, + [valid], + ), + returnValueForMissingStub: null, + ); + + @override + void addLog(_i9.AppLog? log) => super.noSuchMethod( + Invocation.method( + #addLog, + [log], + ), + returnValueForMissingStub: null, + ); + + @override + void setSelectedSettingsScreen({ + required int? screen, + bool? notify, + }) => + super.noSuchMethod( + Invocation.method( + #setSelectedSettingsScreen, + [], + { + #screen: screen, + #notify: notify, + }, + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Future setUseBiometrics(bool? biometrics) => (super.noSuchMethod( + Invocation.method( + #setUseBiometrics, + [biometrics], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setImportantInfoReaden(bool? status) => (super.noSuchMethod( + Invocation.method( + #setImportantInfoReaden, + [status], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setPassCode(String? code) => (super.noSuchMethod( + Invocation.method( + #setPassCode, + [code], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setAutoRefreshTime(int? seconds) => (super.noSuchMethod( + Invocation.method( + #setAutoRefreshTime, + [seconds], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setLogsPerQuery(double? time) => (super.noSuchMethod( + Invocation.method( + #setLogsPerQuery, + [time], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setSendCrashReports(bool? status) => (super.noSuchMethod( + Invocation.method( + #setSendCrashReports, + [status], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + void saveFromDb(_i13.AppDbData? dbData) => super.noSuchMethod( + Invocation.method( + #saveFromDb, + [dbData], + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Future setOverrideSslCheck(bool? status) => (super.noSuchMethod( + Invocation.method( + #setOverrideSslCheck, + [status], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setReducedDataCharts(bool? status) => (super.noSuchMethod( + Invocation.method( + #setReducedDataCharts, + [status], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setHideZeroValues(bool? status) => (super.noSuchMethod( + Invocation.method( + #setHideZeroValues, + [status], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setSelectedTheme(int? value) => (super.noSuchMethod( + Invocation.method( + #setSelectedTheme, + [value], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setSelectedLanguage(String? value) => (super.noSuchMethod( + Invocation.method( + #setSelectedLanguage, + [value], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setStatisticsVisualizationMode(int? value) => + (super.noSuchMethod( + Invocation.method( + #setStatisticsVisualizationMode, + [value], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future restoreAppConfig() => (super.noSuchMethod( + Invocation.method( + #restoreAppConfig, + [], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [ServersProvider]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockServersProvider extends _i1.Mock implements _i15.ServersProvider { + MockServersProvider() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.AppColors get colors => (super.noSuchMethod( + Invocation.getter(#colors), + returnValue: _FakeAppColors_0( + this, + Invocation.getter(#colors), + ), + ) as _i2.AppColors); + + @override + List<_i3.Server> get getServersList => (super.noSuchMethod( + Invocation.getter(#getServersList), + returnValue: <_i3.Server>[], + ) as List<_i3.Server>); + + @override + int get numShown => (super.noSuchMethod( + Invocation.getter(#numShown), + returnValue: 0, + ) as int); + + @override + List<_i16.QueryStatus> get queryStatuses => (super.noSuchMethod( + Invocation.getter(#queryStatuses), + returnValue: <_i16.QueryStatus>[], + ) as List<_i16.QueryStatus>); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + + @override + void update(_i6.AppConfigProvider? provider) => super.noSuchMethod( + Invocation.method( + #update, + [provider], + ), + returnValueForMissingStub: null, + ); + + @override + _i17.ApiGateway? loadApiGateway(_i3.Server? server) => + (super.noSuchMethod(Invocation.method( + #loadApiGateway, + [server], + )) as _i17.ApiGateway?); + + @override + _i16.QueryStatus? getQueryStatus(String? key) => + (super.noSuchMethod(Invocation.method( + #getQueryStatus, + [key], + )) as _i16.QueryStatus?); + + @override + _i16.QueryStatus? findQueryStatus(String? key) => + (super.noSuchMethod(Invocation.method( + #findQueryStatus, + [key], + )) as _i16.QueryStatus?); + + @override + _i12.Future addServer(_i3.Server? server) => (super.noSuchMethod( + Invocation.method( + #addServer, + [server], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future editServer(_i3.Server? server) => (super.noSuchMethod( + Invocation.method( + #editServer, + [server], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future removeServer(String? serverAddress) => (super.noSuchMethod( + Invocation.method( + #removeServer, + [serverAddress], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future setDefaultServer(_i3.Server? server) => (super.noSuchMethod( + Invocation.method( + #setDefaultServer, + [server], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future saveFromDb(List<_i13.ServerDbData>? servers) => + (super.noSuchMethod( + Invocation.method( + #saveFromDb, + [servers], + ), + returnValue: _i12.Future.value(), + ) as _i12.Future); + + @override + _i12.FutureOr> checkUrlExists(String? url) => + (super.noSuchMethod( + Invocation.method( + #checkUrlExists, + [url], + ), + returnValue: + _i12.Future>.value({}), + ) as _i12.FutureOr>); + + @override + void setselectedServer({ + required _i3.Server? server, + bool? toHomeTab, + }) => + super.noSuchMethod( + Invocation.method( + #setselectedServer, + [], + { + #server: server, + #toHomeTab: toHomeTab, + }, + ), + returnValueForMissingStub: null, + ); + + @override + void updateselectedServerStatus(bool? enabled) => super.noSuchMethod( + Invocation.method( + #updateselectedServerStatus, + [enabled], + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Future deleteDbData() => (super.noSuchMethod( + Invocation.method( + #deleteDbData, + [], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + _i12.Future resetSelectedServer() => (super.noSuchMethod( + Invocation.method( + #resetSelectedServer, + [], + ), + returnValue: _i12.Future.value(false), + ) as _i12.Future); + + @override + void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [FiltersProvider]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFiltersProvider extends _i1.Mock implements _i18.FiltersProvider { + MockFiltersProvider() { + _i1.throwOnMissingStub(this); + } + + @override + List get statusAllowedAndRetried => (super.noSuchMethod( + Invocation.getter(#statusAllowedAndRetried), + returnValue: [], + ) as List); + + @override + List get defaultSelected => (super.noSuchMethod( + Invocation.getter(#defaultSelected), + returnValue: [], + ) as List); + + @override + List get statusSelected => (super.noSuchMethod( + Invocation.getter(#statusSelected), + returnValue: [], + ) as List); + + @override + String get statusSelectedString => (super.noSuchMethod( + Invocation.getter(#statusSelectedString), + returnValue: _i8.dummyValue( + this, + Invocation.getter(#statusSelectedString), + ), + ) as String); + + @override + List get totalClients => (super.noSuchMethod( + Invocation.getter(#totalClients), + returnValue: [], + ) as List); + + @override + List get selectedClients => (super.noSuchMethod( + Invocation.getter(#selectedClients), + returnValue: [], + ) as List); + + @override + _i19.RequestStatus get requestStatus => (super.noSuchMethod( + Invocation.getter(#requestStatus), + returnValue: _i19.RequestStatus.all, + ) as _i19.RequestStatus); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + + @override + void update(_i15.ServersProvider? provider) => super.noSuchMethod( + Invocation.method( + #update, + [provider], + ), + returnValueForMissingStub: null, + ); + + @override + void setStatusSelected(List? values) => super.noSuchMethod( + Invocation.method( + #setStatusSelected, + [values], + ), + returnValueForMissingStub: null, + ); + + @override + void setStartTime(DateTime? value) => super.noSuchMethod( + Invocation.method( + #setStartTime, + [value], + ), + returnValueForMissingStub: null, + ); + + @override + void setEndTime(DateTime? value) => super.noSuchMethod( + Invocation.method( + #setEndTime, + [value], + ), + returnValueForMissingStub: null, + ); + + @override + void resetFilters() => super.noSuchMethod( + Invocation.method( + #resetFilters, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void resetTime() => super.noSuchMethod( + Invocation.method( + #resetTime, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void resetStatus() => super.noSuchMethod( + Invocation.method( + #resetStatus, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void setClients(List? clients) => super.noSuchMethod( + Invocation.method( + #setClients, + [clients], + ), + returnValueForMissingStub: null, + ); + + @override + void setSelectedClients(List? selectedClients) => super.noSuchMethod( + Invocation.method( + #setSelectedClients, + [selectedClients], + ), + returnValueForMissingStub: null, + ); + + @override + void setSelectedDomain(String? domain) => super.noSuchMethod( + Invocation.method( + #setSelectedDomain, + [domain], + ), + returnValueForMissingStub: null, + ); + + @override + void resetClients() => super.noSuchMethod( + Invocation.method( + #resetClients, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void setRequestStatus(_i19.RequestStatus? status) => super.noSuchMethod( + Invocation.method( + #setRequestStatus, + [status], + ), + returnValueForMissingStub: null, + ); + + @override + void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [StatusProvider]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockStatusProvider extends _i1.Mock implements _i20.StatusProvider { + MockStatusProvider() { + _i1.throwOnMissingStub(this); + } + + @override + bool get isServerConnected => (super.noSuchMethod( + Invocation.getter(#isServerConnected), + returnValue: false, + ) as bool); + + @override + _i21.LoadStatus get getStatusLoading => (super.noSuchMethod( + Invocation.getter(#getStatusLoading), + returnValue: _i21.LoadStatus.loading, + ) as _i21.LoadStatus); + + @override + int get getOvertimeDataLoadStatus => (super.noSuchMethod( + Invocation.getter(#getOvertimeDataLoadStatus), + returnValue: 0, + ) as int); + + @override + bool get getRefreshServerStatus => (super.noSuchMethod( + Invocation.getter(#getRefreshServerStatus), + returnValue: false, + ) as bool); + + @override + bool get startAutoRefresh => (super.noSuchMethod( + Invocation.getter(#startAutoRefresh), + returnValue: false, + ) as bool); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + + @override + void setIsServerConnected(bool? value) => super.noSuchMethod( + Invocation.method( + #setIsServerConnected, + [value], + ), + returnValueForMissingStub: null, + ); + + @override + void setStartAutoRefresh(bool? value) => super.noSuchMethod( + Invocation.method( + #setStartAutoRefresh, + [value], + ), + returnValueForMissingStub: null, + ); + + @override + void setRefreshServerStatus(bool? status) => super.noSuchMethod( + Invocation.method( + #setRefreshServerStatus, + [status], + ), + returnValueForMissingStub: null, + ); + + @override + void setStatusLoading(_i21.LoadStatus? status) => super.noSuchMethod( + Invocation.method( + #setStatusLoading, + [status], + ), + returnValueForMissingStub: null, + ); + + @override + void setRealtimeStatus(_i22.RealtimeStatus? realtimeStatus) => + super.noSuchMethod( + Invocation.method( + #setRealtimeStatus, + [realtimeStatus], + ), + returnValueForMissingStub: null, + ); + + @override + void setOvertimeDataLoadingStatus(int? status) => super.noSuchMethod( + Invocation.method( + #setOvertimeDataLoadingStatus, + [status], + ), + returnValueForMissingStub: null, + ); + + @override + void setOvertimeData(_i23.OverTimeData? value) => super.noSuchMethod( + Invocation.method( + #setOvertimeData, + [value], + ), + returnValueForMissingStub: null, + ); + + @override + void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [DomainsListProvider]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockDomainsListProvider extends _i1.Mock + implements _i24.DomainsListProvider { + MockDomainsListProvider() { + _i1.throwOnMissingStub(this); + } + + @override + set serversProvider(_i15.ServersProvider? _serversProvider) => + super.noSuchMethod( + Invocation.setter( + #serversProvider, + _serversProvider, + ), + returnValueForMissingStub: null, + ); + + @override + _i21.LoadStatus get loadingStatus => (super.noSuchMethod( + Invocation.getter(#loadingStatus), + returnValue: _i21.LoadStatus.loading, + ) as _i21.LoadStatus); + + @override + List<_i25.Domain> get whitelistDomains => (super.noSuchMethod( + Invocation.getter(#whitelistDomains), + returnValue: <_i25.Domain>[], + ) as List<_i25.Domain>); + + @override + List<_i25.Domain> get blacklistDomains => (super.noSuchMethod( + Invocation.getter(#blacklistDomains), + returnValue: <_i25.Domain>[], + ) as List<_i25.Domain>); + + @override + List<_i25.Domain> get filteredWhitelistDomains => (super.noSuchMethod( + Invocation.getter(#filteredWhitelistDomains), + returnValue: <_i25.Domain>[], + ) as List<_i25.Domain>); + + @override + List<_i25.Domain> get filteredBlacklistDomains => (super.noSuchMethod( + Invocation.getter(#filteredBlacklistDomains), + returnValue: <_i25.Domain>[], + ) as List<_i25.Domain>); + + @override + String get searchTerm => (super.noSuchMethod( + Invocation.getter(#searchTerm), + returnValue: _i8.dummyValue( + this, + Invocation.getter(#searchTerm), + ), + ) as String); + + @override + bool get searchMode => (super.noSuchMethod( + Invocation.getter(#searchMode), + returnValue: false, + ) as bool); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + + @override + void update(_i15.ServersProvider? provider) => super.noSuchMethod( + Invocation.method( + #update, + [provider], + ), + returnValueForMissingStub: null, + ); + + @override + void setLoadingStatus(_i21.LoadStatus? status) => super.noSuchMethod( + Invocation.method( + #setLoadingStatus, + [status], + ), + returnValueForMissingStub: null, + ); + + @override + void setWhitelistDomains(List<_i25.Domain>? domains) => super.noSuchMethod( + Invocation.method( + #setWhitelistDomains, + [domains], + ), + returnValueForMissingStub: null, + ); + + @override + void setBlacklistDomains(List<_i25.Domain>? domains) => super.noSuchMethod( + Invocation.method( + #setBlacklistDomains, + [domains], + ), + returnValueForMissingStub: null, + ); + + @override + void setSelectedTab(int? tab) => super.noSuchMethod( + Invocation.method( + #setSelectedTab, + [tab], + ), + returnValueForMissingStub: null, + ); + + @override + void setSearchMode(bool? value) => super.noSuchMethod( + Invocation.method( + #setSearchMode, + [value], + ), + returnValueForMissingStub: null, + ); + + @override + void onSearch(String? value) => super.noSuchMethod( + Invocation.method( + #onSearch, + [value], + ), + returnValueForMissingStub: null, + ); + + @override + _i12.Future fetchDomainsList(_i3.Server? server) => + (super.noSuchMethod( + Invocation.method( + #fetchDomainsList, + [server], + ), + returnValue: _i12.Future.value(), + ) as _i12.Future); + + @override + void removeDomainFromList(_i25.Domain? domain) => super.noSuchMethod( + Invocation.method( + #removeDomainFromList, + [domain], + ), + returnValueForMissingStub: null, + ); + + @override + void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [ApiGatewayV5]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockApiGatewayV5 extends _i1.Mock implements _i26.ApiGatewayV5 { + MockApiGatewayV5() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Server get server => (super.noSuchMethod( + Invocation.getter(#server), + returnValue: _FakeServer_1( + this, + Invocation.getter(#server), + ), + ) as _i3.Server); + + @override + bool checkBasicAuth( + String? username, + String? password, + ) => + (super.noSuchMethod( + Invocation.method( + #checkBasicAuth, + [ + username, + password, + ], + ), + returnValue: false, + ) as bool); + + @override + _i12.Future<_i4.Response> httpClient({ + required String? method, + required String? url, + Map? headers, + Map? body, + int? timeout = 10, + Map? basicAuth, + }) => + (super.noSuchMethod( + Invocation.method( + #httpClient, + [], + { + #method: method, + #url: url, + #headers: headers, + #body: body, + #timeout: timeout, + #basicAuth: basicAuth, + }, + ), + returnValue: _i12.Future<_i4.Response>.value(_FakeResponse_2( + this, + Invocation.method( + #httpClient, + [], + { + #method: method, + #url: url, + #headers: headers, + #body: body, + #timeout: timeout, + #basicAuth: basicAuth, + }, + ), + )), + ) as _i12.Future<_i4.Response>); + + @override + _i12.Future<_i5.LoginQueryResponse> loginQuery({bool? refresh = false}) => + (super.noSuchMethod( + Invocation.method( + #loginQuery, + [], + {#refresh: refresh}, + ), + returnValue: + _i12.Future<_i5.LoginQueryResponse>.value(_FakeLoginQueryResponse_3( + this, + Invocation.method( + #loginQuery, + [], + {#refresh: refresh}, + ), + )), + ) as _i12.Future<_i5.LoginQueryResponse>); + + @override + _i12.Future<_i5.RealtimeStatusResponse> realtimeStatus() => + (super.noSuchMethod( + Invocation.method( + #realtimeStatus, + [], + ), + returnValue: _i12.Future<_i5.RealtimeStatusResponse>.value( + _FakeRealtimeStatusResponse_4( + this, + Invocation.method( + #realtimeStatus, + [], + ), + )), + ) as _i12.Future<_i5.RealtimeStatusResponse>); + + @override + _i12.Future<_i5.DisableServerResponse> disableServerRequest(int? time) => + (super.noSuchMethod( + Invocation.method( + #disableServerRequest, + [time], + ), + returnValue: _i12.Future<_i5.DisableServerResponse>.value( + _FakeDisableServerResponse_5( + this, + Invocation.method( + #disableServerRequest, + [time], + ), + )), + ) as _i12.Future<_i5.DisableServerResponse>); + + @override + _i12.Future<_i5.EnableServerResponse> enableServerRequest() => + (super.noSuchMethod( + Invocation.method( + #enableServerRequest, + [], + ), + returnValue: _i12.Future<_i5.EnableServerResponse>.value( + _FakeEnableServerResponse_6( + this, + Invocation.method( + #enableServerRequest, + [], + ), + )), + ) as _i12.Future<_i5.EnableServerResponse>); + + @override + _i12.Future<_i5.FetchOverTimeDataResponse> fetchOverTimeData() => + (super.noSuchMethod( + Invocation.method( + #fetchOverTimeData, + [], + ), + returnValue: _i12.Future<_i5.FetchOverTimeDataResponse>.value( + _FakeFetchOverTimeDataResponse_7( + this, + Invocation.method( + #fetchOverTimeData, + [], + ), + )), + ) as _i12.Future<_i5.FetchOverTimeDataResponse>); + + @override + _i12.Future<_i5.FetchLogsResponse> fetchLogs( + DateTime? from, + DateTime? until, + ) => + (super.noSuchMethod( + Invocation.method( + #fetchLogs, + [ + from, + until, + ], + ), + returnValue: + _i12.Future<_i5.FetchLogsResponse>.value(_FakeFetchLogsResponse_8( + this, + Invocation.method( + #fetchLogs, + [ + from, + until, + ], + ), + )), + ) as _i12.Future<_i5.FetchLogsResponse>); + + @override + _i12.Future<_i5.SetWhiteBlacklistResponse> setWhiteBlacklist( + String? domain, + String? list, + ) => + (super.noSuchMethod( + Invocation.method( + #setWhiteBlacklist, + [ + domain, + list, + ], + ), + returnValue: _i12.Future<_i5.SetWhiteBlacklistResponse>.value( + _FakeSetWhiteBlacklistResponse_9( + this, + Invocation.method( + #setWhiteBlacklist, + [ + domain, + list, + ], + ), + )), + ) as _i12.Future<_i5.SetWhiteBlacklistResponse>); + + @override + _i12.Future<_i5.GetDomainLists> getDomainLists() => (super.noSuchMethod( + Invocation.method( + #getDomainLists, + [], + ), + returnValue: + _i12.Future<_i5.GetDomainLists>.value(_FakeGetDomainLists_10( + this, + Invocation.method( + #getDomainLists, + [], + ), + )), + ) as _i12.Future<_i5.GetDomainLists>); + + @override + _i12.Future<_i5.RemoveDomainFromListResponse> removeDomainFromList( + _i25.Domain? domain) => + (super.noSuchMethod( + Invocation.method( + #removeDomainFromList, + [domain], + ), + returnValue: _i12.Future<_i5.RemoveDomainFromListResponse>.value( + _FakeRemoveDomainFromListResponse_11( + this, + Invocation.method( + #removeDomainFromList, + [domain], + ), + )), + ) as _i12.Future<_i5.RemoveDomainFromListResponse>); + + @override + _i12.Future<_i5.AddDomainToListResponse> addDomainToList( + Map? domainData) => + (super.noSuchMethod( + Invocation.method( + #addDomainToList, + [domainData], + ), + returnValue: _i12.Future<_i5.AddDomainToListResponse>.value( + _FakeAddDomainToListResponse_12( + this, + Invocation.method( + #addDomainToList, + [domainData], + ), + )), + ) as _i12.Future<_i5.AddDomainToListResponse>); +} + +/// A class which mocks [ApiGatewayV6]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockApiGatewayV6 extends _i1.Mock implements _i27.ApiGatewayV6 { + MockApiGatewayV6() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Server get server => (super.noSuchMethod( + Invocation.getter(#server), + returnValue: _FakeServer_1( + this, + Invocation.getter(#server), + ), + ) as _i3.Server); + + @override + _i12.Future<_i4.Response> httpClient({ + required String? method, + required String? url, + Map? headers, + Map? body, + int? timeout = 10, + int? maxRetries = 1, + }) => + (super.noSuchMethod( + Invocation.method( + #httpClient, + [], + { + #method: method, + #url: url, + #headers: headers, + #body: body, + #timeout: timeout, + #maxRetries: maxRetries, + }, + ), + returnValue: _i12.Future<_i4.Response>.value(_FakeResponse_2( + this, + Invocation.method( + #httpClient, + [], + { + #method: method, + #url: url, + #headers: headers, + #body: body, + #timeout: timeout, + #maxRetries: maxRetries, + }, + ), + )), + ) as _i12.Future<_i4.Response>); + + @override + _i12.Future<_i5.LoginQueryResponse> loginQuery({bool? refresh = false}) => + (super.noSuchMethod( + Invocation.method( + #loginQuery, + [], + {#refresh: refresh}, + ), + returnValue: + _i12.Future<_i5.LoginQueryResponse>.value(_FakeLoginQueryResponse_3( + this, + Invocation.method( + #loginQuery, + [], + {#refresh: refresh}, + ), + )), + ) as _i12.Future<_i5.LoginQueryResponse>); + + @override + _i12.Future<_i5.RealtimeStatusResponse> realtimeStatus() => + (super.noSuchMethod( + Invocation.method( + #realtimeStatus, + [], + ), + returnValue: _i12.Future<_i5.RealtimeStatusResponse>.value( + _FakeRealtimeStatusResponse_4( + this, + Invocation.method( + #realtimeStatus, + [], + ), + )), + ) as _i12.Future<_i5.RealtimeStatusResponse>); + + @override + _i12.Future<_i5.DisableServerResponse> disableServerRequest(int? time) => + (super.noSuchMethod( + Invocation.method( + #disableServerRequest, + [time], + ), + returnValue: _i12.Future<_i5.DisableServerResponse>.value( + _FakeDisableServerResponse_5( + this, + Invocation.method( + #disableServerRequest, + [time], + ), + )), + ) as _i12.Future<_i5.DisableServerResponse>); + + @override + _i12.Future<_i5.EnableServerResponse> enableServerRequest() => + (super.noSuchMethod( + Invocation.method( + #enableServerRequest, + [], + ), + returnValue: _i12.Future<_i5.EnableServerResponse>.value( + _FakeEnableServerResponse_6( + this, + Invocation.method( + #enableServerRequest, + [], + ), + )), + ) as _i12.Future<_i5.EnableServerResponse>); + + @override + _i12.Future<_i5.FetchOverTimeDataResponse> fetchOverTimeData() => + (super.noSuchMethod( + Invocation.method( + #fetchOverTimeData, + [], + ), + returnValue: _i12.Future<_i5.FetchOverTimeDataResponse>.value( + _FakeFetchOverTimeDataResponse_7( + this, + Invocation.method( + #fetchOverTimeData, + [], + ), + )), + ) as _i12.Future<_i5.FetchOverTimeDataResponse>); + + @override + _i12.Future<_i5.FetchLogsResponse> fetchLogs( + DateTime? from, + DateTime? until, + ) => + (super.noSuchMethod( + Invocation.method( + #fetchLogs, + [ + from, + until, + ], + ), + returnValue: + _i12.Future<_i5.FetchLogsResponse>.value(_FakeFetchLogsResponse_8( + this, + Invocation.method( + #fetchLogs, + [ + from, + until, + ], + ), + )), + ) as _i12.Future<_i5.FetchLogsResponse>); + + @override + _i12.Future<_i5.SetWhiteBlacklistResponse> setWhiteBlacklist( + String? domain, + String? list, + ) => + (super.noSuchMethod( + Invocation.method( + #setWhiteBlacklist, + [ + domain, + list, + ], + ), + returnValue: _i12.Future<_i5.SetWhiteBlacklistResponse>.value( + _FakeSetWhiteBlacklistResponse_9( + this, + Invocation.method( + #setWhiteBlacklist, + [ + domain, + list, + ], + ), + )), + ) as _i12.Future<_i5.SetWhiteBlacklistResponse>); + + @override + _i12.Future<_i5.GetDomainLists> getDomainLists() => (super.noSuchMethod( + Invocation.method( + #getDomainLists, + [], + ), + returnValue: + _i12.Future<_i5.GetDomainLists>.value(_FakeGetDomainLists_10( + this, + Invocation.method( + #getDomainLists, + [], + ), + )), + ) as _i12.Future<_i5.GetDomainLists>); + + @override + _i12.Future<_i5.RemoveDomainFromListResponse> removeDomainFromList( + _i25.Domain? domain) => + (super.noSuchMethod( + Invocation.method( + #removeDomainFromList, + [domain], + ), + returnValue: _i12.Future<_i5.RemoveDomainFromListResponse>.value( + _FakeRemoveDomainFromListResponse_11( + this, + Invocation.method( + #removeDomainFromList, + [domain], + ), + )), + ) as _i12.Future<_i5.RemoveDomainFromListResponse>); + + @override + _i12.Future<_i5.AddDomainToListResponse> addDomainToList( + Map? domainData) => + (super.noSuchMethod( + Invocation.method( + #addDomainToList, + [domainData], + ), + returnValue: _i12.Future<_i5.AddDomainToListResponse>.value( + _FakeAddDomainToListResponse_12( + this, + Invocation.method( + #addDomainToList, + [domainData], + ), + )), + ) as _i12.Future<_i5.AddDomainToListResponse>); +} diff --git a/test/widgets/home/home_test.dart b/test/widgets/home/home_test.dart new file mode 100644 index 00000000..78dfc3a5 --- /dev/null +++ b/test/widgets/home/home_test.dart @@ -0,0 +1,70 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:pi_hole_client/screens/home/home.dart'; +import '../helpers.dart'; +import '../utils.dart'; + +void main() async { + initializeApp(); + + group( + 'Home Screen Widget Tests', + () { + late TestSetupHelper testSetup; + + setUp(() async { + testSetup = TestSetupHelper(); + + testSetup.initializeMock(useApiGatewayVersion: 'v6'); + }); + + testWidgets( + 'should home screen be rendered (all graphs are rendered)', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 1.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Home(), + ), + ); + + expect(find.byType(Home), findsOneWidget); + // await tester.pump(); + // await tester.pump(const Duration(seconds: 3)); + await tester.pumpAndSettle(const Duration(seconds: 3)); + + showText(); // TODO + + // Home App Bar + // expect(find.byIcon(Icons.verified_user_rounded), findsOneWidget); // TODO + // expect(find.byIcon(Icons.more_vert), findsOneWidget); // TODO + expect(find.text('test v6'), findsWidgets); // TODO findsOne + expect( + find.text('http://localhost:8081'), + findsWidgets, + ); // TODO findsOne + + // titles + expect(find.text('Total queries'), findsOneWidget); + expect(find.text('Queries blocked'), findsOneWidget); + expect(find.text('Percentage blocked'), findsOneWidget); + expect(find.text('Domains on Adlists'), findsOneWidget); + + // queries graph + // expect( + // find.text('Total queries lsat 24 hours'), findsOneWidget); // TODO + + // clients graph + expect(find.text('Client activity last 24 hours'), findsOneWidget); + }, + ); + }, + ); +} diff --git a/test/widgets/settings/app_settings/auto_refresh_time_screen_test.dart b/test/widgets/settings/app_settings/auto_refresh_time_screen_test.dart new file mode 100644 index 00000000..ba2cd375 --- /dev/null +++ b/test/widgets/settings/app_settings/auto_refresh_time_screen_test.dart @@ -0,0 +1,162 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:mockito/mockito.dart'; +import 'package:pi_hole_client/screens/settings/app_settings/auto_refresh_time_screen.dart'; +import '../../helpers.dart'; + +void main() async { + initializeApp(); + + group( + 'Auto refresh Screen Widget Tests', + () { + late TestSetupHelper testSetup; + + setUp(() async { + testSetup = TestSetupHelper(); + testSetup.initializeMock(useApiGatewayVersion: 'v6'); + }); + + testWidgets( + 'should save successfully', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const AutoRefreshTimeScreen(), + ), + ); + + expect(find.byType(AutoRefreshTimeScreen), findsOneWidget); + expect(find.text('Auto refresh time'), findsOneWidget); + expect(find.byIcon(Icons.save_rounded), findsOneWidget); + + await tester.tap(find.text('30 seconds')); + await tester.pumpAndSettle(); + + await tester.tap(find.byIcon(Icons.save_rounded)); + await tester.pumpAndSettle(); + expect(find.byType(SnackBar), findsOneWidget); + expect( + find.text('Update time changed successfully.'), + findsOneWidget, + ); + }, + ); + + testWidgets( + 'should save failed', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const AutoRefreshTimeScreen(), + ), + ); + + when(testSetup.mockConfigProvider.setAutoRefreshTime(any)) + .thenAnswer((_) async => false); + + expect(find.byType(AutoRefreshTimeScreen), findsOneWidget); + expect(find.text('Auto refresh time'), findsOneWidget); + expect(find.byIcon(Icons.save_rounded), findsOneWidget); + + await tester.tap(find.text('30 seconds')); + await tester.pumpAndSettle(); + + await tester.tap(find.byIcon(Icons.save_rounded)); + await tester.pumpAndSettle(); + expect(find.byType(SnackBar), findsOneWidget); + expect(find.text('Cannot change update time'), findsOneWidget); + }, + ); + + testWidgets( + 'should save successfully with custom time', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const AutoRefreshTimeScreen(), + ), + ); + + expect(find.byType(AutoRefreshTimeScreen), findsOneWidget); + expect(find.text('Auto refresh time'), findsOneWidget); + expect(find.byIcon(Icons.save_rounded), findsOneWidget); + + await tester.tap(find.text('Custom')); + await tester.pumpAndSettle(); + + expect(find.byType(TextField), findsOneWidget); + await tester.enterText(find.byType(TextField), '11'); + await tester.pumpAndSettle(); + + expect(find.text('Value not valid'), findsNothing); + + await tester.tap(find.byIcon(Icons.save_rounded)); + await tester.pumpAndSettle(); + expect(find.byType(SnackBar), findsOneWidget); + + expect( + find.text('Update time changed successfully.'), + findsOneWidget, + ); + }, + ); + + testWidgets( + 'should save successfully with invalid custom time', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const AutoRefreshTimeScreen(), + ), + ); + + expect(find.byType(AutoRefreshTimeScreen), findsOneWidget); + expect(find.text('Auto refresh time'), findsOneWidget); + expect(find.byIcon(Icons.save_rounded), findsOneWidget); + + await tester.tap(find.text('Custom')); + await tester.pumpAndSettle(); + + expect(find.byType(TextField), findsOneWidget); + await tester.enterText(find.byType(TextField), 'aa'); + await tester.pumpAndSettle(); + + expect(find.text('Value not valid'), findsOneWidget); + }, + ); + }, + ); +} diff --git a/test/widgets/settings/settings_test.dart b/test/widgets/settings/settings_test.dart new file mode 100644 index 00000000..b8b2be7b --- /dev/null +++ b/test/widgets/settings/settings_test.dart @@ -0,0 +1,457 @@ +import 'package:flutter_svg/svg.dart'; +import 'package:flutter_svg_test/flutter_svg_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:pi_hole_client/screens/servers/servers.dart'; +import 'package:pi_hole_client/screens/settings/about/app_detail_modal.dart'; +import 'package:pi_hole_client/screens/settings/about/important_info_modal.dart'; +import 'package:pi_hole_client/screens/settings/about/legal_modal.dart'; +// import 'package:pi_hole_client/screens/settings/about/licenses_screen.dart'; +import 'package:pi_hole_client/screens/settings/about/privacy_modal.dart'; +import 'package:pi_hole_client/screens/settings/app_settings/advanced_settings/advanced_options.dart'; +import 'package:pi_hole_client/screens/settings/app_settings/auto_refresh_time_screen.dart'; +import 'package:pi_hole_client/screens/settings/app_settings/language_screen.dart'; +import 'package:pi_hole_client/screens/settings/app_settings/logs_quantity_load_screen.dart'; +import 'package:pi_hole_client/screens/settings/app_settings/theme_screen.dart'; +import 'package:pi_hole_client/screens/settings/settings.dart'; +import '../helpers.dart'; + +void main() async { + initializeApp(); + + group( + 'Settings Screen Widget Tests', + () { + late TestSetupHelper testSetup; + + setUp(() async { + testSetup = TestSetupHelper(); + testSetup.initializeMock(useApiGatewayVersion: 'v6'); + }); + + testWidgets( + 'should show Settings screen', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + expect(find.text('Settings'), findsNWidgets(2)); //title and nav bar + + expect(find.text('App settings'), findsOneWidget); + + expect(find.text('Theme'), findsOneWidget); + expect(find.text('System theme'), findsOneWidget); + + expect(find.text('Language'), findsOneWidget); + expect(find.text('English'), findsOneWidget); + + expect(find.text('Servers'), findsOneWidget); + expect(find.text('Connected to test v6'), findsOneWidget); + + expect(find.text('Auto refresh time'), findsOneWidget); + expect(find.text('5 Seconds'), findsOneWidget); + + expect(find.text('Logs quantity per request'), findsOneWidget); + expect(find.text('2 hours'), findsOneWidget); + + expect(find.text('Advanced settings'), findsOneWidget); + expect(find.text('Advanced options'), findsOneWidget); + + expect(find.text('About'), findsOneWidget); + + expect(find.text('Application Detail'), findsOneWidget); + expect( + find.text('Get help and learn about this app'), + findsOneWidget, + ); + + expect(find.text('Privacy'), findsOneWidget); + expect(find.text('Privacy and Data Management'), findsOneWidget); + + expect(find.text('Important information'), findsOneWidget); + expect( + find.text('Read this if you are experimenting issues'), + findsOneWidget, + ); + + expect(find.text('Legal'), findsOneWidget); + expect(find.text('Legal information'), findsOneWidget); + + expect(find.text('Licenses'), findsOneWidget); + expect(find.text('OSS Information'), findsOneWidget); + + final SvgPicture googlePlaySvg = + SvgPicture.asset('assets/resources/google-play.svg'); + final SvgPicture githubSvg = + SvgPicture.asset('assets/resources/github.svg'); + expect(find.svg(googlePlaySvg.bytesLoader), findsOneWidget); + expect(find.svg(githubSvg.bytesLoader), findsOneWidget); + }, + ); + + testWidgets( + 'should show Theme screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Theme')); + await tester.pumpAndSettle(); + expect(find.byType(ThemeScreen), findsOneWidget); + }, + ); + + testWidgets( + 'should show Language screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Language')); + await tester.pumpAndSettle(); + expect(find.byType(LanguageScreen), findsOneWidget); + }, + ); + + testWidgets( + 'should show Server screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Servers')); + await tester.pumpAndSettle(); + expect(find.byType(ServersPage), findsOneWidget); + }, + ); + + testWidgets( + 'should show Auto refresh time screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Auto refresh time')); + await tester.pumpAndSettle(); + expect(find.byType(AutoRefreshTimeScreen), findsOneWidget); + }, + ); + + testWidgets( + 'should show Logs quantity screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Logs quantity per request')); + await tester.pumpAndSettle(); + expect(find.byType(LogsQuantityLoadScreen), findsOneWidget); + }, + ); + + testWidgets( + 'should show Advanced settings screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Advanced settings')); + await tester.pumpAndSettle(); + expect(find.byType(AdvancedOptions), findsOneWidget); + }, + ); + + testWidgets( + 'should show Application Detail screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Application Detail')); + await tester.pumpAndSettle(); + expect(find.byType(AppDetailModal), findsOneWidget); + }, + ); + + testWidgets( + 'should show Privacy screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Privacy')); + await tester.pumpAndSettle(); + expect(find.byType(PrivacyModal), findsOneWidget); + }, + ); + + testWidgets( + 'should show Important Info screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Important information')); + await tester.pumpAndSettle(); + expect(find.byType(ImportantInfoModal), findsOneWidget); + }, + ); + + testWidgets( + 'should show Legal screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Legal')); + await tester.pumpAndSettle(); + expect(find.byType(LegalModal), findsOneWidget); + }, + ); + + testWidgets( + 'should show Licenses screen with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + await tester.tap(find.text('Licenses')); + when(testSetup.mockConfigProvider.selectedSettingsScreen) + .thenReturn(6); + await tester.pump(const Duration(seconds: 10)); + + // TODO: Not found, always show ircularProgressIndicator???? + // showText(); + // expect(find.byType(LicensePage), findsOneWidget); + }, + ); + + testWidgets( + 'should show Google play page with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + final SvgPicture googlePlaySvg = + SvgPicture.asset('assets/resources/google-play.svg'); + expect(find.svg(googlePlaySvg.bytesLoader), findsOneWidget); + + await tester.tap(find.svg(googlePlaySvg.bytesLoader)); + // await tester.pumpAndSettle(); + // expect(find.byType(), findsOneWidget); // TODO: open url + }, + ); + + testWidgets( + 'should show Github page with tap', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 2.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Settings(), + ), + ); + + expect(find.byType(Settings), findsOneWidget); + await tester.pump(); + + final SvgPicture githubSvg = + SvgPicture.asset('assets/resources/github.svg'); + expect(find.svg(githubSvg.bytesLoader), findsOneWidget); + + await tester.tap(find.svg(githubSvg.bytesLoader)); + // await tester.pumpAndSettle(); + // expect(find.byType(), findsOneWidget); // TODO: open url + }, + ); + }, + ); +} diff --git a/test/widgets/statistics/statistics_test.dart b/test/widgets/statistics/statistics_test.dart new file mode 100644 index 00000000..297b6ae2 --- /dev/null +++ b/test/widgets/statistics/statistics_test.dart @@ -0,0 +1,144 @@ +import 'package:pi_hole_client/screens/statistics/statistics_list.dart'; +import 'package:pie_chart/pie_chart.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:pi_hole_client/screens/statistics/statistics.dart'; +import '../helpers.dart'; + +void main() async { + initializeApp(); + + group( + 'Statistics Screen Widget Tests', + () { + late TestSetupHelper testSetup; + + setUp(() async { + testSetup = TestSetupHelper(); + + testSetup.initializeMock(useApiGatewayVersion: 'v6'); + }); + + testWidgets( + 'should show statistics screen (all graphs are rendered)', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 1.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Statistics(), + ), + ); + + expect(find.byType(Statistics), findsOneWidget); + await tester.pump(); + + expect(find.text('Statistics'), findsWidgets); + + // Tab + expect(find.text('Queries & servers'), findsWidgets); + expect(find.text('Domains'), findsWidgets); + expect(find.text('Clients'), findsWidgets); + + // default: Show Queries & servers + expect(find.text('Query types'), findsOneWidget); + expect(find.text('Upstream servers'), findsOneWidget); + expect(find.text('Top permitted domains'), findsNothing); + expect(find.text('Top blocked domains'), findsNothing); + expect(find.text('Top clients (total)'), findsNothing); + expect(find.text('Top clients (bloked only)'), findsNothing); + expect(find.byType(PieChart), findsNWidgets(2)); + + // Switch to Domains tab + await tester.tap(find.text('Domains')); + await tester.pumpAndSettle(); + expect(find.text('Query types'), findsNothing); + expect(find.text('Upstream servers'), findsNothing); + expect(find.text('Top permitted domains'), findsOneWidget); + expect(find.text('Top blocked domains'), findsOneWidget); + expect(find.text('Top clients (total)'), findsNothing); + expect(find.text('Top clients (blocked only)'), findsNothing); + expect(find.byType(StatisticsListContent), findsOneWidget); + + // Switch to Clients tab + await tester.tap(find.text('Clients')); + await tester.pumpAndSettle(); + expect(find.text('Query types'), findsNothing); + expect(find.text('Upstream servers'), findsNothing); + expect(find.text('Top permitted domains'), findsNothing); + expect(find.text('Top blocked domains'), findsNothing); + expect(find.text('Top clients (total)'), findsOneWidget); + expect(find.text('Top clients (blocked only)'), findsOneWidget); + expect(find.byType(StatisticsListContent), findsOneWidget); + }, + ); + + // TODO + testWidgets( + 'should show statistics screen (no data, no queries)', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 1.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Statistics(), + ), + ); + + expect(find.byType(Statistics), findsOneWidget); + await tester.pump(); + + expect(find.text('Statistics'), findsWidgets); + + // // Tab + // expect(find.text('Queries & servers'), findsWidgets); + // expect(find.text('Domains'), findsWidgets); + // expect(find.text('Clients'), findsWidgets); + + // // default: Show Queries & servers + // expect(find.text('Query types'), findsOneWidget); + // expect(find.text('Upstream servers'), findsOneWidget); + // expect(find.text('Top permitted domains'), findsNothing); + // expect(find.text('Top blocked domains'), findsNothing); + // expect(find.text('Top clients (total)'), findsNothing); + // expect(find.text('Top clients (bloked only)'), findsNothing); + // expect(find.byType(PieChart), findsNWidgets(2)); + + // // Switch to Domains tab + // await tester.tap(find.text('Domains')); + // await tester.pumpAndSettle(); + // expect(find.text('Query types'), findsNothing); + // expect(find.text('Upstream servers'), findsNothing); + // expect(find.text('Top permitted domains'), findsOneWidget); + // expect(find.text('Top blocked domains'), findsOneWidget); + // expect(find.text('Top clients (total)'), findsNothing); + // expect(find.text('Top clients (blocked only)'), findsNothing); + // expect(find.byType(StatisticsListContent), findsOneWidget); + + // // Switch to Clients tab + // await tester.tap(find.text('Clients')); + // await tester.pumpAndSettle(); + // expect(find.text('Query types'), findsNothing); + // expect(find.text('Upstream servers'), findsNothing); + // expect(find.text('Top permitted domains'), findsNothing); + // expect(find.text('Top blocked domains'), findsNothing); + // expect(find.text('Top clients (total)'), findsOneWidget); + // expect(find.text('Top clients (blocked only)'), findsOneWidget); + // expect(find.byType(StatisticsListContent), findsOneWidget); + }, + ); + }, + ); +} diff --git a/test/widgets/unlock_test.dart b/test/widgets/unlock_test.dart new file mode 100644 index 00000000..f8d70732 --- /dev/null +++ b/test/widgets/unlock_test.dart @@ -0,0 +1,52 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:pi_hole_client/screens/unlock.dart'; +import 'package:pi_hole_client/widgets/numeric_pad.dart'; +import './helpers.dart'; + +void main() async { + initializeApp(); + + group( + 'Unlock Screen Widget Tests', + () { + late TestSetupHelper testSetup; + + setUp(() async { + testSetup = TestSetupHelper(); + testSetup.initializeMock(useApiGatewayVersion: 'v6'); + }); + + testWidgets( + 'should show lock screen', + (WidgetTester tester) async { + tester.view.physicalSize = const Size(1080, 2400); + tester.view.devicePixelRatio = 1.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + }); + + await tester.pumpWidget( + testSetup.buildTestWidget( + const Unlock(), + ), + ); + + expect(find.byType(Unlock), findsOneWidget); + await tester.pump(); + expect(find.byIcon(Icons.lock_open_rounded), findsOneWidget); + expect(find.text('Enter code to unlock'), findsOneWidget); + + // numeric pad + expect(find.byType(NumericPad), findsOneWidget); + for (var i = 0; i <= 9; i++) { + expect(find.text(i.toString()), findsOneWidget); + } + expect(find.byIcon(Icons.backspace), findsOneWidget); + }, + ); + }, + ); +} diff --git a/test/widgets/utils.dart b/test/widgets/utils.dart new file mode 100644 index 00000000..30965fd7 --- /dev/null +++ b/test/widgets/utils.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void showText() { + final texts = find.byType(Text); + for (final text in texts.evaluate()) { + // ignore: avoid_print + print(text.widget.toString()); + } +}