blob: 4da5b7547b092f6a117dda21bbdc0fc204c3cc35 [file] [log] [blame]
// Copyright 2018 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 <string>
#include <utility>
#include "base/files/file_enumerator.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/autofill/automated_tests/cache_replayer.h"
#include "chrome/browser/autofill/captured_sites_test_utils.h"
#include "chrome/browser/password_manager/password_manager_test_base.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
#include "chrome/browser/ui/tab_dialogs.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/test_password_store.h"
#include "content/public/test/test_utils.h"
namespace {
constexpr base::TimeDelta kWaitForSaveFallbackInterval =
base::TimeDelta::FromSeconds(5);
struct TestParams {
std::string scenarioDir;
std::string siteName;
};
// Return path to the Password Manager captured sites test root directory. The
// directory contains subdirectories for different password manager test
// scenarios. The test scenario subdirectories contain site capture files
// and test recipe replay files.
base::FilePath GetReplayFilesRootDirectory() {
base::FilePath src_dir;
if (base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) {
return src_dir.AppendASCII("chrome")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("password")
.AppendASCII("captured_sites");
}
ADD_FAILURE() << "Unable to obtain the Chromium src directory!";
src_dir.clear();
return src_dir;
}
// Iterate through Password Manager's Web Page Replay capture file directory to
// look for captures sites and automation recipe files. Return a list of sites
// for which recipe-based testing is available.
std::vector<TestParams> GetCapturedSites() {
std::vector<TestParams> sites;
base::FileEnumerator sub_dirs(GetReplayFilesRootDirectory(), false,
base::FileEnumerator::DIRECTORIES);
for (base::FilePath dir = sub_dirs.Next(); !dir.empty();
dir = sub_dirs.Next()) {
base::FileEnumerator capture_files(dir, false, base::FileEnumerator::FILES);
for (base::FilePath file = capture_files.Next(); !file.empty();
file = capture_files.Next()) {
// If a site capture file is found, also look to see if the directory has
// a corresponding recorded action recipe log file.
// A site capture file has no extension. A recorded action recipe log file
// has the '.test' extension.
if (file.Extension().empty() &&
base::PathExists(file.AddExtension(FILE_PATH_LITERAL(".test")))) {
TestParams params;
params.scenarioDir =
captured_sites_test_utils::FilePathToUTF8(dir.BaseName().value());
params.siteName =
captured_sites_test_utils::FilePathToUTF8(file.BaseName().value());
sites.push_back(params);
}
}
}
return sites;
}
struct GetParamAsString {
template <class ParamType>
std::string operator()(const testing::TestParamInfo<ParamType>& info) const {
return base::StringPrintf("%s_%s", info.param.scenarioDir.c_str(),
info.param.siteName.c_str());
}
};
} // namespace
namespace password_manager {
using autofill::test::ServerCacheReplayer;
using autofill::test::ServerUrlLoader;
// Harness for running password manager scenarios on captured real-world sites.
// Test params:
// - string Recipe: the name of the captured site file and the test recipe
// file.
class CapturedSitesPasswordManagerBrowserTest
: public InProcessBrowserTest,
public captured_sites_test_utils::
TestRecipeReplayChromeFeatureActionExecutor,
public ::testing::WithParamInterface<TestParams> {
public:
// TestRecipeReplayChromeFeatureActionExecutor:
bool AddCredential(const std::string& origin,
const std::string& username,
const std::string& password) override {
scoped_refptr<password_manager::TestPasswordStore> password_store =
static_cast<password_manager::TestPasswordStore*>(
PasswordStoreFactory::GetForProfile(
browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
.get());
autofill::PasswordForm signin_form;
signin_form.origin = GURL(origin);
signin_form.signon_realm = origin;
signin_form.password_value = base::ASCIIToUTF16(password);
signin_form.username_value = base::ASCIIToUTF16(username);
password_store->AddLogin(signin_form);
return true;
}
bool SavePassword() override {
BubbleObserver bubble_observer(WebContents());
if (bubble_observer.IsSavePromptAvailable()) {
bubble_observer.AcceptSavePrompt();
PasswordManagerBrowserTestBase::WaitForPasswordStore(browser());
// Hide the Save Password Prompt UI.
TabDialogs::FromWebContents(WebContents())->HideManagePasswordsBubble();
content::RunAllPendingInMessageLoop();
return true;
}
ADD_FAILURE() << "No Save Password prompt!";
return false;
}
bool UpdatePassword() override {
BubbleObserver bubble_observer(WebContents());
if (bubble_observer.IsUpdatePromptAvailable()) {
const autofill::PasswordForm& pending_credentials =
ManagePasswordsUIController::FromWebContents(WebContents())
->GetPendingPassword();
bubble_observer.AcceptUpdatePrompt(pending_credentials);
PasswordManagerBrowserTestBase::WaitForPasswordStore(browser());
// Hide the Update Password Prompt UI.
TabDialogs::FromWebContents(WebContents())->HideManagePasswordsBubble();
content::RunAllPendingInMessageLoop();
return true;
}
ADD_FAILURE() << "No Update Password prompt!";
return false;
}
bool WaitForSaveFallback() override {
BubbleObserver bubble_observer(WebContents());
if (bubble_observer.WaitForFallbackForSaving(kWaitForSaveFallbackInterval))
return true;
ADD_FAILURE() << "Chrome did not show the save fallback icon!";
return false;
}
bool HasChromeShownSavePasswordPrompt() override {
BubbleObserver bubble_observer(WebContents());
return bubble_observer.IsSavePromptShownAutomatically();
}
bool HasChromeStoredCredential(const std::string& origin,
const std::string& username,
const std::string& password) override {
scoped_refptr<password_manager::TestPasswordStore> password_store =
static_cast<password_manager::TestPasswordStore*>(
PasswordStoreFactory::GetForProfile(
browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
.get());
auto found = password_store->stored_passwords().find(origin);
if (password_store->stored_passwords().end() == found) {
return false;
}
const std::vector<autofill::PasswordForm>& passwords_vector = found->second;
for (auto it = passwords_vector.begin(); it != passwords_vector.end();
++it) {
if (base::ASCIIToUTF16(username) == it->username_value &&
base::ASCIIToUTF16(password) == it->password_value) {
return true;
}
}
return false;
}
protected:
CapturedSitesPasswordManagerBrowserTest() = default;
~CapturedSitesPasswordManagerBrowserTest() override = default;
// InProcessBrowserTest:
void SetUpOnMainThread() override {
PasswordManagerBrowserTestBase::SetUpOnMainThreadAndGetNewTab(
browser(), &web_contents_);
recipe_replayer_ =
std::make_unique<captured_sites_test_utils::TestRecipeReplayer>(
browser(), this);
recipe_replayer()->Setup();
base::FilePath capture_file_path =
GetReplayFilesRootDirectory()
.AppendASCII(GetParam().scenarioDir.c_str())
.AppendASCII(GetParam().siteName.c_str());
SetServerUrlLoader(std::make_unique<ServerUrlLoader>(
absl::make_unique<ServerCacheReplayer>(
capture_file_path,
ServerCacheReplayer::kOptionFailOnInvalidJsonRecord)));
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
captured_sites_test_utils::TestRecipeReplayer::SetUpCommandLine(
command_line);
}
void SetServerUrlLoader(std::unique_ptr<ServerUrlLoader> server_url_loader) {
server_url_loader_ = std::move(server_url_loader);
}
void TearDownOnMainThread() override {
recipe_replayer()->Cleanup();
// Need to delete the URL loader and its underlying interceptor on the main
// thread. Will result in a fatal crash otherwise. The pointer has its
// memory cleaned up twice: first time in that single thread, a second time
// when the fixture's destructor is called, which will have no effect since
// the raw pointer will be nullptr.
server_url_loader_.reset(nullptr);
}
captured_sites_test_utils::TestRecipeReplayer* recipe_replayer() {
return recipe_replayer_.get();
}
content::WebContents* WebContents() {
// return web_contents_;
return web_contents_;
}
private:
std::unique_ptr<captured_sites_test_utils::TestRecipeReplayer>
recipe_replayer_;
content::WebContents* web_contents_ = nullptr;
std::unique_ptr<ServerUrlLoader> server_url_loader_;
DISALLOW_COPY_AND_ASSIGN(CapturedSitesPasswordManagerBrowserTest);
};
IN_PROC_BROWSER_TEST_P(CapturedSitesPasswordManagerBrowserTest, Recipe) {
// Craft the capture file path.
base::FilePath src_dir;
ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
base::FilePath capture_file_path =
GetReplayFilesRootDirectory()
.AppendASCII(GetParam().scenarioDir.c_str())
.AppendASCII(GetParam().siteName.c_str());
// Craft the recipe file path.
base::FilePath recipe_file_path =
GetReplayFilesRootDirectory()
.AppendASCII(GetParam().scenarioDir.c_str())
.AppendASCII(
base::StringPrintf("%s.test", GetParam().siteName.c_str()));
ASSERT_TRUE(
recipe_replayer()->ReplayTest(capture_file_path, recipe_file_path));
}
INSTANTIATE_TEST_SUITE_P(,
CapturedSitesPasswordManagerBrowserTest,
testing::ValuesIn(GetCapturedSites()),
GetParamAsString());
} // namespace password_manager