Files
jiu/client/test/golden/product_detail_golden_test.dart
T
wangjia 355ac0ebc3 feat(client): 商品详情升级富数据版(KPI + 仓库分布 + 流水)
接后端新能力:listInventory 加 productId 透传;详情异步拉本商品库存+流水(失败
静默不阻塞主数据),渲染原型富数据三件套:
- 3 KPI 卡(当前库存 / 库存货值=库存×进价 / 近30天出库=出库流水30天聚合)
- 各仓库库存分布(按仓库分组 数量+货值)
- 近期流水(StatusPill 入/出 + 增减量,最近 8 条)
价格历史待后端第二步。整屏 golden ×三主题入回归闸。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSKEiHsvauyxYUW2itzUXX
2026-06-25 15:34:57 +08:00

93 lines
3.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:jiu_client/core/models/page_result.dart';
import 'package:jiu_client/models/product.dart';
import 'package:jiu_client/models/inventory.dart';
import 'package:jiu_client/providers/product_provider.dart';
import 'package:jiu_client/providers/inventory_provider.dart';
import 'package:jiu_client/repositories/product_repository.dart';
import 'package:jiu_client/repositories/inventory_repository.dart';
import 'package:jiu_client/screens/products/product_detail_screen.dart';
import '../support/golden_harness.dart';
/// design-distill 阶段4:商品详情「富数据版」golden × 三主题。
/// 验证 3 KPI(当前库存/货值/近30天出库)+ 各仓库分布 + 近期流水(接后端
/// /inventory?product_id + /inventory/logs?product_id 聚合)。
/// 更新基准:flutter test --update-goldens test/golden/product_detail_golden_test.dart
class _FakeProductRepo implements ProductRepository {
@override
Future<Product> getDetail(int id) async => const Product(
id: 1, code: 'MT-FT-500', name: '茅台 飞天 53°', unit: '',
series: '飞天', spec: '500ml×6', brand: '茅台',
purchasePrice: 2680, categoryName: '酱香');
@override
dynamic noSuchMethod(Invocation invocation) =>
super.noSuchMethod(invocation);
}
class _FakeInvRepo implements InventoryRepository {
@override
Future<PageResult<Inventory>> listInventory({
int? warehouseId,
int? productId,
String? keyword,
List<String>? series,
List<String>? spec,
int page = 1,
int pageSize = 50,
}) async =>
const PageResult(data: [
Inventory(id: 1, productId: 1, quantity: 92, warehouseName: '主仓'),
Inventory(id: 2, productId: 1, quantity: 28, warehouseName: '二号仓'),
Inventory(id: 3, productId: 1, quantity: 8, warehouseName: '冷藏库'),
], total: 3, page: 1, pageSize: 1000);
@override
Future<PageResult<InventoryLog>> listLogs({
int? warehouseId,
int? productId,
int page = 1,
int pageSize = 50,
}) async =>
const PageResult(data: [
InventoryLog(
warehouseId: 1, productId: 1, direction: 'in', quantity: 24,
createdAt: '2026-06-20T09:00:00'),
InventoryLog(
warehouseId: 1, productId: 1, direction: 'out', quantity: 12,
createdAt: '2026-06-18T09:00:00'),
InventoryLog(
warehouseId: 2, productId: 1, direction: 'out', quantity: 18,
createdAt: '2026-06-15T09:00:00'),
InventoryLog(
warehouseId: 1, productId: 1, direction: 'in', quantity: 36,
createdAt: '2026-06-12T09:00:00'),
], total: 4, page: 1, pageSize: 100);
@override
dynamic noSuchMethod(Invocation invocation) =>
super.noSuchMethod(invocation);
}
List<Override> _overrides() => [
productRepositoryProvider.overrideWithValue(_FakeProductRepo()),
inventoryRepositoryProvider.overrideWithValue(_FakeInvRepo()),
];
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
SharedPreferences.setMockInitialValues({});
goldenAcrossThemes(
'product detail 富数据(桌面)',
goldenPrefix: 'product_detail',
child: () => const Scaffold(body: ProductDetailScreen(productId: 1)),
overrides: _overrides,
logical: const Size(1100, 1300),
);
}