mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
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:
parent
24c363fc8d
commit
e63daa5585
13
package.json
13
package.json
@ -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
176
scripts/test-react.js
Normal 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;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
@ -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
74
src/intrinsics/react-mocks/global.js
vendored
Normal 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
97
src/intrinsics/react-mocks/mocks.js
vendored
Normal 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"] });
|
||||
}
|
@ -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 = {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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
52
src/utils/json.js
Normal 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;
|
||||
}
|
47
test/react/functional-components/clone-element.js
Normal file
47
test/react/functional-components/clone-element.js
Normal 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;
|
32
test/react/functional-components/conditional.js
Normal file
32
test/react/functional-components/conditional.js
Normal 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;
|
56
test/react/functional-components/dynamic-context.js
Normal file
56
test/react/functional-components/dynamic-context.js
Normal 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;
|
25
test/react/functional-components/dynamic-props.js
Normal file
25
test/react/functional-components/dynamic-props.js
Normal 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;
|
74
test/react/functional-components/key-change.js
Normal file
74
test/react/functional-components/key-change.js
Normal 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;
|
81
test/react/functional-components/key-nesting.js
Normal file
81
test/react/functional-components/key-nesting.js
Normal 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;
|
32
test/react/functional-components/return-text.js
Normal file
32
test/react/functional-components/return-text.js
Normal 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;
|
33
test/react/functional-components/return-undefined.js
Normal file
33
test/react/functional-components/return-undefined.js
Normal 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;
|
30
test/react/functional-components/simple-children.js
Normal file
30
test/react/functional-components/simple-children.js
Normal 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;
|
44
test/react/functional-components/simple-refs.js
Normal file
44
test/react/functional-components/simple-refs.js
Normal 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;
|
40
test/react/functional-components/simple.js
Normal file
40
test/react/functional-components/simple.js
Normal 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;
|
82
test/react/functional-components/type-change.js
Normal file
82
test/react/functional-components/type-change.js
Normal 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;
|
Loading…
Reference in New Issue
Block a user