diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart new file mode 100644 index 0000000..5450523 --- /dev/null +++ b/integration_test/app_test.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:logger/logger.dart'; +import 'package:prasule/api/category.dart'; +import 'package:prasule/api/entry_data.dart'; +import 'package:prasule/api/wallet.dart'; +import 'package:prasule/api/wallet_entry.dart'; +import 'package:prasule/api/wallet_manager.dart'; + +import 'package:prasule/main.dart'; +import 'package:prasule/pw/platformfield.dart'; + +void main() { + final logger = Logger(); + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group("Test classes and API", () { + test("Test wallet operations", () async { + expect( + (await WalletManager.listWallets()).length, + equals(0), + ); // check that there are no other wallets + await WalletManager.saveWallet(Wallet.empty); + var w = (await WalletManager.listWallets()).firstOrNull; + expect(w, isNotNull); // check that the wallet was successfully saved + expect(w!.categories.length, equals(1)); + w.categories.add( + WalletCategory( + name: "Testing", + id: w.nextCategoryId, + icon: Icons.abc, + color: Colors.orange, + ), + ); // create test category + final testId = w.nextId; + w.entries.add( + WalletSingleEntry( + data: EntryData(amount: 200, name: "Automated"), + type: EntryType.expense, + date: DateTime.now(), + category: w.categories.last, + id: w.nextId, + ), + ); // create test entry + await WalletManager.saveWallet(w); // save again + w = await WalletManager.loadWallet(w.name); // try loading manually + final e = w.entries.where((element) => element.id == testId).firstOrNull; + expect( + e, + isNotNull, + ); // check that the entry was successfully created + expect( + w.categories.where((element) => element.id == e!.category.id).length, + equals(1), + ); // check that the category exists too + + await WalletManager.deleteWallet(w); + expect( + (await WalletManager.listWallets()).length, + equals(0), + ); + }); + }); + + group("Test app functionality:", () { + testWidgets('First-time setup', (WidgetTester tester) async { + // Delete all data + await WalletManager.deleteAllData(); + // Build our app and trigger a frame. + await tester.pumpWidget( + const MyApp( + locale: Locale('en', 'US'), + ), + ); + await tester.pumpAndSettle(); + logger.i("Looking for welcome header"); + expect(find.text('Welcome!'), findsOneWidget); + + // Tap "Next" button + await tester.tap(find.text("Next")); + await tester.pumpAndSettle(); + + logger.i("Next view, looking for name+balance fields"); + + final firstFields = find.byType(PlatformField); + + expect(firstFields, findsExactly(2)); + + logger.i("Entering text"); + await tester.enterText(find.byType(PlatformField).first, "Debugging"); + await tester.pumpAndSettle(); + await tester.enterText(find.byType(PlatformField).last, "100"); + await tester.pumpAndSettle(); + + // Tap "Next" button + await tester.tap(find.text("Next")); + await tester.pumpAndSettle(); + + // Tap "Finish" button + await tester.tap(find.text("Finish")); + await tester.pumpAndSettle(); + + expect( + find.byWidgetPredicate( + (widget) => + widget is DropdownButton && + ((widget as DropdownButton).value ?? -1) == 0, + ), + findsOne, + ); + }); + + testWidgets('Test correct rendering of entries', + (WidgetTester tester) async { + // Delete all data + await WalletManager.deleteAllData(); + expect((await WalletManager.listWallets()).length, equals(0)); + + // Create test data + final w = Wallet.empty; + await w.createTestEntries(); + expect((await WalletManager.listWallets()).length, equals(1)); + + // Build our app and trigger a frame. + await tester.pumpWidget( + const MyApp( + locale: Locale('en', 'US'), + ), + ); + await tester.pumpAndSettle(); + + // TODO: better test + + expect( + find.byType(ListTile, skipOffstage: false), + findsAtLeast(10), + ); + }); + }); +} diff --git a/integration_test/setup_test.dart b/integration_test/setup_test.dart deleted file mode 100644 index 4a9294f..0000000 --- a/integration_test/setup_test.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:logger/logger.dart'; -import 'package:prasule/api/wallet_manager.dart'; - -import 'package:prasule/main.dart'; -import 'package:prasule/pw/platformfield.dart'; - -void main() { - final logger = Logger(); - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - group("Test Setup screen:", () { - testWidgets('First-time setup', (WidgetTester tester) async { - // delete all data - await WalletManager.deleteAllData(); - // Build our app and trigger a frame. - await tester.pumpWidget( - const MyApp( - locale: Locale('en', 'US'), - ), - ); - await tester.pumpAndSettle(); - logger.i("Looking for welcome header"); - expect(find.text('Welcome!'), findsOneWidget); - - // Tap "Next" button - await tester.tap(find.text("Next")); - await tester.pumpAndSettle(); - - logger.i("Next view, looking for name+balance fields"); - - final firstFields = find.byType(PlatformField); - - expect(firstFields, findsExactly(2)); - - logger.i("Entering text"); - await tester.enterText(find.byType(PlatformField).first, "Debugging"); - await tester.pumpAndSettle(); - await tester.enterText(find.byType(PlatformField).last, "100"); - await tester.pumpAndSettle(); - - // Tap "Next" button - await tester.tap(find.text("Next")); - await tester.pumpAndSettle(); - - // Tap "Finish" button - await tester.tap(find.text("Finish")); - await tester.pumpAndSettle(); - - expect( - find.byWidgetPredicate( - (widget) => - widget is DropdownButton && - ((widget as DropdownButton).value ?? -1) == 0, - ), - findsOne, - ); - }); - }); -} diff --git a/lib/api/wallet.dart b/lib/api/wallet.dart index 077cea5..fc0cc8b 100644 --- a/lib/api/wallet.dart +++ b/lib/api/wallet.dart @@ -1,7 +1,11 @@ +import 'dart:math'; + import 'package:currency_picker/currency_picker.dart'; +import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:prasule/api/category.dart'; +import 'package:prasule/api/entry_data.dart'; import 'package:prasule/api/recurring_entry.dart'; import 'package:prasule/api/wallet_entry.dart'; import 'package:prasule/api/wallet_manager.dart'; @@ -158,6 +162,19 @@ class Wallet { /// Empty wallet used for placeholders static final Wallet empty = Wallet( name: "Empty", + entries: [], + recurringEntries: [], + categories: [ + WalletCategory( + name: "Default", + id: 0, + icon: IconData( + Icons.payments.codePoint, + fontFamily: 'MaterialIcons', + ), + color: Colors.white, + ) + ], currency: Currency.from( json: { "code": "USD", @@ -174,4 +191,66 @@ class Wallet { }, ), ); + + /// Creates test data used for debugging purposes + Future createTestEntries() async { + entries.clear(); + recurringEntries.clear(); + final random = Random(); + for (var i = 0; i < 30; i++) { + entries.add( + WalletSingleEntry( + data: EntryData( + name: "Test Entry #${i + 1}", + amount: random.nextInt(20000).toDouble(), + ), + type: (random.nextInt(3) > 0) ? EntryType.expense : EntryType.income, + date: DateTime( + 2023, + random.nextInt(12) + 1, + random.nextInt(28) + 1, + ), + category: categories[random.nextInt(categories.length)], + id: nextId, + ), + ); + } + + logger.d( + "Created ${entries.length} regular entries", + ); + + for (var i = 0; i < 3; i++) { + final type = random.nextInt(3); + recurringEntries.add( + RecurringWalletEntry( + data: EntryData( + name: "Recurring Entry #${i + 1}", + amount: random.nextInt(20000).toDouble(), + ), + type: (random.nextInt(3) > 0) ? EntryType.expense : EntryType.income, + date: DateTime( + 2023, + random.nextInt(12) + 1, + random.nextInt(28) + 1, + ), + category: categories[random.nextInt(categories.length)], + id: nextId, + lastRunDate: DateTime.now().subtract( + Duration( + days: (type > 0) ? 3 : 3 * 31, + ), + ), + recurType: (type > 0) ? RecurType.day : RecurType.month, + ), + ); + } + + logger.d( + "Created ${recurringEntries.length} recurring entries", + ); + + // save and reload + await WalletManager.saveWallet(this); + } } diff --git a/lib/api/wallet_manager.dart b/lib/api/wallet_manager.dart index de1a3f6..772c85a 100644 --- a/lib/api/wallet_manager.dart +++ b/lib/api/wallet_manager.dart @@ -24,7 +24,6 @@ class WalletManager { // TODO: do something with unreadable wallets } } - logger.i(wallets.length); return wallets; } @@ -37,7 +36,6 @@ class WalletManager { } for (final entry in path.listSync()) { - if (!entry.path.endsWith(".json")) return; // only delete wallet files logger.d("Deleting ${entry.path}"); entry.deleteSync(); } diff --git a/lib/views/home.dart b/lib/views/home.dart index 6b31dac..ab0a45f 100644 --- a/lib/views/home.dart +++ b/lib/views/home.dart @@ -1,7 +1,6 @@ // ignore_for_file: inference_failure_on_function_invocation import 'dart:async'; -import 'dart:math'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/foundation.dart'; @@ -16,7 +15,6 @@ import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/intl.dart'; import 'package:prasule/api/category.dart'; import 'package:prasule/api/entry_data.dart'; -import 'package:prasule/api/recurring_entry.dart'; import 'package:prasule/api/wallet.dart'; import 'package:prasule/api/wallet_entry.dart'; import 'package:prasule/api/wallet_manager.dart'; @@ -88,70 +86,7 @@ class _HomeViewState extends State { onTap: () { // debug option to quickly fill a wallet with data if (selectedWallet == null) return; - selectedWallet!.entries.clear(); - selectedWallet!.recurringEntries.clear(); - final random = Random(); - for (var i = 0; i < 30; i++) { - selectedWallet!.entries.add( - WalletSingleEntry( - data: EntryData( - name: "Test Entry #${i + 1}", - amount: random.nextInt(20000).toDouble(), - ), - type: (random.nextInt(3) > 0) - ? EntryType.expense - : EntryType.income, - date: DateTime( - 2023, - random.nextInt(12) + 1, - random.nextInt(28) + 1, - ), - category: selectedWallet!.categories[ - random.nextInt(selectedWallet!.categories.length)], - id: selectedWallet!.nextId, - ), - ); - } - - logger.d( - "Created ${selectedWallet!.entries.length} regular entries", - ); - - for (var i = 0; i < 3; i++) { - final type = random.nextInt(3); - selectedWallet!.recurringEntries.add( - RecurringWalletEntry( - data: EntryData( - name: "Recurring Entry #${i + 1}", - amount: random.nextInt(20000).toDouble(), - ), - type: (random.nextInt(3) > 0) - ? EntryType.expense - : EntryType.income, - date: DateTime( - 2023, - random.nextInt(12) + 1, - random.nextInt(28) + 1, - ), - category: selectedWallet!.categories[ - random.nextInt(selectedWallet!.categories.length)], - id: selectedWallet!.nextId, - lastRunDate: DateTime.now().subtract( - Duration( - days: (type > 0) ? 3 : 3 * 31, - ), - ), - recurType: (type > 0) ? RecurType.day : RecurType.month, - ), - ); - } - - logger.d( - "Created ${selectedWallet!.recurringEntries.length} recurring entries", - ); - - // save and reload - WalletManager.saveWallet(selectedWallet!).then((value) { + selectedWallet!.createTestEntries().then((_) { Navigator.of(context).pushReplacement( platformRoute( (p0) => const HomeView(),