Files
buzz/mobile/test/features/home/home_page_test.dart
T
cls 9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
feat: import Chinese-localized Buzz source snapshot
Signed-off-by: cls_宁波本机 <908705107@qq.com>
2026-08-13 18:34:25 +08:00

358 lines
12 KiB
Dart

import 'package:buzz/features/home/home_page.dart';
import 'package:buzz/features/channels/channels_page.dart';
import 'package:buzz/shared/theme/theme.dart';
import 'package:buzz/shared/l10n/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
Future<Widget> buildHome({
int unreadInboxCount = 0,
bool disableAnimations = false,
Gradient? topSectionGradient,
}) async {
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
return ProviderScope(
overrides: [savedPrefsProvider.overrideWithValue(prefs)],
child: MaterialApp(
locale: const Locale('en'),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
theme: AppTheme.light(topSectionGradient: topSectionGradient),
builder: (context, child) => MediaQuery(
data: MediaQuery.of(
context,
).copyWith(disableAnimations: disableAnimations),
child: child!,
),
home: HomePage(
settingsPageBuilder: _buildSettingsPage,
hasUnreadInbox: unreadInboxCount > 0,
),
),
);
}
testWidgets('shows icon-only navigation and an aligned quick action', (
tester,
) async {
await tester.pumpWidget(await buildHome());
await tester.pump();
expect(find.text('Home'), findsNothing);
expect(find.text('Activity'), findsNothing);
expect(find.text('Search'), findsNothing);
expect(find.bySemanticsLabel('Home'), findsOneWidget);
expect(find.bySemanticsLabel('Activity'), findsOneWidget);
expect(find.bySemanticsLabel('Search'), findsOneWidget);
final quickAction = find.byTooltip('Create or start conversation');
expect(quickAction, findsOneWidget);
final launcherSize = tester.getSize(
find.byType(ChannelQuickActionsLauncher),
);
expect(launcherSize.width, 800);
expect(launcherSize.height, greaterThan(0));
final motionRect = tester.getRect(
find.byKey(const Key('channel-quick-actions-motion')),
);
expect(motionRect.width, const Size.square(56).width);
expect(motionRect.left, greaterThanOrEqualTo(0));
expect(tester.getSize(quickAction), const Size.square(56));
final quickActionRect = tester.getRect(quickAction);
expect(quickActionRect.left, greaterThanOrEqualTo(0));
expect(quickActionRect.top, greaterThanOrEqualTo(0));
expect(quickActionRect.right, lessThanOrEqualTo(800));
expect(quickActionRect.bottom, lessThanOrEqualTo(600));
final homeDestinationRect = tester.getRect(find.bySemanticsLabel('Home'));
expect(
quickActionRect.center.dy,
closeTo(homeDestinationRect.center.dy, 0.01),
);
});
testWidgets('keeps the Buzz backdrop behind the scalable Home screen', (
tester,
) async {
const gradient = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.yellow, Colors.blue],
);
await tester.pumpWidget(await buildHome(topSectionGradient: gradient));
await tester.pump();
final backdrop = find.byKey(
const ValueKey('home-settings-transition-backdrop'),
);
final decoration =
tester.widget<DecoratedBox>(backdrop).decoration as BoxDecoration;
expect(decoration.gradient, gradient);
expect(
find.byKey(const ValueKey('home-settings-transition-scale')),
findsOneWidget,
);
expect(
tester
.widget<Transform>(
find.byKey(const ValueKey('home-settings-transition-scale')),
)
.transform
.getMaxScaleOnAxis(),
1,
);
expect(
tester
.widget<Opacity>(
find.byKey(const ValueKey('home-settings-transition-opacity')),
)
.opacity,
1,
);
});
testWidgets('gives selection haptics only when the tab changes', (
tester,
) async {
final hapticCalls = <MethodCall>[];
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, (call) async {
if (call.method == 'HapticFeedback.vibrate') {
hapticCalls.add(call);
}
return null;
});
addTearDown(
() => TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, null),
);
await tester.pumpWidget(await buildHome());
await tester.pump();
await tester.tap(find.byTooltip('Home'));
await tester.pump();
expect(hapticCalls, isEmpty);
await tester.tap(find.byTooltip('Activity'));
await tester.pump();
expect(hapticCalls, hasLength(1));
expect(hapticCalls.single.arguments, 'HapticFeedbackType.selectionClick');
await tester.tap(find.byTooltip('Activity'));
await tester.pump();
expect(hapticCalls, hasLength(1));
await tester.tap(find.byTooltip('Search'));
await tester.pump();
expect(hapticCalls, hasLength(2));
});
testWidgets('gives a light impact when the Home quick action is pressed', (
tester,
) async {
final hapticCalls = <MethodCall>[];
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, (call) async {
if (call.method == 'HapticFeedback.vibrate') {
hapticCalls.add(call);
}
return null;
});
addTearDown(
() => TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, null),
);
await tester.pumpWidget(await buildHome());
await tester.pump();
await tester.tap(find.byTooltip('Create or start conversation'));
await tester.pump();
expect(hapticCalls, hasLength(1));
expect(hapticCalls.single.arguments, 'HapticFeedbackType.lightImpact');
});
testWidgets('badges the Inbox tab when it has unread rows', (tester) async {
await tester.pumpWidget(await buildHome(unreadInboxCount: 1));
await tester.pump();
expect(
find.byKey(const ValueKey('activity-tab-unread-dot')),
findsOneWidget,
);
final badge = tester.widget<Container>(
find.byKey(const ValueKey('activity-tab-unread-dot')),
);
expect(badge.constraints?.maxWidth, 12);
expect(badge.constraints?.maxHeight, 12);
expect(find.bySemanticsLabel('Activity, unread'), findsOneWidget);
AnimatedScale unreadDotScale() => tester.widget<AnimatedScale>(
find.byKey(const ValueKey('activity-tab-unread-dot-scale')),
);
expect(unreadDotScale().scale, 1);
expect(unreadDotScale().alignment, const Alignment(-0.5, 0.5));
expect(unreadDotScale().duration, const Duration(milliseconds: 220));
await tester.tap(find.byTooltip('Activity'));
await tester.pump();
expect(
find.byKey(const ValueKey('activity-tab-unread-dot')),
findsOneWidget,
);
expect(unreadDotScale().scale, 0);
expect(find.bySemanticsLabel('Activity, unread'), findsNothing);
await tester.tap(find.byTooltip('Home'));
await tester.pump();
expect(unreadDotScale().scale, 1);
});
testWidgets('fades and slides tab content in the selected direction', (
tester,
) async {
await tester.pumpWidget(await buildHome());
await tester.pump();
Transform bodyTransform() => tester.widget<Transform>(
find.byKey(const ValueKey('frosted-scaffold-body-transition-transform')),
);
Opacity bodyOpacity() => tester.widget<Opacity>(
find.byKey(const ValueKey('frosted-scaffold-body-transition-opacity')),
);
Transform appBarTransform() => tester.widget<Transform>(
find.byKey(
const ValueKey('frosted-app-bar-content-transition-transform'),
),
);
Opacity appBarOpacity() => tester.widget<Opacity>(
find.byKey(const ValueKey('frosted-app-bar-content-transition-opacity')),
);
double bodyOffset() => bodyTransform().transform.getTranslation().x;
double appBarOffset() => appBarTransform().transform.getTranslation().x;
expect(bodyOffset(), closeTo(0, 0.001));
expect(appBarOffset(), closeTo(0, 0.001));
expect(bodyOpacity().opacity, closeTo(1, 0.001));
expect(appBarOpacity().opacity, closeTo(1, 0.001));
await tester.tap(find.byTooltip('Activity'));
await tester.pump();
expect(bodyOffset(), closeTo(24, 0.001));
expect(appBarOffset(), closeTo(24, 0.001));
expect(bodyOpacity().opacity, closeTo(0, 0.001));
expect(appBarOpacity().opacity, closeTo(0, 0.001));
expect(
find.descendant(
of: find.byKey(const ValueKey('frosted-app-bar-background')),
matching: find.byKey(
const ValueKey('frosted-app-bar-content-transition-transform'),
),
),
findsOneWidget,
);
await tester.pump(const Duration(milliseconds: 120));
expect(bodyOffset(), inExclusiveRange(0, 24));
expect(appBarOffset(), inExclusiveRange(0, 24));
expect(bodyOpacity().opacity, inExclusiveRange(0, 1));
expect(appBarOpacity().opacity, inExclusiveRange(0, 1));
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('Home'));
await tester.pump();
expect(bodyOffset(), closeTo(-24, 0.001));
expect(appBarOffset(), closeTo(-24, 0.001));
expect(bodyOpacity().opacity, closeTo(0, 0.001));
expect(appBarOpacity().opacity, closeTo(0, 0.001));
await tester.pumpAndSettle();
expect(bodyOffset(), closeTo(0, 0.001));
expect(appBarOffset(), closeTo(0, 0.001));
expect(bodyOpacity().opacity, closeTo(1, 0.001));
expect(appBarOpacity().opacity, closeTo(1, 0.001));
});
testWidgets('switches tab content instantly with reduced motion', (
tester,
) async {
await tester.pumpWidget(await buildHome(disableAnimations: true));
await tester.pump();
await tester.tap(find.byTooltip('Activity'));
await tester.pump();
final bodyTransform = tester.widget<Transform>(
find.byKey(const ValueKey('frosted-scaffold-body-transition-transform')),
);
final bodyOpacity = tester.widget<Opacity>(
find.byKey(const ValueKey('frosted-scaffold-body-transition-opacity')),
);
final appBarTransform = tester.widget<Transform>(
find.byKey(
const ValueKey('frosted-app-bar-content-transition-transform'),
),
);
final appBarOpacity = tester.widget<Opacity>(
find.byKey(const ValueKey('frosted-app-bar-content-transition-opacity')),
);
expect(bodyTransform.transform.getTranslation().x, closeTo(0, 0.001));
expect(appBarTransform.transform.getTranslation().x, closeTo(0, 0.001));
expect(bodyOpacity.opacity, closeTo(1, 0.001));
expect(appBarOpacity.opacity, closeTo(1, 0.001));
});
testWidgets('scales and fades the quick action as tabs change', (
tester,
) async {
await tester.pumpWidget(await buildHome());
await tester.pump();
double scale() => tester
.widget<Transform>(find.byKey(const Key('channel-quick-actions-scale')))
.transform
.storage
.first;
double opacity() => tester
.widget<Opacity>(find.byKey(const Key('channel-quick-actions-opacity')))
.opacity;
expect(scale(), closeTo(1, 0.001));
expect(opacity(), closeTo(1, 0.001));
await tester.tap(find.byTooltip('Activity'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 110));
expect(scale(), inExclusiveRange(0.8, 1));
expect(opacity(), inExclusiveRange(0, 1));
await tester.pumpAndSettle();
expect(scale(), closeTo(0.8, 0.001));
expect(opacity(), closeTo(0, 0.001));
await tester.tap(find.byTooltip('Home'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 110));
expect(scale(), inExclusiveRange(0.8, 1));
expect(opacity(), inExclusiveRange(0, 1));
await tester.pumpAndSettle();
expect(scale(), closeTo(1, 0.001));
expect(opacity(), closeTo(1, 0.001));
});
}
Widget _buildSettingsPage(BuildContext context) => const SizedBox.shrink();