diff --git a/expect/expect-tests.ts b/expect/expect-tests.ts index 69ed58b956..37ecefb566 100644 --- a/expect/expect-tests.ts +++ b/expect/expect-tests.ts @@ -11,579 +11,981 @@ describe('chaining assertions', function () { .toExist() .toBeAn(Array) .toInclude('foo') - .toExclude('bar') - }) + .toExclude('bar'); + }); it('should allow chaining for number checking', function () { expect(3.14) .toExist() .toBeLessThan(4.2) + .toBeLessThanOrEqualTo(3.14) .toBeGreaterThan(3.0) - }) -}) + .toBeGreaterThanOrEqualTo(3.14); + }); +}); + describe('createSpy', function () { describe('when given a function', function () { it('returns a spy function', function () { - const spy = createSpy(function () {}) - expect(spy).toBeA(Function) - }) - }) -}) + const spy = createSpy(() => { return; }); + expect(spy).toBeA(Function); + }); + }); +}); describe('A spy', function () { - let targetContext:any, targetArguments:any; + let targetContext: any, targetArguments: any; const target = { - method: function () { - targetContext = this - targetArguments = Array.prototype.slice.call(arguments, 0) + method() { + targetContext = this; + targetArguments = Array.prototype.slice.call(arguments, 0); } - } + }; - let spy:any; + let spy: Spy<{}>; + + beforeEach(() => { + spy = createSpy(target.method); + targetContext = targetArguments = null; + }); it('is a spy', function () { - expect(isSpy(spy)).toBe(true) - }) + expect(isSpy(spy)).toBe(true); + }); + + it('has the same length as the function passed in', () => { + expect(spy.length).toBe(0); + expect(createSpy(a => a).length).toBe(1); + expect(createSpy((a, b, c) => a * b * c).length).toBe(3); + }); it('has a destroy method', function () { - expect(spy.destroy).toBeA(Function) - }) + expect(spy.destroy).toBeA(Function); + }); it('has a restore method', function () { - expect(spy.restore).toBeA(Function) - }) + expect(spy.restore).toBeA(Function); + }); + + it('has a reset method', function () { + expect(spy.reset).toBeA(Function); + }); + + it('reset clears out all previous calls', () => { + spy(); + expect(spy.calls.length).toEqual(1); + spy.reset(); + expect(spy.calls.length).toEqual(0); + }); it('knows how many times it has been called', function () { - spy() - spy() - expect(spy.calls.length).toEqual(2) - }) + spy(); + spy(); + expect(spy.calls.length).toEqual(2); + }); it('knows the arguments it was called with', function () { - spy(1, 2, 3) - expect(spy).toHaveBeenCalledWith(1, 2, 3) - }) + spy(1, 2, 3); + expect(spy).toHaveBeenCalledWith(1, 2, 3); + }); describe('that calls some other function', function () { - let otherContext:any, otherArguments:any; + let otherContext: any, otherArguments: any; function otherFn() { - otherContext = this - otherArguments = Array.prototype.slice.call(arguments, 0) - } + otherContext = this; + otherArguments = Array.prototype.slice.call(arguments, 0); + }; beforeEach(function () { - spy.andCall(otherFn) - otherContext = otherArguments = null - }) + spy.andCall(otherFn); + otherContext = otherArguments = null; + }); it('calls that function', function () { - spy() - expect(otherContext).toNotBe(null) - }) + spy(); + expect(otherContext).toNotBe(null); + }); it('uses the correct context', function () { - const context = {} - spy.call(context) - expect(otherContext).toBe(context) - }) + const context = {}; + spy.call(context); + expect(otherContext).toBe(context); + }); it('passes the arguments through', function () { - spy(1, 2, 3) - expect(otherArguments).toEqual([ 1, 2, 3 ]) - }) - }) + spy(1, 2, 3); + expect(otherArguments).toEqual([ 1, 2, 3 ]); + }); + }); describe('that calls through', function () { beforeEach(function () { - spy.andCallThrough() - }) + spy.andCallThrough(); + }); it('calls the original function', function () { - spy() - expect(targetContext).toNotBe(null) - }) + spy(); + expect(targetContext).toNotBe(null); + }); it('uses the correct context', function () { - const context = {} - spy.call(context) - expect(targetContext).toBe(context) - }) + const context = {}; + spy.call(context); + expect(targetContext).toBe(context); + }); it('passes the arguments through', function () { - spy(1, 2, 3) - expect(targetArguments).toEqual([ 1, 2, 3 ]) - }) - }) + spy(1, 2, 3); + expect(targetArguments).toEqual([ 1, 2, 3 ]); + }); + }); describe('with a thrown value', function () { beforeEach(function () { - spy.andThrow('hello') - }) + spy.andThrow('hello'); + }); it('throws the correct value', function () { - expect(spy).toThrow('hello') - }) - }) + expect(spy).toThrow('hello'); + }); + }); describe('with a return value', function () { beforeEach(function () { - spy.andReturn('hello') - }) + spy.andReturn('hello'); + }); it('returns the correct value', function () { - expect(spy()).toEqual('hello') - }) - }) -}) + expect(spy()).toEqual('hello'); + }); + }); +}); + describe('expect.extend', function () { - const ColorAssertions:Extension = { + const ColorAssertions: Extension = { toBeAColor() { assert( this.actual.match(/^#[a-fA-F0-9]{6}$/), 'expected %s to be an HTML color', this.actual - ) + ); } - } + }; + + let assertSpy: Spy<{}>; - let assertSpy:Spy; beforeEach(function () { - extend(ColorAssertions) - assertSpy = spyOn(expect, 'assert') - }) + extend(ColorAssertions); + assertSpy = spyOn(expect, 'assert'); + }); afterEach(function () { - assertSpy.restore() - }) + assertSpy.restore(); + }); it('works', function () { - interface ColorExpectation extends Expectation { - toBeAColor():Expectation; + interface ColorExpectation extends Expectation { + toBeAColor(): Expectation; } - (expect('#ff00ff')).toBeAColor() - expect(assertSpy).toHaveBeenCalled() - }) -}) + (> expect('#ff00ff')).toBeAColor(); + expect(assertSpy).toHaveBeenCalled(); + }); +}); describe('restoreSpies', function () { describe('with one spy', function () { - const original = function () {} - const target = { method: original } + const original = function () { return; }; + const target = { method: original }; beforeEach(function () { - spyOn(target, 'method') - }) + spyOn(target, 'method'); + }); it('works with spyOn()', function () { - expect(target.method).toNotEqual(original) - restoreSpies() - expect(target.method).toEqual(original) - }) + expect(target.method).toNotEqual(original); + restoreSpies(); + expect(target.method).toEqual(original); + }); it('is idempotent', function () { - expect(target.method).toNotEqual(original) - restoreSpies() - restoreSpies() - expect(target.method).toEqual(original) - }) + expect(target.method).toNotEqual(original); + restoreSpies(); + restoreSpies(); + expect(target.method).toEqual(original); + }); it('can work even on createSpy()', function () { - createSpy(original) - restoreSpies() - }) - }) + createSpy(original); + restoreSpies(); + }); + }); describe('with multiple spies', function () { - const originals = [ function () {}, function () {} ] + const originals = [ function () { return; }, function () { return; } ]; const targets = [ { method: originals[0] }, { method: originals[1] } - ] + ]; it('still works', function () { - spyOn(targets[0], 'method') - spyOn(targets[1], 'method') + spyOn(targets[0], 'method'); + spyOn(targets[1], 'method'); - expect(targets[0].method).toNotEqual(originals[0]) - expect(targets[1].method).toNotEqual(originals[1]) + expect(targets[0].method).toNotEqual(originals[0]); + expect(targets[1].method).toNotEqual(originals[1]); - restoreSpies() + restoreSpies(); - expect(targets[0].method).toEqual(originals[0]) - expect(targets[1].method).toEqual(originals[1]) - }) - }) -}) + expect(targets[0].method).toEqual(originals[0]); + expect(targets[1].method).toEqual(originals[1]); + }); + }); +}); describe('A function that was spied on', function () { const video = { - play: function () {} - } + play(...args: any[]) { return; } + }; + + let spy: Spy<{}>; - let spy:Spy; beforeEach(function () { - spy = spyOn(video, 'play') - }) + spy = spyOn(video, 'play'); + video.play('some', 'args'); + }); it('tracks the number of calls', function () { - expect(spy.calls.length).toEqual(1) - }) + expect(spy.calls.length).toEqual(1); + }); it('tracks the context that was used', function () { - expect(spy.calls[0].context).toBe(video) - }) + expect(spy.calls[0].context).toBe(video); + }); it('tracks the arguments that were used', function () { - expect(spy.calls[0].arguments).toEqual([ 'some', 'args' ]) - }) + expect(spy.calls[0].arguments).toEqual([ 'some', 'args' ]); + }); it('was called', function () { - expect(spy).toHaveBeenCalled() - }) + expect(spy).toHaveBeenCalled(); + }); it('was called with the correct args', function () { - expect(spy).toHaveBeenCalledWith('some', 'args') - }) + expect(spy).toHaveBeenCalledWith('some', 'args'); + }); it('can be restored', function () { - expect(video.play).toEqual(spy) - spy.restore() - expect(video.play).toNotEqual(spy) - }) -}) + expect(video.play).toEqual(spy); + spy.restore(); + expect(video.play).toNotEqual(spy); + }); +}); describe('A function that was spied on but not called', function () { const video = { - play: function () {} - } + play () { return; } + }; + + let spy: Spy<{}>; - let spy:Spy; beforeEach(function () { - spy = spyOn(video, 'play') - }) + spy = spyOn(video, 'play'); + }); it('number of calls to be zero', function () { - expect(spy.calls.length).toEqual(0) - }) + expect(spy.calls.length).toEqual(0); + }); it('was not called', function () { - expect(spy).toNotHaveBeenCalled() - }) -}) + expect(spy).toNotHaveBeenCalled(); + }); +}); describe('toBeA', function () { it('requires the value to be a function or string', function () { expect(function () { - expect('actual').toBeA(4) - }).toThrow(/must be a function or a string/) - }) + expect('actual').toBeA(4); + }).toThrow(/must be a function or a string/); + }); it('does not throw when the actual value is an instanceof the constructor', function () { expect(function () { - expect(new Expectation('foo')).toBeA(Expectation) - }).toNotThrow() - }) + expect(new Expectation('foo')).toBeA(Expectation); + }).toNotThrow(); + }); it('throws when the actual value is not an instanceof the constructor', function () { expect(function () { - expect('actual').toBeA(Expectation) - }).toThrow(/to be/) - }) + expect('actual').toBeA(Expectation); + }).toThrow(/to be/); + }); it('does not throw when the expected value is the typeof the actual value', function () { expect(function () { - expect(4).toBeA('number') - expect(NaN).toBeA('number') // hahaha - }).toNotThrow() - }) + expect(4).toBeA('number'); + expect(NaN).toBeA('number'); // hahaha + }).toNotThrow(); + }); it('throws when the expected value is not the typeof the actual value', function () { expect(function () { - expect('actual').toBeA('number') - }).toThrow(/to be/) - }) + expect('actual').toBeA('number'); + }).toThrow(/to be/); + }); it('does not throw when the actual value is an array', function () { expect(function () { - expect([]).toBeAn('array') - }).toNotThrow() - }) + expect([]).toBeAn('array'); + }).toNotThrow(); + }); it('throws when the actual value is not an array', function () { expect(function () { - expect('actual').toBeAn('array') - }).toThrow(/to be/) - }) -}) + expect('actual').toBeAn('array'); + }).toThrow(/to be/); + }); +}); describe('toBeGreaterThan', function () { it('does not throw when the actual value is greater than the expected value', function () { expect(function () { - expect(3).toBeGreaterThan(2) - }).toNotThrow() - }) + expect(3).toBeGreaterThan(2); + }).toNotThrow(); + }); it('throws when the actual value is not greater than the expected value', function () { expect(function () { - expect(2).toBeGreaterThan(3) - }).toThrow(/to be greater than/) - }) -}) + expect(2).toBeGreaterThan(3); + }).toThrow(/to be greater than/); + }); +}); +describe('toBeGreaterThanOrEqualTo', () => { + it('does not throw when the actual value is greater than the expected value', () => { + expect(() => { + expect(3).toBeGreaterThanOrEqualTo(2); + }).toNotThrow(); + }); + + it('does not throw when the actual value is equal to the expected value', () => { + expect(() => { + expect(3).toBeGreaterThanOrEqualTo(3); + }).toNotThrow(); + }); + + it('throws when the actual value is not greater than or equal to the expected value', () => { + expect(() => { + expect(2).toBeGreaterThanOrEqualTo(3); + }).toThrow(/to be greater than or equal to/); + }); +}); describe('toBeLessThan', function () { it('does not throw when the actual value is less than the expected value', function () { expect(function () { - expect(2).toBeLessThan(3) - }).toNotThrow() - }) + expect(2).toBeLessThan(3); + }).toNotThrow(); + }); it('throws when the actual value is not less than the expected value', function () { expect(function () { - expect(3).toBeLessThan(2) - }).toThrow(/to be less than/) - }) -}) + expect(3).toBeLessThan(2); + }).toThrow(/to be less than/); + }); +}); + +describe('toBeLessThanOrEqualTo', () => { + it('does not throw when the actual value is less than the expected value', () => { + expect(() => { + expect(2).toBeLessThanOrEqualTo(3); + }).toNotThrow(); + }); + + it('does not throw when the actual value is equal to the expected value', () => { + expect(() => { + expect(2).toBeLessThanOrEqualTo(2); + }).toNotThrow(); + }); + + it('throws when the actual value is not less than the expected value', () => { + expect(() => { + expect(3).toBeLessThanOrEqualTo(2); + }).toThrow(/to be less than or equal to/); + }); +}); describe('toBeTruthy', function () { it('does not throw on truthy actual values', function () { expect(function () { - expect(1).toBeTruthy() - expect({ hello: 'world' }).toBeTruthy() - expect([ 1, 2, 3 ]).toBeTruthy() - }).toNotThrow() - }) + expect(1).toBeTruthy(); + expect({ hello: 'world' }).toBeTruthy(); + expect([ 1, 2, 3 ]).toBeTruthy(); + }).toNotThrow(); + }); it('throws on falsy actual values', function () { expect(function () { - expect(0).toBeTruthy() - }).toThrow() + expect(0).toBeTruthy(); + }).toThrow(); expect(function () { - expect(null).toBeTruthy() - }).toThrow() + expect(null).toBeTruthy(); + }).toThrow(); expect(function () { - expect(undefined).toBeTruthy() - }).toThrow() - }) -}) + expect(undefined).toBeTruthy(); + }).toThrow(); + }); +}); describe('toBeFalsy', function () { it('throws on truthy values', function () { expect(function () { - expect(42).toBeFalsy() - }).toThrow() + expect(42).toBeFalsy(); + }).toThrow(); expect(function () { - expect({ foo: 'bar' }).toBeFalsy() - }).toThrow() + expect({ foo: 'bar' }).toBeFalsy(); + }).toThrow(); expect(function () { - expect([]).toBeFalsy() - }).toThrow() - }) + expect([]).toBeFalsy(); + }).toThrow(); + }); it('does not throw with falsy actual values', function () { expect(function () { - expect(0).toBeFalsy() - expect(null).toBeFalsy() - expect(undefined).toBeFalsy() - }).toNotThrow() - }) -}) + expect(0).toBeFalsy(); + expect(null).toBeFalsy(); + expect(undefined).toBeFalsy(); + }).toNotThrow(); + }); +}); describe('toEqual', function () { it('works', function () { expect(function () { - expect('actual').toEqual('expected') - }).toThrow(/Expected 'actual' to equal 'expected'/) - }) + expect('actual').toEqual('expected'); + }).toThrow(/Expected 'actual' to equal 'expected'/); + }); it('works with objects that have the same keys in different order', function () { - const a = { a: 'a', b: 'b', c: 'c' } - const b = { b: 'b', c: 'c', a: 'a' } - expect(a).toEqual(b) - }) + const a = { a: 'a', b: 'b', c: 'c' }; + const b = { b: 'b', c: 'c', a: 'a' }; + expect(a).toEqual(b); + }); it('shows diff', function () { try { - expect('actual').toEqual('expected') + expect('actual').toEqual('expected'); } catch (err) { - expect(err.actual).toEqual('actual') - expect(err.expected).toEqual('expected') - expect(err.showDiff).toEqual(true) + expect(err.actual).toEqual('actual'); + expect(err.expected).toEqual('expected'); + expect(err.showDiff).toEqual(true); } - }) -}) + }); +}); describe('toExclude', function () { it('requires the actual value to be an array or string', function () { expect(function () { - expect(1).toExclude(2) - }).toThrow(/must be an array or a string/) - }) + expect(1).toExclude(2); + }).toThrow(/must be an array or a string/); + }); it('does not throw when an array does not contain the expected value', function () { expect(function () { - expect([ 1, 2, 3 ]).toExclude(4) - }).toNotThrow() - }) + expect([ 1, 2, 3 ]).toExclude(4); + }).toNotThrow(); + }); it('throws when an array contains the expected value', function () { expect(function () { - expect([ 1, 2, 3 ]).toExclude(2) - }).toThrow(/to exclude/) - }) + expect([ 1, 2, 3 ]).toExclude(2); + }).toThrow(/to exclude/); + }); + + it('throws when an object contains an expected object', () => { + expect(() => { + expect({ a: 1 }).toExclude({ a: 1 }); + }).toThrow(/to exclude/); + }); + + it('does not throw when an array contains an unexpected object', () => { + expect(() => { + expect({ a: 1 }).toExclude({ b: 2 }); + }).toNotThrow(); + }); + + it('does not throw when an object contains an expected object with an unexpected value', () => { + expect(() => { + expect({ a: 1 }).toExclude({ a: 2 }); + }).toNotThrow(); + }); it('does not throw when an array does not contain the expected value', function () { expect(function () { - expect('hello world').toExclude('goodbye') - }).toNotThrow() - }) + expect('hello world').toExclude('goodbye'); + }).toNotThrow(); + }); it('throws when a string contains the expected value', function () { expect(function () { - expect('hello world').toExclude('hello') - }).toThrow(/to exclude/) - }) -}) + expect('hello world').toExclude('hello'); + }).toThrow(/to exclude/); + }); +}); + +describe('toExcludeKey', () => { + it('requires the actual value to have keys', () => { + expect(() => { + expect(1).toExcludeKey('hello'); + }).toThrow(/must be an object/); + }); + + it('throws when there is a key that exists', () => { + expect(() => { + expect({ a: 1 }).toExcludeKey('a'); + }).toThrow(/exclude key/); + }); + + it('does not throw when there is a key that does not exist', () => { + expect(() => { + expect({ a: 1 }).toExcludeKey('b'); + }).toNotThrow(); + }); +}); + +describe('toExcludeKeys', () => { + it('throws when there is a key that exists', () => { + expect(() => { + expect({ a: 1 }).toExcludeKeys([ 'a' ]); + }).toThrow(/exclude key/); + }); + + it('does not throw when there is a key that does not exist', () => { + expect(() => { + expect({ a: 1 }).toExcludeKeys([ 'b' ]); + }).toNotThrow(); + }); + + it('throws when all keys exist', () => { + expect(() => { + expect({ a: 1, b: 2, c: 3 }).toExcludeKeys([ 'a', 'b', 'c' ]); + }).toThrow(/exclude key/); + }); + + it('does not throw when even one key does not exist', () => { + expect(() => { + expect({ a: 1, c: 3 }).toExcludeKeys([ 'a', 'b', 'c' ]); + }).toNotThrow(); + }); + + it('works with arrays', () => { + expect(() => { + expect([ 0, 1, 2 ]).toExcludeKeys([ '0', 1, 2 ]); + }).toThrow(/exclude key/); + + expect(() => { + expect([ 0, 1, 2 ]).toExcludeKeys([ 3 ]); + }).toNotThrow(); + }); +}); describe('toExist', function () { it('does not throw on truthy actual values', function () { expect(function () { - expect(1).toExist() - expect({ 'hello': 'world' }).toExist() - expect([ 1, 2, 3 ]).toExist() - }).toNotThrow() - }) + expect(1).toExist(); + expect({ hello: 'world' }).toExist(); + expect([ 1, 2, 3 ]).toExist(); + }).toNotThrow(); + }); it('throws on falsy actual values', function () { expect(function () { - expect(0).toExist() - }).toThrow() + expect(0).toExist(); + }).toThrow(); expect(function () { - expect(null).toExist() - }).toThrow() + expect(null).toExist(); + }).toThrow(); expect(function () { - expect(undefined).toExist() - }).toThrow() - }) -}) + expect(undefined).toExist(); + }).toThrow(); + }); +}); describe('toNotExist', function () { it('throws on truthy values', function () { expect(function () { - expect(42).toNotExist() - }).toThrow() + expect(42).toNotExist(); + }).toThrow(); expect(function () { - expect({ foo: 'bar' }).toNotExist() - }).toThrow() + expect({ foo: 'bar' }).toNotExist(); + }).toThrow(); expect(function () { - expect([]).toNotExist() - }).toThrow() - }) + expect([]).toNotExist(); + }).toThrow(); + }); it('does not throw with falsy actual values', function () { expect(function () { - expect(0).toNotExist() - expect(null).toNotExist() - expect(undefined).toNotExist() - }).toNotThrow() - }) -}) + expect(0).toNotExist(); + expect(null).toNotExist(); + expect(undefined).toNotExist(); + }).toNotThrow(); + }); +}); -describe('toInclude', function () { - it('requires the actual value to be an array or string', function () { - expect(function () { - expect(1).toInclude(2) - }).toThrow(/must be an array or a string/) - }) +describe('toInclude', () => { + it('requires the actual value to be an array, object, or a string', () => { + expect(() => { + expect(1).toInclude(2); + }).toThrow(/must be an array, object, or a string/); + }); - it('does not throw when an array contains an expected integer', function () { - expect(function () { - expect([ 1, 2, 3 ]).toInclude(2) - expect([ { a: 1 }, { c: 2 } ]).toInclude({ c: 2 }) - }).toNotThrow() - }) + it('does not throw when an array contains an expected integer', () => { + expect(() => { + expect([ 1, 2, 3 ]).toInclude(2); + expect([ { a: 1 }, { c: 2 } ]).toInclude({ c: 2 }); + }).toNotThrow(); + }); - it('does not throw when an array contains an expected object', function () { - expect(function () { - expect([ { a: 1 }, { c: 2 } ]).toInclude({ c: 2 }) - }).toNotThrow() - }) + it('does not throw when an array contains an expected object', () => { + expect(() => { + expect([ { a: 1 }, { c: 2 } ]).toInclude({ c: 2 }); + }).toNotThrow(); + }); - it('throws when an array does not contain an expected integer', function () { - expect(function () { - expect([ 1, 2, 3 ]).toInclude(4) - }).toThrow(/to include/) - }) + it('does not throw when an object contains an expected object', () => { + expect(() => { + expect({ a: 1, b: 2, c: 3 }).toInclude({ b: 2 }); + }).toNotThrow(); - it('throws when an array does not contain an expected object', function () { - expect(function () { - expect([ { a: 1 }, { c: 2 } ]).toInclude({ a: 2 }) - }).toThrow(/to include/) - }) + expect(() => { + expect({ a: 1, b: 2 }).toInclude({}); + }).toNotThrow(); + }); - it('does not throw when a string contains the expected value', function () { - expect(function () { - expect('hello world').toInclude('world') - }).toNotThrow() - }) + it('throws when an object does not contain an expected object', () => { + expect(() => { + expect({ a: 1, b: 2, c: 3 }).toInclude({ d: 4 }); + }).toThrow(/to include/); - it('throws when a string does not contain the expected value', function () { - expect(function () { - expect('hello world').toInclude('goodbye') - }).toThrow(/to include/) - }) -}) + expect(() => { + expect({ a: 1, b: 2, c: 3 }).toInclude({ b: 4 }); + }).toThrow(/to include/); + }); -describe('toMatch', function () { - it('requires the pattern to be a RegExp', function () { - expect(function () { - expect('actual').toMatch('expected') - }).toThrow(/must be a RegExp/) - }) + it('does not throw when an object contains an expected object in a deep key', () => { + expect(() => { + expect( + { a: 1, b: 2, c: { d: 4, e: { f: 5, g: 6, h: 7 } } } + ).toInclude( + { b: 2, c: { e: { f: 5, g: 6, h: 7 } } } + ); + }).toNotThrow(); + }); - it('does not throw when the actual value matches the pattern', function () { - expect(function () { - expect('actual').toMatch(/^actual$/) - }).toNotThrow() - }) + it('throws when an object does not contain an expected object in a deep key', () => { + expect(() => { + expect( + { a: 1, b: 2, c: { d: 4, e: { f: 5, g: 6, h: 7 } } } + ).toInclude( + { b: 2, c: { e: { f: 5, g: 999, h: 7 } } } + ); + }).toThrow(/to include/); + }); - it('throws when the actual value does not match the pattern', function () { - expect(function () { - expect('actual').toMatch(/nope/) - }).toThrow(/to match/) - }) -}) + it('compares partial object only when both expected and actual properties are object', () => { + expect(() => { + expect({ a: { b: 2 } }).toInclude({ a: {} }); + }).toNotThrow(); -describe('toNotMatch', function () { - it('requires the pattern to be a RegExp', function () { - expect(function () { - expect('actual').toNotMatch('expected') - }).toThrow(/must be a RegExp/) - }) + expect(() => { + expect({ a: 1 }).toInclude({ a: {} }); + }).toThrow(/to include/); - it('does not throw when the actual value does not match the pattern', function () { - expect(function () { - expect('actual').toNotMatch(/nope/) - }).toNotThrow() - }) + expect(() => { + expect({ a: [] }).toInclude({ a: {} }); + }).toThrow(/to include/); - it('throws when the actual value matches the pattern', function () { - expect(function () { - expect('actual').toNotMatch(/^actual$/) - }).toThrow(/to not match/) - }) -}) + expect(() => { + expect({ a: '1' }).toInclude({ a: {} }); + }).toThrow(/to include/); -describe('toNotEqual', function () { - it('works with arrays of objects', function () { + expect(() => { + expect({ a: () => { return; } }).toInclude({ a: {} }); + }).toThrow(/to include/); + }); + + if (typeof Object.create === 'function') { + it('ignores nonenumerable properties of an expected object', () => { + expect(() => { + expect({}).toInclude(Object.create({}, { a: { value: 1 } })); + }).toNotThrow(); + }); + } + + if (typeof Symbol === 'function') { + const symbol = Symbol(); + + it('does not throw when an object contains an expected object with a symbol key', () => { + expect(() => { + expect({ [symbol]: 1, b: 2 }).toInclude({ [symbol]: 1 }); + }).toNotThrow(); + }); + + it('throws when an object contain an expected object without a symbol key', () => { + expect(() => { + expect({ a: 1, b: 2 }).toInclude({ [symbol]: 1 }); + }).toThrow(/to include/); + }); + } + + it('throws when an array does not contain an expected integer', () => { + expect(() => { + expect([ 1, 2, 3 ]).toInclude(4); + }).toThrow(/to include/); + }); + + it('throws when an array does not contain an expected object', () => { + expect(() => { + expect([ { a: 1 }, { c: 2 } ]).toInclude({ a: 2 }); + }).toThrow(/to include/); + }); + + it('does not throw when a string contains the expected value', () => { + expect(() => { + expect('hello world').toInclude('world'); + }).toNotThrow(); + }); + + it('throws when a string does not contain the expected value', () => { + expect(() => { + expect('hello world').toInclude('goodbye'); + }).toThrow(/to include/); + }); +}); + +describe('toIncludeKey', () => { + it('requires the actual value to have keys', () => { + expect(() => { + expect(1).toIncludeKey('hello'); + }).toThrow(/must be an object/); + }); + + it('does not throw when there is a key that exists', () => { + expect(() => { + expect({ a: 1 }).toIncludeKey('a'); + }).toNotThrow(); + }); + + it('throws when there is a key that does not exist', () => { + expect(() => { + expect({ a: 1 }).toIncludeKey('b'); + }).toThrow(/include key/); + }); +}); + +describe('toIncludeKeys', () => { + it('requires the actual value to have keys', () => { + expect(() => { + expect(1).toIncludeKeys([ 'hello' ]); + }).toThrow(/must be an object/); + }); + + it('does not throw when there is a key that exists', () => { + expect(() => { + expect({ a: 1 }).toIncludeKeys([ 'a' ]); + }).toNotThrow(); + }); + + it('throws when there is a key that does not exist', () => { + expect(() => { + expect({ a: 1 }).toIncludeKeys([ 'b' ]); + }).toThrow(/include key/); + }); + + it('does not throw when all keys exist', () => { + expect(() => { + expect({ a: 1, b: 2, c: 3 }).toIncludeKeys([ 'a', 'b', 'c' ]); + }).toNotThrow(); + }); + + it('throws when even one key does not exist', () => { + expect(() => { + expect({ a: 1, c: 3 }).toIncludeKeys([ 'a', 'b', 'c' ]); + }).toThrow(/include key/); + }); + + it('works with arrays', () => { + expect(() => { + expect([ 0, 1, 2 ]).toIncludeKeys([ '0', 1, 2 ]); + }).toNotThrow(); + + expect(() => { + expect([ 0, 1, 2 ]).toIncludeKeys([ 3 ]); + }).toThrow(/include key/); + }); +}); + +describe('expect(string).toMatch', () => { + it('does not throw when the actual value matches the pattern', () => { + expect(() => { + expect('actual').toMatch(/^actual$/); + }).toNotThrow(); + }); + + it('throws when the actual value does not match the pattern', () => { + expect(() => { + expect('actual').toMatch(/nope/); + }).toThrow(/to match/); + }); +}); + +describe('expect(object).toMatch', () => { + it('does not throw when the actual value matches the pattern', () => { + expect(() => { + expect({ + statusCode: 200, + headers: { + server: 'express web server' + } + }).toMatch({ + statusCode: 200, + headers: { + server: /express/ + } + }); + }).toNotThrow(); + }); + + it('throws when the actual value does not match the pattern', () => { + expect(() => { + expect({ + statusCode: 200, + headers: { + server: 'nginx web server' + } + }).toMatch({ + statusCode: 200, + headers: { + server: /express/ + } + }); + }).toThrow(/to match/); + }); +}); + +describe('expect(array).toMatch', () => { + it('does not throw when the array contains an object that matches the pattern', () => { + const array = [ + { one: 'one' }, + { two: 'two' }, + { three: 'three' } + ]; + + expect(array).toMatch([ { one: /one/ } ]); + }); +}); + +describe('expect(string).toNotMatch', () => { + it('does not throw when the actual value does not match the pattern', () => { + expect(() => { + expect('actual').toNotMatch(/nope/); + }).toNotThrow(); + }); + + it('throws when the actual value matches the pattern', () => { + expect(() => { + expect('actual').toNotMatch(/^actual$/); + }).toThrow(/to not match/); + }); +}); + +describe('expect(object).toNotMatch', () => { + it('does not throw when the actual value does not match the pattern', () => { + expect(() => { + expect({ + statusCode: 200, + headers: { + server: 'nginx web server' + } + }).toNotMatch({ + statusCode: 200, + headers: { + server: /express/ + } + }); + }).toNotThrow(); + }); + + it('throws when the actual value matches the pattern', () => { + expect(() => { + expect({ + statusCode: 200, + headers: { + server: 'express web server' + } + }).toNotMatch({ + statusCode: 200, + headers: { + server: /express/ + } + }); + }).toThrow(/to not match/); + }); +}); + +describe('expect(array).toNotMatch', () => { + it('does not throw when the array does not contain an object that matches the pattern', () => { + const array = [ + { one: 'one' }, + { two: 'two' }, + { three: 'three' } + ]; + + expect(array).toNotMatch([ { one: /two/ } ]); + }); +}); + +describe('toNotEqual', () => { + it('works', () => { + expect('actual').toNotEqual('expected'); + }); + + it('works with objects that do not have the same keys', () => { + const a = { a: 'a', b: 'b', c: 'c' }; + const b = { a: 'a', b: 'b', d: 'c' }; + expect(a).toNotEqual(b); + }); + + it('works with objects that have different prototypes and different keys', () => { + class A { + a: any; + constructor(prop: any) { + this.a = prop; + } + } + + class B { + b: any; + constructor(prop: any) { + this.b = prop; + } + } + + const a = new A('hi'); + const b = new B('hi'); + + expect(a).toNotEqual(b); + }); + + it('works with arrays of objects', () => { const a = [ { id: 0, @@ -595,7 +997,7 @@ describe('toNotEqual', function () { text: 'Array Object 1', boo: false } - ] + ]; const b = [ { @@ -608,88 +1010,93 @@ describe('toNotEqual', function () { text: 'Array Object 1', boo: false } - ] + ]; - expect(a).toNotEqual(b) - }) + expect(a).toNotEqual(b); + }); if (typeof Map !== 'undefined') { - it('works with Map', function () { - const a = new Map() - a.set('key', 'value') + it('works with Map', () => { + const a = new Map(); + a.set('key', 'value'); - const b = new Map() - b.set('key', 'another value') + const b = new Map(); + b.set('key', 'another value'); - expect(a).toNotEqual(b) - }) + expect(a).toNotEqual(b); + }); } if (typeof Set !== 'undefined') { - it('works with Set', function () { - const a = new Set() - a.add('a') + it('works with Set', () => { + const a = new Set(); + a.add('a'); - const b = new Set() - b.add('b') + const b = new Set(); + b.add('b'); - expect(a).toNotEqual(b) - }) - } -}) + expect(a).toNotEqual(b); + }); + }; +}); describe('withArgs', function () { - const fn = function (arg1:any, arg2:any) { + const fn = function (arg1: any, arg2: any) { if (arg1 === 'first' && typeof arg2 === 'undefined') { - throw new Error('first arg found') + throw new Error('first arg found'); } if (arg1 === 'first' && arg2 === 'second') { - throw new Error('both args found') + throw new Error('both args found'); } - } + }; it('invokes actual function with args', function () { expect(function () { - expect(fn).withArgs('first').toThrow(/first arg found/) - }).toNotThrow() - }) + expect(fn).withArgs('first').toThrow(/first arg found/); + }).toNotThrow(); + }); it('can be chained', function () { expect(function () { - expect(fn).withArgs('first').withArgs('second').toThrow(/both args found/) - }).toNotThrow() - }) + expect(fn).withArgs('first').withArgs('second').toThrow(/both args found/); + }).toNotThrow(); + }); it('throws when actual is not a function', function () { expect(function () { - expect('not a function').withArgs('first') - }).toThrow(/must be a function/) - }) -}) + expect('not a function').withArgs('first'); + }).toThrow(/must be a function/); + }); +}); -describe('withContext', function () { +describe('withContext', () => { const context = { check: true - } - const fn = function (arg:any) { - if (this.check && typeof arg === 'undefined') { - throw new Error('context found') - } - if (this.check && arg === 'good') { - throw new Error('context and args found') - } - } + }; - it('calls function with context', function () { - expect(function () { - expect(fn).withContext(context).toThrow(/context found/) - }).toNotThrow() - }) + const fn = function fn(arg: any) { + if (this.check && typeof arg === 'undefined') + throw new Error('context found'); - it('calls function with context and args', function () { - expect(function () { - expect(fn).withContext(context).withArgs('good').toThrow(/context and args found/) - }).toNotThrow() - }) + if (this.check && arg === 'good') + throw new Error('context and args found'); + }; -}) + it('calls function with context', () => { + expect(() => { + expect(fn).withContext(context).toThrow(/context found/); + }).toNotThrow(); + }); + + it('calls function with context and args', () => { + expect(() => { + expect(fn).withContext(context).withArgs('good').toThrow(/context and args found/); + }).toNotThrow(); + }); + + it('throws when actual is not a function', () => { + expect(() => { + expect('not a function').withContext(context).toThrow(); + }).toThrow(/must be a function/); + }); +}); diff --git a/expect/index.d.ts b/expect/index.d.ts index d43bcda98a..7b4e95e2b5 100644 --- a/expect/index.d.ts +++ b/expect/index.d.ts @@ -1,72 +1,99 @@ -// Type definitions for Expect v1.13.4 +// Type definitions for Expect 1.20 // Project: https://github.com/mjackson/expect -// Definitions by: Justin Reidy +// Definitions by: Justin Reidy , Risto Keravuori // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped declare namespace expect { - export class Expectation { + export type CompareValues = (a: T, b: T) => boolean; + export type Comparator = (target: {}, key: string) => boolean; + export type KeyType = string | number; + + export class Expectation { constructor(actual: any); - toExist(message?: string): Expectation; - toBeTruthy(message?: string): Expectation; - toNotExist(message?: string): Expectation; - toBeFalsy(message?: string): Expectation; - toBe(value: any, message?: string): Expectation; - toNotBe(value: any, message?: string): Expectation; - toEqual(value: any, message?: string): Expectation; - toNotEqual(value: any, message?: string): Expectation; - toThrow(value?: any, message?: string): Expectation; - toNotThrow(value?: any, message?: string): Expectation; - toBeA(value: any, message?: string): Expectation; - toBeAn(value: any, message?: string): Expectation; - toNotBeA(value: any, message?: string): Expectation; - toNotBeAn(value: any, message?: string): Expectation; - toMatch(value: any, message?: string): Expectation; - toNotMatch(value: any, message?: string): Expectation; - toBeLessThan(value: any, message?: string): Expectation; - toBeFewerThan(value: any, message?: string): Expectation; - toBeGreaterThan(value: any, message?: string): Expectation; - toBeMoreThan(value: any, message?: string): Expectation; - toInclude(value: any, compareValues?: any, message?: string): Expectation; - toContain(value: any, compareValues?: any, message?: string): Expectation; - toExclude(value: any, compareValues?: any, message?: string): Expectation; - toNotContain(value: any, compareValues?: any, message?: string): Expectation; - toHaveBeenCalled(message?: string): Expectation; - toHaveBeenCalledWith(...args: Array): Expectation; - toNotHaveBeenCalled(message?: string): Expectation; - withContext(context: any): Expectation; - withArgs(...args: Array): Expectation; + toExist(message?: string): Expectation; + toBeTruthy(message?: string): Expectation; + toNotExist(message?: string): Expectation; + toBeFalsy(message?: string): Expectation; + + toBe(value: T, message?: string): Expectation; + toNotBe(value: any, message?: string): Expectation; + toEqual(value: any, message?: string): Expectation; + toNotEqual(value: any, message?: string): Expectation; + toThrow(error?: {}, message?: string): Expectation; + toNotThrow(error?: {}, message?: string): Expectation; + + toBeA(value: string | {}, message?: string): Expectation; + toBeAn(value: string | {}, message?: string): Expectation; + toNotBeA(value: string | {}, message?: string): Expectation; + toNotBeAn(value: string | {}, message?: string): Expectation; + toMatch(value: string | RegExp | {}, message?: string): Expectation; + toNotMatch(value: string | RegExp | {}, message?: string): Expectation; + + toBeLessThan(value: number, message?: string): Expectation; + toBeLessThanOrEqualTo(value: number, messasge?: string): Expectation; + toBeFewerThan(value: number, message?: string): Expectation; + toBeGreaterThan(value: number, message?: string): Expectation; + toBeGreaterThanOrEqualTo(value: number, messasge?: string): Expectation; + toBeMoreThan(value: number, message?: string): Expectation; + + toInclude(value: any, compareValues?: CompareValues, message?: string): Expectation; + toContain(value: any, compareValues?: CompareValues, message?: string): Expectation; + toExclude(value: any, compareValues?: CompareValues, message?: string): Expectation; + toNotContain(value: any, compareValues?: CompareValues, message?: string): Expectation; + + toIncludeKeys(keys: KeyType[], comparator?: Comparator, message?: string): Expectation; + toContainKeys(keys: KeyType[], comparator?: Comparator, message?: string): Expectation; + toExcludeKeys(keys: KeyType[], comparator?: Comparator, message?: string): Expectation; + toNotContainKeys(keys: KeyType[], comparator?: Comparator, message?: string): Expectation; + toNotIncludeKeys(keys: KeyType[], comparator?: Comparator, message?: string): Expectation; + toIncludeKey(key: KeyType, comparator?: Comparator, message?: string): Expectation; + toContainKey(key: KeyType, comparator?: Comparator, message?: string): Expectation; + toExcludeKey(key: KeyType, comparator?: Comparator, message?: string): Expectation; + toNotContainKey(key: KeyType, comparator?: Comparator, message?: string): Expectation; + toNotIncludeKey(key: KeyType, comparator?: Comparator, message?: string): Expectation; + + toHaveBeenCalled(message?: string): Expectation; + toNotHaveBeenCalled(message?: string): Expectation; + toHaveBeenCalledWith(...args: any[]): Expectation; + + // deprecated + withContext(context: any): Expectation; + withArgs(...args: any[]): Expectation; } export interface Extension { - [name: string]: (args?: Array) => void; + [name: string]: (args?: any[]) => void; } - export interface Call { - context: Spy; - arguments: Array; + export interface Call { + context: T; + arguments: any[]; } - export interface Spy { - __isSpy: Boolean; - calls: Array; - andCall(fn: Function): Spy; - andCallThrough(): Spy; - andThrow(object: Object): Spy; - andReturn(value: any): Spy; - getLastCall(): Call; + export interface Spy { + (...args: any[]): void; + __isSpy: boolean; + calls: Array>; + andCall(fn: (...args: any[]) => any): Spy; + andCallThrough(): Spy; + andThrow(object: {}): Spy; + andReturn(value: any): Spy; + getLastCall(): Call; restore(): void; destroy(): void; + reset(): void; } - export function createSpy(fn?: Function, restore?: Function): Spy; - export function spyOn(object: Object, methodName: string): Spy; - export function isSpy(object: any): Boolean; + export function createSpy(fn?: (...args: any[]) => any, restore?: (...args: any[]) => any): Spy; + export function spyOn(object: T, methodName: string): Spy; + export function isSpy(object: {}): boolean; export function restoreSpies(): void; - export function assert(condition: any, messageFormat: string, ...extraArgs: Array): void; + export function assert(condition: boolean, messageFormat: string, ...extraArgs: any[]): void; export function extend(extension: Extension): void; } -declare function expect(actual: any): expect.Expectation; + +declare function expect(actual: T): expect.Expectation; export = expect; diff --git a/expect/tslint.json b/expect/tslint.json new file mode 100644 index 0000000000..377cc837d4 --- /dev/null +++ b/expect/tslint.json @@ -0,0 +1 @@ +{ "extends": "../tslint.json" }