root/ash/frame/caption_buttons/alternate_frame_size_button_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetContentsView
  2. CanResize
  3. CanMaximize
  4. caption_button_container
  5. Layout
  6. ViewHierarchyChanged
  7. CenterPointInScreen
  8. HasStateType
  9. AllButtonsInNormalState
  10. CreateWidget
  11. SetUp
  12. window_state
  13. window_state
  14. minimize_button
  15. size_button
  16. close_button
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F
  22. TEST_F
  23. TEST_F
  24. SetUp
  25. TearDown
  26. TEST_F

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/frame/caption_buttons/alternate_frame_size_button.h"

#include "ash/ash_switches.h"
#include "ash/frame/caption_buttons/frame_caption_button.h"
#include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/window_state.h"
#include "base/command_line.h"
#include "base/i18n/rtl.h"
#include "grit/ash_resources.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/gestures/gesture_configuration.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

namespace ash {
namespace test {

namespace {

class TestWidgetDelegate : public views::WidgetDelegateView {
 public:
  TestWidgetDelegate() {}
  virtual ~TestWidgetDelegate() {}

  // Overridden from views::WidgetDelegate:
  virtual views::View* GetContentsView() OVERRIDE {
    return this;
  }
  virtual bool CanResize() const OVERRIDE {
    return true;
  }
  virtual bool CanMaximize() const OVERRIDE {
    return true;
  }

  ash::FrameCaptionButtonContainerView* caption_button_container() {
    return caption_button_container_;
  }

 private:
  // Overridden from views::View:
  virtual void Layout() OVERRIDE {
    caption_button_container_->Layout();

    // Right align the caption button container.
    gfx::Size preferred_size = caption_button_container_->GetPreferredSize();
    caption_button_container_->SetBounds(width() - preferred_size.width(), 0,
        preferred_size.width(), preferred_size.height());
  }

  virtual void ViewHierarchyChanged(
      const ViewHierarchyChangedDetails& details) OVERRIDE {
    if (details.is_add && details.child == this) {
      caption_button_container_ = new FrameCaptionButtonContainerView(
          GetWidget(), FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);

      // Set arbitrary images for the container's buttons so that the buttons
      // have non-empty sizes.
      for (int icon = 0; icon < CAPTION_BUTTON_ICON_COUNT; ++icon) {
        caption_button_container_->SetButtonImages(
            static_cast<CaptionButtonIcon>(icon),
            IDR_AURA_WINDOW_CONTROL_ICON_CLOSE,
            IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I,
            IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
            IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
      }

      AddChildView(caption_button_container_);
    }
  }

  // Not owned.
  ash::FrameCaptionButtonContainerView* caption_button_container_;

  DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate);
};

}  // namespace

class AlternateFrameSizeButtonTest : public AshTestBase {
 public:
  AlternateFrameSizeButtonTest() {}
  virtual ~AlternateFrameSizeButtonTest() {}

  // Returns the center point of |view| in screen coordinates.
  gfx::Point CenterPointInScreen(views::View* view) {
    return view->GetBoundsInScreen().CenterPoint();
  }

  // Returns true if the window has |state_type|.
  bool HasStateType(wm::WindowStateType state_type) const {
    return window_state()->GetStateType() == state_type;
  }

  // Returns true if all three buttons are in the normal state.
  bool AllButtonsInNormalState() const {
    return minimize_button_->state() == views::Button::STATE_NORMAL &&
        size_button_->state() == views::Button::STATE_NORMAL &&
        close_button_->state() == views::Button::STATE_NORMAL;
  }

