Commit 6d8fd697 authored by Stephane Zermatten's avatar Stephane Zermatten Committed by Commit Bot

[Autofill Assistant] Keep track of runnable scripts.

This change introduces ScriptTracker, which keeps track of the
available scripts, runnable scripts, scripts that are run and scripts
that were run and exposes these pieces of information through a simple
interface to the controller.

The tracker isn't bound to the controller yet. The controller is
expected to:
 - call SetScripts and CheckScripts at the right time
 - listen to changes to the set of runnable script, fetch them
   using runnable_scripts() and make them available for selection
 - call ExecuteScript with whatever script was selected

Change-Id: Ice3d2c81e523449b17db2ba8e7735e55ed837ea6
Bug: 806868
Reviewed-on: https://chromium-review.googlesource.com/1193823
Commit-Queue: Stephane Zermatten <szermatt@chromium.org>
Reviewed-by: default avatarGanggui Tang <gogerald@chromium.org>
Cr-Commit-Position: refs/heads/master@{#588821}
parent 87e06f7b
...@@ -39,6 +39,8 @@ jumbo_static_library("browser") { ...@@ -39,6 +39,8 @@ jumbo_static_library("browser") {
"script_executor_delegate.h", "script_executor_delegate.h",
"script_precondition.cc", "script_precondition.cc",
"script_precondition.h", "script_precondition.h",
"script_tracker.cc",
"script_tracker.h",
"service.cc", "service.cc",
"service.h", "service.h",
"ui_controller.h", "ui_controller.h",
...@@ -69,6 +71,7 @@ source_set("unit_tests") { ...@@ -69,6 +71,7 @@ source_set("unit_tests") {
"mock_web_controller.cc", "mock_web_controller.cc",
"mock_web_controller.h", "mock_web_controller.h",
"script_executor_unittest.cc", "script_executor_unittest.cc",
"script_tracker_unittest.cc",
] ]
deps = [ deps = [
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
#include "components/autofill_assistant/browser/controller.h" #include "components/autofill_assistant/browser/controller.h"
#include <utility>
#include <vector>
#include "components/autofill_assistant/browser/protocol_utils.h" #include "components/autofill_assistant/browser/protocol_utils.h"
#include "components/autofill_assistant/browser/ui_controller.h" #include "components/autofill_assistant/browser/ui_controller.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
...@@ -39,7 +42,8 @@ Controller::Controller(content::WebContents* web_contents, ...@@ -39,7 +42,8 @@ Controller::Controller(content::WebContents* web_contents,
client_(std::move(client)), client_(std::move(client)),
web_controller_(WebController::CreateForWebContents(web_contents)), web_controller_(WebController::CreateForWebContents(web_contents)),
service_(std::make_unique<Service>(client_->GetApiKey(), service_(std::make_unique<Service>(client_->GetApiKey(),
web_contents->GetBrowserContext())) { web_contents->GetBrowserContext())),
script_tracker_(std::make_unique<ScriptTracker>(this, this)) {
GetUiController()->SetUiDelegate(this); GetUiController()->SetUiDelegate(this);
GetUiController()->ShowOverlay(); GetUiController()->ShowOverlay();
if (!web_contents->IsLoading()) { if (!web_contents->IsLoading()) {
...@@ -62,10 +66,10 @@ void Controller::OnGetScripts(bool result, const std::string& response) { ...@@ -62,10 +66,10 @@ void Controller::OnGetScripts(bool result, const std::string& response) {
// TODO(crbug.com/806868): Terminate Autofill Assistant. // TODO(crbug.com/806868): Terminate Autofill Assistant.
return; return;
} }
bool parse_result = ProtocolUtils::ParseScripts(response, &scripts_); std::vector<std::unique_ptr<Script>> scripts;
bool parse_result = ProtocolUtils::ParseScripts(response, &scripts);
DCHECK(parse_result); DCHECK(parse_result);
// TODO(crbug.com/806868): Present assistant scripts if necessary or auto script_tracker_->SetAndCheckScripts(std::move(scripts));
// start a script.
} }
void Controller::OnClickOverlay() { void Controller::OnClickOverlay() {
...@@ -77,6 +81,11 @@ void Controller::OnDestroy() { ...@@ -77,6 +81,11 @@ void Controller::OnDestroy() {
delete this; delete this;
} }
void Controller::OnRunnableScriptsChanged() {
// TODO(crbug.com/806868): Take the set of runnable script from the tracker
// and make them available for selection. Run the selected script.
}
void Controller::DidFinishLoad(content::RenderFrameHost* render_frame_host, void Controller::DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) { const GURL& validated_url) {
// TODO(crbug.com/806868): Find a better time to get and update assistant // TODO(crbug.com/806868): Find a better time to get and update assistant
......
...@@ -5,10 +5,14 @@ ...@@ -5,10 +5,14 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CONTROLLER_H_ #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CONTROLLER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CONTROLLER_H_ #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CONTROLLER_H_
#include <memory>
#include <string>
#include "components/autofill_assistant/browser/client.h" #include "components/autofill_assistant/browser/client.h"
#include "components/autofill_assistant/browser/client_memory.h" #include "components/autofill_assistant/browser/client_memory.h"
#include "components/autofill_assistant/browser/script.h" #include "components/autofill_assistant/browser/script.h"
#include "components/autofill_assistant/browser/script_executor_delegate.h" #include "components/autofill_assistant/browser/script_executor_delegate.h"
#include "components/autofill_assistant/browser/script_tracker.h"
#include "components/autofill_assistant/browser/service.h" #include "components/autofill_assistant/browser/service.h"
#include "components/autofill_assistant/browser/ui_delegate.h" #include "components/autofill_assistant/browser/ui_delegate.h"
#include "components/autofill_assistant/browser/web_controller.h" #include "components/autofill_assistant/browser/web_controller.h"
...@@ -25,6 +29,7 @@ namespace autofill_assistant { ...@@ -25,6 +29,7 @@ namespace autofill_assistant {
// the web contents is being destroyed. // the web contents is being destroyed.
class Controller : public ScriptExecutorDelegate, class Controller : public ScriptExecutorDelegate,
public UiDelegate, public UiDelegate,
public ScriptTracker::Listener,
private content::WebContentsObserver { private content::WebContentsObserver {
public: public:
static void CreateAndStartForWebContents(content::WebContents* web_contents, static void CreateAndStartForWebContents(content::WebContents* web_contents,
...@@ -48,6 +53,9 @@ class Controller : public ScriptExecutorDelegate, ...@@ -48,6 +53,9 @@ class Controller : public ScriptExecutorDelegate,
void OnClickOverlay() override; void OnClickOverlay() override;
void OnDestroy() override; void OnDestroy() override;
// Overrides ScriptTracker::Listener:
void OnRunnableScriptsChanged() override;
// Overrides content::WebContentsObserver: // Overrides content::WebContentsObserver:
void DidFinishLoad(content::RenderFrameHost* render_frame_host, void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) override; const GURL& validated_url) override;
...@@ -56,7 +64,7 @@ class Controller : public ScriptExecutorDelegate, ...@@ -56,7 +64,7 @@ class Controller : public ScriptExecutorDelegate,
std::unique_ptr<Client> client_; std::unique_ptr<Client> client_;
std::unique_ptr<WebController> web_controller_; std::unique_ptr<WebController> web_controller_;
std::unique_ptr<Service> service_; std::unique_ptr<Service> service_;
std::map<Script*, std::unique_ptr<Script>> scripts_; std::unique_ptr<ScriptTracker> script_tracker_;
ClientMemory memory_; ClientMemory memory_;
DISALLOW_COPY_AND_ASSIGN(Controller); DISALLOW_COPY_AND_ASSIGN(Controller);
......
...@@ -35,7 +35,7 @@ std::string ProtocolUtils::CreateGetScriptsRequest(const GURL& url) { ...@@ -35,7 +35,7 @@ std::string ProtocolUtils::CreateGetScriptsRequest(const GURL& url) {
// static // static
bool ProtocolUtils::ParseScripts( bool ProtocolUtils::ParseScripts(
const std::string& response, const std::string& response,
std::map<Script*, std::unique_ptr<Script>>* scripts) { std::vector<std::unique_ptr<Script>>* scripts) {
DCHECK(scripts); DCHECK(scripts);
SupportsScriptResponseProto response_proto; SupportsScriptResponseProto response_proto;
...@@ -44,14 +44,15 @@ bool ProtocolUtils::ParseScripts( ...@@ -44,14 +44,15 @@ bool ProtocolUtils::ParseScripts(
return false; return false;
} }
scripts->clear();
for (const auto& script_proto : response_proto.scripts()) { for (const auto& script_proto : response_proto.scripts()) {
auto script = std::make_unique<Script>(); auto script = std::make_unique<Script>();
script->path = script_proto.path(); script->handle.path = script_proto.path();
if (script_proto.has_presentation()) { if (script_proto.has_presentation()) {
const auto& presentation = script_proto.presentation(); const auto& presentation = script_proto.presentation();
if (presentation.has_name()) if (presentation.has_name())
script->name = presentation.name(); script->handle.name = presentation.name();
if (presentation.has_precondition()) { if (presentation.has_precondition()) {
std::vector<std::vector<std::string>> elements_exist; std::vector<std::vector<std::string>> elements_exist;
...@@ -70,8 +71,7 @@ bool ProtocolUtils::ParseScripts( ...@@ -70,8 +71,7 @@ bool ProtocolUtils::ParseScripts(
} }
} }
} }
scripts->emplace_back(std::move(script));
(*scripts)[script.get()] = std::move(script);
} }
return true; return true;
......
...@@ -31,7 +31,7 @@ class ProtocolUtils { ...@@ -31,7 +31,7 @@ class ProtocolUtils {
// Parsed assistant scripts are returned through |scripts|, which // Parsed assistant scripts are returned through |scripts|, which
// should not be nullptr. Return false if parse failed, otherwise return true. // should not be nullptr. Return false if parse failed, otherwise return true.
static bool ParseScripts(const std::string& response, static bool ParseScripts(const std::string& response,
std::map<Script*, std::unique_ptr<Script>>* scripts); std::vector<std::unique_ptr<Script>>* scripts);
// Create initial request to get script actions for the given |script_path|. // Create initial request to get script actions for the given |script_path|.
static std::string CreateInitialScriptActionsRequest( static std::string CreateInitialScriptActionsRequest(
......
...@@ -6,6 +6,10 @@ ...@@ -6,6 +6,10 @@
namespace autofill_assistant { namespace autofill_assistant {
ScriptHandle::ScriptHandle() = default;
ScriptHandle::~ScriptHandle() = default;
Script::Script() = default; Script::Script() = default;
Script::~Script() = default; Script::~Script() = default;
......
...@@ -11,13 +11,21 @@ ...@@ -11,13 +11,21 @@
namespace autofill_assistant { namespace autofill_assistant {
// Minimal information about a script necessary to display and run it.
struct ScriptHandle {
ScriptHandle();
~ScriptHandle();
std::string name;
std::string path;
};
// Script represents a sequence of actions. // Script represents a sequence of actions.
struct Script { struct Script {
Script(); Script();
~Script(); ~Script();
std::string name; ScriptHandle handle;
std::string path;
std::unique_ptr<ScriptPrecondition> precondition; std::unique_ptr<ScriptPrecondition> precondition;
}; };
......
...@@ -18,9 +18,9 @@ ...@@ -18,9 +18,9 @@
namespace autofill_assistant { namespace autofill_assistant {
ScriptExecutor::ScriptExecutor(Script* script, ScriptExecutorDelegate* delegate) ScriptExecutor::ScriptExecutor(const std::string& script_path,
: script_(script), delegate_(delegate), weak_ptr_factory_(this) { ScriptExecutorDelegate* delegate)
DCHECK(script_); : script_path_(script_path), delegate_(delegate), weak_ptr_factory_(this) {
DCHECK(delegate_); DCHECK(delegate_);
} }
ScriptExecutor::~ScriptExecutor() {} ScriptExecutor::~ScriptExecutor() {}
...@@ -30,8 +30,8 @@ void ScriptExecutor::Run(RunScriptCallback callback) { ...@@ -30,8 +30,8 @@ void ScriptExecutor::Run(RunScriptCallback callback) {
DCHECK(delegate_->GetService()); DCHECK(delegate_->GetService());
delegate_->GetService()->GetActions( delegate_->GetService()->GetActions(
script_->path, base::BindOnce(&ScriptExecutor::OnGetActions, script_path_, base::BindOnce(&ScriptExecutor::OnGetActions,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
void ScriptExecutor::ShowStatusMessage(const std::string& message) { void ScriptExecutor::ShowStatusMessage(const std::string& message) {
......
...@@ -22,9 +22,9 @@ namespace autofill_assistant { ...@@ -22,9 +22,9 @@ namespace autofill_assistant {
// Class to execute an assistant script. // Class to execute an assistant script.
class ScriptExecutor : public ActionDelegate { class ScriptExecutor : public ActionDelegate {
public: public:
// |script| and |delegate| should outlive this object and should not be // |delegate| should outlive this object and should not be nullptr.
// nullptr. ScriptExecutor(const std::string& script_path,
ScriptExecutor(Script* script, ScriptExecutorDelegate* delegate); ScriptExecutorDelegate* delegate);
~ScriptExecutor() override; ~ScriptExecutor() override;
using RunScriptCallback = base::OnceCallback<void(bool)>; using RunScriptCallback = base::OnceCallback<void(bool)>;
...@@ -55,7 +55,7 @@ class ScriptExecutor : public ActionDelegate { ...@@ -55,7 +55,7 @@ class ScriptExecutor : public ActionDelegate {
void GetNextActions(); void GetNextActions();
void OnProcessedAction(std::unique_ptr<Action> action, bool status); void OnProcessedAction(std::unique_ptr<Action> action, bool status);
Script* script_; std::string script_path_;
ScriptExecutorDelegate* delegate_; ScriptExecutorDelegate* delegate_;
RunScriptCallback callback_; RunScriptCallback callback_;
......
...@@ -28,10 +28,7 @@ using ::testing::_; ...@@ -28,10 +28,7 @@ using ::testing::_;
class ScriptExecutorTest : public testing::Test, public ScriptExecutorDelegate { class ScriptExecutorTest : public testing::Test, public ScriptExecutorDelegate {
public: public:
void SetUp() override { void SetUp() override {
script_.name = "script name"; executor_ = std::make_unique<ScriptExecutor>("script path", this);
script_.path = "script path";
executor_ = std::make_unique<ScriptExecutor>(&script_, this);
// In this test, "tell" actions always succeed and "click" actions always // In this test, "tell" actions always succeed and "click" actions always
// fail. // fail.
......
// 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_tracker.h"
#include <utility>
#include "base/bind.h"
#include "components/autofill_assistant/browser/script.h"
#include "components/autofill_assistant/browser/script_executor.h"
namespace autofill_assistant {
ScriptTracker::ScriptTracker(ScriptExecutorDelegate* delegate,
ScriptTracker::Listener* listener)
: delegate_(delegate), listener_(listener), weak_ptr_factory_(this) {
DCHECK(delegate_);
DCHECK(listener_);
}
ScriptTracker::~ScriptTracker() = default;
void ScriptTracker::SetAndCheckScripts(
std::vector<std::unique_ptr<Script>> scripts) {
ClearAvailableScripts();
for (auto& script : scripts) {
available_scripts_[script.get()] = std::move(script);
}
CheckScripts();
}
void ScriptTracker::CheckScripts() {
if (pending_precondition_check_count_ > 0)
return;
DCHECK_EQ(pending_precondition_check_count_, 0);
DCHECK(pending_runnable_scripts_.empty());
std::vector<Script*> scripts_to_check;
for (const auto& entry : available_scripts_) {
Script* script = entry.first;
if (executed_scripts_.find(script->handle.path) ==
executed_scripts_.end() &&
script->precondition) {
scripts_to_check.emplace_back(script);
}
}
// pending_precondition_check_count_ lets OnPreconditionCheck know when to
// stop. It must be set before the callback can possibly be run.
pending_precondition_check_count_ = scripts_to_check.size();
if (pending_precondition_check_count_ == 0) {
// Possibly report an empty set of runnable scripts.
UpdateRunnableScriptsIfNecessary();
} else {
for (Script* script : scripts_to_check) {
script->precondition->Check(
delegate_->GetWebController(),
base::BindOnce(&ScriptTracker::OnPreconditionCheck,
weak_ptr_factory_.GetWeakPtr(), script));
}
}
}
void ScriptTracker::ExecuteScript(const std::string& script_path,
base::OnceCallback<void(bool)> callback) {
if (running()) {
std::move(callback).Run(false);
return;
}
DCHECK(executed_scripts_.find(script_path) == executed_scripts_.end());
executed_scripts_.insert(script_path);
executor_ = std::make_unique<ScriptExecutor>(script_path, delegate_);
executor_->Run(base::BindOnce(&ScriptTracker::OnScriptRun,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback)));
}
void ScriptTracker::OnScriptRun(
base::OnceCallback<void(bool)> original_callback,
bool success) {
executor_.reset();
std::move(original_callback).Run(success);
}
void ScriptTracker::UpdateRunnableScriptsIfNecessary() {
DCHECK_EQ(pending_precondition_check_count_, 0);
bool runnables_changed = RunnablesHaveChanged();
if (runnables_changed) {
runnable_scripts_.clear();
for (Script* script : pending_runnable_scripts_) {
runnable_scripts_.push_back(script->handle);
}
// TODO(crbug.com/806868): Sort runnable scripts by priority.
}
pending_runnable_scripts_.clear();
if (runnables_changed)
listener_->OnRunnableScriptsChanged();
}
bool ScriptTracker::RunnablesHaveChanged() {
if (runnable_scripts_.size() != pending_runnable_scripts_.size())
return true;
std::set<std::string> pending_paths;
for (Script* script : pending_runnable_scripts_) {
pending_paths.insert(script->handle.path);
}
std::set<std::string> current_paths;
for (const auto& handle : runnable_scripts_) {
current_paths.insert(handle.path);
}
return pending_paths != current_paths;
}
void ScriptTracker::OnPreconditionCheck(Script* script,
bool met_preconditions) {
if (available_scripts_.find(script) == available_scripts_.end()) {
// Result is not relevant anymore.
return;
}
if (met_preconditions)
pending_runnable_scripts_.push_back(script);
pending_precondition_check_count_--;
if (pending_precondition_check_count_ > 0)
return;
UpdateRunnableScriptsIfNecessary();
}
void ScriptTracker::ClearAvailableScripts() {
available_scripts_.clear();
// Clearing available_scripts_ has cancelled any pending precondition checks,
// ending them.
pending_precondition_check_count_ = 0;
pending_runnable_scripts_.clear();
}
} // 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_SCRIPT_TRACKER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_TRACKER_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/script.h"
#include "components/autofill_assistant/browser/script_executor.h"
namespace autofill_assistant {
class ScriptExecutorDelegate;
class ScriptTrackerTest;
// The script tracker keeps track of which scripts are available, which are
// running, which have run, which are runnable whose preconditions are met.
//
// User of this class is responsible for retrieving and passing scripts to the
// tracker and letting the tracker know about changes to the DOM.
class ScriptTracker {
public:
// Listens to changes on the ScriptTracker state.
class Listener {
public:
virtual ~Listener() = default;
// Called when the set of runnable scripts have changed.
virtual void OnRunnableScriptsChanged() = 0;
};
// |delegate| and |listener| should outlive this object and should not be
// nullptr.
ScriptTracker(ScriptExecutorDelegate* delegate,
ScriptTracker::Listener* listener);
~ScriptTracker();
// Updates the set of available |scripts| and check them.
void SetAndCheckScripts(std::vector<std::unique_ptr<Script>> scripts);
// Run the preconditions on the current set of scripts, and possibly update
// the set of runnable scripts.
void CheckScripts();
// Runs a script and reports, when the script has ended, whether the run was
// successful. Fails immediately if a script is already running.
//
// Scripts that are already executed won't be considered runnable anymore.
// Call CheckScripts to refresh the set of runnable script after script
// execution.
void ExecuteScript(const std::string& path,
base::OnceCallback<void(bool)> callback);
// Checks whether a script is currently running. There can be at most one
// script running at a time.
bool running() const { return executor_ != nullptr; }
private:
friend ScriptTrackerTest;
// Returns a set of scripts that can be run, according to the last round of
// checks.
const std::vector<ScriptHandle>& runnable_scripts() const {
return runnable_scripts_;
}
void OnScriptRun(base::OnceCallback<void(bool)> original_callback,
bool success);
void UpdateRunnableScriptsIfNecessary();
// Returns true if |runnable_| should be updated.
bool RunnablesHaveChanged();
void OnPreconditionCheck(Script* script, bool met_preconditions);
void ClearAvailableScripts();
ScriptExecutorDelegate* const delegate_;
ScriptTracker::Listener* const listener_;
// Paths and names of scripts known to be runnable.
//
// Note that the set of runnable scripts can survive a SetScripts; as
// long as the new set of runnable script has the same path, it won't be seen
// as a change to the set of runnable, even if the pointers have changed.
std::vector<ScriptHandle> runnable_scripts_;
// Sets of available scripts. SetScripts resets this and interrupts
// any pending check.
std::map<Script*, std::unique_ptr<Script>> available_scripts_;
// Set of scripts that have been executed. They'll be excluded from runnable.
// TODO(crbug.com/806868): Track script execution status and forward
// that information to the script precondition.
std::set<std::string> executed_scripts_;
// Number of precondition checks run for CheckScripts that are still
// pending.
int pending_precondition_check_count_;
// Scripts found to be runnable so far, in the current run of CheckScripts.
std::vector<Script*> pending_runnable_scripts_;
// If a script is currently running, this is the script's executor. Otherwise,
// this is nullptr.
std::unique_ptr<ScriptExecutor> executor_;
base::WeakPtrFactory<ScriptTracker> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ScriptTracker);
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_TRACKER_H_
// 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_tracker.h"
#include <utility>
#include "base/test/mock_callback.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/protocol_utils.h"
#include "components/autofill_assistant/browser/script_executor_delegate.h"
#include "components/autofill_assistant/browser/service.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using ::testing::IsEmpty;
using ::testing::NiceMock;
class ScriptTrackerTest : public testing::Test,
public ScriptTracker::Listener,
public ScriptExecutorDelegate {
public:
void SetUp() override {
ON_CALL(mock_web_controller_, OnElementExists(ElementsAre("exists"), _))
.WillByDefault(RunOnceCallback<1>(true));
ON_CALL(mock_web_controller_,
OnElementExists(ElementsAre("does_not_exist"), _))
.WillByDefault(RunOnceCallback<1>(false));
// Scripts run, but have no actions.
ON_CALL(mock_service_, OnGetActions(_, _))
.WillByDefault(RunOnceCallback<1>(true, ""));
}
protected:
ScriptTrackerTest() : runnable_scripts_changed_(0), tracker_(this, this) {}
// Overrides ScriptTrackerDelegate
Service* GetService() override { return &mock_service_; }
UiController* GetUiController() override { return &mock_ui_controller_; }
WebController* GetWebController() override { return &mock_web_controller_; }
ClientMemory* GetClientMemory() override { return &client_memory_; }
// Overrides ScriptTracker::Listener
void OnRunnableScriptsChanged() override { runnable_scripts_changed_++; }
void SetAndCheckScripts(const SupportsScriptResponseProto& response) {
std::string response_str;
response.SerializeToString(&response_str);
std::vector<std::unique_ptr<Script>> scripts;
ProtocolUtils::ParseScripts(response_str, &scripts);
tracker_.SetAndCheckScripts(std::move(scripts));
}
static void AddScript(SupportsScriptResponseProto* response,
const std::string& name,
const std::string& path,
const std::string& selector) {
SupportedScriptProto* script = response->add_scripts();
script->set_path(path);
script->mutable_presentation()->set_name(name);
script->mutable_presentation()
->mutable_precondition()
->add_elements_exist()
->add_selectors(selector);
}
const std::vector<ScriptHandle>& runnable_scripts() {
return tracker_.runnable_scripts();
}
NiceMock<MockService> mock_service_;
NiceMock<MockWebController> mock_web_controller_;
NiceMock<MockUiController> mock_ui_controller_;
ClientMemory client_memory_;
// Number of times OnRunnableScriptsChanged was called.
int runnable_scripts_changed_;
ScriptTracker tracker_;
};
TEST_F(ScriptTrackerTest, NoScripts) {
tracker_.SetAndCheckScripts({});
EXPECT_EQ(0, runnable_scripts_changed_);
EXPECT_THAT(runnable_scripts(), IsEmpty());
}
TEST_F(ScriptTrackerTest, SomeRunnableScripts) {
SupportsScriptResponseProto scripts;
AddScript(&scripts, "not runnable name", "not runnable path",
"does_not_exist");
AddScript(&scripts, "runnable name", "runnable path", "exists");
SetAndCheckScripts(scripts);
EXPECT_EQ(1, runnable_scripts_changed_);
ASSERT_THAT(runnable_scripts(), SizeIs(1));
EXPECT_EQ("runnable name", runnable_scripts()[0].name);
EXPECT_EQ("runnable path", runnable_scripts()[0].path);
}
TEST_F(ScriptTrackerTest, NewScriptChangesNothing) {
SupportsScriptResponseProto scripts;
AddScript(&scripts, "runnable name", "runnable path", "exists");
SetAndCheckScripts(scripts);
EXPECT_EQ(1, runnable_scripts_changed_);
SetAndCheckScripts(scripts);
EXPECT_EQ(1, runnable_scripts_changed_);
}
TEST_F(ScriptTrackerTest, NewScriptClearsRunnable) {
SupportsScriptResponseProto scripts;
AddScript(&scripts, "runnable name", "runnable path", "exists");
SetAndCheckScripts(scripts);
EXPECT_EQ(1, runnable_scripts_changed_);
EXPECT_THAT(runnable_scripts(), SizeIs(1));
SetAndCheckScripts(SupportsScriptResponseProto::default_instance());
EXPECT_EQ(2, runnable_scripts_changed_);
EXPECT_THAT(runnable_scripts(), IsEmpty());
}
TEST_F(ScriptTrackerTest, NewScriptAddsRunnable) {
SupportsScriptResponseProto scripts;
AddScript(&scripts, "runnable name", "runnable path", "exists");
SetAndCheckScripts(scripts);
EXPECT_EQ(1, runnable_scripts_changed_);
EXPECT_THAT(runnable_scripts(), SizeIs(1));
AddScript(&scripts, "new runnable name", "new runnable path", "exists");
SetAndCheckScripts(scripts);
EXPECT_EQ(2, runnable_scripts_changed_);
EXPECT_THAT(runnable_scripts(), SizeIs(2));
}
TEST_F(ScriptTrackerTest, NewScriptChangesRunnable) {
SupportsScriptResponseProto scripts;
AddScript(&scripts, "runnable name", "runnable path", "exists");
SetAndCheckScripts(scripts);
EXPECT_EQ(1, runnable_scripts_changed_);
EXPECT_THAT(runnable_scripts(), SizeIs(1));
scripts.clear_scripts();
AddScript(&scripts, "new runnable name", "new runnable path", "exists");
SetAndCheckScripts(scripts);
EXPECT_EQ(2, runnable_scripts_changed_);
}
TEST_F(ScriptTrackerTest, CheckScriptsAgainAfterScriptEnd) {
SupportsScriptResponseProto scripts;
AddScript(&scripts, "script 1", "script1", "exists");
AddScript(&scripts, "script 2", "script2", "exists");
SetAndCheckScripts(scripts);
// Both scripts are runnable
EXPECT_EQ(1, runnable_scripts_changed_);
EXPECT_THAT(runnable_scripts(), SizeIs(2));
// run 'script 1'
base::MockCallback<base::OnceCallback<void(bool)>> execute_callback;
EXPECT_CALL(execute_callback, Run(true));
tracker_.ExecuteScript("script1", execute_callback.Get());
tracker_.CheckScripts();
// The 2nd time the scripts are checked, automatically after the script runs,
// 'script1' isn't runnable anymore, because it's already been run.
EXPECT_EQ(2, runnable_scripts_changed_);
ASSERT_THAT(runnable_scripts(), SizeIs(1));
ASSERT_EQ("script2", runnable_scripts()[0].path);
}
TEST_F(ScriptTrackerTest, CheckScriptsAfterDOMChange) {
EXPECT_CALL(mock_web_controller_,
OnElementExists(ElementsAre("maybe_exists"), _))
.WillOnce(RunOnceCallback<1>(false));
SupportsScriptResponseProto scripts;
AddScript(&scripts, "script name", "script path", "maybe_exists");
SetAndCheckScripts(scripts);
// No scripts are runnable.
EXPECT_THAT(runnable_scripts(), IsEmpty());
// DOM has changed; OnElementExists now returns true.
EXPECT_CALL(mock_web_controller_,
OnElementExists(ElementsAre("maybe_exists"), _))
.WillOnce(RunOnceCallback<1>(true));
tracker_.CheckScripts();
// The script can now run
ASSERT_THAT(runnable_scripts(), SizeIs(1));
EXPECT_EQ("script path", runnable_scripts()[0].path);
}
} // namespace autofill_assistant
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