修复用户端订单列表生命周期与刷新

This commit is contained in:
czl231
2026-09-05 20:49:50 +08:00
parent cec02c8887
commit 1dd21e6df1
8 changed files with 378 additions and 43 deletions

View File

@@ -0,0 +1,97 @@
// 功能描述:验证订单退款成功后的跨标签刷新行为。
// 版本1.0.0
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:user_app/data/repositories/client_repository.dart';
import 'package:user_app/data/services/api_client.dart';
import 'package:user_app/domain/models/client_models.dart';
import 'package:user_app/ui/features/orders/orders_page.dart';
/// 提供无需网络的订单数据并记录各列表刷新次数。
class _FakeRepository extends ClientRepository {
_FakeRepository() : super(ApiClient(() => '', baseUrl: 'https://api.example.com'));
int shopCalls = 0;
int gasCalls = 0;
int refundCalls = 0;
int ticketCalls = 0;
int createRefundCalls = 0;
@override
Future<List<ClientRecord>> shopOrders() async {
shopCalls += 1;
return const [
ClientRecord(
identity: 'shop-1',
title: '商城订单一',
subtitle: '',
status: 18,
raw: {
'items': [
{'identity': 'item-1', 'quantity': 1},
],
},
),
];
}
@override
Future<List<ClientRecord>> gasOrders() async {
gasCalls += 1;
return const [];
}
@override
Future<List<ClientRecord>> refunds() async {
refundCalls += 1;
return const [];
}
@override
Future<List<ClientRecord>> tickets() async {
ticketCalls += 1;
return const [];
}
@override
Future<void> createRefund({
required String business,
required String identity,
required String requestNo,
required String reason,
required List<Map<String, Object?>> items,
}) async {
createRefundCalls += 1;
}
}
void main() {
testWidgets('退款成功后刷新商城订单和退款记录', (tester) async {
final repository = _FakeRepository();
await tester.pumpWidget(
MaterialApp(home: OrdersPage(repository: repository)),
);
await tester.pumpAndSettle();
await tester.tap(find.text('退款'));
await tester.pumpAndSettle();
expect(repository.refundCalls, 1);
await tester.tap(find.text('商城'));
await tester.pumpAndSettle();
expect(repository.shopCalls, 1);
await tester.tap(find.text('商城订单一'));
await tester.pumpAndSettle();
await tester.tap(find.text('申请退款'));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField), '商品问题');
await tester.tap(find.text('提交'));
await tester.pumpAndSettle();
expect(repository.createRefundCalls, 1);
expect(repository.shopCalls, 2);
expect(repository.refundCalls, 2);
expect(tester.takeException(), isNull);
});
}

View File

@@ -0,0 +1,45 @@
// 功能描述:验证记录列表在父组件重建和刷新标记变化时的生命周期。
// 版本1.0.0
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:user_app/domain/models/client_models.dart';
import 'package:user_app/ui/features/shared/record_list_page.dart';
void main() {
testWidgets('普通重建不重复请求,刷新标记变化才重新加载', (tester) async {
var calls = 0;
var refreshToken = 0;
late StateSetter rebuild;
Future<List<ClientRecord>> loader() async {
calls += 1;
return const [];
}
await tester.pumpWidget(
MaterialApp(
home: StatefulBuilder(
builder: (context, setState) {
rebuild = setState;
return RecordListPage(
title: '测试列表',
eyebrow: '测试',
sourceKey: 'records',
loader: loader,
refreshToken: refreshToken,
);
},
),
),
);
await tester.pumpAndSettle();
expect(calls, 1);
rebuild(() {});
await tester.pumpAndSettle();
expect(calls, 1);
rebuild(() => refreshToken += 1);
await tester.pumpAndSettle();
expect(calls, 2);
});
}

View File

@@ -0,0 +1,52 @@
// 功能描述:验证记录列表异步请求的代次与销毁保护。
// 版本1.0.0
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:user_app/domain/models/client_models.dart';
import 'package:user_app/ui/features/shared/record_list_view_model.dart';
ClientRecord _record(String identity) => ClientRecord(
identity: identity,
title: identity,
subtitle: '',
raw: const {},
);
void main() {
test('后发请求优先,旧结果不会覆盖新数据', () async {
final first = Completer<List<ClientRecord>>();
final second = Completer<List<ClientRecord>>();
var calls = 0;
final viewModel = RecordListViewModel(() {
calls += 1;
return calls == 1 ? first.future : second.future;
});
final firstLoad = viewModel.load();
final secondLoad = viewModel.load();
second.complete([_record('new')]);
await secondLoad;
first.complete([_record('old')]);
await firstLoad;
expect(viewModel.records.single.identity, 'new');
expect(viewModel.loading, isFalse);
viewModel.dispose();
});
test('销毁后异步结果不会继续通知监听器', () async {
final pending = Completer<List<ClientRecord>>();
final viewModel = RecordListViewModel(() => pending.future);
var notifications = 0;
viewModel.addListener(() => notifications += 1);
final load = viewModel.load();
expect(notifications, 1);
viewModel.dispose();
pending.complete([_record('late')]);
await load;
expect(notifications, 1);
});
}