236 lines
7.7 KiB
Dart
236 lines
7.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
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/widgets/shop_heatmap.dart';
|
|
|
|
import 'test_helpers.dart';
|
|
|
|
void main() {
|
|
ensureTestSqlite();
|
|
|
|
testWidgets('home screen shows overview and bottom navigation', (
|
|
tester,
|
|
) async {
|
|
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);
|
|
expect(find.text('Pick from gallery'), findsOneWidget);
|
|
expect(find.text('Enter manually'), findsOneWidget);
|
|
expect(find.text('Use sample receipt'), findsOneWidget);
|
|
expect(
|
|
find.textContaining('Scan a receipt or enter a trip by hand'),
|
|
findsOneWidget,
|
|
);
|
|
});
|
|
|
|
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 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 {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
body: ShopHeatmap(
|
|
month: DateTime(2026, 8),
|
|
counts: const {},
|
|
monthTotal: 0,
|
|
monthTripCount: 0,
|
|
canGoForward: false,
|
|
onPreviousMonth: () {},
|
|
onNextMonth: () {},
|
|
onSelectDay: (_) {},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(find.text('August 2026'), findsOneWidget);
|
|
expect(find.text('No trips this month'), findsOneWidget);
|
|
expect(find.text('Less'), findsOneWidget);
|
|
expect(find.text('More'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('enter manually opens a blank trip', (tester) async {
|
|
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 pumpUntilFound(tester, find.text('New trip'));
|
|
expect(find.text('Add item'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('lists tab shows shopping lists', (tester) async {
|
|
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);
|
|
});
|
|
|
|
testWidgets('review screen lists items and can delete one', (tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: ReviewScreen(
|
|
items: [
|
|
LineItem(id: '1', name: 'Milk', price: 1.5),
|
|
LineItem(id: '2', name: 'Bread', price: 2),
|
|
],
|
|
rawText: 'Milk 1.50\nBread 2.00',
|
|
repository: memoryRepository(),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Milk'), findsOneWidget);
|
|
expect(find.text('Bread'), findsOneWidget);
|
|
expect(find.text('Add item'), findsOneWidget);
|
|
expect(find.textContaining('Save trip'), findsOneWidget);
|
|
expect(find.text('Supermarket'), findsOneWidget);
|
|
expect(find.text('Trip name'), findsOneWidget);
|
|
expect(find.text('Add tag'), findsOneWidget);
|
|
|
|
await tester.drag(find.text('Milk'), const Offset(-500, 0));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Milk'), findsNothing);
|
|
expect(find.text('Bread'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('review screen preselects a supermarket from OCR text', (
|
|
tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: ReviewScreen(
|
|
items: [LineItem(id: '1', name: 'Milk', price: 1.5)],
|
|
rawText: 'Jumbo\nMilk 1.50',
|
|
repository: memoryRepository(),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Jumbo'), findsWidgets);
|
|
});
|
|
}
|