mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add fake keyboard key generation to the testing framework (#40706)
There were four or five different implementations in various tests for sendFakeKeyEvent, which roughly all did the same thing. I was going to add yet another one, and decided that it needed to be generalized and centralized. This replaces those instances with something that just takes a LogicalKeyboardKey so that it's self-documenting, and can be used with multiple platforms. This adds two functions to widget tester: sendKeyDownEvent and sendKeyUpEvent which simulate key up/down from a physical keyboard. It also adds global functions simulateKeyDownEvent and simulateKeyUpEvent that can be called without a widget tester. All are async functions protected by the async guard.
This commit is contained in:
parent
4815b26d71
commit
0e6cb28dbe
@ -17,6 +17,7 @@ export 'src/services/clipboard.dart';
|
||||
export 'src/services/font_loader.dart';
|
||||
export 'src/services/haptic_feedback.dart';
|
||||
export 'src/services/keyboard_key.dart';
|
||||
export 'src/services/keyboard_maps.dart';
|
||||
export 'src/services/message_codec.dart';
|
||||
export 'src/services/message_codecs.dart';
|
||||
export 'src/services/platform_channel.dart';
|
||||
|
||||
@ -512,4 +512,12 @@ class RawKeyboard {
|
||||
Set<LogicalKeyboardKey> get keysPressed {
|
||||
return _keysPressed.toSet();
|
||||
}
|
||||
|
||||
/// Clears the list of keys returned from [keysPressed].
|
||||
///
|
||||
/// This is used by the testing framework to make sure tests are hermetic.
|
||||
@visibleForTesting
|
||||
void clearKeysPressed() {
|
||||
_keysPressed.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3531,30 +3531,6 @@ void main() {
|
||||
expect(focusNode2.hasPrimaryFocus, isFalse);
|
||||
});
|
||||
|
||||
void sendFakeKeyEvent(Map<String, dynamic> data) {
|
||||
ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
||||
SystemChannels.keyEvent.name,
|
||||
SystemChannels.keyEvent.codec.encodeMessage(data),
|
||||
(ByteData data) { },
|
||||
);
|
||||
}
|
||||
|
||||
void sendKeyEventWithCode(int code, bool down, bool shiftDown, bool ctrlDown) {
|
||||
|
||||
int metaState = shiftDown ? 1 : 0;
|
||||
if (ctrlDown)
|
||||
metaState |= 1 << 12;
|
||||
|
||||
sendFakeKeyEvent(<String, dynamic>{
|
||||
'type': down ? 'keydown' : 'keyup',
|
||||
'keymap': 'android',
|
||||
'keyCode': code,
|
||||
'hidUsage': 0x04,
|
||||
'codePoint': 0x64,
|
||||
'metaState': metaState,
|
||||
});
|
||||
}
|
||||
|
||||
group('Keyboard Tests', () {
|
||||
TextEditingController controller;
|
||||
|
||||
@ -3595,7 +3571,8 @@ void main() {
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown, SHIFT_ON
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowLeft);
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 1);
|
||||
});
|
||||
|
||||
@ -3611,7 +3588,8 @@ void main() {
|
||||
));
|
||||
await tester.pump();
|
||||
|
||||
sendKeyEventWithCode(22, true, true, false);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 1);
|
||||
});
|
||||
@ -3625,8 +3603,9 @@ void main() {
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, true, true, true); // RIGHT_ARROW keydown SHIFT_ON, CONTROL_ON
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.control);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 5);
|
||||
@ -3643,14 +3622,17 @@ void main() {
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
sendKeyEventWithCode(19, true, true, false); // UP_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 11);
|
||||
|
||||
sendKeyEventWithCode(19, false, true, false); // UP_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(20, true, true, false); // DOWN_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 0);
|
||||
@ -3666,42 +3648,47 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, false, false); // RIGHT_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
sendKeyEventWithCode(20, true, true, false); // DOWN_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(20, false, true, false); // DOWN_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 12);
|
||||
|
||||
sendKeyEventWithCode(20, true, true, false); // DOWN_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(20, false, true, false); // DOWN_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 32);
|
||||
|
||||
sendKeyEventWithCode(19, true, true, false); // UP_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(19, false, true, false); // UP_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 12);
|
||||
|
||||
sendKeyEventWithCode(19, true, true, false); // UP_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(19, false, true, false); // UP_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 0);
|
||||
|
||||
sendKeyEventWithCode(19, true, true, false); // UP_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(19, false, true, false); // UP_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 5);
|
||||
@ -3722,17 +3709,12 @@ void main() {
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown, SHIFT_ON
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowLeft);
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 1);
|
||||
});
|
||||
});
|
||||
|
||||
const int _kXKeyCode = 52;
|
||||
const int _kCKeyCode = 31;
|
||||
const int _kVKeyCode = 50;
|
||||
const int _kAKeyCode = 29;
|
||||
const int _kDelKeyCode = 112;
|
||||
|
||||
testWidgets('Copy paste test', (WidgetTester tester) async {
|
||||
final FocusNode focusNode = FocusNode();
|
||||
final TextEditingController controller = TextEditingController();
|
||||
@ -3774,32 +3756,32 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select the first 5 characters
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, true, false); // RIGHT_ARROW keydown shift
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
|
||||
// Copy them
|
||||
sendKeyEventWithCode(_kCKeyCode, true, false, true); // keydown control
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyC);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(_kCKeyCode, false, false, false); // keyup control
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(clipboardContent, 'a big');
|
||||
|
||||
sendKeyEventWithCode(22, true, false, false); // RIGHT_ARROW keydown
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Paste them
|
||||
sendKeyEventWithCode(_kVKeyCode, true, false, true); // Control V keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyV);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
sendKeyEventWithCode(_kVKeyCode, false, false, false); // Control V keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.keyV);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const String expected = 'a biga big house\njumped over a mouse';
|
||||
@ -3847,33 +3829,34 @@ void main() {
|
||||
|
||||
// Select the first 5 characters
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, true, false); // RIGHT_ARROW keydown shift
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
// Cut them
|
||||
sendKeyEventWithCode(_kXKeyCode, true, false, true); // keydown control X
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyX);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(_kXKeyCode, false, false, false); // keyup control X
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(clipboardContent, 'a big');
|
||||
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, false, false); // RIGHT_ARROW keydown
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
// Paste them
|
||||
sendKeyEventWithCode(_kVKeyCode, true, false, true); // Control V keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyV);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
sendKeyEventWithCode(_kVKeyCode, false, false, false); // Control V keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.keyV);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const String expected = ' housa bige\njumped over a mouse';
|
||||
@ -3911,17 +3894,18 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select All
|
||||
sendKeyEventWithCode(_kAKeyCode, true, false, true); // keydown control A
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.control);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyA);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(_kAKeyCode, false, false, true); // keyup control A
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.control);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Delete them
|
||||
sendKeyEventWithCode(_kDelKeyCode, true, false, false); // DEL keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.delete);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
sendKeyEventWithCode(_kDelKeyCode, false, false, false); // DEL keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.delete);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const String expected = '';
|
||||
@ -3960,24 +3944,20 @@ void main() {
|
||||
|
||||
// Delete
|
||||
for (int i = 0; i < 6; i += 1) {
|
||||
sendKeyEventWithCode(_kDelKeyCode, true, false, false); // keydown DEL
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(_kDelKeyCode, false, false, false); // keyup DEL
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.delete);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
const String expected = 'house\njumped over a mouse';
|
||||
expect(find.text(expected), findsOneWidget);
|
||||
|
||||
sendKeyEventWithCode(_kAKeyCode, true, false, true); // keydown control A
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.control);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyA);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(_kAKeyCode, false, false, true); // keyup control A
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.control);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
||||
sendKeyEventWithCode(_kDelKeyCode, true, false, false); // keydown DEL
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(_kDelKeyCode, false, false, false); // keyup DEL
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.delete);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const String expected2 = '';
|
||||
@ -4030,10 +4010,12 @@ void main() {
|
||||
await tester.tap(find.byType(TextField).first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
|
||||
expect(c1.selection.extentOffset - c1.selection.baseOffset, 5);
|
||||
|
||||
@ -4064,10 +4046,12 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
|
||||
expect(c1.selection.extentOffset - c1.selection.baseOffset, 10);
|
||||
});
|
||||
@ -4119,10 +4103,12 @@ void main() {
|
||||
await tester.tap(find.byType(TextField).first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
|
||||
expect(c1.selection.extentOffset - c1.selection.baseOffset, 5);
|
||||
expect(c2.selection.extentOffset - c2.selection.baseOffset, 0);
|
||||
@ -4135,10 +4121,12 @@ void main() {
|
||||
await tester.tap(find.byType(TextField).last);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
|
||||
expect(c1.selection.extentOffset - c1.selection.baseOffset, 0);
|
||||
expect(c2.selection.extentOffset - c2.selection.baseOffset, 5);
|
||||
|
||||
@ -2,22 +2,13 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void sendFakeKeyEvent(Map<String, dynamic> data) {
|
||||
ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
||||
SystemChannels.keyEvent.name,
|
||||
SystemChannels.keyEvent.codec.encodeMessage(data),
|
||||
(ByteData data) {},
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
final GlobalKey widgetKey = GlobalKey();
|
||||
Future<BuildContext> setupWidget(WidgetTester tester) async {
|
||||
@ -426,15 +417,9 @@ void main() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void sendEvent() {
|
||||
Future<void> sendEvent() async {
|
||||
receivedAnEvent.clear();
|
||||
sendFakeKeyEvent(<String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'fuchsia',
|
||||
'hidUsage': 0x04,
|
||||
'codePoint': 0x64,
|
||||
'modifiers': RawKeyEventDataFuchsia.modifierLeftMeta,
|
||||
});
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.metaLeft, platform: 'fuchsia');
|
||||
}
|
||||
|
||||
final BuildContext context = await setupWidget(tester);
|
||||
@ -465,21 +450,21 @@ void main() {
|
||||
child4.requestFocus();
|
||||
await tester.pump();
|
||||
shouldHandle.addAll(<FocusNode>{scope2, parent2, child2, child4});
|
||||
sendEvent();
|
||||
await sendEvent();
|
||||
expect(receivedAnEvent, equals(<FocusNode>{child4}));
|
||||
shouldHandle.remove(child4);
|
||||
sendEvent();
|
||||
await sendEvent();
|
||||
expect(receivedAnEvent, equals(<FocusNode>{parent2}));
|
||||
shouldHandle.remove(parent2);
|
||||
sendEvent();
|
||||
await sendEvent();
|
||||
expect(receivedAnEvent, equals(<FocusNode>{scope2}));
|
||||
shouldHandle.clear();
|
||||
sendEvent();
|
||||
await sendEvent();
|
||||
expect(receivedAnEvent, isEmpty);
|
||||
child1.requestFocus();
|
||||
await tester.pump();
|
||||
shouldHandle.addAll(<FocusNode>{scope2, parent2, child2, child4});
|
||||
sendEvent();
|
||||
await sendEvent();
|
||||
// Since none of the focused nodes handle this event, nothing should
|
||||
// receive it.
|
||||
expect(receivedAnEvent, isEmpty);
|
||||
@ -500,13 +485,7 @@ void main() {
|
||||
expect(lastMode, isNull);
|
||||
focusManager.highlightStrategy = FocusHighlightStrategy.automatic;
|
||||
expect(focusManager.highlightMode, equals(FocusHighlightMode.touch));
|
||||
sendFakeKeyEvent(<String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'fuchsia',
|
||||
'hidUsage': 0x04,
|
||||
'codePoint': 0x64,
|
||||
'modifiers': RawKeyEventDataFuchsia.modifierLeftMeta,
|
||||
});
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.metaLeft, platform: 'fuchsia');
|
||||
expect(callCount, equals(1));
|
||||
expect(lastMode, FocusHighlightMode.traditional);
|
||||
expect(focusManager.highlightMode, equals(FocusHighlightMode.traditional));
|
||||
|
||||
@ -2,20 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void sendFakeKeyEvent(Map<String, dynamic> data) {
|
||||
ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
||||
SystemChannels.keyEvent.name,
|
||||
SystemChannels.keyEvent.codec.encodeMessage(data),
|
||||
(ByteData data) {},
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('Can dispose without keyboard', (WidgetTester tester) async {
|
||||
final FocusNode focusNode = FocusNode();
|
||||
@ -40,21 +30,15 @@ void main() {
|
||||
focusNode.requestFocus();
|
||||
await tester.idle();
|
||||
|
||||
sendFakeKeyEvent(<String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'fuchsia',
|
||||
'hidUsage': 0x04,
|
||||
'codePoint': 0x64,
|
||||
'modifiers': RawKeyEventDataFuchsia.modifierLeftMeta,
|
||||
});
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.metaLeft, platform: 'fuchsia');
|
||||
await tester.idle();
|
||||
|
||||
expect(events.length, 1);
|
||||
expect(events.length, 2);
|
||||
expect(events[0].runtimeType, equals(RawKeyDownEvent));
|
||||
expect(events[0].data.runtimeType, equals(RawKeyEventDataFuchsia));
|
||||
final RawKeyEventDataFuchsia typedData = events[0].data;
|
||||
expect(typedData.hidUsage, 0x04);
|
||||
expect(typedData.codePoint, 0x64);
|
||||
expect(typedData.hidUsage, 0x700e3);
|
||||
expect(typedData.codePoint, 0x0);
|
||||
expect(typedData.modifiers, RawKeyEventDataFuchsia.modifierLeftMeta);
|
||||
expect(typedData.isModifierPressed(ModifierKey.metaModifier, side: KeyboardSide.left), isTrue);
|
||||
|
||||
@ -78,27 +62,15 @@ void main() {
|
||||
focusNode.requestFocus();
|
||||
await tester.idle();
|
||||
|
||||
sendFakeKeyEvent(<String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'fuchsia',
|
||||
'hidUsage': 0x04,
|
||||
'codePoint': 0x64,
|
||||
'modifiers': RawKeyEventDataFuchsia.modifierLeftMeta,
|
||||
});
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.metaLeft, platform: 'fuchsia');
|
||||
await tester.idle();
|
||||
|
||||
expect(events.length, 1);
|
||||
expect(events.length, 2);
|
||||
events.clear();
|
||||
|
||||
await tester.pumpWidget(Container());
|
||||
|
||||
sendFakeKeyEvent(<String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'fuchsia',
|
||||
'hidUsage': 0x04,
|
||||
'codePoint': 0x64,
|
||||
'modifiers': RawKeyEventDataFuchsia.modifierLeftMeta,
|
||||
});
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.metaLeft, platform: 'fuchsia');
|
||||
|
||||
await tester.idle();
|
||||
|
||||
|
||||
@ -1247,30 +1247,6 @@ void main() {
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
void sendFakeKeyEvent(Map<String, dynamic> data) {
|
||||
ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
||||
SystemChannels.keyEvent.name,
|
||||
SystemChannels.keyEvent.codec.encodeMessage(data),
|
||||
(ByteData data) { },
|
||||
);
|
||||
}
|
||||
|
||||
void sendKeyEventWithCode(int code, bool down, bool shiftDown, bool ctrlDown) {
|
||||
|
||||
int metaState = shiftDown ? 1 : 0;
|
||||
if (ctrlDown)
|
||||
metaState |= 1 << 12;
|
||||
|
||||
sendFakeKeyEvent(<String, dynamic>{
|
||||
'type': down ? 'keydown' : 'keyup',
|
||||
'keymap': 'android',
|
||||
'keyCode': code,
|
||||
'hidUsage': 0x04,
|
||||
'codePoint': 0x64,
|
||||
'metaState': metaState,
|
||||
});
|
||||
}
|
||||
|
||||
group('Keyboard Tests', () {
|
||||
TextEditingController controller;
|
||||
|
||||
@ -1300,7 +1276,8 @@ void main() {
|
||||
testWidgets('Shift test 1', (WidgetTester tester) async {
|
||||
await setupWidget(tester, 'a big house');
|
||||
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown, SHIFT_ON
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowLeft);
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 1);
|
||||
});
|
||||
|
||||
@ -1310,7 +1287,8 @@ void main() {
|
||||
controller.selection = const TextSelection.collapsed(offset: 3);
|
||||
await tester.pump();
|
||||
|
||||
sendKeyEventWithCode(22, true, true, false);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 1);
|
||||
});
|
||||
@ -1318,7 +1296,9 @@ void main() {
|
||||
testWidgets('Control Shift test', (WidgetTester tester) async {
|
||||
await setupWidget(tester, 'their big house');
|
||||
|
||||
sendKeyEventWithCode(21, true, true, true); // LEFT_ARROW keydown SHIFT_ON, CONTROL_ON
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.control);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowLeft);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@ -1328,14 +1308,16 @@ void main() {
|
||||
testWidgets('Down and up test', (WidgetTester tester) async {
|
||||
await setupWidget(tester, 'a big house');
|
||||
|
||||
sendKeyEventWithCode(19, true, true, false); // UP_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 11);
|
||||
|
||||
sendKeyEventWithCode(19, false, true, false); // UP_ARROW keyup
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(20, true, true, false); // DOWN_ARROW keydown
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 0);
|
||||
@ -1348,51 +1330,51 @@ void main() {
|
||||
await tester.pump();
|
||||
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, false, false); // RIGHT_ARROW keydown
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
sendKeyEventWithCode(20, true, true, false); // DOWN_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(20, false, true, false); // DOWN_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 12);
|
||||
|
||||
sendKeyEventWithCode(20, true, true, false); // DOWN_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(20, false, true, false); // DOWN_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 32);
|
||||
|
||||
sendKeyEventWithCode(19, true, true, false); // UP_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(19, false, true, false); // UP_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 12);
|
||||
|
||||
sendKeyEventWithCode(19, true, true, false); // UP_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(19, false, true, false); // UP_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 0);
|
||||
|
||||
sendKeyEventWithCode(19, true, true, false); // UP_ARROW keydown
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(19, false, true, false); // UP_ARROW keyup
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.extentOffset - controller.selection.baseOffset, 5);
|
||||
});
|
||||
});
|
||||
|
||||
const int _kCKeyCode = 31;
|
||||
const int _kAKeyCode = 29;
|
||||
|
||||
testWidgets('Copy test', (WidgetTester tester) async {
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
||||
@ -1432,24 +1414,23 @@ void main() {
|
||||
await tester.pump();
|
||||
|
||||
// Select the first 5 characters
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(22, true, true, false); // RIGHT_ARROW keydown shift
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
|
||||
// Copy them
|
||||
sendKeyEventWithCode(_kCKeyCode, true, false, true); // keydown control
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(_kCKeyCode, false, false, false); // keyup control
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyC);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.keyC);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(clipboardContent, 'a big');
|
||||
|
||||
sendKeyEventWithCode(22, true, false, false); // RIGHT_ARROW keydown
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
@ -1479,9 +1460,9 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select All
|
||||
sendKeyEventWithCode(_kAKeyCode, true, false, true); // keydown control A
|
||||
await tester.pumpAndSettle();
|
||||
sendKeyEventWithCode(_kAKeyCode, false, false, true); // keyup control A
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.control);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyA);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.control);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.baseOffset, 0);
|
||||
@ -1528,10 +1509,13 @@ void main() {
|
||||
await tester.tap(find.byType(EditableText).first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(c1.selection.extentOffset - c1.selection.baseOffset, 5);
|
||||
|
||||
@ -1562,10 +1546,12 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown
|
||||
await tester.pumpAndSettle();
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
editableTextWidget = tester.widget(find.byType(EditableText).last);
|
||||
c1 = editableTextWidget.controller;
|
||||
@ -1617,10 +1603,13 @@ void main() {
|
||||
await tester.tap(find.byType(SelectableText).first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(c1.selection.extentOffset - c1.selection.baseOffset, 5);
|
||||
expect(c2.selection.extentOffset - c2.selection.baseOffset, 0);
|
||||
@ -1628,10 +1617,13 @@ void main() {
|
||||
await tester.tap(find.byType(SelectableText).last);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
sendKeyEventWithCode(21, true, true, false); // LEFT_ARROW keydown
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(c1.selection.extentOffset - c1.selection.baseOffset, 0);
|
||||
expect(c2.selection.extentOffset - c2.selection.baseOffset, 5);
|
||||
|
||||
@ -6,18 +6,9 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/src/services/keyboard_key.dart';
|
||||
import 'package:flutter/src/services/keyboard_maps.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void sendFakeKeyEvent(Map<String, dynamic> data) {
|
||||
ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
||||
SystemChannels.keyEvent.name,
|
||||
SystemChannels.keyEvent.codec.encodeMessage(data),
|
||||
(ByteData data) {},
|
||||
);
|
||||
}
|
||||
|
||||
typedef PostInvokeCallback = void Function({Action action, Intent intent, FocusNode focusNode, ActionDispatcher dispatcher});
|
||||
|
||||
class TestAction extends CallbackAction {
|
||||
@ -74,34 +65,6 @@ class TestShortcutManager extends ShortcutManager {
|
||||
}
|
||||
}
|
||||
|
||||
void testKeypress(LogicalKeyboardKey key) {
|
||||
assert(key.debugName != null);
|
||||
int keyCode;
|
||||
kAndroidToLogicalKey.forEach((int code, LogicalKeyboardKey codeKey) {
|
||||
if (key == codeKey) {
|
||||
keyCode = code;
|
||||
}
|
||||
});
|
||||
assert(keyCode != null, 'Key $key not found in Android key map');
|
||||
int scanCode;
|
||||
kAndroidToPhysicalKey.forEach((int code, PhysicalKeyboardKey codeKey) {
|
||||
if (key.debugName == codeKey.debugName) {
|
||||
scanCode = code;
|
||||
}
|
||||
});
|
||||
assert(scanCode != null, 'Physical key for $key not found in Android key map');
|
||||
sendFakeKeyEvent(<String, dynamic>{
|
||||
'type': 'keydown',
|
||||
'keymap': 'android',
|
||||
'keyCode': keyCode,
|
||||
'plainCodePoint': 0,
|
||||
'codePoint': 0,
|
||||
'character': null,
|
||||
'scanCode': scanCode,
|
||||
'metaState': 0,
|
||||
});
|
||||
}
|
||||
|
||||
void main() {
|
||||
group(LogicalKeySet, () {
|
||||
test('$LogicalKeySet passes parameters correctly.', () {
|
||||
@ -248,7 +211,7 @@ void main() {
|
||||
);
|
||||
await tester.pump();
|
||||
expect(Shortcuts.of(containerKey.currentContext), isNotNull);
|
||||
testKeypress(LogicalKeyboardKey.shiftLeft);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shiftLeft);
|
||||
expect(invoked, isTrue);
|
||||
expect(pressedKeys, equals(<LogicalKeyboardKey>[LogicalKeyboardKey.shiftLeft]));
|
||||
});
|
||||
@ -284,7 +247,7 @@ void main() {
|
||||
);
|
||||
await tester.pump();
|
||||
expect(Shortcuts.of(containerKey.currentContext), isNotNull);
|
||||
testKeypress(LogicalKeyboardKey.shiftLeft);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shiftLeft);
|
||||
expect(invoked, isTrue);
|
||||
expect(pressedKeys, equals(<LogicalKeyboardKey>[LogicalKeyboardKey.shiftLeft]));
|
||||
});
|
||||
|
||||
@ -51,6 +51,7 @@ export 'src/accessibility.dart';
|
||||
export 'src/all_elements.dart';
|
||||
export 'src/binding.dart';
|
||||
export 'src/controller.dart';
|
||||
export 'src/event_simulation.dart';
|
||||
export 'src/finders.dart';
|
||||
export 'src/goldens.dart';
|
||||
export 'src/matchers.dart';
|
||||
|
||||
@ -796,6 +796,11 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
_pendingExceptionDetails = null;
|
||||
_parentZone = null;
|
||||
buildOwner.focusManager = FocusManager();
|
||||
// Disabling the warning because @visibleForTesting doesn't take the testing
|
||||
// framework itself into account, but we don't want it visible outside of
|
||||
// tests.
|
||||
// ignore: invalid_use_of_visible_for_testing_member
|
||||
RawKeyboard.instance.clearKeysPressed();
|
||||
assert(!RendererBinding.instance.mouseTracker.mouseIsConnected,
|
||||
'The MouseTracker thinks that there is still a mouse connected, which indicates that a '
|
||||
'test has not removed the mouse pointer which it added. Call removePointer on the '
|
||||
|
||||
370
packages/flutter_test/lib/src/event_simulation.dart
Normal file
370
packages/flutter_test/lib/src/event_simulation.dart
Normal file
@ -0,0 +1,370 @@
|
||||
// Copyright 2019 The Chromium 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:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'test_async_utils.dart';
|
||||
|
||||
// For the synonym keys, just return the left version of it.
|
||||
LogicalKeyboardKey _getSynonym(LogicalKeyboardKey origKey) {
|
||||
if (origKey == LogicalKeyboardKey.shift) {
|
||||
return LogicalKeyboardKey.shiftLeft;
|
||||
}
|
||||
if (origKey == LogicalKeyboardKey.alt) {
|
||||
return LogicalKeyboardKey.altLeft;
|
||||
}
|
||||
if (origKey == LogicalKeyboardKey.meta) {
|
||||
return LogicalKeyboardKey.metaLeft;
|
||||
}
|
||||
if (origKey == LogicalKeyboardKey.control) {
|
||||
return LogicalKeyboardKey.controlLeft;
|
||||
}
|
||||
return origKey;
|
||||
}
|
||||
|
||||
bool _osIsSupported(String platform) {
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
case 'fuchsia':
|
||||
case 'macos':
|
||||
case 'linux':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int _getScanCode(LogicalKeyboardKey key, String platform) {
|
||||
assert(_osIsSupported(platform), 'Platform $platform not supported for key simulation');
|
||||
int scanCode;
|
||||
Map<int, PhysicalKeyboardKey> map;
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
map = kAndroidToPhysicalKey;
|
||||
break;
|
||||
case 'fuchsia':
|
||||
map = kFuchsiaToPhysicalKey;
|
||||
break;
|
||||
case 'macos':
|
||||
map = kMacOsToPhysicalKey;
|
||||
break;
|
||||
case 'linux':
|
||||
map = kLinuxToPhysicalKey;
|
||||
break;
|
||||
}
|
||||
for (int code in map.keys) {
|
||||
if (key.debugName == map[code].debugName) {
|
||||
scanCode = code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return scanCode;
|
||||
}
|
||||
|
||||
int _getKeyCode(LogicalKeyboardKey key, String platform) {
|
||||
assert(_osIsSupported(platform), 'Platform $platform not supported for key simulation');
|
||||
int keyCode;
|
||||
Map<int, LogicalKeyboardKey> map;
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
map = kAndroidToLogicalKey;
|
||||
break;
|
||||
case 'fuchsia':
|
||||
map = kFuchsiaToLogicalKey;
|
||||
break;
|
||||
case 'macos':
|
||||
// macOS doesn't do key codes, just scan codes.
|
||||
return null;
|
||||
case 'linux':
|
||||
map = kGlfwToLogicalKey;
|
||||
break;
|
||||
}
|
||||
for (int code in map.keys) {
|
||||
if (key.debugName == map[code].debugName) {
|
||||
keyCode = code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return keyCode;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _getKeyData(LogicalKeyboardKey key, {String platform, bool isDown = true}) {
|
||||
assert(_osIsSupported(platform), 'Platform $platform not supported for key simulation');
|
||||
|
||||
key = _getSynonym(key);
|
||||
|
||||
assert(key.debugName != null);
|
||||
final int keyCode = platform == 'macos' ? -1 : _getKeyCode(key, platform);
|
||||
assert(platform == 'macos' || keyCode != null, 'Key $key not found in $platform keyCode map');
|
||||
final int scanCode = _getScanCode(key, platform);
|
||||
assert(scanCode != null, 'Physical key for $key not found in $platform scanCode map');
|
||||
|
||||
final Map<String, dynamic> result = <String, dynamic>{
|
||||
'type': isDown ? 'keydown' : 'keyup',
|
||||
'keymap': platform,
|
||||
'character': key.keyLabel,
|
||||
};
|
||||
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
result['keyCode'] = keyCode;
|
||||
result['codePoint'] = key.keyLabel?.codeUnitAt(0);
|
||||
result['scanCode'] = scanCode;
|
||||
result['metaState'] = _getAndroidModifierFlags(key, isDown);
|
||||
break;
|
||||
case 'fuchsia':
|
||||
result['hidUsage'] = key.keyId & LogicalKeyboardKey.hidPlane != 0 ? key.keyId & LogicalKeyboardKey.valueMask : null;
|
||||
result['codePoint'] = key.keyLabel?.codeUnitAt(0);
|
||||
result['modifiers'] = _getFuchsiaModifierFlags(key, isDown);
|
||||
break;
|
||||
case 'linux':
|
||||
result['toolkit'] = 'glfw';
|
||||
result['keyCode'] = keyCode;
|
||||
result['scanCode'] = scanCode;
|
||||
result['modifiers'] = _getGlfwModifierFlags(key, isDown);
|
||||
break;
|
||||
case 'macos':
|
||||
result['keyCode'] = scanCode;
|
||||
result['characters'] = key.keyLabel;
|
||||
result['charactersIgnoringModifiers'] = key.keyLabel;
|
||||
result['modifiers'] = _getMacOsModifierFlags(key, isDown);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int _getAndroidModifierFlags(LogicalKeyboardKey newKey, bool isDown) {
|
||||
int result = 0;
|
||||
final Set<LogicalKeyboardKey> pressed = RawKeyboard.instance.keysPressed;
|
||||
if (isDown) {
|
||||
pressed.add(newKey);
|
||||
} else {
|
||||
pressed.remove(newKey);
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shiftLeft)) {
|
||||
result |= RawKeyEventDataAndroid.modifierLeftShift | RawKeyEventDataAndroid.modifierShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shiftRight)) {
|
||||
result |= RawKeyEventDataAndroid.modifierRightShift | RawKeyEventDataAndroid.modifierShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.metaLeft)) {
|
||||
result |= RawKeyEventDataAndroid.modifierLeftMeta | RawKeyEventDataAndroid.modifierMeta;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.metaRight)) {
|
||||
result |= RawKeyEventDataAndroid.modifierRightMeta | RawKeyEventDataAndroid.modifierMeta;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.controlLeft)) {
|
||||
result |= RawKeyEventDataAndroid.modifierLeftControl | RawKeyEventDataAndroid.modifierControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.controlRight)) {
|
||||
result |= RawKeyEventDataAndroid.modifierRightControl | RawKeyEventDataAndroid.modifierControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.altLeft)) {
|
||||
result |= RawKeyEventDataAndroid.modifierLeftAlt | RawKeyEventDataAndroid.modifierAlt;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.altRight)) {
|
||||
result |= RawKeyEventDataAndroid.modifierRightAlt | RawKeyEventDataAndroid.modifierAlt;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.fn)) {
|
||||
result |= RawKeyEventDataAndroid.modifierFunction;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.scrollLock)) {
|
||||
result |= RawKeyEventDataAndroid.modifierScrollLock;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.numLock)) {
|
||||
result |= RawKeyEventDataAndroid.modifierNumLock;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.capsLock)) {
|
||||
result |= RawKeyEventDataAndroid.modifierCapsLock;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int _getGlfwModifierFlags(LogicalKeyboardKey newKey, bool isDown) {
|
||||
int result = 0;
|
||||
final Set<LogicalKeyboardKey> pressed = RawKeyboard.instance.keysPressed;
|
||||
if (isDown) {
|
||||
pressed.add(newKey);
|
||||
} else {
|
||||
pressed.remove(newKey);
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shiftLeft) || pressed.contains(LogicalKeyboardKey.shiftRight)) {
|
||||
result |= GLFWKeyHelper.modifierShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.metaLeft) || pressed.contains(LogicalKeyboardKey.metaRight)) {
|
||||
result |= GLFWKeyHelper.modifierMeta;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.controlLeft) || pressed.contains(LogicalKeyboardKey.controlRight)) {
|
||||
result |= GLFWKeyHelper.modifierControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.altLeft) || pressed.contains(LogicalKeyboardKey.altRight)) {
|
||||
result |= GLFWKeyHelper.modifierAlt;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.capsLock)) {
|
||||
result |= GLFWKeyHelper.modifierCapsLock;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int _getFuchsiaModifierFlags(LogicalKeyboardKey newKey, bool isDown) {
|
||||
int result = 0;
|
||||
final Set<LogicalKeyboardKey> pressed = RawKeyboard.instance.keysPressed;
|
||||
if (isDown) {
|
||||
pressed.add(newKey);
|
||||
} else {
|
||||
pressed.remove(newKey);
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shiftLeft)) {
|
||||
result |= RawKeyEventDataFuchsia.modifierLeftShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shiftRight)) {
|
||||
result |= RawKeyEventDataFuchsia.modifierRightShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.metaLeft)) {
|
||||
result |= RawKeyEventDataFuchsia.modifierLeftMeta;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.metaRight)) {
|
||||
result |= RawKeyEventDataFuchsia.modifierRightMeta;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.controlLeft)) {
|
||||
result |= RawKeyEventDataFuchsia.modifierLeftControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.controlRight)) {
|
||||
result |= RawKeyEventDataFuchsia.modifierRightControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.altLeft)) {
|
||||
result |= RawKeyEventDataFuchsia.modifierLeftAlt;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.altRight)) {
|
||||
result |= RawKeyEventDataFuchsia.modifierRightAlt;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.capsLock)) {
|
||||
result |= RawKeyEventDataFuchsia.modifierCapsLock;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int _getMacOsModifierFlags(LogicalKeyboardKey newKey, bool isDown) {
|
||||
int result = 0;
|
||||
final Set<LogicalKeyboardKey> pressed = RawKeyboard.instance.keysPressed;
|
||||
if (isDown) {
|
||||
pressed.add(newKey);
|
||||
} else {
|
||||
pressed.remove(newKey);
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shiftLeft)) {
|
||||
result |= RawKeyEventDataMacOs.modifierLeftShift | RawKeyEventDataMacOs.modifierShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.shiftRight)) {
|
||||
result |= RawKeyEventDataMacOs.modifierRightShift | RawKeyEventDataMacOs.modifierShift;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.metaLeft)) {
|
||||
result |= RawKeyEventDataMacOs.modifierLeftCommand | RawKeyEventDataMacOs.modifierCommand;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.metaRight)) {
|
||||
result |= RawKeyEventDataMacOs.modifierRightCommand | RawKeyEventDataMacOs.modifierCommand;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.controlLeft)) {
|
||||
result |= RawKeyEventDataMacOs.modifierLeftControl | RawKeyEventDataMacOs.modifierControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.controlRight)) {
|
||||
result |= RawKeyEventDataMacOs.modifierRightControl | RawKeyEventDataMacOs.modifierControl;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.altLeft)) {
|
||||
result |= RawKeyEventDataMacOs.modifierLeftOption | RawKeyEventDataMacOs.modifierOption;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.altRight)) {
|
||||
result |= RawKeyEventDataMacOs.modifierRightOption | RawKeyEventDataMacOs.modifierOption;
|
||||
}
|
||||
final Set<LogicalKeyboardKey> functionKeys = <LogicalKeyboardKey>{
|
||||
LogicalKeyboardKey.f1,
|
||||
LogicalKeyboardKey.f2,
|
||||
LogicalKeyboardKey.f3,
|
||||
LogicalKeyboardKey.f4,
|
||||
LogicalKeyboardKey.f5,
|
||||
LogicalKeyboardKey.f6,
|
||||
LogicalKeyboardKey.f7,
|
||||
LogicalKeyboardKey.f8,
|
||||
LogicalKeyboardKey.f9,
|
||||
LogicalKeyboardKey.f10,
|
||||
LogicalKeyboardKey.f11,
|
||||
LogicalKeyboardKey.f12,
|
||||
LogicalKeyboardKey.f13,
|
||||
LogicalKeyboardKey.f14,
|
||||
LogicalKeyboardKey.f15,
|
||||
LogicalKeyboardKey.f16,
|
||||
LogicalKeyboardKey.f17,
|
||||
LogicalKeyboardKey.f18,
|
||||
LogicalKeyboardKey.f19,
|
||||
LogicalKeyboardKey.f20,
|
||||
LogicalKeyboardKey.f21,
|
||||
};
|
||||
if (pressed.intersection(functionKeys).isNotEmpty) {
|
||||
result |= RawKeyEventDataMacOs.modifierFunction;
|
||||
}
|
||||
if (pressed.intersection(kMacOsNumPadMap.values.toSet()).isNotEmpty) {
|
||||
result |= RawKeyEventDataMacOs.modifierNumericPad;
|
||||
}
|
||||
if (pressed.contains(LogicalKeyboardKey.capsLock)) {
|
||||
result |= RawKeyEventDataMacOs.modifierCapsLock;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Simulates sending a hardware key down event through the system channel.
|
||||
///
|
||||
/// This only simulates key presses coming from a physical keyboard, not from a
|
||||
/// soft keyboard.
|
||||
///
|
||||
/// Specify `platform` as one of the platforms allowed in
|
||||
/// [Platform.operatingSystem] to make the event appear to be from that type of
|
||||
/// system. Defaults to the operating system that the test is running on. Some
|
||||
/// platforms (e.g. Windows, iOS) are not yet supported.
|
||||
///
|
||||
/// Keys that are down when the test completes are cleared after each test.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// - [simulateKeyUpEvent] to simulate the corresponding key up event.
|
||||
Future<void> simulateKeyDownEvent(LogicalKeyboardKey key, {String platform}) async {
|
||||
return TestAsyncUtils.guard<void>(() async {
|
||||
platform ??= Platform.operatingSystem;
|
||||
assert(_osIsSupported(platform), 'Platform $platform not supported for key simulation');
|
||||
|
||||
final Map<String, dynamic> data = _getKeyData(key, platform: platform, isDown: true);
|
||||
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
||||
SystemChannels.keyEvent.name,
|
||||
SystemChannels.keyEvent.codec.encodeMessage(data),
|
||||
(ByteData data) {},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Simulates sending a hardware key up event through the system channel.
|
||||
///
|
||||
/// This only simulates key presses coming from a physical keyboard, not from a
|
||||
/// soft keyboard.
|
||||
///
|
||||
/// Specify `platform` as one of the platforms allowed in
|
||||
/// [Platform.operatingSystem] to make the event appear to be from that type of
|
||||
/// system. Defaults to the operating system that the test is running on. Some
|
||||
/// platforms (e.g. Windows, iOS) are not yet supported.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// - [simulateKeyDownEvent] to simulate the corresponding key down event.
|
||||
Future<void> simulateKeyUpEvent(LogicalKeyboardKey key, {String platform}) async {
|
||||
return TestAsyncUtils.guard<void>(() async {
|
||||
platform ??= Platform.operatingSystem;
|
||||
assert(_osIsSupported(platform), 'Platform $platform not supported for key simulation');
|
||||
|
||||
final Map<String, dynamic> data = _getKeyData(key, platform: platform, isDown: false);
|
||||
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
|
||||
SystemChannels.keyEvent.name,
|
||||
SystemChannels.keyEvent.codec.encodeMessage(data),
|
||||
(ByteData data) {},
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -9,6 +9,7 @@ import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:test_api/test_api.dart' as test_package;
|
||||
@ -16,6 +17,7 @@ import 'package:test_api/test_api.dart' as test_package;
|
||||
import 'all_elements.dart';
|
||||
import 'binding.dart';
|
||||
import 'controller.dart';
|
||||
import 'event_simulation.dart';
|
||||
import 'finders.dart';
|
||||
import 'matchers.dart';
|
||||
import 'test_async_utils.dart';
|
||||
@ -719,6 +721,74 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
|
||||
});
|
||||
}
|
||||
|
||||
/// Simulates sending physical key down and up events through the system channel.
|
||||
///
|
||||
/// This only simulates key events coming from a physical keyboard, not from a
|
||||
/// soft keyboard.
|
||||
///
|
||||
/// Specify `platform` as one of the platforms allowed in
|
||||
/// [Platform.operatingSystem] to make the event appear to be from that type
|
||||
/// of system. Defaults to "android". Must not be null. Some platforms (e.g.
|
||||
/// Windows, iOS) are not yet supported.
|
||||
///
|
||||
/// Keys that are down when the test completes are cleared after each test.
|
||||
///
|
||||
/// This method sends both the key down and the key up events, to simulate a
|
||||
/// key press. To simulate individual down and/or up events, see
|
||||
/// [sendKeyDownEvent] and [sendKeyUpEvent].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// - [sendKeyDownEvent] to simulate only a key down event.
|
||||
/// - [sendKeyUpEvent] to simulate only a key up event.
|
||||
Future<void> sendKeyEvent(LogicalKeyboardKey key, { String platform = 'android' }) async {
|
||||
assert(platform != null);
|
||||
await simulateKeyDownEvent(key, platform: platform);
|
||||
// Internally wrapped in async guard.
|
||||
return simulateKeyUpEvent(key, platform: platform);
|
||||
}
|
||||
|
||||
/// Simulates sending a physical key down event through the system channel.
|
||||
///
|
||||
/// This only simulates key down events coming from a physical keyboard, not
|
||||
/// from a soft keyboard.
|
||||
///
|
||||
/// Specify `platform` as one of the platforms allowed in
|
||||
/// [Platform.operatingSystem] to make the event appear to be from that type
|
||||
/// of system. Defaults to "android". Must not be null. Some platforms (e.g.
|
||||
/// Windows, iOS) are not yet supported.
|
||||
///
|
||||
/// Keys that are down when the test completes are cleared after each test.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// - [sendKeyUpEvent] to simulate the corresponding key up event.
|
||||
/// - [sendKeyEvent] to simulate both the key up and key down in the same call.
|
||||
Future<void> sendKeyDownEvent(LogicalKeyboardKey key, { String platform = 'android' }) async {
|
||||
assert(platform != null);
|
||||
// Internally wrapped in async guard.
|
||||
return simulateKeyDownEvent(key, platform: platform);
|
||||
}
|
||||
|
||||
/// Simulates sending a physical key up event through the system channel.
|
||||
///
|
||||
/// This only simulates key up events coming from a physical keyboard,
|
||||
/// not from a soft keyboard.
|
||||
///
|
||||
/// Specify `platform` as one of the platforms allowed in
|
||||
/// [Platform.operatingSystem] to make the event appear to be from that type
|
||||
/// of system. Defaults to "android". May not be null.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// - [sendKeyDownEvent] to simulate the corresponding key down event.
|
||||
/// - [sendKeyEvent] to simulate both the key up and key down in the same call.
|
||||
Future<void> sendKeyUpEvent(LogicalKeyboardKey key, { String platform = 'android' }) async {
|
||||
assert(platform != null);
|
||||
// Internally wrapped in async guard.
|
||||
return simulateKeyUpEvent(key, platform: platform);
|
||||
}
|
||||
|
||||
/// Makes an effort to dismiss the current page with a Material [Scaffold] or
|
||||
/// a [CupertinoPageScaffold].
|
||||
///
|
||||
|
||||
55
packages/flutter_test/test/event_simulation_test.dart
Normal file
55
packages/flutter_test/test/event_simulation_test.dart
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2019 The Chromium 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/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
const List<String> platforms = <String>['linux', 'macos', 'android', 'fuchsia'];
|
||||
|
||||
void main() {
|
||||
testWidgets('simulates keyboard events', (WidgetTester tester) async {
|
||||
final List<RawKeyEvent> events = <RawKeyEvent>[];
|
||||
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
||||
await tester.pumpWidget(
|
||||
RawKeyboardListener(
|
||||
focusNode: focusNode,
|
||||
onKey: events.add,
|
||||
child: Container(),
|
||||
),
|
||||
);
|
||||
|
||||
focusNode.requestFocus();
|
||||
await tester.idle();
|
||||
|
||||
for (String platform in platforms) {
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.shiftLeft, platform: platform);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.shift, platform: platform);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyA, platform: platform);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.keyA, platform: platform);
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.numpad1, platform: platform);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.numpad1, platform: platform);
|
||||
await tester.idle();
|
||||
|
||||
expect(events.length, 8);
|
||||
for (int i = 0; i < events.length; ++i) {
|
||||
final bool isEven = i % 2 == 0;
|
||||
if (isEven) {
|
||||
expect(events[i].runtimeType, equals(RawKeyDownEvent));
|
||||
} else {
|
||||
expect(events[i].runtimeType, equals(RawKeyUpEvent));
|
||||
}
|
||||
if (i < 4) {
|
||||
expect(events[i].data.isModifierPressed(ModifierKey.shiftModifier, side: KeyboardSide.left), equals(isEven));
|
||||
}
|
||||
}
|
||||
events.clear();
|
||||
}
|
||||
|
||||
await tester.pumpWidget(Container());
|
||||
focusNode.dispose();
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user