diff --git a/.gitignore b/.gitignore index 4bdfd329..9a64d7c0 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,6 @@ coverage/* installers/* .metadata .fvm/ + +# Testing Files & Folders +test-hive-storage diff --git a/lib/app.dart b/lib/app.dart index a2011dcd..48c49a86 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -120,7 +120,7 @@ class DashApp extends ConsumerWidget { home: showWorkspaceSelector ? WorkspaceSelector( onContinue: (val) async { - await openBoxes(kIsDesktop, val); + await initHiveBoxes(kIsDesktop, val); ref .read(settingsProvider.notifier) .update(workspaceFolderPath: val); diff --git a/lib/main.dart b/lib/main.dart index e1e70ec6..7e212dad 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -42,7 +42,7 @@ Future initApp( try { debugPrint("initializeUsingPath: $initializeUsingPath"); debugPrint("workspaceFolderPath: ${settingsModel?.workspaceFolderPath}"); - final openBoxesStatus = await openBoxes( + final openBoxesStatus = await initHiveBoxes( initializeUsingPath, settingsModel?.workspaceFolderPath, ); diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_body.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_body.dart index 39011d81..86ac5a2f 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_body.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_body.dart @@ -17,6 +17,21 @@ class EditRequestBody extends ConsumerWidget { final contentType = ref.watch(selectedRequestModelProvider .select((value) => value?.httpRequestModel?.bodyContentType)); + // TODO: #178 GET->POST Currently switches to POST everytime user edits body even if the user intentionally chooses GET + // final sm = ScaffoldMessenger.of(context); + // void changeToPostMethod() { + // if (requestModel?.httpRequestModel!.method == HTTPVerb.get) { + // ref + // .read(collectionStateNotifierProvider.notifier) + // .update(selectedId, method: HTTPVerb.post); + // sm.hideCurrentSnackBar(); + // sm.showSnackBar(getSnackBar( + // "Switched to POST method", + // small: false, + // )); + // } + // } + return Column( children: [ const SizedBox( @@ -33,8 +48,12 @@ class EditRequestBody extends ConsumerWidget { ), Expanded( child: switch (contentType) { - ContentType.formdata => - const Padding(padding: kPh4, child: FormDataWidget()), + ContentType.formdata => const Padding( + padding: kPh4, + child: FormDataWidget( + // TODO: See changeToPostMethod above + // changeMethodToPost: changeToPostMethod, + )), // TODO: Fix JsonTextFieldEditor & plug it here ContentType.json => Padding( padding: kPt5o10, @@ -43,6 +62,7 @@ class EditRequestBody extends ConsumerWidget { fieldKey: "$selectedId-json-body-editor", initialValue: requestModel?.httpRequestModel?.body, onChanged: (String value) { + // changeToPostMethod(); ref .read(collectionStateNotifierProvider.notifier) .update(selectedId, body: value); @@ -56,6 +76,7 @@ class EditRequestBody extends ConsumerWidget { fieldKey: "$selectedId-body-editor", initialValue: requestModel?.httpRequestModel?.body, onChanged: (String value) { + // changeToPostMethod(); ref .read(collectionStateNotifierProvider.notifier) .update(selectedId, body: value); diff --git a/lib/services/hive_services.dart b/lib/services/hive_services.dart index cc7f8960..cec74101 100644 --- a/lib/services/hive_services.dart +++ b/lib/services/hive_services.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:hive_flutter/hive_flutter.dart'; const String kDataBox = "apidash-data"; @@ -10,7 +11,7 @@ const String kHistoryMetaBox = "apidash-history-meta"; const String kHistoryBoxIds = "historyIds"; const String kHistoryLazyBox = "apidash-history-lazy"; -Future openBoxes( +Future initHiveBoxes( bool initializeUsingPath, String? workspaceFolderPath, ) async { @@ -24,17 +25,65 @@ Future openBoxes( } else { await Hive.initFlutter(); } + final openHiveBoxesStatus = await openHiveBoxes(); + return openHiveBoxesStatus; + } catch (e) { + return false; + } +} +Future openHiveBoxes() async { + try { await Hive.openBox(kDataBox); await Hive.openBox(kEnvironmentBox); await Hive.openBox(kHistoryMetaBox); await Hive.openLazyBox(kHistoryLazyBox); return true; } catch (e) { + debugPrint("ERROR OPEN HIVE BOXES: $e"); return false; } } +Future clearHiveBoxes() async { + try { + if (Hive.isBoxOpen(kDataBox)) { + await Hive.box(kDataBox).clear(); + } + if (Hive.isBoxOpen(kEnvironmentBox)) { + await Hive.box(kEnvironmentBox).clear(); + } + if (Hive.isBoxOpen(kHistoryMetaBox)) { + await Hive.box(kHistoryMetaBox).clear(); + } + if (Hive.isBoxOpen(kHistoryLazyBox)) { + await Hive.lazyBox(kHistoryLazyBox).clear(); + } + } catch (e) { + debugPrint("ERROR CLEAR HIVE BOXES: $e"); + } +} + +Future deleteHiveBoxes() async { + try { + if (Hive.isBoxOpen(kDataBox)) { + await Hive.box(kDataBox).deleteFromDisk(); + } + if (Hive.isBoxOpen(kEnvironmentBox)) { + await Hive.box(kEnvironmentBox).deleteFromDisk(); + } + if (Hive.isBoxOpen(kHistoryMetaBox)) { + await Hive.box(kHistoryMetaBox).deleteFromDisk(); + } + if (Hive.isBoxOpen(kHistoryLazyBox)) { + await Hive.lazyBox(kHistoryLazyBox).deleteFromDisk(); + } + await Hive.close(); + } catch (e) { + debugPrint("ERROR DELETE HIVE BOXES: $e"); + } +} + final hiveHandler = HiveHandler(); class HiveHandler { @@ -93,6 +142,8 @@ class HiveHandler { Future clear() async { await dataBox.clear(); await environmentBox.clear(); + await historyMetaBox.clear(); + await historyLazyBox.clear(); } Future removeUnused() async { diff --git a/pubspec.lock b/pubspec.lock index 2c257859..c17219a4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -259,10 +259,10 @@ packages: dependency: "direct main" description: name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.3.7" dartx: dependency: transitive description: @@ -596,10 +596,10 @@ packages: dependency: "direct main" description: name: fvp - sha256: "6462fd078de4478a0990d437463897036cff98aff3f0ac9efbbf817c99654c87" + sha256: "040aa12beccd5bc60631259f27a481c4abc11a389aa4f57a47b643f58fe0b060" url: "https://pub.dev" source: hosted - version: "0.24.1" + version: "0.26.1" glob: dependency: transitive description: @@ -778,10 +778,10 @@ packages: dependency: "direct main" description: name: just_audio - sha256: ee50602364ba83fa6308f5512dd560c713ec3e1f2bc75f0db43618f0d82ef71a + sha256: d8e8aaf417d33e345299c17f6457f72bd4ba0c549dc34607abb5183a354edc4d url: "https://pub.dev" source: hosted - version: "0.9.39" + version: "0.9.40" just_audio_mpv: dependency: "direct main" description: @@ -1552,10 +1552,10 @@ packages: dependency: "direct main" description: name: uuid - sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90" + sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77 url: "https://pub.dev" source: hosted - version: "4.4.2" + version: "4.5.0" vector_graphics: dependency: transitive description: @@ -1727,4 +1727,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.5.0-259.0.dev <3.999.0" - flutter: ">=3.22.0" + flutter: ">=3.24.3" diff --git a/pubspec.yaml b/pubspec.yaml index a9bf5998..41d4f3d0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ version: 0.4.0+4 environment: sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.19.0" + flutter: ">=3.24.3" dependencies: flutter: @@ -18,7 +18,7 @@ dependencies: url: https://github.com/foss42/curl_converter.git ref: 726e8cd04aeb326211af27f75920be5b21c90bb4 data_table_2: ^2.5.15 - dart_style: ^2.3.6 + dart_style: ^2.3.7 desktop_drop: ^0.4.4 extended_text_field: ^16.0.0 file_selector: ^1.0.3 @@ -30,7 +30,7 @@ dependencies: flutter_svg: ^2.0.10+1 flutter_typeahead: ^5.2.0 freezed_annotation: ^2.4.1 - fvp: ^0.24.1 + fvp: ^0.26.1 google_fonts: ^6.2.1 highlighter: ^0.1.1 hive_flutter: ^1.1.0 @@ -45,7 +45,7 @@ dependencies: url: https://github.com/foss42/json_data_explorer.git ref: b7dde2f85dff4f482eed7eda4ef2a71344ef8b3a json_text_field: ^1.2.0 - just_audio: ^0.9.34 + just_audio: ^0.9.40 just_audio_mpv: ^0.1.7 just_audio_windows: ^0.2.0 lottie: ^3.1.0 @@ -65,7 +65,7 @@ dependencies: scrollable_positioned_list: ^0.3.8 shared_preferences: ^2.3.2 url_launcher: ^6.2.5 - uuid: ^4.3.3 + uuid: ^4.5.0 vector_graphics_compiler: ^1.1.9+1 video_player: ^2.8.7 video_player_platform_interface: ^6.2.2 diff --git a/test/providers/collection_providers_test.dart b/test/providers/collection_providers_test.dart new file mode 100644 index 00000000..fb939522 --- /dev/null +++ b/test/providers/collection_providers_test.dart @@ -0,0 +1,54 @@ +import 'package:apidash/consts.dart'; +import 'package:apidash/screens/home_page/editor_pane/details_card/request_pane/request_body.dart'; +import 'package:apidash/widgets/editor.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:apidash/providers/providers.dart'; +import 'helpers.dart'; + +void main() async { + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() async { + await testSetUpTempDirForHive(); + }); + + testWidgets( + 'Request method changes from GET to POST when body is added and Snackbar is shown', + (WidgetTester tester) async { + // Set up the test environment + final container = createContainer(); + final notifier = container.read(collectionStateNotifierProvider.notifier); + + // Ensure the initial request is a GET request with no body + final id = notifier.state!.entries.first.key; + expect( + notifier.getRequestModel(id)!.httpRequestModel!.method, HTTPVerb.get); + expect(notifier.getRequestModel(id)!.httpRequestModel!.body, isNull); + + // Build the EditRequestBody widget + await tester.pumpWidget( + ProviderScope( + // ignore: deprecated_member_use + parent: container, + child: const MaterialApp( + home: Scaffold( + body: EditRequestBody(), + ), + ), + ), + ); + + // Add a body to the request, which should trigger the method change + await tester.enterText(find.byType(TextFieldEditor), 'new body added'); + await tester.pump(); // Process the state change + + // Verify that the request method changed to POST + expect( + notifier.getRequestModel(id)!.httpRequestModel!.method, HTTPVerb.post); + + // Verify that the Snackbar is shown + expect(find.text('Switched to POST method'), findsOneWidget); + }, skip: true); +} diff --git a/test/providers/helpers.dart b/test/providers/helpers.dart new file mode 100644 index 00000000..568a849c --- /dev/null +++ b/test/providers/helpers.dart @@ -0,0 +1,57 @@ +import 'dart:io'; +import 'package:apidash/services/hive_services.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// A testing utility which creates a [ProviderContainer] and automatically +/// disposes it at the end of the test. +ProviderContainer createContainer({ + ProviderContainer? parent, + List overrides = const [], + List? observers, +}) { + // Create a ProviderContainer, and optionally allow specifying parameters. + final container = ProviderContainer( + parent: parent, + overrides: overrides, + observers: observers, + ); + + // When the test ends, dispose the container. + addTearDown(container.dispose); + + return container; +} + +Future testSetUpForHive() async { + // override path_provider methodCall to point + // path to temporary location for all unit tests + const MethodChannel channel = + MethodChannel('plugins.flutter.io/path_provider'); + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + return './test-hive-storage/'; + }); + + await initHiveBoxes(false, null); + // await deleteHiveBoxes(); + // await openHiveBoxes(); +} + +Future testSetUpTempDirForHive() async { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/path_provider'); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'getApplicationDocumentsDirectory') { + // Create a mock app doc directory for testing + Directory tempDir = + await Directory.systemTemp.createTemp('mock_app_doc_dir'); + return tempDir.path; // Return the path to the mock directory + } + return null; + }); + await initHiveBoxes(false, null); +} diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index 60b67241..e8fd0582 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -1,4 +1,3 @@ -import 'dart:io'; //import 'package:spot/spot.dart'; import 'package:apidash/providers/providers.dart'; import 'package:apidash/screens/common_widgets/common_widgets.dart'; @@ -12,7 +11,6 @@ import 'package:apidash/screens/home_page/editor_pane/url_card.dart'; import 'package:apidash/screens/home_page/home_page.dart'; import 'package:apidash/screens/settings_page.dart'; import 'package:apidash/screens/history/history_page.dart'; -import 'package:apidash/services/hive_services.dart'; import 'package:apidash/widgets/widgets.dart'; import 'package:extended_text_field/extended_text_field.dart'; import 'package:flutter/material.dart'; @@ -22,24 +20,13 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import '../extensions/widget_tester_extensions.dart'; import '../test_consts.dart'; +import 'helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); setUp(() async { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/path_provider'); - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(channel, (MethodCall methodCall) async { - if (methodCall.method == 'getApplicationDocumentsDirectory') { - // Create a mock app doc directory for testing - Directory tempDir = - await Directory.systemTemp.createTemp('mock_app_doc_dir'); - return tempDir.path; // Return the path to the mock directory - } - return null; - }); - await openBoxes(false, null); + await testSetUpTempDirForHive(); final flamante = rootBundle.load('google_fonts/OpenSans-Medium.ttf'); final fontLoader = FontLoader('OpenSans')..addFont(flamante); await fontLoader.load(); diff --git a/test/widgets/table_request_form_test.dart b/test/widgets/table_request_form_test.dart index a1c67424..c21a9b70 100644 --- a/test/widgets/table_request_form_test.dart +++ b/test/widgets/table_request_form_test.dart @@ -6,6 +6,7 @@ import 'package:apidash/models/models.dart'; import 'package:apidash/consts.dart'; void main() { + dataTableShowLogs = false; testWidgets('Testing RequestFormDataTable', (WidgetTester tester) async { const List sampleData = [ FormDataModel(name: 'Key1', value: 'Value1', type: FormDataType.file), diff --git a/test/widgets/table_request_test.dart b/test/widgets/table_request_test.dart index 6e8c2c9f..cc0e3324 100644 --- a/test/widgets/table_request_test.dart +++ b/test/widgets/table_request_test.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { + dataTableShowLogs = false; testWidgets('Testing RequestDataTable', (WidgetTester tester) async { final Map sampleData = { 'Key1': 'Value1',