define-configurable
Version:
Defines new (or modifies existing) properties (using Object.defineProperty, setting `configurable: true` by default
215 lines (202 loc) • 4.7 kB
JavaScript
const test = require('tape')
const base = require('brisky-base')
const Base = require('brisky-base/base')
const define = require('../')
define.call(Base.prototype, { define: define })
test('define', t => {
t.plan(6)
var subject = {}
var value = 'v'
var fn = function () {}
var obj = base()
var set = function (val) {
t.equal(val, value, 'value is the same')
}
var extras = [
{ one: true },
{ three: fn },
{
four: {
get: fn,
set: set
}
},
{
a: obj,
b: fn
}
]
define.apply(subject, extras)
equal('two', void 0)
equal('three', { value: fn })
equal('four', { get: fn, set: set })
equal('a', { value: obj })
equal('b', { value: fn })
// Let's trigger the setter test
subject.four = value
function equal (field, val) {
var descriptor
if (val !== void 0) {
descriptor = {
configurable: true,
enumerable: false
}
if (val.value && !val.hasOwnProperty('writable')) {
descriptor.writable = false
}
if (val) {
for (let i in val) {
descriptor[i] = val[i]
}
}
}
t.deepLooseEqual(
Object.getOwnPropertyDescriptor(subject, field), descriptor,
'"' + field + '" is defined correctly'
)
}
})
test('define - extend', t => {
t.plan(4)
var obj = {}
define.call(obj, {
method (number) {
return number * 2
}
})
define.call(obj, {
extend: {
method (extend, number) {
return extend(number) * 10
}
}
})
t.equal(obj.method(100), 2000, 'extended "method"')
try {
define.call(obj, {
extend: { method: true }
})
} catch (e) {
t.equal(
e.message,
'Expect function for extension "method"',
'throws error on incorrect type'
)
}
obj.render = {}
define.call(obj.render, {
state (number) {
return number * 2
}
})
define.call(obj, {
extend: {
render: {
state (extend, number) {
return extend(number) * 10
}
}
}
})
t.equal(obj.render.state(100), 2000, 'nested extended "method"')
define.call(obj, {
extend: {
render: {
state (extend, number, extra) {
return extra
}
}
}
})
t.equal(obj.render.state(false, '!'), '!', 'extend can add extra arguments')
})
test('define - extend - base', t => {
const b = base()
b.define({
method () {
// never return "arguments" its a v8 hazzard!
const len = arguments.length
const args = new Array(len)
for (let i = 0; i < len; i++) {
args[i] = arguments[i]
}
return args
},
extend: {
set (set, val, stamp) {
this._last = val
return set.call(this, val, stamp)
}
}
})
b.set({ field: true })
t.equal('field' in b, true, 'set overwritten correctly for base')
const c = new b.Constructor({
define: {
extend: {
set (set, val, stamp) {
return set.call(this, val, stamp)
}
}
}
})
c.set({ bla: true })
t.same(c._last, { bla: true }, 'inherits first extension')
t.equal('bla' in c, true, 'set overwritten correctly for base')
c.define({
extend: {
args: true,
method (method) {
const len = arguments.length
const args = new Array(len - 1)
for (let i = 1; i < len; i++) {
args[i - 1] = arguments[i]
}
return method.apply(this, args)
}
}
})
t.same(c.method(1, 2, 3, 4, 5), [ 1, 2, 3, 4, 5 ], 'using the "args:true" flag')
for (let i = 0; i < 10; i++) {
extendTest(i, t)
}
t.end()
})
function extendTest (amount, t) {
const b = base()
const args = []
for (let i = 0; i < amount; i++) {
args.push('a' + i)
}
const fnArgs = `(${args.join(',')})`
const fn = args.length ? new Function( //eslint-disable-line
`return function ${fnArgs} {
var len = arguments.length
var args = new Array(len)
for (var i = 0; i < len; i++) {
args[i] = arguments[i]
}
return args
}`)() : function () {}
b.define({ method: fn })
const c = new b.Constructor({
define: {
extend: {
method (method) {
const len = arguments.length
if (len > 1) {
const args = new Array(len - 1)
for (let i = 1; i < len; i++) {
args[i - 1] = arguments[i]
}
return method.apply(this, args)
} else {
return method.call(this)
}
}
}
}
})
t.same(c.method.apply(c, args), args.length ? args : void 0, `return correct for ${amount} arguments`)
}