Complete Backstage app context for testing components with routing, theming, and API providers configured automatically. These utilities provide consistent test setup across Backstage plugin and app testing scenarios.
Renders a component within a complete Backstage app context with async effects handling.
/**
* Renders component in test app with full Backstage context and waits for async effects
* @param Component - React component or element to render
* @param options - Test app configuration options including legacy root support
* @returns Promise resolving to React Testing Library render result
*/
function renderInTestApp(
Component: ComponentType | ReactNode,
options?: TestAppOptions & LegacyRootOption
): Promise<RenderResult>;
interface LegacyRootOption {
legacyRoot?: boolean;
}Usage Examples:
import { renderInTestApp } from "@backstage/test-utils";
// Basic usage with component
const { getByText } = await renderInTestApp(<MyPluginPage />);
expect(getByText('Plugin Content')).toBeInTheDocument();
// With routing configuration
const { getByRole } = await renderInTestApp(<MyComponent />, {
routeEntries: ['/catalog/entity/default/component/my-service'],
mountedRoutes: {
'/catalog': catalogRouteRef,
}
});
// With custom components
await renderInTestApp(<MyComponent />, {
components: {
NotFoundErrorPage: CustomNotFoundPage,
}
});Wraps a component in a complete Backstage test app context without rendering.
/**
* Wraps component in test app context without rendering
* @param Component - React component or element to wrap
* @param options - Test app configuration options
* @returns React element wrapped in test app context
*/
function wrapInTestApp(
Component: ComponentType | ReactNode,
options?: TestAppOptions
): ReactElement;Usage Examples:
import { wrapInTestApp } from "@backstage/test-utils";
import { render } from "@testing-library/react";
// Wrap component for custom rendering
const WrappedComponent = wrapInTestApp(<MyComponent />);
const { container } = render(WrappedComponent);
// Use with React Testing Library's render
const { getByTestId } = render(
wrapInTestApp(<MyPluginComponent />, {
routeEntries: ['/my-plugin']
})
);Creates a reusable wrapper component for consistent test app setup across multiple tests.
/**
* Creates reusable wrapper component for Backstage test app
* @param options - Test app configuration options
* @returns Component wrapper function for testing
*/
function createTestAppWrapper(options?: TestAppOptions): ComponentWrapper;
type ComponentWrapper = (props: { children?: ReactNode }) => JSX.Element;Usage Examples:
import { createTestAppWrapper } from "@backstage/test-utils";
import { renderHook } from "@testing-library/react";
// Create wrapper for hook testing
const wrapper = createTestAppWrapper({
routeEntries: ['/catalog'],
mountedRoutes: {
'/catalog': catalogRouteRef
}
});
// Use with renderHook
const { result } = renderHook(() => useEntity(), { wrapper });
expect(result.current.entity).toBeDefined();
// Use with multiple tests
describe('MyComponent', () => {
const Wrapper = createTestAppWrapper({
components: { NotFoundErrorPage: MockNotFound }
});
test('renders correctly', () => {
render(<MyComponent />, { wrapper: Wrapper });
});
});Creates a testing-library matcher function for finding elements by text content.
/**
* Creates testing-library matcher for text content matching
* @param text - Text to match against element content
* @returns MatcherFunction for use with testing-library queries
*/
function textContentMatcher(text: string): MatcherFunction;
type MatcherFunction = (content: string, element: Element | null) => boolean;Usage Examples:
import { textContentMatcher, renderInTestApp } from "@backstage/test-utils";
const { getByText } = await renderInTestApp(<MyComponent />);
// Match text content across multiple elements
const matcher = textContentMatcher('Total: $123.45');
expect(getByText(matcher)).toBeInTheDocument();
// Use with more complex text matching
const complexMatcher = textContentMatcher('Processing 5 of 10 items');
expect(getByText(complexMatcher)).toBeInTheDocument();Configuration interface for customizing the test app environment.
interface TestAppOptions {
/** Initial router entries for React Router */
routeEntries?: string[];
/** Route mappings for mounted routes */
mountedRoutes?: { [path: string]: RouteRef | ExternalRouteRef };
/** Custom app components to override defaults */
components?: Partial<AppComponents>;
/** Custom icons with optional additional icon mappings */
icons?: Partial<AppIcons> & { [key: string]: IconComponent };
}
interface AppComponents {
NotFoundErrorPage: ComponentType<{}>;
BootErrorPage: ComponentType<{ step: string; error: Error }>;
Progress: ComponentType<{}>;
Router: ComponentType<{ children: ReactNode }>;
ErrorBoundaryFallback: ComponentType<{ error: Error; resetErrorBoundary: () => void }>;
ThemeProvider: ComponentType<{ children: ReactNode }>;
}
interface AppIcons {
'kind:api': IconComponent;
'kind:component': IconComponent;
'kind:domain': IconComponent;
'kind:group': IconComponent;
'kind:location': IconComponent;
'kind:system': IconComponent;
'kind:user': IconComponent;
brokenImage: IconComponent;
catalog: IconComponent;
scaffolder: IconComponent;
techdocs: IconComponent;
search: IconComponent;
chat: IconComponent;
dashboard: IconComponent;
docs: IconComponent;
email: IconComponent;
github: IconComponent;
group: IconComponent;
help: IconComponent;
user: IconComponent;
warning: IconComponent;
}
type IconComponent = ComponentType<{ fontSize?: 'inherit' | 'default' | 'small' | 'large' }>;Configuration Examples:
// Route configuration
const routeOptions = {
routeEntries: [
'/catalog/default/component/my-service',
'/docs/default/component/my-service'
],
mountedRoutes: {
'/catalog': catalogRouteRef,
'/docs': docsRouteRef,
'/api-docs': apiDocsRouteRef
}
};
// Component customization
const componentOptions = {
components: {
NotFoundErrorPage: () => <div>Custom 404 Page</div>,
Progress: () => <div>Loading...</div>
}
};
// Icon customization
const iconOptions = {
icons: {
'kind:api': ApiIcon,
'custom:database': DatabaseIcon,
dashboard: CustomDashboardIcon
}
};
// Combined configuration
await renderInTestApp(<MyComponent />, {
...routeOptions,
...componentOptions,
...iconOptions
});type ReactNode = React.ReactNode;
type ComponentType<P = {}> = React.ComponentType<P>;
type ReactElement = React.ReactElement;
interface RenderResult {
container: HTMLElement;
baseElement: HTMLElement;
debug: (baseElement?: HTMLElement | DocumentFragment) => void;
rerender: (ui: React.ReactElement) => void;
unmount: () => boolean;
asFragment: () => DocumentFragment;
getByLabelText: (text: string | RegExp) => HTMLElement;
getByText: (text: string | RegExp) => HTMLElement;
getByRole: (role: string) => HTMLElement;
getByTestId: (testId: string) => HTMLElement;
// ... other React Testing Library query methods
}