2
2
// Use of this source code is governed by a BSD-style license that can be
3
3
// found in the LICENSE file.
4
4
5
+ #import " KeyCodeMap_Internal.h"
5
6
#import " flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h"
6
7
#import " flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
7
8
13
14
#import " flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"
14
15
#import " flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h"
15
16
#import " flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h"
17
+ #include " flutter/shell/platform/embedder/test_utils/key_codes.g.h"
16
18
#import " flutter/testing/testing.h"
17
19
20
+ // A wrap to convert FlutterKeyEvent to a ObjC class.
21
+ @interface KeyEventWrapper : NSObject
22
+ @property (nonatomic ) FlutterKeyEvent* data;
23
+ - (nonnull instancetype )initWithEvent : (const FlutterKeyEvent*)event ;
24
+ @end
25
+
26
+ @implementation KeyEventWrapper
27
+ - (instancetype )initWithEvent : (const FlutterKeyEvent*)event {
28
+ self = [super init ];
29
+ _data = new FlutterKeyEvent (*event);
30
+ return self;
31
+ }
32
+
33
+ - (void )dealloc {
34
+ delete _data;
35
+ }
36
+ @end
37
+
18
38
@interface FlutterViewControllerTestObjC : NSObject
19
39
- (bool )testKeyEventsAreSentToFramework ;
20
40
- (bool )testKeyEventsArePropagatedIfNotHandled ;
21
41
- (bool )testKeyEventsAreNotPropagatedIfHandled ;
22
42
- (bool )testFlagsChangedEventsArePropagatedIfNotHandled ;
23
43
- (bool )testKeyboardIsRestartedOnEngineRestart ;
24
44
- (bool )testTrackpadGesturesAreSentToFramework ;
45
+ - (bool )testModifierKeysAreSynthesizedOnMouseMove ;
25
46
- (bool )testViewWillAppearCalledMultipleTimes ;
26
47
- (bool )testFlutterViewIsConfigured ;
27
48
@@ -30,6 +51,8 @@ + (void)respondFalseForSendEvent:(const FlutterKeyEvent&)event
30
51
userData : (nullable void *)userData ;
31
52
@end
32
53
54
+ using namespace ::flutter::testing::keycodes;
55
+
33
56
namespace flutter ::testing {
34
57
35
58
namespace {
@@ -69,6 +92,19 @@ id MockGestureEvent(NSEventType type, NSEventPhase phase, double magnification,
69
92
OCMStub ([mock flagsChanged: [OCMArg any ]]).andDo (nil );
70
93
return mock;
71
94
}
95
+
96
+ NSEvent * CreateMouseEvent (NSEventModifierFlags modifierFlags) {
97
+ return [NSEvent mouseEventWithType: NSEventTypeMouseMoved
98
+ location: NSZeroPoint
99
+ modifierFlags: modifierFlags
100
+ timestamp: 0
101
+ windowNumber: 0
102
+ context: nil
103
+ eventNumber: 0
104
+ clickCount: 1
105
+ pressure: 1.0 ];
106
+ }
107
+
72
108
} // namespace
73
109
74
110
TEST (FlutterViewController, HasViewThatHidesOtherViewsInAccessibility) {
@@ -161,6 +197,10 @@ id MockGestureEvent(NSEventType type, NSEventPhase phase, double magnification,
161
197
ASSERT_TRUE ([[FlutterViewControllerTestObjC alloc ] testTrackpadGesturesAreSentToFramework ]);
162
198
}
163
199
200
+ TEST (FlutterViewControllerTest, TestModifierKeysAreSynthesizedOnMouseMove) {
201
+ ASSERT_TRUE ([[FlutterViewControllerTestObjC alloc ] testModifierKeysAreSynthesizedOnMouseMove ]);
202
+ }
203
+
164
204
TEST (FlutterViewControllerTest, testViewWillAppearCalledMultipleTimes) {
165
205
ASSERT_TRUE ([[FlutterViewControllerTestObjC alloc ] testViewWillAppearCalledMultipleTimes ]);
166
206
}
@@ -763,4 +803,71 @@ - (bool)testViewWillAppearCalledMultipleTimes {
763
803
return true ;
764
804
}
765
805
806
+ - (bool )testModifierKeysAreSynthesizedOnMouseMove {
807
+ id engineMock = OCMClassMock ([FlutterEngine class ]);
808
+ // Need to return a real renderer to allow view controller to load.
809
+ FlutterRenderer* renderer_ = [[FlutterRenderer alloc ] initWithFlutterEngine: engineMock];
810
+ OCMStub ([engineMock renderer ]).andReturn (renderer_);
811
+
812
+ // Capture calls to sendKeyEvent
813
+ __block NSMutableArray <KeyEventWrapper*>* events =
814
+ [[NSMutableArray <KeyEventWrapper*> alloc] init ];
815
+ OCMStub ([[engineMock ignoringNonObjectArgs ] sendKeyEvent: FlutterKeyEvent {}
816
+ callback: nil
817
+ userData: nil ])
818
+ .andDo ((^(NSInvocation * invocation) {
819
+ FlutterKeyEvent* event;
820
+ [invocation getArgument: &event atIndex: 2 ];
821
+ [events addObject: [[KeyEventWrapper alloc ] initWithEvent: event]];
822
+ }));
823
+
824
+ FlutterViewController* viewController = [[FlutterViewController alloc ] initWithEngine: engineMock
825
+ nibName: @" "
826
+ bundle: nil ];
827
+ [viewController loadView ];
828
+ [engineMock setViewController: viewController];
829
+ [viewController viewWillAppear ];
830
+
831
+ // Zeroed modifier flag should not synthesize events.
832
+ NSEvent * mouseEvent = flutter::testing::CreateMouseEvent (0x00 );
833
+ [viewController mouseMoved: mouseEvent];
834
+ EXPECT_EQ ([events count ], 0u );
835
+
836
+ // For each modifier key, check that key events are synthesized.
837
+ [flutter::keyCodeToModifierFlag
838
+ enumerateKeysAndObjectsUsingBlock: ^(NSNumber * keyCode, NSNumber * flag, BOOL * stop) {
839
+ FlutterKeyEvent* event;
840
+ NSNumber * logicalKey;
841
+ NSNumber * physicalKey;
842
+
843
+ // Should synthesize down event.
844
+ NSEvent * mouseEvent = flutter::testing::CreateMouseEvent ([flag unsignedLongValue ]);
845
+ [viewController mouseMoved: mouseEvent];
846
+ EXPECT_EQ ([events count ], 1u );
847
+ event = events[0 ].data ;
848
+ logicalKey = [flutter::keyCodeToLogicalKey objectForKey: keyCode];
849
+ physicalKey = [flutter::keyCodeToPhysicalKey objectForKey: keyCode];
850
+ EXPECT_EQ (event->type , kFlutterKeyEventTypeDown );
851
+ EXPECT_EQ (event->logical , logicalKey.unsignedLongLongValue );
852
+ EXPECT_EQ (event->physical , physicalKey.unsignedLongLongValue );
853
+ EXPECT_EQ (event->synthesized , true );
854
+
855
+ // Should synthesize up event.
856
+ mouseEvent = flutter::testing::CreateMouseEvent (0x00 );
857
+ [viewController mouseMoved: mouseEvent];
858
+ EXPECT_EQ ([events count ], 2u );
859
+ event = events[1 ].data ;
860
+ logicalKey = [flutter::keyCodeToLogicalKey objectForKey: keyCode];
861
+ physicalKey = [flutter::keyCodeToPhysicalKey objectForKey: keyCode];
862
+ EXPECT_EQ (event->type , kFlutterKeyEventTypeUp );
863
+ EXPECT_EQ (event->logical , logicalKey.unsignedLongLongValue );
864
+ EXPECT_EQ (event->physical , physicalKey.unsignedLongLongValue );
865
+ EXPECT_EQ (event->synthesized , true );
866
+
867
+ [events removeAllObjects ];
868
+ }];
869
+
870
+ return true ;
871
+ }
872
+
766
873
@end
0 commit comments