Commit e9d88424 authored by Sergei Datsenko's avatar Sergei Datsenko Committed by Commit Bot

Headless render tests.

Test redirects with PUT.
Test redirect URL edgecases.

Bug: chromium:787197
Change-Id: I663516828741282f3a6364d71a4fc822a3814045
Reviewed-on: https://chromium-review.googlesource.com/804713
Commit-Queue: Sergei Datsenko <dats@chromium.org>
Reviewed-by: default avatarAlex Clarke <alexclarke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521910}
parent ec018e32
...@@ -21,14 +21,18 @@ class TestInMemoryProtocolHandler::MockURLFetcher : public URLFetcher { ...@@ -21,14 +21,18 @@ class TestInMemoryProtocolHandler::MockURLFetcher : public URLFetcher {
void StartFetch(const Request* request, void StartFetch(const Request* request,
ResultListener* result_listener) override { ResultListener* result_listener) override {
GURL url = request->GetURLRequest()->url(); GURL url = request->GetURLRequest()->url();
if (request->GetURLRequest()->method() == "POST") { const std::string& method = request->GetURLRequest()->method();
if (method == "POST" || method == "PUT") {
request->GetPostData(); request->GetPostData();
} else if (method == "GET") {
// Do nothing.
} else { } else {
DCHECK_EQ("GET", request->GetURLRequest()->method()); DCHECK(false) << "Method " << method << " is not supported. Probably.";
} }
std::string devtools_frame_id = request->GetDevToolsFrameId(); std::string devtools_frame_id = request->GetDevToolsFrameId();
DCHECK_NE(devtools_frame_id, "") << " For url " << url; DCHECK_NE(devtools_frame_id, "") << " For url " << url;
protocol_handler_->methods_requested_.push_back(method);
protocol_handler_->RegisterUrl(url.spec(), devtools_frame_id); protocol_handler_->RegisterUrl(url.spec(), devtools_frame_id);
if (protocol_handler_->request_deferrer()) { if (protocol_handler_->request_deferrer()) {
......
...@@ -65,6 +65,10 @@ class TestInMemoryProtocolHandler ...@@ -65,6 +65,10 @@ class TestInMemoryProtocolHandler
return urls_requested_; return urls_requested_;
} }
const std::vector<std::string>& methods_requested() const {
return methods_requested_;
}
private: private:
const Response* GetResponse(const std::string& url) const; const Response* GetResponse(const std::string& url) const;
...@@ -86,6 +90,7 @@ class TestInMemoryProtocolHandler ...@@ -86,6 +90,7 @@ class TestInMemoryProtocolHandler
HeadlessBrowserContext* headless_browser_context_; HeadlessBrowserContext* headless_browser_context_;
std::map<std::string, std::string> url_to_devtools_frame_id_; std::map<std::string, std::string> url_to_devtools_frame_id_;
std::vector<std::string> urls_requested_; std::vector<std::string> urls_requested_;
std::vector<std::string> methods_requested_;
RequestDeferrer* request_deferrer_; // NOT OWNED. RequestDeferrer* request_deferrer_; // NOT OWNED.
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
......
...@@ -255,13 +255,8 @@ class JavaScriptOverrideTitle_JsDisabled ...@@ -255,13 +255,8 @@ class JavaScriptOverrideTitle_JsDisabled
}; };
HEADLESS_RENDER_BROWSERTEST(JavaScriptOverrideTitle_JsDisabled); HEADLESS_RENDER_BROWSERTEST(JavaScriptOverrideTitle_JsDisabled);
class JavaScriptConsoleErrors : public HeadlessRenderTest, class JavaScriptConsoleErrors : public HeadlessRenderTest {
public runtime::ExperimentalObserver {
private: private:
HeadlessDevToolsClient* client_;
std::vector<std::string> messages_;
bool log_called_ = false;
GURL GetPageUrl(HeadlessDevToolsClient* client) override { GURL GetPageUrl(HeadlessDevToolsClient* client) override {
GetProtocolHandler()->InsertResponse(SOME_URL, HttpOk(R"|( GetProtocolHandler()->InsertResponse(SOME_URL, HttpOk(R"|(
<html> <html>
...@@ -287,34 +282,12 @@ class JavaScriptConsoleErrors : public HeadlessRenderTest, ...@@ -287,34 +282,12 @@ class JavaScriptConsoleErrors : public HeadlessRenderTest,
</body> </body>
</html> </html>
)|")); )|"));
client_ = client;
client_->GetRuntime()->GetExperimental()->AddObserver(this);
base::RunLoop run_loop;
client_->GetRuntime()->GetExperimental()->Enable(run_loop.QuitClosure());
base::MessageLoop::ScopedNestableTaskAllower nest_loop(
base::MessageLoop::current());
run_loop.Run();
return GURL(SOME_URL); return GURL(SOME_URL);
} }
void OnConsoleAPICalled(
const runtime::ConsoleAPICalledParams& params) override {
EXPECT_THAT(*params.GetArgs(), ElementsAre(RemoteString("Hello, Script!")));
log_called_ = true;
}
void OnExceptionThrown(
const runtime::ExceptionThrownParams& params) override {
const runtime::ExceptionDetails* details = params.GetExceptionDetails();
messages_.push_back(details->GetText() + " " +
details->GetException()->GetDescription());
}
void VerifyDom(GetSnapshotResult* dom_snapshot) override { void VerifyDom(GetSnapshotResult* dom_snapshot) override {
client_->GetRuntime()->GetExperimental()->Disable(); EXPECT_THAT(console_log_, ElementsAre("L Hello, Script!"));
client_->GetRuntime()->GetExperimental()->RemoveObserver(this); EXPECT_THAT(js_exceptions_,
EXPECT_TRUE(log_called_);
EXPECT_THAT(messages_,
ElementsAre(StartsWith("Uncaught SyntaxError:"), ElementsAre(StartsWith("Uncaught SyntaxError:"),
StartsWith("Uncaught ReferenceError: func1"), StartsWith("Uncaught ReferenceError: func1"),
StartsWith("Uncaught ReferenceError: func2"), StartsWith("Uncaught ReferenceError: func2"),
...@@ -759,7 +732,7 @@ class RedirectAfterCompletion : public HeadlessRenderTest { ...@@ -759,7 +732,7 @@ class RedirectAfterCompletion : public HeadlessRenderTest {
}; };
HEADLESS_RENDER_BROWSERTEST(RedirectAfterCompletion); HEADLESS_RENDER_BROWSERTEST(RedirectAfterCompletion);
class RedirectPostMethod : public HeadlessRenderTest { class Redirect307PostMethod : public HeadlessRenderTest {
private: private:
GURL GetPageUrl(HeadlessDevToolsClient* client) override { GURL GetPageUrl(HeadlessDevToolsClient* client) override {
GetProtocolHandler()->InsertResponse("http://www.example.com/", HttpOk(R"|( GetProtocolHandler()->InsertResponse("http://www.example.com/", HttpOk(R"|(
...@@ -783,11 +756,225 @@ class RedirectPostMethod : public HeadlessRenderTest { ...@@ -783,11 +756,225 @@ class RedirectPostMethod : public HeadlessRenderTest {
GetProtocolHandler()->urls_requested(), GetProtocolHandler()->urls_requested(),
ElementsAre("http://www.example.com/", "http://www.example.com/1", ElementsAre("http://www.example.com/", "http://www.example.com/1",
"http://www.example.com/2")); "http://www.example.com/2"));
EXPECT_THAT(GetProtocolHandler()->methods_requested(),
ElementsAre("GET", "POST", "POST"));
EXPECT_THAT(NextNode(dom_snapshot, FindTag(dom_snapshot, "P")), EXPECT_THAT(NextNode(dom_snapshot, FindTag(dom_snapshot, "P")),
NodeValue("Pass")); NodeValue("Pass"));
} }
}; };
HEADLESS_RENDER_BROWSERTEST(RedirectPostMethod); HEADLESS_RENDER_BROWSERTEST(Redirect307PostMethod);
class RedirectPostChain : public HeadlessRenderTest {
private:
GURL GetPageUrl(HeadlessDevToolsClient* client) override {
GetProtocolHandler()->InsertResponse("http://www.example.com/", HttpOk(R"|(
<html>
<body onload='document.forms[0].submit();'>
<form action='1' method='post'>
<input name='foo' value='bar'>
</form>
</body>
</html>
)|"));
GetProtocolHandler()->InsertResponse("http://www.example.com/1",
HttpRedirect(307, "/2"));
GetProtocolHandler()->InsertResponse("http://www.example.com/2", HttpOk(R"|(
<html>
<body onload='document.forms[0].submit();'>
<form action='3' method='post'>
</form>
</body>
</html>
)|"));
GetProtocolHandler()->InsertResponse("http://www.example.com/3",
HttpRedirect(307, "/4"));
GetProtocolHandler()->InsertResponse("http://www.example.com/4",
HttpOk("<p>Pass</p>"));
return GURL("http://www.example.com/");
}
void VerifyDom(GetSnapshotResult* dom_snapshot) override {
EXPECT_THAT(
GetProtocolHandler()->urls_requested(),
ElementsAre("http://www.example.com/", "http://www.example.com/1",
"http://www.example.com/2", "http://www.example.com/3",
"http://www.example.com/4"));
EXPECT_THAT(GetProtocolHandler()->methods_requested(),
ElementsAre("GET", "POST", "POST", "POST", "POST"));
EXPECT_THAT(NextNode(dom_snapshot, FindTag(dom_snapshot, "P")),
NodeValue("Pass"));
}
};
HEADLESS_RENDER_BROWSERTEST(RedirectPostChain);
class Redirect307PutMethod : public HeadlessRenderTest {
private:
GURL GetPageUrl(HeadlessDevToolsClient* client) override {
GetProtocolHandler()->InsertResponse("http://www.example.com/", HttpOk(R"|(
<html>
<head>
<script>
function doPut() {
var xhr = new XMLHttpRequest();
xhr.open('PUT', 'http://www.example.com/1');
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.addEventListener('load', function() {
document.getElementById('content').textContent = this.responseText;
});
xhr.send('some data');
}
</script>
</head>
<body onload='doPut();'>
<p id="content"></p>
</body>
</html>
)|"));
GetProtocolHandler()->InsertResponse("http://www.example.com/1",
HttpRedirect(307, "/2"));
GetProtocolHandler()->InsertResponse("http://www.example.com/2",
{"Pass", "text/plain"});
return GURL("http://www.example.com/");
}
void VerifyDom(GetSnapshotResult* dom_snapshot) override {
EXPECT_THAT(
GetProtocolHandler()->urls_requested(),
ElementsAre("http://www.example.com/", "http://www.example.com/1",
"http://www.example.com/2"));
EXPECT_THAT(GetProtocolHandler()->methods_requested(),
ElementsAre("GET", "PUT", "PUT"));
EXPECT_THAT(NextNode(dom_snapshot, FindTag(dom_snapshot, "P")),
NodeValue("Pass"));
}
};
HEADLESS_RENDER_BROWSERTEST(Redirect307PutMethod);
class Redirect303PutGet : public HeadlessRenderTest {
private:
GURL GetPageUrl(HeadlessDevToolsClient* client) override {
GetProtocolHandler()->InsertResponse("http://www.example.com/", HttpOk(R"|(
<html>
<head>
<script>
function doPut() {
var xhr = new XMLHttpRequest();
xhr.open('PUT', 'http://www.example.com/1');
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.addEventListener('load', function() {
document.getElementById('content').textContent = this.responseText;
});
xhr.send('some data');
}
</script>
</head>
<body onload='doPut();'>
<p id="content"></p>
</body>
</html>
)|"));
GetProtocolHandler()->InsertResponse("http://www.example.com/1",
HttpRedirect(303, "/2"));
GetProtocolHandler()->InsertResponse("http://www.example.com/2",
{"Pass", "text/plain"});
return GURL("http://www.example.com/");
}
void VerifyDom(GetSnapshotResult* dom_snapshot) override {
EXPECT_THAT(
GetProtocolHandler()->urls_requested(),
ElementsAre("http://www.example.com/", "http://www.example.com/1",
"http://www.example.com/2"));
EXPECT_THAT(GetProtocolHandler()->methods_requested(),
ElementsAre("GET", "PUT", "GET"));
EXPECT_THAT(NextNode(dom_snapshot, FindTag(dom_snapshot, "P")),
NodeValue("Pass"));
}
};
HEADLESS_RENDER_BROWSERTEST(Redirect303PutGet);
class RedirectBaseUrl : public HeadlessRenderTest {
private:
GURL GetPageUrl(HeadlessDevToolsClient* client) override {
GetProtocolHandler()->InsertResponse("http://foo.com/",
HttpRedirect(302, "http://bar.com/"));
GetProtocolHandler()->InsertResponse("http://bar.com/",
HttpOk("<img src=\"pass\">"));
return GURL("http://foo.com/");
}
void VerifyDom(GetSnapshotResult* dom_snapshot) override {
EXPECT_THAT(GetProtocolHandler()->urls_requested(),
ElementsAre("http://foo.com/", "http://bar.com/",
"http://bar.com/pass"));
}
};
HEADLESS_RENDER_BROWSERTEST(RedirectBaseUrl);
class RedirectNonAsciiUrl : public HeadlessRenderTest {
private:
GURL GetPageUrl(HeadlessDevToolsClient* client) override {
// "中文" is 0xE4 0xB8 0xAD, 0xE6 0x96 0x87
GetProtocolHandler()->InsertResponse(
"http://www.example.com/",
HttpRedirect(302, "http://www.example.com/中文"));
GetProtocolHandler()->InsertResponse(
"http://www.example.com/%E4%B8%AD%E6%96%87",
HttpRedirect(303, "http://www.example.com/pass#中文"));
GetProtocolHandler()->InsertResponse(
"http://www.example.com/pass#%E4%B8%AD%E6%96%87",
HttpOk("<p>Pass</p>"));
GetProtocolHandler()->InsertResponse(
"http://www.example.com/%C3%A4%C2%B8%C2%AD%C3%A6%C2%96%C2%87",
{"HTTP/1.1 500 Bad Response\r\nContent-Type: text/html\r\n\r\nFail"});
return GURL("http://www.example.com/");
}
void VerifyDom(GetSnapshotResult* dom_snapshot) override {
EXPECT_THAT(GetProtocolHandler()->urls_requested(),
ElementsAre("http://www.example.com/",
"http://www.example.com/%E4%B8%AD%E6%96%87",
"http://www.example.com/pass#%E4%B8%AD%E6%96%87"));
EXPECT_THAT(NextNode(dom_snapshot, FindTag(dom_snapshot, "P")),
NodeValue("Pass"));
}
};
HEADLESS_RENDER_BROWSERTEST(RedirectNonAsciiUrl);
class RedirectEmptyUrl : public HeadlessRenderTest {
private:
GURL GetPageUrl(HeadlessDevToolsClient* client) override {
GetProtocolHandler()->InsertResponse(
"http://www.example.com/",
{"HTTP/1.1 302 Found\r\nLocation: \r\n\r\n<!DOCTYPE html><p>Pass</p>"});
return GURL("http://www.example.com/");
}
void VerifyDom(GetSnapshotResult* dom_snapshot) override {
EXPECT_THAT(GetProtocolHandler()->urls_requested(),
ElementsAre("http://www.example.com/"));
EXPECT_THAT(NextNode(dom_snapshot, FindTag(dom_snapshot, "P")),
NodeValue("Pass"));
}
};
HEADLESS_RENDER_BROWSERTEST(RedirectEmptyUrl);
class RedirectInvalidUrl : public HeadlessRenderTest {
private:
GURL GetPageUrl(HeadlessDevToolsClient* client) override {
GetProtocolHandler()->InsertResponse(
"http://www.example.com/",
{"HTTP/1.1 302 Found\r\nLocation: http://\r\n\r\n"
"<!DOCTYPE html><p>Pass</p>"});
return GURL("http://www.example.com/");
}
void VerifyDom(GetSnapshotResult* dom_snapshot) override {
EXPECT_THAT(GetProtocolHandler()->urls_requested(),
ElementsAre("http://www.example.com/"));
}
};
HEADLESS_RENDER_BROWSERTEST(RedirectInvalidUrl);
class RedirectKeepsFragment : public HeadlessRenderTest { class RedirectKeepsFragment : public HeadlessRenderTest {
private: private:
......
...@@ -38,6 +38,8 @@ void HeadlessRenderTest::RunDevTooledTest() { ...@@ -38,6 +38,8 @@ void HeadlessRenderTest::RunDevTooledTest() {
devtools_client_->GetPage()->GetExperimental()->AddObserver(this); devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
devtools_client_->GetPage()->Enable(Sync()); devtools_client_->GetPage()->Enable(Sync());
devtools_client_->GetRuntime()->GetExperimental()->AddObserver(this);
devtools_client_->GetRuntime()->Enable(Sync());
GURL url = GetPageUrl(devtools_client_.get()); GURL url = GetPageUrl(devtools_client_.get());
...@@ -148,6 +150,70 @@ void HeadlessRenderTest::OnFrameNavigated( ...@@ -148,6 +150,70 @@ void HeadlessRenderTest::OnFrameNavigated(
frames_[params.GetFrame()->GetId()].push_back(params.GetFrame()->Clone()); frames_[params.GetFrame()->GetId()].push_back(params.GetFrame()->Clone());
} }
void HeadlessRenderTest::OnConsoleAPICalled(
const runtime::ConsoleAPICalledParams& params) {
std::stringstream str;
switch (params.GetType()) {
case runtime::ConsoleAPICalledType::WARNING:
str << "W";
break;
case runtime::ConsoleAPICalledType::ASSERT:
case runtime::ConsoleAPICalledType::ERR:
str << "E";
break;
case runtime::ConsoleAPICalledType::DEBUG:
str << "D";
break;
case runtime::ConsoleAPICalledType::INFO:
str << "I";
break;
default:
str << "L";
break;
}
const auto& args = *params.GetArgs();
for (const auto& arg : args) {
str << " ";
if (arg->HasDescription()) {
str << arg->GetDescription();
} else if (arg->GetType() == runtime::RemoteObjectType::UNDEFINED) {
str << "undefined";
} else if (arg->HasValue()) {
const base::Value* v = arg->GetValue();
switch (v->type()) {
case base::Value::Type::NONE:
str << "null";
break;
case base::Value::Type::BOOLEAN:
str << v->GetBool();
break;
case base::Value::Type::INTEGER:
str << v->GetInt();
break;
case base::Value::Type::DOUBLE:
str << v->GetDouble();
break;
case base::Value::Type::STRING:
str << v->GetString();
break;
default:
DCHECK(false);
break;
}
} else {
DCHECK(false);
}
}
console_log_.push_back(str.str());
}
void HeadlessRenderTest::OnExceptionThrown(
const runtime::ExceptionThrownParams& params) {
const runtime::ExceptionDetails* details = params.GetExceptionDetails();
js_exceptions_.push_back(details->GetText() + " " +
details->GetException()->GetDescription());
}
void HeadlessRenderTest::OnRequest(const GURL& url, void HeadlessRenderTest::OnRequest(const GURL& url,
base::Closure complete_request) { base::Closure complete_request) {
complete_request.Run(); complete_request.Run();
...@@ -177,15 +243,24 @@ void HeadlessRenderTest::OnGetDomSnapshotDone( ...@@ -177,15 +243,24 @@ void HeadlessRenderTest::OnGetDomSnapshotDone(
std::unique_ptr<dom_snapshot::GetSnapshotResult> result) { std::unique_ptr<dom_snapshot::GetSnapshotResult> result) {
CHECK_EQ(DONE, state_); CHECK_EQ(DONE, state_);
state_ = FINISHED; state_ = FINISHED;
CleanUp();
FinishAsynchronousTest(); FinishAsynchronousTest();
VerifyDom(result.get()); VerifyDom(result.get());
} }
void HeadlessRenderTest::HandleTimeout() { void HeadlessRenderTest::HandleTimeout() {
if (state_ != FINISHED) { if (state_ != FINISHED) {
CleanUp();
FinishAsynchronousTest(); FinishAsynchronousTest();
OnTimeout(); OnTimeout();
} }
} }
void HeadlessRenderTest::CleanUp() {
devtools_client_->GetRuntime()->Disable(Sync());
devtools_client_->GetRuntime()->GetExperimental()->RemoveObserver(this);
devtools_client_->GetPage()->Disable(Sync());
devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
}
} // namespace headless } // namespace headless
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "headless/public/devtools/domains/emulation.h" #include "headless/public/devtools/domains/emulation.h"
#include "headless/public/devtools/domains/page.h" #include "headless/public/devtools/domains/page.h"
#include "headless/public/devtools/domains/runtime.h"
#include "headless/public/headless_browser.h" #include "headless/public/headless_browser.h"
#include "headless/public/headless_browser_context.h" #include "headless/public/headless_browser_context.h"
#include "headless/public/util/testing/test_in_memory_protocol_handler.h" #include "headless/public/util/testing/test_in_memory_protocol_handler.h"
...@@ -32,6 +33,7 @@ class GetSnapshotResult; ...@@ -32,6 +33,7 @@ class GetSnapshotResult;
class HeadlessRenderTest : public HeadlessAsyncDevTooledBrowserTest, class HeadlessRenderTest : public HeadlessAsyncDevTooledBrowserTest,
public HeadlessBrowserContext::Observer, public HeadlessBrowserContext::Observer,
public page::ExperimentalObserver, public page::ExperimentalObserver,
public runtime::ExperimentalObserver,
public TestInMemoryProtocolHandler::RequestDeferrer { public TestInMemoryProtocolHandler::RequestDeferrer {
public: public:
typedef std::pair<std::string, page::FrameScheduledNavigationReason> Redirect; typedef std::pair<std::string, page::FrameScheduledNavigationReason> Redirect;
...@@ -100,6 +102,11 @@ class HeadlessRenderTest : public HeadlessAsyncDevTooledBrowserTest, ...@@ -100,6 +102,11 @@ class HeadlessRenderTest : public HeadlessAsyncDevTooledBrowserTest,
const page::FrameClearedScheduledNavigationParams& params) override; const page::FrameClearedScheduledNavigationParams& params) override;
void OnFrameNavigated(const page::FrameNavigatedParams& params) override; void OnFrameNavigated(const page::FrameNavigatedParams& params) override;
// runtime::ExperimentalObserver implementation:
void OnConsoleAPICalled(
const runtime::ConsoleAPICalledParams& params) override;
void OnExceptionThrown(const runtime::ExceptionThrownParams& params) override;
// TestInMemoryProtocolHandler::RequestDeferrer // TestInMemoryProtocolHandler::RequestDeferrer
void OnRequest(const GURL& url, base::Closure complete_request) override; void OnRequest(const GURL& url, base::Closure complete_request) override;
...@@ -107,12 +114,15 @@ class HeadlessRenderTest : public HeadlessAsyncDevTooledBrowserTest, ...@@ -107,12 +114,15 @@ class HeadlessRenderTest : public HeadlessAsyncDevTooledBrowserTest,
std::map<std::string, Redirect> unconfirmed_frame_redirects_; std::map<std::string, Redirect> unconfirmed_frame_redirects_;
std::map<std::string, std::vector<std::unique_ptr<page::Frame>>> frames_; std::map<std::string, std::vector<std::unique_ptr<page::Frame>>> frames_;
std::string main_frame_; std::string main_frame_;
std::vector<std::string> console_log_;
std::vector<std::string> js_exceptions_;
private: private:
void HandleVirtualTimeExhausted(); void HandleVirtualTimeExhausted();
void OnGetDomSnapshotDone( void OnGetDomSnapshotDone(
std::unique_ptr<dom_snapshot::GetSnapshotResult> result); std::unique_ptr<dom_snapshot::GetSnapshotResult> result);
void HandleTimeout(); void HandleTimeout();
void CleanUp();
enum State { enum State {
INIT, // Setting up the client, no navigation performed yet. INIT, // Setting up the client, no navigation performed yet.
......
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