  // Creates a widget with |delegate|. The returned widget takes ownership of
  // |delegate|.
  views::Widget* CreateWidget(views::WidgetDelegate* delegate) {
    views::Widget* widget = new views::Widget;
    views::Widget::InitParams params(
        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    params.context = CurrentContext();
    params.delegate = delegate;
    params.bounds = gfx::Rect(10, 10, 100, 100);
    widget->Init(params);
    widget->Show();
    return widget;
  }

  // AshTestBase overrides:
  virtual void SetUp() OVERRIDE {
    AshTestBase::SetUp();

    CommandLine* command_line = CommandLine::ForCurrentProcess();
    command_line->AppendSwitch(
        switches::kAshEnableAlternateFrameCaptionButtonStyle);

    TestWidgetDelegate* delegate = new TestWidgetDelegate();
    window_state_ = ash::wm::GetWindowState(
        CreateWidget(delegate)->GetNativeWindow());

    FrameCaptionButtonContainerView::TestApi test(
        delegate->caption_button_container());

    minimize_button_ = test.minimize_button();
    size_button_ = test.size_button();
    static_cast<AlternateFrameSizeButton*>(
        size_button_)->set_delay_to_set_buttons_to_snap_mode(0);
    close_button_ = test.close_button();
  }

  ash::wm::WindowState* window_state() { return window_state_; }
  const ash::wm::WindowState* window_state() const { return window_state_; }

  FrameCaptionButton* minimize_button() { return minimize_button_; }
  FrameCaptionButton* size_button() { return size_button_; }
  FrameCaptionButton* close_button() { return close_button_; }

 private:
  // Not owned.
  ash::wm::WindowState* window_state_;
  FrameCaptionButton* minimize_button_;
  FrameCaptionButton* size_button_;
  FrameCaptionButton* close_button_;

  DISALLOW_COPY_AND_ASSIGN(AlternateFrameSizeButtonTest);
};

// Tests that pressing the left mouse button or tapping down on the size button
// puts the button into the pressed state.
TEST_F(AlternateFrameSizeButtonTest, PressedState) {
  aura::test::EventGenerator& generator = GetEventGenerator();
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressLeftButton();
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  generator.ReleaseLeftButton();
  RunAllPendingInMessageLoop();
  EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());

  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressTouchId(3);
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  generator.ReleaseTouchId(3);
  RunAllPendingInMessageLoop();
  EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
}

// Tests that clicking on the size button toggles between the maximized and
// normal state.
TEST_F(AlternateFrameSizeButtonTest, ClickSizeButtonTogglesMaximize) {
  EXPECT_FALSE(window_state()->IsMaximized());

  aura::test::EventGenerator& generator = GetEventGenerator();
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.ClickLeftButton();
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(window_state()->IsMaximized());

  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.ClickLeftButton();
  RunAllPendingInMessageLoop();
  EXPECT_FALSE(window_state()->IsMaximized());

  generator.GestureTapAt(CenterPointInScreen(size_button()));
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(window_state()->IsMaximized());

  generator.GestureTapAt(CenterPointInScreen(size_button()));
  RunAllPendingInMessageLoop();
  EXPECT_FALSE(window_state()->IsMaximized());
}

// Test that clicking + dragging to a button adjacent to the size button snaps
// the window left or right.
TEST_F(AlternateFrameSizeButtonTest, ButtonDrag) {
  EXPECT_TRUE(window_state()->IsNormalStateType());

  // 1) Test by dragging the mouse.
  // Snap right.
  aura::test::EventGenerator& generator = GetEventGenerator();
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressLeftButton();
  generator.MoveMouseTo(CenterPointInScreen(close_button()));
  generator.ReleaseLeftButton();
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED));

  // Snap left.
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressLeftButton();
  generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
  generator.ReleaseLeftButton();
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));

  // 2) Test with scroll gestures.
  // Snap right.
  generator.GestureScrollSequence(
      CenterPointInScreen(size_button()),
      CenterPointInScreen(close_button()),
      base::TimeDelta::FromMilliseconds(100),
      3);
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED));

  // Snap left.
  generator.GestureScrollSequence(
      CenterPointInScreen(size_button()),
      CenterPointInScreen(minimize_button()),
      base::TimeDelta::FromMilliseconds(100),
      3);
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));

  // 3) Test with tap gestures.
  const int touch_default_radius =
      ui::GestureConfiguration::default_radius();
  ui::GestureConfiguration::set_default_radius(0);
  // Snap right.
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button()));
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED));
  // Snap left.
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button()));
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));
  ui::GestureConfiguration::set_default_radius(touch_default_radius);
}

// Test that clicking, dragging, and overshooting the minimize button a bit
// horizontally still snaps the window left.
TEST_F(AlternateFrameSizeButtonTest, SnapLeftOvershootMinimize) {
  EXPECT_TRUE(window_state()->IsNormalStateType());

  aura::test::EventGenerator& generator = GetEventGenerator();
  generator.MoveMouseTo(CenterPointInScreen(size_button()));

  generator.PressLeftButton();
  // Move to the minimize button.
  generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
  // Overshoot the minimize button.
  generator.MoveMouseBy(-minimize_button()->width(), 0);
  generator.ReleaseLeftButton();
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));
}

// Test that right clicking the size button has no effect.
TEST_F(AlternateFrameSizeButtonTest, RightMouseButton) {
  EXPECT_TRUE(window_state()->IsNormalStateType());

  aura::test::EventGenerator& generator = GetEventGenerator();
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressRightButton();
  generator.ReleaseRightButton();
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(window_state()->IsNormalStateType());
}

