import * as uut from './genfuns'; import * as e from './genfuns'; describe('matches_specializer', () => { test('works in expected cases', () => { function AThing() { } const an_instance = new AThing(); expect(uut.matches_specializer(an_instance, AThing)).toBeTruthy(); expect(uut.matches_specializer(an_instance, String)).toBeFalsy(); expect(uut.matches_specializer(an_instance, Object)).toBeTruthy(); expect(uut.matches_specializer([], Array)).toBeTruthy(); expect(uut.matches_specializer([], Object)).toBeTruthy(); expect(uut.matches_specializer([], Number)).toBeFalsy(); function Foo() {} Foo.prototype = Object.create(null); const inst = new Foo(); expect(uut.matches_specializer(inst, Foo)).toBeTruthy(); expect(uut.matches_specializer(inst, Object)).toBeFalsy(); expect(uut.matches_specializer({a:1}, uut.Shape('a'))).toBeTruthy(); expect(uut.matches_specializer({a:1, b:2}, uut.Shape('a'))).toBeTruthy(); expect(uut.matches_specializer({b:2}, uut.Shape('a'))).toBeFalsy(); expect(uut.matches_specializer({a:1, b:2, c:3}, uut.Shape('a', 'b', 'c'))).toBeTruthy(); expect(uut.matches_specializer({a:1, b:2, c:3, d:4}, uut.Shape('a', 'b', 'c'))).toBeTruthy(); expect(uut.matches_specializer({a:1, c:3}, uut.Shape('a', 'b', 'c'))).toBeFalsy(); expect(uut.matches_specializer({c:3}, uut.Shape('a', 'b', 'c'))).toBeFalsy(); expect(uut.matches_specializer({d:3}, uut.Shape('a', 'b', 'c'))).toBeFalsy(); }); test('null behavior', () => { expect(uut.matches_specializer(null, null)).toBeTruthy(); expect(uut.matches_specializer(null, Number)).toBeFalsy(); expect(uut.matches_specializer(null, String)).toBeFalsy(); expect(uut.matches_specializer(null, Object)).toBeFalsy(); }); test('undefined (the value) behavior', () => { expect(uut.matches_specializer(undefined, undefined)).toBeTruthy(); expect(uut.matches_specializer(undefined, Number)).toBeFalsy(); expect(uut.matches_specializer(undefined, String)).toBeFalsy(); expect(uut.matches_specializer(undefined, Object)).toBeFalsy(); }); test('works for numbers', () => { expect(uut.matches_specializer(new Number(1), Number)).toBeTruthy(); expect(uut.matches_specializer(new Number(1), Object)).toBeTruthy(); expect(uut.matches_specializer(new Number(1), String)).toBeFalsy(); expect(uut.matches_specializer(1, Number)).toBeTruthy(); expect(uut.matches_specializer(1, Object)).toBeTruthy(); expect(uut.matches_specializer(1, String)).toBeFalsy(); }); test('handles strings', () => { expect(uut.matches_specializer(new String("foobar"), String)).toBeTruthy(); expect(uut.matches_specializer(new String("foobar"), Object)).toBeTruthy(); expect(uut.matches_specializer("1", String)).toBeTruthy(); expect(uut.matches_specializer("1", Object)).toBeTruthy(); expect(uut.matches_specializer("1", Number)).toBeFalsy(); }) }); describe('defgeneric', () => { test('methods get called appropriately', () => { expect( uut.defgeneric("testing1", "a", "b") .primary([Object, Object], (_, __) => 1) .fn(1,2) ).toEqual(1); expect(() => { uut.defgeneric("foobar", "a") .primary([String], function (a) {}) .fn({}) }).toThrow(e.NoApplicableMethodError); expect( uut.defgeneric("testing1", "a", "b") .primary([Number, Number], (_, __) => 1) .fn(1,2) ).toEqual(1); expect( uut.defgeneric("testing1", "a", "b") .primary([Number, Number], (_, __) => 2) .primary([String, String], (_, __) => 1) .fn("1","2") ).toEqual(1); let firstCounts = 0; expect( uut.defgeneric("testing1", "a", "b") .primary([Number, Number], (_, __) => firstCounts += 1) .primary([String, String], (_, __) => firstCounts += 1) .fn("1","2") ).toEqual(1); expect(firstCounts).toEqual(1); let secondCounts = 0; expect( uut.defgeneric("testing1", "a", "b") .primary([Object, Object], (_, __) => secondCounts += 1) .primary([String, String], (_, __) => secondCounts += 1) .fn("1","2") ).toEqual(1); expect(secondCounts).toEqual(1); let thirdCounts = 0; expect( uut.defgeneric("testing1", "a", "b") .before([Object, Object], (_, __) => thirdCounts += 1) .primary([String, String], (_, __) => 'hi') .after([Object, String], (_, __) => thirdCounts += 1) .fn("1","2") ).toEqual('hi'); expect(thirdCounts).toEqual(2); expect( uut.defgeneric("foobar", "a") .primary([Object], function (a) { return 1 }) .primary([String], function (a) { return 2 }) .fn("foobar")) .toEqual(2); }); test('next-method-p works', () => { expect.assertions(3); uut.defgeneric("foobar", "a") .primary([Object], function (a) { expect(this.next_method_p).toBe(false); }) .fn({}); uut.defgeneric("foobar", "a") .primary([Object], function (a) { expect(this.next_method_p).toBe(false); }) .primary([String], function (a) { expect(this.next_method_p).toBe(true); }) .fn("foobar"); uut.defgeneric("foobar", "a") .primary([Object], function (a) { expect(this.next_method_p).toBe(false); }) .primary([String], function (a) { expect(this.next_method_p).toBe(true); }) .fn(1); }); test('call-next-method works', () => { expect(() => { uut.defgeneric("foobar", "a") .primary([Object], function (a) { this.call_next_method(); }) .fn({}); }).toThrow(e.NoNextMethodError); expect(() => { uut.defgeneric("foobar", "a") .primary([Object], function (a) { this.call_next_method(); }) .primary([String], function (a) { return 1; }) .fn({}); }); expect( uut.defgeneric("foobar", "a") .primary([Object], function (a) { return 1; }) .primary([String], function (a) { return this.call_next_method(); }) .fn("foobar") ).toEqual(1); expect( uut.defgeneric("foobar", "a", "b") .primary ([String, String], function (a,b) { return `1${this.call_next_method()}`; }) .primary ([Object, String], function (a,b) { return `3${this.call_next_method()}`; }) .primary ([String, Object], function (a,b) { return `2${this.call_next_method()}`; }) .primary ([Object, Object], function (a,b) { return `4`; }).fn("a", "b") ).toEqual("1234"); }); }); describe('custom specializers', () => { test('Shape works', () => { expect(uut.defgeneric("foobar", "a") .primary([uut.Shape('a', 'b')], ({a,b}) => a+b) .primary([Object], _ => null) .fn({a:1,b:2})) .toEqual(3); expect(uut.defgeneric("foobar", "a") .primary([uut.Shape('a', 'b')], ({a,b}) => a+b) .primary([Object], _ => null) .fn({a:1,b:2,c:3})) .toEqual(3); expect(uut.defgeneric("foobar", "a") .primary([uut.Shape('a', 'b')], ({a,b}) => a+b) .primary([Object], _ => null) .fn({a:1})) .toEqual(null); }); test('Shape, prototype precedence', () => { expect(uut.defgeneric("foobar4", "a") .primary([uut.Shape('a')], ({a}) => a) .primary([uut.Shape('a', 'b')], ({a,b}) => {return a+b}) .primary([Object], _ => null).fn({a:1,b:3})) .toEqual(4); expect(uut.defgeneric("foobar", "a") .primary([uut.Shape('a', 'b')], ({a,b}) => a+b) .primary([uut.Shape('b')], ({b}) => b) .primary([Object], _ => null) .fn({a:1,b:2})) .toEqual(3); const Foo = function () {} Foo.prototype = {a: true, b: null}; expect(uut.defgeneric("foobar", "a") .primary([uut.Shape('a')], function ({a}) { return 'a'; }) .primary([uut.Shape('a', 'b', 'c')], function ({a,b,c}) { return `c${this.call_next_method()}`; }) .primary([uut.Shape('a', 'b')], function ({a,b}) { return `b${this.call_next_method()}`; }) .primary([Object], _ => null) .fn(Object.assign(new Foo(), {c: 3}))) .toEqual('cba'); }); }); describe("Shape", () => { test('super_of', () => { expect(uut.Shape().super_of(uut.Shape("a", "b", "c"))).toBeTruthy(); expect(uut.Shape('a').super_of(uut.Shape('a', 'b'))).toBeTruthy(); expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b", "c"))).toBeTruthy(); expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b"))).toBeFalsy(); expect(uut.Shape("a", "b").super_of(uut.Shape("a"))).toBeFalsy(); }); });