blob: 32d969b917a0ee4852348505abe4500f0d64a9ae [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <memory>
#include <vector>
#include "base/json/json_reader.h"
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/testing/stub_window.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/javascript_engine.h"
#include "cobalt/webdriver/script_executor.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::DefaultValue;
using ::testing::Return;
namespace cobalt {
namespace webdriver {
namespace {
class MockElementMapping : public ElementMapping {
public:
MOCK_METHOD1(ElementToId,
protocol::ElementId(const scoped_refptr<dom::Element>&));
MOCK_METHOD1(IdToElement,
scoped_refptr<dom::Element>(const protocol::ElementId& id));
};
class MockScriptExecutorResult : public ScriptExecutorResult::ResultHandler {
public:
MOCK_METHOD1(OnResult, void(const std::string&));
MOCK_METHOD0(OnTimeout, void());
};
class JSONScriptExecutorResult : public ScriptExecutorResult::ResultHandler {
public:
void OnResult(const std::string& result) {
json_result_ = base::JSONReader::Read(result.c_str());
}
void OnTimeout() { NOTREACHED(); }
base::Value* json_result() { return json_result_.get(); }
private:
std::unique_ptr<base::Value> json_result_;
};
class ScriptExecutorTest : public ::testing::Test {
protected:
void SetUp() override {
stub_window_.reset(new dom::testing::StubWindow());
script_executor_ =
ScriptExecutor::Create(&element_mapping_, global_environment());
ON_CALL(element_mapping_, IdToElement(_))
.WillByDefault(Return(scoped_refptr<dom::Element>()));
ON_CALL(element_mapping_, ElementToId(_))
.WillByDefault(Return(protocol::ElementId("bad-id")));
}
scoped_refptr<dom::Window> window() { return stub_window_->window(); }
scoped_refptr<script::GlobalEnvironment> global_environment() {
return stub_window_->global_environment();
}
protected:
std::unique_ptr<dom::testing::StubWindow> stub_window_;
MockElementMapping element_mapping_;
scoped_refptr<ScriptExecutor> script_executor_;
};
} // namespace
TEST_F(ScriptExecutorTest, CreateSyncScript) {
auto gc_prevented_params =
ScriptExecutorParams::Create(global_environment(), "return 5;", "[]");
ASSERT_TRUE(gc_prevented_params.params);
EXPECT_NE(
reinterpret_cast<intptr_t>(gc_prevented_params.params->function_object()),
NULL);
EXPECT_STREQ(gc_prevented_params.params->json_args().c_str(), "[]");
EXPECT_EQ(gc_prevented_params.params->async_timeout(), base::nullopt);
}
TEST_F(ScriptExecutorTest, CreateAsyncScript) {
auto gc_prevented_params =
ScriptExecutorParams::Create(global_environment(), "return 5;", "[]",
base::TimeDelta::FromMilliseconds(5));
ASSERT_TRUE(gc_prevented_params.params);
EXPECT_NE(
reinterpret_cast<intptr_t>(gc_prevented_params.params->function_object()),
NULL);
EXPECT_STREQ(gc_prevented_params.params->json_args().c_str(), "[]");
EXPECT_EQ(gc_prevented_params.params->async_timeout(), 5);
}
TEST_F(ScriptExecutorTest, CreateInvalidScript) {
auto gc_prevented_params =
ScriptExecutorParams::Create(global_environment(), "retarn 5ish;", "[]");
ASSERT_TRUE(gc_prevented_params.params);
EXPECT_EQ(
reinterpret_cast<intptr_t>(gc_prevented_params.params->function_object()),
NULL);
}
TEST_F(ScriptExecutorTest, ExecuteSync) {
auto gc_prevented_params = ScriptExecutorParams::Create(
global_environment(), "return \"retval\";", "[]");
ASSERT_TRUE(gc_prevented_params.params);
MockScriptExecutorResult result_handler;
EXPECT_CALL(result_handler, OnResult(std::string("\"retval\"")));
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
}
TEST_F(ScriptExecutorTest, FLAKY_ExecuteAsync) {
// Create a script that will call the async callback after 50 ms, with
// an async timeout of 100 ms.
auto gc_prevented_params = ScriptExecutorParams::Create(
global_environment(),
"var callback = arguments[0];"
"window.setTimeout(function() { callback(72); }, 50);",
"[]", base::TimeDelta::FromMilliseconds(100));
ASSERT_TRUE(gc_prevented_params.params);
MockScriptExecutorResult result_handler;
EXPECT_CALL(result_handler, OnResult(std::string("72")));
EXPECT_CALL(result_handler, OnTimeout()).Times(0);
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
// Let the message loop run for 200ms to allow enough time for the async
// script to fire the callback.
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(200));
run_loop.Run();
}
TEST_F(ScriptExecutorTest, AsyncTimeout) {
// Create a script that will call the async callback after 10 seconds, with
// an async timeout of 100 ms.
auto gc_prevented_params = ScriptExecutorParams::Create(
global_environment(),
"var callback = arguments[0];"
"window.setTimeout(function() { callback(72); }, 10000);",
"[]", base::TimeDelta::FromMilliseconds(100));
ASSERT_TRUE(gc_prevented_params.params);
MockScriptExecutorResult result_handler;
EXPECT_CALL(result_handler, OnResult(_)).Times(0);
EXPECT_CALL(result_handler, OnTimeout()).Times(1);
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
// Let the message loop run for 200ms to allow enough time for the async
// timeout to fire.
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(200));
run_loop.Run();
}
TEST_F(ScriptExecutorTest, ScriptThrowsException) {
auto gc_prevented_params =
ScriptExecutorParams::Create(global_environment(), "throw Error()", "[]");
ASSERT_TRUE(gc_prevented_params.params);
MockScriptExecutorResult result_handler;
EXPECT_FALSE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
}
TEST_F(ScriptExecutorTest, ConvertBoolean) {
auto gc_prevented_params = ScriptExecutorParams::Create(
global_environment(), "return arguments[0];", "[true]");
ASSERT_TRUE(gc_prevented_params.params);
MockScriptExecutorResult result_handler;
EXPECT_CALL(result_handler, OnResult(std::string("true")));
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
gc_prevented_params = ScriptExecutorParams::Create(
global_environment(), "return arguments[0];", "[false]");
ASSERT_TRUE(gc_prevented_params.params);
EXPECT_CALL(result_handler, OnResult(std::string("false")));
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
}
TEST_F(ScriptExecutorTest, ConvertNull) {
auto gc_prevented_params = ScriptExecutorParams::Create(
global_environment(), "return arguments[0];", "[null]");
ASSERT_TRUE(gc_prevented_params.params);
MockScriptExecutorResult result_handler;
EXPECT_CALL(result_handler, OnResult(std::string("null")));
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
}
TEST_F(ScriptExecutorTest, ConvertNumericType) {
auto gc_prevented_params = ScriptExecutorParams::Create(
global_environment(), "return arguments[0];", "[6]");
ASSERT_TRUE(gc_prevented_params.params);
MockScriptExecutorResult result_handler;
EXPECT_CALL(result_handler, OnResult(std::string("6")));
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
gc_prevented_params = ScriptExecutorParams::Create(
global_environment(), "return arguments[0];", "[-6.4]");
ASSERT_TRUE(gc_prevented_params.params);
EXPECT_CALL(result_handler, OnResult(std::string("-6.4")));
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
}
TEST_F(ScriptExecutorTest, ConvertString) {
auto gc_prevented_params = ScriptExecutorParams::Create(
global_environment(), "return arguments[0];", "[\"Mr. T\"]");
ASSERT_TRUE(gc_prevented_params.params);
MockScriptExecutorResult result_handler;
EXPECT_CALL(result_handler, OnResult(std::string("\"Mr. T\"")));
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
}
TEST_F(ScriptExecutorTest, ConvertWebElement) {
// Create a dom::Element for the MockElementMapping to return.
scoped_refptr<dom::Element> element =
window()->document()->CreateElement("p");
EXPECT_CALL(element_mapping_, IdToElement(protocol::ElementId("id123")))
.WillRepeatedly(Return(element));
EXPECT_CALL(element_mapping_, ElementToId(element))
.WillRepeatedly(Return(protocol::ElementId("id123")));
// Create a script that will pass a web element argument as a parameter, and
// return it back. This will invoke the lookup to and from an id.
auto gc_prevented_params =
ScriptExecutorParams::Create(global_environment(), "return arguments[0];",
"[ {\"ELEMENT\": \"id123\"} ]");
ASSERT_TRUE(gc_prevented_params.params);
// Execute the script and parse the result as JSON, ensuring we got the same
// web element.
JSONScriptExecutorResult result_handler;
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
ASSERT_TRUE(result_handler.json_result());
std::string element_id;
base::DictionaryValue* dictionary_value;
ASSERT_TRUE(result_handler.json_result()->GetAsDictionary(&dictionary_value));
EXPECT_TRUE(dictionary_value->GetString(protocol::ElementId::kElementKey,
&element_id));
EXPECT_STREQ(element_id.c_str(), "id123");
}
TEST_F(ScriptExecutorTest, ConvertArray) {
// Create a script that takes an array of numbers as input, and returns an
// array of those numbers incremented by one.
auto gc_prevented_params = ScriptExecutorParams::Create(
global_environment(),
"return [ (arguments[0][0]+1), (arguments[0][1]+1) ];", "[ [5, 6] ]");
ASSERT_TRUE(gc_prevented_params.params);
JSONScriptExecutorResult result_handler;
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
ASSERT_TRUE(result_handler.json_result());
base::ListValue* list_value;
ASSERT_TRUE(result_handler.json_result()->GetAsList(&list_value));
ASSERT_EQ(list_value->GetSize(), 2);
int value;
EXPECT_TRUE(list_value->GetInteger(0, &value));
EXPECT_EQ(value, 6);
EXPECT_TRUE(list_value->GetInteger(1, &value));
EXPECT_EQ(value, 7);
}
TEST_F(ScriptExecutorTest, ConvertObject) {
// Create a script that takes an Object with two properties as input, and
// returns an Object with one property that is the sum of the other Object's
// properties.
auto gc_prevented_params = ScriptExecutorParams::Create(
global_environment(),
"return {\"sum\": arguments[0].a + arguments[0].b};",
"[ {\"a\":5, \"b\":6} ]");
ASSERT_TRUE(gc_prevented_params.params);
JSONScriptExecutorResult result_handler;
EXPECT_TRUE(
script_executor_->Execute(gc_prevented_params.params, &result_handler));
ASSERT_TRUE(result_handler.json_result());
int value;
base::DictionaryValue* dictionary_value;
ASSERT_TRUE(result_handler.json_result()->GetAsDictionary(&dictionary_value));
EXPECT_TRUE(dictionary_value->GetInteger("sum", &value));
EXPECT_EQ(value, 11);
}
} // namespace webdriver
} // namespace cobalt