367 lines
11 KiB
Dart
367 lines
11 KiB
Dart
/*
|
|
* Copyright (c) 2025
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:integration_test/integration_test.dart';
|
|
import 'package:splash_video/splash_video.dart';
|
|
|
|
void main() {
|
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
group('SplashVideoPage Configuration Validation', () {
|
|
testWidgets('throws error when using both nextScreen and onVideoComplete',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
nextScreen: const Scaffold(),
|
|
onVideoComplete: () {},
|
|
),
|
|
),
|
|
);
|
|
|
|
// Should throw ArgumentError during build
|
|
expect(tester.takeException(), isA<ArgumentError>());
|
|
});
|
|
|
|
testWidgets('throws error when using nextScreen with looping video',
|
|
(WidgetTester tester) async {
|
|
final controller = SplashVideoController(loopVideo: true);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
controller: controller,
|
|
nextScreen: const Scaffold(),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(tester.takeException(), isA<ArgumentError>());
|
|
controller.dispose();
|
|
});
|
|
|
|
testWidgets('throws error when using onVideoComplete with looping video',
|
|
(WidgetTester tester) async {
|
|
final controller = SplashVideoController(loopVideo: true);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
controller: controller,
|
|
onVideoComplete: () {},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(tester.takeException(), isA<ArgumentError>());
|
|
controller.dispose();
|
|
});
|
|
});
|
|
|
|
group('SplashVideoPage Error Handling', () {
|
|
testWidgets('shows default error UI when video source is invalid',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/invalid/nonexistent.mp4'),
|
|
nextScreen: const Scaffold(),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Wait for error to be detected
|
|
await tester.pumpAndSettle(const Duration(seconds: 5));
|
|
|
|
// Should show error icon and message
|
|
expect(find.byIcon(Icons.error_outline), findsOneWidget);
|
|
expect(find.text('Video Load Error'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('calls onVideoError callback when provided',
|
|
(WidgetTester tester) async {
|
|
String? capturedError;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/invalid/nonexistent.mp4'),
|
|
onVideoError: (error) {
|
|
capturedError = error;
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
// Wait for error to be detected
|
|
await tester.pumpAndSettle(const Duration(seconds: 5));
|
|
|
|
expect(capturedError, isNotNull);
|
|
expect(capturedError, contains('Failed'));
|
|
});
|
|
|
|
testWidgets('handles network URL errors gracefully',
|
|
(WidgetTester tester) async {
|
|
String? error;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: NetworkFileSource('https://invalid.example.com/video.mp4'),
|
|
onVideoError: (e) => error = e,
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle(const Duration(seconds: 5));
|
|
|
|
// Should capture network error
|
|
expect(error, isNotNull);
|
|
});
|
|
});
|
|
|
|
group('SplashVideoPage Overlays', () {
|
|
testWidgets('renders title widget overlay', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
titleWidget: const Text('Welcome'),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.text('Welcome'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('renders footer widget overlay', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
footerWidget: const Text('Loading...'),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.text('Loading...'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('renders custom overlay from builder',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
overlayBuilder: (context) => const Text('Custom Overlay'),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.text('Custom Overlay'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('renders all overlays together', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
titleWidget: const Text('Title'),
|
|
footerWidget: const Text('Footer'),
|
|
overlayBuilder: (context) => const Text('Custom'),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.text('Title'), findsOneWidget);
|
|
expect(find.text('Footer'), findsOneWidget);
|
|
expect(find.text('Custom'), findsOneWidget);
|
|
});
|
|
});
|
|
|
|
group('SplashVideoController Integration', () {
|
|
testWidgets('controller can skip video', (WidgetTester tester) async {
|
|
final controller = SplashVideoController(loopVideo: true);
|
|
var completeCalled = false;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Builder(
|
|
builder: (context) => SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
controller: controller,
|
|
footerWidget: ElevatedButton(
|
|
onPressed: () {
|
|
controller.skip();
|
|
completeCalled = true;
|
|
},
|
|
child: const Text('Skip'),
|
|
),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
|
|
// Tap skip button
|
|
await tester.tap(find.text('Skip'));
|
|
await tester.pump();
|
|
|
|
expect(completeCalled, isTrue);
|
|
expect(controller.skipRequested, isTrue);
|
|
|
|
controller.dispose();
|
|
});
|
|
|
|
testWidgets('controller provides player access after initialization',
|
|
(WidgetTester tester) async {
|
|
final controller = SplashVideoController();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
controller: controller,
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// After initialization, player should be accessible
|
|
// Note: This may throw if video fails to load
|
|
try {
|
|
final player = controller.player;
|
|
expect(player, isNotNull);
|
|
} catch (e) {
|
|
// Expected if video doesn't load in test environment
|
|
expect(e, isA<StateError>());
|
|
}
|
|
|
|
controller.dispose();
|
|
});
|
|
});
|
|
|
|
group('Source Types Integration', () {
|
|
testWidgets('AssetSource works with valid asset',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
// Widget should be created without exceptions
|
|
expect(find.byType(SplashVideoPage), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('NetworkFileSource accepts valid URL',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: NetworkFileSource('https://example.com/video.mp4'),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.byType(SplashVideoPage), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('DeviceFileSource accepts file path',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: DeviceFileSource('/path/to/video.mp4'),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.byType(SplashVideoPage), findsOneWidget);
|
|
});
|
|
});
|
|
|
|
group('VideoConfig Integration', () {
|
|
testWidgets('respects useSafeArea configuration',
|
|
(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
videoConfig: const VideoConfig(useSafeArea: true),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.byType(SplashVideoPage), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('supports different visibility modes',
|
|
(WidgetTester tester) async {
|
|
for (final mode in VisibilityEnum.values) {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SplashVideoPage(
|
|
source: AssetSource('assets/videos/splash.mp4'),
|
|
videoConfig: VideoConfig(videoVisibilityEnum: mode),
|
|
onVideoError: (_) {},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.byType(SplashVideoPage), findsOneWidget);
|
|
|
|
// Clean up for next iteration
|
|
await tester.pumpWidget(Container());
|
|
}
|
|
});
|
|
});
|
|
}
|