diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 2daf10bac..7d706e077 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -988,9 +988,28 @@ module.exports = function (chai, _) { function assertInstanceOf (constructor, msg) { if (msg) flag(this, 'message', msg); + + var target = flag(this, 'object') + var validInstanceOfTarget = constructor === Object(constructor) && ( + typeof constructor === 'function' || + (typeof Symbol !== 'undefined' && typeof constructor[Symbol.hasInstance] === 'function') + ); + + if (!validInstanceOfTarget) { + var constructorType = constructor === null ? 'null' : typeof constructor; + throw new Error('The instanceof assertion needs a constructor but ' + constructorType + ' was given.'); + } + + var isInstanceOf; + try { + isInstanceOf = target instanceof constructor + } catch (err) { + throw new Error(target + ' instanceof ' + constructor + ' failed: ' + err.message + '.') + } + var name = _.getName(constructor); this.assert( - flag(this, 'object') instanceof constructor + isInstanceOf , 'expected #{this} to be an instance of ' + name , 'expected #{this} to not be an instance of ' + name ); diff --git a/test/assert.js b/test/assert.js index d8d3db01a..9ee459911 100644 --- a/test/assert.js +++ b/test/assert.js @@ -141,6 +141,50 @@ describe('assert', function () { function Foo(){} assert.instanceOf(new Foo(), Foo); + err(function(){ + assert.instanceOf(new Foo(), 1); + }, "The instanceof assertion needs a constructor but number was given."); + + err(function(){ + assert.instanceOf(new Foo(), 'batman'); + }, "The instanceof assertion needs a constructor but string was given."); + + err(function(){ + assert.instanceOf(new Foo(), {}); + }, "The instanceof assertion needs a constructor but object was given."); + + err(function(){ + assert.instanceOf(new Foo(), true); + }, "The instanceof assertion needs a constructor but boolean was given."); + + err(function(){ + assert.instanceOf(new Foo(), null); + }, "The instanceof assertion needs a constructor but null was given."); + + err(function(){ + assert.instanceOf(new Foo(), undefined); + }, "The instanceof assertion needs a constructor but undefined was given."); + + var expectedError; + try { + t instanceof Thing; + } catch (err) { + errMsg = '[object Object] instanceof function Thing(){} failed: ' + err.message + '.'; + } + + err(function(){ + function Thing(){}; + var t = new Thing(); + Thing.prototype = 1337; + assert.instanceOf(t, Thing); + }, expectedError); + + if (typeof Symbol !== 'undefined') { + err(function(){ + assert.instanceOf(new Foo(), Symbol()); + }, "The instanceof assertion needs a constructor but symbol was given."); + } + err(function () { assert.instanceOf(5, Foo); }, "expected 5 to be an instance of Foo"); @@ -156,6 +200,36 @@ describe('assert', function () { function Foo(){} assert.notInstanceOf(new Foo(), String); + err(function(){ + assert.notInstanceOf(new Foo(), 1); + }, "The instanceof assertion needs a constructor but number was given."); + + err(function(){ + assert.notInstanceOf(new Foo(), 'batman'); + }, "The instanceof assertion needs a constructor but string was given."); + + err(function(){ + assert.notInstanceOf(new Foo(), {}); + }, "The instanceof assertion needs a constructor but object was given."); + + err(function(){ + assert.notInstanceOf(new Foo(), true); + }, "The instanceof assertion needs a constructor but boolean was given."); + + err(function(){ + assert.notInstanceOf(new Foo(), null); + }, "The instanceof assertion needs a constructor but null was given."); + + err(function(){ + assert.notInstanceOf(new Foo(), undefined); + }, "The instanceof assertion needs a constructor but undefined was given."); + + if (typeof Symbol !== 'undefined') { + err(function(){ + assert.notInstanceOf(new Foo(), Symbol()); + }, "The instanceof assertion needs a constructor but symbol was given."); + } + err(function () { assert.notInstanceOf(new Foo(), Foo); }, "expected {} to not be an instance of Foo"); diff --git a/test/expect.js b/test/expect.js index 1fecf02b7..2cc129bc4 100644 --- a/test/expect.js +++ b/test/expect.js @@ -366,6 +366,51 @@ describe('expect', function () { function Foo(){} expect(new Foo()).to.be.an.instanceof(Foo); + err(function(){ + expect(new Foo()).to.an.instanceof(1); + }, "The instanceof assertion needs a constructor but number was given."); + + err(function(){ + expect(new Foo()).to.an.instanceof('batman'); + }, "The instanceof assertion needs a constructor but string was given."); + + err(function(){ + expect(new Foo()).to.an.instanceof({}); + }, "The instanceof assertion needs a constructor but object was given."); + + err(function(){ + expect(new Foo()).to.an.instanceof(true); + }, "The instanceof assertion needs a constructor but boolean was given."); + + err(function(){ + expect(new Foo()).to.an.instanceof(null); + }, "The instanceof assertion needs a constructor but null was given."); + + err(function(){ + expect(new Foo()).to.an.instanceof(undefined); + }, "The instanceof assertion needs a constructor but undefined was given."); + + // Different browsers may have different error messages + var expectedError; + try { + t instanceof Thing; + } catch (err) { + errMsg = '[object Object] instanceof function Thing(){} failed: ' + err.message + '.'; + } + + err(function(){ + function Thing(){}; + var t = new Thing(); + Thing.prototype = 1337; + expect(t).to.an.instanceof(Thing); + }, expectedError) + + if (typeof Symbol !== 'undefined') { + err(function(){ + expect(new Foo()).to.an.instanceof(Symbol()); + }, "The instanceof assertion needs a constructor but symbol was given."); + } + err(function(){ expect(3).to.an.instanceof(Foo, 'blah'); }, "blah: expected 3 to be an instance of Foo"); diff --git a/test/should.js b/test/should.js index 612f74e90..d9751fbd4 100644 --- a/test/should.js +++ b/test/should.js @@ -416,6 +416,51 @@ describe('should', function() { function Foo(){} new Foo().should.be.an.instanceof(Foo); + err(function(){ + new Foo().should.be.an.instanceof(1); + }, "The instanceof assertion needs a constructor but number was given."); + + err(function(){ + new Foo().should.be.an.instanceof('batman'); + }, "The instanceof assertion needs a constructor but string was given."); + + err(function(){ + new Foo().should.be.an.instanceof({}); + }, "The instanceof assertion needs a constructor but object was given."); + + err(function(){ + new Foo().should.be.an.instanceof(true); + }, "The instanceof assertion needs a constructor but boolean was given."); + + err(function(){ + new Foo().should.be.an.instanceof(null); + }, "The instanceof assertion needs a constructor but null was given."); + + err(function(){ + new Foo().should.be.an.instanceof(undefined); + }, "The instanceof assertion needs a constructor but undefined was given."); + + // Different browsers may have different error messages + var expectedError; + try { + t instanceof Thing; + } catch (err) { + errMsg = '[object Object] instanceof function Thing(){} failed: ' + err.message + '.'; + } + + err(function(){ + function Thing(){}; + var t = new Thing(); + Thing.prototype = 1337; + t.should.be.an.instanceof(Thing); + }, expectedError); + + if (typeof Symbol !== 'undefined') { + err(function(){ + new Foo().should.be.an.instanceof(Symbol()); + }, "The instanceof assertion needs a constructor but symbol was given."); + } + err(function(){ (3).should.an.instanceof(Foo, 'blah'); }, "blah: expected 3 to be an instance of Foo");