| Flutter macOS Embedder |

FlutterMenuPluginTest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.h"
6 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin_Internal.h"
7
8 #import "flutter/shell/platform/common/platform_provided_menu.h"
9 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
10 #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginMacOS.h"
11 #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h"
12 #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h"
13 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h"
14 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"
15 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h"
16 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h"
17 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
18 #include "flutter/testing/autoreleasepool_test.h"
19 #include "flutter/testing/testing.h"
20 #include "gtest/gtest.h"
21
22 @interface FakePluginRegistrar : NSObject <FlutterPluginRegistrar>
23 @property(nonatomic, readonly) id<FlutterPlugin> plugin;
24 @property(nonatomic, readonly) FlutterMethodChannel* channel;
25 @end
26
27 @implementation FakePluginRegistrar
28
29 // Synthesize properties declared in FlutterPluginRegistrar protocol.
30 @synthesize messenger;
31 @synthesize textures;
32 @synthesize view;
33
34 - (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
35 channel:(nonnull FlutterMethodChannel*)channel {
36 _plugin = delegate;
37 _channel = channel;
38 [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
39 [delegate handleMethodCall:call result:result];
40 }];
41 }
42
43 - (void)addApplicationDelegate:(nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate {
44 }
45
46 - (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
47 withId:(nonnull NSString*)factoryId {
48 }
49
50 - (void)publish:(nonnull NSObject*)value {
51 }
52
53 - (nonnull NSString*)lookupKeyForAsset:(nonnull NSString*)asset {
54 return @"";
55 }
56
57 - (nonnull NSString*)lookupKeyForAsset:(nonnull NSString*)asset
58 fromPackage:(nonnull NSString*)package {
59 return @"";
60 }
61
62 - (NSViewController*)viewController {
63 return nil;
64 }
65 @end
66
67 namespace flutter::testing {
68
69 // FlutterMenuPluginTest is an AutoreleasePoolTest that allocates an NSView.
70 //
71 // This supports the use of NSApplication features that rely on the assumption of a view, such as
72 // when modifying the application menu bar, or even accessing the NSApplication.localizedName
73 // property.
74 //
75 // See: https://github.com/flutter/flutter/issues/104748#issuecomment-1159336728
76 class FlutterMenuPluginTest : public AutoreleasePoolTest {
77 public:
78 FlutterMenuPluginTest();
79 ~FlutterMenuPluginTest() = default;
80
81 private:
82 NSView* view_;
83 };
84
85 FlutterMenuPluginTest::FlutterMenuPluginTest() {
86 view_ = [[NSView alloc] initWithFrame:NSZeroRect];
87 view_.wantsLayer = YES;
88 }
89
90 TEST_F(FlutterMenuPluginTest, TestSetMenu) {
91 // Build a simulation of the default main menu.
92 NSMenu* mainMenu = [[NSMenu alloc] init];
93 NSMenuItem* appNameMenu = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"APP_NAME", nil)
94 action:nil
95 keyEquivalent:@""];
96 NSMenu* submenu =
97 [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Prexisting APP_NAME menu", nil)];
98 [submenu addItem:[[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"About APP_NAME", nil)
99 action:nil
100 keyEquivalent:@""]];
101 appNameMenu.submenu = submenu;
102 [mainMenu addItem:appNameMenu];
103 [NSApp setMainMenu:mainMenu];
104
105 FakePluginRegistrar* registrar = [[FakePluginRegistrar alloc] init];
106 [FlutterMenuPlugin registerWithRegistrar:registrar];
107 FlutterMenuPlugin* plugin = [registrar plugin];
108
109 NSDictionary* testMenus = @{
110 @"0" : @[
111 @{
112 @"id" : [NSNumber numberWithInt:1],
113 @"label" : @"APP_NAME",
114 @"tooltip" : @"APP_NAME",
115 @"enabled" : @(YES),
116 @"children" : @[
117 @{
118 @"id" : [NSNumber numberWithInt:3],
119 @"platformProvidedMenu" : @(static_cast<int>(flutter::PlatformProvidedMenu::kQuit)),
120 @"enabled" : @(YES),
121 },
122 @{
123 @"id" : [NSNumber numberWithInt:2],
124 @"label" : @"APP_NAME Info",
125 @"tooltip" : @"APP_NAME Info Tooltip",
126 @"enabled" : @(YES),
127 @"shortcutTrigger" : [NSNumber numberWithUnsignedLongLong:0x61],
128 @"shortcutModifiers" : [NSNumber numberWithUnsignedInt:0],
129 },
130 ],
131 },
132 @{
133 @"id" : [NSNumber numberWithInt:4],
134 @"label" : @"Help for APP_NAME",
135 @"enabled" : @(YES),
136 @"children" : @[
137 @{
138 @"id" : [NSNumber numberWithInt:5],
139 @"label" : @"Help me!",
140 @"tooltip" : @"Help me tooltip!",
141 @"enabled" : @(YES),
142 },
143 @{
144 @"id" : [NSNumber numberWithInt:6],
145 @"label" : @"",
146 @"enabled" : @(NO),
147 @"isDivider" : @(YES),
148 },
149 @{
150 @"id" : [NSNumber numberWithInt:7],
151 @"label" : @"Search help",
152 @"enabled" : @(NO),
153 },
154 ],
155 },
156 ],
157 };
158
159 __block id available = @NO;
160 [plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"Menu.isPluginAvailable"
161 arguments:nil]
162 result:^(id _Nullable result) {
163 available = result;
164 }];
165
166 EXPECT_TRUE(available);
167
168 [plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"Menu.setMenus"
169 arguments:testMenus]
170 result:^(id _Nullable result){
171 }];
172
173 EXPECT_EQ([NSApp.mainMenu numberOfItems], 2);
174 NSMenuItem* firstMenu = [NSApp.mainMenu itemAtIndex:0];
175 EXPECT_TRUE([[firstMenu title] isEqualToString:@"flutter_desktop_darwin_unittests"]);
176 EXPECT_TRUE([[firstMenu toolTip] isEqualToString:@"flutter_desktop_darwin_unittests"]);
177 EXPECT_EQ([firstMenu tag], 1);
178 EXPECT_TRUE([firstMenu isEnabled]);
179 EXPECT_FALSE([firstMenu isHidden]);
180 EXPECT_TRUE([[firstMenu keyEquivalent] isEqualToString:@"\0"]);
181
182 EXPECT_EQ([[firstMenu submenu] numberOfItems], 1);
183 NSMenuItem* firstItem = [[firstMenu submenu] itemAtIndex:0];
184 EXPECT_TRUE([[firstItem title] isEqualToString:@"flutter_desktop_darwin_unittests Info"]);
185 EXPECT_TRUE(
186 [[firstItem toolTip] isEqualToString:@"flutter_desktop_darwin_unittests Info Tooltip"]);
187 EXPECT_TRUE([[firstItem keyEquivalent] isEqualToString:@"a"]);
188 EXPECT_TRUE([firstItem isEnabled]);
189 EXPECT_FALSE([firstItem isHidden]);
190 EXPECT_TRUE(
191 [NSStringFromSelector([firstItem action]) isEqualToString:@"flutterMenuItemSelected:"]);
192 EXPECT_EQ([firstItem tag], 2);
193
194 NSMenuItem* secondMenu = [NSApp.mainMenu itemAtIndex:1];
195 EXPECT_TRUE([[secondMenu title] isEqualToString:@"Help for flutter_desktop_darwin_unittests"]);
196 EXPECT_EQ([secondMenu toolTip], nil);
197 EXPECT_EQ([secondMenu tag], 4);
198 EXPECT_TRUE([secondMenu isEnabled]);
199 EXPECT_FALSE([secondMenu isHidden]);
200
201 EXPECT_EQ([[secondMenu submenu] numberOfItems], 3);
202 NSMenuItem* secondMenuFirst = [[secondMenu submenu] itemAtIndex:0];
203 EXPECT_TRUE([[secondMenuFirst title] isEqualToString:@"Help me!"]);
204 EXPECT_TRUE([[secondMenuFirst toolTip] isEqualToString:@"Help me tooltip!"]);
205 EXPECT_TRUE([secondMenuFirst isEnabled]);
206 EXPECT_FALSE([secondMenuFirst isHidden]);
207 EXPECT_TRUE(
208 [NSStringFromSelector([secondMenuFirst action]) isEqualToString:@"flutterMenuItemSelected:"]);
209 EXPECT_EQ([secondMenuFirst tag], 5);
210
211 NSMenuItem* secondMenuDivider = [[secondMenu submenu] itemAtIndex:1];
212 EXPECT_TRUE([[secondMenuDivider title] isEqualToString:@""]);
213 EXPECT_EQ([secondMenuDivider toolTip], nil);
214 EXPECT_TRUE([[secondMenuDivider keyEquivalent] isEqualToString:@""]);
215 EXPECT_FALSE([secondMenuDivider isEnabled]);
216 EXPECT_FALSE([secondMenuDivider isHidden]);
217 EXPECT_EQ([secondMenuDivider action], nil);
218 EXPECT_EQ([secondMenuDivider tag], 0);
219
220 NSMenuItem* secondMenuLast = [[secondMenu submenu] itemAtIndex:2];
221 EXPECT_TRUE([[secondMenuLast title] isEqualToString:@"Search help"]);
222 EXPECT_EQ([secondMenuLast toolTip], nil);
223 EXPECT_FALSE([secondMenuLast isEnabled]);
224 EXPECT_FALSE([secondMenuLast isHidden]);
225 EXPECT_TRUE(
226 [NSStringFromSelector([secondMenuLast action]) isEqualToString:@"flutterMenuItemSelected:"]);
227 EXPECT_EQ([secondMenuLast tag], 7);
228 }
229
230 } // namespace flutter::testing
FlutterChannels.h
FlutterDartProject_Internal.h
FlutterEngine_Internal.h
FlutterPluginMacOS.h
FlutterPluginRegistrarMacOS.h
FlutterTextInputPlugin.h
FlutterTextInputSemanticsObject.h
_plugin
FlutterTextInputPlugin * _plugin
Definition: FlutterTextInputSemanticsObject.mm:62
FlutterViewController.h
FlutterViewController_Internal.h
FakePluginRegistrar
Definition: FlutterMenuPluginTest.mm:22
FakePluginRegistrar::channel
FlutterMethodChannel * channel
Definition: FlutterMenuPluginTest.mm:24
FakePluginRegistrar::plugin
id< FlutterPlugin > plugin
Definition: FlutterMenuPluginTest.mm:23
FlutterMethodCall
Definition: FlutterCodecs.h:221
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
FlutterMethodChannel
Definition: FlutterChannels.h:221
flutter::testing
Definition: AccessibilityBridgeMacTest.mm:13
flutter::testing::TEST_F
TEST_F(FlutterMenuPluginTest, TestSetMenu)
Definition: FlutterMenuPluginTest.mm:90
FlutterAppLifecycleDelegate-p
Definition: FlutterAppLifecycleDelegate.h:21
FlutterPlatformViewFactory-p
Definition: FlutterPlatformViews.h:13
FlutterPlugin-p
Definition: FlutterPluginMacOS.h:29
-[FlutterPlugin-p handleMethodCall:result:]
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
FlutterPluginRegistrar-p
Definition: FlutterPluginRegistrarMacOS.h:28
FlutterPluginRegistrar-p::viewController
NSViewController * viewController
Definition: FlutterPluginRegistrarMacOS.h:63
FlutterPluginRegistrar-p::messenger
id< FlutterBinaryMessenger > messenger
Definition: FlutterPluginRegistrarMacOS.h:33
FlutterPluginRegistrar-p::textures
id< FlutterTextureRegistry > textures
Definition: FlutterPluginRegistrarMacOS.h:39
FlutterPluginRegistrar-p::view
NSView * view
Definition: FlutterPluginRegistrarMacOS.h:50