flutter_flutter/packages/flutter/test/widgets/system_context_menu_test.dart
Jing Shao cc968cae8b
[iOS] Add Live Text option to system context menu (#170969)
<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->

**Live Text (OCR) button disappeared from text field menus on iOS after
the Secure Paste M2 update. This PR adds it back.**

**Fixes #169781**

Note: This is a draft PR for initial review. Still need to add tests or
split to framework and engine

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-07-10 22:32:31 +00:00

741 lines
28 KiB
Dart

// 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/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import '../system_context_menu_utils.dart';
void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
testWidgets(
'asserts when built on an unsupported device',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
await tester.pumpWidget(
// By default, MediaQueryData.supportsShowingSystemContextMenu is false.
MaterialApp(
home: Scaffold(
body: Center(
child: TextField(
controller: controller,
contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(editableTextState: editableTextState);
},
),
),
),
),
);
await tester.tap(find.byType(TextField));
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
await tester.pump();
expect(tester.takeException(), isAssertionError);
},
skip: kIsWeb, // [intended]
variant: TargetPlatformVariant.all(),
);
testWidgets(
'asserts when built on web',
(WidgetTester tester) async {
// Disable the browser context menu so that contextMenuBuilder will be used.
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.contextMenu,
(MethodCall call) {
// Just complete successfully, so that BrowserContextMenu thinks that
// the engine successfully received its call.
return Future<void>.value();
},
);
await BrowserContextMenu.disableContextMenu();
addTearDown(() async {
await BrowserContextMenu.enableContextMenu();
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.contextMenu,
null,
);
});
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
await tester.pumpWidget(
// By default, MediaQueryData.supportsShowingSystemContextMenu is false.
MaterialApp(
home: Scaffold(
body: Center(
child: TextField(
controller: controller,
contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(editableTextState: editableTextState);
},
),
),
),
),
);
await tester.tap(find.byType(TextField));
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
await tester.pump();
expect(tester.takeException(), isAssertionError);
},
skip: !kIsWeb, // [intended]
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
testWidgets(
'can be shown and hidden like a normal context menu',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
child: MaterialApp(
home: Scaffold(
body: Center(
child: TextField(
controller: controller,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
);
},
),
),
),
),
);
},
),
);
expect(find.byType(SystemContextMenu), findsNothing);
await tester.tap(find.byType(TextField));
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
await tester.pump();
expect(find.byType(SystemContextMenu), findsOneWidget);
state.hideToolbar();
await tester.pump();
expect(find.byType(SystemContextMenu), findsNothing);
},
skip: kIsWeb, // [intended]
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
testWidgets(
'can customize the menu items',
(WidgetTester tester) async {
final List<List<IOSSystemContextMenuItemData>> itemsReceived =
<List<IOSSystemContextMenuItemData>>[];
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
(MethodCall methodCall) async {
switch (methodCall.method) {
case 'ContextMenu.showSystemContextMenu':
final Map<String, dynamic> arguments = methodCall.arguments as Map<String, dynamic>;
final List<dynamic> untypedItems = arguments['items'] as List<dynamic>;
final List<IOSSystemContextMenuItemData> lastItems = untypedItems.map((
dynamic value,
) {
final Map<String, dynamic> itemJson = value as Map<String, dynamic>;
return systemContextMenuItemDataFromJson(itemJson);
}).toList();
itemsReceived.add(lastItems);
}
return;
},
);
addTearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
null,
);
});
const List<IOSSystemContextMenuItem> items1 = <IOSSystemContextMenuItem>[
IOSSystemContextMenuItemCopy(),
IOSSystemContextMenuItemShare(title: 'My Share Title'),
IOSSystemContextMenuItemLiveText(),
];
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
child: MaterialApp(
home: Scaffold(
body: Center(
child: TextField(
controller: controller,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
items: items1,
);
},
),
),
),
),
);
},
),
);
expect(find.byType(SystemContextMenu), findsNothing);
expect(itemsReceived, hasLength(0));
await tester.tap(find.byType(TextField));
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
await tester.pump();
expect(find.byType(SystemContextMenu), findsOneWidget);
expect(itemsReceived, hasLength(1));
expect(itemsReceived.last, hasLength(items1.length));
expect(itemsReceived.last[0], equals(const IOSSystemContextMenuItemDataCopy()));
expect(
itemsReceived.last[1],
equals(const IOSSystemContextMenuItemDataShare(title: 'My Share Title')),
);
expect(itemsReceived.last[2], equals(const IOSSystemContextMenuItemDataLiveText()));
state.hideToolbar();
await tester.pump();
expect(find.byType(SystemContextMenu), findsNothing);
},
skip: kIsWeb, // [intended]
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
testWidgets(
"passing empty items builds the widget but doesn't show the system context menu",
(WidgetTester tester) async {
final List<List<IOSSystemContextMenuItemData>> itemsReceived =
<List<IOSSystemContextMenuItemData>>[];
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
(MethodCall methodCall) async {
switch (methodCall.method) {
case 'ContextMenu.showSystemContextMenu':
final Map<String, dynamic> arguments = methodCall.arguments as Map<String, dynamic>;
final List<dynamic> untypedItems = arguments['items'] as List<dynamic>;
final List<IOSSystemContextMenuItemData> lastItems = untypedItems.map((
dynamic value,
) {
final Map<String, dynamic> itemJson = value as Map<String, dynamic>;
return systemContextMenuItemDataFromJson(itemJson);
}).toList();
itemsReceived.add(lastItems);
}
return;
},
);
addTearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
null,
);
});
const List<IOSSystemContextMenuItem> items1 = <IOSSystemContextMenuItem>[];
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
child: MaterialApp(
home: Scaffold(
body: Center(
child: TextField(
controller: controller,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
items: items1,
);
},
),
),
),
),
);
},
),
);
expect(tester.takeException(), isNull);
expect(find.byType(SystemContextMenu), findsNothing);
expect(itemsReceived, hasLength(0));
await tester.tap(find.byType(TextField));
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
expect(tester.takeException(), isNull);
await tester.pump();
expect(tester.takeException(), isNull);
expect(find.byType(SystemContextMenu), findsOneWidget);
expect(itemsReceived, hasLength(0));
},
skip: kIsWeb, // [intended]
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
testWidgets(
'items receive a default title',
(WidgetTester tester) async {
final List<List<IOSSystemContextMenuItemData>> itemsReceived =
<List<IOSSystemContextMenuItemData>>[];
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
(MethodCall methodCall) async {
switch (methodCall.method) {
case 'ContextMenu.showSystemContextMenu':
final Map<String, dynamic> arguments = methodCall.arguments as Map<String, dynamic>;
final List<dynamic> untypedItems = arguments['items'] as List<dynamic>;
final List<IOSSystemContextMenuItemData> lastItems = untypedItems.map((
dynamic value,
) {
final Map<String, dynamic> itemJson = value as Map<String, dynamic>;
return systemContextMenuItemDataFromJson(itemJson);
}).toList();
itemsReceived.add(lastItems);
}
return;
},
);
addTearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
null,
);
});
const List<IOSSystemContextMenuItem> items1 = <IOSSystemContextMenuItem>[
// Copy gets no title, it's set by the platform.
IOSSystemContextMenuItemCopy(),
// Share could take a title, but if not, it gets a localized default.
IOSSystemContextMenuItemShare(),
];
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
child: MaterialApp(
home: Scaffold(
body: Center(
child: TextField(
controller: controller,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
items: items1,
);
},
),
),
),
),
);
},
),
);
expect(find.byType(SystemContextMenu), findsNothing);
expect(itemsReceived, hasLength(0));
await tester.tap(find.byType(TextField));
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
await tester.pump();
expect(find.byType(SystemContextMenu), findsOneWidget);
expect(itemsReceived, hasLength(1));
expect(itemsReceived.last, hasLength(items1.length));
expect(itemsReceived.last[0], equals(const IOSSystemContextMenuItemDataCopy()));
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
expect(
itemsReceived.last[1],
equals(IOSSystemContextMenuItemDataShare(title: localizations.shareButtonLabel)),
);
state.hideToolbar();
await tester.pump();
expect(find.byType(SystemContextMenu), findsNothing);
},
skip: kIsWeb, // [intended]
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
testWidgets(
'can be updated.',
(WidgetTester tester) async {
final List<Map<String, double>> targetRects = <Map<String, double>>[];
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
(MethodCall methodCall) async {
if (methodCall.method == 'ContextMenu.showSystemContextMenu') {
final Map<String, dynamic> arguments = methodCall.arguments as Map<String, dynamic>;
final Map<String, dynamic> untypedTargetRect =
arguments['targetRect'] as Map<String, dynamic>;
final Map<String, double> lastTargetRect = untypedTargetRect.map((
String key,
dynamic value,
) {
return MapEntry<String, double>(key, value as double);
});
targetRects.add(lastTargetRect);
}
return;
},
);
addTearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
null,
);
});
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
child: MaterialApp(
home: Scaffold(
body: Center(
child: TextField(
controller: controller,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
);
},
),
),
),
),
);
},
),
);
expect(targetRects, isEmpty);
await tester.tap(find.byType(TextField));
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
await tester.pump();
expect(targetRects, hasLength(1));
expect(targetRects.last, containsPair('width', 0.0));
controller.selection = const TextSelection(baseOffset: 4, extentOffset: 7);
await tester.pumpAndSettle();
expect(targetRects, hasLength(2));
expect(targetRects.last['width'], greaterThan(0.0));
},
skip: kIsWeb, // [intended]
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
testWidgets(
'can be rebuilt',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
late StateSetter setState;
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
child: MaterialApp(
home: Scaffold(
body: Center(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter localSetState) {
setState = localSetState;
return TextField(
controller: controller,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
);
},
);
},
),
),
),
),
);
},
),
);
await tester.tap(find.byType(TextField));
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
await tester.pump();
setState(() {});
await tester.pumpAndSettle();
expect(tester.takeException(), isNull);
},
skip: kIsWeb, // [intended]
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
testWidgets(
'can handle multiple instances',
(WidgetTester tester) async {
final TextEditingController controller1 = TextEditingController(text: 'one two three');
addTearDown(controller1.dispose);
final TextEditingController controller2 = TextEditingController(text: 'four five six');
addTearDown(controller2.dispose);
final GlobalKey field1Key = GlobalKey();
final GlobalKey field2Key = GlobalKey();
final GlobalKey menu1Key = GlobalKey();
final GlobalKey menu2Key = GlobalKey();
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
child: MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: <Widget>[
TextField(
key: field1Key,
controller: controller1,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
key: menu1Key,
editableTextState: editableTextState,
);
},
),
TextField(
key: field2Key,
controller: controller2,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
key: menu2Key,
editableTextState: editableTextState,
);
},
),
],
),
),
),
),
);
},
),
);
expect(find.byType(SystemContextMenu), findsNothing);
await tester.tap(find.byKey(field1Key));
final EditableTextState state1 = tester.state<EditableTextState>(
find.descendant(of: find.byKey(field1Key), matching: find.byType(EditableText)),
);
expect(state1.showToolbar(), true);
await tester.pump();
expect(find.byKey(menu1Key), findsOneWidget);
expect(find.byKey(menu2Key), findsNothing);
// In a real app, this message is sent by iOS when the user taps anywhere
// outside of the system context menu.
final ByteData? messageBytes = const JSONMessageCodec().encodeMessage(<String, dynamic>{
'method': 'ContextMenu.onDismissSystemContextMenu',
});
await binding.defaultBinaryMessenger.handlePlatformMessage(
'flutter/platform',
messageBytes,
(ByteData? data) {},
);
await tester.pump();
expect(find.byType(SystemContextMenu), findsNothing);
await tester.tap(find.byKey(field2Key));
final EditableTextState state2 = tester.state<EditableTextState>(
find.descendant(of: find.byKey(field2Key), matching: find.byType(EditableText)),
);
expect(state2.showToolbar(), true);
await tester.pump();
expect(find.byKey(menu1Key), findsNothing);
expect(find.byKey(menu2Key), findsOneWidget);
},
skip: kIsWeb, // [intended]
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
test(
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemCopy',
() {
const IOSSystemContextMenuItemCopy item = IOSSystemContextMenuItemCopy();
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
expect(item.getData(localizations), const IOSSystemContextMenuItemDataCopy());
},
);
test(
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemCut',
() {
const IOSSystemContextMenuItemCut item = IOSSystemContextMenuItemCut();
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
expect(item.getData(localizations), const IOSSystemContextMenuItemDataCut());
},
);
test(
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemPaste',
() {
const IOSSystemContextMenuItemPaste item = IOSSystemContextMenuItemPaste();
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
expect(item.getData(localizations), const IOSSystemContextMenuItemDataPaste());
},
);
test(
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemSelectAll',
() {
const IOSSystemContextMenuItemSelectAll item = IOSSystemContextMenuItemSelectAll();
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
expect(item.getData(localizations), const IOSSystemContextMenuItemDataSelectAll());
},
);
test(
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemLookUp',
() {
const IOSSystemContextMenuItemLookUp item = IOSSystemContextMenuItemLookUp();
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
expect(
item.getData(localizations),
IOSSystemContextMenuItemDataLookUp(title: localizations.lookUpButtonLabel),
);
},
);
test(
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemSearchWeb',
() {
const IOSSystemContextMenuItemSearchWeb item = IOSSystemContextMenuItemSearchWeb();
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
expect(
item.getData(localizations),
IOSSystemContextMenuItemDataSearchWeb(title: localizations.searchWebButtonLabel),
);
},
);
test(
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemShare',
() {
const IOSSystemContextMenuItemShare item = IOSSystemContextMenuItemShare();
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
expect(
item.getData(localizations),
IOSSystemContextMenuItemDataShare(title: localizations.shareButtonLabel),
);
},
);
test(
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemLiveText',
() {
const IOSSystemContextMenuItemLiveText item = IOSSystemContextMenuItemLiveText();
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
final IOSSystemContextMenuItemData data = item.getData(localizations);
expect(data, isA<IOSSystemContextMenuItemDataLiveText>());
},
);
test('systemContextMenuItemDataFromJson handles Live Text', () {
final Map<String, dynamic> json = <String, dynamic>{'type': 'captureTextFromCamera'};
final IOSSystemContextMenuItemData item = systemContextMenuItemDataFromJson(json);
expect(item, isA<IOSSystemContextMenuItemDataLiveText>());
});
// Regression test for https://github.com/flutter/flutter/issues/169696.
test('IOSSystemContextMenuItemLookUp debugFillProperties', () {
const String title = 'my title';
const IOSSystemContextMenuItemLookUp item = IOSSystemContextMenuItemLookUp(title: title);
final List<DiagnosticsNode> diagnosticsNodes = item.toDiagnosticsNode().getProperties();
expect(diagnosticsNodes, hasLength(1));
expect(diagnosticsNodes.first.name, 'title');
expect(diagnosticsNodes.first.value, title);
});
// Regression test for https://github.com/flutter/flutter/issues/169696.
test('IOSSystemContextMenuItemSearchWeb debugFillProperties', () {
const String title = 'my title';
const IOSSystemContextMenuItemSearchWeb item = IOSSystemContextMenuItemSearchWeb(title: title);
final List<DiagnosticsNode> diagnosticsNodes = item.toDiagnosticsNode().getProperties();
expect(diagnosticsNodes, hasLength(1));
expect(diagnosticsNodes.first.name, 'title');
expect(diagnosticsNodes.first.value, title);
});
// Regression test for https://github.com/flutter/flutter/issues/169696.
test('IOSSystemContextMenuItemShare debugFillProperties', () {
const String title = 'my title';
const IOSSystemContextMenuItemShare item = IOSSystemContextMenuItemShare(title: title);
final List<DiagnosticsNode> diagnosticsNodes = item.toDiagnosticsNode().getProperties();
expect(diagnosticsNodes, hasLength(1));
expect(diagnosticsNodes.first.name, 'title');
expect(diagnosticsNodes.first.value, title);
});
}