New internal testing helpers: waitFor, waitForAll, waitForPaint#26285
New internal testing helpers: waitFor, waitForAll, waitForPaint#26285acdlite merged 2 commits intofacebook:mainfrom
Conversation
ef4c880 to
4ea9353
Compare
|
Comparing: d49e0e0...8aadd4f Critical size changesIncludes critical production bundles, as well as any change greater than 2%:
Significant size changesIncludes any change greater than 0.2%: Expand to show
|
cc1f99a to
e550732
Compare
|
Need to decide what to do about jest-react package. My idea of just deleting the prod build and using the dev one for both didn't work because I forgot the dev build still checks NODE_ENV. I feel like the whole thing should move to |
tyao1
left a comment
There was a problem hiding this comment.
Looks great!
Probably unrelated to the PR. For tests that do a sync work and expect the output only for the sync work, what should be done?
react/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
Lines 1692 to 1722 in 2881d56
packages/jest-react/src/JestReact.js
Outdated
| error.message = ` | ||
| Expected sequence of events did not occur. | ||
|
|
||
| ${diff(expectedLog, actualLog)} |
There was a problem hiding this comment.
Is it because we would get wrong stack trace that we use diff instead of expect?
That pattern is still fine but maybe as a follow up we could replace |
e550732 to
319e6af
Compare
I'm thinking if we are going to put everything inside a microtask, the |
The idea is you would only call it after an event + microtasks have just run. The most common case is right after The other case is after |
Our internal test utils are currently located in the jest-react package. At one point, the idea was to publish some of these as public, officially supported APIs, but we abandoned that idea in favor of `act`. We should really just delete the jest-react package from our repo. What we can do instead is put our internal test utils into a private package. This adds such a package, called internal-test-utils. There are no exported APIs yet but I'll add some in the next step.
07f991e to
e28f28c
Compare
Over the years, we've gradually aligned on a set of best practices for for testing concurrent React features in this repo. The default in most cases is to use `act`, the same as you would do when testing a real React app. However, because we're testing React itself, as opposed to an app that uses React, our internal tests sometimes need to make assertions on intermediate states that `act` intentionally disallows. For those cases, we built a custom set of Jest assertion matchers that provide greater control over the concurrent work queue. It works by mocking the Scheduler package. (When we eventually migrate to using native postTask, it would probably work by stubbing that instead.) A problem with these helpers that we recently discovered is, because they are synchronous function calls, they aren't sufficient if the work you need to flush is scheduled in a microtask — we don't control the microtask queue, and can't mock it. `act` addresses this problem by encouraging you to await the result of the `act` call. (It's not currently required to await, but in future versions of React it likely will be.) It will then continue flushing work until both the microtask queue and the Scheduler queue is exhausted. We can follow a similar strategy for our custom test helpers, by replacing the current set of synchronous helpers with a corresponding set of async ones: - `expect(Scheduler).toFlushAndYield(log)` -> `await waitForAll(log)` - `expect(Scheduler).toFlushAndYieldThrough(log)` -> `await waitFor(log)` - `expect(Scheduler).toFlushUntilNextPaint(log)` -> `await waitForPaint(log)` These APIs are inspired by the existing best practice for writing e2e React tests. Rather than mock all task queues, in an e2e test you set up a timer loop and wait for the UI to match an expecte condition. Although we are mocking _some_ of the task queues in our tests, the general principle still holds: it makes it less likely that our tests will diverge from real world behavior in an actual browser. In this commit, I've implemented the new testing helpers and converted one of the Suspense tests to use them. In subsequent steps, I'll codemod the rest of our test suite.
e28f28c to
8aadd4f
Compare
|
gamechanger... i'll understand our tests now! |
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
Over the years, we've gradually aligned on a set of best practices for for testing concurrent React features in this repo. The default in most cases is to use `act`, the same as you would do when testing a real React app. However, because we're testing React itself, as opposed to an app that uses React, our internal tests sometimes need to make assertions on intermediate states that `act` intentionally disallows. For those cases, we built a custom set of Jest assertion matchers that provide greater control over the concurrent work queue. It works by mocking the Scheduler package. (When we eventually migrate to using native postTask, it would probably work by stubbing that instead.) A problem with these helpers that we recently discovered is, because they are synchronous function calls, they aren't sufficient if the work you need to flush is scheduled in a microtask — we don't control the microtask queue, and can't mock it. `act` addresses this problem by encouraging you to await the result of the `act` call. (It's not currently required to await, but in future versions of React it likely will be.) It will then continue flushing work until both the microtask queue and the Scheduler queue is exhausted. We can follow a similar strategy for our custom test helpers, by replacing the current set of synchronous helpers with a corresponding set of async ones: - `expect(Scheduler).toFlushAndYield(log)` -> `await waitForAll(log)` - `expect(Scheduler).toFlushAndYieldThrough(log)` -> `await waitFor(log)` - `expect(Scheduler).toFlushUntilNextPaint(log)` -> `await waitForPaint(log)` These APIs are inspired by the existing best practice for writing e2e React tests. Rather than mock all task queues, in an e2e test you set up a timer loop and wait for the UI to match an expecte condition. Although we are mocking _some_ of the task queues in our tests, the general principle still holds: it makes it less likely that our tests will diverge from real world behavior in an actual browser. In this commit, I've implemented the new testing helpers and converted one of the Suspense tests to use them. In subsequent steps, I'll codemod the rest of our test suite. DiffTrain build for [e524467](e524467)
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See #26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See #26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See facebook#26285 for full context.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See #26285 for full context.
Now that the tests have been codemodded, we can delete toFlushAndYield et al from the codebase. See facebook#26285 for full context. The mock implementation of Scheduler still exposes some (unstable- prefixed) APIs that can be used for some edge cases, like when testing the mock Scheduler itself. There are only a few tests that do this, and even some of those could be rewritten to use `act` or `waitFor`.
Now that the tests have been codemodded, we can delete toFlushAndYield et al from the codebase. See facebook#26285 for full context. The mock implementation of Scheduler still exposes some (unstable- prefixed) APIs that can be used for some edge cases, like when testing the mock Scheduler itself. There are only a few tests that do this, and even some of those could be rewritten to use `act` or `waitFor`.
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See #26285 for full context. DiffTrain build for [3cb5afb](3cb5afb)
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See #26285 for full context. DiffTrain build for [64dde70](64dde70)
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See #26285 for full context. DiffTrain build for [25685d8](25685d8)
Similar to the rationale for `waitFor` (see facebook#26285), we should always await the result of an `act` call so that microtasks have a chance to fire. This only affects the internal `act` that we use in our repo, for now. In the public `act` API, we don't yet require this; however, we effectively will for any update that triggers suspense once `use` lands. So we likely will start warning in an upcoming minor.
Similar to the rationale for `waitFor` (see facebook#26285), we should always await the result of an `act` call so that microtasks have a chance to fire. This only affects the internal `act` that we use in our repo, for now. In the public `act` API, we don't yet require this; however, we effectively will for any update that triggers suspense once `use` lands. So we likely will start warning in an upcoming minor.
Similar to the rationale for `waitFor` (see #26285), we should always await the result of an `act` call so that microtasks have a chance to fire. This only affects the internal `act` that we use in our repo, for now. In the public `act` API, we don't yet require this; however, we effectively will for any update that triggers suspense once `use` lands. So we likely will start warning in an upcoming minor.
Over the years, we've gradually aligned on a set of best practices for for testing concurrent React features in this repo. The default in most cases is to use
act, the same as you would do when testing a real React app. However, because we're testing React itself, as opposed to an app that uses React, our internal tests sometimes need to make assertions on intermediate states thatactintentionally disallows.For those cases, we built a custom set of Jest assertion matchers that provide greater control over the concurrent work queue. It works by mocking the Scheduler package. (When we eventually migrate to using native postTask, it would probably work by stubbing that instead.)
A problem with these helpers that we recently discovered is, because they are synchronous function calls, they aren't sufficient if the work you need to flush is scheduled in a microtask — we don't control the microtask queue, and can't mock it.
actaddresses this problem by encouraging you to await the result of theactcall. (It's not currently required to await, but in future versions of React it likely will be.) It will then continue flushing work until both the microtask queue and the Scheduler queue is exhausted.We can follow a similar strategy for our custom test helpers, by replacing the current set of synchronous helpers with a corresponding set of async ones:
expect(Scheduler).toFlushAndYield(log)->await waitForAll(log)expect(Scheduler).toFlushAndYieldThrough(log)->await waitFor(log)expect(Scheduler).toFlushUntilNextPaint(log)->await waitForPaint(log)These APIs are inspired by the existing best practice for writing e2e React tests. Rather than mock all task queues, in an e2e test you set up a timer loop and wait for the UI to match an expecte condition. Although we are mocking some of the task queues in our tests, the general principle still holds: it makes it less likely that our tests will diverge from real world behavior in an actual browser.
In this commit, I've implemented the new testing helpers and converted one of the Suspense tests to use them. In subsequent steps, I'll codemod the rest of our test suite.