Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add propertyDescriptor assertion #408

Merged
merged 1 commit into from Mar 30, 2015
Merged

Conversation

ljharb
Copy link
Contributor

@ljharb ljharb commented Mar 25, 2015

No description provided.

@keithamus
Copy link
Member

@ljharb thanks for the PR!

I'm not entirely sure if having enumerableProperty is the best method to have. Mainly because if we had the full descriptor set, we'd have to have writableProperty, configurableProperty, etc.

Maybe it'd be better to have a generic property to get access to the descriptor interface, something like:

expect(myObject).to.have.propertyDescriptor('length').and.have.property('enumerable', true);

Slightly longer but much more flexible. Thoughts?

@ljharb
Copy link
Contributor Author

ljharb commented Mar 25, 2015

Interesting - I do see how that'd be more flexible, but it's much more verbose - I want to use this in the es6-shim where I have lots of tests that assert that a property exists and is (or is not) enumerable. I don't often check configurability or writability, nor the existence of getters and setters.

How complex would your suggestion be to add? I'm not sure I yet know enough about the internals of chai to be able to add it quickly.

@keithamus
Copy link
Member

If we were to go with my suggestion (I'm not mandating it, just giving some food for thought) then you're about half way there... you'd need something like:

function propertyDescriptor (name, msg) {
  if (msg) flag(this, 'message', msg);
  var obj = flag(this, 'object');
  var descriptor = Object.getOwnPropertyDescriptor(Object(obj), name);
  flag(this, 'object', descriptor);
  this.assert(
      descriptor
    , 'expected #{this} to have descriptor ' + _.inspect(name)
    , 'expected #{this} not to have have descriptor ' + _.inspect(name)
  );
}

Take particular note of flag(this, 'object', descriptor) which sets the object to assert on for the rest of the chain. Meaning that the .and.have.property() part asserts on the descriptor, not the initial object.

@keithamus
Copy link
Member

I'd also say, either way, we should probably also make the assertion deep aware, so that developers could do .to.have.deep.enumerableProperty (or .to.have.deep.propertyDescriptor if that's the path we go down).

@keithamus
Copy link
Member

Just to play devils advocate some more, you could use existing assertions in a fairly non-verbose way (and a way closer to how the API would be consumed) to check non-enumerability:

it('should have non enumerable length', function () {
    expect(object).to.not.include.keys('length');
    expect(object).to.have.property('length');
});

@ljharb
Copy link
Contributor Author

ljharb commented Mar 25, 2015

The failure message for "not to include keys" isn't very clean though, since it would list all the keys iirc.

I'll play with this and see what I come up with, thanks.

@ljharb
Copy link
Contributor Author

ljharb commented Mar 26, 2015

@keithamus For the "deep" part - does that mean that toHavePropertyDescriptor would by default only check own properties? That's not how Object.getOwnPropertyDescriptor works, so that'd be a bit trickier.

@keithamus
Copy link
Member

The deep flag should be used to access far down properties, for consistency with .property, e.g.

expect({ foo: bar: new Thing() }).to.have.deep.propertyDescriptor('foo.bar.length');

We could of course call it .ownPropertyDesciptor to make it even more obvious that it aligns with Object.getOwnPropertyDescriptor - especially as I think Object.getPropertyDescriptor is coming in ES6/7.

expect(new Thing()).to.have.ownPropertyDescriptor('length');

@ljharb
Copy link
Contributor Author

ljharb commented Mar 26, 2015

@keithamus I've updated the PR to cover the generic propertyDescriptor assertion. I didn't add anything for "deep" because I don't know what that entails :-)

@ljharb ljharb changed the title Add enumerableProperty assertion Add propertyDescriptor assertion Mar 26, 2015
@keithamus
Copy link
Member

Also, as an aside, I'm not sure we need havePropertyDescriptor - please do correct me if I'm wrong but I believe the rest of the codebase avoids combining verbs in methods. Just having propertyDescriptor is sufficient.

@ljharb
Copy link
Contributor Author

ljharb commented Mar 26, 2015

I copied the style verbatim from ownProperty above it, which has haveOwnProperty. Would you prefer I remove it? (Personally I prefer the alias, as I have a strong dislike for chaining non-function getters, but that's pretty tough to avoid with mocha/chai anyways)

@keithamus
Copy link
Member

Yup, I see. I'll eat my words then, havePropertyDescriptor can stay.

What are your thoughts about renaming them both to [have]OwnPropertyDescriptor?

P.S. the keywords in chai are entirely optional expect(foo).ownProperty('bar') works the same as expect(foo).to.have.an.ownProperty('bar')

@ljharb
Copy link
Contributor Author

ljharb commented Mar 27, 2015

Sure, it's Object.getOwnPropertyDescriptor so that makes sense. I'll fix that.

@ljharb
Copy link
Contributor Author

ljharb commented Mar 27, 2015

@keithamus hmm - if I were to add deep support, then it would read .to.have.deep.ownPropertyDescriptor which doesn't make sense, whereas .to.have.deep.propertyDescriptor does. Thoughts?

@keithamus
Copy link
Member

Hmm, that is indeed a quandary. If es6/7 is to get Object.getPropertyDescriptor then we have a semantics problem if we use .propertyDescriptor.

My feelings are we should do the simplest thing first - so adding .ownPropertyDescriptor, without deep support. If deep support is requested later, we can add it. If it later transpires it can be shortened to .propertyDescriptor without conflicting with ES* semantics then we can shorten it later. How do you feel about that?

@ljharb
Copy link
Contributor Author

ljharb commented Mar 28, 2015

ES6 absolutely doesn't have that, and as a member of TC39 I haven't seen any proposals for it for ES7.

I'm fine with calling it ownProperty and not adding deep support, for now.

@ljharb
Copy link
Contributor Author

ljharb commented Mar 28, 2015

Just pushed an update with ownProperty and no deep support.

*
* Asserts that the target has an own property descriptor `name`, that optionally matches `descriptor`.
*
* expect('test').to.have.ownPropertyDescriptor('length');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add a few more examples here? Specifically I'd like to see the following:

expect('test').to.have.ownPropertyDescriptor('length', { enumerable: false, configurable: false, writable: false, value: 4 });
expect('test').ownPropertyDescriptor('length').to.have.property('enumerable', false);
expect('test').ownPropertyDescriptor('length').to.have.keys('value');

Feel free to adjust those as you see fit - but it should show a couple of examples to demonstrate what's possible (especially for any extra arguments).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, those look great :-)

@ljharb ljharb force-pushed the enumerableProperty branch 2 times, most recently from 5e1e680 to 8b6a3cc Compare March 30, 2015 04:53
@keithamus
Copy link
Member

Sweet! Awesome work @ljharb. Consider this merged 😄

keithamus added a commit that referenced this pull request Mar 30, 2015
Add `propertyDescriptor` assertion
@keithamus keithamus merged commit 8a29595 into chaijs:master Mar 30, 2015
@keithamus keithamus mentioned this pull request Jul 26, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants