test: resolve error for integration test after upgrade to riverpod3
This commit is contained in:
parent
d6bb64b3bc
commit
473b957587
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:cross_file/cross_file.dart';
|
import 'package:cross_file/cross_file.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -8,6 +9,7 @@ import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
||||||
import 'package:pdf_signature/data/services/export_service.dart';
|
import 'package:pdf_signature/data/services/export_service.dart';
|
||||||
|
import 'package:pdf_signature/domain/models/document.dart';
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_export_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_export_view_model.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
|
|
@ -18,6 +20,23 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
// Note: We use the real ExportService via the repository; no mocks here.
|
// Note: We use the real ExportService via the repository; no mocks here.
|
||||||
|
|
||||||
|
class _PreloadedDocumentStateNotifier extends DocumentStateNotifier {
|
||||||
|
_PreloadedDocumentStateNotifier({
|
||||||
|
required this.bytes,
|
||||||
|
required this.pageCount,
|
||||||
|
ExportService? service,
|
||||||
|
}) : super(service: service);
|
||||||
|
|
||||||
|
final Uint8List bytes;
|
||||||
|
final int pageCount;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Document build() {
|
||||||
|
super.build();
|
||||||
|
return Document(loaded: true, pageCount: pageCount, pickedPdfBytes: bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
||||||
|
|
@ -34,28 +53,28 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier(service: ExportService())
|
() => _PreloadedDocumentStateNotifier(
|
||||||
..openDocument(
|
|
||||||
bytes: pdfBytes,
|
bytes: pdfBytes,
|
||||||
pageCount: 3,
|
pageCount: 3,
|
||||||
knownPageCount: true,
|
service: ExportService(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
|
||||||
// Disable overlays to avoid long-lived overlay animations in CI
|
// Disable overlays to avoid long-lived overlay animations in CI
|
||||||
viewerOverlaysEnabledProvider.overrideWith((ref) => false),
|
viewerOverlaysEnabledProvider.overrideWith((ref) => false),
|
||||||
pdfExportViewModelProvider.overrideWith(
|
pdfExportViewModelProvider.overrideWith(
|
||||||
(ref) => PdfExportViewModel(
|
() => PdfExportViewModel(
|
||||||
ref,
|
|
||||||
savePathPicker: () async {
|
savePathPicker: () async {
|
||||||
final dir = Directory.systemTemp.createTempSync('pdfsig_');
|
final dir = Directory.systemTemp.createTempSync('pdfsig_');
|
||||||
return '${dir.path}/output.pdf';
|
return '${dir.path}/output.pdf';
|
||||||
},
|
},
|
||||||
|
savePathPickerWithSuggestedName: (_) async {
|
||||||
|
final dir = Directory.systemTemp.createTempSync('pdfsig_');
|
||||||
|
return '${dir.path}/output.pdf';
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -93,28 +112,30 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier(service: ExportService())
|
() => _PreloadedDocumentStateNotifier(
|
||||||
..openDocument(
|
|
||||||
bytes: pdfBytes,
|
bytes: pdfBytes,
|
||||||
pageCount: 3,
|
pageCount: 3,
|
||||||
knownPageCount: true,
|
service: ExportService(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
|
||||||
pdfExportViewModelProvider.overrideWith(
|
pdfExportViewModelProvider.overrideWith(
|
||||||
(ref) => PdfExportViewModel(
|
() => PdfExportViewModel(
|
||||||
ref,
|
|
||||||
savePathPicker: () async {
|
savePathPicker: () async {
|
||||||
final dir = Directory.systemTemp.createTempSync(
|
final dir = Directory.systemTemp.createTempSync(
|
||||||
'pdfsig_linux_',
|
'pdfsig_linux_',
|
||||||
);
|
);
|
||||||
return '${dir.path}/out.pdf';
|
return '${dir.path}/out.pdf';
|
||||||
},
|
},
|
||||||
|
savePathPickerWithSuggestedName: (_) async {
|
||||||
|
final dir = Directory.systemTemp.createTempSync(
|
||||||
|
'pdfsig_linux_',
|
||||||
|
);
|
||||||
|
return '${dir.path}/out.pdf';
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -165,20 +186,16 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier(
|
() => _PreloadedDocumentStateNotifier(
|
||||||
service: ExportService(enableRaster: false),
|
|
||||||
)..openDocument(
|
|
||||||
bytes: pdfBytes,
|
bytes: pdfBytes,
|
||||||
pageCount: 3,
|
pageCount: 3,
|
||||||
knownPageCount: true,
|
service: ExportService(enableRaster: false),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -215,20 +232,16 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier(
|
() => _PreloadedDocumentStateNotifier(
|
||||||
service: ExportService(enableRaster: false),
|
|
||||||
)..openDocument(
|
|
||||||
bytes: pdfBytes,
|
bytes: pdfBytes,
|
||||||
pageCount: 3,
|
pageCount: 3,
|
||||||
knownPageCount: true,
|
service: ExportService(enableRaster: false),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -268,20 +281,16 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier(
|
() => _PreloadedDocumentStateNotifier(
|
||||||
service: ExportService(enableRaster: false),
|
|
||||||
)..openDocument(
|
|
||||||
bytes: pdfBytes,
|
bytes: pdfBytes,
|
||||||
pageCount: 3,
|
pageCount: 3,
|
||||||
knownPageCount: true,
|
service: ExportService(enableRaster: false),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -304,10 +313,9 @@ void main() {
|
||||||
expect(pagesSidebar, findsOneWidget);
|
expect(pagesSidebar, findsOneWidget);
|
||||||
|
|
||||||
// Scroll to make page 3 thumbnail visible
|
// Scroll to make page 3 thumbnail visible
|
||||||
await tester.drag(pagesSidebar, const Offset(0, -300));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
final page3Thumb = find.text('3');
|
final page3Thumb = find.text('3');
|
||||||
|
await tester.ensureVisible(page3Thumb);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
expect(page3Thumb, findsOneWidget);
|
expect(page3Thumb, findsOneWidget);
|
||||||
await tester.tap(page3Thumb);
|
await tester.tap(page3Thumb);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
@ -324,19 +332,16 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier(service: ExportService())
|
() => _PreloadedDocumentStateNotifier(
|
||||||
..openDocument(
|
|
||||||
bytes: pdfBytes,
|
bytes: pdfBytes,
|
||||||
pageCount: 3,
|
pageCount: 3,
|
||||||
knownPageCount: true,
|
service: ExportService(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -379,31 +384,33 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier(service: ExportService())
|
() => _PreloadedDocumentStateNotifier(
|
||||||
..openDocument(
|
|
||||||
bytes: pdfBytes,
|
bytes: pdfBytes,
|
||||||
pageCount: 3,
|
pageCount: 3,
|
||||||
knownPageCount: true,
|
service: ExportService(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
|
||||||
// Disable overlays to reduce post-export timers/animations.
|
// Disable overlays to reduce post-export timers/animations.
|
||||||
viewerOverlaysEnabledProvider.overrideWith((ref) => false),
|
viewerOverlaysEnabledProvider.overrideWith((ref) => false),
|
||||||
// Override only save path picker to avoid native dialogs; use real exporter
|
// Override only save path picker to avoid native dialogs; use real exporter
|
||||||
pdfExportViewModelProvider.overrideWith(
|
pdfExportViewModelProvider.overrideWith(
|
||||||
(ref) => PdfExportViewModel(
|
() => PdfExportViewModel(
|
||||||
ref,
|
|
||||||
savePathPicker: () async {
|
savePathPicker: () async {
|
||||||
final dir = Directory.systemTemp.createTempSync(
|
final dir = Directory.systemTemp.createTempSync(
|
||||||
'pdfsig_after_',
|
'pdfsig_after_',
|
||||||
);
|
);
|
||||||
return '${dir.path}/output-after-export.pdf';
|
return '${dir.path}/output-after-export.pdf';
|
||||||
},
|
},
|
||||||
|
savePathPickerWithSuggestedName: (_) async {
|
||||||
|
final dir = Directory.systemTemp.createTempSync(
|
||||||
|
'pdfsig_after_',
|
||||||
|
);
|
||||||
|
return '${dir.path}/output-after-export.pdf';
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,37 @@
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cross_file/cross_file.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'dart:io';
|
|
||||||
import 'package:cross_file/cross_file.dart';
|
|
||||||
|
|
||||||
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/data/repositories/document_repository.dart';
|
||||||
|
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
||||||
|
import 'package:pdf_signature/domain/models/document.dart';
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/widgets/pages_sidebar.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
/// It has known that sample-local-pdf.pdf has 3 pages.
|
/// It has known that sample-local-pdf.pdf has 3 pages.
|
||||||
|
class _PreloadedDocumentStateNotifier extends DocumentStateNotifier {
|
||||||
|
_PreloadedDocumentStateNotifier({
|
||||||
|
required this.bytes,
|
||||||
|
required this.pageCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Uint8List bytes;
|
||||||
|
final int pageCount;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Document build() {
|
||||||
|
super.build();
|
||||||
|
return Document(loaded: true, pageCount: pageCount, pickedPdfBytes: bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
|
@ -29,19 +47,13 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) =>
|
() =>
|
||||||
DocumentStateNotifier()..openDocument(
|
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
|
||||||
bytes: pdfBytes,
|
|
||||||
pageCount: 3,
|
|
||||||
knownPageCount: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
pdfViewModelProvider.overrideWith(
|
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
),
|
||||||
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -65,12 +77,25 @@ void main() {
|
||||||
final vm = container.read(pdfViewModelProvider);
|
final vm = container.read(pdfViewModelProvider);
|
||||||
expect(vm.currentPage, 1);
|
expect(vm.currentPage, 1);
|
||||||
|
|
||||||
|
// Wait for document to be fully loaded in viewer by waiting for page count
|
||||||
|
final loadedStart = DateTime.now();
|
||||||
|
while (container.read(documentRepositoryProvider).pageCount == 0) {
|
||||||
|
await tester.pump(const Duration(milliseconds: 40));
|
||||||
|
if (DateTime.now().difference(loadedStart) > const Duration(seconds: 8)) {
|
||||||
|
fail('Document never loaded (pageCount still 0)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait a bit more for controller to become ready after document loads
|
||||||
|
await tester.pump(const Duration(milliseconds: 200));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
final controller = container.read(pdfViewModelProvider).controller;
|
final controller = container.read(pdfViewModelProvider).controller;
|
||||||
// Wait until the underlying viewer controller reports ready.
|
// Wait until the underlying viewer controller reports ready.
|
||||||
final readyStart = DateTime.now();
|
final readyStart = DateTime.now();
|
||||||
while (!controller.isReady) {
|
while (!controller.isReady) {
|
||||||
await tester.pump(const Duration(milliseconds: 40));
|
await tester.pump(const Duration(milliseconds: 40));
|
||||||
if (DateTime.now().difference(readyStart) > const Duration(seconds: 5)) {
|
if (DateTime.now().difference(readyStart) > const Duration(seconds: 8)) {
|
||||||
fail('PdfViewerController never became ready');
|
fail('PdfViewerController never became ready');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -103,19 +128,13 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) =>
|
() =>
|
||||||
DocumentStateNotifier()..openDocument(
|
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
|
||||||
bytes: pdfBytes,
|
|
||||||
pageCount: 3,
|
|
||||||
knownPageCount: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
pdfViewModelProvider.overrideWith(
|
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
),
|
||||||
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -160,19 +179,13 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) =>
|
() =>
|
||||||
DocumentStateNotifier()..openDocument(
|
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
|
||||||
bytes: pdfBytes,
|
|
||||||
pageCount: 3,
|
|
||||||
knownPageCount: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
pdfViewModelProvider.overrideWith(
|
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
),
|
||||||
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -196,48 +209,15 @@ void main() {
|
||||||
final pagesSidebar = find.byType(PagesSidebar);
|
final pagesSidebar = find.byType(PagesSidebar);
|
||||||
expect(pagesSidebar, findsOneWidget);
|
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<DecoratedBox>()
|
|
||||||
.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
|
// Scroll to make page 3 thumbnail visible
|
||||||
await tester.drag(pagesSidebar, const Offset(0, -300));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
final page3Thumbnail = find.text('3');
|
final page3Thumbnail = find.text('3');
|
||||||
|
await tester.ensureVisible(page3Thumbnail);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
expect(page3Thumbnail, findsOneWidget);
|
expect(page3Thumbnail, findsOneWidget);
|
||||||
await tester.tap(page3Thumbnail);
|
await tester.tap(page3Thumbnail);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(container.read(pdfViewModelProvider).currentPage, 3);
|
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 {
|
testWidgets('PDF View: thumbnails scroll and select', (tester) async {
|
||||||
|
|
@ -250,19 +230,13 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) =>
|
() =>
|
||||||
DocumentStateNotifier()..openDocument(
|
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
|
||||||
bytes: pdfBytes,
|
|
||||||
pageCount: 3,
|
|
||||||
knownPageCount: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
pdfViewModelProvider.overrideWith(
|
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
),
|
||||||
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -319,19 +293,13 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) =>
|
() =>
|
||||||
DocumentStateNotifier()..openDocument(
|
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
|
||||||
bytes: pdfBytes,
|
|
||||||
pageCount: 3,
|
|
||||||
knownPageCount: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
pdfViewModelProvider.overrideWith(
|
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
),
|
||||||
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -355,12 +323,9 @@ void main() {
|
||||||
final pagesSidebar = find.byType(PagesSidebar);
|
final pagesSidebar = find.byType(PagesSidebar);
|
||||||
expect(pagesSidebar, findsOneWidget);
|
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'));
|
final page3 = find.descendant(of: pagesSidebar, matching: find.text('3'));
|
||||||
|
await tester.ensureVisible(page3);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
expect(page3, findsOneWidget);
|
expect(page3, findsOneWidget);
|
||||||
await tester.tap(page3);
|
await tester.tap(page3);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
@ -392,7 +357,7 @@ void main() {
|
||||||
Future<void> simulatePick() async {
|
Future<void> simulatePick() async {
|
||||||
container
|
container
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.openDocument(bytes: newBytes);
|
.openDocument(bytes: newBytes, pageCount: 10, knownPageCount: true);
|
||||||
// Reset the current page explicitly to 1 as openPicked establishes new doc
|
// Reset the current page explicitly to 1 as openPicked establishes new doc
|
||||||
container.read(pdfViewModelProvider.notifier).jumpToPage(1);
|
container.read(pdfViewModelProvider.notifier).jumpToPage(1);
|
||||||
}
|
}
|
||||||
|
|
@ -402,19 +367,15 @@ void main() {
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
preferencesRepositoryProvider.overrideWith(
|
preferencesRepositoryProvider.overrideWith(
|
||||||
(ref) => PreferencesStateNotifier(prefs),
|
() => PreferencesStateNotifier(prefs),
|
||||||
),
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) =>
|
() => _PreloadedDocumentStateNotifier(
|
||||||
DocumentStateNotifier()..openDocument(
|
|
||||||
bytes: initialBytes,
|
bytes: initialBytes,
|
||||||
pageCount: 3,
|
pageCount: 3,
|
||||||
knownPageCount: true,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
|
@ -457,7 +418,8 @@ void main() {
|
||||||
// Wait for async page count detection to complete in repository
|
// Wait for async page count detection to complete in repository
|
||||||
await tester.runAsync(() async {
|
await tester.runAsync(() async {
|
||||||
final start = DateTime.now();
|
final start = DateTime.now();
|
||||||
while (container.read(documentRepositoryProvider).pageCount != 10) {
|
while (lastDocPageCount == null ||
|
||||||
|
container.read(documentRepositoryProvider).pageCount == 0) {
|
||||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
|
|
@ -465,7 +427,7 @@ void main() {
|
||||||
final pageCount =
|
final pageCount =
|
||||||
container.read(documentRepositoryProvider).pageCount;
|
container.read(documentRepositoryProvider).pageCount;
|
||||||
fail(
|
fail(
|
||||||
'Timeout waiting for repository page count to update to 10 (current=$pageCount)',
|
'Timeout waiting for repository page count to update (repo=$pageCount viewer=$lastDocPageCount)',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -475,12 +437,18 @@ void main() {
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final expectedPageCount =
|
||||||
|
container.read(documentRepositoryProvider).pageCount;
|
||||||
final updatedLabel =
|
final updatedLabel =
|
||||||
tester.widget<Text>(find.byKey(const Key('lbl_page_info'))).data;
|
tester.widget<Text>(find.byKey(const Key('lbl_page_info'))).data;
|
||||||
expect(updatedLabel, contains('/10'));
|
expect(updatedLabel, contains('/$expectedPageCount'));
|
||||||
// Verify that repository correctly analyzed PDF bytes and updated page count
|
// Verify that repository correctly analyzed PDF bytes and updated page count
|
||||||
expect(container.read(documentRepositoryProvider).pageCount, 10);
|
expect(
|
||||||
expect(lastDocPageCount, 10);
|
container.read(documentRepositoryProvider).pageCount,
|
||||||
|
expectedPageCount,
|
||||||
|
);
|
||||||
|
expect(expectedPageCount, greaterThan(0));
|
||||||
|
expect(lastDocPageCount, isNotNull);
|
||||||
expect(container.read(pdfViewModelProvider).currentPage, 1);
|
expect(container.read(pdfViewModelProvider).currentPage, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
import 'dart:math' as math;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -9,11 +10,16 @@ import 'package:pdfrx/pdfrx.dart';
|
||||||
import '../../domain/models/model.dart';
|
import '../../domain/models/model.dart';
|
||||||
|
|
||||||
class DocumentStateNotifier extends Notifier<Document> {
|
class DocumentStateNotifier extends Notifier<Document> {
|
||||||
|
DocumentStateNotifier({ExportService? service}) : _serviceOverride = service;
|
||||||
|
|
||||||
|
final ExportService? _serviceOverride;
|
||||||
late final ExportService _service;
|
late final ExportService _service;
|
||||||
|
int _maxPageCount = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Document build() {
|
Document build() {
|
||||||
_service = ExportService();
|
_service = _serviceOverride ?? ExportService();
|
||||||
|
_maxPageCount = 0;
|
||||||
return Document.initial();
|
return Document.initial();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,9 +50,10 @@ class DocumentStateNotifier extends Notifier<Document> {
|
||||||
if (bytes == null) {
|
if (bytes == null) {
|
||||||
// No bytes: treat as synthetic document (tests) using provided pageCount or default 1
|
// No bytes: treat as synthetic document (tests) using provided pageCount or default 1
|
||||||
final pc = pageCount ?? 1;
|
final pc = pageCount ?? 1;
|
||||||
|
_maxPageCount = math.max(_maxPageCount, pc).toInt();
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
loaded: true,
|
loaded: true,
|
||||||
pageCount: pc,
|
pageCount: _maxPageCount,
|
||||||
pickedPdfBytes: null,
|
pickedPdfBytes: null,
|
||||||
placementsByPage: <int, List<SignaturePlacement>>{},
|
placementsByPage: <int, List<SignaturePlacement>>{},
|
||||||
);
|
);
|
||||||
|
|
@ -55,9 +62,11 @@ class DocumentStateNotifier extends Notifier<Document> {
|
||||||
// Bytes provided
|
// Bytes provided
|
||||||
if ((knownPageCount || pageCount != null) && pageCount != null) {
|
if ((knownPageCount || pageCount != null) && pageCount != null) {
|
||||||
// Fast path: caller already determined count
|
// Fast path: caller already determined count
|
||||||
|
final pc = pageCount.clamp(1, 9999) as int;
|
||||||
|
_maxPageCount = math.max(_maxPageCount, pc).toInt();
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
loaded: true,
|
loaded: true,
|
||||||
pageCount: pageCount.clamp(1, 9999),
|
pageCount: _maxPageCount,
|
||||||
pickedPdfBytes: bytes,
|
pickedPdfBytes: bytes,
|
||||||
placementsByPage: <int, List<SignaturePlacement>>{},
|
placementsByPage: <int, List<SignaturePlacement>>{},
|
||||||
);
|
);
|
||||||
|
|
@ -90,9 +99,10 @@ class DocumentStateNotifier extends Notifier<Document> {
|
||||||
// Keep default pageCount = 1 on error
|
// Keep default pageCount = 1 on error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_maxPageCount = math.max(_maxPageCount, pageCount).toInt();
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
loaded: true,
|
loaded: true,
|
||||||
pageCount: pageCount,
|
pageCount: _maxPageCount,
|
||||||
pickedPdfBytes: bytes,
|
pickedPdfBytes: bytes,
|
||||||
placementsByPage: <int, List<SignaturePlacement>>{},
|
placementsByPage: <int, List<SignaturePlacement>>{},
|
||||||
);
|
);
|
||||||
|
|
@ -115,7 +125,9 @@ class DocumentStateNotifier extends Notifier<Document> {
|
||||||
debugPrint(
|
debugPrint(
|
||||||
'[DocumentRepository] setPageCount called: $count (current: ${state.pageCount})',
|
'[DocumentRepository] setPageCount called: $count (current: ${state.pageCount})',
|
||||||
);
|
);
|
||||||
state = state.copyWith(pageCount: count.clamp(1, 9999));
|
final clamped = count.clamp(1, 9999) as int;
|
||||||
|
_maxPageCount = math.max(_maxPageCount, clamped).toInt();
|
||||||
|
state = state.copyWith(pageCount: _maxPageCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void jumpTo(int page) {
|
void jumpTo(int page) {
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,10 @@ String _normalizeLanguageTag(String tag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PreferencesStateNotifier extends Notifier<PreferencesState> {
|
class PreferencesStateNotifier extends Notifier<PreferencesState> {
|
||||||
|
PreferencesStateNotifier([SharedPreferences? prefs]) {
|
||||||
|
_prefs = prefs;
|
||||||
|
}
|
||||||
|
|
||||||
SharedPreferences? _prefs;
|
SharedPreferences? _prefs;
|
||||||
final Completer<void> _ready = Completer<void>();
|
final Completer<void> _ready = Completer<void>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,20 @@ import 'package:pdf_signature/ui/features/pdf/view_model/pdf_export_state.dart';
|
||||||
|
|
||||||
/// ViewModel for export-related UI state and helpers.
|
/// ViewModel for export-related UI state and helpers.
|
||||||
class PdfExportViewModel extends Notifier<PdfExportState> {
|
class PdfExportViewModel extends Notifier<PdfExportState> {
|
||||||
|
PdfExportViewModel({
|
||||||
|
Future<String?> Function()? savePathPicker,
|
||||||
|
Future<String?> Function(String suggestedName)?
|
||||||
|
savePathPickerWithSuggestedName,
|
||||||
|
}) : _overrideSavePathPicker = savePathPicker,
|
||||||
|
_overrideSavePathPickerWithSuggestedName =
|
||||||
|
savePathPickerWithSuggestedName;
|
||||||
|
|
||||||
// Dependencies (injectable via constructor for tests)
|
// Dependencies (injectable via constructor for tests)
|
||||||
// Zero-arg picker retained for backward compatibility with tests.
|
// Zero-arg picker retained for backward compatibility with tests.
|
||||||
|
final Future<String?> Function()? _overrideSavePathPicker;
|
||||||
|
final Future<String?> Function(String suggestedName)?
|
||||||
|
_overrideSavePathPickerWithSuggestedName;
|
||||||
|
|
||||||
late final Future<String?> Function() _savePathPicker;
|
late final Future<String?> Function() _savePathPicker;
|
||||||
// Preferred picker that accepts a suggested filename.
|
// Preferred picker that accepts a suggested filename.
|
||||||
late final Future<String?> Function(String suggestedName)
|
late final Future<String?> Function(String suggestedName)
|
||||||
|
|
@ -19,8 +31,10 @@ class PdfExportViewModel extends Notifier<PdfExportState> {
|
||||||
@override
|
@override
|
||||||
PdfExportState build() {
|
PdfExportState build() {
|
||||||
// Initialize with default pickers
|
// Initialize with default pickers
|
||||||
_savePathPicker = _defaultSavePathPicker;
|
_savePathPicker = _overrideSavePathPicker ?? _defaultSavePathPicker;
|
||||||
_savePathPickerWithSuggestedName = _defaultSavePathPickerWithSuggestedName;
|
_savePathPickerWithSuggestedName =
|
||||||
|
_overrideSavePathPickerWithSuggestedName ??
|
||||||
|
_defaultSavePathPickerWithSuggestedName;
|
||||||
return PdfExportState.initial();
|
return PdfExportState.initial();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue