Raise the declared minimums in pubspec.yaml to what our deps already require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist 0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is the binding floor. Pin the exact build toolchain in .fvmrc (Flutter 3.44.1). Moving to the Dart 3.9 language level switches `dart format` to the new "tall" style and enables two new lints. This commit is the resulting mechanical churn, isolated from any behaviour change: - whole-tree `dart format` reformat (tall style) - `dart fix` for unnecessary_underscores + use_null_aware_elements No runtime behaviour change; `make test` green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
157 lines
5.7 KiB
Dart
157 lines
5.7 KiB
Dart
/// Widget tests for `lib/widgets/src/clide_tooltip.dart` — hover-driven
|
|
/// OverlayEntry that respects showDelay, places itself below the target by
|
|
/// default, and flips above when the screen is short on space below.
|
|
library;
|
|
|
|
import 'package:clide/widgets/widgets.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../../helpers/kernel_fixture.dart';
|
|
import '../../helpers/widget_harness.dart';
|
|
|
|
void main() {
|
|
group('ClideTooltip', () {
|
|
late KernelFixture f;
|
|
setUp(() async => f = await KernelFixture.create());
|
|
tearDown(() async => f.dispose());
|
|
|
|
testWidgets('tooltip appears after showDelay on hover and hides on exit', (tester) async {
|
|
await tester.pumpWidget(
|
|
harness(
|
|
f,
|
|
const Align(
|
|
alignment: Alignment.topLeft,
|
|
child: ClideTooltip(
|
|
message: 'hello',
|
|
showDelay: Duration(milliseconds: 10),
|
|
child: SizedBox(width: 40, height: 20, key: ValueKey('target')),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Not yet hovering — tooltip text is not in the tree.
|
|
expect(find.text('hello'), findsNothing);
|
|
|
|
// Move a mouse pointer over the target.
|
|
final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.addPointer(location: Offset.zero);
|
|
await gesture.moveTo(tester.getCenter(find.byKey(const ValueKey('target'))));
|
|
await tester.pump();
|
|
|
|
// Let the showDelay elapse and the overlay insert.
|
|
await tester.pump(const Duration(milliseconds: 20));
|
|
expect(find.text('hello'), findsOneWidget);
|
|
|
|
// Hover out — overlay entry is removed synchronously.
|
|
await gesture.moveTo(const Offset(2000, 2000));
|
|
await tester.pump();
|
|
expect(find.text('hello'), findsNothing);
|
|
});
|
|
|
|
testWidgets('mouse-exit before showDelay elapses suppresses the overlay', (tester) async {
|
|
await tester.pumpWidget(
|
|
harness(
|
|
f,
|
|
const Align(
|
|
alignment: Alignment.topLeft,
|
|
child: ClideTooltip(
|
|
message: 'late',
|
|
showDelay: Duration(milliseconds: 50),
|
|
child: SizedBox(width: 40, height: 20, key: ValueKey('target')),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.addPointer(location: Offset.zero);
|
|
await gesture.moveTo(tester.getCenter(find.byKey(const ValueKey('target'))));
|
|
await tester.pump(const Duration(milliseconds: 10));
|
|
|
|
// Exit before the delay completes.
|
|
await gesture.moveTo(const Offset(2000, 2000));
|
|
await tester.pump(const Duration(milliseconds: 100));
|
|
expect(find.text('late'), findsNothing);
|
|
});
|
|
|
|
testWidgets('places tooltip above the target when little space below', (tester) async {
|
|
// Shrink the test view so the target sits near the bottom edge.
|
|
tester.view.physicalSize = const Size(400, 100);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
addTearDown(() {
|
|
tester.view.resetPhysicalSize();
|
|
tester.view.resetDevicePixelRatio();
|
|
});
|
|
|
|
await tester.pumpWidget(
|
|
harness(
|
|
f,
|
|
const Align(
|
|
alignment: Alignment.bottomLeft,
|
|
child: ClideTooltip(
|
|
message: 'above',
|
|
showDelay: Duration(milliseconds: 1),
|
|
child: SizedBox(width: 40, height: 20, key: ValueKey('target')),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.addPointer(location: Offset.zero);
|
|
await gesture.moveTo(tester.getCenter(find.byKey(const ValueKey('target'))));
|
|
await tester.pump(const Duration(milliseconds: 10));
|
|
|
|
expect(find.text('above'), findsOneWidget);
|
|
|
|
// The Positioned ancestor of the tooltip uses `bottom:` (above-mode),
|
|
// not `top:`, when there isn't enough room below.
|
|
final positioned = tester.widget<Positioned>(find.ancestor(of: find.text('above'), matching: find.byType(Positioned)));
|
|
expect(positioned.bottom, isNotNull);
|
|
expect(positioned.top, isNull);
|
|
});
|
|
|
|
testWidgets('re-entering after exit shows the tooltip again (replaces overlay entry)', (tester) async {
|
|
await tester.pumpWidget(
|
|
harness(
|
|
f,
|
|
const Align(
|
|
alignment: Alignment.topLeft,
|
|
child: ClideTooltip(
|
|
message: 'again',
|
|
showDelay: Duration(milliseconds: 5),
|
|
child: SizedBox(width: 40, height: 20, key: ValueKey('target')),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.addPointer(location: Offset.zero);
|
|
|
|
// First hover cycle.
|
|
await gesture.moveTo(tester.getCenter(find.byKey(const ValueKey('target'))));
|
|
await tester.pump(const Duration(milliseconds: 20));
|
|
expect(find.text('again'), findsOneWidget);
|
|
|
|
// Exit.
|
|
await gesture.moveTo(const Offset(2000, 2000));
|
|
await tester.pump();
|
|
expect(find.text('again'), findsNothing);
|
|
|
|
// Re-enter — _show takes the `_entry?.remove()` branch (entry is null
|
|
// now, but the rebuild proves the overlay path is re-traversed).
|
|
await gesture.moveTo(tester.getCenter(find.byKey(const ValueKey('target'))));
|
|
await tester.pump(const Duration(milliseconds: 20));
|
|
expect(find.text('again'), findsOneWidget);
|
|
});
|
|
});
|
|
}
|