feat(interaction): 支持游客点赞收藏评论
This commit is contained in:
1 parent
188811fde6
commit
a3d50eb935
18 files changed
+364
-121
No files matched your search
@@ -135,7 +135,9 @@ void main() {
|
||||
debugDefaultTargetPlatformOverride = null;
|
||||
});
|
||||
|
||||
testWidgets('should_gate_guest_comment_input_without_focus', (tester) async {
|
||||
testWidgets('should_open_guest_comment_composer_without_login', (
|
||||
tester,
|
||||
) async {
|
||||
SharedPreferencesAsyncPlatform.instance =
|
||||
InMemorySharedPreferencesAsync.empty();
|
||||
final repository = FakeCommentRepository(pages: [const CommentPage()]);
|
||||
@@ -144,34 +146,20 @@ void main() {
|
||||
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);
|
||||
expect(find.byKey(const Key('comment-composer-sheet')), findsOneWidget);
|
||||
expect(find.byKey(const Key('auth-landing')), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_focus_comment_input_after_successful_login', (
|
||||
tester,
|
||||
) async {
|
||||
testWidgets('should_focus_guest_comment_input_immediately', (tester) async {
|
||||
SharedPreferencesAsyncPlatform.instance =
|
||||
InMemorySharedPreferencesAsync.empty();
|
||||
final repository = FakeCommentRepository(pages: [const CommentPage()]);
|
||||
final auth = await _pumpSheet(tester, repository: repository);
|
||||
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(
|
||||
@@ -218,9 +206,7 @@ void main() {
|
||||
expect(multilineField.style?.height, 1.4);
|
||||
});
|
||||
|
||||
testWidgets('should_gate_guest_post_without_creating_comment', (
|
||||
tester,
|
||||
) async {
|
||||
testWidgets('should_publish_guest_comment_without_login', (tester) async {
|
||||
SharedPreferencesAsyncPlatform.instance =
|
||||
InMemorySharedPreferencesAsync.empty();
|
||||
final repository = FakeCommentRepository(pages: [const CommentPage()]);
|
||||
@@ -229,8 +215,18 @@ void main() {
|
||||
await tester.tap(find.byKey(const Key('comment-post')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Sign in to continue'), findsOneWidget);
|
||||
expect(repository.publishCalls, isEmpty);
|
||||
await tester.enterText(
|
||||
find.byKey(const Key('comment-composer-input')),
|
||||
'Guest comment',
|
||||
);
|
||||
await tester.pump();
|
||||
await tester.tap(find.byKey(const Key('comment-composer-post')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(const Key('auth-landing')), findsNothing);
|
||||
expect(repository.publishCalls, hasLength(1));
|
||||
expect(repository.publishCalls.single.content, 'Guest comment');
|
||||
expect(find.text('Guest comment'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should_show_server_comments_and_append_next_cursor_page', (
|
||||
|
||||
@@ -596,9 +596,7 @@ void main() {
|
||||
expect(find.byType(Slider), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should_pause_feed_when_auth_page_is_pushed_and_resume_on_pop', (
|
||||
tester,
|
||||
) async {
|
||||
testWidgets('should_keep_feed_playing_when_guest_saves', (tester) async {
|
||||
final players = <_RecordingPlayer>[];
|
||||
await _pumpApp(
|
||||
tester,
|
||||
@@ -615,18 +613,23 @@ void main() {
|
||||
|
||||
await tester.tap(find.byKey(const Key('rail-save')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Sign In'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(AuthPage), findsOneWidget);
|
||||
expect(player.playing, isFalse);
|
||||
|
||||
await tester.pageBack();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(AuthPage), findsNothing);
|
||||
expect(player.playing, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('should_like_without_login_from_recommend_feed', (tester) async {
|
||||
final interactions = FakeInteractionRepository();
|
||||
await _pumpApp(tester, interactions: interactions);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('rail-like')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(AuthPage), findsNothing);
|
||||
expect(interactions.likeTargets, ['EPISODE:the-signal-demo-1']);
|
||||
});
|
||||
|
||||
testWidgets('should_keep_video_surface_background_black', (tester) async {
|
||||
await _pumpApp(tester);
|
||||
await _openSeriesPlayer(tester);
|
||||
@@ -643,7 +646,7 @@ void main() {
|
||||
expect(find.byKey(const Key('player-fallback-scrim')), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_gate_comments_before_opening_sheet_when_signed_out', (
|
||||
testWidgets('should_open_comments_without_login', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
@@ -652,8 +655,8 @@ void main() {
|
||||
await tester.tap(find.byKey(const Key('player-rail-comments')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Sign in to continue'), findsOneWidget);
|
||||
expect(find.byKey(const Key('player-comments-sheet')), findsNothing);
|
||||
expect(find.byKey(const Key('player-comments-sheet')), findsOneWidget);
|
||||
expect(find.byType(AuthPage), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_open_feed_comments_when_signed_in', (tester) async {
|
||||
@@ -1872,6 +1875,12 @@ void main() {
|
||||
testWidgets('should_open_unlock_panel_with_honest_method_states', (
|
||||
tester,
|
||||
) async {
|
||||
SharedPreferencesAsyncPlatform.instance =
|
||||
InMemorySharedPreferencesAsync.withData({
|
||||
'auth.accessToken': 'access',
|
||||
'auth.refreshToken': 'refresh',
|
||||
'auth.user': '{"uid":"9","nickname":"Viewer","avatarUrl":""}',
|
||||
});
|
||||
final interactions = FakeInteractionRepository();
|
||||
await _pumpApp(
|
||||
tester,
|
||||
@@ -1905,6 +1914,36 @@ void main() {
|
||||
find.text('Available unlock methods vary by title.'),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
testWidgets('should_require_login_before_opening_unlock_flow', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(
|
||||
tester,
|
||||
playback: FakePlaybackRepository(
|
||||
ticket: const PlayTicket(
|
||||
episodeId: 'city-lights-demo-1',
|
||||
accessible: false,
|
||||
accessType: PlayTicket.coinRequired,
|
||||
unlockCoin: 30,
|
||||
),
|
||||
),
|
||||
);
|
||||
await _openSeriesPlayer(tester);
|
||||
|
||||
await tester.tap(find.byKey(const Key('player-open-unlock')));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
expect(find.byKey(const Key('player-unlock-panel')), findsNothing);
|
||||
|
||||
final signIn = find.widgetWithText(FilledButton, 'Sign In');
|
||||
await tester.ensureVisible(signIn);
|
||||
await tester.tap(signIn);
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
expect(find.byType(AuthPage), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/core/data/models/drama.dart';
|
||||
import 'package:fruitshorts/core/data/models/play_ticket.dart';
|
||||
import 'package:fruitshorts/core/data/models/player_port.dart';
|
||||
import 'package:fruitshorts/core/data/models/wallet.dart';
|
||||
import 'package:fruitshorts/core/data/repositories/commerce_repository.dart';
|
||||
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
|
||||
import 'package:fruitshorts/core/data/services/android_picture_in_picture.dart';
|
||||
import 'package:fruitshorts/core/data/services/playback_session.dart';
|
||||
import 'package:fruitshorts/core/data/services/wallet_controller.dart';
|
||||
import 'package:fruitshorts/pages/player/player_logic.dart';
|
||||
import 'package:fruitshorts/pages/player/player_view.dart';
|
||||
|
||||
import '../support/fake_watch_history.dart';
|
||||
|
||||
const _lockedDrama = Drama(
|
||||
id: 'locked',
|
||||
title: 'Locked',
|
||||
synopsis: 'synopsis',
|
||||
posterUrl: '/files/locked.png',
|
||||
episodes: [
|
||||
Episode(
|
||||
id: 'locked-1',
|
||||
number: 1,
|
||||
title: 'Locked episode',
|
||||
isFree: false,
|
||||
locked: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
test('should_not_request_ticket_for_guest_on_locked_episode', () async {
|
||||
final playback = FakePlaybackRepository();
|
||||
final logic = _logic(playback, signedIn: false);
|
||||
logic.attach(
|
||||
const PlayerPage(drama: _lockedDrama, initialIndex: 0),
|
||||
isMounted: () => true,
|
||||
);
|
||||
logic.start(
|
||||
factory: (_) => _NoopPlayer(),
|
||||
coordinator: PlaybackCoordinator(),
|
||||
);
|
||||
await _drain(logic);
|
||||
|
||||
expect(playback.requests, isEmpty);
|
||||
expect(logic.session.isLocked, isTrue);
|
||||
|
||||
logic.dispose();
|
||||
await _drain(logic);
|
||||
});
|
||||
|
||||
test('should_request_ticket_after_user_is_signed_in', () async {
|
||||
final playback = FakePlaybackRepository(
|
||||
ticket: const PlayTicket(
|
||||
episodeId: 'locked-1',
|
||||
accessible: false,
|
||||
accessType: PlayTicket.coinRequired,
|
||||
unlockCoin: 30,
|
||||
),
|
||||
);
|
||||
final logic = _logic(playback, signedIn: true);
|
||||
logic.attach(
|
||||
const PlayerPage(drama: _lockedDrama, initialIndex: 0),
|
||||
isMounted: () => true,
|
||||
);
|
||||
logic.start(
|
||||
factory: (_) => _NoopPlayer(),
|
||||
coordinator: PlaybackCoordinator(),
|
||||
);
|
||||
await _drain(logic);
|
||||
|
||||
expect(playback.requests, ['locked-1']);
|
||||
expect(logic.blockedEpisodeId, 'locked-1');
|
||||
|
||||
logic.dispose();
|
||||
await _drain(logic);
|
||||
});
|
||||
|
||||
test('should_resume_unlock_and_play_for_same_episode_after_login', () async {
|
||||
var signedIn = false;
|
||||
final playback = FakePlaybackRepository(
|
||||
ticket: const PlayTicket(
|
||||
episodeId: 'locked-1',
|
||||
accessible: true,
|
||||
accessType: 'ENTITLED',
|
||||
playUrl: '/files/locked-1.mp4',
|
||||
),
|
||||
);
|
||||
final logic = _logic(
|
||||
playback,
|
||||
signedIn: false,
|
||||
isSignedIn: () => signedIn,
|
||||
wallet: WalletController(_UnlockCommerceRepository()),
|
||||
);
|
||||
logic.attach(
|
||||
const PlayerPage(drama: _lockedDrama, initialIndex: 0),
|
||||
isMounted: () => true,
|
||||
);
|
||||
logic.start(
|
||||
factory: (_) => _NoopPlayer(),
|
||||
coordinator: PlaybackCoordinator(),
|
||||
);
|
||||
await _drain(logic);
|
||||
|
||||
expect(playback.requests, isEmpty);
|
||||
expect(logic.blockedEpisodeId, 'locked-1');
|
||||
|
||||
signedIn = true;
|
||||
final unlocked = await logic.unlockEpisode(0);
|
||||
await _drain(logic);
|
||||
|
||||
expect(unlocked, isTrue);
|
||||
expect(playback.requests, ['locked-1']);
|
||||
expect(logic.session.active, isNotNull);
|
||||
|
||||
logic.dispose();
|
||||
await _drain(logic);
|
||||
});
|
||||
}
|
||||
|
||||
PlayerLogic _logic(
|
||||
PlaybackRepository playback, {
|
||||
bool signedIn = true,
|
||||
bool Function()? isSignedIn,
|
||||
WalletController? wallet,
|
||||
}) {
|
||||
return PlayerLogic(
|
||||
history: FakeWatchHistory(),
|
||||
playback: playback,
|
||||
wallet: wallet,
|
||||
pictureInPicture: AndroidPictureInPicture(
|
||||
channel: const MethodChannel('test/player_locked_auth_flow/pip'),
|
||||
),
|
||||
waitForFrame: () async {},
|
||||
isSignedIn: isSignedIn ?? () => signedIn,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _drain(PlayerLogic logic) async {
|
||||
for (var attempt = 0; attempt < 4; attempt++) {
|
||||
await pumpEventQueue();
|
||||
await logic.session.playbackSettled;
|
||||
await logic.session.preloadSettled;
|
||||
}
|
||||
}
|
||||
|
||||
class _NoopPlayer implements PlayerPort {
|
||||
@override
|
||||
Future<void> initialize() async {}
|
||||
|
||||
@override
|
||||
Future<void> play() async {}
|
||||
|
||||
@override
|
||||
Future<void> pause() async {}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {}
|
||||
}
|
||||
|
||||
class _UnlockCommerceRepository implements CommerceRepository {
|
||||
@override
|
||||
Future<WalletSnapshot> fetchWallet() async => const WalletSnapshot(
|
||||
wallet: CoinWallet(
|
||||
coinBalance: 100,
|
||||
totalRechargedCoin: 0,
|
||||
totalConsumedCoin: 0,
|
||||
totalRefundedCoin: 0,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<UnlockResult> unlock(String episodeId) async => const UnlockResult(
|
||||
unlocked: true,
|
||||
accessType: 'ENTITLED',
|
||||
coinCost: 30,
|
||||
coinBalance: 70,
|
||||
);
|
||||
}
|
||||
@@ -266,7 +266,7 @@ void main() {
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('should_open_auth_gate_without_liking_when_signed_out', (
|
||||
testWidgets('should_like_without_login_when_signed_out', (
|
||||
tester,
|
||||
) async {
|
||||
SharedPreferencesAsyncPlatform.instance =
|
||||
@@ -279,8 +279,8 @@ void main() {
|
||||
await tester.tap(find.byKey(const Key('player-rail-like')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Sign in to continue'), findsOneWidget);
|
||||
expect(interactions.likeTargets, isEmpty);
|
||||
expect(find.text('Sign in to continue'), findsNothing);
|
||||
expect(interactions.likeTargets, ['EPISODE:city-lights-demo-1']);
|
||||
});
|
||||
|
||||
testWidgets('should_show_saved_player_drama_in_favorites', (tester) async {
|
||||
|
||||
Reference in new issue
Block a user