178 lines
5.6 KiB
Dart
178 lines
5.6 KiB
Dart
// Author: fruit.com.cn
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:fruitshorts/core/data/models/drama.dart';
|
|
import 'package:fruitshorts/core/data/remote/api_client.dart';
|
|
import 'package:fruitshorts/core/data/repositories/catalog_repository.dart';
|
|
import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
|
|
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
|
|
import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart';
|
|
import 'package:fruitshorts/core/data/services/catalog_controller.dart';
|
|
import 'package:fruitshorts/core/data/services/favorite_controller.dart';
|
|
import 'package:fruitshorts/core/data/services/playback_position_cache.dart';
|
|
import 'package:fruitshorts/l10n/app_localizations.dart';
|
|
import 'package:fruitshorts/pages/drama_detail/drama_detail_view.dart';
|
|
|
|
import '../support/fake_catalog.dart';
|
|
|
|
void main() {
|
|
testWidgets('should_show_card_content_before_detail_returns', (tester) async {
|
|
final pending = Completer<Drama>();
|
|
final card = _cardDrama();
|
|
final catalog = _ScriptedCatalog(
|
|
dramas: [card],
|
|
loadDetail: () => pending.future,
|
|
);
|
|
|
|
await _pumpDetail(tester, card: card, catalog: catalog, settle: false);
|
|
|
|
expect(find.byKey(const Key('detail-title')), findsOneWidget);
|
|
expect(find.byKey(const Key('detail-poster')), findsOneWidget);
|
|
expect(find.byKey(const Key('detail-stats')), findsOneWidget);
|
|
expect(find.byKey(const Key('detail-episodes-loading')), findsOneWidget);
|
|
expect(find.byType(CircularProgressIndicator), findsNothing);
|
|
expect(
|
|
tester
|
|
.widget<FilledButton>(find.byKey(const Key('detail-cta')))
|
|
.onPressed,
|
|
isNull,
|
|
);
|
|
|
|
pending.complete(_detailDrama(card));
|
|
await tester.pump();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byKey(const Key('detail-episodes-loading')), findsNothing);
|
|
expect(find.byKey(const Key('detail-episode-chips')), findsOneWidget);
|
|
expect(
|
|
tester
|
|
.widget<FilledButton>(find.byKey(const Key('detail-cta')))
|
|
.onPressed,
|
|
isNotNull,
|
|
);
|
|
});
|
|
|
|
testWidgets('should_keep_card_content_when_detail_load_fails', (
|
|
tester,
|
|
) async {
|
|
final card = _cardDrama();
|
|
final catalog = _ScriptedCatalog(
|
|
dramas: [card],
|
|
loadDetail: () async => throw const ApiException(500, 'detail failed'),
|
|
);
|
|
|
|
await _pumpDetail(tester, card: card, catalog: catalog);
|
|
|
|
expect(find.byKey(const Key('detail-title')), findsOneWidget);
|
|
expect(find.byKey(const Key('detail-poster')), findsOneWidget);
|
|
expect(find.byKey(const Key('detail-stats')), findsOneWidget);
|
|
expect(find.byKey(const Key('detail-episodes-retry')), findsOneWidget);
|
|
expect(find.byType(CircularProgressIndicator), findsNothing);
|
|
expect(
|
|
tester
|
|
.widget<FilledButton>(find.byKey(const Key('detail-cta')))
|
|
.onPressed,
|
|
isNull,
|
|
);
|
|
});
|
|
}
|
|
|
|
Future<void> _pumpDetail(
|
|
WidgetTester tester, {
|
|
required Drama card,
|
|
required CatalogRepository catalog,
|
|
bool settle = true,
|
|
}) async {
|
|
tester.view.physicalSize = const Size(390, 844);
|
|
tester.view.devicePixelRatio = 1;
|
|
addTearDown(tester.view.resetPhysicalSize);
|
|
addTearDown(tester.view.resetDevicePixelRatio);
|
|
final favorites = FavoriteController(
|
|
InMemoryFavoriteRepository(cards: [card]),
|
|
);
|
|
await favorites.load();
|
|
await tester.pumpWidget(
|
|
MultiProvider(
|
|
providers: [
|
|
Provider<WatchHistoryRepository>.value(
|
|
value: InMemoryWatchHistoryRepository(),
|
|
),
|
|
Provider<PlaybackRepository>.value(value: FakePlaybackRepository()),
|
|
Provider<PlaybackPositionCache>(create: (_) => PlaybackPositionCache()),
|
|
ChangeNotifierProvider<FavoriteController>.value(value: favorites),
|
|
ChangeNotifierProvider(
|
|
create: (_) => CatalogController(catalog)..load(),
|
|
),
|
|
],
|
|
child: MaterialApp(
|
|
locale: const Locale('en'),
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
home: DramaDetailPage(drama: card),
|
|
),
|
|
),
|
|
);
|
|
if (settle) {
|
|
await tester.pumpAndSettle();
|
|
} else {
|
|
await tester.pump();
|
|
}
|
|
}
|
|
|
|
Drama _cardDrama() => const Drama(
|
|
id: 'detail-k53',
|
|
title: 'Detail K53',
|
|
synopsis: 'A card synopsis is available before the detail request finishes.',
|
|
posterUrl: '/files/detail-k53.png',
|
|
category: 'Romance',
|
|
genre: 'Urban',
|
|
language: 'id',
|
|
totalEpisodes: 8,
|
|
viewCount: 120000,
|
|
favoriteCount: 3200,
|
|
likeCount: 5600,
|
|
);
|
|
|
|
Drama _detailDrama(Drama card) => Drama(
|
|
id: card.id,
|
|
title: card.title,
|
|
synopsis: card.synopsis,
|
|
posterUrl: card.posterUrl,
|
|
category: card.category,
|
|
genre: card.genre,
|
|
language: card.language,
|
|
totalEpisodes: card.totalEpisodes,
|
|
viewCount: card.viewCount,
|
|
favoriteCount: card.favoriteCount,
|
|
likeCount: card.likeCount,
|
|
detailLoaded: true,
|
|
episodes: [
|
|
for (var number = 1; number <= card.totalEpisodes; number++)
|
|
Episode(
|
|
id: '${card.id}-$number',
|
|
number: number,
|
|
title: 'Episode $number',
|
|
isFree: number == 1,
|
|
videoUrl: number == 1 ? '/files/${card.id}-1.mp4' : '',
|
|
locked: number > 1,
|
|
likeCount: card.likeCount,
|
|
commentCount: card.commentCount,
|
|
),
|
|
],
|
|
);
|
|
|
|
class _ScriptedCatalog extends FakeCatalogRepository {
|
|
_ScriptedCatalog({required List<Drama> dramas, required this.loadDetail})
|
|
: super(dramas: dramas);
|
|
|
|
final Future<Drama> Function() loadDetail;
|
|
|
|
@override
|
|
Future<Drama> fetchDrama(String dramaId) => loadDetail();
|
|
}
|