blob: febf9a04798c2c6b313bfe243558914289d00712 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void _ensureTooltipVisible(GlobalKey key) {
// This function uses "as dynamic" to defeat the static analysis. In general
// you want to avoid using this style in your code, as it will cause the
// analyzer to be unable to help you catch errors.
//
// In this case, we do it because we are trying to call internal methods of
// the tooltip code in order to test it. Normally, the state of a tooltip is a
// private class, but by using a GlobalKey we can get a handle to that object
// and by using "as dynamic" we can bypass the analyzer's type checks and call
// methods that we aren't supposed to be able to know about.
//
// It's ok to do this in tests, but you really don't want to do it in
// production code.
// ignore: avoid_dynamic_calls
(key.currentState as dynamic).ensureTooltipVisible();
}
const String tooltipText = 'TIP';
void main() {
testWidgets('Tooltip does not build MouseRegion when mouse is detected and in TooltipVisibility with visibility = false', (WidgetTester tester) async {
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(() async {
if (gesture != null)
return gesture.removePointer();
});
await gesture.addPointer();
await gesture.moveTo(const Offset(1.0, 1.0));
await tester.pump();
await gesture.moveTo(Offset.zero);
await tester.pumpWidget(
const MaterialApp(
home: TooltipVisibility(
visible: false,
child: Tooltip(
message: tooltipText,
child: SizedBox(
width: 100.0,
height: 100.0,
),
),
),
),
);
expect(find.descendant(of: find.byType(Tooltip), matching: find.byType(MouseRegion)), findsNothing);
});
testWidgets('Tooltip does not show when hovered when in TooltipVisibility with visible = false', (WidgetTester tester) async {
const Duration waitDuration = Duration.zero;
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(() async {
if (gesture != null)
return gesture.removePointer();
});
await gesture.addPointer();
await gesture.moveTo(const Offset(1.0, 1.0));
await tester.pump();
await gesture.moveTo(Offset.zero);
await tester.pumpWidget(
const MaterialApp(
home: Center(
child: TooltipVisibility(
visible: false,
child: Tooltip(
message: tooltipText,
waitDuration: waitDuration,
child: SizedBox(
width: 100.0,
height: 100.0,
),
),
),
),
),
);
final Finder tooltip = find.byType(Tooltip);
await gesture.moveTo(Offset.zero);
await tester.pump();
await gesture.moveTo(tester.getCenter(tooltip));
await tester.pump();
// Wait for it to appear.
await tester.pump(waitDuration);
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip shows when hovered when in TooltipVisibility with visible = true', (WidgetTester tester) async {
const Duration waitDuration = Duration.zero;
TestGesture? gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(() async {
if (gesture != null)
return gesture.removePointer();
});
await gesture.addPointer();
await gesture.moveTo(const Offset(1.0, 1.0));
await tester.pump();
await gesture.moveTo(Offset.zero);
await tester.pumpWidget(
const MaterialApp(
home: Center(
child: TooltipVisibility(
visible: true,
child: Tooltip(
message: tooltipText,
waitDuration: waitDuration,
child: SizedBox(
width: 100.0,
height: 100.0,
),
),
),
),
),
);
final Finder tooltip = find.byType(Tooltip);
await gesture.moveTo(Offset.zero);
await tester.pump();
await gesture.moveTo(tester.getCenter(tooltip));
await tester.pump();
// Wait for it to appear.
await tester.pump(waitDuration);
expect(find.text(tooltipText), findsOneWidget);
// Wait for it to disappear.
await gesture.moveTo(Offset.zero);
await tester.pumpAndSettle();
await gesture.removePointer();
gesture = null;
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip does not build GestureDetector when in TooltipVisibility with visibility = false', (WidgetTester tester) async {
await setWidgetForTooltipMode(tester, TooltipTriggerMode.tap, false);
expect(find.byType(GestureDetector), findsNothing);
});
testWidgets('Tooltip triggers on tap when trigger mode is tap and in TooltipVisibility with visible = true', (WidgetTester tester) async {
await setWidgetForTooltipMode(tester, TooltipTriggerMode.tap, true);
final Finder tooltip = find.byType(Tooltip);
expect(find.text(tooltipText), findsNothing);
await testGestureTap(tester, tooltip);
expect(find.text(tooltipText), findsOneWidget);
});
testWidgets('Tooltip does not trigger manually when in TooltipVisibility with visible = false', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
await tester.pumpWidget(
MaterialApp(
home: TooltipVisibility(
visible: false,
child: Tooltip(
key: key,
message: tooltipText,
child: const SizedBox(width: 100.0, height: 100.0),
),
),
),
);
_ensureTooltipVisible(key);
await tester.pump();
expect(find.text(tooltipText), findsNothing);
});
testWidgets('Tooltip triggers manually when in TooltipVisibility with visible = true', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
await tester.pumpWidget(
MaterialApp(
home: TooltipVisibility(
visible: true,
child: Tooltip(
key: key,
message: tooltipText,
child: const SizedBox(width: 100.0, height: 100.0),
),
),
),
);
_ensureTooltipVisible(key);
await tester.pump();
expect(find.text(tooltipText), findsOneWidget);
});
}
Future<void> setWidgetForTooltipMode(WidgetTester tester, TooltipTriggerMode triggerMode, bool visibility) async {
await tester.pumpWidget(
MaterialApp(
home: TooltipVisibility(
visible: visibility,
child: Tooltip(
message: tooltipText,
triggerMode: triggerMode,
child: const SizedBox(width: 100.0, height: 100.0),
),
),
),
);
}
Future<void> testGestureTap(WidgetTester tester, Finder tooltip) async {
await tester.tap(tooltip);
await tester.pump(const Duration(milliseconds: 10));
}