Add React/Jest testing infrastructure

Summary:
Release note: none

This PR adds the React/Jest testing infrastructure to Prepack and is stacked on the PR https://github.com/facebook/prepack/pull/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` and `React.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`.
Closes https://github.com/facebook/prepack/pull/1118

Reviewed By: cblappert

Differential Revision: D6208263

Pulled By: trueadm

fbshipit-source-id: d54f3b0e1cc3240e1f142d3da08bc279e4153889
This commit is contained in:
Dominic Gannaway 2017-11-02 04:28:37 -07:00 committed by Facebook Github Bot
parent 24c363fc8d
commit e63daa5585
27 changed files with 1902 additions and 83 deletions

View File

@ -41,7 +41,8 @@
"test-error-handler-with-coverage": "./node_modules/.bin/istanbul cover ./lib/test-error-handler.js --dir coverage.error && ./node_modules/.bin/remap-istanbul -i coverage.error/coverage.json -o coverage-sourcemapped.error -t html",
"test-node-cli-mode": "bash < scripts/test-node-cli-mode.sh",
"test-std-in": "bash < scripts/test-std-in.sh",
"test": "yarn test-residual && yarn test-serializer && yarn test-sourcemaps && yarn test-error-handler && yarn test-std-in && yarn test-test262 && yarn test-internal",
"test-react": "jest scripts/test-react",
"test": "yarn test-residual && yarn test-serializer && yarn test-sourcemaps && yarn test-error-handler && yarn test-std-in && yarn test-test262 && yarn test-internal && yarn test-react",
"test-coverage-most": "./node_modules/.bin/istanbul --stack_size=10000 --max_old_space_size=16384 cover ./lib/multi-runner.js --dir coverage.most && ./node_modules/.bin/remap-istanbul -i coverage.most/coverage.json -o coverage-sourcemapped -t html",
"test-all-coverage": "./node_modules/.bin/istanbul --stack_size=10000 --max_old_space_size=16384 cover ./lib/multi-runner.js --dir coverage.most && ./node_modules/.bin/istanbul --stack_size=10000 --max_old_space_size=16384 cover -- ./lib/test262-runner.js --timeout 50 --singleThreaded && ./node_modules/.bin/remap-istanbul -i coverage/coverage.json -i coverage.most/coverage.json -o coverage-sourcemapped -t html",
"repl": "node lib/repl-cli.js",
@ -88,12 +89,15 @@
"graceful-fs": "^4.1.11",
"invariant": "^2.2.0",
"istanbul": "^0.4.5",
"jest": "^21.2.1",
"js-yaml": "^3.6.1",
"jsdom": "^9.2.1",
"kcheck": "^2.0.0",
"madge": "^1.6.0",
"minimist": "^1.2.0",
"prettier": "1.5.2",
"react": "^16.0.0",
"react-test-renderer": "^16.0.0",
"remap-istanbul": "^0.9.1",
"source-map-support": "^0.4.6",
"uglify-js": "^2.6.2",
@ -106,5 +110,10 @@
"prepack"
],
"license": "BSD-3-Clause",
"author": "Facebook"
"author": "Facebook",
"jest": {
"testMatch": [
"**/scripts/test-react.js"
]
}
}

176
scripts/test-react.js Normal file
View File

