simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

construction.test.js (9996B)


      1 // test parse
      2 var assert = require('assert');
      3 var typed = require('../typed-function');
      4 
      5 describe('construction', function() {
      6 
      7   it('should throw an error when not providing any signatures', function() {
      8     assert.throws(function () {
      9       typed({});
     10     }, /Error: No signatures provided/);
     11   });
     12 
     13   it('should create a named function', function() {
     14     var fn = typed('myFunction',  {
     15       'string': function (str) {
     16         return 'foo';
     17       }
     18     });
     19 
     20     assert.equal(fn('bar'), 'foo');
     21     assert.equal(fn.name, 'myFunction');
     22   });
     23 
     24   it('should create a typed function from a regular function with a signature', function() {
     25     function myFunction(str) {
     26       return 'foo';
     27     }
     28     myFunction.signature = 'string'
     29 
     30     var fn = typed(myFunction);
     31 
     32     assert.equal(fn('bar'), 'foo');
     33     assert.equal(fn.name, 'myFunction');
     34     assert.deepEqual(Object.keys(fn.signatures), ['string']);
     35   });
     36 
     37   it('should create an unnamed function', function() {
     38     var fn = typed({
     39       'string': function (str) {
     40         return 'foo';
     41       }
     42     });
     43 
     44     assert.equal(fn('bar'), 'foo');
     45     assert.equal(fn.name, '');
     46   });
     47 
     48   it('should inherit the name of typed functions', function() {
     49     var fn = typed({
     50       'string': typed('fn1', {
     51         'string': function (str) {
     52           return 'foo';
     53         }
     54       })
     55     });
     56 
     57     assert.equal(fn('bar'), 'foo');
     58     assert.equal(fn.name, 'fn1');
     59   });
     60 
     61   it('should not inherit the name of the JavaScript functions (only from typed functions)', function() {
     62     var fn = typed({
     63       'string': function fn1 (str) {
     64         return 'foo';
     65       }
     66     });
     67 
     68     assert.equal(fn('bar'), 'foo');
     69     assert.equal(fn.name, '');
     70   });
     71 
     72   it('should compose a function with zero arguments', function() {
     73     var signatures = {
     74       '': function () {
     75         return 'noargs';
     76       }
     77     };
     78     var fn = typed(signatures);
     79 
     80     assert.equal(fn(), 'noargs');
     81     assert(fn.signatures instanceof Object);
     82     assert.strictEqual(Object.keys(fn.signatures).length, 1);
     83     assert.strictEqual(fn.signatures[''], signatures['']);
     84   });
     85 
     86   it('should create a typed function with one argument', function() {
     87     var fn = typed({
     88       'string': function () {
     89         return 'string';
     90       }
     91     });
     92 
     93     assert.equal(fn('hi'), 'string');
     94   });
     95 
     96   it('should create a typed function with two arguments', function() {
     97     var fn = typed({
     98       'string, boolean': function () {
     99         return 'foo';
    100       }
    101     });
    102 
    103     assert.equal(fn('hi', true), 'foo');
    104   });
    105 
    106   it('should create a named, typed function', function() {
    107     var fn = typed('myFunction', {
    108       'string, boolean': function () {
    109         return 'noargs';
    110       }
    111     });
    112 
    113     assert.equal(fn('hi', true), 'noargs');
    114     assert.equal(fn.name, 'myFunction');
    115   });
    116 
    117   it('should correctly recognize Date from Object (both are an Object)', function() {
    118     var signatures = {
    119       'Object': function (value) {
    120         assert(value instanceof Object);
    121         return 'Object';
    122       },
    123       'Date': function (value) {
    124         assert(value instanceof Date);
    125         return 'Date';
    126       }
    127     };
    128     var fn = typed(signatures);
    129 
    130     assert.equal(fn({foo: 'bar'}), 'Object');
    131     assert.equal(fn(new Date()), 'Date');
    132   });
    133 
    134   it('should correctly handle null', function () {
    135     var fn = typed({
    136       'Object': function (a) {
    137         return 'Object';
    138       },
    139       'null': function (a) {
    140         return 'null';
    141       },
    142       'undefined': function (a) {
    143         return 'undefined';
    144       }
    145     });
    146 
    147     assert.equal(fn(new Object(null)), 'Object');
    148     assert.equal(fn(null), 'null');
    149     assert.equal(fn(undefined), 'undefined');
    150   });
    151 
    152   it('should throw correct error message when passing null from an Object', function() {
    153     var signatures = {
    154       'Object': function (value) {
    155         assert(value instanceof Object);
    156         return 'Object';
    157       }
    158     };
    159     var fn = typed(signatures);
    160 
    161     assert.equal(fn({}), 'Object');
    162     assert.throws(function () { fn(null) },
    163         /TypeError: Unexpected type of argument in function unnamed \(expected: Object, actual: null, index: 0\)/);
    164   });
    165 
    166   it('should create a new, isolated instance of typed-function', function() {
    167     var typed1 = typed.create();
    168     var typed2 = typed.create();
    169     function Person() {}
    170 
    171     typed1.types.push({
    172       name: 'Person',
    173       test: function (x) {
    174         return x instanceof Person;
    175       }
    176     });
    177 
    178     assert.strictEqual(typed.create, typed1.create);
    179     assert.notStrictEqual(typed.types, typed1.types);
    180     assert.notStrictEqual(typed.conversions, typed1.conversions);
    181 
    182     assert.strictEqual(typed.create, typed2.create);
    183     assert.notStrictEqual(typed.types, typed2.types);
    184     assert.notStrictEqual(typed.conversions, typed2.conversions);
    185 
    186     assert.strictEqual(typed1.create, typed2.create);
    187     assert.notStrictEqual(typed1.types, typed2.types);
    188     assert.notStrictEqual(typed1.conversions, typed2.conversions);
    189 
    190     typed1({
    191       'Person': function (p) {return 'Person'}
    192     });
    193 
    194     assert.throws(function () {
    195       typed2({
    196         'Person': function (p) {return 'Person'}
    197       });
    198     }, /Error: Unknown type "Person"/)
    199   });
    200 
    201   it('should add a type using addType (before object)', function() {
    202     var typed2 = typed.create();
    203     function Person() {}
    204 
    205     var newType = {
    206       name: 'Person',
    207       test: function (x) {
    208         return x instanceof Person;
    209       }
    210     };
    211 
    212     var objectEntry = typed2.types.find(function (entry) {
    213       return entry.name === 'Object';
    214     });
    215     var objectIndex = typed2.types.indexOf(objectEntry);
    216 
    217     typed2.addType(newType);
    218 
    219     assert.strictEqual(typed2.types[objectIndex], newType);
    220   });
    221 
    222   it('should add a type using addType at the end (after Object)', function() {
    223     var typed2 = typed.create();
    224     function Person() {}
    225 
    226     var newType = {
    227       name: 'Person',
    228       test: function (x) {
    229         return x instanceof Person;
    230       }
    231     };
    232 
    233     typed2.addType(newType, false);
    234 
    235     assert.strictEqual(typed2.types[typed2.types.length - 1], newType);
    236   });
    237 
    238   it('should throw an error when passing an invalid type to addType', function() {
    239     var typed2 = typed.create();
    240     var errMsg = /TypeError: Object with properties {name: string, test: function} expected/;
    241 
    242     assert.throws(function () {typed2.addType({})}, errMsg);
    243     assert.throws(function () {typed2.addType({name: 2, test: function () {}})}, errMsg);
    244     assert.throws(function () {typed2.addType({name: 'foo', test: 'bar'})}, errMsg);
    245   });
    246 
    247   it('should throw an error when providing an unsupported type of argument', function() {
    248     var fn = typed('fn1', {
    249       'number': function (value) {
    250         return 'number:' + value;
    251       }
    252     });
    253 
    254     assert.throws(function () {fn(new Date())}, /TypeError: Unexpected type of argument in function fn1 \(expected: number, actual: Date, index: 0\)/);
    255   });
    256 
    257   it('should throw an error when providing a wrong function signature', function() {
    258     var fn = typed('fn1', {
    259       'number': function (value) {
    260         return 'number:' + value;
    261       }
    262     });
    263 
    264     assert.throws(function () {fn(1, 2)}, /TypeError: Too many arguments in function fn1 \(expected: 1, actual: 2\)/);
    265   });
    266 
    267   it('should throw an error when composing with an unknown type', function() {
    268     assert.throws(function () {
    269       var fn = typed({
    270         'foo': function (value) {
    271           return 'number:' + value;
    272         }
    273       });
    274     }, /Error: Unknown type "foo"/);
    275   });
    276 
    277   it('should ignore types from typed.ignore', function() {
    278     var typed2 = typed.create();
    279     typed2.ignore = ['string'];
    280 
    281     var fn = typed2({
    282       'number': function () {},
    283       'number, number': function () {},
    284 
    285       'string, number': function () {},
    286       'number, string': function () {},
    287       'boolean | string, boolean': function () {},
    288       'any, ...string': function () {},
    289       'string': function () {}
    290     });
    291 
    292     assert.deepEqual(Object.keys(fn.signatures).sort(), ['boolean,boolean', 'number', 'number,number']);
    293   });
    294 
    295   it('should give a hint when composing with a wrongly cased type', function() {
    296     assert.throws(function () {
    297       var fn = typed({
    298         'array': function (value) {
    299           return 'array:' + value;
    300         }
    301       });
    302     }, /Error: Unknown type "array". Did you mean "Array"?/);
    303 
    304     assert.throws(function () {
    305       var fn = typed({
    306         'function': function (value) {
    307           return 'Function:' + value;
    308         }
    309       });
    310     }, /Error: Unknown type "function". Did you mean "Function"?/);
    311   });
    312 
    313   it('should attach signatures to the created typed-function', function() {
    314     var fn1 = function () {}
    315     var fn2 = function () {}
    316     var fn3 = function () {}
    317     var fn4 = function () {}
    318 
    319     var fn = typed({
    320       'string': fn1,
    321       'string, boolean': fn2,
    322       'number | Date, boolean': fn3,
    323       'Array | Object, string | RegExp': fn3,
    324       'number, ...string | number': fn4
    325     });
    326 
    327     assert.deepStrictEqual(fn.signatures, {
    328       'string': fn1,
    329       'string,boolean': fn2,
    330       'number,boolean': fn3,
    331       'Date,boolean': fn3,
    332       'Array,string': fn3,
    333       'Array,RegExp': fn3,
    334       'Object,string': fn3,
    335       'Object,RegExp': fn3,
    336       'number,...string|number': fn4
    337     });
    338   });
    339 
    340   it('should correctly order signatures', function () {
    341     var fn = typed({
    342       'boolean': function (a) {
    343         return 'boolean';
    344       },
    345       'string': function (a) {
    346         return 'string';
    347       },
    348       'number': function (a) {
    349         return 'number';
    350       }
    351     });
    352 
    353     // TODO: this is tricky, object keys do not officially have a guaranteed order
    354     assert.deepEqual(Object.keys(fn.signatures),
    355         ['number', 'string', 'boolean']);
    356   });
    357 
    358   it('should allow a function to be defined recursively', function () {
    359     var fn = typed({
    360       'number': function (value) {
    361         return 'number:' + value;
    362       },
    363       'string': function (value) {
    364         return this(parseInt(value, 10));
    365       }
    366     });
    367 
    368     assert.equal(fn('2'), 'number:2');
    369   })
    370 
    371 });