File: nsmenu_additions_unittest.mm

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (235 lines) | stat: -rw-r--r-- 8,637 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ui/base/cocoa/nsmenu_additions.h"

#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"

@interface NSMenuAdditionsUnitTestMenuItem : NSMenuItem
@end

@implementation NSMenuAdditionsUnitTestMenuItem {
  BOOL includeFunctionModifierInFlags_;
}

- (void)setKeyEquivalentModifierMask:(NSEventModifierFlags)mask {
  // The AppKit ignores NSEventModifierFlagFunction when it's included
  // in the mask. Note that it was included so we can fake it later.
  includeFunctionModifierInFlags_ = (mask & NSEventModifierFlagFunction) > 0;

  // Remove the flag to avoid a warning from the AppKit.
  mask &= ~NSEventModifierFlagFunction;

  [super setKeyEquivalentModifierMask:mask];
}

- (NSEventModifierFlags)keyEquivalentModifierMask {
  NSEventModifierFlags flags = [super keyEquivalentModifierMask];
  if (includeFunctionModifierInFlags_) {
    flags |= NSEventModifierFlagFunction;
  }
  return flags;
}

@end

namespace {

NSMenu* Menu(NSString* title) {
  NSMenu* menu = [[NSMenu alloc] initWithTitle:title];
  menu.autoenablesItems = NO;
  return menu;
}

NSMenuItem* MenuItem(NSString* title,
                     NSString* key_equivalent = @"",
                     NSEventModifierFlags modifier_mask = 0) {
  NSMenuAdditionsUnitTestMenuItem* item =
      [[NSMenuAdditionsUnitTestMenuItem alloc] initWithTitle:title
                                                      action:nil
                                               keyEquivalent:key_equivalent];
  item.keyEquivalentModifierMask = modifier_mask;
  item.enabled = YES;
  return item;
}

NSEvent* KeyEvent(const NSEventModifierFlags modifierFlags,
                  NSString* chars,
                  NSString* charsNoMods = nil,
                  const NSUInteger keyCode = 0) {
  if (charsNoMods == nil)
    charsNoMods = chars;

  return [NSEvent keyEventWithType:NSEventTypeKeyDown
                          location:NSZeroPoint
                     modifierFlags:modifierFlags
                         timestamp:0.0
                      windowNumber:0
                           context:nil
                        characters:chars
       charactersIgnoringModifiers:charsNoMods
                         isARepeat:NO
                           keyCode:keyCode];
}

}  // namespace

TEST(NSMenuAdditionsTest, TestMenuItemForKeyEquivalentEvent) {
  NSMenu* main_menu = Menu(@"Main Menu");

  [main_menu addItem:MenuItem(@"App")];
  NSString* file_title = @"File";
  [main_menu addItem:MenuItem(file_title)];
  [main_menu addItem:MenuItem(@"Edit")];
  [main_menu addItem:MenuItem(@"Window")];
  [main_menu addItem:MenuItem(@"Help")];

  NSMenu* file_menu = Menu(file_title);
  NSMenuItem* file_menu_item = [main_menu itemWithTitle:[file_menu title]];
  [file_menu_item setSubmenu:file_menu];

  [file_menu addItem:MenuItem(@"New")];
  NSString* open_title = @"Open";
  [file_menu addItem:MenuItem(open_title, @"o", NSEventModifierFlagCommand)];
  NSString* open_recent_title = @"Open Recent";
  [file_menu addItem:MenuItem(open_recent_title)];
  NSString* close_all_title = @"Close All";
  [file_menu
      addItem:MenuItem(close_all_title, @"W", NSEventModifierFlagCommand)];

  NSMenu* open_recent_menu = Menu(open_recent_title);
  NSMenuItem* open_recent_menu_item =
      [file_menu itemWithTitle:[open_recent_menu title]];
  [open_recent_menu_item setSubmenu:open_recent_menu];

  [open_recent_menu addItem:MenuItem(@"Mock up")];
  [open_recent_menu addItem:MenuItem(@"Preview-1")];
  [open_recent_menu addItem:MenuItem(@"Scratchpad")];
  [open_recent_menu addItem:[NSMenuItem separatorItem]];
  NSString* clear_menu_title = @"Clear Menu";
  [open_recent_menu
      addItem:MenuItem(clear_menu_title, @"c",
                       NSEventModifierFlagCommand | NSEventModifierFlagControl |
                           NSEventModifierFlagOption |
                           NSEventModifierFlagFunction)];

  NSEvent* event = KeyEvent(NSEventModifierFlagCommand, @"o");
  EXPECT_EQ([file_menu itemWithTitle:open_title],
            [main_menu cr_menuItemForKeyEquivalentEvent:event]);

  event = KeyEvent(NSEventModifierFlagCommand, @"W");
  EXPECT_EQ([file_menu itemWithTitle:close_all_title],
            [main_menu cr_menuItemForKeyEquivalentEvent:event]);

  event = KeyEvent(NSEventModifierFlagCommand | NSEventModifierFlagControl |
                       NSEventModifierFlagOption | NSEventModifierFlagFunction,
                   @"c");
  EXPECT_EQ([open_recent_menu itemWithTitle:clear_menu_title],
            [main_menu cr_menuItemForKeyEquivalentEvent:event]);

  event = KeyEvent(NSEventModifierFlagCommand, @"g");
  EXPECT_EQ(nil, [main_menu cr_menuItemForKeyEquivalentEvent:event]);
}

