fix(player): 播放页改为非阻塞加载
This commit is contained in:
1 parent
e8f192ccf8
commit
ee4e573f4f
7 files changed
+239
-455
No files matched your search
@@ -129,30 +129,32 @@ void main() {
|
||||
expect(sourceLiveBefore, greaterThan(1));
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
||||
await _waitForFullPlayerEntry(tester);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final fullPlayer = _fullPlayerFinder();
|
||||
final fullLogic = _pageLogic(tester, fullPlayer);
|
||||
expect(fullLogic.session, isNot(same(sourceLogic.session)));
|
||||
final fullPlayerWidget = tester.widget<PlayerPage>(fullPlayer);
|
||||
expect(
|
||||
fullPlayerWidget.initialFrameBytes != null ||
|
||||
fullPlayerWidget.initialFrameProvider != null,
|
||||
isTrue,
|
||||
);
|
||||
expect(fullPlayerWidget.revealAfterFirstFrame, isTrue);
|
||||
final fullPlayerPort = fullLogic.session.active! as VideoPlayerPort;
|
||||
expect(fullPlayerPort.controller, isNot(same(feedPlayer.controller)));
|
||||
expect(feedPlayer.disposed, isFalse);
|
||||
expect(feedPlayer.playing, isFalse);
|
||||
|
||||
final fullPlayerPage = find
|
||||
.descendant(
|
||||
of: _fullPlayerFinder(),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PageView && widget.scrollDirection == Axis.vertical,
|
||||
),
|
||||
)
|
||||
.first;
|
||||
for (var index = 0; index < 4; index++) {
|
||||
await tester.drag(
|
||||
find.byKey(const Key('player-video-tap')),
|
||||
fullPlayerPage,
|
||||
const Offset(0, -560),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
}
|
||||
|
||||
expect(fullLogic.session.currentEpisode.number, 5);
|
||||
@@ -168,7 +170,8 @@ void main() {
|
||||
expect(feedPlayer.disposed, isFalse);
|
||||
|
||||
final requestsBeforeReturn = playback.requests.length;
|
||||
await tester.tap(find.byKey(const Key('player-back')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('player-back')).hitTestable());
|
||||
await tester.pumpAndSettle();
|
||||
await sourceLogic.session.preloadSettled;
|
||||
await tester.pumpAndSettle();
|
||||
@@ -229,7 +232,6 @@ void main() {
|
||||
expect(find.byKey(const Key('home-feed-center-play')), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
||||
await _waitForFullPlayerEntry(tester);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(_fullPlayerFinder(), findsOneWidget);
|
||||
@@ -250,36 +252,109 @@ void main() {
|
||||
expect(find.byKey(const Key('home-tabs-bar')), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should_capture_feed_frame_when_opening_series_from_picker', (
|
||||
testWidgets('should_show_non_blocking_loading_when_opening_series', (
|
||||
tester,
|
||||
) async {
|
||||
final factory = _RouteVideoFactory();
|
||||
await _pumpApp(tester, playerFactory: factory.create);
|
||||
final initialization = Completer<void>();
|
||||
final routeFactory = _RouteVideoFactory();
|
||||
await _pumpApp(
|
||||
tester,
|
||||
playerFactory: (episode) => episode.id == 'the-signal-demo-2'
|
||||
? _GatedPlayer(initialization.future)
|
||||
: routeFactory.create(episode),
|
||||
);
|
||||
await tester.tap(find.byKey(const Key('tab-recommend')));
|
||||
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 _waitForRevealedPlayerEntry(tester);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 16));
|
||||
|
||||
final player = find.byWidgetPredicate(
|
||||
(widget) => widget is PlayerPage && widget.revealAfterFirstFrame,
|
||||
final loadingOverlay = find.byKey(
|
||||
const Key('player-entry-loading-overlay'),
|
||||
);
|
||||
expect(player, findsOneWidget);
|
||||
final playerWidget = tester.widget<PlayerPage>(player);
|
||||
final fullPlayer = find.byType(PlayerPage).last;
|
||||
final fullLogic = _pageLogic(tester, fullPlayer);
|
||||
final fullPlayerPage = find
|
||||
.descendant(
|
||||
of: fullPlayer,
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PageView && widget.scrollDirection == Axis.vertical,
|
||||
),
|
||||
)
|
||||
.first;
|
||||
expect(loadingOverlay, findsOneWidget);
|
||||
expect(tester.widget<IgnorePointer>(loadingOverlay).ignoring, isTrue);
|
||||
expect(
|
||||
playerWidget.initialFrameBytes != null ||
|
||||
playerWidget.initialFrameProvider != null,
|
||||
isTrue,
|
||||
tester
|
||||
.widget<AnimatedOpacity>(
|
||||
find.descendant(
|
||||
of: loadingOverlay,
|
||||
matching: find.byType(AnimatedOpacity),
|
||||
),
|
||||
)
|
||||
.duration,
|
||||
const Duration(milliseconds: 140),
|
||||
);
|
||||
expect(playerWidget.revealAfterFirstFrame, isTrue);
|
||||
expect(playerWidget.initialIndex, 1);
|
||||
expect(
|
||||
tester.getSize(find.byKey(const Key('player-loading-surface'))),
|
||||
const Size(40, 40),
|
||||
);
|
||||
expect(
|
||||
tester.getSize(find.byKey(const Key('player-entry-loading'))),
|
||||
const Size(20, 20),
|
||||
);
|
||||
expect(
|
||||
tester
|
||||
.widget<CircularProgressIndicator>(
|
||||
find.byKey(const Key('player-entry-loading')),
|
||||
)
|
||||
.color,
|
||||
Colors.white,
|
||||
);
|
||||
expect(
|
||||
tester
|
||||
.widget<ColoredBox>(
|
||||
find.descendant(
|
||||
of: loadingOverlay,
|
||||
matching: find.byType(ColoredBox),
|
||||
),
|
||||
)
|
||||
.color,
|
||||
AppColors.mediaBlack,
|
||||
);
|
||||
final loadingSurface = tester.widget<Container>(
|
||||
find.byKey(const Key('player-loading-surface')),
|
||||
);
|
||||
final decoration = loadingSurface.decoration! as BoxDecoration;
|
||||
expect(decoration.color, AppColors.refreshIndicatorSurface);
|
||||
expect(decoration.borderRadius, BorderRadius.circular(20));
|
||||
|
||||
await tester.tap(find.byKey(const Key('player-back')));
|
||||
// IgnorePointer 让加载中的播放暂停与上下滑仍能透传。
|
||||
expect(fullLogic.session.userWantsPlay, isTrue);
|
||||
await tester.tap(find.byKey(const Key('player-video-tap')).hitTestable());
|
||||
await tester.pump();
|
||||
expect(fullLogic.session.userWantsPlay, isFalse);
|
||||
final indexBeforeSwipe = fullLogic.session.currentIndex;
|
||||
await tester.drag(
|
||||
fullPlayerPage,
|
||||
const Offset(0, -560),
|
||||
);
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
expect(fullLogic.session.currentIndex, isNot(indexBeforeSwipe));
|
||||
|
||||
// IgnorePointer 让加载中的返回按钮仍可点击。
|
||||
await tester.pumpAndSettle();
|
||||
expect(loadingOverlay, findsNothing);
|
||||
await tester.tap(find.byKey(const Key('player-back')).hitTestable());
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const Key('recommend-page')), findsOneWidget);
|
||||
initialization.complete();
|
||||
await tester.pump();
|
||||
});
|
||||
|
||||
testWidgets('should_keep_drama_and_episode_indices_separate', (tester) async {
|
||||
@@ -304,7 +379,6 @@ void main() {
|
||||
expect(feedIndex, 1);
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
||||
await _waitForFullPlayerEntry(tester);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final fullLogic = _pageLogic(tester, _fullPlayerFinder());
|
||||
@@ -1700,35 +1774,6 @@ Finder _fullPlayerFinder() {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _waitForFullPlayerEntry(WidgetTester tester) async {
|
||||
for (var attempt = 0; attempt < 120; attempt++) {
|
||||
await tester.runAsync(
|
||||
() => Future<void>.delayed(const Duration(milliseconds: 10)),
|
||||
);
|
||||
await tester.pump(const Duration(milliseconds: 16));
|
||||
if (_fullPlayerFinder().evaluate().isNotEmpty) return;
|
||||
}
|
||||
fail('full player route was not pushed after frame capture');
|
||||
}
|
||||
|
||||
Future<void> _waitForRevealedPlayerEntry(WidgetTester tester) async {
|
||||
for (var attempt = 0; attempt < 120; attempt++) {
|
||||
await tester.runAsync(
|
||||
() => Future<void>.delayed(const Duration(milliseconds: 10)),
|
||||
);
|
||||
await tester.pump(const Duration(milliseconds: 16));
|
||||
if (find
|
||||
.byWidgetPredicate(
|
||||
(widget) => widget is PlayerPage && widget.revealAfterFirstFrame,
|
||||
)
|
||||
.evaluate()
|
||||
.isNotEmpty) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail('revealed player route was not pushed after frame capture');
|
||||
}
|
||||
|
||||
class _RouteVideoFactory {
|
||||
final List<_RouteVideoPlayer> created = [];
|
||||
final List<Episode> episodes = [];
|
||||
|
||||
@@ -470,7 +470,6 @@ void main() {
|
||||
await _pumpRecommend(tester);
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-series-name')));
|
||||
await _waitForFullPlayer(tester);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(PlayerPage).last, findsOneWidget);
|
||||
expect(
|
||||
@@ -485,7 +484,6 @@ void main() {
|
||||
expect(find.byKey(const Key('recommend-tab-recommend')), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
||||
await _waitForFullPlayer(tester);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(PlayerPage).last, findsOneWidget);
|
||||
expect(
|
||||
@@ -552,24 +550,6 @@ void main() {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _waitForFullPlayer(WidgetTester tester) async {
|
||||
for (var attempt = 0; attempt < 120; attempt++) {
|
||||
await tester.runAsync(
|
||||
() => Future<void>.delayed(const Duration(milliseconds: 10)),
|
||||
);
|
||||
await tester.pump(const Duration(milliseconds: 16));
|
||||
if (find
|
||||
.byWidgetPredicate(
|
||||
(widget) => widget is PlayerPage && !widget.homeMode,
|
||||
)
|
||||
.evaluate()
|
||||
.isNotEmpty) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail('full player route was not pushed after frame capture');
|
||||
}
|
||||
|
||||
Future<void> _pumpRecommend(WidgetTester tester) async {
|
||||
tester.view.physicalSize = const Size(390, 844);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
|
||||
Reference in new issue
Block a user