diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index ce1b3bf2d..1bfce5957 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1618,4 +1618,85 @@ module.exports = function (chai, _) { Assertion.addChainableMethod('decrease', assertDecreases); Assertion.addChainableMethod('decreases', assertDecreases); + /** + * ### .extensible + * + * Asserts that the target is extensible (can have new properties added to + * it). + * + * var nonExtensibleObject = Object.preventExtensions({}); + * var sealedObject = Object.seal({}); + * var frozenObject = Object.freeze({}); + * + * expect({}).to.be.extensible; + * expect(nonExtensibleObject).to.not.be.extensible; + * expect(sealedObject).to.not.be.extensible; + * expect(frozenObject).to.not.be.extensible; + * + * @name extensible + * @api public + */ + + Assertion.addProperty('extensible', function() { + var obj = flag(this, 'object'); + + this.assert( + Object.isExtensible(obj) + , 'expected #{this} to be extensible' + , 'expected #{this} to not be extensible' + ); + }); + + /** + * ### .sealed + * + * Asserts that the target is sealed (cannot have new properties added to it + * and its existing properties cannot be removed). + * + * var sealedObject = Object.seal({}); + * var frozenObject = Object.freeze({}); + * + * expect(sealedObject).to.be.sealed; + * expect(frozenObject).to.be.sealed; + * expect({}).to.not.be.sealed; + * + * @name sealed + * @api public + */ + + Assertion.addProperty('sealed', function() { + var obj = flag(this, 'object'); + + this.assert( + Object.isSealed(obj) + , 'expected #{this} to be sealed' + , 'expected #{this} to not be sealed' + ); + }); + + /** + * ### .frozen + * + * Asserts that the target is frozen (cannot have new properties added to it + * and its existing properties cannot be modified). + * + * var frozenObject = Object.freeze({}); + * + * expect(frozenObject).to.be.frozen; + * expect({}).to.not.be.frozen; + * + * @name frozen + * @api public + */ + + Assertion.addProperty('frozen', function() { + var obj = flag(this, 'object'); + + this.assert( + Object.isSealed(obj) + , 'expected #{this} to be frozen' + , 'expected #{this} to not be frozen' + ); + }); + }; diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index 30b1bb41f..47d7b31d8 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -1263,6 +1263,121 @@ module.exports = function (chai, util) { } }; + /** + * ### .extensible(object) + * + * Asserts that `object` is extensible (can have new properties added to it). + * + * assert.extensible({}); + * + * @name extensible + * @param {Object} object + * @param {String} message _optional_ + * @api public + */ + + assert.extensible = function (obj, msg) { + new Assertion(obj, msg).to.be.extensible; + }; + + /** + * ### .notExtensible(object) + * + * Asserts that `object` is _not_ extensible. + * + * var nonExtensibleObject = Object.preventExtensions({}); + * var sealedObject = Object.seal({}); + * var frozenObject = Object.freese({}); + * + * assert.notExtensible(nonExtensibleObject); + * assert.notExtensible(sealedObject); + * assert.notExtensible(frozenObject); + * + * @name notExtensible + * @param {Object} object + * @param {String} message _optional_ + * @api public + */ + + assert.notExtensible = function (obj, msg) { + new Assertion(obj, msg).to.not.be.extensible; + }; + + /** + * ### .sealed(object) + * + * Asserts that `object` is sealed (cannot have new properties added to it + * and its existing properties cannot be removed). + * + * var sealedObject = Object.seal({}); + * var frozenObject = Object.seal({}); + * + * assert.sealed(sealedObject); + * assert.sealed(frozenObject); + * + * @name sealed + * @param {Object} object + * @param {String} message _optional_ + * @api public + */ + + assert.sealed = function (obj, msg) { + new Assertion(obj, msg).to.be.sealed; + }; + + /** + * ### .notSealed(object) + * + * Asserts that `object` is _not_ sealed. + * + * assert.notSealed({}); + * + * @name notSealed + * @param {Object} object + * @param {String} message _optional_ + * @api public + */ + + assert.notSealed = function (obj, msg) { + new Assertion(obj, msg).to.not.be.sealed; + }; + + /** + * ### .frozen(object) + * + * Asserts that `object` is frozen (cannot have new properties added to it + * and its existing properties cannot be modified). + * + * var frozenObject = Object.freeze({}); + * assert.frozen(frozenObject); + * + * @name frozen + * @param {Object} object + * @param {String} message _optional_ + * @api public + */ + + assert.frozen = function (obj, msg) { + new Assertion(obj, msg).to.be.frozen; + }; + + /** + * ### .notFrozen(object) + * + * Asserts that `object` is _not_ frozen. + * + * assert.notFrozen({}); + * + * @name notSealed + * @param {Object} object + * @param {String} message _optional_ + * @api public + */ + + assert.notFrozen = function (obj, msg) { + new Assertion(obj, msg).to.not.be.frozen; + }; + /*! * Aliases. */ diff --git a/test/assert.js b/test/assert.js index 66c239627..4f8abcdca 100644 --- a/test/assert.js +++ b/test/assert.js @@ -738,4 +738,64 @@ describe('assert', function () { assert.doesNotIncrease(smFn, obj, 'value'); }); + it('extensible', function() { + var nonExtensibleObject = Object.preventExtensions({}); + + assert.extensible({}); + + err(function() { + assert.extensible(nonExtensibleObject); + }, 'expected {} to be extensible'); + }); + + it('notExtensible', function() { + var nonExtensibleObject = Object.preventExtensions({}); + + assert.notExtensible(nonExtensibleObject); + + err(function() { + assert.notExtensible({}); + }, 'expected {} to not be extensible'); + }); + + it('sealed', function() { + var sealedObject = Object.seal({}); + + assert.sealed(sealedObject); + + err(function() { + assert.sealed({}); + }, 'expected {} to be sealed'); + }); + + it('notSealed', function() { + var sealedObject = Object.seal({}); + + assert.notSealed({}); + + err(function() { + assert.notSealed(sealedObject); + }, 'expected {} to not be sealed'); + }); + + it('frozen', function() { + var frozenObject = Object.freeze({}); + + assert.frozen(frozenObject); + + err(function() { + assert.frozen({}); + }, 'expected {} to be frozen'); + }); + + it('notFrozen', function() { + var frozenObject = Object.freeze({}); + + assert.notFrozen({}); + + err(function() { + assert.notFrozen(frozenObject); + }, 'expected {} to not be frozen'); + }); + }); diff --git a/test/expect.js b/test/expect.js index 4507d4c75..4d8f90469 100644 --- a/test/expect.js +++ b/test/expect.js @@ -1060,5 +1060,49 @@ describe('expect', function () { 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'); + }); + + 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'); + }); + + 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'); + }); }); diff --git a/test/should.js b/test/should.js index 58300d4bd..085fe0a7a 100644 --- a/test/should.js +++ b/test/should.js @@ -923,4 +923,49 @@ describe('should', function() { 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'); + }); + + 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'); + }); + + 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'); + }); });