feat: partially implement new feature test

This commit is contained in:
insleker 2025-09-09 22:26:33 +08:00
parent 380be43c05
commit 21a0638bf0
87 changed files with 661 additions and 732 deletions

View File

@ -122,11 +122,11 @@ void main() {
final sigState = container.read(signatureProvider);
final r = sigState.rect!;
final lib = container.read(signatureLibraryProvider);
final imageId = lib.isNotEmpty ? lib.first.id : 'default.png';
final imageId = lib.isNotEmpty ? lib.first.id : '';
final pdf = container.read(pdfProvider);
container
.read(pdfProvider.notifier)
.addPlacement(page: pdf.currentPage, rect: r, imageId: imageId);
.addPlacement(page: pdf.currentPage, rect: r, assetId: imageId);
container.read(signatureProvider.notifier).clearActiveOverlay();
await tester.pumpAndSettle();

View File

@ -38,11 +38,23 @@ class GraphicAdjust {
class SignatureCard {
final double rotationDeg;
final SignatureAsset asset;
final GraphicAdjust graphicAdjust;
GraphicAdjust graphicAdjust;
const SignatureCard({
required this.rotationDeg,
required this.asset,
this.graphicAdjust = const GraphicAdjust(),
});
SignatureCard({required this.rotationDeg, required this.asset})
: graphicAdjust = GraphicAdjust();
SignatureCard copyWith({
double? rotationDeg,
SignatureAsset? asset,
GraphicAdjust? graphicAdjust,
}) => SignatureCard(
rotationDeg: rotationDeg ?? this.rotationDeg,
asset: asset ?? this.asset,
graphicAdjust: graphicAdjust ?? this.graphicAdjust,
);
}
/// Represents a single signature placement on a page combining both the
@ -52,29 +64,28 @@ class SignaturePlacement {
// The bounding box of this placement in UI coordinate space, implies scaling and position.
final Rect rect;
/// from `SignatureCard`
/// Rotation in degrees to apply when rendering/exporting this placement.
final double rotationDeg;
GraphicAdjust graphicAdjust;
final SignatureAsset asset;
final GraphicAdjust graphicAdjust;
final String assetId; // ID of the signature asset
SignaturePlacement({
const SignaturePlacement({
required this.rect,
required this.asset,
required this.assetId,
this.rotationDeg = 0.0,
GraphicAdjust graphicAdjust = const GraphicAdjust(),
}) : graphicAdjust = graphicAdjust;
this.graphicAdjust = const GraphicAdjust(),
});
SignaturePlacement copyWith({
required Rect rect,
required SignatureAsset asset,
double rotationDeg = 0.0,
GraphicAdjust graphicAdjust = const GraphicAdjust(),
Rect? rect,
String? assetId,
double? rotationDeg,
GraphicAdjust? graphicAdjust,
}) => SignaturePlacement(
rect: rect,
asset: asset,
rotationDeg: rotationDeg,
graphicAdjust: graphicAdjust,
rect: rect ?? this.rect,
assetId: assetId ?? this.assetId,
rotationDeg: rotationDeg ?? this.rotationDeg,
graphicAdjust: graphicAdjust ?? this.graphicAdjust,
);
}
@ -85,7 +96,7 @@ class PdfState {
final String? pickedPdfPath;
final Uint8List? pickedPdfBytes;
final int? signedPage;
// Multiple signature placements per page, each combines geometry and optional image id.
// Multiple signature placements per page, each combines geometry and asset id.
final Map<int, List<SignaturePlacement>> placementsByPage;
// UI state: selected placement index on the current page (if any)
final int? selectedPlacementIndex;

View File

@ -148,8 +148,8 @@ class ExportService {
final w = r.width / uiPageSize.width * widthPts;
final h = r.height / uiPageSize.height * heightPts;
Uint8List? bytes;
final id = placement.imageId;
if (id != null) {
final id = placement.assetId;
if (id.isNotEmpty) {
bytes = libraryBytes?[id];
}
bytes ??= signatureImageBytes; // fallback
@ -275,8 +275,8 @@ class ExportService {
final w = r.width / uiPageSize.width * widthPts;
final h = r.height / uiPageSize.height * heightPts;
Uint8List? bytes;
final id = placement.imageId;
if (id != null) {
final id = placement.assetId;
if (id.isNotEmpty) {
bytes = libraryBytes?[id];
}
bytes ??= signatureImageBytes; // fallback

View File

@ -65,7 +65,7 @@ class PdfController extends StateNotifier<PdfState> {
void addPlacement({
required int page,
required Rect rect,
String? imageId = 'default.png',
String? assetId,
double rotationDeg = 0.0,
}) {
if (!state.loaded) return;
@ -75,7 +75,7 @@ class PdfController extends StateNotifier<PdfState> {
list.add(
SignaturePlacement(
rect: rect,
imageId: imageId,
assetId: assetId ?? '',
rotationDeg: rotationDeg,
),
);
@ -165,11 +165,11 @@ class PdfController extends StateNotifier<PdfState> {
// NOTE: Programmatic reassignment of images has been removed.
// Convenience to get image name for a placement
String? imageOfPlacement({required int page, required int index}) {
// Convenience to get asset id for a placement
String? assetIdOfPlacement({required int page, required int index}) {
final list = state.placementsByPage[page] ?? const [];
if (index < 0 || index >= list.length) return null;
return list[index].imageId;
return list[index].assetId;
}
}

View File

@ -2,7 +2,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/data/model/model.dart' as model;
import '../../../../data/services/export_providers.dart';
import '../../signature/view_model/signature_controller.dart';
@ -54,7 +54,7 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
key: ValueKey('sig_card_${a.id}'),
asset:
(sig.assetId == a.id)
? SignatureAsset(
? model.SignatureAsset(
id: a.id,
bytes: (processed ?? a.bytes),
name: a.name,
@ -97,7 +97,11 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
bytes == null
? Text(l.noSignatureLoaded)
: SignatureCard(
asset: SignatureAsset(id: '', bytes: bytes, name: ''),
asset: model.SignatureAsset(
id: '',
bytes: bytes,
name: '',
),
rotationDeg: sig.rotation,
disabled: disabled,
useCurrentBytesForDrag: true,

View File

@ -245,8 +245,8 @@ class _SignatureImage extends ConsumerWidget {
(placementList != null && placedIndex! < placementList.length)
? placementList[placedIndex!]
: null;
final imgId = placement?.imageId;
if (imgId != null) {
final imgId = placement?.assetId;
if (imgId != null && imgId.isNotEmpty) {
final lib = ref.watch(signatureLibraryProvider);
for (final a in lib) {
if (a.id == imgId) {

View File

@ -188,7 +188,7 @@ class SignatureController extends StateNotifier<SignatureState> {
.addPlacement(
page: pdf.currentPage,
rect: r,
imageId: id,
assetId: id,
rotationDeg: state.rotation,
);
// Newly placed index is the last one on the page
@ -220,7 +220,7 @@ class SignatureController extends StateNotifier<SignatureState> {
.addPlacement(
page: pdf.currentPage,
rect: r,
imageId: id,
assetId: id,
rotationDeg: state.rotation,
);
final idx =

View File

@ -0,0 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: a created signature card
Future<void> aCreatedSignatureCard(WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -1,23 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the signature on page {5} is shown on page {5}
Future<void> theSignatureOnPageIsShownOnPage(
/// Usage: a document is open and contains at least one signature placement
Future<void> aDocumentIsOpenAndContainsAtLeastOneSignaturePlacement(
WidgetTester tester,
num sourcePage,
num targetPage,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final srcList = container
container
.read(pdfProvider.notifier)
.placementsOn(sourcePage.toInt());
final tgtList = container
.openPicked(path: 'test.pdf', pageCount: 5);
container
.read(pdfProvider.notifier)
.placementsOn(targetPage.toInt());
// At least one exists on both
expect(srcList, isNotEmpty);
expect(tgtList, isNotEmpty);
.addPlacement(
page: 1,
rect: Rect.fromLTWH(10, 10, 100, 50),
assetId: 'sig.png',
);
}

View File

@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a document is open and contains multiple placed signature placements across pages
Future<void>
aDocumentIsOpenAndContainsMultiplePlacedSignaturePlacementsAcrossPages(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.openPicked(path: 'multi.pdf', pageCount: 5);
container
.read(pdfProvider.notifier)
.addPlacement(
page: 1,
rect: Rect.fromLTWH(10, 10, 100, 50),
assetId: 'sig1.png',
);
container
.read(pdfProvider.notifier)
.addPlacement(
page: 2,
rect: Rect.fromLTWH(20, 20, 100, 50),
assetId: 'sig2.png',
);
container
.read(pdfProvider.notifier)
.addPlacement(
page: 3,
rect: Rect.fromLTWH(30, 30, 100, 50),
assetId: 'sig3.png',
);
}

View File

@ -3,12 +3,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a sample multi-page PDF (5 pages) is available
Future<void> aSampleMultipagePdf5PagesIsAvailable(WidgetTester tester) async {
/// Usage: a document is open with no signature placements placed
Future<void> aDocumentIsOpenWithNoSignaturePlacementsPlaced(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
// Open a mock document with 5 pages
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
.openPicked(path: 'empty.pdf', pageCount: 5);
// No placements added
}

View File

@ -3,12 +3,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a PDF page is selected for signing
Future<void> aPdfPageIsSelectedForSigning(WidgetTester tester) async {
/// Usage: a document page is selected for signing
Future<void> aDocumentPageIsSelectedForSigning(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 1);
container.read(pdfProvider.notifier).setSignedPage(1);
container.read(pdfProvider.notifier).jumpTo(1);
}

View File

@ -1,20 +1,19 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_library.dart';
import 'package:pdf_signature/data/model/model.dart';
import '_world.dart';
/// Usage: a signature image is placed on the page
Future<void> aSignatureImageIsPlacedOnThePage(WidgetTester tester) async {
/// Usage: a multi-page document is open
Future<void> aMultipageDocumentIsOpen(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container.read(signatureLibraryProvider.notifier).state = [];
container.read(pdfProvider.notifier).state = PdfState.initial();
container.read(signatureProvider.notifier).state = SignatureState.initial();
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
container.read(pdfProvider.notifier).setSignedPage(1);
// Set an image to ensure rect exists
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([1, 2, 3]));
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: a new document file is saved at specified full path, location and file name
Future<void> aNewDocumentFileIsSavedAtSpecifiedFullPathLocationAndFileName(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -1,15 +0,0 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import '_world.dart';
/// Usage: a new PDF file is saved at specified full path, location and file name
Future<void> aNewPdfFileIsSavedAtSpecifiedFullPathLocationAndFileName(
WidgetTester tester,
) async {
if (TestWorld.lastSavedPath != null) {
expect(File(TestWorld.lastSavedPath!).existsSync(), isTrue);
} else {
expect(TestWorld.lastExportBytes, isNotNull);
expect(TestWorld.lastExportBytes!.isNotEmpty, isTrue);
}
}

View File

@ -1,26 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a PDF is open and contains at least one placed signature
Future<void> aPdfIsOpenAndContainsAtLeastOnePlacedSignature(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.openPicked(
path: 'mock.pdf',
pageCount: 2,
bytes: Uint8List.fromList([1, 2, 3]),
);
container.read(pdfProvider.notifier).setSignedPage(1);
container.read(signatureProvider.notifier).placeDefaultRect();
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([1, 2, 3]));
}

View File

@ -1,32 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a PDF is open and contains multiple placed signatures across pages
Future<void> aPdfIsOpenAndContainsMultiplePlacedSignaturesAcrossPages(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 6);
// Ensure signature image exists
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([1, 2, 3]));
// Place on two pages
container
.read(pdfProvider.notifier)
.addPlacement(page: 1, rect: const Rect.fromLTWH(10, 10, 80, 40));
container
.read(pdfProvider.notifier)
.addPlacement(page: 4, rect: const Rect.fromLTWH(120, 200, 100, 50));
// Keep backward compatibility with existing export step expectations
container.read(pdfProvider.notifier).setSignedPage(1);
container.read(signatureProvider.notifier).placeDefaultRect();
}

View File

@ -1,17 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a PDF is open with no signatures placed
Future<void> aPdfIsOpenWithNoSignaturesPlaced(WidgetTester tester) async {
// Fresh world for this scenario to avoid leftover rect/image from previous tests
TestWorld.reset();
final container = ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 1);
container.read(signatureProvider.notifier).resetForNewPage();
}

View File

@ -3,11 +3,13 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a multi-page PDF is open
Future<void> aMultipagePdfIsOpen(WidgetTester tester) async {
/// Usage: a sample multi-page document (5 pages) is available
Future<void> aSampleMultipageDocument5PagesIsAvailable(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.openPicked(path: 'sample.pdf', pageCount: 10);
.openPicked(path: 'sample.pdf', pageCount: 5);
}

View File

@ -0,0 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: a signature asset is created
Future<void> aSignatureAssetIsCreated(WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: a signature asset is loaded or drawn
Future<void> aSignatureAssetIsLoadedOrDrawn(WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: a signature asset is placed on the page
Future<void> aSignatureAssetIsPlacedOnThePage(WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: a signature asset is selected
Future<void> aSignatureAssetIsSelected(WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,23 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_library.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/data/model/model.dart';
import '_world.dart';
/// Usage: a signature asset loaded or drawn is wrapped in a signature card
Future<void> aSignatureAssetLoadedOrDrawnIsWrappedInASignatureCard(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container.read(signatureLibraryProvider.notifier).state = [];
container.read(pdfProvider.notifier).state = PdfState.initial();
container.read(signatureProvider.notifier).state = SignatureState.initial();
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
container
.read(signatureLibraryProvider.notifier)
.add(bytes, name: 'test.png');
}

View File

@ -1,10 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: a signature image is created
Future<void> aSignatureImageIsCreated(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
expect(container.read(signatureProvider).imageBytes, isNotNull);
}

View File

@ -1,14 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: a signature image is loaded or drawn
Future<void> aSignatureImageIsLoadedOrDrawn(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([1, 2, 3]));
}

View File

@ -1,28 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a signature image is selected
Future<void> aSignatureImageIsSelected(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 2);
container.read(pdfProvider.notifier).setSignedPage(1);
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([1, 2, 3]));
// Allow provider scheduler to process queued updates fully
await tester.pumpAndSettle();
// Extra pump with a non-zero duration to flush zero-delay timers
await tester.pump(const Duration(milliseconds: 1));
// Teardown to avoid pending timers from Riverpod's scheduler
addTearDown(() {
TestWorld.container?.dispose();
TestWorld.container = null;
});
}

View File

@ -1,25 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a signature is placed on page {2}
Future<void> aSignatureIsPlacedOnPage(WidgetTester tester, num page) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 6);
// Ensure image and rect
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([1, 2, 3]));
container.read(signatureProvider.notifier).placeDefaultRect();
final Rect r = container.read(signatureProvider).rect!;
container
.read(pdfProvider.notifier)
.addPlacement(page: page.toInt(), rect: r, imageId: 'default.png');
}

View File

@ -1,37 +0,0 @@
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a signature is placed with a position and size relative to the page
Future<void> aSignatureIsPlacedWithAPositionAndSizeRelativeToThePage(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.openPicked(
path: 'mock.pdf',
pageCount: 2,
bytes: Uint8List.fromList([1, 2, 3]),
);
container.read(pdfProvider.notifier).setSignedPage(1);
final r = Rect.fromLTWH(50, 100, 120, 60);
final sigN = container.read(signatureProvider.notifier);
sigN.placeDefaultRect();
// overwrite to desired rect
final sig = container.read(signatureProvider);
sigN
..toggleAspect(true)
..resize(Offset(r.width - sig.rect!.width, r.height - sig.rect!.height));
// move to target top-left
final movedDelta = Offset(r.left - sig.rect!.left, r.top - sig.rect!.top);
sigN.drag(movedDelta);
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([4, 5, 6]));
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: a signature placement appears on the page based on the signature card
Future<void> aSignaturePlacementAppearsOnThePageBasedOnTheSignatureCard(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -1,20 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: three signatures are placed on the current page
Future<void> threeSignaturesArePlacedOnTheCurrentPage(
/// Usage: a signature placement is placed on page {2}
Future<void> aSignaturePlacementIsPlacedOnPage(
WidgetTester tester,
num param1,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final page = param1.toInt();
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
final n = container.read(pdfProvider.notifier);
n.addPlacement(page: 1, rect: const Rect.fromLTWH(10, 10, 80, 40));
n.addPlacement(page: 1, rect: const Rect.fromLTWH(100, 50, 80, 40));
n.addPlacement(page: 1, rect: const Rect.fromLTWH(200, 90, 80, 40));
.addPlacement(
page: page,
rect: Rect.fromLTWH(20, 20, 100, 50),
assetId: 'test.png',
);
}

View File

@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: a signature placement is placed with a position and size relative to the page
Future<void> aSignaturePlacementIsPlacedWithAPositionAndSizeRelativeToThePage(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
container
.read(pdfProvider.notifier)
.addPlacement(
page: pdf.currentPage,
rect: Rect.fromLTWH(50, 50, 200, 100),
assetId: 'test.png',
);
}

View File

@ -14,7 +14,7 @@ Future<void> adjustingOneInstanceDoesNotAffectTheOthers(
container.read(pdfProvider.notifier).removePlacement(page: 2, index: 0);
container
.read(pdfProvider.notifier)
.addPlacement(page: 2, rect: modified, imageId: before[0].imageId);
.addPlacement(page: 2, rect: modified, assetId: before[0].assetId);
final after = container.read(pdfProvider.notifier).placementsOn(2);
expect(after.any((p) => p.rect == before[1].rect), isTrue);
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: adjusting one of the signature placements does not affect the others
Future<void> adjustingOneOfTheSignaturePlacementsDoesNotAffectTheOthers(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,18 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: all placed signature placements appear on their corresponding pages in the output
Future<void>
allPlacedSignaturePlacementsAppearOnTheirCorrespondingPagesInTheOutput(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final totalPlacements = pdf.placementsByPage.values.fold(
0,
(sum, list) => sum + list.length,
);
expect(totalPlacements, greaterThan(1));
}

View File

@ -1,17 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: all placed signatures appear on their corresponding pages in the output
Future<void> allPlacedSignaturesAppearOnTheirCorrespondingPagesInTheOutput(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
expect(container.read(pdfProvider.notifier).placementsOn(1), isNotEmpty);
// One of 4 or 5 depending on scenario
final p4 = container.read(pdfProvider.notifier).placementsOn(4);
final p5 = container.read(pdfProvider.notifier).placementsOn(5);
expect(p4.isNotEmpty || p5.isNotEmpty, isTrue);
expect(TestWorld.lastExportBytes, isNotNull);
}

View File

@ -0,0 +1,14 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: both signature placements are shown on their respective pages
Future<void> bothSignaturePlacementsAreShownOnTheirRespectivePages(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
expect(pdf.placementsByPage[1], isNotEmpty);
expect(pdf.placementsByPage[3], isNotEmpty);
}

View File

@ -1,15 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: both signatures are shown on their respective pages
Future<void> bothSignaturesAreShownOnTheirRespectivePages(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final p1 = container.read(pdfProvider.notifier).placementsOn(1);
final p3 = container.read(pdfProvider.notifier).placementsOn(3);
expect(p1, isNotEmpty);
expect(p3, isNotEmpty);
}

View File

@ -20,7 +20,7 @@ Future<void> draggingOrResizingOneDoesNotChangeTheOther(
.addPlacement(
page: 1,
rect: changed,
imageId: list[1].imageId,
assetId: list[1].assetId,
rotationDeg: list[1].rotationDeg,
);
final after = container.read(pdfProvider.notifier).placementsOn(1);

View File

@ -1,19 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: each signature can be dragged and resized independently
Future<void> eachSignatureCanBeDraggedAndResizedIndependently(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final list = container.read(pdfProvider.notifier).placementsOn(1);
expect(list.length, greaterThanOrEqualTo(2));
// Independence is modeled by distinct rects; ensure not equal and both within page
expect(list[0].rect, isNot(equals(list[1].rect)));
for (final p in list.take(2)) {
expect(p.rect.left, greaterThanOrEqualTo(0));
expect(p.rect.top, greaterThanOrEqualTo(0));
}
}

View File

@ -0,0 +1,14 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: each signature placement can be dragged and resized independently
Future<void> eachSignaturePlacementCanBeDraggedAndResizedIndependently(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
expect(placements.length, greaterThan(1));
}

View File

@ -0,0 +1,16 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: identical signature placements appear in each location
Future<void> identicalSignaturePlacementsAppearInEachLocation(
WidgetTester tester,
) async {
final container = TestWorld.container!;
final pdf = container.read(pdfProvider);
final allPlacements =
pdf.placementsByPage.values.expand((list) => list).toList();
final assetIds = allPlacements.map((p) => p.assetId).toSet();
expect(assetIds.length, 1); // All the same
}

View File

@ -1,10 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: it is placed on the selected page
Future<void> itIsPlacedOnTheSelectedPage(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
expect(container.read(signatureProvider).imageBytes, isNotNull);
}

View File

@ -1,11 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: only the selected signature is removed
Future<void> onlyTheSelectedSignatureIsRemoved(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final list = container.read(pdfProvider.notifier).placementsOn(1);
expect(list.length, 2);
}

View File

@ -0,0 +1,14 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: only the selected signature placement is removed
Future<void> onlyTheSelectedSignaturePlacementIsRemoved(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
expect(placements.length, lessThan(3)); // Assuming started with 3, removed 1
}

View File

@ -0,0 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: resize to fit within bounding box
Future<void> resizeToFitWithinBoundingBox(WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: signature placement occurs on the selected page
Future<void> signaturePlacementOccursOnTheSelectedPage(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the app attempts to load the asset
Future<void> theAppAttemptsToLoadTheAsset(WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -1,6 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the app attempts to load the image
Future<void> theAppAttemptsToLoadTheImage(WidgetTester tester) async {
// No-op for logic-level test; selection step already applied state.
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the asset is loaded and shown as a signature asset
Future<void> theAssetIsLoadedAndShownAsASignatureAsset(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the asset is loaded and shown as a signature card
Future<void> theAssetIsLoadedAndShownAsASignatureCard(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the asset is not added to the document
Future<void> theAssetIsNotAddedToTheDocument(WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -1,14 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: the image is loaded and shown as a signature asset
Future<void> theImageIsLoadedAndShownAsASignatureAsset(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final sig = container.read(signatureProvider);
expect(sig.imageBytes, isNotNull);
expect(sig.rect, isNotNull);
}

View File

@ -1,11 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: the image is not added to the document
Future<void> theImageIsNotAddedToTheDocument(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final sig = container.read(signatureProvider);
expect(sig.rect, isNull);
}

View File

@ -1,12 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: the image scales proportionally
Future<void> theImageScalesProportionally(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final sig = container.read(signatureProvider);
final aspect = sig.rect!.width / sig.rect!.height;
expect((aspect - (TestWorld.prevAspect ?? aspect)).abs() < 0.05, isTrue);
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the other signature placements remain unchanged
Future<void> theOtherSignaturePlacementsRemainUnchanged(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -1,12 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the other signatures remain unchanged
Future<void> theOtherSignaturesRemainUnchanged(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final list = container.read(pdfProvider.notifier).placementsOn(1);
// After deleting index 1, two should remain
expect(list.length, 2);
}

View File

@ -1,15 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: the signature is stamped at the exact PDF page coordinates and size
Future<void> theSignatureIsStampedAtTheExactPdfPageCoordinatesAndSize(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final sig = container.read(signatureProvider);
expect(sig.rect, isNotNull);
expect(sig.rect!.width, greaterThan(0));
expect(sig.rect!.height, greaterThan(0));
}

View File

@ -1,12 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the signature on page {2} remains
Future<void> theSignatureOnPageRemains(WidgetTester tester, num page) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final list = container.read(pdfProvider.notifier).placementsOn(page.toInt());
expect(list, isNotEmpty);
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the signature placement is stamped at the exact PDF page coordinates and size
Future<void> theSignaturePlacementIsStampedAtTheExactPdfPageCoordinatesAndSize(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,16 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the signature placement on page {5} is shown on page {5}
Future<void> theSignaturePlacementOnPageIsShownOnPage(
WidgetTester tester,
num param1,
num param2,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final page = param1.toInt();
expect(pdf.placementsByPage[page], isNotEmpty);
}

View File

@ -0,0 +1,15 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the signature placement on page {2} remains
Future<void> theSignaturePlacementOnPageRemains(
WidgetTester tester,
num param1,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final page = param1.toInt();
expect(pdf.placementsByPage[page], isNotEmpty);
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the signature placement remains within the page area
Future<void> theSignaturePlacementRemainsWithinThePageArea(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the signature placement rotates around its center in real time
Future<void> theSignaturePlacementRotatesAroundItsCenterInRealTime(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the signature placements appear on the corresponding page in the output
Future<void> theSignaturePlacementsAppearOnTheCorrespondingPageInTheOutput(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -1,14 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: the signature remains within the page area
Future<void> theSignatureRemainsWithinThePageArea(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final sig = container.read(signatureProvider);
final r = sig.rect!;
expect(r.left >= 0 && r.top >= 0, isTrue);
expect(r.right <= SignatureController.pageSize.width, isTrue);
expect(r.bottom <= SignatureController.pageSize.height, isTrue);
}

View File

@ -1,17 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the signatures appear on the corresponding page in the output
Future<void> theSignaturesAppearOnTheCorrespondingPageInTheOutput(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final sig = container.read(signatureProvider);
expect(pdf.signedPage, isNotNull);
expect(sig.rect, isNotNull);
expect(sig.imageBytes, isNotNull);
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the user chooses a image file as a signature asset
Future<void> theUserChoosesAImageFileAsASignatureAsset(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the user chooses a signature asset to created a signature card
Future<void> theUserChoosesASignatureAssetToCreatedASignatureCard(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -1,82 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: the user chooses a signature image file
Future<void> theUserChoosesASignatureImageFile(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
// Simulate loading a tiny valid PNG/JPEG bytes; using 1x1 transparent PNG
final bytes = 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,
0x06,
0x00,
0x00,
0x00,
0x1F,
0x15,
0xC4,
0x89,
0x00,
0x00,
0x00,
0x0A,
0x49,
0x44,
0x41,
0x54,
0x78,
0x9C,
0x63,
0x00,
0x01,
0x00,
0x00,
0x05,
0x00,
0x01,
0x0D,
0x0A,
0x2D,
0xB4,
0x00,
0x00,
0x00,
0x00,
0x49,
0x45,
0x4E,
0x44,
0xAE,
0x42,
0x60,
0x82,
]);
container.read(signatureProvider.notifier).setImageBytes(bytes);
}

View File

@ -1,11 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the user deletes one selected signature
Future<void> theUserDeletesOneSelectedSignature(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
// Remove the middle one (index 1)
container.read(pdfProvider.notifier).removePlacement(page: 1, index: 1);
}

View File

@ -0,0 +1,17 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the user deletes one selected signature placement
Future<void> theUserDeletesOneSelectedSignaturePlacement(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
if (pdf.selectedPlacementIndex == null) {
container.read(pdfProvider.notifier).selectPlacement(0);
}
container.read(pdfProvider.notifier).deleteSelectedPlacement();
}

View File

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_library.dart';
import '_world.dart';
/// Usage: the user drags it on the page of the document to place signature placements in multiple locations in the document
Future<void>
theUserDragsItOnThePageOfTheDocumentToPlaceSignaturePlacementsInMultipleLocationsInTheDocument(
WidgetTester tester,
) async {
final container = TestWorld.container!;
final lib = container.read(signatureLibraryProvider);
final assetId = lib.isNotEmpty ? lib.first.id : 'shared.png';
container
.read(pdfProvider.notifier)
.addPlacement(
page: 1,
rect: Rect.fromLTWH(10, 10, 100, 50),
assetId: assetId,
);
container
.read(pdfProvider.notifier)
.addPlacement(
page: 2,
rect: Rect.fromLTWH(20, 20, 100, 50),
assetId: assetId,
);
container
.read(pdfProvider.notifier)
.addPlacement(
page: 3,
rect: Rect.fromLTWH(30, 30, 100, 50),
assetId: assetId,
);
}

View File

@ -0,0 +1,8 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the user drags this signature card on the page of the document to place a signature placement
Future<void>
theUserDragsThisSignatureCardOnThePageOfTheDocumentToPlaceASignaturePlacement(
WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -1,16 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import '_world.dart';
/// Usage: the user enables aspect ratio lock and resizes
Future<void> theUserEnablesAspectRatioLockAndResizes(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final sigN = container.read(signatureProvider.notifier);
final sig = container.read(signatureProvider);
TestWorld.prevAspect = sig.rect!.width / sig.rect!.height;
sigN.toggleAspect(true);
sigN.resize(const Offset(100, 50));
}

View File

@ -1,35 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the user navigates to page {3} and places another signature
Future<void> theUserNavigatesToPageAndPlacesAnotherSignature(
WidgetTester tester,
num page,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
// Ensure doc open
final pdf = container.read(pdfProvider);
if (!pdf.loaded) {
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 6);
}
container.read(pdfProvider.notifier).jumpTo(page.toInt());
// Ensure an image is loaded
if (container.read(signatureProvider).imageBytes == null) {
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([1, 2, 3]));
}
container.read(signatureProvider.notifier).placeDefaultRect();
final Rect r = container.read(signatureProvider).rect!;
container
.read(pdfProvider.notifier)
.addPlacement(page: page.toInt(), rect: r, imageId: 'default.png');
}

View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the user navigates to page {5} and places another signature placement
Future<void> theUserNavigatesToPageAndPlacesAnotherSignaturePlacement(
WidgetTester tester,
num param1,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final page = param1.toInt();
container.read(pdfProvider.notifier).jumpTo(page);
container
.read(pdfProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(40, 40, 100, 50),
assetId: 'another.png',
);
}

View File

@ -1,58 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the user places a signature from picture <second_image> on page <second_page>
Future<void> theUserPlacesASignatureFromPictureOnPage(
WidgetTester tester, [
dynamic imageName,
dynamic pageNumber,
]) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
// Ensure a document is open
final pdf = container.read(pdfProvider);
if (!pdf.loaded) {
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 6);
}
// Load image bytes based on provided name
if (imageName == null) {
// Alternate between alice/bob for the first two calls to match Examples
final idx = TestWorld.placeFromPictureCallCount++;
imageName = (idx % 2 == 0) ? 'alice.png' : 'bob.png';
}
final String name =
imageName is String
? imageName
: (imageName?.toString() ?? 'default.png');
Uint8List bytes;
switch (name) {
case 'alice.png':
bytes = Uint8List.fromList([1, 2, 3]);
break;
case 'bob.png':
bytes = Uint8List.fromList([4, 5, 6]);
break;
default:
bytes = Uint8List.fromList([7, 8, 9]);
}
container.read(signatureProvider.notifier).setImageBytes(bytes);
// Place default rect and add placement on target page with image name
container.read(signatureProvider.notifier).placeDefaultRect();
final Rect r = container.read(signatureProvider).rect!;
final int page =
(pageNumber is num)
? pageNumber.toInt()
: int.tryParse(pageNumber?.toString() ?? '') ??
// Default pages for the two calls in the scenario: 1 then 3
((TestWorld.placeFromPictureCallCount <= 1) ? 1 : 3);
container
.read(pdfProvider.notifier)
.addPlacement(page: page, rect: r, imageId: name);
}

View File

@ -1,34 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the user places a signature on page {1}
Future<void> theUserPlacesASignatureOnPage(
WidgetTester tester,
num page,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
// Ensure doc open
final pdf = container.read(pdfProvider);
if (!pdf.loaded) {
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 6);
}
// Ensure an image is loaded
if (container.read(signatureProvider).imageBytes == null) {
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([1, 2, 3]));
}
container.read(signatureProvider.notifier).placeDefaultRect();
final Rect r = container.read(signatureProvider).rect!;
container
.read(pdfProvider.notifier)
.addPlacement(page: page.toInt(), rect: r, imageId: 'default.png');
}

View File

@ -0,0 +1,36 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_library.dart';
import 'package:pdf_signature/data/model/model.dart';
import '_world.dart';
/// Usage: the user places a signature placement from asset <second_asset> on page <second_page>
Future<void> theUserPlacesASignaturePlacementFromAssetOnPage(
WidgetTester tester,
String assetName,
int page,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final library = container.read(signatureLibraryProvider);
var asset = library.where((a) => a.name == assetName).firstOrNull;
if (asset == null) {
// add dummy asset
final id = container
.read(signatureLibraryProvider.notifier)
.add(Uint8List(0), name: assetName);
asset = container
.read(signatureLibraryProvider)
.firstWhere((a) => a.id == id);
}
container
.read(pdfProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(10, 10, 50, 50),
assetId: asset.id,
);
}

View File

@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the user places a signature placement on page {1}
Future<void> theUserPlacesASignaturePlacementOnPage(
WidgetTester tester,
num param1,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final page = param1.toInt();
container
.read(pdfProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(20, 20, 100, 50),
assetId: 'test.png',
);
}

View File

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the user places two signature placements on the same page
Future<void> theUserPlacesTwoSignaturePlacementsOnTheSamePage(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
final page = pdf.currentPage;
container
.read(pdfProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(10, 10, 100, 50),
assetId: 'sig1.png',
);
container
.read(pdfProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(120, 10, 100, 50),
assetId: 'sig2.png',
);
}

View File

@ -1,24 +0,0 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import '_world.dart';
/// Usage: the user places two signatures on the same page
Future<void> theUserPlacesTwoSignaturesOnTheSamePage(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(signatureProvider.notifier)
.setImageBytes(Uint8List.fromList([1, 2, 3]));
// First
container.read(signatureProvider.notifier).placeDefaultRect();
final r1 = container.read(signatureProvider).rect!;
container.read(pdfProvider.notifier).addPlacement(page: 1, rect: r1);
// Second (offset a bit)
final r2 = r1.shift(const Offset(30, 30));
container.read(pdfProvider.notifier).addPlacement(page: 1, rect: r2);
}

View File

@ -15,9 +15,22 @@ Future<void> theUserSavesexportsTheDocument(WidgetTester tester) async {
final pdf = container.read(pdfProvider);
final sig = container.read(signatureProvider);
expect(pdf.loaded, isTrue, reason: 'PDF must be loaded before export');
expect(pdf.signedPage, isNotNull, reason: 'A signed page must be selected');
expect(sig.rect, isNotNull, reason: 'Signature rect must exist');
expect(sig.imageBytes, isNotNull, reason: 'Signature image must exist');
// Check if there are placements
final hasPlacements = pdf.placementsByPage.values.any(
(list) => list.isNotEmpty,
);
if (!hasPlacements) {
expect(
sig.rect,
isNotNull,
reason: 'Signature rect must exist if no placements',
);
expect(
sig.imageBytes,
isNotNull,
reason: 'Signature image must exist if no placements',
);
}
// Simulate output
TestWorld.lastExportBytes =

View File

@ -0,0 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
/// Usage: the user uses rotate controls
Future<void> theUserUsesRotateControls(WidgetTester tester) async {
throw UnimplementedError();
}

View File

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_library.dart';
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
import 'package:pdf_signature/data/model/model.dart';
import '_world.dart';
/// Usage: three signature placements are placed on the current page
Future<void> threeSignaturePlacementsArePlacedOnTheCurrentPage(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container.read(signatureLibraryProvider.notifier).state = [];
container.read(pdfProvider.notifier).state = PdfState.initial();
container.read(signatureProvider.notifier).state = SignatureState.initial();
container
.read(pdfProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
final pdfN = container.read(pdfProvider.notifier);
final pdf = container.read(pdfProvider);
final page = pdf.currentPage;
pdfN.addPlacement(
page: page,
rect: Rect.fromLTWH(10, 10, 50, 50),
assetId: 'test1',
);
pdfN.addPlacement(
page: page,
rect: Rect.fromLTWH(70, 10, 50, 50),
assetId: 'test2',
);
pdfN.addPlacement(
page: page,
rect: Rect.fromLTWH(130, 10, 50, 50),
assetId: 'test3',
);
}

View File

@ -119,8 +119,9 @@ void main() {
final processed = container3.read(processedSignatureImageProvider);
expect(processed, isNotNull);
final pdf = container3.read(pdfProvider);
final imgId = pdf.placementsByPage[pdf.currentPage]?.first.imageId;
final imgId = pdf.placementsByPage[pdf.currentPage]?.first.assetId;
expect(imgId, isNotNull);
expect(imgId, isNotEmpty);
final lib = container3.read(signatureLibraryProvider);
final match = lib.firstWhere((a) => a.id == imgId);
expect(match.bytes, equals(processed));