Home | History | Annotate | Download | only in toolbar
      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.
      4 
      5 #import <Cocoa/Cocoa.h>
      6 
      7 #import "chrome/browser/ui/cocoa/toolbar/reload_button.h"
      8 
      9 #include "base/memory/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/test_event_utils.h"
     13 #import "testing/gtest_mac.h"
     14 #include "testing/platform_test.h"
     15 #import "third_party/ocmock/OCMock/OCMock.h"
     16 
     17 @protocol TargetActionMock <NSObject>
     18 - (void)anAction:(id)sender;
     19 @end
     20 
     21 namespace {
     22 
     23 class ReloadButtonTest : public CocoaTest {
     24  public:
     25   ReloadButtonTest() {
     26     NSRect frame = NSMakeRect(0, 0, 20, 20);
     27     scoped_nsobject<ReloadButton> button(
     28         [[ReloadButton alloc] initWithFrame:frame]);
     29     button_ = button.get();
     30 
     31     // Set things up so unit tests have a reliable baseline.
     32     [button_ setTag:IDC_RELOAD];
     33     [button_ awakeFromNib];
     34 
     35     [[test_window() contentView] addSubview:button_];
     36   }
     37 
     38   ReloadButton* button_;
     39 };
     40 
     41 TEST_VIEW(ReloadButtonTest, button_)
     42 
     43 // Test that mouse-tracking is setup and does the right thing.
     44 TEST_F(ReloadButtonTest, IsMouseInside) {
     45   EXPECT_TRUE([[button_ trackingAreas] containsObject:[button_ trackingArea]]);
     46 
     47   EXPECT_FALSE([button_ isMouseInside]);
     48   [button_ mouseEntered:nil];
     49   EXPECT_TRUE([button_ isMouseInside]);
     50   [button_ mouseExited:nil];
     51 }
     52 
     53 // Verify that multiple clicks do not result in multiple messages to
     54 // the target.
     55 TEST_F(ReloadButtonTest, IgnoredMultiClick) {
     56   id mock_target = [OCMockObject mockForProtocol:@protocol(TargetActionMock)];
     57   [button_ setTarget:mock_target];
     58   [button_ setAction:@selector(anAction:)];
     59 
     60   // Expect the action once.
     61   [[mock_target expect] anAction:button_];
     62 
     63   const std::pair<NSEvent*,NSEvent*> click_one =
     64       test_event_utils::MouseClickInView(button_, 1);
     65   const std::pair<NSEvent*,NSEvent*> click_two =
     66       test_event_utils::MouseClickInView(button_, 2);
     67   [NSApp postEvent:click_one.second atStart:YES];
     68   [button_ mouseDown:click_one.first];
     69   [NSApp postEvent:click_two.second atStart:YES];
     70   [button_ mouseDown:click_two.first];
     71 
     72   [button_ setTarget:nil];
     73 }
     74 
     75 TEST_F(ReloadButtonTest, UpdateTag) {
     76   [button_ setTag:IDC_STOP];
     77 
     78   [button_ updateTag:IDC_RELOAD];
     79   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
     80   NSImage* reloadImage = [button_ image];
     81   NSString* const reloadToolTip = [button_ toolTip];
     82 
     83   [button_ updateTag:IDC_STOP];
     84   EXPECT_EQ(IDC_STOP, [button_ tag]);
     85   NSImage* stopImage = [button_ image];
     86   NSString* const stopToolTip = [button_ toolTip];
     87   EXPECT_NSNE(reloadImage, stopImage);
     88   EXPECT_NSNE(reloadToolTip, stopToolTip);
     89 
     90   [button_ updateTag:IDC_RELOAD];
     91   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
     92   EXPECT_NSEQ(reloadImage, [button_ image]);
     93   EXPECT_NSEQ(reloadToolTip, [button_ toolTip]);
     94 }
     95 
     96 // Test that when forcing the mode, it takes effect immediately,
     97 // regardless of whether the mouse is hovering.
     98 TEST_F(ReloadButtonTest, SetIsLoadingForce) {
     99   EXPECT_FALSE([button_ isMouseInside]);
    100   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    101 
    102   // Changes to stop immediately.
    103   [button_ setIsLoading:YES force:YES];
    104   EXPECT_EQ(IDC_STOP, [button_ tag]);
    105 
    106   // Changes to reload immediately.
    107   [button_ setIsLoading:NO force:YES];
    108   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    109 
    110   // Changes to stop immediately when the mouse is hovered, and
    111   // doesn't change when the mouse exits.
    112   [button_ mouseEntered:nil];
    113   EXPECT_TRUE([button_ isMouseInside]);
    114   [button_ setIsLoading:YES force:YES];
    115   EXPECT_EQ(IDC_STOP, [button_ tag]);
    116   [button_ mouseExited:nil];
    117   EXPECT_FALSE([button_ isMouseInside]);
    118   EXPECT_EQ(IDC_STOP, [button_ tag]);
    119 
    120   // Changes to reload immediately when the mouse is hovered, and
    121   // doesn't change when the mouse exits.
    122   [button_ mouseEntered:nil];
    123   EXPECT_TRUE([button_ isMouseInside]);
    124   [button_ setIsLoading:NO force:YES];
    125   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    126   [button_ mouseExited:nil];
    127   EXPECT_FALSE([button_ isMouseInside]);
    128   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    129 }
    130 
    131 // Test that without force, stop mode is set immediately, but reload
    132 // is affected by the hover status.
    133 TEST_F(ReloadButtonTest, SetIsLoadingNoForceUnHover) {
    134   EXPECT_FALSE([button_ isMouseInside]);
    135   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    136 
    137   // Changes to stop immediately when the mouse is not hovering.
    138   [button_ setIsLoading:YES force:NO];
    139   EXPECT_EQ(IDC_STOP, [button_ tag]);
    140 
    141   // Changes to reload immediately when the mouse is not hovering.
    142   [button_ setIsLoading:NO force:NO];
    143   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    144 
    145   // Changes to stop immediately when the mouse is hovered, and
    146   // doesn't change when the mouse exits.
    147   [button_ mouseEntered:nil];
    148   EXPECT_TRUE([button_ isMouseInside]);
    149   [button_ setIsLoading:YES force:NO];
    150   EXPECT_EQ(IDC_STOP, [button_ tag]);
    151   [button_ mouseExited:nil];
    152   EXPECT_FALSE([button_ isMouseInside]);
    153   EXPECT_EQ(IDC_STOP, [button_ tag]);
    154 
    155   // Does not change to reload immediately when the mouse is hovered,
    156   // changes when the mouse exits.
    157   [button_ mouseEntered:nil];
    158   EXPECT_TRUE([button_ isMouseInside]);
    159   [button_ setIsLoading:NO force:NO];
    160   EXPECT_EQ(IDC_STOP, [button_ tag]);
    161   [button_ mouseExited:nil];
    162   EXPECT_FALSE([button_ isMouseInside]);
    163   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    164 }
    165 
    166 // Test that without force, stop mode is set immediately, and reload
    167 // will be set after a timeout.
    168 // TODO(shess): Reenable, http://crbug.com/61485
    169 TEST_F(ReloadButtonTest, DISABLED_SetIsLoadingNoForceTimeout) {
    170   // When the event loop first spins, some delayed tracking-area setup
    171   // is done, which causes -mouseExited: to be called.  Spin it at
    172   // least once, and dequeue any pending events.
    173   // TODO(shess): It would be more reasonable to have an MockNSTimer
    174   // factory for the class to use, which this code could fire
    175   // directly.
    176   while ([NSApp nextEventMatchingMask:NSAnyEventMask
    177                             untilDate:nil
    178                                inMode:NSDefaultRunLoopMode
    179                               dequeue:YES]) {
    180   }
    181 
    182   const NSTimeInterval kShortTimeout = 0.1;
    183   [ReloadButton setPendingReloadTimeout:kShortTimeout];
    184 
    185   EXPECT_FALSE([button_ isMouseInside]);
    186   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    187 
    188   // Move the mouse into the button and press it.
    189   [button_ mouseEntered:nil];
    190   EXPECT_TRUE([button_ isMouseInside]);
    191   [button_ setIsLoading:YES force:NO];
    192   EXPECT_EQ(IDC_STOP, [button_ tag]);
    193 
    194   // Does not change to reload immediately when the mouse is hovered.
    195   EXPECT_TRUE([button_ isMouseInside]);
    196   [button_ setIsLoading:NO force:NO];
    197   EXPECT_TRUE([button_ isMouseInside]);
    198   EXPECT_EQ(IDC_STOP, [button_ tag]);
    199   EXPECT_TRUE([button_ isMouseInside]);
    200 
    201   // Spin event loop until the timeout passes.
    202   NSDate* pastTimeout = [NSDate dateWithTimeIntervalSinceNow:2 * kShortTimeout];
    203   [NSApp nextEventMatchingMask:NSAnyEventMask
    204                      untilDate:pastTimeout
    205                         inMode:NSDefaultRunLoopMode
    206                        dequeue:NO];
    207 
    208   // Mouse is still hovered, button is in reload mode.  If the mouse
    209   // is no longer hovered, see comment at top of function.
    210   EXPECT_TRUE([button_ isMouseInside]);
    211   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    212 }
    213 
    214 // Test that pressing stop after reload mode has been requested
    215 // doesn't forward the stop message.
    216 TEST_F(ReloadButtonTest, StopAfterReloadSet) {
    217   id mock_target = [OCMockObject mockForProtocol:@protocol(TargetActionMock)];
    218   [button_ setTarget:mock_target];
    219   [button_ setAction:@selector(anAction:)];
    220 
    221   EXPECT_FALSE([button_ isMouseInside]);
    222 
    223   // Get to stop mode.
    224   [button_ setIsLoading:YES force:YES];
    225   EXPECT_EQ(IDC_STOP, [button_ tag]);
    226   EXPECT_TRUE([button_ isEnabled]);
    227 
    228   // Expect the action once.
    229   [[mock_target expect] anAction:button_];
    230 
    231   // Clicking in stop mode should send the action and transition to
    232   // reload mode.
    233   const std::pair<NSEvent*,NSEvent*> click =
    234       test_event_utils::MouseClickInView(button_, 1);
    235   [NSApp postEvent:click.second atStart:YES];
    236   [button_ mouseDown:click.first];
    237   EXPECT_EQ(IDC_RELOAD, [button_ tag]);
    238   EXPECT_TRUE([button_ isEnabled]);
    239 
    240   // Get back to stop mode.
    241   [button_ setIsLoading:YES force:YES];
    242   EXPECT_EQ(IDC_STOP, [button_ tag]);
    243   EXPECT_TRUE([button_ isEnabled]);
    244 
    245   // If hover prevented reload mode immediately taking effect, clicks should do
    246   // nothing, because the button should be disabled.
    247   [button_ mouseEntered:nil];
    248   EXPECT_TRUE([button_ isMouseInside]);
    249   [button_ setIsLoading:NO force:NO];
    250   EXPECT_EQ(IDC_STOP, [button_ tag]);
    251   EXPECT_FALSE([button_ isEnabled]);
    252   [NSApp postEvent:click.second atStart:YES];
    253   [button_ mouseDown:click.first];
    254   EXPECT_EQ(IDC_STOP, [button_ tag]);
    255 
    256   [button_ setTarget:nil];
    257 }
    258 
    259 }  // namespace
    260