// 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.
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_signin.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_test_message_listener.h"
#include "chrome/browser/extensions/extension_webstore_private_api.h"
#include "chrome/browser/net/gaia/token_service.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/ui_test_utils.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "googleurl/src/gurl.h"
#include "net/base/mock_host_resolver.h"
using chrome::kHttpScheme;
using chrome::kStandardSchemeSeparator;
namespace {
const char kTestUrlHostname[] = "www.example.com";
} // namespace
// A fake version of ProfileSyncService used for testing.
class FakeProfileSyncService : public ProfileSyncService {
public:
FakeProfileSyncService()
: ProfileSyncService(NULL, NULL, ""),
setup_(false) {
}
virtual ~FakeProfileSyncService() {}
// Overrides of virtual methods in ProfileSyncService.
virtual bool HasSyncSetupCompleted() const {
return setup_;
}
virtual void ChangePreferredDataTypes(const syncable::ModelTypeSet& types) {
types_ = types;
}
virtual void GetPreferredDataTypes(syncable::ModelTypeSet* types) const {
*types = types_;
}
virtual void SetSyncSetupCompleted() {
setup_ = true;
}
private:
bool setup_;
syncable::ModelTypeSet types_;
};
class FakeBrowserSignin : public BrowserSignin {
public:
// The |username_after_login| parameter determines what this fake
// BrowserSignin will set the username to when ShowLoginDialog is called.
FakeBrowserSignin(bool should_succeed,
const std::string& initial_username,
const std::string& username_after_login)
: BrowserSignin(NULL),
should_succeed_(should_succeed),
username_(initial_username),
username_after_login_(username_after_login) {
}
virtual ~FakeBrowserSignin() {}
virtual std::string GetSignedInUsername() const {
return username_;
}
virtual void RequestSignin(TabContents* tab_contents,
const string16& preferred_email,
const string16& message,
SigninDelegate* delegate) {
if (should_succeed_) {
// Simulate valid login.
username_ = username_after_login_;
delegate->OnLoginSuccess();
// Fake a token available notification.
Profile* profile = tab_contents->profile();
if (profile->IsOffTheRecord()) {
profile = g_browser_process->profile_manager()->GetDefaultProfile();
}
TokenService* token_service = profile->GetTokenService();
token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService,
"new_token");
} else {
delegate->OnLoginFailure(GoogleServiceAuthError(
GoogleServiceAuthError::REQUEST_CANCELED));
}
}
private:
bool should_succeed_;
std::string username_;
std::string username_after_login_;
};
class ExtensionWebstorePrivateBrowserTest : public ExtensionBrowserTest {
public:
ExtensionWebstorePrivateBrowserTest() {
test_url_base_ = std::string() + kHttpScheme + kStandardSchemeSeparator +
kTestUrlHostname;
}
void SetUpCommandLine(CommandLine* command_line) {
ExtensionBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(switches::kAppsGalleryURL, test_url_base_);
}
// This generates a regular test server url pointing to a test file at
// |relative_path|, but replaces the hostname with kTestUrlHostname so that
// we get the webstore private APIs injected (this happens because of the
// command line switch we added in SetupCommandLine).
GURL GetUrl(const std::string& relative_path) {
GURL base_url = test_server()->GetURL(
"files/extensions/webstore_private/" + relative_path);
GURL::Replacements replacements;
std::string replacement_host = std::string(kTestUrlHostname);
replacements.SetHostStr(replacement_host);
return base_url.ReplaceComponents(replacements);
}
void RunLoginTestImpl(bool incognito,
const std::string& relative_path,
const std::string& initial_login,
bool login_succeeds,
const std::string& login_result) {
// Clear the token service so previous tests don't affect things.
TokenService* token_service = browser()->profile()->GetTokenService();
token_service->ResetCredentialsInMemory();
if (!initial_login.empty()) {
// Initialize the token service with an existing token.
token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService,
"existing_token");
}
FakeProfileSyncService sync_service;
FakeBrowserSignin signin(login_succeeds, initial_login, login_result);
WebstorePrivateApi::SetTestingProfileSyncService(&sync_service);
WebstorePrivateApi::SetTestingBrowserSignin(&signin);
ExtensionTestMessageListener listener("success", false);
GURL url = GetUrl(relative_path);
if (incognito) {
ui_test_utils::OpenURLOffTheRecord(browser()->profile(), url);
} else {
ui_test_utils::NavigateToURL(browser(), url);
}
EXPECT_TRUE(listener.WaitUntilSatisfied());
WebstorePrivateApi::SetTestingBrowserSignin(NULL);
WebstorePrivateApi::SetTestingProfileSyncService(NULL);
}
void RunLoginTest(const std::string& relative_path,
const std::string& initial_login,
bool login_succeeds,
const std::string& login_result) {
RunLoginTestImpl(true,
relative_path,
initial_login,
login_succeeds,
login_result);
RunLoginTestImpl(false,
relative_path,
initial_login,
login_succeeds,
login_result);
}
protected:
std::string test_url_base_;
};
IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateBrowserTest, BrowserLogin) {
host_resolver()->AddRule(kTestUrlHostname, "127.0.0.1");
ASSERT_TRUE(test_server()->Start());
RunLoginTest("browser_login/expect_nonempty.html",
"foo@bar.com", false, "");
RunLoginTest("browser_login/prompt_no_preferred.html", "", true, "");
RunLoginTest("browser_login/prompt_preferred.html", "", true, "foo@bar.com");
RunLoginTest("browser_login/prompt_login_fails.html",
"", false, "foo@bar.com");
}