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 
7 
18 #include "flutter/testing/autoreleasepool_test.h"
19 #include "flutter/testing/testing.h"
20 #include "gtest/gtest.h"
21 
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:
80 
81  private:
82  NSView* view_;
83 };
84 
85 FlutterMenuPluginTest::FlutterMenuPluginTest() {
86  view_ = [[NSView alloc] initWithFrame:NSZeroRect];
87  view_.wantsLayer = YES;
88 }
89 
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];
107  FlutterMenuPlugin* plugin = [registrar plugin];
108 
109  NSDictionary* testMenus = @{
110  @"0" : @[
111  @{
112  @"id" : [NSNumber numberWithInt:1],
113  @"label" : @"APP_NAME",
114  @"enabled" : @(YES),
115  @"children" : @[
116  @{
117  @"id" : [NSNumber numberWithInt:3],
118  @"platformProvidedMenu" : @(static_cast<int>(flutter::PlatformProvidedMenu::kQuit)),
119  @"enabled" : @(YES),
120  },
121  @{
122  @"id" : [NSNumber numberWithInt:2],
123  @"label" : @"APP_NAME Info",
124  @"enabled" : @(YES),
125  @"shortcutTrigger" : [NSNumber numberWithUnsignedLongLong:0x61],
126  @"shortcutModifiers" : [NSNumber numberWithUnsignedInt:0],
127  },
128  ],
129  },
130  @{
131  @"id" : [NSNumber numberWithInt:4],
132  @"label" : @"Help for APP_NAME",
133  @"enabled" : @(YES),
134  @"children" : @[
135  @{
136  @"id" : [NSNumber numberWithInt:5],
137  @"label" : @"Help me!",
138  @"enabled" : @(YES),
139  },
140  @{
141  @"id" : [NSNumber numberWithInt:6],
142  @"label" : @"",
143  @"enabled" : @(NO),
144  @"isDivider" : @(YES),
145  },
146  @{
147  @"id" : [NSNumber numberWithInt:7],
148  @"label" : @"Search help",
149  @"enabled" : @(NO),
150  },
151  ],
152  },
153  ],
154  };
155 
156  __block id available = @NO;
157  [plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"Menu.isPluginAvailable"
158  arguments:nil]
159  result:^(id _Nullable result) {
160  available = result;
161  }];
162 
163  EXPECT_TRUE(available);
164 
166  arguments:testMenus]
167  result:^(id _Nullable result){
168  }];
169 
170  EXPECT_EQ([NSApp.mainMenu numberOfItems], 2);
171  NSMenuItem* firstMenu = [NSApp.mainMenu itemAtIndex:0];
172  EXPECT_TRUE([[firstMenu title] isEqualToString:@"flutter_desktop_darwin_unittests"]);
173  EXPECT_EQ([firstMenu tag], 1);
174  EXPECT_TRUE([firstMenu isEnabled]);
175  EXPECT_FALSE([firstMenu isHidden]);
176  EXPECT_TRUE([[firstMenu keyEquivalent] isEqualToString:@"\0"]);
177 
178  EXPECT_EQ([[firstMenu submenu] numberOfItems], 1);
179  NSMenuItem* firstItem = [[firstMenu submenu] itemAtIndex:0];
180  EXPECT_TRUE([[firstItem title] isEqualToString:@"flutter_desktop_darwin_unittests Info"]);
181  EXPECT_TRUE([[firstItem keyEquivalent] isEqualToString:@"a"]);
182  EXPECT_TRUE([firstItem isEnabled]);
183  EXPECT_FALSE([firstItem isHidden]);
184  EXPECT_TRUE(
185  [NSStringFromSelector([firstItem action]) isEqualToString:@"flutterMenuItemSelected:"]);
186  EXPECT_EQ([firstItem tag], 2);
187 
188  NSMenuItem* secondMenu = [NSApp.mainMenu itemAtIndex:1];
189  EXPECT_TRUE([[secondMenu title] isEqualToString:@"Help for flutter_desktop_darwin_unittests"]);
190  EXPECT_EQ([secondMenu tag], 4);
191  EXPECT_TRUE([secondMenu isEnabled]);
192  EXPECT_FALSE([secondMenu isHidden]);
193 
194  EXPECT_EQ([[secondMenu submenu] numberOfItems], 3);
195  NSMenuItem* secondMenuFirst = [[secondMenu submenu] itemAtIndex:0];
196  EXPECT_TRUE([[secondMenuFirst title] isEqualToString:@"Help me!"]);
197  EXPECT_TRUE([secondMenuFirst isEnabled]);
198  EXPECT_FALSE([secondMenuFirst isHidden]);
199  EXPECT_TRUE(
200  [NSStringFromSelector([secondMenuFirst action]) isEqualToString:@"flutterMenuItemSelected:"]);
201  EXPECT_EQ([secondMenuFirst tag], 5);
202 
203  NSMenuItem* secondMenuDivider = [[secondMenu submenu] itemAtIndex:1];
204  EXPECT_TRUE([[secondMenuDivider title] isEqualToString:@""]);
205  EXPECT_TRUE([[secondMenuDivider keyEquivalent] isEqualToString:@""]);
206  EXPECT_FALSE([secondMenuDivider isEnabled]);
207  EXPECT_FALSE([secondMenuDivider isHidden]);
208  EXPECT_EQ([secondMenuDivider action], nil);
209  EXPECT_EQ([secondMenuDivider tag], 0);
210 
211  NSMenuItem* secondMenuLast = [[secondMenu submenu] itemAtIndex:2];
212  EXPECT_TRUE([[secondMenuLast title] isEqualToString:@"Search help"]);
213  EXPECT_FALSE([secondMenuLast isEnabled]);
214  EXPECT_FALSE([secondMenuLast isHidden]);
215  EXPECT_TRUE(
216  [NSStringFromSelector([secondMenuLast action]) isEqualToString:@"flutterMenuItemSelected:"]);
217  EXPECT_EQ([secondMenuLast tag], 7);
218 }
219 
220 } // namespace flutter::testing
FlutterTextInputPlugin * _plugin
FlutterMethodChannel * channel
id< FlutterPlugin > plugin
void registerWithRegistrar:(nonnull id< FlutterPluginRegistrar > registrar)
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
TEST_F(FlutterMenuPluginTest, TestSetMenu)
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
id< FlutterBinaryMessenger > messenger
id< FlutterTextureRegistry > textures