blob: 13bc2e2b2badfefffe96af15b956674f8891f441 [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 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'clipboard_utils.dart';
import 'editable_text_utils.dart';
void main() {
final MockClipboard mockClipboard = MockClipboard();
TestWidgetsFlutterBinding.ensureInitialized()
.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, mockClipboard.handleMethodCall);
setUp(() async {
// Fill the clipboard so that the Paste option is available in the text
// selection menu.
await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
});
testWidgets('Hides and shows only a single menu', (WidgetTester tester) async {
final GlobalKey key1 = GlobalKey();
final GlobalKey key2 = GlobalKey();
late final BuildContext context;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext localContext) {
context = localContext;
return const SizedBox.shrink();
},
),
),
),
);
expect(find.byKey(key1), findsNothing);
expect(find.byKey(key2), findsNothing);
final ContextMenuController controller1 = ContextMenuController();
await tester.pump();
expect(find.byKey(key1), findsNothing);
expect(find.byKey(key2), findsNothing);
controller1.show(
context: context,
contextMenuBuilder: (BuildContext context) {
return Placeholder(key: key1);
},
);
await tester.pump();
expect(find.byKey(key1), findsOneWidget);
expect(find.byKey(key2), findsNothing);
// Showing the same thing again does nothing and is not an error.
controller1.show(
context: context,
contextMenuBuilder: (BuildContext context) {
return Placeholder(key: key1);
},
);
await tester.pump();
expect(tester.takeException(), null);
expect(find.byKey(key1), findsOneWidget);
expect(find.byKey(key2), findsNothing);
// Showing a new menu hides the first.
final ContextMenuController controller2 = ContextMenuController();
controller2.show(
context: context,
contextMenuBuilder: (BuildContext context) {
return Placeholder(key: key2);
},
);
await tester.pump();
expect(find.byKey(key1), findsNothing);
expect(find.byKey(key2), findsOneWidget);
controller2.remove();
await tester.pump();
expect(find.byKey(key1), findsNothing);
expect(find.byKey(key2), findsNothing);
});
testWidgets('A menu can be hidden and then reshown', (WidgetTester tester) async {
final GlobalKey key1 = GlobalKey();
late final BuildContext context;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext localContext) {
context = localContext;
return const SizedBox.shrink();
},
),
),
),
);
expect(find.byKey(key1), findsNothing);
final ContextMenuController controller = ContextMenuController();
addTearDown(controller.remove);
// Instantiating the controller does not shown it.
await tester.pump();
expect(find.byKey(key1), findsNothing);
controller.show(
context: context,
contextMenuBuilder: (BuildContext context) {
return Placeholder(key: key1);
},
);
await tester.pump();
expect(find.byKey(key1), findsOneWidget);
controller.remove();
await tester.pump();
expect(find.byKey(key1), findsNothing);
controller.show(
context: context,
contextMenuBuilder: (BuildContext context) {
return Placeholder(key: key1);
},
);
await tester.pump();
expect(find.byKey(key1), findsOneWidget);
});
testWidgets('markNeedsBuild causes the builder to update', (WidgetTester tester) async {
int buildCount = 0;
late final BuildContext context;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext localContext) {
context = localContext;
return const SizedBox.shrink();
},
),
),
),
);
final ContextMenuController controller = ContextMenuController();
controller.show(
context: context,
contextMenuBuilder: (BuildContext context) {
buildCount++;
return const Placeholder();
},
);
expect(buildCount, 0);
await tester.pump();
expect(buildCount, 1);
controller.markNeedsBuild();
expect(buildCount, 1);
await tester.pump();
expect(buildCount, 2);
controller.remove();
});
testWidgets('Calling show when a built-in widget is already showing its context menu hides the built-in menu', (WidgetTester tester) async {
final GlobalKey builtInKey = GlobalKey();
final GlobalKey directKey = GlobalKey();
late final BuildContext context;
final TextEditingController textEditingController = TextEditingController();
addTearDown(textEditingController.dispose);
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext localContext) {
context = localContext;
return EditableText(
controller: textEditingController,
backgroundCursorColor: Colors.grey,
focusNode: focusNode,
style: const TextStyle(),
cursorColor: Colors.red,
selectionControls: materialTextSelectionHandleControls,
contextMenuBuilder: (
BuildContext context,
EditableTextState editableTextState,
) {
return Placeholder(key: builtInKey);
},
);
},
),
),
),
);
expect(find.byKey(builtInKey), findsNothing);
expect(find.byKey(directKey), findsNothing);
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
await tester.tapAt(textOffsetToPosition(tester, 0));
await tester.pump();
expect(state.showToolbar(), true);
await tester.pump();
expect(find.byKey(builtInKey), findsOneWidget);
expect(find.byKey(directKey), findsNothing);
final ContextMenuController controller = ContextMenuController();
controller.show(
context: context,
contextMenuBuilder: (BuildContext context) {
return Placeholder(key: directKey);
},
);
await tester.pump();
expect(find.byKey(builtInKey), findsNothing);
expect(find.byKey(directKey), findsOneWidget);
expect(controller.isShown, isTrue);
// And showing the built-in menu hides the directly shown menu.
expect(state.showToolbar(), isTrue);
await tester.pump();
expect(find.byKey(builtInKey), findsOneWidget);
expect(find.byKey(directKey), findsNothing);
expect(controller.isShown, isFalse);
// Calling remove on the hidden ContextMenuController does not hide the
// built-in menu.
controller.remove();
await tester.pump();
expect(find.byKey(builtInKey), findsOneWidget);
expect(find.byKey(directKey), findsNothing);
expect(controller.isShown, isFalse);
state.hideToolbar();
await tester.pump();
expect(find.byKey(builtInKey), findsNothing);
expect(find.byKey(directKey), findsNothing);
expect(controller.isShown, isFalse);
},
skip: isContextMenuProvidedByPlatform, // [intended] no Flutter-drawn text selection toolbar on web.
);
}