168 lines
5.2 KiB
Dart
168 lines
5.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:leaf_widgets/leaf_widgets.dart';
|
|
|
|
class FakeAdapter extends VideoPlayerAdapter {
|
|
bool playing = false;
|
|
bool disposed = false;
|
|
|
|
final _positionCtrl = StreamController<Duration>.broadcast();
|
|
final _bufferCtrl = StreamController<Duration>.broadcast();
|
|
final _playingCtrl = StreamController<bool>.broadcast();
|
|
final _readyCtrl = StreamController<void>.broadcast();
|
|
final _errorCtrl = StreamController<Object>.broadcast();
|
|
|
|
@override
|
|
Widget get surface => const ColoredBox(color: Color(0xFF332211));
|
|
|
|
@override
|
|
Future<void> load({required String url, Map<String, String>? headers}) async {}
|
|
|
|
void emitPlaying(bool value) {
|
|
playing = value;
|
|
_playingCtrl.add(value);
|
|
}
|
|
|
|
@override
|
|
Stream<Duration> get positionStream => _positionCtrl.stream;
|
|
@override
|
|
Stream<Duration> get bufferStream => _bufferCtrl.stream;
|
|
@override
|
|
Stream<bool> get playingStream => _playingCtrl.stream;
|
|
@override
|
|
Stream<void> get readyStream => _readyCtrl.stream;
|
|
@override
|
|
Stream<Object> get errorStream => _errorCtrl.stream;
|
|
|
|
@override
|
|
Duration get position => Duration.zero;
|
|
@override
|
|
Duration get duration => const Duration(seconds: 60);
|
|
@override
|
|
Duration get buffer => Duration.zero;
|
|
@override
|
|
bool get isPlaying => playing;
|
|
|
|
@override
|
|
void play() => emitPlaying(true);
|
|
@override
|
|
void pause() => emitPlaying(false);
|
|
@override
|
|
void playOrPause() => playing ? pause() : play();
|
|
@override
|
|
void seek(Duration target) {}
|
|
@override
|
|
void dispose() => disposed = true;
|
|
}
|
|
|
|
class FakeVolumeAdapter extends FakeAdapter implements HasVolume {
|
|
double _volume = 80;
|
|
final setVolumes = <double>[];
|
|
final _volumeCtrl = StreamController<double>.broadcast();
|
|
|
|
@override
|
|
Stream<double> get volumeStream => _volumeCtrl.stream;
|
|
@override
|
|
double get volume => _volume;
|
|
@override
|
|
void setVolume(double volume) {
|
|
setVolumes.add(volume);
|
|
_volume = volume;
|
|
_volumeCtrl.add(volume);
|
|
}
|
|
}
|
|
|
|
Widget harness(VideoPlayerAdapter adapter, {Duration hideDelay = const Duration(milliseconds: 500)}) {
|
|
return MaterialApp(
|
|
home: Scaffold(
|
|
body: LeafDesktopVideoControls(adapter: adapter, hideDelay: hideDelay),
|
|
),
|
|
);
|
|
}
|
|
|
|
AnimatedOpacity barOf(WidgetTester tester) {
|
|
final found = find.byWidgetPredicate((w) => w is AnimatedOpacity && w.child is GestureDetector);
|
|
return tester.widget<AnimatedOpacity>(found);
|
|
}
|
|
|
|
void main() {
|
|
testWidgets('single click anywhere toggles playback', (tester) async {
|
|
final adapter = FakeAdapter();
|
|
await tester.pumpWidget(harness(adapter));
|
|
expect(adapter.playing, isFalse);
|
|
|
|
await tester.tap(find.byType(LeafDesktopVideoControls));
|
|
await tester.pump(const Duration(milliseconds: 500));
|
|
expect(adapter.playing, isTrue);
|
|
|
|
await tester.tap(find.byType(LeafDesktopVideoControls));
|
|
await tester.pump(const Duration(milliseconds: 500));
|
|
expect(adapter.playing, isFalse);
|
|
});
|
|
|
|
testWidgets('bar hides after inactivity while playing, shows on pause', (tester) async {
|
|
final adapter = FakeAdapter()..playing = true;
|
|
await tester.pumpWidget(harness(adapter));
|
|
expect(barOf(tester).opacity, 1.0);
|
|
|
|
await tester.pump(const Duration(milliseconds: 600));
|
|
expect(barOf(tester).opacity, 0.0);
|
|
|
|
adapter.emitPlaying(false);
|
|
await tester.pump();
|
|
expect(barOf(tester).opacity, 1.0);
|
|
});
|
|
|
|
testWidgets('bar stays up while the cursor is active', (tester) async {
|
|
final adapter = FakeAdapter()..playing = true;
|
|
await tester.pumpWidget(harness(adapter));
|
|
await tester.pump(const Duration(milliseconds: 400));
|
|
expect(barOf(tester).opacity, 1.0);
|
|
|
|
final mouse = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
|
await mouse.moveTo(const Offset(400, 300));
|
|
await tester.pump(const Duration(milliseconds: 400));
|
|
expect(barOf(tester).opacity, 1.0);
|
|
|
|
await tester.pump(const Duration(milliseconds: 600));
|
|
expect(barOf(tester).opacity, 0.0);
|
|
});
|
|
|
|
testWidgets('volume UI only with HasVolume; mute toggles', (tester) async {
|
|
await tester.pumpWidget(harness(FakeAdapter()));
|
|
expect(find.byType(Slider), findsNothing);
|
|
|
|
final volume = FakeVolumeAdapter();
|
|
await tester.pumpWidget(harness(volume));
|
|
expect(find.byType(Slider), findsOneWidget);
|
|
expect(find.byIcon(Icons.volume_up), findsOneWidget);
|
|
|
|
await tester.tap(find.byIcon(Icons.volume_up));
|
|
await tester.pump();
|
|
expect(volume.setVolumes, contains(0.0));
|
|
expect(find.byIcon(Icons.volume_off), findsOneWidget);
|
|
|
|
await tester.tap(find.byIcon(Icons.volume_off));
|
|
await tester.pump();
|
|
expect(volume.setVolumes.last, 80.0);
|
|
});
|
|
|
|
testWidgets('hovering the scrubber shows the time bubble before clicking', (tester) async {
|
|
final adapter = FakeAdapter();
|
|
await tester.pumpWidget(harness(adapter));
|
|
|
|
final mouse = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
|
await mouse.moveTo(tester.getCenter(find.byType(LeafScrubber)));
|
|
await tester.pump();
|
|
expect(find.text('00:30'), findsOneWidget);
|
|
|
|
await mouse.moveTo(Offset.zero);
|
|
await tester.pump();
|
|
expect(find.text('00:30'), findsNothing);
|
|
});
|
|
}
|