diff --git a/src/app/api/send/route.test.ts b/src/app/api/send/route.test.ts index 1b1d3b75a..9733c0da2 100644 --- a/src/app/api/send/route.test.ts +++ b/src/app/api/send/route.test.ts @@ -482,6 +482,39 @@ describe('event url and referrer parsing', () => { expect(saveEventMock.mock.calls[0][0]).toMatchObject({ urlPath: '/blog' }); }); + test('REMOVE_TRAILING_SLASH keeps the root path as "/"', async () => { + process.env.REMOVE_TRAILING_SLASH = '1'; + + await callPOST({ + type: 'event', + payload: { website: WEBSITE_ID, hostname: 'example.com', url: '/' }, + }); + + expect(saveEventMock.mock.calls[0][0]).toMatchObject({ urlPath: '/' }); + }); + + test('REMOVE_TRAILING_SLASH keeps the root path when the url has a hash', async () => { + process.env.REMOVE_TRAILING_SLASH = '1'; + + await callPOST({ + type: 'event', + payload: { website: WEBSITE_ID, hostname: 'example.com', url: '/#hero' }, + }); + + expect(saveEventMock.mock.calls[0][0]).toMatchObject({ urlPath: '/#hero' }); + }); + + test('REMOVE_TRAILING_SLASH strips a trailing slash before the hash', async () => { + process.env.REMOVE_TRAILING_SLASH = '1'; + + await callPOST({ + type: 'event', + payload: { website: WEBSITE_ID, hostname: 'example.com', url: '/blog/#hero' }, + }); + + expect(saveEventMock.mock.calls[0][0]).toMatchObject({ urlPath: '/blog#hero' }); + }); + test('maps a "/undefined" pathname to an empty url path', async () => { await callPOST({ type: 'event', diff --git a/src/app/api/send/route.ts b/src/app/api/send/route.ts index 6da12076c..cd5d42634 100644 --- a/src/app/api/send/route.ts +++ b/src/app/api/send/route.ts @@ -214,7 +214,8 @@ export async function POST(request: Request) { const twclid = currentUrl.searchParams.get('twclid'); if (process.env.REMOVE_TRAILING_SLASH) { - urlPath = urlPath.replace(/\/(?=(#.*)?$)/, ''); + // Never strip the root slash, otherwise the home page is saved with an empty path + urlPath = urlPath.replace(/(?!^)\/(?=(#.*)?$)/, ''); } if (referrer) {