diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index fcca6bf2f..90553553d 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -75,6 +75,10 @@ module.exports = function (chai, _) { * expect({ foo: { bar: { baz: 'quux' } } }) * .to.have.deep.property('foo.bar.baz', 'quux'); * + * In the `property` assertion, setting `deep` flag may require + * to escape dot and brackets, while that is a rare case. + * See also the documentation of `.deep.property`. + * * @name deep * @api public */ @@ -793,6 +797,18 @@ module.exports = function (chai, _) { * .with.deep.property('[2]') * .that.deep.equals({ tea: 'konacha' }); * + * Note that dots and bracket in `name` must be backslash-escaped when + * the `deep` flag is set, while they must NOT be escaped when the `deep` + * flag is not set. + * + * // simple referencing + * var css = { '.link[target]': 42 }; + * expect(css).to.have.property('.link[target]', 42); + * + * // deep referencing + * var deepCss = { '.link': { '[target]': 42 }}; + * expect(deepCss).to.have.deep.property('\\.link.\\[target\\]', 42); + * * @name property * @alias deep.property * @param {String} name diff --git a/lib/chai/utils/getPathInfo.js b/lib/chai/utils/getPathInfo.js index 67aa66198..3bb19d224 100644 --- a/lib/chai/utils/getPathInfo.js +++ b/lib/chai/utils/getPathInfo.js @@ -54,6 +54,7 @@ module.exports = function getPathInfo(path, obj) { * * * Can be as near infinitely deep and nested * * Arrays are also valid using the formal `myobject.document[3].property`. + * * Literal dots and brackets (not delimiter) must be backslash-escaped. * * @param {String} path * @returns {Object} parsed @@ -61,13 +62,13 @@ module.exports = function getPathInfo(path, obj) { */ function parsePath (path) { - var str = path.replace(/\[/g, '.[') + var str = path.replace(/([^\\])\[/g, '$1.[') , parts = str.match(/(\\\.|[^.]+?)+/g); return parts.map(function (value) { - var re = /\[(\d+)\]$/ + var re = /^\[(\d+)\]$/ , mArr = re.exec(value); if (mArr) return { i: parseFloat(mArr[1]) }; - else return { p: value }; + else return { p: value.replace(/\\([.\[\]])/g, '$1') }; }); } diff --git a/test/expect.js b/test/expect.js index cd204a9b0..0d11c67e6 100644 --- a/test/expect.js +++ b/test/expect.js @@ -411,6 +411,9 @@ describe('expect', 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'"); @@ -429,6 +432,9 @@ describe('expect', function () { 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'); diff --git a/test/utilities.js b/test/utilities.js index 2ea0cb684..9c6b8e69b 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -90,6 +90,9 @@ describe('utilities', function () { dimensions: { units: 'mm', lengths: [[1.2, 3.5], [2.2, 1.5], [5, 7]] + }, + 'dimensions.lengths': { + '[2]': [1.2, 3.5] } }; @@ -152,6 +155,15 @@ describe('utilities', function () { 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() {