Adds `getClientRects()` to fragment instances with a fixture test case.
`Element.getClientRect` returns a collection of `DOMRect`s (see example
of multiline span returning two `DOMRect` boxes).
`fragmentInstance.getClientRects` here flattens those collections into
an array of rects.
This implements `observeUsing(observer)` and `unobserverUsing(observer)`
on fragment instances. IntersectionObservers and ResizeObservers can be
passed to observe each host child of the fragment. This is the
equivalent to calling `observer.observe(child)` or
`observer.unobserve(child)` for each child target.
Just like the addEventListener, the observer is held on the fragment
instance and applied to any newly mounted child. So you can do things
like wrap a paginated list in a fragment and have each child
automatically observed as they commit in.
Unlike, the event listeners though, we don't `unobserve` when a child is
removed. If a removed child is currently intersecting, the observer
callback will be called when it is removed with an empty rect. This lets
you track all the currently intersecting elements by setting state from
the observer callback and either adding or removing them from your list
depending on the intersecting state. If you want to track the removal of
items offscreen, you'd have to maintain that state separately and append
intersecting data to it in the observer callback. This is what the
fixture example does.
There could be more convenient ways of managing the state of multiple
child intersections, but basic examples are able to be modeled with the
simple implementation. Let's see how the usage goes as we integrate this
with more advanced loggers and other features.
For now you can only attach one observer to an instance. This could
change based on usage but the fragments are composable and could be
stacked as one way to apply multiple observers to the same elements.
In practice, one pattern we expect to enable is more composable logging
such as
```javascript
function Feed({ items }) {
return (
<ImpressionLogger>
{items.map((item) => (
<FeedItem />
))}
</ImpressionLogger>
);
}
```
where `ImpressionLogger` would set up the IntersectionObserver using a
fragment ref with the required business logic and various components
could layer it wherever the logging is needed. Currently most callsites
use a hook form, which can require wiring up refs through the tree and
merging refs for multiple loggers.
Moving this to `internal-test-utils` so I can add helpers in the next PR
for:
- assertLogDev
- assertWarnDev
- assertErrorDev
Which will be exported from `internal-test-utils`. This isn't strictly
necessary, but it makes the factoring nicer, so internal-test-until
doesn't need to depend on `scripts/jest`.
If false, this ignores text comparison checks during hydration at the
risk of privacy safety.
Since React 18 we recreate the DOM starting from the nearest Suspense
boundary if any of the text content mismatches. This ensures that if we
have nodes that otherwise line up correctly such as if they're the same
type of Component but in a different order, then we don't accidentally
transfer state or attributes to the wrong one.
If we didn't do this e.g. attributes like image src might not line up
with the text. E.g. you might show the wrong profile picture with the
wrong name. However, the main reason we do this is because it's a
security/privacy concern if state from the original node can transfer to
the other one. For example if you start typing into a text field to
reply to a story but then it turns out that the hydration was in a
different order, you might submit that text into a different story than
you intended. Similarly, if you've already clicked an item and that gets
replayed using Action replaying or is synchronously force hydrated -
that click might end up applying to a different item in the list than
you intended. E.g. liking the wrong photo.
Unfortunately a common case where this happens is when Google Translate
is applied to a page. It'll always cause mismatches and recreate the
tree. Most of the time this wouldn't be visible to users because it'd
just recreate to the same thing and then translate again. It can affect
metrics that trace when this hydration happened though.
Meta can use this flag to decide if they favor this perf metric over the
risk to user privacy.
This is similar to the old enableClientRenderFallbackOnTextMismatch flag
except this flag doesn't patch up the text when there's a mismatch.
Because we don't have the patching anymore. The assumption is that it is
safe to ignore the safety concern because we assume it's a match and
therefore favoring not patching it will lead to better perf.
Stacked on #28476.
We used to `console.error` for every mismatch we found, up until the
error we threw for the hydration mismatch.
This changes it so that we build up a set of diffs up until we either
throw or complete hydrating the root/suspense boundary. If we throw, we
append the diff to the error message which gets passed to
onRecoverableError (which by default is also logged to console). If we
complete, we append it to a `console.error`.
Since we early abort when something throws, it effectively means that we
can only collect multiple diffs if there were preceding non-throwing
mismatches - i.e. only properties mismatched but tag name matched.
There can still be multiple logs if multiple siblings Suspense
boundaries all error hydrating but then they're separate errors
entirely.
We still log an extra line about something erroring but I think the goal
should be that it leads to a single recoverable or console.error.
This doesn't yet actually print the diff as part of this message. That's
in a follow up PR.
While Meta is still using legacy mode and we can't remove completely,
Meta is not using legacy hydration so we should be able to remove that.
This is just the first step. Once removed, we can vastly simplify the
DOMConfig for hydration.
This will have to be rebased when tests are upgraded.
## Overview
Adds support for `ReactDOMClient` for `ServerIntegration*` tests.
Also converts tests that pass without any other changes. Will follow up
with other PRs for more complex cases.
Prior to #26347, our internal `act` API (not the public API) behaved
differently depending on whether the scope function returned a promise
(i.e. was an async function), for historical reasons that no longer
apply. Now that this is fixed, I've codemodded all async act scopes that
don't contain an await to be sync.
No pressing motivation other than it looks nicer and the codemod was
easy. Might help avoid confusion for new contributors who see async act
scopes with nothing async inside and infer it must be like that for a
reason.
This is not a public API. We only use it for our internal tests, the
ones in this repo. Let's move it to this private package. Practically
speaking this will also let us use async/await in the implementation.
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.
The old version of prettier we were using didn't support the Flow syntax
to access properties in a type using `SomeType['prop']`. This updates
`prettier` and `rollup-plugin-prettier` to the latest versions.
I added the prettier config `arrowParens: "avoid"` to reduce the diff
size as the default has changed in Prettier 2.0. The largest amount of
changes comes from function expressions now having a space. This doesn't
have an option to preserve the old behavior, so we have to update this.
* Facebook -> Meta in copyright
rg --files | xargs sed -i 's#Copyright (c) Facebook, Inc. and its affiliates.#Copyright (c) Meta Platforms, Inc. and affiliates.#g'
* Manual tweaks
* I forgot to call onFatalError
I can't figure out how to write a test for this because it only happens
when there is a bug in React itself which would then be fixed if we found
it.
We're also covered by the protection of ReadableStream which doesn't leak
other errors to us.
* Abort requests if the reader cancels
No need to continue computing at this point.
* Abort requests if node streams get destroyed
This is if the downstream cancels is for example.
* Rename Node APIs for Parity with allReady
The "Complete" terminology is a little misleading because not everything
has been written yet. It's just "Ready" to be written now.
onShellReady
onShellError
onAllReady
* 'close' should be enough
* Deprecate renderToNodeStream
* Use renderToPipeableStream in tests instead of renderToNodeStream
This is the equivalent API. This means that we have way less test coverage
of this API but I feel like that's fine since it has a deprecation warning
in it and we have coverage on renderToString that is mostly the same.
* Fix textarea bug
The test changes revealed a bug with textarea. It happens because we
currently always insert trailing comment nodes. We should optimize that
away. However, we also don't really support complex children so we
should toString it anyway which is what partial renderer used to do.
* Update tests that assert number of nodes
These tests are unnecessarily specific about number of nodes.
I special case these, which these tests already do, because they're good
tests to test that the optimization actually works later when we do
fix it.
* Move internal version of act to shared module
No reason to have three different copies of this anymore.
I've left the the renderer-specific `act` entry points because legacy
mode tests need to also be wrapped in `batchedUpdates`. Next, I'll update
the tests to use `batchedUpdates` manually when needed.
* Migrates tests to use internal module directly
Instead of the `unstable_concurrentAct` exports. Now we can drop those
from the public builds.
I put it in the jest-react package since that's where we put our other
testing utilities (like `toFlushAndYield`). Not so much so it can be
consumed publicly (nobody uses that package except us), but so it works
with our build tests.
* Remove unused internal fields
These were used by the old act implementation. No longer needed.
* Use existing test warning filter for server tests
We have a warning filter for our internal tests to ignore warnings
that are too noisy or that we haven't removed from our test suite yet:
shouldIgnoreConsoleError.
Many of our server rendering tests don't use this filter, though,
because it has its own special of asserting warnings.
So I added the warning filter to the server tests, too.
* Deprecate ReactDOM.render and ReactDOM.hydrate
These are no longer supported in React 18. They are replaced by the
`createRoot` API.
The warning includes a link to documentation of the new API. Currently
it redirects to the corresponding working group post. Here's the PR to
set up the redirect: https://github.com/reactjs/reactjs.org/pull/3730
Many of our tests still use ReactDOM.render. We will need to gradually
migrate them over to createRoot.
In the meantime, I added the warnings to our internal warning filter.
This was used to implicitly hydrate if you call ReactDOM.render.
We've had a warning to explicitly use ReactDOM.hydrate(...) instead of
ReactDOM.render(...). We can now remove this from the generated markup.
(And avoid adding it to Fizz.)
This is a little strange to do now since we're trying hard to make the
root API work the same.
But if we kept it, we'd need to keep it in the generated output which adds
unnecessary bytes. It also risks people relying on it, in the Fizz world
where as this is an opportunity to create that clean state.
We could possibly only keep it in the old server rendering APIs but then
that creates an implicit dependency between which server API and which
client API that you use. Currently you can really mix and match either way.
In the next major release, we intend to drop support for using the `act`
testing helper in production. (It already fires a warning.) The
rationale is that, in order for `act` to work, you must either mock the
testing environment or add extra logic at runtime. Mocking the testing
environment isn't ideal because it requires extra set up for the user.
Extra logic at runtime is fine only in development mode — we don't want
to slow down the production builds.
Since most people only run their tests in development mode, dropping
support for production should be fine; if there's demand, we can add it
back later using a special testing build that is identical to the
production build except for the additional testing logic.
One blocker for removing production support is that we currently use
`act` to test React itself. We must test React in both development and
production modes.
So, the solution is to fork `act` into separate public and
internal implementations:
- *public implementation of `act`* – exposed to users, only works in
development mode, uses special runtime logic, does not support partial
rendering
- *internal implementation of `act`* – private, works in both
development and productionm modes, only used by the React Core test
suite, uses no special runtime logic, supports partial rendering (i.e.
`toFlushAndYieldThrough`)
The internal implementation should mostly match the public
implementation's behavior, but since it's a private API, it doesn't have
to match exactly. It works by mocking the test environment: it uses a
mock build of Scheduler to flush rendering tasks, and Jest's mock timers
to flush Suspense placeholders.
---
In this first commit, I've added the internal forks of `act` and
migrated our tests to use them. The public `act` implementation is
unaffected for now; I will leave refactoring/clean-up for a later step.
* Use gate pragma instead of if (__EXPERIMENTAL__)
* Fix stream error handling in tests
Added an error listener so that the tests fail within their Jest scope,
instead of crashing the whole process.
* Enable prefer-const rule
Stylistically I don't like this but Closure Compiler takes advantage of
this information.
* Auto-fix lints
* Manually fix the remaining callsites
* warn if passive effects get queued outside of an act() call
While the code itself isn't much (it adds the warning to mountEffect() and updateEffect() in ReactFiberHooks), it does change a lot of our tests. We follow a bad-ish pattern here, which is doing asserts inside act() scopes, but it makes sense for *us* because we're testing intermediate states, and we're manually flush/yield what we need in these tests.
This commit has one last failing test. Working on it.
* pass lint
* pass failing test, fixes another
- a test was failing in ReactDOMServerIntegrationHooks while testing an effect; the behaviour of yields was different from browser and server when wrapped with act(). further, because of how we initialized modules, act() around renders wasn't working corrrectly. solved by passing in ReactTestUtils in initModules, and checking on the finally yielded values in the specific test.
- in ReactUpdates, while testing an infinite recursion detection, the test needed to be wrapped in an act(), which would have caused the recusrsion error to throw. solived by rethrowing the error from inside the act().
* pass ReactDOMServerSuspense
* stray todo
* a better message, consistent with the state update one.
* Prevent javascript protocol URLs
* Just warn when disableJavaScriptURLs is false
This avoids a breaking change.
* Allow framesets
* Allow <html> to be used in integration tests
Full document renders requires server rendering so the client path
just uses the hydration path in this case to simplify writing these tests.
* Detect leading and intermediate characters and test mixed case
These are considered valid javascript urls by browser so they must be
included in the filter.
This is an exact match according to the spec but maybe we should include
a super set to be safer?
* Test updates to ensure we have coverage there too
* Fix toString invocation and Flow types
Right now we invoke toString twice when we hydrate (three times
with the flag off). Ideally we should only do it once even in this case
but the code structure doesn't really allow for that right now.
* s/itRejects/itRejectsRendering
* Dedupe warning and add the unsafe URL to the warning message
* Add test that fails if g is added to the sanitizer
This only affects the prod version since the warning is deduped anyway.
* Fix prod test
* Make option children a text content by default
fix#11911
* Apply requested changes
- Remove meaningless comments
- revert scripts/rollup/results.json
* remove empty row
* Update comment
* Add a simple unit-test
* [WIP: no flow] Pass through hostContext
* [WIP: no flow] Give better description for test
* Fixes
* Don't pass hostContext through
It ended up being more complicated than I thought.
* Also warn on hydration
* Warn about spying on the console
* Added suppress warning flag for spyOn(console)
* Nits
* Removed spy-on-console guard
* Fixed a potential source of false-positives in toWarnDev() matcher
Also updated (most of) ReactIncrementalErrorLogging-test.internal to use the new matcher
* Removed unused third param to spyOn
* Improved clarity of inline comments
* Removed unused normalizeCodeLocInfo() method
* Bump deps to Jest 22
* Prevent jsdom from logging intentionally thrown errors
This relies on our existing special field that we use to mute errors.
Perhaps, it would be better to instead rely on preventDefault() directly.
I outlined a possible strategy here: https://github.com/facebook/react/issues/11098#issuecomment-355032539
* Update snapshots
* Mock out a method called by ReactART that now throws
* Calling .click() no longer works, dispatch event instead
* Fix incorrect SVG element creation in test
* Render SVG elements inside <svg> to avoid extra warnings
* Fix range input test to use numeric value
* Fix creating SVG element in test
* Replace brittle test that relied on jsdom behavior
The test passed in jsdom due to its implementation details.
The original intention was to test the mutation method, but it was removed a while ago.
Following @nhunzaker's suggestion, I moved the tests to ReactDOMInput and adjusted them to not rely on implementation details.
* Add a workaround for the expected extra client-side warning
This is a bit ugly but it's just two places. I think we can live with this.
* Only warn once for mismatches caused by bad attribute casing
We used to warn both about bad casing and about a mismatch.
The mismatch warning was a bit confusing. We didn't know we warned twice because jsdom didn't faithfully emulate SVG.
This changes the behavior to only leave the warning about bad casing if that's what caused the mismatch.
It also adjusts the test to have an expectation that matches the real world behavior.
* Add an expected warning per comment in the same test
* Extract Jest config into a separate file
* Refactor Jest scripts directory structure
Introduces a more consistent naming scheme.
* Add yarn test-bundles and yarn test-prod-bundles
Only files ending with -test.public.js are opted in (so far we don't have any).
* Fix error decoding for production bundles
GCC seems to remove `new` from `new Error()` which broke our proxy.
* Build production version of react-noop-renderer
This lets us test more bundles.
* Switch to blacklist (exclude .private.js tests)
* Rename tests that are currently broken against bundles to *-test.internal.js
Some of these are using private APIs. Some have other issues.
* Add bundle tests to CI
* Split private and public ReactJSXElementValidator tests
* Remove internal deps from ReactServerRendering-test and make it public
* Only run tests directly in __tests__
This lets us share code between test files by placing them in __tests__/utils.
* Remove ExecutionEnvironment dependency from DOMServerIntegrationTest
It's not necessary since Stack.
* Split up ReactDOMServerIntegration into test suite and utilities
This enables us to further split it down. Good both for parallelization and extracting public parts.
* Split Fragment tests from other DOMServerIntegration tests
This enables them to opt other DOMServerIntegration tests into bundle testing.
* Split ReactDOMServerIntegration into different test files
It was way too slow to run all these in sequence.
* Don't reset the cache twice in DOMServerIntegration tests
We used to do this to simulate testing separate bundles.
But now we actually *do* test bundles. So there is no need for this, as it makes tests slower.
* Rename test-bundles* commands to test-build*
Also add test-prod-build as alias for test-build-prod because I keep messing them up.
* Use regenerator polyfill for react-noop
This fixes other issues and finally lets us run ReactNoop tests against a prod bundle.
* Run most Incremental tests against bundles
Now that GCC generator issue is fixed, we can do this.
I split ErrorLogging test separately because it does mocking. Other error handling tests don't need it.
* Update sizes
* Fix ReactMount test
* Enable ReactDOMComponent test
* Fix a warning issue uncovered by flat bundle testing
With flat bundles, we couldn't produce a good warning for <div onclick={}> on SSR
because it doesn't use the event system. However the issue was not visible in normal
Jest runs because the event plugins have been injected by the time the test ran.
To solve this, I am explicitly passing whether event system is available as an argument
to the hook. This makes the behavior consistent between source and bundle tests. Then
I change the tests to document the actual logic and _attempt_ to show a nice message
(e.g. we know for sure `onclick` is a bad event but we don't know the right name for it
on the server so we just say a generic message about camelCase naming convention).