Commit be790f0d authored by Yiming Zhou's avatar Yiming Zhou Committed by Commit Bot

Add new actions to the Captured Sites Automation framework.

Adding a list of new actions to the Captured Sites Automation framework,
as well as a measure to hide the geolocation dialog, and a measure to
dismiss modal beforeunload JavaScript dialogs.

Bug: 847905
Change-Id: Icb687ebc46a339c0ecc6be799200aac73b32b5ae
Reviewed-on: https://chromium-review.googlesource.com/1207892
Commit-Queue: Yiming Zhou <uwyiming@google.com>
Reviewed-by: default avatarSebastien Seguin-Gagnon <sebsg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589310}
parent b4bebe1a
...@@ -200,31 +200,6 @@ class AutofillCapturedSitesInteractiveTest ...@@ -200,31 +200,6 @@ class AutofillCapturedSitesInteractiveTest
bool ShowAutofillSuggestion(content::RenderFrameHost* frame, bool ShowAutofillSuggestion(content::RenderFrameHost* frame,
const std::string& target_element_xpath) { const std::string& target_element_xpath) {
const std::string get_target_field_x_js(base::StringPrintf(
"window.domAutomationController.send("
" (function() {"
" try {"
" const element = automation_helper.getElementByXpath(`%s`);"
" const rect = element.getBoundingClientRect();"
" console.log(`Window href x: ${location.href}`);"
" return Math.floor(rect.left + rect.width / 2);"
" } catch(ex) {}"
" return -1;"
" })());",
target_element_xpath.c_str()));
const std::string get_target_field_y_js(base::StringPrintf(
"window.domAutomationController.send("
" (function() {"
" try {"
" const element = automation_helper.getElementByXpath(`%s`);"
" const rect = element.getBoundingClientRect();"
" console.log(`Window href y: ${location.href}`);"
" return Math.floor(rect.top + rect.height / 2);"
" } catch(ex) {}"
" return -1;"
" })());",
target_element_xpath.c_str()));
// First, automation should focus on the frame containg the autofill form. // First, automation should focus on the frame containg the autofill form.
// Doing so ensures that Chrome scrolls the element into view if the // Doing so ensures that Chrome scrolls the element into view if the
// element is off the page. // element is off the page.
...@@ -232,16 +207,10 @@ class AutofillCapturedSitesInteractiveTest ...@@ -232,16 +207,10 @@ class AutofillCapturedSitesInteractiveTest
frame, target_element_xpath)) frame, target_element_xpath))
return false; return false;
int x; int x, y;
if (!content::ExecuteScriptAndExtractInt(frame, get_target_field_x_js, &x)) if (!captured_sites_test_utils::TestRecipeReplayer::
return false; GetCenterCoordinateOfTargetElement(frame, target_element_xpath, x,
if (x == -1) y))
return false;
int y;
if (!content::ExecuteScriptAndExtractInt(frame, get_target_field_y_js, &y))
return false;
if (y == -1)
return false; return false;
test_delegate()->Reset(); test_delegate()->Reset();
......
...@@ -16,9 +16,12 @@ ...@@ -16,9 +16,12 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "base/values.h" #include "base/values.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/permissions/permission_request_manager.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h" #include "chrome/test/base/ui_test_utils.h"
#include "components/app_modal/javascript_app_modal_dialog.h"
#include "components/app_modal/native_app_modal_dialog.h"
#include "content/public/browser/browsing_data_remover.h" #include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
...@@ -32,6 +35,9 @@ ...@@ -32,6 +35,9 @@
#include "ipc/ipc_logging.h" #include "ipc/ipc_logging.h"
#include "ipc/ipc_message_macros.h" #include "ipc/ipc_message_macros.h"
#include "ipc/ipc_sync_message.h" #include "ipc/ipc_sync_message.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace { namespace {
// The maximum amount of time to wait for Chrome to finish autofilling a form. // The maximum amount of time to wait for Chrome to finish autofilling a form.
...@@ -44,7 +50,6 @@ const base::TimeDelta kAutofillActionWaitForVisualUpdateTimeout = ...@@ -44,7 +50,6 @@ const base::TimeDelta kAutofillActionWaitForVisualUpdateTimeout =
// Automation Framework will retry an autofill action a couple times before // Automation Framework will retry an autofill action a couple times before
// concluding that Chrome Autofill does not work. // concluding that Chrome Autofill does not work.
const int kAutofillActionNumRetries = 5; const int kAutofillActionNumRetries = 5;
} // namespace } // namespace
namespace captured_sites_test_utils { namespace captured_sites_test_utils {
...@@ -81,7 +86,7 @@ void PageActivityObserver::WaitTillPageIsIdle( ...@@ -81,7 +86,7 @@ void PageActivityObserver::WaitTillPageIsIdle(
web_contents()->IsWaitingForResponse() || web_contents()->IsLoading(); web_contents()->IsWaitingForResponse() || web_contents()->IsLoading();
if (page_is_loading) { if (page_is_loading) {
finished_load_time = base::TimeTicks::Now(); finished_load_time = base::TimeTicks::Now();
} else if (base::TimeTicks::Now() - finished_load_time > } else if ((base::TimeTicks::Now() - finished_load_time) >
continuous_paint_timeout) { continuous_paint_timeout) {
// |continuous_paint_timeout| has expired since Chrome loaded the page. // |continuous_paint_timeout| has expired since Chrome loaded the page.
// During this period of time, Chrome has been continuously painting // During this period of time, Chrome has been continuously painting
...@@ -93,6 +98,20 @@ void PageActivityObserver::WaitTillPageIsIdle( ...@@ -93,6 +98,20 @@ void PageActivityObserver::WaitTillPageIsIdle(
} while (page_is_loading || paint_occurred_during_last_loop_); } while (page_is_loading || paint_occurred_during_last_loop_);
} }
bool PageActivityObserver::WaitForVisualUpdate(base::TimeDelta timeout) {
base::TimeTicks start_time = base::TimeTicks::Now();
while (!paint_occurred_during_last_loop_) {
base::RunLoop heart_beat;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, heart_beat.QuitClosure(), kPaintEventCheckInterval);
heart_beat.Run();
if ((base::TimeTicks::Now() - start_time) > timeout) {
return false;
}
}
return true;
}
void PageActivityObserver::DidCommitAndDrawCompositorFrame() { void PageActivityObserver::DidCommitAndDrawCompositorFrame() {
paint_occurred_during_last_loop_ = true; paint_occurred_during_last_loop_ = true;
} }
...@@ -219,6 +238,10 @@ void TestRecipeReplayer::Setup() { ...@@ -219,6 +238,10 @@ void TestRecipeReplayer::Setup() {
<< "Cannot install the root certificate " << "Cannot install the root certificate "
<< "for the local web page replay server."; << "for the local web page replay server.";
CleanupSiteData(); CleanupSiteData();
// Bypass permission dialogs.
PermissionRequestManager::FromWebContents(GetWebContents())
->set_auto_response_for_test(PermissionRequestManager::ACCEPT_ALL);
} }
void TestRecipeReplayer::Cleanup() { void TestRecipeReplayer::Cleanup() {
...@@ -237,6 +260,10 @@ TestRecipeReplayer::feature_action_executor() { ...@@ -237,6 +260,10 @@ TestRecipeReplayer::feature_action_executor() {
return feature_action_executor_; return feature_action_executor_;
} }
Browser* TestRecipeReplayer::browser() {
return browser_;
}
content::WebContents* TestRecipeReplayer::GetWebContents() { content::WebContents* TestRecipeReplayer::GetWebContents() {
return browser_->tab_strip_model()->GetActiveWebContents(); return browser_->tab_strip_model()->GetActiveWebContents();
} }
...@@ -464,12 +491,21 @@ bool TestRecipeReplayer::ReplayRecordedActions( ...@@ -464,12 +491,21 @@ bool TestRecipeReplayer::ReplayRecordedActions(
} else if (base::CompareCaseInsensitiveASCII(type, "click") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "click") == 0) {
if (!ExecuteClickAction(*action)) if (!ExecuteClickAction(*action))
return false; return false;
} else if (base::CompareCaseInsensitiveASCII(type, "hover") == 0) {
if (!ExecuteHoverAction(*action))
return false;
} else if (base::CompareCaseInsensitiveASCII(type, "pressEnter") == 0) {
if (!ExecutePressEnterAction(*action))
return false;
} else if (base::CompareCaseInsensitiveASCII(type, "select") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "select") == 0) {
if (!ExecuteSelectDropdownAction(*action)) if (!ExecuteSelectDropdownAction(*action))
return false; return false;
} else if (base::CompareCaseInsensitiveASCII(type, "type") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "type") == 0) {
if (!ExecuteTypeAction(*action)) if (!ExecuteTypeAction(*action))
return false; return false;
} else if (base::CompareCaseInsensitiveASCII(type, "typePassword") == 0) {
if (!ExecuteTypePasswordAction(*action))
return false;
} else if (base::CompareCaseInsensitiveASCII(type, "validateField") == 0) { } else if (base::CompareCaseInsensitiveASCII(type, "validateField") == 0) {
if (!ExecuteValidateFieldValueAction(*action)) if (!ExecuteValidateFieldValueAction(*action))
return false; return false;
...@@ -480,6 +516,13 @@ bool TestRecipeReplayer::ReplayRecordedActions( ...@@ -480,6 +516,13 @@ bool TestRecipeReplayer::ReplayRecordedActions(
ADD_FAILURE() << "Unrecognized action type: " << type; ADD_FAILURE() << "Unrecognized action type: " << type;
} }
} // end foreach action } // end foreach action
// Dismiss the beforeUnloadDialog if the last page of the test has a
// beforeUnload function.
if (recipe->FindKey("dismissBeforeUnload")) {
NavigateAwayAndDismissBeforeUnloadDialog();
}
return true; return true;
} }
...@@ -573,6 +616,116 @@ bool TestRecipeReplayer::ExecuteClickAction( ...@@ -573,6 +616,116 @@ bool TestRecipeReplayer::ExecuteClickAction(
return true; return true;
} }
bool TestRecipeReplayer::ExecuteHoverAction(
const base::DictionaryValue& action) {
std::string xpath;
if (!GetTargetHTMLElementXpathFromAction(action, &xpath))
return false;
content::RenderFrameHost* frame;
if (!GetTargetFrameFromAction(action, &frame))
return false;
if (!WaitForElementToBeReady(frame, xpath))
return false;
VLOG(1) << "Hovering over `" << xpath << "`.";
PageActivityObserver page_activity_observer(frame);
int x, y;
if (!GetCenterCoordinateOfTargetElement(frame, xpath, x, y))
return false;
if (!SimulateMouseHoverAt(frame, gfx::Point(x, y)))
return false;
if (!page_activity_observer.WaitForVisualUpdate()) {
ADD_FAILURE() << "The page did not respond to a mouse hover action!";
return false;
}
return true;
}
bool TestRecipeReplayer::ExecutePressEnterAction(
const base::DictionaryValue& action) {
std::string xpath;
if (!GetTargetHTMLElementXpathFromAction(action, &xpath))
return false;
content::RenderFrameHost* frame;
if (!GetTargetFrameFromAction(action, &frame))
return false;
if (!WaitForElementToBeReady(frame, xpath))
return false;
VLOG(1) << "Press 'Enter' on `" << xpath << "`.";
PageActivityObserver page_activity_observer(frame);
if (!PlaceFocusOnElement(frame, xpath))
return false;
ui::DomKey key = ui::DomKey::ENTER;
ui::KeyboardCode key_code = ui::NonPrintableDomKeyToKeyboardCode(key);
ui::DomCode code = ui::UsLayoutKeyboardCodeToDomCode(key_code);
SimulateKeyPress(content::WebContents::FromRenderFrameHost(frame), key, code,
key_code, false, false, false, false);
page_activity_observer.WaitTillPageIsIdle();
return true;
}
bool TestRecipeReplayer::ExecuteRunCommandAction(
const base::DictionaryValue& action) {
// Extract the list of JavaScript commands into a vector.
std::vector<std::string> commands;
const base::Value* commands_list_container = action.FindKey("commands");
if (!commands_list_container) {
ADD_FAILURE()
<< "Failed to extract the list of commands from the run command "
<< "action!";
return false;
}
if (base::Value::Type::LIST != commands_list_container->type()) {
ADD_FAILURE() << "commands is not an array!";
return false;
}
const base::Value::ListStorage& commands_list =
commands_list_container->GetList();
for (base::ListValue::const_iterator it_command = commands_list.begin();
it_command != commands_list.end(); ++it_command) {
if (base::Value::Type::STRING != it_command->type()) {
ADD_FAILURE() << "command is not a string!";
return false;
}
commands.push_back(it_command->GetString());
}
content::RenderFrameHost* frame;
if (!GetTargetFrameFromAction(action, &frame)) {
return false;
}
VLOG(1) << "Running JavaScript commands on the page.";
// Execute the commands.
PageActivityObserver page_activity_observer(frame);
for (const std::string& command : commands) {
if (!content::ExecuteScript(frame, command)) {
ADD_FAILURE() << "Failed to execute JavaScript command `" << command
<< "`!";
return false;
}
// Wait in case the JavaScript command triggers page load or layout
// changes.
page_activity_observer.WaitTillPageIsIdle();
}
return true;
}
bool TestRecipeReplayer::ExecuteSelectDropdownAction( bool TestRecipeReplayer::ExecuteSelectDropdownAction(
const base::DictionaryValue& action) { const base::DictionaryValue& action) {
const base::Value* index_container = action.FindKey("index"); const base::Value* index_container = action.FindKey("index");
...@@ -657,6 +810,58 @@ bool TestRecipeReplayer::ExecuteTypeAction( ...@@ -657,6 +810,58 @@ bool TestRecipeReplayer::ExecuteTypeAction(
return true; return true;
} }
bool TestRecipeReplayer::ExecuteTypePasswordAction(
const base::DictionaryValue& action) {
std::string xpath;
if (!GetTargetHTMLElementXpathFromAction(action, &xpath))
return false;
content::RenderFrameHost* frame;
if (!GetTargetFrameFromAction(action, &frame))
return false;
if (!WaitForElementToBeReady(frame, xpath))
return false;
const base::Value* value_container = action.FindKey("value");
if (!value_container) {
ADD_FAILURE() << "Failed to extract the value from the type password"
<< " action!";
return false;
}
if (base::Value::Type::STRING != value_container->type()) {
ADD_FAILURE() << "Value is not a string!";
return false;
}
std::string value = value_container->GetString();
// Clear the password field first, in case a previous value is there.
if (!ExecuteJavaScriptOnElementByXpath(
frame, xpath,
"automation_helper.setInputElementValue(target, ``);")) {
ADD_FAILURE() << "Failed to execute JavaScript to clear the input value!";
return false;
}
if (!PlaceFocusOnElement(frame, xpath))
return false;
VLOG(1) << "Typing '" << value << "' inside `" << xpath << "`.";
const char* c_array = value.c_str();
for (size_t index = 0; index < value.size(); index++) {
ui::DomKey key = ui::DomKey::FromCharacter(c_array[index]);
ui::KeyboardCode key_code = ui::NonPrintableDomKeyToKeyboardCode(key);
ui::DomCode code = ui::UsLayoutKeyboardCodeToDomCode(key_code);
SimulateKeyPress(content::WebContents::FromRenderFrameHost(frame), key,
code, key_code, false, false, false, false);
}
return true;
}
bool TestRecipeReplayer::ExecuteValidateFieldValueAction( bool TestRecipeReplayer::ExecuteValidateFieldValueAction(
const base::DictionaryValue& action) { const base::DictionaryValue& action) {
std::string xpath; std::string xpath;
...@@ -986,9 +1191,66 @@ bool TestRecipeReplayer::PlaceFocusOnElement(content::RenderFrameHost* frame, ...@@ -986,9 +1191,66 @@ bool TestRecipeReplayer::PlaceFocusOnElement(content::RenderFrameHost* frame,
} }
} }
bool TestRecipeReplayer::GetCenterCoordinateOfTargetElement(
content::RenderFrameHost* frame,
const std::string& target_element_xpath,
int& x,
int& y) {
const std::string get_target_field_x_js(base::StringPrintf(
"window.domAutomationController.send("
" (function() {"
" try {"
" const element = automation_helper.getElementByXpath(`%s`);"
" const rect = element.getBoundingClientRect();"
" console.log(`Window href x: ${location.href}`);"
" return Math.floor(rect.left + rect.width / 2);"
" } catch(ex) {}"
" return -1;"
" })());",
target_element_xpath.c_str()));
const std::string get_target_field_y_js(base::StringPrintf(
"window.domAutomationController.send("
" (function() {"
" try {"
" const element = automation_helper.getElementByXpath(`%s`);"
" const rect = element.getBoundingClientRect();"
" console.log(`Window href y: ${location.href}`);"
" return Math.floor(rect.top + rect.height / 2);"
" } catch(ex) {}"
" return -1;"
" })());",
target_element_xpath.c_str()));
if (!content::ExecuteScriptAndExtractInt(frame, get_target_field_x_js, &x)) {
ADD_FAILURE()
<< "Failed to run script to extract target element's x coordinate!";
return false;
}
if (x == -1) {
ADD_FAILURE() << "Failed to extract target element's x coordinate!";
return false;
}
if (!content::ExecuteScriptAndExtractInt(frame, get_target_field_y_js, &y)) {
ADD_FAILURE()
<< "Failed to run script to extract target element's y coordinate!";
return false;
}
if (y == -1) {
ADD_FAILURE() << "Failed to extract target element's y coordinate!";
return false;
}
return true;
}
bool TestRecipeReplayer::SimulateLeftMouseClickAt( bool TestRecipeReplayer::SimulateLeftMouseClickAt(
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
const gfx::Point& point) { const gfx::Point& point) {
if (!SimulateMouseHoverAt(render_frame_host, point))
return false;
blink::WebMouseEvent mouse_event( blink::WebMouseEvent mouse_event(
blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers, blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests()); blink::WebInputEvent::GetStaticTimeStampForTests());
...@@ -1005,20 +1267,37 @@ bool TestRecipeReplayer::SimulateLeftMouseClickAt( ...@@ -1005,20 +1267,37 @@ bool TestRecipeReplayer::SimulateLeftMouseClickAt(
content::RenderWidgetHost* widget = content::RenderWidgetHost* widget =
render_frame_host->GetView()->GetRenderWidgetHost(); render_frame_host->GetView()->GetRenderWidgetHost();
gfx::Point reset_mouse(offset.origin()); widget->ForwardMouseEvent(mouse_event);
reset_mouse = mouse_event.SetType(blink::WebInputEvent::kMouseUp);
gfx::Point(reset_mouse.x() + point.x(), reset_mouse.y() + point.y()); widget->ForwardMouseEvent(mouse_event);
return true;
}
bool TestRecipeReplayer::SimulateMouseHoverAt(
content::RenderFrameHost* render_frame_host,
const gfx::Point& point) {
gfx::Rect offset =
content::WebContents::FromRenderFrameHost(render_frame_host)
->GetContainerBounds();
gfx::Point reset_mouse =
gfx::Point(offset.x() + point.x(), offset.y() + point.y());
if (!ui_test_utils::SendMouseMoveSync(reset_mouse)) { if (!ui_test_utils::SendMouseMoveSync(reset_mouse)) {
ADD_FAILURE() << "Failed to position the mouse!"; ADD_FAILURE() << "Failed to position the mouse!";
return false; return false;
} }
widget->ForwardMouseEvent(mouse_event);
mouse_event.SetType(blink::WebInputEvent::kMouseUp);
widget->ForwardMouseEvent(mouse_event);
return true; return true;
} }
void TestRecipeReplayer::NavigateAwayAndDismissBeforeUnloadDialog() {
content::PrepContentsForBeforeUnloadTest(GetWebContents());
ui_test_utils::NavigateToURLWithDisposition(
browser(), GURL(url::kAboutBlankURL), WindowOpenDisposition::CURRENT_TAB,
ui_test_utils::BROWSER_TEST_NONE);
app_modal::JavaScriptAppModalDialog* alert =
ui_test_utils::WaitForAppModalDialog();
alert->native_dialog()->AcceptAppModalDialog();
}
// TestRecipeReplayChromeFeatureActionExecutor -------------------------------- // TestRecipeReplayChromeFeatureActionExecutor --------------------------------
TestRecipeReplayChromeFeatureActionExecutor:: TestRecipeReplayChromeFeatureActionExecutor::
TestRecipeReplayChromeFeatureActionExecutor() {} TestRecipeReplayChromeFeatureActionExecutor() {}
......
...@@ -27,6 +27,10 @@ namespace captured_sites_test_utils { ...@@ -27,6 +27,10 @@ namespace captured_sites_test_utils {
// has expired for the page to load or for the page to respond to the last // has expired for the page to load or for the page to respond to the last
// user action. // user action.
const base::TimeDelta default_action_timeout = base::TimeDelta::FromSeconds(30); const base::TimeDelta default_action_timeout = base::TimeDelta::FromSeconds(30);
// The amount of time to wait for a page to trigger a paint in response to a
// an ation. The Captured Site Automation Framework uses this timeout to
// break out of a wait loop after a hover action.
const base::TimeDelta visual_update_timeout = base::TimeDelta::FromSeconds(5);
std::string FilePathToUTF8(const base::FilePath::StringType& str); std::string FilePathToUTF8(const base::FilePath::StringType& str);
...@@ -68,6 +72,9 @@ class PageActivityObserver : public content::WebContentsObserver { ...@@ -68,6 +72,9 @@ class PageActivityObserver : public content::WebContentsObserver {
// finished loading the page. // finished loading the page.
void WaitTillPageIsIdle( void WaitTillPageIsIdle(
base::TimeDelta continuous_paint_timeout = default_action_timeout); base::TimeDelta continuous_paint_timeout = default_action_timeout);
// Wait until Chrome makes at least 1 visual update, or until timeout
// expires.
bool WaitForVisualUpdate(base::TimeDelta timeout = visual_update_timeout);
private: private:
// PageActivityObserver determines if Chrome stopped painting by checking if // PageActivityObserver determines if Chrome stopped painting by checking if
...@@ -185,11 +192,19 @@ class TestRecipeReplayer { ...@@ -185,11 +192,19 @@ class TestRecipeReplayer {
static void SetUpCommandLine(base::CommandLine* command_line); static void SetUpCommandLine(base::CommandLine* command_line);
static bool PlaceFocusOnElement(content::RenderFrameHost* frame, static bool PlaceFocusOnElement(content::RenderFrameHost* frame,
const std::string& element_xpath); const std::string& element_xpath);
static bool GetCenterCoordinateOfTargetElement(
content::RenderFrameHost* frame,
const std::string& target_element_xpath,
int& x,
int& y);
static bool SimulateLeftMouseClickAt( static bool SimulateLeftMouseClickAt(
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
const gfx::Point& point); const gfx::Point& point);
static bool SimulateMouseHoverAt(content::RenderFrameHost* render_frame_host,
const gfx::Point& point);
private: private:
Browser* browser();
TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor(); TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor();
content::WebContents* GetWebContents(); content::WebContents* GetWebContents();
void CleanupSiteData(); void CleanupSiteData();
...@@ -209,8 +224,12 @@ class TestRecipeReplayer { ...@@ -209,8 +224,12 @@ class TestRecipeReplayer {
std::unique_ptr<base::DictionaryValue>& recipe); std::unique_ptr<base::DictionaryValue>& recipe);
bool ExecuteAutofillAction(const base::DictionaryValue& action); bool ExecuteAutofillAction(const base::DictionaryValue& action);
bool ExecuteClickAction(const base::DictionaryValue& action); bool ExecuteClickAction(const base::DictionaryValue& action);
bool ExecuteHoverAction(const base::DictionaryValue& action);
bool ExecutePressEnterAction(const base::DictionaryValue& action);
bool ExecuteRunCommandAction(const base::DictionaryValue& action);
bool ExecuteSelectDropdownAction(const base::DictionaryValue& action); bool ExecuteSelectDropdownAction(const base::DictionaryValue& action);
bool ExecuteTypeAction(const base::DictionaryValue& action); bool ExecuteTypeAction(const base::DictionaryValue& action);
bool ExecuteTypePasswordAction(const base::DictionaryValue& action);
bool ExecuteValidateFieldValueAction(const base::DictionaryValue& action); bool ExecuteValidateFieldValueAction(const base::DictionaryValue& action);
bool ExecuteWaitForStateAction(const base::DictionaryValue& action); bool ExecuteWaitForStateAction(const base::DictionaryValue& action);
bool GetTargetHTMLElementXpathFromAction(const base::DictionaryValue& action, bool GetTargetHTMLElementXpathFromAction(const base::DictionaryValue& action,
...@@ -236,6 +255,7 @@ class TestRecipeReplayer { ...@@ -236,6 +255,7 @@ class TestRecipeReplayer {
const std::string& get_property_function_body, const std::string& get_property_function_body,
const std::string& expected_value, const std::string& expected_value,
const bool ignoreCase = false); const bool ignoreCase = false);
void NavigateAwayAndDismissBeforeUnloadDialog();
Browser* browser_; Browser* browser_;
TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor_; TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor_;
......
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