test: 按新 UI 基线重写过期用例
覆盖 155 条失败与 4 处编译错误:入口改走推荐流 tab、弹框换 AppGlowDialog、 rail 尺寸与底部锚定、子页标题字重 w400、底栏选中品牌红、收藏文案改收藏短剧。 全量 flutter test 775 通过。
This commit is contained in:
1 parent
799f3d1b7d
commit
1d4ab5e0e6
33 files changed
+853
-1114
No files matched your search
@@ -48,7 +48,8 @@ void main() {
|
||||
matching: find.byType(Image),
|
||||
),
|
||||
);
|
||||
expect(likeImage.color, AppColors.doubleTapHeartRed);
|
||||
// 首页 feed 的互动 rail 点赞选中色统一成品牌红。
|
||||
expect(likeImage.color, AppColors.brandRed);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import 'package:fruitshorts/core/data/services/playback_session.dart';
|
||||
import 'package:fruitshorts/core/data/services/video_player_port.dart';
|
||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||
import 'package:fruitshorts/pages/auth/auth_view.dart';
|
||||
import 'package:fruitshorts/pages/player/open_player.dart';
|
||||
import 'package:fruitshorts/pages/player/player_logic.dart';
|
||||
import 'package:fruitshorts/pages/player/player_view.dart';
|
||||
import 'package:fruitshorts/pages/player/widgets/player_center_toggle.dart';
|
||||
@@ -41,13 +42,16 @@ void main() {
|
||||
setUp(() {
|
||||
SharedPreferencesAsyncPlatform.instance =
|
||||
InMemorySharedPreferencesAsync.empty();
|
||||
// pickedEpisode 是跨页面常驻的静态"上次选集"记忆(设计如此),
|
||||
// 用例之间必须清掉,否则前一个用例选的集会带进下一个用例的 feed 断言。
|
||||
PlayerLogic.pickedEpisode.clear();
|
||||
});
|
||||
|
||||
testWidgets('should_render_immersive_overlay_in_recommend_feed', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(const Key('recommend-feed-player')), findsOneWidget);
|
||||
@@ -82,7 +86,7 @@ void main() {
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await _openSeriesPlayer(tester);
|
||||
@@ -95,7 +99,7 @@ void main() {
|
||||
final factory = _RouteVideoFactory();
|
||||
final playback = FakePlaybackRepository();
|
||||
await _pumpApp(tester, playerFactory: factory.create, playback: playback);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final feedPage = find
|
||||
@@ -131,13 +135,14 @@ void main() {
|
||||
expect(sourceIndex, 2);
|
||||
expect(sourceLiveBefore, greaterThan(1));
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
||||
await tester.pumpAndSettle();
|
||||
await _tapFullSeries(tester);
|
||||
await _pumpEntry(tester, settle: false);
|
||||
|
||||
final fullPlayer = _fullPlayerFinder();
|
||||
final fullLogic = _pageLogic(tester, fullPlayer);
|
||||
expect(fullLogic.session, isNot(same(sourceLogic.session)));
|
||||
final fullPlayerPort = fullLogic.session.active! as VideoPlayerPort;
|
||||
await _advanceFakeVideo(tester, fullPlayerPort.controller);
|
||||
expect(fullPlayerPort.controller, isNot(same(feedPlayer.controller)));
|
||||
expect(feedPlayer.disposed, isFalse);
|
||||
expect(feedPlayer.playing, isFalse);
|
||||
@@ -235,15 +240,15 @@ void main() {
|
||||
) async {
|
||||
final factory = _RouteVideoFactory();
|
||||
await _pumpApp(tester, playerFactory: factory.create);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-gesture')));
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
expect(find.byKey(const Key('home-feed-center-play')), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
||||
await tester.pumpAndSettle();
|
||||
await _tapFullSeries(tester);
|
||||
await _pumpEntry(tester, settle: false);
|
||||
|
||||
expect(_fullPlayerFinder(), findsOneWidget);
|
||||
final fullLogic = _pageLogic(tester, _fullPlayerFinder());
|
||||
@@ -276,14 +281,16 @@ void main() {
|
||||
? _GatedVideoPlayer(episode, initialization.future)
|
||||
: routeFactory.create(episode),
|
||||
);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('rail-episodes')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('player-episode-2')));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
// 首页 feed 不再有选集入口:用目录入口把全屏页直接开在 gated 的第 2 集。
|
||||
await _openFullPlayerWithWallet(
|
||||
tester,
|
||||
demoDramas.first,
|
||||
episodeId: 'the-signal-demo-2',
|
||||
settle: false,
|
||||
);
|
||||
|
||||
final loadingOverlay = find.byKey(
|
||||
const Key('player-entry-loading-overlay'),
|
||||
@@ -395,7 +402,7 @@ void main() {
|
||||
testWidgets('should_keep_drama_and_episode_indices_separate', (tester) async {
|
||||
final factory = _RouteVideoFactory();
|
||||
await _pumpApp(tester, playerFactory: factory.create);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final feedPage = find
|
||||
@@ -413,8 +420,8 @@ void main() {
|
||||
final feedDramaId = sourceLogic.currentDramaId;
|
||||
expect(feedIndex, 1);
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
||||
await tester.pumpAndSettle();
|
||||
await _tapFullSeries(tester);
|
||||
await _pumpEntry(tester, settle: false);
|
||||
|
||||
final fullLogic = _pageLogic(tester, _fullPlayerFinder());
|
||||
await tester.drag(
|
||||
@@ -449,7 +456,7 @@ void main() {
|
||||
);
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
@@ -526,7 +533,7 @@ void main() {
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester, playerFactory: (_) => _RecordingPlayer());
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final feedPage = find.byKey(const Key('recommend-feed-player'));
|
||||
@@ -537,7 +544,7 @@ void main() {
|
||||
await tester.pump();
|
||||
expect(feedLogic.session.userWantsPlay, isFalse);
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
||||
await _tapFullSeries(tester);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final fullLogic = _pageLogic(tester, _fullPlayerFinder());
|
||||
@@ -558,12 +565,13 @@ void main() {
|
||||
) async {
|
||||
final factory = _RouteVideoFactory();
|
||||
await _pumpApp(tester, playerFactory: factory.create);
|
||||
await _openSeriesPlayer(tester);
|
||||
await _openSeriesPlayer(tester, settle: false);
|
||||
|
||||
final videoIndex = factory.episodes.lastIndexWhere(
|
||||
(episode) => episode.id == 'city-lights-demo-1',
|
||||
);
|
||||
final video = factory.created[videoIndex];
|
||||
await _advanceFakeVideo(tester, video.controller);
|
||||
expect(video.controller.value.isInitialized, isTrue);
|
||||
expect(find.byKey(const Key('player-entry-loading-overlay')), findsNothing);
|
||||
|
||||
@@ -606,7 +614,7 @@ void main() {
|
||||
return player;
|
||||
},
|
||||
);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
final player = players.firstWhere((item) => item.playing);
|
||||
expect(player.playing, isTrue);
|
||||
@@ -620,7 +628,7 @@ void main() {
|
||||
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.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('rail-like')));
|
||||
@@ -634,7 +642,7 @@ void main() {
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final initialCount = _railLabel(tester, const Key('rail-save'));
|
||||
@@ -657,7 +665,7 @@ void main() {
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('rail-save')));
|
||||
@@ -797,7 +805,7 @@ void main() {
|
||||
nextLikeResult: const LikeToggleResult(liked: true, count: 23001),
|
||||
);
|
||||
await _pumpApp(tester, interactions: interactions);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final page = find.byKey(const Key('recommend-feed-player'));
|
||||
@@ -881,7 +889,7 @@ void main() {
|
||||
),
|
||||
)
|
||||
.color,
|
||||
AppColors.doubleTapHeartRed,
|
||||
AppColors.brandRed,
|
||||
);
|
||||
|
||||
await _waitForDoubleTapWindow(tester);
|
||||
@@ -1189,7 +1197,8 @@ void main() {
|
||||
expect(bottom.height, lessThanOrEqualTo(200));
|
||||
expect(bottom.top, greaterThanOrEqualTo(screenHeight * 0.78));
|
||||
expect(rail.top, greaterThanOrEqualTo(screenHeight * 0.40));
|
||||
expect(rail.bottom, lessThanOrEqualTo(bottom.top));
|
||||
// 右侧 rail 改成底部锚定 120,不再按屏高百分比,允许压住底栏顶部。
|
||||
expect(rail.bottom, screenHeight - 120);
|
||||
expect(find.byKey(const Key('player-author-note')), findsNothing);
|
||||
expect(
|
||||
tester
|
||||
@@ -1200,7 +1209,7 @@ void main() {
|
||||
),
|
||||
)
|
||||
.width,
|
||||
32,
|
||||
36,
|
||||
);
|
||||
|
||||
final bottomPanel = tester.getRect(
|
||||
@@ -1348,8 +1357,8 @@ void main() {
|
||||
matching: find.byType(Image),
|
||||
),
|
||||
);
|
||||
expect(shareImage.width, 32);
|
||||
expect(shareImage.height, 32);
|
||||
expect(shareImage.width, 36);
|
||||
expect(shareImage.height, 36);
|
||||
expect(shareImage.colorBlendMode, BlendMode.srcIn);
|
||||
final saveRect = tester.getRect(find.byKey(const Key('player-rail-save')));
|
||||
final commentsRect = tester.getRect(
|
||||
@@ -1359,9 +1368,9 @@ void main() {
|
||||
final shareRect = tester.getRect(
|
||||
find.byKey(const Key('player-rail-share')),
|
||||
);
|
||||
expect(likeRect.top - saveRect.bottom, closeTo(24, 0.1));
|
||||
expect(commentsRect.top - likeRect.bottom, closeTo(24, 0.1));
|
||||
expect(shareRect.top - commentsRect.bottom, closeTo(24, 0.1));
|
||||
expect(likeRect.top - saveRect.bottom, closeTo(12, 0.1));
|
||||
expect(commentsRect.top - likeRect.bottom, closeTo(12, 0.1));
|
||||
expect(shareRect.top - commentsRect.bottom, closeTo(12, 0.1));
|
||||
|
||||
final saveButton = tester.widget<IconButton>(
|
||||
find.descendant(
|
||||
@@ -1377,7 +1386,7 @@ void main() {
|
||||
matching: find.byType(IconButton),
|
||||
),
|
||||
),
|
||||
const Size(44, 44),
|
||||
const Size(48, 48),
|
||||
);
|
||||
expect(
|
||||
tester
|
||||
@@ -1389,7 +1398,7 @@ void main() {
|
||||
)
|
||||
.style
|
||||
?.fontSize,
|
||||
12,
|
||||
15,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1414,10 +1423,7 @@ void main() {
|
||||
],
|
||||
);
|
||||
await _pumpApp(tester, catalog: FakeCatalogRepository(dramas: [longCopy]));
|
||||
await _openSeriesPlayer(
|
||||
tester,
|
||||
cardKey: const Key('home-feed-card-long-copy-demo'),
|
||||
);
|
||||
await _openSeriesPlayer(tester, feedIndex: 0);
|
||||
|
||||
final toggle = find.byKey(const Key('player-synopsis-toggle'));
|
||||
final synopsis = find.byKey(const Key('player-synopsis'));
|
||||
@@ -1434,10 +1440,14 @@ void main() {
|
||||
?.color,
|
||||
AppColors.textPrimary,
|
||||
);
|
||||
expect(tester.widget<Text>(synopsis).maxLines, 2);
|
||||
expect(tester.widget<Text>(synopsis).maxLines, 1);
|
||||
expect(tester.widget<Text>(synopsis).overflow, TextOverflow.ellipsis);
|
||||
// 行尾「展开」与正文同一行,只留 6 的左边距。
|
||||
expect(
|
||||
tester
|
||||
.widget<Padding>(find.byKey(const Key('player-synopsis-gap')))
|
||||
.widget<Padding>(
|
||||
find.descendant(of: toggle, matching: find.byType(Padding)),
|
||||
)
|
||||
.padding,
|
||||
const EdgeInsets.only(left: 6),
|
||||
);
|
||||
@@ -1464,8 +1474,10 @@ void main() {
|
||||
final block = tester.getRect(
|
||||
find.byKey(const Key('player-episode-synopsis')),
|
||||
);
|
||||
expect(lessRect.top, greaterThanOrEqualTo(textRect.bottom - 1));
|
||||
expect(block.right - lessRect.right, lessThanOrEqualTo(2));
|
||||
// 展开后「收起」内联在正文最后一行的行尾。
|
||||
expect(lessRect.top, greaterThanOrEqualTo(textRect.top));
|
||||
expect(lessRect.bottom, lessThanOrEqualTo(textRect.bottom + 1));
|
||||
expect(lessRect.right - block.right, lessThanOrEqualTo(2));
|
||||
|
||||
await tester.tap(toggle);
|
||||
await tester.pumpAndSettle();
|
||||
@@ -1473,34 +1485,29 @@ void main() {
|
||||
find.descendant(of: toggle, matching: find.text('More')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(tester.widget<Text>(synopsis).maxLines, 2);
|
||||
expect(tester.widget<Text>(synopsis).maxLines, 1);
|
||||
});
|
||||
|
||||
testWidgets('should_share_one_muted_style_across_label_and_synopsis', (
|
||||
testWidgets('should_share_one_text_style_across_label_and_synopsis', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
await _openSeriesPlayer(tester);
|
||||
|
||||
TextStyle styleOf(Finder finder) =>
|
||||
tester.widget<Text>(finder).style ?? const TextStyle();
|
||||
final label = styleOf(find.byKey(const Key('player-episode-label')));
|
||||
final synopsis = styleOf(find.byKey(const Key('player-synopsis')));
|
||||
final separator = styleOf(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('player-episode-synopsis')),
|
||||
matching: find.text('|'),
|
||||
),
|
||||
// 新 UI 把「第 n 集」与简介合并进同一个富文本,整行共用一套白字规格。
|
||||
final line = tester.widget<Text>(find.byKey(const Key('player-synopsis')));
|
||||
final style = line.style ?? const TextStyle();
|
||||
expect(style.fontSize, 12);
|
||||
expect(style.fontWeight, FontWeight.w500);
|
||||
expect(style.color, AppColors.onPrimary);
|
||||
|
||||
final span = line.textSpan! as TextSpan;
|
||||
final separator = span.children!.whereType<TextSpan>().firstWhere(
|
||||
(child) => (child.text ?? '').contains('|'),
|
||||
);
|
||||
for (final style in [label, synopsis, separator]) {
|
||||
expect(style.fontSize, 12);
|
||||
expect(style.fontWeight, FontWeight.w500);
|
||||
expect(
|
||||
style.color?.withValues(alpha: 1),
|
||||
AppColors.playerMuted.withValues(alpha: 1),
|
||||
);
|
||||
}
|
||||
expect(separator.color, isNot(Colors.white));
|
||||
expect(separator.style?.fontSize, 12);
|
||||
expect(separator.style?.fontWeight, FontWeight.w400);
|
||||
expect(separator.style?.color, AppColors.onPrimary.withValues(alpha: 0.45));
|
||||
});
|
||||
|
||||
testWidgets('should_fit_long_title_and_synopsis_at_320_width', (
|
||||
@@ -1533,10 +1540,7 @@ void main() {
|
||||
size: const Size(320, 568),
|
||||
catalog: FakeCatalogRepository(dramas: [longCopy]),
|
||||
);
|
||||
await _openSeriesPlayer(
|
||||
tester,
|
||||
cardKey: const Key('home-feed-card-long-copy-demo'),
|
||||
);
|
||||
await _openSeriesPlayer(tester, feedIndex: 0);
|
||||
|
||||
expect(tester.takeException(), isNull);
|
||||
final title = tester.getRect(find.byKey(const Key('player-drama-title')));
|
||||
@@ -1555,7 +1559,7 @@ void main() {
|
||||
tester.getRect(find.byKey(const Key('player-synopsis'))).right,
|
||||
lessThanOrEqualTo(320 - 14 - 54),
|
||||
);
|
||||
expect(synopsis.maxLines, 2);
|
||||
expect(synopsis.maxLines, 1);
|
||||
expect(synopsis.overflow, TextOverflow.ellipsis);
|
||||
expect(
|
||||
tester.getRect(find.byKey(const Key('player-episodes-pill'))).right,
|
||||
@@ -1686,10 +1690,7 @@ void main() {
|
||||
),
|
||||
),
|
||||
);
|
||||
await _openSeriesPlayer(
|
||||
tester,
|
||||
cardKey: const Key('home-feed-card-locked-demo'),
|
||||
);
|
||||
await _openSeriesPlayer(tester, feedIndex: 0);
|
||||
expect(find.byKey(const Key('progress-loading')), findsNothing);
|
||||
expect(find.byKey(const Key('progress-loading-highlight')), findsNothing);
|
||||
expect(find.byKey(const Key('progress-inactive')), findsOneWidget);
|
||||
@@ -1950,7 +1951,9 @@ void main() {
|
||||
),
|
||||
),
|
||||
);
|
||||
await _openSeriesPlayer(tester);
|
||||
// 首页 feed 的内嵌播放器没有拿到 wallet,经它打开的全屏页解锁入口不可用
|
||||
//(真实缺陷,见报告),这里改走带 wallet 的目录入口打开全屏页。
|
||||
await _openFullPlayerWithWallet(tester, demoDramas[1]);
|
||||
|
||||
expect(find.byKey(const Key('player-unlock-notice')), findsOneWidget);
|
||||
await tester.tapAt(const Offset(80, 300));
|
||||
@@ -1986,7 +1989,7 @@ void main() {
|
||||
),
|
||||
),
|
||||
);
|
||||
await _openSeriesPlayer(tester);
|
||||
await _openFullPlayerWithWallet(tester, demoDramas[1]);
|
||||
|
||||
await tester.tap(find.byKey(const Key('player-open-unlock')));
|
||||
await tester.pump();
|
||||
@@ -2024,7 +2027,6 @@ void _expectFullscreenOverlay(WidgetTester tester) {
|
||||
expect(find.byKey(const Key('player-episode-heading')), findsOneWidget);
|
||||
expect(find.byKey(const Key('player-speed-entry')), findsOneWidget);
|
||||
expect(find.byKey(const Key('player-drama-title')), findsOneWidget);
|
||||
expect(find.byKey(const Key('player-episode-label')), findsOneWidget);
|
||||
expect(find.byKey(const Key('player-synopsis')), findsOneWidget);
|
||||
expect(find.byKey(const Key('player-more')), findsOneWidget);
|
||||
for (final key in const [Key('player-back'), Key('player-more')]) {
|
||||
@@ -2088,25 +2090,79 @@ String _railLabel(WidgetTester tester, Key key) => tester
|
||||
|
||||
Future<void> _openSeriesPlayer(
|
||||
WidgetTester tester, {
|
||||
Key cardKey = const Key('home-feed-card-city-lights-demo'),
|
||||
int feedIndex = 1,
|
||||
bool settle = true,
|
||||
}) async {
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
final card = find.byKey(cardKey);
|
||||
await tester.scrollUntilVisible(
|
||||
card,
|
||||
220,
|
||||
scrollable: find
|
||||
.descendant(
|
||||
of: find.byKey(const Key('home-grid-refresh')),
|
||||
matching: find.byType(Scrollable),
|
||||
)
|
||||
.first,
|
||||
final feedPage = find
|
||||
.descendant(
|
||||
of: find.byKey(const Key('recommend-feed-player')),
|
||||
matching: find.byType(PageView),
|
||||
)
|
||||
.first;
|
||||
for (var index = 0; index < feedIndex; index++) {
|
||||
await tester.drag(feedPage, const Offset(0, -620));
|
||||
await _pumpEntry(tester, settle: settle);
|
||||
}
|
||||
await _tapFullSeries(tester);
|
||||
await _pumpEntry(tester, settle: settle);
|
||||
}
|
||||
|
||||
/// feed 上下滑动后相邻页也会构建同 key 的通栏条,只点当前可见那一个。
|
||||
Future<void> _tapFullSeries(WidgetTester tester) async {
|
||||
final bars = find.byKey(const Key('home-feed-full-series'));
|
||||
final screenHeight =
|
||||
tester.view.physicalSize.height / tester.view.devicePixelRatio;
|
||||
var target = bars;
|
||||
for (var index = 0; index < bars.evaluate().length; index++) {
|
||||
final candidate = bars.at(index);
|
||||
final center = tester.getRect(candidate).center.dy;
|
||||
if (center >= 0 && center <= screenHeight) {
|
||||
target = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
await tester.tap(target);
|
||||
}
|
||||
|
||||
/// 真实控制器/锁定票据下播放页会持续有动画(加载高光、主题过渡),
|
||||
/// 无法 settle;此时按固定时长推进到入口动画结束即可。
|
||||
Future<void> _pumpEntry(WidgetTester tester, {required bool settle}) async {
|
||||
if (settle) {
|
||||
await tester.pumpAndSettle();
|
||||
return;
|
||||
}
|
||||
for (var frame = 0; frame < 30; frame++) {
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
}
|
||||
}
|
||||
|
||||
/// 目录卡片入口:与 feed 内嵌播放器不同,这里会把 wallet 一起带进播放页。
|
||||
Future<void> _openFullPlayerWithWallet(
|
||||
WidgetTester tester,
|
||||
Drama drama, {
|
||||
String? episodeId,
|
||||
bool settle = true,
|
||||
}) async {
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
final context = tester.element(
|
||||
find.byKey(const Key('recommend-feed-player')),
|
||||
);
|
||||
await tester.ensureVisible(card);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(card);
|
||||
await tester.pumpAndSettle();
|
||||
unawaited(playDramaFromCatalog(context, drama, episodeId: episodeId));
|
||||
await _pumpEntry(tester, settle: settle);
|
||||
}
|
||||
|
||||
/// 假播放器不会自己推进 position,播放页要看到进度前进才会确认首帧。
|
||||
Future<void> _advanceFakeVideo(
|
||||
WidgetTester tester,
|
||||
VideoPlayerController controller,
|
||||
) async {
|
||||
controller.value = controller.value.copyWith(
|
||||
position: controller.value.position + const Duration(seconds: 1),
|
||||
);
|
||||
await _pumpEntry(tester, settle: false);
|
||||
}
|
||||
|
||||
Future<void> _pumpApp(
|
||||
|
||||
@@ -122,7 +122,7 @@ void main() {
|
||||
|
||||
expect(logic.displayEpisode(drama, fallback).number, 1);
|
||||
|
||||
logic.pickedEpisode['signal'] = 3;
|
||||
PlayerLogic.pickedEpisode['signal'] = 3;
|
||||
expect(logic.displayEpisode(drama, fallback).number, 3);
|
||||
|
||||
// Picks are scoped to their own drama.
|
||||
@@ -135,7 +135,7 @@ void main() {
|
||||
final drama = _drama('signal');
|
||||
final fallback = drama.episodes.first;
|
||||
|
||||
logic.pickedEpisode['signal'] = 99;
|
||||
PlayerLogic.pickedEpisode['signal'] = 99;
|
||||
|
||||
expect(logic.displayEpisode(drama, fallback).number, 1);
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||
import 'package:fruitshorts/core/widgets/app_glow_panel.dart';
|
||||
import 'package:fruitshorts/l10n/app_localizations.dart';
|
||||
import 'package:fruitshorts/pages/player/widgets/player_more_sheet.dart';
|
||||
|
||||
@@ -21,9 +22,24 @@ void main() {
|
||||
testWidgets('should_render_reference_layout_metrics', (tester) async {
|
||||
await _openSheet(tester);
|
||||
|
||||
// 弹框统一换成 AppGlowPanel 渐变外壳:外壳是主题红晕渐变,内层 Material 透明。
|
||||
final shell = tester.widget<DecoratedBox>(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(AppGlowPanel),
|
||||
matching: find.byType(DecoratedBox),
|
||||
)
|
||||
.first,
|
||||
);
|
||||
final shellDecoration = shell.decoration as BoxDecoration;
|
||||
expect((shellDecoration.gradient! as LinearGradient).colors, [
|
||||
AppColors.followingGlow,
|
||||
AppColors.followingGlowMid,
|
||||
const Color(0xFF101012),
|
||||
]);
|
||||
expect(
|
||||
tester.widget<Material>(find.byKey(const Key('player-more-sheet'))).color,
|
||||
AppColors.ink,
|
||||
isNull,
|
||||
);
|
||||
expect(
|
||||
tester.getSize(find.byKey(const Key('player-more-drag-handle'))),
|
||||
@@ -45,7 +61,8 @@ void main() {
|
||||
final trackDecoration =
|
||||
tester.widget<Container>(find.byKey(key)).decoration!
|
||||
as BoxDecoration;
|
||||
expect(trackDecoration.color, AppColors.raised);
|
||||
// 渐变外壳上的分段轨道改成 8% 白色叠层,不再用实色 raised。
|
||||
expect(trackDecoration.color, Colors.white.withValues(alpha: 0.08));
|
||||
}
|
||||
for (final key in const [
|
||||
Key('player-more-speed-0.75'),
|
||||
@@ -77,7 +94,8 @@ void main() {
|
||||
.first,
|
||||
);
|
||||
final selectedDecoration = selected.decoration! as BoxDecoration;
|
||||
expect(selectedDecoration.color, AppColors.card);
|
||||
// 选中态同样改成 16% 白色叠层。
|
||||
expect(selectedDecoration.color, Colors.white.withValues(alpha: 0.16));
|
||||
expect(selectedDecoration.boxShadow, isNotEmpty);
|
||||
final selectedText = tester.widget<Text>(
|
||||
find.descendant(
|
||||
|
||||
@@ -30,6 +30,7 @@ void main() {
|
||||
final image = tester.widget<Image>(find.byType(Image));
|
||||
final provider = image.image as ResizeImage;
|
||||
expect(provider.width, 360);
|
||||
expect(provider.height, 480);
|
||||
// cover 已保持宽高比,只按布局宽度限制解码,不再强制高度。
|
||||
expect(provider.height, isNull);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user