172 lines
5.6 KiB
Dart
172 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:fruitshorts/core/theme/app_colors.dart';
|
|
import 'package:fruitshorts/core/theme/app_theme.dart';
|
|
import 'package:fruitshorts/pages/player/widgets/interaction_rail.dart';
|
|
import 'package:fruitshorts/pages/player/widgets/interaction_rail_burst.dart';
|
|
|
|
const _itemKey = Key('asset-like');
|
|
const _burstKey = Key('rail-tap-burst');
|
|
const _buttonSize = 48.0;
|
|
const _iconSize = 36.0;
|
|
const _motionFrame = Duration(milliseconds: 130);
|
|
const _asset = 'assets/icon/rail/rail_like.png';
|
|
const _epsilon = 0.01;
|
|
const _rayLimit = 26.4;
|
|
const _rayLengthLimit = 3.5;
|
|
const _burstPhases = [0.2, 0.5, 0.8];
|
|
|
|
Finder get _button => find.descendant(
|
|
of: find.byKey(_itemKey),
|
|
matching: find.byType(IconButton),
|
|
);
|
|
|
|
Finder get _image =>
|
|
find.descendant(of: find.byKey(_itemKey), matching: find.byType(Image));
|
|
|
|
double _paintedWidth(WidgetTester tester) {
|
|
final icon = tester.renderObject<RenderBox>(_image);
|
|
final rail = tester.renderObject<RenderBox>(find.byKey(_itemKey));
|
|
return MatrixUtils.transformRect(
|
|
icon.getTransformTo(rail),
|
|
Offset.zero & icon.size,
|
|
).width;
|
|
}
|
|
|
|
Future<void> _pumpAssetRail(
|
|
WidgetTester tester,
|
|
ValueNotifier<bool> selected,
|
|
) => tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: AppTheme.dark(),
|
|
home: Scaffold(
|
|
body: Center(
|
|
child: ValueListenableBuilder<bool>(
|
|
valueListenable: selected,
|
|
builder: (context, value, _) => InteractionRail(
|
|
buttonSize: _buttonSize,
|
|
iconSize: _iconSize,
|
|
items: [
|
|
InteractionRailItem(
|
|
key: _itemKey,
|
|
label: 'Like',
|
|
asset: _asset,
|
|
selected: value,
|
|
selectedColor: AppColors.brandRed,
|
|
animateTap: true,
|
|
onTap: () => selected.value = !selected.value,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
void main() {
|
|
testWidgets('should_not_show_ink_or_shadow_when_button_is_pressed', (
|
|
tester,
|
|
) async {
|
|
final selected = ValueNotifier(false);
|
|
addTearDown(selected.dispose);
|
|
await _pumpAssetRail(tester, selected);
|
|
final button = tester.widget<IconButton>(_button);
|
|
const pressed = {WidgetState.pressed};
|
|
expect(button.style?.overlayColor?.resolve(pressed), AppColors.transparent);
|
|
expect(button.style?.shadowColor?.resolve(pressed), AppColors.transparent);
|
|
expect(button.style?.splashFactory, NoSplash.splashFactory);
|
|
expect(button.style?.elevation?.resolve(pressed), 0);
|
|
});
|
|
|
|
for (final contracting in [false, true]) {
|
|
testWidgets('should_keep_rays_compact_when_contracting_$contracting', (
|
|
tester,
|
|
) async {
|
|
for (final progress in _burstPhases) {
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: SizedBox.square(
|
|
dimension: _buttonSize,
|
|
child: RailTapBurst(
|
|
progress: progress,
|
|
color: AppColors.brandRed,
|
|
contracting: contracting,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
final paint = tester.renderObject(find.byType(CustomPaint));
|
|
expect(paint, paintsExactlyCountTimes(#drawCircle, 0));
|
|
expect(paint, paintsExactlyCountTimes(#drawLine, 8));
|
|
expect(paint, paints..everything(_isCompactRay));
|
|
}
|
|
});
|
|
}
|
|
|
|
testWidgets('should_paint_feedback_at_button_size_when_asset_is_tapped', (
|
|
tester,
|
|
) async {
|
|
final selected = ValueNotifier(false);
|
|
addTearDown(selected.dispose);
|
|
await _pumpAssetRail(tester, selected);
|
|
final touchRect = tester.getRect(_button);
|
|
await tester.tap(_button);
|
|
await tester.pump();
|
|
await tester.pump(_motionFrame);
|
|
expect(selected.value, isTrue);
|
|
expect(_paintedWidth(tester), greaterThan(_iconSize));
|
|
expect(
|
|
tester.getSize(find.byKey(_burstKey)),
|
|
const Size.square(_buttonSize),
|
|
);
|
|
expect(tester.getRect(_button), touchRect);
|
|
await tester.pumpAndSettle();
|
|
expect(_paintedWidth(tester), closeTo(_iconSize, _epsilon));
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
|
|
testWidgets(
|
|
'should_paint_opposite_directions_after_preserved_state_reassembly',
|
|
(tester) async {
|
|
final selected = ValueNotifier(false);
|
|
addTearDown(selected.dispose);
|
|
await _pumpAssetRail(tester, selected);
|
|
unawaited(tester.binding.reassembleApplication());
|
|
await tester.pump();
|
|
for (final cancelling in [false, true]) {
|
|
await tester.tap(_button);
|
|
await tester.pump();
|
|
await tester.pump(_motionFrame);
|
|
expect(
|
|
_paintedWidth(tester),
|
|
cancelling ? lessThan(_iconSize) : greaterThan(_iconSize),
|
|
);
|
|
final feedback = tester.widget<RailTapBurst>(find.byKey(_burstKey));
|
|
expect(feedback.contracting, cancelling);
|
|
expect(selected.value, !cancelling);
|
|
await tester.pumpAndSettle();
|
|
expect(_paintedWidth(tester), closeTo(_iconSize, _epsilon));
|
|
}
|
|
expect(tester.takeException(), isNull);
|
|
},
|
|
);
|
|
}
|
|
|
|
bool _isCompactRay(Symbol method, List<dynamic> arguments) {
|
|
if (method != #drawLine) return true;
|
|
const center = Offset(_buttonSize / 2, _buttonSize / 2);
|
|
final start = arguments[0] as Offset;
|
|
final end = arguments[1] as Offset;
|
|
return (start - center).distance <= _rayLimit &&
|
|
(end - center).distance <= _rayLimit &&
|
|
(end - start).distance <= _rayLengthLimit;
|
|
}
|