feat: partially implement integration test

This commit is contained in:
insleker 2025-09-10 22:17:36 +08:00
parent f0a8e25890
commit 189bc7e6e6
5 changed files with 49 additions and 24 deletions

View File

@ -8,9 +8,12 @@ import 'package:image/image.dart' as img;
import 'package:pdf_signature/data/services/export_service.dart'; import 'package:pdf_signature/data/services/export_service.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'; import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
import 'package:pdf_signature/data/repositories/document_repository.dart'; import 'package:pdf_signature/data/repositories/document_repository.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart'; import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_providers.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
import 'package:pdf_signature/l10n/app_localizations.dart'; import 'package:pdf_signature/l10n/app_localizations.dart';
class RecordingExporter extends ExportService { class RecordingExporter extends ExportService {
@ -29,14 +32,18 @@ void main() {
tester, tester,
) async { ) async {
final fake = RecordingExporter(); final fake = RecordingExporter();
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
await tester.pumpWidget( await tester.pumpWidget(
ProviderScope( ProviderScope(
overrides: [ overrides: [
documentRepositoryProvider.overrideWith( preferencesRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier()..openPicked(path: 'test.pdf'), (ref) => PreferencesStateNotifier(prefs),
), ),
signatureProvider.overrideWith( documentRepositoryProvider.overrideWith(
(ref) => SignatureCardStateNotifier()..placeDefaultRect(), (ref) =>
DocumentStateNotifier()
..openPicked(path: 'test.pdf', pageCount: 5),
), ),
useMockViewerProvider.overrideWith((ref) => true), useMockViewerProvider.overrideWith((ref) => true),
exportServiceProvider.overrideWith((_) => fake), exportServiceProvider.overrideWith((_) => fake),
@ -82,11 +89,18 @@ void main() {
) async { ) async {
final sigBytes = _makeSig(); final sigBytes = _makeSig();
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
await tester.pumpWidget( await tester.pumpWidget(
ProviderScope( ProviderScope(
overrides: [ overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith( documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier()..openPicked(path: 'test.pdf'), (ref) =>
DocumentStateNotifier()
..openPicked(path: 'test.pdf', pageCount: 5),
), ),
signatureAssetRepositoryProvider.overrideWith((ref) { signatureAssetRepositoryProvider.overrideWith((ref) {
final c = SignatureAssetRepository(); final c = SignatureAssetRepository();
@ -119,15 +133,17 @@ void main() {
// Programmatically simulate confirm: add placement with current rect and bound image, then clear active overlay. // Programmatically simulate confirm: add placement with current rect and bound image, then clear active overlay.
final ctx = tester.element(find.byType(PdfSignatureHomePage)); final ctx = tester.element(find.byType(PdfSignatureHomePage));
final container = ProviderScope.containerOf(ctx); final container = ProviderScope.containerOf(ctx);
final sigState = container.read(signatureProvider); final r = container.read(activeRectProvider)!;
final r = sigState.rect!;
final lib = container.read(signatureAssetRepositoryProvider); final lib = container.read(signatureAssetRepositoryProvider);
final asset = lib.isNotEmpty ? lib.first : null; final asset = lib.isNotEmpty ? lib.first : null;
final pdf = container.read(documentRepositoryProvider); final pdf = container.read(documentRepositoryProvider);
container container
.read(documentRepositoryProvider.notifier) .read(documentRepositoryProvider.notifier)
.addPlacement(page: pdf.currentPage, rect: r, asset: asset); .addPlacement(page: pdf.currentPage, rect: r, asset: asset);
container.read(signatureProvider.notifier).clearActiveOverlay(); // Clear active overlay by hiding signatures temporarily
container.read(signatureVisibilityProvider.notifier).state = false;
await tester.pump();
container.read(signatureVisibilityProvider.notifier).state = true;
await tester.pumpAndSettle(); await tester.pumpAndSettle();
final placed = find.byKey(const Key('placed_signature_0')); final placed = find.byKey(const Key('placed_signature_0'));

View File

@ -138,6 +138,14 @@ class _PdfMockContinuousListState extends ConsumerState<PdfMockContinuousList> {
final aspectLocked = ref.watch( final aspectLocked = ref.watch(
aspectLockedProvider, aspectLockedProvider,
); );
// Publish rect for tests/other UI to observe
WidgetsBinding.instance.addPostFrameCallback((
_,
) {
if (!mounted) return;
ref.read(activeRectProvider.notifier).state =
_activeRect;
});
return Stack( return Stack(
children: [ children: [
Positioned( Positioned(

View File

@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
/// Whether to use a mock continuous viewer (ListView) instead of a real PDF viewer. /// Whether to use a mock continuous viewer (ListView) instead of a real PDF viewer.
@ -9,3 +10,7 @@ final signatureVisibilityProvider = StateProvider<bool>((ref) => true);
/// Whether resizing keeps the current aspect ratio for the active overlay /// Whether resizing keeps the current aspect ratio for the active overlay
final aspectLockedProvider = StateProvider<bool>((ref) => false); final aspectLockedProvider = StateProvider<bool>((ref) => false);
/// Current active overlay rect (normalized 0..1) for the mock viewer.
/// Integration tests can read this to confirm or compute placements.
final activeRectProvider = StateProvider<Rect?>((ref) => null);

View File

@ -143,20 +143,16 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
if (path == null || path.trim().isEmpty) return; if (path == null || path.trim().isEmpty) return;
final fullPath = _ensurePdfExtension(path.trim()); final fullPath = _ensurePdfExtension(path.trim());
savedPath = fullPath; savedPath = fullPath;
if (pdf.pickedPdfBytes != null) { final src = pdf.pickedPdfBytes ?? Uint8List(0);
final out = await exporter.exportSignedPdfFromBytes( final out = await exporter.exportSignedPdfFromBytes(
srcBytes: pdf.pickedPdfBytes!, srcBytes: src,
uiPageSize: _pageSize, uiPageSize: _pageSize,
signatureImageBytes: null, signatureImageBytes: null,
placementsByPage: pdf.placementsByPage, placementsByPage: pdf.placementsByPage,
targetDpi: targetDpi, targetDpi: targetDpi,
); );
if (out != null) { if (out != null) {
ok = await exporter.saveBytesToFile( ok = await exporter.saveBytesToFile(bytes: out, outputPath: fullPath);
bytes: out,
outputPath: fullPath,
);
}
} }
} }
if (!kIsWeb) { if (!kIsWeb) {

View File

@ -33,6 +33,7 @@ class SignatureOverlay extends StatelessWidget {
width: width, width: width,
height: height, height: height,
child: DecoratedBox( child: DecoratedBox(
key: Key('placed_signature_$placedIndex'),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2), border: Border.all(color: Colors.red, width: 2),
), ),
@ -41,7 +42,6 @@ class SignatureOverlay extends StatelessWidget {
child: RotatedSignatureImage( child: RotatedSignatureImage(
bytes: placement.asset.bytes, bytes: placement.asset.bytes,
rotationDeg: placement.rotationDeg, rotationDeg: placement.rotationDeg,
key: Key('placed_signature_$placedIndex'),
), ),
), ),
), ),