Compare commits
No commits in common. "c1b7824cbd13509bac89fa09e531f391e8f60ee4" and "fba880e1be64ca205371bd2cc388b17b4a606cc9" have entirely different histories.
c1b7824cbd
...
fba880e1be
|
@ -135,5 +135,3 @@ AppDir/bundle/
|
|||
appimage-build/
|
||||
/*.AppImage
|
||||
.vscode/settings.json
|
||||
|
||||
*.patch
|
||||
|
|
|
@ -6,7 +6,7 @@ Additionally read relevant files depends on task.
|
|||
|
||||
* If want to modify use cases (files at `test/features/*.feature`)
|
||||
* read [`FRs.md`](docs/FRs.md)
|
||||
* If want to modify code (implement or test) of `ViewModel`, `View` of MVVM (UI widget) (files at `lib/ui/features/*/widgets/*`)
|
||||
* If want to modify code (implement or test) of `View` of MVVM (UI widget) (files at `lib/ui/features/*/widgets/*`)
|
||||
* read [`wireframe.md`](docs/wireframe.md), [`NFRs.md`](docs/NFRs.md), `test/features/*.feature`
|
||||
* If want to modify code (implement or test) of non-View e.g. `Model`, services...
|
||||
* If want to modify code (implement or test) of non-View e.g. `Model`, `View Model`, services...
|
||||
* read `test/features/*.feature`, [`NFRs.md`](docs/NFRs.md)
|
||||
|
|
14
docs/FRs.md
14
docs/FRs.md
|
@ -2,27 +2,25 @@
|
|||
|
||||
## user stories
|
||||
|
||||
The following user stories may not use formal terminology as [meta-arch.md](./meta-arch.md) and use cases(`test/*.feature`), but use oral descriptions.
|
||||
|
||||
* name: [PDF browser](../test/features/pdf_browser.feature)
|
||||
* role: user
|
||||
* functionality: view and navigate PDF documents
|
||||
* benefit: select page to add signature
|
||||
* name: [load signature](../test/features/load_signature.feature)
|
||||
* name: [load signature picture](../test/features/load_signature_picture.feature)
|
||||
* role: user
|
||||
* functionality: load a signature asset file and create a signature card
|
||||
* functionality: load a signature picture file
|
||||
* benefit: easily add signature to PDF
|
||||
* name: [geometrically adjust signature picture](../test/features/geometrically_adjust_signature_picture.feature)
|
||||
* role: user
|
||||
* functionality: adjust the scale, rotation and position of the signature placement on the PDF page
|
||||
* functionality: adjust the size and position of the signature picture
|
||||
* benefit: ensure the signature fits well on the PDF page
|
||||
* name: [graphically adjust signature picture](../test/features/graphically_adjust_signature_picture.feature)
|
||||
* role: user
|
||||
* functionality: background removal, contrast adjustment... to enhance the appearance of the signature asset within the signature card
|
||||
* functionality: background removal, contrast adjustment...
|
||||
* benefit: easily improve the appearance of the signature on the PDF without additional software.
|
||||
* name: [draw signature](../test/features/draw_signature.feature)
|
||||
* role: user
|
||||
* functionality: draw a signature asset using mouse or touch input
|
||||
* functionality: draw a signature using mouse or touch input
|
||||
* benefit: create a custom signature directly on the PDF if no pre-made signature is available.
|
||||
* name: [save signed PDF](../test/features/save_signed_pdf.feature)
|
||||
* role: user
|
||||
|
@ -30,7 +28,7 @@ The following user stories may not use formal terminology as [meta-arch.md](./me
|
|||
* benefit: easily keep a copy of the signed document for records.
|
||||
* name: [preferences for app](../test/features/app_preferences.feature)
|
||||
* role: user
|
||||
* functionality: configure app preferences such as `language`, `theme`, `theme-color`.
|
||||
* functionality: configure app preferences such as `theme`, `language`.
|
||||
* benefit: customize the app experience to better fit user needs
|
||||
* name: [remember preferences](../test/features/remember_preferences.feature)
|
||||
* role: user
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
# meta archietecture
|
||||
|
||||
* [MVVM](https://docs.flutter.dev/app-architecture/guide)
|
||||
* [Data layer](https://docs.flutter.dev/app-architecture/case-study/data-layer)
|
||||
* View ⇆ ViewModel ⇆ Repository ⇆ Service
|
||||
* Model is used across.
|
||||
|
||||
## Package structure
|
||||
|
||||
|
@ -14,24 +11,6 @@ The repo structure follows official [Package structure](https://docs.flutter.dev
|
|||
* `test/widget/` contains UI widget(component) tests which focus on `View` from MVVM of each component.
|
||||
* `integration_test/` for integration tests. They should be volatile to follow UI layout changes.
|
||||
|
||||
Some rule of thumb:
|
||||
* `<object>Provider` only placed at `/lib/data/repositories/` or `/lib/data/services/` to provide data source.
|
||||
|
||||
## Abstraction
|
||||
|
||||
### terminology
|
||||
|
||||
* signature asset
|
||||
* image file of a signature, stored in the device or cloud storage
|
||||
* can drawing from canvas
|
||||
* signature card
|
||||
* template of signature placement
|
||||
* It will include modifications such as brightness, contrast, background removal, rotation of the signature asset.
|
||||
* signature placement
|
||||
* placed modified signature asset from signature card on a specific position on a specific page of a specific PDF document
|
||||
* document
|
||||
* PDF document to be signed
|
||||
|
||||
## key dependencies
|
||||
|
||||
* [pdfrx](https://pub.dev/packages/pdfrx)
|
||||
|
|
|
@ -7,9 +7,9 @@ import 'package:image/image.dart' as img;
|
|||
|
||||
import 'package:pdf_signature/data/services/export_service.dart';
|
||||
import 'package:pdf_signature/data/services/export_providers.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.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/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
|
||||
|
@ -122,11 +122,11 @@ void main() {
|
|||
final sigState = container.read(signatureProvider);
|
||||
final r = sigState.rect!;
|
||||
final lib = container.read(signatureLibraryProvider);
|
||||
final asset = lib.isNotEmpty ? lib.first : null;
|
||||
final imageId = lib.isNotEmpty ? lib.first.id : 'default.png';
|
||||
final pdf = container.read(pdfProvider);
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.addPlacement(page: pdf.currentPage, rect: r, asset: asset);
|
||||
.addPlacement(page: pdf.currentPage, rect: r, imageId: imageId);
|
||||
container.read(signatureProvider.notifier).clearActiveOverlay();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
|
||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import 'package:pdf_signature/ui/features/welcome/widgets/welcome_screen.dart';
|
||||
import 'data/services/preferences_providers.dart';
|
||||
import 'package:pdf_signature/ui/features/preferences/widgets/settings_screen.dart';
|
||||
|
|
|
@ -1,91 +1,32 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
/// A simple library of signature images available to the user in the sidebar.
|
||||
class SignatureAsset {
|
||||
final String id; // unique id
|
||||
final Uint8List bytes;
|
||||
// List<List<Offset>>? strokes;
|
||||
final String? name; // optional display name (e.g., filename)
|
||||
const SignatureAsset({required this.id, required this.bytes, this.name});
|
||||
}
|
||||
|
||||
class GraphicAdjust {
|
||||
final double contrast;
|
||||
final double brightness;
|
||||
final bool bgRemoval;
|
||||
|
||||
const GraphicAdjust({
|
||||
this.contrast = 1.0,
|
||||
this.brightness = 0.0,
|
||||
this.bgRemoval = false,
|
||||
});
|
||||
|
||||
GraphicAdjust copyWith({
|
||||
double? contrast,
|
||||
double? brightness,
|
||||
bool? bgRemoval,
|
||||
}) => GraphicAdjust(
|
||||
contrast: contrast ?? this.contrast,
|
||||
brightness: brightness ?? this.brightness,
|
||||
bgRemoval: bgRemoval ?? this.bgRemoval,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* signature card is template of signature placement
|
||||
*/
|
||||
class SignatureCard {
|
||||
final double rotationDeg;
|
||||
final SignatureAsset asset;
|
||||
final GraphicAdjust graphicAdjust;
|
||||
|
||||
const SignatureCard({
|
||||
required this.rotationDeg,
|
||||
required this.asset,
|
||||
this.graphicAdjust = const 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
|
||||
/// geometric rectangle (UI coordinate space) and the signature asset
|
||||
/// assigned to that placement.
|
||||
/// geometric rectangle (UI coordinate space) and the identifier of the
|
||||
/// image/signature asset assigned to that placement.
|
||||
class SignaturePlacement {
|
||||
// The bounding box of this placement in UI coordinate space, implies scaling and position.
|
||||
final Rect rect;
|
||||
|
||||
/// Rotation in degrees to apply when rendering/exporting this placement.
|
||||
final double rotationDeg;
|
||||
final GraphicAdjust graphicAdjust;
|
||||
final SignatureAsset asset;
|
||||
|
||||
/// Identifier of the image (e.g., filename / asset id) assigned to this placement.
|
||||
/// Nullable to allow a placement reserved before an image is chosen.
|
||||
final String? imageId;
|
||||
const SignaturePlacement({
|
||||
required this.rect,
|
||||
required this.asset,
|
||||
this.imageId,
|
||||
this.rotationDeg = 0.0,
|
||||
this.graphicAdjust = const GraphicAdjust(),
|
||||
});
|
||||
|
||||
SignaturePlacement copyWith({
|
||||
Rect? rect,
|
||||
SignatureAsset? asset,
|
||||
String? imageId,
|
||||
double? rotationDeg,
|
||||
GraphicAdjust? graphicAdjust,
|
||||
}) => SignaturePlacement(
|
||||
rect: rect ?? this.rect,
|
||||
asset: asset ?? this.asset,
|
||||
imageId: imageId ?? this.imageId,
|
||||
rotationDeg: rotationDeg ?? this.rotationDeg,
|
||||
graphicAdjust: graphicAdjust ?? this.graphicAdjust,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -96,7 +37,7 @@ class PdfState {
|
|||
final String? pickedPdfPath;
|
||||
final Uint8List? pickedPdfBytes;
|
||||
final int? signedPage;
|
||||
// Multiple signature placements per page, each combines geometry and asset.
|
||||
// Multiple signature placements per page, each combines geometry and optional image id.
|
||||
final Map<int, List<SignaturePlacement>> placementsByPage;
|
||||
// UI state: selected placement index on the current page (if any)
|
||||
final int? selectedPlacementIndex;
|
||||
|
@ -151,8 +92,8 @@ class SignatureState {
|
|||
final double rotation;
|
||||
final List<List<Offset>> strokes;
|
||||
final Uint8List? imageBytes;
|
||||
// The signature asset the current overlay is based on (from library)
|
||||
final SignatureAsset? asset;
|
||||
// The ID of the signature asset the current overlay is based on (from library)
|
||||
final String? assetId;
|
||||
// When true, the active signature overlay is movable/resizable and should not be exported.
|
||||
// When false, the overlay is confirmed (unmovable) and eligible for export.
|
||||
final bool editingEnabled;
|
||||
|
@ -165,7 +106,7 @@ class SignatureState {
|
|||
this.rotation = 0.0,
|
||||
required this.strokes,
|
||||
this.imageBytes,
|
||||
this.asset,
|
||||
this.assetId,
|
||||
this.editingEnabled = false,
|
||||
});
|
||||
factory SignatureState.initial() => const SignatureState(
|
||||
|
@ -177,7 +118,7 @@ class SignatureState {
|
|||
rotation: 0.0,
|
||||
strokes: [],
|
||||
imageBytes: null,
|
||||
asset: null,
|
||||
assetId: null,
|
||||
editingEnabled: false,
|
||||
);
|
||||
SignatureState copyWith({
|
||||
|
@ -189,7 +130,7 @@ class SignatureState {
|
|||
double? rotation,
|
||||
List<List<Offset>>? strokes,
|
||||
Uint8List? imageBytes,
|
||||
SignatureAsset? asset,
|
||||
String? assetId,
|
||||
bool? editingEnabled,
|
||||
}) => SignatureState(
|
||||
rect: rect ?? this.rect,
|
||||
|
@ -200,7 +141,7 @@ class SignatureState {
|
|||
rotation: rotation ?? this.rotation,
|
||||
strokes: strokes ?? this.strokes,
|
||||
imageBytes: imageBytes ?? this.imageBytes,
|
||||
asset: asset ?? this.asset,
|
||||
assetId: assetId ?? this.assetId,
|
||||
editingEnabled: editingEnabled ?? this.editingEnabled,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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.asset.id;
|
||||
if (id.isNotEmpty) {
|
||||
final id = placement.imageId;
|
||||
if (id != null) {
|
||||
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.asset.id;
|
||||
if (id.isNotEmpty) {
|
||||
final id = placement.imageId;
|
||||
if (id != null) {
|
||||
bytes = libraryBytes?[id];
|
||||
}
|
||||
bytes ??= signatureImageBytes; // fallback
|
||||
|
|
|
@ -65,7 +65,7 @@ class PdfController extends StateNotifier<PdfState> {
|
|||
void addPlacement({
|
||||
required int page,
|
||||
required Rect rect,
|
||||
SignatureAsset? asset,
|
||||
String? imageId = 'default.png',
|
||||
double rotationDeg = 0.0,
|
||||
}) {
|
||||
if (!state.loaded) return;
|
||||
|
@ -75,7 +75,7 @@ class PdfController extends StateNotifier<PdfState> {
|
|||
list.add(
|
||||
SignaturePlacement(
|
||||
rect: rect,
|
||||
asset: asset ?? SignatureAsset(id: '', bytes: Uint8List(0)),
|
||||
imageId: imageId,
|
||||
rotationDeg: rotationDeg,
|
||||
),
|
||||
);
|
||||
|
@ -165,11 +165,11 @@ class PdfController extends StateNotifier<PdfState> {
|
|||
|
||||
// NOTE: Programmatic reassignment of images has been removed.
|
||||
|
||||
// Convenience to get asset for a placement
|
||||
SignatureAsset? assetOfPlacement({required int page, required int index}) {
|
||||
// Convenience to get image name for a placement
|
||||
String? imageOfPlacement({required int page, required int index}) {
|
||||
final list = state.placementsByPage[page] ?? const [];
|
||||
if (index < 0 || index >= list.length) return null;
|
||||
return list[index].asset;
|
||||
return list[index].imageId;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
|
||||
import '../../../../data/model/model.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import '../../signature/view_model/signature_controller.dart';
|
||||
|
||||
class AdjustmentsPanel extends ConsumerWidget {
|
||||
const AdjustmentsPanel({super.key, required this.sig});
|
||||
|
|
|
@ -2,7 +2,7 @@ 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/repositories/signature_repository.dart';
|
||||
import '../../signature/view_model/signature_controller.dart';
|
||||
import 'adjustments_panel.dart';
|
||||
import '../../signature/widgets/rotated_signature_image.dart';
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import 'package:pdf_signature/l10n/app_localizations.dart';
|
|||
import 'package:pdfrx/pdfrx.dart';
|
||||
|
||||
import '../../../../data/services/export_providers.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '../../signature/view_model/signature_controller.dart';
|
||||
import '../view_model/pdf_controller.dart';
|
||||
import '../../signature/widgets/signature_drag_data.dart';
|
||||
import 'pdf_mock_continuous_list.dart';
|
||||
import 'pdf_page_overlays.dart';
|
||||
|
@ -340,11 +340,11 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
|
|||
final cx = (local.dx / size.width) * widget.pageSize.width;
|
||||
final cy = (local.dy / size.height) * widget.pageSize.height;
|
||||
final data = details.data;
|
||||
if (data is SignatureDragData && data.asset != null) {
|
||||
if (data is SignatureDragData && data.assetId != null) {
|
||||
// Set current overlay to use this asset
|
||||
ref
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageFromLibrary(asset: data.asset!);
|
||||
.setImageFromLibrary(assetId: data.assetId!);
|
||||
}
|
||||
ref.read(signatureProvider.notifier).placeAtCenter(Offset(cx, cy));
|
||||
ref
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import '../../signature/view_model/signature_controller.dart';
|
||||
import '../../../../data/model/model.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '../view_model/pdf_controller.dart';
|
||||
import 'signature_overlay.dart';
|
||||
|
||||
/// Builds all overlays for a given page: placed signatures and the active one.
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:pdfrx/pdfrx.dart';
|
||||
|
||||
import '../../../../data/services/export_providers.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '../view_model/pdf_controller.dart';
|
||||
|
||||
class PdfPagesOverview extends ConsumerWidget {
|
||||
const PdfPagesOverview({super.key});
|
||||
|
|
|
@ -10,9 +10,9 @@ import 'package:multi_split_view/multi_split_view.dart';
|
|||
|
||||
import '../../../../data/services/export_providers.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import '../../signature/view_model/signature_controller.dart';
|
||||
import '../view_model/pdf_controller.dart';
|
||||
import '../../signature/view_model/signature_library.dart';
|
||||
import 'draw_canvas.dart';
|
||||
import 'pdf_toolbar.dart';
|
||||
import 'pdf_page_area.dart';
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '../view_model/pdf_controller.dart';
|
||||
|
||||
class PdfToolbar extends ConsumerStatefulWidget {
|
||||
const PdfToolbar({
|
||||
|
|
|
@ -2,11 +2,10 @@ 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' as model;
|
||||
|
||||
import '../../../../data/services/export_providers.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import '../../signature/view_model/signature_controller.dart';
|
||||
import '../../signature/view_model/signature_library.dart';
|
||||
import 'image_editor_dialog.dart';
|
||||
import '../../signature/widgets/signature_card.dart';
|
||||
|
||||
|
@ -53,14 +52,14 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
|
|||
child: SignatureCard(
|
||||
key: ValueKey('sig_card_${a.id}'),
|
||||
asset:
|
||||
(sig.asset?.id == a.id)
|
||||
? model.SignatureAsset(
|
||||
(sig.assetId == a.id)
|
||||
? SignatureAsset(
|
||||
id: a.id,
|
||||
bytes: (processed ?? a.bytes),
|
||||
name: a.name,
|
||||
)
|
||||
: a,
|
||||
rotationDeg: (sig.asset?.id == a.id) ? sig.rotation : 0.0,
|
||||
rotationDeg: (sig.assetId == a.id) ? sig.rotation : 0.0,
|
||||
disabled: disabled,
|
||||
onDelete:
|
||||
() => ref
|
||||
|
@ -69,7 +68,7 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
|
|||
onAdjust: () async {
|
||||
ref
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageFromLibrary(asset: a);
|
||||
.setImageFromLibrary(assetId: a.id);
|
||||
if (!mounted) return;
|
||||
await showDialog(
|
||||
context: context,
|
||||
|
@ -80,7 +79,7 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
|
|||
// Never reassign placed signatures via tap; only set active overlay source
|
||||
ref
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageFromLibrary(asset: a);
|
||||
.setImageFromLibrary(assetId: a.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -97,11 +96,7 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
|
|||
bytes == null
|
||||
? Text(l.noSignatureLoaded)
|
||||
: SignatureCard(
|
||||
asset: model.SignatureAsset(
|
||||
id: '',
|
||||
bytes: bytes,
|
||||
name: '',
|
||||
),
|
||||
asset: SignatureAsset(id: '', bytes: bytes, name: ''),
|
||||
rotationDeg: sig.rotation,
|
||||
disabled: disabled,
|
||||
useCurrentBytesForDrag: true,
|
||||
|
@ -153,14 +148,9 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
|
|||
final id = ref
|
||||
.read(signatureLibraryProvider.notifier)
|
||||
.add(b, name: 'image');
|
||||
final asset = ref
|
||||
.read(signatureLibraryProvider.notifier)
|
||||
.byId(id);
|
||||
if (asset != null) {
|
||||
ref
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageFromLibrary(asset: asset);
|
||||
}
|
||||
ref
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageFromLibrary(assetId: id);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.image_outlined),
|
||||
|
@ -181,14 +171,9 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
|
|||
final id = ref
|
||||
.read(signatureLibraryProvider.notifier)
|
||||
.add(b, name: 'drawing');
|
||||
final asset = ref
|
||||
.read(signatureLibraryProvider.notifier)
|
||||
.byId(id);
|
||||
if (asset != null) {
|
||||
ref
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageFromLibrary(asset: asset);
|
||||
}
|
||||
ref
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageFromLibrary(assetId: id);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.gesture),
|
||||
|
|
|
@ -5,9 +5,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
|
||||
import '../../../../data/model/model.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import '../../signature/view_model/signature_controller.dart';
|
||||
import '../view_model/pdf_controller.dart';
|
||||
import '../../signature/view_model/signature_library.dart';
|
||||
import 'image_editor_dialog.dart';
|
||||
import '../../signature/widgets/rotated_signature_image.dart';
|
||||
|
||||
|
@ -245,8 +245,8 @@ class _SignatureImage extends ConsumerWidget {
|
|||
(placementList != null && placedIndex! < placementList.length)
|
||||
? placementList[placedIndex!]
|
||||
: null;
|
||||
final imgId = (placement?.asset)?.id;
|
||||
if (imgId != null && imgId.isNotEmpty) {
|
||||
final imgId = placement?.imageId;
|
||||
if (imgId != null) {
|
||||
final lib = ref.watch(signatureLibraryProvider);
|
||||
for (final a in lib) {
|
||||
if (a.id == imgId) {
|
||||
|
|
|
@ -7,7 +7,8 @@ import 'package:image/image.dart' as img;
|
|||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
|
||||
import '../../../../data/model/model.dart';
|
||||
import 'pdf_repository.dart';
|
||||
import '../../pdf/view_model/pdf_controller.dart';
|
||||
import 'signature_library.dart';
|
||||
|
||||
class SignatureController extends StateNotifier<SignatureState> {
|
||||
SignatureController() : super(SignatureState.initial());
|
||||
|
@ -138,7 +139,7 @@ class SignatureController extends StateNotifier<SignatureState> {
|
|||
}
|
||||
|
||||
void setImageBytes(Uint8List bytes) {
|
||||
state = state.copyWith(imageBytes: bytes, asset: null);
|
||||
state = state.copyWith(imageBytes: bytes, assetId: null);
|
||||
if (state.rect == null) {
|
||||
placeDefaultRect();
|
||||
}
|
||||
|
@ -147,8 +148,8 @@ class SignatureController extends StateNotifier<SignatureState> {
|
|||
}
|
||||
|
||||
// Select image from the shared signature library
|
||||
void setImageFromLibrary({required SignatureAsset asset}) {
|
||||
state = state.copyWith(asset: asset);
|
||||
void setImageFromLibrary({required String assetId}) {
|
||||
state = state.copyWith(assetId: assetId);
|
||||
if (state.rect == null) {
|
||||
placeDefaultRect();
|
||||
}
|
||||
|
@ -176,17 +177,18 @@ class SignatureController extends StateNotifier<SignatureState> {
|
|||
if (!pdf.loaded) return null;
|
||||
// Bind the processed image at placement time (so placed preview matches adjustments).
|
||||
// If processed bytes exist, always create a new asset for this placement.
|
||||
// Prefer reusing an existing library asset when the active overlay is
|
||||
// Prefer reusing an existing library asset id when the active overlay is
|
||||
// based on a library item. If there is no library asset, do NOT create
|
||||
// a new library card here — keep the placement's asset empty so the
|
||||
// a new library card here — keep the placement's image id empty so the
|
||||
// UI and exporter will fall back to using the processed/current bytes.
|
||||
String id = state.assetId ?? '';
|
||||
// Store as UI-space rect (consistent with export and rendering paths)
|
||||
ref
|
||||
.read(pdfProvider.notifier)
|
||||
.addPlacement(
|
||||
page: pdf.currentPage,
|
||||
rect: r,
|
||||
asset: state.asset,
|
||||
imageId: id,
|
||||
rotationDeg: state.rotation,
|
||||
);
|
||||
// Newly placed index is the last one on the page
|
||||
|
@ -210,14 +212,15 @@ class SignatureController extends StateNotifier<SignatureState> {
|
|||
if (r == null) return null;
|
||||
final pdf = container.read(pdfProvider);
|
||||
if (!pdf.loaded) return null;
|
||||
// Reuse existing library asset if present; otherwise leave empty so the
|
||||
// Reuse existing library id if present; otherwise leave empty so the
|
||||
// placement will reference the current bytes via fallback paths.
|
||||
String id = state.assetId ?? '';
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.addPlacement(
|
||||
page: pdf.currentPage,
|
||||
rect: r,
|
||||
asset: state.asset,
|
||||
imageId: id,
|
||||
rotationDeg: state.rotation,
|
||||
);
|
||||
final idx =
|
||||
|
@ -227,11 +230,9 @@ class SignatureController extends StateNotifier<SignatureState> {
|
|||
?.length ??
|
||||
1) -
|
||||
1;
|
||||
// Auto-select the newly placed item so the red box appears
|
||||
if (idx >= 0) {
|
||||
container.read(pdfProvider.notifier).selectPlacement(idx);
|
||||
}
|
||||
// Freeze editing: keep rect for preview but disable interaction
|
||||
state = state.copyWith(editingEnabled: false);
|
||||
return r;
|
||||
}
|
||||
|
@ -252,9 +253,7 @@ final signatureProvider =
|
|||
/// Returns null if no image is loaded. The output is a PNG to preserve alpha.
|
||||
final processedSignatureImageProvider = Provider<Uint8List?>((ref) {
|
||||
// Watch only the fields that affect pixel processing to avoid recompute on rotation.
|
||||
final SignatureAsset? asset = ref.watch(
|
||||
signatureProvider.select((s) => s.asset),
|
||||
);
|
||||
final String? assetId = ref.watch(signatureProvider.select((s) => s.assetId));
|
||||
final Uint8List? directBytes = ref.watch(
|
||||
signatureProvider.select((s) => s.imageBytes),
|
||||
);
|
||||
|
@ -270,8 +269,14 @@ final processedSignatureImageProvider = Provider<Uint8List?>((ref) {
|
|||
|
||||
// If active overlay is based on a library asset, pull its bytes
|
||||
Uint8List? bytes;
|
||||
if (asset != null) {
|
||||
bytes = asset.bytes;
|
||||
if (assetId != null) {
|
||||
final lib = ref.watch(signatureLibraryProvider);
|
||||
for (final a in lib) {
|
||||
if (a.id == assetId) {
|
||||
bytes = a.bytes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bytes = directBytes;
|
||||
}
|
|
@ -1,6 +1,13 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
|
||||
/// A simple library of signature images available to the user in the sidebar.
|
||||
class SignatureAsset {
|
||||
final String id; // unique id
|
||||
final Uint8List bytes;
|
||||
final String? name; // optional display name (e.g., filename)
|
||||
const SignatureAsset({required this.id, required this.bytes, this.name});
|
||||
}
|
||||
|
||||
class SignatureLibraryController extends StateNotifier<List<SignatureAsset>> {
|
||||
SignatureLibraryController() : super(const []);
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import '../view_model/signature_library.dart';
|
||||
import 'signature_drag_data.dart';
|
||||
import 'rotated_signature_image.dart';
|
||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
|
@ -142,7 +142,7 @@ class SignatureCard extends StatelessWidget {
|
|||
data:
|
||||
useCurrentBytesForDrag
|
||||
? const SignatureDragData()
|
||||
: SignatureDragData(asset: asset),
|
||||
: SignatureDragData(assetId: asset.id),
|
||||
feedback: Opacity(
|
||||
opacity: 0.9,
|
||||
child: ConstrainedBox(
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import 'package:pdf_signature/data/model/model.dart';
|
||||
|
||||
class SignatureDragData {
|
||||
final SignatureAsset? asset; // null means use current processed signature
|
||||
const SignatureDragData({this.asset});
|
||||
final String? assetId; // null means use current processed signature
|
||||
const SignatureDragData({this.assetId});
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ 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/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '../../signature/view_model/signature_controller.dart';
|
||||
import '../../pdf/view_model/pdf_controller.dart';
|
||||
// Settings dialog is provided via global AppBar in MyApp
|
||||
|
||||
// Abstraction to make drop handling testable without constructing
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
Feature: draw signature asset
|
||||
Feature: draw signature
|
||||
|
||||
Scenario: Draw with mouse or touch and place on page
|
||||
Given an empty signature canvas
|
||||
When the user draws strokes and confirms
|
||||
Then a signature asset is created
|
||||
And signature placement occurs on the selected page
|
||||
Then a signature image is created
|
||||
And it is placed on the selected page
|
||||
|
||||
Scenario: Clear and redraw
|
||||
Given a drawn signature exists in the canvas
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
Feature: geometrically adjust signature asset
|
||||
Feature: geometrically adjust signature picture
|
||||
|
||||
Scenario: Resize and move the signature within page bounds
|
||||
Given a signature asset is placed on the page
|
||||
Given a signature image is placed on the page
|
||||
When the user drags handles to resize and drags to reposition
|
||||
Then the size and position update in real time
|
||||
And the signature placement remains within the page area
|
||||
And the signature remains within the page area
|
||||
|
||||
Scenario: Rotate the signature
|
||||
Given a signature asset is placed on the page
|
||||
When the user uses rotate controls
|
||||
Then the signature placement rotates around its center in real time
|
||||
And resize to fit within bounding box
|
||||
Scenario: Lock aspect ratio while resizing
|
||||
Given a signature image is selected
|
||||
When the user enables aspect ratio lock and resizes
|
||||
Then the image scales proportionally
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
Feature: graphically adjust signature asset
|
||||
Feature: graphically adjust signature picture
|
||||
|
||||
Scenario: Remove background
|
||||
Given a signature asset is selected
|
||||
Given a signature image is selected
|
||||
When the user enables background removal
|
||||
Then near-white background becomes transparent in the preview
|
||||
And the user can apply the change
|
||||
|
||||
Scenario: Adjust contrast and brightness
|
||||
Given a signature asset is selected
|
||||
Given a signature image is selected
|
||||
When the user changes contrast and brightness controls
|
||||
Then the preview updates immediately
|
||||
And the user can apply or reset adjustments
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
Feature: load signature asset
|
||||
|
||||
Scenario Outline: Handle invalid or unsupported files
|
||||
Given the user selects "<file>"
|
||||
When the app attempts to load the asset
|
||||
Then the user is notified of the issue
|
||||
And the asset is not added to the document
|
||||
|
||||
Examples:
|
||||
| file |
|
||||
| 'corrupted.png' |
|
||||
| 'signature.bmp' |
|
||||
| 'empty.jpg' |
|
||||
|
||||
Scenario: Import a signature asset
|
||||
When the user chooses a image file as a signature asset
|
||||
Then the asset is loaded and shown as a signature asset
|
||||
|
||||
Scenario: Import a signature card
|
||||
When the user chooses a signature asset to created a signature card
|
||||
Then the asset is loaded and shown as a signature card
|
||||
|
||||
Scenario: Import a signature placement
|
||||
Given a created signature card
|
||||
When the user drags this signature card on the page of the document to place a signature placement
|
||||
Then a signature placement appears on the page based on the signature card
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
Feature: load signature picture
|
||||
|
||||
Scenario: Import a signature image
|
||||
Given a PDF page is selected for signing
|
||||
When the user chooses a signature image file
|
||||
Then the image is loaded and shown as a signature asset
|
||||
|
||||
Scenario Outline: Handle invalid or unsupported files
|
||||
Given the user selects "<file>"
|
||||
When the app attempts to load the image
|
||||
Then the user is notified of the issue
|
||||
And the image is not added to the document
|
||||
|
||||
Examples:
|
||||
| file |
|
||||
| 'corrupted.png' |
|
||||
| 'signature.bmp' |
|
||||
| 'empty.jpg' |
|
|
@ -1,9 +1,9 @@
|
|||
Feature: document browser
|
||||
Feature: PDF browser
|
||||
|
||||
Background:
|
||||
Given a sample multi-page document (5 pages) is available
|
||||
Given a sample multi-page PDF (5 pages) is available
|
||||
|
||||
Scenario: Open a document and navigate pages
|
||||
Scenario: Open a PDF and navigate pages
|
||||
When the user opens the document
|
||||
Then the first page is displayed
|
||||
And the user can move to the next or previous page
|
||||
|
@ -47,6 +47,6 @@ Feature: document browser
|
|||
Then the last page is displayed (page {5})
|
||||
And the page label shows "Page {5} of {5}"
|
||||
|
||||
Scenario: Go to is disabled when no document is loaded
|
||||
Scenario: Go to is disabled when no PDF is loaded
|
||||
Given no document is open
|
||||
Then the Go to input cannot be used
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
Feature: save signed document
|
||||
Feature: save signed PDF
|
||||
|
||||
Scenario: Export the signed document to a new file
|
||||
Given a document is open and contains at least one signature placement
|
||||
Given a PDF is open and contains at least one placed signature
|
||||
When the user saves/exports the document
|
||||
Then a new document file is saved at specified full path, location and file name
|
||||
And the signature placements appear on the corresponding page in the output
|
||||
Then a new PDF file is saved at specified full path, location and file name
|
||||
And the signatures appear on the corresponding page in the output
|
||||
And keep other unchanged content(pages) intact in the output
|
||||
|
||||
Scenario: Vector-accurate stamping into PDF page coordinates
|
||||
Given a signature placement is placed with a position and size relative to the page
|
||||
Given a signature is placed with a position and size relative to the page
|
||||
When the user saves/exports the document
|
||||
Then the signature placement is stamped at the exact PDF page coordinates and size
|
||||
Then the signature is stamped at the exact PDF page coordinates and size
|
||||
And the stamp remains crisp at any zoom level (not rasterized by the screen)
|
||||
And other page content remains vector and unaltered
|
||||
|
||||
Scenario: Prevent saving when nothing is placed
|
||||
Given a document is open with no signature placements placed
|
||||
Given a PDF is open with no signatures placed
|
||||
When the user attempts to save
|
||||
Then the user is notified there is nothing to save
|
||||
|
||||
Scenario: Loading sign when exporting/saving files
|
||||
Given a signature placement is placed with a position and size relative to the page
|
||||
Given a signature is placed with a position and size relative to the page
|
||||
When the user starts exporting the document
|
||||
And the export process is not yet finished
|
||||
Then the user is notified that the export is still in progress
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a created signature card
|
||||
Future<void> aCreatedSignatureCard(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
// Create a dummy signature asset
|
||||
final asset = SignatureAsset(
|
||||
id: 'test_card',
|
||||
bytes: Uint8List(100),
|
||||
name: 'Test Card',
|
||||
);
|
||||
container.read(signatureLibraryProvider.notifier).state = [asset];
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
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/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a document is open and contains at least one signature placement
|
||||
Future<void> aDocumentIsOpenAndContainsAtLeastOneSignaturePlacement(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.openPicked(path: 'test.pdf', pageCount: 5);
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.addPlacement(
|
||||
page: 1,
|
||||
rect: Rect.fromLTWH(10, 10, 100, 50),
|
||||
asset: SignatureAsset(id: 'sig.png', bytes: Uint8List(0)),
|
||||
);
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
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/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.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),
|
||||
asset: SignatureAsset(id: 'sig1.png', bytes: Uint8List(0)),
|
||||
);
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.addPlacement(
|
||||
page: 2,
|
||||
rect: Rect.fromLTWH(20, 20, 100, 50),
|
||||
asset: SignatureAsset(id: 'sig2.png', bytes: Uint8List(0)),
|
||||
);
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.addPlacement(
|
||||
page: 3,
|
||||
rect: Rect.fromLTWH(30, 30, 100, 50),
|
||||
asset: SignatureAsset(id: 'sig3.png', bytes: Uint8List(0)),
|
||||
);
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a document is open with no signature placements placed
|
||||
Future<void> aDocumentIsOpenWithNoSignaturePlacementsPlaced(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.openPicked(path: 'empty.pdf', pageCount: 5);
|
||||
// No placements added
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// 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).setSignedPage(1);
|
||||
container.read(pdfProvider.notifier).jumpTo(1);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a drawn signature exists in the canvas
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// 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);
|
||||
}
|
|
@ -1,15 +1,13 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a sample multi-page document (5 pages) is available
|
||||
Future<void> aSampleMultipageDocument5PagesIsAvailable(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
/// Usage: a multi-page PDF is open
|
||||
Future<void> aMultipagePdfIsOpen(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.openPicked(path: 'sample.pdf', pageCount: 5);
|
||||
.openPicked(path: 'sample.pdf', pageCount: 10);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a new document file is saved at specified full path, location and file name
|
||||
Future<void> aNewDocumentFileIsSavedAtSpecifiedFullPathLocationAndFileName(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
// Verify that export bytes were generated
|
||||
expect(
|
||||
TestWorld.lastExportBytes,
|
||||
isNotNull,
|
||||
reason: 'Export bytes should be generated after save',
|
||||
);
|
||||
|
||||
// Simulate a saved path (in a real implementation this would come from file picker)
|
||||
TestWorld.lastSavedPath =
|
||||
TestWorld.lastSavedPath ?? '/tmp/signed_document.pdf';
|
||||
|
||||
expect(
|
||||
TestWorld.lastSavedPath,
|
||||
isNotNull,
|
||||
reason: 'A save path should be specified',
|
||||
);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
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]));
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
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();
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
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();
|
||||
}
|
|
@ -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: a PDF page is selected for signing
|
||||
Future<void> aPdfPageIsSelectedForSigning(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);
|
||||
}
|
|
@ -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: a sample multi-page PDF (5 pages) is available
|
||||
Future<void> aSampleMultipagePdf5PagesIsAvailable(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);
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
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/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a signature asset is created
|
||||
Future<void> aSignatureAssetIsCreated(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
|
||||
// Ensure PDF is open
|
||||
if (!container.read(pdfProvider).loaded) {
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.openPicked(path: 'mock.pdf', pageCount: 5);
|
||||
}
|
||||
|
||||
// Create a dummy signature asset
|
||||
final asset = SignatureAsset(
|
||||
id: 'test_asset',
|
||||
bytes: Uint8List(100),
|
||||
name: 'Test Asset',
|
||||
);
|
||||
container.read(signatureLibraryProvider.notifier).state = [asset];
|
||||
|
||||
// Place it on the current page
|
||||
final pdf = container.read(pdfProvider);
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.addPlacement(
|
||||
page: pdf.currentPage,
|
||||
rect: Rect.fromLTWH(50, 50, 100, 50),
|
||||
asset: asset,
|
||||
);
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a signature asset is loaded or drawn
|
||||
Future<void> aSignatureAssetIsLoadedOrDrawn(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');
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
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/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a signature asset is placed on the page
|
||||
Future<void> aSignatureAssetIsPlacedOnThePage(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
|
||||
// Ensure PDF is open
|
||||
if (!container.read(pdfProvider).loaded) {
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.openPicked(path: 'mock.pdf', pageCount: 5);
|
||||
}
|
||||
|
||||
// Get or create an asset
|
||||
var library = container.read(signatureLibraryProvider);
|
||||
SignatureAsset asset;
|
||||
if (library.isNotEmpty) {
|
||||
asset = library.first;
|
||||
} else {
|
||||
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
|
||||
final id = container
|
||||
.read(signatureLibraryProvider.notifier)
|
||||
.add(bytes, name: 'test.png');
|
||||
asset = container
|
||||
.read(signatureLibraryProvider)
|
||||
.firstWhere((a) => a.id == id);
|
||||
}
|
||||
|
||||
// Place it on the current page
|
||||
final pdf = container.read(pdfProvider);
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.addPlacement(
|
||||
page: pdf.currentPage,
|
||||
rect: Rect.fromLTWH(50, 50, 100, 50),
|
||||
asset: asset,
|
||||
);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a signature asset is selected
|
||||
Future<void> aSignatureAssetIsSelected(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
var library = container.read(signatureLibraryProvider);
|
||||
|
||||
// If library is empty, add a dummy asset
|
||||
if (library.isEmpty) {
|
||||
final asset = SignatureAsset(
|
||||
id: 'selected_asset',
|
||||
bytes: Uint8List(100),
|
||||
name: 'Selected Asset',
|
||||
);
|
||||
container.read(signatureLibraryProvider.notifier).state = [asset];
|
||||
// Re-read the library
|
||||
library = container.read(signatureLibraryProvider);
|
||||
}
|
||||
|
||||
expect(
|
||||
library.isNotEmpty,
|
||||
true,
|
||||
reason: 'Library should have at least one asset',
|
||||
);
|
||||
// For test purposes, we consider the first asset as selected
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.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');
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
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);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
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]));
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
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 placed on the page
|
||||
Future<void> aSignatureImageIsPlacedOnThePage(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
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]));
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
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;
|
||||
});
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
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');
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
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]));
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a signature placement appears on the page based on the signature card
|
||||
Future<void> aSignaturePlacementAppearsOnThePageBasedOnTheSignatureCard(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container!;
|
||||
final pdf = container.read(pdfProvider);
|
||||
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
|
||||
expect(
|
||||
placements.isNotEmpty,
|
||||
true,
|
||||
reason: 'A signature placement should appear on the page',
|
||||
);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
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/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// 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)
|
||||
.addPlacement(
|
||||
page: page,
|
||||
rect: Rect.fromLTWH(20, 20, 100, 50),
|
||||
asset: SignatureAsset(id: 'test.png', bytes: Uint8List(0)),
|
||||
);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
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/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.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),
|
||||
asset: SignatureAsset(id: 'test.png', bytes: Uint8List(0)),
|
||||
);
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: adjusting one instance does not affect the others
|
||||
|
@ -15,7 +14,7 @@ Future<void> adjustingOneInstanceDoesNotAffectTheOthers(
|
|||
container.read(pdfProvider.notifier).removePlacement(page: 2, index: 0);
|
||||
container
|
||||
.read(pdfProvider.notifier)
|
||||
.addPlacement(page: 2, rect: modified, asset: before[0].asset);
|
||||
.addPlacement(page: 2, rect: modified, imageId: before[0].imageId);
|
||||
final after = container.read(pdfProvider.notifier).placementsOn(2);
|
||||
expect(after.any((p) => p.rect == before[1].rect), isTrue);
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: adjusting one of the signature placements does not affect the others
|
||||
Future<void> adjustingOneOfTheSignaturePlacementsDoesNotAffectTheOthers(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container!;
|
||||
final pdf = container.read(pdfProvider);
|
||||
final placements =
|
||||
pdf.placementsByPage.values.expand((list) => list).toList();
|
||||
|
||||
// All placements should have the same asset ID (reusing the same asset)
|
||||
final assetIds = placements.map((p) => p.asset.id).toSet();
|
||||
expect(assetIds.length, 1);
|
||||
|
||||
// All should have default rotation (0.0) since none were adjusted
|
||||
final rotations = placements.map((p) => p.rotationDeg).toSet();
|
||||
expect(rotations.length, 1);
|
||||
expect(rotations.first, 0.0);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.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));
|
||||
}
|
|
@ -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: 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);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: an empty signature canvas
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.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);
|
||||
}
|
|
@ -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: 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);
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import 'dart:ui';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/data/model/model.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: dragging or resizing one does not change the other
|
||||
|
@ -21,7 +20,7 @@ Future<void> draggingOrResizingOneDoesNotChangeTheOther(
|
|||
.addPlacement(
|
||||
page: 1,
|
||||
rect: changed,
|
||||
asset: list[1].asset,
|
||||
imageId: list[1].imageId,
|
||||
rotationDeg: list[1].rotationDeg,
|
||||
);
|
||||
final after = container.read(pdfProvider.notifier).placementsOn(1);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
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));
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.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));
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: identical signature instances appear in each location
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.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.asset.id).toSet();
|
||||
expect(assetIds.length, 1); // All the same
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
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);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: multiple strokes were drawn
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'dart:typed_data';
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: near-white background becomes transparent in the preview
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
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);
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.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, 2); // Started with 3, removed 1, should have 2
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: page {5} becomes visible in the scroll area
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: page {1} is displayed
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: resize to fit within bounding box
|
||||
Future<void> resizeToFitWithinBoundingBox(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final pdf = container.read(pdfProvider);
|
||||
|
||||
if (pdf.selectedPlacementIndex != null) {
|
||||
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
|
||||
if (pdf.selectedPlacementIndex! < placements.length) {
|
||||
final placement = placements[pdf.selectedPlacementIndex!];
|
||||
// Assume page size is 800x600 for testing
|
||||
const pageWidth = 800.0;
|
||||
const pageHeight = 600.0;
|
||||
|
||||
expect(placement.rect.left, greaterThanOrEqualTo(0));
|
||||
expect(placement.rect.top, greaterThanOrEqualTo(0));
|
||||
expect(placement.rect.right, lessThanOrEqualTo(pageWidth));
|
||||
expect(placement.rect.bottom, lessThanOrEqualTo(pageHeight));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature placement occurs on the selected page
|
||||
Future<void> signaturePlacementOccursOnTheSelectedPage(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
final pdf = container.read(pdfProvider);
|
||||
|
||||
// Check that there's at least one placement on the current page
|
||||
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
|
||||
expect(placements.isNotEmpty, true);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the app attempts to load the asset
|
||||
Future<void> theAppAttemptsToLoadTheAsset(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
// Simulate attempting to load an asset - for now just ensure library is accessible
|
||||
final library = container.read(signatureLibraryProvider);
|
||||
expect(library, isNotNull);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
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.
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the asset is loaded and shown as a signature asset
|
||||
Future<void> theAssetIsLoadedAndShownAsASignatureAsset(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container!;
|
||||
final library = container.read(signatureLibraryProvider);
|
||||
expect(
|
||||
library.isNotEmpty,
|
||||
true,
|
||||
reason: 'Asset should be loaded and shown in library',
|
||||
);
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the asset is loaded and shown as a signature card
|
||||
Future<void> theAssetIsLoadedAndShownAsASignatureCard(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container!;
|
||||
final library = container.read(signatureLibraryProvider);
|
||||
expect(
|
||||
library.isNotEmpty,
|
||||
true,
|
||||
reason: 'Asset should be loaded and shown as a card',
|
||||
);
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the asset is not added to the document
|
||||
Future<void> theAssetIsNotAddedToTheDocument(WidgetTester tester) async {
|
||||
final container = TestWorld.container!;
|
||||
final library = container.read(signatureLibraryProvider);
|
||||
expect(
|
||||
library.isEmpty,
|
||||
true,
|
||||
reason: 'Invalid asset should not be added to library',
|
||||
);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the canvas becomes blank
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the document is open
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the first page is displayed
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the Go to input cannot be used
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
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);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
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);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
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);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the last page is displayed (page {5})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the last stroke is removed
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the left pages overview highlights page {5}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the other signature placements remain unchanged
|
||||
Future<void> theOtherSignaturePlacementsRemainUnchanged(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container!;
|
||||
final pdf = container.read(pdfProvider);
|
||||
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
|
||||
expect(placements.length, 2); // Should have 2 remaining after deleting 1
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
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);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the page label shows "Page {5} of {5}"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/data/repositories/signature_repository.dart';
|
||||
import 'package:pdf_signature/ui/features/signature/view_model/signature_controller.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the preview updates immediately
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
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));
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue