blob: 2251968cf51d4973904136924fb8081e2bca39f9 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/**
* @fileoverview Tests for js-value.js
* Conversion of JSValue to and from native values should be idempotent.
* Tests of the shallow convertTonative are performed in canonicalize.
*/
var test = require('tape');
var registry = require('./../../src/vdl/registry.js');
var types = require('./../../src/vdl/types.js');
var jsValueConvert = require('./../../src/vdl/js-value-convert.js');
var stringify = require('./../../src/vdl/stringify.js');
// A helper function that shallow copies an object into an object with the
// JSValue prototype. It makes the test cases a lot more readable.
function JS(obj) {
var JSValue = registry.lookupOrCreateConstructor(types.JSVALUE);
var jsval = Object.create(JSValue.prototype);
Object.keys(obj).forEach(function(key) {
jsval[key] = obj[key];
});
return jsval;
}
// Note: These test cases usually convert native and jsval correctly. There are
// some edge cases like 'undefined', so nativeFinal is used in such cases.
test('js-value convert to and from native', function(t) {
var Float32 = registry.lookupOrCreateConstructor(types.FLOAT32);
var tests = [
{
name: 'Undefined',
native: undefined,
jsval: JS({
'null': {}
}),
nativeFinal: null
},
{
name: 'Null',
native: null,
jsval: JS({
'null': {}
})
},
{
name: 'Boolean',
native: false,
jsval: JS({
'boolean': false
})
},
{
name: 'Number',
native: 4,
jsval: JS({
'number': 4
})
},
{
name: 'String',
native: 'abc',
jsval: JS({
'string': 'abc'
})
},
{
name: 'Bytes',
native: new Uint8Array([3, 55, 128]),
jsval: JS({
'bytes': new Uint8Array([3, 55, 128])
})
},
{
name: 'List of various things',
native: ['', 'pi', 3.14, true, null],
jsval: JS({
'list': [
JS({
'string': ''
}),
JS({
'string': 'pi'
}),
JS({
'number': 3.14
}),
JS({
'boolean': true
}),
JS({
'null': {}
})
]
})
},
{
name: 'Set of various things',
native: new Set(['', 'pi', 3.14, true, null]),
jsval: JS({
'set': [
JS({
'string': ''
}),
JS({
'string': 'pi'
}),
JS({
'number': 3.14
}),
JS({
'boolean': true
}),
JS({
'null': {}
})
]
})
},
{
name: 'Set with undefined',
native: new Set([null, undefined]),
jsval: JS({
'set': [
JS({
'null': {}
}),
JS({
'null': {}
}),
]
}),
nativeFinal: new Set([null])
},
{
name: 'Map of various things',
native: new Map([
['3', 3],
[false, null],
]),
jsval: JS({
'map': [
{
'key': JS({
'string': '3'
}),
'value': JS({
'number': 3
})
},
{
'key': JS({
'boolean': false
}),
'value': JS({
'null': {}
})
}
]
})
},
{
name: 'Object',
native: {},
jsval: JS({
'object': []
})
},
{
name: 'Object with private properties',
native: {
_private: 'Gonna be dropped',
survivor: 'I\'m gonna make it'
},
jsval: JS({
'object': [
{
'key': 'survivor',
'value': JS({
'string': 'I\'m gonna make it'
})
}
]
}),
nativeFinal: {
survivor: 'I\'m gonna make it'
}
},
{
name: 'Object with undefined',
native: {
'num': -9.4,
'str': '\n',
'abc': undefined
},
jsval: JS({
'object': [
{
'key': 'num',
'value': JS({
'number': -9.4
})
},
{
'key': 'str',
'value': JS({
'string': '\n'
})
},
{
'key': 'abc',
'value': JS({
'null': {}
})
}
]
}),
nativeFinal: {
'num': -9.4,
'str': '\n',
'abc': null // An object with an 'undefined' field value will be
// converted back to native form with 'null' in that field.
}
},
{
name: 'List with some typed values',
native: [3, false, null, 'abc', undefined, new Float32(3.14)],
jsval: JS({
'list': [
JS({
'number': 3
}),
JS({
'boolean': false
}),
JS({
'null': {}
}),
JS({
'string': 'abc'
}),
JS({
'null': {}
}),
new Float32(3.14)
]
}),
nativeFinal: [3, false, null, 'abc', null, new Float32(3.14)]
}
];
for (var i = 0; i < tests.length; i++) {
var name = tests[i].name;
var native = tests[i].native;
var jsval = tests[i].jsval;
// Note: Uses native if nativeFinal is not defined by the test case.
var nativeFinal = tests[i].nativeFinal;
if (nativeFinal === undefined) {
nativeFinal = native;
}
// Check that native => JSValue is ok and has the correct type.
var jsValueFromNative = jsValueConvert.fromNative(native);
t.deepEqual(jsValueFromNative, jsval, name + ' - from native');
t.equal(stringify(jsValueFromNative), stringify(jsval),
name + ' - from native (stringify)');
t.ok(jsValueFromNative._type === types.JSVALUE,
name + ' - from native has Type JSValue');
// Check that JSValue => native is ok.
var nativeFromJSValue = jsValueConvert.toNative(jsval);
t.deepEqual(nativeFromJSValue, nativeFinal,
name + ' - to native');
t.equal(stringify(nativeFromJSValue), stringify(nativeFinal),
name + ' - to native (stringify)');
// Check that native remains the same if converted to native.
var stillNative = jsValueConvert.toNative(native);
t.deepEqual(native, stillNative, name + ' - native to native is no-op');
t.equal(stringify(native), stringify(stillNative),
name + ' - native to native is no-op (stringify)');
// Check that jsval remains the same if converted from native.
var stillJSVal = jsValueConvert.fromNative(jsval);
t.deepEqual(jsval, stillJSVal, name + ' - jsval to jsval is no-op');
t.equal(stringify(jsval), stringify(stillJSVal),
name + ' - jsval to jsval is no-op (stringify)');
}
t.end();
});