feat(reward): 新增客户端奖励中心
This commit is contained in:
1 parent
67670d46b1
commit
bea08110c9
68 files changed
+3987
-332
No files matched your search
@@ -63,7 +63,7 @@ void main() {
|
||||
|
||||
logic.openProfile();
|
||||
|
||||
expect(logic.tab, 3);
|
||||
expect(logic.tab, 4);
|
||||
logic.dispose();
|
||||
});
|
||||
|
||||
@@ -159,10 +159,10 @@ void main() {
|
||||
expect(logic.tab, 1);
|
||||
|
||||
logic.myAction(MyPageAction.following);
|
||||
expect(logic.tab, 2);
|
||||
expect(logic.tab, 3);
|
||||
|
||||
logic.openFollowingHistory();
|
||||
expect(logic.tab, 2);
|
||||
expect(logic.tab, 3);
|
||||
expect(logic.followingHistoryRequest, 1);
|
||||
|
||||
logic.myAction(MyPageAction.resume);
|
||||
@@ -179,10 +179,10 @@ void main() {
|
||||
final logic = HomeLogic(dramas);
|
||||
|
||||
logic.discoverAction(DiscoverPageAction.mine);
|
||||
expect(logic.tab, 3);
|
||||
expect(logic.tab, 4);
|
||||
|
||||
logic.discoverAction(DiscoverPageAction.following);
|
||||
expect(logic.tab, 2);
|
||||
expect(logic.tab, 3);
|
||||
|
||||
logic.discoverAction(DiscoverPageAction.home);
|
||||
expect(logic.tab, 0);
|
||||
|
||||
+138
-64
@@ -3,10 +3,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/app.dart';
|
||||
import 'package:fruitshorts/core/data/models/reward.dart';
|
||||
import 'package:fruitshorts/core/data/models/wallet.dart';
|
||||
import 'package:fruitshorts/core/data/repositories/commerce_repository.dart';
|
||||
import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
|
||||
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
|
||||
import 'package:fruitshorts/core/data/repositories/reward_repository.dart';
|
||||
import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart';
|
||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||
import 'package:fruitshorts/core/theme/app_dimens.dart';
|
||||
@@ -26,6 +28,7 @@ const _keys = [
|
||||
Key('home-tabs-bar'),
|
||||
Key('tab-home'),
|
||||
Key('tab-recommend'),
|
||||
Key('tab-reward'),
|
||||
Key('tab-follow'),
|
||||
Key('tab-mine'),
|
||||
];
|
||||
@@ -40,7 +43,7 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('should_theme_four_tabs_and_switch_selected_state_when_tapped', (
|
||||
testWidgets('should_theme_five_tabs_and_switch_selected_state_when_tapped', (
|
||||
tester,
|
||||
) async {
|
||||
final haptics = FakeHaptics();
|
||||
@@ -83,6 +86,13 @@ void main() {
|
||||
.map((key) => tester.getCenter(find.byKey(key)).dx)
|
||||
.toList();
|
||||
expect(tabCenters, orderedEquals(tabCenters.toList()..sort()));
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('tab-reward')),
|
||||
matching: find.text('Rewards'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('tab-home')),
|
||||
@@ -90,6 +100,13 @@ void main() {
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('tab-reward')),
|
||||
matching: find.byType(RootNavGlyph),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('tab-recommend')),
|
||||
@@ -128,17 +145,6 @@ void main() {
|
||||
?.color,
|
||||
AppColors.navUnselected,
|
||||
);
|
||||
expect(
|
||||
tester
|
||||
.widget<AnimatedScale>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('tab-home')),
|
||||
matching: find.byType(AnimatedScale),
|
||||
),
|
||||
)
|
||||
.duration,
|
||||
lessThanOrEqualTo(const Duration(milliseconds: 200)),
|
||||
);
|
||||
final selectedHomeIcon = tester.widget<RootNavGlyph>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('tab-home')),
|
||||
@@ -239,64 +245,72 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('should_map_four_tabs_to_home_recommend_following_and_me', (
|
||||
tester,
|
||||
) async {
|
||||
// 追剧页头部的双语分段文案在测试字体(Ahem)下比真机字体宽约 98pt,
|
||||
// 这里放宽视口宽度,避免测字体撑破 390 宽度下的分段行。
|
||||
await _pumpApp(tester, size: const Size(520, 844));
|
||||
testWidgets(
|
||||
'should_map_five_tabs_to_home_recommend_reward_following_and_me',
|
||||
(tester) async {
|
||||
// 追剧页头部的双语分段文案在测试字体(Ahem)下比真机字体宽约 98pt,
|
||||
// 这里放宽视口宽度,避免测字体撑破 390 宽度下的分段行。
|
||||
await _pumpApp(tester, size: const Size(520, 844));
|
||||
|
||||
expect(find.byKey(const Key('tab-home')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-recommend')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-follow')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-mine')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-create')), findsNothing);
|
||||
expect(find.byKey(const Key('recommend-feed-player')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-home')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-recommend')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-reward')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-follow')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-mine')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-create')), findsNothing);
|
||||
expect(find.byKey(const Key('recommend-feed-player')), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const Key('recommend-channel-nav')), findsOneWidget);
|
||||
_expectNoFloatingOverlays();
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const Key('recommend-channel-nav')), findsOneWidget);
|
||||
_expectNoFloatingOverlays();
|
||||
|
||||
await tester.tap(find.byKey(const Key('tab-follow')));
|
||||
await tester.pumpAndSettle();
|
||||
final favorites = find.byType(FollowingPage);
|
||||
expect(favorites, findsOneWidget);
|
||||
expect(find.byKey(const Key('following-page')), findsOneWidget);
|
||||
expect(find.byType(AppBottomNav), findsOneWidget);
|
||||
expect(
|
||||
find.descendant(of: favorites, matching: find.byType(Scaffold)),
|
||||
findsNothing,
|
||||
);
|
||||
expect(Navigator.canPop(tester.element(favorites)), isFalse);
|
||||
_expectNoFloatingOverlays();
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('tab-follow')),
|
||||
matching: find.byType(RootNavGlyph),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
await tester.tap(find.byKey(const Key('tab-reward')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Reward Center'), findsOneWidget);
|
||||
expect(find.text('Daily task'), findsOneWidget);
|
||||
_expectNoFloatingOverlays();
|
||||
|
||||
await tester.tap(find.byKey(const Key('tab-mine')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Viewer'), findsOneWidget);
|
||||
expect(find.byKey(const Key('mine-menu')), findsOneWidget);
|
||||
expect(find.byKey(const Key('mine-wallet')), findsOneWidget);
|
||||
expect(find.byKey(const Key('mine-edit')), findsOneWidget);
|
||||
expect(find.byKey(const Key('mine-library-tab-0')), findsNothing);
|
||||
_expectNoFloatingOverlays();
|
||||
await tester.tap(find.byKey(const Key('tab-follow')));
|
||||
await tester.pumpAndSettle();
|
||||
final favorites = find.byType(FollowingPage);
|
||||
expect(favorites, findsOneWidget);
|
||||
expect(find.byKey(const Key('following-page')), findsOneWidget);
|
||||
expect(find.byType(AppBottomNav), findsOneWidget);
|
||||
expect(
|
||||
find.descendant(of: favorites, matching: find.byType(Scaffold)),
|
||||
findsNothing,
|
||||
);
|
||||
expect(Navigator.canPop(tester.element(favorites)), isFalse);
|
||||
_expectNoFloatingOverlays();
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('tab-follow')),
|
||||
matching: find.byType(RootNavGlyph),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
await tester.tap(find.byKey(const Key('tab-follow')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(favorites, findsOneWidget);
|
||||
expect(find.byType(AppBottomNav), findsOneWidget);
|
||||
expect(Navigator.canPop(tester.element(favorites)), isFalse);
|
||||
await tester.tap(find.byKey(const Key('tab-mine')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Viewer'), findsOneWidget);
|
||||
expect(find.byKey(const Key('mine-menu')), findsOneWidget);
|
||||
expect(find.byKey(const Key('mine-wallet')), findsOneWidget);
|
||||
expect(find.byKey(const Key('mine-edit')), findsOneWidget);
|
||||
expect(find.byKey(const Key('mine-library-tab-0')), findsNothing);
|
||||
_expectNoFloatingOverlays();
|
||||
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const Key('recommend-feed-player')), findsOneWidget);
|
||||
});
|
||||
await tester.tap(find.byKey(const Key('tab-follow')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(favorites, findsOneWidget);
|
||||
expect(find.byType(AppBottomNav), findsOneWidget);
|
||||
expect(Navigator.canPop(tester.element(favorites)), isFalse);
|
||||
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const Key('recommend-feed-player')), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('should_not_haptic_when_ordinary_list_item_is_opened', (
|
||||
tester,
|
||||
@@ -337,6 +351,7 @@ Future<void> _pumpApp(
|
||||
commentRepository: FakeCommentRepository(),
|
||||
interactionRepository: FakeInteractionRepository(),
|
||||
commerceRepository: const _WalletCommerceRepository(),
|
||||
rewardRepository: const _RewardRepository(),
|
||||
haptics: haptics ?? FakeHaptics(),
|
||||
),
|
||||
);
|
||||
@@ -371,6 +386,65 @@ class _WalletCommerceRepository implements CommerceRepository {
|
||||
}
|
||||
}
|
||||
|
||||
class _RewardRepository implements RewardRepository {
|
||||
const _RewardRepository();
|
||||
|
||||
@override
|
||||
Future<RewardOverview> fetchOverview(String locale) async {
|
||||
return const RewardOverview(
|
||||
serverDate: '2026-11-05',
|
||||
timezone: 'Asia/Jakarta',
|
||||
coinBalance: 120,
|
||||
checkin: RewardCheckinState(
|
||||
taskId: 'checkin',
|
||||
checkedInToday: false,
|
||||
streak: 1,
|
||||
currentDay: 2,
|
||||
),
|
||||
sections: [
|
||||
RewardSection(
|
||||
code: 'DAILY',
|
||||
title: 'Daily task',
|
||||
tasks: [
|
||||
RewardTask(
|
||||
id: 'watch',
|
||||
code: 'WATCH',
|
||||
type: 'WATCH_DURATION',
|
||||
title: 'Watch a drama',
|
||||
subtitle: 'Watch for five minutes',
|
||||
ctaText: 'Go',
|
||||
actionType: 'OPEN_HOME',
|
||||
actionUrl: '',
|
||||
progressValue: 0,
|
||||
targetValue: 300,
|
||||
rewardCoin: 20,
|
||||
status: 'IN_PROGRESS',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RewardActionResult> checkIn() async {
|
||||
return const RewardActionResult(
|
||||
rewardCoin: 10,
|
||||
coinBalance: 130,
|
||||
alreadyCompleted: false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RewardActionResult> claim(String taskId) async {
|
||||
return const RewardActionResult(
|
||||
rewardCoin: 20,
|
||||
coinBalance: 140,
|
||||
alreadyCompleted: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _expectNoFloatingOverlays() {
|
||||
expect(find.byKey(const Key('mine-mini-player')), findsNothing);
|
||||
expect(find.byType(FloatingActionButton), findsNothing);
|
||||
|
||||
@@ -24,7 +24,7 @@ void main() {
|
||||
InMemorySharedPreferencesAsync.empty();
|
||||
});
|
||||
|
||||
testWidgets('should_render_four_tabs_and_open_following_page', (
|
||||
testWidgets('should_render_five_tabs_and_open_following_page', (
|
||||
tester,
|
||||
) async {
|
||||
// 追剧页头部双语分段在测试字体(Ahem)下比真机字体宽约 98pt,
|
||||
@@ -189,6 +189,7 @@ void main() {
|
||||
const _tabKeys = [
|
||||
Key('tab-home'),
|
||||
Key('tab-recommend'),
|
||||
Key('tab-reward'),
|
||||
Key('tab-follow'),
|
||||
Key('tab-mine'),
|
||||
];
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'package:fruitshorts/l10n/app_localizations.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'should_render_four_ordered_glyphs_with_home_selected_when_root_nav_builds',
|
||||
'should_render_five_ordered_glyphs_with_home_selected_when_root_nav_builds',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@@ -32,7 +32,19 @@ void main() {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
]);
|
||||
final tabKeys = [
|
||||
const Key('tab-home'),
|
||||
const Key('tab-recommend'),
|
||||
const Key('tab-reward'),
|
||||
const Key('tab-follow'),
|
||||
const Key('tab-mine'),
|
||||
];
|
||||
final centers = tabKeys
|
||||
.map((key) => tester.getCenter(find.byKey(key)).dx)
|
||||
.toList();
|
||||
expect(centers, orderedEquals(centers.toList()..sort()));
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user