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 236 237 238 239 240 241 242 243
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/share_menu_controller.h"
#import "base/path_service.h"
#include "base/strings/sys_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
#include "net/base/apple/url_conversions.h"
#include "testing/gtest_mac.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/events/test/cocoa_test_event_utils.h"
// Mock sharing service for sensing shared items.
@interface MockSharingService : NSSharingService
@property(nonatomic, strong) id sharedItem;
@end
@implementation MockSharingService
// The real one is backed by SHKSharingService parameters which
// don't appear to be present when inheriting from vanilla
// |NSSharingService|.
@synthesize subject;
@synthesize sharedItem = _sharedItem;
- (NSString*)menuItemTitle {
return @"Test";
}
- (void)performWithItems:(NSArray*)items {
self.sharedItem = items.firstObject;
}
@end
namespace {
MockSharingService* MakeMockSharingService() {
return [[MockSharingService alloc]
initWithTitle:@"Mock service"
image:[NSImage imageNamed:NSImageNameAddTemplate]
alternateImage:nil
handler:^{
}];
}
} // namespace
@interface ShareMenuController (ExposedForTesting)
- (NSMenuItem*)menuItemForService:(NSSharingService*)service;
@end
class ShareMenuControllerTest : public InProcessBrowserTest {
public:
ShareMenuControllerTest() = default;
void SetUpOnMainThread() override {
base::FilePath test_data_dir;
base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
embedded_test_server()->ServeFilesFromDirectory(test_data_dir);
ASSERT_TRUE(embedded_test_server()->Start());
url_ = embedded_test_server()->GetURL("/title2.html");
ASSERT_TRUE(AddTabAtIndex(0, url_, ui::PAGE_TRANSITION_TYPED));
controller_ = [[ShareMenuController alloc] init];
}
protected:
// Create a menu item for |service| and trigger it using
// the target/action of real menu items created by
// |controller_|
void PerformShare(NSSharingService* service) {
NSMenuItem* menu_item = [controller_ menuItemForService:service];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[menu_item.target performSelector:menu_item.action withObject:menu_item];
#pragma clang diagnostic pop
}
GURL url_;
ShareMenuController* __strong controller_;
};
IN_PROC_BROWSER_TEST_F(ShareMenuControllerTest, PopulatesMenu) {
NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Share"];
NSArray* sharing_services_for_url = [NSSharingService
sharingServicesForItems:@[ [NSURL URLWithString:@"http://example.com"] ]];
EXPECT_GT(sharing_services_for_url.count, 0U);
[controller_ menuNeedsUpdate:menu];
// -1 for reading list, +1 for "More..." if it's showing.
// This cancels out, so only decrement if the "More..." item
// isn't showing.
NSInteger expected_count = sharing_services_for_url.count;
EXPECT_EQ(menu.numberOfItems, expected_count);
NSSharingService* reading_list_service = [NSSharingService
sharingServiceNamed:NSSharingServiceNameAddToSafariReadingList];
NSSharingService* email_service =
[NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail];
// The email menu item doesn't use the share system.
NSUInteger i = 1;
// Ensure there's a menu item for each service besides reading list.
for (NSSharingService* service in sharing_services_for_url) {
if ([service isEqual:reading_list_service]) {
continue;
}
if ([service isEqual:email_service]) {
continue;
}
NSMenuItem* menu_item = [menu itemAtIndex:i];
EXPECT_NSEQ(menu_item.representedObject, service);
EXPECT_EQ(menu_item.target, static_cast<id>(controller_));
++i;
}
// Ensure the menu is cleared between updates.
[controller_ menuNeedsUpdate:menu];
EXPECT_EQ(menu.numberOfItems, expected_count);
}
IN_PROC_BROWSER_TEST_F(ShareMenuControllerTest, AddsMoreButton) {
NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Share"];
[controller_ menuNeedsUpdate:menu];
NSInteger number_of_items = menu.numberOfItems;
EXPECT_GT(number_of_items, 0);
NSMenuItem* last_item = [menu itemAtIndex:number_of_items - 1];
EXPECT_NSEQ(last_item.title, l10n_util::GetNSString(IDS_SHARING_MORE_MAC));
}
IN_PROC_BROWSER_TEST_F(ShareMenuControllerTest, ActionPerformsShare) {
MockSharingService* service = MakeMockSharingService();
EXPECT_FALSE(service.sharedItem);
PerformShare(service);
EXPECT_NSEQ(service.sharedItem, net::NSURLWithGURL(url_));
// Title of chrome/test/data/title2.html
EXPECT_NSEQ(service.subject, @"Title Of Awesomeness");
EXPECT_EQ(service.delegate,
static_cast<id<NSSharingServiceDelegate>>(controller_));
}
IN_PROC_BROWSER_TEST_F(ShareMenuControllerTest, SharingDelegate) {
NSURL* url = [NSURL URLWithString:@"http://google.com"];
NSSharingService* service = [[NSSharingService alloc]
initWithTitle:@"Mock service"
image:[NSImage imageNamed:NSImageNameAddTemplate]
alternateImage:nil
handler:^{
// Verify inside the block since everything is cleared after the
// share.
// Extra service since the service param on the delegate
// methods is nonnull and circular references could get hairy.
MockSharingService* mockService = MakeMockSharingService();
NSWindow* browser_window =
browser()->window()->GetNativeWindow().GetNativeNSWindow();
EXPECT_NSNE([controller_ sharingService:mockService
sourceFrameOnScreenForShareItem:url],
NSZeroRect);
NSSharingContentScope scope = NSSharingContentScopeItem;
EXPECT_NSEQ([controller_ sharingService:mockService
sourceWindowForShareItems:@[ url ]
sharingContentScope:&scope],
browser_window);
EXPECT_EQ(scope, NSSharingContentScopeFull);
NSRect contentRect;
EXPECT_TRUE([controller_ sharingService:mockService
transitionImageForShareItem:url
contentRect:&contentRect]);
}];
PerformShare(service);
}
IN_PROC_BROWSER_TEST_F(ShareMenuControllerTest, Histograms) {
base::HistogramTester tester;
const std::string histogram_name = "Mac.FileMenuNativeShare";
tester.ExpectTotalCount(histogram_name, 0);
MockSharingService* service = MakeMockSharingService();
[controller_ sharingService:service didShareItems:@[]];
tester.ExpectBucketCount(histogram_name, true, 1);
tester.ExpectTotalCount(histogram_name, 1);
[controller_ sharingService:service didShareItems:@[]];
tester.ExpectBucketCount(histogram_name, true, 2);
tester.ExpectTotalCount(histogram_name, 2);
[controller_ sharingService:service
didFailToShareItems:@[]
error:[NSError errorWithDomain:@""
code:0
userInfo:nil]];
tester.ExpectTotalCount(histogram_name, 3);
tester.ExpectBucketCount(histogram_name, false, 1);
}
IN_PROC_BROWSER_TEST_F(ShareMenuControllerTest, MenuHasKeyEquivalent) {
// If this method isn't implemented, |menuNeedsUpdate:| is called any time
// *any* hotkey is used
ASSERT_TRUE([controller_ respondsToSelector:@selector
(menuHasKeyEquivalent:forEvent:target:action:)]);
// Ensure that calling |menuHasKeyEquivalent:...| the first time populates the
// menu.
NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Share"];
EXPECT_EQ(menu.numberOfItems, 0);
NSEvent* event = cocoa_test_event_utils::KeyEventWithKeyCode(
'i', 'i', NSEventTypeKeyDown,
NSEventModifierFlagCommand | NSEventModifierFlagShift);
id ignored_target;
SEL ignored_action;
EXPECT_FALSE([controller_ menuHasKeyEquivalent:menu
forEvent:event
target:&ignored_target
action:&ignored_action]);
EXPECT_GT([menu numberOfItems], 0);
NSMenuItem* item = [menu itemAtIndex:0];
// |menuHasKeyEquivalent:....| shouldn't populate the menu after the first
// time.
[controller_ menuHasKeyEquivalent:menu
forEvent:event
target:&ignored_target
action:&ignored_action];
EXPECT_EQ(item, [menu itemAtIndex:0]); // Pointer equality intended.
}
|