-
-
Notifications
You must be signed in to change notification settings - Fork 3k
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
initial implementation of "functional" interface #3399
Conversation
lol, guess I can't use |
this needs
|
imo this is exactly what i would expect an arrow-func-centric Mocha to look like! |
Is it possible to omit explicit describe("...", () => {
- it("...", () => {
+ it("...", t => {
// some asserts...
+ t.done();
});
}); |
if we didn’t require it then how would we know a test is async? if we supported a probably just as effective to use |
Cab you check if the return value is a promise instead?
…On Wed, May 30, 2018, 17:20 Christopher Hiller ***@***.***> wrote:
if we didn’t require it then how would we know a test is async? if we
supported a test, done signature, then every async test that didn’t need
to use methods on test would have test anyway.
probably just as effective to useasync keyword instead of test.done() ..
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#3399 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAKyrw4TeQzIxCUiHSGIlFS3ixIsgTMIks5t3zdngaJpZM4UT_DI>
.
|
Something like this? describe("...", () => {
it("...", t => {
t.async(true);
setTimeout(t.done, 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 really love this feature. I still did not figure out how the done
parameter is being passed to the tests, I do not see the implementation anywhere.
/** | ||
* Default Mocha behavior | ||
*/ | ||
exports.Default = { |
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.
Can we rename this from Default
to something else?
- It is a little confusing with es6 default modules.
- Whenever we want to change the default behavior to another one, we have to refactor 2 places now.
- A semantic name indicating what it does is more meaningful IMO.
noCtxAsArg
or something in this context (pun intended 😄 ).
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, probably a good idea to rename this, or just use module.exports
and put the Functional
behavior within the func
interface... what do you think of that?
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.
Is it Funtional
is meaningful? How about Contextual
?
I'm not sure that it is functional thing.
*/ | ||
run: function(done) { | ||
var result = this.fn.call(this.ctx); | ||
if (result && typeof result.then === 'function') { |
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 know this code is just moved, but if we wrap the result in Promise.resolve(result).then(...)
instead of doing this check, we'll always have the returned value treated as a promise, without the need for the extra hassle.
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.
This would require adding a Promise polyfill to the production bundle.
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'm not necessarily offering an opinion on that; I'm just telling you why Mocha historically does it this way)
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'm actually really annoyed about IE11/ES5, so maybe we can just write Mocha in ES6 going forward and pipe the distfile thru Babel...
* @private | ||
* @returns {*} Whatever the behavior method returns | ||
*/ | ||
Runnable.prototype.callBehavior = function(name) { |
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.
not sure about backward compatibility but can't we use (name, ...args)
?
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.
No, Mocha is written in ES5 😄
@@ -0,0 +1,43 @@ | |||
'use strict'; | |||
|
|||
var Promise = require('es6-promise'); |
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.
why is this needed? Promises are supported for all node versions we support (anything above 4)
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.
IE11
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.
though, I don't think this test actually runs in the browser. it should.
I wonder if I can use this feature to pass a |
@wavebeem We do check for a @Bamieh I'm not entirely sure what you're asking, but if I had to guess, it's here. We call @eight04 Is there a precedent for that--does another test framework do it that way? I'm basing my implementation on stuff like tape, which requires This is why I mentioned it'd actually be easier to use the it('should synchronously assert', t => {
assert(true);
t.done(); // might as well alias t.end() to this if we do it
}); is effectively: it('should synchronously assert', async t => {
assert(true);
}); FWIW, the Jasmine/Jest strategy seems to actually make timers |
* @this {Runnable} | ||
* @returns {boolean} `true` if we should run the test fn with a callback | ||
*/ | ||
shouldRunWithCallback: function() { |
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.
So, IMO, this is too specific.
What may be better is supporting a function which answer the question "what arguments should we pass to the execution function?", and another "what context should the execution function run with?"
Obviously we're going to need to document this specification...
No. Actually, I think The original purpose of passing the context as the first argument is to solve the problem that users can't access I prefer
If we still want to keep async as the default, it is probably worth adding an |
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 like this approach.
afterEach(function() { | ||
return Promise.resolve(); | ||
}); | ||
}); |
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.
These tests don't be run.
We should add it in package-scripts.js like:
func: {
script: test('func', '--ui func test/interfaces/func.spec'),
description: 'Run Node.js Functional interface tests',
hiddenFromHelp: true
},
/** | ||
* Default Mocha behavior | ||
*/ | ||
exports.Default = { |
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.
Is it Funtional
is meaningful? How about Contextual
?
I'm not sure that it is functional thing.
}; | ||
var result = this.fn.call(null, this.ctx); | ||
if (result && typeof result.then === 'function') { | ||
this.resetTimeout(); |
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 think this line should ve moved above if
statement.
Because tests just exit if test.done()
is not called if synchronous tests. Also we should fix the error message for func ui.
This is pretty darn stale. I still like the idea, but will need more iteration after v6. |
@outsideris I think I agree that |
* @param {Function} done - Callback | ||
*/ | ||
run: function(done) { | ||
this.ctx.done = function(err) { |
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 love this monkeypatching; Context
should have a method which accepts a callback and assigns it to its own done
method.
@boneskull do you think there is a chance you work on this approach again? Could you explain why it was postponed / what's currently blocking it? I would be happy to help if I can. |
🤖 Closing out old PRs to keep the review queue small. Cheers! |
This is an initial attempt at a "functional" interface; those of us who prefer using arrow functions will hopefully appreciate this. I've called it the
func
UI; here's how it looks:Usage
Implementation Details
Mocha makes assumptions about how Runnables (Tests, Hooks) are executed; what function context they are called with, and what parameters. To address this, I needed to decouple some parts of Mocha's core.
So, I've introduced a new concept: the Behavior. We can think of this as a collection of mixins which may apply (currently) to Suites and Runnables. These mixins introduce alternative behavior for certain methods within those objects. This is not the mythical plugin API, but it could be a step in that direction.
There are currently a select few places where the Behaviors are delegated to, and I've broken out the default functionality into a Behavior (the
Default
one, fwiw).Due to the way Mocha's Suites configure their children (Suites, Hooks, and Tests), in which all children "inherit" the configuration of their parent Suite, an Interface (
bdd
,tdd
,qunit
, etc.) only need configure the Behavior on the Root Suite. This configuration propagates throughout all Suites and children thereof.This means an Interface can provide an alternate Behavior. It's not the only way to provide an alternate Behavior, but seems like a reasonable one; see the simplicity of
lib/interfaces/func.js
. (Maybe the interface is where theFunctional
Behavior should live?)A Behavior may have one or more methods and needn't implement all of them. The default methods will always be present, and if multiple behaviors are configured, then the last one wins (think of how
Object.assign()
works).Ultimately, I'm not married to this API. I'm open to considering an event-driven model, as long as the scope doesn't blow up. But once it's merged, we are married to it, so it behooves us to be confident about what we're doing.
See #1856, #2657, probably others.