Add tests for `keybindings.dart` (KeyEvent → Keybinding mapping, parse-error edges, resolver entries view), `toolchain_paths.dart` (the Flutter-free `ToolchainView.resolved` static view), and several `widgets/src/` primitives: tooltip hover/overlay, palette filter + submit, multitab controller `copyWith` + size getters, and additional markdown branches (h3–h6 headings, tables, strikethrough, default block fallback, record-link tap). Unfreezes the pre-push coverage floor that was held at 90 on 2026-05-14 by mistake and ratchets to 93. Tidies eight test files that had accumulated unused imports flagged by `unnecessary_import`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
162 lines
5.7 KiB
Dart
162 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);
|
|
});
|
|
});
|
|
}
|