Hello,
is there a workaround to allow something like this:
chai.expect({ foo: 'bar', bar: 'foo' })
.to.have.property('foo', 'bar')
.and.to.have.property('bar', 'foo')
;
+1
Did you ever find a solution for this? Basically, we're looking for a way to reset the chain to the original subject within expect(...)
No, I didn’t.
should.js is missing this feature too…
Am 07.12.2013 um 18:18 schrieb Jeff Jo [email protected]:
+1
Did you ever find a solution for this? Basically, we're looking for a way to reset the chain to the original subject within expect(...)
—
Reply to this email directly or view it on GitHub.
Was thinking about this earlier. Two ways:
.pop() that resets the topic back to the original. Second would be better for backwards compatibility but the first makes more logical sense.
/cc @vesln @domenic for additional opinions
edit: logical typo
What do you think about:
obj.should.have.property('foo').and.should.have.properyt('bar').which.equals('foobar');
Means intention depends on the used word chain?
Am 07.12.2013 um 18:39 schrieb Jake Luer [email protected]:
Was thinking about this earlier. Two ways:
If a second argument is provided then don't "indent" the topic.
Store a reference to the "parent" outdent and add a helper called .pop() that resets the topic back to the original.
Second would be better for backwards compatibility but the first makes more logical sense./cc @vesln @domenic for additional opinions
—
Reply to this email directly or view it on GitHub.
I find that this would be especially nice when used in conjunction with @domenic's chai-as-promised to avoid repetitive .should.eventually. Currently you have to:
var profile = getProfile();
profile.should.eventually.have.property('id', 1);
profile.should.eventually.have.property('username', 'wbyoung');
But I think this would be a bit nicer chained:
getProfile().should.eventually.have.property('id', 1)
.and.have.property('username', 'wbyoung');
In some ways, this could be solved via a properties assertion (which was discussed & understandably rejected in #72).
I like the _outdent_ idea proposed by @logicalparadox best. Perhaps also could serve as this word:
getProfile().should.eventually.have.property('id', 1)
.and.also.have.property('username', 'wbyoung');
I made a small plugin, called chai-also for this & just pushed it to npm. No browser support, but that wouldn't be hard to add if anyone is interested.
I quite like the idea of also... but it needs some more discussion to fill gaps; for example I can see people expecting to traverse deep objects like so:
var multiLayerObject = {
foo: {
bar: 1,
baz: 2,
},
bing: {
bong: 3
bash: 4
}
};
// Does it just go up a level?
expect(multiLayerObject).to.have.property('foo')
.and.have.property('bar', 1)
.and.also.have.property('baz', 2)
.and.also.also.have.property('bing') // ???
.and.have.property('bong', 3)
.and.also.have.property('bash', 4);
// Or does it reset to the originally object within expect()?
expect(multiLayerObject).to.have.deep.property('foo.bar', 1)
.and.also.have.deep.property('foo.baz', 2)
.and.also.have.deep.property('bing.bong', 3)
.and.also.have.deep.property('bing.bash', 4);
At the risk of sounding cynical - I can see either implementation getting a few issues springing up discussing semantics and trying to alter them to the users use-case.
Also worth pointing out that .property() isn't the only thing that sets the object to a new one; .throw() and .ownPropertyDescriptor() do this too - and potentially more will. We'll need a solution that works for all of these I believe.
@keithamus I agree that it could be confusing. After making this little plugin, I was thinking the same thing — it certainly could read either way. I chose reset to original in my plugin, but feel that having more options could work better. Perhaps also goes back to the previous subject, parent goes one level up, and subject returns to the original:
var obj = {
foo: {
bar: 'baz'
}
};
expect(obj).to.have.deep.property('foo.bar.baz')
.and.parent.has.property('baz'); // just return to `obj.foo.bar`
expect(obj).to.have.deep.property('foo.bar.baz')
.and.subject.has.property('baz'); // just return to `obj`
expect(obj).to.have.deep.property('foo.bar')
.and.also.have.deep.property('foo.bar.baz'); // return to subject before last change, `obj`
expect(obj).to.have.property('foo')
.and.have.property('bar')
.and.also.have.deep.property('bar.baz') // return to subject before last change, `obj.foo`
.and.subject.has.deep.property('foo.bar.baz'); // just return to `obj`
And I don't think it sounded cynical at all, but I also had the same thoughts lingering in the back of my head. ;)
I was pointed to this issue from my stack-overflow post here, so it seems there's no way to currently do this?
@reggi as of right now, not really. You can simply create multiple assertions as you suggested in your question on Stack Overflow. I agree with @keithamus that my suggestion of also can be confusing, and it probably makes sense to wait for decisions to be made here & something to be incorporated into the main library, but if you're adventurous (and using node), you can use chai-also.
I just remembered that we have .include which can test subset bodies:
chai.expect({ foo: 'bar', bar: 'foo', baz: 'bing' }).to.include({ foo: 'bar', bar: 'foo' });
This should be suitable for testing many properties. You can even combine it with property for complex nesting:
var multiLayerObject = {
foo: {
bar: 1,
baz: 2,
},
bing: 3,
bong: 4,
};
chai.expect(multiLayerObject)
.to.include({ bing: 3, bong: 4 })
.and.have.property('foo').include({ bar: 1 });
@keithamus awesome. The docs should probably be updated to show that this is possible!
Hey @wbyoung you're totally right. It looks the docs were missed in the original PR. Luckily it would be simple enough to add in - here is the comment which the docs are generated from - fancy making a PR?
FYI looks like this has been a feature since 1.9.0! A hidden feature tucked away, I only just realised it existed the other day myself!
Just curious — is it one of those things that works by happenstance or is it deliberate (i.e. test coverage)?
I'll try to get a PR up when I have a few min.
I'm excited about this discovery! Thanks for sharing.
Nevermind… looked at the PR you linked to & see that there are tests. Definitely deliberate.
@keithamus I just opened #610.
I searched this feature for a while! Why the official documentation is not up-to-date one year after?
There's one case in the documentation where it does seem that the context goes back up to the top level, and I can't see an explanation for why this would be:
expect({a: 1}).to.have.property('b').but.not.own.property('b');
I am curious if anyone can tell whether this does indeed work as expected or just by coincidence?
@fineline Good catch. That's my mistake. It should be split into two assertions:
expect({a: 1}).to.have.property('b');
expect({a: 1}).to.not.have.own.property('b');
@meeber Thanks for clarifying that for me and others.
Most helpful comment
I just remembered that we have
.includewhich can test subset bodies:This should be suitable for testing many properties. You can even combine it with
propertyfor complex nesting: