blob: 0016ac8b30a74bddcbd5e3a6680e9348615e1ed9 [file] [log] [blame]
// Copyright 2016 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 <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/extensions/extension_action_test_util.h"
#include "chrome/browser/extensions/load_error_reporter.h"
#include "chrome/browser/media/router/media_router_factory.h"
#include "chrome/browser/media/router/test/mock_media_router.h"
#include "chrome/browser/signin/fake_signin_manager_builder.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/ui/extensions/browser_action_test_util.h"
#include "chrome/browser/ui/media_router/media_router_ui_service.h"
#include "chrome/browser/ui/media_router/media_router_ui_service_factory.h"
#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
#include "chrome/browser/ui/toolbar/media_router_action.h"
#include "chrome/browser/ui/toolbar/media_router_action_controller.h"
#include "chrome/browser/ui/toolbar/media_router_contextual_menu.h"
#include "chrome/browser/ui/toolbar/mock_media_router_action_controller.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "services/identity/public/cpp/identity_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
// These constants are used to inject the state of the Media Router action
// that would be inferred in the production code.
constexpr bool kInToolbar = true;
constexpr bool kInOverflowMenu = false;
constexpr bool kShownByPolicy = true;
constexpr bool kShownByUser = false;
bool HasCommandId(ui::MenuModel* menu_model, int command_id) {
for (int i = 0; i < menu_model->GetItemCount(); i++) {
if (menu_model->GetCommandIdAt(i) == command_id)
return true;
}
return false;
}
std::unique_ptr<KeyedService> BuildUIService(content::BrowserContext* context) {
Profile* profile = Profile::FromBrowserContext(context);
auto controller = std::make_unique<MockMediaRouterActionController>(profile);
return std::make_unique<media_router::MediaRouterUIService>(
profile, std::move(controller));
}
class MockMediaRouterContextualMenuObserver
: public MediaRouterContextualMenu::Observer {
public:
MOCK_METHOD0(OnContextMenuShown, void());
MOCK_METHOD0(OnContextMenuHidden, void());
};
} // namespace
class MediaRouterContextualMenuUnitTest : public BrowserWithTestWindowTest {
public:
MediaRouterContextualMenuUnitTest() {}
~MediaRouterContextualMenuUnitTest() override {}
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
extensions::LoadErrorReporter::Init(true);
toolbar_actions_model_ =
extensions::extension_action_test_util::CreateToolbarModelForProfile(
profile());
browser_action_test_util_ = BrowserActionTestUtil::Create(browser(), false);
action_ = std::make_unique<MediaRouterAction>(
browser(), browser_action_test_util_->GetToolbarActionsBar());
// Pin the Media Router action to the toolbar.
MediaRouterActionController::SetAlwaysShowActionPref(profile(), true);
media_router::MediaRouterUIServiceFactory::GetInstance()->SetTestingFactory(
profile()->GetOffTheRecordProfile(),
base::BindRepeating(&BuildUIService));
}
void TearDown() override {
action_.reset();
browser_action_test_util_.reset();
BrowserWithTestWindowTest::TearDown();
}
TestingProfile::TestingFactories GetTestingFactories() override {
return {{media_router::MediaRouterFactory::GetInstance(),
base::BindRepeating(&media_router::MockMediaRouter::Create)},
{media_router::MediaRouterUIServiceFactory::GetInstance(),
base::BindRepeating(&BuildUIService)},
{SigninManagerFactory::GetInstance(),
base::BindRepeating(&BuildFakeSigninManagerBase)}};
}
protected:
std::unique_ptr<BrowserActionTestUtil> browser_action_test_util_;
std::unique_ptr<MediaRouterAction> action_;
ToolbarActionsModel* toolbar_actions_model_ = nullptr;
MockMediaRouterContextualMenuObserver observer_;
private:
DISALLOW_COPY_AND_ASSIGN(MediaRouterContextualMenuUnitTest);
};
// Tests the basic state of the contextual menu.
TEST_F(MediaRouterContextualMenuUnitTest, Basic) {
// About
// -----
// Learn more
// Help
// Always show icon (checkbox)
// Hide in Chrome menu / Show in toolbar
// -----
// Enable cloud services (checkbox)
// Report an issue
int expected_number_items = 9;
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
// On all platforms except Linux, there's an additional menu item to access
// Cast device management.
expected_number_items++;
#endif // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
ui::SimpleMenuModel* model =
static_cast<ui::SimpleMenuModel*>(action_->GetContextMenu());
// Verify the number of menu items, including separators.
EXPECT_EQ(model->GetItemCount(), expected_number_items);
for (int i = 0; i < expected_number_items; i++) {
EXPECT_TRUE(model->IsEnabledAt(i));
// The cloud services toggle exists and is enabled, but not visible until
// the user has authenticated their account.
const bool expected_visibility =
model->GetCommandIdAt(i) != IDC_MEDIA_ROUTER_CLOUD_SERVICES_TOGGLE;
EXPECT_EQ(expected_visibility, model->IsVisibleAt(i));
}
// Set up an authenticated account.
(void)identity::SetPrimaryAccount(
SigninManagerFactory::GetInstance()->GetForProfile(profile()),
IdentityManagerFactory::GetForProfile(profile()), "foo@bar.com");
// Run the same checks as before. All existing menu items should be now
// enabled and visible.
EXPECT_EQ(model->GetItemCount(), expected_number_items);
for (int i = 0; i < expected_number_items; i++) {
EXPECT_TRUE(model->IsEnabledAt(i));
EXPECT_TRUE(model->IsVisibleAt(i));
}
}
// Note that "Manage devices" is always disabled on Linux.
TEST_F(MediaRouterContextualMenuUnitTest, ManageDevicesDisabledInIncognito) {
std::unique_ptr<BrowserWindow> window(CreateBrowserWindow());
std::unique_ptr<Browser> incognito_browser(
CreateBrowser(profile()->GetOffTheRecordProfile(), Browser::TYPE_TABBED,
false, window.get()));
action_ = std::make_unique<MediaRouterAction>(
incognito_browser.get(),
browser_action_test_util_->GetToolbarActionsBar());
ui::SimpleMenuModel* model =
static_cast<ui::SimpleMenuModel*>(action_->GetContextMenu());
EXPECT_EQ(-1, model->GetIndexOfCommandId(IDC_MEDIA_ROUTER_MANAGE_DEVICES));
action_.reset();
}
// "Report an issue" should be present for normal profiles but not for
// incognito.
TEST_F(MediaRouterContextualMenuUnitTest, EnableAndDisableReportIssue) {
ui::SimpleMenuModel* model =
static_cast<ui::SimpleMenuModel*>(action_->GetContextMenu());
EXPECT_NE(-1, model->GetIndexOfCommandId(IDC_MEDIA_ROUTER_REPORT_ISSUE));
std::unique_ptr<BrowserWindow> window(CreateBrowserWindow());
std::unique_ptr<Browser> incognito_browser(
CreateBrowser(profile()->GetOffTheRecordProfile(), Browser::TYPE_TABBED,
false, window.get()));
action_ = std::make_unique<MediaRouterAction>(
incognito_browser.get(),
browser_action_test_util_->GetToolbarActionsBar());
model = static_cast<ui::SimpleMenuModel*>(action_->GetContextMenu());
EXPECT_EQ(-1, model->GetIndexOfCommandId(IDC_MEDIA_ROUTER_REPORT_ISSUE));
action_.reset();
}
// Tests whether the cloud services item is correctly toggled. This menu item
// is only availble on official Chrome builds.
// TODO(takumif): Add a test case that checks that the cloud services dialog is
// shown when the services are enabled for the first time.
TEST_F(MediaRouterContextualMenuUnitTest, ToggleCloudServicesItem) {
// The Media Router Action has a getter for the model, but not the delegate.
// Create the MediaRouterContextualMenu ui::SimpleMenuModel::Delegate here.
MediaRouterContextualMenu menu(browser(), kInToolbar, kShownByPolicy,
&observer_);
// Set up an authenticated account such that the cloud services menu item is
// surfaced. Whether or not it is surfaced is tested in the "Basic" test.
(void)identity::SetPrimaryAccount(
SigninManagerFactory::GetInstance()->GetForProfile(profile()),
IdentityManagerFactory::GetForProfile(profile()), "foo@bar.com");
// Set this preference so that the cloud services can be enabled without
// showing the opt-in dialog.
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kMediaRouterCloudServicesPrefSet, true);
// By default, the command is not checked.
EXPECT_FALSE(menu.IsCommandIdChecked(
IDC_MEDIA_ROUTER_CLOUD_SERVICES_TOGGLE));
menu.ExecuteCommand(IDC_MEDIA_ROUTER_CLOUD_SERVICES_TOGGLE, 0);
EXPECT_TRUE(menu.IsCommandIdChecked(
IDC_MEDIA_ROUTER_CLOUD_SERVICES_TOGGLE));
menu.ExecuteCommand(IDC_MEDIA_ROUTER_CLOUD_SERVICES_TOGGLE, 0);
EXPECT_FALSE(menu.IsCommandIdChecked(
IDC_MEDIA_ROUTER_CLOUD_SERVICES_TOGGLE));
}
TEST_F(MediaRouterContextualMenuUnitTest, ToggleMediaRemotingItem) {
MediaRouterContextualMenu menu(browser(), kInToolbar, kShownByPolicy,
&observer_);
PrefService* pref_service = browser()->profile()->GetPrefs();
pref_service->SetBoolean(prefs::kMediaRouterMediaRemotingEnabled, false);
EXPECT_FALSE(menu.IsCommandIdChecked(IDC_MEDIA_ROUTER_TOGGLE_MEDIA_REMOTING));
menu.ExecuteCommand(IDC_MEDIA_ROUTER_TOGGLE_MEDIA_REMOTING, 0);
EXPECT_TRUE(menu.IsCommandIdChecked(IDC_MEDIA_ROUTER_TOGGLE_MEDIA_REMOTING));
EXPECT_TRUE(
pref_service->GetBoolean(prefs::kMediaRouterMediaRemotingEnabled));
menu.ExecuteCommand(IDC_MEDIA_ROUTER_TOGGLE_MEDIA_REMOTING, 0);
EXPECT_FALSE(menu.IsCommandIdChecked(IDC_MEDIA_ROUTER_TOGGLE_MEDIA_REMOTING));
EXPECT_FALSE(
pref_service->GetBoolean(prefs::kMediaRouterMediaRemotingEnabled));
}
TEST_F(MediaRouterContextualMenuUnitTest, ToggleAlwaysShowIconItem) {
MediaRouterContextualMenu menu(browser(), kInToolbar, kShownByUser,
&observer_);
// Whether the option is checked should reflect the pref.
MediaRouterActionController::SetAlwaysShowActionPref(profile(), true);
EXPECT_TRUE(
menu.IsCommandIdChecked(IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION));
MediaRouterActionController::SetAlwaysShowActionPref(profile(), false);
EXPECT_FALSE(
menu.IsCommandIdChecked(IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION));
// Executing the option should toggle the pref.
menu.ExecuteCommand(IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION, 0);
EXPECT_TRUE(MediaRouterActionController::GetAlwaysShowActionPref(profile()));
menu.ExecuteCommand(IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION, 0);
EXPECT_FALSE(MediaRouterActionController::GetAlwaysShowActionPref(profile()));
}
TEST_F(MediaRouterContextualMenuUnitTest, ActionShownByPolicy) {
// Create a contextual menu for an icon shown by administrator policy.
MediaRouterContextualMenu menu(browser(), kInToolbar, kShownByPolicy,
&observer_);
// The item "Added by your administrator" should be shown disabled.
EXPECT_TRUE(menu.IsCommandIdVisible(IDC_MEDIA_ROUTER_SHOWN_BY_POLICY));
EXPECT_FALSE(menu.IsCommandIdEnabled(IDC_MEDIA_ROUTER_SHOWN_BY_POLICY));
// The checkbox item "Always show icon" should not be shown.
EXPECT_FALSE(HasCommandId(menu.menu_model(),
IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION));
}
TEST_F(MediaRouterContextualMenuUnitTest, HideActionInOverflowItem) {
MediaRouterContextualMenu menu(browser(), kInToolbar, kShownByUser,
&observer_);
// When the action icon is in the toolbar, this menu item should say "Hide
// in Chrome menu".
const base::string16& menu_item_label = menu.menu_model()->GetLabelAt(
menu.menu_model()->GetIndexOfCommandId(IDC_MEDIA_ROUTER_SHOW_IN_TOOLBAR));
EXPECT_EQ(menu_item_label,
l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_BUTTON_IN_MENU));
}
TEST_F(MediaRouterContextualMenuUnitTest, ShowActionInToolbarItem) {
MediaRouterContextualMenu menu(browser(), kInOverflowMenu, kShownByUser,
&observer_);
// When the action icon is in the overflow menu, this menu item should say
// "Show in toolbar".
const base::string16& menu_item_label = menu.menu_model()->GetLabelAt(
menu.menu_model()->GetIndexOfCommandId(IDC_MEDIA_ROUTER_SHOW_IN_TOOLBAR));
EXPECT_EQ(menu_item_label,
l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON_IN_TOOLBAR));
}
TEST_F(MediaRouterContextualMenuUnitTest, NotifyActionController) {
EXPECT_CALL(observer_, OnContextMenuShown());
auto menu = std::make_unique<MediaRouterContextualMenu>(
browser(), kInToolbar, kShownByUser, &observer_);
menu->OnMenuWillShow(menu->menu_model());
EXPECT_CALL(observer_, OnContextMenuHidden());
menu->MenuClosed(menu->menu_model());
menu.reset();
}