[tests] Revive Forget e2e tests

--- 

Revives e2e test infra from #587. 

- All React component-like functions are compiled. 

- `yarn jest` runs each e2e test twice (forget and no forget) 

Github Actions is already running `yarn test`, which includes all jest tests 

``` 

Run yarn test 

yarn run v1.22.19 

$ yarn workspaces run test 

> babel-plugin-react-forget 

$ yarn jest && yarn snap:build && yarn snap 

$ tsc && jest 

PASS main src/__tests__/Result-test.ts 

PASS main src/__tests__/DisjointSet-test.ts 

PASS e2e with forget src/__tests__/e2e/hello.e2e.js 

PASS e2e no forget src/__tests__/e2e/hello.e2e.js 

Test Suites: 
[4](https://github.com/facebook/react-forget/actions/runs/5732016200/job/15534129231?pr=1881#step:8:5) 
passed, 4 total 

Tests:       23 passed, 23 total 

Snapshots:   11 passed, 11 total 

Time:        
6.1[5](https://github.com/facebook/react-forget/actions/runs/5732016200/job/15534129231?pr=1881#step:8:6)3 
s 

```
This commit is contained in:
Mofei Zhang
2023-08-02 17:30:55 -04:00
parent 2d8da0d32d
commit 2a5a8d552f
6 changed files with 208 additions and 25 deletions

View File

@@ -55,8 +55,8 @@
"glob": "^7.1.6",
"jest": "^29.0.3",
"jest-environment-jsdom": "^29.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^0.0.0-experimental-493f72b0a-20230727",
"react-dom": "^0.0.0-experimental-493f72b0a-20230727",
"rimraf": "^3.0.2",
"test262-harness": "^8.0.0",
"ts-jest": "^29.1.1",

View File

