123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628 |
- // Copyright 2016 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/bindings/api_bindings_system_unittest.h"
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/containers/contains.h"
- #include "base/strings/stringprintf.h"
- #include "base/values.h"
- #include "extensions/common/mojom/event_dispatcher.mojom.h"
- #include "extensions/renderer/bindings/api_binding.h"
- #include "extensions/renderer/bindings/api_binding_hooks.h"
- #include "extensions/renderer/bindings/api_binding_hooks_test_delegate.h"
- #include "extensions/renderer/bindings/api_binding_test_util.h"
- #include "extensions/renderer/bindings/api_binding_types.h"
- #include "extensions/renderer/bindings/api_bindings_system.h"
- #include "extensions/renderer/bindings/api_invocation_errors.h"
- #include "extensions/renderer/bindings/test_interaction_provider.h"
- #include "gin/arguments.h"
- #include "gin/converter.h"
- #include "gin/try_catch.h"
- #include "testing/gmock/include/gmock/gmock.h"
- namespace extensions {
- namespace {
- // Fake API for testing.
- const char kAlphaAPIName[] = "alpha";
- const char kAlphaAPISpec[] = R"(
- {
- "types": [{
- "id": "alpha.objRef",
- "type": "object",
- "properties": {
- "prop1": {"type": "string"},
- "prop2": {"type": "integer", "optional": true}
- }
- }, {
- "id": "alpha.enumRef",
- "type": "string",
- "enum": ["cat", "dog"]
- }],
- "functions": [{
- "name": "functionWithCallback",
- "parameters": [{
- "name": "str",
- "type": "string"
- }],
- "returns_async": {
- "name": "callback",
- "parameters": [{"name": "strResult", "type": "string"}]
- }
- }, {
- "name": "functionWithRefAndCallback",
- "parameters": [{
- "name": "ref",
- "$ref": "alpha.objRef"
- }],
- "returns_async": {
- "name": "callback",
- "parameters": []
- }
- }, {
- "name": "functionWithEnum",
- "parameters": [{"name": "e", "$ref": "alpha.enumRef"}]
- }],
- "events": [{
- "name": "alphaEvent",
- "parameters": [{
- "name": "eventArg",
- "type": "object",
- "properties": { "key": {"type": "integer"} }
- }]
- }, {
- "name": "alphaOtherEvent"
- }]
- })";
- // Another fake API for testing.
- const char kBetaAPIName[] = "beta";
- const char kBetaAPISpec[] = R"(
- {
- "functions": [{
- "name": "simpleFunc",
- "parameters": [{"name": "int", "type": "integer"}]
- }]
- })";
- const char kGammaAPIName[] = "gamma";
- const char kGammaAPISpec[] = R"(
- {
- "functions": [{
- "name": "functionWithExternalRef",
- "parameters": [{ "name": "someRef", "$ref": "alpha.objRef" }]
- }]
- })";
- // JS strings used for registering custom callbacks for testing.
- const char kCustomCallbackHook[] = R"(
- (function(hooks) {
- hooks.setCustomCallback(
- 'functionWithCallback', (originalCallback,
- firstResult, secondResult) => {
- this.results = [firstResult, secondResult];
- originalCallback(secondResult);
- });
- }))";
- const char kCustomCallbackThrowHook[] = R"(
- (function(hooks) {
- hooks.setCustomCallback(
- 'functionWithCallback', (name, originalCallback,
- firstResult, secondResult) => {
- throw new Error('Custom callback threw');
- });
- }))";
- bool AllowAllAPIs(v8::Local<v8::Context> context, const std::string& name) {
- return true;
- }
- bool AllowPromises(v8::Local<v8::Context> context) {
- return true;
- }
- } // namespace
- APIBindingsSystemTest::APIBindingsSystemTest() {}
- APIBindingsSystemTest::~APIBindingsSystemTest() = default;
- void APIBindingsSystemTest::SetUp() {
- APIBindingTest::SetUp();
- // Create the fake API schemas.
- for (const auto& api : GetAPIs()) {
- std::unique_ptr<base::DictionaryValue> api_schema =
- DeprecatedDictionaryValueFromString(api.spec);
- ASSERT_TRUE(api_schema);
- api_schemas_[api.name] = std::move(api_schema);
- }
- binding::AddConsoleError add_console_error(base::BindRepeating(
- &APIBindingsSystemTest::AddConsoleError, base::Unretained(this)));
- auto get_context_owner = [](v8::Local<v8::Context>) {
- return std::string("context");
- };
- bindings_system_ = std::make_unique<APIBindingsSystem>(
- base::BindRepeating(&APIBindingsSystemTest::GetAPISchema,
- base::Unretained(this)),
- base::BindRepeating(&AllowAllAPIs), base::BindRepeating(&AllowPromises),
- base::BindRepeating(&APIBindingsSystemTest::OnAPIRequest,
- base::Unretained(this)),
- std::make_unique<TestInteractionProvider>(),
- base::BindRepeating(&APIBindingsSystemTest::OnEventListenersChanged,
- base::Unretained(this)),
- base::BindRepeating(get_context_owner), base::DoNothing(),
- add_console_error,
- APILastError(
- base::BindRepeating(&APIBindingsSystemTest::GetLastErrorParent,
- base::Unretained(this)),
- add_console_error));
- }
- void APIBindingsSystemTest::TearDown() {
- // Dispose all contexts now so that we call WillReleaseContext().
- DisposeAllContexts();
- bindings_system_.reset();
- APIBindingTest::TearDown();
- }
- void APIBindingsSystemTest::OnWillDisposeContext(
- v8::Local<v8::Context> context) {
- bindings_system_->WillReleaseContext(context);
- }
- std::vector<APIBindingsSystemTest::FakeSpec> APIBindingsSystemTest::GetAPIs() {
- return {
- {kAlphaAPIName, kAlphaAPISpec},
- {kBetaAPIName, kBetaAPISpec},
- {kGammaAPIName, kGammaAPISpec},
- };
- }
- v8::Local<v8::Object> APIBindingsSystemTest::GetLastErrorParent(
- v8::Local<v8::Context> context,
- v8::Local<v8::Object>* secondary_parent) {
- return v8::Local<v8::Object>();
- }
- void APIBindingsSystemTest::AddConsoleError(v8::Local<v8::Context> context,
- const std::string& error) {
- console_errors_.push_back(error);
- }
- const base::DictionaryValue& APIBindingsSystemTest::GetAPISchema(
- const std::string& api_name) {
- EXPECT_TRUE(base::Contains(api_schemas_, api_name));
- return *api_schemas_[api_name];
- }
- void APIBindingsSystemTest::OnAPIRequest(
- std::unique_ptr<APIRequestHandler::Request> request,
- v8::Local<v8::Context> context) {
- ASSERT_FALSE(last_request_);
- last_request_ = std::move(request);
- }
- void APIBindingsSystemTest::OnEventListenersChanged(
- const std::string& event_name,
- binding::EventListenersChanged changed,
- const base::DictionaryValue* filter,
- bool was_manual,
- v8::Local<v8::Context> context) {}
- void APIBindingsSystemTest::ValidateLastRequest(
- const std::string& expected_name,
- const std::string& expected_arguments) {
- ASSERT_TRUE(last_request());
- // Note that even if no arguments are provided by the API call, we should
- // have an empty list.
- ASSERT_TRUE(last_request()->arguments_list);
- EXPECT_EQ(expected_name, last_request()->method_name);
- EXPECT_EQ(ReplaceSingleQuotes(expected_arguments),
- ValueToString(*last_request()->arguments_list));
- }
- v8::Local<v8::Value> APIBindingsSystemTest::CallFunctionOnObject(
- v8::Local<v8::Context> context,
- v8::Local<v8::Object> object,
- const std::string& script_source) {
- std::string wrapped_script_source =
- base::StringPrintf("(function(obj) { %s })", script_source.c_str());
- v8::Local<v8::Function> func =
- FunctionFromString(context, wrapped_script_source);
- // Use ADD_FAILURE() to avoid messing up the return type with ASSERT.
- if (func.IsEmpty()) {
- ADD_FAILURE() << script_source;
- return v8::Local<v8::Value>();
- }
- v8::Local<v8::Value> argv[] = {object};
- return RunFunction(func, context, 1, argv);
- }
- // Tests API object initialization, calling a method on the supplied APIs, and
- // triggering the callback for the request.
- TEST_F(APIBindingsSystemTest, TestInitializationAndCallbacks) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- v8::Local<v8::Object> alpha_api =
- bindings_system()->CreateAPIInstance(kAlphaAPIName, context, nullptr);
- ASSERT_FALSE(alpha_api.IsEmpty());
- v8::Local<v8::Object> beta_api =
- bindings_system()->CreateAPIInstance(kBetaAPIName, context, nullptr);
- ASSERT_FALSE(beta_api.IsEmpty());
- {
- // Test a simple call -> response.
- const char kTestCall[] = R"(
- obj.functionWithCallback('foo', function() {
- this.callbackArguments = Array.from(arguments);
- });)";
- CallFunctionOnObject(context, alpha_api, kTestCall);
- ValidateLastRequest("alpha.functionWithCallback", "['foo']");
- const char kResponseArgsJson[] = R"(["response"])";
- bindings_system()->CompleteRequest(last_request()->request_id,
- ListValueFromString(kResponseArgsJson),
- std::string());
- EXPECT_EQ(kResponseArgsJson,
- GetStringPropertyFromObject(context->Global(), context,
- "callbackArguments"));
- reset_last_request();
- }
- {
- // Test a call with references -> response.
- const char kTestCall[] = R"(
- obj.functionWithRefAndCallback({prop1: 'alpha', prop2: 42},
- function() {
- this.callbackArguments = Array.from(arguments);
- });)";
- CallFunctionOnObject(context, alpha_api, kTestCall);
- ValidateLastRequest("alpha.functionWithRefAndCallback",
- "[{'prop1':'alpha','prop2':42}]");
- bindings_system()->CompleteRequest(last_request()->request_id,
- base::Value::List(), std::string());
- EXPECT_EQ("[]", GetStringPropertyFromObject(context->Global(), context,
- "callbackArguments"));
- reset_last_request();
- }
- {
- // Test an invalid invocation -> throwing error.
- const char kTestCall[] =
- "(function(obj) { obj.functionWithEnum('mouse') })";
- v8::Local<v8::Function> function = FunctionFromString(context, kTestCall);
- v8::Local<v8::Value> args[] = {alpha_api};
- RunFunctionAndExpectError(
- function, context, std::size(args), args,
- "Uncaught TypeError: " +
- api_errors::InvocationError(
- "alpha.functionWithEnum", "alpha.enumRef e",
- api_errors::ArgumentError(
- "e", api_errors::InvalidEnumValue({"cat", "dog"}))));
- EXPECT_FALSE(last_request());
- reset_last_request(); // Just to not pollute future results.
- }
- {
- // Test an event registration -> event occurrence.
- const char kTestCall[] = R"(
- obj.alphaEvent.addListener(function() {
- this.eventArguments = Array.from(arguments);
- });)";
- CallFunctionOnObject(context, alpha_api, kTestCall);
- const char kResponseArgsJson[] = R"([{"key":42}])";
- base::Value::List expected_args = ListValueFromString(kResponseArgsJson);
- bindings_system()->FireEventInContext("alpha.alphaEvent", context,
- expected_args, nullptr);
- EXPECT_EQ(kResponseArgsJson,
- GetStringPropertyFromObject(context->Global(), context,
- "eventArguments"));
- }
- {
- // Test a call -> response on the second API.
- const char kTestCall[] = "obj.simpleFunc(2)";
- CallFunctionOnObject(context, beta_api, kTestCall);
- ValidateLastRequest("beta.simpleFunc", "[2]");
- reset_last_request();
- }
- }
- // Tests adding a custom hook to an API.
- TEST_F(APIBindingsSystemTest, TestCustomHooks) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- bool did_call = false;
- auto hook = [](bool* did_call, const APISignature* signature,
- v8::Local<v8::Context> context,
- std::vector<v8::Local<v8::Value>>* arguments,
- const APITypeReferenceMap& type_refs) {
- *did_call = true;
- APIBindingHooks::RequestResult result(
- APIBindingHooks::RequestResult::HANDLED);
- if (arguments->size() != 2) { // ASSERT* messes with the return type.
- EXPECT_EQ(2u, arguments->size());
- return result;
- }
- std::string argument;
- EXPECT_EQ("foo", gin::V8ToString(context->GetIsolate(), arguments->at(0)));
- if (!arguments->at(1)->IsFunction()) {
- EXPECT_TRUE(arguments->at(1)->IsFunction());
- return result;
- }
- v8::Local<v8::String> response =
- gin::StringToV8(context->GetIsolate(), "bar");
- v8::Local<v8::Value> response_args[] = {response};
- RunFunctionOnGlobal(arguments->at(1).As<v8::Function>(),
- context, 1, response_args);
- return result;
- };
- auto test_hooks = std::make_unique<APIBindingHooksTestDelegate>();
- test_hooks->AddHandler("alpha.functionWithCallback",
- base::BindRepeating(hook, &did_call));
- APIBindingHooks* binding_hooks =
- bindings_system()->GetHooksForAPI(kAlphaAPIName);
- binding_hooks->SetDelegate(std::move(test_hooks));
- v8::Local<v8::Object> alpha_api =
- bindings_system()->CreateAPIInstance(kAlphaAPIName, context, nullptr);
- ASSERT_FALSE(alpha_api.IsEmpty());
- {
- // Test a simple call -> response.
- const char kTestCall[] = R"(
- obj.functionWithCallback('foo', function() {
- this.callbackArguments = Array.from(arguments);
- });)";
- CallFunctionOnObject(context, alpha_api, kTestCall);
- EXPECT_TRUE(did_call);
- EXPECT_EQ(R"(["bar"])",
- GetStringPropertyFromObject(context->Global(), context,
- "callbackArguments"));
- }
- }
- // Tests a call with a callback into an API using a setCustomCallback hook
- // works as expected.
- TEST_F(APIBindingsSystemTest, TestSetCustomCallback_SuccessWithCallback) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- APIBindingHooks* hooks = nullptr;
- v8::Local<v8::Object> alpha_api =
- bindings_system()->CreateAPIInstance(kAlphaAPIName, context, &hooks);
- ASSERT_FALSE(alpha_api.IsEmpty());
- ASSERT_TRUE(hooks);
- v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
- v8::Local<v8::Function> function =
- FunctionFromString(context, kCustomCallbackHook);
- v8::Local<v8::Value> args[] = {js_hooks};
- RunFunctionOnGlobal(function, context, std::size(args), args);
- const char kTestCall[] = R"(
- obj.functionWithCallback('foo', function() {
- this.callbackArguments = Array.from(arguments);
- });)";
- CallFunctionOnObject(context, alpha_api, kTestCall);
- ValidateLastRequest("alpha.functionWithCallback", "['foo']");
- // Although this response would violate the return on the spec, since this
- // method has a custom callback defined it skips response validation. We
- // expect the custom callback will transform the return to the correct form
- // when calling the original callback, but this is not currently enforced or
- // validated.
- // TODO(tjudkins): Now that we use the CustomCallbackAdaptor, we could
- // potentially send the response validator to the custom callback adaptor
- // and validate the result returned from the custom callback before sending
- // it on to the original callback.
- bindings_system()->CompleteRequest(last_request()->request_id,
- ListValueFromString(R"(["alpha","beta"])"),
- std::string());
- EXPECT_EQ(R"(["alpha","beta"])",
- GetStringPropertyFromObject(context->Global(), context, "results"));
- EXPECT_EQ(R"(["beta"])",
- GetStringPropertyFromObject(context->Global(), context,
- "callbackArguments"));
- }
- // Tests a call with a promise into an API using a setCustomCallback hook works
- // as expected.
- TEST_F(APIBindingsSystemTest, TestSetCustomCallback_SuccessWithPromise) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- APIBindingHooks* hooks = nullptr;
- v8::Local<v8::Object> alpha_api =
- bindings_system()->CreateAPIInstance(kAlphaAPIName, context, &hooks);
- ASSERT_FALSE(alpha_api.IsEmpty());
- ASSERT_TRUE(hooks);
- v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
- v8::Local<v8::Function> function =
- FunctionFromString(context, kCustomCallbackHook);
- v8::Local<v8::Value> args[] = {js_hooks};
- RunFunctionOnGlobal(function, context, std::size(args), args);
- const char kTestCall[] = R"(return obj.functionWithCallback('bar');)";
- v8::Local<v8::Value> result =
- CallFunctionOnObject(context, alpha_api, kTestCall);
- ValidateLastRequest("alpha.functionWithCallback", "['bar']");
- v8::Local<v8::Promise> promise;
- ASSERT_TRUE(GetValueAs(result, &promise));
- EXPECT_EQ(v8::Promise::kPending, promise->State());
- bindings_system()->CompleteRequest(
- last_request()->request_id, ListValueFromString(R"(["gamma","delta"])"),
- std::string());
- EXPECT_EQ(R"(["gamma","delta"])",
- GetStringPropertyFromObject(context->Global(), context, "results"));
- EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
- EXPECT_EQ(R"("delta")", V8ToString(promise->Result(), context));
- }
- // Tests that an error thrown in a setCustomCallback hook while using a callback
- // based call works as expected.
- TEST_F(APIBindingsSystemTest, TestSetCustomCallback_ErrorWithCallback) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- APIBindingHooks* hooks = nullptr;
- v8::Local<v8::Object> alpha_api =
- bindings_system()->CreateAPIInstance(kAlphaAPIName, context, &hooks);
- ASSERT_FALSE(alpha_api.IsEmpty());
- ASSERT_TRUE(hooks);
- v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
- v8::Local<v8::Function> function =
- FunctionFromString(context, kCustomCallbackThrowHook);
- v8::Local<v8::Value> args[] = {js_hooks};
- RunFunctionOnGlobal(function, context, std::size(args), args);
- const char kTestCall[] = R"(
- obj.functionWithCallback('baz', function() {
- this.callbackCalled = true;
- });)";
- CallFunctionOnObject(context, alpha_api, kTestCall);
- ValidateLastRequest("alpha.functionWithCallback", "['baz']");
- ASSERT_TRUE(console_errors().empty());
- TestJSRunner::AllowErrors allow_errors;
- bindings_system()->CompleteRequest(
- last_request()->request_id, ListValueFromString(R"(["alpha", "beta"])"),
- std::string());
- // The callback should have never been called and there should now be a
- // console error logged.
- EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(), context,
- "callbackCalled"));
- ASSERT_EQ(1u, console_errors().size());
- EXPECT_THAT(console_errors()[0],
- testing::StartsWith(
- "Error handling response: Error: Custom callback threw"));
- }
- // Tests that an error thrown in a setCustomCallback hook while using a promise
- // based call works as expected.
- TEST_F(APIBindingsSystemTest, TestSetCustomCallback_ErrorWithPromise) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- APIBindingHooks* hooks = nullptr;
- v8::Local<v8::Object> alpha_api =
- bindings_system()->CreateAPIInstance(kAlphaAPIName, context, &hooks);
- ASSERT_FALSE(alpha_api.IsEmpty());
- ASSERT_TRUE(hooks);
- v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
- v8::Local<v8::Function> function =
- FunctionFromString(context, kCustomCallbackThrowHook);
- v8::Local<v8::Value> args[] = {js_hooks};
- RunFunctionOnGlobal(function, context, std::size(args), args);
- const char kTestCall[] = R"(return obj.functionWithCallback('boz');)";
- v8::Local<v8::Value> result =
- CallFunctionOnObject(context, alpha_api, kTestCall);
- ValidateLastRequest("alpha.functionWithCallback", "['boz']");
- v8::Local<v8::Promise> promise;
- ASSERT_TRUE(GetValueAs(result, &promise));
- EXPECT_EQ(v8::Promise::kPending, promise->State());
- ASSERT_TRUE(console_errors().empty());
- TestJSRunner::AllowErrors allow_errors;
- bindings_system()->CompleteRequest(
- last_request()->request_id, ListValueFromString(R"(["gamma", "delta"])"),
- std::string());
- // The promise will remain pending and there should now be a console error
- // logged.
- // TODO(tjudkins): Ideally we should be rejecting the promise here instead.
- EXPECT_EQ(v8::Promise::kPending, promise->State());
- ASSERT_EQ(1u, console_errors().size());
- EXPECT_THAT(console_errors()[0],
- testing::StartsWith(
- "Error handling response: Error: Custom callback threw"));
- }
- // Test that references to other API's types works.
- TEST_F(APIBindingsSystemTest, CrossAPIReferences) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- // Instantiate gamma API. Note: It's important that we haven't instantiated
- // alpha API yet, since this tests that we can lazily populate the type
- // information.
- v8::Local<v8::Object> gamma_api =
- bindings_system()->CreateAPIInstance(kGammaAPIName, context, nullptr);
- ASSERT_FALSE(gamma_api.IsEmpty());
- {
- // Test a simple call -> response.
- const char kTestCall[] = "obj.functionWithExternalRef({prop1: 'foo'});";
- CallFunctionOnObject(context, gamma_api, kTestCall);
- ValidateLastRequest("gamma.functionWithExternalRef", "[{'prop1':'foo'}]");
- reset_last_request();
- }
- }
- TEST_F(APIBindingsSystemTest, TestCustomEvent) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- auto create_custom_event = [](v8::Local<v8::Context> context,
- const std::string& event_name) {
- v8::Isolate* isolate = context->GetIsolate();
- v8::Local<v8::Object> ret = v8::Object::New(isolate);
- ret->Set(context, gin::StringToSymbol(isolate, "name"),
- gin::StringToSymbol(isolate, event_name))
- .ToChecked();
- return ret.As<v8::Value>();
- };
- auto test_hooks = std::make_unique<APIBindingHooksTestDelegate>();
- test_hooks->SetCustomEvent(base::BindRepeating(create_custom_event));
- APIBindingHooks* binding_hooks =
- bindings_system()->GetHooksForAPI(kAlphaAPIName);
- binding_hooks->SetDelegate(std::move(test_hooks));
- v8::Local<v8::Object> api =
- bindings_system()->CreateAPIInstance(kAlphaAPIName, context, nullptr);
- v8::Local<v8::Object> event;
- ASSERT_TRUE(GetPropertyFromObjectAs(api, context, "alphaEvent", &event));
- EXPECT_EQ(R"("alpha.alphaEvent")",
- GetStringPropertyFromObject(event, context, "name"));
- v8::Local<v8::Value> event2 =
- GetPropertyFromObject(api, context, "alphaEvent");
- EXPECT_EQ(event, event2);
- v8::Local<v8::Object> other_event;
- ASSERT_TRUE(
- GetPropertyFromObjectAs(api, context, "alphaOtherEvent", &other_event));
- EXPECT_EQ(R"("alpha.alphaOtherEvent")",
- GetStringPropertyFromObject(other_event, context, "name"));
- EXPECT_NE(event, other_event);
- }
- } // namespace extensions
|