/// /// import expect, {Expectation, Extension, Spy, createSpy, isSpy, assert, spyOn, extend, restoreSpies} from 'expect'; describe('chaining assertions', function () { it('should allow chaining for array-like applications', function () { expect([ 1, 2, 'foo', 3 ]) .toExist() .toBeAn(Array) .toInclude('foo') .toExclude('bar') }) it('should allow chaining for number checking', function () { expect(3.14) .toExist() .toBeLessThan(4.2) .toBeGreaterThan(3.0) }) }) describe('createSpy', function () { describe('when given a function', function () { it('returns a spy function', function () { const spy = createSpy(function () {}) expect(spy).toBeA(Function) }) }) }) describe('A spy', function () { let targetContext:any, targetArguments:any; const target = { method: function () { targetContext = this targetArguments = Array.prototype.slice.call(arguments, 0) } } let spy:any; it('is a spy', function () { expect(isSpy(spy)).toBe(true) }) it('has a destroy method', function () { expect(spy.destroy).toBeA(Function) }) it('has a restore method', function () { expect(spy.restore).toBeA(Function) }) it('knows how many times it has been called', function () { 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) }) describe('that calls some other function', function () { let otherContext:any, otherArguments:any; function otherFn() { otherContext = this otherArguments = Array.prototype.slice.call(arguments, 0) } beforeEach(function () { spy.andCall(otherFn) otherContext = otherArguments = null }) it('calls that function', function () { spy() expect(otherContext).toNotBe(null) }) it('uses the correct context', function () { 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 ]) }) }) describe('that calls through', function () { beforeEach(function () { spy.andCallThrough() }) it('calls the original function', function () { spy() expect(targetContext).toNotBe(null) }) it('uses the correct context', function () { 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 ]) }) }) describe('with a thrown value', function () { beforeEach(function () { spy.andThrow('hello') }) it('throws the correct value', function () { expect(spy).toThrow('hello') }) }) describe('with a return value', function () { beforeEach(function () { spy.andReturn('hello') }) it('returns the correct value', function () { expect(spy()).toEqual('hello') }) }) }) describe('expect.extend', function () { 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; beforeEach(function () { extend(ColorAssertions) assertSpy = spyOn(expect, 'assert') }) afterEach(function () { assertSpy.restore() }) it('works', function () { interface ColorExpectation extends Expectation { toBeAColor():Expectation; } (expect('#ff00ff')).toBeAColor() expect(assertSpy).toHaveBeenCalled() }) }) describe('restoreSpies', function () { describe('with one spy', function () { const original = function () {} const target = { method: original } beforeEach(function () { spyOn(target, 'method') }) it('works with spyOn()', function () { 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) }) it('can work even on createSpy()', function () { createSpy(original) restoreSpies() }) }) describe('with multiple spies', function () { const originals = [ function () {}, function () {} ] const targets = [ { method: originals[0] }, { method: originals[1] } ] it('still works', function () { spyOn(targets[0], 'method') spyOn(targets[1], 'method') expect(targets[0].method).toNotEqual(originals[0]) expect(targets[1].method).toNotEqual(originals[1]) restoreSpies() 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 () {} } let spy:Spy; beforeEach(function () { spy = spyOn(video, 'play') }) it('tracks the number of calls', function () { expect(spy.calls.length).toEqual(1) }) it('tracks the context that was used', function () { expect(spy.calls[0].context).toBe(video) }) it('tracks the arguments that were used', function () { expect(spy.calls[0].arguments).toEqual([ 'some', 'args' ]) }) it('was called', function () { expect(spy).toHaveBeenCalled() }) it('was called with the correct args', function () { expect(spy).toHaveBeenCalledWith('some', 'args') }) it('can be restored', function () { 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 () {} } let spy:Spy; beforeEach(function () { spy = spyOn(video, 'play') }) it('number of calls to be zero', function () { expect(spy.calls.length).toEqual(0) }) it('was not called', function () { 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/) }) it('does not throw when the actual value is an instanceof the constructor', function () { expect(function () { 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/) }) 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() }) it('throws when the expected value is not the typeof the actual value', function () { expect(function () { 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() }) it('throws when the actual value is not an array', function () { expect(function () { 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() }) 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/) }) }) 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() }) 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/) }) }) 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() }) it('throws on falsy actual values', function () { expect(function () { expect(0).toBeTruthy() }).toThrow() expect(function () { expect(null).toBeTruthy() }).toThrow() expect(function () { expect(undefined).toBeTruthy() }).toThrow() }) }) describe('toBeFalsy', function () { it('throws on truthy values', function () { expect(function () { expect(42).toBeFalsy() }).toThrow() expect(function () { expect({ foo: 'bar' }).toBeFalsy() }).toThrow() expect(function () { expect([]).toBeFalsy() }).toThrow() }) it('does not throw with falsy actual values', function () { expect(function () { 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'/) }) 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) }) it('shows diff', function () { try { expect('actual').toEqual('expected') } catch (err) { 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/) }) it('does not throw when an array does not contain the expected value', function () { expect(function () { 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/) }) it('does not throw when an array does not contain the expected value', function () { expect(function () { 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/) }) }) 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() }) it('throws on falsy actual values', function () { expect(function () { expect(0).toExist() }).toThrow() expect(function () { expect(null).toExist() }).toThrow() expect(function () { expect(undefined).toExist() }).toThrow() }) }) describe('toNotExist', function () { it('throws on truthy values', function () { expect(function () { expect(42).toNotExist() }).toThrow() expect(function () { expect({ foo: 'bar' }).toNotExist() }).toThrow() expect(function () { expect([]).toNotExist() }).toThrow() }) it('does not throw with falsy actual values', function () { expect(function () { 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/) }) 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 object', function () { expect(function () { 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('throws when an array does not contain an expected object', function () { expect(function () { expect([ { a: 1 }, { c: 2 } ]).toInclude({ a: 2 }) }).toThrow(/to include/) }) it('does not throw when a string contains the expected value', function () { expect(function () { expect('hello world').toInclude('world') }).toNotThrow() }) it('throws when a string does not contain the expected value', function () { expect(function () { expect('hello world').toInclude('goodbye') }).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 the actual value matches the pattern', function () { expect(function () { expect('actual').toMatch(/^actual$/) }).toNotThrow() }) it('throws when the actual value does not match the pattern', function () { expect(function () { expect('actual').toMatch(/nope/) }).toThrow(/to match/) }) }) describe('toNotMatch', function () { it('requires the pattern to be a RegExp', function () { expect(function () { expect('actual').toNotMatch('expected') }).toThrow(/must be a RegExp/) }) it('does not throw when the actual value does not match the pattern', function () { expect(function () { expect('actual').toNotMatch(/nope/) }).toNotThrow() }) it('throws when the actual value matches the pattern', function () { expect(function () { expect('actual').toNotMatch(/^actual$/) }).toThrow(/to not match/) }) }) describe('toNotEqual', function () { it('works with arrays of objects', function () { const a = [ { id: 0, text: 'Array Object 0', boo: false }, { id: 1, text: 'Array Object 1', boo: false } ] const b = [ { id: 0, text: 'Array Object 0', boo: true // value of boo is changed to true here }, { id: 1, text: 'Array Object 1', boo: false } ] expect(a).toNotEqual(b) }) if (typeof Map !== 'undefined') { it('works with Map', function () { const a = new Map() a.set('key', 'value') const b = new Map() b.set('key', 'another value') expect(a).toNotEqual(b) }) } if (typeof Set !== 'undefined') { it('works with Set', function () { const a = new Set() a.add('a') const b = new Set() b.add('b') expect(a).toNotEqual(b) }) } }) describe('withArgs', function () { const fn = function (arg1:any, arg2:any) { if (arg1 === 'first' && typeof arg2 === 'undefined') { throw new Error('first arg found') } if (arg1 === 'first' && arg2 === 'second') { throw new Error('both args found') } } it('invokes actual function with args', function () { expect(function () { 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() }) it('throws when actual is not a function', function () { expect(function () { expect('not a function').withArgs('first') }).toThrow(/must be a function/) }) }) describe('withContext', function () { 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() }) it('calls function with context and args', function () { expect(function () { expect(fn).withContext(context).withArgs('good').toThrow(/context and args found/) }).toNotThrow() }) })