Index: chrome/browser/ui/views/toolbar/toolbar_action_view_unittest.cc |
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fe71157957ea14682cdd0ff446d3e5cd94a05f5b |
--- /dev/null |
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view_unittest.cc |
@@ -0,0 +1,267 @@ |
+// Copyright 2015 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 "base/run_loop.h" |
+#include "base/strings/string16.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "chrome/browser/sessions/session_tab_helper.h" |
+#include "chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h" |
+#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" |
+#include "chrome/browser/ui/views/toolbar/toolbar_action_view.h" |
+#include "chrome/test/base/testing_profile.h" |
+#include "content/public/test/test_browser_thread.h" |
+#include "content/public/test/test_renderer_host.h" |
+#include "content/public/test/web_contents_tester.h" |
+#include "ui/accessibility/ax_view_state.h" |
+#include "ui/events/test/event_generator.h" |
+#include "ui/views/test/views_test_base.h" |
+ |
+#if defined(USE_AURA) |
+#include "ui/aura/env.h" |
+#endif |
+ |
+namespace { |
+ |
+// A helper class to create test web contents (tabs) for unit tests, without |
+// inheriting from RenderViewTestHarness. Can create web contents, and will |
+// clean up after itself upon destruction. Owns all created web contents. |
+// A few notes: |
+// - Works well allocated on the stack, because it should be destroyed before |
+// associated browser context. |
+// - Doesn't play nice with web contents created any other way (because of |
+// the implementation of RenderViewHostTestEnabler). But if you are creating |
+// web contents already, what do you need this for? ;) |
+// TODO(devlin): Look around and see if this class is needed elsewhere; if so, |
+// move it there and expand the API a bit (methods to, e.g., delete/close a |
+// web contents, access existing web contents, etc). |
+class TestWebContentsFactory { |
+ public: |
+ // |init_aura| initializes the aura environment (and cleans it up at |
+ // shutdown, which is necessary for web contents. Since this method should |
+ // only be called once, this should only be true if no other part of the test |
+ // has initialized the environment. |
+ explicit TestWebContentsFactory(bool init_aura); |
+ ~TestWebContentsFactory(); |
+ |
+ // Creates a new WebContents with the given |context|, and returns it. |
+ content::WebContents* CreateWebContents(content::BrowserContext* context); |
+ private: |
+ // The test factory (and friends) for creating test web contents. |
+ scoped_ptr<content::RenderViewHostTestEnabler> rvh_enabler_; |
+ // The vector of web contents that this class created. |
+ ScopedVector<content::WebContents> web_contents_; |
+ |
+ // True if the factory initialized aura (and should thus tear it down). |
+ bool init_aura_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TestWebContentsFactory); |
+}; |
+ |
+TestWebContentsFactory::TestWebContentsFactory(bool init_aura) |
+ : rvh_enabler_(new content::RenderViewHostTestEnabler()), |
+ init_aura_(init_aura) { |
+#if defined(USE_AURA) |
+ if (init_aura) |
+ aura::Env::CreateInstance(true); |
+#endif |
+} |
+ |
+TestWebContentsFactory::~TestWebContentsFactory() { |
+ web_contents_.clear(); |
+ // Let any posted tasks for web contents deletion run. |
+ base::RunLoop().RunUntilIdle(); |
+ rvh_enabler_.reset(); |
+ // Let any posted tasks for RenderProcess/ViewHost deletion run. |
+ base::RunLoop().RunUntilIdle(); |
+#if defined(USE_AURA) |
+ if (init_aura_) |
+ aura::Env::DeleteInstance(); |
+#endif |
+} |
+ |
+content::WebContents* TestWebContentsFactory::CreateWebContents( |
+ content::BrowserContext* context) { |
+ scoped_ptr<content::WebContents> web_contents( |
+ content::WebContentsTester::CreateTestWebContents(context, nullptr)); |
+ DCHECK(web_contents); |
+ web_contents_.push_back(web_contents.release()); |
+ return web_contents_.back(); |
+} |
+ |
+// A test delegate for a toolbar action view. |
+class TestToolbarActionViewDelegate : public ToolbarActionView::Delegate { |
+ public: |
+ TestToolbarActionViewDelegate() : shown_in_menu_(false), |
+ overflow_reference_view_(nullptr), |
+ web_contents_(nullptr) {} |
+ ~TestToolbarActionViewDelegate() override {} |
+ |
+ // ToolbarActionView::Delegate: |
+ content::WebContents* GetCurrentWebContents() override { |
+ return web_contents_; |
+ } |
+ bool ShownInsideMenu() const override { return shown_in_menu_; } |
+ void OnToolbarActionViewDragDone() override {} |
+ views::MenuButton* GetOverflowReferenceView() override { |
+ return overflow_reference_view_; |
+ } |
+ void SetPopupOwner(ToolbarActionView* popup_owner) override {} |
+ ToolbarActionView* GetMainViewForAction(ToolbarActionView* view) override { |
+ return nullptr; |
+ } |
+ void WriteDragDataForView(views::View* sender, |
+ const gfx::Point& press_pt, |
+ ui::OSExchangeData* data) override {} |
+ int GetDragOperationsForView(views::View* sender, |
+ const gfx::Point& p) override { |
+ return ui::DragDropTypes::DRAG_NONE; |
+ } |
+ bool CanStartDragForView(views::View* sender, |
+ const gfx::Point& press_pt, |
+ const gfx::Point& p) override { return false; } |
+ |
+ void set_shown_in_menu(bool shown_in_menu) { shown_in_menu_ = shown_in_menu; } |
+ void set_overflow_reference_view(views::MenuButton* overflow_reference_view) { |
+ overflow_reference_view_ = overflow_reference_view; |
+ } |
+ void set_web_contents(content::WebContents* web_contents) { |
+ web_contents_ = web_contents; |
+ } |
+ |
+ private: |
+ bool shown_in_menu_; |
+ |
+ views::MenuButton* overflow_reference_view_; |
+ |
+ content::WebContents* web_contents_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TestToolbarActionViewDelegate); |
+}; |
+ |
+} // namespace |
+ |
+class ToolbarActionViewUnitTest : public views::ViewsTestBase { |
+ public: |
+ ToolbarActionViewUnitTest() |
+ : widget_(nullptr), |
+ ui_thread_(content::BrowserThread::UI, message_loop()) {} |
+ ~ToolbarActionViewUnitTest() override {} |
+ |
+ void SetUp() override { |
+ views::ViewsTestBase::SetUp(); |
+ |
+ widget_ = new views::Widget; |
+ views::Widget::InitParams params = |
+ CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
+ params.bounds = gfx::Rect(0, 0, 200, 200); |
+ widget_->Init(params); |
+ } |
+ void TearDown() override { |
+ if (!widget_->IsClosed()) |
+ widget_->Close(); |
+ views::ViewsTestBase::TearDown(); |
+ } |
+ |
+ views::Widget* widget() { return widget_; } |
+ |
+ private: |
+ // The widget managed by this test. |
+ views::Widget* widget_; |
+ |
+ // Web contents need a fake ui thread. |
+ content::TestBrowserThread ui_thread_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ToolbarActionViewUnitTest); |
+}; |
+ |
+// Test the basic ui of a ToolbarActionView and that it responds correctly to |
+// a controller's state. |
+TEST_F(ToolbarActionViewUnitTest, BasicToolbarActionViewTest) { |
+ TestingProfile profile; |
+ |
+ // ViewsTestBase initializees the aura environment, so the factory shouldn't. |
+ TestWebContentsFactory web_contents_factory(false); |
+ |
+ TestToolbarActionViewController controller("fake controller"); |
+ TestToolbarActionViewDelegate action_view_delegate; |
+ |
+ // Configure the test controller and delegate. |
+ base::string16 name = base::ASCIIToUTF16("name"); |
+ controller.SetAccessibleName(name); |
+ base::string16 tooltip = base::ASCIIToUTF16("tooltip"); |
+ controller.SetTooltip(tooltip); |
+ content::WebContents* web_contents = |
+ web_contents_factory.CreateWebContents(&profile); |
+ SessionTabHelper::CreateForWebContents(web_contents); |
+ action_view_delegate.set_web_contents(web_contents); |
+ |
+ // Move the mouse off the not-yet-existent button. |
+ ui::test::EventGenerator generator(GetContext(), widget()->GetNativeWindow()); |
+ generator.MoveMouseTo(gfx::Point(300, 300)); |
+ |
+ // Create a new toolbar action view. |
+ ToolbarActionView view(&controller, &profile, &action_view_delegate); |
+ view.set_owned_by_client(); |
+ view.SetBoundsRect(gfx::Rect(0, 0, 200, 20)); |
+ widget()->SetContentsView(&view); |
+ widget()->Show(); |
+ |
+ // Check that the tooltip and accessible state of the view match the |
+ // controller's. |
+ base::string16 tooltip_test; |
+ EXPECT_TRUE(view.GetTooltipText(gfx::Point(), &tooltip_test)); |
+ EXPECT_EQ(tooltip, tooltip_test); |
+ ui::AXViewState ax_state; |
+ view.GetAccessibleState(&ax_state); |
+ EXPECT_EQ(name, ax_state.name); |
+ |
+ // The button should start in normal state, with no actions executed. |
+ EXPECT_EQ(views::Button::STATE_NORMAL, view.state()); |
+ EXPECT_EQ(0, controller.execute_action_count()); |
+ |
+ // Click the button. This should execute it. |
+ generator.MoveMouseTo(gfx::Point(10, 10)); |
+ generator.ClickLeftButton(); |
+ EXPECT_EQ(1, controller.execute_action_count()); |
+ |
+ // Move the mouse off the button, and show a popup through a non-user action. |
+ // Since this was not a user action, the button should not be pressed. |
+ generator.MoveMouseTo(gfx::Point(300, 300)); |
+ controller.ShowPopup(false); |
+ EXPECT_EQ(views::Button::STATE_NORMAL, view.state()); |
+ controller.HidePopup(); |
+ |
+ // Show the popup through a user action - the button should be pressed. |
+ controller.ShowPopup(true); |
+ EXPECT_EQ(views::Button::STATE_PRESSED, view.state()); |
+ controller.HidePopup(); |
+ EXPECT_EQ(views::Button::STATE_NORMAL, view.state()); |
+ |
+ // Ensure that the button's enabled state reflects that of the controller. |
+ controller.SetEnabled(false); |
+ EXPECT_EQ(views::Button::STATE_DISABLED, view.state()); |
+ controller.SetEnabled(true); |
+ EXPECT_EQ(views::Button::STATE_NORMAL, view.state()); |
+ |
+ // Ensure that the button's want-to-run state reflects that of the controller. |
+ controller.SetWantsToRun(true); |
+ EXPECT_TRUE(view.wants_to_run_for_testing()); |
+ controller.SetWantsToRun(false); |
+ EXPECT_FALSE(view.wants_to_run_for_testing()); |
+ |
+ // Create an overflow button. |
+ views::MenuButton overflow_button(nullptr, base::string16(), nullptr, false); |
+ overflow_button.set_owned_by_client(); |
+ action_view_delegate.set_overflow_reference_view(&overflow_button); |
+ |
+ // If the view isn't visible, the overflow button should be pressed for |
+ // popups. |
+ view.SetVisible(false); |
+ controller.ShowPopup(true); |
+ EXPECT_EQ(views::Button::STATE_NORMAL, view.state()); |
+ EXPECT_EQ(views::Button::STATE_PRESSED, overflow_button.state()); |
+ controller.HidePopup(); |
+ EXPECT_EQ(views::Button::STATE_NORMAL, view.state()); |
+ EXPECT_EQ(views::Button::STATE_NORMAL, overflow_button.state()); |
+} |