Commit 8669b846 authored by Yiming Zhou's avatar Yiming Zhou Committed by Commit Bot

Support password manager scenarios in the captured sites test framework

Bug: 847905
Change-Id: Ic8da3d3d2214a06cf0cc8a180d66a81fbc89ed90
Reviewed-on: https://chromium-review.googlesource.com/1214208
Commit-Queue: Yiming Zhou <uwyiming@google.com>
Reviewed-by: default avatarSebastien Seguin-Gagnon <sebsg@chromium.org>
Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#593060}
parent a5a00728
...@@ -52,6 +52,13 @@ namespace { ...@@ -52,6 +52,13 @@ namespace {
const base::TimeDelta autofill_wait_for_action_interval = const base::TimeDelta autofill_wait_for_action_interval =
base::TimeDelta::FromSeconds(5); base::TimeDelta::FromSeconds(5);
struct GetParamAsString {
template <class ParamType>
std::string operator()(const testing::TestParamInfo<ParamType>& info) const {
return info.param;
}
};
base::FilePath GetReplayFilesDirectory() { base::FilePath GetReplayFilesDirectory() {
base::FilePath src_dir; base::FilePath src_dir;
if (base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) { if (base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) {
...@@ -248,13 +255,6 @@ IN_PROC_BROWSER_TEST_P(AutofillCapturedSitesInteractiveTest, Recipe) { ...@@ -248,13 +255,6 @@ IN_PROC_BROWSER_TEST_P(AutofillCapturedSitesInteractiveTest, Recipe) {
recipe_replayer()->ReplayTest(capture_file_path, recipe_file_path)); recipe_replayer()->ReplayTest(capture_file_path, recipe_file_path));
} }
struct GetParamAsString {
template <class ParamType>
std::string operator()(const testing::TestParamInfo<ParamType>& info) const {
return info.param;
}
};
INSTANTIATE_TEST_CASE_P(, INSTANTIATE_TEST_CASE_P(,
AutofillCapturedSitesInteractiveTest, AutofillCapturedSitesInteractiveTest,
testing::ValuesIn(GetCapturedSites()), testing::ValuesIn(GetCapturedSites()),
......
...@@ -209,7 +209,7 @@ TestRecipeReplayer::TestRecipeReplayer( ...@@ -209,7 +209,7 @@ TestRecipeReplayer::TestRecipeReplayer(
TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor) TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor)
: browser_(browser), feature_action_executor_(feature_action_executor) {} : browser_(browser), feature_action_executor_(feature_action_executor) {}
TestRecipeReplayer::~TestRecipeReplayer(){}; TestRecipeReplayer::~TestRecipeReplayer() {}
bool TestRecipeReplayer::ReplayTest(const base::FilePath capture_file_path, bool TestRecipeReplayer::ReplayTest(const base::FilePath capture_file_path,
const base::FilePath recipe_file_path) { const base::FilePath recipe_file_path) {
...@@ -227,6 +227,7 @@ void TestRecipeReplayer::SetUpCommandLine(base::CommandLine* command_line) { ...@@ -227,6 +227,7 @@ void TestRecipeReplayer::SetUpCommandLine(base::CommandLine* command_line) {
base::StringPrintf( base::StringPrintf(
"MAP *:80 127.0.0.1:%d," "MAP *:80 127.0.0.1:%d,"
"MAP *:443 127.0.0.1:%d," "MAP *:443 127.0.0.1:%d,"
// Uncomment to use the live autofill prediction server. // Uncomment to use the live autofill prediction server.
//"EXCLUDE clients1.google.com," //"EXCLUDE clients1.google.com,"
"EXCLUDE localhost", "EXCLUDE localhost",
...@@ -491,12 +492,20 @@ bool TestRecipeReplayer::ReplayRecordedActions( ...@@ -491,12 +492,20 @@ bool TestRecipeReplayer::ReplayRecordedActions(
} else if (base::CompareCaseInsensitiveASCII(type, "click") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "click") == 0) {
if (!ExecuteClickAction(*action)) if (!ExecuteClickAction(*action))
return false; return false;
} else if (base::CompareCaseInsensitiveASCII(type, "executeScript") == 0) {
if (!ExecuteRunCommandAction(*action))
return false;
} else if (base::CompareCaseInsensitiveASCII(type, "hover") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "hover") == 0) {
if (!ExecuteHoverAction(*action)) if (!ExecuteHoverAction(*action))
return false; return false;
} else if (base::CompareCaseInsensitiveASCII(type, "loadPage") == 0) {
// Load page is an no-op action.
} else if (base::CompareCaseInsensitiveASCII(type, "pressEnter") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "pressEnter") == 0) {
if (!ExecutePressEnterAction(*action)) if (!ExecutePressEnterAction(*action))
return false; return false;
} else if (base::CompareCaseInsensitiveASCII(type, "savePassword") == 0) {
if (!ExecuteSavePasswordAction(*action))
return false;
} else if (base::CompareCaseInsensitiveASCII(type, "select") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "select") == 0) {
if (!ExecuteSelectDropdownAction(*action)) if (!ExecuteSelectDropdownAction(*action))
return false; return false;
...@@ -506,9 +515,16 @@ bool TestRecipeReplayer::ReplayRecordedActions( ...@@ -506,9 +515,16 @@ bool TestRecipeReplayer::ReplayRecordedActions(
} else if (base::CompareCaseInsensitiveASCII(type, "typePassword") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "typePassword") == 0) {
if (!ExecuteTypePasswordAction(*action)) if (!ExecuteTypePasswordAction(*action))
return false; return false;
} else if (base::CompareCaseInsensitiveASCII(type, "updatePassword") == 0) {
if (!ExecuteUpdatePasswordAction(*action))
return false;
} else if (base::CompareCaseInsensitiveASCII(type, "validateField") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "validateField") == 0) {
if (!ExecuteValidateFieldValueAction(*action)) if (!ExecuteValidateFieldValueAction(*action))
return false; return false;
} else if (base::CompareCaseInsensitiveASCII(
type, "validateNoSavePasswordPrompt") == 0) {
if (!ExecuteValidateNoSavePasswordPromptAction(*action))
return false;
} else if (base::CompareCaseInsensitiveASCII(type, "waitFor") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "waitFor") == 0) {
if (!ExecuteWaitForStateAction(*action)) if (!ExecuteWaitForStateAction(*action))
return false; return false;
...@@ -530,12 +546,21 @@ bool TestRecipeReplayer::ReplayRecordedActions( ...@@ -530,12 +546,21 @@ bool TestRecipeReplayer::ReplayRecordedActions(
// JSON object. // JSON object.
bool TestRecipeReplayer::InitializeBrowserToExecuteRecipe( bool TestRecipeReplayer::InitializeBrowserToExecuteRecipe(
std::unique_ptr<base::DictionaryValue>& recipe) { std::unique_ptr<base::DictionaryValue>& recipe) {
// Setup any saved passwords at the start of the test.
const base::Value* saved_password_container =
recipe->FindKey("passwordManagerProfiles");
if (saved_password_container &&
!SetupSavedPasswords(*saved_password_container))
return false;
// Extract the starting URL from the test recipe. // Extract the starting URL from the test recipe.
base::Value* starting_url_container = recipe->FindKey("startingURL"); base::Value* starting_url_container = recipe->FindKey("startingURL");
if (!starting_url_container) { if (!starting_url_container) {
ADD_FAILURE() << "Failed to extract the starting url from the recipe!"; ADD_FAILURE() << "Failed to extract the starting url from the recipe!";
return false; return false;
} }
if (base::Value::Type::STRING != starting_url_container->type()) { if (base::Value::Type::STRING != starting_url_container->type()) {
ADD_FAILURE() << "Starting url is not a string!"; ADD_FAILURE() << "Starting url is not a string!";
return false; return false;
...@@ -726,6 +751,25 @@ bool TestRecipeReplayer::ExecuteRunCommandAction( ...@@ -726,6 +751,25 @@ bool TestRecipeReplayer::ExecuteRunCommandAction(
return true; return true;
} }
bool TestRecipeReplayer::ExecuteSavePasswordAction(
const base::DictionaryValue& action) {
VLOG(1) << "Save password.";
if (!feature_action_executor()->SavePassword())
return false;
bool stored_cred;
if (!HasChromeStoredCredential(action, &stored_cred))
return false;
if (!stored_cred) {
ADD_FAILURE() << "Chrome did not save the credential!";
return false;
}
return true;
}
bool TestRecipeReplayer::ExecuteSelectDropdownAction( bool TestRecipeReplayer::ExecuteSelectDropdownAction(
const base::DictionaryValue& action) { const base::DictionaryValue& action) {
const base::Value* index_container = action.FindKey("index"); const base::Value* index_container = action.FindKey("index");
...@@ -862,6 +906,25 @@ bool TestRecipeReplayer::ExecuteTypePasswordAction( ...@@ -862,6 +906,25 @@ bool TestRecipeReplayer::ExecuteTypePasswordAction(
return true; return true;
} }
bool TestRecipeReplayer::ExecuteUpdatePasswordAction(
const base::DictionaryValue& action) {
VLOG(1) << "Update password.";
if (!feature_action_executor()->UpdatePassword())
return false;
bool stored_cred;
if (!HasChromeStoredCredential(action, &stored_cred))
return false;
if (!stored_cred) {
ADD_FAILURE() << "Chrome did not update the credential!";
return false;
}
return true;
}
bool TestRecipeReplayer::ExecuteValidateFieldValueAction( bool TestRecipeReplayer::ExecuteValidateFieldValueAction(
const base::DictionaryValue& action) { const base::DictionaryValue& action) {
std::string xpath; std::string xpath;
...@@ -913,6 +976,13 @@ bool TestRecipeReplayer::ExecuteValidateFieldValueAction( ...@@ -913,6 +976,13 @@ bool TestRecipeReplayer::ExecuteValidateFieldValueAction(
return true; return true;
} }
bool TestRecipeReplayer::ExecuteValidateNoSavePasswordPromptAction(
const base::DictionaryValue& action) {
VLOG(1) << "Verify that the page hasn't shown a save password prompt.";
EXPECT_FALSE(feature_action_executor()->HasChromeShownSavePasswordPrompt());
return true;
}
bool TestRecipeReplayer::ExecuteWaitForStateAction( bool TestRecipeReplayer::ExecuteWaitForStateAction(
const base::DictionaryValue& action) { const base::DictionaryValue& action) {
// Extract the list of JavaScript assertions into a vector. // Extract the list of JavaScript assertions into a vector.
...@@ -1202,7 +1272,6 @@ bool TestRecipeReplayer::GetCenterCoordinateOfTargetElement( ...@@ -1202,7 +1272,6 @@ bool TestRecipeReplayer::GetCenterCoordinateOfTargetElement(
" try {" " try {"
" const element = automation_helper.getElementByXpath(`%s`);" " const element = automation_helper.getElementByXpath(`%s`);"
" const rect = element.getBoundingClientRect();" " const rect = element.getBoundingClientRect();"
" console.log(`Window href x: ${location.href}`);"
" return Math.floor(rect.left + rect.width / 2);" " return Math.floor(rect.left + rect.width / 2);"
" } catch(ex) {}" " } catch(ex) {}"
" return -1;" " return -1;"
...@@ -1214,7 +1283,6 @@ bool TestRecipeReplayer::GetCenterCoordinateOfTargetElement( ...@@ -1214,7 +1283,6 @@ bool TestRecipeReplayer::GetCenterCoordinateOfTargetElement(
" try {" " try {"
" const element = automation_helper.getElementByXpath(`%s`);" " const element = automation_helper.getElementByXpath(`%s`);"
" const rect = element.getBoundingClientRect();" " const rect = element.getBoundingClientRect();"
" console.log(`Window href y: ${location.href}`);"
" return Math.floor(rect.top + rect.height / 2);" " return Math.floor(rect.top + rect.height / 2);"
" } catch(ex) {}" " } catch(ex) {}"
" return -1;" " return -1;"
...@@ -1248,6 +1316,7 @@ bool TestRecipeReplayer::GetCenterCoordinateOfTargetElement( ...@@ -1248,6 +1316,7 @@ bool TestRecipeReplayer::GetCenterCoordinateOfTargetElement(
bool TestRecipeReplayer::SimulateLeftMouseClickAt( bool TestRecipeReplayer::SimulateLeftMouseClickAt(
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
const gfx::Point& point) { const gfx::Point& point) {
content::RenderWidgetHostView* view = render_frame_host->GetView();
if (!SimulateMouseHoverAt(render_frame_host, point)) if (!SimulateMouseHoverAt(render_frame_host, point))
return false; return false;
...@@ -1264,8 +1333,7 @@ bool TestRecipeReplayer::SimulateLeftMouseClickAt( ...@@ -1264,8 +1333,7 @@ bool TestRecipeReplayer::SimulateLeftMouseClickAt(
mouse_event.SetPositionInScreen(point.x() + offset.x(), mouse_event.SetPositionInScreen(point.x() + offset.x(),
point.y() + offset.y()); point.y() + offset.y());
mouse_event.click_count = 1; mouse_event.click_count = 1;
content::RenderWidgetHost* widget = content::RenderWidgetHost* widget = view->GetRenderWidgetHost();
render_frame_host->GetView()->GetRenderWidgetHost();
widget->ForwardMouseEvent(mouse_event); widget->ForwardMouseEvent(mouse_event);
mouse_event.SetType(blink::WebInputEvent::kMouseUp); mouse_event.SetType(blink::WebInputEvent::kMouseUp);
...@@ -1298,6 +1366,99 @@ void TestRecipeReplayer::NavigateAwayAndDismissBeforeUnloadDialog() { ...@@ -1298,6 +1366,99 @@ void TestRecipeReplayer::NavigateAwayAndDismissBeforeUnloadDialog() {
alert->native_dialog()->AcceptAppModalDialog(); alert->native_dialog()->AcceptAppModalDialog();
} }
bool TestRecipeReplayer::HasChromeStoredCredential(
const base::DictionaryValue& action,
bool* stored_cred) {
const base::Value* orgin_container = action.FindKey("origin");
if (!orgin_container) {
ADD_FAILURE() << "Failed to extract the origin from the action!";
return false;
}
if (base::Value::Type::STRING != orgin_container->type()) {
ADD_FAILURE() << "Origin is not a string!";
return false;
}
const base::Value* user_name_container = action.FindKey("userName");
if (!user_name_container) {
ADD_FAILURE() << "Failed to extract the user name from the action!";
return false;
}
if (base::Value::Type::STRING != user_name_container->type()) {
ADD_FAILURE() << "User name is not a string!";
return false;
}
const base::Value* password_container = action.FindKey("password");
if (!password_container) {
ADD_FAILURE() << "Failed to extract the password from the action!";
return false;
}
if (base::Value::Type::STRING != password_container->type()) {
ADD_FAILURE() << "Password is not a string!";
return false;
}
*stored_cred = feature_action_executor()->HasChromeStoredCredential(
orgin_container->GetString(), user_name_container->GetString(),
password_container->GetString());
return true;
}
bool TestRecipeReplayer::SetupSavedPasswords(
const base::Value& saved_password_list_container) {
if (base::Value::Type::LIST != saved_password_list_container.type()) {
ADD_FAILURE() << "Saved Password List is not a list!";
return false;
}
const base::Value::ListStorage& saved_password_list =
saved_password_list_container.GetList();
for (base::ListValue::const_iterator it_password =
saved_password_list.begin();
it_password != saved_password_list.end(); ++it_password) {
const base::DictionaryValue* cred;
if (!it_password->GetAsDictionary(&cred)) {
ADD_FAILURE() << "Failed to extract a saved password!";
return false;
}
const base::Value* origin_container = cred->FindKey("website");
if (base::Value::Type::STRING != origin_container->type()) {
ADD_FAILURE() << "Website is not a string!";
return false;
}
const std::string origin = origin_container->GetString();
const base::Value* username_container = cred->FindKey("username");
if (base::Value::Type::STRING != username_container->type()) {
ADD_FAILURE() << "User name is not a string!";
return false;
}
const std::string username = username_container->GetString();
const base::Value* password_container = cred->FindKey("password");
if (base::Value::Type::STRING != password_container->type()) {
ADD_FAILURE() << "User name is not a string!";
return false;
}
const std::string password = password_container->GetString();
if (!feature_action_executor()->AddCredential(origin, username, password)) {
return false;
}
}
return true;
}
// TestRecipeReplayChromeFeatureActionExecutor -------------------------------- // TestRecipeReplayChromeFeatureActionExecutor --------------------------------
TestRecipeReplayChromeFeatureActionExecutor:: TestRecipeReplayChromeFeatureActionExecutor::
TestRecipeReplayChromeFeatureActionExecutor() {} TestRecipeReplayChromeFeatureActionExecutor() {}
...@@ -1313,4 +1474,41 @@ bool TestRecipeReplayChromeFeatureActionExecutor::AutofillForm( ...@@ -1313,4 +1474,41 @@ bool TestRecipeReplayChromeFeatureActionExecutor::AutofillForm(
return false; return false;
} }
bool TestRecipeReplayChromeFeatureActionExecutor::AddCredential(
const std::string& origin,
const std::string& username,
const std::string& password) {
ADD_FAILURE() << "TestRecipeReplayChromeFeatureActionExecutor::AddCredential"
" is not implemented!";
return false;
}
bool TestRecipeReplayChromeFeatureActionExecutor::SavePassword() {
ADD_FAILURE() << "TestRecipeReplayChromeFeatureActionExecutor::SavePassword"
" is not implemented!";
return false;
}
bool TestRecipeReplayChromeFeatureActionExecutor::UpdatePassword() {
ADD_FAILURE() << "TestRecipeReplayChromeFeatureActionExecutor"
"::UpdatePassword is not implemented!";
return false;
}
bool TestRecipeReplayChromeFeatureActionExecutor::
HasChromeShownSavePasswordPrompt() {
ADD_FAILURE() << "TestRecipeReplayChromeFeatureActionExecutor"
"::HasChromeShownSavePasswordPrompt is not implemented!";
return false;
}
bool TestRecipeReplayChromeFeatureActionExecutor::HasChromeStoredCredential(
const std::string& origin,
const std::string& username,
const std::string& password) {
ADD_FAILURE() << "TestRecipeReplayChromeFeatureActionExecutor"
"::HasChromeStoredCredential is not implemented!";
return false;
}
} // namespace captured_sites_test_utils } // namespace captured_sites_test_utils
...@@ -30,7 +30,7 @@ const base::TimeDelta default_action_timeout = base::TimeDelta::FromSeconds(30); ...@@ -30,7 +30,7 @@ const base::TimeDelta default_action_timeout = base::TimeDelta::FromSeconds(30);
// The amount of time to wait for a page to trigger a paint in response to a // The amount of time to wait for a page to trigger a paint in response to a
// an ation. The Captured Site Automation Framework uses this timeout to // an ation. The Captured Site Automation Framework uses this timeout to
// break out of a wait loop after a hover action. // break out of a wait loop after a hover action.
const base::TimeDelta visual_update_timeout = base::TimeDelta::FromSeconds(5); const base::TimeDelta visual_update_timeout = base::TimeDelta::FromSeconds(20);
std::string FilePathToUTF8(const base::FilePath::StringType& str); std::string FilePathToUTF8(const base::FilePath::StringType& str);
...@@ -148,6 +148,16 @@ class TestRecipeReplayChromeFeatureActionExecutor { ...@@ -148,6 +148,16 @@ class TestRecipeReplayChromeFeatureActionExecutor {
virtual bool AutofillForm(content::RenderFrameHost* frame, virtual bool AutofillForm(content::RenderFrameHost* frame,
const std::string& focus_element_css_selector, const std::string& focus_element_css_selector,
const int attempts = 1); const int attempts = 1);
// Chrome Password Manager feature methods.
virtual bool AddCredential(const std::string& origin,
const std::string& username,
const std::string& password);
virtual bool SavePassword();
virtual bool UpdatePassword();
virtual bool HasChromeShownSavePasswordPrompt();
virtual bool HasChromeStoredCredential(const std::string& origin,
const std::string& username,
const std::string& password);
protected: protected:
TestRecipeReplayChromeFeatureActionExecutor(); TestRecipeReplayChromeFeatureActionExecutor();
...@@ -227,10 +237,14 @@ class TestRecipeReplayer { ...@@ -227,10 +237,14 @@ class TestRecipeReplayer {
bool ExecuteHoverAction(const base::DictionaryValue& action); bool ExecuteHoverAction(const base::DictionaryValue& action);
bool ExecutePressEnterAction(const base::DictionaryValue& action); bool ExecutePressEnterAction(const base::DictionaryValue& action);
bool ExecuteRunCommandAction(const base::DictionaryValue& action); bool ExecuteRunCommandAction(const base::DictionaryValue& action);
bool ExecuteSavePasswordAction(const base::DictionaryValue& action);
bool ExecuteSelectDropdownAction(const base::DictionaryValue& action); bool ExecuteSelectDropdownAction(const base::DictionaryValue& action);
bool ExecuteTypeAction(const base::DictionaryValue& action); bool ExecuteTypeAction(const base::DictionaryValue& action);
bool ExecuteTypePasswordAction(const base::DictionaryValue& action); bool ExecuteTypePasswordAction(const base::DictionaryValue& action);
bool ExecuteUpdatePasswordAction(const base::DictionaryValue& action);
bool ExecuteValidateFieldValueAction(const base::DictionaryValue& action); bool ExecuteValidateFieldValueAction(const base::DictionaryValue& action);
bool ExecuteValidateNoSavePasswordPromptAction(
const base::DictionaryValue& action);
bool ExecuteWaitForStateAction(const base::DictionaryValue& action); bool ExecuteWaitForStateAction(const base::DictionaryValue& action);
bool GetTargetHTMLElementXpathFromAction(const base::DictionaryValue& action, bool GetTargetHTMLElementXpathFromAction(const base::DictionaryValue& action,
std::string* xpath); std::string* xpath);
...@@ -256,6 +270,9 @@ class TestRecipeReplayer { ...@@ -256,6 +270,9 @@ class TestRecipeReplayer {
const std::string& expected_value, const std::string& expected_value,
const bool ignoreCase = false); const bool ignoreCase = false);
void NavigateAwayAndDismissBeforeUnloadDialog(); void NavigateAwayAndDismissBeforeUnloadDialog();
bool HasChromeStoredCredential(const base::DictionaryValue& action,
bool* stored_cred);
bool SetupSavedPasswords(const base::Value& saved_password_list_container);
Browser* browser_; Browser* browser_;
TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor_; TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor_;
......
// 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/utf_string_conversions.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 {
// Return path to the Password Manager captured sites test directory. The
// directory contains site capture files and test recipe replay files.
base::FilePath GetReplayFilesDirectory() {
base::FilePath src_dir;
if (base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) {
return src_dir.Append(
FILE_PATH_LITERAL("chrome/test/data/password/captured_sites/sign_in"));
}
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<std::string> GetCapturedSites() {
std::vector<std::string> sites;
base::FileEnumerator capture_files(GetReplayFilesDirectory(), 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")))) {
sites.push_back(
captured_sites_test_utils::FilePathToUTF8(file.BaseName().value()));
}
}
std::sort(sites.begin(), sites.end());
return sites;
}
struct GetParamAsString {
template <class ParamType>
std::string operator()(const testing::TestParamInfo<ParamType>& info) const {
return info.param;
}
};
} // namespace
namespace password_manager {
// 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<std::string> {
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 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 (std::vector<autofill::PasswordForm>::const_iterator 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();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
captured_sites_test_utils::TestRecipeReplayer::SetUpCommandLine(
command_line);
}
void TearDownOnMainThread() override { recipe_replayer()->Cleanup(); }
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;
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 =
GetReplayFilesDirectory().AppendASCII(GetParam().c_str());
// Craft the recipe file path.
base::FilePath recipe_file_path = GetReplayFilesDirectory().AppendASCII(
base::StringPrintf("%s.test", GetParam().c_str()));
ASSERT_TRUE(
recipe_replayer()->ReplayTest(capture_file_path, recipe_file_path));
}
INSTANTIATE_TEST_CASE_P(All,
CapturedSitesPasswordManagerBrowserTest,
testing::ValuesIn(GetCapturedSites()),
GetParamAsString());
} // namespace password_manager
...@@ -324,10 +324,8 @@ bool BubbleObserver::IsUpdatePromptShownAutomatically() const { ...@@ -324,10 +324,8 @@ bool BubbleObserver::IsUpdatePromptShownAutomatically() const {
->was_prompt_automatically_shown(); ->was_prompt_automatically_shown();
} }
void BubbleObserver::Dismiss() const { void BubbleObserver::Hide() const {
passwords_ui_controller_->OnBubbleHidden(); passwords_ui_controller_->OnBubbleHidden();
ASSERT_EQ(password_manager::ui::INACTIVE_STATE,
passwords_ui_controller_->GetState());
} }
void BubbleObserver::AcceptSavePrompt() const { void BubbleObserver::AcceptSavePrompt() const {
...@@ -384,15 +382,6 @@ PasswordManagerBrowserTestBase::PasswordManagerBrowserTestBase() ...@@ -384,15 +382,6 @@ PasswordManagerBrowserTestBase::PasswordManagerBrowserTestBase()
PasswordManagerBrowserTestBase::~PasswordManagerBrowserTestBase() = default; PasswordManagerBrowserTestBase::~PasswordManagerBrowserTestBase() = default;
void PasswordManagerBrowserTestBase::SetUpOnMainThread() { void PasswordManagerBrowserTestBase::SetUpOnMainThread() {
// Use TestPasswordStore to remove a possible race. Normally the
// PasswordStore does its database manipulation on the DB thread, which
// creates a possible race during navigation. Specifically the
// PasswordManager will ignore any forms in a page if the load from the
// PasswordStore has not completed.
PasswordStoreFactory::GetInstance()->SetTestingFactory(
browser()->profile(),
password_manager::BuildPasswordStore<
content::BrowserContext, password_manager::TestPasswordStore>);
ASSERT_TRUE(embedded_test_server()->Start()); ASSERT_TRUE(embedded_test_server()->Start());
// Setup HTTPS server serving files from standard test directory. // Setup HTTPS server serving files from standard test directory.
...@@ -412,41 +401,65 @@ void PasswordManagerBrowserTestBase::SetUpOnMainThread() { ...@@ -412,41 +401,65 @@ void PasswordManagerBrowserTestBase::SetUpOnMainThread() {
verify_result.verified_cert = cert; verify_result.verified_cert = cert;
mock_cert_verifier()->AddResultForCert(cert.get(), verify_result, net::OK); mock_cert_verifier()->AddResultForCert(cert.get(), verify_result, net::OK);
SetUpOnMainThreadAndGetNewTab(browser(), &web_contents_);
}
void PasswordManagerBrowserTestBase::TearDownOnMainThread() {
ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
}
void PasswordManagerBrowserTestBase::TearDownInProcessBrowserTestFixture() {
ProfileIOData::SetCertVerifierForTesting(nullptr);
}
void PasswordManagerBrowserTestBase::SetUpOnMainThreadAndGetNewTab(
Browser* browser,
content::WebContents** web_contents) {
// Use TestPasswordStore to remove a possible race. Normally the
// PasswordStore does its database manipulation on a background thread, which
// creates a possible race during navigation. Specifically the
// PasswordManager will ignore any forms in a page if the load from the
// PasswordStore has not completed.
PasswordStoreFactory::GetInstance()->SetTestingFactory(
browser->profile(),
password_manager::BuildPasswordStore<
content::BrowserContext, password_manager::TestPasswordStore>);
// Add a tab with a customized ManagePasswordsUIController. Thus, we can // Add a tab with a customized ManagePasswordsUIController. Thus, we can
// intercept useful UI events. // intercept useful UI events.
content::WebContents* tab = content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents(); browser->tab_strip_model()->GetActiveWebContents();
std::unique_ptr<content::WebContents> owned_web_contents = std::unique_ptr<content::WebContents> owned_web_contents =
content::WebContents::Create( content::WebContents::Create(
content::WebContents::CreateParams(tab->GetBrowserContext())); content::WebContents::CreateParams(tab->GetBrowserContext()));
web_contents_ = owned_web_contents.get(); *web_contents = owned_web_contents.get();
ASSERT_TRUE(web_contents_); ASSERT_TRUE(*web_contents);
// ManagePasswordsUIController needs ChromePasswordManagerClient for logging. // ManagePasswordsUIController needs ChromePasswordManagerClient for logging.
autofill::ChromeAutofillClient::CreateForWebContents(web_contents_); autofill::ChromeAutofillClient::CreateForWebContents(*web_contents);
ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient( ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
web_contents_, *web_contents,
autofill::ChromeAutofillClient::FromWebContents(web_contents_)); autofill::ChromeAutofillClient::FromWebContents(*web_contents));
ASSERT_TRUE(ChromePasswordManagerClient::FromWebContents(web_contents_)); ASSERT_TRUE(ChromePasswordManagerClient::FromWebContents(*web_contents));
CustomManagePasswordsUIController* controller = CustomManagePasswordsUIController* controller =
new CustomManagePasswordsUIController(web_contents_); new CustomManagePasswordsUIController(*web_contents);
browser()->tab_strip_model()->AppendWebContents(std::move(owned_web_contents), browser->tab_strip_model()->AppendWebContents(std::move(owned_web_contents),
true); true);
browser()->tab_strip_model()->CloseWebContentsAt(0, browser->tab_strip_model()->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
TabStripModel::CLOSE_NONE);
ASSERT_EQ(controller, ASSERT_EQ(controller,
ManagePasswordsUIController::FromWebContents(web_contents_)); ManagePasswordsUIController::FromWebContents(*web_contents));
ASSERT_EQ(web_contents_, ASSERT_EQ(*web_contents, browser->tab_strip_model()->GetActiveWebContents());
browser()->tab_strip_model()->GetActiveWebContents()); ASSERT_FALSE((*web_contents)->IsLoading());
ASSERT_FALSE(web_contents_->IsLoading());
} }
void PasswordManagerBrowserTestBase::TearDownOnMainThread() { void PasswordManagerBrowserTestBase::WaitForPasswordStore(Browser* browser) {
ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); scoped_refptr<password_manager::PasswordStore> password_store =
} PasswordStoreFactory::GetForProfile(browser->profile(),
ServiceAccessType::IMPLICIT_ACCESS);
void PasswordManagerBrowserTestBase::TearDownInProcessBrowserTestFixture() { PasswordStoreResultsObserver syncer;
ProfileIOData::SetCertVerifierForTesting(nullptr); password_store->GetAutofillableLoginsWithAffiliationAndBrandingInformation(
&syncer);
syncer.Wait();
} }
content::WebContents* PasswordManagerBrowserTestBase::WebContents() const { content::WebContents* PasswordManagerBrowserTestBase::WebContents() const {
...@@ -583,13 +596,7 @@ void PasswordManagerBrowserTestBase::WaitForJsElementValue( ...@@ -583,13 +596,7 @@ void PasswordManagerBrowserTestBase::WaitForJsElementValue(
} }
void PasswordManagerBrowserTestBase::WaitForPasswordStore() { void PasswordManagerBrowserTestBase::WaitForPasswordStore() {
scoped_refptr<password_manager::PasswordStore> password_store = WaitForPasswordStore(browser());
PasswordStoreFactory::GetForProfile(browser()->profile(),
ServiceAccessType::IMPLICIT_ACCESS);
PasswordStoreResultsObserver syncer;
password_store->GetAutofillableLoginsWithAffiliationAndBrandingInformation(
&syncer);
syncer.Wait();
} }
void PasswordManagerBrowserTestBase::CheckElementValue( void PasswordManagerBrowserTestBase::CheckElementValue(
......
...@@ -85,9 +85,8 @@ class BubbleObserver { ...@@ -85,9 +85,8 @@ class BubbleObserver {
// PasswordManagerBrowserTestBase. // PasswordManagerBrowserTestBase.
bool IsUpdatePromptShownAutomatically() const; bool IsUpdatePromptShownAutomatically() const;
// Dismisses the prompt currently open and moves the controller to the // Hide the currently open prompt.
// inactive state. void Hide() const;
void Dismiss() const;
// Expecting that the prompt is available, saves the password. At the end, // Expecting that the prompt is available, saves the password. At the end,
// checks that the prompt is no longer available afterwards. // checks that the prompt is no longer available afterwards.
...@@ -139,6 +138,18 @@ class PasswordManagerBrowserTestBase : public CertVerifierBrowserTest { ...@@ -139,6 +138,18 @@ class PasswordManagerBrowserTestBase : public CertVerifierBrowserTest {
void TearDownOnMainThread() override; void TearDownOnMainThread() override;
void TearDownInProcessBrowserTestFixture() override; void TearDownInProcessBrowserTestFixture() override;
// Bring up a new Chrome tab set up with password manager test hooks.
// @param[in] browser the browser running the password manager test, upon
// which this function will perform the setup steps.
// @param[out] a new tab on the browser set up with password manager test
// hooks.
static void SetUpOnMainThreadAndGetNewTab(
Browser* browser,
content::WebContents** web_contents);
// Make sure that the password store associated with the given browser
// processed all the previous calls, calls executed on another thread.
static void WaitForPasswordStore(Browser* browser);
protected: protected:
// Wrapper around ui_test_utils::NavigateToURL that waits until // Wrapper around ui_test_utils::NavigateToURL that waits until
// DidFinishLoad() fires. Normally this function returns after // DidFinishLoad() fires. Normally this function returns after
......
...@@ -4844,6 +4844,7 @@ if (!is_android) { ...@@ -4844,6 +4844,7 @@ if (!is_android) {
ldflags = [] ldflags = []
deps = [ deps = [
":captured_sites_interactive_tests",
":test_support", ":test_support",
":test_support_ui", ":test_support_ui",
"//chrome:packed_resources", "//chrome:packed_resources",
...@@ -5691,7 +5692,7 @@ if (!is_android && !is_fuchsia) { ...@@ -5691,7 +5692,7 @@ if (!is_android && !is_fuchsia) {
} }
# Tests autofill on captured websites # Tests autofill on captured websites
test("autofill_captured_sites_interactive_tests") { test("captured_sites_interactive_tests") {
sources = [ sources = [
"../browser/autofill/autofill_captured_sites_interactive_uitest.cc", "../browser/autofill/autofill_captured_sites_interactive_uitest.cc",
"../browser/autofill/autofill_uitest.cc", "../browser/autofill/autofill_uitest.cc",
...@@ -5700,6 +5701,9 @@ if (!is_android && !is_fuchsia) { ...@@ -5700,6 +5701,9 @@ if (!is_android && !is_fuchsia) {
"../browser/autofill/autofill_uitest_util.h", "../browser/autofill/autofill_uitest_util.h",
"../browser/autofill/captured_sites_test_utils.cc", "../browser/autofill/captured_sites_test_utils.cc",
"../browser/autofill/captured_sites_test_utils.h", "../browser/autofill/captured_sites_test_utils.h",
"../browser/password_manager/password_manager_captured_sites_interactive_uitest.cc",
"../browser/password_manager/password_manager_test_base.cc",
"../browser/password_manager/password_manager_test_base.h",
"base/interactive_test_utils.cc", "base/interactive_test_utils.cc",
"base/interactive_test_utils.h", "base/interactive_test_utils.h",
"base/interactive_test_utils_common_views.cc", "base/interactive_test_utils_common_views.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment