Skip to content

Commit

Permalink
Merge pull request #62 from astorije/astorije/property-improvements
Browse files Browse the repository at this point in the history
Improvements for property assertion
  • Loading branch information
astorije committed May 30, 2016
2 parents 1c6dc91 + 4465b1e commit bb49b6d
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 91 deletions.
85 changes: 85 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,91 @@ expect(new Map({ foo: 1, bar: 2 })).to.have.all.keys('foo', 'bar');
expect(new Map({ foo: 1, bar: 2 })).to.contain.key('foo');
```

### .property(name, [value])

- **@param** *{ String | Array | Iterable }* name
- **@param** *{ Mixed }* value (optional)

Asserts that the target has a property `name`, optionally asserting that
the value of that property is equal to `value`. `value` can be an
Immutable object.
If the `deep` flag is set, you can use dot- and bracket-notation for deep
references into objects and arrays.

```js
// Simple referencing
var map = new Map({ foo: 'bar' });
expect(map).to.have.property('foo');
expect(map).to.have.property('foo', 'bar');

// Deep referencing
var deepMap = new Map({
green: new Map({ tea: 'matcha' }),
teas: new List(['chai', 'matcha', new Map({ tea: 'konacha' })])
});

expect(deepMap).to.have.deep.property('green.tea', 'matcha');
expect(deepMap).to.have.deep.property(['green', 'tea'], 'matcha');
expect(deepMap).to.have.deep.property(new List(['green', 'tea']), 'matcha');
expect(deepMap).to.have.deep.property('teas[1]', 'matcha');
expect(deepMap).to.have.deep.property(['teas', 1], 'matcha');
expect(deepMap).to.have.deep.property(new List(['teas', 1]), 'matcha');
expect(deepMap).to.have.deep.property('teas[2].tea', 'konacha');
expect(deepMap).to.have.deep.property(['teas', 2, 'tea'], 'konacha');
expect(deepMap).to.have.deep.property(new List(['teas', 2, 'tea']), 'konacha');
```

You can also use a `List` as the starting point of a `deep.property`
assertion, or traverse nested `List`s.

```js
var list = new List([
new List(['chai', 'matcha', 'konacha']),
new List([
new Map({ tea: 'chai' }),
new Map({ tea: 'matcha' }),
new Map({ tea: 'konacha' })
])
]);

expect(list).to.have.deep.property('[0][1]', 'matcha');
expect(list).to.have.deep.property([0, 1], 'matcha');
expect(list).to.have.deep.property(new List([0, 1]), 'matcha');
expect(list).to.have.deep.property('[1][2].tea', 'konacha');
expect(list).to.have.deep.property([1, 2, 'tea'], 'konacha');
expect(list).to.have.deep.property(new List([1, 2, 'tea']), 'konacha');
```

Furthermore, `property` changes the subject of the assertion
to be the value of that property from the original object. This
permits for further chainable assertions on that property.

```js
expect(map).to.have.property('foo')
.that.is.a('string');
expect(deepMap).to.have.property('green')
.that.is.an.instanceof(Map)
.that.equals(new Map({ tea: 'matcha' }));
expect(deepMap).to.have.property('teas')
.that.is.an.instanceof(List)
.with.deep.property([2])
.that.equals(new Map({ tea: 'konacha' }));
```

Note that dots and brackets 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.

```js
// Simple referencing
var css = new Map({ '.link[target]': 42 });
expect(css).to.have.property('.link[target]', 42);

// Deep referencing
var deepCss = new Map({ '.link': new Map({ '[target]': 42 }) });
expect(deepCss).to.have.deep.property('\\.link.\\[target\\]', 42);
```

### .size(value)

- **@param** *{ Number }* size
Expand Down
91 changes: 76 additions & 15 deletions chai-immutable.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,30 +326,91 @@
}

/**
* ### .property(path, value)
* ### .property(name, [value])
*
* Asserts that property specified exists, and if a value specified, is equal
* to the given value.
* Asserts that the target has a property `name`, optionally asserting that
* the value of that property is equal to `value`. `value` can be an
* Immutable object.
* If the `deep` flag is set, you can use dot- and bracket-notation for deep
* references into objects and arrays.
*
* ```js
* var obj = Immutable.fromJS({
* left: 1,
* right: {
* left: 2
* right: 3
* }
* // Simple referencing
* var map = new Map({ foo: 'bar' });
* expect(map).to.have.property('foo');
* expect(map).to.have.property('foo', 'bar');
*
* // Deep referencing
* var deepMap = new Map({
* green: new Map({ tea: 'matcha' }),
* teas: new List(['chai', 'matcha', new Map({ tea: 'konacha' })])
* });
*
* assert.property(obj, 'left');
* assert.propertyVal(obj, 'left', 1);
* expect(deepMap).to.have.deep.property('green.tea', 'matcha');
* expect(deepMap).to.have.deep.property(['green', 'tea'], 'matcha');
* expect(deepMap).to.have.deep.property(new List(['green', 'tea']), 'matcha');
* expect(deepMap).to.have.deep.property('teas[1]', 'matcha');
* expect(deepMap).to.have.deep.property(['teas', 1], 'matcha');
* expect(deepMap).to.have.deep.property(new List(['teas', 1]), 'matcha');
* expect(deepMap).to.have.deep.property('teas[2].tea', 'konacha');
* expect(deepMap).to.have.deep.property(['teas', 2, 'tea'], 'konacha');
* expect(deepMap).to.have.deep.property(new List(['teas', 2, 'tea']), 'konacha');
* ```
*
* You can also use a `List` as the starting point of a `deep.property`
* assertion, or traverse nested `List`s.
*
* ```js
* var list = new List([
* new List(['chai', 'matcha', 'konacha']),
* new List([
* new Map({ tea: 'chai' }),
* new Map({ tea: 'matcha' }),
* new Map({ tea: 'konacha' })
* ])
* ]);
*
* expect(list).to.have.deep.property('[0][1]', 'matcha');
* expect(list).to.have.deep.property([0, 1], 'matcha');
* expect(list).to.have.deep.property(new List([0, 1]), 'matcha');
* expect(list).to.have.deep.property('[1][2].tea', 'konacha');
* expect(list).to.have.deep.property([1, 2, 'tea'], 'konacha');
* expect(list).to.have.deep.property(new List([1, 2, 'tea']), 'konacha');
* ```
*
* Furthermore, `property` changes the subject of the assertion
* to be the value of that property from the original object. This
* permits for further chainable assertions on that property.
*
* ```js
* expect(map).to.have.property('foo')
* .that.is.a('string');
* expect(deepMap).to.have.property('green')
* .that.is.an.instanceof(Map)
* .that.equals(new Map({ tea: 'matcha' }));
* expect(deepMap).to.have.property('teas')
* .that.is.an.instanceof(List)
* .with.deep.property([2])
* .that.equals(new Map({ tea: 'konacha' }));
* ```
*
* Note that dots and brackets 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.
*
* ```js
* // Simple referencing
* var css = new Map({ '.link[target]': 42 });
* expect(css).to.have.property('.link[target]', 42);
*
* assert.deepProperty(obj, ['right', 'right']);
* assert.deepPropertyVal(obj, ['right', 'right'], 3);
* // Deep referencing
* var deepCss = new Map({ '.link': new Map({ '[target]': 42 }) });
* expect(deepCss).to.have.deep.property('\\.link.\\[target\\]', 42);
* ```
*
* @name property
* @param {path} either the single propery name or array used for `getIn()`
* @param {value} expected value found at that location
* @param {String|Array|Iterable} name
* @param {Mixed} value (optional)
* @namespace BDD
* @api public
*/
Expand Down
149 changes: 73 additions & 76 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,121 +405,118 @@ describe('chai-immutable (' + typeEnv + ')', function () {
});

describe('property property', function () {
it('falls back to normal property if non-immutable', function () {
it('should not affect the original assertion', function () {
expect({ x: 1 }).to.have.property('x', 1);
});

it('falls back to normal deep property if non-immutable', function () {
expect({ x: 1, y: { x: 2, y: 3 } }).to.have.deep.property('y.x', 2);
});

it('should fail for missing property', function () {
it('should fail given an inexisting property', function () {
var obj = Immutable.fromJS({ x: 1 });
fail(function () { expect(obj).to.have.property('z'); });
});

it('should fail for missing deep property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
fail(function () { expect(obj).to.have.deep.property(['y', 'z']); });
});

it('not - should succeed for missing property', function () {
it('should pass using `not` given an inexisting property', function () {
var obj = Immutable.fromJS({ x: 1 });
expect(obj).not.to.have.property('z');
});

it('not - should succeed for missing deep property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
expect(obj).not.to.have.deep.property(['y', 'z']);
});

it('should succeed for existing property', function () {
it('should pass given an existing property', function () {
var obj = Immutable.fromJS({ x: 1 });
expect(obj).to.have.property('x');
});

it('should succeed for existing deep property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
expect(obj).to.have.deep.property(['y', 'x']);
});

it('should succeed for index', function () {
var obj = Immutable.fromJS({
items: ['a', 'b', 'c'],
});
expect(obj).to.have.deep.property(['items', 2], 'c');
});

it('not - should fail for existing property', function () {
it('should fail using `not` given an existing property', function () {
var obj = Immutable.fromJS({ x: 1 });
fail(function () { expect(obj).not.to.have.property('x'); });
});

it('not - should fail for existing deep property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
fail(function () { expect(obj).not.to.have.deep.property(['y', 'x']); });
});

it('should fail for unequal property', function () {
it('should fail given a property with a bad value', function () {
var obj = Immutable.fromJS({ x: 1 });
fail(function () { expect(obj).to.have.property('x', 'different'); });
});

it('should fail for unequal deep property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
fail(function () { expect(obj).to.have.property(['y', 'x'], 'different'); });
});

it('should succeed for equal property', function () {
it('should pass given a property with the good value', function () {
var obj = Immutable.fromJS({ x: 1 });
expect(obj).to.have.property('x', 1);
});

it('should succeed for equal deep property', function () {
it('should pass given an immutable value', function () {
var obj = Immutable.fromJS({ foo: { bar: 42 } });
expect(obj).to.have.property('foo', new Map({ bar: 42 }));
});

it('should change the subject to the value of that property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
expect(obj).to.have.deep.property(['y', 'x'], 2);
var sub = obj.get('y');
expect(obj).to.have.property('y').that.equal(sub);
});

it('not - should fail for missing deep property', function () {
var obj = Immutable.fromJS({ x: 1 });
fail(function () {
expect(obj).not.to.have.deep.property(['y', 'x'], 'different', 'message');
describe('using the `deep` flag', function () {
it('should not affect the original assertion', function () {
expect({ x: 1, y: { x: 2, y: 3 } }).to.have.deep.property('y.x', 2);
});
});

it('not - should fail for deep property, no message', function () {
var obj = Immutable.fromJS({ x: 1 });
fail(function () {
expect(obj).not.to.have.deep.property(['y', 'x'], 'different');
it('should fail given an inexisting property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
fail(function () { expect(obj).to.have.deep.property(['y', 'z']); });
});
});

it('not - should fail for equal deep property value', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2 } });
fail(function () {
expect(obj).not.to.have.deep.property(['y', 'x'], 2, 'message');
it('should pass using `not` given an inexisting property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
expect(obj).not.to.have.deep.property(['y', 'z']);
});
});

it('not - should succeed for unequal deep property value', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2 } });
expect(obj).not.to.have.deep.property(['y', 'x'], 'different');
});
it('should pass given an existing property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
expect(obj).to.have.deep.property(['y', 'x']);
});

it('should pass given an immutable value', function () {
var obj = Immutable.fromJS({ foo: { bar: 42 } });
expect(obj).to.have.property('foo', new Map({ bar: 42 }));
});
it('should pass given an index', function () {
var obj = Immutable.fromJS({
items: ['a', 'b', 'c'],
});
expect(obj).to.have.deep.property(['items', 2], 'c');
});

it('should pass using `deep` given an immutable value', function () {
var obj = Immutable.fromJS({ foo: [{ bar: 42 }] });
expect(obj).to.have.deep.property('foo[0]', new Map({ bar: 42 }));
});
it('should fail using `not` given an existing property', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
fail(function () { expect(obj).not.to.have.deep.property(['y', 'x']); });
});

it('should allow access to property via .that', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
var sub = obj.get('y');
expect(obj).to.have.property('y').that.equal(sub);
it('should fail given a property with a bad value', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
fail(function () {
expect(obj).to.have.property(['y', 'x'], 'different');
});
});

it('should pass given a property with the good value', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2, y: 3 } });
expect(obj).to.have.deep.property(['y', 'x'], 2);
});

it('should fail using `not` given an inexisting property', function () {
var obj = Immutable.fromJS({ x: 1 });
fail(function () {
expect(obj).not.to.have.deep.property(['y', 'x'], 'different');
});
});

it('should fail using `not` given a property with good value', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2 } });
fail(function () {
expect(obj).not.to.have.deep.property(['y', 'x'], 2);
});
});

it('should pass using `not` given a property with a bad value', function () {
var obj = Immutable.fromJS({ x: 1, y: { x: 2 } });
expect(obj).not.to.have.deep.property(['y', 'x'], 'different');
});

it('should pass given an immutable value', function () {
var obj = Immutable.fromJS({ foo: [{ bar: 42 }] });
expect(obj).to.have.deep.property('foo[0]', new Map({ bar: 42 }));
});
});

describe('given a string-based path', function () {
Expand Down

0 comments on commit bb49b6d

Please sign in to comment.