-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
Allow custom environments or other setup scripts to bind to Circus events. #8307
Conversation
@aaronabramov Pretty sure you wrote Circus originally so would appreciate your feedback on what I'm trying to do here. |
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 is super exciting stuff! Great idea to add it the the env
… restore globals export.
Codecov Report
@@ Coverage Diff @@
## master #8307 +/- ##
==========================================
- Coverage 62.2% 61.94% -0.26%
==========================================
Files 266 266
Lines 10673 10678 +5
Branches 2596 2598 +2
==========================================
- Hits 6639 6615 -24
- Misses 3445 3477 +32
+ Partials 589 586 -3
Continue to review full report at Codecov.
|
This looks really cool! Can we keep this open in RFCish state for a bit? I'd love to mess around with it as well to see how it covers some use cases |
@jeysal Definitely, please let me know if you have any feedback. I'd like to get this into the next release within a week-ish but I'll hold off on merging it until necessary. |
Thanks for mentioning Circus was a big question mark for me when working at that jest <> polly integration as I wasn't sure at all how to do it when jasmine is gone 😅Can't wait to start hacking with this branch to prepare for Circus release 😸 |
that looks amazing! 👍 |
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 is missing the promised docs, but the implementation looks solid to me! 👍
I've been using it for a bit now and I feel confident in the interface + no feedback about changing it, will write up docs and then get it merged in shortly! |
@palmerj3 does this help with Spotify's http interceptor? |
I have some time to try this out today :) |
@SimenB oh man this looks like exactly what I need |
@scotthovestadt I've played around with this and found a use case that this might not cover well. I tried to wrap the test fn in a Zone (basically in const test = state.currentDescribeBlock.tests.find(
({name}) => name === event.testName,
); and then alter that As an alternative idea for this API, I would suggest using a chain of handlers that can either perform side effects (which the current API seems to be good at) or modify the events (which is not currently possible): const handler = (event, next) => {
switch(event.name) {
case 'add_test':
console.log(`side effect for ${event.testName}`);
next({
...event,
fn: someApi.wrap(event.fn),
})
break;
default:
next(event);
}
} The final Another thing to note is that the If we manage to cover the relevant use cases while only exposing events, we can move much faster ™️. Exposing the state means almost any change to its structure will be breaking because of code like the one at the top of this comment. Exposing events and a way to operate on them, on the other hand, means we can e.g. call handlers on the old deprecated event, convert it to the new format, and then call handlers including circus itself on the new event. Would love to hear thoughts from everyone involved! This makes a quite large and essential piece of previously internal API public, so I think it's not something to be taken lightly :) |
@jeysal Thanks for the feedback! Public InterfaceIt's worth noting that the intention of Circus all along was to expose the events and state as public interface so other code could hook into it. It's been a long-standing problem that there's no way to hook into the lifecycle (as evidenced even in this thread by @palmerj3, @gribnoysup, and myself). Event MutationThe way Circus works internally is by mutating the case 'test_done': {
event.test.duration = getTestDuration(event.test);
event.test.status = 'done';
state.currentlyRunningTest = null;
break;
} Any change to this is also going to be basically a refactor of the whole Circus runner. I don't think just dropping in a different way of doing things solves any of the core problems. I'm not saying it can't be improved; I'm just saying if we do, it probably belongs in a major version anyway, since it would be a very large refactor. There are a couple of improvements that can be made incrementally but I didn't make here because I don't have a use-case for them and didn't want to over-engineer:
Moving FastYes, this exposes more as a public interface. Much of this is already exposed with the If we are truly worried about exposing it to the public quickly, I'm happy to hold off on any documentation. We can use it internally for the use-cases I mentioned and have more time to make any breaking changes needed. If we do choose to document, these points can be clear in the documentation:
Thanks again for your feedback. |
Thanks for the detailed response @scotthovestadt!
Could you elaborate on how this solves it? It looks to me like the other bullet point about priorities would solve it by making my handler run before circus' handler, not this one 😄
I'm not suggesting dropping mutation internally. I guess my suggestion is not really about immutability either (the whole
I don't know what exactly you've used custom event handlers for, do you think such an API would be sufficient or are there things which cannot be mapped to event mutations? |
I see your point about mutations. My use-case doesn't involve mutation and you've changed my mind to be explicit about documenting that the mutation use-case isn't supported for now. I think at a minimum we'll need some sort of a Long-term, I think it's going to be important to allowing 3rd party proper hooks into the system without going and adding an explicit hook for them everywhere, but we should discuss it more. One use-case that comes to mind for allowing mutations is being able to remove the custom ... but we'll defer that for now. The use-case I'm trying to solve is to just hook into various stages of the lifecycle so you can do external things, similar to I don't think event pre-handlers are a good solution because most of the data you need for events is generated by the default handlers. Exposing the state is probably necessary because you may need data not in a specific event. The state is just an assembly of parts of the event data, you could just re-create it yourself from all the events... and people will do this if we don't expose state. I think what you actually want is to separate event mutation from state mutation, which makes total sense for mutations but would be a large change to Circus. I agree (tentatively, I'd need to write some code to fully grok it). To sum up what I've learned from your feedback:
As-is, what we'll document is that you can synchronously listen to events and access read-only event data and state data. I personally think that this is an acceptable public API surface for a major version and can be broken in a future release if needed. Lastly, I want to note that
|
For my use-case I would ideally need the event handlers to be blocking in some form. Wait for me to resolve a promise, return, etc. Would give time to configure things and clean up before/after a test is run. But from a reporting standpoint this gets us closer to individual tests instead of a per-suite level we are at now which is great. |
@palmerj3 It's pretty trivial to make the individual event handlers await a Promise if returned, I'll do it (event: Event, state: State) => void | Promise<void>; |
@@ -13,7 +13,7 @@ import crypto from 'crypto'; | |||
import {sync as spawnSync, ExecaReturns} from 'execa'; | |||
import {skipSuiteOnWindows} from '@jest/test-utils'; | |||
|
|||
const CIRCUS_PATH = require.resolve('../../build'); | |||
const CIRCUS_PATH = require.resolve('../../build/'); |
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 we don't need to add /
after build
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.
It'll resolve to the index.js path. It could probably be without build
as well, and it'll use main
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.
adding the /
wasn't strictly necessary, it's just a result of churn because I changed the import path to something else and then back again
doesn't make a difference either way, it'll resolve to index
Simplified implementation: #8344 |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Summary
Currently, it's not possible for custom environments, setup scripts, or even other code inside of Jest to bind to events from Circus. This reduces the value of the new test runner when it could be used for a lot of awesome features that aren't possible today without hacks.
Exposing these events generally has the following use-cases:
it
blocks: https://github.com/gribnoysup/setup-polly-jest/blob/master/src/setupJasmine.js#L137With this change, it's possible to do something like this:
It also works in
setupFilesAfterEnv
. The key is that the globals have to be setup first by the environment before callingaddEventHandler
. When writing documentation, I'll make that clear. Unfortunately, it is difficult to detect when it's called too early and warn the user because of the variety of test environments possible. If you can think of a way, let me know.I'm planning on writing documentation for this but wanted to put this out there first to get any initial feedback on the implementation and the interface for using it. The events themselves and event metadata are fairly Circus-specific so I think it's ideal that any external users realize that and must import circus directly. This will be less awkward when it's the default test runner.
Open to all feedback.
Test plan