@@ -5,9 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/
const ReactForgetBabelPlugin = require("../../dist").BabelPlugin;
const babelJest = require("babel-jest");
const { readFileSync } = require("fs");
const { compile } = require("babel-plugin-react-forget");
const { jsx } = require("@babel/plugin-syntax-jsx");
const { execSync } = require("child_process");
module.exports = (useForget) => {
function createTransformer() {
@@ -17,18 +18,18 @@ module.exports = (useForget) => {
"@babel/preset-typescript",
{
plugins: [
"@babel/plugin-syntax-jsx",
...(useForget
useForget
? [
[
ReactForgetBabelPlugin,
{
// Jest hashes the babel config as a cache breaker.
cacheBreaker: readFileSync("dist/HASH", "utf8"),
},
],
ReactForgetFunctionTransform,
{
// Jest hashes the babel config as a cache breaker.
// (see https://github.com/jestjs/jest/blob/v29.6.2/packages/babel-jest/src/index.ts#L84)
cacheKey: execSync(
"yarn --silent --cwd ../.. hash packages/babel-plugin-react-forget/dist"
).toString(),
},
]
: []),
: "@babel/plugin-syntax-jsx",
],
},
"@babel/preset-react",
@@ -68,3 +69,68 @@ module.exports = (useForget) => {
createTransformer,
};
};
// Copied from react/scripts/babel/transform-forget.js
function isReactComponentLike(fn) {
let isReactComponent = false;
let hasNoUseForgetDirective = false;
// React components start with an upper case letter
if (fn.node.id.name[0].toUpperCase() !== fn.node.id.name[0]) {
return false;
}
fn.traverse({
DirectiveLiteral(path) {
if (path.node.value === "use no forget") {
hasNoUseForgetDirective = true;
}
},
JSX(path) {
// Is there is a JSX node created in the current function context?
if (path.scope.getFunctionParent()?.path.node === fn.node) {
isReactComponent = true;
}
},
CallExpression(path) {
// Is there hook usage?
if (
path.node.callee.type === "Identifier" &&
path.node.callee.name.startsWith("use")
) {
isReactComponent = true;
}
},
});
if (hasNoUseForgetDirective) {
return false;
}
return isReactComponent;
}
function ReactForgetFunctionTransform() {
const compiledFns = new Set();
return {
name: "react-forget-e2e",
inherits: jsx,
visitor: {
FunctionDeclaration(fn) {
if (compiledFns.has(fn.node)) {
return;
}
if (!isReactComponentLike(fn)) {
return;
}
const compiled = compile(fn);
compiledFns.add(compiled);
fn.replaceWith(compiled);
},
},
};
}

View File

@@ -6,6 +6,9 @@
*/
const React = require("react");
const ForgetRuntime = require("../../packages/react-forget-runtime");
React.unstable_ForgetRuntime = ForgetRuntime;
React.unstable_useMemoCache = ForgetRuntime.unstable_useMemoCache;
// Our e2e babel transform currently only compiles functions, not programs.
// As a result, our e2e transpiled code does not contain an import for `useMemoCache`
// This is a hack.
React.useMemoCache = React.unstable_useMemoCache;
globalThis.useMemoCache = React.unstable_useMemoCache;

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const logs = [];
export function log(message) {
logs.push(message);
}
export function expectLogsAndClear(expected) {
expect(logs).toEqual(expected);
logs.length = 0;
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import * as React from "react";
import { render } from "@testing-library/react";
import { expectLogsAndClear, log } from "./expectLogs";
function Hello({ name }) {
const items = [1, 2, 3].map((item) => {
log(`recomputing ${item}`);
return <div key={item}>Item {item}</div>;
});
return (
<div>
Hello<b>{name}</b>
{items}
</div>
);
}
test("hello", () => {
const { asFragment, rerender } = render(<Hello name="World" />);
expect(asFragment()).toMatchInlineSnapshot(`
<DocumentFragment>
<div>
Hello
<b>
World
</b>
<div>
Item 1
</div>
<div>
Item 2
</div>
<div>
Item 3
</div>
</div>
</DocumentFragment>
`);
expectLogsAndClear(["recomputing 1", "recomputing 2", "recomputing 3"]);
rerender(<Hello name="Universe" />);
expect(asFragment()).toMatchInlineSnapshot(`
<DocumentFragment>
<div>
Hello
<b>
Universe
</b>
<div>
Item 1
</div>
<div>
Item 2
</div>
<div>
Item 3
</div>
</div>
</DocumentFragment>
`);
expectLogsAndClear(
__FORGET__ ? [] : ["recomputing 1", "recomputing 2", "recomputing 3"]
);
});

View File

@@ -4208,9 +4208,9 @@ caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.300014
integrity sha512-2efF8SAZwgAX1FJr87KWhvuJxnGJKOnctQa8xLOskAXNXq8oiuqgl6u1kk3fFpsp3GgvzlRjiK1sl63hNtFADw==
caniuse-lite@^1.0.30001503:
version "1.0.30001517"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8"
integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==
version "1.0.30001518"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001518.tgz#b3ca93904cb4699c01218246c4d77a71dbe97150"
integrity sha512-rup09/e3I0BKjncL+FesTayKtPrdwKhUufQFd3riFw1hHg8JmIFoInYfB102cFcY/pPgGmdyl/iy+jgiDi2vdA==
caseless@~0.12.0:
version "0.12.0"
@@ -4979,9 +4979,9 @@ electron-to-chromium@^1.4.411:
integrity sha512-1KnpDTS9onwAfMzW50LcpNtyOkMyjd/OLoD2Kx/DDITZqgNYixY71XNszPHNxyQQ/Brh+FDcUnf4BaM041sdWg==
electron-to-chromium@^1.4.431:
version "1.4.476"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.476.tgz#693df619ce1785ada6d5aec71fd3ce7ace71adc3"
integrity sha512-gzWl1m8pNy+5Kj17XcziNcbOhripjTqR2wAQmtdlFUngPYuFy7zUpJScVQAvCvQSFHNk3mS5fetNKW6BSpytFg==
version "1.4.479"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.479.tgz#ec9f676f23d3a0b0e429bc454d25e0b3253d2118"
integrity sha512-ABv1nHMIR8I5n3O3Een0gr6i0mfM+YcTZqjHy3pAYaOjgFG+BMquuKrSyfYf5CbEkLr9uM05RA3pOk4udNB/aQ==
elliptic@^6.5.3:
version "6.5.4"
@@ -9695,7 +9695,7 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-dom@18.2.0, react-dom@^18.2.0:
react-dom@18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
@@ -9703,6 +9703,14 @@ react-dom@18.2.0, react-dom@^18.2.0:
loose-envify "^1.1.0"
scheduler "^0.23.0"
react-dom@^0.0.0-experimental-493f72b0a-20230727:
version "0.0.0-experimental-493f72b0a-20230727"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.0.0-experimental-493f72b0a-20230727.tgz#02b95966cbccafdb5eff411bd77cf9a99b4ac38c"
integrity sha512-Ms+rLKteABVNHRjqj2VIQg7biJYb3jB2paJJGGF579Mi22UQnyAVypbCTedzFUDhtpxs+LDR45QNFT5kDIEeaw==
dependencies:
loose-envify "^1.1.0"
scheduler "0.0.0-experimental-493f72b0a-20230727"
react-is@^16.13.1, react-is@^16.8.4:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -9718,13 +9726,20 @@ react-is@^18.0.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react@18.2.0, react@^18.2.0:
react@18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies:
loose-envify "^1.1.0"
react@^0.0.0-experimental-493f72b0a-20230727:
version "0.0.0-experimental-493f72b0a-20230727"
resolved "https://registry.yarnpkg.com/react/-/react-0.0.0-experimental-493f72b0a-20230727.tgz#c1e165db1d1c2dc944e5e1759a860807b06503ed"
integrity sha512-AivNXEA/rd+x9Oe+5JIgsAFGaBQZg0c85upbziIYbgxW1BT6iOFUFA38i+SMfd1ktD82E1yj4nH/OpfA8kC3Cg==
dependencies:
loose-envify "^1.1.0"
read-cache@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
@@ -10098,6 +10113,13 @@ saxes@^6.0.0:
dependencies:
xmlchars "^2.2.0"
scheduler@0.0.0-experimental-493f72b0a-20230727:
version "0.0.0-experimental-493f72b0a-20230727"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.0.0-experimental-493f72b0a-20230727.tgz#a4d1fd57d9bfbd68b1b3e3c3249765f92c1a7b34"
integrity sha512-6bKnt2pR0XjH4ix7rYCp8+KR5eutR0hDpCiU++14SuImTH0uMYa3Gp4ayLeMyAvOEFTCgewDS/faZF1J14sRew==
dependencies:
loose-envify "^1.1.0"
scheduler@^0.23.0:
version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"