@ -0,0 +1,176 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow */
let fs = require("fs");
let path = require("path");
let { prepackSources } = require("../lib/prepack-node.js");
let babel = require("babel-core");
let React = require("react");
let ReactTestRenderer = require("react-test-renderer");
let { mergeAdacentJSONTextNodes } = require("../lib/utils/json.js");
/* eslint-disable no-undef */
let { expect, describe, it } = global;
let reactTestRoot = path.join(__dirname, "../test/react/");
let prepackOptions = {
compatibility: "react-mocks",
errorHandler: diag => "Fail",
internalDebug: true,
serialize: true,
uniqueSuffix: "",
maxStackDepth: 100,
reactEnabled: true,
inlineExpressions: true,
omitInvariants: true,
};
function compileSourceWithPrepack(source) {
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
// the regex checks for any Prepack variable that matches "_$**any digit**.React"
let compiledSource = serialized.code.replace(/_\$[\d].React/, "React = require('react')");
if (serialized == null || serialized.reactStatistics == null) {
throw new Error("React test runner failed during serialization");
}
return {
// replace the code to put back the generator (Prepack doesn't serialize them yet)
compiledSource,
statistics: serialized.reactStatistics,
};
}
function runSource(source) {
let codeAfterBabel = babel.transform(source, {
presets: ["babel-preset-react"],
plugins: ["transform-object-rest-spread"],
}).code;
/* eslint-disable no-new-func */
let fn = new Function("require", "module", codeAfterBabel);
let moduleShim = { exports: null };
let requireShim = name => {
switch (name) {
case "react":
return React;
default:
throw new Error(`Unrecognized import: "${name}".`);
}
};
let global = {
require: requireShim,
module: moduleShim,
Object,
String,
};
try {
// $FlowFixMe flow doesn't new Function
fn.call(global, requireShim, moduleShim);
} catch (e) {
console.log(codeAfterBabel);
throw e;
}
return moduleShim.exports;
}
async function runTest(directory, name) {
let source = fs.readFileSync(path.join(reactTestRoot, directory, name)).toString();
let { compiledSource } = compileSourceWithPrepack(source);
let A = runSource(source);
expect(typeof A).toBe("function");
let B = runSource(compiledSource);
expect(typeof B).toBe("function");
let rendererA = ReactTestRenderer.create(null);
let rendererB = ReactTestRenderer.create(null);
if (A == null || B == null) {
throw new Error("React test runner issue");
}
// // Use the original version of the test in case transforming messes it up.
let { getTrials } = A;
// // Run tests that assert the rendered output matches.
let resultA = getTrials(rendererA, A);
let resultB = getTrials(rendererB, B);
// // the test has returned many values for us to check
for (let i = 0; i < resultA.length; i++) {
let [nameA, valueA] = resultA[i];
let [nameB, valueB] = resultB[i];
expect(mergeAdacentJSONTextNodes(valueB)).toEqual(mergeAdacentJSONTextNodes(valueA));
expect(nameB).toEqual(nameA);
}
}
// Jest tests
let originalConsoleError = console.error;
describe("Test React", () => {
describe("Functional component folding", () => {
let directory = "functional-components";
it("Simple", async () => {
await runTest(directory, "simple.js");
});
it("Simple children", async () => {
await runTest(directory, "simple-children.js");
});
it("Simple refs", async () => {
await runTest(directory, "simple-refs.js");
});
it("Conditional", async () => {
await runTest(directory, "conditional.js");
});
it("Key nesting", async () => {
await runTest(directory, "key-nesting.js");
});
it("Key change", async () => {
await runTest(directory, "key-change.js");
});
it("Component type change", async () => {
await runTest(directory, "type-change.js");
});
it("Dynamic props", async () => {
await runTest(directory, "dynamic-props.js");
});
it("Dynamic context", async () => {
await runTest(directory, "dynamic-context.js");
});
it("React.cloneElement", async () => {
await runTest(directory, "clone-element.js");
});
it("Return text", async () => {
await runTest(directory, "return-text.js");
});
it("Return undefined", async () => {
// this test will cause a React console.error to show
// we monkey patch it to stop it polluting the test output
// with a false-negative error
global.console.error = () => {};
try {
await runTest(directory, "return-undefined.js");
} finally {
global.console.error = originalConsoleError;
}
});
});
});

View File

