A whole bunch of changes
This commit is contained in:
parent
901ec678a3
commit
956231048e
122 changed files with 5442 additions and 914 deletions
158
test/fuel_report_pdf_test.dart
Normal file
158
test/fuel_report_pdf_test.dart
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fuel_tax_tracker/models/fuel_entry.dart';
|
||||
import 'package:fuel_tax_tracker/models/vehicle.dart';
|
||||
import 'package:fuel_tax_tracker/services/fuel_report.dart';
|
||||
import 'package:fuel_tax_tracker/services/fuel_report_pdf.dart';
|
||||
|
||||
/// PDF content isn't practically assertable byte-for-byte, so this is a
|
||||
/// smoke test: every include-images/include-line-items combination should
|
||||
/// render without throwing and produce a well-formed (non-trivial, valid
|
||||
/// header) document — the real coverage of what each section *contains*
|
||||
/// lives in fuel_report_test.dart (data) and is exercised by hand.
|
||||
void main() {
|
||||
final vehicle = Vehicle(
|
||||
id: 'v1',
|
||||
vin: '1FMPU18L1TLB51349',
|
||||
nickname: 'Truck',
|
||||
updatedAt: DateTime.utc(2026, 1, 1),
|
||||
);
|
||||
|
||||
final entryA = FuelEntry(
|
||||
id: 'e1',
|
||||
vehicleId: 'v1',
|
||||
date: DateTime(2026, 3, 5),
|
||||
gallons: 10,
|
||||
pricePerGallon: 3.5,
|
||||
totalCost: 35,
|
||||
updatedAt: DateTime.utc(2026, 3, 5),
|
||||
receiptImagePath: '/local/a.jpg',
|
||||
);
|
||||
final entryB = FuelEntry(
|
||||
id: 'e2',
|
||||
vehicleId: 'v1',
|
||||
date: DateTime(2026, 3, 12),
|
||||
gallons: 12,
|
||||
pricePerGallon: 3.6,
|
||||
totalCost: 43.2,
|
||||
updatedAt: DateTime.utc(2026, 3, 12),
|
||||
receiptDriveFileId: 'remote-1',
|
||||
);
|
||||
|
||||
final report = FuelReport(
|
||||
startDate: DateTime(2026, 3, 1),
|
||||
endDate: DateTime(2026, 3, 31),
|
||||
rows: [
|
||||
VehicleReportRow(
|
||||
vehicle: vehicle,
|
||||
entries: [entryA, entryB],
|
||||
totalGallons: 22,
|
||||
totalCost: 78.2,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// A 1x1 red pixel PNG — enough for the pdf package to embed as an image.
|
||||
final onePixelPng = Uint8List.fromList([
|
||||
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D,
|
||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
|
||||
0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE, 0x00, 0x00, 0x00,
|
||||
0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xCF, 0xC0, 0x00,
|
||||
0x00, 0x03, 0x01, 0x01, 0x00, 0x18, 0xDD, 0x8D, 0xB0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
|
||||
]);
|
||||
|
||||
Future<void> expectValidPdf(Uint8List bytes) async {
|
||||
expect(bytes.length, greaterThan(100));
|
||||
expect(String.fromCharCodes(bytes.take(5)), '%PDF-');
|
||||
}
|
||||
|
||||
test('summary only (both toggles off)', () async {
|
||||
final bytes = await buildFuelReportPdf(report);
|
||||
await expectValidPdf(bytes);
|
||||
});
|
||||
|
||||
test('with line items, no images', () async {
|
||||
final bytes = await buildFuelReportPdf(report, includeLineItems: true);
|
||||
await expectValidPdf(bytes);
|
||||
});
|
||||
|
||||
test('with images, no line items', () async {
|
||||
final bytes = await buildFuelReportPdf(
|
||||
report,
|
||||
includeReceiptImages: true,
|
||||
receiptImages: {'e1': onePixelPng, 'e2': onePixelPng},
|
||||
);
|
||||
await expectValidPdf(bytes);
|
||||
});
|
||||
|
||||
test('with both — line items and images each get their own section, cross-referenced by number',
|
||||
() async {
|
||||
final bytes = await buildFuelReportPdf(
|
||||
report,
|
||||
includeLineItems: true,
|
||||
includeReceiptImages: true,
|
||||
receiptImages: {'e1': onePixelPng, 'e2': onePixelPng},
|
||||
);
|
||||
await expectValidPdf(bytes);
|
||||
});
|
||||
|
||||
test('a report with enough entries to span multiple pages does not throw', () async {
|
||||
// Regression test: the page banner/watermark used to be drawn by
|
||||
// wrapping the *entire*, unbounded-height body in a Stack, which
|
||||
// doesn't paginate — MultiPage threw "Widget won't fit into the page"
|
||||
// for anything long enough to need a page break. Enough months/entries
|
||||
// here to force at least one break.
|
||||
final vehicle2 = Vehicle(id: 'v2', vin: '1GNWK5EG4DR302326', updatedAt: DateTime.utc(2026, 1, 1));
|
||||
final manyEntries = <FuelEntry>[];
|
||||
for (var month = 1; month <= 12; month++) {
|
||||
for (var day = 2; day <= 26; day += 6) {
|
||||
final id = 'm$month-d$day';
|
||||
manyEntries.add(FuelEntry(
|
||||
id: id,
|
||||
vehicleId: month.isEven ? 'v1' : 'v2',
|
||||
date: DateTime(2026, month, day),
|
||||
gallons: 20,
|
||||
pricePerGallon: 3.5,
|
||||
totalCost: 70,
|
||||
updatedAt: DateTime.utc(2026, month, day),
|
||||
receiptDriveFileId: 'remote-$id',
|
||||
));
|
||||
}
|
||||
}
|
||||
final bigReport = buildFuelReport(
|
||||
vehicles: [vehicle, vehicle2],
|
||||
fuelEntries: manyEntries,
|
||||
startDate: DateTime(2026, 1, 1),
|
||||
endDate: DateTime(2026, 12, 31),
|
||||
);
|
||||
|
||||
final bytes = await buildFuelReportPdf(
|
||||
bigReport,
|
||||
includeLineItems: true,
|
||||
includeReceiptImages: true,
|
||||
receiptImages: {for (final e in manyEntries) e.id: onePixelPng},
|
||||
);
|
||||
await expectValidPdf(bytes);
|
||||
});
|
||||
|
||||
test('receiptImages missing an entry does not crash — that photo is just skipped', () async {
|
||||
final bytes = await buildFuelReportPdf(
|
||||
report,
|
||||
includeReceiptImages: true,
|
||||
receiptImages: {'e1': onePixelPng}, // e2 deliberately missing
|
||||
);
|
||||
await expectValidPdf(bytes);
|
||||
});
|
||||
|
||||
test('empty report (no rows) with all sections on', () async {
|
||||
final empty = FuelReport(startDate: DateTime(2026, 1, 1), endDate: DateTime(2026, 1, 31), rows: []);
|
||||
final bytes = await buildFuelReportPdf(empty, includeLineItems: true, includeReceiptImages: true);
|
||||
await expectValidPdf(bytes);
|
||||
});
|
||||
|
||||
test('fuelReportFileName formats the date range', () {
|
||||
expect(fuelReportFileName(report), 'fuel-report-20260301-20260331.pdf');
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue