fix(favorite): 统一收藏状态与互动图标
This commit is contained in:
1 parent
4b97f89c0b
commit
d3dbc9f8dc
13 files changed
+347
-213
No files matched your search
@@ -16,20 +16,20 @@ DramaDetailLogic _logic(
|
||||
FakeWatchHistory history, {
|
||||
FavoriteController? favorites,
|
||||
PlaybackPositionCache? positionCache,
|
||||
}) =>
|
||||
DramaDetailLogic(
|
||||
drama: drama,
|
||||
history: history,
|
||||
playback: FakePlaybackRepository(),
|
||||
favorites: favorites ?? FavoriteController(InMemoryFavoriteRepository()),
|
||||
positionCache: positionCache ?? PlaybackPositionCache(),
|
||||
);
|
||||
}) => DramaDetailLogic(
|
||||
drama: drama,
|
||||
history: history,
|
||||
playback: FakePlaybackRepository(),
|
||||
favorites: favorites ?? FavoriteController(InMemoryFavoriteRepository()),
|
||||
positionCache: positionCache ?? PlaybackPositionCache(),
|
||||
);
|
||||
|
||||
Drama _card(String id) => Drama(
|
||||
Drama _card(String id, {int? favoriteCount}) => Drama(
|
||||
id: id,
|
||||
title: id,
|
||||
synopsis: 'synopsis',
|
||||
posterUrl: '/files/$id.jpg',
|
||||
favoriteCount: favoriteCount,
|
||||
totalEpisodes: 3,
|
||||
episodes: [
|
||||
Episode(
|
||||
@@ -42,11 +42,12 @@ Drama _card(String id) => Drama(
|
||||
],
|
||||
);
|
||||
|
||||
Drama _detail(String id) => Drama(
|
||||
Drama _detail(String id, {int? favoriteCount}) => Drama(
|
||||
id: id,
|
||||
title: id,
|
||||
synopsis: 'synopsis',
|
||||
posterUrl: '/files/$id.jpg',
|
||||
favoriteCount: favoriteCount,
|
||||
totalEpisodes: 3,
|
||||
detailLoaded: true,
|
||||
episodes: [
|
||||
@@ -62,21 +63,24 @@ Drama _detail(String id) => Drama(
|
||||
);
|
||||
|
||||
void main() {
|
||||
test('should_upgrade_to_full_episode_list_when_detail_loads_for_list_card', () async {
|
||||
var calls = 0;
|
||||
final logic = _logic(_card('signal'), FakeWatchHistory());
|
||||
test(
|
||||
'should_upgrade_to_full_episode_list_when_detail_loads_for_list_card',
|
||||
() async {
|
||||
var calls = 0;
|
||||
final logic = _logic(_card('signal'), FakeWatchHistory());
|
||||
|
||||
await logic.load(() async {
|
||||
calls++;
|
||||
return _detail('signal');
|
||||
});
|
||||
await logic.load(() async {
|
||||
calls++;
|
||||
return _detail('signal');
|
||||
});
|
||||
|
||||
expect(calls, 1);
|
||||
expect(logic.loading, isFalse);
|
||||
expect(logic.failed, isFalse);
|
||||
expect(logic.drama.episodes, hasLength(3));
|
||||
logic.dispose();
|
||||
});
|
||||
expect(calls, 1);
|
||||
expect(logic.loading, isFalse);
|
||||
expect(logic.failed, isFalse);
|
||||
expect(logic.drama.episodes, hasLength(3));
|
||||
logic.dispose();
|
||||
},
|
||||
);
|
||||
|
||||
test('should_keep_card_and_report_reason_when_load_fails', () async {
|
||||
final logic = _logic(_card('signal'), FakeWatchHistory());
|
||||
@@ -107,6 +111,19 @@ void main() {
|
||||
logic.dispose();
|
||||
});
|
||||
|
||||
test('should_use_detail_favorite_count_as_authoritative', () async {
|
||||
final favorites = FavoriteController(InMemoryFavoriteRepository());
|
||||
final card = _card('signal', favoriteCount: 2);
|
||||
expect(favorites.favoriteCountFor(card), 2);
|
||||
final logic = _logic(card, FakeWatchHistory(), favorites: favorites);
|
||||
|
||||
await logic.load(() async => _detail('signal', favoriteCount: 9));
|
||||
|
||||
expect(favorites.favoriteCount('signal'), 9);
|
||||
expect(logic.adjustedFavoriteCount, 9);
|
||||
logic.dispose();
|
||||
});
|
||||
|
||||
test('should_prefer_recent_local_position_when_loading_continue', () async {
|
||||
final cache = PlaybackPositionCache();
|
||||
cache.save(
|
||||
|
||||
@@ -323,19 +323,59 @@ void main() {
|
||||
tester.widget<Text>(find.byKey(const Key('rail-save-count'))).data,
|
||||
'3.2万',
|
||||
);
|
||||
expect(
|
||||
final likeImage = tester.widget<Image>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('rail-like')),
|
||||
matching: find.byIcon(Icons.favorite_rounded),
|
||||
matching: find.byType(Image),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
final saveImage = tester.widget<Image>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('rail-save')),
|
||||
matching: find.byIcon(Icons.bookmark_border_rounded),
|
||||
matching: find.byType(Image),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
(likeImage.image as AssetImage).assetName,
|
||||
'assets/icon/rail/rail_like.png',
|
||||
);
|
||||
expect(likeImage.color, AppColors.doubleTapHeartRed);
|
||||
expect(
|
||||
(saveImage.image as AssetImage).assetName,
|
||||
'assets/icon/rail/rail_save.png',
|
||||
);
|
||||
expect(saveImage.color, AppColors.onPrimary);
|
||||
expect(find.byKey(const Key('rail-comments')), findsOneWidget);
|
||||
expect(find.byKey(const Key('rail-share')), findsOneWidget);
|
||||
expect(
|
||||
tester.widget<Text>(find.byKey(const Key('rail-comments-count'))).data,
|
||||
'12',
|
||||
);
|
||||
expect(
|
||||
(tester
|
||||
.widget<Image>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('rail-comments')),
|
||||
matching: find.byType(Image),
|
||||
),
|
||||
)
|
||||
.image
|
||||
as AssetImage)
|
||||
.assetName,
|
||||
'assets/icon/rail/rail_comment.png',
|
||||
);
|
||||
expect(
|
||||
(tester
|
||||
.widget<Image>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('rail-share')),
|
||||
matching: find.byType(Image),
|
||||
),
|
||||
)
|
||||
.image
|
||||
as AssetImage)
|
||||
.assetName,
|
||||
'assets/icon/rail/rail_share.png',
|
||||
);
|
||||
expect(find.byKey(const Key('rail-episodes')), findsOneWidget);
|
||||
expect(
|
||||
@@ -345,9 +385,6 @@ void main() {
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byKey(const Key('rail-comments')), findsNothing);
|
||||
expect(find.byKey(const Key('rail-share')), findsNothing);
|
||||
expect(find.text('Comments'), findsNothing);
|
||||
expect(
|
||||
tester.getRect(find.byKey(const Key('home-feed-action-rail'))).right,
|
||||
closeTo(
|
||||
@@ -672,7 +709,10 @@ void main() {
|
||||
final previewDecoration = previewContainer.decoration! as BoxDecoration;
|
||||
expect(previewDecoration.color, AppColors.card);
|
||||
expect(previewDecoration.borderRadius, BorderRadius.circular(8));
|
||||
expect((previewDecoration.border! as Border).top.color, AppColors.onPrimary);
|
||||
expect(
|
||||
(previewDecoration.border! as Border).top.color,
|
||||
AppColors.onPrimary,
|
||||
);
|
||||
await gesture.moveBy(const Offset(-600, 0));
|
||||
await tester.pump();
|
||||
expect(
|
||||
|
||||
@@ -6,11 +6,12 @@ import 'package:fruitshorts/core/data/models/page_result.dart';
|
||||
import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
|
||||
import 'package:fruitshorts/core/data/services/favorite_controller.dart';
|
||||
|
||||
Drama _card(String id) => Drama(
|
||||
Drama _card(String id, {int? favoriteCount}) => Drama(
|
||||
id: id,
|
||||
title: 'Drama $id',
|
||||
synopsis: 'synopsis',
|
||||
posterUrl: '/files/$id.png',
|
||||
favoriteCount: favoriteCount,
|
||||
);
|
||||
|
||||
void main() {
|
||||
@@ -37,6 +38,31 @@ void main() {
|
||||
expect(controller.cards.map((drama) => drama.id), ['signal']);
|
||||
});
|
||||
|
||||
test(
|
||||
'should_adjust_the_single_favorite_count_when_toggle_succeeds',
|
||||
() async {
|
||||
final controller = FavoriteController(InMemoryFavoriteRepository());
|
||||
final card = _card('signal', favoriteCount: 12);
|
||||
|
||||
expect(controller.favoriteCountFor(card), 12);
|
||||
expect(await controller.toggle('signal', card: card), isTrue);
|
||||
expect(controller.favoriteCount('signal'), 13);
|
||||
expect(await controller.toggle('signal', card: card), isTrue);
|
||||
expect(controller.favoriteCount('signal'), 12);
|
||||
},
|
||||
);
|
||||
|
||||
test('should_apply_server_count_and_roll_back_toggle_failure', () async {
|
||||
final repository = InMemoryFavoriteRepository()..failToggle = true;
|
||||
final controller = FavoriteController(repository);
|
||||
|
||||
controller.applyServerState('signal', saved: false, favoriteCount: 20);
|
||||
expect(controller.favoriteCount('signal'), 20);
|
||||
expect(await controller.toggle('signal'), isFalse);
|
||||
expect(controller.isSaved('signal'), isFalse);
|
||||
expect(controller.favoriteCount('signal'), 20);
|
||||
});
|
||||
|
||||
test('should_remove_card_and_state_when_toggled_off', () async {
|
||||
final repository = InMemoryFavoriteRepository(saved: {'signal'});
|
||||
final controller = FavoriteController(repository);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/core/data/services/interaction_controller.dart';
|
||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||
import 'package:fruitshorts/core/theme/app_theme.dart';
|
||||
import 'package:fruitshorts/l10n/app_localizations.dart';
|
||||
import 'package:fruitshorts/pages/player/widgets/home_feed_overlay.dart';
|
||||
@@ -41,13 +42,13 @@ void main() {
|
||||
);
|
||||
expect(repository.likeTargets, hasLength(1));
|
||||
expect(haptics.lightImpacts, 1);
|
||||
expect(
|
||||
final likeImage = tester.widget<Image>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('rail-like')),
|
||||
matching: find.byIcon(Icons.favorite_rounded),
|
||||
matching: find.byType(Image),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(likeImage.color, AppColors.doubleTapHeartRed);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -630,6 +630,53 @@ void main() {
|
||||
expect(interactions.likeTargets, ['EPISODE:the-signal-demo-1']);
|
||||
});
|
||||
|
||||
testWidgets('should_keep_favorite_count_when_player_saves_and_returns', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final initialCount = _railLabel(tester, const Key('rail-save'));
|
||||
await tester.tap(find.byKey(const Key('home-feed-series-name')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(_railLabel(tester, const Key('player-rail-save')), initialCount);
|
||||
await tester.tap(find.byKey(const Key('player-rail-save')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final savedCount = _railLabel(tester, const Key('player-rail-save'));
|
||||
expect(savedCount, isNot(initialCount));
|
||||
await tester.tap(find.byKey(const Key('player-back')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(AuthPage), findsNothing);
|
||||
expect(_railLabel(tester, const Key('rail-save')), savedCount);
|
||||
});
|
||||
|
||||
testWidgets('should_keep_favorite_state_when_recommend_opens_player', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('rail-save')));
|
||||
await tester.pumpAndSettle();
|
||||
final savedCount = _railLabel(tester, const Key('rail-save'));
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-series-name')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(_railLabel(tester, const Key('player-rail-save')), savedCount);
|
||||
final saveImage = tester.widget<Image>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('player-rail-save')),
|
||||
matching: find.byType(Image),
|
||||
),
|
||||
);
|
||||
expect(saveImage.color, AppColors.blue);
|
||||
});
|
||||
|
||||
testWidgets('should_keep_video_surface_background_black', (tester) async {
|
||||
await _pumpApp(tester);
|
||||
await _openSeriesPlayer(tester);
|
||||
@@ -646,9 +693,7 @@ void main() {
|
||||
expect(find.byKey(const Key('player-fallback-scrim')), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_open_comments_without_login', (
|
||||
tester,
|
||||
) async {
|
||||
testWidgets('should_open_comments_without_login', (tester) async {
|
||||
await _pumpApp(tester);
|
||||
await _openSeriesPlayer(tester);
|
||||
|
||||
@@ -1914,7 +1959,6 @@ void main() {
|
||||
find.text('Available unlock methods vary by title.'),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
testWidgets('should_require_login_before_opening_unlock_flow', (
|
||||
|
||||
Reference in new issue
Block a user