Merge feat/bulk-operator-workflows into main (Stage 7B complete)
Publish Docs / publish-docs (push) Successful in 41s
Details
Publish Docs / publish-docs (push) Successful in 41s
Details
This commit is contained in:
commit
c67f92f844
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
## Current status
|
||||
|
||||
- main baseline updated through: feat/inventory-mobile-surface (Stage 6C complete)
|
||||
- next branch: feat/bulk-operator-workflows (Stage 7B)
|
||||
- current stage: Stage 6C complete — mobile inventory surface with MobileInventoryPage (search, filter chips, item count, pull-to-refresh, compact 120px cards), MobileInventoryDetailPage (haptic feedback, adjust quantity dialog, local SnackBar listener, SafeArea), shell updated from shared InventoryPage to MobileInventoryPage, and 6 new kell_mobile tests (26/26 total). Stage 6 complete.
|
||||
- main baseline updated through: bulk-operator-workflows (Stage 7B complete)
|
||||
- next branch: feat/integrations-contracts (Stage 8A)
|
||||
- current stage: Stage 7 complete — proceeding to infrastructure activation
|
||||
|
||||
## Slice tracker
|
||||
|
||||
|
|
@ -377,3 +377,20 @@
|
|||
- tests: passed (311/311 feature_wordpress, 14/14 kell_mobile, 24/24 kell_web, 41/41 design_system, 20/20 core — 410 total)
|
||||
- analyze: passed (dart analyze --fatal-infos — no issues found across all packages)
|
||||
- brief updated: yes
|
||||
|
||||
### feat/bulk-operator-workflows
|
||||
|
||||
- status: merged to main
|
||||
- date: 2026-05-30
|
||||
- inspection: complete
|
||||
- implementation: complete
|
||||
- files changed:
|
||||
- `feature_wordpress/lib/src/data/wordpress_product_mapper.dart` — `toWooCommerceStatus()` now maps `PublishStatus.pendingReview` → `'pending'` instead of throwing `ArgumentError`; only `unpublished` throws
|
||||
- `feature_wordpress/lib/src/presentation/product_publishing_page.dart` — added `_confirmBulkPublish()` and `_confirmBulkSubmitForReview()` confirmation dialogs; updated `_MultiSelectBar` with `onBulkPublish` and `onBulkSubmitForReview` callbacks; added "Publish" and "Submit for Review" `OutlinedButton.icon` buttons; refactored bar layout from `Row` to `Wrap` for responsive overflow
|
||||
- `feature_wordpress/test/wordpress_product_mapper_test.dart` — updated `pendingReview` test: now expects `'pending'` return value instead of `ArgumentError`
|
||||
- `feature_wordpress/test/product_publishing_controller_test.dart` — added 3 new bulk tests: publishes all selected products, submits all for review, handles mixed statuses to published
|
||||
- `feature_wordpress/test/widgets/product_publishing_page_test.dart` — added 5 new widget tests: multi-select bar shows Publish button, Publish confirmation dialog, Submit for Review confirmation dialog, cancel preserves selection, plural count in dialog
|
||||
- `feature_wordpress/test/wordpress_product_publishing_repository_test.dart` — removed `pendingReview` from unsupported-throws test; only `unpublished` asserted
|
||||
- tests: passed (319/319 feature_wordpress)
|
||||
- analyze: passed (dart analyze --fatal-infos — no issues found)
|
||||
- brief updated: yes
|
||||
|
|
|
|||
|
|
@ -110,9 +110,18 @@ Rules:
|
|||
- ✅ Cross-platform shell composition strategy landed (Stage 4B complete — merged `feat/shared-composition-pattern` → `main`, 2026-05-22).
|
||||
- ✅ Flutter CI/CD pipeline landed (Stage 4C complete — merged `feat/flutter-cicd` → `main`, 2026-05-22).
|
||||
- ✅ Test coverage visibility landed (Stage 4D complete — merged `feat/test-coverage-visibility` → `main`, 2026-05-22).
|
||||
- ✅ Android app shell and bootstrap landed (Stage 5A complete — merged `feat/android-app-shell` → `main`, 2026-05-28).
|
||||
- ✅ Android publishing surface landed (Stage 5B complete — merged `feat/android-publishing-surface` → `main`, 2026-05-29). Stage 5 complete.
|
||||
- ✅ Android feedback and action polish landed (Stage 6A complete — merged `feat/android-feedback-polish` → `main`, 2026-05-29).
|
||||
- ✅ Android mobile workflow hardening landed (Stage 6B complete — merged `feat/android-mobile-ux-hardening` → `main`, 2026-05-30).
|
||||
- ✅ Bulk move-to-draft action landed (Stage 7A complete — merged `feat/bulk-status-actions` → `main`, 2026-05-30).
|
||||
- ✅ Inventory domain expansion landed (Stage 5A-inv complete — merged `feat/inventory-domain-expansion` → `main`, 2026-07-10).
|
||||
- ✅ Inventory page UI landed (Stage 5B-inv complete — merged `feat/inventory-page-ui` → `main`, 2026-07-10).
|
||||
- ✅ Orders domain expansion landed (Stage 5C complete — merged `feat/orders-domain` → `main`, 2026-07-10).
|
||||
- ✅ Orders page UI hardening landed (Stage 5D complete — merged `feat/orders-page-ui` → `main`, 2026-07-10).
|
||||
- ✅ Orders page UI landed (Stage 5D complete — merged `feat/orders-page-ui` → `main`, 2026-07-10).
|
||||
- ✅ Orders mobile surface landed (Stage 6B-orders complete — merged `feat/orders-mobile-surface` → `main`, 2026-07-10).
|
||||
- ✅ Inventory mobile surface landed (Stage 6C complete — merged `feat/inventory-mobile-surface` → `main`, 2026-07-10). Stage 6 complete.
|
||||
- ✅ Bulk publish and bulk submit-for-review actions landed (Stage 7B complete — merged `feat/bulk-operator-workflows` → `main`, 2026-07-11). Stage 7 complete.
|
||||
|
||||
### Current narrow edit capabilities on `main`
|
||||
|
||||
|
|
@ -127,7 +136,6 @@ Rules:
|
|||
- `dart analyze` clean
|
||||
- `core` tests passing
|
||||
- `design_system` tests passing
|
||||
- `feature_inventory` tests passing
|
||||
- `feature_wordpress` tests passing
|
||||
- `kell_web` tests passing
|
||||
- `kell_mobile` tests passing
|
||||
|
|
@ -135,11 +143,11 @@ Rules:
|
|||
- latest reported count for `design_system`: `41/41 passed`
|
||||
- latest reported count for `feature_inventory`: `75/75 passed`
|
||||
- latest reported count for `feature_orders`: `115/115 passed`
|
||||
- latest reported count for `feature_wordpress`: `311/311 passed`
|
||||
- latest reported count for `feature_wordpress`: `319/319 passed`
|
||||
- latest reported count for `kell_web`: `24/24 passed`
|
||||
- latest reported count for `kell_mobile`: `26/26 passed`
|
||||
- total: `612/612 passed`
|
||||
- baseline commit: merge of `feat/inventory-mobile-surface` (Stage 6C complete, 2026-07-10)
|
||||
- total: `620/620 passed`
|
||||
- baseline commit: merge of `feat/bulk-operator-workflows` into `main` (Stage 7B complete, 2026-07-11)
|
||||
|
||||
#### Baseline test coverage (established 2026-05-22)
|
||||
|
||||
|
|
@ -155,8 +163,8 @@ No minimum thresholds are enforced — this is visibility-only tracking. Coverag
|
|||
|
||||
### Next recommended branch
|
||||
|
||||
**`feat/bulk-operator-workflows`** — Stage 7B: Additional bulk actions (bulk publish, bulk move to pending review).
|
||||
Branch from latest `main`. Stage 6C (inventory mobile surface) is complete. All three primary features (Products, Orders, Inventory) now have mobile-optimized surfaces on Android. Stage 6 is complete. Next logical step is adding more bulk publishing actions.
|
||||
**`feat/integrations-contracts`** — Stage 8A: Integration abstractions.
|
||||
Branch from latest `main`. Stage 7 (bulk actions) is complete. Stage 8A begins infrastructure activation required for all subsequent feature work (Stages 9–16).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -449,12 +457,10 @@ Add carefully scoped bulk operations once single-item workflows are stable acros
|
|||
> Merged `feat/bulk-status-actions` → `main` (2026-05-30).
|
||||
> Added `BulkActionResult` value class, `bulkUpdateStatus()` controller method (sequential processing with per-row updating state, single reload after completion, auto-clear multi-selection), `showBulkActionSnackBar()` with success/partial-failure/total-failure messaging. Updated `_MultiSelectBar` with "Move to Draft" button and confirmation dialog. 11 new bulk action tests added (311 total `feature_wordpress` tests passing, 24 `kell_web` tests passing).
|
||||
|
||||
#### Stage 7B — Bulk operator workflows (future)
|
||||
#### ~~Stage 7B — Bulk operator workflows~~ ✅ COMPLETE
|
||||
|
||||
Candidate additional bulk actions (implement one per slice):
|
||||
|
||||
- bulk publish
|
||||
- bulk move to pending review
|
||||
> Merged `feat/bulk-operator-workflows` → `main` (2026-05-30).
|
||||
> Enabled `pendingReview` status in `WordPressProductMapper.toWooCommerceStatus()` (maps to WooCommerce `'pending'`). Added "Publish" and "Submit for Review" bulk action buttons to `_MultiSelectBar` with confirmation dialogs. Refactored bar layout from `Row` to `Wrap` for responsive overflow. 8 new tests added across mapper, controller, page, and repository test suites (319 total `feature_wordpress` tests passing). Stage 7 complete.
|
||||
|
||||
#### Explicitly defer
|
||||
|
||||
|
|
@ -1254,7 +1260,7 @@ Working rules:
|
|||
| ------------------- | ------------ | ----------------- | ------------ | -------------- | -------------------------------------------------- | ------- | -------------------- |
|
||||
| `feature_wordpress` | ✅ Complete | ✅ Complete | ✅ Complete | ✅ WooCommerce | ✅ Complete | 294 | **Production-ready** |
|
||||
| `feature_inventory` | ⚠️ Read-only | ⚠️ Read-only | ⚠️ Read-only | ❌ None | ⚠️ Read-only | Minimal | **Fake-only MVP** |
|
||||
| `feature_orders` | ✅ Write ops | ✅ Write ops | ✅ Write ops | ❌ None | ✅ Write-capable UI | 115 | **Fake write-ready** |
|
||||
| `feature_orders` | ⚠️ Read-only | ⚠️ Read-only | ⚠️ Read-only | ❌ None | ⚠️ Read-only | Some | **Fake-only MVP** |
|
||||
| `feature_policy` | ✅ Complete | ✅ Complete | ✅ Complete | ❌ None | ✅ Complete | Minimal | **Fake-only MVP** |
|
||||
| `feature_finance` | ❌ Stub | ❌ Stub | ❌ Stub | ❌ None | ❌ Stub | None | **Scaffolded only** |
|
||||
| `feature_mrp` | ❌ Stub | ❌ Stub | ❌ Stub | ❌ None | ❌ Stub | None | **Scaffolded only** |
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ class WordPressProductMapper {
|
|||
/// Converts a [PublishStatus] domain value to the corresponding
|
||||
/// WooCommerce REST API status string.
|
||||
///
|
||||
/// Only [PublishStatus.draft] and [PublishStatus.published] are supported
|
||||
/// in the first pass. Throws [ArgumentError] for unsupported values.
|
||||
/// Supports [PublishStatus.draft], [PublishStatus.published], and
|
||||
/// [PublishStatus.pendingReview]. Throws [ArgumentError] for
|
||||
/// [PublishStatus.unpublished] which has no direct WooCommerce equivalent.
|
||||
///
|
||||
/// WooCommerce accepts: `publish`, `draft`, `pending`, `private`, `trash`.
|
||||
static String toWooCommerceStatus(PublishStatus status) {
|
||||
|
|
@ -43,11 +44,12 @@ class WordPressProductMapper {
|
|||
case PublishStatus.published:
|
||||
return 'publish';
|
||||
case PublishStatus.pendingReview:
|
||||
return 'pending';
|
||||
case PublishStatus.unpublished:
|
||||
throw ArgumentError.value(
|
||||
status,
|
||||
'status',
|
||||
'Unsupported status for WooCommerce update in the first pass',
|
||||
'Unsupported status for WooCommerce update — unpublished has no direct WooCommerce equivalent',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,6 +159,58 @@ class _ProductPublishingPageState extends State<ProductPublishingPage> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Shows a confirmation dialog before executing bulk publish.
|
||||
Future<void> _confirmBulkPublish(BuildContext context) async {
|
||||
final count = controller.multiSelectedCount;
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Publish Products'),
|
||||
content: Text('Publish $count ${count == 1 ? 'product' : 'products'} to the store?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('Publish'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirmed == true) {
|
||||
await controller.bulkUpdateStatus(PublishStatus.published);
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows a confirmation dialog before executing bulk submit for review.
|
||||
Future<void> _confirmBulkSubmitForReview(BuildContext context) async {
|
||||
final count = controller.multiSelectedCount;
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Submit for Review'),
|
||||
content: Text('Submit $count ${count == 1 ? 'product' : 'products'} for review?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('Submit for Review'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirmed == true) {
|
||||
await controller.bulkUpdateStatus(PublishStatus.pendingReview);
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles keyboard events for list navigation.
|
||||
KeyEventResult _handleKeyEvent(FocusNode node, KeyEvent event) {
|
||||
if (event is! KeyDownEvent && event is! KeyRepeatEvent) {
|
||||
|
|
@ -271,6 +323,8 @@ class _ProductPublishingPageState extends State<ProductPublishingPage> {
|
|||
onSelectAll: controller.selectAllVisible,
|
||||
onClearSelection: controller.clearMultiSelection,
|
||||
onBulkMoveToDraft: () => _confirmBulkMoveToDraft(context),
|
||||
onBulkPublish: () => _confirmBulkPublish(context),
|
||||
onBulkSubmitForReview: () => _confirmBulkSubmitForReview(context),
|
||||
),
|
||||
// ── List header with count and density toggle ───────────────
|
||||
Padding(
|
||||
|
|
@ -360,6 +414,8 @@ class _MultiSelectBar extends StatelessWidget {
|
|||
final VoidCallback onSelectAll;
|
||||
final VoidCallback onClearSelection;
|
||||
final VoidCallback onBulkMoveToDraft;
|
||||
final VoidCallback onBulkPublish;
|
||||
final VoidCallback onBulkSubmitForReview;
|
||||
|
||||
const _MultiSelectBar({
|
||||
required this.selectedCount,
|
||||
|
|
@ -367,6 +423,8 @@ class _MultiSelectBar extends StatelessWidget {
|
|||
required this.onSelectAll,
|
||||
required this.onClearSelection,
|
||||
required this.onBulkMoveToDraft,
|
||||
required this.onBulkPublish,
|
||||
required this.onBulkSubmitForReview,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -381,10 +439,12 @@ class _MultiSelectBar extends StatelessWidget {
|
|||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: KcColors.denimBlue.withValues(alpha: 0.3)),
|
||||
),
|
||||
child: Row(
|
||||
child: Wrap(
|
||||
spacing: KcSpacing.xs,
|
||||
runSpacing: KcSpacing.xs,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.checklist, size: 18, color: KcColors.denimBlue),
|
||||
const SizedBox(width: KcSpacing.xs),
|
||||
Text(
|
||||
'$selectedCount selected',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
|
|
@ -392,13 +452,21 @@ class _MultiSelectBar extends StatelessWidget {
|
|||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: KcSpacing.sm),
|
||||
OutlinedButton.icon(
|
||||
onPressed: onBulkMoveToDraft,
|
||||
icon: const Icon(Icons.drafts_outlined, size: 16),
|
||||
label: const Text('Move to Draft'),
|
||||
),
|
||||
const Spacer(),
|
||||
OutlinedButton.icon(
|
||||
onPressed: onBulkPublish,
|
||||
icon: const Icon(Icons.publish_outlined, size: 16),
|
||||
label: const Text('Publish'),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: onBulkSubmitForReview,
|
||||
icon: const Icon(Icons.rate_review_outlined, size: 16),
|
||||
label: const Text('Submit for Review'),
|
||||
),
|
||||
if (!allSelected) TextButton(onPressed: onSelectAll, child: const Text('Select All')),
|
||||
TextButton(onPressed: onClearSelection, child: const Text('Clear')),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1589,6 +1589,66 @@ void main() {
|
|||
// Should complete without throwing.
|
||||
await future;
|
||||
});
|
||||
|
||||
test('publishes all selected products via bulk action', () async {
|
||||
await controller.load();
|
||||
|
||||
// Select two draft products (ids 4 and 5).
|
||||
controller.toggleMultiSelect('4');
|
||||
controller.toggleMultiSelect('5');
|
||||
expect(controller.multiSelectedCount, 2);
|
||||
|
||||
await controller.bulkUpdateStatus(PublishStatus.published);
|
||||
|
||||
final p4 = controller.drafts.firstWhere((d) => d.id == '4');
|
||||
final p5 = controller.drafts.firstWhere((d) => d.id == '5');
|
||||
expect(p4.status, PublishStatus.published);
|
||||
expect(p5.status, PublishStatus.published);
|
||||
expect(controller.lastBulkActionResult, isNotNull);
|
||||
expect(controller.lastBulkActionResult!.successCount, 2);
|
||||
expect(controller.lastBulkActionResult!.targetStatus, PublishStatus.published);
|
||||
expect(controller.lastBulkActionResult!.allSucceeded, isTrue);
|
||||
});
|
||||
|
||||
test('submits all selected products for review via bulk action', () async {
|
||||
await controller.load();
|
||||
|
||||
// Select two draft products (ids 4 and 5).
|
||||
controller.toggleMultiSelect('4');
|
||||
controller.toggleMultiSelect('5');
|
||||
|
||||
await controller.bulkUpdateStatus(PublishStatus.pendingReview);
|
||||
|
||||
final p4 = controller.drafts.firstWhere((d) => d.id == '4');
|
||||
final p5 = controller.drafts.firstWhere((d) => d.id == '5');
|
||||
expect(p4.status, PublishStatus.pendingReview);
|
||||
expect(p5.status, PublishStatus.pendingReview);
|
||||
expect(controller.lastBulkActionResult, isNotNull);
|
||||
expect(controller.lastBulkActionResult!.successCount, 2);
|
||||
expect(controller.lastBulkActionResult!.targetStatus, PublishStatus.pendingReview);
|
||||
expect(controller.lastBulkActionResult!.allSucceeded, isTrue);
|
||||
});
|
||||
|
||||
test('handles mixed statuses — all become published', () async {
|
||||
await controller.load();
|
||||
|
||||
// Select one of each status type.
|
||||
controller.toggleMultiSelect('3'); // pendingReview
|
||||
controller.toggleMultiSelect('4'); // draft
|
||||
controller.toggleMultiSelect('6'); // unpublished
|
||||
|
||||
await controller.bulkUpdateStatus(PublishStatus.published);
|
||||
|
||||
for (final id in ['3', '4', '6']) {
|
||||
final product = controller.drafts.firstWhere((d) => d.id == id);
|
||||
expect(
|
||||
product.status,
|
||||
PublishStatus.published,
|
||||
reason: 'Product $id should be published',
|
||||
);
|
||||
}
|
||||
expect(controller.lastBulkActionResult!.successCount, 3);
|
||||
});
|
||||
});
|
||||
|
||||
group('disposed guard', () {
|
||||
|
|
|
|||
|
|
@ -215,4 +215,147 @@ void main() {
|
|||
expect(find.byIcon(Icons.touch_app_outlined), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
group('ProductPublishingPage multi-select bulk actions', () {
|
||||
Widget buildPage({FakeProductPublishingRepository? repo}) {
|
||||
return MaterialApp(
|
||||
theme: buildKcTheme(),
|
||||
home: Scaffold(
|
||||
body: ProductPublishingPage(repository: repo ?? FakeProductPublishingRepository()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('multi-select bar shows Publish button', (tester) async {
|
||||
tester.view.physicalSize = const Size(600, 800);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
|
||||
await tester.pumpWidget(buildPage());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Activate multi-select by long-pressing the first product card's checkbox area.
|
||||
// The checkbox appears on the card — we toggle multi-select via the card's onMultiSelectToggle.
|
||||
// ProductDraftCard has a Checkbox when in multi-select mode, but multi-select is
|
||||
// triggered by tapping the checkbox. Since no items are selected initially,
|
||||
// the checkbox is shown as leading widget. Let's find checkboxes.
|
||||
final checkboxes = find.byType(Checkbox);
|
||||
expect(checkboxes, findsWidgets);
|
||||
|
||||
// Tap first checkbox to enter multi-select mode.
|
||||
await tester.tap(checkboxes.first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Multi-select bar should appear with all three bulk action buttons.
|
||||
expect(find.text('Move to Draft'), findsOneWidget);
|
||||
expect(find.text('Publish'), findsOneWidget);
|
||||
expect(find.text('Submit for Review'), findsOneWidget);
|
||||
expect(find.text('1 selected'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Publish button shows confirmation dialog', (tester) async {
|
||||
tester.view.physicalSize = const Size(600, 800);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
|
||||
await tester.pumpWidget(buildPage());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Activate multi-select.
|
||||
final checkboxes = find.byType(Checkbox);
|
||||
await tester.tap(checkboxes.first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap the Publish button.
|
||||
await tester.tap(find.text('Publish'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Confirmation dialog should appear.
|
||||
expect(find.text('Publish Products'), findsOneWidget);
|
||||
expect(find.text('Publish 1 product to the store?'), findsOneWidget);
|
||||
expect(find.text('Cancel'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Submit for Review button shows confirmation dialog', (tester) async {
|
||||
tester.view.physicalSize = const Size(600, 800);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
|
||||
await tester.pumpWidget(buildPage());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Activate multi-select.
|
||||
final checkboxes = find.byType(Checkbox);
|
||||
await tester.tap(checkboxes.first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap the Submit for Review button.
|
||||
await tester.tap(find.text('Submit for Review'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Confirmation dialog should appear.
|
||||
expect(find.text('Submit for Review'), findsWidgets); // button + dialog title
|
||||
expect(find.text('Submit 1 product for review?'), findsOneWidget);
|
||||
expect(find.text('Cancel'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('cancelling publish dialog does not change statuses', (tester) async {
|
||||
tester.view.physicalSize = const Size(600, 800);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
|
||||
await tester.pumpWidget(buildPage());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Activate multi-select.
|
||||
final checkboxes = find.byType(Checkbox);
|
||||
await tester.tap(checkboxes.first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap the Publish button.
|
||||
await tester.tap(find.text('Publish'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Cancel the dialog.
|
||||
await tester.tap(find.text('Cancel'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Multi-select should still be active (nothing changed).
|
||||
expect(find.text('1 selected'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('plural product count in confirmation dialog', (tester) async {
|
||||
tester.view.physicalSize = const Size(600, 800);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
|
||||
await tester.pumpWidget(buildPage());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select two products.
|
||||
final checkboxes = find.byType(Checkbox);
|
||||
await tester.tap(checkboxes.at(0));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(checkboxes.at(1));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('2 selected'), findsOneWidget);
|
||||
|
||||
// Tap the Publish button.
|
||||
await tester.tap(find.text('Publish'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Dialog should use plural form.
|
||||
expect(find.text('Publish 2 products to the store?'), findsOneWidget);
|
||||
|
||||
// Dismiss.
|
||||
await tester.tap(find.text('Cancel'));
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,11 +175,8 @@ void main() {
|
|||
expect(WordPressProductMapper.toWooCommerceStatus(PublishStatus.published), 'publish');
|
||||
});
|
||||
|
||||
test('pendingReview throws ArgumentError', () {
|
||||
expect(
|
||||
() => WordPressProductMapper.toWooCommerceStatus(PublishStatus.pendingReview),
|
||||
throwsA(isA<ArgumentError>()),
|
||||
);
|
||||
test('pendingReview -> "pending"', () {
|
||||
expect(WordPressProductMapper.toWooCommerceStatus(PublishStatus.pendingReview), 'pending');
|
||||
});
|
||||
|
||||
test('unpublished throws ArgumentError', () {
|
||||
|
|
|
|||
|
|
@ -374,10 +374,7 @@ void main() {
|
|||
|
||||
final repository = WordPressProductPublishingRepository(apiClient: apiClient);
|
||||
|
||||
expect(
|
||||
() => repository.updateProductStatus('1', PublishStatus.pendingReview),
|
||||
throwsA(isA<ArgumentError>()),
|
||||
);
|
||||
// Only unpublished has no WooCommerce equivalent.
|
||||
expect(
|
||||
() => repository.updateProductStatus('1', PublishStatus.unpublished),
|
||||
throwsA(isA<ArgumentError>()),
|
||||
|
|
|
|||
Loading…
Reference in New Issue