Files
ReelShort/test/comment/drama_comments_sheet_test.dart
T

851 lines
29 KiB
Dart

// Author: fruit.com.cn
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/data/models/comment.dart';
import 'package:fruitshorts/core/data/models/drama.dart';
import 'package:fruitshorts/core/data/remote/api_client.dart';
import 'package:fruitshorts/core/data/remote/auth_api.dart';
import 'package:fruitshorts/core/data/repositories/comment_repository.dart';
import 'package:fruitshorts/core/data/repositories/interaction_repository.dart';
import 'package:fruitshorts/core/data/services/auth_controller.dart';
import 'package:fruitshorts/core/data/services/auth_session_store.dart';
import 'package:fruitshorts/core/platform/haptics.dart';
import 'package:fruitshorts/core/theme/app_colors.dart';
import 'package:fruitshorts/core/widgets/empty_state.dart';
import 'package:fruitshorts/l10n/app_localizations.dart';
import 'package:fruitshorts/pages/player/widgets/drama_comments_sheet.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
import '../support/fake_http_adapter.dart';
import '../support/fake_haptics.dart';
import '../support/fake_interaction_repositories.dart';
const _drama = Drama(
id: '5',
title: 'The Signal',
synopsis: 'synopsis',
posterUrl: '/files/5.jpg',
);
void main() {
setUp(() {
SharedPreferencesAsyncPlatform.instance =
InMemorySharedPreferencesAsync.withData({
'auth.accessToken': 'test-access',
'auth.refreshToken': 'test-refresh',
'auth.user': '{"uid":"9","nickname":"Me","avatarUrl":""}',
});
});
testWidgets('should_gate_guest_comment_input_without_focus', (tester) async {
SharedPreferencesAsyncPlatform.instance =
InMemorySharedPreferencesAsync.empty();
final repository = FakeCommentRepository(pages: [const CommentPage()]);
await _pumpSheet(tester, repository: repository);
final placeholder = find.byKey(const Key('comment-input-placeholder'));
expect(placeholder, findsOneWidget);
expect(find.text('Sign in to comment'), findsOneWidget);
await tester.tap(placeholder);
await tester.pumpAndSettle();
expect(find.text('Sign in to continue'), findsOneWidget);
await tester.tap(find.text('Not now'));
await tester.pumpAndSettle();
expect(find.byKey(const Key('comment-composer-sheet')), findsNothing);
expect(placeholder, findsOneWidget);
});
testWidgets('should_focus_comment_input_after_successful_login', (
tester,
) async {
SharedPreferencesAsyncPlatform.instance =
InMemorySharedPreferencesAsync.empty();
final repository = FakeCommentRepository(pages: [const CommentPage()]);
final auth = await _pumpSheet(tester, repository: repository);
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
await tester.pump();
await tester.pump(const Duration(milliseconds: 300));
await tester.tap(find.text('Sign In'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 300));
(auth as _TestAuthController).setSignedIn(true);
await tester.pump();
Navigator.of(tester.element(find.byKey(const Key('auth-landing')))).pop();
await tester.pumpAndSettle();
expect(
tester
.widget<TextField>(find.byKey(const Key('comment-composer-input')))
.focusNode
?.hasFocus,
isTrue,
);
});
testWidgets('should_open_composer_sheet_when_fake_send_is_tapped', (
tester,
) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
final repository = FakeCommentRepository(pages: [const CommentPage()]);
await _pumpSheet(tester, repository: repository);
await tester.tap(find.byKey(const Key('comment-post')));
await tester.pumpAndSettle();
final composer = find.byKey(const Key('comment-composer-sheet'));
expect(composer, findsOneWidget);
final compactHeight = tester.getSize(composer).height;
expect(compactHeight, inInclusiveRange(100, 150));
final input = find.byKey(const Key('comment-composer-input'));
expect(input, findsOneWidget);
expect(tester.widget<TextField>(input).focusNode?.hasFocus, isTrue);
expect(
tester.widget<TextField>(input).textAlignVertical,
TextAlignVertical.center,
);
await tester.enterText(input, 'First line\nSecond line');
await tester.pump();
expect(tester.getSize(composer).height, greaterThan(compactHeight));
expect(tester.getSize(composer).height, lessThan(844 / 2));
final multilineField = tester.widget<TextField>(input);
expect(multilineField.textAlignVertical, TextAlignVertical.top);
expect(multilineField.style?.height, 1.4);
});
testWidgets('should_gate_guest_post_without_creating_comment', (
tester,
) async {
SharedPreferencesAsyncPlatform.instance =
InMemorySharedPreferencesAsync.empty();
final repository = FakeCommentRepository(pages: [const CommentPage()]);
await _pumpSheet(tester, repository: repository);
await tester.tap(find.byKey(const Key('comment-post')));
await tester.pumpAndSettle();
expect(find.text('Sign in to continue'), findsOneWidget);
expect(repository.publishCalls, isEmpty);
});
testWidgets('should_show_server_comments_and_append_next_cursor_page', (
tester,
) async {
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [
commentFixture(id: '10', content: 'Great episode', replyCount: 1),
],
nextCursor: '10',
hasMore: true,
),
CommentPage(
comments: [commentFixture(id: '9', content: 'Second page')],
),
],
);
await _pumpSheet(tester, repository: repository);
expect(find.text('Great episode'), findsOneWidget);
expect(find.byKey(const Key('comment-list-load-more')), findsOneWidget);
expect(
tester.widget<Text>(find.byKey(const Key('comment-count-label'))).data,
'Comments',
);
await tester.tap(find.byKey(const Key('comment-list-load-more')));
await tester.pumpAndSettle();
expect(find.text('Second page'), findsOneWidget);
expect(find.text('2 comments'), findsOneWidget);
expect(repository.pageCalls.last.cursor, '10');
});
testWidgets('should_show_server_total_that_includes_replies', (tester) async {
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [
commentFixture(id: '10', content: 'Root one'),
commentFixture(id: '11', content: 'Root two'),
],
),
],
countResult: 9,
);
await _pumpSheet(tester, repository: repository);
expect(find.text('9 comments'), findsOneWidget);
expect(repository.countCalls, ['5']);
});
testWidgets('should_fall_back_to_exact_count_when_server_total_fails', (
tester,
) async {
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [
commentFixture(id: '10', content: 'Root one'),
commentFixture(id: '11', content: 'Root two'),
],
),
],
countError: StateError('offline'),
);
await _pumpSheet(tester, repository: repository);
expect(find.text('2 comments'), findsOneWidget);
});
testWidgets('should_show_compact_empty_state_when_no_comments_exist', (
tester,
) async {
final repository = FakeCommentRepository(pages: [const CommentPage()]);
await _pumpSheet(tester, repository: repository);
expect(find.byKey(const Key('comment-list-empty')), findsOneWidget);
expect(find.byType(EmptyState), findsOneWidget);
expect(tester.widget<EmptyState>(find.byType(EmptyState)).compact, isTrue);
expect(find.text('No comments yet.'), findsOneWidget);
});
testWidgets(
'should_keep_input_and_show_backend_message_when_comment_is_blocked',
(tester) async {
final repository = FakeCommentRepository(
pages: [const CommentPage()],
publishError: const ApiException(400, '评论包含敏感词'),
);
await _pumpSheet(
tester,
repository: repository,
locale: const Locale('zh'),
);
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const Key('comment-composer-input')),
'blocked text',
);
await tester.pump();
await tester.tap(find.byKey(const Key('comment-composer-post')));
await tester.pumpAndSettle();
expect(find.text('评论包含敏感词'), findsOneWidget);
final field = tester.widget<TextField>(
find.byKey(const Key('comment-composer-input')),
);
expect(field.controller?.text, 'blocked text');
expect(find.text('发表成功'), findsNothing);
// 发送失败时保留输入与焦点,用户可以直接改完再发。
expect(field.focusNode?.hasFocus, isTrue);
},
);
testWidgets('should_close_composer_after_successful_post', (tester) async {
addTearDown(tester.view.resetViewInsets);
final repository = FakeCommentRepository(
pages: [const CommentPage()],
publishResult: commentFixture(id: '11', content: 'Nice demo', mine: true),
countResult: 1,
);
await _pumpSheet(
tester,
repository: repository,
locale: const Locale('zh'),
);
final input = find.byKey(const Key('comment-composer-input'));
tester.view.viewInsets = const FakeViewPadding(bottom: 180);
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
await tester.pumpAndSettle();
expect(tester.widget<TextField>(input).focusNode?.hasFocus, isTrue);
await tester.enterText(input, 'Nice demo');
await tester.pump();
expect(
tester
.widget<TextButton>(find.byKey(const Key('comment-composer-post')))
.onPressed,
isNotNull,
);
await tester.tap(find.byKey(const Key('comment-composer-post')));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
expect(find.byKey(const Key('comment-composer-sheet')), findsNothing);
final successOverlay = find.byKey(
const Key('comment-post-success-snackbar'),
);
expect(successOverlay, findsOneWidget);
expect(find.text('发表成功'), findsOneWidget);
expect(find.byType(SnackBar), findsNothing);
final capsule = tester.widget<Container>(successOverlay);
final decoration = capsule.decoration! as BoxDecoration;
expect(decoration.color, Colors.black.withValues(alpha: 0.82));
expect(decoration.borderRadius, BorderRadius.circular(8));
expect(
capsule.padding,
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
);
final successText = tester.widget<Text>(find.text('发表成功'));
expect(successText.style?.color, Colors.white);
expect(successText.style?.fontSize, 14);
final placeholderRect = tester.getRect(
find.byKey(const Key('comment-input-placeholder')),
);
expect(
tester.getRect(successOverlay).bottom,
lessThan(placeholderRect.top),
);
await tester.pump(const Duration(milliseconds: 1200));
expect(successOverlay, findsNothing);
expect(find.byKey(const Key('comment-list')), findsOneWidget);
expect(find.byKey(const Key('comment-count-label')), findsOneWidget);
expect(find.byKey(const Key('comment-input-placeholder')), findsOneWidget);
expect(repository.countCalls, ['5', '5']);
});
testWidgets('should_keep_comment_panel_fixed_and_open_composer_sheet', (
tester,
) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
addTearDown(tester.view.resetViewInsets);
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [commentFixture(id: '10', content: 'A comment')],
),
],
);
await _pumpSheet(tester, repository: repository);
final commentsSheet = find.byKey(const Key('player-comments-sheet'));
final list = find.byKey(const Key('comment-list'));
final placeholder = find.byKey(const Key('comment-input-placeholder'));
final sheetRect = tester.getRect(commentsSheet);
final sheetHeight = tester.getSize(commentsSheet).height;
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
await tester.pumpAndSettle();
final composer = find.byKey(const Key('comment-composer-sheet'));
final composerInput = find.byKey(const Key('comment-composer-input'));
expect(composer, findsOneWidget);
expect(
ModalRoute.of(tester.element(composer))?.barrierColor,
Colors.transparent,
);
final compactHeight = tester.getSize(composer).height;
expect(compactHeight, inInclusiveRange(100, 150));
expect(tester.widget<TextField>(composerInput).focusNode?.hasFocus, isTrue);
final underlyingSheet = find.byKey(
const Key('player-comments-sheet'),
skipOffstage: false,
);
expect(tester.getRect(underlyingSheet), sheetRect);
expect(tester.getSize(underlyingSheet).height, closeTo(sheetHeight, 0.1));
final field = tester.widget<TextField>(composerInput);
expect(field.minLines, 1);
expect(field.maxLines, 4);
expect(field.style?.fontSize, 15);
expect(find.byIcon(Icons.image_outlined), findsNothing);
expect(find.byIcon(Icons.emoji_emotions_outlined), findsNothing);
expect(find.byIcon(Icons.alternate_email_rounded), findsNothing);
tester.view.viewInsets = const FakeViewPadding(bottom: 180);
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
final keyboardUpBottom = tester.getBottomLeft(composer).dy;
expect(tester.getSize(composer).height, closeTo(compactHeight, 0.1));
expect(keyboardUpBottom, closeTo(844 - 180, 0.1));
expect(
tester.getBottomLeft(composerInput).dy,
lessThanOrEqualTo(844 - 180),
);
expect(tester.getRect(underlyingSheet), sheetRect);
tester.view.viewInsets = const FakeViewPadding();
await tester.pumpAndSettle();
expect(composer, findsOneWidget);
expect(tester.getBottomLeft(composer).dy, closeTo(keyboardUpBottom, 0.1));
expect(commentsSheet, findsOneWidget);
expect(list, findsOneWidget);
expect(placeholder, findsOneWidget);
expect(tester.getRect(commentsSheet), sheetRect);
expect(tester.getSize(commentsSheet).height, closeTo(sheetHeight, 0.1));
await tester.tapAt(const Offset(20, 20));
await tester.pumpAndSettle();
expect(composer, findsNothing);
await tester.tap(placeholder);
await tester.pumpAndSettle();
expect(composer, findsOneWidget);
expect(tester.getBottomLeft(composer).dy, closeTo(844, 0.1));
});
testWidgets('should_keep_composer_open_after_input_loses_focus', (
tester,
) async {
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [commentFixture(id: '10', content: 'A comment')],
),
],
);
await _pumpSheet(tester, repository: repository);
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
await tester.pumpAndSettle();
final composer = find.byKey(const Key('comment-composer-sheet'));
final input = find.byKey(const Key('comment-composer-input'));
expect(composer, findsOneWidget);
tester.widget<TextField>(input).focusNode?.unfocus();
await tester.pumpAndSettle();
expect(composer, findsOneWidget);
await tester.tap(input);
await tester.pump();
expect(composer, findsOneWidget);
expect(tester.widget<TextField>(input).focusNode?.hasFocus, isTrue);
});
testWidgets('should_close_composer_before_comment_panel_from_outside', (
tester,
) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
addTearDown(tester.view.resetViewInsets);
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [commentFixture(id: '10', content: 'A comment')],
),
],
);
await _pumpSheet(tester, repository: repository);
final sheet = find.byKey(const Key('player-comments-sheet'));
expect(
find.byKey(const Key('player-comments-dismiss-layer')),
findsOneWidget,
);
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
await tester.pumpAndSettle();
expect(sheet, findsOneWidget);
final composer = find.byKey(const Key('comment-composer-sheet'));
expect(composer, findsOneWidget);
expect(
find.byKey(const Key('comment-composer-dismiss-layer')),
findsOneWidget,
);
final sheetRect = tester.getRect(sheet);
final composerRect = tester.getRect(composer);
await tester.tap(find.byKey(const Key('comment-composer-input')));
await tester.pump();
expect(composer, findsOneWidget);
await tester.tapAt(
Offset(sheetRect.center.dx, (sheetRect.top + composerRect.top) / 2),
);
await tester.pumpAndSettle();
expect(sheet, findsOneWidget);
expect(composer, findsNothing);
await tester.tapAt(const Offset(20, 20));
await tester.pumpAndSettle();
expect(sheet, findsNothing);
});
testWidgets('should_use_polished_comment_sizes', (tester) async {
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [commentFixture(id: '10', content: 'Great episode')],
),
],
);
await _pumpSheet(tester, repository: repository);
expect(
tester.getSize(find.byKey(const Key('comment-post'))),
const Size(38, 38),
);
expect(
tester.getSize(find.byKey(const Key('comment-input-placeholder'))).height,
44,
);
final placeholderLabel = find.byKey(
const Key('comment-input-placeholder-label'),
);
final placeholderSendIcon = find.descendant(
of: find.byKey(const Key('comment-post')),
matching: find.byIcon(Icons.send_rounded),
);
expect(
tester.getCenter(placeholderLabel).dy,
closeTo(tester.getCenter(placeholderSendIcon).dy, 1),
);
expect(find.byKey(const Key('player-comments-drag-handle')), findsNothing);
expect(
tester.getSize(find.byKey(const Key('player-comments-close'))),
const Size(44, 44),
);
expect(
find.descendant(
of: find.byKey(const Key('player-comments-close')),
matching: find.byIcon(Icons.keyboard_arrow_down_rounded),
),
findsOneWidget,
);
final title = tester.widget<Text>(
find.byKey(const Key('comment-count-label')),
);
expect(title.style?.fontSize, 16);
expect(title.style?.fontWeight, FontWeight.w600);
expect(
tester.getCenter(find.byKey(const Key('comment-count-label'))).dx,
closeTo(
tester.getCenter(find.byKey(const Key('player-comments-sheet'))).dx,
0.5,
),
);
final avatar = tester.widget<CircleAvatar>(
find.descendant(
of: find.byKey(const Key('comment-item-10')),
matching: find.byType(CircleAvatar),
),
);
expect(avatar.radius, 20);
final body = tester.widget<Text>(find.text('Great episode'));
expect(body.style?.fontSize, 16);
expect(body.style?.height, 1.4);
final composer = tester.widget<Container>(
find.byKey(const Key('comment-input-placeholder')),
);
final decoration = composer.decoration! as BoxDecoration;
expect(decoration.color, AppColors.commentInput);
expect(decoration.borderRadius, BorderRadius.circular(22));
});
testWidgets('should_hide_report_and_delete_actions', (tester) async {
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [
commentFixture(id: '10', content: 'My comment', mine: true),
],
),
],
);
await _pumpSheet(tester, repository: repository);
expect(
find.byWidgetPredicate(
(widget) =>
widget.key is ValueKey<String> &&
(widget.key! as ValueKey<String>).value.startsWith('comment-menu-'),
),
findsNothing,
);
expect(find.text('Delete comment'), findsNothing);
expect(find.text('Report comment'), findsNothing);
expect(find.byIcon(Icons.more_horiz_rounded), findsNothing);
});
testWidgets('should_use_half_screen_height_for_one_comment', (tester) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [commentFixture(id: '10', content: 'One comment')],
),
],
);
await _pumpSheet(tester, repository: repository);
final sheet = find.byKey(const Key('player-comments-sheet'));
expect(tester.getSize(sheet).height, closeTo(844 / 2, 0.1));
});
testWidgets('should_keep_minimum_height_for_empty_state', (tester) async {
final repository = FakeCommentRepository(pages: [const CommentPage()]);
await _pumpSheet(tester, repository: repository);
expect(
tester.getSize(find.byKey(const Key('player-comments-sheet'))).height,
greaterThanOrEqualTo(220),
);
});
testWidgets('should_keep_long_comment_list_scrollable', (tester) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
final repository = FakeCommentRepository(
pages: [CommentPage(comments: _comments(20))],
);
await _pumpSheet(tester, repository: repository);
final scrollable = find
.descendant(
of: find.byKey(const Key('comment-list')),
matching: find.byType(Scrollable),
)
.first;
final position = tester.state<ScrollableState>(scrollable).position;
expect(position.maxScrollExtent, greaterThan(0));
await tester.drag(
find.byKey(const Key('comment-list')),
const Offset(0, -300),
);
await tester.pumpAndSettle();
expect(position.pixels, greaterThan(0));
expect(
tester.getSize(find.byKey(const Key('player-comments-sheet'))).height,
closeTo(844 / 2, 0.1),
);
});
testWidgets('should_hide_comment_like_controls', (tester) async {
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [commentFixture(id: '10', content: 'No likes shown')],
),
],
);
await _pumpSheet(tester, repository: repository);
expect(
find.byWidgetPredicate(
(widget) =>
widget.key is ValueKey<String> &&
(widget.key! as ValueKey<String>).value.startsWith('comment-like-'),
),
findsNothing,
);
expect(find.byIcon(Icons.favorite_rounded), findsNothing);
expect(find.byIcon(Icons.favorite_border_rounded), findsNothing);
});
testWidgets('should_close_sheet_from_down_arrow', (tester) async {
final repository = FakeCommentRepository(pages: [const CommentPage()]);
await _pumpSheet(tester, repository: repository);
await tester.tap(find.byKey(const Key('player-comments-close')));
await tester.pumpAndSettle();
expect(find.byKey(const Key('player-comments-sheet')), findsNothing);
});
testWidgets('should_show_review_badge_and_server_replaced_content', (
tester,
) async {
final repository = FakeCommentRepository(
pages: [const CommentPage()],
publishResult: commentFixture(
id: '11',
content: 'hello ***',
status: 'pending',
mine: true,
),
);
final haptics = FakeHaptics();
await _pumpSheet(tester, repository: repository, haptics: haptics);
await tester.tap(find.byKey(const Key('comment-input-placeholder')));
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const Key('comment-composer-input')),
'hello badword',
);
await tester.pump();
await tester.tap(find.byKey(const Key('comment-composer-post')));
await tester.pumpAndSettle();
expect(find.byKey(const Key('comment-composer-sheet')), findsNothing);
expect(find.text('hello ***'), findsOneWidget);
expect(find.text('Under review'), findsOneWidget);
expect(haptics.lightImpacts, 1);
expect(find.byKey(const Key('comment-input-placeholder')), findsOneWidget);
});
testWidgets('should_send_parent_id_when_replying_to_top_level_comment', (
tester,
) async {
final root = commentFixture(id: '10', content: 'Root', replyCount: 0);
final repository = FakeCommentRepository(
pages: [
CommentPage(comments: [root]),
],
publishResult: commentFixture(
id: '11',
parentId: '10',
rootId: '10',
content: 'My reply',
mine: true,
),
);
await _pumpSheet(tester, repository: repository);
await tester.tap(find.byKey(const Key('comment-reply-10')));
await tester.pumpAndSettle();
expect(find.byKey(const Key('comment-composer-sheet')), findsOneWidget);
expect(find.textContaining('Replying to'), findsOneWidget);
await tester.enterText(
find.byKey(const Key('comment-composer-input')),
'My reply',
);
await tester.pump();
await tester.tap(find.byKey(const Key('comment-composer-post')));
await tester.pumpAndSettle();
expect(repository.publishCalls.single.parentId, '10');
expect(find.byKey(const Key('comment-composer-sheet')), findsNothing);
expect(find.text('My reply'), findsOneWidget);
});
testWidgets('should_show_error_and_retry_when_comment_page_fails', (
tester,
) async {
final repository = FakeCommentRepository(pageError: StateError('offline'));
await _pumpSheet(tester, repository: repository);
expect(find.byKey(const Key('comment-list-error')), findsOneWidget);
expect(find.byKey(const Key('comment-list-retry')), findsOneWidget);
});
}
Future<AuthController> _pumpSheet(
WidgetTester tester, {
required FakeCommentRepository repository,
FakeHaptics? haptics,
Locale locale = const Locale('en'),
}) async {
final auth = await _auth();
await tester.pumpWidget(
MultiProvider(
providers: [
Provider<CommentRepository>.value(value: repository),
Provider<InteractionRepository>.value(
value: FakeInteractionRepository(),
),
ChangeNotifierProvider<AuthController>.value(value: auth),
Provider<Haptics>.value(value: haptics ?? FakeHaptics()),
],
child: MaterialApp(
locale: locale,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
initialRoute: '/comments',
routes: {
'/': (_) => const Scaffold(backgroundColor: AppColors.ink),
'/comments': (_) => const Scaffold(
backgroundColor: AppColors.ink,
resizeToAvoidBottomInset: false,
body: Align(
alignment: Alignment.bottomCenter,
child: DramaCommentsSheet(drama: _drama),
),
),
},
),
),
);
await tester.pumpAndSettle();
return auth;
}
Future<AuthController> _auth() async {
final client = ApiClient(
baseUrl: 'http://127.0.0.1:8081',
adapter: FakeHttpAdapter((options) async {
if (options.uri.path == '/app/auth/login') {
return FakeHttpResponse(
jsonEncode({
'code': 0,
'message': 'ok',
'data': {
'accessToken': 'access',
'refreshToken': 'refresh',
'expiresIn': 7200,
'user': {'uid': '9', 'nickname': 'Viewer', 'avatarUrl': ''},
},
}),
);
}
return FakeHttpResponse('{"code":0,"message":"ok","data":null}');
}),
);
final store = AuthSessionStore();
final session = await store.read();
final auth = _TestAuthController(
AuthApi(client),
store,
signedIn: session != null,
);
await auth.load();
return auth;
}
List<DramaComment> _comments(int count) => [
for (var index = 0; index < count; index++)
commentFixture(id: '${index + 10}', content: 'Comment ${index + 1}'),
];
class _TestAuthController extends AuthController {
_TestAuthController(super.api, super.store, {required this._signedIn});
bool _signedIn;
@override
bool get isSignedIn => _signedIn;
void setSignedIn(bool value) {
_signedIn = value;
notifyListeners();
}
}