diff --git a/lib/Assertion.d.ts b/lib/Assertion.d.ts index d98f57a..0d4e91a 100644 --- a/lib/Assertion.d.ts +++ b/lib/Assertion.d.ts @@ -1,5 +1,5 @@ export interface AssertionStatic { - (target: any, message?: string): Assertion; + (target?: any, message?: string, stack?: Function): Assertion; } export interface Assertion extends LanguageChains, NumericComparison, TypeComparison { @@ -37,9 +37,9 @@ export interface Assertion extends LanguageChains, NumericComparison, TypeCompar lengthOf: Length; match: Match; matches: Match; - string(string: string, message?: string): Assertion; + string(str: string, message?: string): Assertion; keys: Keys; - key(string: string): Assertion; + key(str: string): Assertion; throw: Throw; throws: Throw; Throw: Throw; @@ -113,6 +113,7 @@ export interface CloseTo { export interface Deep { equal: Equal; + equals: Equal; include: Include; property: Property; members: Members; @@ -147,6 +148,7 @@ export interface Include { (value: Object, message?: string): Assertion; (value: string, message?: string): Assertion; (value: number, message?: string): Assertion; + string(value: string, message?: string): Assertion; keys: Keys; members: Members; any: KeyFilter; @@ -158,7 +160,7 @@ export interface Match { } export interface Keys { - (...keys: string[]): Assertion; + (...keys: any[]): Assertion; (keys: any[]): Assertion; (keys: Object): Assertion; } @@ -187,4 +189,4 @@ export interface Members { export interface PropertyChange { (object: Object, prop: string, msg?: string): Assertion; -} \ No newline at end of file +} diff --git a/lib/Chai.d.ts b/lib/Chai.d.ts index ade6d7d..6ef01c4 100644 --- a/lib/Chai.d.ts +++ b/lib/Chai.d.ts @@ -1,21 +1,22 @@ import * as AE from 'assertion-error'; import * as Assert from './Assert'; -import * as Assertion from './Assertion'; +import * as A from './Assertion'; import * as Expect from './Expect'; import * as Should from './Should'; import * as Config from './Config'; import * as Utils from './Utils'; declare namespace chai { - export interface AssertionStatic extends Assertion.AssertionStatic {} + export interface AssertionStatic extends A.AssertionStatic {} export class AssertionError extends AE.AssertionError {} - export var Assertion: Assertion.Assertion; + export var Assertion: A.AssertionStatic; export var expect: Expect.ExpectStatic; export var assert: Assert.AssertStatic; export var config: Config.Config; export var util: Utils.Utils; export function should(): Should.Should; + export function Should(): Should.Should; /** * Provides a way to extend the internals of Chai */ @@ -26,6 +27,6 @@ export = chai; declare global { interface Object { - should: Assertion.Assertion; + should: A.Assertion; } } diff --git a/lib/Config.d.ts b/lib/Config.d.ts index 48cd4cf..b903fbf 100644 --- a/lib/Config.d.ts +++ b/lib/Config.d.ts @@ -1,3 +1,4 @@ export interface Config { includeStack: boolean; + truncateThreshold: number; } diff --git a/lib/Utils.d.ts b/lib/Utils.d.ts index 8fc078f..1d093d5 100644 --- a/lib/Utils.d.ts +++ b/lib/Utils.d.ts @@ -1,3 +1,5 @@ +import {Assertion} from './Assertion'; + export interface Utils { addChainableMethod(ctx: any, name: string, chainingBehavior: (value: any) => void); addMethod(ctx: any, name: string, method: (value: any) => void); @@ -6,10 +8,13 @@ export interface Utils { flag(obj: Object, key: string, value?: any); getActual(obj: Object, actual?: any); getEnumerableProperties(obj: Object); + getMessage(obj: Object, params: any[]); getMessage(obj: Object, message: string, negateMessage: string); getName(func: Function); getPathInfo(path: string, obj: Object); getPathValue(path: string, obj: Object); getProperties(obj: Object); hasProperty(obj: Object, name: string); + transferFlags(assertion: Assertion | any, obj: Object, includeAll?: boolean); + inspect(obj: any); } diff --git a/source-test/configuration.ts b/source-test/configuration.ts new file mode 100644 index 0000000..1f5b6da --- /dev/null +++ b/source-test/configuration.ts @@ -0,0 +1,172 @@ +import err from './bootstrap/index'; +import chai = require('chai'); + +describe('configuration', function () { + var assert = chai.assert; + + var origConfig; + + beforeEach(function() { + // backup current config + function clone(o) { + return JSON.parse(JSON.stringify(o)); + } + origConfig = clone(chai.config); + }); + + afterEach(function() { + // restore config + Object.keys(origConfig).forEach(function(key) { + chai.config[key] = origConfig[key]; + }); + }); + + function fooThrows () { + chai.expect('foo').to.be.equal('bar'); + } + function fooPropThrows () { + chai.expect('foo').to.not.exist; + } + + it('includeStack is true for method assertions', function () { + chai.config.includeStack = true; + + try { + fooThrows(); + assert.ok(false, 'should not get here because error thrown'); + } catch (err) { + // not all browsers support err.stack + if ('undefined' !== typeof err.stack) { + assert.include(err.stack, 'assertEqual', 'should have internal stack trace in error message'); + assert.include(err.stack, 'fooThrows', 'should have user stack trace in error message'); + } + } + + }); + + it('includeStack is false for method assertions', function () { + chai.config.includeStack = false; + + try { + fooThrows(); + assert.ok(false, 'should not get here because error thrown'); + } catch (err) { + // IE 10 supports err.stack in Chrome format, but without + // `Error.captureStackTrace` support that allows tuning of the error + // message. + // if ('undefined' !== typeof err.stack && 'undefined' !== typeof Error.captureStackTrace) { + // assert.notInclude(err.stack, 'assertEqual', 'should not have internal stack trace in error message'); + // assert.include(err.stack, 'fooThrows', 'should have user stack trace in error message'); + // } + } + }); + + it('includeStack is true for property assertions', function () { + chai.config.includeStack = true; + + try { + fooPropThrows(); + assert.ok(false, 'should not get here because error thrown'); + } catch (err) { + // not all browsers support err.stack + // Phantom does not include function names for getter exec + // if ('undefined' !== typeof err.stack && 'undefined' !== typeof Error.captureStackTrace) { + // assert.include(err.stack, 'addProperty', 'should have internal stack trace in error message'); + // assert.include(err.stack, 'fooPropThrows', 'should have user stack trace in error message'); + // } + } + + }); + + it('includeStack is false for property assertions', function () { + chai.config.includeStack = false; + + try { + fooPropThrows(); + assert.ok(false, 'should not get here because error thrown'); + } catch (err) { + // IE 10 supports err.stack in Chrome format, but without + // `Error.captureStackTrace` support that allows tuning of the error + // message. + // if ('undefined' !== typeof err.stack && 'undefined' !== typeof Error.captureStackTrace) { + // assert.notInclude(err.stack, 'addProperty', 'should not have internal stack trace in error message'); + // assert.include(err.stack, 'fooPropThrows', 'should have user stack trace in error message'); + // } + } + }); + + describe('truncateThreshold', function() { + it('is 20', function() { + chai.config.truncateThreshold = 20; + + err(function() { + assert.deepEqual({v: 'something longer than 20'}, {v: 'x'}); + }, "expected { Object (v) } to deeply equal { v: 'x' }"); + }); + + it('is 0', function() { + chai.config.truncateThreshold = 0; + + err(function() { + assert.deepEqual({v: 'something longer than 20'}, {v: 'x'}); + }, "expected { v: 'something longer than 20' } to deeply equal { v: 'x' }"); + }); + }); + + describe('deprecated properties', function() { + var origWarnFn; + var warnings; + + beforeEach(function() { + origWarnFn = console.warn; + warnings = []; + console.warn = function(message) { + warnings.push(message); + }; + }); + + afterEach(function() { + console.warn = origWarnFn; + }); + + // it('Assertion.includeStack warns that it is deprecated', function() { + // chai.Assertion.includeStack; + + // assert.equal(warnings.length, 1); + // assert.equal(warnings[0], 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + + // chai.Assertion.includeStack = true; + + // assert.equal(warnings.length, 2); + // assert.equal(warnings[1], 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + // }); + + // it('Assertion.includeStack is kept in sync with config.includeStack', function() { + // assert.equal(chai.Assertion.includeStack, chai.config.includeStack); + // chai.Assertion.includeStack = !chai.Assertion.includeStack; + // assert.equal(chai.Assertion.includeStack, chai.config.includeStack); + // chai.config.includeStack = !chai.config.includeStack; + // assert.equal(chai.Assertion.includeStack, chai.config.includeStack); + // }); + + // it('Assertion.showDiff warns that it is deprecated', function() { + // chai.Assertion.showDiff; + + // assert.equal(warnings.length, 1); + // assert.equal(warnings[0], 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + + // chai.Assertion.showDiff = true; + + // assert.equal(warnings.length, 2); + // assert.equal(warnings[1], 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + // }); + + // it('Assertion.showDiff is kept in sync with config.showDiff', function() { + // assert.equal(chai.Assertion.showDiff, chai.config.showDiff); + // chai.Assertion.showDiff = !chai.Assertion.showDiff; + // assert.equal(chai.Assertion.showDiff, chai.config.showDiff); + // chai.config.showDiff = !chai.config.showDiff; + // assert.equal(chai.Assertion.showDiff, chai.config.showDiff); + // }); + }); +}); diff --git a/source-test/expect.ts b/source-test/expect.ts new file mode 100644 index 0000000..7c27476 --- /dev/null +++ b/source-test/expect.ts @@ -0,0 +1,1265 @@ +import err from './bootstrap/index'; +import chai = require('chai'); + +describe('expect', function () { + var expect = chai.expect; + chai.should(); + + it('chai.version', function() { + expect(chai).to.have.property('version'); + }); + + it('assertion', function(){ + expect('test').to.be.a('string'); + expect('foo').to.equal('foo'); + }); + + it('fail', function () { + err(function() { + expect.fail(0, 1, 'this has failed'); + }, /this has failed/); + }); + + it('true', function(){ + expect(true).to.be.true; + expect(false).to.not.be.true; + expect(1).to.not.be.true; + + err(function(){ + expect('test').to.be.true; + }, "expected 'test' to be true") + }); + + it('ok', function(){ + expect(true).to.be.ok; + expect(false).to.not.be.ok; + expect(1).to.be.ok; + expect(0).to.not.be.ok; + + err(function(){ + expect('').to.be.ok; + }, "expected '' to be truthy"); + + err(function(){ + expect('test').to.not.be.ok; + }, "expected 'test' to be falsy"); + }); + + it('false', function(){ + expect(false).to.be.false; + expect(true).to.not.be.false; + expect(0).to.not.be.false; + + err(function(){ + expect('').to.be.false; + }, "expected '' to be false") + }); + + it('null', function(){ + expect(null).to.be.null; + expect(false).to.not.be.null; + + err(function(){ + expect('').to.be.null; + }, "expected '' to be null") + + }); + + it('undefined', function(){ + expect(undefined).to.be.undefined; + expect(null).to.not.be.undefined; + + err(function(){ + expect('').to.be.undefined; + }, "expected '' to be undefined") + }); + + it('exist', function(){ + var foo = 'bar' + , bar; + expect(foo).to.exist; + expect(bar).to.not.exist; + }); + + it('arguments', function(){ + var args = (function(...args){ return arguments; })(1,2,3); + expect(args).to.be.arguments; + expect([]).to.not.be.arguments; + expect(args).to.be.an('arguments').and.be.arguments; + expect([]).to.be.an('array').and.not.be.Arguments; + }); + + it('.equal()', function(){ + var foo; + expect(undefined).to.equal(foo); + + err(function(){ + expect(undefined).to.equal(null); + }, "expected undefined to equal null") + }); + + it('typeof', function(){ + expect('test').to.be.a('string'); + + err(function(){ + expect('test').to.not.be.a('string'); + }, "expected 'test' not to be a string"); + + (function (...args) { + expect(arguments).to.be.an('arguments'); + })(1, 2); + + expect(5).to.be.a('number'); + expect(new Number(1)).to.be.a('number'); + expect(Number(1)).to.be.a('number'); + expect(true).to.be.a('boolean'); + expect(new Array()).to.be.a('array'); + expect(new Object()).to.be.a('object'); + expect({}).to.be.a('object'); + expect([]).to.be.a('array'); + expect(function() {}).to.be.a('function'); + expect(null).to.be.a('null'); + + err(function(){ + expect(5).to.not.be.a('number', 'blah'); + }, "blah: expected 5 not to be a number"); + }); + + it('instanceof', function(){ + function Foo(){} + expect(new Foo()).to.be.an.instanceof(Foo); + + err(function(){ + expect(3).to.an.instanceof(Foo, 'blah'); + }, "blah: expected 3 to be an instance of Foo"); + }); + + it('within(start, finish)', function(){ + expect(5).to.be.within(5, 10); + expect(5).to.be.within(3,6); + expect(5).to.be.within(3,5); + expect(5).to.not.be.within(1,3); + expect('foo').to.have.length.within(2,4); + expect([ 1, 2, 3 ]).to.have.length.within(2,4); + + err(function(){ + expect(5).to.not.be.within(4,6, 'blah'); + }, "blah: expected 5 to not be within 4..6"); + + err(function(){ + expect(10).to.be.within(50,100, 'blah'); + }, "blah: expected 10 to be within 50..100"); + + err(function () { + expect('foo').to.have.length.within(5,7, 'blah'); + }, "blah: expected \'foo\' to have a length within 5..7"); + + err(function () { + expect([ 1, 2, 3 ]).to.have.length.within(5,7, 'blah'); + }, "blah: expected [ 1, 2, 3 ] to have a length within 5..7"); + }); + + it('above(n)', function(){ + expect(5).to.be.above(2); + expect(5).to.be.greaterThan(2); + expect(5).to.not.be.above(5); + expect(5).to.not.be.above(6); + expect('foo').to.have.length.above(2); + expect([ 1, 2, 3 ]).to.have.length.above(2); + + err(function(){ + expect(5).to.be.above(6, 'blah'); + }, "blah: expected 5 to be above 6"); + + err(function(){ + expect(10).to.not.be.above(6, 'blah'); + }, "blah: expected 10 to be at most 6"); + + err(function () { + expect('foo').to.have.length.above(4, 'blah'); + }, "blah: expected \'foo\' to have a length above 4 but got 3"); + + err(function () { + expect([ 1, 2, 3 ]).to.have.length.above(4, 'blah'); + }, "blah: expected [ 1, 2, 3 ] to have a length above 4 but got 3"); + }); + + it('least(n)', function(){ + expect(5).to.be.at.least(2); + expect(5).to.be.at.least(5); + expect(5).to.not.be.at.least(6); + expect('foo').to.have.length.of.at.least(2); + expect([ 1, 2, 3 ]).to.have.length.of.at.least(2); + + err(function(){ + expect(5).to.be.at.least(6, 'blah'); + }, "blah: expected 5 to be at least 6"); + + err(function(){ + expect(10).to.not.be.at.least(6, 'blah'); + }, "blah: expected 10 to be below 6"); + + err(function () { + expect('foo').to.have.length.of.at.least(4, 'blah'); + }, "blah: expected \'foo\' to have a length at least 4 but got 3"); + + err(function () { + expect([ 1, 2, 3 ]).to.have.length.of.at.least(4, 'blah'); + }, "blah: expected [ 1, 2, 3 ] to have a length at least 4 but got 3"); + + err(function () { + expect([ 1, 2, 3, 4 ]).to.not.have.length.of.at.least(4, 'blah'); + }, "blah: expected [ 1, 2, 3, 4 ] to have a length below 4"); + }); + + it('below(n)', function(){ + expect(2).to.be.below(5); + expect(2).to.be.lessThan(5); + expect(2).to.not.be.below(2); + expect(2).to.not.be.below(1); + expect('foo').to.have.length.below(4); + expect([ 1, 2, 3 ]).to.have.length.below(4); + + err(function(){ + expect(6).to.be.below(5, 'blah'); + }, "blah: expected 6 to be below 5"); + + err(function(){ + expect(6).to.not.be.below(10, 'blah'); + }, "blah: expected 6 to be at least 10"); + + err(function () { + expect('foo').to.have.length.below(2, 'blah'); + }, "blah: expected \'foo\' to have a length below 2 but got 3"); + + err(function () { + expect([ 1, 2, 3 ]).to.have.length.below(2, 'blah'); + }, "blah: expected [ 1, 2, 3 ] to have a length below 2 but got 3"); + }); + + it('most(n)', function(){ + expect(2).to.be.at.most(5); + expect(2).to.be.at.most(2); + expect(2).to.not.be.at.most(1); + expect(2).to.not.be.at.most(1); + expect('foo').to.have.length.of.at.most(4); + expect([ 1, 2, 3 ]).to.have.length.of.at.most(4); + + err(function(){ + expect(6).to.be.at.most(5, 'blah'); + }, "blah: expected 6 to be at most 5"); + + err(function(){ + expect(6).to.not.be.at.most(10, 'blah'); + }, "blah: expected 6 to be above 10"); + + err(function () { + expect('foo').to.have.length.of.at.most(2, 'blah'); + }, "blah: expected \'foo\' to have a length at most 2 but got 3"); + + err(function () { + expect([ 1, 2, 3 ]).to.have.length.of.at.most(2, 'blah'); + }, "blah: expected [ 1, 2, 3 ] to have a length at most 2 but got 3"); + + err(function () { + expect([ 1, 2 ]).to.not.have.length.of.at.most(2, 'blah'); + }, "blah: expected [ 1, 2 ] to have a length above 2"); + }); + + it('match(regexp)', function(){ + expect('foobar').to.match(/^foo/) + expect('foobar').to.matches(/^foo/) + expect('foobar').to.not.match(/^bar/) + + err(function(){ + expect('foobar').to.match(/^bar/i, 'blah') + }, "blah: expected 'foobar' to match /^bar/i"); + + err(function(){ + expect('foobar').to.matches(/^bar/i, 'blah') + }, "blah: expected 'foobar' to match /^bar/i"); + + err(function(){ + expect('foobar').to.not.match(/^foo/i, 'blah') + }, "blah: expected 'foobar' not to match /^foo/i"); + }); + + it('length(n)', function(){ + expect('test').to.have.length(4); + expect('test').to.not.have.length(3); + expect([1,2,3]).to.have.length(3); + + err(function(){ + expect(4).to.have.length(3, 'blah'); + }, 'blah: expected 4 to have a property \'length\''); + + err(function(){ + expect('asd').to.not.have.length(3, 'blah'); + }, "blah: expected 'asd' to not have a length of 3"); + }); + + it('eql(val)', function(){ + expect('test').to.eql('test'); + expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); + expect(1).to.eql(1); + expect('4').to.not.eql(4); + + err(function(){ + expect(4).to.eql(3, 'blah'); + }, 'blah: expected 4 to deeply equal 3'); + }); + + if ('undefined' !== typeof Buffer) { + it('Buffer eql()', function () { + expect(new Buffer([ 1 ])).to.eql(new Buffer([ 1 ])); + + err(function () { + expect(new Buffer([ 0 ])).to.eql(new Buffer([ 1 ])); + }, 'expected to deeply equal '); + }); + } + + it('equal(val)', function(){ + expect('test').to.equal('test'); + expect(1).to.equal(1); + + err(function(){ + expect(4).to.equal(3, 'blah'); + }, 'blah: expected 4 to equal 3'); + + err(function(){ + expect('4').to.equal(4, 'blah'); + }, "blah: expected '4' to equal 4"); + }); + + it('deep.equal(val)', function(){ + expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); + expect({ foo: 'bar' }).not.to.deep.equal({ foo: 'baz' }); + }); + + it('deep.equal(/regexp/)', function(){ + expect(/a/).to.deep.equal(/a/); + expect(/a/).not.to.deep.equal(/b/); + expect(/a/).not.to.deep.equal({}); + expect(/a/g).to.deep.equal(/a/g); + expect(/a/g).not.to.deep.equal(/b/g); + expect(/a/i).to.deep.equal(/a/i); + expect(/a/i).not.to.deep.equal(/b/i); + expect(/a/m).to.deep.equal(/a/m); + expect(/a/m).not.to.deep.equal(/b/m); + }); + + it('deep.equal(Date)', function(){ + var a = new Date(1, 2, 3) + , b = new Date(4, 5, 6); + expect(a).to.deep.equal(a); + expect(a).not.to.deep.equal(b); + expect(a).not.to.deep.equal({}); + }); + + it('empty', function(){ + function FakeArgs() {}; + FakeArgs.prototype.length = 0; + + expect('').to.be.empty; + expect('foo').not.to.be.empty; + expect([]).to.be.empty; + expect(['foo']).not.to.be.empty; + expect(new FakeArgs).to.be.empty; + expect({arguments: 0}).not.to.be.empty; + expect({}).to.be.empty; + expect({foo: 'bar'}).not.to.be.empty; + + err(function(){ + expect('').not.to.be.empty; + }, "expected \'\' not to be empty"); + + err(function(){ + expect('foo').to.be.empty; + }, "expected \'foo\' to be empty"); + + err(function(){ + expect([]).not.to.be.empty; + }, "expected [] not to be empty"); + + err(function(){ + expect(['foo']).to.be.empty; + }, "expected [ \'foo\' ] to be empty"); + + err(function(){ + expect(new FakeArgs).not.to.be.empty; + }, "expected { length: 0 } not to be empty"); + + err(function(){ + expect({arguments: 0}).to.be.empty; + }, "expected { arguments: 0 } to be empty"); + + err(function(){ + expect({}).not.to.be.empty; + }, "expected {} not to be empty"); + + err(function(){ + expect({foo: 'bar'}).to.be.empty; + }, "expected { foo: \'bar\' } to be empty"); + }); + + it('NaN', function() { + expect(NaN).to.be.NaN; + expect('foo').to.be.NaN; + expect({}).to.be.NaN; + expect(4).not.to.be.NaN; + expect([]).not.to.be.NaN; + + err(function(){ + expect(4).to.be.NaN; + }, "expected 4 to be NaN"); + + err(function(){ + expect([]).to.be.NaN; + }, "expected [] to be NaN"); + + err(function(){ + expect('foo').not.to.be.NaN; + }, "expected 'foo' not to be NaN"); + }); + + it('property(name)', function(){ + expect('test').to.have.property('length'); + expect(4).to.not.have.property('length'); + + expect({ 'foo.bar': 'baz' }) + .to.have.property('foo.bar'); + expect({ foo: { bar: 'baz' } }) + .to.not.have.property('foo.bar'); + + // Properties with the value 'undefined' are still properties + var obj = { foo: undefined }; + Object.defineProperty(obj, 'bar', { + get: function() { } + }); + expect(obj).to.have.property('foo'); + expect(obj).to.have.property('bar'); + + expect({ 'foo.bar[]': 'baz'}) + .to.have.property('foo.bar[]'); + + err(function(){ + expect('asd').to.have.property('foo'); + }, "expected 'asd' to have a property 'foo'"); + err(function(){ + expect({ foo: { bar: 'baz' } }) + .to.have.property('foo.bar'); + }, "expected { foo: { bar: 'baz' } } to have a property 'foo.bar'"); + }); + + it('deep.property(name)', function(){ + expect({ 'foo.bar': 'baz'}) + .to.not.have.deep.property('foo.bar'); + expect({ foo: { bar: 'baz' } }) + .to.have.deep.property('foo.bar'); + + expect({ 'foo': [1, 2, 3] }) + .to.have.deep.property('foo[1]'); + + expect({ 'foo.bar[]': 'baz'}) + .to.have.deep.property('foo\\.bar\\[\\]'); + + err(function(){ + expect({ 'foo.bar': 'baz' }) + .to.have.deep.property('foo.bar'); + }, "expected { 'foo.bar': 'baz' } to have a deep property 'foo.bar'"); + }); + + it('property(name, val)', function(){ + expect('test').to.have.property('length', 4); + expect('asd').to.have.property('constructor', String); + + var deepObj = { + green: { tea: 'matcha' } + , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] + }; + expect(deepObj).to.have.deep.property('green.tea', 'matcha'); + expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); + expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); + + expect(deepObj).to.have.property('teas') + .that.is.an('array') + .with.deep.property('[2]') + .that.deep.equals({tea: 'konacha'}); + + err(function(){ + expect(deepObj).to.have.deep.property('teas[3]'); + }, "expected { Object (green, teas) } to have a deep property 'teas[3]'"); + err(function(){ + expect(deepObj).to.have.deep.property('teas[3]', 'bar'); + }, "expected { Object (green, teas) } to have a deep property 'teas[3]'"); + err(function(){ + expect(deepObj).to.have.deep.property('teas[3].tea', 'bar'); + }, "expected { Object (green, teas) } to have a deep property 'teas[3].tea'"); + + var arr = [ + [ 'chai', 'matcha', 'konacha' ] + , [ { tea: 'chai' } + , { tea: 'matcha' } + , { tea: 'konacha' } ] + ]; + expect(arr).to.have.deep.property('[0][1]', 'matcha'); + expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); + err(function(){ + expect(arr).to.have.deep.property('[2][1]'); + }, "expected [ Array(2) ] to have a deep property '[2][1]'"); + err(function(){ + expect(arr).to.have.deep.property('[2][1]', 'none'); + }, "expected [ Array(2) ] to have a deep property '[2][1]'"); + err(function(){ + expect(arr).to.have.deep.property('[0][3]', 'none'); + }, "expected [ Array(2) ] to have a deep property '[0][3]'"); + + err(function(){ + expect('asd').to.have.property('length', 4, 'blah'); + }, "blah: expected 'asd' to have a property 'length' of 4, but got 3"); + + err(function(){ + expect('asd').to.not.have.property('length', 3, 'blah'); + }, "blah: expected 'asd' to not have a property 'length' of 3"); + + err(function(){ + expect('asd').to.not.have.property('foo', 3, 'blah'); + }, "blah: 'asd' has no property 'foo'"); + + err(function(){ + expect('asd').to.have.property('constructor', Number, 'blah'); + }, "blah: expected 'asd' to have a property 'constructor' of [Function: Number], but got [Function: String]"); + }); + + it('deep.property(name, val)', function(){ + expect({ foo: { bar: 'baz' } }) + .to.have.deep.property('foo.bar', 'baz'); + + err(function(){ + expect({ foo: { bar: 'baz' } }) + .to.have.deep.property('foo.bar', 'quux', 'blah'); + }, "blah: expected { foo: { bar: 'baz' } } to have a deep property 'foo.bar' of 'quux', but got 'baz'"); + err(function(){ + expect({ foo: { bar: 'baz' } }) + .to.not.have.deep.property('foo.bar', 'baz', 'blah'); + }, "blah: expected { foo: { bar: 'baz' } } to not have a deep property 'foo.bar' of 'baz'"); + err(function(){ + expect({ foo: 5 }) + .to.not.have.deep.property('foo.bar', 'baz', 'blah'); + }, "blah: { foo: 5 } has no deep property 'foo.bar'"); + }); + + it('ownProperty(name)', function(){ + expect('test').to.have.ownProperty('length'); + expect('test').to.haveOwnProperty('length'); + expect({ length: 12 }).to.have.ownProperty('length'); + + err(function(){ + expect({ length: 12 }).to.not.have.ownProperty('length', 'blah'); + }, "blah: expected { length: 12 } to not have own property 'length'"); + }); + + it('ownPropertyDescriptor(name)', function(){ + expect('test').to.have.ownPropertyDescriptor('length'); + expect('test').to.haveOwnPropertyDescriptor('length'); + expect('test').not.to.have.ownPropertyDescriptor('foo'); + + var obj = {}; + var descriptor = { + configurable: false, + enumerable: true, + writable: true, + value: NaN + }; + Object.defineProperty(obj, 'test', descriptor); + expect(obj).to.have.ownPropertyDescriptor('test', descriptor); + err(function(){ + expect(obj).not.to.have.ownPropertyDescriptor('test', descriptor, 'blah'); + }, /^blah: expected the own property descriptor for 'test' on \{ test: NaN \} to not match \{ [^\}]+ \}$/); + err(function(){ + var wrongDescriptor = { + configurable: false, + enumerable: true, + writable: false, + value: NaN + }; + expect(obj).to.have.ownPropertyDescriptor('test', wrongDescriptor, 'blah'); + }, /^blah: expected the own property descriptor for 'test' on \{ test: NaN \} to match \{ [^\}]+ \}, got \{ [^\}]+ \}$/); + + err(function(){ + expect(obj).to.have.ownPropertyDescriptor('test2', 'blah'); + }, "blah: expected { test: NaN } to have an own property descriptor for 'test2'"); + + expect(obj).to.have.ownPropertyDescriptor('test').and.have.property('enumerable', true); + }); + + it('string()', function(){ + expect('foobar').to.have.string('bar'); + expect('foobar').to.have.string('foo'); + expect('foobar').to.not.have.string('baz'); + + err(function(){ + expect(3).to.have.string('baz'); + }, "expected 3 to be a string"); + + err(function(){ + expect('foobar').to.have.string('baz', 'blah'); + }, "blah: expected 'foobar' to contain 'baz'"); + + err(function(){ + expect('foobar').to.not.have.string('bar', 'blah'); + }, "blah: expected 'foobar' to not contain 'bar'"); + }); + + it('include()', function(){ + expect(['foo', 'bar']).to.include('foo'); + expect(['foo', 'bar']).to.include('foo'); + expect(['foo', 'bar']).to.include('bar'); + expect([1,2]).to.include(1); + expect(['foo', 'bar']).to.not.include('baz'); + expect(['foo', 'bar']).to.not.include(1); + expect({a:1,b:2}).to.include({b:2}); + expect({a:1,b:2}).to.not.include({b:3}); + expect({a:1,b:2}).to.include({a:1,b:2}); + expect({a:1,b:2}).to.not.include({a:1,c:2}); + + expect([{a:1},{b:2}]).to.include({a:1}); + expect([{a:1}]).to.include({a:1}); + expect([{a:1}]).to.not.include({b:1}); + + err(function(){ + expect(['foo']).to.include('bar', 'blah'); + }, "blah: expected [ 'foo' ] to include 'bar'"); + + err(function(){ + expect(['bar', 'foo']).to.not.include('foo', 'blah'); + }, "blah: expected [ 'bar', 'foo' ] to not include 'foo'"); + + err(function(){ + expect({a:1}).to.include({b:2}); + }, "expected { a: 1 } to have a property 'b'"); + + err(function(){ + expect({a:1,b:2}).to.not.include({b:2}); + }, "expected { a: 1, b: 2 } to not include { b: 2 }"); + + err(function(){ + expect([{a:1},{b:2}]).to.not.include({b:2}); + }, "expected [ { a: 1 }, { b: 2 } ] to not include { b: 2 }"); + + err(function(){ + expect(true).to.include(true); + }, "object tested must be an array, an object, or a string, but boolean given"); + + err(function(){ + expect(42.0).to.include(42); + }, "object tested must be an array, an object, or a string, but number given"); + + err(function(){ + expect(null).to.include(42); + }, "object tested must be an array, an object, or a string, but null given"); + + err(function(){ + expect(undefined).to.include(42); + }, "object tested must be an array, an object, or a string, but undefined given"); + + err(function(){ + expect(true).to.not.include(true); + }, "object tested must be an array, an object, or a string, but boolean given"); + + err(function(){ + expect(42.0).to.not.include(42); + }, "object tested must be an array, an object, or a string, but number given"); + + err(function(){ + expect(null).to.not.include(42); + }, "object tested must be an array, an object, or a string, but null given"); + + err(function(){ + expect(undefined).to.not.include(42); + }, "object tested must be an array, an object, or a string, but undefined given"); + }); + + it('keys(array|Object|arguments)', function(){ + expect({ foo: 1 }).to.have.keys(['foo']); + expect({ foo: 1 }).have.keys({ 'foo': 6 }); + expect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']); + expect({ foo: 1, bar: 2 }).to.have.keys('foo', 'bar'); + expect({ foo: 1, bar: 2 }).have.keys({ 'foo': 6, 'bar': 7 }); + expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar'); + expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('bar', 'foo'); + expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('baz'); + expect({ foo: 1, bar: 2 }).contain.keys({ 'foo': 6 }); + expect({ foo: 1, bar: 2 }).contain.keys({ 'bar': 7 }); + expect({ foo: 1, bar: 2 }).contain.keys({ 'foo': 6 }); + + expect({ foo: 1, bar: 2 }).to.contain.keys('foo'); + expect({ foo: 1, bar: 2 }).to.contain.keys('bar', 'foo'); + expect({ foo: 1, bar: 2 }).to.contain.keys(['foo']); + expect({ foo: 1, bar: 2 }).to.contain.keys(['bar']); + expect({ foo: 1, bar: 2 }).to.contain.keys(['bar', 'foo']); + expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys(['bar', 'foo']); + + expect({ foo: 1, bar: 2 }).to.not.have.keys('baz'); + expect({ foo: 1, bar: 2 }).to.not.have.keys('foo', 'baz'); + expect({ foo: 1, bar: 2 }).to.not.contain.keys('baz'); + expect({ foo: 1, bar: 2 }).to.not.contain.keys('foo', 'baz'); + expect({ foo: 1, bar: 2 }).to.not.contain.keys('baz', 'foo'); + + expect({ foo: 1, bar: 2 }).to.have.any.keys('foo', 'baz'); + expect({ foo: 1, bar: 2 }).to.have.any.keys('foo'); + expect({ foo: 1, bar: 2 }).to.contain.any.keys('bar', 'baz'); + expect({ foo: 1, bar: 2 }).to.contain.any.keys(['foo']); + expect({ foo: 1, bar: 2 }).to.have.all.keys(['bar', 'foo']); + expect({ foo: 1, bar: 2 }).to.contain.all.keys(['bar', 'foo']); + expect({ foo: 1, bar: 2 }).contain.any.keys({ 'foo': 6 }); + expect({ foo: 1, bar: 2 }).have.all.keys({ 'foo': 6, 'bar': 7 }); + expect({ foo: 1, bar: 2 }).contain.all.keys({ 'bar': 7, 'foo': 6 }); + + expect({ foo: 1, bar: 2 }).to.not.have.any.keys('baz', 'abc', 'def'); + expect({ foo: 1, bar: 2 }).to.not.have.any.keys('baz'); + expect({ foo: 1, bar: 2 }).to.not.contain.any.keys('baz'); + expect({ foo: 1, bar: 2 }).to.not.have.all.keys(['baz', 'foo']); + expect({ foo: 1, bar: 2 }).to.not.contain.all.keys(['baz', 'foo']); + expect({ foo: 1, bar: 2 }).not.have.all.keys({ 'baz': 8, 'foo': 7 }); + expect({ foo: 1, bar: 2 }).not.contain.all.keys({ 'baz': 8, 'foo': 7 }); + + err(function(){ + expect({ foo: 1 }).to.have.keys(); + }, "keys required"); + + err(function(){ + expect({ foo: 1 }).to.have.keys([]); + }, "keys required"); + + err(function(){ + expect({ foo: 1 }).to.not.have.keys([]); + }, "keys required"); + + err(function(){ + expect({ foo: 1 }).to.contain.keys([]); + }, "keys required"); + + var mixedArgsMsg = 'keys must be given single argument of Array|Object|String, or multiple String arguments' + + err(function(){ + expect({}).contain.keys(['a'], "b"); + }, mixedArgsMsg); + + err(function(){ + expect({}).contain.keys({ 'a': 1 }, "b"); + }, mixedArgsMsg); + + err(function(){ + expect({ foo: 1 }).to.have.keys(['bar']); + }, "expected { foo: 1 } to have key 'bar'"); + + err(function(){ + expect({ foo: 1 }).to.have.keys(['bar', 'baz']); + }, "expected { foo: 1 } to have keys 'bar', and 'baz'"); + + err(function(){ + expect({ foo: 1 }).to.have.keys(['foo', 'bar', 'baz']); + }, "expected { foo: 1 } to have keys 'foo', 'bar', and 'baz'"); + + err(function(){ + expect({ foo: 1 }).to.not.have.keys(['foo']); + }, "expected { foo: 1 } to not have key 'foo'"); + + err(function(){ + expect({ foo: 1 }).to.not.have.keys(['foo']); + }, "expected { foo: 1 } to not have key 'foo'"); + + err(function(){ + expect({ foo: 1, bar: 2 }).to.not.have.keys(['foo', 'bar']); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', and 'bar'"); + + err(function(){ + expect({ foo: 1, bar: 2 }).to.have.all.keys('foo'); + }, "expected { foo: 1, bar: 2 } to have key 'foo'"); + + err(function(){ + expect({ foo: 1 }).to.not.contain.keys(['foo']); + }, "expected { foo: 1 } to not contain key 'foo'"); + + err(function(){ + expect({ foo: 1 }).to.contain.keys('foo', 'bar'); + }, "expected { foo: 1 } to contain keys 'foo', and 'bar'"); + + err(function() { + expect({ foo: 1 }).to.have.any.keys('baz'); + }, "expected { foo: 1 } to have key 'baz'"); + + err(function(){ + expect({ foo: 1, bar: 2 }).to.not.have.all.keys(['foo', 'bar']); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', and 'bar'"); + + err(function(){ + expect({ foo: 1, bar: 2 }).to.not.have.any.keys(['foo', 'baz']); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', or 'baz'"); + + // repeat previous tests with Object as arg. + err(function(){ + expect({ foo: 1 }).have.keys({ 'bar': 1 }); + }, "expected { foo: 1 } to have key 'bar'"); + + err(function(){ + expect({ foo: 1 }).have.keys({ 'bar': 1, 'baz': 1}); + }, "expected { foo: 1 } to have keys 'bar', and 'baz'"); + + err(function(){ + expect({ foo: 1 }).have.keys({ 'foo': 1, 'bar': 1, 'baz': 1}); + }, "expected { foo: 1 } to have keys 'foo', 'bar', and 'baz'"); + + err(function(){ + expect({ foo: 1 }).not.have.keys({ 'foo': 1 }); + }, "expected { foo: 1 } to not have key 'foo'"); + + err(function(){ + expect({ foo: 1 }).not.have.keys({ 'foo': 1 }); + }, "expected { foo: 1 } to not have key 'foo'"); + + err(function(){ + expect({ foo: 1, bar: 2 }).not.have.keys({ 'foo': 1, 'bar': 1}); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', and 'bar'"); + + err(function(){ + expect({ foo: 1 }).not.contain.keys({ 'foo': 1 }); + }, "expected { foo: 1 } to not contain key 'foo'"); + + err(function(){ + expect({ foo: 1 }).contain.keys('foo', 'bar'); + }, "expected { foo: 1 } to contain keys 'foo', and 'bar'"); + + err(function() { + expect({ foo: 1 }).have.any.keys('baz'); + }, "expected { foo: 1 } to have key 'baz'"); + + err(function(){ + expect({ foo: 1, bar: 2 }).not.have.all.keys({ 'foo': 1, 'bar': 1}); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', and 'bar'"); + + err(function(){ + expect({ foo: 1, bar: 2 }).not.have.any.keys({ 'foo': 1, 'baz': 1}); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', or 'baz'"); + + }); + + it('keys(array) will not mutate array (#359)', function () { + var expected = [ 'b', 'a' ]; + var original_order = [ 'b', 'a' ]; + var obj = { "b": 1, "a": 1 }; + expect(expected).deep.equal(original_order); + expect(obj).keys(original_order); + expect(expected).deep.equal(original_order); + }); + + it('chaining', function(){ + var tea = { name: 'chai', extras: ['milk', 'sugar', 'smile'] }; + expect(tea).to.have.property('extras').with.lengthOf(3); + + expect(tea).to.have.property('extras').which.contains('smile'); + + err(function(){ + expect(tea).to.have.property('extras').with.lengthOf(4); + }, "expected [ 'milk', 'sugar', 'smile' ] to have a length of 4 but got 3"); + + expect(tea).to.be.a('object').and.have.property('name', 'chai'); + + var badFn = function () { throw new Error('testing'); }; + + expect(badFn).to.throw(Error).with.property('message', 'testing'); + }); + + it('throw', function () { + // See GH-45: some poorly-constructed custom errors don't have useful names + // on either their constructor or their constructor prototype, but instead + // only set the name inside the constructor itself. + var PoorlyConstructedError = function () { + this.name = 'PoorlyConstructedError'; + }; + PoorlyConstructedError.prototype = Object.create(Error.prototype); + + function CustomError(message) { + this.name = 'CustomError'; + this.message = message; + } + CustomError.prototype = Error.prototype; + + var specificError = new RangeError('boo'); + + var goodFn = function () { 1==1; } + , badFn = function () { throw new Error('testing'); } + , refErrFn = function () { throw new ReferenceError('hello'); } + , ickyErrFn = function () { throw new PoorlyConstructedError(); } + , specificErrFn = function () { throw specificError; } + , customErrFn = function() { throw new CustomError('foo'); }; + + expect(goodFn).to.not.throw(); + expect(goodFn).to.not.throw(Error); + expect(goodFn).to.not.throw(specificError); + expect(badFn).to.throw(); + expect(badFn).to.throw(Error); + expect(badFn).to.not.throw(ReferenceError); + expect(badFn).to.not.throw(specificError); + expect(refErrFn).to.throw(); + expect(refErrFn).to.throw(ReferenceError); + expect(refErrFn).to.throw(Error); + expect(refErrFn).to.not.throw(TypeError); + expect(refErrFn).to.not.throw(specificError); + expect(ickyErrFn).to.throw(); + expect(ickyErrFn).to.throw(PoorlyConstructedError); + expect(ickyErrFn).to.throw(Error); + expect(ickyErrFn).to.not.throw(specificError); + expect(specificErrFn).to.throw(specificError); + + expect(badFn).to.throw(/testing/); + expect(badFn).to.not.throw(/hello/); + expect(badFn).to.throw('testing'); + expect(badFn).to.not.throw('hello'); + + expect(badFn).to.throw(Error, /testing/); + expect(badFn).to.throw(Error, 'testing'); + + err(function(){ + expect(goodFn).to.throw(); + }, "expected [Function] to throw an error"); + + err(function(){ + expect(goodFn).to.throw(ReferenceError); + }, "expected [Function] to throw ReferenceError"); + + err(function(){ + expect(goodFn).to.throw(specificError); + }, "expected [Function] to throw 'RangeError: boo'"); + + err(function(){ + expect(badFn).to.not.throw(); + }, "expected [Function] to not throw an error but 'Error: testing' was thrown"); + + err(function(){ + expect(badFn).to.throw(ReferenceError); + }, "expected [Function] to throw 'ReferenceError' but 'Error: testing' was thrown"); + + err(function(){ + expect(badFn).to.throw(specificError); + }, "expected [Function] to throw 'RangeError: boo' but 'Error: testing' was thrown"); + + err(function(){ + expect(badFn).to.not.throw(Error); + }, "expected [Function] to not throw 'Error' but 'Error: testing' was thrown"); + + err(function(){ + expect(refErrFn).to.not.throw(ReferenceError); + }, "expected [Function] to not throw 'ReferenceError' but 'ReferenceError: hello' was thrown"); + + err(function(){ + expect(badFn).to.throw(PoorlyConstructedError); + }, "expected [Function] to throw 'PoorlyConstructedError' but 'Error: testing' was thrown"); + + err(function(){ + expect(ickyErrFn).to.not.throw(PoorlyConstructedError); + }, /^(expected \[Function\] to not throw 'PoorlyConstructedError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/); + + err(function(){ + expect(ickyErrFn).to.throw(ReferenceError); + }, /^(expected \[Function\] to throw 'ReferenceError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/); + + err(function(){ + expect(specificErrFn).to.throw(new ReferenceError('eek')); + }, "expected [Function] to throw 'ReferenceError: eek' but 'RangeError: boo' was thrown"); + + err(function(){ + expect(specificErrFn).to.not.throw(specificError); + }, "expected [Function] to not throw 'RangeError: boo'"); + + err(function (){ + expect(badFn).to.not.throw(/testing/); + }, "expected [Function] to throw error not matching /testing/"); + + err(function () { + expect(badFn).to.throw(/hello/); + }, "expected [Function] to throw error matching /hello/ but got 'testing'"); + + err(function () { + expect(badFn).to.throw(Error, /hello/, 'blah'); + }, "blah: expected [Function] to throw error matching /hello/ but got 'testing'"); + + err(function () { + expect(badFn).to.throw(Error, 'hello', 'blah'); + }, "blah: expected [Function] to throw error including 'hello' but got 'testing'"); + + err(function () { + (customErrFn).should.not.throw(); + }, "expected [Function] to not throw an error but 'CustomError: foo' was thrown"); + }); + + it('respondTo', function(){ + function Foo(){}; + Foo.prototype.bar = function(){}; + (Foo as any).func = function() {}; + + var bar: any = {}; + bar.foo = function(){}; + + expect(Foo).to.respondTo('bar'); + expect(Foo).to.not.respondTo('foo'); + expect(Foo).itself.to.respondTo('func'); + expect(Foo).itself.not.to.respondTo('bar'); + + expect(bar).to.respondTo('foo'); + + err(function(){ + expect(Foo).to.respondTo('baz', 'constructor'); + }, /^(constructor: expected)(.*)(\[Function: Foo\])(.*)(to respond to \'baz\')$/); + + err(function(){ + expect(bar).to.respondTo('baz', 'object'); + }, /^(object: expected)(.*)(\{ foo: \[Function\] \}|\{ Object \()(.*)(to respond to \'baz\')$/); + }); + + it('satisfy', function(){ + var matcher = function (num) { + return num === 1; + }; + + expect(1).to.satisfy(matcher); + + err(function(){ + expect(2).to.satisfy(matcher, 'blah'); + }, "blah: expected 2 to satisfy [Function]"); + }); + + it('closeTo', function(){ + expect(1.5).to.be.closeTo(1.0, 0.5); + expect(10).to.be.closeTo(20, 20); + expect(-10).to.be.closeTo(20, 30); + + err(function(){ + expect(2).to.be.closeTo(1.0, 0.5, 'blah'); + }, "blah: expected 2 to be close to 1 +/- 0.5"); + + err(function(){ + expect(-10).to.be.closeTo(20, 29, 'blah'); + }, "blah: expected -10 to be close to 20 +/- 29"); + + err(function() { + expect([1.5]).to.be.closeTo(1.0, 0.5); + }, "expected [ 1.5 ] to be a number"); + }); + + it('approximately', function(){ + expect(1.5).to.be.approximately(1.0, 0.5); + expect(10).to.be.approximately(20, 20); + expect(-10).to.be.approximately(20, 30); + + err(function(){ + expect(2).to.be.approximately(1.0, 0.5, 'blah'); + }, "blah: expected 2 to be close to 1 +/- 0.5"); + + err(function(){ + expect(-10).to.be.approximately(20, 29, 'blah'); + }, "blah: expected -10 to be close to 20 +/- 29"); + + err(function() { + expect([1.5]).to.be.approximately(1.0, 0.5); + }, "expected [ 1.5 ] to be a number"); + }); + + it('oneOf', function() { + expect(1).to.be.oneOf([1, 2, 3]); + expect('1').to.not.be.oneOf([1, 2, 3]); + expect([3, [4]]).to.not.be.oneOf([1, 2, [3, 4]]); + var threeFour = [3, [4]]; + expect(threeFour).to.be.oneOf([1, 2, threeFour]); + }); + + it('include.members', function() { + expect([1, 2, 3]).to.include.members([]); + expect([1, 2, 3]).to.include.members([3, 2]); + expect([1, 2, 3]).to.not.include.members([8, 4]); + expect([1, 2, 3]).to.not.include.members([1, 2, 3, 4]); + }); + + it('same.members', function() { + expect([5, 4]).to.have.same.members([4, 5]); + expect([5, 4]).to.have.same.members([5, 4]); + expect([5, 4]).to.not.have.same.members([]); + expect([5, 4]).to.not.have.same.members([6, 3]); + expect([5, 4]).to.not.have.same.members([5, 4, 2]); + }); + + it('members', function() { + expect([5, 4]).members([4, 5]); + expect([5, 4]).members([5, 4]); + expect([5, 4]).not.members([]); + expect([5, 4]).not.members([6, 3]); + expect([5, 4]).not.members([5, 4, 2]); + expect([{ id: 1 }]).not.members([{ id: 1 }]); + }); + + it('deep.members', function() { + expect([{ id: 1 }]).deep.members([{ id: 1 }]); + expect([{ id: 2 }]).not.deep.members([{ id: 1 }]); + err(function(){ + expect([{ id: 1 }]).deep.members([{ id: 2 }]) + }, "expected [ { id: 1 } ] to have the same members as [ { id: 2 } ]"); + }); + + it('change', function() { + var obj = { value: 10, str: 'foo' }, + fn = function() { obj.value += 5 }, + sameFn = function() { 'foo' + 'bar' }, + bangFn = function() { obj.str += '!' }; + + expect(fn).to.change(obj, 'value'); + expect(sameFn).to.not.change(obj, 'value'); + expect(sameFn).to.not.change(obj, 'str'); + expect(bangFn).to.change(obj, 'str'); + }); + + it('increase, decrease', function() { + var obj = { value: 10 }, + incFn = function() { obj.value += 2 }, + decFn = function() { obj.value -= 3 }, + smFn = function() { obj.value += 0 }; + + expect(smFn).to.not.increase(obj, 'value'); + expect(decFn).to.not.increase(obj, 'value'); + expect(incFn).to.increase(obj, 'value'); + + expect(smFn).to.not.decrease(obj, 'value'); + expect(incFn).to.not.decrease(obj, 'value'); + expect(decFn).to.decrease(obj, 'value'); + }); + + it('extensible', function() { + var nonExtensibleObject = Object.preventExtensions({}); + + expect({}).to.be.extensible; + expect(nonExtensibleObject).to.not.be.extensible; + + err(function() { + expect(nonExtensibleObject).to.be.extensible; + }, 'expected {} to be extensible'); + + err(function() { + expect({}).to.not.be.extensible; + }, 'expected {} to not be extensible'); + + // Making sure ES6-like Object.isExtensible response is respected for all primitive types + + expect(42).to.not.be.extensible; + expect(null).to.not.be.extensible; + expect('foo').to.not.be.extensible; + expect(false).to.not.be.extensible; + expect(undefined).to.not.be.extensible; + + err(function() { + expect(42).to.be.extensible; + }, 'expected 42 to be extensible'); + + err(function() { + expect(null).to.be.extensible; + }, 'expected null to be extensible'); + + err(function() { + expect('foo').to.be.extensible; + }, 'expected \'foo\' to be extensible'); + + err(function() { + expect(false).to.be.extensible; + }, 'expected false to be extensible'); + + err(function() { + expect(undefined).to.be.extensible; + }, 'expected undefined to be extensible'); + }); + + it('sealed', function() { + var sealedObject = Object.seal({}); + + expect(sealedObject).to.be.sealed; + expect({}).to.not.be.sealed; + + err(function() { + expect({}).to.be.sealed; + }, 'expected {} to be sealed'); + + err(function() { + expect(sealedObject).to.not.be.sealed; + }, 'expected {} to not be sealed'); + + // Making sure ES6-like Object.isSealed response is respected for all primitive types + + expect(42).to.be.sealed; + expect(null).to.be.sealed; + expect('foo').to.be.sealed; + expect(false).to.be.sealed; + expect(undefined).to.be.sealed; + + err(function() { + expect(42).to.not.be.sealed; + }, 'expected 42 to not be sealed'); + + err(function() { + expect(null).to.not.be.sealed; + }, 'expected null to not be sealed'); + + err(function() { + expect('foo').to.not.be.sealed; + }, 'expected \'foo\' to not be sealed'); + + err(function() { + expect(false).to.not.be.sealed; + }, 'expected false to not be sealed'); + + err(function() { + expect(undefined).to.not.be.sealed; + }, 'expected undefined to not be sealed'); + }); + + it('frozen', function() { + var frozenObject = Object.freeze({}); + + expect(frozenObject).to.be.frozen; + expect({}).to.not.be.frozen; + + err(function() { + expect({}).to.be.frozen; + }, 'expected {} to be frozen'); + + err(function() { + expect(frozenObject).to.not.be.frozen; + }, 'expected {} to not be frozen'); + + // Making sure ES6-like Object.isFrozen response is respected for all primitive types + + expect(42).to.be.frozen; + expect(null).to.be.frozen; + expect('foo').to.be.frozen; + expect(false).to.be.frozen; + expect(undefined).to.be.frozen; + + err(function() { + expect(42).to.not.be.frozen; + }, 'expected 42 to not be frozen'); + + err(function() { + expect(null).to.not.be.frozen; + }, 'expected null to not be frozen'); + + err(function() { + expect('foo').to.not.be.frozen; + }, 'expected \'foo\' to not be frozen'); + + err(function() { + expect(false).to.not.be.frozen; + }, 'expected false to not be frozen'); + + err(function() { + expect(undefined).to.not.be.frozen; + }, 'expected undefined to not be frozen'); + }); +}); diff --git a/source-test/globalShould.ts b/source-test/globalShould.ts new file mode 100644 index 0000000..a2d4908 --- /dev/null +++ b/source-test/globalShould.ts @@ -0,0 +1,17 @@ +import chai = require('chai'); + +describe('global should', function () { + it('works', function () { + var theGlobal: any = typeof window !== 'undefined' + ? window + : global; + + theGlobal.globalShould = chai.should(); + + try { + theGlobal.globalShould.not.exist(undefined); + } finally { + delete theGlobal.globalShould; + } + }); +}); diff --git a/source-test/plugin.ts b/source-test/plugin.ts new file mode 100644 index 0000000..12e581c --- /dev/null +++ b/source-test/plugin.ts @@ -0,0 +1,25 @@ +import chai = require('chai'); + +describe('plugins', function () { + function plugin (chai) { + if (chai.Assertion.prototype.testing) return; + + Object.defineProperty(chai.Assertion.prototype, 'testing', { + get: function () { + return 'successful'; + } + }); + } + + it('basic usage', function () { + chai.use(plugin); + var expect = chai.expect; + expect((expect('') as any).testing).to.equal('successful'); + }); + + it('double plugin', function () { + chai.expect(function () { + chai.use(plugin); + }).to.not.throw(); + }); +}); diff --git a/source-test/should.ts b/source-test/should.ts new file mode 100644 index 0000000..8b5abd9 --- /dev/null +++ b/source-test/should.ts @@ -0,0 +1,1080 @@ +import err from './bootstrap/index'; +import chai = require('chai'); + +describe('should', function() { + var should = chai.Should(); + + it('assertion', function(){ + 'test'.should.be.a('string'); + should.equal('foo', 'foo'); + should.not.equal('foo', 'bar'); + }); + + it('fail', function () { + err(function() { + should.fail(0, 1, 'this has failed'); + }, 'this has failed'); + }); + + it('root exist', function () { + var foo = 'foo' + , bar = undefined; + should.exist(foo); + should.not.exist(bar); + + err(function () { + should.exist(bar, 'blah'); + }, "blah: expected undefined to exist"); + + err(function () { + should.not.exist(foo, 'blah'); + }, "blah: expected 'foo' to not exist") + }); + + it('root equal', function () { + var value1 = 'value' + , value2 = 'value' + , foo = 'foo'; + should.equal(value1, value2); + should.not.equal(value1, foo); + + err(function () { + should.equal(value1, foo, 'blah'); + }, "blah: expected 'value' to equal 'foo'"); + + err(function () { + should.not.equal(value1, value2, 'blah'); + }, "blah: expected 'value' to not equal 'value'") + }); + + it('root Throw', function () { + should.Throw(function() { throw new Error('error!') }, Error, 'error!'); + should.not.Throw(function () { }); + + err(function () { + should.Throw(function () { throw new Error('error!') }, Error, 'needed user!', 'blah'); + }, "blah: expected [Function] to throw error including 'needed user!' but got 'error!'"); + + err(function () { + should.not.Throw(function () { throw new Error('error!') }, Error, 'error!', 'blah'); + }, "blah: expected [Function] to not throw 'Error' but 'Error: error!' was thrown"); + }); + + it('true', function(){ + (true).should.be.true; + false.should.not.be.true; + (1).should.not.be.true;false + false.should.have.been.false; + + err(function(){ + 'test'.should.be.true; + }, "expected 'test' to be true") + }); + + it('ok', function(){ + true.should.be.ok; + false.should.not.be.ok; + (1).should.be.ok; + (0).should.not.be.ok; + + err(function(){ + ''.should.be.ok; + }, "expected '' to be truthy"); + + err(function(){ + 'test'.should.not.be.ok; + }, "expected 'test' to be falsy"); + }); + + it('false', function(){ + false.should.be.false; + true.should.not.be.false; + (0).should.not.be.false; + + err(function(){ + ''.should.be.false; + }, "expected '' to be false") + }); + + it('null', function(){ + (0).should.not.be.null; + + err(function(){ + ''.should.be.null; + }, "expected '' to be null") + }); + + it('NaN', function(){ + 'foo'.should.be.NaN; + (4).should.not.be.NaN; + + err(function(){ + (4).should.be.NaN; + }, "expected 4 to be NaN") + }); + + it('undefined', function(){ + (0).should.not.be.undefined; + + err(function(){ + ''.should.be.undefined; + }, "expected '' to be undefined") + }); + + it('arguments', function(){ + var args = (function(...args){ return arguments; })(1,2,3); + args.should.be.arguments; + [].should.not.be.arguments; + }); + + it('.equal()', function(){ + var foo; + should.equal(undefined, foo); + }); + + it('typeof', function(){ + 'test'.should.be.a('string'); + + err(function(){ + 'test'.should.not.be.a('string'); + }, "expected 'test' not to be a string"); + + (5).should.be.a('number'); + (new Number(1)).should.be.a('number'); + Number(1).should.be.a('number'); + (true).should.be.a('boolean'); + (new Array()).should.be.a('array'); + (new Object()).should.be.a('object'); + ({}).should.be.a('object'); + ([]).should.be.a('array'); + (function() {}).should.be.a('function'); + + (5).should.be.a('number'); + + err(function(){ + (5).should.not.be.a('number'); + }, "expected 5 not to be a number"); + }); + + it('instanceof', function(){ + function Foo(){} + new Foo().should.be.an.instanceof(Foo); + + err(function(){ + (3).should.an.instanceof(Foo, 'blah'); + }, "blah: expected 3 to be an instance of Foo"); + }); + + it('within(start, finish)', function(){ + (5).should.be.within(5, 10); + (5).should.be.within(3,6); + (5).should.be.within(3,5); + (5).should.not.be.within(1,3); + + err(function(){ + (5).should.not.be.within(4,6, 'blah'); + }, "blah: expected 5 to not be within 4..6"); + + err(function(){ + (10).should.be.within(50,100, 'blah'); + }, "blah: expected 10 to be within 50..100"); + + err(function(){ + ({ foo: 1 }).should.have.length.within(50,100, 'blah'); + }, "blah: expected { foo: 1 } to have a property 'length'"); + }); + + it('above(n)', function(){ + (5).should.be.above(2); + (5).should.be.greaterThan(2); + (5).should.not.be.above(5); + (5).should.not.be.above(6); + + err(function(){ + (5).should.be.above(6, 'blah'); + }, "blah: expected 5 to be above 6"); + + err(function(){ + (10).should.not.be.above(6, 'blah'); + }, "blah: expected 10 to be at most 6"); + + err(function(){ + ({foo: 1}).should.have.length.above(3, 'blah'); + }, "blah: expected { foo: 1 } to have a property 'length'"); + }); + + it('least(n)', function(){ + (5).should.be.at.least(5); + (5).should.not.be.at.least(6); + + err(function(){ + (5).should.be.at.least(6, 'blah'); + }, "blah: expected 5 to be at least 6"); + + err(function(){ + (10).should.not.be.at.least(6, 'blah'); + }, "blah: expected 10 to be below 6"); + + err(function(){ + ({foo: 1}).should.have.length.of.at.least(3, 'blah'); + }, "blah: expected { foo: 1 } to have a property 'length'"); + }); + + it('below(n)', function(){ + (2).should.be.below(5); + (2).should.be.lessThan(5); + (2).should.not.be.below(2); + (2).should.not.be.below(1); + + err(function(){ + (6).should.be.below(5, 'blah'); + }, "blah: expected 6 to be below 5"); + + err(function(){ + (6).should.not.be.below(10, 'blah'); + }, "blah: expected 6 to be at least 10"); + + err(function(){ + ({foo: 1}).should.have.length.below(3, 'blah'); + }, "blah: expected { foo: 1 } to have a property 'length'"); + }); + + it('most(n)', function(){ + (2).should.be.at.most(2); + (2).should.not.be.at.most(1); + + err(function(){ + (6).should.be.at.most(5, 'blah'); + }, "blah: expected 6 to be at most 5"); + + err(function(){ + (6).should.not.be.at.most(10, 'blah'); + }, "blah: expected 6 to be above 10"); + + err(function(){ + ({foo: 1}).should.have.length.of.at.most(3, 'blah'); + }, "blah: expected { foo: 1 } to have a property 'length'"); + }); + + it('match(regexp)', function(){ + 'foobar'.should.match(/^foo/) + 'foobar'.should.not.match(/^bar/) + + err(function(){ + 'foobar'.should.match(/^bar/i, 'blah') + }, "blah: expected 'foobar' to match /^bar/i"); + + err(function(){ + 'foobar'.should.not.match(/^foo/i, 'blah') + }, "blah: expected 'foobar' not to match /^foo/i"); + }); + + it('length(n)', function(){ + 'test'.should.have.length(4); + 'test'.should.not.have.length(3); + [1,2,3].should.have.length(3); + + err(function(){ + (4).should.have.length(3, 'blah'); + }, 'blah: expected 4 to have a property \'length\''); + + err(function(){ + 'asd'.should.not.have.length(3, 'blah'); + }, "blah: expected 'asd' to not have a length of 3"); + }); + + it('eql(val)', function(){ + var a = new Date(1, 2, 3) + , b = new Date(4, 5, 6); + + a.should.eql(a); + a.should.not.eql(b); + a.should.not.eql({}); + 'test'.should.eql('test'); + ({ foo: 'bar' }).should.eql({ foo: 'bar' }); + /a/.should.eql(/a/); + /a/.should.not.eql(/b/); + /a/.should.not.eql({}); + /a/g.should.eql(/a/g); + /a/g.should.not.eql(/b/g); + /a/i.should.eql(/a/i); + /a/i.should.not.eql(/b/i); + /a/m.should.eql(/a/m); + /a/m.should.not.eql(/b/m); + (1).should.eql(1); + '4'.should.not.eql(4); + + err(function(){ + (4).should.eql(3, 'blah'); + }, 'blah: expected 4 to deeply equal 3'); + }); + + it('equal(val)', function(){ + 'test'.should.equal('test'); + (1).should.equal(1); + + err(function(){ + (4).should.equal(3, 'blah'); + }, 'blah: expected 4 to equal 3'); + + err(function(){ + '4'.should.equal(4, 'blah'); + }, "blah: expected '4' to equal 4"); + }); + + it('empty', function(){ + function FakeArgs() {}; + FakeArgs.prototype.length = 0; + + ''.should.be.empty; + 'foo'.should.not.be.empty; + ([]).should.be.empty; + (['foo']).should.not.be.empty; + (new FakeArgs).should.be.empty; + ({arguments: 0}).should.not.be.empty; + ({}).should.be.empty; + ({foo: 'bar'}).should.not.be.empty; + + err(function(){ + ''.should.not.be.empty; + }, "expected \'\' not to be empty"); + + err(function(){ + 'foo'.should.be.empty; + }, "expected \'foo\' to be empty"); + + err(function(){ + ([]).should.not.be.empty; + }, "expected [] not to be empty"); + + err(function(){ + (['foo']).should.be.empty; + }, "expected [ \'foo\' ] to be empty"); + + err(function(){ + (new FakeArgs).should.not.be.empty; + }, "expected { length: 0 } not to be empty"); + + err(function(){ + ({arguments: 0}).should.be.empty; + }, "expected { arguments: 0 } to be empty"); + + err(function(){ + ({}).should.not.be.empty; + }, "expected {} not to be empty"); + + err(function(){ + ({foo: 'bar'}).should.be.empty; + }, "expected { foo: \'bar\' } to be empty"); + }); + + it('property(name)', function(){ + 'test'.should.have.property('length'); + (4).should.not.have.property('length'); + + err(function(){ + 'asd'.should.have.property('foo'); + }, "expected 'asd' to have a property 'foo'"); + }); + + it('property(name, val)', function(){ + 'test'.should.have.property('length', 4); + 'asd'.should.have.property('constructor', String); + + err(function(){ + 'asd'.should.have.property('length', 4, 'blah'); + }, "blah: expected 'asd' to have a property 'length' of 4, but got 3"); + + err(function(){ + 'asd'.should.not.have.property('length', 3, 'blah'); + }, "blah: expected 'asd' to not have a property 'length' of 3"); + + err(function(){ + 'asd'.should.not.have.property('foo', 3, 'blah'); + }, "blah: 'asd' has no property 'foo'"); + + err(function(){ + 'asd'.should.have.property('constructor', Number, 'blah'); + }, "blah: expected 'asd' to have a property 'constructor' of [Function: Number], but got [Function: String]"); + }); + + it('ownProperty(name)', function(){ + 'test'.should.have.ownProperty('length'); + 'test'.should.haveOwnProperty('length'); + ({ length: 12 }).should.have.ownProperty('length'); + + err(function(){ + ({ length: 12 }).should.not.have.ownProperty('length', 'blah'); + }, "blah: expected { length: 12 } to not have own property 'length'"); + }); + + it('ownPropertyDescriptor(name)', function(){ + 'test'.should.haveOwnPropertyDescriptor('length'); + 'test'.should.have.ownPropertyDescriptor('length'); + 'test'.should.not.have.ownPropertyDescriptor('foo'); + + var obj = { }; + var descriptor = { + configurable: false, + enumerable: true, + writable: true, + value: NaN + }; + Object.defineProperty(obj, 'test', descriptor); + obj.should.haveOwnPropertyDescriptor('test', descriptor); + err(function(){ + obj.should.not.haveOwnPropertyDescriptor('test', descriptor, 'blah'); + }, /^blah: expected the own property descriptor for 'test' on \{ test: NaN \} to not match \{ [^\}]+ \}$/); + err(function(){ + var wrongDescriptor = { + configurable: false, + enumerable: true, + writable: false, + value: NaN + }; + obj.should.haveOwnPropertyDescriptor('test', wrongDescriptor, 'blah'); + }, /^blah: expected the own property descriptor for 'test' on \{ test: NaN \} to match \{ [^\}]+ \}, got \{ [^\}]+ \}$/); + + err(function(){ + obj.should.haveOwnPropertyDescriptor('test2', 'blah'); + }, "blah: expected { test: NaN } to have an own property descriptor for 'test2'"); + + obj.should.have.ownPropertyDescriptor('test').and.have.property('enumerable', true); + }); + + it('string()', function(){ + 'foobar'.should.contain.string('bar'); + 'foobar'.should.contain.string('foo'); + 'foobar'.should.not.contain.string('baz'); + + err(function(){ + (3).should.contain.string('baz', 'blah'); + }, "blah: expected 3 to be a string"); + + err(function(){ + 'foobar'.should.contain.string('baz', 'blah'); + }, "blah: expected 'foobar' to contain 'baz'"); + + err(function(){ + 'foobar'.should.not.contain.string('bar', 'blah'); + }, "blah: expected 'foobar' to not contain 'bar'"); + }); + + it('oneOf()', function(){ + 'foo'.should.be.oneOf(['foo', 'bar']); + 'bar'.should.be.oneOf(['foo', 'bar']); + 'baz'.should.not.be.oneOf(['foo', 'bar']); + 'baz'.should.not.be.oneOf([]); + }); + + it('include()', function(){ + ['foo', 'bar'].should.include('foo'); + ['foo', 'bar'].should.contain('foo'); + ['foo', 'bar'].should.include('bar'); + [1,2].should.include(1); + ['foo', 'bar'].should.not.include('baz'); + ['foo', 'bar'].should.not.include(1); + ({a:1,b:2}).should.include({b:2}); + ({a:1,b:2}).should.not.include({b:3}); + + err(function(){ + ['foo'].should.include('bar', 'blah'); + }, "blah: expected [ 'foo' ] to include 'bar'"); + + err(function(){ + ['bar', 'foo'].should.not.include('foo', 'blah'); + }, "blah: expected [ 'bar', 'foo' ] to not include 'foo'"); + + err(function(){ + ({a:1}).should.include({b:2}); + }, "expected { a: 1 } to have a property 'b'"); + + err(function(){ + (true).should.include(true); + }, "object tested must be an array, an object, or a string, but boolean given"); + + err(function(){ + (42).should.include(4); + }, "object tested must be an array, an object, or a string, but number given"); + + err(function(){ + (true).should.not.include(true); + }, "object tested must be an array, an object, or a string, but boolean given"); + + err(function(){ + (42).should.not.include(4); + }, "object tested must be an array, an object, or a string, but number given"); + }); + + it('keys(array|Object|arguments)', function(){ + ({ foo: 1 }).should.have.keys(['foo']); + ({ foo: 1 }).should.have.keys({ 'foo': 6 }); + + ({ foo: 1, bar: 2 }).should.have.keys(['foo', 'bar']); + ({ foo: 1, bar: 2 }).should.have.keys('foo', 'bar'); + ({ foo: 1, bar: 2 }).should.have.keys({ 'foo': 6, 'bar': 7 }); + + ({ foo: 1, bar: 2, baz: 3 }).should.include.keys('foo', 'bar'); + ({ foo: 1, bar: 2, baz: 3 }).should.contain.keys('bar', 'foo'); + ({ foo: 1, bar: 2, baz: 3 }).should.contain.keys('baz'); + + ({ foo: 1, bar: 2 }).should.contain.keys('foo'); + ({ foo: 1, bar: 2 }).should.contain.keys('bar', 'foo'); + ({ foo: 1, bar: 2 }).should.contain.keys(['foo']); + ({ foo: 1, bar: 2 }).should.contain.keys(['bar']); + ({ foo: 1, bar: 2 }).should.contain.keys(['bar', 'foo']); + ({ foo: 1, bar: 2 }).should.contain.keys({ 'foo': 6 }); + ({ foo: 1, bar: 2 }).should.contain.keys({ 'bar': 7 }); + ({ foo: 1, bar: 2 }).should.contain.keys({ 'foo': 6 }); + + ({ foo: 1, bar: 2 }).should.not.have.keys('baz'); + ({ foo: 1, bar: 2 }).should.not.have.keys('foo', 'baz'); + ({ foo: 1, bar: 2 }).should.not.contain.keys('baz'); + ({ foo: 1, bar: 2 }).should.not.contain.keys('foo', 'baz'); + ({ foo: 1, bar: 2 }).should.not.contain.keys('baz', 'foo'); + + ({ foo: 1, bar: 2 }).should.have.any.keys('foo', 'baz'); + ({ foo: 1, bar: 2 }).should.have.any.keys('foo'); + ({ foo: 1, bar: 2 }).should.contain.any.keys('bar', 'baz'); + ({ foo: 1, bar: 2 }).should.contain.any.keys(['foo']); + ({ foo: 1, bar: 2 }).should.have.all.keys(['bar', 'foo']); + ({ foo: 1, bar: 2 }).should.contain.all.keys(['bar', 'foo']); + ({ foo: 1, bar: 2 }).should.contain.any.keys({ 'foo': 6 }); + ({ foo: 1, bar: 2 }).should.have.all.keys({ 'foo': 6, 'bar': 7 }); + ({ foo: 1, bar: 2 }).should.contain.all.keys({ 'bar': 7, 'foo': 6 }); + + ({ foo: 1, bar: 2 }).should.not.have.any.keys('baz', 'abc', 'def'); + ({ foo: 1, bar: 2 }).should.not.have.any.keys('baz'); + ({ foo: 1, bar: 2 }).should.not.contain.any.keys('baz'); + ({ foo: 1, bar: 2 }).should.not.have.all.keys(['baz', 'foo']); + ({ foo: 1, bar: 2 }).should.not.contain.all.keys(['baz', 'foo']); + ({ foo: 1, bar: 2 }).should.not.have.all.keys({ 'baz': 8, 'foo': 7 }); + ({ foo: 1, bar: 2 }).should.not.contain.all.keys({ 'baz': 8, 'foo': 7 }); + + err(function(){ + ({ foo: 1 }).should.have.keys(); + }, "keys required"); + + err(function(){ + ({ foo: 1 }).should.have.keys([]); + }, "keys required"); + + err(function(){ + ({ foo: 1 }).should.not.have.keys([]); + }, "keys required"); + + err(function(){ + ({ foo: 1 }).should.contain.keys([]); + }, "keys required"); + + var mixedArgsMsg = 'keys must be given single argument of Array|Object|String, or multiple String arguments' + + err(function(){ + ({}).should.contain.keys(['a'], "b"); + }, mixedArgsMsg); + + err(function(){ + ({}).should.contain.keys({ 'a': 1 }, "b"); + }, mixedArgsMsg); + + err(function(){ + ({ foo: 1 }).should.have.keys(['bar']); + }, "expected { foo: 1 } to have key 'bar'"); + + err(function(){ + ({ foo: 1 }).should.have.keys(['bar', 'baz']); + }, "expected { foo: 1 } to have keys 'bar', and 'baz'"); + + err(function(){ + ({ foo: 1 }).should.have.keys(['foo', 'bar', 'baz']); + }, "expected { foo: 1 } to have keys 'foo', 'bar', and 'baz'"); + + err(function(){ + ({ foo: 1 }).should.not.have.keys(['foo']); + }, "expected { foo: 1 } to not have key 'foo'"); + + err(function(){ + ({ foo: 1 }).should.not.have.keys(['foo']); + }, "expected { foo: 1 } to not have key 'foo'"); + + err(function(){ + ({ foo: 1, bar: 2 }).should.not.have.keys(['foo', 'bar']); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', and 'bar'"); + + err(function(){ + ({ foo: 1 }).should.not.contain.keys(['foo']); + }, "expected { foo: 1 } to not contain key 'foo'"); + + err(function(){ + ({ foo: 1 }).should.contain.keys('foo', 'bar'); + }, "expected { foo: 1 } to contain keys 'foo', and 'bar'"); + + err(function() { + ({ foo: 1 }).should.have.any.keys('baz'); + }, "expected { foo: 1 } to have key 'baz'"); + + err(function(){ + ({ foo: 1, bar: 2 }).should.not.have.all.keys(['foo', 'bar']); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', and 'bar'"); + + err(function(){ + ({ foo: 1, bar: 2 }).should.not.have.any.keys(['foo', 'baz']); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', or 'baz'"); + + // repeat previous tests with Object as arg. + err(function(){ + ({ foo: 1 }).should.have.keys({ 'bar': 1 }); + }, "expected { foo: 1 } to have key 'bar'"); + + err(function(){ + ({ foo: 1 }).should.have.keys({ 'bar': 1, 'baz': 1}); + }, "expected { foo: 1 } to have keys 'bar', and 'baz'"); + + err(function(){ + ({ foo: 1 }).should.have.keys({ 'foo': 1, 'bar': 1, 'baz': 1}); + }, "expected { foo: 1 } to have keys 'foo', 'bar', and 'baz'"); + + err(function(){ + ({ foo: 1 }).should.not.have.keys({ 'foo': 1 }); + }, "expected { foo: 1 } to not have key 'foo'"); + + err(function(){ + ({ foo: 1 }).should.not.have.keys({ 'foo': 1 }); + }, "expected { foo: 1 } to not have key 'foo'"); + + err(function(){ + ({ foo: 1, bar: 2 }).should.not.have.keys({ 'foo': 1, 'bar': 1}); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', and 'bar'"); + + err(function(){ + ({ foo: 1 }).should.not.contain.keys({ 'foo': 1 }); + }, "expected { foo: 1 } to not contain key 'foo'"); + + err(function(){ + ({ foo: 1 }).should.contain.keys('foo', 'bar'); + }, "expected { foo: 1 } to contain keys 'foo', and 'bar'"); + + err(function() { + ({ foo: 1 }).should.have.any.keys('baz'); + }, "expected { foo: 1 } to have key 'baz'"); + + err(function(){ + ({ foo: 1, bar: 2 }).should.not.have.all.keys({ 'foo': 1, 'bar': 1}); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', and 'bar'"); + + err(function(){ + ({ foo: 1, bar: 2 }).should.not.have.any.keys({ 'foo': 1, 'baz': 1}); + }, "expected { foo: 1, bar: 2 } to not have keys 'foo', or 'baz'"); + + }); + + it('keys(array) will not mutate array (#359)', function () { + var expected = [ 'b', 'a' ]; + var original_order = [ 'b', 'a' ]; + var obj = { "b": 1, "a": 1 }; + expected.should.deep.equal(original_order); + obj.should.keys(original_order); + expected.should.deep.equal(original_order); + }); + + it('throw', function () { + // See GH-45: some poorly-constructed custom errors don't have useful names + // on either their constructor or their constructor prototype, but instead + // only set the name inside the constructor itself. + var PoorlyConstructedError = function () { + this.name = 'PoorlyConstructedError'; + }; + PoorlyConstructedError.prototype = Object.create(Error.prototype); + + function CustomError(message) { + this.name = 'CustomError'; + this.message = message; + } + CustomError.prototype = Error.prototype; + + var specificError = new RangeError('boo'); + + var goodFn = function () { 1==1; } + , badFn = function () { throw new Error('testing'); } + , stringErrFn = function () { throw 'testing'; } + , refErrFn = function () { throw new ReferenceError('hello'); } + , ickyErrFn = function () { throw new PoorlyConstructedError(); } + , specificErrFn = function () { throw specificError; } + , customErrFn = function() { throw new CustomError('foo'); }; + + (goodFn).should.not.throw(); + (goodFn).should.not.throw(Error); + (goodFn).should.not.throw(specificError); + (badFn).should.throw(); + (badFn).should.throw(Error); + (badFn).should.not.throw(ReferenceError); + (badFn).should.not.throw(specificError); + (stringErrFn).should.throw(); + (stringErrFn).should.not.throw(ReferenceError); + (stringErrFn).should.not.throw(specificError); + (refErrFn).should.throw(); + (refErrFn).should.throw(ReferenceError); + (refErrFn).should.throw(Error); + (refErrFn).should.not.throw(TypeError); + (refErrFn).should.not.throw(specificError); + (ickyErrFn).should.throw(); + (ickyErrFn).should.throw(PoorlyConstructedError); + (ickyErrFn).should.throw(Error); + (ickyErrFn).should.not.throw(specificError); + (specificErrFn).should.throw(specificError); + + (badFn).should.throw(/testing/); + (badFn).should.throw('testing'); + (badFn).should.not.throw(/hello/); + (badFn).should.not.throw('hello'); + (badFn).should.throw(Error, /testing/); + (badFn).should.throw(Error, 'testing'); + + (stringErrFn).should.throw(/testing/); + (stringErrFn).should.throw('testing'); + (stringErrFn).should.not.throw(/hello/); + (stringErrFn).should.not.throw('hello'); + + should.throw(badFn); + should.throw(refErrFn, ReferenceError); + should.throw(refErrFn, Error); + should.throw(ickyErrFn, PoorlyConstructedError); + should.throw(specificErrFn, specificError); + should.not.throw(goodFn); + should.not.throw(badFn, ReferenceError); + should.not.throw(badFn, specificError); + + should.throw(badFn, Error, /testing/); + should.throw(badFn, Error, 'testing'); + + err(function(){ + (goodFn).should.throw(); + }, "expected [Function] to throw an error"); + + err(function(){ + (goodFn).should.throw(ReferenceError); + }, "expected [Function] to throw ReferenceError"); + + err(function(){ + (goodFn).should.throw(specificError); + }, "expected [Function] to throw 'RangeError: boo'"); + + err(function(){ + (badFn).should.not.throw(); + }, "expected [Function] to not throw an error but 'Error: testing' was thrown"); + + err(function(){ + (badFn).should.throw(ReferenceError); + }, "expected [Function] to throw 'ReferenceError' but 'Error: testing' was thrown"); + + err(function(){ + (badFn).should.throw(specificError); + }, "expected [Function] to throw 'RangeError: boo' but 'Error: testing' was thrown"); + + err(function(){ + (badFn).should.not.throw(Error); + }, "expected [Function] to not throw 'Error' but 'Error: testing' was thrown"); + + err(function(){ + (stringErrFn).should.not.throw(); + }, "expected [Function] to not throw an error but 'testing' was thrown"); + + err(function(){ + (stringErrFn).should.throw(ReferenceError); + }, "expected [Function] to throw 'ReferenceError' but 'testing' was thrown"); + + err(function(){ + (stringErrFn).should.throw(specificError); + }, "expected [Function] to throw 'RangeError: boo' but 'testing' was thrown"); + + err(function(){ + (stringErrFn).should.not.throw('testing'); + }, "expected [Function] to throw error not including 'testing'"); + + err(function(){ + (refErrFn).should.not.throw(ReferenceError); + }, "expected [Function] to not throw 'ReferenceError' but 'ReferenceError: hello' was thrown"); + + err(function(){ + (badFn).should.throw(PoorlyConstructedError); + }, "expected [Function] to throw 'PoorlyConstructedError' but 'Error: testing' was thrown") + + err(function(){ + (ickyErrFn).should.not.throw(PoorlyConstructedError); + }, /^(expected \[Function\] to not throw 'PoorlyConstructedError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/); + + err(function(){ + (ickyErrFn).should.throw(ReferenceError); + }, /^(expected \[Function\] to throw 'ReferenceError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/); + + err(function(){ + (specificErrFn).should.throw(new ReferenceError('eek')); + }, "expected [Function] to throw 'ReferenceError: eek' but 'RangeError: boo' was thrown"); + + err(function(){ + (specificErrFn).should.not.throw(specificError); + }, "expected [Function] to not throw 'RangeError: boo'"); + + err(function (){ + (badFn).should.not.throw(/testing/); + }, "expected [Function] to throw error not matching /testing/"); + + err(function () { + (badFn).should.throw(/hello/); + }, "expected [Function] to throw error matching /hello/ but got \'testing\'"); + + err(function () { + (badFn).should.throw(Error, /hello/, 'blah'); + }, "blah: expected [Function] to throw error matching /hello/ but got 'testing'"); + + err(function () { + (badFn).should.throw(Error, 'hello', 'blah'); + }, "blah: expected [Function] to throw error including 'hello' but got 'testing'"); + + err(function () { + (customErrFn).should.not.throw(); + }, "expected [Function] to not throw an error but 'CustomError: foo' was thrown"); + }); + + it('respondTo', function(){ + function Foo(){}; + Foo.prototype.bar = function(){}; + (Foo as any).func = function(){}; + + var bar: any = {}; + bar.foo = function(){}; + + Foo.should.respondTo('bar'); + Foo.should.not.respondTo('foo'); + Foo.should.itself.respondTo('func'); + Foo.should.itself.not.respondTo('bar'); + + bar.should.respondTo('foo'); + + err(function(){ + Foo.should.respondTo('baz', 'constructor'); + }, /^(constructor: expected)(.*)(\[Function: Foo\])(.*)(to respond to \'baz\')$/); + + err(function(){ + bar.should.respondTo('baz', 'object'); + }, /^(object: expected)(.*)(\{ foo: \[Function\] \}|\{ Object \()(.*)(to respond to \'baz\')$/); + }); + + it('satisfy', function(){ + var matcher = function (num){ + return num === 1; + }; + + (1).should.satisfy(matcher); + + err(function(){ + (2).should.satisfy(matcher, 'blah'); + }, "blah: expected 2 to satisfy [Function]"); + }); + + it('closeTo', function(){ + (1.5).should.be.closeTo(1.0, 0.5); + + err(function(){ + (2).should.be.closeTo(1.0, 0.5, 'blah'); + }, "blah: expected 2 to be close to 1 +/- 0.5"); + + err(function() { + [1.5].should.be.closeTo(1.0, 0.5); + }, "expected [ 1.5 ] to be a number"); + + // err(function() { + // (1.5).should.be.closeTo("1.0", 0.5); + // }, "the arguments to closeTo or approximately must be numbers"); + + // err(function() { + // (1.5).should.be.closeTo(1.0, true); + // }, "the arguments to closeTo or approximately must be numbers"); + }); + + it('approximately', function(){ + (1.5).should.be.approximately(1.0, 0.5); + + err(function(){ + (2).should.be.approximately(1.0, 0.5, 'blah'); + }, "blah: expected 2 to be close to 1 +/- 0.5"); + + err(function() { + [1.5].should.be.approximately(1.0, 0.5); + }, "expected [ 1.5 ] to be a number"); + + // err(function() { + // (1.5).should.be.approximately("1.0", 0.5); + // }, "the arguments to closeTo or approximately must be numbers"); + + // err(function() { + // (1.5).should.be.approximately(1.0, true); + // }, "the arguments to closeTo or approximately must be numbers"); + }); + + it('include.members', function() { + [1, 2, 3].should.include.members([3]); + [1, 2, 3].should.include.members([]); + [1, 2, 3].should.include.members([2, 1]); + + [1, 2, 3].should.not.include.members([999]); + [].should.not.include.members([23]); + + err(function() { + [].should.include.members([43]); + }, 'expected [] to be a superset of [ 43 ]'); + + err(function() { + [5, 2, 1].should.not.include.members([2]); + }, 'expected [ 5, 2, 1 ] to not be a superset of [ 2 ]'); + + err(function() { + 'foo'.should.include.members([12]); + }, "expected 'foo' to be an array"); + + // err(function() { + // [1, 2, 3].should.include.members('o'); + // }, "expected 'o' to be an array"); + }); + + it('memberEquals', function() { + [1, 2, 3].should.have.same.members([3, 2, 1]); + [5, 4].should.have.same.members([5, 4]); + [].should.have.same.members([]); + + err(function() { + [1, 2, 3].should.have.same.members([]); + }, 'expected [ 1, 2, 3 ] to have the same members as []'); + + // err(function() { + // [1, 2, 3].should.have.same.members(4); + // }, 'expected 4 to be an array'); + }); + + it('change', function() { + var obj = { value: 10, str: 'foo' }, + fn = function() { obj.value += 5 }, + sameFn = function() { obj.value += 0 }, + decFn = function() { obj.value -= 3 }, + bangFn = function() { obj.str += '!' }; + + fn.should.change(obj, 'value'); + sameFn.should.not.change(obj, 'value'); + sameFn.should.not.change(obj, 'str'); + bangFn.should.change(obj, 'str'); + }); + + it('increase, decrease', function() { + var obj = { value: 10 }, + incFn = function() { obj.value += 2 }, + decFn = function() { obj.value -= 3 }, + smFn = function() { obj.value += 0 }; + + smFn.should.not.increase(obj, 'value'); + decFn.should.not.increase(obj, 'value'); + incFn.should.increase(obj, 'value'); + + smFn.should.not.decrease(obj, 'value'); + incFn.should.not.decrease(obj, 'value'); + decFn.should.decrease(obj, 'value'); + }); + + it('extensible', function() { + var nonExtensibleObject = Object.preventExtensions({}); + + ({}).should.be.extensible; + nonExtensibleObject.should.not.be.extensible; + + err(function() { + nonExtensibleObject.should.be.extensible; + }, 'expected {} to be extensible'); + + err(function() { + ({}).should.not.be.extensible; + }, 'expected {} to not be extensible'); + + // Making sure ES6-like Object.isExtensible response is respected for all primitive types + + (42).should.not.be.extensible; + 'foo'.should.not.be.extensible; + false.should.not.be.extensible; + + err(function() { + (42).should.be.extensible; + }, 'expected 42 to be extensible'); + + err(function() { + 'foo'.should.be.extensible; + }, 'expected \'foo\' to be extensible'); + + err(function() { + false.should.be.extensible; + }, 'expected false to be extensible'); + }); + + it('sealed', function() { + var sealedObject = Object.seal({}); + + sealedObject.should.be.sealed; + ({}).should.not.be.sealed; + + err(function() { + ({}).should.be.sealed; + }, 'expected {} to be sealed'); + + err(function() { + sealedObject.should.not.be.sealed; + }, 'expected {} to not be sealed'); + + // Making sure ES6-like Object.isSealed response is respected for all primitive types + + (42).should.be.sealed; + 'foo'.should.be.sealed; + false.should.be.sealed; + + err(function() { + (42).should.not.be.sealed; + }, 'expected 42 to not be sealed'); + + err(function() { + 'foo'.should.not.be.sealed; + }, 'expected \'foo\' to not be sealed'); + + err(function() { + false.should.not.be.sealed; + }, 'expected false to not be sealed'); + }); + + it('frozen', function() { + var frozenObject = Object.freeze({}); + + frozenObject.should.be.frozen; + ({}).should.not.be.frozen; + + err(function() { + ({}).should.be.frozen; + }, 'expected {} to be frozen'); + + err(function() { + frozenObject.should.not.be.frozen; + }, 'expected {} to not be frozen'); + + // Making sure ES6-like Object.isFrozen response is respected for all primitive types + + (42).should.be.frozen; + 'foo'.should.be.frozen; + false.should.be.frozen; + + err(function() { + (42).should.not.be.frozen; + }, 'expected 42 to not be frozen'); + + err(function() { + 'foo'.should.not.be.frozen; + }, 'expected \'foo\' to not be frozen'); + + err(function() { + false.should.not.be.frozen; + }, 'expected false to not be frozen'); + }); +}); diff --git a/source-test/utilities.ts b/source-test/utilities.ts new file mode 100644 index 0000000..597c308 --- /dev/null +++ b/source-test/utilities.ts @@ -0,0 +1,519 @@ +import err from './bootstrap/index'; +import chai = require('chai'); + +describe('utilities', function () { + var expect = chai.expect; + + after(function() { + // Some clean-up so we can run tests in a --watch + delete chai.Assertion.prototype.eqqqual; + delete chai.Assertion.prototype.result; + delete chai.Assertion.prototype.doesnotexist; + }); + + it('_obj', function () { + var foo = 'bar' + , test = expect(foo); + + expect(test).to.have.property('_obj', foo); + + var bar = 'baz'; + (test as any)._obj = bar; + + expect(test).to.have.property('_obj', bar); + test.equal(bar); + }); + + it('transferFlags', function () { + var foo = 'bar' + , test = expect(foo).not; + + chai.use(function (_chai, utils) { + var obj = {}; + utils.transferFlags(test, obj); + expect(utils.flag(obj, 'object')).to.equal(foo); + expect(utils.flag(obj, 'negate')).to.equal(true); + }); + }); + + it('transferFlags, includeAll = false', function () { + var foo = 'bar'; + + chai.use(function (_chai, utils) { + var obj = {}; + var test = function() {}; + + var assertion = new chai.Assertion({}, "message", test); + var flag = {}; + utils.flag(obj, 'flagMe', flag); + utils.flag(obj, 'negate', true); + utils.transferFlags(test, obj, false); + + expect(utils.flag(obj, 'object')).to.equal(undefined); + expect(utils.flag(obj, 'message')).to.equal(undefined); + expect(utils.flag(obj, 'ssfi')).to.equal(undefined); + expect(utils.flag(obj, 'negate')).to.equal(true); + expect(utils.flag(obj, 'flagMe')).to.equal(flag); + }); + }); + + + it('getPathValue', function () { + var object = { + hello: 'universe' + , universe: { + hello: 'world' + } + , world: [ 'hello', 'universe' ] + , complex: [ + { hello: 'universe' } + , { universe: 'world' } + , [ { hello: 'world' } ] + ] + } + + var arr = [ [ true ] ]; + + chai.use(function (_chai, utils) { + var gpv = utils.getPathValue; + expect(gpv('hello', object)).to.equal('universe'); + expect(gpv('universe.hello', object)).to.equal('world'); + expect(gpv('world[1]', object)).to.equal('universe'); + expect(gpv('complex[1].universe', object)).to.equal('world'); + expect(gpv('complex[2][0].hello', object)).to.equal('world'); + expect(gpv('[0][0]', arr)).to.be.true; + }); + }); + + describe('getPathInfo', function() { + var gpi, + obj = { + id: '10702S300W', + primes: [2, 3, 5, 7, 11], + dimensions: { + units: 'mm', + lengths: [[1.2, 3.5], [2.2, 1.5], [5, 7]] + }, + 'dimensions.lengths': { + '[2]': [1.2, 3.5] + } + }; + + beforeEach(function() { + chai.use(function (_chai, utils) { + gpi = utils.getPathInfo; + }); + }); + + it('should handle simple property', function() { + var info = gpi('dimensions.units', obj); + + info.parent.should.equal(obj.dimensions); + info.value.should.equal(obj.dimensions.units); + info.name.should.equal('units'); + info.exists.should.be.true; + }); + + it('should handle non-existent property', function() { + var info = gpi('dimensions.size', obj); + + info.parent.should.equal(obj.dimensions); + expect(info.value).to.be.undefined; + info.name.should.equal('size'); + info.exists.should.be.false; + }); + + it('should handle array index', function() { + var info = gpi('primes[2]', obj); + + info.parent.should.equal(obj.primes); + info.value.should.equal(obj.primes[2]); + info.name.should.equal(2); + info.exists.should.be.true; + }); + + it('should handle dimensional array', function() { + var info = gpi('dimensions.lengths[2][1]', obj); + + info.parent.should.equal(obj.dimensions.lengths[2]); + info.value.should.equal(obj.dimensions.lengths[2][1]); + info.name.should.equal(1); + info.exists.should.be.true; + }); + + it('should handle out of bounds array index', function() { + var info = gpi('dimensions.lengths[3]', obj); + + info.parent.should.equal(obj.dimensions.lengths); + expect(info.value).to.be.undefined; + info.name.should.equal(3); + info.exists.should.be.false; + }); + + it('should handle out of bounds dimensional array index', function() { + var info = gpi('dimensions.lengths[2][5]', obj); + + info.parent.should.equal(obj.dimensions.lengths[2]); + expect(info.value).to.be.undefined; + info.name.should.equal(5); + info.exists.should.be.false; + }); + + it('should handle backslash-escaping for .[]', function() { + var info = gpi('dimensions\\.lengths.\\[2\\][1]', obj); + + info.parent.should.equal(obj['dimensions.lengths']['[2]']); + info.value.should.equal(obj['dimensions.lengths']['[2]'][1]); + info.name.should.equal(1); + info.exists.should.be.true; + }); + }); + + describe('hasProperty', function() { + var hp; + beforeEach(function() { + chai.use(function (_chai, utils) { + hp = utils.hasProperty; + }); + }); + + it('should handle array index', function() { + var arr = [1, 2, 'cheeseburger']; + + hp(1, arr).should.be.true; + hp(3, arr).should.be.false; + }); + + it('should handle literal types', function() { + var s = 'string literal'; + hp('length', s).should.be.true; + hp(3, s).should.be.true; + hp(14, s).should.be.false; + + hp('foo', 1).should.be.false; + }); + + it('should handle undefined', function() { + var o = { + foo: 'bar' + }; + + hp('foo', o).should.be.true; + hp('baz', o).should.be.false; + hp(0, o).should.be.false; + }); + + it('should handle undefined', function() { + hp('foo', undefined).should.be.false; + }); + + it('should handle null', function() { + hp('foo', null).should.be.false; + }); + }); + + it('addMethod', function () { + chai.use(function(_chai, utils) { + expect(_chai.Assertion).to.not.respondTo('eqqqual'); + _chai.Assertion.addMethod('eqqqual', function (str) { + var object = utils.flag(this, 'object'); + new _chai.Assertion(object).to.be.eql(str); + }); + expect(_chai.Assertion).to.respondTo('eqqqual'); + }); + + // expect('spec').to.eqqqual('spec'); + }); + + it('addMethod returning result', function () { + chai.use(function(_chai, utils) { + _chai.Assertion.addMethod('result', function () { + return 'result'; + }) + }); + + // expect(expect('foo').result()).to.equal('result'); + }); + + it('overwriteMethod', function () { + chai.use(function (_chai, _) { + expect(_chai.Assertion).to.respondTo('eqqqual'); + _chai.Assertion.overwriteMethod('eqqqual', function (_super) { + return function (str) { + var object = _.flag(this, 'object'); + if (object == 'cucumber' && str == 'cuke') { + _.flag(this, 'cucumber', true); + } else { + _super.apply(this, arguments); + } + }; + }); + + }); + + // var vege = expect('cucumber').to.eqqqual('cucumber'); + // expect(vege.__flags).to.not.have.property('cucumber'); + // var cuke = expect('cucumber').to.eqqqual('cuke'); + // expect(cuke.__flags).to.have.property('cucumber'); + + chai.use(function (_chai, _) { + expect(_chai.Assertion).to.not.respondTo('doesnotexist'); + _chai.Assertion.overwriteMethod('doesnotexist', function (_super) { + expect(_super).to.be.a('function'); + return function () { + _.flag(this, 'doesnt', true); + _super.apply(this, arguments); + } + }); + }); + + // var dne = expect('something').to.doesnotexist(); + // expect(dne.__flags).to.have.property('doesnt'); + }); + + it('overwriteMethod returning result', function () { + chai.use(function (_chai, _) { + _chai.Assertion.overwriteMethod('result', function (_super) { + return function () { + return 'result'; + } + }); + }); + + // expect(expect('foo').result()).to.equal('result'); + }); + + it('addProperty', function () { + chai.use(function (_chai, _) { + _chai.Assertion.addProperty('tea', function () { + _.flag(this, 'tea', 'chai'); + }); + }); + + // var assert = expect('chai').to.be.tea; + // expect(assert.__flags.tea).to.equal('chai'); + }); + + it('addProperty returning result', function () { + chai.use(function(_chai, _) { + _chai.Assertion.addProperty('result', function () { + return 'result'; + }) + }); + + // expect(expect('foo').result).to.equal('result'); + }); + + it('overwriteProperty', function () { + chai.use(function (_chai, _) { + expect(new chai.Assertion()).to.have.property('tea'); + _chai.Assertion.overwriteProperty('tea', function (_super) { + return function () { + var act = _.flag(this, 'object'); + if (act === 'matcha') { + _.flag(this, 'tea', 'matcha'); + } else { + _super.call(this); + } + } + }); + }); + + // var matcha = expect('matcha').to.be.tea; + // expect(matcha.__flags.tea).to.equal('matcha'); + // var assert = expect('something').to.be.tea; + // expect(assert.__flags.tea).to.equal('chai'); + }); + + it('overwriteProperty returning result', function () { + chai.use(function(_chai, _) { + _chai.Assertion.overwriteProperty('result', function (_super) { + return function () { + return 'result'; + } + }); + }); + + // expect(expect('foo').result).to.equal('result'); + }); + + it('getMessage', function () { + chai.use(function (_chai, _) { + expect(_.getMessage({}, [])).to.equal(''); + expect(_.getMessage({}, [null, null, null])).to.equal(''); + + var obj = {}; + _.flag(obj, 'message', 'foo'); + expect(_.getMessage(obj, [])).to.contain('foo'); + }); + }); + + it('getMessage passed message as function', function () { + chai.use(function (_chai, _) { + var obj = {}; + var msg = function() { return "expected a to eql b"; } + var negateMsg = function() { return "expected a not to eql b"; } + expect(_.getMessage(obj, [null, msg, negateMsg])).to.equal("expected a to eql b"); + _.flag(obj, 'negate', true); + expect(_.getMessage(obj, [null, msg, negateMsg])).to.equal("expected a not to eql b"); + }); + }); + + it('getMessage template tag substitution', function () { + chai.use(function (_chai, _) { + var objName = 'trojan horse'; + var actualValue = 'an actual value'; + var expectedValue = 'an expected value'; + [ + // known template tags + { + template: 'one #{this} two', + expected: 'one \'' + objName + '\' two' + }, + { + template: 'one #{act} two', + expected: 'one \'' + actualValue + '\' two' + }, + { + template: 'one #{exp} two', + expected: 'one \'' + expectedValue + '\' two' + }, + // unknown template tag + { + template: 'one #{unknown} two', + expected: 'one #{unknown} two' + }, + // repeated template tag + { + template: '#{this}#{this}', + expected: '\'' + objName + '\'\'' + objName + '\'' + }, + // multiple template tags in different order + { + template: '#{this}#{act}#{exp}#{act}#{this}', + expected: '\'' + objName + '\'\'' + actualValue + '\'\'' + expectedValue + '\'\'' + actualValue + '\'\'' + objName + '\'' + }, + // immune to string.prototype.replace() `$` substitution + { + objName: '-$$-', + template: '#{this}', + expected: '\'-$$-\'' + }, + { + actualValue: '-$$-', + template: '#{act}', + expected: '\'-$$-\'' + }, + { + expectedValue: '-$$-', + template: '#{exp}', + expected: '\'-$$-\'' + } + ].forEach(function (config: any) { + config.objName = config.objName || objName; + config.actualValue = config.actualValue || actualValue; + config.expectedValue = config.expectedValue || expectedValue; + var obj = {_obj: config.actualValue}; + _.flag(obj, 'object', config.objName); + expect(_.getMessage(obj, [null, config.template, null, config.expectedValue])).to.equal(config.expected); + }); + }); + }); + + it('inspect with custom object-returning inspect()s', function () { + chai.use(function (_chai, _) { + var obj = { + outer: { + inspect: function () { + return { foo: 'bar' }; + } + } + }; + + expect(_.inspect(obj)).to.equal('{ outer: { foo: \'bar\' } }'); + }); + }); + + it('inspect negative zero', function () { + chai.use(function (_chai, _) { + expect(_.inspect(-0)).to.equal('-0'); + expect(_.inspect([-0])).to.equal('[ -0 ]'); + expect(_.inspect({ hp: -0 })).to.equal('{ hp: -0 }'); + }); + }); + + it('addChainableMethod', function () { + chai.use(function (_chai, _) { + _chai.Assertion.addChainableMethod('x', + function () { + new chai.Assertion(this._obj).to.be.equal('x'); + } + , function () { + this._obj = this._obj || {}; + this._obj.__x = 'X!' + } + ); + + // expect("foo").x.to.equal("foo"); + // expect("x").x(); + + // expect(function () { + // expect("foo").x(); + // }).to.throw(_chai.AssertionError); + + // Verify whether the original Function properties are present. + // see https://github.com/chaijs/chai/commit/514dd6ce4#commitcomment-2593383 + var propertyDescriptor = Object.getOwnPropertyDescriptor(chai.Assertion.prototype, "x"); + expect(propertyDescriptor.get).to.have.property("call", Function.prototype.call); + expect(propertyDescriptor.get).to.have.property("apply", Function.prototype.apply); + expect(propertyDescriptor.get()).to.have.property("call", Function.prototype.call); + expect(propertyDescriptor.get()).to.have.property("apply", Function.prototype.apply); + + // var obj = {}; + // expect(obj).x.to.be.ok; + // expect(obj).to.have.property('__x', 'X!'); + }) + }); + + it('overwriteChainableMethod', function () { + chai.use(function (_chai, _) { + _chai.Assertion.overwriteChainableMethod('x', + function(_super) { + return function() { + if (_.flag(this, 'marked')) { + new chai.Assertion(this._obj).to.be.equal('spot'); + } else { + _super.apply(this, arguments); + } + }; + } + , function(_super) { + return function() { + _.flag(this, 'message', 'x marks the spot'); + _super.apply(this, arguments); + }; + } + ); + + // Make sure the original behavior of 'x' remains the same + // expect('foo').x.to.equal("foo"); + // expect("x").x(); + // expect(function () { + // expect("foo").x(); + // }).to.throw(_chai.AssertionError); + // var obj = {}; + // expect(obj).x.to.be.ok; + // expect(obj).to.have.property('__x', 'X!'); + + // // Test the new behavior of 'x' + // var assertion = expect('foo').x.to.be.ok; + // expect(_.flag(assertion, 'message')).to.equal('x marks the spot'); + // expect(function () { + // var assertion = expect('x'); + // _.flag(assertion, 'marked', true); + // assertion.x() + // }).to.throw(_chai.AssertionError); + }); + }); + +});