Files
ReelShort/test/notice/notices_page_test.dart
T

170 lines
4.9 KiB
Dart

// Author: fruit.com.cn
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/data/models/notice.dart';
import 'package:fruitshorts/core/data/remote/api_client.dart';
import 'package:fruitshorts/core/data/repositories/notice_repository.dart';
import 'package:fruitshorts/core/formatting/localized_date_time.dart';
import 'package:fruitshorts/core/widgets/empty_state.dart';
import 'package:fruitshorts/l10n/app_localizations.dart';
import 'package:fruitshorts/pages/notices/notices_page.dart';
import 'package:provider/provider.dart';
import '../support/fake_notice_repository.dart';
void main() {
testWidgets('should_render_first_page_and_append_next_page', (tester) async {
final repository = FakeNoticeRepository(
pages: [
NoticePage(total: 3, notices: [_notice('1'), _notice('2')]),
NoticePage(total: 3, notices: [_notice('3')]),
],
);
await _pump(tester, repository);
expect(find.text('Notice 1'), findsOneWidget);
expect(find.byKey(const Key('notices-load-more')), findsOneWidget);
await tester.tap(find.byKey(const Key('notices-load-more')));
await tester.pumpAndSettle();
expect(find.text('Notice 3'), findsOneWidget);
expect(repository.calls.last.pageIndex, 2);
});
testWidgets('should_show_empty_state_when_no_notices_exist', (tester) async {
await _pump(
tester,
FakeNoticeRepository(pages: [const NoticePage(total: 0)]),
);
expect(find.byKey(const Key('notices-empty')), findsOneWidget);
expect(find.byType(EmptyState), findsOneWidget);
});
testWidgets('should_show_backend_error_and_retry_when_page_fails', (
tester,
) async {
await _pump(
tester,
FakeNoticeRepository(error: const ApiException(500, '公告加载失败')),
);
expect(find.text('公告加载失败'), findsOneWidget);
expect(find.byKey(const Key('notices-retry')), findsOneWidget);
});
testWidgets('should_show_loading_state_before_first_page_arrives', (
tester,
) async {
final repository = FakeNoticeRepository(
pages: [const NoticePage(total: 0)],
delay: const Duration(milliseconds: 50),
);
await tester.pumpWidget(_app(repository));
await tester.pump();
expect(find.byKey(const Key('notices-loading')), findsOneWidget);
await tester.pumpAndSettle();
});
testWidgets('should_open_detail_with_newlines_and_indonesian_time', (
tester,
) async {
final notice = _notice(
'2111012753262120962',
content: 'Line one\nLine two',
);
await _pump(
tester,
FakeNoticeRepository(
pages: [
NoticePage(total: 1, notices: [notice]),
],
),
);
await tester.tap(
find.byKey(const ValueKey('notice-item-2111012753262120962')),
);
await tester.pumpAndSettle();
expect(find.byKey(const Key('notice-detail-close')), findsOneWidget);
expect(
tester
.widget<SelectableText>(
find.byKey(const Key('notice-detail-content')),
)
.data,
'Line one\nLine two',
);
final context = tester.element(
find.byKey(const Key('notice-detail-content')),
);
final expected = formatLocalizedDateTime(context, notice.publishAt!);
expect(
find.descendant(
of: find.byType(AlertDialog),
matching: find.text(expected),
),
findsOneWidget,
);
});
testWidgets('should_scroll_long_detail_without_overflow_on_small_screen', (
tester,
) async {
tester.view.physicalSize = const Size(320, 568);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
final notice = _notice(
'1',
title: 'A very long notice title that wraps across several lines',
content: List.filled(40, 'Long notice content line.').join('\n'),
);
await _pump(
tester,
FakeNoticeRepository(
pages: [
NoticePage(total: 1, notices: [notice]),
],
),
);
await tester.tap(find.byKey(const ValueKey('notice-item-1')));
await tester.pumpAndSettle();
expect(find.byKey(const Key('notice-detail-content')), findsOneWidget);
expect(tester.takeException(), isNull);
});
}
AppNotice _notice(
String id, {
String? title,
String content = 'Notice content',
}) => AppNotice(
id: id,
title: title ?? 'Notice $id',
content: content,
type: 'SYSTEM',
publishAt: DateTime(2026, 10, 17, 12, 30),
);
Future<void> _pump(WidgetTester tester, NoticeRepository repository) async {
await tester.pumpWidget(_app(repository));
await tester.pumpAndSettle();
}
Widget _app(NoticeRepository repository) => Provider<NoticeRepository>.value(
value: repository,
child: MaterialApp(
locale: const Locale('id'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: const NoticesPage(),
),
);