@ -13,12 +13,17 @@ import type { Realm } from "./realm.js";
import initializePrepackGlobals from "./intrinsics/prepack/global.js";
import initializeDOMGlobals from "./intrinsics/dom/global.js";
import initializeReactNativeGlobals from "./intrinsics/react-native/global.js";
import initializeReactMocks from "./intrinsics/react-mocks/global.js";
export default function(realm: Realm): Realm {
initializePrepackGlobals(realm);
if (realm.isCompatibleWith("browser")) {
initializeDOMGlobals(realm);
}
if (realm.isCompatibleWith("react-mocks")) {
initializeDOMGlobals(realm);
initializeReactMocks(realm);
}
if (realm.isCompatibleWith(realm.MOBILE_JSC_VERSION)) {
initializeReactNativeGlobals(realm);
}

74
src/intrinsics/react-mocks/global.js vendored Normal file
View File

@ -0,0 +1,74 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow */
import type { Realm } from "../../realm.js";
import { AbstractValue, NativeFunctionValue, ObjectValue, Value } from "../../values/index.js";
import { ObjectCreate, CreateDataPropertyOrThrow, GetValue } from "../../methods/index.js";
import buildExpressionTemplate from "../../utils/builder.js";
import { createMockReactComponent, createMockReactCloneElement } from "./mocks.js";
export default function(realm: Realm): void {
let global = realm.$GlobalObject;
// module.exports support
let exportsValue = ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
exportsValue.intrinsicName = "exports";
let moduleValue = ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
moduleValue.intrinsicName = "module";
moduleValue.$Set("exports", exportsValue, moduleValue);
global.$DefineOwnProperty("module", {
value: moduleValue,
writable: true,
enumerable: false,
configurable: true,
});
// require("SomeModule") support (makes them abstract)
let type = Value.getTypeFromName("function");
let requireValue = AbstractValue.createFromTemplate(
realm,
buildExpressionTemplate("require"),
((type: any): typeof Value),
[],
"require"
);
requireValue.intrinsicName = "require";
global.$DefineOwnProperty("require", {
value: requireValue,
writable: true,
enumerable: false,
configurable: true,
});
// apply React mock (for now just React.Component)
global.$DefineOwnProperty("__createReactMock", {
value: new NativeFunctionValue(realm, "global.__createReactMock", "__createReactMock", 0, (context, []) => {
// React object
let reactValue = ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
reactValue.intrinsicName = "React";
// React.Component
let reactComponent = GetValue(realm, realm.$GlobalEnv.evaluate(createMockReactComponent(), false));
reactComponent.intrinsicName = "React.Component";
let prototypeValue = ((reactComponent: any): ObjectValue).properties.get("prototype");
if (prototypeValue && prototypeValue.descriptor) {
((prototypeValue.descriptor.value: any): Value).intrinsicName = `React.Component.prototype`;
}
CreateDataPropertyOrThrow(realm, reactValue, "Component", reactComponent);
// React.cloneElement
let reactCloneElement = GetValue(realm, realm.$GlobalEnv.evaluate(createMockReactCloneElement(), false));
reactCloneElement.intrinsicName = "React.cloneElement";
CreateDataPropertyOrThrow(realm, reactValue, "cloneElement", reactCloneElement);
return reactValue;
}),
writable: true,
enumerable: false,
configurable: true,
});
}

97
src/intrinsics/react-mocks/mocks.js vendored Normal file
View File

@ -0,0 +1,97 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow */
import { parseExpression } from "babylon";
// this a mock of React.Component, to be used for tests
export function createMockReactComponent() {
let componentCode = `
class Component {
constructor(props, context) {
this.props = props || {};
this.context = context || {};
this.refs = {};
this.state = {};
}
getChildContext() {}
}
`;
return parseExpression(componentCode, { plugins: ["flow"] });
}
// this a mock of React.Component, to be used for tests
export function createMockReactCloneElement() {
let cloneElementCode = `
function cloneElement(element, config, children) {
var propName;
var RESERVED_PROPS = {
key: true,
ref: true,
__self: true,
__source: true,
};
var hasOwnProperty = Object.prototype.hasOwnProperty;
var props = Object.assign({}, element.props);
var key = element.key;
var ref = element.ref;
var self = element._self;
var source = element._source;
var owner = element._owner;
if (config != null) {
if (config.ref !== undefined) {
// owner = ReactCurrentOwner.current;
}
if (config.key !== undefined) {
key = '' + config.key;
}
var defaultProps;
if (element.type && element.type.defaultProps) {
defaultProps = element.type.defaultProps;
}
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
if (config[propName] === undefined && defaultProps !== undefined) {
// Resolve default props
props[propName] = defaultProps[propName];
} else {
props[propName] = config[propName];
}
}
}
}
var childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
return {
$$typeof: element.$$typeof,
type: element.type,
key: key,
ref: ref,
props: props,
_owner: owner,
};
}
`;
return parseExpression(cloneElementCode, { plugins: ["flow"] });
}

View File

@ -11,7 +11,7 @@
import type { ErrorHandler } from "./errors.js";
export type Compatibility = "browser" | "jsc-600-1-4-17" | "node-source-maps" | "node-cli";
export type Compatibility = "browser" | "jsc-600-1-4-17" | "node-source-maps" | "node-cli" | "react-mocks";
export const CompatibilityValues = ["browser", "jsc-600-1-4-17", "node-source-maps", "node-cli"];
export type RealmOptions = {

View File

@ -20,7 +20,7 @@ import traverseFast from "../utils/traverse-fast.js";
import { stripFlowTypeAnnotations } from "../flow/utils.js";
import invariant from "../invariant.js";
import type { SerializerOptions } from "../options.js";
import { TimingStatistics, SerializerStatistics } from "./types.js";
import { TimingStatistics, SerializerStatistics, ReactStatistics } from "./types.js";
import type { ReactSerializerState } from "./types.js";
import { Functions } from "./functions.js";
import { Logger } from "./logger.js";
@ -113,7 +113,9 @@ export class Serializer {
if (this.logger.hasErrors()) return undefined;
this.modules.resolveInitializedModules();
this.functions.checkThatFunctionsAreIndependent();
let reactStatistics = null;
if (this.realm.react.enabled) {
reactStatistics = new ReactStatistics();
this.functions.checkReactRootComponents(this.react);
}
@ -199,6 +201,7 @@ export class Serializer {
return {
code: generated.code,
map: generated.map,
reactStatistics,
statistics: residualHeapSerializer.statistics,
timingStats: timingStats,
};

View File

@ -120,6 +120,15 @@ export class TimingStatistics {
serializePassTime: number;
}
export class ReactStatistics {
constructor() {
this.optimizedTrees = 0;
this.inlinedComponents = 0;
}
optimizedTrees: number;
inlinedComponents: number;
}
export class SerializerStatistics {
constructor() {
this.objects = 0;

52
src/utils/json.js Normal file
View File

@ -0,0 +1,52 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow */
type JSONValue = Array<JSONValue> | string | number | JSON;
type JSON = { [key: string]: JSONValue };
// this will mutate the original JSON object
export function mergeAdacentJSONTextNodes(node: JSON) {
// we merge adjacent text nodes
if (Array.isArray(node)) {
// we create a new array rather than mutating the original
let arr = [];
let length = node.length;
let concatString = null;
let i = -1;
while (i++ < length) {
let child = node[i];
if (typeof child === "string" || typeof child === "number") {
if (concatString !== null) {
concatString += child;
} else {
concatString = child;
}
} else if (typeof child === "object" && child !== null) {
if (concatString !== null) {
arr.push(concatString);
concatString = null;
}
arr.push(mergeAdacentJSONTextNodes(child));
}
}
if (concatString !== null) {
arr.push(concatString);
}
return arr;
} else {
for (let key in node) {
let value = node[key];
if (typeof value === "object" && value !== null) {
node[key] = mergeAdacentJSONTextNodes(((value: any): JSON));
}
}
}
return node;
}

View File

@ -0,0 +1,47 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
function MaybeShow(props) {
if (props.show) {
return props.children;
}
return null;
}
function Override(props) {
var child = props.children;
var shouldShow = props.overrideShow;
return React.cloneElement(child, {
show: shouldShow
});
}
function App(props/*: {show: boolean}*/) {
return (
<Override overrideShow={props.show}>
<MaybeShow show={true}>
<h1>Hi</h1>
</MaybeShow>
</Override>
);
}
App.getTrials = function(renderer, Root) {
let results = [];
renderer.update(<Root show={true} />);
results.push(['clone element (true)', renderer.toJSON()]);
renderer.update(<Root show={false} />);
results.push(['clone element (false)', renderer.toJSON()]);
return results;
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,32 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
function MaybeShow(props) {
if (props.show) {
return props.children;
}
return null;
}
function App() {
return (
<MaybeShow show={true}>
<h1>Hi</h1>
</MaybeShow>
);
}
App.getTrials = function(renderer, Root) {
renderer.update(<Root />);
return [['conditional render', renderer.toJSON()]];
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,56 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
function SubChild(props, context) {
return <span>The context title is: {context.title}</span>;
}
function Child(props: any, context/*: {title: string}*/) {
return <span><SubChild /></span>;
}
// we can't use ES2015 classes in Prepack yet (they don't serialize)
// so we have to use ES5 instead
var StatefulComponent = (function (superclass) {
function StatefulComponent () {
superclass.apply(this, arguments);
}
if ( superclass ) {
StatefulComponent.__proto__ = superclass;
}
StatefulComponent.prototype = Object.create( superclass && superclass.prototype );
StatefulComponent.prototype.constructor = StatefulComponent;
StatefulComponent.prototype.getChildContext = function getChildContext () {
return {
title: "Hello world!",
}
};
StatefulComponent.prototype.render = function render () {
return <Child />;
};
StatefulComponent.childContextTypes = {
title: () => {},
};
return StatefulComponent;
}(React.Component));
function App() {
return <StatefulComponent />;
}
App.getTrials = function(renderer, Root) {
renderer.update(<Root />);
return [['render with dynamic context access', renderer.toJSON()]];
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,25 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
function Fn(props) {
return <div>Hello {props[props.dynamicKey]}</div>;
}
function App(props/*: {dynamicKey: string}*/) {
return <Fn foo="World" dynamicKey={props.dynamicKey} />;
}
App.getTrials = function(renderer, Root) {
renderer.update(<Root dynamicKey="foo" />);
return [['render with dynamic prop access', renderer.toJSON()]];
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,74 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
// we can't use ES2015 classes in Prepack yet (they don't serialize)
// so we have to use ES5 instead
var Stateful = (function (superclass) {
function Stateful () {
superclass.apply(this, arguments);
this.state = { updated: false };
}
if ( superclass ) {
Stateful.__proto__ = superclass;
}
Stateful.prototype = Object.create( superclass && superclass.prototype );
Stateful.prototype.constructor = Stateful;
Stateful.prototype.componentWillReceiveProps = function componentWillReceiveProps() {
this.setState({ updated: true });
}
Stateful.prototype.render = function render () {
return (
<div>
{this.props.children}
(is update: {String(this.state.updated)})
</div>
);
};
return Stateful;
}(React.Component));
function App(props/*: {switch: boolean}*/) {
if (props.switch) {
return (
<div>
<Stateful key='hi'>Hi</Stateful>
</div>
);
}
return (
<div>
<Stateful key='bye'>Bye</Stateful>
</div>
);
}
App.getTrials = function(renderer, Root) {
let results = [];
renderer.update(<Root switch={false} />);
results.push(['mount', renderer.toJSON()]);
renderer.update(<Root switch={false} />);
results.push(['update with same key', renderer.toJSON()]);
renderer.update(<Root switch={true} />);
results.push(['update with different key', renderer.toJSON()]);
renderer.update(<Root switch={true} />);
results.push(['update with same key (again)', renderer.toJSON()]);
renderer.update(<Root switch={false} />);
results.push(['update with different key (again)', renderer.toJSON()]);
return results;
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,81 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
// we can't use ES2015 classes in Prepack yet (they don't serialize)
// so we have to use ES5 instead
var Stateful = (function (superclass) {
function Stateful () {
superclass.apply(this, arguments);
this.state = { updated: false };
}
if ( superclass ) {
Stateful.__proto__ = superclass;
}
Stateful.prototype = Object.create( superclass && superclass.prototype );
Stateful.prototype.constructor = Stateful;
Stateful.prototype.componentWillReceiveProps = function componentWillReceiveProps() {
this.setState({ updated: true });
}
Stateful.prototype.render = function render () {
return (
<div>
(is update: {String(this.state.updated)})
</div>
);
};
return Stateful;
}(React.Component));
function MessagePane() {
return <div key='ha'><Stateful /></div>;
}
function SettingsPane() {
return <div key='ha'><Stateful /></div>;
}
function App(props/*: {switch: boolean}*/) {
if (props.switch) {
return (
<div>
<MessagePane />
</div>
);
}
return (
<div>
<SettingsPane />
</div>
);
}
App.getTrials = function(renderer, Root) {
let results = [];
renderer.update(<Root switch={false} />);
results.push(['mount', renderer.toJSON()]);
renderer.update(<Root switch={false} />);
results.push(['update with same type', renderer.toJSON()]);
renderer.update(<Root switch={true} />);
results.push(['update with different type', renderer.toJSON()]);
renderer.update(<Root switch={true} />);
results.push(['update with same type (again)', renderer.toJSON()]);
renderer.update(<Root switch={false} />);
results.push(['update with different type (again)', renderer.toJSON()]);
return results;
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,32 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
function A(props) {
return 'Hello, ';
}
function B(props) {
return 'world!';
}
function App() {
return [
<A key="1" />,
<B key="2" />,
];
}
App.getTrials = function(renderer, Root) {
renderer.update(<Root />);
return [['render text', renderer.toJSON()]];
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,33 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
function A() {
}
function App() {
return (
<div>
<A />
</div>
);
}
App.getTrials = function(renderer, Root) {
let didError = false;
try {
renderer.update(<Root />);
} catch (err) {
didError = true;
}
return [['error rendering', didError]];
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,30 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
function A(props) {
return props.children;
}
function App(props: any) {
return (
<A>
<A>
Hi
</A>
</A>
);
}
App.getTrials = function(renderer, Root) {
renderer.update(<Root />);
return [['simple children', renderer.toJSON()]];
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,44 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
let refB = false;
function A(foo) {
return (
<div>
<span className="findMe" ref={foo.rootRef} />,
<span ref={() => refB = true} />,
</div>
);
}
function App({rootRef}/*: {rootRef: Function}*/) {
return (
<div>
<A rootRef={rootRef} />
</div>
);
}
App.getTrials = function(renderer, Root) {
let refA = false;
let rootRef = () => {
refA = true;
};
renderer.update(<Root rootRef={rootRef} />);
let results = [];
results.push(['simple refs', renderer.toJSON()]);
results.push(['ref A', refA]);
results.push(['ref B', refB]);
return results;
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,40 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
function A(props) {
return <div>Hello {props.x}</div>;
}
function B() {
return <div>World</div>;
}
function C() {
return "!";
}
function App() {
return (
<div>
<A x={42} />
<B />
<C />
</div>
);
}
App.getTrials = function(renderer, Root) {
React.createElement("div")
renderer.update(<Root />);
return [['simple render', renderer.toJSON()]];
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

View File

@ -0,0 +1,82 @@
if (this.__createReactMock) {
var React = __createReactMock();
} else {
var React = require('react');
}
// we can't use ES2015 classes in Prepack yet (they don't serialize)
// so we have to use ES5 instead
var Stateful = (function (superclass) {
function Stateful () {
superclass.apply(this, arguments);
this.state = { updated: false };
}
if ( superclass ) {
Stateful.__proto__ = superclass;
}
Stateful.prototype = Object.create( superclass && superclass.prototype );
Stateful.prototype.constructor = Stateful;
Stateful.prototype.componentWillReceiveProps = function componentWillReceiveProps() {
this.setState({ updated: true });
}
Stateful.prototype.render = function render () {
return (
<div>
{this.props.children}
(is update: {String(this.state.updated)})
</div>
);
};
return Stateful;
}(React.Component));
function MessagePane() {
return <Stateful>Hi</Stateful>;
}
function SettingsPane() {
return <Stateful>Bye</Stateful>;
}
function App(props/*: {switch: boolean}*/) {
if (props.switch) {
return (
<div>
<MessagePane />
</div>
);
}
return (
<div>
<SettingsPane />
</div>
);
}
App.getTrials = function(renderer, Root) {
renderer.update(<Root switch={false} />);
let results = [];
results.push(['mount', renderer.toJSON()]);
renderer.update(<Root switch={false} />);
results.push(['update with same type', renderer.toJSON()]);
renderer.update(<Root switch={true} />);
results.push(['update with different type', renderer.toJSON()]);
renderer.update(<Root switch={true} />);
results.push(['update with same type (again)', renderer.toJSON()]);
renderer.update(<Root switch={false} />);
results.push(['update with different type (again)', renderer.toJSON()]);
return results;
};
if (this.__registerReactComponentRoot) {
// to be used when component folding is added in separate PR
// __registerReactComponentRoot(App);
}
module.exports = App;

976
yarn.lock

File diff suppressed because it is too large Load Diff