import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:tane/services/file_service.dart'; import 'package:tane/services/label_sheet_service.dart'; /// Captures the bytes handed to the save dialog instead of touching disk. class _RecordingFileService implements FileService { Uint8List? saved; String? savedName; @override Future saveFile({ required String suggestedName, required Uint8List bytes, }) async { saved = bytes; savedName = suggestedName; return '/dev/null/$suggestedName'; } @override Future pickFileBytes({List? allowedExtensions}) async => null; } const _labels = [ LabelSheetLabel( varietyLabel: 'Acelga de Perales', commonName: 'Acelga', scientificName: 'Beta vulgaris', details: '2006 · Perales', qrData: 'tane://seed?v=Acelga+de+Perales&y=2006', ), LabelSheetLabel( varietyLabel: 'Alubia de Tolosa', qrData: 'tane://seed?v=Alubia+de+Tolosa', ), ]; void main() { TestWidgetsFlutterBinding.ensureInitialized(); for (final format in LabelSheetFormat.values) { test('buildPdf renders a well-formed PDF for $format', () async { final service = LabelSheetService(files: _RecordingFileService()); final bytes = await service.buildPdf(labels: _labels, format: format); // %PDF header + non-trivial body; text content is compressed, so the // human-readable assertions live in the widget/data tests. expect(String.fromCharCodes(bytes.take(5)), '%PDF-'); expect(bytes.length, greaterThan(1000)); }); } test('buildPdf renders a name-only label (no optional fields)', () async { final service = LabelSheetService(files: _RecordingFileService()); final bytes = await service.buildPdf( labels: const [ LabelSheetLabel(varietyLabel: 'Maíz', qrData: 'tane://seed?v=Maíz'), ], format: LabelSheetFormat.stickers, ); expect(String.fromCharCodes(bytes.take(5)), '%PDF-'); }); test('buildPdf honours an RTL text direction', () async { final service = LabelSheetService(files: _RecordingFileService()); final bytes = await service.buildPdf( labels: const [ LabelSheetLabel(varietyLabel: 'بامية', qrData: 'tane://seed?v=x'), ], format: LabelSheetFormat.cards, rtl: true, ); expect(String.fromCharCodes(bytes.take(5)), '%PDF-'); }); test('buildPdf embeds CJK and Arabic glyphs (no tofu)', () async { // Japanese (CJK) and Arabic (RTL) fall outside DejaVu's coverage; the Noto // fallbacks in the PDF theme must render them without throwing. Guards // against a regression that drops the fallback fonts. final service = LabelSheetService(files: _RecordingFileService()); final bytes = await service.buildPdf( labels: const [ LabelSheetLabel( varietyLabel: '日本の在来種のトマト', commonName: 'トマト', scientificName: 'Solanum lycopersicum', qrData: 'tane://seed?v=tomato', ), LabelSheetLabel(varietyLabel: 'طماطم بلدية', qrData: 'tane://seed?v=x'), ], format: LabelSheetFormat.cards, ); expect(String.fromCharCodes(bytes.take(5)), '%PDF-'); expect(bytes.length, greaterThan(1000)); }); test('saveLabels hands the PDF to the save dialog', () async { final files = _RecordingFileService(); final service = LabelSheetService(files: files); final saved = await service.saveLabels( labels: _labels, format: LabelSheetFormat.cards, suggestedName: 'tane-labels-2026-07-10.pdf', ); expect(saved, isTrue); expect(files.savedName, 'tane-labels-2026-07-10.pdf'); expect(String.fromCharCodes(files.saved!.take(5)), '%PDF-'); }); }