import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'dart:io'; import 'package:flutter/services.dart'; import 'package:file_selector/file_selector.dart' as fs; import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart'; import 'package:pdf_signature/ui/features/pdf/widgets/pages_sidebar.dart'; import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:pdf_signature/data/repositories/preferences_repository.dart'; import 'package:pdf_signature/data/repositories/document_repository.dart'; import 'package:pdf_signature/l10n/app_localizations.dart'; /// It has known that sample-local-pdf.pdf has 3 pages. void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('PDF View: programmatic page jumps reach last page', ( tester, ) async { final pdfBytes = await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); await tester.pumpWidget( ProviderScope( overrides: [ preferencesRepositoryProvider.overrideWith( (ref) => PreferencesStateNotifier(prefs), ), documentRepositoryProvider.overrideWith( (ref) => DocumentStateNotifier() ..openPicked(pageCount: 3, bytes: pdfBytes), ), pdfViewModelProvider.overrideWith( (ref) => PdfViewModel(ref, useMockViewer: false), ), ], child: MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, locale: Locale('en'), home: PdfSignatureHomePage( onPickPdf: () async {}, onClosePdf: () {}, currentFile: fs.XFile('test.pdf'), ), ), ), ); await tester.pumpAndSettle(); // Extra settle to avoid startup race when running with other integration tests. await tester.pump(const Duration(milliseconds: 200)); final ctx = tester.element(find.byType(PdfSignatureHomePage)); final container = ProviderScope.containerOf(ctx); final vm = container.read(pdfViewModelProvider); expect(vm.currentPage, 1); container.read(pdfViewModelProvider.notifier).jumpToPage(2); await tester.pumpAndSettle(); await tester.pump(const Duration(milliseconds: 120)); expect(container.read(pdfViewModelProvider).currentPage, 2); container.read(pdfViewModelProvider.notifier).jumpToPage(3); await tester.pumpAndSettle(); await tester.pump(const Duration(milliseconds: 120)); expect(container.read(pdfViewModelProvider).currentPage, 3); }); testWidgets('PDF View: zoom in/out', (tester) async { final pdfBytes = await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); await tester.pumpWidget( ProviderScope( overrides: [ preferencesRepositoryProvider.overrideWith( (ref) => PreferencesStateNotifier(prefs), ), documentRepositoryProvider.overrideWith( (ref) => DocumentStateNotifier() ..openPicked(pageCount: 3, bytes: pdfBytes), ), pdfViewModelProvider.overrideWith( (ref) => PdfViewModel(ref, useMockViewer: false), ), ], child: MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, locale: Locale('en'), home: PdfSignatureHomePage( onPickPdf: () async {}, onClosePdf: () {}, currentFile: fs.XFile('test.pdf'), ), ), ), ); await tester.pumpAndSettle(); await tester.pump(const Duration(milliseconds: 120)); final pdfViewer = find.byKey(const ValueKey('pdf_page_area')); expect(pdfViewer, findsOneWidget); final center = tester.getCenter(pdfViewer); final gesture1 = await tester.createGesture(); final gesture2 = await tester.createGesture(); await gesture1.down(center - const Offset(10, 0)); await gesture2.down(center + const Offset(10, 0)); await gesture1.moveTo(center - const Offset(20, 0)); await gesture2.moveTo(center + const Offset(20, 0)); await gesture1.up(); await gesture2.up(); await tester.pumpAndSettle(); expect(pdfViewer, findsOneWidget); }); testWidgets('PDF View: jump to page by clicking thumbnail', (tester) async { final pdfBytes = await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); await tester.pumpWidget( ProviderScope( overrides: [ preferencesRepositoryProvider.overrideWith( (ref) => PreferencesStateNotifier(prefs), ), documentRepositoryProvider.overrideWith( (ref) => DocumentStateNotifier() ..openPicked(pageCount: 3, bytes: pdfBytes), ), pdfViewModelProvider.overrideWith( (ref) => PdfViewModel(ref, useMockViewer: false), ), ], child: MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, locale: Locale('en'), home: PdfSignatureHomePage( onPickPdf: () async {}, onClosePdf: () {}, currentFile: fs.XFile('test.pdf'), ), ), ), ); await tester.pumpAndSettle(); final ctx = tester.element(find.byType(PdfSignatureHomePage)); final container = ProviderScope.containerOf(ctx); expect(container.read(pdfViewModelProvider).currentPage, 1); final pagesSidebar = find.byType(PagesSidebar); expect(pagesSidebar, findsOneWidget); // Helper to read the background color of a thumbnail tile by page label Color? tileBgForPage(int page) { final pageLabel = find.descendant( of: pagesSidebar, matching: find.text('$page'), ); if (pageLabel.evaluate().isEmpty) return null; // not visible yet final decoratedAncestors = find.ancestor( of: pageLabel, matching: find.byType(DecoratedBox), ); final decoratedBoxes = decoratedAncestors .evaluate() .map((e) => e.widget) .whereType() .toList(); for (final d in decoratedBoxes) { final dec = d.decoration; if (dec is BoxDecoration && dec.color != null) { return dec.color; } } return null; } final theme = Theme.of(tester.element(pagesSidebar)); // Initially, page 1 should be highlighted expect(tileBgForPage(1), theme.colorScheme.primaryContainer); // Scroll to make page 3 thumbnail visible await tester.drag(pagesSidebar, const Offset(0, -300)); await tester.pumpAndSettle(); final page3Thumbnail = find.text('3'); expect(page3Thumbnail, findsOneWidget); await tester.tap(page3Thumbnail); await tester.pumpAndSettle(); expect(container.read(pdfViewModelProvider).currentPage, 3); // After navigation completes, page 3 should be highlighted expect(tileBgForPage(3), theme.colorScheme.primaryContainer); }); testWidgets('PDF View: thumbnails scroll and select', (tester) async { final pdfBytes = await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); await tester.pumpWidget( ProviderScope( overrides: [ preferencesRepositoryProvider.overrideWith( (ref) => PreferencesStateNotifier(prefs), ), documentRepositoryProvider.overrideWith( (ref) => DocumentStateNotifier() ..openPicked(pageCount: 3, bytes: pdfBytes), ), pdfViewModelProvider.overrideWith( (ref) => PdfViewModel(ref, useMockViewer: false), ), ], child: MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, locale: Locale('en'), home: PdfSignatureHomePage( onPickPdf: () async {}, onClosePdf: () {}, currentFile: fs.XFile('test.pdf'), ), ), ), ); await tester.pumpAndSettle(); final ctx = tester.element(find.byType(PdfSignatureHomePage)); final container = ProviderScope.containerOf(ctx); expect(container.read(pdfViewModelProvider).currentPage, 1); final pagesSidebar = find.byType(PagesSidebar); expect(pagesSidebar, findsOneWidget); await tester.drag(pagesSidebar, const Offset(0, -200)); await tester.pumpAndSettle(); // Page number '1' may appear in multiple text widgets (e.g., overlay/toolbar); restrict to sidebar. final page1InSidebar = find.descendant( of: pagesSidebar, matching: find.text('1'), ); expect(page1InSidebar, findsOneWidget); expect(container.read(pdfViewModelProvider).currentPage, 1); // Select page 2 thumbnail and verify page changes final page2InSidebar = find.descendant( of: pagesSidebar, matching: find.text('2'), ); await tester.tap(page2InSidebar); await tester.pumpAndSettle(); expect(container.read(pdfViewModelProvider).currentPage, 2); }); testWidgets('PDF View: scroll thumb to reveal and select last page', ( tester, ) async { final pdfBytes = await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); await tester.pumpWidget( ProviderScope( overrides: [ preferencesRepositoryProvider.overrideWith( (ref) => PreferencesStateNotifier(prefs), ), documentRepositoryProvider.overrideWith( (ref) => DocumentStateNotifier() ..openPicked(pageCount: 3, bytes: pdfBytes), ), pdfViewModelProvider.overrideWith( (ref) => PdfViewModel(ref, useMockViewer: false), ), ], child: MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, locale: const Locale('en'), home: PdfSignatureHomePage( onPickPdf: () async {}, onClosePdf: () {}, currentFile: fs.XFile('test.pdf'), ), ), ), ); await tester.pumpAndSettle(); final ctx = tester.element(find.byType(PdfSignatureHomePage)); final container = ProviderScope.containerOf(ctx); expect(container.read(pdfViewModelProvider).currentPage, 1); final pagesSidebar = find.byType(PagesSidebar); expect(pagesSidebar, findsOneWidget); // Ensure page 3 not initially in view by trying to find it and allowing that it might be offstage. // Perform a scroll/drag to bring page 3 into view. await tester.drag(pagesSidebar, const Offset(0, -400)); await tester.pumpAndSettle(); final page3 = find.descendant(of: pagesSidebar, matching: find.text('3')); expect(page3, findsOneWidget); await tester.tap(page3); await tester.pumpAndSettle(); expect(container.read(pdfViewModelProvider).currentPage, 3); // Scroll back upward and verify selection persists. await tester.drag(pagesSidebar, const Offset(0, 300)); await tester.pumpAndSettle(); expect(container.read(pdfViewModelProvider).currentPage, 3); }); }