added categories, import export, settings
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:receipity/services/backup_format.dart';
|
||||
|
||||
void main() {
|
||||
test('round-trips a backup document', () {
|
||||
final original = const BackupDocument(
|
||||
version: 1,
|
||||
exportedAt: '2026-08-29T12:00:00.000',
|
||||
darkMode: true,
|
||||
supermarkets: [
|
||||
{'id': 1, 'name': 'Jumbo', 'color': 0xFFEEC21B, 'sort_order': 0},
|
||||
],
|
||||
products: [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'Milk',
|
||||
'normalized_name': 'MILK',
|
||||
'last_price': 1.5,
|
||||
'times_seen': 2,
|
||||
'favourite': 1,
|
||||
'image_path': 'products/product_1.jpg',
|
||||
'supermarket': 'Jumbo',
|
||||
'notes': 'Organic',
|
||||
'created_at': '2026-08-29T12:00:00.000',
|
||||
'updated_at': '2026-08-29T12:00:00.000',
|
||||
},
|
||||
],
|
||||
shops: [
|
||||
{
|
||||
'id': 8,
|
||||
'supermarket': 'Jumbo',
|
||||
'shopped_at': '2026-08-29T12:00:00.000',
|
||||
'receipt_image_path': 'receipts/shop_8.jpg',
|
||||
'total': 1.5,
|
||||
'created_at': '2026-08-29T12:00:00.000',
|
||||
},
|
||||
],
|
||||
shopItems: [
|
||||
{
|
||||
'id': 1,
|
||||
'shop_id': 8,
|
||||
'product_id': 1,
|
||||
'name': 'Milk',
|
||||
'price': 1.5,
|
||||
'quantity': 1,
|
||||
'unit_price': 1.5,
|
||||
},
|
||||
],
|
||||
images: {'products/product_1.jpg': 'abc123'},
|
||||
);
|
||||
|
||||
final parsed = BackupDocument.parse(original.encode());
|
||||
expect(parsed.darkMode, isTrue);
|
||||
expect(parsed.products.single['name'], 'Milk');
|
||||
expect(parsed.shops.single['id'], 8);
|
||||
expect(parsed.shopItems.single['product_id'], 1);
|
||||
expect(parsed.images['products/product_1.jpg'], 'abc123');
|
||||
});
|
||||
|
||||
test('rejects a file that is not a Receipity backup', () {
|
||||
expect(
|
||||
() => BackupDocument.parse('{"hello":"world"}'),
|
||||
throwsFormatException,
|
||||
);
|
||||
});
|
||||
|
||||
test('maps stored photo paths to backup keys', () {
|
||||
expect(
|
||||
backupImageKey('/data/app_flutter/products/product_12.jpg'),
|
||||
'products/product_12.jpg',
|
||||
);
|
||||
expect(
|
||||
backupImageKey('/data/app_flutter/receipts/shop_3.jpg'),
|
||||
'receipts/shop_3.jpg',
|
||||
);
|
||||
expect(backupImageKey(null), isNull);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:receipity/utils/fuzzy.dart';
|
||||
|
||||
void main() {
|
||||
test('matches substrings and character subsequences', () {
|
||||
expect(fuzzyMatch('melk', 'JUMBO BIO VOLLE MELK'), isTrue);
|
||||
expect(fuzzyMatch('jb melk', 'JUMBO BIO VOLLE MELK'), isTrue);
|
||||
expect(fuzzyMatch('aard', 'AARDBEIEN'), isTrue);
|
||||
expect(fuzzyMatch('xyz', 'AARDBEIEN'), isFalse);
|
||||
});
|
||||
|
||||
test('ranks closer matches higher', () {
|
||||
expect(fuzzyScore('melk', 'MELK')! > fuzzyScore('melk', 'JUMBO BIO VOLLE MELK')!, isTrue);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:receipity/utils/supermarkets.dart';
|
||||
|
||||
void main() {
|
||||
test('detects Jumbo and Lidl from receipt text', () {
|
||||
expect(detectSupermarket('Jumbo\nMelk 1,50'), 'Jumbo');
|
||||
expect(
|
||||
detectSupermarket('Dekamarkt\nMelk 1,50', names: ['Dekamarkt']),
|
||||
'Dekamarkt',
|
||||
);
|
||||
expect(detectSupermarket('LIDL SUPERMARKT\nBananas 1.20'), 'Lidl');
|
||||
});
|
||||
|
||||
test('detects Albert Heijn aliases', () {
|
||||
expect(detectSupermarket('Albert Heijn\nKaas 2,00'), 'Albert Heijn');
|
||||
expect(detectSupermarket('AH TO GO\nCoffee 1,80'), 'Albert Heijn');
|
||||
expect(detectSupermarket('AH\nMilk 1.00'), 'Albert Heijn');
|
||||
});
|
||||
|
||||
test('returns null when no supermarket is present', () {
|
||||
expect(detectSupermarket('Milk 1.50\nBread 2.00'), isNull);
|
||||
});
|
||||
}
|
||||
+25
-3
@@ -6,7 +6,9 @@ import 'package:receipity/review_screen.dart';
|
||||
import 'package:receipity/services/product_repository.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('scan screen shows capture actions', (tester) async {
|
||||
testWidgets('scan screen shows capture actions and bottom navigation', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(ReceipityApp());
|
||||
|
||||
expect(find.text('Receipity'), findsOneWidget);
|
||||
@@ -17,6 +19,9 @@ void main() {
|
||||
find.textContaining('Take a photo of a shopping receipt'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('Products'), findsOneWidget);
|
||||
expect(find.text('Scan'), findsOneWidget);
|
||||
expect(find.text('Shop log'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('review screen lists items and can delete one', (tester) async {
|
||||
@@ -28,7 +33,7 @@ void main() {
|
||||
LineItem(id: '2', name: 'Bread', price: 2),
|
||||
],
|
||||
rawText: 'Milk 1.50\nBread 2.00',
|
||||
repository: ProductRepository(),
|
||||
repository: ProductRepository(databasePath: ':memory:'),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -36,11 +41,28 @@ void main() {
|
||||
expect(find.text('Milk'), findsOneWidget);
|
||||
expect(find.text('Bread'), findsOneWidget);
|
||||
expect(find.text('Add item'), findsOneWidget);
|
||||
expect(find.text('Confirm'), findsOneWidget);
|
||||
expect(find.textContaining('Save trip'), findsOneWidget);
|
||||
expect(find.text('Supermarket'), 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: ProductRepository(databasePath: ':memory:'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('Jumbo'), findsWidgets);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user