feat(ui): 更新品牌启动页与选集交互
This commit is contained in:
1 parent
4778ce2c75
commit
c1cb7d1c11
87 files changed
+919
-552
No files matched your search
@@ -11,7 +11,9 @@ void main() {
|
||||
final repository = ScriptedCatalogRepository();
|
||||
final controller = CatalogController(repository);
|
||||
|
||||
expect(controller.settled, isFalse);
|
||||
await controller.load();
|
||||
expect(controller.settled, isTrue);
|
||||
expect(controller.dramas, isEmpty);
|
||||
expect(controller.error, isNull);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:fruitshorts/core/data/repositories/interaction_repository.dart';
|
||||
import 'package:fruitshorts/core/data/services/auth_controller.dart';
|
||||
import 'package:fruitshorts/core/data/services/auth_session_store.dart';
|
||||
import 'package:fruitshorts/core/platform/haptics.dart';
|
||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||
import 'package:fruitshorts/core/widgets/empty_state.dart';
|
||||
import 'package:fruitshorts/l10n/app_localizations.dart';
|
||||
import 'package:fruitshorts/pages/player/widgets/drama_comments_sheet.dart';
|
||||
@@ -352,7 +353,7 @@ Future<void> _pumpSheet(
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: const Scaffold(
|
||||
backgroundColor: Color(0xFF232328),
|
||||
backgroundColor: AppColors.ink,
|
||||
body: DramaCommentsSheet(drama: _drama),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -148,6 +148,15 @@ void main() {
|
||||
|
||||
expect(find.byKey(const Key('home-all-dramas-title')), findsNothing);
|
||||
expect(find.text('No matching dramas.'), findsOneWidget);
|
||||
final content = tester.getCenter(
|
||||
find.byKey(const Key('empty-state-content')),
|
||||
);
|
||||
final chipsBottom = tester
|
||||
.getBottomLeft(find.byKey(const Key('home-category-chips')))
|
||||
.dy;
|
||||
final feed = tester.getRect(find.byType(CustomScrollView));
|
||||
expect(content.dx, closeTo(feed.center.dx, 1));
|
||||
expect(content.dy, closeTo((chipsBottom + feed.bottom) / 2, 36));
|
||||
|
||||
await tester.tap(find.text('Romance'));
|
||||
await tester.pumpAndSettle();
|
||||
@@ -168,6 +177,10 @@ void main() {
|
||||
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);
|
||||
final icon = tester.getCenter(find.byKey(const Key('empty-state-icon')));
|
||||
final feed = tester.getRect(find.byType(CustomScrollView));
|
||||
expect(icon.dx, closeTo(feed.center.dx, 1));
|
||||
expect(find.byKey(const Key('home-all-dramas-grid')), findsNothing);
|
||||
});
|
||||
|
||||
|
||||
@@ -22,34 +22,19 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(const Key('empty-state-art')), findsOneWidget);
|
||||
expect(find.byKey(const Key('empty-state-art-inner')), findsOneWidget);
|
||||
expect(find.byKey(const Key('empty-state-art')), findsNothing);
|
||||
expect(find.byKey(const Key('empty-state-art-inner')), findsNothing);
|
||||
expect(find.byKey(const Key('empty-state-icon')), findsOneWidget);
|
||||
expect(find.text('No stories'), findsOneWidget);
|
||||
expect(find.text('New content will appear here.'), findsOneWidget);
|
||||
expect(
|
||||
tester.getSize(find.byKey(const Key('empty-state-art'))),
|
||||
const Size(84, 84),
|
||||
);
|
||||
expect(
|
||||
tester.getSize(find.byKey(const Key('empty-state-art-inner'))),
|
||||
const Size(56, 56),
|
||||
);
|
||||
expect(
|
||||
tester.getSize(find.byKey(const Key('empty-state-icon'))),
|
||||
const Size(27, 27),
|
||||
const Size(48, 48),
|
||||
);
|
||||
final outer = tester.widget<Container>(
|
||||
find.byKey(const Key('empty-state-art')),
|
||||
expect(
|
||||
tester.widget<Icon>(find.byKey(const Key('empty-state-icon'))).color,
|
||||
AppColors.gold,
|
||||
);
|
||||
final outerDecoration = outer.decoration! as BoxDecoration;
|
||||
expect(outerDecoration.shape, BoxShape.circle);
|
||||
expect(outerDecoration.color?.a, closeTo(0.055, 0.001));
|
||||
expect(outerDecoration.border!.top.color.a, closeTo(0.11, 0.001));
|
||||
final inner = tester.widget<Container>(
|
||||
find.byKey(const Key('empty-state-art-inner')),
|
||||
);
|
||||
expect((inner.decoration! as BoxDecoration).color?.a, closeTo(0.11, 0.001));
|
||||
expect(
|
||||
tester
|
||||
.widget<Padding>(find.byKey(const Key('empty-state-padding')))
|
||||
@@ -97,18 +82,11 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
tester.getSize(find.byKey(const Key('empty-state-art'))),
|
||||
const Size(60, 60),
|
||||
);
|
||||
expect(
|
||||
tester.getSize(find.byKey(const Key('empty-state-art-inner'))),
|
||||
const Size(40, 40),
|
||||
);
|
||||
expect(
|
||||
tester.getSize(find.byKey(const Key('empty-state-icon'))),
|
||||
const Size(21, 21),
|
||||
const Size(32, 32),
|
||||
);
|
||||
expect(find.byType(DecoratedBox), findsNothing);
|
||||
expect(
|
||||
tester
|
||||
.widget<Text>(find.byKey(const Key('empty-state-title')))
|
||||
@@ -125,7 +103,7 @@ void main() {
|
||||
expect(find.byKey(const Key('empty-state-action')), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_use_theme_aware_icon_colors', (tester) async {
|
||||
testWidgets('should_tint_the_bare_icon_for_light_and_dark', (tester) async {
|
||||
await _pump(
|
||||
tester,
|
||||
theme: AppTheme.dark(),
|
||||
@@ -133,7 +111,7 @@ void main() {
|
||||
);
|
||||
expect(
|
||||
tester.widget<Icon>(find.byKey(const Key('empty-state-icon'))).color,
|
||||
AppColors.blueSoft,
|
||||
AppColors.gold,
|
||||
);
|
||||
|
||||
await _pump(
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/core/data/models/drama.dart';
|
||||
import 'package:fruitshorts/pages/player/widgets/episode_picker_cell.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('should_show_lock_when_episode_needs_unlock', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
_host(
|
||||
const Episode(
|
||||
id: '12',
|
||||
number: 12,
|
||||
title: 'Paid',
|
||||
isFree: false,
|
||||
locked: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byIcon(Icons.lock_outline), findsOneWidget);
|
||||
expect(find.text('12'), findsOneWidget);
|
||||
final cell = tester.getRect(find.byType(EpisodePickerCell));
|
||||
final cover = tester.getRect(find.byKey(const ValueKey('episode-lock-cover-12')));
|
||||
final number = tester.getRect(find.text('12'));
|
||||
final lock = tester.getRect(find.byIcon(Icons.lock_outline));
|
||||
expect(number.overlaps(lock), isFalse);
|
||||
expect(number.top, greaterThanOrEqualTo(lock.bottom));
|
||||
final wash = tester.widget<ColoredBox>(
|
||||
find.byKey(const ValueKey('episode-lock-cover-12')),
|
||||
);
|
||||
expect(cover.width, greaterThanOrEqualTo(cell.width - 1));
|
||||
expect(cover.height, greaterThanOrEqualTo(cell.height - 1));
|
||||
expect(cover.contains(lock.center), isTrue);
|
||||
expect(wash.color.a, greaterThan(0));
|
||||
expect(wash.color.a, lessThan(1));
|
||||
});
|
||||
|
||||
testWidgets('should_hide_lock_when_episode_is_free', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
_host(
|
||||
const Episode(
|
||||
id: '1',
|
||||
number: 1,
|
||||
title: 'Free',
|
||||
isFree: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byIcon(Icons.lock_outline), findsNothing);
|
||||
expect(find.byKey(const ValueKey('episode-lock-cover-1')), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _host(Episode episode) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: SizedBox(
|
||||
width: 52,
|
||||
height: 50,
|
||||
child: EpisodePickerCell(
|
||||
episode: episode,
|
||||
selected: false,
|
||||
lockLabel: 'Locked',
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.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/services/catalog_controller.dart';
|
||||
import 'package:fruitshorts/l10n/app_localizations.dart';
|
||||
import 'package:fruitshorts/pages/home/home_view.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'should_keep_splash_with_loading_until_home_feed_settles',
|
||||
(tester) async {
|
||||
final repository = _PendingCatalog();
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: ChangeNotifierProvider(
|
||||
create: (_) => CatalogController(repository)..load(),
|
||||
child: const HomePage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(const Key('splash-screen')), findsOneWidget);
|
||||
expect(find.byKey(const Key('splash-loading')), findsOneWidget);
|
||||
expect(find.byKey(const Key('home-empty')), findsNothing);
|
||||
final clip = tester.widget<ClipRRect>(find.byType(ClipRRect));
|
||||
expect(clip.borderRadius, BorderRadius.circular(18));
|
||||
|
||||
repository.pending.complete(const HomeFeed());
|
||||
await tester.pump();
|
||||
await tester.pump();
|
||||
|
||||
expect(find.byKey(const Key('splash-screen')), findsNothing);
|
||||
expect(find.byKey(const Key('home-empty')), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class _PendingCatalog implements CatalogRepository {
|
||||
final Completer<HomeFeed> pending = Completer<HomeFeed>();
|
||||
|
||||
@override
|
||||
Future<HomeFeed> fetchHomeFeed() => pending.future;
|
||||
|
||||
@override
|
||||
Future<HomeFeed> fetchDiscoverFeed() => pending.future;
|
||||
|
||||
@override
|
||||
Future<Drama> fetchDrama(String dramaId) =>
|
||||
Future<Drama>.error(StateError('unused'));
|
||||
}
|
||||
@@ -14,11 +14,16 @@ double _contrast(Color foreground, Color background) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('should_expose_approved_klein_blue_tokens', () {
|
||||
expect(AppColors.blue, const Color(0xFF002FA7));
|
||||
expect(AppColors.bluePressed, const Color(0xFF00227A));
|
||||
expect(AppColors.blueSoft, const Color(0xFF5B7BE0));
|
||||
test('should_expose_indonesia_evening_brand_tokens', () {
|
||||
expect(AppColors.blue, const Color(0xFFC4322A));
|
||||
expect(AppColors.bluePressed, const Color(0xFF8E221C));
|
||||
expect(AppColors.blueSoft, const Color(0xFFFF8A72));
|
||||
expect(AppColors.gold, const Color(0xFFF6C453));
|
||||
expect(AppColors.onPrimary, const Color(0xFFFFFFFF));
|
||||
expect(
|
||||
_contrast(AppColors.onGold, AppColors.gold),
|
||||
greaterThanOrEqualTo(4.5),
|
||||
);
|
||||
});
|
||||
|
||||
test('should_meet_text_and_icon_contrast_when_using_theme_tokens', () {
|
||||
@@ -33,7 +38,7 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
test('should_use_klein_blue_in_light_and_dark_color_schemes', () {
|
||||
test('should_use_chili_brand_in_light_and_dark_color_schemes', () {
|
||||
for (final theme in [AppTheme.light(), AppTheme.dark()]) {
|
||||
expect(theme.colorScheme.primary, AppColors.blue);
|
||||
expect(theme.colorScheme.onPrimary, AppColors.onPrimary);
|
||||
@@ -52,7 +57,7 @@ void main() {
|
||||
expect(light.outlineVariant.computeLuminance(), lessThan(0.92));
|
||||
});
|
||||
|
||||
testWidgets('should_expose_klein_blue_through_widget_theme', (tester) async {
|
||||
testWidgets('should_expose_chili_brand_through_widget_theme', (tester) async {
|
||||
late ThemeData applied;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
|
||||
@@ -278,7 +278,7 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('should_fit_compact_dark_viewport_when_klein_theme_is_used', (
|
||||
testWidgets('should_fit_compact_dark_viewport_when_evening_theme_is_used', (
|
||||
tester,
|
||||
) async {
|
||||
tester.view.physicalSize = const Size(320, 568);
|
||||
|
||||
Reference in new issue
Block a user