// Test that upon releasing the mouse button after having pressed the size
// button
// - The state of all the caption buttons is reset.
// - The icon displayed by all of the caption buttons is reset.
TEST_F(AlternateFrameSizeButtonTest, ResetButtonsAfterClick) {
  EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
  EXPECT_TRUE(AllButtonsInNormalState());

  // Pressing the size button should result in the size button being pressed and
  // the minimize and close button icons changing.
  aura::test::EventGenerator& generator = GetEventGenerator();
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressLeftButton();
  EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());

  // Dragging the mouse over the minimize button should hover the minimize
  // button and the minimize and close button icons should stay changed.
  generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
  EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state());
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());

  // Release the mouse, snapping the window left.
  generator.ReleaseLeftButton();
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));

  // None of the buttons should stay pressed and the buttons should have their
  // regular icons.
  EXPECT_TRUE(AllButtonsInNormalState());
  EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());

  // Repeat test but release button where it does not affect the window's state
  // because the code path is different.
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressLeftButton();
  EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());

  const gfx::Rect& kWorkAreaBoundsInScreen =
      ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
  generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left());

  // None of the buttons should be pressed because we are really far away from
  // any of the caption buttons. The minimize and close button icons should
  // be changed because the mouse is pressed.
  EXPECT_TRUE(AllButtonsInNormalState());
  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());

  // Release the mouse. The window should stay snapped left.
  generator.ReleaseLeftButton();
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));

  // The buttons should stay unpressed and the buttons should now have their
  // regular icons.
  EXPECT_TRUE(AllButtonsInNormalState());
  EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
}

// Test that the size button is pressed whenever the snap left/right buttons
// are hovered.
TEST_F(AlternateFrameSizeButtonTest, SizeButtonPressedWhenSnapButtonHovered) {
  EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
  EXPECT_TRUE(AllButtonsInNormalState());

  // Pressing the size button should result in the size button being pressed and
  // the minimize and close button icons changing.
  aura::test::EventGenerator& generator = GetEventGenerator();
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressLeftButton();
  EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());

  // Dragging the mouse over the minimize button (snap left button) should hover
  // the minimize button and keep the size button pressed.
  generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
  EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state());
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());

  // Moving the mouse far away from the caption buttons and then moving it over
  // the close button (snap right button) should hover the close button and
  // keep the size button pressed.
  const gfx::Rect& kWorkAreaBoundsInScreen =
      ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
  generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left());
  EXPECT_TRUE(AllButtonsInNormalState());
  generator.MoveMouseTo(CenterPointInScreen(close_button()));
  EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  EXPECT_EQ(views::Button::STATE_HOVERED, close_button()->state());
}

class AlternateFrameSizeButtonTestRTL : public AlternateFrameSizeButtonTest {
 public:
  AlternateFrameSizeButtonTestRTL() {}
  virtual ~AlternateFrameSizeButtonTestRTL() {}

  virtual void SetUp() OVERRIDE {
    original_locale_ = l10n_util::GetApplicationLocale(std::string());
    base::i18n::SetICUDefaultLocale("he");

    AlternateFrameSizeButtonTest::SetUp();
  }

  virtual void TearDown() OVERRIDE {
    AlternateFrameSizeButtonTest::TearDown();
    base::i18n::SetICUDefaultLocale(original_locale_);
  }

 private:
  std::string original_locale_;

  DISALLOW_COPY_AND_ASSIGN(AlternateFrameSizeButtonTestRTL);
};

// Test that clicking + dragging to a button adjacent to the size button presses
// the correct button and snaps the window to the correct side.
TEST_F(AlternateFrameSizeButtonTestRTL, ButtonDrag) {
  // In RTL the close button should be left of the size button and the minimize
  // button should be right of the size button.
  ASSERT_LT(close_button()->GetBoundsInScreen().x(),
            size_button()->GetBoundsInScreen().x());
  ASSERT_LT(size_button()->GetBoundsInScreen().x(),
            minimize_button()->GetBoundsInScreen().x());

  // Test initial state.
  EXPECT_TRUE(window_state()->IsNormalStateType());
  EXPECT_TRUE(AllButtonsInNormalState());
  EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());

  // Pressing the size button should swap the icons of the minimize and close
  // buttons to icons for snapping right and for snapping left respectively.
  aura::test::EventGenerator& generator = GetEventGenerator();
  generator.MoveMouseTo(CenterPointInScreen(size_button()));
  generator.PressLeftButton();
  EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, close_button()->icon());

  // Dragging over to the minimize button should press it.
  generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
  EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state());
  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());

  // Releasing should snap the window right.
  generator.ReleaseLeftButton();
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED));

  // None of the buttons should stay pressed and the buttons should have their
  // regular icons.
  EXPECT_TRUE(AllButtonsInNormalState());
  EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
  EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
}

}  // namespace test
}  // namespace ash

/* [<][>][^][v][top][bottom][index][help] */