// Tests that a set pre-search block is executed during calls to
// -[NSMenu cr_menuItemForKeyEquivalentEvent:].
TEST(NSMenuAdditionsTest, TestPreSearchBlock) {
  __block bool block_was_called = false;

  [NSMenu cr_setMenuItemForKeyEquivalentEventPreSearchBlock:^{
    block_was_called = true;
  }];

  NSMenu* main_menu = Menu(@"Main Menu");
  NSEvent* event = KeyEvent(NSEventModifierFlagCommand, @"c");
  [main_menu cr_menuItemForKeyEquivalentEvent:event];

  EXPECT_TRUE(block_was_called);

  // Setting the block again should cause a crash (the API only supports a
  // single pre-search block).
  EXPECT_CHECK_DEATH(
      [NSMenu cr_setMenuItemForKeyEquivalentEventPreSearchBlock:^{
        block_was_called = true;
      }]);
}

// Tests that +flashMenuForChromeCommand: can locate a menu item with a
// particular Chrome command (tag) and that it correctly restores the menu item
// after the flash.
TEST(NSMenuAdditionsTest, TestLocateMenuItemWithTag) {
  NSMenu* orig_main_menu = [NSApp mainMenu];
  NSMenu* main_menu = Menu(@"Main Menu");
  [NSApp setMainMenu:main_menu];

  [main_menu addItem:MenuItem(@"App")];
  NSString* file_title = @"File";
  [main_menu addItem:MenuItem(file_title)];
  [main_menu addItem:MenuItem(@"Edit")];
  [main_menu addItem:MenuItem(@"Window")];
  [main_menu addItem:MenuItem(@"Help")];

  NSMenu* file_menu = Menu(file_title);
  NSMenuItem* file_menu_item = [main_menu itemWithTitle:[file_menu title]];
  [file_menu_item setSubmenu:file_menu];

  [file_menu addItem:MenuItem(@"New")];
  NSString* open_title = @"Open";
  [file_menu addItem:MenuItem(open_title, @"o", NSEventModifierFlagCommand)];
  NSString* open_recent_title = @"Open Recent";
  [file_menu addItem:MenuItem(open_recent_title)];
  NSString* close_tab_title = @"Close Tab";
  [file_menu
      addItem:MenuItem(close_tab_title, @"W", NSEventModifierFlagCommand)];
  NSMenuItem* close_item = [file_menu itemWithTitle:close_tab_title];

  NSMenu* open_recent_menu = Menu(open_recent_title);
  NSMenuItem* open_recent_menu_item =
      [file_menu itemWithTitle:[open_recent_menu title]];
  [open_recent_menu_item setSubmenu:open_recent_menu];

  [open_recent_menu addItem:MenuItem(@"Mock up")];
  [open_recent_menu addItem:MenuItem(@"Preview-1")];
  [open_recent_menu addItem:MenuItem(@"Scratchpad")];
  [open_recent_menu addItem:[NSMenuItem separatorItem]];
  NSString* clear_menu_title = @"Clear Menu";
  [open_recent_menu
      addItem:MenuItem(clear_menu_title, @"c",
                       NSEventModifierFlagCommand | NSEventModifierFlagControl |
                           NSEventModifierFlagOption |
                           NSEventModifierFlagFunction)];
  NSMenuItem* clear_item = [open_recent_menu itemWithTitle:clear_menu_title];

  // Commands have not been set so +flashMenuForChromeCommand: should fail.
  const int kCloseTab = 5001;
  EXPECT_FALSE([NSMenu flashMenuForChromeCommand:kCloseTab]);

  [close_item setTag:kCloseTab];
  [close_item setTarget:nil];
  const SEL close_action = @selector(close:);
  [close_item setAction:close_action];

  EXPECT_TRUE([NSMenu flashMenuForChromeCommand:kCloseTab]);

  // +flashMenuForChromeCommand: fiddles with the item's target and action. Make
  // sure they're properly restored.
  EXPECT_EQ([close_item target], nil);
  EXPECT_EQ([close_item action], close_action);

  const int kClearMenu = 5002;
  EXPECT_FALSE([NSMenu flashMenuForChromeCommand:kClearMenu]);

  [clear_item setTag:kClearMenu];
  [clear_item setTarget:NSApp];
  const SEL terminate_action = @selector(terminate:);
  [clear_item setAction:terminate_action];

  EXPECT_TRUE([NSMenu flashMenuForChromeCommand:kClearMenu]);

  EXPECT_EQ([clear_item target], NSApp);
  EXPECT_EQ([clear_item action], terminate_action);

  [NSApp setMainMenu:orig_main_menu];
}