package/test-utils: provide renderInTestApp + act wrapping + docs + tests

This commit is contained in:
Patrik Oldsberg
2020-06-08 16:41:17 +02:00
parent f55959b680
commit 676c4af4fd
3 changed files with 79 additions and 16 deletions
@@ -14,22 +14,59 @@
* limitations under the License.
*/
import React from 'react';
import React, { FC } from 'react';
import { render } from '@testing-library/react';
import { wrapInTestApp } from './appWrappers';
import { wrapInTestApp, renderInTestApp } from './appWrappers';
import { Route } from 'react-router';
import { withLogCollector } from '@backstage/test-utils-core';
describe('wrapInTestApp', () => {
it('should provide routing', () => {
const rendered = render(
wrapInTestApp(
<>
<Route path="/route1">Route 1</Route>
<Route path="/route2">Route 2</Route>
</>,
{ routeEntries: ['/route2'] },
it('should provide routing and warn about missing act()', async () => {
const { error } = await withLogCollector(['error'], async () => {
const rendered = render(
wrapInTestApp(
<>
<Route path="/route1">Route 1</Route>
<Route path="/route2">Route 2</Route>
</>,
{ routeEntries: ['/route2'] },
),
);
expect(rendered.getByText('Route 2')).toBeInTheDocument();
// Wait for async actions to trigger the act() warnings that we assert below
await Promise.resolve();
});
expect(error).toEqual([
expect.stringMatching(
/^Warning: An update to %s inside a test was not wrapped in act\(...\)/,
),
);
expect(rendered.getByText('Route 2')).toBeInTheDocument();
expect.stringMatching(
/^Warning: An update to %s inside a test was not wrapped in act\(...\)/,
),
]);
});
it('should render a component in a test app without warning about missing act()', async () => {
const { error } = await withLogCollector(['error'], async () => {
const Foo: FC<{}> = () => {
return <p>foo</p>;
};
const rendered = await renderInTestApp(Foo);
expect(rendered.getByText('foo')).toBeInTheDocument();
});
expect(error).toEqual([]);
});
it('should render a node in a test app', async () => {
const Foo: FC<{}> = () => {
return <p>foo</p>;
};
const rendered = await renderInTestApp(<Foo />);
expect(rendered.getByText('foo')).toBeInTheDocument();
});
});
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import React, { ComponentType, ReactNode, FunctionComponent, FC } from 'react';
import React, { ComponentType, ReactNode, FC, ReactElement } from 'react';
import { MemoryRouter } from 'react-router';
import { Route } from 'react-router-dom';
import { lightTheme } from '@backstage/theme';
@@ -23,6 +23,8 @@ import privateExports, {
ApiTestRegistry,
BootErrorPageProps,
} from '@backstage/core-api';
import { RenderResult } from '@testing-library/react';
import { renderWithEffects } from '@backstage/test-utils-core';
const { PrivateAppImpl } = privateExports;
const NotFoundErrorPage = () => {
@@ -43,10 +45,17 @@ type TestAppOptions = {
routeEntries?: string[];
};
/**
* Wraps a component inside a Backstage test app, providing a mocked theme
* and app context, along with mocked APIs.
*
* @param Component - A component or react node to render inside the test app.
* @param options - Additional options for the rendering.
*/
export function wrapInTestApp(
Component: ComponentType | ReactNode,
options: TestAppOptions = {},
) {
): ReactElement {
const { routeEntries = ['/'] } = options;
const app = new PrivateAppImpl({
@@ -72,7 +81,7 @@ export function wrapInTestApp(
if (Component instanceof Function) {
Wrapper = Component;
} else {
Wrapper = (() => Component) as FunctionComponent;
Wrapper = (() => Component) as FC;
}
const AppProvider = app.getProvider();
@@ -85,3 +94,20 @@ export function wrapInTestApp(
</AppProvider>
);
}
/**
* Renders a component inside a Backstage test app, providing a mocked theme
* and app context, along with mocked APIs.
*
* The render executes async effects similar to `renderWithEffects`. To avoid this
* behavior, use a regular `render()` + `wrapInTestApp()` instead.
*
* @param Component - A component or react node to render inside the test app.
* @param options - Additional options for the rendering.
*/
export async function renderInTestApp(
Component: ComponentType | ReactNode,
options: TestAppOptions = {},
): Promise<RenderResult> {
return renderWithEffects(wrapInTestApp(Component, options));
}
+1 -1
View File
@@ -15,4 +15,4 @@
*/
export { default as mockBreakpoint } from './mockBreakpoint';
export * from './appWrappers';
export { wrapInTestApp, renderInTestApp } from './appWrappers';