-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Implement pluggable watch mode #4841
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
Implement pluggable watch mode #4841
Conversation
5fd1272
to
446560a
Compare
Codecov Report
@@ Coverage Diff @@
## master #4841 +/- ##
==========================================
- Coverage 59.64% 59.27% -0.37%
==========================================
Files 194 201 +7
Lines 6502 6691 +189
Branches 3 4 +1
==========================================
+ Hits 3878 3966 +88
- Misses 2624 2725 +101
Continue to review full report at Codecov.
|
@@ -0,0 +1,5 @@ | |||
module.exports = { | |||
enter() {}, // override in test | |||
key: 0x73, // S key |
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.
lower-case s
?
}); | ||
|
||
it('prevents Jest from handling keys when active and returns control when end is called', () => { | ||
// load the plugin before `watch` does and patch it with a spy |
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.
could use jest.doMock
for this, but doesn't hurt to do it manually
} | ||
|
||
const plugin: WatchPlugin = ((maybePlugin: any): WatchPlugin); | ||
// TODO: Reject registering when another plugin has claimed the key? |
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 here. Could we separate between stuff built-in to jest (so you can override e.g p
) with stuff from userland (to avoid hard-to=debug conflicts)?
Maybe printing a warning is enough?
@@ -89,6 +89,86 @@ describe('Watch mode flows', () => { | |||
expect(pipe.write.mock.calls.reverse()[0]).toMatchSnapshot(); | |||
}); | |||
|
|||
it('resolves relative to the package root', async () => { | |||
expect(async () => { |
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.
need to await
this expect
(if not, just remove async from the top)?
I think the story around asynctoThrow
is pretty confusing, I usually have to tweak the code to be certain the test actually tests anyting.
What about await expect(watch(...)).resolves.toBeDefined()
? Or just await watch(...)
, without the expect
(as the test will fail if there are rejected promises)?
Left a couple inline comments. Overall I'm really excited about this :D Overall direction LGTM, but I haven't really touched the watch-code before. |
(Lint errors on ci) |
const watch = require('../watch').default; | ||
afterEach(runJestMock.mockReset); | ||
|
||
describe('Watch mode flows', () => { | ||
describe.only('Watch mode flows', () => { |
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.
Nit: .only
should be removed
@@ -51,6 +51,7 @@ const DEFAULT_GLOBAL_CONFIG: GlobalConfig = { | |||
verbose: false, | |||
watch: false, | |||
watchAll: false, | |||
watchPlugins: [], |
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.
With Jest Runners we have the problem that they are hard to configure... This is mainly because there is no way to pass options to runners, I wonder if we would have a similar problem with plugins
Proposed solution for customizing runners: #4278
Example of how existing runners need to do configuration right now: https://github.com/jest-community/jest-runner-eslint#options
I really like the idea of using an array for customization
- without options:
watchPlugins: ['my-plugin']
- with options:
watchPlugins: [['my-plugin', { someOptions: true, other: 'foo' }]]
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 that, yes.
|
||
// Since we're loading the module from a dynamic path, assert its shape | ||
// before assuming it's a valid watch plugin. | ||
if (typeof maybePlugin !== 'object') { |
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 can probably use https://github.com/facebook/jest/tree/master/packages/jest-get-type for all of these
const onKeypress = (key: string) => { | ||
if (key === KEYS.CONTROL_C || key === KEYS.CONTROL_D) { | ||
process.exit(0); | ||
return; | ||
} | ||
|
||
if (activePlugin != null) { |
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 that @cpojer and @aaronabramov think but I feel that it would be interesting to internally implement p
and t
as plugins so that they are the codebase just treat them as yet another plugin... It might also help to both use them as plugin references and also to always have something to validate and test that api...
Nvm, I just read #4824 (comment)
export type WatchPlugin = { | ||
key: number, | ||
prompt: string, | ||
enter: (globalConfig: GlobalConfig, end: () => mixed) => mixed, |
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.
Does end only gets called once? If so, what about changing the api to be promise based?
Nvm, I just read #4824 (comment)
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 great! I'm excited about it!
f3cc96b
to
5fa8647
Compare
Thanks for all this great work. I'd like to understand how much work it is to get typeaheads as a plugin to work with this. @wbinnssmith are you planning on resurrecting the old code in a plugin or was @rogeliog going to do this? There is a jest-community org on GitHub where this could live. My ideal end-state is this one:
|
I agree with @cpojer it would also be great to eventually provide a way where any plugin can easily leverage the typeahead infra |
5fa8647
to
4f757f7
Compare
Resolves jestjs#4824 This is my first nontrivial contribution to Jest so please be gentle :) This makes watch mode pluggable, allowing for arbitrary watch plugin modules to be registered in the global config and used to generate prompt messages and key listeners when watch mode is activated. When a particular plugin is activated, Jest no longer handles keypresses until the plugin calls the `end` callback (see jestjs#4824 for design details) This currently does not implement any existing watch functionality as a plugin and is rather adding the infrastructure to do so. That will follow soon :) @cpojer @aaronabramov I'm not sure if the globalConfig is the best place for this (to be honest I can't make sense of the the sheer number of config types). I also haven't gotten around to running a manual test for this quite yet. More interested in design feedback at this point :) Test Plan: `jest`
4f757f7
to
f7620f8
Compare
Now that you mention options, I think it would be best to have a first-class option that lets the user customize the trigger key for a given plugin. A plugin could still configure a preferred key in the case the user doesn't override it. I'm thinking that with the addition of options, plugins should instead export a function that is given the set of options and looks something like this: For example: module.exports = (opts) => ({
preferredKey: 's'.codePointAt(0),
action: 'filter by awesomeness',
enter: (globalConfig, startRun, end) => {
...
},
}); Looking at existing watch usage, I think this shape is beginning to make sense to me: type WatchPlugin = (options: {triggerKey: number}) => WatchDescriptor;
type StartRun = (globalConfig: GlobalConfig) => Promise<mixed>;
type WatchDescriptor = {
preferredKey: number, // code point of trigger key
action: string, // prompt is "press key to " + action
enter: (
globalConfig: GlobalConfig,
startRun: StartRun,
end: () => mixed,
),
} If the user passes Let me know what you think or if more examples would clarify :D And of course I'm open to alternatives. |
I like that idea! |
This is really great work. I agree with @rogeliog we should convert t and p into plugins so that you can base the new typeahead on that same code possibly. Thanks sooooo much for doing this. |
@wbinnssmith Thank you! For your info, being gentle of course :) After
but fails for either
● Watch mode flows › shows prompts for WatchPlugins in alphabetical order
expect(value).toMatchSnapshot()
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,7 +1,13 @@
Array [
Array [
+ "",
+ ],
+ Array [
+ "Determining test suites to run...",
+ ],
+ Array [
"
Watch Usage
› Press a to run all tests.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
at Object.<anonymous> (packages/jest-cli/src/__tests__/watch.test.js:134:35) |
Thanks for the work @wbinnssmith ! This would be great to have, as, if I understand correctly, this way we can use webdriverio REPL ( http://webdriver.io/api/utility/debug.html ) inside jest tests. (otherwise jest catches whatever I try to type inside the debugger) |
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. |
Resolves #4824
This is my first nontrivial contribution to Jest so please be gentle :)
This makes watch mode pluggable, allowing for arbitrary watch plugin
modules to be registered in the global config and used to generate
prompt messages and key listeners when watch mode is activated.
When a particular plugin is activated, Jest no longer handles keypresses
until the plugin calls the
end
callback (see #4824 for design details)This currently does not implement any existing watch functionality as a
plugin and is rather adding the infrastructure to do so. That will
follow soon :)
@cpojer @aaronabramov I'm not sure if the globalConfig is the best place
for this (to be honest I can't make sense of the the sheer number of
config types). I also haven't gotten around to running a manual test for
this quite yet. More interested in design feedback at this point :)
Test Plan:
jest