diff --git a/packages/test-utils/src/testUtils/appWrappers.test.tsx b/packages/test-utils/src/testUtils/appWrappers.test.tsx
index 29f8d97793..4899f07061 100644
--- a/packages/test-utils/src/testUtils/appWrappers.test.tsx
+++ b/packages/test-utils/src/testUtils/appWrappers.test.tsx
@@ -21,9 +21,11 @@ import { Route, Routes } from 'react-router';
import { withLogCollector } from '@backstage/test-utils-core';
import {
useApi,
+ useRouteRef,
errorApiRef,
ApiProvider,
ApiRegistry,
+ createRouteRef,
} from '@backstage/core-api';
import { MockErrorApi } from './apis';
@@ -113,4 +115,29 @@ describe('wrapInTestApp', () => {
expect(rendered.getByText('foo')).toBeInTheDocument();
expect(mockErrorApi.getErrors()).toEqual([{ error: new Error('NOPE') }]);
});
+
+ it('should allow route refs to be mounted on specific paths', async () => {
+ const aRouteRef = createRouteRef({ title: 'A' });
+ const bRouteRef = createRouteRef({ title: 'B', params: ['name'] });
+
+ const MyComponent = () => {
+ const a = useRouteRef(aRouteRef);
+ const b = useRouteRef(bRouteRef);
+ return (
+
+
Link A: {a()}
+
Link B: {b({ name: 'x' })}
+
+ );
+ };
+
+ const rendered = await renderInTestApp(, {
+ mountedRoutes: {
+ '/my-a-path': aRouteRef,
+ '/my-b-path/:name': bRouteRef,
+ },
+ });
+ expect(rendered.getByText('Link A: /my-a-path')).toBeInTheDocument();
+ expect(rendered.getByText('Link B: /my-b-path/x')).toBeInTheDocument();
+ });
});
diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx
index 1f6c7622b8..cc67a5b707 100644
--- a/packages/test-utils/src/testUtils/appWrappers.tsx
+++ b/packages/test-utils/src/testUtils/appWrappers.tsx
@@ -21,6 +21,9 @@ import { lightTheme } from '@backstage/theme';
import privateExports, {
defaultSystemIcons,
BootErrorPageProps,
+ RouteRef,
+ createPlugin,
+ createRoutableExtension,
} from '@backstage/core-api';
import { RenderResult } from '@testing-library/react';
import { renderWithEffects } from '@backstage/test-utils-core';
@@ -44,6 +47,22 @@ type TestAppOptions = {
* Initial route entries to pass along as `initialEntries` to the router.
*/
routeEntries?: string[];
+
+ /**
+ * An object of paths to mount route ref on, with the key being the path and the value
+ * being the RouteRef that the path will be bound to. This allows the route refs to be
+ * used by `useRouteRef` in the rendered elements.
+ *
+ * @example
+ * wrapInTestApp(, {
+ * mountedRoutes: {
+ * '/my-path': myRouteRef,
+ * }
+ * })
+ * // ...
+ * const link = useRouteRef(myRouteRef)
+ */
+ mountedRoutes?: { [path: string]: RouteRef };
};
/**
@@ -90,12 +109,27 @@ export function wrapInTestApp(
Wrapper = () => Component as React.ReactElement;
}
+ const routePlugin = createPlugin({ id: 'mock-route-plugin' });
+ const routeElements = Object.entries(options.mountedRoutes ?? {}).map(
+ ([path, routeRef]) => {
+ const PageComponent = () => Mounted at {path}
;
+ const Page = routePlugin.provide(
+ createRoutableExtension({
+ component: async () => PageComponent,
+ mountPoint: routeRef,
+ }),
+ );
+ return } />;
+ },
+ );
+
const AppProvider = app.getProvider();
const AppRouter = app.getRouter();
return (
+ {routeElements}
{/* The path of * here is needed to be set as a catch all, so it will render the wrapper element
* and work with nested routes if they exist too */}
} />