import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pangolin_vpn/l10n/strings_en.dart'; import 'package:pangolin_vpn/screens/notifications_page.dart'; import 'package:pangolin_vpn/services/notices_api.dart'; import 'package:pangolin_vpn/state/notices_provider.dart'; import '../helpers/harness.dart'; void main() { testWidgets('通知页真实数据渲染:type pill + 未读红点 + version 去更新按钮 + 进页清读', (tester) async { final now = DateTime.now(); final data = NoticesData(items: [ NoticeItem( id: 1, type: 'reward', titleZh: '测试到账', titleEn: 'Reward credited', bodyZh: '你获得了 7 天会员', bodyEn: 'You earned 7 days membership', link: '', publishedAt: now, unread: true, ), NoticeItem( id: 2, type: 'version', titleZh: '新版本发布', titleEn: 'New version available', bodyZh: '', bodyEn: '', link: '', publishedAt: now, unread: false, ), NoticeItem( id: 3, type: 'news', titleZh: '产品新闻', titleEn: 'Product news', bodyZh: '', bodyEn: '', link: '', publishedAt: now, unread: false, ), ], unreadCount: 1); final fake = _FakeNotifier(data); await tester.binding.setSurfaceSize(const Size(420, 900)); addTearDown(() => tester.binding.setSurfaceSize(null)); await tester.pumpWidget(wrapThemed( NotificationsScreen(t: StringsEn()), overrides: [noticesProvider.overrideWith(() => fake)], )); await tester.pumpAndSettle(); // reward 行 type pill 文案(StringsEn 的 'Credited')在。 expect(find.text('Credited'), findsOneWidget); // reward 行未读红点在(fake.markAllRead 不改写数据,仅记录调用)。 expect(find.byKey(const ValueKey('notif-unread-1')), findsOneWidget); // version 行「去更新」按钮在(t.lang.updateNow == 'Update now')。 expect(find.text('Update now'), findsOneWidget); // 首帧后调用了 markAllRead()。 expect(fake.markAllReadCalled, isTrue); }); } /// 照 invite_page_test.dart 的 `_FakeNotifier extends InviteNotifier` 模式:override /// build() 直接返回固定数据;markAllRead 只记录调用、不改写数据(便于断言未读态渲染 /// 与「已调用清读」两件事互不干扰)。 class _FakeNotifier extends NoticesNotifier { _FakeNotifier(this._v); final NoticesData _v; bool markAllReadCalled = false; @override Future build() async => _v; @override Future markAllRead() async { markAllReadCalled = true; } }