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 @end
62 
63 namespace flutter::testing {
64 
65 // FlutterMenuPluginTest is an AutoreleasePoolTest that allocates an NSView.
66 //
67 // This supports the use of NSApplication features that rely on the assumption of a view, such as
68 // when modifying the application menu bar, or even accessing the NSApplication.localizedName
69 // property.
70 //
71 // See: https://github.com/flutter/flutter/issues/104748#issuecomment-1159336728
72 class FlutterMenuPluginTest : public AutoreleasePoolTest {
73  public:
75  ~FlutterMenuPluginTest() = default;
76 
77  private:
78  NSView* view_;
79 };
80 
81 FlutterMenuPluginTest::FlutterMenuPluginTest() {
82  view_ = [[NSView alloc] initWithFrame:NSZeroRect];
83  view_.wantsLayer = YES;
84 }
85 
87  // Build a simulation of the default main menu.
88  NSMenu* mainMenu = [[NSMenu alloc] init];
89  NSMenuItem* appNameMenu = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"APP_NAME", nil)
90  action:nil
91  keyEquivalent:@""];
92  NSMenu* submenu =
93  [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Prexisting APP_NAME menu", nil)];
94  [submenu addItem:[[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"About APP_NAME", nil)
95  action:nil
96  keyEquivalent:@""]];
97  appNameMenu.submenu = submenu;
98  [mainMenu addItem:appNameMenu];
99  [NSApp setMainMenu:mainMenu];
100 
101  FakePluginRegistrar* registrar = [[FakePluginRegistrar alloc] init];
103  FlutterMenuPlugin* plugin = [registrar plugin];
104 
105  NSDictionary* testMenus = @{
106  @"0" : @[
107  @{
108  @"id" : [NSNumber numberWithInt:1],
109  @"label" : @"APP_NAME",
110  @"enabled" : @(YES),
111  @"children" : @[
112  @{
113  @"id" : [NSNumber numberWithInt:3],
114  @"platformProvidedMenu" : @(static_cast<int>(flutter::PlatformProvidedMenu::kQuit)),
115  @"enabled" : @(YES),
116  },
117  @{
118  @"id" : [NSNumber numberWithInt:2],
119  @"label" : @"APP_NAME Info",
120  @"enabled" : @(YES),
121  @"shortcutTrigger" : [NSNumber numberWithUnsignedLongLong:0x61],
122  @"shortcutModifiers" : [NSNumber numberWithUnsignedInt:0],
123  },
124  ],
125  },
126  @{
127  @"id" : [NSNumber numberWithInt:4],
128  @"label" : @"Help for APP_NAME",
129  @"enabled" : @(YES),
130  @"children" : @[
131  @{
132  @"id" : [NSNumber numberWithInt:5],
133  @"label" : @"Help me!",
134  @"enabled" : @(YES),
135  },
136  @{
137  @"id" : [NSNumber numberWithInt:6],
138  @"label" : @"",
139  @"enabled" : @(NO),
140  @"isDivider" : @(YES),
141  },
142  @{
143  @"id" : [NSNumber numberWithInt:7],
144  @"label" : @"Search help",
145  @"enabled" : @(NO),
146  },
147  ],
148  },
149  ],
150  };
151 
152  __block id available = @NO;
153  [plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"Menu.isPluginAvailable"
154  arguments:nil]
155  result:^(id _Nullable result) {
156  available = result;
157  }];
158 
159  EXPECT_TRUE(available);
160 
162  arguments:testMenus]
163  result:^(id _Nullable result){
164  }];
165 
166  EXPECT_EQ([NSApp.mainMenu numberOfItems], 2);
167  NSMenuItem* firstMenu = [NSApp.mainMenu itemAtIndex:0];
168  EXPECT_TRUE([[firstMenu title] isEqualToString:@"flutter_desktop_darwin_unittests"]);
169  EXPECT_EQ([firstMenu tag], 1);
170  EXPECT_TRUE([firstMenu isEnabled]);
171  EXPECT_FALSE([firstMenu isHidden]);
172  EXPECT_TRUE([[firstMenu keyEquivalent] isEqualToString:@"\0"]);
173 
174  EXPECT_EQ([[firstMenu submenu] numberOfItems], 1);
175  NSMenuItem* firstItem = [[firstMenu submenu] itemAtIndex:0];
176  EXPECT_TRUE([[firstItem title] isEqualToString:@"flutter_desktop_darwin_unittests Info"]);
177  EXPECT_TRUE([[firstItem keyEquivalent] isEqualToString:@"a"]);
178  EXPECT_TRUE([firstItem isEnabled]);
179  EXPECT_FALSE([firstItem isHidden]);
180  EXPECT_TRUE(
181  [NSStringFromSelector([firstItem action]) isEqualToString:@"flutterMenuItemSelected:"]);
182  EXPECT_EQ([firstItem tag], 2);
183 
184  NSMenuItem* secondMenu = [NSApp.mainMenu itemAtIndex:1];
185  EXPECT_TRUE([[secondMenu title] isEqualToString:@"Help for flutter_desktop_darwin_unittests"]);
186  EXPECT_EQ([secondMenu tag], 4);
187  EXPECT_TRUE([secondMenu isEnabled]);
188  EXPECT_FALSE([secondMenu isHidden]);
189 
190  EXPECT_EQ([[secondMenu submenu] numberOfItems], 3);
191  NSMenuItem* secondMenuFirst = [[secondMenu submenu] itemAtIndex:0];
192  EXPECT_TRUE([[secondMenuFirst title] isEqualToString:@"Help me!"]);
193  EXPECT_TRUE([secondMenuFirst isEnabled]);
194  EXPECT_FALSE([secondMenuFirst isHidden]);
195  EXPECT_TRUE(
196  [NSStringFromSelector([secondMenuFirst action]) isEqualToString:@"flutterMenuItemSelected:"]);
197  EXPECT_EQ([secondMenuFirst tag], 5);
198 
199  NSMenuItem* secondMenuDivider = [[secondMenu submenu] itemAtIndex:1];
200  EXPECT_TRUE([[secondMenuDivider title] isEqualToString:@""]);
201  EXPECT_TRUE([[secondMenuDivider keyEquivalent] isEqualToString:@""]);
202  EXPECT_FALSE([secondMenuDivider isEnabled]);
203  EXPECT_FALSE([secondMenuDivider isHidden]);
204  EXPECT_EQ([secondMenuDivider action], nil);
205  EXPECT_EQ([secondMenuDivider tag], 0);
206 
207  NSMenuItem* secondMenuLast = [[secondMenu submenu] itemAtIndex:2];
208  EXPECT_TRUE([[secondMenuLast title] isEqualToString:@"Search help"]);
209  EXPECT_FALSE([secondMenuLast isEnabled]);
210  EXPECT_FALSE([secondMenuLast isHidden]);
211  EXPECT_TRUE(
212  [NSStringFromSelector([secondMenuLast action]) isEqualToString:@"flutterMenuItemSelected:"]);
213  EXPECT_EQ([secondMenuLast tag], 7);
214 }
215 
216 } // namespace flutter::testing
FlutterMenuPlugin.h
FlutterPlugin-p
Definition: FlutterPluginMacOS.h:29
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
FlutterMethodChannel
Definition: FlutterChannels.h:220
FlutterPluginMacOS.h
flutter::testing::FlutterMenuPluginTest
Definition: FlutterMenuPluginTest.mm:72
FlutterPluginRegistrar-p::textures
id< FlutterTextureRegistry > textures
Definition: FlutterPluginRegistrarMacOS.h:39
FlutterTextInputPlugin.h
FlutterEngine_Internal.h
FlutterChannels.h
flutter::testing
Definition: AccessibilityBridgeMacTest.mm:13
FakePluginRegistrar::plugin
id< FlutterPlugin > plugin
Definition: FlutterMenuPluginTest.mm:23
FlutterMenuPlugin
Definition: FlutterMenuPlugin.h:20
FlutterPluginRegistrar-p
Definition: FlutterPluginRegistrarMacOS.h:28
FlutterPluginRegistrar-p::messenger
id< FlutterBinaryMessenger > messenger
Definition: FlutterPluginRegistrarMacOS.h:33
FlutterPluginRegistrarMacOS.h
FlutterAppLifecycleDelegate-p
Definition: FlutterAppLifecycleDelegate.h:21
platform_provided_menu.h
FlutterMethodCall
Definition: FlutterCodecs.h:220
FlutterMenuPlugin_Internal.h
FakePluginRegistrar::channel
FlutterMethodChannel * channel
Definition: FlutterMenuPluginTest.mm:24
-[FlutterPlugin-p handleMethodCall:result:]
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
FlutterPlatformViewFactory-p
Definition: FlutterPlatformViews.h:13
FlutterDartProject_Internal.h
FlutterViewController_Internal.h
+[FlutterMenuPlugin registerWithRegistrar:]
void registerWithRegistrar:(nonnull id< FlutterPluginRegistrar > registrar)
Definition: FlutterMenuPlugin.mm:412
FlutterTextInputSemanticsObject.h
_plugin
FlutterTextInputPlugin * _plugin
Definition: FlutterTextInputSemanticsObject.mm:62
FakePluginRegistrar
Definition: FlutterMenuPluginTest.mm:22
FlutterPluginRegistrar-p::view
NSView * view
Definition: FlutterPluginRegistrarMacOS.h:50
flutter::testing::TEST_F
TEST_F(FlutterMenuPluginTest, TestSetMenu)
Definition: FlutterMenuPluginTest.mm:86
FlutterViewController.h