chore: adopt bdd_widget_test for BDD
This commit is contained in:
parent
3551cdf274
commit
60630c6ddd
|
@ -10,6 +10,7 @@ checkout [`docs/FRs.md`](docs/FRs.md)
|
|||
|
||||
```bash
|
||||
flutter pub get
|
||||
# flutter run build_runner build --delete-conflicting-outputs
|
||||
|
||||
# run the app
|
||||
flutter run
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# meta archietecture
|
||||
|
||||
* [MVVM](https://docs.flutter.dev/app-architecture/guide)
|
||||
|
|
@ -2,126 +2,5 @@
|
|||
|
||||
Use cases are derived from `FRs.md` (user stories) and `meta-arch.md`. Each Feature name matches the corresponding user story; scenarios focus on observable behavior without restating story details.
|
||||
|
||||
```gherkin
|
||||
Feature: PDF browser
|
||||
|
||||
Scenario: Open a PDF and navigate pages
|
||||
Given a PDF document is available
|
||||
When the user opens the document
|
||||
Then the first page is displayed
|
||||
And the user can move to the next or previous page
|
||||
|
||||
Scenario: Jump to a specific page
|
||||
Given a multi-page PDF is open
|
||||
When the user selects a specific page number
|
||||
Then that page is displayed
|
||||
|
||||
Scenario: Select a page for signing
|
||||
Given a PDF is open
|
||||
When the user marks the current page for signing
|
||||
Then the page is set as the signature target
|
||||
```
|
||||
|
||||
```gherkin
|
||||
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 |
|
||||
```
|
||||
|
||||
```gherkin
|
||||
Feature: geometrically adjust signature picture
|
||||
|
||||
Scenario: Resize and move the signature within page bounds
|
||||
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 remains within the page area
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
```gherkin
|
||||
Feature: graphically adjust signature picture
|
||||
|
||||
Scenario: Remove background
|
||||
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 image is selected
|
||||
When the user changes contrast and brightness controls
|
||||
Then the preview updates immediately
|
||||
And the user can apply or reset adjustments
|
||||
```
|
||||
|
||||
```gherkin
|
||||
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 image is created
|
||||
And it is placed on the selected page
|
||||
|
||||
Scenario: Clear and redraw
|
||||
Given a drawn signature exists in the canvas
|
||||
When the user clears the canvas
|
||||
Then the canvas becomes blank
|
||||
|
||||
Scenario: Undo the last stroke
|
||||
Given multiple strokes were drawn
|
||||
When the user chooses undo
|
||||
Then the last stroke is removed
|
||||
```
|
||||
|
||||
```gherkin
|
||||
Feature: save signed PDF
|
||||
|
||||
Scenario: Export the signed document to a new file
|
||||
Given a PDF is open and contains at least one placed signature
|
||||
When the user saves/exports the document
|
||||
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 is placed with a position and size relative to the page
|
||||
When the user saves/exports the document
|
||||
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 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 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
|
||||
And the user cannot edit the document
|
||||
|
||||
```
|
||||
The Gherkin scenarios live in runnable BDD feature files under `test/features/`.
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ dependencies:
|
|||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.4.12
|
||||
bdd_widget_test: ^2.0.1
|
||||
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
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 image is created
|
||||
And it is placed on the selected page
|
||||
|
||||
Scenario: Clear and redraw
|
||||
Given a drawn signature exists in the canvas
|
||||
When the user clears the canvas
|
||||
Then the canvas becomes blank
|
||||
|
||||
Scenario: Undo the last stroke
|
||||
Given multiple strokes were drawn
|
||||
When the user chooses undo
|
||||
Then the last stroke is removed
|
|
@ -0,0 +1,38 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import './step/an_empty_signature_canvas.dart';
|
||||
import './step/the_user_draws_strokes_and_confirms.dart';
|
||||
import './step/a_signature_image_is_created.dart';
|
||||
import './step/it_is_placed_on_the_selected_page.dart';
|
||||
import './step/a_drawn_signature_exists_in_the_canvas.dart';
|
||||
import './step/the_user_clears_the_canvas.dart';
|
||||
import './step/the_canvas_becomes_blank.dart';
|
||||
import './step/multiple_strokes_were_drawn.dart';
|
||||
import './step/the_user_chooses_undo.dart';
|
||||
import './step/the_last_stroke_is_removed.dart';
|
||||
|
||||
void main() {
|
||||
group('''draw signature''', () {
|
||||
testWidgets('''Draw with mouse or touch and place on page''',
|
||||
(tester) async {
|
||||
await anEmptySignatureCanvas(tester);
|
||||
await theUserDrawsStrokesAndConfirms(tester);
|
||||
await aSignatureImageIsCreated(tester);
|
||||
await itIsPlacedOnTheSelectedPage(tester);
|
||||
});
|
||||
testWidgets('''Clear and redraw''', (tester) async {
|
||||
await aDrawnSignatureExistsInTheCanvas(tester);
|
||||
await theUserClearsTheCanvas(tester);
|
||||
await theCanvasBecomesBlank(tester);
|
||||
});
|
||||
testWidgets('''Undo the last stroke''', (tester) async {
|
||||
await multipleStrokesWereDrawn(tester);
|
||||
await theUserChoosesUndo(tester);
|
||||
await theLastStrokeIsRemoved(tester);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
Feature: geometrically adjust signature picture
|
||||
|
||||
Scenario: Resize and move the signature within page bounds
|
||||
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 remains within the page area
|
||||
|
||||
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
|
|
@ -0,0 +1,30 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import './step/a_signature_image_is_placed_on_the_page.dart';
|
||||
import './step/the_user_drags_handles_to_resize_and_drags_to_reposition.dart';
|
||||
import './step/the_size_and_position_update_in_real_time.dart';
|
||||
import './step/the_signature_remains_within_the_page_area.dart';
|
||||
import './step/a_signature_image_is_selected.dart';
|
||||
import './step/the_user_enables_aspect_ratio_lock_and_resizes.dart';
|
||||
import './step/the_image_scales_proportionally.dart';
|
||||
|
||||
void main() {
|
||||
group('''geometrically adjust signature picture''', () {
|
||||
testWidgets('''Resize and move the signature within page bounds''',
|
||||
(tester) async {
|
||||
await aSignatureImageIsPlacedOnThePage(tester);
|
||||
await theUserDragsHandlesToResizeAndDragsToReposition(tester);
|
||||
await theSizeAndPositionUpdateInRealTime(tester);
|
||||
await theSignatureRemainsWithinThePageArea(tester);
|
||||
});
|
||||
testWidgets('''Lock aspect ratio while resizing''', (tester) async {
|
||||
await aSignatureImageIsSelected(tester);
|
||||
await theUserEnablesAspectRatioLockAndResizes(tester);
|
||||
await theImageScalesProportionally(tester);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
Feature: graphically adjust signature picture
|
||||
|
||||
Scenario: Remove background
|
||||
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 image is selected
|
||||
When the user changes contrast and brightness controls
|
||||
Then the preview updates immediately
|
||||
And the user can apply or reset adjustments
|
|
@ -0,0 +1,30 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import './step/a_signature_image_is_selected.dart';
|
||||
import './step/the_user_enables_background_removal.dart';
|
||||
import './step/nearwhite_background_becomes_transparent_in_the_preview.dart';
|
||||
import './step/the_user_can_apply_the_change.dart';
|
||||
import './step/the_user_changes_contrast_and_brightness_controls.dart';
|
||||
import './step/the_preview_updates_immediately.dart';
|
||||
import './step/the_user_can_apply_or_reset_adjustments.dart';
|
||||
|
||||
void main() {
|
||||
group('''graphically adjust signature picture''', () {
|
||||
testWidgets('''Remove background''', (tester) async {
|
||||
await aSignatureImageIsSelected(tester);
|
||||
await theUserEnablesBackgroundRemoval(tester);
|
||||
await nearwhiteBackgroundBecomesTransparentInThePreview(tester);
|
||||
await theUserCanApplyTheChange(tester);
|
||||
});
|
||||
testWidgets('''Adjust contrast and brightness''', (tester) async {
|
||||
await aSignatureImageIsSelected(tester);
|
||||
await theUserChangesContrastAndBrightnessControls(tester);
|
||||
await thePreviewUpdatesImmediately(tester);
|
||||
await theUserCanApplyOrResetAdjustments(tester);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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 |
|
|
@ -0,0 +1,51 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import './step/a_pdf_page_is_selected_for_signing.dart';
|
||||
import './step/the_user_chooses_a_signature_image_file.dart';
|
||||
import './step/the_image_is_loaded_and_shown_as_a_signature_asset.dart';
|
||||
import './step/the_user_selects.dart';
|
||||
import './step/the_app_attempts_to_load_the_image.dart';
|
||||
import './step/the_user_is_notified_of_the_issue.dart';
|
||||
import './step/the_image_is_not_added_to_the_document.dart';
|
||||
import './step/_tokens.dart';
|
||||
|
||||
void main() {
|
||||
group('''load signature picture''', () {
|
||||
testWidgets('''Import a signature image''', (tester) async {
|
||||
await aPdfPageIsSelectedForSigning(tester);
|
||||
await theUserChoosesASignatureImageFile(tester);
|
||||
await theImageIsLoadedAndShownAsASignatureAsset(tester);
|
||||
});
|
||||
testWidgets(
|
||||
'''Outline: Handle invalid or unsupported files (corrupted.png)''',
|
||||
(tester) async {
|
||||
await theUserSelects(tester, corrupted.png);
|
||||
await theAppAttemptsToLoadTheImage(tester);
|
||||
await theUserIsNotifiedOfTheIssue(tester);
|
||||
await theImageIsNotAddedToTheDocument(tester);
|
||||
},
|
||||
);
|
||||
testWidgets(
|
||||
'''Outline: Handle invalid or unsupported files (signature.bmp)''',
|
||||
(tester) async {
|
||||
await theUserSelects(tester, signature.bmp);
|
||||
await theAppAttemptsToLoadTheImage(tester);
|
||||
await theUserIsNotifiedOfTheIssue(tester);
|
||||
await theImageIsNotAddedToTheDocument(tester);
|
||||
},
|
||||
);
|
||||
testWidgets(
|
||||
'''Outline: Handle invalid or unsupported files (empty.jpg)''',
|
||||
(tester) async {
|
||||
await theUserSelects(tester, empty.jpg);
|
||||
await theAppAttemptsToLoadTheImage(tester);
|
||||
await theUserIsNotifiedOfTheIssue(tester);
|
||||
await theImageIsNotAddedToTheDocument(tester);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
Feature: PDF browser
|
||||
|
||||
Scenario: Open a PDF and navigate pages
|
||||
Given a PDF document is available
|
||||
When the user opens the document
|
||||
Then the first page is displayed
|
||||
And the user can move to the next or previous page
|
||||
|
||||
Scenario: Jump to a specific page
|
||||
Given a multi-page PDF is open
|
||||
When the user selects a specific page number
|
||||
Then that page is displayed
|
|
@ -0,0 +1,29 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import './step/a_pdf_document_is_available.dart';
|
||||
import './step/the_user_opens_the_document.dart';
|
||||
import './step/the_first_page_is_displayed.dart';
|
||||
import './step/the_user_can_move_to_the_next_or_previous_page.dart';
|
||||
import './step/a_multipage_pdf_is_open.dart';
|
||||
import './step/the_user_selects_a_specific_page_number.dart';
|
||||
import './step/that_page_is_displayed.dart';
|
||||
|
||||
void main() {
|
||||
group('''PDF browser''', () {
|
||||
testWidgets('''Open a PDF and navigate pages''', (tester) async {
|
||||
await aPdfDocumentIsAvailable(tester);
|
||||
await theUserOpensTheDocument(tester);
|
||||
await theFirstPageIsDisplayed(tester);
|
||||
await theUserCanMoveToTheNextOrPreviousPage(tester);
|
||||
});
|
||||
testWidgets('''Jump to a specific page''', (tester) async {
|
||||
await aMultipagePdfIsOpen(tester);
|
||||
await theUserSelectsASpecificPageNumber(tester);
|
||||
await thatPageIsDisplayed(tester);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
Feature: PDF state logic
|
||||
|
||||
Scenario: openPicked loads document and initializes state
|
||||
Given a new provider container
|
||||
When I openPicked with path {'test.pdf'} and pageCount {7}
|
||||
Then pdf state is loaded {true}
|
||||
And pdf picked path is {'test.pdf'}
|
||||
And pdf page count is {7}
|
||||
And pdf current page is {1}
|
||||
And pdf marked for signing is {false}
|
||||
|
||||
Scenario: jumpTo clamps within page boundaries
|
||||
Given a new provider container
|
||||
And a pdf is open with path {'test.pdf'} and pageCount {5}
|
||||
When I jumpTo {10}
|
||||
Then pdf current page is {5}
|
||||
When I jumpTo {0}
|
||||
Then pdf current page is {1}
|
||||
When I jumpTo {3}
|
||||
Then pdf current page is {3}
|
||||
|
||||
Scenario: setPageCount updates count without toggling other flags
|
||||
Given a new provider container
|
||||
And a pdf is open with path {'test.pdf'} and pageCount {2}
|
||||
When I toggle mark
|
||||
And I set page count {9}
|
||||
Then pdf page count is {9}
|
||||
And pdf state is loaded {true}
|
||||
And pdf marked for signing is {true}
|
|
@ -0,0 +1,52 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import './step/a_new_provider_container.dart';
|
||||
import './step/i_openpicked_with_path_and_pagecount.dart';
|
||||
import './step/pdf_state_is_loaded.dart';
|
||||
import './step/pdf_picked_path_is.dart';
|
||||
import './step/pdf_page_count_is.dart';
|
||||
import './step/pdf_current_page_is.dart';
|
||||
import './step/pdf_marked_for_signing_is.dart';
|
||||
import './step/a_pdf_is_open_with_path_and_pagecount.dart';
|
||||
import './step/i_jumpto.dart';
|
||||
import './step/i_toggle_mark.dart';
|
||||
import './step/i_set_page_count.dart';
|
||||
|
||||
void main() {
|
||||
group('''PDF state logic''', () {
|
||||
testWidgets('''openPicked loads document and initializes state''',
|
||||
(tester) async {
|
||||
await aNewProviderContainer(tester);
|
||||
await iOpenpickedWithPathAndPagecount(tester, 'test.pdf', 7);
|
||||
await pdfStateIsLoaded(tester, true);
|
||||
await pdfPickedPathIs(tester, 'test.pdf');
|
||||
await pdfPageCountIs(tester, 7);
|
||||
await pdfCurrentPageIs(tester, 1);
|
||||
await pdfMarkedForSigningIs(tester, false);
|
||||
});
|
||||
testWidgets('''jumpTo clamps within page boundaries''', (tester) async {
|
||||
await aNewProviderContainer(tester);
|
||||
await aPdfIsOpenWithPathAndPagecount(tester, 'test.pdf', 5);
|
||||
await iJumpto(tester, 10);
|
||||
await pdfCurrentPageIs(tester, 5);
|
||||
await iJumpto(tester, 0);
|
||||
await pdfCurrentPageIs(tester, 1);
|
||||
await iJumpto(tester, 3);
|
||||
await pdfCurrentPageIs(tester, 3);
|
||||
});
|
||||
testWidgets('''setPageCount updates count without toggling other flags''',
|
||||
(tester) async {
|
||||
await aNewProviderContainer(tester);
|
||||
await aPdfIsOpenWithPathAndPagecount(tester, 'test.pdf', 2);
|
||||
await iToggleMark(tester);
|
||||
await iSetPageCount(tester, 9);
|
||||
await pdfPageCountIs(tester, 9);
|
||||
await pdfStateIsLoaded(tester, true);
|
||||
await pdfMarkedForSigningIs(tester, true);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
Feature: save signed PDF
|
||||
|
||||
Scenario: Export the signed document to a new file
|
||||
Given a PDF is open and contains at least one placed signature
|
||||
When the user saves/exports the document
|
||||
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 is placed with a position and size relative to the page
|
||||
When the user saves/exports the document
|
||||
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 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 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
|
||||
And the user cannot edit the document
|
|
@ -0,0 +1,71 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import './step/a_pdf_is_open_and_contains_at_least_one_placed_signature.dart';
|
||||
import './step/the_user_savesexports_the_document.dart';
|
||||
import './step/a_new_pdf_file_is_saved_at_specified_full_path_location_and_file_name.dart';
|
||||
import './step/the_signatures_appear_on_the_corresponding_page_in_the_output.dart';
|
||||
import './step/keep_other_unchanged_contentpages_intact_in_the_output.dart';
|
||||
import './step/a_signature_is_placed_with_a_position_and_size_relative_to_the_page.dart';
|
||||
import './step/the_signature_is_stamped_at_the_exact_pdf_page_coordinates_and_size.dart';
|
||||
import './step/the_stamp_remains_crisp_at_any_zoom_level_not_rasterized_by_the_screen.dart';
|
||||
import './step/other_page_content_remains_vector_and_unaltered.dart';
|
||||
import './step/a_pdf_is_open_with_no_signatures_placed.dart';
|
||||
import './step/the_user_attempts_to_save.dart';
|
||||
import './step/the_user_is_notified_there_is_nothing_to_save.dart';
|
||||
import './step/the_user_starts_exporting_the_document.dart';
|
||||
import './step/the_export_process_is_not_yet_finished.dart';
|
||||
import './step/the_user_is_notified_that_the_export_is_still_in_progress.dart';
|
||||
import './step/the_user_cannot_edit_the_document.dart';
|
||||
|
||||
void main() {
|
||||
group('''save signed PDF''', () {
|
||||
testWidgets(
|
||||
'''Export the signed document to a new file''',
|
||||
(tester) async {
|
||||
await aPdfIsOpenAndContainsAtLeastOnePlacedSignature(tester);
|
||||
await theUserSavesexportsTheDocument(tester);
|
||||
await aNewPdfFileIsSavedAtSpecifiedFullPathLocationAndFileName(tester);
|
||||
await theSignaturesAppearOnTheCorrespondingPageInTheOutput(tester);
|
||||
await keepOtherUnchangedContentpagesIntactInTheOutput(tester);
|
||||
},
|
||||
timeout: const Timeout(Duration(seconds: 30)),
|
||||
);
|
||||
testWidgets(
|
||||
'''Vector-accurate stamping into PDF page coordinates''',
|
||||
(tester) async {
|
||||
await aSignatureIsPlacedWithAPositionAndSizeRelativeToThePage(tester);
|
||||
await theUserSavesexportsTheDocument(tester);
|
||||
await theSignatureIsStampedAtTheExactPdfPageCoordinatesAndSize(tester);
|
||||
await theStampRemainsCrispAtAnyZoomLevelNotRasterizedByTheScreen(
|
||||
tester,
|
||||
);
|
||||
await otherPageContentRemainsVectorAndUnaltered(tester);
|
||||
},
|
||||
timeout: const Timeout(Duration(seconds: 30)),
|
||||
);
|
||||
testWidgets(
|
||||
'''Prevent saving when nothing is placed''',
|
||||
(tester) async {
|
||||
await aPdfIsOpenWithNoSignaturesPlaced(tester);
|
||||
await theUserAttemptsToSave(tester);
|
||||
await theUserIsNotifiedThereIsNothingToSave(tester);
|
||||
},
|
||||
timeout: const Timeout(Duration(seconds: 30)),
|
||||
);
|
||||
testWidgets(
|
||||
'''Loading sign when exporting/saving files''',
|
||||
(tester) async {
|
||||
await aSignatureIsPlacedWithAPositionAndSizeRelativeToThePage(tester);
|
||||
await theUserStartsExportingTheDocument(tester);
|
||||
await theExportProcessIsNotYetFinished(tester);
|
||||
await theUserIsNotifiedThatTheExportIsStillInProgress(tester);
|
||||
await theUserCannotEditTheDocument(tester);
|
||||
},
|
||||
timeout: const Timeout(Duration(seconds: 30)),
|
||||
);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
Feature: Signature state logic
|
||||
|
||||
Scenario: placeDefaultRect centers a reasonable default rect
|
||||
Given a new provider container
|
||||
Then signature rect is null
|
||||
When I place default signature rect
|
||||
Then signature rect left >= {0}
|
||||
And signature rect top >= {0}
|
||||
And signature rect right <= {400}
|
||||
And signature rect bottom <= {560}
|
||||
And signature rect width > {50}
|
||||
And signature rect height > {20}
|
||||
|
||||
Scenario: drag clamps to canvas bounds
|
||||
Given a new provider container
|
||||
And a default signature rect is placed
|
||||
When I drag signature by {Offset(10000, -10000)}
|
||||
Then signature rect left >= {0}
|
||||
And signature rect top >= {0}
|
||||
And signature rect right <= {400}
|
||||
And signature rect bottom <= {560}
|
||||
And signature rect moved from center
|
||||
|
||||
Scenario: resize respects aspect lock and clamps
|
||||
Given a new provider container
|
||||
And a default signature rect is placed
|
||||
And aspect lock is {true}
|
||||
When I resize signature by {Offset(1000, 1000)}
|
||||
Then signature aspect ratio is preserved within {0.05}
|
||||
And signature rect left >= {0}
|
||||
And signature rect top >= {0}
|
||||
And signature rect right <= {400}
|
||||
And signature rect bottom <= {560}
|
||||
|
||||
Scenario: setImageBytes ensures a rect exists for display
|
||||
Given a new provider container
|
||||
Then signature rect is null
|
||||
When I set tiny signature image bytes
|
||||
Then signature image bytes is not null
|
||||
And signature rect is not null
|
|
@ -0,0 +1,70 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import './step/a_new_provider_container.dart';
|
||||
import './step/signature_rect_is_null.dart';
|
||||
import './step/i_place_default_signature_rect.dart';
|
||||
import './step/signature_rect_left.dart';
|
||||
import './step/signature_rect_top.dart';
|
||||
import './step/signature_rect_right.dart';
|
||||
import './step/signature_rect_bottom.dart';
|
||||
import './step/signature_rect_width.dart';
|
||||
import './step/signature_rect_height.dart';
|
||||
import './step/a_default_signature_rect_is_placed.dart';
|
||||
import './step/i_drag_signature_by.dart';
|
||||
import './step/signature_rect_moved_from_center.dart';
|
||||
import './step/aspect_lock_is.dart';
|
||||
import './step/i_resize_signature_by.dart';
|
||||
import './step/signature_aspect_ratio_is_preserved_within.dart';
|
||||
import './step/i_set_tiny_signature_image_bytes.dart';
|
||||
import './step/signature_image_bytes_is_not_null.dart';
|
||||
import './step/signature_rect_is_not_null.dart';
|
||||
|
||||
void main() {
|
||||
group('''Signature state logic''', () {
|
||||
testWidgets('''placeDefaultRect centers a reasonable default rect''',
|
||||
(tester) async {
|
||||
await aNewProviderContainer(tester);
|
||||
await signatureRectIsNull(tester);
|
||||
await iPlaceDefaultSignatureRect(tester);
|
||||
await signatureRectLeft(tester, 0);
|
||||
await signatureRectTop(tester, 0);
|
||||
await signatureRectRight(tester, 400);
|
||||
await signatureRectBottom(tester, 560);
|
||||
await signatureRectWidth(tester, 50);
|
||||
await signatureRectHeight(tester, 20);
|
||||
});
|
||||
testWidgets('''drag clamps to canvas bounds''', (tester) async {
|
||||
await aNewProviderContainer(tester);
|
||||
await aDefaultSignatureRectIsPlaced(tester);
|
||||
await iDragSignatureBy(tester, Offset(10000, -10000));
|
||||
await signatureRectLeft(tester, 0);
|
||||
await signatureRectTop(tester, 0);
|
||||
await signatureRectRight(tester, 400);
|
||||
await signatureRectBottom(tester, 560);
|
||||
await signatureRectMovedFromCenter(tester);
|
||||
});
|
||||
testWidgets('''resize respects aspect lock and clamps''', (tester) async {
|
||||
await aNewProviderContainer(tester);
|
||||
await aDefaultSignatureRectIsPlaced(tester);
|
||||
await aspectLockIs(tester, true);
|
||||
await iResizeSignatureBy(tester, Offset(1000, 1000));
|
||||
await signatureAspectRatioIsPreservedWithin(tester, 0.05);
|
||||
await signatureRectLeft(tester, 0);
|
||||
await signatureRectTop(tester, 0);
|
||||
await signatureRectRight(tester, 400);
|
||||
await signatureRectBottom(tester, 560);
|
||||
});
|
||||
testWidgets('''setImageBytes ensures a rect exists for display''',
|
||||
(tester) async {
|
||||
await aNewProviderContainer(tester);
|
||||
await signatureRectIsNull(tester);
|
||||
await iSetTinySignatureImageBytes(tester);
|
||||
await signatureImageBytesIsNotNull(tester);
|
||||
await signatureRectIsNotNull(tester);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import 'dart:typed_data';
|
||||
import 'dart:ui' show Rect, Size;
|
||||
import 'dart:io';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
// A lightweight fake exporter to avoid platform rasterization in tests.
|
||||
class FakeExportService {
|
||||
Future<bool> exportSignedPdfFromFile({
|
||||
required String inputPath,
|
||||
required String outputPath,
|
||||
required int? signedPage,
|
||||
required Rect? signatureRectUi,
|
||||
required Size uiPageSize,
|
||||
required Uint8List? signatureImageBytes,
|
||||
double targetDpi = 144.0,
|
||||
}) async {
|
||||
final bytes = await exportSignedPdfFromBytes(
|
||||
srcBytes: Uint8List.fromList([0x25, 0x50, 0x44, 0x46]),
|
||||
signedPage: signedPage,
|
||||
signatureRectUi: signatureRectUi,
|
||||
uiPageSize: uiPageSize,
|
||||
signatureImageBytes: signatureImageBytes,
|
||||
targetDpi: targetDpi,
|
||||
);
|
||||
if (bytes == null) return false;
|
||||
try {
|
||||
final file = File(outputPath);
|
||||
await file.writeAsBytes(bytes, flush: true);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uint8List?> exportSignedPdfFromBytes({
|
||||
required Uint8List srcBytes,
|
||||
required int? signedPage,
|
||||
required Rect? signatureRectUi,
|
||||
required Size uiPageSize,
|
||||
required Uint8List? signatureImageBytes,
|
||||
double targetDpi = 144.0,
|
||||
}) async {
|
||||
// Return a deterministic tiny PDF-like byte array
|
||||
final header = <int>[0x25, 0x50, 0x44, 0x46, 0x2D]; // %PDF-
|
||||
final payload = <int>[...srcBytes.take(4)];
|
||||
final sigFlag =
|
||||
(signatureRectUi != null &&
|
||||
signatureImageBytes != null &&
|
||||
signatureImageBytes.isNotEmpty)
|
||||
? 1
|
||||
: 0;
|
||||
final meta = <int>[
|
||||
sigFlag,
|
||||
uiPageSize.width.toInt() & 0xFF,
|
||||
uiPageSize.height.toInt() & 0xFF,
|
||||
];
|
||||
return Uint8List.fromList([...header, ...payload, ...meta]);
|
||||
}
|
||||
}
|
||||
|
||||
ProviderContainer getOrCreateContainer() {
|
||||
if (TestWorld.container != null) return TestWorld.container!;
|
||||
final container = ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
return container;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
class _Token {
|
||||
final String base;
|
||||
const _Token(this.base);
|
||||
String get png => '$base.png';
|
||||
String get jpg => '$base.jpg';
|
||||
String get jpeg => '$base.jpeg';
|
||||
String get webp => '$base.webp';
|
||||
String get bmp => '$base.bmp';
|
||||
@override
|
||||
String toString() => base;
|
||||
}
|
||||
|
||||
// Tokens used by generated Scenario Outline substitutions
|
||||
const corrupted = _Token('corrupted');
|
||||
const signature = _Token('signature');
|
||||
const empty = _Token('empty');
|
|
@ -0,0 +1,36 @@
|
|||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
/// A tiny shared world for BDD steps to share state within a scenario.
|
||||
class TestWorld {
|
||||
static ProviderContainer? container;
|
||||
|
||||
// Signature helpers
|
||||
static Offset? prevCenter;
|
||||
static double? prevAspect;
|
||||
static double? prevContrast;
|
||||
static double? prevBrightness;
|
||||
|
||||
// Export/save helpers
|
||||
static Uint8List? lastExportBytes;
|
||||
static String? lastSavedPath;
|
||||
static bool exportInProgress = false;
|
||||
static bool nothingToSaveAttempt = false;
|
||||
|
||||
// Generic flags/values
|
||||
static int? selectedPage;
|
||||
|
||||
static void reset() {
|
||||
prevCenter = null;
|
||||
prevAspect = null;
|
||||
prevContrast = null;
|
||||
prevBrightness = null;
|
||||
lastExportBytes = null;
|
||||
lastSavedPath = null;
|
||||
exportInProgress = false;
|
||||
nothingToSaveAttempt = false;
|
||||
selectedPage = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a default signature rect is placed
|
||||
Future<void> aDefaultSignatureRectIsPlaced(WidgetTester tester) async {
|
||||
final c = TestWorld.container!;
|
||||
c.read(signatureProvider.notifier).placeDefaultRect();
|
||||
// remember center for movement checks
|
||||
TestWorld.prevCenter = c.read(signatureProvider).rect!.center;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a drawn signature exists in the canvas
|
||||
Future<void> aDrawnSignatureExistsInTheCanvas(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sigN = container.read(signatureProvider.notifier);
|
||||
sigN.setStrokes([
|
||||
[const Offset(0, 0), const Offset(1, 1)],
|
||||
]);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// 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: 10);
|
||||
}
|
|
@ -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,15 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a new provider container
|
||||
Future<void> aNewProviderContainer(WidgetTester tester) async {
|
||||
// Ensure a fresh world per scenario
|
||||
TestWorld.container?.dispose();
|
||||
TestWorld.reset();
|
||||
TestWorld.container = ProviderContainer();
|
||||
addTearDown(() {
|
||||
TestWorld.container?.dispose();
|
||||
TestWorld.container = null;
|
||||
});
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a PDF document is available
|
||||
Future<void> aPdfDocumentIsAvailable(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
container.read(pdfProvider.notifier).openSample();
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.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).toggleMark();
|
||||
container.read(signatureProvider.notifier).placeDefaultRect();
|
||||
container
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.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,13 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: a pdf is open with path {'test.pdf'} and pageCount {5}
|
||||
Future<void> aPdfIsOpenWithPathAndPagecount(
|
||||
WidgetTester tester,
|
||||
String path,
|
||||
int pageCount,
|
||||
) async {
|
||||
final c = TestWorld.container!;
|
||||
c.read(pdfProvider.notifier).openPicked(path: path, pageCount: pageCount);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.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).toggleMark();
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.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,19 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.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).toggleMark();
|
||||
// Set an image to ensure rect exists
|
||||
container
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.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).toggleMark();
|
||||
container
|
||||
.read(signatureProvider.notifier)
|
||||
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
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/features/pdf/viewer.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).toggleMark();
|
||||
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]));
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: an empty signature canvas
|
||||
Future<void> anEmptySignatureCanvas(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
container.read(signatureProvider.notifier).setStrokes([]);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: aspect lock is {true}
|
||||
Future<void> aspectLockIs(WidgetTester tester, bool value) async {
|
||||
final c = TestWorld.container!;
|
||||
// snapshot current aspect for later validation
|
||||
final r = c.read(signatureProvider).rect;
|
||||
if (r != null) {
|
||||
TestWorld.prevAspect = r.width / r.height;
|
||||
}
|
||||
c.read(signatureProvider.notifier).toggleAspect(value);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: I drag signature by {Offset(10000, -10000)}
|
||||
Future<void> iDragSignatureBy(WidgetTester tester, Offset delta) async {
|
||||
final c = TestWorld.container!;
|
||||
c.read(signatureProvider.notifier).drag(delta);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: I jumpTo {10}
|
||||
Future<void> iJumpto(WidgetTester tester, int page) async {
|
||||
final c = TestWorld.container!;
|
||||
c.read(pdfProvider.notifier).jumpTo(page);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: I openPicked with path {'test.pdf'} and pageCount {7}
|
||||
Future<void> iOpenpickedWithPathAndPagecount(
|
||||
WidgetTester tester,
|
||||
String path,
|
||||
int pageCount,
|
||||
) async {
|
||||
final c = TestWorld.container!;
|
||||
c.read(pdfProvider.notifier).openPicked(path: path, pageCount: pageCount);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: I place default signature rect
|
||||
Future<void> iPlaceDefaultSignatureRect(WidgetTester tester) async {
|
||||
final c = TestWorld.container!;
|
||||
c.read(signatureProvider.notifier).placeDefaultRect();
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: I resize signature by {Offset(1000, 1000)}
|
||||
Future<void> iResizeSignatureBy(WidgetTester tester, Offset delta) async {
|
||||
final c = TestWorld.container!;
|
||||
c.read(signatureProvider.notifier).resize(delta);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: I set page count {9}
|
||||
Future<void> iSetPageCount(WidgetTester tester, int count) async {
|
||||
final c = TestWorld.container!;
|
||||
c.read(pdfProvider.notifier).setPageCount(count);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: I set signature image bytes {Uint8List.fromList([0, 1, 2])}
|
||||
Future<void> iSetSignatureImageBytes(WidgetTester tester, dynamic value) async {
|
||||
final c = TestWorld.container!;
|
||||
final bytes = value as Uint8List;
|
||||
c.read(signatureProvider.notifier).setImageBytes(bytes);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: I set tiny signature image bytes
|
||||
Future<void> iSetTinySignatureImageBytes(WidgetTester tester) async {
|
||||
final c = TestWorld.container!;
|
||||
final bytes = Uint8List.fromList([0, 1, 2, 3]);
|
||||
c.read(signatureProvider.notifier).setImageBytes(bytes);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: I toggle mark
|
||||
Future<void> iToggleMark(WidgetTester tester) async {
|
||||
final c = TestWorld.container!;
|
||||
c.read(pdfProvider.notifier).toggleMark();
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.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);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
/// Usage: keep other unchanged content(pages) intact in the output
|
||||
Future<void> keepOtherUnchangedContentpagesIntactInTheOutput(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
// Logic-only: no additional checks here.
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: multiple strokes were drawn
|
||||
Future<void> multipleStrokesWereDrawn(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
container.read(signatureProvider.notifier).setStrokes([
|
||||
[const Offset(0, 0), const Offset(1, 1)],
|
||||
[const Offset(2, 2), const Offset(3, 3)],
|
||||
]);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: near-white background becomes transparent in the preview
|
||||
Future<void> nearwhiteBackgroundBecomesTransparentInThePreview(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
expect(container.read(signatureProvider).bgRemoval, isTrue);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
/// Usage: other page content remains vector and unaltered
|
||||
Future<void> otherPageContentRemainsVectorAndUnaltered(
|
||||
WidgetTester tester,
|
||||
) async {}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: pdf current page is {1}
|
||||
Future<void> pdfCurrentPageIs(WidgetTester tester, int expected) async {
|
||||
final c = TestWorld.container!;
|
||||
expect(c.read(pdfProvider).currentPage, expected);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: pdf marked for signing is {false}
|
||||
Future<void> pdfMarkedForSigningIs(WidgetTester tester, bool expected) async {
|
||||
final c = TestWorld.container!;
|
||||
expect(c.read(pdfProvider).markedForSigning, expected);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: pdf page count is {7}
|
||||
Future<void> pdfPageCountIs(WidgetTester tester, int expected) async {
|
||||
final c = TestWorld.container!;
|
||||
expect(c.read(pdfProvider).pageCount, expected);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: pdf picked path is {'test.pdf'}
|
||||
Future<void> pdfPickedPathIs(WidgetTester tester, String expected) async {
|
||||
final c = TestWorld.container!;
|
||||
final s = c.read(pdfProvider);
|
||||
expect(s.pickedPdfPath, expected);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: pdf state is loaded {true}
|
||||
Future<void> pdfStateIsLoaded(WidgetTester tester, bool expected) async {
|
||||
final c = TestWorld.container!;
|
||||
expect(c.read(pdfProvider).loaded, expected);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature aspect ratio is preserved within {0.05}
|
||||
Future<void> signatureAspectRatioIsPreservedWithin(
|
||||
WidgetTester tester,
|
||||
num tolerance,
|
||||
) async {
|
||||
final c = TestWorld.container!;
|
||||
final r = c.read(signatureProvider).rect!;
|
||||
final before = TestWorld.prevAspect;
|
||||
if (before == null) {
|
||||
// save and pass
|
||||
TestWorld.prevAspect = r.width / r.height;
|
||||
return;
|
||||
}
|
||||
final after = r.width / r.height;
|
||||
expect((after - before).abs(), lessThanOrEqualTo(tolerance.toDouble()));
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature image bytes is not null
|
||||
Future<void> signatureImageBytesIsNotNull(WidgetTester tester) async {
|
||||
final c = TestWorld.container!;
|
||||
expect(c.read(signatureProvider).imageBytes, isNotNull);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature rect bottom <= {560}
|
||||
Future<void> signatureRectBottom(WidgetTester tester, num maxBottom) async {
|
||||
final c = TestWorld.container!;
|
||||
final r = c.read(signatureProvider).rect!;
|
||||
expect(r.bottom, lessThanOrEqualTo(maxBottom.toDouble()));
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature rect height > {20}
|
||||
Future<void> signatureRectHeight(WidgetTester tester, num minHeight) async {
|
||||
final c = TestWorld.container!;
|
||||
final r = c.read(signatureProvider).rect!;
|
||||
expect(r.height, greaterThan(minHeight.toDouble()));
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature rect is not null
|
||||
Future<void> signatureRectIsNotNull(WidgetTester tester) async {
|
||||
final c = TestWorld.container!;
|
||||
expect(c.read(signatureProvider).rect, isNotNull);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature rect is null
|
||||
Future<void> signatureRectIsNull(WidgetTester tester) async {
|
||||
final c = TestWorld.container!;
|
||||
expect(c.read(signatureProvider).rect, isNull);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature rect left >= {0}
|
||||
Future<void> signatureRectLeft(WidgetTester tester, num minLeft) async {
|
||||
final c = TestWorld.container!;
|
||||
final r = c.read(signatureProvider).rect!;
|
||||
expect(r.left, greaterThanOrEqualTo(minLeft.toDouble()));
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature rect moved from center
|
||||
Future<void> signatureRectMovedFromCenter(WidgetTester tester) async {
|
||||
final c = TestWorld.container!;
|
||||
final prev = TestWorld.prevCenter;
|
||||
final now = c.read(signatureProvider).rect!.center;
|
||||
expect(prev, isNotNull);
|
||||
expect(now, isNot(equals(prev)));
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature rect right <= {400}
|
||||
Future<void> signatureRectRight(WidgetTester tester, num maxRight) async {
|
||||
final c = TestWorld.container!;
|
||||
final r = c.read(signatureProvider).rect!;
|
||||
expect(r.right, lessThanOrEqualTo(maxRight.toDouble()));
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature rect top >= {0}
|
||||
Future<void> signatureRectTop(WidgetTester tester, num minTop) async {
|
||||
final c = TestWorld.container!;
|
||||
final r = c.read(signatureProvider).rect!;
|
||||
expect(r.top, greaterThanOrEqualTo(minTop.toDouble()));
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: signature rect width > {50}
|
||||
Future<void> signatureRectWidth(WidgetTester tester, num minWidth) async {
|
||||
final c = TestWorld.container!;
|
||||
final r = c.read(signatureProvider).rect!;
|
||||
expect(r.width, greaterThan(minWidth.toDouble()));
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: that page is displayed
|
||||
Future<void> thatPageIsDisplayed(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
expect(container.read(pdfProvider).currentPage, 3);
|
||||
}
|
|
@ -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.
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the canvas becomes blank
|
||||
Future<void> theCanvasBecomesBlank(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
expect(container.read(signatureProvider).strokes, isEmpty);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the export process is not yet finished
|
||||
Future<void> theExportProcessIsNotYetFinished(WidgetTester tester) async {
|
||||
expect(TestWorld.exportInProgress, isTrue);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the first page is displayed
|
||||
Future<void> theFirstPageIsDisplayed(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final pdf = container.read(pdfProvider);
|
||||
expect(pdf.currentPage, 1);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.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/features/pdf/viewer.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/features/pdf/viewer.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);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the last stroke is removed
|
||||
Future<void> theLastStrokeIsRemoved(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sig = container.read(signatureProvider);
|
||||
expect(sig.strokes.length, 1);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the preview updates immediately
|
||||
Future<void> thePreviewUpdatesImmediately(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sig = container.read(signatureProvider);
|
||||
expect(sig.contrast, closeTo(1.3, 1e-6));
|
||||
expect(sig.brightness, closeTo(0.2, 1e-6));
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.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));
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the signature remains within the page area
|
||||
Future<void> theSignatureRemainsWithinThePageArea(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sig = container.read(signatureProvider);
|
||||
final r = sig.rect!;
|
||||
expect(r.left >= 0 && r.top >= 0, isTrue);
|
||||
expect(r.right <= SignatureController.pageSize.width, isTrue);
|
||||
expect(r.bottom <= SignatureController.pageSize.height, isTrue);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the signatures appear on the corresponding page in the output
|
||||
Future<void> theSignaturesAppearOnTheCorrespondingPageInTheOutput(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final pdf = container.read(pdfProvider);
|
||||
final sig = container.read(signatureProvider);
|
||||
expect(pdf.signedPage, isNotNull);
|
||||
expect(sig.rect, isNotNull);
|
||||
expect(sig.imageBytes, isNotNull);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the size and position update in real time
|
||||
Future<void> theSizeAndPositionUpdateInRealTime(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sig = container.read(signatureProvider);
|
||||
expect(sig.rect, isNotNull);
|
||||
expect(sig.rect!.center, isNot(TestWorld.prevCenter));
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
/// Usage: the stamp remains crisp at any zoom level (not rasterized by the screen)
|
||||
Future<void> theStampRemainsCrispAtAnyZoomLevelNotRasterizedByTheScreen(
|
||||
WidgetTester tester,
|
||||
) async {}
|
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user attempts to save
|
||||
Future<void> theUserAttemptsToSave(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
final pdf = container.read(pdfProvider);
|
||||
final sig = container.read(signatureProvider);
|
||||
// Simulate save attempt: since rect is null, mark flag
|
||||
if (!pdf.loaded || sig.rect == null) {
|
||||
TestWorld.nothingToSaveAttempt = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user can apply or reset adjustments
|
||||
Future<void> theUserCanApplyOrResetAdjustments(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sig = container.read(signatureProvider);
|
||||
expect(sig.contrast, isNotNull);
|
||||
expect(sig.brightness, isNotNull);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
/// Usage: the user can apply the change
|
||||
Future<void> theUserCanApplyTheChange(WidgetTester tester) async {
|
||||
// No-op in logic tests.
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user can move to the next or previous page
|
||||
Future<void> theUserCanMoveToTheNextOrPreviousPage(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final pdfN = container.read(pdfProvider.notifier);
|
||||
final pdf = container.read(pdfProvider);
|
||||
expect(pdf.currentPage, 1);
|
||||
pdfN.jumpTo(2);
|
||||
expect(container.read(pdfProvider).currentPage, 2);
|
||||
pdfN.jumpTo(1);
|
||||
expect(container.read(pdfProvider).currentPage, 1);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user cannot edit the document
|
||||
Future<void> theUserCannotEditTheDocument(WidgetTester tester) async {
|
||||
expect(TestWorld.exportInProgress, isTrue);
|
||||
// Reset flag to simulate export completion
|
||||
TestWorld.exportInProgress = false;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user changes contrast and brightness controls
|
||||
Future<void> theUserChangesContrastAndBrightnessControls(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
container.read(signatureProvider.notifier)
|
||||
..setContrast(1.3)
|
||||
..setBrightness(0.2);
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user chooses a signature image file
|
||||
Future<void> theUserChoosesASignatureImageFile(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
// Simulate loading a tiny valid PNG/JPEG bytes; using 1x1 transparent PNG
|
||||
final bytes = Uint8List.fromList([
|
||||
0x89,
|
||||
0x50,
|
||||
0x4E,
|
||||
0x47,
|
||||
0x0D,
|
||||
0x0A,
|
||||
0x1A,
|
||||
0x0A,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0D,
|
||||
0x49,
|
||||
0x48,
|
||||
0x44,
|
||||
0x52,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x08,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1F,
|
||||
0x15,
|
||||
0xC4,
|
||||
0x89,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0A,
|
||||
0x49,
|
||||
0x44,
|
||||
0x41,
|
||||
0x54,
|
||||
0x78,
|
||||
0x9C,
|
||||
0x63,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x01,
|
||||
0x0D,
|
||||
0x0A,
|
||||
0x2D,
|
||||
0xB4,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x49,
|
||||
0x45,
|
||||
0x4E,
|
||||
0x44,
|
||||
0xAE,
|
||||
0x42,
|
||||
0x60,
|
||||
0x82,
|
||||
]);
|
||||
container.read(signatureProvider.notifier).setImageBytes(bytes);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user chooses undo
|
||||
Future<void> theUserChoosesUndo(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sig = container.read(signatureProvider);
|
||||
if (sig.strokes.isNotEmpty) {
|
||||
final newStrokes = List<List<Offset>>.from(sig.strokes)..removeLast();
|
||||
container.read(signatureProvider.notifier).setStrokes(newStrokes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user clears the canvas
|
||||
Future<void> theUserClearsTheCanvas(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
container.read(signatureProvider.notifier).setStrokes([]);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user drags handles to resize and drags to reposition
|
||||
Future<void> theUserDragsHandlesToResizeAndDragsToReposition(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sigN = container.read(signatureProvider.notifier);
|
||||
final sig = container.read(signatureProvider);
|
||||
TestWorld.prevCenter = sig.rect?.center;
|
||||
sigN.resize(const Offset(50, 30));
|
||||
sigN.drag(const Offset(20, -10));
|
||||
}
|
|
@ -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/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user draws strokes and confirms
|
||||
Future<void> theUserDrawsStrokesAndConfirms(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
TestWorld.container = container;
|
||||
// Simulate drawn signature bytes
|
||||
final bytes = Uint8List.fromList([1, 2, 3]);
|
||||
container.read(signatureProvider.notifier).setImageBytes(bytes);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user enables aspect ratio lock and resizes
|
||||
Future<void> theUserEnablesAspectRatioLockAndResizes(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sigN = container.read(signatureProvider.notifier);
|
||||
final sig = container.read(signatureProvider);
|
||||
TestWorld.prevAspect = sig.rect!.width / sig.rect!.height;
|
||||
sigN.toggleAspect(true);
|
||||
sigN.resize(const Offset(100, 50));
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user enables background removal
|
||||
Future<void> theUserEnablesBackgroundRemoval(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
container.read(signatureProvider.notifier).setBgRemoval(true);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdf_signature/features/pdf/viewer.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user is notified of the issue
|
||||
Future<void> theUserIsNotifiedOfTheIssue(WidgetTester tester) async {
|
||||
final container = TestWorld.container ?? ProviderContainer();
|
||||
final sig = container.read(signatureProvider);
|
||||
// For our logic simulation: invalid selections result in no usable bytes
|
||||
expect(sig.imageBytes == null || sig.imageBytes!.isEmpty, isTrue);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user is notified that the export is still in progress
|
||||
Future<void> theUserIsNotifiedThatTheExportIsStillInProgress(
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
expect(TestWorld.exportInProgress, isTrue);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import '_world.dart';
|
||||
|
||||
/// Usage: the user is notified there is nothing to save
|
||||
Future<void> theUserIsNotifiedThereIsNothingToSave(WidgetTester tester) async {
|
||||
expect(TestWorld.nothingToSaveAttempt, isTrue);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue