From b6e1b573e07d108b4ecd6c9da733f20f010986a8 Mon Sep 17 00:00:00 2001 From: Kaito Udagawa Date: Tue, 17 Mar 2015 19:39:14 +0900 Subject: [PATCH 1/4] Escaping dot should be taken in deep property * removing backslash prior to "." * test case: "foo\\.bar" names {"foo.bar": "baz"} --- lib/chai/utils/getPathInfo.js | 2 +- test/expect.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/chai/utils/getPathInfo.js b/lib/chai/utils/getPathInfo.js index 67aa66198..8d30dfb4d 100644 --- a/lib/chai/utils/getPathInfo.js +++ b/lib/chai/utils/getPathInfo.js @@ -67,7 +67,7 @@ function parsePath (path) { var re = /\[(\d+)\]$/ , mArr = re.exec(value); if (mArr) return { i: parseFloat(mArr[1]) }; - else return { p: value }; + else return { p: value.replace(/\\\./g, '.') }; }); } diff --git a/test/expect.js b/test/expect.js index cd204a9b0..a9b76211e 100644 --- a/test/expect.js +++ b/test/expect.js @@ -429,6 +429,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'); From 704670a23b3ab92f415bb0e82f4fa84abf7e47b6 Mon Sep 17 00:00:00 2001 From: Kaito Udagawa Date: Wed, 18 Mar 2015 21:57:03 +0900 Subject: [PATCH 2/4] Feature: backslash-escaping in `.deep.property` This commit includes: * getPathInfo handles escaping `.[]` * Documentation added to `.property` * Documentation added to `.getPathInfo` * Test cases for `.property` * Test cases for `.deep.property` * Test cases for `.getPathInfo` Note that the input of `.getPathInfo` assumed to match the following: /^(?:(?:\\[.\[\]]|[^.\[\]])+|\[\d+\])(?:\.(?:\\[.\[\]]|[^.\[\]])+|\[\d+\])$/ --- lib/chai/core/assertions.js | 12 ++++++++++++ lib/chai/utils/getPathInfo.js | 18 ++++++++++-------- test/expect.js | 7 +++++-- test/utilities.js | 12 ++++++++++++ 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index fcca6bf2f..4617123ba 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -793,6 +793,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 8d30dfb4d..17c35daf8 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,14 +62,15 @@ module.exports = function getPathInfo(path, obj) { */ function parsePath (path) { - var str = path.replace(/\[/g, '.[') - , parts = str.match(/(\\\.|[^.]+?)+/g); - return parts.map(function (value) { - var re = /\[(\d+)\]$/ - , mArr = re.exec(value); - if (mArr) return { i: parseFloat(mArr[1]) }; - else return { p: value.replace(/\\\./g, '.') }; - }); + /* assumed /^(?:(?:\\[.\[\]]|[^.\[\]])+|\[\d+\])(?:\.(?:\\[.\[\]]|[^.\[\]])+|\[\d+\])$/ */ + var re = /(?:\\[.\[\]]|[^.\[\]])+|\[(\d+)\]/g + , parsed = [] + , value; + while ((value = re.exec(path)) !== null) { + if (value[1]) parsed.push({ i: parseFloat(value[1]) }); + else parsed.push({ p: value[0].replace(/\\([.\[\]])/g, '$1') }); + } + return parsed; } diff --git a/test/expect.js b/test/expect.js index a9b76211e..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,8 +432,8 @@ describe('expect', function () { expect({ 'foo': [1, 2, 3] }) .to.have.deep.property('foo[1]'); - expect({ 'foo.bar': 'baz'}) - .to.have.deep.property('foo\\.bar'); + expect({ 'foo.bar[]': 'baz'}) + .to.have.deep.property('foo\\.bar\\[\\]'); err(function(){ expect({ 'foo.bar': 'baz' }) 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() { From 8d370cf4f37af5b2fc38ce5987899d41bc0d37f7 Mon Sep 17 00:00:00 2001 From: Kaito Udagawa Date: Thu, 19 Mar 2015 20:39:55 +0900 Subject: [PATCH 3/4] take regular expression apart --- lib/chai/utils/getPathInfo.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/chai/utils/getPathInfo.js b/lib/chai/utils/getPathInfo.js index 17c35daf8..3bb19d224 100644 --- a/lib/chai/utils/getPathInfo.js +++ b/lib/chai/utils/getPathInfo.js @@ -62,15 +62,14 @@ module.exports = function getPathInfo(path, obj) { */ function parsePath (path) { - /* assumed /^(?:(?:\\[.\[\]]|[^.\[\]])+|\[\d+\])(?:\.(?:\\[.\[\]]|[^.\[\]])+|\[\d+\])$/ */ - var re = /(?:\\[.\[\]]|[^.\[\]])+|\[(\d+)\]/g - , parsed = [] - , value; - while ((value = re.exec(path)) !== null) { - if (value[1]) parsed.push({ i: parseFloat(value[1]) }); - else parsed.push({ p: value[0].replace(/\\([.\[\]])/g, '$1') }); - } - return parsed; + var str = path.replace(/([^\\])\[/g, '$1.[') + , parts = str.match(/(\\\.|[^.]+?)+/g); + return parts.map(function (value) { + var re = /^\[(\d+)\]$/ + , mArr = re.exec(value); + if (mArr) return { i: parseFloat(mArr[1]) }; + else return { p: value.replace(/\\([.\[\]])/g, '$1') }; + }); } From 1aae1b05dba85f048a6b1f7af484b9fe47800261 Mon Sep 17 00:00:00 2001 From: Kaito Udagawa Date: Fri, 20 Mar 2015 16:15:13 +0900 Subject: [PATCH 4/4] Documentation of escaping in `.deep` flag. --- lib/chai/core/assertions.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 4617123ba..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 */