Commit 67117548 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Commit Bot

[Autofill Assistant] Add tests for AutofillAction.

Bug: 806868
Change-Id: Id9ffa19550de44af7e4f89bc8f3d4439e572398f
Reviewed-on: https://chromium-review.googlesource.com/1248784
Commit-Queue: Jordan Demeulenaere <jdemeulenaere@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#594717}
parent f87ef787
......@@ -72,7 +72,12 @@ jumbo_static_library("browser") {
source_set("unit_tests") {
testonly = true
sources = [
"actions/autofill_action_unittest.cc",
"actions/mock_action_delegate.cc",
"actions/mock_action_delegate.h",
"controller_unittest.cc",
"mock_client_memory.cc",
"mock_client_memory.h",
"mock_run_once_callback.h",
"mock_service.cc",
"mock_service.h",
......@@ -91,6 +96,7 @@ source_set("unit_tests") {
":proto",
"//base",
"//base/test:test_support",
"//components/autofill/core/browser:test_support",
"//content/test:test_support",
"//testing/gmock",
"//testing/gtest",
......
......@@ -16,6 +16,7 @@ class AutofillProfile;
namespace autofill_assistant {
class ClientMemory;
class NodeProto;
// Action delegate called when processing actions.
class ActionDelegate {
......
......@@ -20,6 +20,7 @@ namespace autofill_assistant {
AutofillAction::AutofillAction(const ActionProto& proto)
: Action(proto), pending_set_field_value_(0), weak_ptr_factory_(this) {
if (proto.has_use_address()) {
is_autofill_card_ = false;
prompt_ = proto.use_address().prompt();
name_ = proto.use_address().name();
for (const auto& selector :
......@@ -30,6 +31,7 @@ AutofillAction::AutofillAction(const ActionProto& proto)
DCHECK(proto.has_use_card());
is_autofill_card_ = true;
prompt_ = proto.use_card().prompt();
name_ = "";
for (const auto& selector :
proto.use_card().form_field_element().selectors()) {
selectors_.emplace_back(selector);
......@@ -101,15 +103,6 @@ void AutofillAction::OnDataSelected(ActionDelegate* delegate,
delegate->GetClientMemory()->set_selected_address(name_, guid);
}
if (guid.empty()) {
// User selected 'Fill manually'.
// TODO(crbug.com/806868): We need to differentiate between action failure
// and stopping an action to let the user fill a form (expected stop).
UpdateProcessedAction(false);
std::move(process_action_callback_).Run(std::move(processed_action_proto_));
return;
}
if (selectors_.empty()) {
// If there is no selector, finish the action directly. This can be the case
// when we want to trigger the selection of address or card at the beginning
......@@ -119,6 +112,15 @@ void AutofillAction::OnDataSelected(ActionDelegate* delegate,
return;
}
if (guid.empty()) {
// User selected 'Fill manually'.
// TODO(crbug.com/806868): We need to differentiate between action failure
// and stopping an action to let the user fill a form (expected stop).
UpdateProcessedAction(false);
std::move(process_action_callback_).Run(std::move(processed_action_proto_));
return;
}
// TODO(crbug.com/806868): Validate form and use fallback if needed.
FillFormWithData(guid, delegate);
}
......@@ -127,14 +129,14 @@ void AutofillAction::FillFormWithData(const std::string& guid,
ActionDelegate* delegate) {
DCHECK(!selectors_.empty());
if (is_autofill_card_) {
delegate->FillAddressForm(
delegate->FillCardForm(
guid, selectors_,
base::BindOnce(&AutofillAction::OnFormFilled,
weak_ptr_factory_.GetWeakPtr(), guid, delegate));
return;
}
delegate->FillCardForm(
delegate->FillAddressForm(
guid, selectors_,
base::BindOnce(&AutofillAction::OnFormFilled,
weak_ptr_factory_.GetWeakPtr(), guid, delegate));
......
// 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 "components/autofill_assistant/browser/actions/autofill_action.h"
#include "base/bind.h"
#include "base/guid.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/mock_client_memory.h"
#include "components/autofill_assistant/browser/mock_run_once_callback.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
namespace {
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::StrNe;
// A callback that expects to be called immediately.
//
// This relies on mocked methods calling their callbacks immediately (which is
// the case in this test).
class DirectCallback {
public:
DirectCallback() : was_run_(false), result_(nullptr) {}
// Returns a base::OnceCallback. The current instance must exist until
// GetResultOrDie is called.
base::OnceCallback<void(std::unique_ptr<ProcessedActionProto>)> Get() {
return base::BindOnce(&DirectCallback::Run, base::Unretained(this));
}
ProcessedActionProto* GetResultOrDie() {
CHECK(was_run_);
return result_.get();
}
private:
void Run(std::unique_ptr<ProcessedActionProto> result) {
was_run_ = true;
result_ = std::move(result);
}
bool was_run_;
std::unique_ptr<ProcessedActionProto> result_;
};
class AutofillActionTest : public testing::Test {
public:
void SetUp() override {
autofill::AutofillProfile profile(base::GenerateGUID(),
autofill::test::kEmptyOrigin);
autofill::test::SetProfileInfo(&profile, kFirstName, "", kLastName, kEmail,
"", "", "", "", "", "", "", "");
autofill_profile_ = std::move(profile);
ON_CALL(mock_action_delegate_, GetClientMemory)
.WillByDefault(Return(&mock_client_memory_));
ON_CALL(mock_action_delegate_, GetAutofillProfile(autofill_profile_.guid()))
.WillByDefault(Return(&autofill_profile_));
}
protected:
const char* const kAddressName = "billing";
const char* const kFakeSelector = "#selector";
const char* const kSelectionPrompt = "prompt";
const char* const kFirstName = "Foo";
const char* const kLastName = "Bar";
const char* const kEmail = "foobar@gmail.com";
ActionProto CreateUseAddressAction() {
ActionProto action;
UseAddressProto* use_address = action.mutable_use_address();
use_address->set_name(kAddressName);
use_address->mutable_form_field_element()->add_selectors(kFakeSelector);
return action;
}
ActionProto CreateUseCardAction() {
ActionProto action;
action.mutable_use_card()->mutable_form_field_element()->add_selectors(
kFakeSelector);
return action;
}
bool ProcessAction(const ActionProto& action_proto) {
AutofillAction action(action_proto);
// We can use DirectCallback given that methods in ActionDelegate are mocked
// and return directly.
DirectCallback callback;
action.ProcessAction(&mock_action_delegate_, callback.Get());
return callback.GetResultOrDie()->status() ==
ProcessedActionStatus::ACTION_APPLIED;
}
MockActionDelegate mock_action_delegate_;
MockClientMemory mock_client_memory_;
autofill::AutofillProfile autofill_profile_;
};
TEST_F(AutofillActionTest, FillManually) {
InSequence seq;
ActionProto action_proto = CreateUseAddressAction();
action_proto.mutable_use_address()->set_prompt(kSelectionPrompt);
// No selection was made previously.
EXPECT_CALL(mock_client_memory_, selected_address(kAddressName))
.WillOnce(Return(base::nullopt));
// Expect prompt.
EXPECT_CALL(mock_action_delegate_, ShowStatusMessage(kSelectionPrompt));
// Return empty address guid (manual filling).
EXPECT_CALL(mock_action_delegate_, OnChooseAddress(_))
.WillOnce(RunOnceCallback<0>(""));
// We save the selection in memory.
EXPECT_CALL(mock_client_memory_, set_selected_address(kAddressName, ""));
EXPECT_FALSE(ProcessAction(action_proto));
}
TEST_F(AutofillActionTest, FillCardFormFails) {
InSequence seq;
ActionProto action_proto = CreateUseCardAction();
// Return a fake selected card.
EXPECT_CALL(mock_client_memory_, selected_card())
.WillOnce(Return(autofill_profile_.guid()));
// Autofill fails.
EXPECT_CALL(mock_action_delegate_,
OnFillCardForm(autofill_profile_.guid(), _, _))
.WillOnce(RunOnceCallback<2>(false));
EXPECT_FALSE(ProcessAction(action_proto));
}
TEST_F(AutofillActionTest, FillCardFormSucceeds) {
InSequence seq;
ActionProto action_proto = CreateUseCardAction();
// Return a fake selected card.
EXPECT_CALL(mock_client_memory_, selected_card())
.WillOnce(Return(autofill_profile_.guid()));
// Autofill succeeds.
EXPECT_CALL(
mock_action_delegate_,
OnFillCardForm(autofill_profile_.guid(), ElementsAre(kFakeSelector), _))
.WillOnce(RunOnceCallback<2>(true));
EXPECT_TRUE(ProcessAction(action_proto));
}
TEST_F(AutofillActionTest, ValidationSucceeds) {
InSequence seq;
ActionProto action_proto = CreateUseAddressAction();
int required_fields_count = 3;
for (int i = 0; i < required_fields_count; i++) {
auto* required_field =
action_proto.mutable_use_address()->add_required_fields();
required_field->set_address_field(
UseAddressProto::RequiredField::FIRST_NAME);
required_field->mutable_element()->add_selectors(kFakeSelector);
}
// Return a fake selected address.
EXPECT_CALL(mock_client_memory_, selected_address(kAddressName))
.WillOnce(Return(autofill_profile_.guid()));
// Autofill succeeds.
EXPECT_CALL(mock_action_delegate_,
OnFillAddressForm(autofill_profile_.guid(),
ElementsAre(kFakeSelector), _))
.WillOnce(RunOnceCallback<2>(true));
// Validation succeeds.
EXPECT_CALL(mock_action_delegate_,
OnGetFieldValue(ElementsAre(kFakeSelector), _))
.Times(required_fields_count)
.WillRepeatedly(RunOnceCallback<1>("not empty"));
EXPECT_TRUE(ProcessAction(action_proto));
}
TEST_F(AutofillActionTest, FallbackFails) {
InSequence seq;
ActionProto action_proto = CreateUseAddressAction();
std::vector<UseAddressProto::RequiredField::AddressField> address_fields = {
UseAddressProto::RequiredField::FIRST_NAME,
UseAddressProto::RequiredField::LAST_NAME,
UseAddressProto::RequiredField::EMAIL};
for (size_t i = 0; i < address_fields.size(); i++) {
auto* required_field =
action_proto.mutable_use_address()->add_required_fields();
required_field->set_address_field(address_fields[i]);
required_field->mutable_element()->add_selectors(kFakeSelector);
}
// Return a fake selected address.
EXPECT_CALL(mock_client_memory_, selected_address(kAddressName))
.WillOnce(Return(autofill_profile_.guid()));
// Autofill succeeds.
EXPECT_CALL(mock_action_delegate_,
OnFillAddressForm(autofill_profile_.guid(),
ElementsAre(kFakeSelector), _))
.WillOnce(RunOnceCallback<2>(true));
// Validation fails when getting FIRST_NAME.
EXPECT_CALL(mock_action_delegate_,
OnGetFieldValue(ElementsAre(kFakeSelector), _))
.Times(address_fields.size())
.WillOnce(RunOnceCallback<1>(""))
.WillRepeatedly(RunOnceCallback<1>("not empty"));
// Fallback fails.
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(ElementsAre(kFakeSelector), kFirstName, _))
.WillOnce(RunOnceCallback<2>(false));
EXPECT_FALSE(ProcessAction(action_proto));
}
TEST_F(AutofillActionTest, FallbackSucceeds) {
InSequence seq;
ActionProto action_proto = CreateUseAddressAction();
std::vector<UseAddressProto::RequiredField::AddressField> address_fields = {
UseAddressProto::RequiredField::FIRST_NAME,
UseAddressProto::RequiredField::LAST_NAME,
UseAddressProto::RequiredField::EMAIL};
for (size_t i = 0; i < address_fields.size(); i++) {
auto* required_field =
action_proto.mutable_use_address()->add_required_fields();
required_field->set_address_field(address_fields[i]);
required_field->mutable_element()->add_selectors(kFakeSelector);
}
// Return a fake selected address.
EXPECT_CALL(mock_client_memory_, selected_address(kAddressName))
.WillOnce(Return(autofill_profile_.guid()));
// Autofill succeeds.
EXPECT_CALL(mock_action_delegate_,
OnFillAddressForm(autofill_profile_.guid(),
ElementsAre(kFakeSelector), _))
.WillOnce(RunOnceCallback<2>(true));
// Validation fails when getting FIRST_NAME.
EXPECT_CALL(mock_action_delegate_,
OnGetFieldValue(ElementsAre(kFakeSelector), _))
.Times(address_fields.size())
.WillOnce(RunOnceCallback<1>(""))
.WillRepeatedly(RunOnceCallback<1>("not empty"));
// Fallback succeeds.
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(ElementsAre(kFakeSelector), kFirstName, _))
.WillOnce(RunOnceCallback<2>(true));
// Second validation succeeds.
EXPECT_CALL(mock_action_delegate_,
OnGetFieldValue(ElementsAre(kFakeSelector), _))
.Times(address_fields.size())
.WillRepeatedly(RunOnceCallback<1>("not empty"));
EXPECT_TRUE(ProcessAction(action_proto));
}
} // namespace
} // namespace autofill_assistant
// 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 "components/autofill_assistant/browser/actions/mock_action_delegate.h"
namespace autofill_assistant {
MockActionDelegate::MockActionDelegate() = default;
MockActionDelegate::~MockActionDelegate() = default;
} // namespace autofill_assistant
// 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.
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_MOCK_ACTION_DELEGATE_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_MOCK_ACTION_DELEGATE_H_
#include "base/callback.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
class MockActionDelegate : public ActionDelegate {
public:
MockActionDelegate();
~MockActionDelegate() override;
MOCK_METHOD1(ShowStatusMessage, void(const std::string& message));
MOCK_METHOD2(ClickElement,
void(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback));
MOCK_METHOD2(ElementExists,
void(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback));
void ChooseAddress(
base::OnceCallback<void(const std::string&)> callback) override {
OnChooseAddress(callback);
}
MOCK_METHOD1(OnChooseAddress,
void(base::OnceCallback<void(const std::string&)>& callback));
void FillAddressForm(const std::string& guid,
const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) override {
OnFillAddressForm(guid, selectors, callback);
}
MOCK_METHOD3(OnFillAddressForm,
void(const std::string& guid,
const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)>& callback));
void ChooseCard(
base::OnceCallback<void(const std::string&)> callback) override {
OnChooseCard(callback);
}
MOCK_METHOD1(OnChooseCard,
void(base::OnceCallback<void(const std::string&)>& callback));
void FillCardForm(const std::string& guid,
const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) override {
OnFillCardForm(guid, selectors, callback);
}
MOCK_METHOD3(OnFillCardForm,
void(const std::string& guid,
const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)>& callback));
MOCK_METHOD3(SelectOption,
void(const std::vector<std::string>& selectors,
const std::string& selected_option,
base::OnceCallback<void(bool)> callback));
MOCK_METHOD2(FocusElement,
void(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback));
void GetFieldValue(
const std::vector<std::string>& selectors,
base::OnceCallback<void(const std::string&)> callback) override {
OnGetFieldValue(selectors, callback);
}
MOCK_METHOD2(OnGetFieldValue,
void(const std::vector<std::string>& selectors,
base::OnceCallback<void(const std::string&)>& callback));
void SetFieldValue(const std::vector<std::string>& selectors,
const std::string& value,
base::OnceCallback<void(bool)> callback) {
OnSetFieldValue(selectors, value, callback);
}
MOCK_METHOD3(OnSetFieldValue,
void(const std::vector<std::string>& selectors,
const std::string& value,
base::OnceCallback<void(bool)>& callback));
MOCK_METHOD1(GetAutofillProfile,
const autofill::AutofillProfile*(const std::string& guid));
MOCK_METHOD3(BuildNodeTree,
void(const std::vector<std::string>& selectors,
NodeProto* node_tree_out,
base::OnceCallback<void(bool)> callback));
MOCK_METHOD0(GetClientMemory, ClientMemory*());
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_MOCK_ACTION_DELEGATE_H_
......@@ -15,23 +15,24 @@ namespace autofill_assistant {
class ClientMemory {
public:
ClientMemory();
~ClientMemory();
virtual ~ClientMemory();
// GUID of the currently selected credit card, if any. It will be an empty
// optional if user didn't select anything, empty string if user selected
// 'Fill manually', or the guid of a selected card.
base::Optional<std::string> selected_card();
virtual base::Optional<std::string> selected_card();
// GUID of the currently selected address for |name|. It will be an empty
// optional if user didn't select anything, empty string if user selected
// 'Fill manually', or the guid of a selected address.
base::Optional<std::string> selected_address(const std::string& name);
virtual base::Optional<std::string> selected_address(const std::string& name);
// Set the |guid| of the selected card.
void set_selected_card(const std::string& guid);
virtual void set_selected_card(const std::string& guid);
// Set the |guid| of the selected address for |name|.
void set_selected_address(const std::string& name, const std::string& guid);
virtual void set_selected_address(const std::string& name,
const std::string& guid);
// TODO(crbug.com/806868): Add a clear() method that resets the memory and
// call it when necessary (TBD).
......
// 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 "components/autofill_assistant/browser/mock_client_memory.h"
namespace autofill_assistant {
MockClientMemory::MockClientMemory() = default;
MockClientMemory::~MockClientMemory() = default;
} // namespace autofill_assistant
\ No newline at end of file
// 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.
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_CLIENT_MEMORY_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_CLIENT_MEMORY_H_
#include "components/autofill_assistant/browser/client_memory.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
class MockClientMemory : public ClientMemory {
public:
MockClientMemory();
~MockClientMemory();
MOCK_METHOD0(selected_card, base::Optional<std::string>());
MOCK_METHOD1(selected_address,
base::Optional<std::string>(const std::string& name));
MOCK_METHOD1(set_selected_card, void(const std::string& guid));
MOCK_METHOD2(set_selected_address,
void(const std::string& name, const std::string& guid));
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_CLIENT_MEMORY_H_
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