From 65d259d45db80a9aecf0e50e7a19532520c5f932 Mon Sep 17 00:00:00 2001 From: JoseExposito Date: Fri, 5 Mar 2021 18:33:54 +0100 Subject: [PATCH 1/9] Test that Object::GetPropertyNames does not return properties whose key is a Symbol --- test/object/object.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/object/object.js b/test/object/object.js index 8741e27f1..b9036560f 100644 --- a/test/object/object.js +++ b/test/object/object.js @@ -110,9 +110,10 @@ function test(binding) { } { - const obj = {'one': 1, 'two': 2, 'three': 3}; + const testSym = Symbol(); + const obj = { 'one': 1, 'two': 2, 'three': 3, [testSym]: 4 }; var arr = binding.object.GetPropertyNames(obj); - assert.deepStrictEqual(arr, ['one', 'two', 'three']) + assert.deepStrictEqual(arr, ['one', 'two', 'three']); } { From 47fcf4a51254e36d20505419bf8661ae17b3e5ce Mon Sep 17 00:00:00 2001 From: JoseExposito Date: Fri, 5 Mar 2021 18:40:58 +0100 Subject: [PATCH 2/9] Test that Object::Get returns undefined if the key does not exist --- test/object/get_property.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/test/object/get_property.js b/test/object/get_property.js index 7ec9b119e..8028165c3 100644 --- a/test/object/get_property.js +++ b/test/object/get_property.js @@ -12,19 +12,27 @@ function test(binding) { assert.strictEqual(nativeGetProperty(obj, 'test'), 1); } + function testShouldReturnUndefinedIfKeyIsNotPresent(nativeGetProperty) { + const obj = { }; + assert.strictEqual(nativeGetProperty(obj, 'test'), undefined); + } + function testShouldThrowErrorIfKeyIsInvalid(nativeGetProperty) { assert.throws(() => { nativeGetProperty(undefined, 'test'); }, /Cannot convert undefined or null to object/); } - testGetProperty(binding.object.getPropertyWithNapiValue); - testGetProperty(binding.object.getPropertyWithNapiWrapperValue); - testGetProperty(binding.object.getPropertyWithCStyleString); - testGetProperty(binding.object.getPropertyWithCppStyleString); + const nativeFunctions = [ + binding.object.getPropertyWithNapiValue, + binding.object.getPropertyWithNapiWrapperValue, + binding.object.getPropertyWithCStyleString, + binding.object.getPropertyWithCppStyleString + ]; - testShouldThrowErrorIfKeyIsInvalid(binding.object.getPropertyWithNapiValue); - testShouldThrowErrorIfKeyIsInvalid(binding.object.getPropertyWithNapiWrapperValue); - testShouldThrowErrorIfKeyIsInvalid(binding.object.getPropertyWithCStyleString); - testShouldThrowErrorIfKeyIsInvalid(binding.object.getPropertyWithCppStyleString); + nativeFunctions.forEach((nativeFunction) => { + testGetProperty(nativeFunction); + testShouldReturnUndefinedIfKeyIsNotPresent(nativeFunction); + testShouldThrowErrorIfKeyIsInvalid(nativeFunction); + }); } From cdd5c9f7495cb7e5713ca62f525cc5cb9810ba6b Mon Sep 17 00:00:00 2001 From: JoseExposito Date: Fri, 5 Mar 2021 18:49:17 +0100 Subject: [PATCH 3/9] Add test for Object::InstanceOf --- test/object/object.cc | 8 ++++++++ test/object/object.js | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/test/object/object.cc b/test/object/object.cc index 2c0ce420b..2a2bd2e51 100644 --- a/test/object/object.cc +++ b/test/object/object.cc @@ -228,6 +228,12 @@ Value CreateObjectUsingMagic(const CallbackInfo& info) { return obj; } +Value InstanceOf(const CallbackInfo& info) { + Object obj = info[0].As(); + Function constructor = info[1].As(); + return Boolean::New(info.Env(), obj.InstanceOf(constructor)); +} + Object InitObject(Env env) { Object exports = Object::New(env); @@ -265,5 +271,7 @@ Object InitObject(Env env) { exports["addFinalizer"] = Function::New(env, AddFinalizer); exports["addFinalizerWithHint"] = Function::New(env, AddFinalizerWithHint); + exports["instanceOf"] = Function::New(env, InstanceOf); + return exports; } diff --git a/test/object/object.js b/test/object/object.js index b9036560f..52646cb8c 100644 --- a/test/object/object.js +++ b/test/object/object.js @@ -137,4 +137,13 @@ function test(binding) { circular2: magicObject }); } + + { + function Ctor() {}; + + assert.strictEqual(binding.object.instanceOf(new Ctor(), Ctor), true); + assert.strictEqual(binding.object.instanceOf(new Ctor(), Object), true); + assert.strictEqual(binding.object.instanceOf({}, Ctor), false); + assert.strictEqual(binding.object.instanceOf(null, Ctor), false); + } } From 785c38fa52370eb529a08c6a66712e886c73a188 Mon Sep 17 00:00:00 2001 From: JoseExposito Date: Fri, 5 Mar 2021 18:53:05 +0100 Subject: [PATCH 4/9] Add test for Object::Object empty constructor --- test/object/object.cc | 6 ++++++ test/object/object.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/test/object/object.cc b/test/object/object.cc index 2a2bd2e51..320cd67b7 100644 --- a/test/object/object.cc +++ b/test/object/object.cc @@ -69,6 +69,10 @@ Value TestFunctionWithUserData(const CallbackInfo& info) { return Number::New(info.Env(), holder->value); } +Value GetEmptyConstructor(const CallbackInfo& /*info*/) { + return Object(); +} + Array GetPropertyNames(const CallbackInfo& info) { Object obj = info[0].As(); Array arr = obj.GetPropertyNames(); @@ -237,6 +241,8 @@ Value InstanceOf(const CallbackInfo& info) { Object InitObject(Env env) { Object exports = Object::New(env); + exports["getEmptyConstructor"] = Function::New(env, GetEmptyConstructor); + exports["GetPropertyNames"] = Function::New(env, GetPropertyNames); exports["defineProperties"] = Function::New(env, DefineProperties); exports["defineValueProperty"] = Function::New(env, DefineValueProperty); diff --git a/test/object/object.js b/test/object/object.js index 52646cb8c..0b734027e 100644 --- a/test/object/object.js +++ b/test/object/object.js @@ -102,6 +102,12 @@ function test(binding) { testDefineProperties('string'); testDefineProperties('value'); + { + const expected = undefined; + const actual = binding.object.getEmptyConstructor(); + assert.strictEqual(actual, expected); + } + { const obj = {}; const testSym = Symbol(); From cc65d0aacac72e6a3d84f3823c809b1220867c03 Mon Sep 17 00:00:00 2001 From: JoseExposito Date: Fri, 5 Mar 2021 19:03:51 +0100 Subject: [PATCH 5/9] Add test for Object::operator[] --- test/binding.gyp | 1 + test/object/object.cc | 15 +++++++++++ test/object/subscript_operator.cc | 42 +++++++++++++++++++++++++++++++ test/object/subscript_operator.js | 18 +++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 test/object/subscript_operator.cc create mode 100644 test/object/subscript_operator.js diff --git a/test/binding.gyp b/test/binding.gyp index 90dee4565..130bfa121 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -34,6 +34,7 @@ 'object/has_property.cc', 'object/object.cc', 'object/set_property.cc', + 'object/subscript_operator.cc', 'promise.cc', 'run_script.cc', 'threadsafe_function/threadsafe_function_ctx.cc', diff --git a/test/object/object.cc b/test/object/object.cc index 320cd67b7..fe3d0f7ed 100644 --- a/test/object/object.cc +++ b/test/object/object.cc @@ -36,6 +36,14 @@ Value HasPropertyWithCppStyleString(const CallbackInfo& info); Value AddFinalizer(const CallbackInfo& info); Value AddFinalizerWithHint(const CallbackInfo& info); +// Native wrappers for testing Object::operator [] +Value SubscriptGetWithCStyleString(const CallbackInfo& info); +Value SubscriptGetWithCppStyleString(const CallbackInfo& info); +Value SubscriptGetAtIndex(const CallbackInfo& info); +void SubscriptSetWithCStyleString(const CallbackInfo& info); +void SubscriptSetWithCppStyleString(const CallbackInfo& info); +void SubscriptSetAtIndex(const CallbackInfo& info); + static bool testValue = true; // Used to test void* Data() integrity struct UserDataHolder { @@ -279,5 +287,12 @@ Object InitObject(Env env) { exports["instanceOf"] = Function::New(env, InstanceOf); + exports["subscriptGetWithCStyleString"] = Function::New(env, SubscriptGetWithCStyleString); + exports["subscriptGetWithCppStyleString"] = Function::New(env, SubscriptGetWithCppStyleString); + exports["subscriptGetAtIndex"] = Function::New(env, SubscriptGetAtIndex); + exports["subscriptSetWithCStyleString"] = Function::New(env, SubscriptSetWithCStyleString); + exports["subscriptSetWithCppStyleString"] = Function::New(env, SubscriptSetWithCppStyleString); + exports["subscriptSetAtIndex"] = Function::New(env, SubscriptSetAtIndex); + return exports; } diff --git a/test/object/subscript_operator.cc b/test/object/subscript_operator.cc new file mode 100644 index 000000000..3af39fc54 --- /dev/null +++ b/test/object/subscript_operator.cc @@ -0,0 +1,42 @@ +#include "napi.h" + +using namespace Napi; + +Value SubscriptGetWithCStyleString(const CallbackInfo& info) { + Object obj = info[0].As(); + String jsKey = info[1].As(); + return obj[jsKey.Utf8Value().c_str()]; +} + +Value SubscriptGetWithCppStyleString(const CallbackInfo& info) { + Object obj = info[0].As(); + String jsKey = info[1].As(); + return obj[jsKey.Utf8Value()]; +} + +Value SubscriptGetAtIndex(const CallbackInfo& info) { + Object obj = info[0].As(); + uint32_t index = info[1].As(); + return obj[index]; +} + +void SubscriptSetWithCStyleString(const CallbackInfo& info) { + Object obj = info[0].As(); + String jsKey = info[1].As(); + Value value = info[2]; + obj[jsKey.Utf8Value().c_str()] = value; +} + +void SubscriptSetWithCppStyleString(const CallbackInfo& info) { + Object obj = info[0].As(); + String jsKey = info[1].As(); + Value value = info[2]; + obj[jsKey.Utf8Value()] = value; +} + +void SubscriptSetAtIndex(const CallbackInfo& info) { + Object obj = info[0].As(); + uint32_t index = info[1].As(); + Value value = info[2]; + obj[index] = value; +} diff --git a/test/object/subscript_operator.js b/test/object/subscript_operator.js new file mode 100644 index 000000000..2aaa09a8e --- /dev/null +++ b/test/object/subscript_operator.js @@ -0,0 +1,18 @@ +'use strict'; + +const buildType = process.config.target_defaults.default_configuration; +const assert = require('assert'); + +test(require(`../build/${buildType}/binding.node`)); +test(require(`../build/${buildType}/binding_noexcept.node`)); + +function test(binding) { + function testProperty(obj, key, value, nativeGetProperty, nativeSetProperty) { + nativeSetProperty(obj, key, value); + assert.strictEqual(nativeGetProperty(obj, key), value); + } + + testProperty({}, 'key', 'value', binding.object.subscriptGetWithCStyleString, binding.object.subscriptSetWithCStyleString); + testProperty({ key: 'override me' }, 'key', 'value', binding.object.subscriptGetWithCppStyleString, binding.object.subscriptSetWithCppStyleString); + testProperty({}, 0, 'value', binding.object.subscriptGetAtIndex, binding.object.subscriptSetAtIndex); +} From 896a74614d4c8d815e3ae90f53189c07e491e51a Mon Sep 17 00:00:00 2001 From: JoseExposito Date: Thu, 11 Mar 2021 07:36:41 +0100 Subject: [PATCH 6/9] Fix linter issues --- test/object/object.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/object/object.cc b/test/object/object.cc index fe3d0f7ed..c7e71d742 100644 --- a/test/object/object.cc +++ b/test/object/object.cc @@ -287,11 +287,15 @@ Object InitObject(Env env) { exports["instanceOf"] = Function::New(env, InstanceOf); - exports["subscriptGetWithCStyleString"] = Function::New(env, SubscriptGetWithCStyleString); - exports["subscriptGetWithCppStyleString"] = Function::New(env, SubscriptGetWithCppStyleString); + exports["subscriptGetWithCStyleString"] = + Function::New(env, SubscriptGetWithCStyleString); + exports["subscriptGetWithCppStyleString"] = + Function::New(env, SubscriptGetWithCppStyleString); exports["subscriptGetAtIndex"] = Function::New(env, SubscriptGetAtIndex); - exports["subscriptSetWithCStyleString"] = Function::New(env, SubscriptSetWithCStyleString); - exports["subscriptSetWithCppStyleString"] = Function::New(env, SubscriptSetWithCppStyleString); + exports["subscriptSetWithCStyleString"] = + Function::New(env, SubscriptSetWithCStyleString); + exports["subscriptSetWithCppStyleString"] = + Function::New(env, SubscriptSetWithCppStyleString); exports["subscriptSetAtIndex"] = Function::New(env, SubscriptSetAtIndex); return exports; From 4abe5b8e91ef5e97f31868925f83acb6fe05213c Mon Sep 17 00:00:00 2001 From: JoseExposito Date: Sat, 13 Mar 2021 14:15:32 +0100 Subject: [PATCH 7/9] Use IsEmpty to test Object constructor --- test/object/object.cc | 9 ++++++--- test/object/object.js | 5 ++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/object/object.cc b/test/object/object.cc index c7e71d742..04a7ccc01 100644 --- a/test/object/object.cc +++ b/test/object/object.cc @@ -77,8 +77,11 @@ Value TestFunctionWithUserData(const CallbackInfo& info) { return Number::New(info.Env(), holder->value); } -Value GetEmptyConstructor(const CallbackInfo& /*info*/) { - return Object(); +Value EmptyConstructor(const CallbackInfo& info) { + auto env = info.Env(); + bool isEmpty = info[0].As(); + Object object = isEmpty ? Object() : Object(env, Object::New(env)); + return Boolean::New(env, object.IsEmpty()); } Array GetPropertyNames(const CallbackInfo& info) { @@ -249,7 +252,7 @@ Value InstanceOf(const CallbackInfo& info) { Object InitObject(Env env) { Object exports = Object::New(env); - exports["getEmptyConstructor"] = Function::New(env, GetEmptyConstructor); + exports["emptyConstructor"] = Function::New(env, EmptyConstructor); exports["GetPropertyNames"] = Function::New(env, GetPropertyNames); exports["defineProperties"] = Function::New(env, DefineProperties); diff --git a/test/object/object.js b/test/object/object.js index 0b734027e..c4b1ce3b9 100644 --- a/test/object/object.js +++ b/test/object/object.js @@ -103,9 +103,8 @@ function test(binding) { testDefineProperties('value'); { - const expected = undefined; - const actual = binding.object.getEmptyConstructor(); - assert.strictEqual(actual, expected); + assert.strictEqual(binding.object.emptyConstructor(true), true); + assert.strictEqual(binding.object.emptyConstructor(false), false); } { From 8e2ee12daedff316f3c0ff1316bae820b7c375cb Mon Sep 17 00:00:00 2001 From: JoseExposito Date: Sat, 13 Mar 2021 14:23:34 +0100 Subject: [PATCH 8/9] Test that Object::operator[] overrides object keys by index --- test/object/subscript_operator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/object/subscript_operator.js b/test/object/subscript_operator.js index 2aaa09a8e..21a6ee891 100644 --- a/test/object/subscript_operator.js +++ b/test/object/subscript_operator.js @@ -15,4 +15,5 @@ function test(binding) { testProperty({}, 'key', 'value', binding.object.subscriptGetWithCStyleString, binding.object.subscriptSetWithCStyleString); testProperty({ key: 'override me' }, 'key', 'value', binding.object.subscriptGetWithCppStyleString, binding.object.subscriptSetWithCppStyleString); testProperty({}, 0, 'value', binding.object.subscriptGetAtIndex, binding.object.subscriptSetAtIndex); + testProperty({ key: 'override me' }, 0, 'value', binding.object.subscriptGetAtIndex, binding.object.subscriptSetAtIndex); } From 61a0c7698d510305d3f1fd7d3116f5e889a8e379 Mon Sep 17 00:00:00 2001 From: JoseExposito Date: Sat, 13 Mar 2021 14:43:10 +0100 Subject: [PATCH 9/9] Test that it is possible to create a JavaScript object from another JavaScript object --- test/object/object.cc | 7 +++++++ test/object/object.js | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/test/object/object.cc b/test/object/object.cc index 04a7ccc01..b2f6b5f95 100644 --- a/test/object/object.cc +++ b/test/object/object.cc @@ -84,6 +84,12 @@ Value EmptyConstructor(const CallbackInfo& info) { return Boolean::New(env, object.IsEmpty()); } +Value ConstructorFromObject(const CallbackInfo& info) { + auto env = info.Env(); + Object object = info[0].As(); + return Object(env, object); +} + Array GetPropertyNames(const CallbackInfo& info) { Object obj = info[0].As(); Array arr = obj.GetPropertyNames(); @@ -253,6 +259,7 @@ Object InitObject(Env env) { Object exports = Object::New(env); exports["emptyConstructor"] = Function::New(env, EmptyConstructor); + exports["constructorFromObject"] = Function::New(env, ConstructorFromObject); exports["GetPropertyNames"] = Function::New(env, GetPropertyNames); exports["defineProperties"] = Function::New(env, DefineProperties); diff --git a/test/object/object.js b/test/object/object.js index c4b1ce3b9..4b6f72fc7 100644 --- a/test/object/object.js +++ b/test/object/object.js @@ -107,6 +107,12 @@ function test(binding) { assert.strictEqual(binding.object.emptyConstructor(false), false); } + { + const expected = { 'one': 1, 'two': 2, 'three': 3 }; + const actual = binding.object.constructorFromObject(expected); + assert.deepStrictEqual(actual, expected); + } + { const obj = {}; const testSym = Symbol();