Skip to content

Commit 8ade81f

Browse files
authored
[flutter_test] Change KeyEventSimulator default transit mode (#143847)
## Description This PRs changes the default value transit mode for key event simulation. The default transit mode for key event simulation is currently `KeyDataTransitMode.rawKeyData` while on the framework side `KeyDataTransitMode.keyDataThenRawKeyData` is the preferred transit mode. `KeyDataTransitMode.keyDataThenRawKeyData` is more accurate and can help detect issues. For instance the following test will fail with `KeyDataTransitMode.rawKeyData` because raw keyboard logic for modifier keys is less accurate: ```dart testWidgets('Press control left once', (WidgetTester tester) async { debugKeyEventSimulatorTransitModeOverride = KeyDataTransitMode.keyDataThenRawKeyData; final List<KeyEvent> events = <KeyEvent>[]; final FocusNode focusNode = FocusNode(); addTearDown(focusNode.dispose); await tester.pumpWidget( Focus( focusNode: focusNode, autofocus: true, onKeyEvent: (_, KeyEvent event) { events.add(event); return KeyEventResult.handled; }, child: Container(), ), ); await simulateKeyDownEvent(LogicalKeyboardKey.controlLeft); // This will fail when transit mode is KeyDataTransitMode.rawKeyData // because a down event for controlRight is synthesized. expect(events.length, 1); debugKeyEventSimulatorTransitModeOverride = null; }); ``` And the following this test is ok with `KeyDataTransitMode.rawKeyData` but rightly fails with `KeyDataTransitMode.keyDataThenRawKeyData`: ```dart testWidgets('Simulates consecutive key down events', (WidgetTester tester) async { debugKeyEventSimulatorTransitModeOverride = KeyDataTransitMode.rawKeyData; // Simulating several key down events without key up in between is tolerated // when transit mode is KeyDataTransitMode.rawKeyData, it will trigger an // assert on KeyDataTransitMode.keyDataThenRawKeyData. await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown); await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown); debugKeyEventSimulatorTransitModeOverride = null; }); ``` ## Related Issue Related to flutter/flutter#143845 ## Tests Adds two tests.
1 parent f677027 commit 8ade81f

File tree

6 files changed

+101
-39
lines changed

6 files changed

+101
-39
lines changed

packages/flutter/test/cupertino/text_field_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8507,14 +8507,12 @@ void main() {
85078507
expect(controller.selection.extentOffset, 20);
85088508

85098509
await tester.pump(kDoubleTapTimeout);
8510-
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
85118510
await tester.tapAt(textOffsetToPosition(tester, 23));
85128511
await tester.pumpAndSettle();
85138512
expect(controller.selection.baseOffset, 13);
85148513
expect(controller.selection.extentOffset, 23);
85158514

85168515
await tester.pump(kDoubleTapTimeout);
8517-
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
85188516
await tester.tapAt(textOffsetToPosition(tester, 4));
85198517
await tester.pumpAndSettle();
85208518
expect(controller.selection.baseOffset, 13);

packages/flutter/test/material/dropdown_menu_test.dart

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ void main() {
694694
await tester.tap(find.byType(DropdownMenu<TestMenu>));
695695
await tester.pump();
696696

697-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
697+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
698698
await tester.pumpAndSettle();
699699
Finder button0Material = find.descendant(
700700
of: find.widgetWithText(MenuItemButton, 'Item 0').last,
@@ -705,7 +705,7 @@ void main() {
705705
expect(item0material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
706706

707707
// Press down key one more time, the highlight should move to the next item.
708-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
708+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
709709
await tester.pumpAndSettle();
710710
final Finder button1Material = find.descendant(
711711
of: find.widgetWithText(MenuItemButton, 'Menu 1').last,
@@ -735,7 +735,7 @@ void main() {
735735
await tester.tap(find.byType(DropdownMenu<TestMenu>));
736736
await tester.pump();
737737

738-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
738+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
739739
await tester.pumpAndSettle();
740740
Finder button5Material = find.descendant(
741741
of: find.widgetWithText(MenuItemButton, 'Item 5').last,
@@ -746,7 +746,7 @@ void main() {
746746
expect(item5material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
747747

748748
// Press up key one more time, the highlight should move up to the item 4.
749-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
749+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
750750
await tester.pumpAndSettle();
751751
final Finder button4Material = find.descendant(
752752
of: find.widgetWithText(MenuItemButton, 'Item 4').last,
@@ -779,17 +779,17 @@ void main() {
779779
await tester.tap(find.byType(DropdownMenu<TestMenu>));
780780
await tester.pump();
781781

782-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
782+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
783783
await tester.pump();
784784
expect(find.widgetWithText(TextField, 'Item 0'), findsOneWidget);
785785

786786
// Press down key one more time to the next item.
787-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
787+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
788788
await tester.pump();
789789
expect(find.widgetWithText(TextField, 'Menu 1'), findsOneWidget);
790790

791791
// Press down to the next item.
792-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
792+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
793793
await tester.pump();
794794
expect(find.widgetWithText(TextField, 'Item 2'), findsOneWidget);
795795
}, variant: TargetPlatformVariant.desktop());
@@ -810,17 +810,17 @@ void main() {
810810
await tester.tap(find.byType(DropdownMenu<TestMenu>));
811811
await tester.pump();
812812

813-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
813+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
814814
await tester.pump();
815815
expect(find.widgetWithText(TextField, 'Item 5'), findsOneWidget);
816816

817817
// Press up key one more time to the upper item.
818-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
818+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
819819
await tester.pump();
820820
expect(find.widgetWithText(TextField, 'Item 4'), findsOneWidget);
821821

822822
// Press up to the upper item.
823-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
823+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
824824
await tester.pump();
825825
expect(find.widgetWithText(TextField, 'Item 3'), findsOneWidget);
826826
}, variant: TargetPlatformVariant.desktop());
@@ -849,7 +849,7 @@ void main() {
849849
await tester.tap(find.byType(DropdownMenu<TestMenu>));
850850
await tester.pumpAndSettle();
851851

852-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
852+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
853853
await tester.pumpAndSettle();
854854
final Finder button0Material = find.descendant(
855855
of: find.widgetWithText(MenuItemButton, 'Item 0').last,
@@ -859,7 +859,7 @@ void main() {
859859
expect(item0Material.color, themeData.colorScheme.onSurface.withOpacity(0.12)); // first item can be highlighted as it's enabled.
860860

861861
// Continue to press down key. Item 3 should be highlighted as Menu 1 and Item 2 are both disabled.
862-
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
862+
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
863863
await tester.pumpAndSettle();
864864
final Finder button3Material = find.descendant(
865865
of: find.widgetWithText(MenuItemButton, 'Item 3').last,
@@ -941,6 +941,7 @@ void main() {
941941

942942
// Press up to the upper item (Item 0).
943943
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
944+
await simulateKeyUpEvent(LogicalKeyboardKey.arrowUp);
944945
await tester.pumpAndSettle();
945946
expect(find.widgetWithText(TextField, 'Item 0'), findsOneWidget);
946947
final Finder button0Material = find.descendant(
@@ -952,6 +953,7 @@ void main() {
952953

953954
// Continue to move up to the last item (Item 5).
954955
await simulateKeyDownEvent(LogicalKeyboardKey.arrowUp);
956+
await simulateKeyUpEvent(LogicalKeyboardKey.arrowUp);
955957
await tester.pumpAndSettle();
956958
expect(find.widgetWithText(TextField, 'Item 5'), findsOneWidget);
957959
final Finder button5Material = find.descendant(

packages/flutter/test/material/text_field_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15933,14 +15933,12 @@ void main() {
1593315933
expect(controller.selection.extentOffset, 20);
1593415934

1593515935
await tester.pump(kDoubleTapTimeout);
15936-
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
1593715936
await tester.tapAt(textOffsetToPosition(tester, 23));
1593815937
await tester.pumpAndSettle();
1593915938
expect(controller.selection.baseOffset, 13);
1594015939
expect(controller.selection.extentOffset, 23);
1594115940

1594215941
await tester.pump(kDoubleTapTimeout);
15943-
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
1594415942
await tester.tapAt(textOffsetToPosition(tester, 4));
1594515943
await tester.pumpAndSettle();
1594615944
expect(controller.selection.baseOffset, 13);

packages/flutter/test/services/raw_keyboard_test.dart

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ void main() {
3030
await simulateKeyDownEvent(LogicalKeyboardKey.backquote, platform: platform);
3131
RawKeyboard.instance.removeListener(handleKey);
3232
}
33-
});
33+
}, variant: KeySimulatorTransitModeVariant.rawKeyData());
3434

3535
testWidgets('No character is produced for non-printables', (WidgetTester tester) async {
3636
for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia', 'windows', 'web', 'ios']) {
@@ -41,7 +41,7 @@ void main() {
4141
await simulateKeyDownEvent(LogicalKeyboardKey.shiftLeft, platform: platform);
4242
RawKeyboard.instance.removeListener(handleKey);
4343
}
44-
});
44+
}, variant: KeySimulatorTransitModeVariant.rawKeyData());
4545

4646
testWidgets('keysPressed is maintained', (WidgetTester tester) async {
4747
for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia', 'windows', 'ios']) {
@@ -147,7 +147,10 @@ void main() {
147147
expect(RawKeyboard.instance.keysPressed, isEmpty, reason: 'on $platform');
148148
}
149149
}
150-
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/61021
150+
},
151+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
152+
skip: isBrowser, // https://github.com/flutter/flutter/issues/61021
153+
);
151154

152155
testWidgets('keysPressed is correct when modifier is released before key', (WidgetTester tester) async {
153156
for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia', 'windows', 'ios']) {
@@ -198,7 +201,10 @@ void main() {
198201
await simulateKeyUpEvent(LogicalKeyboardKey.keyA, platform: platform, physicalKey: PhysicalKeyboardKey.keyA);
199202
expect(RawKeyboard.instance.keysPressed, isEmpty, reason: 'on $platform');
200203
}
201-
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/76741
204+
},
205+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
206+
skip: isBrowser, // https://github.com/flutter/flutter/issues/76741
207+
);
202208

203209
testWidgets('keysPressed modifiers are synchronized with key events on macOS', (WidgetTester tester) async {
204210
expect(RawKeyboard.instance.keysPressed, isEmpty);
@@ -222,7 +228,10 @@ void main() {
222228
<LogicalKeyboardKey>{LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.keyA},
223229
),
224230
);
225-
}, skip: isBrowser); // [intended] This is a macOS-specific test.
231+
},
232+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
233+
skip: isBrowser, // [intended] This is a macOS-specific test.
234+
);
226235

227236
testWidgets('keysPressed modifiers are synchronized with key events on iOS', (WidgetTester tester) async {
228237
expect(RawKeyboard.instance.keysPressed, isEmpty);
@@ -246,7 +255,10 @@ void main() {
246255
<LogicalKeyboardKey>{LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.keyA},
247256
),
248257
);
249-
}, skip: isBrowser); // [intended] This is an iOS-specific test.
258+
},
259+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
260+
skip: isBrowser, // [intended] This is a iOS-specific test.
261+
);
250262

251263
testWidgets('keysPressed modifiers are synchronized with key events on Windows', (WidgetTester tester) async {
252264
expect(RawKeyboard.instance.keysPressed, isEmpty);
@@ -270,7 +282,10 @@ void main() {
270282
<LogicalKeyboardKey>{LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.keyA},
271283
),
272284
);
273-
}, skip: isBrowser); // [intended] This is a Windows-specific test.
285+
},
286+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
287+
skip: isBrowser, // [intended] This is a Windows-specific test.
288+
);
274289

275290
testWidgets('keysPressed modifiers are synchronized with key events on android', (WidgetTester tester) async {
276291
expect(RawKeyboard.instance.keysPressed, isEmpty);
@@ -294,7 +309,10 @@ void main() {
294309
<LogicalKeyboardKey>{LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.keyA},
295310
),
296311
);
297-
}, skip: isBrowser); // [intended] This is an Android-specific test.
312+
},
313+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
314+
skip: isBrowser, // [intended] This is an Android-specific test.
315+
);
298316

