Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Chain .property() assertions on objects #193

Open
bodokaiser opened this Issue · 10 comments

6 participants

@bodokaiser

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')
  ;
@jeffjo

+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(...)

@bodokaiser
@logicalparadox

Was thinking about this earlier. Two ways:

  • If a second argument is NOT 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

edit: logical typo

@bodokaiser
@wbyoung

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');
@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.

@keithamus
Owner

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.

@wbyoung

@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. ;)

@reggi

I was pointed to this issue from my stack-overflow post here, so it seems there's no way to currently do this?

@wbyoung

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.