diff --git a/packages/core-api/src/app/App.test.tsx b/packages/core-api/src/app/App.test.tsx
index 431ffa5959..7ab4928abf 100644
--- a/packages/core-api/src/app/App.test.tsx
+++ b/packages/core-api/src/app/App.test.tsx
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-import { renderWithEffects } from '@backstage/test-utils';
+import { renderWithEffects, withLogCollector } from '@backstage/test-utils';
import { lightTheme } from '@backstage/theme';
-import { screen } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
import React from 'react';
import { BrowserRouter, Routes } from 'react-router-dom';
import { createRoutableExtension } from '../extensions';
@@ -125,4 +125,60 @@ describe('Integration Test', () => {
expect(screen.getByText('Our Route Is: /foo/bar')).toBeInTheDocument();
});
+
+ it('should throw some error when the route has duplicate params', () => {
+ const components = {
+ NotFoundErrorPage: () => null,
+ BootErrorPage: () => null,
+ Progress: () => null,
+ Router: BrowserRouter,
+ };
+
+ const app = new PrivateAppImpl({
+ apis: [],
+ defaultApis: [],
+ themes: [
+ {
+ id: 'light',
+ title: 'Light Theme',
+ variant: 'light',
+ theme: lightTheme,
+ },
+ ],
+ icons: defaultSystemIcons,
+ plugins: [],
+ components,
+ bindRoutes: ({ bind }) => {
+ bind(plugin1.externalRoutes, { foo: plugin2RouteRef });
+ },
+ });
+
+ const Provider = app.getProvider();
+ const Router = app.getRouter();
+ const { error: errorLogs } = withLogCollector(() => {
+ expect(() =>
+ render(
+
+
+
+
+
+
+
+
+ ,
+ ),
+ ).toThrow(
+ 'Parameter :thing is duplicated in path /test/:thing/some/:thing',
+ );
+ });
+ expect(errorLogs).toEqual([
+ expect.stringContaining(
+ 'Parameter :thing is duplicated in path /test/:thing/some/:thing',
+ ),
+ expect.stringContaining(
+ 'The above error occurred in the component',
+ ),
+ ]);
+ });
});
diff --git a/packages/core-api/src/app/App.tsx b/packages/core-api/src/app/App.tsx
index 4a23c26bba..08393f3e9e 100644
--- a/packages/core-api/src/app/App.tsx
+++ b/packages/core-api/src/app/App.tsx
@@ -56,7 +56,7 @@ import {
routeParentCollector,
routePathCollector,
} from '../routing/collectors';
-import { RoutingProvider } from '../routing/hooks';
+import { RoutingProvider, validateRoutes } from '../routing/hooks';
import { ExternalRouteRef } from '../routing/RouteRef';
import { AppContextProvider } from './AppContext';
import { AppIdentity } from './AppIdentity';
@@ -253,6 +253,8 @@ export class PrivateAppImpl implements BackstageApp {
},
});
+ validateRoutes(routePaths, routeParents);
+
const loadedConfig = useConfigLoader(
this.configLoader,
this.components,