1 // Copyright (c) 2011 The Chromium 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.
5 #import <Cocoa/Cocoa.h>
7 #import "chrome/browser/ui/cocoa/toolbar/reload_button_cocoa.h"
9 #include "base/mac/scoped_nsobject.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
12 #import "chrome/browser/ui/cocoa/image_button_cell.h"
13 #import "testing/gtest_mac.h"
14 #include "testing/platform_test.h"
15 #import "third_party/ocmock/OCMock/OCMock.h"
16 #import "ui/events/test/cocoa_test_event_utils.h"
18 @interface ReloadButton (Testing)
19 + (void)setPendingReloadTimeout:(NSTimeInterval)seconds;
22 @interface ReloadButtonTarget : NSObject
23 - (void)anAction:(id)sender;
26 @implementation ReloadButtonTarget
27 - (void)anAction:(id)sender {
33 class ReloadButtonTest : public CocoaTest {
36 NSRect frame = NSMakeRect(0, 0, 20, 20);
37 base::scoped_nsobject<ReloadButton> button(
38 [[ReloadButton alloc] initWithFrame:frame]);
39 button_ = button.get();
41 // Set things up so unit tests have a reliable baseline.
42 [button_ setTag:IDC_RELOAD];
43 [button_ awakeFromNib];
45 [[test_window() contentView] addSubview:button_];
48 bool IsMouseInside() {
49 return [[button_ cell] isMouseInside];
53 [[button_ cell] mouseEntered:nil];
57 [[button_ cell] mouseExited:nil];
60 ReloadButton* button_;
63 TEST_VIEW(ReloadButtonTest, button_)
65 // Test that mouse-tracking is setup and does the right thing.
66 TEST_F(ReloadButtonTest, IsMouseInside) {
67 EXPECT_FALSE(IsMouseInside());
69 EXPECT_TRUE(IsMouseInside());
73 // Verify that multiple clicks do not result in multiple messages to
75 TEST_F(ReloadButtonTest, IgnoredMultiClick) {
76 base::scoped_nsobject<ReloadButtonTarget> target(
77 [[ReloadButtonTarget alloc] init]);
78 id mock_target = [OCMockObject partialMockForObject:target];
79 [button_ setTarget:mock_target];
80 [button_ setAction:@selector(anAction:)];
82 // Expect the action once.
83 [[mock_target expect] anAction:button_];
85 const std::pair<NSEvent*,NSEvent*> click_one =
86 cocoa_test_event_utils::MouseClickInView(button_, 1);
87 const std::pair<NSEvent*,NSEvent*> click_two =
88 cocoa_test_event_utils::MouseClickInView(button_, 2);
89 [NSApp postEvent:click_one.second atStart:YES];
90 [button_ mouseDown:click_one.first];
91 [NSApp postEvent:click_two.second atStart:YES];
92 [button_ mouseDown:click_two.first];
94 [button_ setTarget:nil];
97 TEST_F(ReloadButtonTest, UpdateTag) {
98 [button_ setTag:IDC_STOP];
100 [button_ updateTag:IDC_RELOAD];
101 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
102 NSString* const reloadToolTip = [button_ toolTip];
104 [button_ updateTag:IDC_STOP];
105 EXPECT_EQ(IDC_STOP, [button_ tag]);
106 NSString* const stopToolTip = [button_ toolTip];
107 EXPECT_NSNE(reloadToolTip, stopToolTip);
109 [button_ updateTag:IDC_RELOAD];
110 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
111 EXPECT_NSEQ(reloadToolTip, [button_ toolTip]);
114 // Test that when forcing the mode, it takes effect immediately,
115 // regardless of whether the mouse is hovering.
116 TEST_F(ReloadButtonTest, SetIsLoadingForce) {
117 EXPECT_FALSE(IsMouseInside());
118 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
120 // Changes to stop immediately.
121 [button_ setIsLoading:YES force:YES];
122 EXPECT_EQ(IDC_STOP, [button_ tag]);
124 // Changes to reload immediately.
125 [button_ setIsLoading:NO force:YES];
126 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
128 // Changes to stop immediately when the mouse is hovered, and
129 // doesn't change when the mouse exits.
131 EXPECT_TRUE(IsMouseInside());
132 [button_ setIsLoading:YES force:YES];
133 EXPECT_EQ(IDC_STOP, [button_ tag]);
135 EXPECT_FALSE(IsMouseInside());
136 EXPECT_EQ(IDC_STOP, [button_ tag]);
138 // Changes to reload immediately when the mouse is hovered, and
139 // doesn't change when the mouse exits.
141 EXPECT_TRUE(IsMouseInside());
142 [button_ setIsLoading:NO force:YES];
143 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
145 EXPECT_FALSE(IsMouseInside());
146 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
149 // Test that without force, stop mode is set immediately, but reload
150 // is affected by the hover status.
151 TEST_F(ReloadButtonTest, SetIsLoadingNoForceUnHover) {
152 EXPECT_FALSE(IsMouseInside());
153 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
155 // Changes to stop immediately when the mouse is not hovering.
156 [button_ setIsLoading:YES force:NO];
157 EXPECT_EQ(IDC_STOP, [button_ tag]);
159 // Changes to reload immediately when the mouse is not hovering.
160 [button_ setIsLoading:NO force:NO];
161 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
163 // Changes to stop immediately when the mouse is hovered, and
164 // doesn't change when the mouse exits.
166 EXPECT_TRUE(IsMouseInside());
167 [button_ setIsLoading:YES force:NO];
168 EXPECT_EQ(IDC_STOP, [button_ tag]);
170 EXPECT_FALSE(IsMouseInside());
171 EXPECT_EQ(IDC_STOP, [button_ tag]);
173 // Does not change to reload immediately when the mouse is hovered,
174 // changes when the mouse exits.
176 EXPECT_TRUE(IsMouseInside());
177 [button_ setIsLoading:NO force:NO];
178 EXPECT_EQ(IDC_STOP, [button_ tag]);
180 EXPECT_FALSE(IsMouseInside());
181 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
184 // Test that without force, stop mode is set immediately, and reload
185 // will be set after a timeout.
186 // TODO(shess): Reenable, http://crbug.com/61485
187 TEST_F(ReloadButtonTest, DISABLED_SetIsLoadingNoForceTimeout) {
188 // When the event loop first spins, some delayed tracking-area setup
189 // is done, which causes -mouseExited: to be called. Spin it at
190 // least once, and dequeue any pending events.
191 // TODO(shess): It would be more reasonable to have an MockNSTimer
192 // factory for the class to use, which this code could fire
194 while ([NSApp nextEventMatchingMask:NSAnyEventMask
196 inMode:NSDefaultRunLoopMode
200 const NSTimeInterval kShortTimeout = 0.1;
201 [ReloadButton setPendingReloadTimeout:kShortTimeout];
203 EXPECT_FALSE(IsMouseInside());
204 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
206 // Move the mouse into the button and press it.
208 EXPECT_TRUE(IsMouseInside());
209 [button_ setIsLoading:YES force:NO];
210 EXPECT_EQ(IDC_STOP, [button_ tag]);
212 // Does not change to reload immediately when the mouse is hovered.
213 EXPECT_TRUE(IsMouseInside());
214 [button_ setIsLoading:NO force:NO];
215 EXPECT_TRUE(IsMouseInside());
216 EXPECT_EQ(IDC_STOP, [button_ tag]);
217 EXPECT_TRUE(IsMouseInside());
219 // Spin event loop until the timeout passes.
220 NSDate* pastTimeout = [NSDate dateWithTimeIntervalSinceNow:2 * kShortTimeout];
221 [NSApp nextEventMatchingMask:NSAnyEventMask
222 untilDate:pastTimeout
223 inMode:NSDefaultRunLoopMode
226 // Mouse is still hovered, button is in reload mode. If the mouse
227 // is no longer hovered, see comment at top of function.
228 EXPECT_TRUE(IsMouseInside());
229 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
232 // Test that pressing stop after reload mode has been requested
233 // doesn't forward the stop message.
234 TEST_F(ReloadButtonTest, StopAfterReloadSet) {
235 base::scoped_nsobject<ReloadButtonTarget> target(
236 [[ReloadButtonTarget alloc] init]);
237 id mock_target = [OCMockObject partialMockForObject:target];
238 [button_ setTarget:mock_target];
239 [button_ setAction:@selector(anAction:)];
241 EXPECT_FALSE(IsMouseInside());
244 [button_ setIsLoading:YES force:YES];
245 EXPECT_EQ(IDC_STOP, [button_ tag]);
246 EXPECT_TRUE([button_ isEnabled]);
248 // Expect the action once.
249 [[mock_target expect] anAction:button_];
251 // Clicking in stop mode should send the action and transition to
253 const std::pair<NSEvent*,NSEvent*> click =
254 cocoa_test_event_utils::MouseClickInView(button_, 1);
255 [NSApp postEvent:click.second atStart:YES];
256 [button_ mouseDown:click.first];
257 EXPECT_EQ(IDC_RELOAD, [button_ tag]);
258 EXPECT_TRUE([button_ isEnabled]);
260 // Get back to stop mode.
261 [button_ setIsLoading:YES force:YES];
262 EXPECT_EQ(IDC_STOP, [button_ tag]);
263 EXPECT_TRUE([button_ isEnabled]);
265 // If hover prevented reload mode immediately taking effect, clicks should do
266 // nothing, because the button should be disabled.
268 EXPECT_TRUE(IsMouseInside());
269 [button_ setIsLoading:NO force:NO];
270 EXPECT_EQ(IDC_STOP, [button_ tag]);
271 EXPECT_FALSE([button_ isEnabled]);
272 [NSApp postEvent:click.second atStart:YES];
273 [button_ mouseDown:click.first];
274 EXPECT_EQ(IDC_STOP, [button_ tag]);
276 [button_ setTarget:nil];