diff --git a/.changeset/wet-emus-work.md b/.changeset/wet-emus-work.md new file mode 100644 index 0000000000..413670a325 --- /dev/null +++ b/.changeset/wet-emus-work.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-compat-api': patch +--- + +collectLegacyRoutes throws in case invalid element is found diff --git a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx index 240aa33e2c..2c8288b878 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx @@ -27,7 +27,7 @@ import { PuppetDbPage } from '@backstage/plugin-puppetdb'; import { StackstormPage } from '@backstage/plugin-stackstorm'; import { ScoreBoardPage } from '@oriflame/backstage-plugin-score-card'; import React, { Fragment } from 'react'; -import { Route, Routes } from 'react-router-dom'; +import { Navigate, Route, Routes } from 'react-router-dom'; import { collectLegacyRoutes } from './collectLegacyRoutes'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports @@ -279,4 +279,98 @@ describe('collectLegacyRoutes', () => { screen.findByText('plugins: test'), ).resolves.toBeInTheDocument(); }); + + it('should throw if invalid Route has been detected', async () => { + const plugin = createPlugin({ + id: 'test', + }); + const routeRef = createRouteRef({ id: 'test' }); + const Page = plugin.provide( + createRoutableExtension({ + name: 'Test', + mountPoint: routeRef, + component: async () => () => { + const app = useApp(); + return
plugins: {app.getPlugins().map(p => p.getId())}
; + }, + }), + ); + + expect(() => + collectLegacyRoutes( + + } /> + } /> +
+ , + ), + ).toThrow( + /Invalid element inside FlatRoutes, expected Route but found div./, + ); + }); + + it('should throw if invalid element has been detected', async () => { + const plugin = createPlugin({ + id: 'test', + }); + const routeRef = createRouteRef({ id: 'test' }); + const Page = plugin.provide( + createRoutableExtension({ + name: 'Test', + mountPoint: routeRef, + component: async () => () => { + const app = useApp(); + return
plugins: {app.getPlugins().map(p => p.getId())}
; + }, + }), + ); + + expect(() => + collectLegacyRoutes( + + } />a string + , + ), + ).toThrow( + /Invalid element inside FlatRoutes, expected Route but found element of type string./, + ); + }); + + it('should throw if has no path', async () => { + const plugin = createPlugin({ + id: 'test', + }); + const routeRef = createRouteRef({ id: 'test' }); + const Page = plugin.provide( + createRoutableExtension({ + name: 'Test', + mountPoint: routeRef, + component: () => + Promise.resolve(() => { + const app = useApp(); + return
plugins: {app.getPlugins().map(p => p.getId())}
; + }), + }), + ); + + expect(() => + collectLegacyRoutes( + + } /> + , + ), + ).toThrow(/Route element inside FlatRoutes had no path prop value given/); + }); + + it('should throw if element cannot be converted', async () => { + expect(() => + collectLegacyRoutes( + + } /> + , + ), + ).toThrow( + /Route with path undefined has en element that can not be converted/, + ); + }); }); diff --git a/packages/core-compat-api/src/collectLegacyRoutes.tsx b/packages/core-compat-api/src/collectLegacyRoutes.tsx index 7ddb8c4f7e..b9f4e5528c 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.tsx @@ -175,10 +175,16 @@ export function collectLegacyRoutes( flatRoutesElement.props.children, (route: ReactNode) => { // TODO(freben): Handle feature flag and permissions framework wrapper elements - if (!React.isValidElement(route) || route.type !== Route) { - return; + if (!React.isValidElement(route)) { + throw new Error( + `Invalid element inside FlatRoutes, expected Route but found element of type ${typeof route}.`, + ); + } + if (route.type !== Route) { + throw new Error( + `Invalid element inside FlatRoutes, expected Route but found ${route.type}.`, + ); } - const routeElement = route.props.element; const path: string | undefined = route.props.path; const plugin = getComponentData( @@ -189,8 +195,16 @@ export function collectLegacyRoutes( routeElement, 'core.mountPoint', ); - if (!plugin || !path) { - return; + if (!plugin) { + throw new Error( + // TODO(vinzscam): add See for more info + `Route with path ${path} has en element that can not be converted as it does not belong to a plugin. Make sure that the top-level React element of the element prop is an extension from a Backstage plugin, or remove the Route completely.`, + ); + } + if (path === undefined) { + throw new Error( + `Route element inside FlatRoutes had no path prop value given`, + ); } const extensions = getPluginExtensions(plugin);