blob: d574bbe45902931af7125c52ef80f117142f39b7 [file] [log] [blame]
// Copyright 2019 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/wait_for_dom_action.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/test/mock_callback.h"
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/mock_run_once_callback.h"
#include "components/autofill_assistant/browser/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
namespace {
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Invoke;
using ::testing::Pointee;
using ::testing::Property;
class WaitForDomActionTest : public testing::Test {
public:
WaitForDomActionTest() {}
void SetUp() override {
ON_CALL(mock_web_controller_, OnElementCheck(_, _))
.WillByDefault(RunOnceCallback<1>(false));
EXPECT_CALL(mock_action_delegate_, OnWaitForDom(_, _, _, _))
.WillRepeatedly(Invoke(this, &WaitForDomActionTest::FakeWaitForDom));
}
protected:
// Fakes ActionDelegate::WaitForDom.
void FakeWaitForDom(
base::TimeDelta max_wait_time,
bool allow_interrupt,
base::RepeatingCallback<void(BatchElementChecker*,
base::OnceCallback<void(bool)>)>&
check_elements,
base::OnceCallback<void(ProcessedActionStatusProto)>& callback) {
checker_ = std::make_unique<BatchElementChecker>();
has_check_elements_result_ = false;
check_elements.Run(
checker_.get(),
base::BindOnce(&WaitForDomActionTest::OnCheckElementsDone,
base::Unretained(this)));
checker_->AddAllDoneCallback(
base::BindOnce(&WaitForDomActionTest::OnWaitForDomDone,
base::Unretained(this), std::move(callback)));
checker_->Run(&mock_web_controller_);
}
// Called from the check_elements callback passed to FakeWaitForDom.
void OnCheckElementsDone(bool result) {
ASSERT_FALSE(has_check_elements_result_); // Duplicate calls
has_check_elements_result_ = true;
check_elements_result_ = result;
}
// Called by |checker_| once it's done. This ends the call to
// FakeWaitForDom().
void OnWaitForDomDone(
base::OnceCallback<void(ProcessedActionStatusProto)> callback) {
ASSERT_TRUE(
has_check_elements_result_); // OnCheckElementsDone() not called
std::move(callback).Run(check_elements_result_ ? ACTION_APPLIED
: ELEMENT_RESOLUTION_FAILED);
}
// Runs the action defined in |proto_| and reports the result to |callback_|.
void Run() {
ActionProto action_proto;
*action_proto.mutable_wait_for_dom() = proto_;
WaitForDomAction action(action_proto);
action.ProcessAction(&mock_action_delegate_, callback_.Get());
}
MockActionDelegate mock_action_delegate_;
MockWebController mock_web_controller_;
base::MockCallback<Action::ProcessActionCallback> callback_;
WaitForDomProto proto_;
std::unique_ptr<BatchElementChecker> checker_;
bool has_check_elements_result_ = false;
bool check_elements_result_ = false;
};
TEST_F(WaitForDomActionTest, NoSelectors) {
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
Run();
}
TEST_F(WaitForDomActionTest, MatchOneElementFound) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element"}), _))
.WillRepeatedly(RunOnceCallback<1>(true));
proto_.mutable_wait_until()->add_selectors("#element");
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
Run();
}
TEST_F(WaitForDomActionTest, MatchOneElementNotFound) {
proto_.mutable_wait_until()->add_selectors("#element");
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
ELEMENT_RESOLUTION_FAILED))));
Run();
}
TEST_F(WaitForDomActionTest, NoMatchOneElementFound) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element"}), _))
.WillRepeatedly(RunOnceCallback<1>(true));
proto_.mutable_wait_while()->add_selectors("#element");
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
ELEMENT_RESOLUTION_FAILED))));
Run();
}
TEST_F(WaitForDomActionTest, NoMatchOneElementNotFound) {
proto_.mutable_wait_while()->add_selectors("#element");
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
Run();
}
TEST_F(WaitForDomActionTest, AllConditionsMetWantAll) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element1"}), _))
.WillRepeatedly(RunOnceCallback<1>(true));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element2"}), _))
.WillRepeatedly(RunOnceCallback<1>(false));
proto_.mutable_wait_for_all()
->add_conditions()
->mutable_must_match()
->add_selectors("#element1");
proto_.mutable_wait_for_all()
->add_conditions()
->mutable_must_not_match()
->add_selectors("#element2");
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
Run();
}
TEST_F(WaitForDomActionTest, AllConditionsMetWantSome) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element1"}), _))
.WillRepeatedly(RunOnceCallback<1>(true));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element2"}), _))
.WillRepeatedly(RunOnceCallback<1>(false));
proto_.mutable_wait_for_any()
->add_conditions()
->mutable_must_match()
->add_selectors("#element1");
proto_.mutable_wait_for_any()
->add_conditions()
->mutable_must_not_match()
->add_selectors("#element2");
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
Run();
}
TEST_F(WaitForDomActionTest, NoConditionsMetWantAll) {
proto_.mutable_wait_for_all()
->add_conditions()
->mutable_must_match()
->add_selectors("#element1");
proto_.mutable_wait_for_all()
->add_conditions()
->mutable_must_match()
->add_selectors("#element2");
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
ELEMENT_RESOLUTION_FAILED))));
Run();
}
TEST_F(WaitForDomActionTest, NoConditionsMetWantSome) {
proto_.mutable_wait_for_any()
->add_conditions()
->mutable_must_match()
->add_selectors("#element1");
proto_.mutable_wait_for_any()
->add_conditions()
->mutable_must_match()
->add_selectors("#element2");
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
ELEMENT_RESOLUTION_FAILED))));
Run();
}
TEST_F(WaitForDomActionTest, OnConditionMetWantAll) {
// element1 should be there, but isn't, so this doesn't satisfy wait_for_all
proto_.mutable_wait_for_all()
->add_conditions()
->mutable_must_match()
->add_selectors("#element1");
proto_.mutable_wait_for_all()
->add_conditions()
->mutable_must_not_match()
->add_selectors("#element2");
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
ELEMENT_RESOLUTION_FAILED))));
Run();
}
TEST_F(WaitForDomActionTest, OnConditionMetWantSome) {
// element1 should be there, but isn't. element2 is not there, as expected.
// This satisfies wait_for_one.
proto_.mutable_wait_for_any()
->add_conditions()
->mutable_must_match()
->add_selectors("#element1");
proto_.mutable_wait_for_any()
->add_conditions()
->mutable_must_not_match()
->add_selectors("#element2");
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
Run();
}
TEST_F(WaitForDomActionTest, ReportMatchesToServer) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element1"}), _))
.WillRepeatedly(RunOnceCallback<1>(true));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element2"}), _))
.WillRepeatedly(RunOnceCallback<1>(false));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element3"}), _))
.WillRepeatedly(RunOnceCallback<1>(false));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element4"}), _))
.WillRepeatedly(RunOnceCallback<1>(true));
auto* condition1 = proto_.mutable_wait_for_any()->add_conditions();
condition1->mutable_must_match()->add_selectors("#element1");
condition1->set_server_payload("1");
auto* condition2 = proto_.mutable_wait_for_any()->add_conditions();
condition2->mutable_must_not_match()->add_selectors("#element2");
condition2->set_server_payload("2");
auto* condition3 = proto_.mutable_wait_for_any()->add_conditions();
condition3->mutable_must_match()->add_selectors("#element3");
condition3->set_server_payload("3");
auto* condition4 = proto_.mutable_wait_for_any()->add_conditions();
condition4->mutable_must_not_match()->add_selectors("#element4");
condition4->set_server_payload("4");
// Condition 1 and 2 are met, conditions 3 and 4 are not.
ProcessedActionProto capture;
EXPECT_CALL(callback_, Run(_)).WillOnce(testing::SaveArgPointee<0>(&capture));
Run();
EXPECT_THAT(capture.wait_for_dom_result().matching_condition_payloads(),
ElementsAre("1", "2"));
}
} // namespace
} // namespace autofill_assistant