home added
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:receipity/models/line_item.dart';
|
||||
|
||||
import 'test_helpers.dart';
|
||||
|
||||
void main() {
|
||||
ensureTestSqlite();
|
||||
|
||||
test('deleting a product keeps the trip line and drops the catalog row',
|
||||
() async {
|
||||
final repository = memoryRepository();
|
||||
final shopId = await repository.saveShop(
|
||||
supermarket: 'Jumbo',
|
||||
shoppedAt: DateTime(2026, 8, 12),
|
||||
items: [LineItem(id: '1', name: 'Milk', price: 1.5)],
|
||||
);
|
||||
|
||||
final products = await repository.getAll();
|
||||
expect(products, hasLength(1));
|
||||
await repository.deleteProduct(products.single.id);
|
||||
|
||||
expect(await repository.getAll(), isEmpty);
|
||||
final items = await repository.getShopItems(shopId);
|
||||
expect(items, hasLength(1));
|
||||
expect(items.single.name, 'Milk');
|
||||
expect(items.single.productId, isNull);
|
||||
});
|
||||
|
||||
test('deleting all products leaves shopping trips in place', () async {
|
||||
final repository = memoryRepository();
|
||||
await repository.saveShop(
|
||||
supermarket: 'Jumbo',
|
||||
shoppedAt: DateTime(2026, 8, 12),
|
||||
items: [
|
||||
LineItem(id: '1', name: 'Milk', price: 1.5),
|
||||
LineItem(id: '2', name: 'Bread', price: 2),
|
||||
],
|
||||
);
|
||||
await repository.deleteAllProducts();
|
||||
|
||||
expect(await repository.getAll(), isEmpty);
|
||||
expect(await repository.getShops(), hasLength(1));
|
||||
final overview = await repository.getOverview();
|
||||
expect(overview.productCount, 0);
|
||||
expect(overview.tripCount, 1);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:receipity/services/product_repository.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
var _dbSeq = 0;
|
||||
var _sqliteReady = false;
|
||||
|
||||
void ensureTestSqlite() {
|
||||
if (_sqliteReady) return;
|
||||
sqfliteFfiInit();
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
_sqliteReady = true;
|
||||
}
|
||||
|
||||
ProductRepository memoryRepository() {
|
||||
ensureTestSqlite();
|
||||
final dir = Directory(p.join('.dart_tool', 'receipity_test_db'))
|
||||
..createSync(recursive: true);
|
||||
final name =
|
||||
'db_${DateTime.now().microsecondsSinceEpoch}_${_dbSeq++}_${Random().nextInt(1 << 32)}.sqlite';
|
||||
return ProductRepository(databasePath: p.join(dir.path, name));
|
||||
}
|
||||
|
||||
Future<void> pumpUntilFound(
|
||||
WidgetTester tester,
|
||||
Finder finder, {
|
||||
int attempts = 40,
|
||||
}) async {
|
||||
for (var i = 0; i < attempts; i++) {
|
||||
await tester.pump();
|
||||
if (finder.evaluate().isNotEmpty) return;
|
||||
await tester.runAsync(
|
||||
() => Future<void>.delayed(const Duration(milliseconds: 20)),
|
||||
);
|
||||
}
|
||||
throw TestFailure('Timed out waiting for $finder');
|
||||
}
|
||||
+133
-17
@@ -3,14 +3,34 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:receipity/main.dart';
|
||||
import 'package:receipity/models/line_item.dart';
|
||||
import 'package:receipity/review_screen.dart';
|
||||
import 'package:receipity/services/product_repository.dart';
|
||||
import 'package:receipity/widgets/shop_heatmap.dart';
|
||||
|
||||
import 'test_helpers.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('scan screen shows capture actions and bottom navigation', (
|
||||
ensureTestSqlite();
|
||||
|
||||
testWidgets('home screen shows overview and bottom navigation', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(ReceipityApp());
|
||||
await tester.pumpWidget(ReceipityApp(repository: memoryRepository()));
|
||||
await pumpUntilFound(tester, find.text('Nothing logged yet.'));
|
||||
|
||||
expect(find.text('Home'), findsWidgets);
|
||||
expect(find.text('Products'), findsOneWidget);
|
||||
expect(find.text('Lists'), findsOneWidget);
|
||||
expect(find.byTooltip('Add'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.add), findsWidgets);
|
||||
expect(find.text('Shop log'), findsOneWidget);
|
||||
expect(find.byTooltip('Profile'), findsOneWidget);
|
||||
expect(find.byTooltip('Settings'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('scan screen shows capture actions', (tester) async {
|
||||
await tester.pumpWidget(ReceipityApp(repository: memoryRepository()));
|
||||
await pumpUntilFound(tester, find.text('Overview'));
|
||||
await tester.tap(find.byTooltip('Add'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Receipity'), findsOneWidget);
|
||||
expect(find.text('Take photo'), findsOneWidget);
|
||||
@@ -21,18 +41,111 @@ void main() {
|
||||
find.textContaining('Scan a receipt or enter a trip by hand'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('Products'), findsOneWidget);
|
||||
expect(find.text('Lists'), findsOneWidget);
|
||||
expect(find.byTooltip('Add'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.add), findsOneWidget);
|
||||
expect(find.text('Shop log'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shop log has product search', (tester) async {
|
||||
await tester.pumpWidget(ReceipityApp());
|
||||
testWidgets('empty shop log hides edit and disables search', (tester) async {
|
||||
await tester.pumpWidget(ReceipityApp(repository: memoryRepository()));
|
||||
await pumpUntilFound(tester, find.text('Overview'));
|
||||
await tester.tap(find.text('Shop log'));
|
||||
await tester.pump();
|
||||
await pumpUntilFound(
|
||||
tester,
|
||||
find.text('Scan a receipt or enter a trip to log it.'),
|
||||
);
|
||||
|
||||
expect(find.byTooltip('Edit'), findsNothing);
|
||||
expect(find.byTooltip('Search products'), findsOneWidget);
|
||||
expect(
|
||||
tester
|
||||
.widget<IconButton>(
|
||||
find.ancestor(
|
||||
of: find.byIcon(Icons.search),
|
||||
matching: find.byType(IconButton),
|
||||
),
|
||||
)
|
||||
.onPressed,
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('deleting a trip removes it without leaving the screen', (
|
||||
tester,
|
||||
) async {
|
||||
final repository = memoryRepository();
|
||||
await tester.runAsync(() async {
|
||||
await repository.saveShop(
|
||||
supermarket: 'Jumbo',
|
||||
shoppedAt: DateTime.now(),
|
||||
name: 'Weekly shop',
|
||||
items: [LineItem(id: '1', name: 'Milk', price: 1.5)],
|
||||
);
|
||||
});
|
||||
|
||||
await tester.pumpWidget(ReceipityApp(repository: repository));
|
||||
await pumpUntilFound(tester, find.text('Overview'));
|
||||
await tester.tap(find.text('Shop log'));
|
||||
await pumpUntilFound(tester, find.text('Weekly shop'));
|
||||
|
||||
await tester.tap(find.byTooltip('Edit'));
|
||||
await tester.pump();
|
||||
await tester.tap(find.byTooltip('Remove'));
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('Delete'));
|
||||
await tester.pump();
|
||||
await tester.runAsync(
|
||||
() => Future<void>.delayed(const Duration(milliseconds: 50)),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Weekly shop'), findsNothing);
|
||||
expect(
|
||||
find.text('Scan a receipt or enter a trip to log it.'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byTooltip('Edit'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('products edit mode shows delete, star, and wipe all', (
|
||||
tester,
|
||||
) async {
|
||||
final repository = memoryRepository();
|
||||
await tester.runAsync(() async {
|
||||
await repository.saveShop(
|
||||
supermarket: 'Jumbo',
|
||||
shoppedAt: DateTime.now(),
|
||||
items: [LineItem(id: '1', name: 'Milk', price: 1.5)],
|
||||
);
|
||||
});
|
||||
|
||||
await tester.pumpWidget(ReceipityApp(repository: repository));
|
||||
await pumpUntilFound(tester, find.text('Overview'));
|
||||
await tester.tap(find.text('Products').last);
|
||||
await pumpUntilFound(tester, find.text('Milk'));
|
||||
|
||||
await tester.tap(find.byTooltip('Edit'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.byTooltip('Delete'), findsOneWidget);
|
||||
expect(find.byTooltip('Favourite'), findsOneWidget);
|
||||
expect(find.byTooltip('Delete all products'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byTooltip('Delete all products'));
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('Delete'));
|
||||
await tester.pump();
|
||||
await tester.runAsync(
|
||||
() => Future<void>.delayed(const Duration(milliseconds: 50)),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Milk'), findsNothing);
|
||||
expect(find.byTooltip('Edit'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('profile screen is a placeholder', (tester) async {
|
||||
await tester.pumpWidget(ReceipityApp(repository: memoryRepository()));
|
||||
await pumpUntilFound(tester, find.text('Overview'));
|
||||
await tester.tap(find.byTooltip('Profile'));
|
||||
await pumpUntilFound(tester, find.text('Guest'));
|
||||
});
|
||||
|
||||
testWidgets('shop heatmap renders the visible month', (tester) async {
|
||||
@@ -59,15 +172,18 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('enter manually opens a blank trip', (tester) async {
|
||||
await tester.pumpWidget(ReceipityApp());
|
||||
await tester.pumpWidget(ReceipityApp(repository: memoryRepository()));
|
||||
await pumpUntilFound(tester, find.text('Overview'));
|
||||
await tester.tap(find.byTooltip('Add'));
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('Enter manually'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('New trip'), findsOneWidget);
|
||||
await pumpUntilFound(tester, find.text('New trip'));
|
||||
expect(find.text('Add item'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('lists tab shows shopping lists', (tester) async {
|
||||
await tester.pumpWidget(ReceipityApp());
|
||||
await tester.pumpWidget(ReceipityApp(repository: memoryRepository()));
|
||||
await pumpUntilFound(tester, find.text('Overview'));
|
||||
await tester.tap(find.text('Lists'));
|
||||
await tester.pump();
|
||||
expect(find.text('Shopping lists'), findsOneWidget);
|
||||
@@ -82,7 +198,7 @@ void main() {
|
||||
LineItem(id: '2', name: 'Bread', price: 2),
|
||||
],
|
||||
rawText: 'Milk 1.50\nBread 2.00',
|
||||
repository: ProductRepository(databasePath: ':memory:'),
|
||||
repository: memoryRepository(),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -109,7 +225,7 @@ void main() {
|
||||
home: ReviewScreen(
|
||||
items: [LineItem(id: '1', name: 'Milk', price: 1.5)],
|
||||
rawText: 'Jumbo\nMilk 1.50',
|
||||
repository: ProductRepository(databasePath: ':memory:'),
|
||||
repository: memoryRepository(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user