-
Notifications
You must be signed in to change notification settings - Fork 29
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
Spike: Camel case syntax #784
base: master
Are you sure you want to change the base?
Conversation
I took a look at this PR - some thoughts follow. Implementation wise I think it is very straightforward - one thing I learnt from trying unassessed was that we may may not want to expose all the assertions like this. In it I had somewhat of a whitelist, although it was implemented by looking at the assertion string / type information and at least filtering out any that were middle-rocket. On a perhaps more controversial point, I can't help but be in two minds about whether this should really be in unexpected core. It almost feels like it's might be something else if we expose the methods like this. From what I can tell, we can almost do this by making use of being able to hook unexpected - perhaps we should consider making that infrastructure work and putting this out under a new name? |
lib/createTopLevelExpect.js
Outdated
if (typeof format === 'undefined') { | ||
return this._outputFormat; | ||
} else { | ||
this._assertTopLevelExpect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC I hit an issue with this myself at some point and I think this fix may want to be applied directly to master.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree: 5349b0fa
if (args.length < 1) { | ||
throw new Error('The expect function requires at least one parameter.'); | ||
} else if (args.length === 1) { | ||
return this._camelCaser(context, this.findTypeOf(subject), subject); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re the latter part of my comment here, it's weakening the two parameter rule and making a single argument suddenly make the library behave very differently that got me pondering whether this bends things too far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's too bad -- it actually aligns the one parameter expect case with what the nested expect
made available to assertion rule handlers does:
unexpected/lib/createTopLevelExpect.js
Line 1313 in 55268ea
throw new Error('The expect function requires at least one parameter.'); |
... And if we agree about a long term goal of deprecating/removing the old string-based syntax, I think we can live with a bit of temporary overloading.
This implementation preserves middle rocket support via this syntax: expect([1, 2, 3]).whenPassedAsParametersTo(Math.max).toEqual(3); Then we can have a separate conversation about whether to unsupport middle rocket altogether. Last time we talked about it, I remember that we were a bit back and forth about whether to part with it, and that especially We've also talked about requiring middle rocket assertions to declare the type of what they're
In my opinion the camel case syntax is superior to what we have now (and less controversial), so I think we should go all in on it, switch all the docs over to use it, and aim to remove the current assertion syntax. If we hook it in and keep it in a plugin, we'll be sending mixed signals and end up with a fragmented ecosystem.
That's called unassessed, isn't it? 😼 In my opinion we've essentially already done that and proven that it's pleasant to work with, so I think now's the time to put it in core. |
it('should fail', () => { | ||
expect(() => | ||
expect({ foo: 123 }).toSatisfy({ | ||
foo: expect.toBeANumber().andToBeGreaterThan(200), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we could still attach these to a .it object and simply hand one out that has had the camelCaser run over it when we're in that mode?
Reason this occurs to me is that I think the .it. still serves a purpose syntactically in saying "and use the subject from the situation in which you are called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was a bit back and forth on that, decided that it looked fairly clean without any it
. Could you spell out with some examples exactly which alternative(s) you're thinking about? expect.it.toBeANumber()
? To me that reminded me too much of chai's weirdness where the dot notation and camel case are mixed -- expect(foo).to.shallowDeepEqual(bar)
🙀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I see what you're getting at. Let me sit with this some more...
More thinking aloud, for the purposes of discussion, what do we do with the value returning form of expect.it? Perhaps the normal assertions should be callable directly, but we provide a .it() that is specifically meant to be used only as .it(value => { ... })
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, yeah, the expect.it(fn)
case is interesting to consider. It shouldn't really be a problem to keep supporting it as-is.
You could argue that if we're going to migrate it over to something that's not backwards compatible (or if we want to add another variant of it that's easier on the eyes in a camel case context), we might as well switch to something completely different that isn't necessarily an overloading of the expect.it
replacement.
).toThrow( | ||
'expected { foo: 123 } to satisfy\n' + | ||
'{\n' + | ||
" foo: expect.it('to be a number')\n" + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we proceed to making this user-ready we probably need to consider what to do here - perhaps we could switch out the expect.it type such that this would render the camel cases names? Wondering if that can fit with my comment about about keeping the .it. above as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, absolutely, that would be fairly easy. We can try to make the rendering respect the syntax that was actually used in the source code, or just make an unconditional switch to camel case.
describe('and extra chaining with .or...', () => { | ||
it('should succeed', () => { | ||
expect({ foo: 123 }).toSatisfy({ | ||
foo: expect.toBeAString().orToBeGreaterThan(100), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im not too keen of having different spelling of the assertions like this - I think that would get confusing when it came to documentation and the like.
Thinking aloud were ok chaining through a .or. we could potentially generate the object with all these assertions only once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair, we have a bunch of wiggle room wrt. the syntax here. Again, I'm not too keen on mixing dot notation and camel case, but something like expect.toBeAString().or().toBeGreaterThan(100)
could be arranged?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is far better to my eyes at least!
c77267f
to
7eb88b9
Compare
23105f0
to
16e4b28
Compare
7eb88b9
to
c70d2d5
Compare
c70d2d5
to
66eac00
Compare
66eac00
to
7b0eb42
Compare
Removes support for expect(oneArg) returning an object with .and etc., but we can maybe get that back later
ed68984
to
a3b3ee0
Compare
In my opinion the current assertion syntax is far superior to this DSL-chaining thing. It is one of the primary reasons why I use unexpected in the first place. If unexpected is used in the "exact" same way as the rest of the assertion frameworks, then why do we need unexpected at all? |
Hopefully because of the extensibility and quality of the output when an assertion fails? :) |
This is a super inefficient proof-of-concept implementation so that we can discuss the syntax etc.
If we can achieve some kind of consensus, I promise to clean it up and optimize it :)
expect([1, 2,3]).whenPassedAsParametersTo(Math.max).toEqual(4)
-- right now that just fails withexpected 3 to equal 4
here.expect.it(fn)
should map to.