| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginMacOS.h" |
| #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h" |
| #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" |
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" |
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" |
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.h" |
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin_Internal.h" |
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h" |
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" |
| |
| #include "flutter/shell/platform/common/platform_provided_menu.h" |
| #include "gtest/gtest.h" |
| |
| #import <OCMock/OCMock.h> |
| #import "flutter/testing/testing.h" |
| |
| @interface FlutterMenuPluginTestObjc : NSObject |
| - (bool)testSetMenu; |
| @end |
| |
| @implementation FlutterMenuPluginTestObjc |
| |
| - (bool)testSetMenu { |
| // Workaround to deflake the test. |
| // See: https://github.com/flutter/flutter/issues/104748#issuecomment-1159336728 |
| NSView* view = [[NSView alloc] initWithFrame:NSZeroRect]; |
| view.wantsLayer = YES; |
| |
| // Build a simulation of the default main menu. |
| NSMenu* mainMenu = [[NSMenu alloc] init]; |
| NSMenuItem* appNameMenu = [[NSMenuItem alloc] initWithTitle:@"APP_NAME" |
| action:nil |
| keyEquivalent:@""]; |
| NSMenu* submenu = [[NSMenu alloc] initWithTitle:@"Prexisting APP_NAME menu"]; |
| [submenu addItem:[[NSMenuItem alloc] initWithTitle:@"About APP_NAME" |
| action:nil |
| keyEquivalent:@""]]; |
| appNameMenu.submenu = submenu; |
| [mainMenu addItem:appNameMenu]; |
| [NSApp setMainMenu:mainMenu]; |
| |
| id<FlutterPluginRegistrar> pluginRegistrarMock = |
| OCMProtocolMock(@protocol(FlutterPluginRegistrar)); |
| __block FlutterMethodChannel* pluginChannel; |
| __block FlutterMenuPlugin* plugin; |
| id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); |
| OCMStub([pluginRegistrarMock messenger]).andReturn(binaryMessengerMock); |
| OCMStub( // NOLINT(google-objc-avoid-throwing-exception) |
| [pluginRegistrarMock addMethodCallDelegate:[OCMArg any] channel:[OCMArg any]]) |
| .andDo(^(NSInvocation* invocation) { |
| id<FlutterPlugin> delegate; |
| FlutterMethodChannel* channel; |
| [invocation getArgument:&delegate atIndex:2]; |
| [invocation getArgument:&channel atIndex:3]; |
| pluginChannel = channel; |
| plugin = delegate; |
| [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { |
| [delegate handleMethodCall:call result:result]; |
| }]; |
| }); |
| [FlutterMenuPlugin registerWithRegistrar:pluginRegistrarMock]; |
| |
| NSDictionary* testMenus = @{ |
| @"0" : @[ |
| @{ |
| @"id" : [NSNumber numberWithInt:1], |
| @"label" : @"APP_NAME", |
| @"enabled" : @(YES), |
| @"children" : @[ |
| @{ |
| @"id" : [NSNumber numberWithInt:3], |
| @"platformProvidedMenu" : @(static_cast<int>(flutter::PlatformProvidedMenu::kQuit)), |
| @"enabled" : @(YES), |
| }, |
| @{ |
| @"id" : [NSNumber numberWithInt:2], |
| @"label" : @"APP_NAME Info", |
| @"enabled" : @(YES), |
| @"shortcutTrigger" : [NSNumber numberWithUnsignedLongLong:0x61], |
| @"shortcutModifiers" : [NSNumber numberWithUnsignedInt:0], |
| }, |
| ], |
| }, |
| @{ |
| @"id" : [NSNumber numberWithInt:4], |
| @"label" : @"Help for APP_NAME", |
| @"enabled" : @(YES), |
| @"children" : @[ |
| @{ |
| @"id" : [NSNumber numberWithInt:5], |
| @"label" : @"Help me!", |
| @"enabled" : @(YES), |
| }, |
| @{ |
| @"id" : [NSNumber numberWithInt:6], |
| @"label" : @"", |
| @"enabled" : @(NO), |
| @"isDivider" : @(YES), |
| }, |
| @{ |
| @"id" : [NSNumber numberWithInt:7], |
| @"label" : @"Search help", |
| @"enabled" : @(NO), |
| }, |
| ], |
| }, |
| ], |
| }; |
| |
| __block id available = @NO; |
| [plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"Menu.isPluginAvailable" |
| arguments:nil] |
| result:^(id _Nullable result) { |
| available = result; |
| }]; |
| |
| EXPECT_TRUE(available); |
| |
| [plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"Menu.setMenus" |
| arguments:testMenus] |
| result:^(id _Nullable result){ |
| }]; |
| |
| EXPECT_EQ([NSApp.mainMenu numberOfItems], 2); |
| NSMenuItem* firstMenu = [NSApp.mainMenu itemAtIndex:0]; |
| EXPECT_TRUE([[firstMenu title] isEqualToString:@"flutter_desktop_darwin_unittests"]); |
| EXPECT_EQ([firstMenu tag], 1); |
| EXPECT_TRUE([firstMenu isEnabled]); |
| EXPECT_FALSE([firstMenu isHidden]); |
| EXPECT_TRUE([[firstMenu keyEquivalent] isEqualToString:@"\0"]); |
| |
| EXPECT_EQ([[firstMenu submenu] numberOfItems], 1); |
| NSMenuItem* firstItem = [[firstMenu submenu] itemAtIndex:0]; |
| EXPECT_TRUE([[firstItem title] isEqualToString:@"flutter_desktop_darwin_unittests Info"]); |
| EXPECT_TRUE([[firstItem keyEquivalent] isEqualToString:@"a"]); |
| EXPECT_TRUE([firstItem isEnabled]); |
| EXPECT_FALSE([firstItem isHidden]); |
| EXPECT_TRUE( |
| [NSStringFromSelector([firstItem action]) isEqualToString:@"flutterMenuItemSelected:"]); |
| EXPECT_EQ([firstItem tag], 2); |
| |
| NSMenuItem* secondMenu = [NSApp.mainMenu itemAtIndex:1]; |
| EXPECT_TRUE([[secondMenu title] isEqualToString:@"Help for flutter_desktop_darwin_unittests"]); |
| EXPECT_EQ([secondMenu tag], 4); |
| EXPECT_TRUE([secondMenu isEnabled]); |
| EXPECT_FALSE([secondMenu isHidden]); |
| |
| EXPECT_EQ([[secondMenu submenu] numberOfItems], 3); |
| NSMenuItem* secondMenuFirst = [[secondMenu submenu] itemAtIndex:0]; |
| EXPECT_TRUE([[secondMenuFirst title] isEqualToString:@"Help me!"]); |
| EXPECT_TRUE([secondMenuFirst isEnabled]); |
| EXPECT_FALSE([secondMenuFirst isHidden]); |
| EXPECT_TRUE( |
| [NSStringFromSelector([secondMenuFirst action]) isEqualToString:@"flutterMenuItemSelected:"]); |
| EXPECT_EQ([secondMenuFirst tag], 5); |
| |
| NSMenuItem* secondMenuDivider = [[secondMenu submenu] itemAtIndex:1]; |
| EXPECT_TRUE([[secondMenuDivider title] isEqualToString:@""]); |
| EXPECT_TRUE([[secondMenuDivider keyEquivalent] isEqualToString:@""]); |
| EXPECT_FALSE([secondMenuDivider isEnabled]); |
| EXPECT_FALSE([secondMenuDivider isHidden]); |
| EXPECT_EQ([secondMenuDivider action], nil); |
| EXPECT_EQ([secondMenuDivider tag], 0); |
| |
| NSMenuItem* secondMenuLast = [[secondMenu submenu] itemAtIndex:2]; |
| EXPECT_TRUE([[secondMenuLast title] isEqualToString:@"Search help"]); |
| EXPECT_FALSE([secondMenuLast isEnabled]); |
| EXPECT_FALSE([secondMenuLast isHidden]); |
| EXPECT_TRUE( |
| [NSStringFromSelector([secondMenuLast action]) isEqualToString:@"flutterMenuItemSelected:"]); |
| EXPECT_EQ([secondMenuLast tag], 7); |
| |
| return true; |
| } |
| |
| @end |
| |
| namespace flutter::testing { |
| TEST(FlutterMenuPluginTest, TestSetMenu) { |
| ASSERT_TRUE([[FlutterMenuPluginTestObjc alloc] testSetMenu]); |
| } |
| } // namespace flutter::testing |