blob: 4908ea2ba7deb592f7b5db246fb1fae5ce1c4de6 [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 "chrome/browser/ui/webui/signin/sync_confirmation_handler.h"
#include <memory>
#include <unordered_map>
#include <vector>
#include "base/bind_helpers.h"
#include "base/scoped_observer.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/values.h"
#include "chrome/browser/consent_auditor/consent_auditor_factory.h"
#include "chrome/browser/consent_auditor/consent_auditor_test_utils.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/signin/account_fetcher_service_factory.h"
#include "chrome/browser/signin/account_tracker_service_factory.h"
#include "chrome/browser/signin/fake_account_fetcher_service_builder.h"
#include "chrome/browser/signin/fake_signin_manager_builder.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/dialog_test_browser_window.h"
#include "chrome/test/base/testing_profile.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/consent_auditor/fake_consent_auditor.h"
#include "components/signin/core/browser/account_fetcher_service.h"
#include "components/signin/core/browser/avatar_icon_util.h"
#include "components/signin/core/browser/fake_account_fetcher_service.h"
#include "components/signin/core/browser/fake_signin_manager.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_web_ui.h"
const int kExpectedProfileImageSize = 128;
// The dialog needs to be initialized with a height but the actual value doesn't
// really matter in unit tests.
const double kDefaultDialogHeight = 350.0;
class TestingSyncConfirmationHandler : public SyncConfirmationHandler {
public:
TestingSyncConfirmationHandler(
Browser* browser,
content::WebUI* web_ui,
std::unordered_map<std::string, int> string_to_grd_id_map)
: SyncConfirmationHandler(browser,
string_to_grd_id_map,
consent_auditor::Feature::CHROME_SYNC) {
set_web_ui(web_ui);
}
using SyncConfirmationHandler::HandleConfirm;
using SyncConfirmationHandler::HandleUndo;
using SyncConfirmationHandler::HandleInitializedWithSize;
using SyncConfirmationHandler::HandleGoToSettings;
using SyncConfirmationHandler::RecordConsent;
using SyncConfirmationHandler::SetUserImageURL;
private:
DISALLOW_COPY_AND_ASSIGN(TestingSyncConfirmationHandler);
};
class SyncConfirmationHandlerTest : public BrowserWithTestWindowTest,
public LoginUIService::Observer {
public:
static const char kConsentText1[];
static const char kConsentText2[];
static const char kConsentText3[];
static const char kConsentText4[];
static const char kConsentText5[];
SyncConfirmationHandlerTest()
: did_user_explicitly_interact(false),
on_sync_confirmation_ui_closed_called_(false),
sync_confirmation_ui_closed_result_(LoginUIService::ABORT_SIGNIN),
web_ui_(new content::TestWebUI),
login_ui_service_observer_(this) {}
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
chrome::NewTab(browser());
web_ui()->set_web_contents(
browser()->tab_strip_model()->GetActiveWebContents());
auto handler = std::make_unique<TestingSyncConfirmationHandler>(
browser(), web_ui(), GetStringToGrdIdMap());
handler_ = handler.get();
sync_confirmation_ui_.reset(new SyncConfirmationUI(web_ui()));
web_ui()->AddMessageHandler(std::move(handler));
signin_manager()->StartSignInWithRefreshToken("refresh_token", "gaia",
"foo@example.com", "password",
base::DoNothing());
signin_manager()->CompletePendingSignin();
login_ui_service_observer_.Add(
LoginUIServiceFactory::GetForProfile(profile()));
}
void TearDown() override {
login_ui_service_observer_.RemoveAll();
sync_confirmation_ui_.reset();
web_ui_.reset();
BrowserWithTestWindowTest::TearDown();
if (did_user_explicitly_interact) {
EXPECT_EQ(0, user_action_tester()->GetActionCount("Signin_Abort_Signin"));
} else {
EXPECT_EQ(1, user_action_tester()->GetActionCount("Signin_Abort_Signin"));
}
}
TestingSyncConfirmationHandler* handler() {
return handler_;
}
content::TestWebUI* web_ui() {
return web_ui_.get();
}
FakeAccountFetcherService* account_fetcher_service() {
return static_cast<FakeAccountFetcherService*>(
AccountFetcherServiceFactory::GetForProfile(profile()));
}
FakeSigninManager* signin_manager() {
return static_cast<FakeSigninManager*>(
SigninManagerFactory::GetForProfile(profile()));
}
browser_sync::ProfileSyncService* sync() {
return ProfileSyncServiceFactory::GetForProfile(profile());
}
base::UserActionTester* user_action_tester() {
return &user_action_tester_;
}
consent_auditor::FakeConsentAuditor* consent_auditor() {
return static_cast<consent_auditor::FakeConsentAuditor*>(
ConsentAuditorFactory::GetForProfile(profile()));
}
// BrowserWithTestWindowTest:
BrowserWindow* CreateBrowserWindow() override {
return new DialogTestBrowserWindow;
}
TestingProfile::TestingFactories GetTestingFactories() override {
return {{AccountFetcherServiceFactory::GetInstance(),
FakeAccountFetcherServiceBuilder::BuildForTests},
{SigninManagerFactory::GetInstance(), BuildFakeSigninManagerBase},
{ConsentAuditorFactory::GetInstance(), BuildFakeConsentAuditor}};
}
const std::unordered_map<std::string, int>& GetStringToGrdIdMap() {
if (string_to_grd_id_map_.empty()) {
string_to_grd_id_map_[kConsentText1] = 1;
string_to_grd_id_map_[kConsentText2] = 2;
string_to_grd_id_map_[kConsentText3] = 3;
string_to_grd_id_map_[kConsentText4] = 4;
string_to_grd_id_map_[kConsentText5] = 5;
}
return string_to_grd_id_map_;
}
// LoginUIService::Observer:
void OnSyncConfirmationUIClosed(
LoginUIService::SyncConfirmationUIClosedResult result) override {
on_sync_confirmation_ui_closed_called_ = true;
sync_confirmation_ui_closed_result_ = result;
}
protected:
bool did_user_explicitly_interact;
bool on_sync_confirmation_ui_closed_called_;
LoginUIService::SyncConfirmationUIClosedResult
sync_confirmation_ui_closed_result_;
private:
std::unique_ptr<content::TestWebUI> web_ui_;
std::unique_ptr<SyncConfirmationUI> sync_confirmation_ui_;
TestingSyncConfirmationHandler* handler_; // Not owned.
base::UserActionTester user_action_tester_;
std::unordered_map<std::string, int> string_to_grd_id_map_;
ScopedObserver<LoginUIService, LoginUIService::Observer>
login_ui_service_observer_;
DISALLOW_COPY_AND_ASSIGN(SyncConfirmationHandlerTest);
};
const char SyncConfirmationHandlerTest::kConsentText1[] = "consentText1";
const char SyncConfirmationHandlerTest::kConsentText2[] = "consentText2";
const char SyncConfirmationHandlerTest::kConsentText3[] = "consentText3";
const char SyncConfirmationHandlerTest::kConsentText4[] = "consentText4";
const char SyncConfirmationHandlerTest::kConsentText5[] = "consentText5";
TEST_F(SyncConfirmationHandlerTest, TestSetImageIfPrimaryAccountReady) {
account_fetcher_service()->FakeUserInfoFetchSuccess(
"gaia",
"foo@example.com",
"gaia",
"",
"full_name",
"given_name",
"locale",
"http://picture.example.com/picture.jpg");
base::ListValue args;
args.Set(0, std::make_unique<base::Value>(kDefaultDialogHeight));
handler()->HandleInitializedWithSize(&args);
EXPECT_EQ(2U, web_ui()->call_data().size());
// When the primary account is ready, setUserImageURL happens before
// clearFocus since the image URL is known before showing the dialog.
EXPECT_EQ("sync.confirmation.setUserImageURL",
web_ui()->call_data()[0]->function_name());
EXPECT_TRUE(web_ui()->call_data()[0]->arg1()->is_string());
std::string passed_picture_url;
EXPECT_TRUE(
web_ui()->call_data()[0]->arg1()->GetAsString(&passed_picture_url));
EXPECT_EQ("sync.confirmation.clearFocus",
web_ui()->call_data()[1]->function_name());
std::string original_picture_url =
AccountTrackerServiceFactory::GetForProfile(profile())->
GetAccountInfo("gaia").picture_url;
GURL picture_url_with_size = signin::GetAvatarImageURLWithOptions(
GURL(original_picture_url), kExpectedProfileImageSize,
false /* no_silhouette */);
EXPECT_EQ(picture_url_with_size.spec(), passed_picture_url);
}
TEST_F(SyncConfirmationHandlerTest, TestSetImageIfPrimaryAccountReadyLater) {
base::ListValue args;
args.Set(0, std::make_unique<base::Value>(kDefaultDialogHeight));
handler()->HandleInitializedWithSize(&args);
EXPECT_EQ(2U, web_ui()->call_data().size());
account_fetcher_service()->FakeUserInfoFetchSuccess(
"gaia",
"foo@example.com",
"gaia",
"",
"full_name",
"given_name",
"locale",
"http://picture.example.com/picture.jpg");
EXPECT_EQ(3U, web_ui()->call_data().size());
// When the primary account isn't yet ready when the dialog is shown,
// setUserImageURL is called with the default placeholder image.
EXPECT_EQ("sync.confirmation.setUserImageURL",
web_ui()->call_data()[0]->function_name());
EXPECT_TRUE(web_ui()->call_data()[0]->arg1()->is_string());
std::string passed_picture_url;
EXPECT_TRUE(
web_ui()->call_data()[0]->arg1()->GetAsString(&passed_picture_url));
EXPECT_EQ(profiles::GetPlaceholderAvatarIconUrl(), passed_picture_url);
// When the primary account isn't yet ready when the dialog is shown,
// clearFocus is called before the second call to setUserImageURL.
EXPECT_EQ("sync.confirmation.clearFocus",
web_ui()->call_data()[1]->function_name());
EXPECT_EQ("sync.confirmation.setUserImageURL",
web_ui()->call_data()[2]->function_name());
EXPECT_TRUE(web_ui()->call_data()[2]->arg1()->is_string());
EXPECT_TRUE(
web_ui()->call_data()[2]->arg1()->GetAsString(&passed_picture_url));
std::string original_picture_url =
AccountTrackerServiceFactory::GetForProfile(profile())->
GetAccountInfo("gaia").picture_url;
GURL picture_url_with_size = signin::GetAvatarImageURLWithOptions(
GURL(original_picture_url), kExpectedProfileImageSize,
false /* no_silhouette */);
EXPECT_EQ(picture_url_with_size.spec(), passed_picture_url);
}
TEST_F(SyncConfirmationHandlerTest,
TestSetImageIgnoredIfSecondaryAccountUpdated) {
base::ListValue args;
args.Set(0, std::make_unique<base::Value>(kDefaultDialogHeight));
handler()->HandleInitializedWithSize(&args);
EXPECT_EQ(2U, web_ui()->call_data().size());
AccountTrackerServiceFactory::GetForProfile(profile())->SeedAccountInfo(
"bar_gaia", "bar@example.com");
account_fetcher_service()->FakeUserInfoFetchSuccess(
"bar_gaia", "bar@example.com", "bar_gaia", "", "bar_full_name",
"bar_given_name", "bar_locale",
"http://picture.example.com/bar_picture.jpg");
// Updating the account info of a secondary account should not update the
// image of the sync confirmation dialog.
EXPECT_EQ(2U, web_ui()->call_data().size());
account_fetcher_service()->FakeUserInfoFetchSuccess(
"gaia", "foo@example.com", "gaia", "", "full_name", "given_name",
"locale", "http://picture.example.com/picture.jpg");
// Updating the account info of the primary account should update the
// image of the sync confirmation dialog.
EXPECT_EQ(3U, web_ui()->call_data().size());
EXPECT_EQ("sync.confirmation.setUserImageURL",
web_ui()->call_data()[2]->function_name());
}
TEST_F(SyncConfirmationHandlerTest, TestHandleUndo) {
handler()->HandleUndo(nullptr);
did_user_explicitly_interact = true;
EXPECT_TRUE(on_sync_confirmation_ui_closed_called_);
EXPECT_EQ(LoginUIService::ABORT_SIGNIN, sync_confirmation_ui_closed_result_);
EXPECT_EQ(1, user_action_tester()->GetActionCount("Signin_Undo_Signin"));
EXPECT_EQ(0, user_action_tester()->GetActionCount(
"Signin_Signin_WithDefaultSyncSettings"));
EXPECT_EQ(0, user_action_tester()->GetActionCount(
"Signin_Signin_WithAdvancedSyncSettings"));
}
TEST_F(SyncConfirmationHandlerTest, TestHandleConfirm) {
// The consent description consists of strings 1, 2, and 4.
base::ListValue consent_description;
consent_description.GetList().push_back(
base::Value(SyncConfirmationHandlerTest::kConsentText1));
consent_description.GetList().push_back(
base::Value(SyncConfirmationHandlerTest::kConsentText2));
consent_description.GetList().push_back(
base::Value(SyncConfirmationHandlerTest::kConsentText4));
// The consent confirmation contains string 5.
base::Value consent_confirmation(SyncConfirmationHandlerTest::kConsentText5);
// These are passed as parameters to HandleConfirm().
base::ListValue args;
args.GetList().push_back(std::move(consent_description));
args.GetList().push_back(std::move(consent_confirmation));
handler()->HandleConfirm(&args);
did_user_explicitly_interact = true;
EXPECT_TRUE(on_sync_confirmation_ui_closed_called_);
EXPECT_EQ(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS,
sync_confirmation_ui_closed_result_);
EXPECT_EQ(0, user_action_tester()->GetActionCount("Signin_Undo_Signin"));
EXPECT_EQ(1, user_action_tester()->GetActionCount(
"Signin_Signin_WithDefaultSyncSettings"));
EXPECT_EQ(0, user_action_tester()->GetActionCount(
"Signin_Signin_WithAdvancedSyncSettings"));
// The corresponding string IDs get recorded.
std::vector<std::vector<int>> expected_id_vectors = {{1, 2, 4}};
std::vector<int> expected_confirmation_ids = {5};
EXPECT_EQ(expected_id_vectors, consent_auditor()->recorded_id_vectors());
EXPECT_EQ(expected_confirmation_ids,
consent_auditor()->recorded_confirmation_ids());
EXPECT_EQ(signin_manager()->GetAuthenticatedAccountId(),
consent_auditor()->account_id());
}
TEST_F(SyncConfirmationHandlerTest, TestHandleConfirmWithAdvancedSyncSettings) {
// The consent description consists of strings 2, 3, and 5.
base::ListValue consent_description;
consent_description.GetList().push_back(
base::Value(SyncConfirmationHandlerTest::kConsentText2));
consent_description.GetList().push_back(
base::Value(SyncConfirmationHandlerTest::kConsentText3));
consent_description.GetList().push_back(
base::Value(SyncConfirmationHandlerTest::kConsentText5));
// The consent confirmation contains string 2.
base::Value consent_confirmation(SyncConfirmationHandlerTest::kConsentText2);
// These are passed as parameters to HandleGoToSettings().
base::ListValue args;
args.GetList().push_back(std::move(consent_description));
args.GetList().push_back(std::move(consent_confirmation));
handler()->HandleGoToSettings(&args);
did_user_explicitly_interact = true;
EXPECT_TRUE(on_sync_confirmation_ui_closed_called_);
EXPECT_EQ(LoginUIService::CONFIGURE_SYNC_FIRST,
sync_confirmation_ui_closed_result_);
EXPECT_EQ(0, user_action_tester()->GetActionCount("Signin_Undo_Signin"));
EXPECT_EQ(0, user_action_tester()->GetActionCount(
"Signin_Signin_WithDefaultSyncSettings"));
EXPECT_EQ(1, user_action_tester()->GetActionCount(
"Signin_Signin_WithAdvancedSyncSettings"));
// The corresponding string IDs get recorded.
std::vector<std::vector<int>> expected_id_vectors = {{2, 3, 5}};
std::vector<int> expected_confirmation_ids = {2};
EXPECT_EQ(expected_id_vectors, consent_auditor()->recorded_id_vectors());
EXPECT_EQ(expected_confirmation_ids,
consent_auditor()->recorded_confirmation_ids());
EXPECT_EQ(signin_manager()->GetAuthenticatedAccountId(),
consent_auditor()->account_id());
}