Commit 3e5b1cd1 authored by sandromaggi's avatar sandromaggi Committed by Commit Bot

[Autofill Assistant] Add profile value for selecting option

This allows the SelectOptionAction to use a value from a
previously selected profile. If the profile is not available
the action will terminate with an error. If the value
expression cannot be resolved the action will terminate
with an error.

Bug: b/169521597
Change-Id: Ibd34fa922b9c7d1ac2846e0327caf194697e5f44
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2437810Reviewed-by: default avatarLuca Hunkeler <hluca@google.com>
Commit-Queue: Sandro Maggi <sandromaggi@google.com>
Cr-Commit-Position: refs/heads/master@{#811677}
parent 226925f0
......@@ -8,9 +8,11 @@
#include "base/bind.h"
#include "base/callback.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/actions/action_delegate_util.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/field_formatter.h"
namespace autofill_assistant {
......@@ -23,50 +25,86 @@ SelectOptionAction::SelectOptionAction(ActionDelegate* delegate,
SelectOptionAction::~SelectOptionAction() {}
void SelectOptionAction::InternalProcessAction(ProcessActionCallback callback) {
process_action_callback_ = std::move(callback);
const SelectOptionProto& select_option = proto_.select_option();
// A non prefilled |select_option| is not supported.
if (!select_option.has_selected_option()) {
VLOG(1) << __func__ << ": empty option";
UpdateProcessedAction(INVALID_ACTION);
std::move(callback).Run(std::move(processed_action_proto_));
return;
}
Selector selector = Selector(select_option.element());
if (selector.empty()) {
VLOG(1) << __func__ << ": empty selector";
UpdateProcessedAction(INVALID_SELECTOR);
std::move(callback).Run(std::move(processed_action_proto_));
EndAction(ClientStatus(INVALID_SELECTOR));
return;
}
switch (select_option.value_case()) {
case SelectOptionProto::kSelectedOption:
if (select_option.selected_option().empty()) {
VLOG(1) << __func__ << ": empty |selected_option|";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
value_ = select_option.selected_option();
break;
case SelectOptionProto::kAutofillValue: {
if (select_option.autofill_value().profile().identifier().empty() ||
select_option.autofill_value().value_expression().empty()) {
VLOG(1) << "SelectOptionAction: |autofill_value| with empty "
"|profile.identifier| or |value_expression|";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
const autofill::AutofillProfile* address =
delegate_->GetUserData()->selected_address(
select_option.autofill_value().profile().identifier());
if (address == nullptr) {
VLOG(1) << "SelectOptionAction: requested unknown address '"
<< select_option.autofill_value().profile().identifier() << "'";
EndAction(ClientStatus(PRECONDITION_FAILED));
return;
}
auto value = field_formatter::FormatString(
select_option.autofill_value().value_expression(),
field_formatter::CreateAutofillMappings(*address,
/* locale= */ "en-US"));
if (!value.has_value()) {
EndAction(ClientStatus(AUTOFILL_INFO_NOT_AVAILABLE));
return;
}
value_ = *value;
break;
}
default:
VLOG(1) << "Unrecognized field for SelectOptionAction";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
delegate_->ShortWaitForElement(
selector, base::BindOnce(&SelectOptionAction::OnWaitForElement,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback), selector));
weak_ptr_factory_.GetWeakPtr(), selector));
}
void SelectOptionAction::OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
void SelectOptionAction::OnWaitForElement(const Selector& selector,
const ClientStatus& element_status) {
if (!element_status.ok()) {
UpdateProcessedAction(element_status.proto_status());
std::move(callback).Run(std::move(processed_action_proto_));
EndAction(element_status);
return;
}
ActionDelegateUtil::FindElementAndPerform(
delegate_, selector,
base::BindOnce(&ActionDelegate::SelectOption, delegate_->GetWeakPtr(),
proto_.select_option().selected_option(),
proto_.select_option().select_strategy()),
base::BindOnce(&SelectOptionAction::OnSelectOption,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
value_, proto_.select_option().select_strategy()),
base::BindOnce(&SelectOptionAction::EndAction,
weak_ptr_factory_.GetWeakPtr()));
}
void SelectOptionAction::OnSelectOption(ProcessActionCallback callback,
const ClientStatus& status) {
void SelectOptionAction::EndAction(const ClientStatus& status) {
UpdateProcessedAction(status);
std::move(callback).Run(std::move(processed_action_proto_));
std::move(process_action_callback_).Run(std::move(processed_action_proto_));
}
} // namespace autofill_assistant
......@@ -28,11 +28,13 @@ class SelectOptionAction : public Action {
// Overrides Action:
void InternalProcessAction(ProcessActionCallback callback) override;
void OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
void OnWaitForElement(const Selector& selector,
const ClientStatus& element_status);
void OnSelectOption(ProcessActionCallback callback,
const ClientStatus& status);
void EndAction(const ClientStatus& status);
std::string value_;
ProcessActionCallback process_action_callback_;
base::WeakPtrFactory<SelectOptionAction> weak_ptr_factory_{this};
......
......@@ -4,8 +4,14 @@
#include "components/autofill_assistant/browser/actions/select_option_action.h"
#include "base/guid.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill_assistant/browser/actions/action_test_utils.h"
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/selector.h"
......@@ -20,6 +26,7 @@ using ::testing::_;
using ::testing::InSequence;
using ::testing::Pointee;
using ::testing::Property;
using ::testing::Return;
class SelectOptionActionTest : public testing::Test {
public:
......@@ -29,6 +36,9 @@ class SelectOptionActionTest : public testing::Test {
protected:
void Run() {
ON_CALL(mock_action_delegate_, GetUserData)
.WillByDefault(Return(&user_data_));
ActionProto action_proto;
*action_proto.mutable_select_option() = proto_;
SelectOptionAction action(&mock_action_delegate_, action_proto);
......@@ -38,11 +48,32 @@ class SelectOptionActionTest : public testing::Test {
MockActionDelegate mock_action_delegate_;
base::MockCallback<Action::ProcessActionCallback> callback_;
SelectOptionProto proto_;
UserData user_data_;
};
TEST_F(SelectOptionActionTest, EmptyOptionFails) {
TEST_F(SelectOptionActionTest, NoValueToSelectFails) {
Selector selector({"#select"});
*proto_.mutable_element() = selector.proto;
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
Run();
}
TEST_F(SelectOptionActionTest, EmptySelectedOptionFails) {
Selector selector({"#select"});
*proto_.mutable_element() = selector.proto;
proto_.set_selected_option("");
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
Run();
}
TEST_F(SelectOptionActionTest, EmptyAutofillValueFails) {
Selector selector({"#select"});
*proto_.mutable_element() = selector.proto;
proto_.mutable_autofill_value();
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
......@@ -82,5 +113,77 @@ TEST_F(SelectOptionActionTest, CheckExpectedCallChain) {
Run();
}
TEST_F(SelectOptionActionTest, RequestDataFromUnknownProfile) {
Selector selector({"#select"});
*proto_.mutable_element() = selector.proto;
auto* value = proto_.mutable_autofill_value();
value->mutable_profile()->set_identifier("none");
value->set_value_expression("value");
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
PRECONDITION_FAILED))));
Run();
}
TEST_F(SelectOptionActionTest, RequestUnknownDataFromProfile) {
autofill::AutofillProfile contact(base::GenerateGUID(),
autofill::test::kEmptyOrigin);
// Middle name is expected to be empty.
autofill::test::SetProfileInfo(&contact, "John", /* middle name */ "", "Doe",
"", "", "", "", "", "", "", "", "");
user_data_.selected_addresses_["contact"] =
std::make_unique<autofill::AutofillProfile>(contact);
Selector selector({"#select"});
*proto_.mutable_element() = selector.proto;
auto* value = proto_.mutable_autofill_value();
value->mutable_profile()->set_identifier("contact");
value->set_value_expression(
base::StrCat({"${",
base::NumberToString(static_cast<int>(
autofill::ServerFieldType::NAME_MIDDLE)),
"}"}));
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
AUTOFILL_INFO_NOT_AVAILABLE))));
Run();
}
TEST_F(SelectOptionActionTest, SelectOptionFromProfileValue) {
autofill::AutofillProfile contact(base::GenerateGUID(),
autofill::test::kEmptyOrigin);
autofill::test::SetProfileInfo(&contact, "John", "", "Doe", "", "", "", "",
"", "", "", "", "");
user_data_.selected_addresses_["contact"] =
std::make_unique<autofill::AutofillProfile>(contact);
InSequence sequence;
Selector selector({"#select"});
*proto_.mutable_element() = selector.proto;
auto* value = proto_.mutable_autofill_value();
value->mutable_profile()->set_identifier("contact");
value->set_value_expression(
base::StrCat({"${",
base::NumberToString(static_cast<int>(
autofill::ServerFieldType::NAME_FIRST)),
"}"}));
Selector expected_selector = selector;
EXPECT_CALL(mock_action_delegate_,
OnShortWaitForElement(expected_selector, _))
.WillOnce(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_action_delegate_,
SelectOption("John", _,
EqualsElement(test_util::MockFindElement(
mock_action_delegate_, expected_selector)),
_))
.WillOnce(RunOnceCallback<3>(OkClientStatus()));
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
Run();
}
} // namespace
} // namespace autofill_assistant
......@@ -939,13 +939,20 @@ message SelectOptionProto {
// The drop down element on which to select an option.
optional SelectorProto element = 2;
oneof value {
// Value of the option to use.
optional string selected_option = 3;
string selected_option = 3;
// A value from an Autofill source. Note that this must be proceeded by a
// |CollectUserDataAction|.
AutofillValue autofill_value = 5;
}
// The strategy used to select a value option. This defaults to
// |LABEL_STARTS_WITH| for the legacy case.
optional DropdownSelectStrategy select_strategy = 4
[default = LABEL_STARTS_WITH];
reserved 1;
}
// Contain a localized text message from the server.
......
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