refactor(ui): 清理 K26 旧 UI 与死代码
This commit is contained in:
1 parent
a5398daab3
commit
5422581f8b
23 files changed
+291
-1225
No files matched your search
@@ -1,193 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/l10n/app_localizations.dart';
|
||||
import 'package:fruitshorts/pages/discover/widgets/cover_carousel.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
List<CoverItem> items(int count) => [
|
||||
for (var index = 0; index < count; index++)
|
||||
CoverItem(
|
||||
title: 'Banner $index',
|
||||
subtitle: 'Subtitle $index',
|
||||
image: '/files/banner-$index.png',
|
||||
onOpen: _noop,
|
||||
),
|
||||
];
|
||||
|
||||
testWidgets('should_turn_cube_and_keep_title_on_poster_when_carousel_pages', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
_localizedApp(
|
||||
CoverCarousel(
|
||||
items: [
|
||||
for (final title in ['The Signal', 'City Lights', 'Vengeance'])
|
||||
CoverItem(
|
||||
title: title,
|
||||
subtitle: 'Mock',
|
||||
image: '/files/poster.png',
|
||||
onOpen: _noop,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('The Signal'), findsOneWidget);
|
||||
expect(find.text('Mock'), findsOneWidget);
|
||||
|
||||
final box = tester.getRect(find.byType(CoverCarousel));
|
||||
final gesture = await tester.startGesture(
|
||||
Offset(box.right - 40, box.center.dy),
|
||||
);
|
||||
await gesture.moveBy(const Offset(-500, 0));
|
||||
await tester.pump();
|
||||
|
||||
final turned = tester
|
||||
.widgetList<Transform>(find.byType(Transform))
|
||||
.any((transform) => (transform.transform.entry(0, 0) - 1).abs() > 0.15);
|
||||
expect(turned, isTrue);
|
||||
|
||||
await gesture.moveBy(const Offset(-200, 0));
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 450));
|
||||
|
||||
expect(find.text('City Lights'), findsOneWidget);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('should_auto_advance_and_show_dots_when_multiple_items_exist', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(_localizedApp(CoverCarousel(items: items(3))));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Banner 0'), findsOneWidget);
|
||||
expect(find.byKey(const Key('carousel-dot-0')), findsOneWidget);
|
||||
expect(find.byKey(const Key('carousel-dot-1')), findsOneWidget);
|
||||
expect(find.byKey(const Key('carousel-dot-2')), findsOneWidget);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 4500));
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
expect(find.text('Banner 1'), findsOneWidget);
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
});
|
||||
|
||||
testWidgets('should_not_schedule_auto_advance_when_only_one_item_exists', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(_localizedApp(CoverCarousel(items: items(1))));
|
||||
await tester.pump(const Duration(seconds: 12));
|
||||
|
||||
expect(find.text('Banner 0'), findsOneWidget);
|
||||
expect(find.byKey(const Key('carousel-dot-0')), findsOneWidget);
|
||||
expect(find.byKey(const Key('carousel-dot-1')), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_pause_auto_advance_while_pointer_is_down', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(_localizedApp(CoverCarousel(items: items(3))));
|
||||
await tester.pump();
|
||||
final box = tester.getRect(find.byType(CoverCarousel));
|
||||
final gesture = await tester.startGesture(box.center);
|
||||
await tester.pump(const Duration(seconds: 6));
|
||||
|
||||
expect(find.text('Banner 0'), findsOneWidget);
|
||||
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 4500));
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
expect(find.text('Banner 1'), findsOneWidget);
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
});
|
||||
|
||||
testWidgets('should_pause_when_inactive_and_resume_when_active_again', (
|
||||
tester,
|
||||
) async {
|
||||
final active = ValueNotifier(false);
|
||||
await tester.pumpWidget(
|
||||
_localizedApp(
|
||||
ValueListenableBuilder<bool>(
|
||||
valueListenable: active,
|
||||
builder: (context, enabled, _) =>
|
||||
CoverCarousel(items: items(2), active: enabled),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump(const Duration(seconds: 6));
|
||||
expect(find.text('Banner 0'), findsOneWidget);
|
||||
|
||||
active.value = true;
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 4500));
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
expect(find.text('Banner 1'), findsOneWidget);
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
});
|
||||
|
||||
testWidgets('should_show_icon_fallback_when_banner_image_is_empty', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
_localizedApp(
|
||||
CoverCarousel(
|
||||
items: const [
|
||||
CoverItem(
|
||||
title: 'Broken image',
|
||||
subtitle: '',
|
||||
image: '',
|
||||
onOpen: _noop,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.byKey(const Key('carousel-image-fallback')), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'should_keep_existing_open_callback_when_active_poster_is_tapped',
|
||||
(tester) async {
|
||||
var opened = 0;
|
||||
await tester.pumpWidget(
|
||||
_localizedApp(
|
||||
CoverCarousel(
|
||||
items: [
|
||||
CoverItem(
|
||||
title: 'Tap me',
|
||||
subtitle: '',
|
||||
image: '/files/banner.png',
|
||||
onOpen: () => opened++,
|
||||
),
|
||||
...items(1),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.text('Tap me'));
|
||||
expect(opened, 1);
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _noop() {}
|
||||
|
||||
Widget _localizedApp(Widget child) => MaterialApp(
|
||||
locale: const Locale('en'),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: Scaffold(body: child),
|
||||
);
|
||||
@@ -38,7 +38,6 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Continue Watching'), findsNothing);
|
||||
expect(find.byKey(const Key('home-all-dramas-title')), findsNothing);
|
||||
expect(find.text('Recommended'), findsOneWidget);
|
||||
expect(find.text('For You'), findsNothing);
|
||||
final grid = tester.widget<SliverGrid>(
|
||||
@@ -133,7 +132,6 @@ void main() {
|
||||
await tester.tap(find.byKey(const Key('tab-discover')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(const Key('home-all-dramas-title')), findsNothing);
|
||||
expect(find.text('No matching dramas.'), findsOneWidget);
|
||||
expect(find.byKey(const Key('empty-state-content')), findsOneWidget);
|
||||
|
||||
@@ -153,7 +151,6 @@ void main() {
|
||||
await tester.tap(find.byKey(const Key('tab-discover')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(const Key('home-all-dramas-title')), findsNothing);
|
||||
expect(find.text('Categories could not be loaded.'), findsOneWidget);
|
||||
expect(find.text('Retry'), findsOneWidget);
|
||||
expect(find.byKey(const Key('empty-state-icon')), findsOneWidget);
|
||||
@@ -179,7 +176,6 @@ void main() {
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(const Key('home-all-dramas-title')), findsNothing);
|
||||
expect(find.text('City Lights & Velvet Nights'), findsWidgets);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/app.dart';
|
||||
import 'package:fruitshorts/core/data/models/drama.dart';
|
||||
import 'package:fruitshorts/core/data/models/home_feed.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/pages/discover/widgets/cover_carousel.dart';
|
||||
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
|
||||
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
|
||||
|
||||
import '../support/fake_catalog.dart';
|
||||
import '../support/fake_category.dart';
|
||||
import '../support/fake_interaction_repositories.dart';
|
||||
import '../support/fake_notice_repository.dart';
|
||||
import '../support/widget_player.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'should_not_mount_carousel_on_discover_even_when_home_banners_exist',
|
||||
(tester) async {
|
||||
SharedPreferencesAsyncPlatform.instance =
|
||||
InMemorySharedPreferencesAsync.empty();
|
||||
final catalog = _HomeBannerCatalogRepository();
|
||||
await tester.pumpWidget(
|
||||
LumaDramaApp(
|
||||
playerFactory: createWidgetPlayer,
|
||||
catalogRepository: catalog,
|
||||
categoryRepository: FakeCategoryRepository(catalog: catalog),
|
||||
watchHistoryRepository: InMemoryWatchHistoryRepository(),
|
||||
favoriteRepository: InMemoryFavoriteRepository(),
|
||||
playbackRepository: FakePlaybackRepository(),
|
||||
commentRepository: FakeCommentRepository(),
|
||||
interactionRepository: FakeInteractionRepository(),
|
||||
noticeRepository: FakeNoticeRepository(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('tab-discover')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(CoverCarousel), findsNothing);
|
||||
expect(find.byKey(const Key('discover-search')), findsOneWidget);
|
||||
expect(find.byKey(const Key('home-all-dramas-grid')), findsOneWidget);
|
||||
expect(find.byKey(const Key('discover-quick-filter')), findsOneWidget);
|
||||
expect(find.text('The Signal'), findsWidgets);
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class _HomeBannerCatalogRepository implements CatalogRepository {
|
||||
final _delegate = FakeCatalogRepository();
|
||||
|
||||
@override
|
||||
Future<HomeFeed> fetchHomeFeed() async => HomeFeed(
|
||||
banners: const [
|
||||
FeedBanner(
|
||||
id: 'banner-1',
|
||||
imageUrl: '/files/banner-1.png',
|
||||
linkType: 'DRAMA',
|
||||
linkValue: 'the-signal-demo',
|
||||
),
|
||||
FeedBanner(
|
||||
id: 'banner-2',
|
||||
imageUrl: '/files/banner-2.png',
|
||||
linkType: 'DRAMA',
|
||||
linkValue: 'city-lights-demo',
|
||||
),
|
||||
],
|
||||
sections: [FeedSection(code: 'hot', title: 'Hot', dramas: demoDramas)],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<HomeFeed> fetchDiscoverFeed() async => HomeFeed(
|
||||
sections: [FeedSection(code: 'hot', title: 'Hot', dramas: demoDramas)],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<Drama> fetchDrama(String dramaId) => _delegate.fetchDrama(dramaId);
|
||||
}
|
||||
@@ -109,16 +109,15 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
test('should_return_home_and_open_drawer_when_system_back_is_pressed', () {
|
||||
test('should_return_to_discover_and_handle_immersive_back', () {
|
||||
final logic = HomeLogic(dramas);
|
||||
|
||||
// Home is the launch tab; the first back press lands on Discover.
|
||||
logic.onSystemBack();
|
||||
expect(logic.tab, 1);
|
||||
logic.onSystemBack();
|
||||
expect(logic.drawerOpen, isTrue);
|
||||
expect(logic.tab, 1);
|
||||
expect(logic.backRequest, 0);
|
||||
logic.onDrawerChanged(false);
|
||||
|
||||
logic.select(0);
|
||||
logic.onSystemBack();
|
||||
@@ -131,30 +130,25 @@ void main() {
|
||||
expect(logic.backRequest, 1);
|
||||
expect(logic.tab, 0);
|
||||
|
||||
logic.onDrawerChanged(true);
|
||||
logic.onSystemBack();
|
||||
expect(logic.backRequest, 1);
|
||||
|
||||
logic.onDrawerChanged(false);
|
||||
logic.onImmersiveChanged(false);
|
||||
logic.onSystemBack();
|
||||
expect(logic.tab, 1);
|
||||
logic.dispose();
|
||||
});
|
||||
|
||||
test('should_notify_only_on_actual_change_when_drawer_state_is_set', () {
|
||||
test('should_notify_only_on_actual_change_when_menu_state_is_set', () {
|
||||
final logic = HomeLogic(dramas);
|
||||
var notifications = 0;
|
||||
logic.addListener(() => notifications++);
|
||||
|
||||
logic.onDrawerChanged(false);
|
||||
logic.setMenuOpen(false);
|
||||
expect(notifications, 0);
|
||||
|
||||
logic.onDrawerChanged(true);
|
||||
logic.setMenuOpen(true);
|
||||
expect(logic.drawerOpen, isTrue);
|
||||
expect(notifications, 1);
|
||||
|
||||
logic.onDrawerChanged(true);
|
||||
logic.setMenuOpen(true);
|
||||
expect(notifications, 1);
|
||||
logic.dispose();
|
||||
});
|
||||
|
||||
@@ -95,24 +95,9 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('should_hide_themed_navigation_when_immersive_mode_is_enabled', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
await tester.tap(find.byKey(const Key('home-feed-more')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('home-feed-clear')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
for (final key in _keys) {
|
||||
expect(find.byKey(key), findsNothing);
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('should_map_three_tabs_to_home_discover_and_me', (tester) async {
|
||||
await _pumpApp(tester);
|
||||
|
||||
expect(find.byKey(const Key('tab-my-list')), findsNothing);
|
||||
expect(find.byKey(const Key('tab-home')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-discover')), findsOneWidget);
|
||||
expect(find.byKey(const Key('tab-my')), findsOneWidget);
|
||||
|
||||
@@ -81,9 +81,6 @@ void _expectFullscreenOverlay() {
|
||||
|
||||
expect(find.byKey(const Key('rail-like')), findsNothing);
|
||||
expect(find.byKey(const Key('rail-save')), findsNothing);
|
||||
expect(find.byKey(const Key('card-player-video-frame')), findsNothing);
|
||||
expect(find.byKey(const Key('card-player-progress')), findsNothing);
|
||||
expect(find.byKey(const Key('card-player-next')), findsNothing);
|
||||
}
|
||||
|
||||
Future<void> _pumpApp(WidgetTester tester) async {
|
||||
|
||||
@@ -117,33 +117,7 @@ void main() {
|
||||
});
|
||||
|
||||
group('surface flags', () {
|
||||
test('should_hide_chrome_until_restored_when_screen_is_cleared', () {
|
||||
final logic = _logic(FakeWatchHistory());
|
||||
final immersive = <bool>[];
|
||||
logic.onImmersiveChanged = immersive.add;
|
||||
var notifications = 0;
|
||||
logic.addListener(() => notifications++);
|
||||
|
||||
expect(logic.chromeHidden, isFalse);
|
||||
expect(logic.immersive, isFalse);
|
||||
|
||||
logic.setScreenCleared(true);
|
||||
expect(logic.chromeHidden, isTrue);
|
||||
expect(notifications, 1);
|
||||
expect(immersive, [true]);
|
||||
|
||||
// Repeating the same value is not a state change.
|
||||
logic.setScreenCleared(true);
|
||||
expect(notifications, 1);
|
||||
expect(immersive, [true]);
|
||||
|
||||
logic.setScreenCleared(false);
|
||||
expect(logic.chromeHidden, isFalse);
|
||||
expect(notifications, 2);
|
||||
expect(immersive, [true, false]);
|
||||
});
|
||||
|
||||
test('should_outrank_cleared_screen_flag_when_picture_in_picture_callback_fires', () async {
|
||||
test('should_set_immersive_when_picture_in_picture_callback_fires', () async {
|
||||
final factory = _RecordingFactory();
|
||||
final drama = _drama('signal');
|
||||
final logic = _logic(FakeWatchHistory());
|
||||
|
||||
+1
-33
@@ -122,38 +122,12 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const Key('home-feed-download')), findsOneWidget);
|
||||
expect(find.byKey(const Key('home-feed-pip')), findsOneWidget);
|
||||
expect(find.byKey(const Key('home-feed-clear')), findsOneWidget);
|
||||
await tester.binding.handlePopRoute();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Watch Full'), findsNothing);
|
||||
expect(find.byTooltip('Explore series'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_hide_tabs_and_restore_them_when_clear_screen_is_tapped', (
|
||||
tester,
|
||||
) async {
|
||||
tester.view.physicalSize = const Size(390, 844);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
|
||||
await tester.pumpWidget(appUnderTest());
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('tab-home')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('home-feed-more')));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const Key('home-feed-clear')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const Key('home-tabs-bar')), findsNothing);
|
||||
expect(find.byTooltip('Exit clear screen'), findsOneWidget);
|
||||
await tester.tap(find.byTooltip('Exit clear screen'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const Key('home-tabs-bar')), findsOneWidget);
|
||||
expect(find.byKey(const Key('home-feed-more')), findsOneWidget);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'should_open_current_series_and_switch_root_tabs_when_quick_drawer_is_used',
|
||||
(tester) async {
|
||||
@@ -177,13 +151,7 @@ void main() {
|
||||
);
|
||||
expect(find.text('Quick access'), findsOneWidget);
|
||||
expect(find.text('Bahasa Indonesia'), findsOneWidget);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(Drawer),
|
||||
matching: find.text('7 EPISODES'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('7 EPISODES'), findsOneWidget);
|
||||
expect(tester.takeException(), isNull);
|
||||
|
||||
await tester.tap(find.byKey(const Key('drawer-open-series')));
|
||||
|
||||
Reference in new issue
Block a user