189 lines
6.8 KiB
Dart
189 lines
6.8 KiB
Dart
// 功能描述:购物车服务端确认、失效处理、管理删除与多商品重试回归;版本:1.0.0。
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import 'package:user_app/domain/models/cart_item.dart';
|
||
import 'package:user_app/domain/models/primary_models.dart';
|
||
import 'package:user_app/domain/models/shipping_address.dart';
|
||
import 'package:user_app/data/services/api_client.dart';
|
||
import 'package:user_app/ui/features/shop/cart_page.dart';
|
||
import 'package:user_app/ui/core/app_theme.dart';
|
||
import 'package:user_app/ui/features/shop/checkout_page.dart';
|
||
import 'package:user_app/ui/features/shop/product_detail_page.dart';
|
||
import '../support/a1_fixture.dart';
|
||
|
||
class _Repo extends A1FixtureRepository {
|
||
List<CartItem> items = [
|
||
const CartItem(
|
||
product: ProductSummary(identity: 'p1', name: '测试商品一', price: 1999, stock: 4),
|
||
quantity: 1,
|
||
selected: true,
|
||
available: true,
|
||
revision: 'one',
|
||
),
|
||
const CartItem(
|
||
product: ProductSummary(identity: 'p2', name: '测试商品二', price: 3000, stock: 5),
|
||
quantity: 2,
|
||
selected: true,
|
||
available: true,
|
||
revision: 'two',
|
||
),
|
||
const CartItem(
|
||
product: ProductSummary(identity: 'p3', name: '失效商品', price: 1000, stock: 0),
|
||
quantity: 1,
|
||
selected: true,
|
||
available: false,
|
||
revision: 'three',
|
||
),
|
||
];
|
||
final submitted = <({String request, int amount, List<CartItem> items})>[];
|
||
int writes = 0;
|
||
bool failWrite = false;
|
||
final targets = <int>[];
|
||
@override
|
||
Future<List<CartItem>> cart() async => List.of(items);
|
||
@override
|
||
Future<CartItem> cartItem(String identity) async =>
|
||
items.firstWhere((i) => i.product.identity == identity);
|
||
@override
|
||
Future<CartItem> setCartItem(
|
||
CartItem item, {
|
||
required int quantity,
|
||
required bool selected,
|
||
}) async {
|
||
writes++;
|
||
targets.add(quantity);
|
||
final updated = CartItem(
|
||
product: item.product,
|
||
quantity: quantity,
|
||
selected: selected,
|
||
available: item.available,
|
||
revision: 'v$writes',
|
||
);
|
||
items = [
|
||
for (final old in items)
|
||
if (old.product.identity != item.product.identity) old else if (quantity > 0) updated,
|
||
];
|
||
if (failWrite) {
|
||
failWrite = false;
|
||
throw const ApiException(500, '结果待确认');
|
||
}
|
||
return updated;
|
||
}
|
||
|
||
@override
|
||
Future<Map<String, Object?>> submitCartOrder({
|
||
required String requestNo,
|
||
required List<CartItem> items,
|
||
required ShippingAddress address,
|
||
required int expectedAmount,
|
||
String remark = '',
|
||
}) async {
|
||
submitted.add((request: requestNo, amount: expectedAmount, items: List.of(items)));
|
||
if (submitted.length == 1) throw const ApiException(500, '提交结果未知');
|
||
return {'identity': 'order'};
|
||
}
|
||
}
|
||
|
||
Future<GoRouter> _show(WidgetTester tester, _Repo repo, {String path = '/cart'}) async {
|
||
final router = GoRouter(
|
||
initialLocation: path,
|
||
routes: [
|
||
GoRoute(
|
||
path: '/cart',
|
||
builder: (_, s) => CartPage(repository: repo),
|
||
),
|
||
GoRoute(
|
||
path: '/cart/checkout',
|
||
builder: (_, s) => CheckoutPage(repository: repo, productIdentity: '', fromCart: true),
|
||
),
|
||
GoRoute(
|
||
path: '/products/p1',
|
||
builder: (_, s) => ProductDetailPage(repository: repo, identity: 'p1'),
|
||
),
|
||
GoRoute(
|
||
path: '/orders',
|
||
builder: (_, s) => const Scaffold(body: Text('订单列表')),
|
||
),
|
||
GoRoute(
|
||
path: '/payment/shop/:identity',
|
||
builder: (_, s) => Scaffold(body: Text('支付订单 ${s.pathParameters['identity']}')),
|
||
),
|
||
],
|
||
);
|
||
addTearDown(router.dispose);
|
||
await tester.pumpWidget(MaterialApp.router(routerConfig: router, theme: AppTheme.light()));
|
||
await tester.pumpAndSettle();
|
||
return router;
|
||
}
|
||
|
||
void main() {
|
||
testWidgets('服务端数量与失败重读,全选、失效取消、管理删除', (tester) async {
|
||
final repo = _Repo();
|
||
await _show(tester, repo);
|
||
expect(find.text('合计:¥79.99'), findsOneWidget);
|
||
expect(find.text('商品已失效'), findsOneWidget);
|
||
repo.failWrite = true;
|
||
await tester.tap(find.byTooltip('增加 测试商品一'));
|
||
await tester.pumpAndSettle();
|
||
expect(repo.items.first.quantity, 2);
|
||
expect(find.text('合计:¥99.98'), findsOneWidget);
|
||
expect(find.text('结果待确认'), findsOneWidget);
|
||
await tester.tap(find.text('重新加载'));
|
||
await tester.pumpAndSettle();
|
||
final invalid = find.descendant(
|
||
of: find.bySemanticsLabel('选择 失效商品'),
|
||
matching: find.byType(Checkbox),
|
||
);
|
||
await tester.scrollUntilVisible(invalid, 180);
|
||
await tester.drag(find.byType(Scrollable).first, const Offset(0, -120));
|
||
await tester.pumpAndSettle();
|
||
await tester.tap(invalid);
|
||
await tester.pumpAndSettle();
|
||
expect(repo.items.last.selected, isFalse);
|
||
await tester.tap(find.text('管理'));
|
||
await tester.pumpAndSettle();
|
||
final all = find.descendant(of: find.bySemanticsLabel('全选商品'), matching: find.byType(Checkbox));
|
||
await tester.tap(all);
|
||
await tester.pumpAndSettle();
|
||
await tester.tap(find.text('删除(3)'));
|
||
await tester.pumpAndSettle();
|
||
await tester.tap(find.widgetWithText(TextButton, '删除'));
|
||
await tester.pumpAndSettle();
|
||
expect(repo.items, isEmpty);
|
||
expect(find.text('购物车还是空的'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('多商品确认与未知结果重试保留同一请求和整数金额', (tester) async {
|
||
final repo = _Repo()..items.removeLast();
|
||
await _show(tester, repo);
|
||
await tester.tap(find.text('去结算(2)'));
|
||
await tester.pumpAndSettle();
|
||
expect(find.text('测试商品一'), findsOneWidget);
|
||
expect(find.text('测试商品二'), findsOneWidget);
|
||
expect(repo.submitted, isEmpty);
|
||
await tester.tap(find.widgetWithText(FilledButton, '提交订单'));
|
||
await tester.pumpAndSettle();
|
||
await tester.tap(find.widgetWithText(FilledButton, '重试提交'));
|
||
await tester.pumpAndSettle();
|
||
expect(repo.submitted.length, 2);
|
||
expect(repo.submitted[0].request, repo.submitted[1].request);
|
||
expect(repo.submitted[0].amount, 7999);
|
||
expect(repo.submitted[1].items.last.quantity, 2);
|
||
expect(find.text('支付订单 order'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('加入购物车未知结果重试不在新数量上重复累加', (tester) async {
|
||
final repo = _Repo()..failWrite = true;
|
||
await _show(tester, repo, path: '/products/p1');
|
||
await tester.ensureVisible(find.text('加入购物车'));
|
||
await tester.tap(find.text('加入购物车'));
|
||
await tester.pumpAndSettle();
|
||
expect(repo.targets, [2]);
|
||
await tester.tap(find.text('重试加入'));
|
||
await tester.pumpAndSettle();
|
||
expect(repo.targets, [2, 2]);
|
||
expect(repo.items.first.quantity, 2);
|
||
});
|
||
}
|