// Copyright (c) 2011 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.
// TODO(jhawkins): Rewrite these tests to handle the new inlined sync UI.
#include "chrome/browser/sync/sync_setup_wizard.h"
#include "base/json/json_writer.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util-inl.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/sync/profile_sync_factory_mock.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/sync_setup_flow.h"
#include "chrome/browser/sync/sync_setup_flow_handler.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/browser_with_test_window_test.h"
#include "chrome/test/test_browser_window.h"
#include "chrome/test/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"
static const char kTestUser[] = "chrome.p13n.test@gmail.com";
static const char kTestPassword[] = "passwd";
static const char kTestCaptcha[] = "pizzamyheart";
static const char kTestCaptchaUrl[] = "http://pizzamyheart/";
typedef GoogleServiceAuthError AuthError;
// A PSS subtype to inject.
class ProfileSyncServiceForWizardTest : public ProfileSyncService {
public:
ProfileSyncServiceForWizardTest(ProfileSyncFactory* factory, Profile* profile)
: ProfileSyncService(factory, profile, ""),
user_cancelled_dialog_(false) {
RegisterPreferences();
ResetTestStats();
}
virtual ~ProfileSyncServiceForWizardTest() { }
virtual void OnUserSubmittedAuth(const std::string& username,
const std::string& password,
const std::string& captcha,
const std::string& access_code) {
username_ = username;
password_ = password;
captcha_ = captcha;
}
virtual void OnUserChoseDatatypes(bool sync_everything,
const syncable::ModelTypeSet& chosen_types) {
user_chose_data_types_ = true;
chosen_data_types_ = chosen_types;
}
virtual void OnUserCancelledDialog() {
user_cancelled_dialog_ = true;
}
virtual void SetPassphrase(const std::string& passphrase,
bool is_explicit,
bool is_creation) {
passphrase_ = passphrase;
}
virtual string16 GetAuthenticatedUsername() const {
return UTF8ToUTF16(username_);
}
void set_auth_state(const std::string& last_email,
const AuthError& error) {
last_attempted_user_email_ = last_email;
last_auth_error_ = error;
}
void set_passphrase_required(bool required) {
observed_passphrase_required_ = required;
}
void ResetTestStats() {
username_.clear();
password_.clear();
captcha_.clear();
user_cancelled_dialog_ = false;
user_chose_data_types_ = false;
keep_everything_synced_ = false;
chosen_data_types_.clear();
}
std::string username_;
std::string password_;
std::string captcha_;
bool user_cancelled_dialog_;
bool user_chose_data_types_;
bool keep_everything_synced_;
syncable::ModelTypeSet chosen_data_types_;
std::string passphrase_;
private:
DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceForWizardTest);
};
class TestingProfileWithSyncService : public TestingProfile {
public:
TestingProfileWithSyncService() {
sync_service_.reset(new ProfileSyncServiceForWizardTest(&factory_, this));
}
virtual ProfileSyncService* GetProfileSyncService() {
return sync_service_.get();
}
private:
ProfileSyncFactoryMock factory_;
scoped_ptr<ProfileSyncService> sync_service_;
};
class TestBrowserWindowForWizardTest : public TestBrowserWindow {
public:
explicit TestBrowserWindowForWizardTest(Browser* browser)
: TestBrowserWindow(browser), flow_(NULL),
was_show_html_dialog_called_(false) {
}
virtual ~TestBrowserWindowForWizardTest() {
if (flow_.get()) {
// The handler contract is that they are valid for the lifetime of the
// sync login overlay, but are cleaned up after the dialog is closed
// and/or deleted.
flow_.reset();
}
}
bool TestAndResetWasShowHTMLDialogCalled() {
bool ret = was_show_html_dialog_called_;
was_show_html_dialog_called_ = false;
return ret;
}
// Simulates the user (or browser view hierarchy) closing the html dialog.
// Handles cleaning up the delegate and associated handlers.
void CloseDialog() {
if (flow_.get()) {
// The flow deletes itself here. Don't use reset().
flow_.release()->OnDialogClosed("");
}
}
SyncSetupFlow* flow() { return flow_.get(); }
private:
// In real life, this is owned by the view that is opened by the browser. We
// mock all that out, so we need to take ownership so the flow doesn't leak.
scoped_ptr<SyncSetupFlow> flow_;
bool was_show_html_dialog_called_;
};
class SyncSetupWizardTest : public BrowserWithTestWindowTest {
public:
SyncSetupWizardTest()
: test_window_(NULL),
wizard_(NULL) { }
virtual ~SyncSetupWizardTest() { }
virtual void SetUp() {
set_profile(new TestingProfileWithSyncService());
profile()->CreateBookmarkModel(false);
// Wait for the bookmarks model to load.
profile()->BlockUntilBookmarkModelLoaded();
set_browser(new Browser(Browser::TYPE_NORMAL, profile()));
test_window_ = new TestBrowserWindowForWizardTest(browser());
set_window(test_window_);
browser()->set_window(window());
BrowserList::SetLastActive(browser());
service_ = static_cast<ProfileSyncServiceForWizardTest*>(
profile()->GetProfileSyncService());
wizard_.reset(new SyncSetupWizard(service_));
}
virtual void TearDown() {
test_window_ = NULL;
service_ = NULL;
wizard_.reset();
}
TestBrowserWindowForWizardTest* test_window_;
scoped_ptr<SyncSetupWizard> wizard_;
ProfileSyncServiceForWizardTest* service_;
};
// See http://code.google.com/p/chromium/issues/detail?id=40715 for
// why we skip the below tests on OS X. We don't use DISABLED_ as we
// would have to change the corresponding FRIEND_TEST() declarations.
#if defined(OS_MACOSX)
#define SKIP_TEST_ON_MACOSX() \
do { LOG(WARNING) << "Test skipped on OS X"; return; } while (0)
#else
#define SKIP_TEST_ON_MACOSX() do {} while (0)
#endif
TEST_F(SyncSetupWizardTest, DISABLED_InitialStepLogin) {
SKIP_TEST_ON_MACOSX();
DictionaryValue dialog_args;
SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
std::string json_start_args;
base::JSONWriter::Write(&dialog_args, false, &json_start_args);
ListValue credentials;
std::string auth = "{\"user\":\"";
auth += std::string(kTestUser) + "\",\"pass\":\"";
auth += std::string(kTestPassword) + "\",\"captcha\":\"";
auth += std::string(kTestCaptcha) + "\",\"access_code\":\"";
auth += std::string() + "\"}";
credentials.Append(new StringValue(auth));
EXPECT_FALSE(wizard_->IsVisible());
EXPECT_FALSE(test_window_->flow());
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
EXPECT_TRUE(wizard_->IsVisible());
EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->end_state_);
EXPECT_EQ(json_start_args, test_window_->flow()->dialog_start_args_);
#if 0
// Simulate the user submitting credentials.
test_window_->flow()->flow_handler_->HandleSubmitAuth(&credentials);
EXPECT_TRUE(wizard_->IsVisible());
EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
EXPECT_EQ(kTestUser, service_->username_);
EXPECT_EQ(kTestPassword, service_->password_);
EXPECT_EQ(kTestCaptcha, service_->captcha_);
EXPECT_FALSE(service_->user_cancelled_dialog_);
service_->ResetTestStats();
#endif
// Simulate failed credentials.
AuthError invalid_gaia(AuthError::INVALID_GAIA_CREDENTIALS);
service_->set_auth_state(kTestUser, invalid_gaia);
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
EXPECT_TRUE(wizard_->IsVisible());
EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
dialog_args.Clear();
SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
EXPECT_EQ(5U, dialog_args.size());
std::string iframe_to_show;
dialog_args.GetString("iframeToShow", &iframe_to_show);
EXPECT_EQ("login", iframe_to_show);
std::string actual_user;
dialog_args.GetString("user", &actual_user);
EXPECT_EQ(kTestUser, actual_user);
int error = -1;
dialog_args.GetInteger("error", &error);
EXPECT_EQ(static_cast<int>(AuthError::INVALID_GAIA_CREDENTIALS), error);
service_->set_auth_state(kTestUser, AuthError::None());
// Simulate captcha.
AuthError captcha_error(AuthError::FromCaptchaChallenge(
std::string(), GURL(kTestCaptchaUrl), GURL()));
service_->set_auth_state(kTestUser, captcha_error);
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
EXPECT_EQ(5U, dialog_args.size());
dialog_args.GetString("iframeToShow", &iframe_to_show);
EXPECT_EQ("login", iframe_to_show);
std::string captcha_url;
dialog_args.GetString("captchaUrl", &captcha_url);
EXPECT_EQ(kTestCaptchaUrl, GURL(captcha_url).spec());
error = -1;
dialog_args.GetInteger("error", &error);
EXPECT_EQ(static_cast<int>(AuthError::CAPTCHA_REQUIRED), error);
service_->set_auth_state(kTestUser, AuthError::None());
// Simulate success.
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
EXPECT_TRUE(wizard_->IsVisible());
EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
// In a non-discrete run, GAIA_SUCCESS immediately transitions you to
// SYNC_EVERYTHING.
EXPECT_EQ(SyncSetupWizard::SYNC_EVERYTHING,
test_window_->flow()->current_state_);
// That's all we're testing here, just move on to DONE. We'll test the
// "choose data types" scenarios elsewhere.
wizard_->Step(SyncSetupWizard::SETTING_UP); // No merge and sync.
wizard_->Step(SyncSetupWizard::DONE); // No merge and sync.
EXPECT_TRUE(wizard_->IsVisible());
EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->current_state_);
}
TEST_F(SyncSetupWizardTest, DISABLED_ChooseDataTypesSetsPrefs) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
wizard_->Step(SyncSetupWizard::CONFIGURE);
ListValue data_type_choices_value;
std::string data_type_choices = "{\"keepEverythingSynced\":false,";
data_type_choices += "\"syncBookmarks\":true,\"syncPreferences\":true,";
data_type_choices += "\"syncThemes\":false,\"syncPasswords\":false,";
data_type_choices += "\"syncAutofill\":false,\"syncExtensions\":false,";
data_type_choices += "\"syncTypedUrls\":true,\"syncApps\":true,";
data_type_choices += "\"syncSessions\":false,\"usePassphrase\":false}";
data_type_choices_value.Append(new StringValue(data_type_choices));
#if 0
// Simulate the user choosing data types; bookmarks, prefs, typed
// URLS, and apps are on, the rest are off.
test_window_->flow()->flow_handler_->HandleConfigure(
&data_type_choices_value);
EXPECT_TRUE(wizard_->IsVisible());
EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
EXPECT_FALSE(service_->keep_everything_synced_);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::BOOKMARKS), 1U);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::PREFERENCES), 1U);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::THEMES), 0U);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::PASSWORDS), 0U);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::AUTOFILL), 0U);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::EXTENSIONS), 0U);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::TYPED_URLS), 1U);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::APPS), 1U);
#endif
test_window_->CloseDialog();
}
TEST_F(SyncSetupWizardTest, DISABLED_EnterPassphraseRequired) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
wizard_->Step(SyncSetupWizard::CONFIGURE);
wizard_->Step(SyncSetupWizard::SETTING_UP);
service_->set_passphrase_required(true);
wizard_->Step(SyncSetupWizard::ENTER_PASSPHRASE);
EXPECT_EQ(SyncSetupWizard::ENTER_PASSPHRASE,
test_window_->flow()->current_state_);
#if 0
ListValue value;
value.Append(new StringValue("{\"passphrase\":\"myPassphrase\","
"\"mode\":\"gaia\"}"));
test_window_->flow()->flow_handler_->HandlePassphraseEntry(&value);
EXPECT_EQ("myPassphrase", service_->passphrase_);
#endif
}
TEST_F(SyncSetupWizardTest, DISABLED_PassphraseMigration) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::PASSPHRASE_MIGRATION);
#if 0
ListValue value;
value.Append(new StringValue("{\"option\":\"explicit\","
"\"passphrase\":\"myPassphrase\"}"));
test_window_->flow()->flow_handler_->HandleFirstPassphrase(&value);
EXPECT_EQ("myPassphrase", service_->passphrase_);
ListValue value2;
value2.Append(new StringValue("{\"option\":\"nothanks\","
"\"passphrase\":\"myPassphrase\"}"));
test_window_->flow()->flow_handler_->HandleFirstPassphrase(&value2);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::PASSWORDS), 0U);
#endif
}
TEST_F(SyncSetupWizardTest, DISABLED_DialogCancelled) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
// Simulate the user closing the dialog.
test_window_->CloseDialog();
EXPECT_FALSE(wizard_->IsVisible());
EXPECT_TRUE(service_->user_cancelled_dialog_);
EXPECT_EQ(std::string(), service_->username_);
EXPECT_EQ(std::string(), service_->password_);
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
EXPECT_TRUE(wizard_->IsVisible());
EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
test_window_->CloseDialog();
EXPECT_FALSE(wizard_->IsVisible());
EXPECT_TRUE(service_->user_cancelled_dialog_);
EXPECT_EQ(std::string(), service_->username_);
EXPECT_EQ(std::string(), service_->password_);
}
TEST_F(SyncSetupWizardTest, DISABLED_InvalidTransitions) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
EXPECT_FALSE(wizard_->IsVisible());
EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
wizard_->Step(SyncSetupWizard::DONE);
EXPECT_FALSE(wizard_->IsVisible());
EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME);
EXPECT_FALSE(wizard_->IsVisible());
EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::DONE);
EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME);
EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
wizard_->Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN,
test_window_->flow()->current_state_);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
EXPECT_EQ(SyncSetupWizard::SYNC_EVERYTHING,
test_window_->flow()->current_state_);
wizard_->Step(SyncSetupWizard::FATAL_ERROR);
EXPECT_EQ(SyncSetupWizard::FATAL_ERROR, test_window_->flow()->current_state_);
}
TEST_F(SyncSetupWizardTest, DISABLED_FullSuccessfulRunSetsPref) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
wizard_->Step(SyncSetupWizard::SETTING_UP);
wizard_->Step(SyncSetupWizard::DONE);
test_window_->CloseDialog();
EXPECT_FALSE(wizard_->IsVisible());
EXPECT_TRUE(service_->profile()->GetPrefs()->GetBoolean(
prefs::kSyncHasSetupCompleted));
}
TEST_F(SyncSetupWizardTest, DISABLED_FirstFullSuccessfulRunSetsPref) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
wizard_->Step(SyncSetupWizard::SETTING_UP);
wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME);
test_window_->CloseDialog();
EXPECT_FALSE(wizard_->IsVisible());
EXPECT_TRUE(service_->profile()->GetPrefs()->GetBoolean(
prefs::kSyncHasSetupCompleted));
}
TEST_F(SyncSetupWizardTest, DISABLED_AbortedByPendingClear) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
wizard_->Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
EXPECT_EQ(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR,
test_window_->flow()->current_state_);
test_window_->CloseDialog();
EXPECT_FALSE(wizard_->IsVisible());
}
TEST_F(SyncSetupWizardTest, DISABLED_DiscreteRunChooseDataTypes) {
SKIP_TEST_ON_MACOSX();
// For a discrete run, we need to have ran through setup once.
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
wizard_->Step(SyncSetupWizard::DONE);
test_window_->CloseDialog();
EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
wizard_->Step(SyncSetupWizard::CONFIGURE);
EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->end_state_);
wizard_->Step(SyncSetupWizard::DONE);
test_window_->CloseDialog();
EXPECT_FALSE(wizard_->IsVisible());
}
TEST_F(SyncSetupWizardTest,
DISABLED_DiscreteRunChooseDataTypesAbortedByPendingClear) {
SKIP_TEST_ON_MACOSX();
// For a discrete run, we need to have ran through setup once.
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
wizard_->Step(SyncSetupWizard::DONE);
test_window_->CloseDialog();
EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
wizard_->Step(SyncSetupWizard::CONFIGURE);
EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->end_state_);
wizard_->Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
EXPECT_EQ(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR,
test_window_->flow()->current_state_);
test_window_->CloseDialog();
EXPECT_FALSE(wizard_->IsVisible());
}
TEST_F(SyncSetupWizardTest, DISABLED_DiscreteRunGaiaLogin) {
SKIP_TEST_ON_MACOSX();
DictionaryValue dialog_args;
// For a discrete run, we need to have ran through setup once.
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
wizard_->Step(SyncSetupWizard::SETTING_UP);
wizard_->Step(SyncSetupWizard::DONE);
test_window_->CloseDialog();
EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
EXPECT_EQ(SyncSetupWizard::GAIA_SUCCESS, test_window_->flow()->end_state_);
AuthError invalid_gaia(AuthError::INVALID_GAIA_CREDENTIALS);
service_->set_auth_state(kTestUser, invalid_gaia);
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
EXPECT_TRUE(wizard_->IsVisible());
SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
EXPECT_EQ(5U, dialog_args.size());
std::string iframe_to_show;
dialog_args.GetString("iframeToShow", &iframe_to_show);
EXPECT_EQ("login", iframe_to_show);
std::string actual_user;
dialog_args.GetString("user", &actual_user);
EXPECT_EQ(kTestUser, actual_user);
int error = -1;
dialog_args.GetInteger("error", &error);
EXPECT_EQ(static_cast<int>(AuthError::INVALID_GAIA_CREDENTIALS), error);
service_->set_auth_state(kTestUser, AuthError::None());
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
}
#undef SKIP_TEST_ON_MACOSX