blob: 3c0fdec85c71b6fcdc47620690d6b4310bb1010f [file] [log] [blame]
// 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 "extensions/renderer/api_binding_js_util.h"
#include "base/bind.h"
#include "extensions/renderer/api_binding_test_util.h"
#include "extensions/renderer/api_bindings_system.h"
#include "extensions/renderer/api_bindings_system_unittest.h"
#include "gin/handle.h"
namespace extensions {
class APIBindingJSUtilUnittest : public APIBindingsSystemTest {
protected:
APIBindingJSUtilUnittest() {}
~APIBindingJSUtilUnittest() override {}
gin::Handle<APIBindingJSUtil> CreateUtil() {
return gin::CreateHandle(
isolate(),
new APIBindingJSUtil(bindings_system()->type_reference_map(),
bindings_system()->request_handler(),
bindings_system()->event_handler(),
base::Bind(&RunFunctionOnGlobalAndIgnoreResult)));
}
v8::Local<v8::Object> GetLastErrorParent(
v8::Local<v8::Context> context) override {
return context->Global();
}
std::string GetExposedError(v8::Local<v8::Context> context) {
v8::Local<v8::Value> last_error =
GetPropertyFromObject(context->Global(), context, "lastError");
// Use ADD_FAILURE() to avoid messing up the return type with ASSERT.
if (last_error.IsEmpty()) {
ADD_FAILURE();
return std::string();
}
if (!last_error->IsObject() && !last_error->IsUndefined()) {
ADD_FAILURE();
return std::string();
}
if (last_error->IsUndefined())
return "[undefined]";
return GetStringPropertyFromObject(last_error.As<v8::Object>(), context,
"message");
}
APILastError* last_error() {
return bindings_system()->request_handler()->last_error();
}
private:
DISALLOW_COPY_AND_ASSIGN(APIBindingJSUtilUnittest);
};
TEST_F(APIBindingJSUtilUnittest, TestSetLastError) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
gin::Handle<APIBindingJSUtil> util = CreateUtil();
v8::Local<v8::Object> v8_util = util.ToV8().As<v8::Object>();
EXPECT_FALSE(last_error()->HasError(context));
EXPECT_EQ("[undefined]", GetExposedError(context));
const char kSetLastError[] = "obj.setLastError('a last error');";
CallFunctionOnObject(context, v8_util, kSetLastError);
EXPECT_TRUE(last_error()->HasError(context));
EXPECT_EQ("\"a last error\"", GetExposedError(context));
CallFunctionOnObject(context, v8_util,
"obj.setLastError('a new last error')");
EXPECT_TRUE(last_error()->HasError(context));
EXPECT_EQ("\"a new last error\"", GetExposedError(context));
}
TEST_F(APIBindingJSUtilUnittest, TestHasLastError) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
gin::Handle<APIBindingJSUtil> util = CreateUtil();
v8::Local<v8::Object> v8_util = util.ToV8().As<v8::Object>();
EXPECT_FALSE(last_error()->HasError(context));
EXPECT_EQ("[undefined]", GetExposedError(context));
const char kHasLastError[] = "return obj.hasLastError();";
v8::Local<v8::Value> has_error =
CallFunctionOnObject(context, v8_util, kHasLastError);
EXPECT_EQ("false", V8ToString(has_error, context));
last_error()->SetError(context, "an error");
EXPECT_TRUE(last_error()->HasError(context));
EXPECT_EQ("\"an error\"", GetExposedError(context));
has_error = CallFunctionOnObject(context, v8_util, kHasLastError);
EXPECT_EQ("true", V8ToString(has_error, context));
last_error()->ClearError(context, false);
EXPECT_FALSE(last_error()->HasError(context));
EXPECT_EQ("[undefined]", GetExposedError(context));
has_error = CallFunctionOnObject(context, v8_util, kHasLastError);
EXPECT_EQ("false", V8ToString(has_error, context));
}
TEST_F(APIBindingJSUtilUnittest, TestRunWithLastError) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
gin::Handle<APIBindingJSUtil> util = CreateUtil();
v8::Local<v8::Object> v8_util = util.ToV8().As<v8::Object>();
EXPECT_FALSE(last_error()->HasError(context));
EXPECT_EQ("[undefined]", GetExposedError(context));
const char kRunWithLastError[] =
"obj.runCallbackWithLastError('last error', function() {\n"
" this.exposedLastError =\n"
" this.lastError ? this.lastError.message : 'undefined';\n"
"}, [1, 'foo']);";
CallFunctionOnObject(context, v8_util, kRunWithLastError);
EXPECT_FALSE(last_error()->HasError(context));
EXPECT_EQ("[undefined]", GetExposedError(context));
EXPECT_EQ("\"last error\"",
GetStringPropertyFromObject(context->Global(), context,
"exposedLastError"));
}
TEST_F(APIBindingJSUtilUnittest, TestSendRequestWithOptions) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
gin::Handle<APIBindingJSUtil> util = CreateUtil();
v8::Local<v8::Object> v8_util = util.ToV8().As<v8::Object>();
const char kSendRequestWithNoOptions[] =
"obj.sendRequest('alpha.functionWithCallback',\n"
" ['someString', function() {}], undefined, undefined);";
CallFunctionOnObject(context, v8_util, kSendRequestWithNoOptions);
ASSERT_TRUE(last_request());
EXPECT_EQ("alpha.functionWithCallback", last_request()->method_name);
EXPECT_EQ("[\"someString\"]", ValueToString(*last_request()->arguments));
EXPECT_EQ(binding::RequestThread::UI, last_request()->thread);
reset_last_request();
const char kSendRequestForIOThread[] =
"obj.sendRequest('alpha.functionWithCallback',\n"
" ['someOtherString', function() {}], undefined,\n"
" {__proto__: null, forIOThread: true});";
CallFunctionOnObject(context, v8_util, kSendRequestForIOThread);
ASSERT_TRUE(last_request());
EXPECT_EQ("alpha.functionWithCallback", last_request()->method_name);
EXPECT_EQ("[\"someOtherString\"]", ValueToString(*last_request()->arguments));
EXPECT_EQ(binding::RequestThread::IO, last_request()->thread);
reset_last_request();
const char kSendRequestForUIThread[] =
"obj.sendRequest('alpha.functionWithCallback',\n"
" ['someOtherString', function() {}], undefined,\n"
" {__proto__: null, forIOThread: false});";
CallFunctionOnObject(context, v8_util, kSendRequestForUIThread);
ASSERT_TRUE(last_request());
EXPECT_EQ("alpha.functionWithCallback", last_request()->method_name);
EXPECT_EQ("[\"someOtherString\"]", ValueToString(*last_request()->arguments));
EXPECT_EQ(binding::RequestThread::UI, last_request()->thread);
reset_last_request();
const char kSendRequestWithCustomCallback[] =
R"(obj.sendRequest(
'alpha.functionWithCallback',
['stringy', function() {}], undefined,
{
__proto__: null,
customCallback: function() {
this.callbackCalled = true;
},
});)";
CallFunctionOnObject(context, v8_util, kSendRequestWithCustomCallback);
ASSERT_TRUE(last_request());
EXPECT_EQ("alpha.functionWithCallback", last_request()->method_name);
EXPECT_EQ("[\"stringy\"]", ValueToString(*last_request()->arguments));
EXPECT_EQ(binding::RequestThread::UI, last_request()->thread);
bindings_system()->CompleteRequest(last_request()->request_id,
base::ListValue(), std::string());
EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
"callbackCalled"));
}
} // namespace extensions