js-checker
Version:
a type checker for javascript
825 lines (779 loc) • 28.4 kB
JavaScript
/**
* Created by zhangdianpeng on 2018/11/6.
*/
let assert = require('assert');
let {c, t, renderHtml, getHtml} = require('../index.js');
assert.throwError = (fn, params, message) => {
try{
fn.apply(null, params);
}catch(err){
return assert.deepEqual(err.message, message);
}
throw new Error(`it need to throw Error with message<${message}>`);
};
let wrapTest = (obj, typeName) => (fn) => {
let type = obj[typeName];
let dType = obj['D' + typeName]('description');
fn(type);
fn(dType);
};
describe('t', function(){
describe('t.Null and t.DNull', function(){
let wrapType = wrapTest(t, 'Null');
it('undefined test', function (){
wrapType(type => {
let result = type(undefined);
assert.deepEqual(result, undefined);
});
});
it('null test', function (){
wrapType(type => {
let result = type(null);
assert.deepEqual(result, null);
});
});
it('error test', function (){
wrapType(type => {
assert.throwError(type, [0], 'is not Null');
});
});
});
describe('t.Any and t.DAny', function(){
let wrapType = wrapTest(t, 'Any');
it('0 test', function (){
wrapType(type => {
let result = type(0);
assert.deepEqual(result, 0);
});
});
it('object test', function (){
wrapType(type => {
let result = type({a: 1});
assert.deepEqual(result, {a: 1});
});
});
it('error null test', function (){
wrapType(type => {
assert.throwError(type, [null], 'is Null');
});
});
});
describe('t.Num and t.DNum', function(){
let wrapType = wrapTest(t, 'Num');
it('0 test', function (){
wrapType(type => {
let result = type(0);
assert.deepEqual(result, 0);
});
});
it('"2" test', function (){
wrapType(type => {
let result = type('2');
assert.deepEqual(result, 2);
});
});
it('string test', function (){
wrapType(type => {
assert.throwError(type, ['2xx'], 'is not a Num');
});
});
it('[] test', function (){
wrapType(type => {
assert.throwError(type, [[]], 'is not a Num');
});
});
it('{} test', function (){
wrapType(type => {
assert.throwError(type, [{}], 'is not a Num');
});
});
it('empty string test', function (){
wrapType(type => {
assert.throwError(type, [''], 'is not a Num');
});
wrapType(type => {
assert.throwError(type, [' '], 'is not a Num');
});
});
});
describe('t.Str and t.DStr', function(){
let wrapType = wrapTest(t, 'Str');
it('0 test', function (){
wrapType(type => {
let result = type('0');
assert.deepEqual(result, '0');
});
});
it('"2" test', function (){
wrapType(type => {
let result = type('2');
assert.deepEqual(result, '2');
});
});
it('string test', function (){
wrapType(type => {
assert.throwError(type, [null], 'is not a Str');
});
});
});
describe('t.Fn and t.DFn', function(){
let wrapType = wrapTest(t, 'Fn');
it('fn test', function (){
wrapType(type => {
let fn = a => {};
let result = type(fn);
assert.deepEqual(result, fn);
});
});
it('string test', function (){
wrapType(type => {
assert.throwError(type, ['hello'], 'is not a function');
});
});
});
describe('t.Json and t.DJson', function(){
let wrapType = wrapTest(t, 'Json');
it('Array test', function (){
wrapType(type => {
let result = type([1, 2]);
assert.deepEqual(result, [1, 2]);
});
});
it('Object test', function (){
wrapType(type => {
let result = type({a: 1});
assert.deepEqual(result, {a: 1});
});
});
it('num test', function (){
wrapType(type => {
assert.throwError(type, [1], 'is not an Json');
});
});
it('null test', function (){
wrapType(type => {
assert.throwError(type, [null], 'is not an Json');
});
});
});
describe('t.Arr and t.DArr', function(){
let wrapType = wrapTest(t, 'Arr');
it('Array test', function (){
wrapType(type => {
let result = type([1, 2]);
assert.deepEqual(result, [1, 2]);
});
});
it('num test', function (){
wrapType(type => {
assert.throwError(type, [1], 'is not an Arr');
});
});
it('null test', function (){
wrapType(type => {
assert.throwError(type, [null], 'is not an Arr');
});
});
});
describe('t.Obj and t.DObj', function(){
let wrapType = wrapTest(t, 'Obj');
it('Object test', function (){
wrapType(type => {
let result = type({a: 1});
assert.deepEqual(result, {a: 1});
});
});
it('Array test', function (){
wrapType(type => {
assert.throwError(type, [[1, 2]], 'is not an Obj');
});
});
it('null test', function (){
wrapType(type => {
assert.throwError(type, [null], 'is not an Obj');
});
});
});
//t.Bool
describe('t.Bool and t.DBool', function(){
let wrapType = wrapTest(t, 'Bool');
it('true test', function (){
wrapType(type => {
let result = type(true);
assert.deepEqual(result, true);
});
});
it('"true" test', function (){
wrapType(type => {
let result = type("true");
assert.deepEqual(result, true);
});
});
it('false test', function (){
wrapType(type => {
let result = type(false);
assert.deepEqual(result, false);
});
});
it('"false" test', function (){
wrapType(type => {
let result = type("false");
assert.deepEqual(result, false);
});
});
it('0 test', function (){
wrapType(type => {
assert.throwError(type, [0], 'is not a Bool');
});
});
it('null test', function (){
wrapType(type => {
assert.throwError(type, [null], 'is not a Bool');
});
});
});
describe('t.Date and t.DDate', function(){
let wrapType = wrapTest(t, 'Date');
it('new Date test', function (){
wrapType(type => {
let date = new Date();
let result = type(date);
assert.deepEqual(result, date);
});
});
it('date number test', function (){
wrapType(type => {
let result = type(1541477782023);
assert.deepEqual(result, new Date(1541477782023));
});
});
it('str test', function (){
wrapType(type => {
assert.throwError(type, ['xxx'], 'is not a Date');
});
});
it('null test', function (){
wrapType(type => {
assert.throwError(type, [null], 'is not a Date');
});
});
});
describe('t.Time and t.DTime', function(){
let wrapType = wrapTest(t, 'Time');
it('01:00', function (){
wrapType(type => {
let result = type('01:00');
assert.deepEqual(result, '01:00');
});
});
it('12:00', function (){
wrapType(type => {
let result = type('01:00');
assert.deepEqual(result, '01:00');
});
});
it('1:22', function (){
wrapType(type => {
assert.throwError(type, ['1:22'], 'is not a Time');
});
});
it('01:2:22', function (){
wrapType(type => {
assert.throwError(type, ['01:2:22'], 'is not a Time');
});
});
it('str test', function (){
wrapType(type => {
assert.throwError(type, ['xxx'], 'is not a Time');
});
});
it('null test', function (){
wrapType(type => {
assert.throwError(type, [null], 'is not a Time');
});
});
});
});
describe('c', function(){
describe('c.Val and c.DVal', function(){
let wrapType = wrapTest(c, 'Val');
it('value test', function (){
wrapType(type => {
let result = type('helloworld')('helloworld');
assert.deepEqual(result, 'helloworld');
});
});
it('object test', function (){
wrapType(type => {
assert.throwError(type({a: 1}), [{a: 1}], 'is not eq to [object Object]');
});
});
it('error test', function (){
wrapType(type => {
assert.throwError(type('helloworld'), ['hello world'], 'is not eq to helloworld');
});
});
});
describe('c.OrVal and c.DOrVal', function(){
let wrapType = wrapTest(c, 'OrVal');
it('one of the values test', function (){
wrapType(oType => {
let type = oType('helloworld', 'hello world');
assert.deepEqual(type('helloworld'), 'helloworld');
assert.deepEqual(type('hello world'), 'hello world');
assert.throwError(type, ['hello-world'], 'is not eq to helloworld\nis not eq to hello world');
});
});
});
describe('c.Obj and c.DObj', function(){
let wrapType = wrapTest(c, 'Obj');
it('one of the values test', function (){
wrapType(oType => {
let type = oType({
age: t.Num,
nick: t.Str
});
assert.deepEqual(type({age: 13, nick: 'Lucy'}), {age: 13, nick: 'Lucy'});
assert.deepEqual(type({age: '13', nick: 'Lucy'}), {age: 13, nick: 'Lucy'});
assert.deepEqual(type({age: '13', nick: 'Lucy', school: 'hangzhou'}), {age: 13, nick: 'Lucy'});
assert.throwError(type, [{age: 13}], 'nick is not a Str');
assert.throwError(type, [null], 'is not an Obj');
});
});
});
describe('c.Arr and c.DArr', function(){
let wrapType = wrapTest(c, 'Arr');
it('one of the values test', function (){
wrapType(oType => {
let type = oType(c.Obj({
age: t.Num,
nick: t.Str
}));
assert.deepEqual(
type([{age: 13, nick: 'Lucy'}, {age: '14', nick: 'Lilin'}]),
[{age: 13, nick: 'Lucy'}, {age: 14, nick: 'Lilin'}]
);
assert.throwError(type, [{age: 13}], 'is not an Arr');
assert.throwError(type, ['age'], 'is not an Arr');
assert.throwError(type, [null], 'is not an Arr');
assert.throwError(type, [[{age: 13, nick: 'Lucy'}, {age: '14x', nick: 'Lilin'}]], '[1].age is not a Num');
});
});
});
describe('c.Or and c.DOr', function(){
let wrapType = wrapTest(c, 'Or');
it('one of the values test', function (){
wrapType(oType => {
let type = oType(t.Num, t.Arr);
assert.deepEqual(type(1), 1);
assert.deepEqual(type([1, 2]), [1, 2]);
assert.throwError(type, [{age: 13}], 'is not a Num\nis not an Arr');
assert.throwError(type, ['age'], 'is not a Num\nis not an Arr');
assert.throwError(type, [null], 'is not a Num\nis not an Arr');
});
});
it('or includes or', function (){
wrapType(oType => {
let type = oType(oType(c.Obj({a: t.Num})), oType(c.Obj({b: t.Num})));
assert.deepEqual(type({a: 1}), {a: 1});
assert.deepEqual(type({b: 2}), {b: 2});
assert.throwError(type, [{age: 13}], 'a is not a Num\nb is not a Num');
assert.throwError(type, ['age'], 'is not an Obj');
assert.throwError(type, [null], 'is not an Obj');
});
});
});
describe('c.Optional and c.DOptional', function(){
let wrapType = wrapTest(c, 'Optional');
it('Only Optional', function (){
wrapType(oType => {
let type = oType(t.Num);
assert.deepEqual(type(1), 1);
assert.deepEqual(type('1'), 1);
assert.deepEqual(type(null), null);
assert.deepEqual(type(undefined), undefined);
assert.throwError(type, [{age: 13}], 'is not Null\nis not a Num');
assert.throwError(type, ['age'], 'is not Null\nis not a Num');
});
});
it('Optional in Obj', function (){
wrapType(oType => {
let type = c.Obj({
nick: oType(t.Str),
age: t.Num
});
assert.deepEqual(type({age: 1, nick: 'Lucy'}), {age: 1, nick: 'Lucy'});
assert.deepEqual(type({age: 1}), {age: 1});
assert.throwError(type, [null], 'is not an Obj');
});
});
});
describe('c.Default and c.DDefault', function(){
let wrapType = wrapTest(c, 'Default');
it('Only Default', function (){
wrapType(oType => {
let type = oType(t.Num, 13);
assert.deepEqual(type(1), 1);
assert.deepEqual(type('1'), 1);
assert.deepEqual(type(undefined), 13);
assert.deepEqual(type(null), 13);
assert.throwError(type, ['hello'], 'is not a Num');
});
});
});
describe('c.Map and c.DMap', function(){
let wrapType = wrapTest(c, 'Map');
it('Only Map', function (){
wrapType(oType => {
let type = oType(t.Str, t.Num);
assert.deepEqual(type({
a: 1,
b: '2'
}), {
a: 1,
b: 2
});
assert.throwError(type, [{
a: 1,
b: 'hello'
}], 'the value<hello> of key<b> in Map is not a Num');
assert.throwError(type, [null], 'is not an Obj');
});
});
});
describe('c.Extend and c.DExtend', function(){
let wrapType = wrapTest(c, 'Extend');
it('Only Extend', function (){
wrapType(oType => {
let type = oType(
c.Obj({
age: t.Num
}),
c.Or(
c.Obj({
type: c.Val('Student'),
school: t.Str
}),
c.Obj({
type: c.Val('Worker'),
workplace: t.Str
})
)
);
assert.deepEqual(type({age: 13, type: 'Student', school: 'hangzhou'}), {age: 13, type: 'Student', school: 'hangzhou'});
assert.deepEqual(type({age: 13, type: 'Worker', workplace: 'hangzhou'}), {age: 13, type: 'Worker', workplace: 'hangzhou'});
assert.throwError(type, [{age: 13, type: 'Worker', school: 'hangzhou'}], 'type is not eq to Student\nworkplace is not a Str');
assert.throwError(type, [null], 'is not an Obj');
});
});
it('Extend not Obj and Or', function (){
wrapType(oType => {
assert.throwError(oType, [
t.Num,
c.Or(
c.Obj({
type: c.Val('Student'),
school: t.Str
}),
c.Obj({
type: c.Val('Worker'),
workplace: t.Str
})
)
], 'Extend does not support Num type');
});
});
it('Only Extend for Or before Obj', function (){
wrapType(oType => {
let type = oType(
c.Or(
c.Obj({
type: c.Val('Student'),
school: t.Str
}),
c.Obj({
type: c.Val('Worker'),
workplace: t.Str
})
),
c.Obj({
age: t.Num
})
);
assert.deepEqual(type({age: 13, type: 'Student', school: 'hangzhou'}), {age: 13, type: 'Student', school: 'hangzhou'});
assert.deepEqual(type({age: 13, type: 'Worker', workplace: 'hangzhou'}), {age: 13, type: 'Worker', workplace: 'hangzhou'});
assert.throwError(type, [{age: 13, type: 'Worker', school: 'hangzhou'}], 'type is not eq to Student\nworkplace is not a Str');
assert.throwError(type, [null], 'is not an Obj');
});
});
it('Extend for only Obj', function (){
wrapType(oType => {
let type = oType(
c.Obj({
age: t.Num
})
);
assert.deepEqual(type({age: 13}), {age: 13});
assert.throwError(type, [null], 'is not an Obj');
});
});
});
describe('c.OrValType and c.DOrValType', function(){
let wrapType = wrapTest(c, 'OrValType');
it('OrValType', function (){
wrapType(oType => {
let type = oType([1, 2], t.Num);
assert.deepEqual(type(1), 1);
assert.deepEqual(type(2), 2);
assert.deepEqual(type('2'), 2);
assert.deepEqual(type('1'), 1);
assert.throwError(type, ['str'], 'is not eq to 1\nis not eq to 2');
assert.throwError(type, [null], 'is not eq to 1\nis not eq to 2');
});
});
});
describe('c.ValType and c.DValType', function(){
let wrapType = wrapTest(c, 'ValType');
it('ValType', function (){
wrapType(oType => {
let type = oType(1, t.Num);
assert.deepEqual(type(1), 1);
assert.deepEqual(type('1'), 1);
assert.throwError(type, ['str'], 'is not eq to 1');
assert.throwError(type, [null], 'is not eq to 1');
});
});
});
describe('c.ValSet and c.DValSet', function(){
let wrapType = wrapTest(c, 'ValSet');
it('ValType', function (){
wrapType(oType => {
let type = oType('helloworld');
assert.deepEqual(type(1), 'helloworld');
assert.deepEqual(type(null), 'helloworld');
});
});
});
describe('c.ValConvert and c.DValConvert', function(){
let wrapType = wrapTest(c, 'ValConvert');
it('ValConvert', function (){
wrapType(oType => {
let type = oType('helloworld', 'hello-world');
assert.deepEqual(type('helloworld'), 'hello-world');
assert.throwError(type, ['hello-world'], 'is not eq to helloworld');
assert.throwError(type, [null], 'is not eq to helloworld');
});
});
});
describe('c.ObjConvert and c.DObjConvert', function(){
let wrapType = wrapTest(c, 'ObjConvert');
it('ObjConvert', function (){
wrapType(oType => {
let type = oType({
id: t.Str
}, 'id', 'tableId');
assert.deepEqual(type({id: 'hello'}), {tableId: 'hello'});
assert.throwError(type, ['hello-world'], 'is not an Obj');
assert.throwError(type, [null], 'is not an Obj');
});
});
});
describe('c.Fn and c.DFn', function(){
let wrapType = wrapTest(c, 'Fn');
it('Fn', function (){
wrapType(oType => {
let type = oType({
input: [t.Num, t.Str],
output: c.Val('ok')
});
let fn = type((age, nick) => 'ok');
assert(fn(12, 'Lucy'));
assert.throwError(fn, ['Lucy', 'Lucy'], 'The 0th parameter of the function is not a Num');
let fn1 = type((age, nick) => 'ok1');
assert.throwError(fn1, ['12', 'Lucy'], 'the output of the function is not eq to ok');
});
});
});
describe('c.Custom', function(){
it('Custom1', function (){
let type = c.Custom(
() => c.Obj({age: t.Num, nick: t.Str})
);
assert.deepEqual(type({age: 13, nick: 'zhangsan'}), {age: 13, nick: 'zhangsan'});
});
it('Custom2', function (){
let attribute = c.Obj({
age: t.Num,
nick: t.Str,
next: c.Optional(t.Num)
}).show.attribute;
attribute.next.attribute = attribute;
let type = c.Custom(
() => {
return c.Obj({
age: t.Num,
nick: t.Str,
next: c.Optional(type)
})
},
{attribute}
);
// console.dir(type.show, {depth: 10});
assert.deepEqual(
type({age: 13, nick: 'zhangsan', next: {age: 12, nick: 'lisi'}}),
{age: 13, nick: 'zhangsan', next: {age: 12, nick: 'lisi'}}
);
});
});
describe('c.Num', function(){
it('Num', function (){
let type = c.Num(1);
assert.deepEqual(type(1), 1);
type = c.Num(2);
assert.deepEqual(type(2), 2);
assert.throwError(type, ['lucy'], 'is not eq to 2');
});
});
describe('c.Str', function(){
it('Str', function (){
let type = c.Str(1);
assert.deepEqual(type(1), '1');
type = c.Str('2');
assert.deepEqual(type('2'), '2');
assert.throwError(type, ['lucy'], 'is not eq to 2');
});
});
describe('c.OrStr', function(){
it('OrStr', function (){
let type = c.OrStr('1', '2');
assert.deepEqual(type(1), '1');
assert.deepEqual(type('2'), '2');
assert.throwError(type, ['lucy'], 'is not eq to 1\nis not eq to 2');
});
});
describe('c.OrNum', function(){
it('OrNum', function (){
let type = c.OrNum(1, 2);
assert.deepEqual(type(1), 1);
assert.deepEqual(type('2'), 2);
assert.throwError(type, ['lucy'], 'is not eq to 1\nis not eq to 2');
});
});
describe('c.TagOr', function(){
it('TagOr with two tag', function (){
let type = c.TagOr(
[
c.Obj({
A: c.Str('a1'),
B: c.Str('b1'),
value: t.Num
}),
c.Obj({
A: c.Str('a2'),
B: c.Str('b2'),
value: t.Num
})
],
['A', 'B']
);
assert.deepEqual(type({A: 'a1', B: 'b1', value: 1}), {A: 'a1', B: 'b1', value: 1});
assert.throwError(type, ['lucy'], 'Tag definition<A,B> does not satisfy: a1-b1/a2-b2');
assert.throwError(type, [{A: 'a1', B: 'b1'}], 'value is not a Num');
});
it('TagOr with one tag', function (){
let type = c.TagOr(
[
c.Obj({
A: c.Str('a1'),
value: t.Num
}),
c.Obj({
A: c.Str('a2'),
value: t.Num
})
],
'A'
);
assert.deepEqual(type({A: 'a1', value: 1}), {A: 'a1', value: 1});
assert.throwError(type, ['lucy'], 'Tag definition<A> does not satisfy: a1/a2');
assert.throwError(type, [{A: 'a1', B: 'b1'}], 'value is not a Num');
});
it('TagOr spec error', function (){
assert.throwError(c.TagOr, [[
c.Obj({
A: c.Str('a1'),
value: t.Num
}),
c.Obj({
B: c.Str('a2'),
value: t.Num
})
], 'A'], 'these has a type does not satisfy the tag definition:A');
assert.throwError(c.TagOr, [[
c.Obj({
A: c.Str('a1'),
value: t.Num
}),
c.Obj({
A: c.Str('a2'),
value: t.Num
}),
c.Obj({
A: c.Str('a1'),
value: t.Num
})
], 'A'], 'Tag<A> repeat defined: a1');
assert.throwError(c.TagOr, [[
c.Obj({
A: c.Str('a1'),
value: t.Num
}),
c.Obj({
A: c.Str('a2'),
value: t.Num
}),
t.Num
], 'A'], 'TagOr does not support Num type');
});
});
});
describe('getHtml and renderHtml', function(){
it('getHtml and renderHtml', function (){
let type = c.DObj('type description')({
arr: t.Arr,
darr: t.DArr('dArr'),
num: t.Num,
dnum: t.DNum('dNum'),
or: c.Or(t.Arr, t.Num),
def: c.Default(t.Num, 11),
value1: c.Val('a'),
value5: c.Val(1),
value2: c.Val({a: 1}),
value3: c.Val(null),
value4: c.Val(true),
map: c.Map(t.Str, t.Num),
dmap: c.DMap('dMap')(t.Str, t.Num),
orValType: c.OrValType([1, 2], t.Num),
aorValType: c.DOrValType('DOrValType')([1, 2], t.Num),
tObj: t.Obj,
options: c.Optional(c.Arr(t.Num)),
doptions: c.DOptional('optional')(c.Arr(t.Num)),
extend: c.Extend(c.Obj({a: t.Num})),
cFn: c.DFn('this is a function')({
input: [t.Num, t.Str],
output: c.Obj({
a: t.Num,
b: t.Str
})
})
});
getHtml(type.show);
renderHtml(type.show);
getHtml(type.show, './lib/render/describe-test.html');
});
});
describe('assert.throwError test', function(){
it('assert.throwError test', function (){
assert.throws(() => assert.throwError(a => a));
});
});