| // Copyright 2017 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/system/status_area_widget.h" |
| |
| #include "ash/focus_cycler.h" |
| #include "ash/keyboard/ui/keyboard_controller.h" |
| #include "ash/keyboard/ui/keyboard_util.h" |
| #include "ash/keyboard/ui/test/keyboard_test_util.h" |
| #include "ash/public/cpp/ash_switches.h" |
| #include "ash/public/cpp/keyboard/keyboard_switches.h" |
| #include "ash/public/cpp/system_tray_focus_observer.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/session/test_session_controller_client.h" |
| #include "ash/shell.h" |
| #include "ash/system/ime_menu/ime_menu_tray.h" |
| #include "ash/system/overview/overview_button_tray.h" |
| #include "ash/system/palette/palette_tray.h" |
| #include "ash/system/session/logout_button_tray.h" |
| #include "ash/system/status_area_widget_test_helper.h" |
| #include "ash/system/tray/system_tray_notifier.h" |
| #include "ash/system/unified/unified_system_tray.h" |
| #include "ash/system/virtual_keyboard/virtual_keyboard_tray.h" |
| #include "ash/test/ash_test_base.h" |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "chromeos/dbus/shill/shill_clients.h" |
| #include "chromeos/network/network_handler.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/session_manager/session_manager_types.h" |
| #include "ui/events/test/event_generator.h" |
| |
| using session_manager::SessionState; |
| |
| namespace ash { |
| |
| using StatusAreaWidgetTest = AshTestBase; |
| |
| // Tests that status area trays are constructed. |
| TEST_F(StatusAreaWidgetTest, Basics) { |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| |
| // Status area is visible by default. |
| EXPECT_TRUE(status->IsVisible()); |
| |
| // No bubbles are open at startup. |
| EXPECT_FALSE(status->IsMessageBubbleShown()); |
| |
| // Auto-hidden shelf would not be forced to be visible. |
| EXPECT_FALSE(status->ShouldShowShelf()); |
| |
| // Default trays are constructed. |
| EXPECT_TRUE(status->overview_button_tray()); |
| EXPECT_TRUE(status->unified_system_tray()); |
| EXPECT_TRUE(status->logout_button_tray_for_testing()); |
| EXPECT_TRUE(status->ime_menu_tray()); |
| EXPECT_TRUE(status->virtual_keyboard_tray_for_testing()); |
| EXPECT_TRUE(status->palette_tray()); |
| |
| // Default trays are visible. |
| EXPECT_FALSE(status->overview_button_tray()->GetVisible()); |
| EXPECT_TRUE(status->unified_system_tray()->GetVisible()); |
| EXPECT_FALSE(status->logout_button_tray_for_testing()->GetVisible()); |
| EXPECT_FALSE(status->ime_menu_tray()->GetVisible()); |
| EXPECT_FALSE(status->virtual_keyboard_tray_for_testing()->GetVisible()); |
| } |
| |
| class SystemTrayFocusTestObserver : public SystemTrayFocusObserver { |
| public: |
| SystemTrayFocusTestObserver() = default; |
| ~SystemTrayFocusTestObserver() override = default; |
| |
| int focus_out_count() { return focus_out_count_; } |
| int reverse_focus_out_count() { return reverse_focus_out_count_; } |
| |
| protected: |
| // SystemTrayFocusObserver: |
| void OnFocusLeavingSystemTray(bool reverse) override { |
| reverse ? ++reverse_focus_out_count_ : ++focus_out_count_; |
| } |
| |
| private: |
| int focus_out_count_ = 0; |
| int reverse_focus_out_count_ = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(SystemTrayFocusTestObserver); |
| }; |
| |
| class StatusAreaWidgetFocusTest : public AshTestBase { |
| public: |
| StatusAreaWidgetFocusTest() = default; |
| ~StatusAreaWidgetFocusTest() override = default; |
| |
| // AshTestBase: |
| void SetUp() override { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kShowWebUiLock); |
| |
| AshTestBase::SetUp(); |
| test_observer_.reset(new SystemTrayFocusTestObserver); |
| Shell::Get()->system_tray_notifier()->AddSystemTrayFocusObserver( |
| test_observer_.get()); |
| } |
| |
| // AshTestBase: |
| void TearDown() override { |
| Shell::Get()->system_tray_notifier()->RemoveSystemTrayFocusObserver( |
| test_observer_.get()); |
| test_observer_.reset(); |
| AshTestBase::TearDown(); |
| } |
| |
| void GenerateTabEvent(bool reverse) { |
| ui::KeyEvent tab_pressed(ui::ET_KEY_PRESSED, ui::VKEY_TAB, |
| reverse ? ui::EF_SHIFT_DOWN : ui::EF_NONE); |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget()->OnKeyEvent(&tab_pressed); |
| } |
| |
| protected: |
| std::unique_ptr<SystemTrayFocusTestObserver> test_observer_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(StatusAreaWidgetFocusTest); |
| }; |
| |
| // Tests that tab traversal through status area widget in non-active session |
| // could properly send FocusOut event. |
| // TODO(crbug.com/934939): Failing on trybot. |
| TEST_F(StatusAreaWidgetFocusTest, DISABLED_FocusOutObserverUnified) { |
| // Set session state to LOCKED. |
| SessionControllerImpl* session = Shell::Get()->session_controller(); |
| ASSERT_TRUE(session->IsActiveUserSessionStarted()); |
| TestSessionControllerClient* client = GetSessionControllerClient(); |
| client->SetSessionState(SessionState::LOCKED); |
| ASSERT_TRUE(session->IsScreenLocked()); |
| |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| // Default trays are constructed. |
| ASSERT_TRUE(status->overview_button_tray()); |
| ASSERT_TRUE(status->unified_system_tray()); |
| ASSERT_TRUE(status->logout_button_tray_for_testing()); |
| ASSERT_TRUE(status->ime_menu_tray()); |
| ASSERT_TRUE(status->virtual_keyboard_tray_for_testing()); |
| |
| // Default trays are visible. |
| ASSERT_FALSE(status->overview_button_tray()->GetVisible()); |
| ASSERT_TRUE(status->unified_system_tray()->GetVisible()); |
| ASSERT_FALSE(status->logout_button_tray_for_testing()->GetVisible()); |
| ASSERT_FALSE(status->ime_menu_tray()->GetVisible()); |
| ASSERT_FALSE(status->virtual_keyboard_tray_for_testing()->GetVisible()); |
| |
| // In Unified, we don't have notification tray, so ImeMenuTray is used for |
| // tab testing. |
| status->ime_menu_tray()->OnIMEMenuActivationChanged(true); |
| ASSERT_TRUE(status->ime_menu_tray()->GetVisible()); |
| |
| // Set focus to status area widget. The first focused view will be the IME |
| // tray. |
| ASSERT_TRUE(Shell::Get()->focus_cycler()->FocusWidget(status)); |
| views::FocusManager* focus_manager = status->GetFocusManager(); |
| EXPECT_EQ(status->ime_menu_tray(), focus_manager->GetFocusedView()); |
| |
| // A tab key event will move focus to the system tray. |
| GenerateTabEvent(false); |
| EXPECT_EQ(status->unified_system_tray(), focus_manager->GetFocusedView()); |
| EXPECT_EQ(0, test_observer_->focus_out_count()); |
| EXPECT_EQ(0, test_observer_->reverse_focus_out_count()); |
| |
| // Another tab key event will send FocusOut event, since we are not handling |
| // this event, focus will remain within the status widhet and will be |
| // moved to the IME tray. |
| GenerateTabEvent(false); |
| EXPECT_EQ(status->ime_menu_tray(), focus_manager->GetFocusedView()); |
| EXPECT_EQ(1, test_observer_->focus_out_count()); |
| EXPECT_EQ(0, test_observer_->reverse_focus_out_count()); |
| |
| // A reverse tab key event will send reverse FocusOut event, since we are not |
| // handling this event, focus will remain within the status widget and will |
| // be moved to the system tray. |
| GenerateTabEvent(true); |
| EXPECT_EQ(status->unified_system_tray(), focus_manager->GetFocusedView()); |
| EXPECT_EQ(1, test_observer_->focus_out_count()); |
| EXPECT_EQ(1, test_observer_->reverse_focus_out_count()); |
| } |
| |
| class StatusAreaWidgetPaletteTest : public AshTestBase { |
| public: |
| StatusAreaWidgetPaletteTest() = default; |
| ~StatusAreaWidgetPaletteTest() override = default; |
| |
| // testing::Test: |
| void SetUp() override { |
| base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); |
| cmd->AppendSwitch(switches::kAshForceEnableStylusTools); |
| // It's difficult to write a test that marks the primary display as |
| // internal before the status area is constructed. Just force the palette |
| // for all displays. |
| cmd->AppendSwitch(switches::kAshEnablePaletteOnAllDisplays); |
| AshTestBase::SetUp(); |
| } |
| }; |
| |
| // Tests that the stylus palette tray is constructed. |
| TEST_F(StatusAreaWidgetPaletteTest, Basics) { |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| EXPECT_TRUE(status->palette_tray()); |
| |
| // Auto-hidden shelf would not be forced to be visible. |
| EXPECT_FALSE(status->ShouldShowShelf()); |
| } |
| |
| class UnifiedStatusAreaWidgetTest : public AshTestBase { |
| public: |
| UnifiedStatusAreaWidgetTest() = default; |
| ~UnifiedStatusAreaWidgetTest() override = default; |
| |
| // AshTestBase: |
| void SetUp() override { |
| chromeos::shill_clients::InitializeFakes(); |
| // Initializing NetworkHandler before ash is more like production. |
| chromeos::NetworkHandler::Initialize(); |
| AshTestBase::SetUp(); |
| chromeos::NetworkHandler::Get()->InitializePrefServices(&profile_prefs_, |
| &local_state_); |
| // Networking stubs may have asynchronous initialization. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void TearDown() override { |
| // This roughly matches production shutdown order. |
| chromeos::NetworkHandler::Get()->ShutdownPrefServices(); |
| AshTestBase::TearDown(); |
| chromeos::NetworkHandler::Shutdown(); |
| chromeos::shill_clients::Shutdown(); |
| } |
| |
| private: |
| TestingPrefServiceSimple profile_prefs_; |
| TestingPrefServiceSimple local_state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(UnifiedStatusAreaWidgetTest); |
| }; |
| |
| TEST_F(UnifiedStatusAreaWidgetTest, Basics) { |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| EXPECT_TRUE(status->unified_system_tray()); |
| } |
| |
| class StatusAreaWidgetVirtualKeyboardTest : public AshTestBase { |
| protected: |
| void SetUp() override { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| keyboard::switches::kEnableVirtualKeyboard); |
| AshTestBase::SetUp(); |
| ASSERT_TRUE(keyboard::IsKeyboardEnabled()); |
| keyboard::test::WaitUntilLoaded(); |
| |
| // These tests only apply to the floating virtual keyboard, as it is the |
| // only case where both the virtual keyboard and the shelf are visible. |
| const gfx::Rect keyboard_bounds(0, 0, 1, 1); |
| keyboard_controller()->SetContainerType( |
| keyboard::mojom::ContainerType::kFloating, keyboard_bounds, |
| base::DoNothing()); |
| } |
| |
| keyboard::KeyboardController* keyboard_controller() { |
| return keyboard::KeyboardController::Get(); |
| } |
| }; |
| |
| // See https://crbug.com/897672. |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, |
| ClickingVirtualKeyboardTrayHidesShownKeyboard) { |
| // Set up the virtual keyboard tray icon along with some other tray icons. |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| status->virtual_keyboard_tray_for_testing()->SetVisible(true); |
| status->ime_menu_tray()->SetVisible(true); |
| |
| keyboard_controller()->ShowKeyboard(false /* locked */); |
| ASSERT_TRUE(keyboard::WaitUntilShown()); |
| |
| // The keyboard should hide when clicked. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->set_current_screen_location( |
| status->virtual_keyboard_tray_for_testing() |
| ->GetBoundsInScreen() |
| .CenterPoint()); |
| generator->ClickLeftButton(); |
| ASSERT_TRUE(keyboard::WaitUntilHidden()); |
| } |
| |
| // See https://crbug.com/897672. |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, |
| TappingVirtualKeyboardTrayHidesShownKeyboard) { |
| // Set up the virtual keyboard tray icon along with some other tray icons. |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| status->virtual_keyboard_tray_for_testing()->SetVisible(true); |
| status->ime_menu_tray()->SetVisible(true); |
| |
| keyboard_controller()->ShowKeyboard(false /* locked */); |
| ASSERT_TRUE(keyboard::WaitUntilShown()); |
| |
| // The keyboard should hide when tapped. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->GestureTapAt(status->virtual_keyboard_tray_for_testing() |
| ->GetBoundsInScreen() |
| .CenterPoint()); |
| ASSERT_TRUE(keyboard::WaitUntilHidden()); |
| } |
| |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, ClickingHidesVirtualKeyboard) { |
| keyboard_controller()->ShowKeyboard(false /* locked */); |
| ASSERT_TRUE(keyboard_controller()->IsKeyboardVisible()); |
| |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->set_current_screen_location( |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget() |
| ->GetWindowBoundsInScreen() |
| .CenterPoint()); |
| generator->ClickLeftButton(); |
| |
| // Times out if test fails. |
| ASSERT_TRUE(keyboard::WaitUntilHidden()); |
| } |
| |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, TappingHidesVirtualKeyboard) { |
| keyboard_controller()->ShowKeyboard(false /* locked */); |
| ASSERT_TRUE(keyboard::WaitUntilShown()); |
| |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->set_current_screen_location( |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget() |
| ->GetWindowBoundsInScreen() |
| .CenterPoint()); |
| generator->PressTouch(); |
| |
| // Times out if test fails. |
| ASSERT_TRUE(keyboard::WaitUntilHidden()); |
| } |
| |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, DoesNotHideLockedVirtualKeyboard) { |
| keyboard_controller()->ShowKeyboard(true /* locked */); |
| ASSERT_TRUE(keyboard::WaitUntilShown()); |
| |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->set_current_screen_location( |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget() |
| ->GetWindowBoundsInScreen() |
| .CenterPoint()); |
| |
| generator->ClickLeftButton(); |
| EXPECT_FALSE(keyboard::IsKeyboardHiding()); |
| |
| generator->PressTouch(); |
| EXPECT_FALSE(keyboard::IsKeyboardHiding()); |
| } |
| |
| } // namespace ash |