import {getVersionedRenderImplementation} from './utils';
describe('Fast Refresh', () => {
let React;
let ReactFreshRuntime;
let act;
let babel;
let exportsObj;
let freshPlugin;
let store;
let withErrorsOrWarningsIgnored;
beforeEach(() => {
global.IS_REACT_ACT_ENVIRONMENT = true;
exportsObj = undefined;
babel = require('@babel/core');
freshPlugin = require('react-refresh/babel');
store = global.store;
React = require('react');
ReactFreshRuntime = require('react-refresh/runtime');
ReactFreshRuntime.injectIntoGlobalHook(global);
const utils = require('./utils');
act = utils.act;
withErrorsOrWarningsIgnored = utils.withErrorsOrWarningsIgnored;
});
const {render: renderImplementation, getContainer} =
getVersionedRenderImplementation();
function execute(source) {
const compiled = babel.transform(source, {
babelrc: false,
presets: ['@babel/react'],
plugins: [
[freshPlugin, {skipEnvCheck: true}],
'@babel/plugin-transform-modules-commonjs',
'@babel/plugin-transform-destructuring',
].filter(Boolean),
}).code;
exportsObj = {};
new Function(
'global',
'React',
'exports',
'$RefreshReg$',
'$RefreshSig$',
compiled,
)(global, React, exportsObj, $RefreshReg$, $RefreshSig$);
$RefreshReg$(exportsObj.default, 'exports.default');
return exportsObj.default;
}
function render(source) {
const Component = execute(source);
act(() => {
renderImplementation(<Component />);
});
expect(ReactFreshRuntime.performReactRefresh()).toBe(null);
}
function patch(source) {
const prevExports = exportsObj;
execute(source);
const nextExports = exportsObj;
const didExportsChange =
ReactFreshRuntime.getFamilyByType(prevExports.default) !==
ReactFreshRuntime.getFamilyByType(nextExports.default);
if (didExportsChange) {
const NextComponent = nextExports.default;
act(() => {
renderImplementation(<NextComponent />);
});
}
act(() => {
const result = ReactFreshRuntime.performReactRefresh();
if (!didExportsChange) {
expect(result).not.toBe(null);
} else {
}
});
expect(ReactFreshRuntime._getMountedRootCount()).toBe(1);
}
function $RefreshReg$(type, id) {
ReactFreshRuntime.register(type, id);
}
function $RefreshSig$() {
return ReactFreshRuntime.createSignatureFunctionForTransform();
}
it('should not break the DevTools store', () => {
render(`
function Parent() {
return <Child key="A" />;
};
function Child() {
return <div />;
};
export default Parent;
`);
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Parent>
<Child key="A">
`);
let element = getContainer().firstChild;
expect(getContainer().firstChild).not.toBe(null);
patch(`
function Parent() {
return <Child key="A" />;
};
function Child() {
return <div />;
};
export default Parent;
`);
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Parent>
<Child key="A">
`);
expect(getContainer().firstChild).toBe(element);
element = getContainer().firstChild;
patch(`
function Parent() {
return <Child key="B" />;
};
function Child() {
return <div />;
};
export default Parent;
`);
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Parent>
<Child key="B">
`);
expect(getContainer().firstChild).not.toBe(element);
});
it('should not break when there are warnings in between patching', () => {
withErrorsOrWarningsIgnored(['Expected:'], () => {
render(`
const {useState} = React;
export default function Component() {
const [state, setState] = useState(1);
console.warn("Expected: warning during render");
return null;
}
`);
});
expect(store).toMatchInlineSnapshot(`
✕ 0, ⚠ 1
[root]
<Component> ⚠
`);
withErrorsOrWarningsIgnored(['Expected:'], () => {
patch(`
const {useEffect, useState} = React;
export default function Component() {
const [state, setState] = useState(1);
console.warn("Expected: warning during render");
return null;
}
`);
});
expect(store).toMatchInlineSnapshot(`
✕ 0, ⚠ 2
[root]
<Component> ⚠
`);
withErrorsOrWarningsIgnored(['Expected:'], () => {
patch(`
const {useEffect, useState} = React;
export default function Component() {
const [state, setState] = useState(1);
useEffect(() => {
console.error("Expected: error during effect");
});
console.warn("Expected: warning during render");
return null;
}
`);
});
expect(store).toMatchInlineSnapshot(`
✕ 1, ⚠ 1
[root]
<Component> ✕⚠
`);
withErrorsOrWarningsIgnored(['Expected:'], () => {
patch(`
const {useEffect, useState} = React;
export default function Component() {
const [state, setState] = useState(1);
console.warn("Expected: warning during render");
return null;
}
`);
});
expect(store).toMatchInlineSnapshot(`
✕ 0, ⚠ 1
[root]
<Component> ⚠
`);
});
});