Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ export default function Page() {
router.push()
</button>
</li>
<li>
<button
onClick={() => {
router.push(`${window.location.origin}/my-app/navigation/42/router-push`);
}}
>
Absolute URL push
</button>
</li>
<li>
<Link href="/navigation/42/link">Normal Link</Link>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,21 @@ test('Creates a navigation span for basePath <Link> with prefix', async ({ page

expect(await navigationSpanPromise).toBeDefined();
});

test('Does not prepend basePath to absolute router.push URLs', async ({ page }) => {
const navigationSpanPromise = waitForStreamedSpan('nextjs-15-basepath', span => {
return getSpanOp(span) === 'navigation' && span.is_segment;
});

await page.goto('/my-app/navigation');
await page.waitForTimeout(1000);
await page.getByText('Absolute URL push').click();

const navigationSpan = await navigationSpanPromise;

expect(navigationSpan.name).toBe('/my-app/navigation/:param/router-push');
expect(navigationSpan.attributes).toMatchObject({
'sentry.segment.name.source': { value: 'route', type: 'string' },
'url.path': { value: '/my-app/navigation/42/router-push', type: 'string' },
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ const globalWithInjectedBasePath = GLOBAL_OBJ as typeof GLOBAL_OBJ & {
export function appRouterInstrumentNavigation(client: Client): void {
routerTransitionHandler = (href, navigationType) => {
const basePath = process.env._sentryBasePath ?? globalWithInjectedBasePath._sentryBasePath;
const normalizedHref = basePath && !href.startsWith(basePath) ? `${basePath}${href}` : href;
const normalizedHref = basePath && href.startsWith('/') && !href.startsWith(basePath) ? `${basePath}${href}` : href;
Comment thread
Lms24 marked this conversation as resolved.
const unparameterizedPathname = stripTrailingSlash(new URL(normalizedHref, WINDOW.location.href).pathname);
const parameterizedPathname = maybeParameterizeRoute(unparameterizedPathname);
// With span streaming, span names have to be low cardinality, so we can't fall back to the URL.
Expand Down Expand Up @@ -304,7 +304,9 @@ function patchRouter(client: Client, router: NextRouter, currentNavigationSpanRe
const href = argArray[0];
const basePath = process.env._sentryBasePath ?? globalWithInjectedBasePath._sentryBasePath;
const normalizedHref =
basePath && typeof href === 'string' && !href.startsWith(basePath) ? `${basePath}${href}` : href;
basePath && typeof href === 'string' && href.startsWith('/') && !href.startsWith(basePath)
? `${basePath}${href}`
: href;
const transactionName = stripTrailingSlash(transactionNameifyRouterArgument(normalizedHref));
const parameterizedPathname = maybeParameterizeRoute(transactionName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface NextRouter {
const globalWithNext = globalThis as typeof globalThis & {
next?: { router?: NextRouter };
_sentryRouteManifest?: string;
_sentryBasePath?: string;
};

const manifest: RouteManifest = {
Expand All @@ -38,6 +39,12 @@ const manifest: RouteManifest = {
paramNames: ['param'],
hasOptionalPrefix: false,
},
{
path: '/my-app/navigation/:param/router-push',
regex: '^/my-app/navigation/([^/]+)/router-push$',
paramNames: ['param'],
hasOptionalPrefix: false,
},
],
isrRoutes: [],
};
Expand All @@ -56,6 +63,7 @@ async function setup(traceLifecycle: 'stream' | 'static'): Promise<{
core: Core;
router: NextRouter;
client: Client;
instrumentation: Instrumentation;
}> {
vi.resetModules();
const core: Core = await import('@sentry/core');
Expand All @@ -80,7 +88,7 @@ async function setup(traceLifecycle: 'stream' | 'static'): Promise<{
instrumentation.appRouterInstrumentNavigation(client);
await vi.waitFor(() => expect(router.back).not.toBe(originalBack));

return { core, router, client };
return { core, router, client, instrumentation };
}

describe('appRouterInstrumentNavigation (router-patch mode)', () => {
Expand Down Expand Up @@ -207,3 +215,49 @@ describe('appRouterInstrumentNavigation (router-patch mode)', () => {
});
});
});

describe('appRouterInstrumentNavigation with basePath', () => {
beforeEach(() => {
globalWithNext._sentryRouteManifest = JSON.stringify(manifest);
globalWithNext._sentryBasePath = '/my-app';
window.history.replaceState({}, '', '/my-app/navigation');
});

afterEach(() => {
delete globalWithNext.next;
delete globalWithNext._sentryRouteManifest;
delete globalWithNext._sentryBasePath;
});

it.each([
['a root-relative path without basePath', '/navigation/42/router-push'],
['a root-relative path with basePath', '/my-app/navigation/42/router-push'],
['an absolute URL', 'http://localhost:3000/my-app/navigation/42/router-push'],
])('names the router-patch navigation span correctly for %s', async (_, href) => {
const { core, router } = await setup('static');

router.push(href);

const span = core.getActiveSpan();
expect(span).toBeDefined();
const spanJson = core.spanToJSON(span!);
expect(spanJson.name).toBe('/my-app/navigation/:param/router-push');
expect(spanJson.attributes).toEqual(
expect.objectContaining({ 'url.full': 'http://localhost:3000/my-app/navigation/42/router-push' }),
);
});

it.each([
['a root-relative path without basePath', '/navigation/42/router-push'],
['a root-relative path with basePath', '/my-app/navigation/42/router-push'],
['an absolute URL', 'http://localhost:3000/my-app/navigation/42/router-push'],
])('names the transition-start-hook navigation span correctly for %s', async (_, href) => {
const { core, instrumentation } = await setup('static');

instrumentation.captureRouterTransitionStart(href, 'push');

const span = core.getActiveSpan();
expect(span).toBeDefined();
expect(core.spanToJSON(span!).name).toBe('/my-app/navigation/:param/router-push');
});
});
Loading