blob: 8844b9505147496cdb9b505f01ea62a383d5c349 [file] [log] [blame] [edit]
// 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/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
const List<String> platforms = <String>['linux', 'macos', 'android', 'fuchsia'];
void _verifyKeyEvent<T extends KeyEvent>(KeyEvent event, PhysicalKeyboardKey physical, LogicalKeyboardKey logical, String? character) {
expect(event, isA<T>());
expect(event.physicalKey, physical);
expect(event.logicalKey, logical);
expect(event.character, character);
expect(event.synthesized, false);
}
void _verifyRawKeyEvent<T extends RawKeyEvent>(RawKeyEvent event, PhysicalKeyboardKey physical, LogicalKeyboardKey logical, String? character) {
expect(event, isA<T>());
expect(event.physicalKey, physical);
expect(event.logicalKey, logical);
expect(event.character, character);
}
Future<void> _shouldThrow<T extends Error>(AsyncValueGetter<void> func) async {
bool hasError = false;
try {
await func();
} catch (e) {
expect(e, isA<T>());
hasError = true;
} finally {
expect(hasError, true);
}
}
void main() {
testWidgets('simulates keyboard events (RawEvent)', (WidgetTester tester) async {
debugKeyEventSimulatorTransitModeOverride = KeyDataTransitMode.rawKeyData;
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 (final 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.isEven;
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();
debugKeyEventSimulatorTransitModeOverride = null;
});
testWidgets('simulates keyboard events (KeyData then RawKeyEvent)', (WidgetTester tester) async {
debugKeyEventSimulatorTransitModeOverride = KeyDataTransitMode.keyDataThenRawKeyData;
final List<KeyEvent> events = <KeyEvent>[];
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(
KeyboardListener(
focusNode: focusNode,
onKeyEvent: events.add,
child: Container(),
),
);
focusNode.requestFocus();
await tester.idle();
// Key press shiftLeft
await tester.sendKeyDownEvent(LogicalKeyboardKey.shiftLeft);
expect(events.length, 1);
_verifyKeyEvent<KeyDownEvent>(events[0], PhysicalKeyboardKey.shiftLeft, LogicalKeyboardKey.shiftLeft, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.shiftLeft}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.shiftLeft}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyRepeatEvent(LogicalKeyboardKey.shiftLeft);
expect(events.length, 1);
_verifyKeyEvent<KeyRepeatEvent>(events[0], PhysicalKeyboardKey.shiftLeft, LogicalKeyboardKey.shiftLeft, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.shiftLeft}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.shiftLeft}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyUpEvent(LogicalKeyboardKey.shiftLeft);
expect(events.length, 1);
_verifyKeyEvent<KeyUpEvent>(events[0], PhysicalKeyboardKey.shiftLeft, LogicalKeyboardKey.shiftLeft, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.logicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
// Key press keyA
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyA);
expect(events.length, 1);
_verifyKeyEvent<KeyDownEvent>(events[0], PhysicalKeyboardKey.keyA, LogicalKeyboardKey.keyA, 'a');
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.keyA}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.keyA}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyRepeatEvent(LogicalKeyboardKey.keyA);
_verifyKeyEvent<KeyRepeatEvent>(events[0], PhysicalKeyboardKey.keyA, LogicalKeyboardKey.keyA, 'a');
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.keyA}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.keyA}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyUpEvent(LogicalKeyboardKey.keyA);
_verifyKeyEvent<KeyUpEvent>(events[0], PhysicalKeyboardKey.keyA, LogicalKeyboardKey.keyA, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.logicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
// Key press keyA with physical keyQ
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyQ);
expect(events.length, 1);
_verifyKeyEvent<KeyDownEvent>(events[0], PhysicalKeyboardKey.keyQ, LogicalKeyboardKey.keyA, 'a');
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.keyQ}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.keyA}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyRepeatEvent(LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyQ);
_verifyKeyEvent<KeyRepeatEvent>(events[0], PhysicalKeyboardKey.keyQ, LogicalKeyboardKey.keyA, 'a');
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.keyQ}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.keyA}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyUpEvent(LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyQ);
_verifyKeyEvent<KeyUpEvent>(events[0], PhysicalKeyboardKey.keyQ, LogicalKeyboardKey.keyA, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.logicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
// Key press numpad1
await tester.sendKeyDownEvent(LogicalKeyboardKey.numpad1);
_verifyKeyEvent<KeyDownEvent>(events[0], PhysicalKeyboardKey.numpad1, LogicalKeyboardKey.numpad1, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.numpad1}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.numpad1}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyRepeatEvent(LogicalKeyboardKey.numpad1);
_verifyKeyEvent<KeyRepeatEvent>(events[0], PhysicalKeyboardKey.numpad1, LogicalKeyboardKey.numpad1, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.numpad1}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.numpad1}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyUpEvent(LogicalKeyboardKey.numpad1);
_verifyKeyEvent<KeyUpEvent>(events[0], PhysicalKeyboardKey.numpad1, LogicalKeyboardKey.numpad1, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.logicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
// Key press numLock (1st time)
await tester.sendKeyDownEvent(LogicalKeyboardKey.numLock);
_verifyKeyEvent<KeyDownEvent>(events[0], PhysicalKeyboardKey.numLock, LogicalKeyboardKey.numLock, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.numLock}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.numLock}));
expect(HardwareKeyboard.instance.lockModesEnabled, equals(<KeyboardLockMode>{KeyboardLockMode.numLock}));
events.clear();
await tester.sendKeyRepeatEvent(LogicalKeyboardKey.numLock);
_verifyKeyEvent<KeyRepeatEvent>(events[0], PhysicalKeyboardKey.numLock, LogicalKeyboardKey.numLock, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.numLock}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.numLock}));
expect(HardwareKeyboard.instance.lockModesEnabled, equals(<KeyboardLockMode>{KeyboardLockMode.numLock}));
events.clear();
await tester.sendKeyUpEvent(LogicalKeyboardKey.numLock);
_verifyKeyEvent<KeyUpEvent>(events[0], PhysicalKeyboardKey.numLock, LogicalKeyboardKey.numLock, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.logicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.lockModesEnabled, equals(<KeyboardLockMode>{KeyboardLockMode.numLock}));
events.clear();
// Key press numLock (2nd time)
await tester.sendKeyDownEvent(LogicalKeyboardKey.numLock);
_verifyKeyEvent<KeyDownEvent>(events[0], PhysicalKeyboardKey.numLock, LogicalKeyboardKey.numLock, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.numLock}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.numLock}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyRepeatEvent(LogicalKeyboardKey.numLock);
_verifyKeyEvent<KeyRepeatEvent>(events[0], PhysicalKeyboardKey.numLock, LogicalKeyboardKey.numLock, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, equals(<PhysicalKeyboardKey>{PhysicalKeyboardKey.numLock}));
expect(HardwareKeyboard.instance.logicalKeysPressed, equals(<LogicalKeyboardKey>{LogicalKeyboardKey.numLock}));
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.sendKeyUpEvent(LogicalKeyboardKey.numLock);
_verifyKeyEvent<KeyUpEvent>(events[0], PhysicalKeyboardKey.numLock, LogicalKeyboardKey.numLock, null);
expect(HardwareKeyboard.instance.physicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.logicalKeysPressed, isEmpty);
expect(HardwareKeyboard.instance.lockModesEnabled, isEmpty);
events.clear();
await tester.idle();
await tester.pumpWidget(Container());
focusNode.dispose();
debugKeyEventSimulatorTransitModeOverride = null;
});
testWidgets('simulates using the correct transit mode: rawKeyData', (WidgetTester tester) async {
debugKeyEventSimulatorTransitModeOverride = KeyDataTransitMode.rawKeyData;
final List<Object> events = <Object>[];
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(
Focus(
focusNode: focusNode,
onKey: (FocusNode node, RawKeyEvent event) {
events.add(event);
return KeyEventResult.ignored;
},
onKeyEvent: (FocusNode node, KeyEvent event) {
events.add(event);
return KeyEventResult.ignored;
},
child: Container(),
),
);
focusNode.requestFocus();
await tester.idle();
// A (physical keyA, logical keyA) is pressed.
await simulateKeyDownEvent(LogicalKeyboardKey.keyA);
expect(events.length, 2);
expect(events[0], isA<KeyEvent>());
_verifyKeyEvent<KeyDownEvent>(events[0] as KeyEvent, PhysicalKeyboardKey.keyA, LogicalKeyboardKey.keyA, 'a');
expect(events[1], isA<RawKeyEvent>());
_verifyRawKeyEvent<RawKeyDownEvent>(events[1] as RawKeyEvent, PhysicalKeyboardKey.keyA, LogicalKeyboardKey.keyA, 'a');
events.clear();
// A (physical keyA, logical keyB) is released.
//
// Since this event was synthesized and regularized before being sent to
// HardwareKeyboard, this event will be accepted.
await simulateKeyUpEvent(LogicalKeyboardKey.keyB, physicalKey: PhysicalKeyboardKey.keyA);
expect(events.length, 2);
expect(events[0], isA<KeyEvent>());
_verifyKeyEvent<KeyUpEvent>(events[0] as KeyEvent, PhysicalKeyboardKey.keyA, LogicalKeyboardKey.keyA, null);
expect(events[1], isA<RawKeyEvent>());
_verifyRawKeyEvent<RawKeyUpEvent>(events[1] as RawKeyEvent, PhysicalKeyboardKey.keyA, LogicalKeyboardKey.keyB, null);
events.clear();
// Manually switch the transit mode to `keyDataThenRawKeyData`. This will
// never happen in real applications so the assertion error can verify that
// the transit mode is correctly applied.
debugKeyEventSimulatorTransitModeOverride = KeyDataTransitMode.keyDataThenRawKeyData;
await _shouldThrow<AssertionError>(() =>
simulateKeyUpEvent(LogicalKeyboardKey.keyB, physicalKey: PhysicalKeyboardKey.keyA));
debugKeyEventSimulatorTransitModeOverride = null;
});
testWidgets('simulates using the correct transit mode: keyDataThenRawKeyData', (WidgetTester tester) async {
debugKeyEventSimulatorTransitModeOverride = KeyDataTransitMode.keyDataThenRawKeyData;
final List<Object> events = <Object>[];
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(
Focus(
focusNode: focusNode,
onKey: (FocusNode node, RawKeyEvent event) {
events.add(event);
return KeyEventResult.ignored;
},
onKeyEvent: (FocusNode node, KeyEvent event) {
events.add(event);
return KeyEventResult.ignored;
},
child: Container(),
),
);
focusNode.requestFocus();
await tester.idle();
// A (physical keyA, logical keyA) is pressed.
await simulateKeyDownEvent(LogicalKeyboardKey.keyA);
expect(events.length, 2);
expect(events[0], isA<KeyEvent>());
_verifyKeyEvent<KeyDownEvent>(events[0] as KeyEvent, PhysicalKeyboardKey.keyA, LogicalKeyboardKey.keyA, 'a');
expect(events[1], isA<RawKeyEvent>());
_verifyRawKeyEvent<RawKeyDownEvent>(events[1] as RawKeyEvent, PhysicalKeyboardKey.keyA, LogicalKeyboardKey.keyA, 'a');
events.clear();
// A (physical keyA, logical keyB) is released.
//
// Since this event is transmitted to HardwareKeyboard as-is, it will be rejected due to
// inconsistent logical key. This does not indicate behavioral difference,
// since KeyData is will never send malformed data sequence in real applications.
await _shouldThrow<AssertionError>(() =>
simulateKeyUpEvent(LogicalKeyboardKey.keyB, physicalKey: PhysicalKeyboardKey.keyA));
debugKeyEventSimulatorTransitModeOverride = null;
});
}