Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 272 additions & 1 deletion packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
let React;
let ReactDOM;
let ReactTestRenderer;
let Scheduler;
let Suspense;
Expand All @@ -25,6 +26,7 @@ describe('ReactLazy', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
Suspense = React.Suspense;
lazy = React.lazy;
ReactTestRenderer = require('react-test-renderer');
Expand Down Expand Up @@ -791,7 +793,7 @@ describe('ReactLazy', () => {
);
});

it('throws with a useful error when wrapping fragment with lazy()', async () => {
it('throws with a useful error when wrapping Fragment with lazy()', async () => {
const BadLazy = lazy(() => fakeImport(React.Fragment));

const root = ReactTestRenderer.create(
Expand All @@ -817,6 +819,275 @@ describe('ReactLazy', () => {
);
});

it('throws with a useful error when wrapping createPortal with lazy()', async () => {
const container = document.createElement('div');
const portal = ReactDOM.createPortal(<div />, container);
const BadLazy = lazy(() => fakeImport(portal));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(portal);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: Portal. ' +
'Lazy element type must resolve to a class or function.',
);
});

it('throws with a useful error when wrapping Profiler with lazy()', async () => {
const BadLazy = lazy(() => fakeImport(React.Profiler));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(React.Profiler);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: Profiler. ' +
'Lazy element type must resolve to a class or function.',
);
});

it('throws with a useful error when wrapping StrictMode with lazy()', async () => {
const BadLazy = lazy(() => fakeImport(React.StrictMode));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(React.StrictMode);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: StrictMode. ' +
'Lazy element type must resolve to a class or function.',
);
});

it('throws with a useful error when wrapping Suspense with lazy()', async () => {
const BadLazy = lazy(() => fakeImport(React.Suspense));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(React.Suspense);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: Suspense. ' +
'Lazy element type must resolve to a class or function.',
);
});

it('throws with a useful error when wrapping Context with lazy()', async () => {
const Context = React.createContext(null);
const BadLazy = lazy(() => fakeImport(Context));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(Context);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: Context.Provider. ' +
'Lazy element type must resolve to a class or function.',
);
});

// @gate enableRenderableContext
it('throws with a useful error when wrapping Context.Consumer with lazy()', async () => {
const Context = React.createContext(null);
const BadLazy = lazy(() => fakeImport(Context.Consumer));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(Context.Consumer);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: Context.Consumer. ' +
'Lazy element type must resolve to a class or function.',
);
});

// @gate enableSuspenseList
it('throws with a useful error when wrapping SuspenseList with lazy()', async () => {
const BadLazy = lazy(() => fakeImport(React.unstable_SuspenseList));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(React.unstable_SuspenseList);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: SuspenseList. ' +
'Lazy element type must resolve to a class or function.',
);
});

// @gate enableViewTransition
it('throws with a useful error when wrapping ViewTransition with lazy()', async () => {
const BadLazy = lazy(() => fakeImport(React.unstable_ViewTransition));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(React.unstable_ViewTransition);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: ViewTransition. ' +
'Lazy element type must resolve to a class or function.',
);
});

// @gate enableActivity
it('throws with a useful error when wrapping Activity with lazy()', async () => {
const BadLazy = lazy(() => fakeImport(React.unstable_Activity));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(React.unstable_Activity);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: Activity. ' +
'Lazy element type must resolve to a class or function.',
);
});

// @gate enableTransitionTracing
it('throws with a useful error when wrapping TracingMarker with lazy()', async () => {
const BadLazy = lazy(() => fakeImport(React.unstable_TracingMarker));

const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
{
unstable_isConcurrent: true,
},
);

await waitForAll(['Loading...']);

await resolveFakeImport(React.unstable_TracingMarker);
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<BadLazy />
</Suspense>,
);
await waitForThrow(
'Element type is invalid. Received a promise that resolves to: TracingMarker. ' +
'Lazy element type must resolve to a class or function.',
);
});

it('throws with a useful error when wrapping lazy() multiple times', async () => {
const Lazy1 = lazy(() => fakeImport(Text));
const Lazy2 = lazy(() => fakeImport(Lazy1));
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/getComponentNameFromType.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ export default function getComponentNameFromType(type: mixed): string | null {
switch (type) {
case REACT_FRAGMENT_TYPE:
return 'Fragment';
case REACT_PORTAL_TYPE:
return 'Portal';
case REACT_PROFILER_TYPE:
return 'Profiler';
case REACT_STRICT_MODE_TYPE:
Expand Down Expand Up @@ -106,6 +104,8 @@ export default function getComponentNameFromType(type: mixed): string | null {
}
}
switch (type.$$typeof) {
case REACT_PORTAL_TYPE:
return 'Portal';
case REACT_PROVIDER_TYPE:
if (enableRenderableContext) {
return null;
Expand Down
Loading