299317
testWidgets('keysPressed modifiers are synchronized with key events on fuchsia', (WidgetTester tester) async {
300318
expect(RawKeyboard.instance.keysPressed, isEmpty);
@@ -318,7 +336,10 @@ void main() {
318336
<LogicalKeyboardKey>{LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.keyA},
319337
),
320338
);
321-
}, skip: isBrowser); // [intended] This is a Fuchsia-specific test.
339+
},
340+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
341+
skip: isBrowser, // [intended] This is a Fuchsia-specific test.
342+
);
322343

323344
testWidgets('keysPressed modifiers are synchronized with key events on Linux GLFW', (WidgetTester tester) async {
324345
expect(RawKeyboard.instance.keysPressed, isEmpty);
@@ -348,7 +369,10 @@ void main() {
348369
},
349370
),
350371
);
351-
}, skip: isBrowser); // [intended] This is a GLFW-specific test.
372+
},
373+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
374+
skip: isBrowser, // [intended] This is a GLFW-specific test.
375+
);
352376

353377
Future<void> simulateGTKKeyEvent(bool keyDown, int scancode, int keycode, int modifiers) async {
354378
final Map<String, dynamic> data = <String, dynamic>{
@@ -397,7 +421,10 @@ void main() {
397421
},
398422
),
399423
);
400-
}, skip: isBrowser); // [intended] This is a GTK-specific test.
424+
},
425+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
426+
skip: isBrowser, // [intended] This is a GTK-specific test.
427+
);
401428

