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
Signed-off-by: cls_宁波本机 <908705107@qq.com>
187 lines
5.6 KiB
Dart
187 lines
5.6 KiB
Dart
import 'package:buzz/shared/theme/theme.dart';
|
|
import 'package:buzz/shared/widgets/skeleton.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
Widget buildShimmerTestable({
|
|
bool disableAnimations = false,
|
|
bool enabled = true,
|
|
}) {
|
|
return MaterialApp(
|
|
theme: AppTheme.light(),
|
|
builder: (context, child) => MediaQuery(
|
|
data: MediaQuery.of(
|
|
context,
|
|
).copyWith(disableAnimations: disableAnimations),
|
|
child: child!,
|
|
),
|
|
home: Scaffold(
|
|
body: SkeletonShimmer(
|
|
enabled: enabled,
|
|
child: const SkeletonBar(width: 120, height: 16),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildRevealTestable({
|
|
required ValueNotifier<bool> loading,
|
|
bool disableAnimations = false,
|
|
}) {
|
|
return MaterialApp(
|
|
theme: AppTheme.light(),
|
|
builder: (context, child) => MediaQuery(
|
|
data: MediaQuery.of(
|
|
context,
|
|
).copyWith(disableAnimations: disableAnimations),
|
|
child: child!,
|
|
),
|
|
home: Scaffold(
|
|
body: ValueListenableBuilder(
|
|
valueListenable: loading,
|
|
builder: (context, isLoading, _) => SkeletonReveal(
|
|
loading: isLoading,
|
|
skeleton: const SkeletonBar(width: 120, height: 16),
|
|
content: const Text('Loaded content'),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
double layerOpacity(WidgetTester tester, String key) =>
|
|
tester.widget<Opacity>(find.byKey(Key(key))).opacity;
|
|
|
|
bool contentFocusExcluded(WidgetTester tester) => tester
|
|
.widget<ExcludeFocus>(
|
|
find.descendant(
|
|
of: find.byKey(const Key('skeleton-reveal-content')),
|
|
matching: find.byType(ExcludeFocus),
|
|
),
|
|
)
|
|
.excluding;
|
|
|
|
testWidgets('sweeps a highlight across skeleton elements while loading', (
|
|
tester,
|
|
) async {
|
|
await tester.pumpWidget(buildShimmerTestable());
|
|
|
|
final shimmer = find.byType(SkeletonShimmer);
|
|
expect(find.byType(SkeletonBar), findsOneWidget);
|
|
expect(
|
|
find.descendant(of: shimmer, matching: find.byType(ShaderMask)),
|
|
findsOneWidget,
|
|
);
|
|
|
|
final boundary = tester.renderObject<RenderRepaintBoundary>(
|
|
find
|
|
.descendant(of: shimmer, matching: find.byType(RepaintBoundary))
|
|
.first,
|
|
);
|
|
final beforeBytes = await tester.runAsync(() async {
|
|
final image = await boundary.toImage();
|
|
return image.toByteData();
|
|
});
|
|
|
|
await tester.pump(const Duration(milliseconds: 1000));
|
|
final afterBytes = await tester.runAsync(() async {
|
|
final image = await boundary.toImage();
|
|
return image.toByteData();
|
|
});
|
|
|
|
expect(
|
|
beforeBytes!.buffer.asUint8List(),
|
|
isNot(equals(afterBytes!.buffer.asUint8List())),
|
|
);
|
|
});
|
|
|
|
testWidgets('keeps skeleton elements static for reduced motion', (
|
|
tester,
|
|
) async {
|
|
await tester.pumpWidget(buildShimmerTestable(disableAnimations: true));
|
|
|
|
final shimmer = find.byType(SkeletonShimmer);
|
|
expect(find.byType(SkeletonBar), findsOneWidget);
|
|
expect(
|
|
find.descendant(of: shimmer, matching: find.byType(ShaderMask)),
|
|
findsNothing,
|
|
);
|
|
});
|
|
|
|
testWidgets('keeps skeleton elements static when shimmer is disabled', (
|
|
tester,
|
|
) async {
|
|
await tester.pumpWidget(buildShimmerTestable(enabled: false));
|
|
|
|
final shimmer = find.byType(SkeletonShimmer);
|
|
expect(find.byType(SkeletonBar), findsOneWidget);
|
|
expect(
|
|
find.descendant(of: shimmer, matching: find.byType(ShaderMask)),
|
|
findsNothing,
|
|
);
|
|
});
|
|
|
|
testWidgets('reveals content in the same slot with a 400ms cross-fade', (
|
|
tester,
|
|
) async {
|
|
final loading = ValueNotifier(true);
|
|
await tester.pumpWidget(buildRevealTestable(loading: loading));
|
|
|
|
expect(layerOpacity(tester, 'skeleton-reveal-placeholder'), 1);
|
|
expect(layerOpacity(tester, 'skeleton-reveal-content'), 0);
|
|
expect(contentFocusExcluded(tester), isTrue);
|
|
|
|
loading.value = false;
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
|
|
expect(
|
|
layerOpacity(tester, 'skeleton-reveal-placeholder'),
|
|
closeTo(0.5, 0.01),
|
|
);
|
|
expect(layerOpacity(tester, 'skeleton-reveal-content'), closeTo(0.5, 0.01));
|
|
expect(contentFocusExcluded(tester), isTrue);
|
|
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
|
|
expect(layerOpacity(tester, 'skeleton-reveal-placeholder'), 0);
|
|
expect(layerOpacity(tester, 'skeleton-reveal-content'), 1);
|
|
expect(contentFocusExcluded(tester), isFalse);
|
|
expect(find.text('Loaded content'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('resets to the skeleton without animating backwards', (
|
|
tester,
|
|
) async {
|
|
final loading = ValueNotifier(false);
|
|
await tester.pumpWidget(buildRevealTestable(loading: loading));
|
|
expect(layerOpacity(tester, 'skeleton-reveal-content'), 1);
|
|
|
|
loading.value = true;
|
|
await tester.pump();
|
|
|
|
expect(layerOpacity(tester, 'skeleton-reveal-placeholder'), 1);
|
|
expect(layerOpacity(tester, 'skeleton-reveal-content'), 0);
|
|
});
|
|
|
|
testWidgets('reveals immediately for reduced motion', (tester) async {
|
|
final loading = ValueNotifier(true);
|
|
await tester.pumpWidget(
|
|
buildRevealTestable(loading: loading, disableAnimations: true),
|
|
);
|
|
final reveal = find.byType(SkeletonReveal);
|
|
expect(
|
|
find.descendant(of: reveal, matching: find.byType(ShaderMask)),
|
|
findsNothing,
|
|
);
|
|
|
|
loading.value = false;
|
|
await tester.pump();
|
|
|
|
expect(layerOpacity(tester, 'skeleton-reveal-placeholder'), 0);
|
|
expect(layerOpacity(tester, 'skeleton-reveal-content'), 1);
|
|
});
|
|
}
|