blob: 63e93708d75e4f1137ce38e3aecbfcc6b4da9240 [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 "base/bind.h"
#include "base/memory/ref_counted.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/string_conversions_util.h"
#include "components/autofill_assistant/browser/web_controller.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
using ::testing::IsEmpty;
const char* kTargetWebsitePath = "/autofill_assistant_target_website.html";
class WebControllerBrowserTest : public content::ContentBrowserTest,
public content::WebContentsObserver {
public:
WebControllerBrowserTest() {}
~WebControllerBrowserTest() override {}
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
http_server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTP);
http_server_->ServeFilesFromSourceDirectory(
"components/test/data/autofill_assistant");
ASSERT_TRUE(http_server_->Start());
ASSERT_TRUE(
NavigateToURL(shell(), http_server_->GetURL(kTargetWebsitePath)));
web_controller_ =
WebController::CreateForWebContents(shell()->web_contents());
Observe(shell()->web_contents());
}
void DidCommitAndDrawCompositorFrame() override {
paint_occurred_during_last_loop_ = true;
}
void WaitTillPageIsIdle(base::TimeDelta continuous_paint_timeout) {
base::TimeTicks finished_load_time = base::TimeTicks::Now();
bool page_is_loading = false;
do {
paint_occurred_during_last_loop_ = false;
base::RunLoop heart_beat;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, heart_beat.QuitClosure(), base::TimeDelta::FromSeconds(3));
heart_beat.Run();
page_is_loading =
web_contents()->IsWaitingForResponse() || web_contents()->IsLoading();
if (page_is_loading) {
finished_load_time = base::TimeTicks::Now();
} else if ((base::TimeTicks::Now() - finished_load_time) >
continuous_paint_timeout) {
// |continuous_paint_timeout| has expired since Chrome loaded the page.
// During this period of time, Chrome has been continuously painting
// the page. In this case, the page is probably idle, but a bug, a
// blinking caret or a persistent animation is making Chrome paint at
// regular intervals. Exit.
break;
}
} while (page_is_loading || paint_occurred_during_last_loop_);
}
void RunStrictElementCheck(const Selector& selector, bool result) {
RunElementCheck(/* strict= */ true, selector, result);
}
void RunLaxElementCheck(const Selector& selector, bool result) {
RunElementCheck(/* strict= */ false, selector, result);
}
void RunElementCheck(bool strict, const Selector& selector, bool result) {
std::vector<Selector> selectors{selector};
std::vector<bool> results{result};
RunElementChecks(strict, selectors, results);
}
void RunElementChecks(bool strict,
const std::vector<Selector>& selectors,
const std::vector<bool> results) {
base::RunLoop run_loop;
ASSERT_EQ(selectors.size(), results.size());
size_t pending_number_of_checks = selectors.size();
for (size_t i = 0; i < selectors.size(); i++) {
web_controller_->ElementCheck(
selectors[i], strict,
base::BindOnce(&WebControllerBrowserTest::CheckElementVisibleCallback,
base::Unretained(this), run_loop.QuitClosure(),
selectors[i], &pending_number_of_checks, results[i]));
}
run_loop.Run();
}
void CheckElementVisibleCallback(const base::Closure& done_callback,
const Selector& selector,
size_t* pending_number_of_checks_output,
bool expected_result,
bool result) {
EXPECT_EQ(expected_result, result) << "selector: " << selector;
*pending_number_of_checks_output -= 1;
if (*pending_number_of_checks_output == 0) {
done_callback.Run();
}
}
void ClickElement(const Selector& selector) {
base::RunLoop run_loop;
web_controller_->ClickElement(
selector,
base::BindOnce(&WebControllerBrowserTest::ClickElementCallback,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
void ClickElementCallback(const base::Closure& done_callback,
const ClientStatus& status) {
EXPECT_EQ(ACTION_APPLIED, status.proto_status());
done_callback.Run();
}
void TapElement(const Selector& selector) {
base::RunLoop run_loop;
web_controller_->TapElement(
selector,
base::BindOnce(&WebControllerBrowserTest::ClickElementCallback,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
void TapElementCallback(const base::Closure& done_callback, bool result) {
ASSERT_TRUE(result);
done_callback.Run();
}
void WaitForElementRemove(const Selector& selector) {
base::RunLoop run_loop;
web_controller_->ElementCheck(
selector, /* strict= */ false,
base::BindOnce(&WebControllerBrowserTest::OnWaitForElementRemove,
base::Unretained(this), run_loop.QuitClosure(),
selector));
run_loop.Run();
}
void OnWaitForElementRemove(const base::Closure& done_callback,
const Selector& selector,
bool result) {
done_callback.Run();
if (result) {
WaitForElementRemove(selector);
}
}
void FocusElement(const Selector& selector) {
base::RunLoop run_loop;
web_controller_->FocusElement(
selector,
base::BindOnce(&WebControllerBrowserTest::OnFocusElement,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
void OnFocusElement(base::OnceClosure done_callback,
const ClientStatus& status) {
EXPECT_EQ(ACTION_APPLIED, status.proto_status());
std::move(done_callback).Run();
}
ClientStatus SelectOption(const Selector& selector,
const std::string& option) {
base::RunLoop run_loop;
ClientStatus result;
web_controller_->SelectOption(
selector, option,
base::BindOnce(&WebControllerBrowserTest::OnSelectOption,
base::Unretained(this), run_loop.QuitClosure(),
&result));
run_loop.Run();
return result;
}
void OnSelectOption(base::Closure done_callback,
ClientStatus* result_output,
const ClientStatus& status) {
*result_output = status;
std::move(done_callback).Run();
}
ClientStatus HighlightElement(const Selector& selector) {
base::RunLoop run_loop;
ClientStatus result;
web_controller_->HighlightElement(
selector, base::BindOnce(&WebControllerBrowserTest::OnHighlightElement,
base::Unretained(this), run_loop.QuitClosure(),
&result));
run_loop.Run();
return result;
}
void OnHighlightElement(base::Closure done_callback,
ClientStatus* result_output,
const ClientStatus& status) {
*result_output = status;
std::move(done_callback).Run();
}
ClientStatus GetOuterHtml(const Selector& selector,
std::string* html_output) {
base::RunLoop run_loop;
ClientStatus result;
web_controller_->GetOuterHtml(
selector, base::BindOnce(&WebControllerBrowserTest::OnGetOuterHtml,
base::Unretained(this), run_loop.QuitClosure(),
&result, html_output));
run_loop.Run();
return result;
}
void OnGetOuterHtml(const base::Closure& done_callback,
ClientStatus* successful_output,
std::string* html_output,
const ClientStatus& status,
const std::string& html) {
EXPECT_EQ(ACTION_APPLIED, status.proto_status());
*successful_output = status;
*html_output = html;
done_callback.Run();
}
void FindElement(const Selector& selector,
ClientStatus* status_out,
WebController::FindElementResult* result_out) {
base::RunLoop run_loop;
web_controller_->FindElement(
selector, /* strict_mode= */ true,
base::BindOnce(&WebControllerBrowserTest::OnFindElement,
base::Unretained(this), run_loop.QuitClosure(),
base::Unretained(status_out),
base::Unretained(result_out)));
run_loop.Run();
}
void OnFindElement(const base::Closure& done_callback,
ClientStatus* status_out,
WebController::FindElementResult* result_out,
const ClientStatus& status,
std::unique_ptr<WebController::FindElementResult> result) {
ASSERT_TRUE(result);
done_callback.Run();
if (status_out)
*status_out = status;
if (result_out)
*result_out = *result;
}
void FindElementAndCheck(const Selector& selector,
size_t expected_index,
bool is_main_frame) {
SCOPED_TRACE(::testing::Message() << selector << " strict");
ClientStatus status;
WebController::FindElementResult result;
FindElement(selector, &status, &result);
EXPECT_EQ(ACTION_APPLIED, status.proto_status());
CheckFindElementResult(result, expected_index, is_main_frame);
}
void FindElementExpectEmptyResult(const Selector& selector) {
SCOPED_TRACE(::testing::Message() << selector << " strict");
ClientStatus status;
WebController::FindElementResult result;
FindElement(selector, &status, &result);
EXPECT_EQ(ELEMENT_RESOLUTION_FAILED, status.proto_status());
EXPECT_THAT(result.object_id, IsEmpty());
}
void CheckFindElementResult(const WebController::FindElementResult& result,
size_t expected_index,
bool is_main_frame) {
if (is_main_frame) {
EXPECT_EQ(shell()->web_contents()->GetMainFrame(),
result.container_frame_host);
} else {
EXPECT_NE(shell()->web_contents()->GetMainFrame(),
result.container_frame_host);
}
EXPECT_EQ(result.container_frame_selector_index, expected_index);
EXPECT_FALSE(result.object_id.empty());
}
void GetFieldsValue(const std::vector<Selector>& selectors,
const std::vector<std::string>& expected_values) {
base::RunLoop run_loop;
ASSERT_EQ(selectors.size(), expected_values.size());
size_t pending_number_of_checks = selectors.size();
for (size_t i = 0; i < selectors.size(); i++) {
web_controller_->GetFieldValue(
selectors[i],
base::BindOnce(&WebControllerBrowserTest::OnGetFieldValue,
base::Unretained(this), run_loop.QuitClosure(),
&pending_number_of_checks, expected_values[i]));
}
run_loop.Run();
}
void OnGetFieldValue(const base::Closure& done_callback,
size_t* pending_number_of_checks_output,
const std::string& expected_value,
bool exists,
const std::string& value) {
// Don't use ASSERT_EQ here: if the check fails, this would result in
// an endless loop without meaningful test results.
EXPECT_EQ(expected_value, value);
*pending_number_of_checks_output -= 1;
if (*pending_number_of_checks_output == 0) {
std::move(done_callback).Run();
}
}
ClientStatus SetFieldValue(const Selector& selector,
const std::string& value,
bool simulate_key_presses) {
base::RunLoop run_loop;
ClientStatus result;
web_controller_->SetFieldValue(
selector, value, simulate_key_presses, 0,
base::BindOnce(&WebControllerBrowserTest::OnSetFieldValue,
base::Unretained(this), run_loop.QuitClosure(),
&result));
run_loop.Run();
return result;
}
void OnSetFieldValue(const base::Closure& done_callback,
ClientStatus* result_output,
const ClientStatus& status) {
*result_output = status;
std::move(done_callback).Run();
}
ClientStatus SendKeyboardInput(const Selector& selector,
const std::vector<UChar32>& codepoints,
int delay_in_milli) {
base::RunLoop run_loop;
ClientStatus result;
web_controller_->SendKeyboardInput(
selector, codepoints, delay_in_milli,
base::BindOnce(&WebControllerBrowserTest::OnSendKeyboardInput,
base::Unretained(this), run_loop.QuitClosure(),
&result));
run_loop.Run();
return result;
}
ClientStatus SendKeyboardInput(const Selector& selector,
const std::vector<UChar32>& codepoints) {
return SendKeyboardInput(selector, codepoints, -1);
}
void OnSendKeyboardInput(const base::Closure& done_callback,
ClientStatus* result_output,
const ClientStatus& status) {
*result_output = status;
std::move(done_callback).Run();
}
ClientStatus SetAttribute(const Selector& selector,
const std::vector<std::string>& attribute,
const std::string& value) {
base::RunLoop run_loop;
ClientStatus result;
web_controller_->SetAttribute(
selector, attribute, value,
base::BindOnce(&WebControllerBrowserTest::OnSetAttribute,
base::Unretained(this), run_loop.QuitClosure(),
&result));
run_loop.Run();
return result;
}
void OnSetAttribute(const base::Closure& done_callback,
ClientStatus* result_output,
const ClientStatus& status) {
*result_output = status;
std::move(done_callback).Run();
}
bool HasCookie() {
base::RunLoop run_loop;
bool result;
web_controller_->HasCookie(base::BindOnce(
&WebControllerBrowserTest::OnCookieResult, base::Unretained(this),
run_loop.QuitClosure(), &result));
run_loop.Run();
return result;
}
bool SetCookie() {
base::RunLoop run_loop;
bool result;
web_controller_->SetCookie(
"http://example.com",
base::BindOnce(&WebControllerBrowserTest::OnCookieResult,
base::Unretained(this), run_loop.QuitClosure(),
&result));
run_loop.Run();
return result;
}
void OnCookieResult(const base::Closure& done_callback,
bool* result_output,
bool result) {
*result_output = result;
std::move(done_callback).Run();
}
protected:
std::unique_ptr<WebController> web_controller_;
private:
std::unique_ptr<net::EmbeddedTestServer> http_server_;
bool paint_occurred_during_last_loop_ = false;
DISALLOW_COPY_AND_ASSIGN(WebControllerBrowserTest);
};
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ElementExistenceCheck) {
// A visible element
RunLaxElementCheck(Selector({"#button"}), true);
// A hidden element.
RunLaxElementCheck(Selector({"#hidden"}), true);
// A nonexistent element.
RunLaxElementCheck(Selector({"#doesnotexist"}), false);
// A pseudo-element
RunLaxElementCheck(Selector({"#terms-and-conditions"}, BEFORE), true);
// An invisible pseudo-element
//
// TODO(b/129461999): This is wrong; it should exist. Fix it.
RunLaxElementCheck(Selector({"#button"}, BEFORE), false);
// A non-existent pseudo-element
RunLaxElementCheck(Selector({"#button"}, AFTER), false);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, VisibilityRequirementCheck) {
// A visible element
RunLaxElementCheck(Selector({"#button"}).MustBeVisible(), true);
// A hidden element.
RunLaxElementCheck(Selector({"#hidden"}).MustBeVisible(), false);
// A non-existent element
RunLaxElementCheck(Selector({"#doesnotexist"}).MustBeVisible(), false);
// A pseudo-element
RunLaxElementCheck(
Selector({"#terms-and-conditions"}, BEFORE).MustBeVisible(), true);
// An invisible pseudo-element
RunLaxElementCheck(Selector({"#button"}, BEFORE).MustBeVisible(), false);
// A non-existent pseudo-element
RunLaxElementCheck(Selector({"#button"}, AFTER).MustBeVisible(), false);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, MultipleVisibleElementCheck) {
// both visible
RunLaxElementCheck(Selector({"#button,#select"}).MustBeVisible(), true);
RunStrictElementCheck(Selector({"#button,#select"}).MustBeVisible(), false);
// one visible (first non-visible)
RunLaxElementCheck(Selector({"#hidden,#select"}).MustBeVisible(), true);
RunStrictElementCheck(Selector({"#hidden,#select"}).MustBeVisible(), true);
// one visible (first visible)
RunLaxElementCheck(Selector({"#button,#hidden"}).MustBeVisible(), true);
RunStrictElementCheck(Selector({"#hidden,#select"}).MustBeVisible(), true);
// one invisible, one non-existent
RunLaxElementCheck(Selector({"#doesnotexist,#hidden"}).MustBeVisible(),
false);
RunStrictElementCheck(Selector({"#doesnotexist,#hidden"}).MustBeVisible(),
false);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, InnerTextCondition) {
Selector selector({"#with_inner_text span"});
selector.must_be_visible = true;
RunLaxElementCheck(selector, true);
RunStrictElementCheck(selector.MustBeVisible(), false);
// No matches
selector.inner_text_pattern = "no match";
selector.must_be_visible = false;
RunLaxElementCheck(selector, false);
selector.must_be_visible = true;
RunLaxElementCheck(selector, false);
// Matches exactly one visible element.
selector.inner_text_pattern = "hello, world";
selector.must_be_visible = false;
RunLaxElementCheck(selector, true);
RunStrictElementCheck(selector, true);
selector.must_be_visible = true;
RunLaxElementCheck(selector, true);
RunStrictElementCheck(selector, true);
// Matches two visible elements
selector.inner_text_pattern = "^hello";
selector.must_be_visible = false;
RunLaxElementCheck(selector, true);
RunStrictElementCheck(selector, false);
selector.must_be_visible = true;
RunLaxElementCheck(selector, true);
RunStrictElementCheck(selector, false);
// Matches one visible, one invisible element
selector.inner_text_pattern = "world$";
selector.must_be_visible = false;
RunLaxElementCheck(selector, true);
RunStrictElementCheck(selector, false);
selector.must_be_visible = true;
RunLaxElementCheck(selector, true);
RunStrictElementCheck(selector, true);
// Inner text conditions are applied before looking for the pseudo-type.
selector.pseudo_type = PseudoType::BEFORE;
selector.inner_text_pattern = "world";
selector.must_be_visible = false;
RunLaxElementCheck(selector, true);
selector.inner_text_pattern = "before"; // matches :before content
RunLaxElementCheck(selector, false);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ValueCondition) {
// One match
RunLaxElementCheck(Selector({"#input1"}).MatchingValue("helloworld1"), true);
RunStrictElementCheck(Selector({"#input1"}).MatchingValue("helloworld1"),
true);
// No matches
RunLaxElementCheck(Selector({"#input2"}).MatchingValue("doesnotmatch"),
false);
RunStrictElementCheck(Selector({"#input2"}).MatchingValue("doesnotmatch"),
false);
// Multiple matches
RunLaxElementCheck(Selector({"#input1,#input2"}).MatchingValue("^hello"),
true);
RunStrictElementCheck(Selector({"#input1,#input2"}).MatchingValue("^hello"),
false);
// Multiple selector matches, one value match
RunLaxElementCheck(Selector({"#input1,#input2"}).MatchingValue("helloworld1"),
true);
RunStrictElementCheck(
Selector({"#input1,#input2"}).MatchingValue("helloworld1"), true);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
ConcurrentElementsVisibilityCheck) {
std::vector<Selector> selectors;
std::vector<bool> results;
Selector a_selector;
a_selector.must_be_visible = true;
a_selector.selectors.emplace_back("#button");
selectors.emplace_back(a_selector);
results.emplace_back(true);
a_selector.selectors.emplace_back("#whatever");
selectors.emplace_back(a_selector);
results.emplace_back(false);
// IFrame.
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#iframe");
a_selector.selectors.emplace_back("#button");
selectors.emplace_back(a_selector);
results.emplace_back(true);
a_selector.selectors.emplace_back("#whatever");
selectors.emplace_back(a_selector);
results.emplace_back(false);
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#iframe");
a_selector.selectors.emplace_back("[name=name]");
selectors.emplace_back(a_selector);
results.emplace_back(true);
// Shadow DOM.
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#iframe");
a_selector.selectors.emplace_back("#shadowsection");
a_selector.selectors.emplace_back("#shadowbutton");
selectors.emplace_back(a_selector);
results.emplace_back(true);
a_selector.selectors.emplace_back("#whatever");
selectors.emplace_back(a_selector);
results.emplace_back(false);
// IFrame inside IFrame.
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#iframe");
a_selector.selectors.emplace_back("#iframe");
a_selector.selectors.emplace_back("#button");
selectors.emplace_back(a_selector);
results.emplace_back(true);
a_selector.selectors.emplace_back("#whatever");
selectors.emplace_back(a_selector);
results.emplace_back(false);
// Hidden element.
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#hidden");
selectors.emplace_back(a_selector);
results.emplace_back(false);
RunElementChecks(/* strict= */ false, selectors, results);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ClickElement) {
Selector selector;
selector.selectors.emplace_back("#button");
ClickElement(selector);
WaitForElementRemove(selector);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
ClickElementInIFrame) {
Selector selector;
selector.selectors.emplace_back("#iframe");
selector.selectors.emplace_back("#shadowsection");
selector.selectors.emplace_back("#shadowbutton");
ClickElement(selector);
selector.selectors.clear();
selector.selectors.emplace_back("#iframe");
selector.selectors.emplace_back("#button");
WaitForElementRemove(selector);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, TapElement) {
Selector selector;
selector.selectors.emplace_back("#touch_area_two");
TapElement(selector);
WaitForElementRemove(selector);
selector.selectors.clear();
selector.selectors.emplace_back("#touch_area_one");
TapElement(selector);
WaitForElementRemove(selector);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, TapElementMovingOutOfView) {
Selector selector;
selector.selectors.emplace_back("#touch_area_three");
TapElement(selector);
WaitForElementRemove(selector);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, TapElementAfterPageIsIdle) {
// Set a very long timeout to make sure either the page is idle or the test
// timeout.
WaitTillPageIsIdle(base::TimeDelta::FromHours(1));
Selector selector;
selector.selectors.emplace_back("#touch_area_one");
TapElement(selector);
WaitForElementRemove(selector);
}
// TODO(crbug.com/920948) Disabled for strong flakiness.
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, DISABLED_TapElementInIFrame) {
Selector selector;
selector.selectors.emplace_back("#iframe");
selector.selectors.emplace_back("#touch_area");
TapElement(selector);
WaitForElementRemove(selector);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ClickPseudoElement) {
const std::string javascript = R"(
document.querySelector("#terms-and-conditions").checked;
)";
EXPECT_FALSE(content::EvalJs(shell(), javascript).ExtractBool());
Selector selector({R"(label[for="terms-and-conditions"])"},
PseudoType::BEFORE);
ClickElement(selector);
EXPECT_TRUE(content::EvalJs(shell(), javascript).ExtractBool());
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FindElement) {
Selector selector;
selector.selectors.emplace_back("#button");
FindElementAndCheck(selector, 0, true);
selector.must_be_visible = true;
FindElementAndCheck(selector, 0, true);
// IFrame.
selector.selectors.clear();
selector.selectors.emplace_back("#iframe");
selector.selectors.emplace_back("#button");
selector.must_be_visible = false;
FindElementAndCheck(selector, 0, false);
selector.must_be_visible = true;
FindElementAndCheck(selector, 0, false);
selector.selectors.clear();
selector.selectors.emplace_back("#iframe");
selector.selectors.emplace_back("[name=name]");
selector.must_be_visible = false;
FindElementAndCheck(selector, 0, false);
selector.must_be_visible = true;
FindElementAndCheck(selector, 0, false);
// IFrame inside IFrame.
selector.selectors.clear();
selector.selectors.emplace_back("#iframe");
selector.selectors.emplace_back("#iframe");
selector.selectors.emplace_back("#button");
selector.must_be_visible = false;
FindElementAndCheck(selector, 1, false);
selector.must_be_visible = true;
FindElementAndCheck(selector, 1, false);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FindElementNotFound) {
FindElementExpectEmptyResult(Selector({"#notfound"}));
FindElementExpectEmptyResult(Selector({"#hidden"}).MustBeVisible());
FindElementExpectEmptyResult(Selector({"#iframe", "#iframe", "#notfound"}));
FindElementExpectEmptyResult(
Selector({"#iframe", "#iframe", "#hidden"}).MustBeVisible());
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FindElementErrorStatus) {
ClientStatus status;
FindElement(
Selector(ElementReferenceProto::default_instance()).MustBeVisible(),
&status, nullptr);
EXPECT_EQ(INVALID_SELECTOR, status.proto_status());
FindElement(Selector({"#doesnotexist"}).MustBeVisible(), &status, nullptr);
EXPECT_EQ(ELEMENT_RESOLUTION_FAILED, status.proto_status());
FindElement(Selector({"div"}), &status, nullptr);
EXPECT_EQ(TOO_MANY_ELEMENTS, status.proto_status());
FindElement(Selector({"div"}).MustBeVisible(), &status, nullptr);
EXPECT_EQ(TOO_MANY_ELEMENTS, status.proto_status());
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FocusElement) {
Selector selector;
selector.selectors.emplace_back("#iframe");
selector.selectors.emplace_back("#focus");
// The element is not visible initially.
const std::string checkNotVisibleScript = R"(
let iframe = document.querySelector("#iframe");
let div = iframe.contentDocument.querySelector("#focus");
let iframeRect = iframe.getBoundingClientRect();
let divRect = div.getBoundingClientRect();
iframeRect.y + divRect.y > window.innerHeight;
)";
EXPECT_EQ(true, content::EvalJs(shell(), checkNotVisibleScript));
FocusElement(selector);
// Verify that the scroll moved the div in the iframe into view.
const std::string checkVisibleScript = R"(
const scrollTimeoutMs = 500;
var timer = null;
function check() {
let iframe = document.querySelector("#iframe");
let div = iframe.contentDocument.querySelector("#focus");
let iframeRect = iframe.getBoundingClientRect();
let divRect = div.getBoundingClientRect();
return iframeRect.y + divRect.y < window.innerHeight;
}
function onScrollDone() {
window.removeEventListener("scroll", onScroll);
domAutomationController.send(check());
}
function onScroll(e) {
if (timer != null) {
clearTimeout(timer);
}
timer = setTimeout(onScrollDone, scrollTimeoutMs);
}
if (check()) {
// Scrolling finished before this script started. Just return the result.
domAutomationController.send(true);
} else {
window.addEventListener("scroll", onScroll);
}
)";
EXPECT_EQ(true, content::EvalJsWithManualReply(shell(), checkVisibleScript));
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SelectOption) {
Selector selector;
selector.selectors.emplace_back("#select");
EXPECT_EQ(OPTION_VALUE_NOT_FOUND,
SelectOption(selector, "incorrect_label").proto_status());
EXPECT_EQ(ACTION_APPLIED, SelectOption(selector, "two").proto_status());
const std::string javascript = R"(
let select = document.querySelector("#select");
select.options[select.selectedIndex].label;
)";
EXPECT_EQ("Two", content::EvalJs(shell(), javascript));
EXPECT_EQ(ACTION_APPLIED, SelectOption(selector, "one").proto_status());
EXPECT_EQ("One", content::EvalJs(shell(), javascript));
selector.selectors.clear();
selector.selectors.emplace_back("#incorrect_selector");
EXPECT_EQ(ELEMENT_RESOLUTION_FAILED,
SelectOption(selector, "two").proto_status());
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SelectOptionInIframe) {
Selector selector;
selector.selectors.emplace_back("#iframe");
selector.selectors.emplace_back("select[name=state]");
EXPECT_EQ(ACTION_APPLIED, SelectOption(selector, "NY").proto_status());
const std::string javascript = R"(
let iframe = document.querySelector("iframe").contentDocument;
let select = iframe.querySelector("select[name=state]");
select.options[select.selectedIndex].label;
)";
EXPECT_EQ("NY", content::EvalJs(shell(), javascript));
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, GetOuterHtml) {
Selector selector;
selector.selectors.emplace_back("#testOuterHtml");
std::string html;
ASSERT_EQ(ACTION_APPLIED, GetOuterHtml(selector, &html).proto_status());
EXPECT_EQ(
R"(<div id="testOuterHtml"><span>Span</span><p>Paragraph</p></div>)",
html);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, GetAndSetFieldValue) {
std::vector<Selector> selectors;
std::vector<std::string> expected_values;
Selector a_selector;
a_selector.selectors.emplace_back("#input1");
selectors.emplace_back(a_selector);
expected_values.emplace_back("helloworld1");
GetFieldsValue(selectors, expected_values);
EXPECT_EQ(ACTION_APPLIED,
SetFieldValue(a_selector, "foo", /* simulate_key_presses= */ false)
.proto_status());
expected_values.clear();
expected_values.emplace_back("foo");
GetFieldsValue(selectors, expected_values);
selectors.clear();
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#uppercase_input");
selectors.emplace_back(a_selector);
EXPECT_EQ(ACTION_APPLIED,
SetFieldValue(a_selector, /* Zürich */ "Z\xc3\xbcrich",
/* simulate_key_presses= */ true)
.proto_status());
expected_values.clear();
expected_values.emplace_back(/* ZÜRICH */ "Z\xc3\x9cRICH");
GetFieldsValue(selectors, expected_values);
selectors.clear();
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#invalid_selector");
selectors.emplace_back(a_selector);
expected_values.clear();
expected_values.emplace_back("");
GetFieldsValue(selectors, expected_values);
EXPECT_EQ(
ELEMENT_RESOLUTION_FAILED,
SetFieldValue(a_selector, "foobar", /* simulate_key_presses= */ false)
.proto_status());
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SendKeyboardInput) {
auto input = UTF8ToUnicode("Zürich");
std::string expected_output = "Zürich";
std::vector<Selector> selectors;
Selector a_selector;
a_selector.selectors.emplace_back("#input6");
selectors.emplace_back(a_selector);
EXPECT_EQ(ACTION_APPLIED,
SendKeyboardInput(a_selector, input).proto_status());
GetFieldsValue(selectors, {expected_output});
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
SendKeyboardInputSetsKeyProperty) {
auto input = UTF8ToUnicode("Zürich\r");
std::string expected_output = "ZürichEnter";
std::vector<Selector> selectors;
Selector a_selector;
a_selector.selectors.emplace_back("#input_js_event_listener");
selectors.emplace_back(a_selector);
EXPECT_EQ(ACTION_APPLIED,
SendKeyboardInput(a_selector, input).proto_status());
GetFieldsValue(selectors, {expected_output});
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
SendKeyboardInputSetsKeyPropertyWithTimeout) {
// Sends input keys to a field where JS will intercept KeyDown and
// at index 3 it will set a timeout whick inserts an space after the
// timeout. If key press delay is enabled, it should handle this
// correctly.
auto input = UTF8ToUnicode("012345");
std::string expected_output = "012 345";
std::vector<Selector> selectors;
Selector a_selector;
a_selector.selectors.emplace_back("#input_js_event_with_timeout");
selectors.emplace_back(a_selector);
EXPECT_EQ(ACTION_APPLIED,
SendKeyboardInput(a_selector, input, /*delay_in_milli*/ 100)
.proto_status());
GetFieldsValue(selectors, {expected_output});
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SetAttribute) {
Selector selector;
std::vector<std::string> attribute;
selector.selectors.emplace_back("#full_height_section");
attribute.emplace_back("style");
attribute.emplace_back("backgroundColor");
std::string value = "red";
EXPECT_EQ(ACTION_APPLIED,
SetAttribute(selector, attribute, value).proto_status());
const std::string javascript = R"(
document.querySelector("#full_height_section").style.backgroundColor;
)";
EXPECT_EQ(value, content::EvalJs(shell(), javascript));
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ConcurrentGetFieldsValue) {
std::vector<Selector> selectors;
std::vector<std::string> expected_values;
Selector a_selector;
a_selector.selectors.emplace_back("#input1");
selectors.emplace_back(a_selector);
expected_values.emplace_back("helloworld1");
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#input2");
selectors.emplace_back(a_selector);
expected_values.emplace_back("helloworld2");
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#input3");
selectors.emplace_back(a_selector);
expected_values.emplace_back("helloworld3");
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#input4");
selectors.emplace_back(a_selector);
expected_values.emplace_back("helloworld4");
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#input5");
selectors.emplace_back(a_selector);
expected_values.emplace_back("helloworld5");
GetFieldsValue(selectors, expected_values);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, NavigateToUrl) {
EXPECT_EQ(kTargetWebsitePath,
shell()->web_contents()->GetLastCommittedURL().path());
web_controller_->LoadURL(GURL(url::kAboutBlankURL));
WaitForLoadStop(shell()->web_contents());
EXPECT_EQ(url::kAboutBlankURL,
shell()->web_contents()->GetLastCommittedURL().spec());
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, HighlightElement) {
Selector selector;
selector.selectors.emplace_back("#select");
const std::string javascript = R"(
let select = document.querySelector("#select");
select.style.boxShadow;
)";
EXPECT_EQ("", content::EvalJs(shell(), javascript));
EXPECT_EQ(ACTION_APPLIED, HighlightElement(selector).proto_status());
// We only make sure that the element has a non-empty boxShadow style without
// requiring an exact string match.
EXPECT_NE("", content::EvalJs(shell(), javascript));
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, GetAndSetCookie) {
EXPECT_FALSE(HasCookie());
EXPECT_TRUE(SetCookie());
}
} // namespace