// Author: fruit.com.cn import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:video_player/video_player.dart'; import 'package:fruitshorts/pages/player/widgets/player_video_surface.dart'; void main() { testWidgets('should_show_black_without_placeholder_before_video', ( tester, ) async { await _pumpSurface(tester); expect(_fallbackColor(tester), Colors.black); expect(find.byKey(const Key('player-single-placeholder')), findsNothing); expect(find.byKey(const Key('player-video-layer')), findsNothing); }); testWidgets('should_show_exactly_one_placeholder_before_first_frame', ( tester, ) async { await _pumpSurface(tester, placeholder: _placeholder('opening')); expect(find.byKey(const Key('player-single-placeholder')), findsOneWidget); expect(find.byKey(const Key('opening')), findsOneWidget); expect(find.byKey(const Key('player-video-crossfade')), findsNothing); expect(find.byKey(const Key('player-video-layer')), findsNothing); }); testWidgets('should_skip_placeholder_for_warm_controller', (tester) async { await _pumpSurface( tester, placeholder: _placeholder('opening'), warmFrameAvailable: true, thumbnailReady: true, value: _initializedValue, ); expect(find.byKey(const Key('player-single-placeholder')), findsNothing); expect(find.byKey(const Key('player-video-layer')), findsOneWidget); expect(find.byKey(const Key('player-video-crossfade')), findsNothing); }); testWidgets('should_cross_fade_once_after_first_frame_is_confirmed', ( tester, ) async { final controller = await _pumpSurface( tester, placeholder: _placeholder('opening'), thumbnailReady: true, waitForPlaybackFrame: true, ); controller.value = _readyValue(position: Duration.zero, playing: true); await tester.pump(); expect(find.byKey(const Key('player-single-placeholder')), findsOneWidget); expect(find.byKey(const Key('player-video-crossfade')), findsNothing); controller.value = _readyValue( position: const Duration(milliseconds: 1), playing: true, ); await tester.pump(); await tester.pump(); await tester.pump(); expect(find.byKey(const Key('player-single-placeholder')), findsOneWidget); expect(find.byKey(const Key('player-video-crossfade')), findsOneWidget); expect(find.byKey(const Key('player-video-layer')), findsNothing); expect(_crossFadeValue(tester), 0); await tester.pump(const Duration(milliseconds: 90)); expect(find.byKey(const Key('player-single-placeholder')), findsOneWidget); expect(find.byKey(const Key('player-video-crossfade')), findsOneWidget); expect(_crossFadeValue(tester), greaterThan(0)); expect(_crossFadeValue(tester), lessThan(1)); await tester.pump(const Duration(milliseconds: 100)); expect(find.byKey(const Key('player-single-placeholder')), findsNothing); expect(find.byKey(const Key('player-video-crossfade')), findsNothing); expect(find.byKey(const Key('player-video-layer')), findsOneWidget); controller.value = _readyValue( position: const Duration(seconds: 1), playing: true, ); await tester.pump(); expect(find.byKey(const Key('player-video-crossfade')), findsNothing); expect(find.byKey(const Key('player-video-layer')), findsOneWidget); }); testWidgets('should_keep_placeholder_above_video_error', (tester) async { await _pumpSurface( tester, placeholder: _placeholder('opening'), value: const VideoPlayerValue.erroneous('No route to host'), ); expect(find.byKey(const Key('opening')), findsOneWidget); expect(find.byKey(const Key('player-video-error')), findsOneWidget); expect(find.text('No route to host'), findsOneWidget); }); } Widget _placeholder(String key) => ColoredBox(key: Key(key), color: Colors.red); const VideoPlayerValue _initializedValue = VideoPlayerValue( duration: Duration(minutes: 1), size: Size(1280, 720), position: Duration.zero, isInitialized: true, isPlaying: false, ); VideoPlayerValue _readyValue({ required Duration position, required bool playing, }) => VideoPlayerValue( duration: const Duration(minutes: 1), size: const Size(1280, 720), position: position, isInitialized: true, isPlaying: playing, ); Future _pumpSurface( WidgetTester tester, { VideoPlayerValue? value, Widget? placeholder, bool warmFrameAvailable = false, bool thumbnailReady = false, bool waitForPlaybackFrame = false, }) async { final controller = VideoPlayerController.networkUrl( Uri.parse('http://192.168.31.187:8081/files/video.mp4'), ); addTearDown(controller.dispose); if (value != null) controller.value = value; await tester.pumpWidget( MaterialApp( home: Scaffold( body: PlayerVideoSurface( controller: controller, videoUrl: controller.dataSource, theater: false, placeholder: placeholder, warmFrameAvailable: warmFrameAvailable, thumbnailReady: thumbnailReady, waitForPlaybackFrame: waitForPlaybackFrame, ), ), ), ); await tester.pump(); return controller; } Color _fallbackColor(WidgetTester tester) { return tester .widget(find.byKey(const Key('player-fallback-background'))) .color; } double _crossFadeValue(WidgetTester tester) { return tester .widget(find.byKey(const Key('player-video-crossfade'))) .opacity .value; }