package/test-utils: provide renderInTestApp + act wrapping + docs + tests
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
*/
|
||||
|
||||
export { default as mockBreakpoint } from './mockBreakpoint';
|
||||
export * from './appWrappers';
|
||||
export { wrapInTestApp, renderInTestApp } from './appWrappers';
|
||||
|
||||
Reference in New Issue
Block a user