feat(app): 完善播放协议搜索与空状态
This commit is contained in:
1 parent
671e0ce143
commit
ef29fc5f15
85 files changed
+2976
-768
No files matched your search
@@ -12,8 +12,8 @@ import '../support/fake_haptics.dart';
|
||||
import '../support/fake_interaction_repositories.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('double tap within 320ms never pauses', (tester) async {
|
||||
for (final delay in [120, 250, 300]) {
|
||||
testWidgets('double tap within 400ms never pauses', (tester) async {
|
||||
for (final delay in [120, 250, 300, 380]) {
|
||||
final repository = FakeInteractionRepository();
|
||||
final haptics = FakeHaptics();
|
||||
final key = GlobalKey<_HarnessState>();
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/pages/player/widgets/player_double_tap_hearts.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('should_show_three_medium_hearts_when_double_tap_burst_starts', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: Scaffold(body: PlayerDoubleTapHeartBurst(onCompleted: _noop)),
|
||||
),
|
||||
);
|
||||
await tester.pump(const Duration(milliseconds: 160));
|
||||
|
||||
expect(find.byIcon(Icons.favorite_rounded), findsNWidgets(3));
|
||||
expect(_size(tester, 0), 48);
|
||||
expect(_size(tester, 1), 36);
|
||||
expect(_size(tester, 2), 40);
|
||||
});
|
||||
}
|
||||
|
||||
double _size(WidgetTester tester, int index) {
|
||||
return tester.widget<Icon>(find.byKey(Key('double-tap-heart-$index'))).size!;
|
||||
}
|
||||
|
||||
void _noop() {}
|
||||
@@ -118,6 +118,75 @@ void main() {
|
||||
expect(disabledText.style?.color, AppColors.subtle);
|
||||
});
|
||||
|
||||
testWidgets('should_enable_quality_when_ticket_uses_lowercase_codes', (
|
||||
tester,
|
||||
) async {
|
||||
await _openSheet(
|
||||
tester,
|
||||
qualities: const ['AUTO', '360p', '480p', '720p', '1080p'],
|
||||
);
|
||||
|
||||
expect(find.text('720P'), findsOneWidget);
|
||||
expect(find.text('1080P'), findsOneWidget);
|
||||
expect(find.textContaining('暂不可用'), findsNothing);
|
||||
final enabled = tester.widget<Text>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('player-more-quality-720P')),
|
||||
matching: find.byType(Text),
|
||||
),
|
||||
);
|
||||
expect(enabled.style?.color, isNot(AppColors.subtle));
|
||||
});
|
||||
|
||||
testWidgets('should_report_selected_quality_when_a_rendition_is_tapped', (
|
||||
tester,
|
||||
) async {
|
||||
String? picked;
|
||||
await _openSheet(
|
||||
tester,
|
||||
qualities: const ['AUTO', '720p'],
|
||||
onQuality: (quality) async {
|
||||
picked = quality;
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
await tester.ensureVisible(find.byKey(const Key('player-more-quality-720P')));
|
||||
await tester.tap(find.byKey(const Key('player-more-quality-720P')));
|
||||
await tester.pump();
|
||||
|
||||
expect(picked, '720P');
|
||||
final selected = tester.widget<Text>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('player-more-quality-720P')),
|
||||
matching: find.byType(Text),
|
||||
),
|
||||
);
|
||||
expect(selected.style?.color, AppColors.onSurface);
|
||||
});
|
||||
|
||||
testWidgets('should_restore_previous_quality_when_switch_fails', (
|
||||
tester,
|
||||
) async {
|
||||
await _openSheet(
|
||||
tester,
|
||||
qualities: const ['AUTO', '720p'],
|
||||
onQuality: (_) async => false,
|
||||
);
|
||||
|
||||
await tester.ensureVisible(find.byKey(const Key('player-more-quality-720P')));
|
||||
await tester.tap(find.byKey(const Key('player-more-quality-720P')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final restored = tester.widget<Text>(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('player-more-quality-AUTO')),
|
||||
matching: find.byType(Text),
|
||||
),
|
||||
);
|
||||
expect(restored.style?.color, AppColors.onSurface);
|
||||
});
|
||||
|
||||
testWidgets('should_trigger_pip_and_close_sheet_when_tapped', (tester) async {
|
||||
var pipCalls = 0;
|
||||
await _openSheet(tester, onPip: () => pipCalls++);
|
||||
@@ -142,7 +211,12 @@ void main() {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _openSheet(WidgetTester tester, {VoidCallback? onPip}) async {
|
||||
Future<void> _openSheet(
|
||||
WidgetTester tester, {
|
||||
VoidCallback? onPip,
|
||||
Future<bool> Function(String quality)? onQuality,
|
||||
List<String> qualities = const ['AUTO'],
|
||||
}) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
@@ -158,11 +232,12 @@ Future<void> _openSheet(WidgetTester tester, {VoidCallback? onPip}) async {
|
||||
isScrollControlled: true,
|
||||
builder: (_) => PlayerMoreSheet(
|
||||
l10n: AppLocalizations.of(context)!,
|
||||
state: const PlayerMoreState(speed: 1.0, qualities: ['AUTO']),
|
||||
state: PlayerMoreState(speed: 1.0, qualities: qualities),
|
||||
actions: PlayerMoreActions(
|
||||
onSpeedChanged: (_) {},
|
||||
onClear: () {},
|
||||
onDownload: () {},
|
||||
onQualityChanged: onQuality ?? (_) async => true,
|
||||
onPip: onPip,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -69,6 +69,51 @@ void main() {
|
||||
expect(_opacity(tester), 0);
|
||||
expect(find.byKey(const Key('player-debug-video')), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_cover_new_video_only_while_handoff_is_held', (
|
||||
tester,
|
||||
) async {
|
||||
final current = VideoPlayerController.networkUrl(
|
||||
Uri.parse('http://192.168.31.187:8081/files/720.m3u8'),
|
||||
);
|
||||
final previous = VideoPlayerController.networkUrl(
|
||||
Uri.parse('http://192.168.31.187:8081/files/360.m3u8'),
|
||||
);
|
||||
addTearDown(current.dispose);
|
||||
addTearDown(previous.dispose);
|
||||
const ready = VideoPlayerValue(
|
||||
duration: Duration(minutes: 1),
|
||||
size: Size(1280, 720),
|
||||
position: Duration(seconds: 3),
|
||||
isInitialized: true,
|
||||
isPlaying: true,
|
||||
);
|
||||
current.value = ready;
|
||||
previous.value = ready;
|
||||
|
||||
Future<void> pump(VideoPlayerController? handoff) {
|
||||
return tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: PlayerVideoSurface(
|
||||
controller: current,
|
||||
handoff: handoff,
|
||||
videoUrl: current.dataSource,
|
||||
theater: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await pump(previous);
|
||||
expect(find.byKey(const Key('player-video-handoff')), findsOneWidget);
|
||||
expect(_opacity(tester), 1);
|
||||
|
||||
await pump(null);
|
||||
expect(find.byKey(const Key('player-video-handoff')), findsNothing);
|
||||
expect(find.byType(VideoPlayer), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
Future<VideoPlayerController> _pumpSurface(
|
||||
|
||||
@@ -154,12 +154,37 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('feed progress thumb stays on the finger when dragged right', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpFeedOverlay(tester);
|
||||
final bar = find.byKey(const Key('home-feed-progress'));
|
||||
final gesture = await tester.startGesture(tester.getCenter(bar));
|
||||
|
||||
await gesture.moveBy(const Offset(800, 0));
|
||||
await tester.pump();
|
||||
|
||||
final thumbCenter = tester
|
||||
.getRect(find.byKey(const Key('home-feed-progress-thumb')))
|
||||
.center
|
||||
.dx;
|
||||
final playedRight = tester
|
||||
.getRect(find.byKey(const Key('home-feed-progress-played')))
|
||||
.right;
|
||||
expect(thumbCenter, closeTo(tester.getRect(bar).right, 0.5));
|
||||
expect(playedRight, closeTo(thumbCenter, 0.5));
|
||||
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('full player drag clears chrome and release restores it', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpFullPlayerOverlay(tester);
|
||||
expect(find.byKey(const Key('player-controls-top')), findsOneWidget);
|
||||
expect(find.byKey(const Key('player-bottom-actions')), findsOneWidget);
|
||||
final progressTop = tester.getTopLeft(find.byType(Slider)).dy;
|
||||
|
||||
final gesture = await tester.startGesture(
|
||||
tester.getCenter(find.byKey(const Key('player-progress'))),
|
||||
@@ -170,8 +195,9 @@ void main() {
|
||||
expect(_opacity(tester, const Key('player-controls-top')), 0);
|
||||
expect(_opacity(tester, const Key('player-interaction-rail')), 0);
|
||||
expect(_offstage(tester, const Key('player-controls-bottom')), isTrue);
|
||||
expect(_offstage(tester, const Key('player-bottom-actions')), isTrue);
|
||||
expect(_opacity(tester, const Key('player-bottom-actions')), 0);
|
||||
expect(find.byKey(const Key('player-progress')), findsOneWidget);
|
||||
expect(tester.getTopLeft(find.byType(Slider)).dy, progressTop);
|
||||
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
@@ -179,6 +205,7 @@ void main() {
|
||||
expect(_opacity(tester, const Key('player-controls-top')), 1);
|
||||
expect(_opacity(tester, const Key('player-interaction-rail')), 1);
|
||||
expect(_offstage(tester, const Key('player-bottom-actions')), isFalse);
|
||||
expect(tester.getTopLeft(find.byType(Slider)).dy, progressTop);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user