Index: headless/test/headless_js_bindings_browsertest.cc |
diff --git a/headless/test/headless_js_bindings_browsertest.cc b/headless/test/headless_js_bindings_browsertest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7b9fad5fb42402f93a397a3a4ce5b924888ae125 |
--- /dev/null |
+++ b/headless/test/headless_js_bindings_browsertest.cc |
@@ -0,0 +1,160 @@ |
+// Copyright 2017 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 <memory> |
+#include <string> |
+#include <vector> |
+ |
+#include "base/base64.h" |
+#include "base/json/json_reader.h" |
+#include "base/path_service.h" |
+#include "base/threading/thread_restrictions.h" |
+#include "content/public/test/browser_test.h" |
+#include "headless/grit/headless_browsertest_resources.h" |
+#include "headless/public/devtools/domains/runtime.h" |
+#include "headless/public/headless_browser.h" |
+#include "headless/public/headless_devtools_client.h" |
+#include "headless/public/headless_tab_socket.h" |
+#include "headless/public/headless_web_contents.h" |
+#include "headless/test/headless_browser_test.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "ui/base/resource/resource_bundle.h" |
+ |
+namespace headless { |
+ |
+class HeadlessJsBindingsTest |
+ : public HeadlessAsyncDevTooledBrowserTest, |
+ public HeadlessTabSocket::Listener, |
+ public HeadlessDevToolsClient::RawProtocolListener { |
+ public: |
+ void SetUp() override { |
+ options()->mojo_service_names.insert("headless::TabSocket"); |
+ HeadlessAsyncDevTooledBrowserTest::SetUp(); |
+ } |
+ |
+ void SetUpOnMainThread() override { |
+ base::ThreadRestrictions::SetIOAllowed(true); |
+ base::FilePath pak_path; |
+ ASSERT_TRUE(PathService::Get(base::DIR_MODULE, &pak_path)); |
+ pak_path = pak_path.AppendASCII("headless_browser_tests.pak"); |
+ ResourceBundle::GetSharedInstance().AddDataPackFromPath( |
+ pak_path, ui::SCALE_FACTOR_NONE); |
+ } |
+ |
+ void RunDevTooledTest() override { |
+ headless_tab_socket_ = web_contents_->GetHeadlessTabSocket(); |
+ DCHECK(headless_tab_socket_); |
+ headless_tab_socket_->SetListener(this); |
+ devtools_client_->SetRawProtocolListener(this); |
+ devtools_client_->GetRuntime()->Evaluate( |
+ ResourceBundle::GetSharedInstance() |
+ .GetRawDataResource(DEVTOOLS_BINDINGS_TEST) |
+ .as_string(), |
+ base::Bind(&HeadlessJsBindingsTest::FailOnJsEvaluateException, |
+ base::Unretained(this))); |
+ RunJsBindingsTest(); |
+ } |
+ |
+ virtual void RunJsBindingsTest() = 0; |
+ virtual std::string GetExpectedResult() = 0; |
+ |
+ void FailOnJsEvaluateException( |
+ std::unique_ptr<runtime::EvaluateResult> result) { |
+ if (!result->HasExceptionDetails()) |
+ return; |
+ |
+ FinishAsynchronousTest(); |
+ |
+ const runtime::ExceptionDetails* exception_details = |
+ result->GetExceptionDetails(); |
+ FAIL() << exception_details->GetText() |
+ << (exception_details->HasException() |
+ ? exception_details->GetException()->GetDescription().c_str() |
+ : ""); |
+ } |
+ |
+ void OnMessageFromTab(const std::string& json_message) override { |
+ std::unique_ptr<base::Value> message = |
+ base::JSONReader::Read(json_message, base::JSON_PARSE_RFC); |
+ const base::DictionaryValue* message_dict; |
+ const base::DictionaryValue* params_dict; |
+ std::string method; |
+ int id; |
+ if (!message || !message->GetAsDictionary(&message_dict) || |
+ !message_dict->GetString("method", &method) || |
+ !message_dict->GetDictionary("params", ¶ms_dict) || |
+ !message_dict->GetInteger("id", &id)) { |
+ FinishAsynchronousTest(); |
+ FAIL() << "Badly formed message " << json_message; |
+ return; |
+ } |
+ |
+ if (method == "__Result") { |
+ std::string result; |
+ params_dict->GetString("result", &result); |
+ EXPECT_EQ(GetExpectedResult(), result); |
+ FinishAsynchronousTest(); |
+ return; |
+ } |
+ |
+ devtools_client_->SendRawDevToolsMessage(json_message); |
+ } |
+ |
+ HeadlessWebContents::Builder::TabSocketType GetTabSocketType() override { |
+ return HeadlessWebContents::Builder::TabSocketType::MAIN_WORLD; |
+ } |
+ |
+ bool OnProtocolMessage(const std::string& devtools_agent_host_id, |
+ const std::string& json_message, |
+ const base::DictionaryValue& parsed_message) override { |
+ int id; |
+ // If |parsed_message| contains an id we know this is a message reply. |
+ if (parsed_message.GetInteger("id", &id)) { |
+ // We are only interested in message replies (ones with an id) where the |
+ // id is odd. The reason is HeadlessDevToolsClientImpl uses even/oddness |
+ // to distinguish between commands send from the C++ bindings and those |
+ // via HeadlessDevToolsClientImpl::SendRawDevToolsMessage. |
+ if ((id % 2) == 0) |
+ return false; |
+ } |
+ |
+ headless_tab_socket_->SendMessageToTab(json_message); |
+ return true; |
+ } |
+ |
+ private: |
+ HeadlessTabSocket* headless_tab_socket_; |
+}; |
+ |
+class SimpleCommandJsBindingsTest : public HeadlessJsBindingsTest { |
+ public: |
+ void RunJsBindingsTest() override { |
+ devtools_client_->GetRuntime()->Evaluate( |
+ "new chromium.BindingsTest().evalOneAddOne();", |
+ base::Bind(&HeadlessJsBindingsTest::FailOnJsEvaluateException, |
+ base::Unretained(this))); |
+ } |
+ |
+ std::string GetExpectedResult() override { return "2"; } |
+}; |
+ |
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(SimpleCommandJsBindingsTest); |
+ |
+class SimpleEventJsBindingsTest : public HeadlessJsBindingsTest { |
+ public: |
+ void RunJsBindingsTest() override { |
+ devtools_client_->GetRuntime()->Evaluate( |
+ "new chromium.BindingsTest().listenForChildNodeCountUpdated();", |
+ base::Bind(&HeadlessJsBindingsTest::FailOnJsEvaluateException, |
+ base::Unretained(this))); |
+ } |
+ |
+ std::string GetExpectedResult() override { |
+ return "{\"nodeId\":4,\"childNodeCount\":1}"; |
+ } |
+}; |
+ |
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(SimpleEventJsBindingsTest); |
+} // namespace headless |