Wrap test element in Routes when mountedRoutes are provided
renderInTestApp disables app/routes, so the test element was rendered
without any Route matching. This meant useParams() always returned {}
even when initialRouteEntries and mountedRoutes were set — the mounted
routes only fed the route resolution API for link generation, never
created actual React Router <Route> matching for param extraction.
Wrap the test element in <Routes> with a <Route> for each mounted path
(plus a path="*" fallback), mirroring what the real AppRoutes extension
does via useRoutes(). This makes useParams() return real params when
initialRouteEntries matches a mounted route path.
Update entity page test href expectations to match the now-correct
production behavior where sub-route links resolve relative to the
matched parent Route, and replace synchronous getByTestId/getByText
with async findBy queries for lazy-loaded content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Fragment } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { prepareSpecializedApp } from '@backstage/frontend-app-api';
|
||||
import { RenderResult, render } from '@testing-library/react';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
@@ -125,11 +125,31 @@ export function renderInTestApp<const TApiPairs extends any[] = any[]>(
|
||||
element: JSX.Element,
|
||||
options?: TestAppOptions<TApiPairs>,
|
||||
): RenderResult {
|
||||
const mountedPaths = options?.mountedRoutes
|
||||
? Object.keys(options.mountedRoutes)
|
||||
: [];
|
||||
|
||||
const extensions: Array<ExtensionDefinition> = [
|
||||
createExtension({
|
||||
attachTo: { id: 'app/root', input: 'children' },
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory: () => {
|
||||
if (mountedPaths.length > 0) {
|
||||
return [
|
||||
coreExtensionData.reactElement(
|
||||
<Routes>
|
||||
{mountedPaths.map(path => (
|
||||
<Route
|
||||
key={path}
|
||||
path={path === '/' ? path : `${path.replace(/\/$/, '')}/*`}
|
||||
element={element}
|
||||
/>
|
||||
))}
|
||||
<Route path="*" element={element} />
|
||||
</Routes>,
|
||||
),
|
||||
];
|
||||
}
|
||||
return [coreExtensionData.reactElement(element)];
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -176,11 +176,17 @@ describe('Entity page', () => {
|
||||
|
||||
await expect(
|
||||
screen.findByRole('button', { name: /TechDocs/ }),
|
||||
).resolves.toHaveAttribute('href', '/techdocs');
|
||||
).resolves.toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/default/component/artist-lookup/techdocs',
|
||||
);
|
||||
|
||||
await expect(
|
||||
screen.findByRole('button', { name: /ApiDocs/ }),
|
||||
).resolves.toHaveAttribute('href', '/apidocs');
|
||||
).resolves.toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/default/component/artist-lookup/apidocs',
|
||||
);
|
||||
});
|
||||
|
||||
it('Should rename a default group', async () => {
|
||||
@@ -222,11 +228,17 @@ describe('Entity page', () => {
|
||||
|
||||
await expect(
|
||||
screen.findByRole('button', { name: /TechDocs/ }),
|
||||
).resolves.toHaveAttribute('href', '/techdocs');
|
||||
).resolves.toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/default/component/artist-lookup/techdocs',
|
||||
);
|
||||
|
||||
await expect(
|
||||
screen.findByRole('button', { name: /ApiDocs/ }),
|
||||
).resolves.toHaveAttribute('href', '/apidocs');
|
||||
).resolves.toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/default/component/artist-lookup/apidocs',
|
||||
);
|
||||
});
|
||||
|
||||
it('Should disassociate a content with a default group', async () => {
|
||||
@@ -317,11 +329,17 @@ describe('Entity page', () => {
|
||||
|
||||
await expect(
|
||||
screen.findByRole('button', { name: /TechDocs/ }),
|
||||
).resolves.toHaveAttribute('href', '/techdocs');
|
||||
).resolves.toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/default/component/artist-lookup/techdocs',
|
||||
);
|
||||
|
||||
await expect(
|
||||
screen.findByRole('button', { name: /ApiDocs/ }),
|
||||
).resolves.toHaveAttribute('href', '/apidocs');
|
||||
).resolves.toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/default/component/artist-lookup/apidocs',
|
||||
);
|
||||
});
|
||||
|
||||
it('Should render a single-content groups as a normal tab', async () => {
|
||||
@@ -441,11 +459,17 @@ describe('Entity page', () => {
|
||||
|
||||
await expect(
|
||||
screen.findByRole('button', { name: /TechDocs/ }),
|
||||
).resolves.toHaveAttribute('href', '/techdocs');
|
||||
).resolves.toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/default/component/artist-lookup/techdocs',
|
||||
);
|
||||
|
||||
await expect(
|
||||
screen.findByRole('button', { name: /ApiDocs/ }),
|
||||
).resolves.toHaveAttribute('href', '/apidocs');
|
||||
).resolves.toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/default/component/artist-lookup/apidocs',
|
||||
);
|
||||
});
|
||||
|
||||
it('Should sort content by title by default', async () => {
|
||||
@@ -816,14 +840,14 @@ describe('Entity page', () => {
|
||||
screen.findByText(/artist-lookup/),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByTestId('menu-button'));
|
||||
await userEvent.click(await screen.findByTestId('menu-button'));
|
||||
|
||||
await expect(
|
||||
screen.findByText('Test Title'),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
await expect(screen.findByText('Test Icon')).resolves.toBeInTheDocument();
|
||||
const listItem = screen.getByText('Test Title').closest('li');
|
||||
const listItem = (await screen.findByText('Test Title')).closest('li');
|
||||
expect(listItem).toHaveAttribute('aria-disabled', disabled.toString());
|
||||
if (!disabled) {
|
||||
await userEvent.click(screen.getByText('Test Title'));
|
||||
|
||||
Reference in New Issue
Block a user