object.js/test.js

1079 lines
28 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**********************************************************************
*
* TODO:
* - test:
* - ASAP: method calls from methods from different contexts:
* - constructor
* - instance
* - sub-instance
* - callable
* - .call(..) and friends
* - normalizeIndent(..)/normalizeTextIndent(..) -- tab_size values...
* - object.mixout(..)
* - callback STOP in object.mixins(..)
* - props arg in object.values(..)
* - RawInstance(..).toString()
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
var colors = require('colors')
var test = require('ig-test')
var doc = require('ig-doc')
var object = require('./object')
//---------------------------------------------------------------------
// helpers...
// compare two arrays by items...
var arrayCmp = function(a, b){
var ka = Object.keys(a)
var kb = Object.keys(a)
return a === b
|| (a.length == b.length
&& ka
// keep only non matching stuff...
.filter(function(k){
return a[k] !== b[k]
&& a[k] != a[k] })
.length == 0) }
// a constructor is a thing that starts with a capital and has a .prototype
var constructors = function(obj){
return Object.entries(obj)
.filter(function([k, o]){
return !k.startsWith('_')
&& k[0] == k[0].toUpperCase()
&& o.prototype }) }
// an instance is a thing that starts with a lowercase and has a .constructor
var instances = function(obj){
return Object.entries(obj)
.filter(function([k, o]){
return !k.startsWith('_')
&& k[0] == k[0].toLowerCase()
&& o.constructor }) }
// data wrapper...
var data = function(d){ return d }
// text test runner...
var testText = function(assert, func, input, expect){
var res
return assert((res = object[func](input)) == expect,
func +'(..):'
+'\n---input---\n'
+ (input instanceof Array ?
input[0]
: input)
+'\n---Expected---\n'
+ expect
+'\n---Got---\n'
+ res
+'\n---') }
//---------------------------------------------------------------------
// General tests...
var setups = test.Setups({
// basic constructor and inheritance...
//
// X
// Y <- A <- B <- C <- D <- E
//
// This will test:
// - object creation
// - basic inheritance
// - general usecase
// - .__extends__
// - method overloading
// - .parent(..)
// - .parentCall(..)
// - .parentProperty(..)
// - constructor methods (XXX not done...)
//
basic: function(assert){
var X, Y, A, B, C, D, E
return {
X: X = assert(object.Constructor('X'), `.Constructor(..)`),
Y: Y = assert(object.C('Y', {
method: function(){
var x
assert(
(x = object.parentCall(Y.prototype.method, this, ...arguments)) === undefined,
'y.method(..): expected:', undefined, 'got:', x)
return 'Y'
},
}), `.C(..)`),
A: A = assert(object.C('A', Y, {
get prop(){
return 'A.prop' },
method: function(){
var x
assert(
(x = object.parentCall(A.prototype.method, this, ...arguments)) == 'Y',
'a.method(..): expected:', 'Y', 'got:', x)
return 'A'
},
}), `inherit (gen1)`),
B: B = assert(object.C('B', A, {
// XXX constructor methods...
testRelations: function(){
assert(object.parentOf(A, this),
'parentOf(A, B): expected to be true')
assert(object.childOf(this, A),
'childOf(B, A): expected to be true')
assert(!object.parentOf(X, this),
'parentOf(X, B): expected to be false')
assert(object.parentOf(A, E),
'parentOf(A, E): expected to be true')
assert(object.childOf(E, A),
'childOf(E, A): expected to be true')
assert(object.related(A, E) && object.related(E, A),
'related(A, E) and related(E, A): expected to be true')
assert(!object.related(X, E)
&& !object.related(E, X),
'related(X, E) and related(E, X): expected to be flase')
},
}, {
get prop(){
return 'B.prop' },
// XXX methods...
}), `inherit (gen2) with constructor mixin`),
C: C = assert(object.C('C', B, {
method: function(){
var x
assert(
(x = object.parentCall(C.prototype.method, this, ...arguments)) == 'A',
'c.method(..): expected:', 'A', 'got:', x)
assert(this.prop == 'B.prop',
'get property value')
// NOTE: these get "next visible" not "outside current object",
// this is intentional, the "outside" value is simply
// accessible via:
// C.prototype.prop
assert(object.parent(C.prototype, 'prop') == 'A.prop',
'get parent property value')
assert(object.parentProperty(C.prototype, 'prop').get() == 'A.prop',
'get parent property')
assert(object.parentProperty(C.prototype, 'does-not-exist') === undefined,
'get non-existent property')
return 'C'
},
}), `inherit (gen3)`),
D: D = assert(object.C('D', {}, {
__extends__: C,
method: function(){
var x
assert(
(x = object.parentCall(D.prototype.method, this, ...arguments)) == 'C',
'c.method(..): expected:', 'C', 'got:', x)
return 'D'
},
}), '.__extends__ test'),
E: E = assert(object.C('E', {
__extends__: C,
}, {
method: function(){
var x
assert(
(x = object.parentCall(D.prototype.method, this, ...arguments)) === undefined,
'c.method(..): expected:', undefined, 'got:', x)
return 'E'
},
}), '.__extends__ test'),
} },
// initialization...
init: function(assert){
var A, B, C, D
return {
// init...
A: A = assert(object.C('A', {
msg: '.__init__()',
__init__: function(){
this.init_has_run = true },
test_init: function(){
this.__created_raw ?
assert(!this.init_has_run, this.msg+' did not run')
: assert(this.init_has_run, this.msg+' run') },
}), 'basic .__init__(..)'),
// new...
B: B = assert(object.C('B', {
__new__: function(){
var o = {}
o.new_has_run = true
return o
},
test_new: function(){
assert(this instanceof B, 'instanceof correct')
assert(this.new_has_run, '.__new__() run') },
}), 'basic .__new__(..)'),
// new + init...
C: C = assert(object.C('C', B, {
msg: '.__init__() after .__new__()',
__init__: function(){
this.init_has_run = true },
test_init: A.prototype.test_init,
}), `inherit .__new__()`),
// XXX
} },
// callable instances...
call: function(assert){
// constructors...
var A, B, C, D, F, G
var res = {
A: A = assert(object.C('A',
function(){
return 'A'
}), 'callable'),
B: B = assert(object.C('B', {
__non_function: true,
__call__: function(){
assert(
object.parentCall(B.prototype, '__call__', this, ...arguments) === undefined,
'call non-existent parent method', 'B')
return 'B'
},
}), 'callable'),
C: C = assert(object.C('C', A, {}), 'basic inherit'),
D: D = assert(object.C('D', B, {}), 'basic inherit'),
E: E = assert(object.C('E', A,
function(){
assert(
object.parentCall(E.prototype, '__call__', this, ...arguments) == 'A',
'parrent call')
return 'E'
}), 'call parent'),
F: F = assert(object.C('F', B, {
__call__: function(){
assert(
object.parentCall(F.prototype, '__call__', this, ...arguments) == 'B',
'parent call')
return 'F'
},
}), 'call parent\'s .__call__'),
}
// create instances...
var objs = tests.instance(assert, res)
// all instances must be callable...
// NOTE: not all instances are going to be instanceof Function...
Object.entries(objs)
.forEach(function([k, o]){
assert(typeof(o) == 'function', 'instance is callable', k) })
return Object.assign(res, objs) },
// inherit from native constructors...
native: function(assert){
return [
Object,
Array,
Number,
Map,
Set,
].reduce(function(res, type){
var n = type.name
// direct inherit...
var O = res[n] =
assert(object.C(n, type, {}), 'inherit from '+n)
return res
}, {}) },
// compatibility: native constructors...
js_constructors: function(assert){
return {
Object,
Array,
Number,
Map,
Set,
}},
// compatibility: prototype tree...
js_prototype: function(assert){
var a, b, c, d
return {
a: a = {
x: 'a',
method: function(){
return 'a' },
},
b: b = {
__proto__: a,
x: 'b',
},
c: c = {
__proto__: b,
x: 'c',
method: function(){
var x, y
assert.array(
[...object.values(c, 'x')],
['c', 'a', 'b'],
'reach all values of attr')
assert.array(
[...object.values(c, 'x')]
.map(function(v){
return v.toUpperCase() }),
['C', 'A', 'B'],
'reach all values of attr')
assert.array(
[...object.sources(c, 'method')],
// NOTE: not passing an explicit list as we need
// to account for mixins...
[...object.sources(c)]
.filter(function(s){
return s.hasOwnProperty('method') }),
'reach all values of method')
assert(
(x = object.parent(c, 'x')) == 'b',
'reach parent attr: expected:', 'b', 'got:'.bold.yellow, x)
assert(
(x = object.parentCall(c.method, this)) == (y = a.method()),
'reach parent method: expected:', y, 'got:'.bold.yellow, x)
return 'c' },
},
d: d = {
__proto__: c,
method: function(){
assert(object.parentCall(d.method, this) == 'c', 'reach parent method', 'd')
return 'd' },
},
}},
// compatibility: class/instance...
js_class: function(assert){
var X, Y, Z
return {
X: X = class {
x = 'x'
method(){
return 'x' }
},
Y: Y = class extends X {
x = 'y'
},
Z: Z = class extends Y {
x = 'z'
method(){
// XXX this is almost the same as for js_prototype...
assert.array(
[...object.values(c, 'x')],
['z', 'y', 'x'],
'reach all values of attr (class)')
assert.array(
[...object.values(c, 'x')]
.map(function(v){
return v.toUpperCase() }),
['C', 'A', 'B'],
'reach all values of attr (class)')
assert.array(
[...object.sources(c, 'method')],
[Z.prototype, X.prototype],
'reach all values of method (class)')
assert(
object.parent(c, 'x') == super.x,
'reach super attr (class)')
assert(
object.parentCall(c.method, this) == super.method(),
'reach super method (class)')
return 'c' }
},
}},
})
var modifiers = test.Modifiers({
// make gen2-3 constructors...
//
// NOTE: there is almost no need to test below gen3...
gen2: function(assert, setup, gen){
gen = gen || 2
return constructors(setup)
.reduce(function(res, [n, O]){
res[n+'g'+gen] = object.C(n+'g'+gen, O, {})
return res }, {}) },
gen3: function(assert, setup){
return this.gen2(assert, this.gen2(assert, setup), '3') },
// create instance clones via create(..)
//
clones: function(assert, setup){
return instances(setup)
.reduce(function(res, [k, o]){
res[k] = object.create(o)
return res }, {}) },
// generate instances...
//
// NOTE: these are re-used as tests too...
instance: function(assert, setup, mode){
return constructors(setup)
.reduce(function(res, [k, O]){
// native JS constructors do not support no_new or raw modes...
if((mode == 'raw' || mode == 'no_new') && !O.__rawinstance__){
return res }
// create instance with lowercase name of constructor...
// NOTE: constructor is expected to be capitalized...
var o = res[k.toLowerCase()] =
mode == 'no_new' ?
assert(O(), `new:`, k)
: mode == 'raw' ?
assert(O.__rawinstance__(), `.__rawinstance__()`, k)
: assert(new O(), `new:`, k)
assert(o instanceof O, `instanceof:`, k)
O.__proto__ instanceof Function
// XXX need to test this for constructor mixins too...
&& !(O.__mixin_constructors && !O.__mixin_flat)
&& assert(o instanceof o.constructor.__proto__, `instanceof-nested:`, k)
assert(o.constructor === O, `.constructor:`, k)
assert(o.__proto__ === O.prototype, `.__proto__:`, k)
return res }, {}) },
instance_no_new: function(assert, setup){
return this.instance(assert, setup, 'no_new') },
// NOTE: here we mark the raw instances with .__created_raw, this is
// done to be able to distinguish them from fully initialized
// instances...
instance_raw: function(assert, setup){
var res = this.instance(assert, setup, 'raw')
Object.values(res)
.forEach(function(e){
Object.assign(
e,
{__created_raw: true}) })
return res },
// mixins...
// NOTE: running this in flat mode will have side-effects -- overwriting
// existing attributes and methods...
// XXX might be a good idea to get the method name from the context... how?
mixin_instance: function(assert, setup, flat, filter, get){
filter = filter || instances
var attr = '__mixin_' + filter.name
var mixin = setup[attr] = {
// NOTE: in the real world mixins should have no state, just
// methods...
__mixin: true,
[attr]: true,
__mixin_flat: !!flat,
method: function(){
var res = object.parent(mixin.method, this) !== undefined ?
assert(
object.parentCall(mixin.method, this, ...arguments),
'mixin method parent call')
: false
return res
|| 'mixin' },
mixinMethod: function(){
return 'mixin' },
}
mixin[attr] = mixin
filter(setup)
.forEach(function([n, o]){
o = get ? get(o) : o
// mixin once per chain...
if(!o || o[attr]){
return }
assert(!object.hasMixin(o, mixin), 'pre mixin test', n)
assert(flat ?
object.mixinFlat(o, mixin)
: object.mixin(o, mixin),
flat ?
'mixin (flat)'
:'mixin', n)
assert(object.hasMixin(o, mixin), 'mixin test', n)
assert(o.mixinMethod() == 'mixin', 'mixin method call')
})
return setup },
mixin_instance_flat: function(assert, setup){
return this.mixin_instance(assert, setup, true) },
mixin_constructor: function(assert, setup, flat){
return this.mixin_instance(assert, setup, false, constructors) },
mixin_constructor_proto: function(assert, setup, flat){
return this.mixin_instance(assert, setup, false, constructors,
function(o){
// skip mixing into Object.prototype...
return o !== Object
&& o.prototype }) },
mixin_constructor_flat: function(assert, setup){
return this.mixin_constructor_proto(assert, setup, true) },
/*/ XXX
mixout: function(assert, setup){
return {}
},
//*/
// sanity checks...
//
// NOTE: these should have no side-effects but since we can run
// them why not run them and verify ;)
get methods(){ return tests.methods },
get constructor_methods(){ return tests.constructor_methods },
get callables(){ return tests.callables },
})
var tests = test.Tests({
// instance creation...
instance: modifiers.instance,
instance_no_new: modifiers.instance_no_new,
instance_raw: modifiers.instance_raw,
/*/ XXX
attributes: function(assert, setup){
return {} },
//*/
// methods...
methods: function(assert, setup){
instances(setup)
.forEach(function([k, o]){
object.deepKeys(o)
.forEach(function(m){
typeof(o[m]) == 'function'
// skip special methods...
&& !m.startsWith('__')
&& o[m]() }) })
return setup },
constructor_methods: function(assert, setup){
constructors(setup)
.forEach(function([k, O]){
object.deepKeys(O)
.forEach(function(m){
typeof(O[m]) == 'function'
// skip special methods...
&& !m.startsWith('__')
&& O[m]() }) })
return setup },
// callables...
callables: function(assert, setup){
// test special case .values(x, '__call__')
var test = function(obj, name){
var a, b
return assert(arrayCmp(
a = [...object.values(obj, '__call__')]
.map(function(func){
return func.call(obj) })
.flat(),
// get all callables in prototype chain and call them...
b = [...object.sources(obj)]
.filter(function(o){
return typeof(o) == 'function'
|| o.hasOwnProperty('__call__') })
.map(function(o){
return o.hasOwnProperty('__call__') ?
o.__call__.call(obj)
// NOTE: not all callables are instances of Function...
: Reflect.apply(Function.prototype, o, [obj]) })),
'values of .__call__ of '+ name +': got:', a, 'expected:', b) }
instances(setup)
.filter(function([_, o]){
// NOTE: not all callables are instances of Function...
return typeof(o) == 'function'
|| !!o.__call__ })
.forEach(function([k, o]){
o.__non_function ?
assert(!(o instanceof Function), 'non-instanceof Function', k)
: assert(o instanceof Function, 'instanceof Function', k)
typeof(o) == 'function'
&& assert(o(), 'call', k)
assert(o.call(), '.call(..)', k)
assert(o.apply(), 'apply(..)', k)
assert(o.bind(null)(), '.bind(..)(..)', k)
test(o, k)
})
return setup },
})
var cases = test.Cases({
'as-is-new': function(assert){
var A = object.C('A', {
__init__: function(){
this.init_A_did_run = true },
})
var B = object.C('B', {
__new__: function(){
return object.ASIS(A()) },
})
var b = B()
assert(b instanceof A, 'not instance of A')
assert(!(b instanceof B), 'not instance of B')
var C = object.C('C', {
__new__: function(){
return object.ASIS(A()) },
__init__: function(){
this.init_C_did_run = true },
})
var c = C()
assert(c.init_A_did_run, 'base .__init__() ran')
assert(c.init_C_did_run, 'constructor .__init__() ran')
},
'edge-cases': function(assert){
// bad names...
assert.error('Constructor(..): malformed name',
function(){
var X = object.Constructor('bad name', {}) })
assert.error('create(..): malformed name',
function(){
object.create('bad name', function(){}) })
assert.error('double __extends__ fail', function(){
var X = object.C('X',
{ __extends__: Object },
{ __extends__: Function }) })
// native constructor...
assert.array(
object.RawInstance(null, Array, 'a', 'b', 'c'),
['a', 'b', 'c'],
'native constructor')
assert(object.RawInstance(null, Number, '123') == 123, 'native constructor')
var x, y
// object.match(..)
assert(object.match(x = {a: 123, b: '333', c: []}, x) === true, 'match self')
assert(object.match(x, {a: x.a, b: x.b, c: []}) == false, 'strict mismatch')
assert(object.match(x, {a: 123, b: 333, c: x.c}, true) === true, 'non-strict match')
// object.matchPartial(..)
assert(object.matchPartial(x, {a: x.a}, true) === true, 'non-strict partial match')
assert(object.matchPartial(x, {a: x.a, b: x.b, c: x.c}) === true, 'strict partial match')
// object.parent(..)
assert(object.parent({}) === {}.__proto__, 'basic proto')
assert.error('.parent(..) of anonymous function', function(){
object.parent(function(){}, {}) })
// object.values(..)
var obj = Object.create({x: 123})
obj.x = 321
assert.array(
[...object.values(obj, 'x', true)]
.map(function(e){ return e.value }),
// XXX assert ignores the order here -- this should fail...
[123, 321], '.values(.., true) ')
/* XXX GENERATOR not relevant...
assert(
object.values(obj, 'x', function(){ return object.STOP })[0] == 321,
// XXX assert ignores the order here -- this should fail...
'.values(.., func) ')
assert(
object.values(obj, 'x', function(){ return object.STOP }, true)[0].value == 321,
// XXX assert ignores the order here -- this should fail...
'.values(.., func, true) ')
assert(
object.values(obj, 'x', function(){ return object.STOP(555) }, true)[0] == 555,
// XXX assert ignores the order here -- this should fail...
'.values(.., func, true) with explicit stop value')
//*/
},
deepKeys: function(assert){
var a = {
a: true
}
var b = {
__proto__: a,
b: true,
}
Object.defineProperty(b, 'h', {
enumerable: false,
value: true,
})
var c = {
__proto__: b,
c: true,
}
assert.array(object.deepKeys(c), ['c', 'b', 'a'], 'full chain')
assert.array(object.deepKeys(c, a), ['c', 'b', 'a'], 'full chain')
assert.array(object.deepKeys(c, b), ['c', 'b'], 'partial chain')
assert.array(object.deepKeys(c, c), ['c'], 'partial chain')
var o = Object.keys(Object.getOwnPropertyDescriptors(Object.prototype))
assert.array(object.deepKeys(c, true), ['c', 'b', 'h', 'a', ...o], 'full chain (non-enumerable)')
assert.array(object.deepKeys(c, a, true), ['c', 'b', 'h', 'a'], 'full chain (non-enumerable)')
assert.array(object.deepKeys(c, b, true), ['c', 'b', 'h'], 'partial chain (non-enumerable)')
assert.array(object.deepKeys(c, c, true), ['c'], 'partial chain (non-enumerable)')
},
funcMethods: function(assert){
var X = object.C('X', {
__call__: function(){
return true },
})
var x = new X()
assert(x(), 'x()')
assert(x.call(null), 'x.call(null)')
var xx = Object.create(x)
assert(typeof(xx.call) == 'function', 'xx.call is a function')
assert(xx.call(null), 'xx.call(null)')
},
mixins: function(assert){
var A = assert(object.Mixin('A', {
a: 'aaa',
}), 'Mixin("A", ...)')
assert(A.proto.a == 'aaa', 'A content')
assert(A.mode == 'proto', 'A mode')
var B = assert(object.Mixin('B', 'flat', {
b: 'bbb'
}), 'Mixin("B", "flat", ..)')
assert(B.proto.b == 'bbb', 'B content')
assert(B.mode == 'flat', 'B mode')
var C = assert(object.Mixin('C',
A,
B,
{
get c(){
return 'ccc' },
}), 'Mixin("C", A, B, ...)')
assert(C.proto.a == 'aaa', 'C content from A')
assert(C.proto.b == 'bbb', 'C content from B')
assert(C.proto.c == 'ccc', 'C content local')
var x = assert(C({}), 'C({})')
assert(x.a == 'aaa', 'mixin content from C')
assert(x.b == 'bbb', 'mixin content from C')
assert(x.c == 'ccc', 'mixin content from C')
// flat mode...
var y = assert(C('flat', {}), `C("flat", {})`)
assert(y.a == 'aaa', 'mixin content from C')
assert(y.b == 'bbb', 'mixin content from C')
assert(y.c == 'ccc', 'mixin content from C')
// mixout...
assert('a' in x == true, 'pre-mixout content present')
assert('b' in x == true, 'pre-mixout content present')
assert('c' in x == true, 'pre-mixout content present')
assert(C.mixout(x) === x, 'C.mixout(..)')
assert('a' in x == false, 'post-mixout content gone')
assert('b' in x == false, 'post-mixout content gone')
assert('c' in x == false, 'post-mixout content gone')
var z = assert(C('soft', {a:'zzz'}), `C("flat", {})`)
assert(z.a == 'zzz', 'no overwrite')
assert(z.b == 'bbb', 'mixin content from C')
assert(z.c == 'ccc', 'mixin content from C')
/*/ XXX
assert(C.mixout(z) === z, 'C.mixout(..) partial')
assert('a' in z, 'post-mixout (partial) non mixed content retained')
assert('b' in z == false, 'post-mixout (partial) content gone')
assert('c' in z == false, 'post-mixout (partial) content gone')
//*/
},
'extending-call': function(assert){
var A = object.C('A', {
__call__: function(){
return ['A'] },
})
var B = object.C('B', A, {
__call__: function(){
return object.parentCall(B.prototype, '__call__', this).concat(['B']) },
//return object.parentCall(B.prototype.__call__, this).concat(['B']) },
})
var C = object.C('C', B, {
__call__: function(){
// XXX BUG: this will break..
// the broblem is in that:
// object.parent(C.prototype, '__call__', this)
// object.parent(C.prototype.__call__, this)
// ..are not the same...
// the problem is that .source(..) call @object.js:531
// will get the instance (c) when getting by method while
// it should get the parent, i.e. c.__proto__ / c.constructor.prototype
return object.parentCall(C.prototype.__call__, this).concat(['C']) },
})
var a = A()
var b = B()
var c = C()
// XXX BUG: this will fail....
assert(
object.parent(C.prototype, '__call__', c)
=== object.parent(C.prototype.__call__, c), '...')
assert.array(a(), ['A'], 'call')
assert.array(b(), ['A', 'B'], 'call')
assert.array(c(), ['A', 'B', 'C'], 'call')
},
'docs': function(assert){
var func = function(){
console.log('some string...') }
var func_w_tostring = Object.assign(
function(){
console.log('some string...') },
{ toString: function(){
return 'func_w_tostring(){ .. }' } })
var callable_a =
object.Constructor('callable_a',
function(){
console.log(this.constructor.name) })()
var callable_b =
object.Constructor('callable_b', {
__call__: function(){
console.log(this.constructor.name) },
})()
var callable_c =
object.Constructor('callable_c', {
__call__: function(){
console.log(this.constructor.name) },
toString: function(){
return this.constructor.name + '.toString()' },
})()
// .toString(..)
assert(
callable_b.toString() == doc.normalizeIndent(callable_b.__call__.toString()),
'toString(..) proxy to .__call__(..)')
assert(
callable_b.toString() != doc.normalizeIndent(callable_b.__proto__.toString()),
'toString(..) proxy not to .__proto__.toString(..)')
assert(
callable_c.toString() == callable_c.__proto__.toString(),
'toString(..) proxy to .toString(..)')
// create(..)...
assert(
object.create(func).toString() == doc.normalizeIndent(func.toString()),
'create(..): function .toString() proxy (builtin).')
assert(
object.create(func_w_tostring).toString() == func_w_tostring.toString(),
'create(..): function .toString() proxy (custom).')
assert(
object.create(callable_a).toString() == callable_a.toString(),
'create(..): callable .toString() proxy (func).')
assert(
object.create(callable_b).toString() == callable_b.toString(),
'create(..): callable .toString() proxy (__call__).')
assert(
object.create(callable_c).toString() == callable_c.toString(),
'create(..): callable .toString() proxy (__call__ w. custom toString).')
},
})
//---------------------------------------------------------------------
// Text util tests...
var text = test.TestSet()
test.Case('text', text)
// XXX still need to test tab_size values (0, ...)
// ...a good candidate for modifiers...
// XXX naming is not clear -- should be close to automatic...
// XXX still need to test tab_size values (0, ...)
text.setups({
// sanity checks...
'""': data({
input: '',
all: '', }),
'"abc"': data({
input: 'abc',
all: 'abc'}),
'leading whitespace': data({
input: '\n\t\tabc',
all: 'abc'}),
'trailing whitespace': data({
input: 'abc\n\t \n',
all: 'abc'}),
'leading and trailing whitespace': data({
input: '\n\t \tabc\n\t \n',
all: 'abc'}),
// NOTE: there is no way to know what is the indent of 'a'
// relative to the rest of the text...
text_04: data({
input: `a
c`,
text: 'a\nc',
doc: 'a\n c'}),
text_05: data({
input: `a\nc`,
text: 'a\nc',
doc: 'a\nc'}),
text_06: data({
input: `\
a
c`,
all: 'a\n c'}),
text_07: data({
input: `
a
c`,
all: 'a\n c'}),
text_08: data({
input: `
a
c`,
all: 'a\n c' }),
text_09: data({
input: `a
b
c`,
text: 'a\nb\nc',
doc: 'a\n b\n c' }),
text_10: data({
input: `
a
b
c`,
all: 'a\nb\n c' }),
text_11: data({
input: `
a
b
c`,
all: 'a\n b\n c' }),
text_12: data({
input: `a
b
c`,
all: `a\n b\nc` }),
text_13: data({
input: `a
b
c`,
all: `a\nb\n c` }),
text_14: data({
input: `
a
b
c`,
all: `a\n b\n c` }),
text_15: data({
input: `a
b
b
b
c`,
all: `a\n b\n b\n b\nc` }),
text_16: data({
input: `a
b
b
b
c`,
all: `a\n b\n b\n b\nc` }),
text_17: data({
input: `
a
b
c`,
all: `a\n b\nc` }),
text_18: data({
input: `a
b
c
d`,
text: `a\n b\nc\nd`,
// XXX not sure about this...
doc: `a\n b\n c\n d` }),
})
text.tests({
doc: function(assert, data){
var e = data.doc != null ?
data.doc
: data.all
data.doc != null
&& testText(assert, 'normalizeIndent', data.input, data.doc)
&& testText(assert, 'doc', [data.input], data.doc) },
text: function(assert, data){
var e = data.text != null ?
data.text
: data.all
e != null
&& testText(assert, 'normalizeTextIndent', data.input, e)
&& testText(assert, 'text', [data.input], e) },
})
//---------------------------------------------------------------------
typeof(__filename) != 'undefined'
&& __filename == (require.main || {}).filename
&& test.run()
/**********************************************************************
* vim:set ts=4 sw=4 nowrap : */ return module })