blob: 175dcecfad5e93268cbf7977c78ddbb718c66244 [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 "components/autofill_assistant/browser/script_executor.h"
#include <map>
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "components/autofill_assistant/browser/client_memory.h"
#include "components/autofill_assistant/browser/mock_run_once_callback.h"
#include "components/autofill_assistant/browser/mock_service.h"
#include "components/autofill_assistant/browser/mock_ui_controller.h"
#include "components/autofill_assistant/browser/mock_web_controller.h"
#include "components/autofill_assistant/browser/service.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
namespace {
using ::testing::AllOf;
using ::testing::Contains;
using ::testing::DoAll;
using ::testing::Field;
using ::testing::NiceMock;
using ::testing::Pair;
using ::testing::SaveArg;
using ::testing::StrEq;
using ::testing::StrictMock;
using ::testing::_;
class ScriptExecutorTest : public testing::Test, public ScriptExecutorDelegate {
public:
void SetUp() override {
executor_ = std::make_unique<ScriptExecutor>("script path", this);
// In this test, "tell" actions always succeed and "click" actions always
// fail. The following makes a click action fail immediately
ON_CALL(mock_web_controller_, OnElementExists(_, _))
.WillByDefault(RunOnceCallback<1>(true));
ON_CALL(mock_web_controller_, OnClickElement(_, _))
.WillByDefault(RunOnceCallback<1>(false));
}
protected:
ScriptExecutorTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {}
Service* GetService() override { return &mock_service_; }
UiController* GetUiController() override { return &mock_ui_controller_; }
WebController* GetWebController() override { return &mock_web_controller_; }
ClientMemory* GetClientMemory() override { return &memory_; }
const std::map<std::string, std::string>& GetParameters() override {
return parameters_;
}
autofill::PersonalDataManager* GetPersonalDataManager() override {
return nullptr;
}
content::WebContents* GetWebContents() override { return nullptr; }
std::string Serialize(const google::protobuf::MessageLite& message) {
std::string output;
message.SerializeToString(&output);
return output;
}
// scoped_task_environment_ must be first to guarantee other field
// creation run in that environment.
base::test::ScopedTaskEnvironment scoped_task_environment_;
Script script_;
ClientMemory memory_;
StrictMock<MockService> mock_service_;
NiceMock<MockWebController> mock_web_controller_;
NiceMock<MockUiController> mock_ui_controller_;
std::unique_ptr<ScriptExecutor> executor_;
std::map<std::string, std::string> parameters_;
StrictMock<base::MockCallback<ScriptExecutor::RunScriptCallback>>
executor_callback_;
};
TEST_F(ScriptExecutorTest, GetActionsFails) {
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(false, ""));
EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, false),
Field(&ScriptExecutor::Result::at_end,
ScriptExecutor::CONTINUE))));
executor_->Run(executor_callback_.Get());
}
TEST_F(ScriptExecutorTest, ForwardParameters) {
parameters_["param1"] = "value1";
parameters_["param2"] = "value2";
EXPECT_CALL(mock_service_,
OnGetActions(StrEq("script path"),
AllOf(Contains(Pair("param1", "value1")),
Contains(Pair("param2", "value2"))),
_))
.WillOnce(RunOnceCallback<2>(true, ""));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
executor_->Run(executor_callback_.Get());
}
TEST_F(ScriptExecutorTest, RunOneActionReportAndReturn) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
actions_response.add_actions()
->mutable_click()
->mutable_element_to_click()
->add_selectors("will fail");
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
std::vector<ProcessedActionProto> processed_actions_capture;
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(DoAll(SaveArg<1>(&processed_actions_capture),
RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, true),
Field(&ScriptExecutor::Result::at_end,
ScriptExecutor::CONTINUE))));
executor_->Run(executor_callback_.Get());
ASSERT_EQ(1u, processed_actions_capture.size());
EXPECT_EQ(OTHER_ACTION_STATUS, processed_actions_capture[0].status());
}
TEST_F(ScriptExecutorTest, RunMultipleActions) {
ActionsResponseProto initial_actions_response;
initial_actions_response.set_server_payload("payload1");
initial_actions_response.add_actions()->mutable_tell()->set_message("1");
initial_actions_response.add_actions()->mutable_tell()->set_message("2");
EXPECT_CALL(mock_service_, OnGetActions(StrEq("script path"), _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(initial_actions_response)));
ActionsResponseProto next_actions_response;
next_actions_response.set_server_payload("payload2");
next_actions_response.add_actions()->mutable_tell()->set_message("3");
std::vector<ProcessedActionProto> processed_actions1_capture;
std::vector<ProcessedActionProto> processed_actions2_capture;
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(
DoAll(SaveArg<1>(&processed_actions1_capture),
RunOnceCallback<2>(true, Serialize(next_actions_response))))
.WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture),
RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
executor_->Run(executor_callback_.Get());
EXPECT_EQ(2u, processed_actions1_capture.size());
EXPECT_EQ(1u, processed_actions2_capture.size());
}
TEST_F(ScriptExecutorTest, UnsupportedAction) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
actions_response.add_actions(); // action definition missing
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
std::vector<ProcessedActionProto> processed_actions_capture;
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(DoAll(SaveArg<1>(&processed_actions_capture),
RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
executor_->Run(executor_callback_.Get());
ASSERT_EQ(1u, processed_actions_capture.size());
EXPECT_EQ(UNKNOWN_ACTION_STATUS, processed_actions_capture[0].status());
}
TEST_F(ScriptExecutorTest, StopAfterEnd) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
actions_response.add_actions()->mutable_stop();
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, ""));
EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, true),
Field(&ScriptExecutor::Result::at_end,
ScriptExecutor::SHUTDOWN))));
executor_->Run(executor_callback_.Get());
}
TEST_F(ScriptExecutorTest, ResetAfterEnd) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
actions_response.add_actions()->mutable_reset();
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, ""));
EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, true),
Field(&ScriptExecutor::Result::at_end,
ScriptExecutor::RESTART))));
executor_->Run(executor_callback_.Get());
}
TEST_F(ScriptExecutorTest, InterruptActionListOnError) {
ActionsResponseProto initial_actions_response;
initial_actions_response.set_server_payload("payload");
initial_actions_response.add_actions()->mutable_tell()->set_message(
"will pass");
initial_actions_response.add_actions()
->mutable_click()
->mutable_element_to_click()
->add_selectors("will fail");
initial_actions_response.add_actions()->mutable_tell()->set_message(
"never run");
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(initial_actions_response)));
ActionsResponseProto next_actions_response;
next_actions_response.set_server_payload("payload2");
next_actions_response.add_actions()->mutable_tell()->set_message(
"will run after error");
std::vector<ProcessedActionProto> processed_actions1_capture;
std::vector<ProcessedActionProto> processed_actions2_capture;
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(
DoAll(SaveArg<1>(&processed_actions1_capture),
RunOnceCallback<2>(true, Serialize(next_actions_response))))
.WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture),
RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
executor_->Run(executor_callback_.Get());
ASSERT_EQ(2u, processed_actions1_capture.size());
EXPECT_EQ(ACTION_APPLIED, processed_actions1_capture[0].status());
EXPECT_EQ(OTHER_ACTION_STATUS, processed_actions1_capture[1].status());
ASSERT_EQ(1u, processed_actions2_capture.size());
EXPECT_EQ(ACTION_APPLIED, processed_actions2_capture[0].status());
// make sure "never run" wasn't the one that was run.
EXPECT_EQ("will run after error",
processed_actions2_capture[0].action().tell().message());
}
TEST_F(ScriptExecutorTest, RunDelayedAction) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
ActionProto* action = actions_response.add_actions();
action->mutable_tell()->set_message("delayed");
action->set_action_delay_ms(1000);
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
std::vector<ProcessedActionProto> processed_actions_capture;
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(DoAll(SaveArg<1>(&processed_actions_capture),
RunOnceCallback<2>(true, "")));
// executor_callback_.Run() not expected to be run just yet, as the action is
// delayed.
executor_->Run(executor_callback_.Get());
EXPECT_TRUE(scoped_task_environment_.MainThreadHasPendingTask());
// Moving forward in time triggers action execution.
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
scoped_task_environment_.FastForwardBy(
base::TimeDelta::FromMilliseconds(1000));
EXPECT_FALSE(scoped_task_environment_.MainThreadHasPendingTask());
}
TEST_F(ScriptExecutorTest, HideDetailsWhenFinished) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
ActionProto click_with_clean_contextual_ui;
click_with_clean_contextual_ui.set_clean_contextual_ui(true);
click_with_clean_contextual_ui.mutable_tell()->set_message("clean");
;
*actions_response.add_actions() = click_with_clean_contextual_ui;
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, ""));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
EXPECT_CALL(mock_ui_controller_, HideDetails());
executor_->Run(executor_callback_.Get());
}
TEST_F(ScriptExecutorTest, DontHideDetailsIfOtherActionsAreLeft) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
ActionProto click_with_clean_contextual_ui;
click_with_clean_contextual_ui.set_clean_contextual_ui(true);
click_with_clean_contextual_ui.mutable_tell()->set_message("clean");
*actions_response.add_actions() = click_with_clean_contextual_ui;
actions_response.add_actions()->mutable_tell()->set_message("Wait no!");
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, ""));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
EXPECT_CALL(mock_ui_controller_, HideDetails()).Times(0);
executor_->Run(executor_callback_.Get());
}
TEST_F(ScriptExecutorTest, HideDetailsOnError) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
actions_response.add_actions()->mutable_tell()->set_message("Hello");
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(RunOnceCallback<2>(false, ""));
EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, false)));
EXPECT_CALL(mock_ui_controller_, HideDetails());
executor_->Run(executor_callback_.Get());
}
} // namespace
} // namespace autofill_assistant