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 @synthesize messenger;
29 @synthesize textures;
30 @synthesize view;
31 
32 - (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
33  channel:(nonnull FlutterMethodChannel*)channel {
34  _plugin = delegate;
35  _channel = channel;
36  [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
37  [delegate handleMethodCall:call result:result];
38  }];
39 }
40 
41 - (void)addApplicationDelegate:(nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate {
42 }
43 
44 - (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
45  withId:(nonnull NSString*)factoryId {
46 }
47 
48 - (void)publish:(nonnull NSObject*)value {
49 }
50 
51 - (nonnull NSString*)lookupKeyForAsset:(nonnull NSString*)asset {
52  return @"";
53 }
54 
55 - (nonnull NSString*)lookupKeyForAsset:(nonnull NSString*)asset
56  fromPackage:(nonnull NSString*)package {
57  return @"";
58 }
59 @end
60 
61 namespace flutter::testing {
62 
63 // FlutterMenuPluginTest is an AutoreleasePoolTest that allocates an NSView.
64 //
65 // This supports the use of NSApplication features that rely on the assumption of a view, such as
66 // when modifying the application menu bar, or even accessing the NSApplication.localizedName
67 // property.
68 //
69 // See: https://github.com/flutter/flutter/issues/104748#issuecomment-1159336728
70 class FlutterMenuPluginTest : public AutoreleasePoolTest {
71  public:
73  ~FlutterMenuPluginTest() = default;
74 
75  private:
76  NSView* view_;
77 };
78 
79 FlutterMenuPluginTest::FlutterMenuPluginTest() {
80  view_ = [[NSView alloc] initWithFrame:NSZeroRect];
81  view_.wantsLayer = YES;
82 }
83 
85  // Build a simulation of the default main menu.
86  NSMenu* mainMenu = [[NSMenu alloc] init];
87  NSMenuItem* appNameMenu = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"APP_NAME", nil)
88  action:nil
89  keyEquivalent:@""];
90  NSMenu* submenu =
91  [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Prexisting APP_NAME menu", nil)];
92  [submenu addItem:[[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"About APP_NAME", nil)
93  action:nil
94  keyEquivalent:@""]];
95  appNameMenu.submenu = submenu;
96  [mainMenu addItem:appNameMenu];
97  [NSApp setMainMenu:mainMenu];
98 
99  FakePluginRegistrar* registrar = [[FakePluginRegistrar alloc] init];
101  FlutterMenuPlugin* plugin = [registrar plugin];
102 
103  NSDictionary* testMenus = @{
104  @"0" : @[
105  @{
106  @"id" : [NSNumber numberWithInt:1],
107  @"label" : @"APP_NAME",
108  @"enabled" : @(YES),
109  @"children" : @[
110  @{
111  @"id" : [NSNumber numberWithInt:3],
112  @"platformProvidedMenu" : @(static_cast<int>(flutter::PlatformProvidedMenu::kQuit)),
113  @"enabled" : @(YES),
114  },
115  @{
116  @"id" : [NSNumber numberWithInt:2],
117  @"label" : @"APP_NAME Info",
118  @"enabled" : @(YES),
119  @"shortcutTrigger" : [NSNumber numberWithUnsignedLongLong:0x61],
120  @"shortcutModifiers" : [NSNumber numberWithUnsignedInt:0],
121  },
122  ],
123  },
124  @{
125  @"id" : [NSNumber numberWithInt:4],
126  @"label" : @"Help for APP_NAME",
127  @"enabled" : @(YES),
128  @"children" : @[
129  @{
130  @"id" : [NSNumber numberWithInt:5],
131  @"label" : @"Help me!",
132  @"enabled" : @(YES),
133  },
134  @{
135  @"id" : [NSNumber numberWithInt:6],
136  @"label" : @"",
137  @"enabled" : @(NO),
138  @"isDivider" : @(YES),
139  },
140  @{
141  @"id" : [NSNumber numberWithInt:7],
142  @"label" : @"Search help",
143  @"enabled" : @(NO),
144  },
145  ],
146  },
147  ],
148  };
149 
150  __block id available = @NO;
151  [plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"Menu.isPluginAvailable"
152  arguments:nil]
153  result:^(id _Nullable result) {
154  available = result;
155  }];
156 
157  EXPECT_TRUE(available);
158 
160  arguments:testMenus]
161  result:^(id _Nullable result){
162  }];
163 
164  EXPECT_EQ([NSApp.mainMenu numberOfItems], 2);
165  NSMenuItem* firstMenu = [NSApp.mainMenu itemAtIndex:0];
166  EXPECT_TRUE([[firstMenu title] isEqualToString:@"flutter_desktop_darwin_unittests"]);
167  EXPECT_EQ([firstMenu tag], 1);
168  EXPECT_TRUE([firstMenu isEnabled]);
169  EXPECT_FALSE([firstMenu isHidden]);
170  EXPECT_TRUE([[firstMenu keyEquivalent] isEqualToString:@"\0"]);
171 
172  EXPECT_EQ([[firstMenu submenu] numberOfItems], 1);
173  NSMenuItem* firstItem = [[firstMenu submenu] itemAtIndex:0];
174  EXPECT_TRUE([[firstItem title] isEqualToString:@"flutter_desktop_darwin_unittests Info"]);
175  EXPECT_TRUE([[firstItem keyEquivalent] isEqualToString:@"a"]);
176  EXPECT_TRUE([firstItem isEnabled]);
177  EXPECT_FALSE([firstItem isHidden]);
178  EXPECT_TRUE(
179  [NSStringFromSelector([firstItem action]) isEqualToString:@"flutterMenuItemSelected:"]);
180  EXPECT_EQ([firstItem tag], 2);
181 
182  NSMenuItem* secondMenu = [NSApp.mainMenu itemAtIndex:1];
183  EXPECT_TRUE([[secondMenu title] isEqualToString:@"Help for flutter_desktop_darwin_unittests"]);
184  EXPECT_EQ([secondMenu tag], 4);
185  EXPECT_TRUE([secondMenu isEnabled]);
186  EXPECT_FALSE([secondMenu isHidden]);
187 
188  EXPECT_EQ([[secondMenu submenu] numberOfItems], 3);
189  NSMenuItem* secondMenuFirst = [[secondMenu submenu] itemAtIndex:0];
190  EXPECT_TRUE([[secondMenuFirst title] isEqualToString:@"Help me!"]);
191  EXPECT_TRUE([secondMenuFirst isEnabled]);
192  EXPECT_FALSE([secondMenuFirst isHidden]);
193  EXPECT_TRUE(
194  [NSStringFromSelector([secondMenuFirst action]) isEqualToString:@"flutterMenuItemSelected:"]);
195  EXPECT_EQ([secondMenuFirst tag], 5);
196 
197  NSMenuItem* secondMenuDivider = [[secondMenu submenu] itemAtIndex:1];
198  EXPECT_TRUE([[secondMenuDivider title] isEqualToString:@""]);
199  EXPECT_TRUE([[secondMenuDivider keyEquivalent] isEqualToString:@""]);
200  EXPECT_FALSE([secondMenuDivider isEnabled]);
201  EXPECT_FALSE([secondMenuDivider isHidden]);
202  EXPECT_EQ([secondMenuDivider action], nil);
203  EXPECT_EQ([secondMenuDivider tag], 0);
204 
205  NSMenuItem* secondMenuLast = [[secondMenu submenu] itemAtIndex:2];
206  EXPECT_TRUE([[secondMenuLast title] isEqualToString:@"Search help"]);
207  EXPECT_FALSE([secondMenuLast isEnabled]);
208  EXPECT_FALSE([secondMenuLast isHidden]);
209  EXPECT_TRUE(
210  [NSStringFromSelector([secondMenuLast action]) isEqualToString:@"flutterMenuItemSelected:"]);
211  EXPECT_EQ([secondMenuLast tag], 7);
212 }
213 
214 } // 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:70
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:84
FlutterViewController.h