203 lines
5.9 KiB
Dart
203 lines
5.9 KiB
Dart
// Author: fruit.com.cn
|
|
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:fruitshorts/core/data/services/sprite_thumbnail_asset_loader.dart';
|
|
import 'package:fruitshorts/core/data/services/sprite_thumbnail_axis.dart';
|
|
import 'package:fruitshorts/pages/player/widgets/player_opening_frame.dart';
|
|
import 'package:fruitshorts/pages/player/widgets/scrub_preview_card.dart';
|
|
|
|
const String _spriteVtt = '''
|
|
WEBVTT
|
|
|
|
00:00:00.000 --> 00:00:01.000
|
|
sprite_001.jpg#xywh=0,0,160,90
|
|
''';
|
|
|
|
void main() {
|
|
test('should_choose_poster_before_thumbnails_when_both_are_available', () {
|
|
final service = SpriteThumbnailAxisService(loader: _FakeSpriteLoader());
|
|
addTearDown(service.dispose);
|
|
|
|
final frame =
|
|
playerOpeningFrame(
|
|
'https://cdn.test/thumbnails.vtt',
|
|
posterUrl: 'https://cdn.test/poster.jpg',
|
|
spriteSource: service,
|
|
fit: BoxFit.cover,
|
|
)
|
|
as PlayerOpeningFrame;
|
|
|
|
expect(frame.posterUrl, 'https://cdn.test/poster.jpg');
|
|
expect(frame.thumbnailsUrl, 'https://cdn.test/thumbnails.vtt');
|
|
expect(frame.fit, BoxFit.cover);
|
|
expect(
|
|
playerOpeningFallback(
|
|
posterUrl: frame.posterUrl,
|
|
thumbnailsUrl: frame.thumbnailsUrl,
|
|
),
|
|
PlayerOpeningFallback.poster,
|
|
);
|
|
});
|
|
|
|
testWidgets('should_use_poster_without_decoding_sprite_when_poster_exists', (
|
|
tester,
|
|
) async {
|
|
final loader = _FakeSpriteLoader();
|
|
final service = SpriteThumbnailAxisService(loader: loader);
|
|
addTearDown(service.dispose);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: PlayerOpeningFrame(
|
|
posterUrl: 'https://cdn.test/poster.jpg',
|
|
thumbnailsUrl: 'https://cdn.test/thumbnails.vtt',
|
|
spriteSource: service,
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byKey(const Key('player-opening-poster')), findsOneWidget);
|
|
expect(find.byKey(const Key('player-opening-sprite')), findsNothing);
|
|
expect(loader.vttCalls, 0);
|
|
});
|
|
|
|
testWidgets('should_decode_fullscreen_poster_at_screen_width', (
|
|
tester,
|
|
) async {
|
|
final service = SpriteThumbnailAxisService(loader: _FakeSpriteLoader());
|
|
addTearDown(service.dispose);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: MediaQuery(
|
|
data: const MediaQueryData(devicePixelRatio: 3),
|
|
child: Center(
|
|
child: SizedBox(
|
|
width: 390,
|
|
height: 844,
|
|
child: PlayerOpeningFrame(
|
|
posterUrl: 'https://cdn.test/poster.jpg',
|
|
thumbnailsUrl: '',
|
|
spriteSource: service,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final image = tester.widget<Image>(
|
|
find.byKey(const Key('player-opening-poster')),
|
|
);
|
|
final provider = image.image as ResizeImage;
|
|
expect(provider.width, 1170);
|
|
});
|
|
|
|
testWidgets('should_return_no_opening_frame_when_thumbnails_is_empty', (
|
|
tester,
|
|
) async {
|
|
final service = SpriteThumbnailAxisService(loader: _FakeSpriteLoader());
|
|
addTearDown(service.dispose);
|
|
|
|
expect(playerOpeningFrame(null, spriteSource: service), isNull);
|
|
expect(playerOpeningFrame(' ', spriteSource: service), isNull);
|
|
});
|
|
|
|
testWidgets('should_render_prewarmed_zero_frame_from_shared_cache', (
|
|
tester,
|
|
) async {
|
|
final loader = _FakeSpriteLoader();
|
|
final service = SpriteThumbnailAxisService(loader: loader);
|
|
addTearDown(service.dispose);
|
|
const url = 'https://cdn.test/thumbnails.vtt';
|
|
await service.prepareAt(url, Duration.zero);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: PlayerOpeningFrame(thumbnailsUrl: url, spriteSource: service),
|
|
),
|
|
);
|
|
await tester.pump();
|
|
|
|
expect(find.byKey(const Key('player-opening-sprite')), findsOneWidget);
|
|
expect(loader.vttCalls, 1);
|
|
expect(loader.spriteCalls, ['sprite_001.jpg']);
|
|
});
|
|
|
|
testWidgets('should_load_sprite_when_not_prewarmed', (tester) async {
|
|
final service = SpriteThumbnailAxisService(loader: _FakeSpriteLoader());
|
|
addTearDown(service.dispose);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: PlayerOpeningFrame(
|
|
thumbnailsUrl: 'https://cdn.test/thumbnails.vtt',
|
|
spriteSource: service,
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byKey(const Key('player-opening-sprite')), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('should_use_same_fit_for_sprite_and_video', (tester) async {
|
|
final service = SpriteThumbnailAxisService(loader: _FakeSpriteLoader());
|
|
addTearDown(service.dispose);
|
|
const url = 'https://cdn.test/thumbnails.vtt';
|
|
await service.prepareAt(url, Duration.zero);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: PlayerOpeningFrame(
|
|
thumbnailsUrl: url,
|
|
spriteSource: service,
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
);
|
|
|
|
final preview = tester.widget<SpriteCropPreview>(
|
|
find.byKey(const Key('player-opening-sprite')),
|
|
);
|
|
expect(preview.fit, BoxFit.contain);
|
|
});
|
|
}
|
|
|
|
class _FakeSpriteLoader implements SpriteThumbnailAssetLoader {
|
|
int vttCalls = 0;
|
|
final List<String> spriteCalls = [];
|
|
|
|
@override
|
|
Future<String?> loadVtt(Uri uri) async {
|
|
vttCalls++;
|
|
return _spriteVtt;
|
|
}
|
|
|
|
@override
|
|
Future<SpriteThumbnailDecodedImage?> loadSprite(Uri uri) async {
|
|
spriteCalls.add(uri.pathSegments.last);
|
|
return SpriteThumbnailDecodedImage(
|
|
image: await _createImage(320, 180),
|
|
originalWidth: 320,
|
|
originalHeight: 180,
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<ui.Image> _createImage(int width, int height) async {
|
|
final recorder = ui.PictureRecorder();
|
|
final canvas = Canvas(recorder);
|
|
canvas.drawRect(
|
|
Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble()),
|
|
Paint()..color = Colors.blue,
|
|
);
|
|
final picture = recorder.endRecording();
|
|
final image = await picture.toImage(width, height);
|
|
picture.dispose();
|
|
return image;
|
|
}
|