new plus button

This commit is contained in:
davmar
2026-08-31 09:40:38 +02:00
parent 3b698a3667
commit 92581e6d55
17 changed files with 1336 additions and 152 deletions
+8 -1
View File
@@ -4,7 +4,7 @@ import 'package:receipity/services/backup_format.dart';
void main() {
test('round-trips a backup document', () {
const original = BackupDocument(
version: 2,
version: 3,
exportedAt: '2026-08-29T12:00:00.000',
darkMode: true,
supermarkets: [
@@ -29,6 +29,7 @@ void main() {
{
'id': 8,
'supermarket': 'Jumbo',
'name': 'Weekly shop',
'shopped_at': '2026-08-29T12:00:00.000',
'receipt_image_path': 'receipts/shop_8.jpg',
'total': 1.5,
@@ -46,6 +47,9 @@ void main() {
'unit_price': 1.5,
},
],
shopTags: [
{'id': 1, 'shop_id': 8, 'tag': 'weekly', 'normalized_tag': 'weekly'},
],
shoppingLists: [
{
'id': 1,
@@ -71,7 +75,9 @@ void main() {
expect(parsed.darkMode, isTrue);
expect(parsed.products.single['name'], 'Milk');
expect(parsed.shops.single['id'], 8);
expect(parsed.shops.single['name'], 'Weekly shop');
expect(parsed.shopItems.single['product_id'], 1);
expect(parsed.shopTags.single['tag'], 'weekly');
expect(parsed.shoppingLists.single['name'], 'Weekly');
expect(parsed.shoppingListItems.single['name'], 'Milk');
expect(parsed.images['products/product_1.jpg'], 'abc123');
@@ -95,6 +101,7 @@ void main() {
expect(parsed.version, 1);
expect(parsed.shoppingLists, isEmpty);
expect(parsed.shoppingListItems, isEmpty);
expect(parsed.shopTags, isEmpty);
});
test('rejects a file that is not a Receipity backup', () {
+96
View File
@@ -0,0 +1,96 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:receipity/models/shop.dart';
import 'package:receipity/utils/shop_calendar.dart';
import 'package:receipity/utils/shop_log_filter.dart';
Shop _shop({
required int id,
required DateTime at,
String supermarket = 'Jumbo',
String? name,
List<String> tags = const [],
}) {
return Shop(
id: id,
supermarket: supermarket,
name: name,
shoppedAt: at,
total: 10,
itemCount: 1,
tags: tags,
);
}
void main() {
group('monthGrid', () {
test('pads August 2026 from Saturday with Monday-first weeks', () {
final cells = monthGrid(DateTime(2026, 8));
expect(cells.length % 7, 0);
expect(cells[0], isNull);
expect(cells[1], isNull);
expect(cells[2], isNull);
expect(cells[3], isNull);
expect(cells[4], isNull);
expect(cells[5], DateTime(2026, 8, 1));
expect(cells[35], DateTime(2026, 8, 31));
expect(cells.last, isNull);
});
});
test('tripCountsByDay groups multiple trips on the same day', () {
final counts = tripCountsByDay([
DateTime(2026, 8, 12, 9),
DateTime(2026, 8, 12, 18),
DateTime(2026, 8, 13),
]);
expect(counts[DateTime(2026, 8, 12)], 2);
expect(counts[DateTime(2026, 8, 13)], 1);
expect(heatmapLevel(0), 0);
expect(heatmapLevel(1), 1);
expect(heatmapLevel(4), 4);
expect(heatmapLevel(9), 4);
});
group('filterShops', () {
final shops = [
_shop(id: 1, at: DateTime(2026, 8, 12), tags: const ['weekly']),
_shop(
id: 2,
at: DateTime(2026, 8, 13),
name: 'Work lunch',
tags: const ['work'],
),
];
const names = {
1: ['Milk', 'Bread'],
2: ['Coffee'],
};
test('filters by product name', () {
final found = filterShops(
shops: shops,
productNames: names,
query: 'milk',
);
expect(found.map((shop) => shop.id), [1]);
});
test('filters by selected day', () {
final found = filterShops(
shops: shops,
productNames: names,
selectedDay: DateTime(2026, 8, 13),
);
expect(found.single.id, 2);
});
test('filters by tag', () {
final found = filterShops(
shops: shops,
productNames: names,
tag: 'weekly',
);
expect(found.single.id, 1);
});
});
}
+35 -1
View File
@@ -4,6 +4,7 @@ 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';
void main() {
testWidgets('scan screen shows capture actions and bottom navigation', (
@@ -22,10 +23,41 @@ void main() {
);
expect(find.text('Products'), findsOneWidget);
expect(find.text('Lists'), findsOneWidget);
expect(find.text('Add'), 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());
await tester.tap(find.text('Shop log'));
await tester.pump();
expect(find.byTooltip('Search products'), findsOneWidget);
});
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());
await tester.tap(find.text('Enter manually'));
@@ -60,6 +92,8 @@ void main() {
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();