- 「下午好,豆豆」改真实展示名(`nickname ?? username`,与资料页共用同一个 ProfileController:一次 /me 供两个消费点,改昵称后两处一起变)。资料未到手 时退化为不称名的「下午好 👋」,不编造假名。 - 按 ADR-022 决策 D3.5-1 钉死范围,**其余首页 demo 一律不动**,并在代码里逐项 标注为「刻意保留的 demo 占位」:天气/位置(需接外部服务)、圈子=话题 (ADR-018 剪出)、促销卡(M5 服务域)、搜索与本地服务段。 widget 测试同时钉住「保留项仍在」,避免后续以「顺手清理」越出拍板范围。 - 新增 compose 真链路桌面实测脚本 `integration_test/profile_avatar_live_test.dart` (默认跳过,`PATBOND_PROFILE_LIVE=1 ... -d linux` 开启):UI 注册 → 登录 → 资料真实化 → 设昵称 → 传用户头像 → Feed 作者名同步 → 宠物头像,逐步截图。 只有选图与压缩两层是桌面替身,createUpload / 预签名 PUT / confirm / PATCH 全为生产实现。两处实测坑写进注释:Feed 作者名有服务端 60s 缓存滞后; 1×1 的极小图能上传能下载但 Flutter 解码器拒绝,会被误判成「没传上」。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:patbond_flutter/core/network/api_exception.dart';
|
||||
import 'package:patbond_flutter/core/theme/app_theme.dart';
|
||||
import 'package:patbond_flutter/features/auth/auth_models.dart';
|
||||
import 'package:patbond_flutter/features/community/community_controller.dart';
|
||||
import 'package:patbond_flutter/features/home/home_page.dart';
|
||||
import 'package:patbond_flutter/features/profile/profile_controller.dart';
|
||||
import 'package:patbond_flutter/state/app_state.dart';
|
||||
|
||||
import '../../helpers/auth_test_helpers.dart';
|
||||
import '../../helpers/community_test_helpers.dart';
|
||||
|
||||
/// T3.5-10 首页问候语真实化。
|
||||
///
|
||||
/// ADR-022 决策 D3.5-1:本迭代**只做问候语**,天气 / 位置 / 圈子 / 促销卡
|
||||
/// 刻意保留为 demo 占位——本文件同时钉住「保留项仍在」,避免后续有人以
|
||||
/// 「顺手清理」为名越出拍板范围(真要动它们得先改 ADR)。
|
||||
void main() {
|
||||
late FakeCommunityRepository community;
|
||||
late CommunityController feed;
|
||||
|
||||
setUp(() {
|
||||
community = FakeCommunityRepository();
|
||||
community.onFeed = (_, _) async => feedPage(const []);
|
||||
feed = CommunityController(repository: community);
|
||||
});
|
||||
|
||||
Future<ProfileController> pumpHome(
|
||||
WidgetTester tester, {
|
||||
required FakeAuthRepository auth,
|
||||
}) async {
|
||||
tester.view.physicalSize = const Size(700, 1600);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.reset);
|
||||
final profile = ProfileController(
|
||||
authRepository: auth,
|
||||
communityRepository: community,
|
||||
);
|
||||
addTearDown(profile.dispose);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: buildAppTheme(),
|
||||
home: Scaffold(
|
||||
body: HomePage(
|
||||
appState: AppState(),
|
||||
communityController: feed,
|
||||
profileController: profile,
|
||||
onOpenServices: (_) {},
|
||||
onOpenCompose: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return profile;
|
||||
}
|
||||
|
||||
/// 当前时段问候前缀(与页面实现同一套阈值;避免测试跟着挂钟漂移)。
|
||||
String greetingPrefix([DateTime? now]) {
|
||||
final hour = (now ?? DateTime.now()).hour;
|
||||
if (hour < 6) return '夜深了';
|
||||
if (hour < 11) return '早上好';
|
||||
if (hour < 14) return '中午好';
|
||||
if (hour < 18) return '下午好';
|
||||
return '晚上好';
|
||||
}
|
||||
|
||||
testWidgets('有昵称:问候语用昵称,demo 宠物名「豆豆」不再出现', (tester) async {
|
||||
final profile = await pumpHome(
|
||||
tester,
|
||||
auth: FakeAuthRepository(
|
||||
meHandler: () async => buildProfile(username: 'llx', nickname: '小柴'),
|
||||
),
|
||||
);
|
||||
await profile.refresh();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('${greetingPrefix()},小柴 👋'), findsOneWidget);
|
||||
expect(find.textContaining('豆豆'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('无昵称:问候语回退 username(与资料页同规则)', (tester) async {
|
||||
final profile = await pumpHome(
|
||||
tester,
|
||||
auth: FakeAuthRepository(
|
||||
meHandler: () async => buildProfile(username: 'llx'),
|
||||
),
|
||||
);
|
||||
await profile.refresh();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('${greetingPrefix()},llx 👋'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('资料未到手:只问候不称名(不编造假名)', (tester) async {
|
||||
await pumpHome(
|
||||
tester,
|
||||
auth: FakeAuthRepository(
|
||||
meHandler: () async => throw const ApiNetworkException('断网'),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('${greetingPrefix()} 👋'), findsOneWidget);
|
||||
// 不出现「问候,某某」形态(更不会退回 demo 宠物名)。
|
||||
expect(find.textContaining('${greetingPrefix()},'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('改昵称后首页同步(与资料页共用同一控制器,无需手动刷新)', (tester) async {
|
||||
final auth = FakeAuthRepository(
|
||||
meHandler: () async => buildProfile(username: 'llx'),
|
||||
updateMeHandler: (_) async =>
|
||||
buildProfile(username: 'llx', nickname: '小柴'),
|
||||
);
|
||||
final profile = await pumpHome(tester, auth: auth);
|
||||
await profile.refresh();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('${greetingPrefix()},llx 👋'), findsOneWidget);
|
||||
|
||||
await profile.save(
|
||||
const UpdateMeRequest(nickname: PatchField<String>.value('小柴')),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('${greetingPrefix()},小柴 👋'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('刻意保留的 demo 占位仍在(ADR-022 钉死的范围)', (tester) async {
|
||||
final profile = await pumpHome(
|
||||
tester,
|
||||
auth: FakeAuthRepository(meHandler: () async => buildProfile()),
|
||||
);
|
||||
await profile.refresh();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// 天气 / 位置(外部服务未接)。
|
||||
expect(find.text('北京 · 朝阳区'), findsOneWidget);
|
||||
expect(find.text('28°C 晴'), findsOneWidget);
|
||||
// 圈子 = 话题(ADR-018 剪出)。
|
||||
expect(find.text('柴犬圈'), findsOneWidget);
|
||||
// 促销卡(M5 服务域)。
|
||||
expect(find.text('新用户首单立减 ¥20'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user