123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- import type {RouteComponent} from 'react-router';
- import {createRoutes} from 'react-router';
- import * as constants from 'sentry/constants';
- import {buildRoutes} from 'sentry/routes';
- import {normalizeUrl} from './utils/withDomainRequired';
- // Setup a module mock so that we can replace
- // USING_CUSTOMER_DOMAIN with a getter.
- jest.mock('sentry/constants', () => {
- const originalModule = jest.requireActual('sentry/constants');
- return {
- __esModule: true,
- ...originalModule,
- get USING_CUSTOMER_DOMAIN() {
- return false;
- },
- };
- });
- // Workaround react-router PlainRoute type not covering redirect routes.
- type RouteShape = {
- childRoutes?: RouteShape[];
- component?: RouteComponent;
- from?: string;
- path?: string;
- };
- type RouteMetadata = {
- leadingPath: string;
- route: RouteShape;
- };
- function extractRoutes(rootRoute: any): Record<string, RouteComponent> {
- const routeTree = createRoutes(rootRoute);
- const routeMap: Record<string, RouteComponent> = {};
- // A queue of routes we need to visit
- const visitQueue: RouteMetadata[] = [{leadingPath: '', route: routeTree[0]}];
- while (visitQueue.length > 0) {
- const current = visitQueue.pop();
- if (!current) {
- break;
- }
- let leading = current.leadingPath;
- if (current.route.path?.startsWith('/')) {
- leading = '';
- }
- const currentPath = `${leading}${current.route.path ?? ''}`.replace('//', '/');
- if (current.route.childRoutes) {
- for (const childRoute of current.route.childRoutes ?? []) {
- visitQueue.push({
- leadingPath: currentPath,
- route: childRoute,
- });
- }
- } else {
- if (current.route.from) {
- // Redirect routes are not relevant to us.
- continue;
- }
- // We are on a terminal route in the tree. Add to the map of route components.
- // We are less interested in container route components.
- if (current.route.component) {
- routeMap[currentPath] = current.route.component;
- }
- }
- }
- return routeMap;
- }
- describe('buildRoutes()', function () {
- // Until customer-domains is enabled for single-tenant, self-hosted and path
- // based slug routes are removed we need to ensure
- // that each orgId route also has slugless path.
- test('orgId routes also have domain routes', function () {
- const spy = jest.spyOn(constants, 'USING_CUSTOMER_DOMAIN', 'get');
- // Get routes for with customer domains off.
- spy.mockReturnValue(false);
- const routeMap = extractRoutes(buildRoutes());
- // Get routes with customer domains on.
- spy.mockReturnValue(true);
- const domainRoutes = extractRoutes(buildRoutes());
- // All routes that exist under orgId path slugs should
- // have a sibling under customer-domains.
- const mismatch: Array<{domain: string; slug: string}> = [];
- for (const path in routeMap) {
- // Normalize the URLs so that we know the path we're looking for.
- const domainPath = normalizeUrl(path, {forceCustomerDomain: true});
- // Path is not different under customer domains.
- if (domainPath === path) {
- continue;
- }
- if (!domainRoutes[domainPath]) {
- mismatch.push({slug: path, domain: domainPath});
- }
- }
- if (mismatch.length > 0) {
- const routelist = mismatch
- .map(item => `- slug: ${item.slug}\n domain: ${item.domain}`)
- .join('\n');
- throw new Error(
- `Unable to find matching URLs for the following ${mismatch.length} routes:\n\n` +
- routelist +
- '\n\nEach route with the :orgId parameter is expected to have corresponding domain based route as well. ' +
- 'If you need help with this drop by #proj-hybrid-cloud.'
- );
- }
- });
- });
|