402429
// Regression test for https://github.com/flutter/flutter/issues/114591 .
403430
//
@@ -414,7 +441,10 @@ void main() {
414441
},
415442
),
416443
);
417-
}, skip: isBrowser); // [intended] This is a GTK-specific test.
444+
},
445+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
446+
skip: isBrowser, // [intended] This is a GTK-specific test.
447+
);
418448

419449
// Regression test for https://github.com/flutter/flutter/issues/114591 .
420450
//
@@ -447,7 +477,10 @@ void main() {
447477
},
448478
),
449479
);
450-
}, skip: !isBrowser); // [intended] This is a Browser-specific test.
480+
},
481+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
482+
skip: !isBrowser, // [intended] This is a Browser-specific test.
483+
);
451484

452485
testWidgets('keysPressed modifiers are synchronized with key events on web', (WidgetTester tester) async {
453486
expect(RawKeyboard.instance.keysPressed, isEmpty);
@@ -572,7 +605,10 @@ void main() {
572605
},
573606
),
574607
);
575-
}, skip: isBrowser); // [intended] This is an Android-specific test.
608+
},
609+
variant: KeySimulatorTransitModeVariant.rawKeyData(),
610+
skip: isBrowser, // [intended] This is a Android-specific test.
611+
);
576612

577613
testWidgets('sided modifiers without a side set return all sides on macOS', (WidgetTester tester) async {
578614
expect(RawKeyboard.instance.keysPressed, isEmpty);

packages/flutter_test/lib/src/event_simulation.dart

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ abstract final class KeyEventSimulator {
684684
return result!;
685685
}
686686

687-
static const KeyDataTransitMode _defaultTransitMode = KeyDataTransitMode.rawKeyData;
687+
static const KeyDataTransitMode _defaultTransitMode = KeyDataTransitMode.keyDataThenRawKeyData;
688688

689689
// The simulation transit mode for [simulateKeyDownEvent], [simulateKeyUpEvent],
690690
// and [simulateKeyRepeatEvent].
@@ -693,8 +693,8 @@ abstract final class KeyEventSimulator {
693693
// and delivered. For detailed introduction, see [KeyDataTransitMode] and
694694
// its values.
695695
//
696-
// The `_transitMode` defaults to [KeyDataTransitMode.rawKeyEvent], and can be
697-
// overridden with [debugKeyEventSimulatorTransitModeOverride]. In widget tests, it
696+
// The `_transitMode` defaults to [KeyDataTransitMode.keyDataThenRawKeyData], and can
697+
// be overridden with [debugKeyEventSimulatorTransitModeOverride]. In widget tests, it
698698
// is often set with [KeySimulationModeVariant].
699699
static KeyDataTransitMode get _transitMode {
700700
KeyDataTransitMode? result;
@@ -705,6 +705,12 @@ abstract final class KeyEventSimulator {
705705
return result ?? _defaultTransitMode;
706706
}
707707

708+
/// Returns the transit mode that simulated key events are constructed
709+
/// and delivered. For detailed introduction, see [KeyDataTransitMode]
710+
/// and its values.
711+
@visibleForTesting
712+
static KeyDataTransitMode get transitMode => _transitMode;
713+
708714
static String get _defaultPlatform => kIsWeb ? 'web' : Platform.operatingSystem;
709715

710716
/// Simulates sending a hardware key down event.
@@ -965,6 +971,15 @@ class KeySimulatorTransitModeVariant extends TestVariant<KeyDataTransitMode> {
965971
KeySimulatorTransitModeVariant.keyDataThenRawKeyData()
966972
: this(<KeyDataTransitMode>{KeyDataTransitMode.keyDataThenRawKeyData});
967973

974+
/// Creates a [KeySimulatorTransitModeVariant] that only contains
975+
/// [KeyDataTransitMode.rawKeyData].
976+
@Deprecated(
977+
'No longer supported. Transit mode is always key data only. '
978+
'This feature was deprecated after v3.18.0-2.0.pre.',
979+
)
980+
KeySimulatorTransitModeVariant.rawKeyData()
981+
: this(<KeyDataTransitMode>{KeyDataTransitMode.rawKeyData});
982+
968983
@override
969984
final Set<KeyDataTransitMode> values;
970985

0 commit comments

Comments
 (0)