codelenny / chai-events Goto Github PK
View Code? Open in Web Editor NEWMake assertions about event emitters
Make assertions about event emitters
Allow the user to specify an event emitter implementation, instead of just using a global EventEmitter/EventTarget.
Ensure package works in browser environments.
Many event emitters attach data when emitting events. It would be nice if we could test assertions on that data:
return expect(emitter).to.emit('event').that.equals(42);
At the moment, this is rather difficult, like await
ing a Promise that resolves when the event I am looking for is emitted:
return expect(new Promise(resolve => emitter.on('event', (...args) => resolve(args)))).to
.eventually.equal(/* expected arguments */); // Using chai-as-promised
I think the required changes are probably not very big. Mostly emit
would have to be marked as chainable, and the assertion target would need to be changed to emitted event's data.
If you're interested in this at all, drop me a reply, and I could have a look at a pull request.
Currently timeout is defaulting to 1500ms (appropriate for Mocha). Allow user to configure:
(x).should.after(200).not.emit("test");
At the moment yout cannot use this in TypeScript because there aren't any TypeScript definition files.
I'm not sure how to create them for an Node library
Simplify testing and improve coverage.
(x).should.emit;
(x).should.be.able.to.emit("foo");
Make the above syntax in turn run x.should.emit("foo"); x.emit("foo");
NOTE: chai-as-promised
has an one significant side affect:
chai.use(require('chai-events'));
chai.use(require('chai-as-promised'));
// 'chai-as-promised' ignores our Promise and returns plain Assertion (or Proxy) object!
// chai.config.useProxy = true(default)/false;
const onEvent = expect(eventEmiter).to.emit('eventA'); // <- lost the Promise !
await onEvent; // <- doesn't wait
It happen only if expection chain doesn't include eventually
property. Underhood plugin wraps all chai methods and add then
function with custom logic only if this._obj
is promise. But in our case the tested object is EventEmiter
. Anyway if try to add eventually
to example above, we catch an error: eventNames: [Function: eventNames] } is not a thenable.
Workaround: put chai-event
after chai-as-promised
chai.use(require('chai-as-promised'));
chai.use(require('chai-events'));
Possible fix:
Assertion.addMethod('emit', function(name, timeout) {
const promise = new Promise((resolve, reject) => {...};
// If use 'chai-as-promised' after 'chai-events'
// make Assertion then-able to allow 'await'
this.then = promise.then.bind(promise);
// if use 'chai-as-promised' before 'chai-events' or without 'chai-as-promised'
// return Promise as usual
return promise;
});
Propousal:
It would be nice to mention about such issue in README.md
Test should FAIL but it's OK (chai v4.1.2):
describe('chai-events', function() {
const eventEmitter = new EventEmitter();
it('should not emit', async function() {
const onEventA = expect(eventEmitter).to.have.not.been.emit('eventA');
eventEmitter.emit('eventA');
await onEventA;
});
});
Root cause:
https://github.com/CodeLenny/chai-events/blob/master/chai-events.js#L71
if(utils.flag(this, 'negate')) {
...
assert(false, "expected #{this} to not emit "+name.toString()+".");
...
}
If you look at chai.Assertion.assert()
API you can see that util.test(this, arguments)
converts expr
from false
to true
if negative flag is turned ON
https://github.com/chaijs/chai/blob/master/chai.js#L231
Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) {
var ok = util.test(this, arguments);
...
if (!ok) {
...
throw new AssertionError(...)
};
https://github.com/chaijs/chai/blob/master/lib/chai/utils/test.js#L27
module.exports = function test(obj, args) {
var negate = flag(obj, 'negate')
, expr = args[0];
return negate ? !expr : expr;
};
Fix: Change wrong assert()
usage to:
assert(true, '', 'expected #{this} to not emit '+name.toString() + '.');
Wrong Promise rejection implementation:
assert()
just throws exception and DOESN'T reject Promise that we expect in out test.
In this case sometimes we see UnhandledPromiseRejectionWarning
.
Case 1
const eventEmitter = new EventEmitter();
const onEventA = expect(eventEmitter).to.not.emit('eventA');
eventEmitter.emit('eventA'); // <--- HERE we get exception from on() handler
await onEventA; // NOT HERE !
});
Case 2
const eventEmitter = new EventEmitter();
const onEventA = expect(eventEmitter).to.emit('eventA');
await onEventA; // Promise will be NEVER resolved
// Test fails only because setTimeout throws an error and mocha handles it
});
Tech details
process.on('uncaughtException', uncaught)
:Possible solution
Use try-catch
for setTimeout()
and on()
handlers:
try {
assert(...);
resolve(args);
} catch (err) {
reject(err);
}
Currently, x.should.be.an.emitter
allows:
x instanceof EventEmitter
x instanceof EventTarget
typeof x[method] === "function"
for method in ["on", "emit", "off"]
typeof x[method] === "function"
for method in ["addEventListener", "dispatchEvent", "removeEventListener"]
Allow the user to specify which of the above rules are allowed.
(x).should.be.a.custom.emitter // Either browser or node
(x).should.be.a.browser.emitter
(x).should.be.a.node.emitter
(or something along those lines)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.