-
Notifications
You must be signed in to change notification settings - Fork 425
Add React/Jest testing infrastructure #1118
Add React/Jest testing infrastructure #1118
Conversation
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.
Looks OK to me aside from style nits and some things I don't fully understand.
I reviewed bcb80bd.
scripts/test-react.js
Outdated
let React = require("react"); | ||
let ReactTestRenderer = require("react-test-renderer"); | ||
let { normalize } = require("../lib/utils/json.js"); | ||
/* eslint-disable no-undef */ |
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.
Doesn't this disable them globally for the whole file? I think you want // eslint-disable-next-line
or something like that.
scripts/test-react.js
Outdated
let code = `(function(){${source}})()`; | ||
let serialized = prepackSources([{ filePath: "", fileContents: code, sourceMapContents: "" }], prepackOptions); | ||
// add the React require back in, as we've removed it with our Prepack mock | ||
let compiledSource = serialized.code.replace(/_\$[\d].React/, "React = require('react')"); |
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.
Let's add a comment explaining what the regex is trying to find.
scripts/test-react.js
Outdated
let resultB = getTrials(rendererB, B); | ||
|
||
// // the test has returned many values for us to check | ||
if (Array.isArray(resultA) && Array.isArray(resultA[0])) { |
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.
Can we restructure this so that instead of repeating similar logic in both branches, we first wrap resultA
and resultB
into an array if necessary and then do the assertions unconditionally?
scripts/test-react.js
Outdated
// with a false-negative error | ||
global.console.error = () => {}; | ||
await runTest(directory, "return-undefined.js"); | ||
global.console.error = originalConsoleError; |
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.
Maybe try { } finally { }
so this is isolated from other tests in case it fails?
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 thought this wasn't actual JS error that is occurring, rather it's console.error
being fired?
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.
If runTest
fails for some reason, global.console.error
won't be restored.
If you do
global.console.error = ...
try {
doStuff
} finally {
global.console.error = original
}
it is restored no matter what.
src/intrinsics/react/global.js
Outdated
let requireValue = AbstractValue.createFromTemplate( | ||
realm, | ||
buildExpressionTemplate("require"), | ||
(type: any), // Flow complains that type of Value isn't compatible with class Value :/ |
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.
Would be nice to understand why.
src/utils/json.js
Outdated
type JSON = { [key: string]: JSONValue }; | ||
|
||
// this will mutate the original JSON object | ||
export function normalize(node: JSON) { |
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 utility name sounds very generic but it's not very clear what this does.
What is normalized JSON looking like?
Is it useful in any other places than this test infra?
if (this.__createReactMock) { | ||
var React = __createReactMock(); | ||
} else { | ||
var React = require('react'); |
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: indent
|
||
function App() { | ||
return ( | ||
<MaybeShow show={true}> |
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: indent
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.
(and in most places below)
superclass.apply(this, arguments); | ||
} | ||
|
||
if ( superclass ) StatefulComponent.__proto__ = superclass; |
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: style is odd, can we use the same code style as in code?
return StatefulComponent; | ||
}(React.Component)); | ||
|
||
this.StatefulComponent = StatefulComponent; |
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.
Why do we need this.*
assignment here but not in other places?
The move from |
src/intrinsics/prepack/global.js
Outdated
@@ -155,6 +155,39 @@ export default function(realm: Realm): void { | |||
configurable: true, | |||
}); | |||
|
|||
if (realm.react.enabled) { | |||
global.$DefineOwnProperty("__reactComponentRoots", { |
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.
@cblappert This is unrelated to this PR. I saw that this pattern was added in the additional functions one, but why do we make these a public object instead of just an internal Map? Seems weird for the compiled script to be able to reason about this and mutate this.
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.
@trueadm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
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.
@trueadm has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
Summary: Release note: Adds experimental React functional component folding optimizations This PR is stacked upon PRs #1118 and #1117. Thus, those PRs should be merged before this PR is merged to reduce noise in the diff. This PR adds a new React Reconciler into Prepack's serialization process, so that React components trees can be folded/inlined into a single component at build time. To fold a component tree, it must be explicitly done via `__registerReactComponentRoot(nameOfComponent)`. This PR only attempts to fold React functional components, not React ES2015 class components (that will come in another PR at a later date). Furthermore, the `props` parameter on a root component must contain Flow type annotations (otherwise we will have no idea what the values might be). Support flow `propTypes` might also be an addition, but not for this PR. If the reconciler comes across a component that it cannot fold/inline, it will "bail-out" and try and continue the process without that particular component being folded into the tree. An example of how this all works (input): ```jsx function App(props: {title: string}) { return ( <div> <ChildComponent title={props.title} /> </div> ); } function ChildComponent(props) { return ( <span> <SubChildComponent {...props} /> </span> ); } function SubChildComponent(props) { return <span>{props.title.toString()}</span> } __registerReactComponentRoot(App); global.App = App; ``` Output: ```jsx (function () { "use strict"; var _$1 = this; var _0 = function (props) { var _$0 = props.title; return <div><span><span>{_$0}</span></span></div>; }; _$1.App = _0; }).call(this); ``` Closes #1120 Differential Revision: D6237333 Pulled By: trueadm fbshipit-source-id: b58c7d8979ca79a766bb2ee2eb01a380d37c3101
Release note: none
This PR adds the React/Jest testing infrastructure to Prepack and is stacked on the PR #1117. Thus, that PR should be merged before this PR is merged to reduce noise in the diff.
This will allow us to test various runtime outputs of React 16 when running original source vs Prepacked source to see if there are any issues/differences that might have an impact on applications. The primary reason for this is to track regressions for the component folding PRs that will land in the future.
Please note, this PR does not contain any reconciler changes, thus
__registerReactComponentRoot(App);
has been commented out of tests to ensure they don't prematurely fail. A follow up PR will enable them once the other React functional component folding PRs get landed.This PR also adds some mock React globals to be used within tests (and maybe to be further integrated into folding at a future point too). The mocks include
React.Component
andReact.cloneElement
for now.Furthermore, a
utils/json.js
utility file exists to help normalize the results from the React test renderer so that adjacent JSON text nodes get merged, which is something that may exist because of how the reconciler (once the PR lands) handles inlining of child nodes.The command to run React tests is
yarn test-react
.