blob: 905f8d40f0d7cd9a8dadebe23cdfe513fd6eeb05 [file] [log] [blame]
// 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 <utility>
#include <vector>
#include "ash/session/session_controller.h"
#include "ash/session/test_session_controller_client.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/user/rounded_image_view.h"
#include "ash/system/user/tray_user.h"
#include "ash/system/user/user_view.h"
#include "ash/test/ash_test_base.h"
#include "ash/test_shell_delegate.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/signin/core/account_id/account_id.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/animation/animation_container_element.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
const char* kPredefinedUserEmails[] = {
// This is intended to be capitalized.
"First@tray",
// This is intended to be capitalized.
"Second@tray"};
// Creates an ImageSkia with a 1x rep of the given dimensions and filled with
// the given color.
gfx::ImageSkia CreateImageSkiaWithColor(int width, int height, SkColor color) {
SkBitmap bitmap = gfx::test::CreateBitmap(width, height);
bitmap.eraseColor(color);
return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
}
class TrayUserTest : public NoSessionAshTestBase {
public:
TrayUserTest() = default;
// testing::Test:
void SetUp() override;
// This has to be called prior to first use with the proper configuration.
void InitializeParameters(int users_logged_in, bool multiprofile);
// Show the system tray menu using the provided event generator.
void ShowTrayMenu(ui::test::EventGenerator* generator);
// Move the mouse over the user item.
void MoveOverUserItem(ui::test::EventGenerator* generator);
// Click on the user item. Note that the tray menu needs to be shown.
void ClickUserItem(ui::test::EventGenerator* generator);
// Accessors to various system components.
SystemTray* tray() { return tray_; }
SessionController* controller() { return Shell::Get()->session_controller(); }
TrayUser* tray_user() { return tray_user_; }
private:
SystemTray* tray_ = nullptr;
// Note that the ownership of these items is on the shelf.
TrayUser* tray_user_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(TrayUserTest);
};
void TrayUserTest::SetUp() {
AshTestBase::SetUp();
tray_ = GetPrimarySystemTray();
}
void TrayUserTest::InitializeParameters(int users_logged_in,
bool can_add_users) {
// Set our default assumptions. Note that it is sufficient to set these
// after everything was created.
TestSessionControllerClient* session = GetSessionControllerClient();
session->Reset();
ASSERT_LE(users_logged_in,
static_cast<int>(arraysize(kPredefinedUserEmails)));
for (int i = 0; i < users_logged_in; ++i)
SimulateUserLogin(kPredefinedUserEmails[i]);
session->SetAddUserSessionPolicy(
can_add_users ? AddUserSessionPolicy::ALLOWED
: AddUserSessionPolicy::ERROR_NO_ELIGIBLE_USERS);
// Instead of using the existing tray panels we create new ones which makes
// the access easier.
tray_user_ = new TrayUser(tray_);
tray_->AddTrayItem(base::WrapUnique(tray_user_));
}
void TrayUserTest::ShowTrayMenu(ui::test::EventGenerator* generator) {
gfx::Point center = tray()->GetBoundsInScreen().CenterPoint();
generator->MoveMouseTo(center.x(), center.y());
EXPECT_FALSE(tray()->IsSystemBubbleVisible());
generator->ClickLeftButton();
}
void TrayUserTest::MoveOverUserItem(ui::test::EventGenerator* generator) {
gfx::Point center =
tray_user()->GetUserPanelBoundsInScreenForTest().CenterPoint();
generator->MoveMouseTo(center.x(), center.y());
}
void TrayUserTest::ClickUserItem(ui::test::EventGenerator* generator) {
MoveOverUserItem(generator);
generator->ClickLeftButton();
}
} // namespace
// Make sure that we show items for all users in the tray accordingly.
TEST_F(TrayUserTest, CheckTrayItemSize) {
InitializeParameters(1, false);
tray_user()->UpdateAfterLoginStatusChangeForTest(LoginStatus::GUEST);
gfx::Size size = tray_user()->GetLayoutSizeForTest();
EXPECT_EQ(kTrayItemSize, size.height());
tray_user()->UpdateAfterLoginStatusChangeForTest(LoginStatus::USER);
size = tray_user()->GetLayoutSizeForTest();
EXPECT_EQ(kTrayItemSize, size.height());
}
// Make sure that in single user mode the user panel cannot be activated.
TEST_F(TrayUserTest, SingleUserModeDoesNotAllowAddingUser) {
InitializeParameters(1, false);
// Move the mouse over the status area and click to open the status menu.
ui::test::EventGenerator& generator = GetEventGenerator();
EXPECT_FALSE(tray()->IsSystemBubbleVisible());
EXPECT_EQ(TrayUser::HIDDEN, tray_user()->GetStateForTest());
ShowTrayMenu(&generator);
EXPECT_TRUE(tray()->HasSystemBubble());
EXPECT_TRUE(tray()->IsSystemBubbleVisible());
EXPECT_EQ(TrayUser::SHOWN, tray_user()->GetStateForTest());
tray()->CloseBubble();
}
TEST_F(TrayUserTest, AccessibleLabelContainsSingleUserInfo) {
InitializeParameters(1, false);
ui::test::EventGenerator& generator = GetEventGenerator();
ShowTrayMenu(&generator);
views::View* view =
tray_user()->user_view_for_test()->user_card_view_for_test();
ui::AXNodeData node_data;
view->GetAccessibleNodeData(&node_data);
EXPECT_EQ(
base::UTF8ToUTF16("Über tray Über tray Über tray Über tray First@tray"),
node_data.GetString16Attribute(ui::AX_ATTR_NAME));
EXPECT_EQ(ui::AX_ROLE_STATIC_TEXT, node_data.role);
}
TEST_F(TrayUserTest, AccessibleLabelContainsMultiUserInfo) {
InitializeParameters(1, true);
ui::test::EventGenerator& generator = GetEventGenerator();
ShowTrayMenu(&generator);
views::View* view =
tray_user()->user_view_for_test()->user_card_view_for_test();
ui::AXNodeData node_data;
view->GetAccessibleNodeData(&node_data);
EXPECT_EQ(
base::UTF8ToUTF16("Über tray Über tray Über tray Über tray First@tray"),
node_data.GetString16Attribute(ui::AX_ATTR_NAME));
EXPECT_EQ(ui::AX_ROLE_BUTTON, node_data.role);
}
// Make sure that in multi user mode the user panel can be activated and there
// will be one panel for each user.
// Note: the mouse watcher (for automatic closing upon leave) cannot be tested
// here since it does not work with the event system in unit tests.
TEST_F(TrayUserTest, MultiUserModeDoesNotAllowToAddUser) {
// Sign in more than one user.
InitializeParameters(2, true);
// Move the mouse over the status area and click to open the status menu.
ui::test::EventGenerator& generator = GetEventGenerator();
generator.set_async(false);
// Verify that nothing is shown.
EXPECT_FALSE(tray()->IsSystemBubbleVisible());
EXPECT_FALSE(tray_user()->GetStateForTest());
// After clicking on the tray the menu should get shown and for each logged
// in user we should get a visible item.
ShowTrayMenu(&generator);
EXPECT_TRUE(tray()->HasSystemBubble());
EXPECT_TRUE(tray()->IsSystemBubbleVisible());
EXPECT_EQ(TrayUser::SHOWN, tray_user()->GetStateForTest());
// Move the mouse over the user item and it should hover.
MoveOverUserItem(&generator);
EXPECT_EQ(TrayUser::HOVERED, tray_user()->GetStateForTest());
// Check that clicking the button allows to add item if we have still room
// for one more user.
ClickUserItem(&generator);
EXPECT_EQ(TrayUser::ACTIVE, tray_user()->GetStateForTest());
// Click the button again to see that the menu goes away.
ClickUserItem(&generator);
MoveOverUserItem(&generator);
EXPECT_EQ(TrayUser::HOVERED, tray_user()->GetStateForTest());
// Close and check that everything is deleted.
tray()->CloseBubble();
EXPECT_FALSE(tray()->IsSystemBubbleVisible());
EXPECT_EQ(TrayUser::HIDDEN, tray_user()->GetStateForTest());
}
// Make sure that user changing gets properly executed.
TEST_F(TrayUserTest, MultiUserModeButtonClicks) {
// Have two users.
InitializeParameters(2, true);
ui::test::EventGenerator& generator = GetEventGenerator();
ShowTrayMenu(&generator);
// Gets the second user before user switching.
const mojom::UserSession* second_user = controller()->GetUserSession(1);
// Switch to a new user "Second@tray" - which has a capitalized name.
ClickUserItem(&generator);
gfx::Rect user_card_bounds = tray_user()->GetUserPanelBoundsInScreenForTest();
gfx::Point second_user_point = user_card_bounds.CenterPoint() +
gfx::Vector2d(0, user_card_bounds.height());
generator.MoveMouseTo(second_user_point);
generator.ClickLeftButton();
// SwitchActiverUser is an async mojo call. Spin the loop to let it finish.
RunAllPendingInMessageLoop();
const mojom::UserSession* active_user = controller()->GetUserSession(0);
EXPECT_EQ(active_user->user_info->account_id,
second_user->user_info->account_id);
// Since the name is capitalized, the email should be different than the
// user_id.
EXPECT_NE(active_user->user_info->account_id.GetUserEmail(),
second_user->user_info->display_email);
tray()->CloseBubble();
}
// Test SessionController updates avatar image.
TEST_F(TrayUserTest, AvatarChange) {
InitializeParameters(1, false);
// Expect empty avatar initially (that is how the test sets up).
EXPECT_TRUE(tray_user()->avatar_view_for_test()->image_for_test().isNull());
// Change user avatar via SessionController and verify.
const gfx::ImageSkia red_icon =
CreateImageSkiaWithColor(kTrayItemSize, kTrayItemSize, SK_ColorRED);
mojom::UserSessionPtr user = controller()->GetUserSession(0)->Clone();
user->user_info->avatar = red_icon;
controller()->UpdateUserSession(std::move(user));
EXPECT_TRUE(gfx::test::AreImagesEqual(
gfx::Image(red_icon),
gfx::Image(tray_user()->avatar_view_for_test()->image_for_test())));
}
} // namespace ash