From 309501b9eb1fce5c60845957314b0d9eb3ce328a Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Tue, 1 Sep 2026 09:44:51 +0200 Subject: [PATCH] test(e2e): Port Next.js routing test apps to span streaming Moves nextjs-15-basepath, nextjs-15-intl and nextjs-16-trailing-slash off the static trace lifecycle and rewrites their assertions to span v2. Ref #23513 Co-Authored-By: Claude Opus 5 (1M context) --- .../instrumentation-client.ts | 1 - .../nextjs-15-basepath/sentry.edge.config.ts | 1 - .../sentry.server.config.ts | 1 - .../tests/routing-basepath-span.test.ts | 83 +++++++++ .../routing-basepath-transaction.test.ts | 85 --------- .../nextjs-15-intl/instrumentation-client.ts | 1 - .../nextjs-15-intl/sentry.edge.config.ts | 1 - .../nextjs-15-intl/sentry.server.config.ts | 1 - .../nextjs-15-intl/tests/i18n-routing.test.ts | 90 +++------- .../instrumentation-client.ts | 1 - .../sentry.edge.config.ts | 1 - .../sentry.server.config.ts | 1 - .../trailing-slash-parameterization.test.ts | 164 ++++++------------ 13 files changed, 165 insertions(+), 266 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15-basepath/tests/routing-basepath-span.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15-basepath/tests/routing-basepath-transaction.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/instrumentation-client.ts b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/instrumentation-client.ts index 97cc19287aeb..6dd7cce7b75e 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/instrumentation-client.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/instrumentation-client.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/sentry.edge.config.ts b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/sentry.edge.config.ts index 490a00cff366..0ef32107efff 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/sentry.edge.config.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/sentry.edge.config.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/sentry.server.config.ts b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/sentry.server.config.ts index 490a00cff366..0ef32107efff 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/sentry.server.config.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/sentry.server.config.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/tests/routing-basepath-span.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/tests/routing-basepath-span.test.ts new file mode 100644 index 000000000000..fbdb962d4262 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/tests/routing-basepath-span.test.ts @@ -0,0 +1,83 @@ +import { expect, test } from '@playwright/test'; +import { getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils'; + +test('Creates a pageload span for basePath root route with prefix', async ({ page }) => { + const pageloadSpanPromise = waitForStreamedSpan('nextjs-15-basepath', span => { + return span.name === '/my-app' && getSpanOp(span) === 'pageload' && span.is_segment; + }); + + await page.goto('/my-app'); + + expect(await pageloadSpanPromise).toBeDefined(); +}); + +test('Creates a dynamic pageload span for basePath dynamic route with prefix', async ({ page }) => { + const randomRoute = String(Math.random()); + + const pageloadSpanPromise = waitForStreamedSpan('nextjs-15-basepath', span => { + return span.name === '/my-app/dynamic/:parameter' && getSpanOp(span) === 'pageload' && span.is_segment; + }); + + await page.goto(`/my-app/dynamic/${randomRoute}`); + + expect(await pageloadSpanPromise).toBeDefined(); +}); + +test('Creates a dynamic pageload span for basePath dynamic catch-all route with prefix', async ({ page }) => { + const randomRoute = String(Math.random()); + + const pageloadSpanPromise = waitForStreamedSpan('nextjs-15-basepath', span => { + return span.name === '/my-app/dynamic/:parameters*' && getSpanOp(span) === 'pageload' && span.is_segment; + }); + + await page.goto(`/my-app/dynamic/${randomRoute}/foo/bar/baz`); + + expect(await pageloadSpanPromise).toBeDefined(); +}); + +test('Creates a navigation span for basePath router with prefix', async ({ page }) => { + const navigationSpanPromise = waitForStreamedSpan('nextjs-15-basepath', span => { + return ( + span.name === '/my-app/navigation/:param/router-push' && + getSpanOp(span) === 'navigation' && + span.is_segment && + span.attributes['navigation.type']?.value === 'router.push' + ); + }); + + await page.goto('/my-app/navigation'); + await page.waitForTimeout(1000); + await page.getByText('router.push()').click(); + + const navigationSpan = await navigationSpanPromise; + expect(navigationSpan).toBeDefined(); + + expect(navigationSpan.attributes).toMatchObject({ + 'sentry.op': { value: 'navigation', type: 'string' }, + 'sentry.origin': { value: 'auto.navigation.nextjs.app_router_instrumentation', type: 'string' }, + 'sentry.segment.name.source': { value: 'route', type: 'string' }, + 'url.full': { + value: expect.stringMatching(/^https?:\/\/localhost:\d+\/my-app\/navigation\/42\/router-push$/), + type: 'string', + }, + 'url.path': { value: '/my-app/navigation/42/router-push', type: 'string' }, + 'url.template': { value: '/my-app/navigation/:param/router-push', type: 'string' }, + }); +}); + +test('Creates a navigation span for basePath with prefix', async ({ page }) => { + const navigationSpanPromise = waitForStreamedSpan('nextjs-15-basepath', span => { + return ( + span.name === '/my-app/navigation/:param/link' && + getSpanOp(span) === 'navigation' && + span.is_segment && + span.attributes['navigation.type']?.value === 'router.push' + ); + }); + + await page.goto('/my-app/navigation'); + await page.waitForTimeout(1000); + await page.getByText('Normal Link').click(); + + expect(await navigationSpanPromise).toBeDefined(); +}); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/tests/routing-basepath-transaction.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/tests/routing-basepath-transaction.test.ts deleted file mode 100644 index 2ba15d2d6903..000000000000 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-basepath/tests/routing-basepath-transaction.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('Creates a pageload transaction for basePath root route with prefix', async ({ page }) => { - const clientPageloadTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => { - return transactionEvent?.transaction === '/my-app' && transactionEvent.contexts?.trace?.op === 'pageload'; - }); - - await page.goto('/my-app'); - - expect(await clientPageloadTransactionPromise).toBeDefined(); -}); - -test('Creates a dynamic pageload transaction for basePath dynamic route with prefix', async ({ page }) => { - const randomRoute = String(Math.random()); - - const clientPageloadTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => { - return ( - transactionEvent?.transaction === '/my-app/dynamic/:parameter' && - transactionEvent.contexts?.trace?.op === 'pageload' - ); - }); - - await page.goto(`/my-app/dynamic/${randomRoute}`); - - expect(await clientPageloadTransactionPromise).toBeDefined(); -}); - -test('Creates a dynamic pageload transaction for basePath dynamic catch-all route with prefix', async ({ page }) => { - const randomRoute = String(Math.random()); - - const clientPageloadTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => { - return ( - transactionEvent?.transaction === '/my-app/dynamic/:parameters*' && - transactionEvent.contexts?.trace?.op === 'pageload' - ); - }); - - await page.goto(`/my-app/dynamic/${randomRoute}/foo/bar/baz`); - - expect(await clientPageloadTransactionPromise).toBeDefined(); -}); - -test('Creates a navigation transaction for basePath router with prefix', async ({ page }) => { - const navigationTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => { - return ( - transactionEvent?.transaction === '/my-app/navigation/:param/router-push' && - transactionEvent.contexts?.trace?.op === 'navigation' && - transactionEvent.contexts.trace.data?.['navigation.type'] === 'router.push' - ); - }); - - await page.goto('/my-app/navigation'); - await page.waitForTimeout(1000); - await page.getByText('router.push()').click(); - - const navigationTransaction = await navigationTransactionPromise; - expect(navigationTransaction).toBeDefined(); - - const attributes = navigationTransaction.contexts?.trace?.data; - expect(attributes).toMatchObject({ - 'sentry.op': 'navigation', - 'sentry.origin': 'auto.navigation.nextjs.app_router_instrumentation', - 'sentry.segment.name.source': 'route', - 'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/my-app\/navigation\/42\/router-push$/), - 'url.path': '/my-app/navigation/42/router-push', - 'url.template': '/my-app/navigation/:param/router-push', - }); -}); - -test('Creates a navigation transaction for basePath with prefix', async ({ page }) => { - const navigationTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => { - return ( - transactionEvent?.transaction === '/my-app/navigation/:param/link' && - transactionEvent.contexts?.trace?.op === 'navigation' && - transactionEvent.contexts.trace.data?.['navigation.type'] === 'router.push' - ); - }); - - await page.goto('/my-app/navigation'); - await page.waitForTimeout(1000); - await page.getByText('Normal Link').click(); - - expect(await navigationTransactionPromise).toBeDefined(); -}); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/instrumentation-client.ts b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/instrumentation-client.ts index 101a053aefc1..ac378b07f095 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/instrumentation-client.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/instrumentation-client.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/sentry.edge.config.ts b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/sentry.edge.config.ts index ad18186cb80a..ecea686e4217 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/sentry.edge.config.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/sentry.edge.config.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', dsn: process.env.SENTRY_DSN, tunnel: `http://localhost:3031/`, diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/sentry.server.config.ts b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/sentry.server.config.ts index 494c476361f1..6cec152f81d4 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/sentry.server.config.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/sentry.server.config.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/tests/i18n-routing.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/tests/i18n-routing.test.ts index 60d535471180..c079defbe67b 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15-intl/tests/i18n-routing.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15-intl/tests/i18n-routing.test.ts @@ -1,90 +1,54 @@ import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; +import { getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils'; -test('should create consistent parameterized transaction for default locale without prefix', async ({ page }) => { - const transactionPromise = waitForTransaction('nextjs-15-intl', async transactionEvent => { - return transactionEvent.transaction === '/:locale/i18n-test' && transactionEvent.contexts?.trace?.op === 'pageload'; +test('should create consistent parameterized span for default locale without prefix', async ({ page }) => { + const spanPromise = waitForStreamedSpan('nextjs-15-intl', span => { + return span.name === '/:locale/i18n-test' && getSpanOp(span) === 'pageload' && span.is_segment; }); await page.goto(`/i18n-test`); - const transaction = await transactionPromise; - - expect(transaction).toMatchObject({ - transaction: '/:locale/i18n-test', - transaction_info: { source: 'route' }, - contexts: { - trace: { - data: { - 'sentry.segment.name.source': 'route', - }, - }, - }, - }); + const span = await spanPromise; + + expect(span.name).toBe('/:locale/i18n-test'); + expect(span.attributes['sentry.segment.name.source']?.value).toBe('route'); }); -test('should create consistent parameterized transaction for non-default locale with prefix', async ({ page }) => { - const transactionPromise = waitForTransaction('nextjs-15-intl', async transactionEvent => { - return transactionEvent.transaction === '/:locale/i18n-test' && transactionEvent.contexts?.trace?.op === 'pageload'; +test('should create consistent parameterized span for non-default locale with prefix', async ({ page }) => { + const spanPromise = waitForStreamedSpan('nextjs-15-intl', span => { + return span.name === '/:locale/i18n-test' && getSpanOp(span) === 'pageload' && span.is_segment; }); await page.goto(`/ar/i18n-test`); - const transaction = await transactionPromise; - - expect(transaction).toMatchObject({ - transaction: '/:locale/i18n-test', - transaction_info: { source: 'route' }, - contexts: { - trace: { - data: { - 'sentry.segment.name.source': 'route', - }, - }, - }, - }); + const span = await spanPromise; + + expect(span.name).toBe('/:locale/i18n-test'); + expect(span.attributes['sentry.segment.name.source']?.value).toBe('route'); }); test('should parameterize locale root page correctly for default locale without prefix', async ({ page }) => { - const transactionPromise = waitForTransaction('nextjs-15-intl', async transactionEvent => { - return transactionEvent.transaction === '/:locale' && transactionEvent.contexts?.trace?.op === 'pageload'; + const spanPromise = waitForStreamedSpan('nextjs-15-intl', span => { + return span.name === '/:locale' && getSpanOp(span) === 'pageload' && span.is_segment; }); await page.goto(`/`); - const transaction = await transactionPromise; - - expect(transaction).toMatchObject({ - transaction: '/:locale', - transaction_info: { source: 'route' }, - contexts: { - trace: { - data: { - 'sentry.segment.name.source': 'route', - }, - }, - }, - }); + const span = await spanPromise; + + expect(span.name).toBe('/:locale'); + expect(span.attributes['sentry.segment.name.source']?.value).toBe('route'); }); test('should parameterize locale root page correctly for non-default locale with prefix', async ({ page }) => { - const transactionPromise = waitForTransaction('nextjs-15-intl', async transactionEvent => { - return transactionEvent.transaction === '/:locale' && transactionEvent.contexts?.trace?.op === 'pageload'; + const spanPromise = waitForStreamedSpan('nextjs-15-intl', span => { + return span.name === '/:locale' && getSpanOp(span) === 'pageload' && span.is_segment; }); await page.goto(`/fr`); - const transaction = await transactionPromise; - - expect(transaction).toMatchObject({ - transaction: '/:locale', - transaction_info: { source: 'route' }, - contexts: { - trace: { - data: { - 'sentry.segment.name.source': 'route', - }, - }, - }, - }); + const span = await spanPromise; + + expect(span.name).toBe('/:locale'); + expect(span.attributes['sentry.segment.name.source']?.value).toBe('route'); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/instrumentation-client.ts b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/instrumentation-client.ts index 97cc19287aeb..6dd7cce7b75e 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/instrumentation-client.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/instrumentation-client.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/sentry.edge.config.ts b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/sentry.edge.config.ts index ee00e6481622..ad780407a5b7 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/sentry.edge.config.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/sentry.edge.config.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/sentry.server.config.ts b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/sentry.server.config.ts index ee00e6481622..ad780407a5b7 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/sentry.server.config.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/sentry.server.config.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/tests/trailing-slash-parameterization.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/tests/trailing-slash-parameterization.test.ts index eb47d4881544..b2407c6673ce 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/tests/trailing-slash-parameterization.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-trailing-slash/tests/trailing-slash-parameterization.test.ts @@ -1,150 +1,96 @@ import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; +import { getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils'; -// These tests verify that pageload transactions are correctly named when +// These tests verify that pageload spans are correctly named when // trailingSlash: true is enabled in next.config.mjs, even when a catch-all // route exists. See: https://github.com/getsentry/sentry-javascript/issues/19241 -test('should create a correctly named pageload transaction for a static route', async ({ page }) => { - const transactionPromise = waitForTransaction('nextjs-16-trailing-slash', async transactionEvent => { - return transactionEvent.transaction === '/static-page' && transactionEvent.contexts?.trace?.op === 'pageload'; +test('should create a correctly named pageload span for a static route', async ({ page }) => { + const spanPromise = waitForStreamedSpan('nextjs-16-trailing-slash', span => { + return span.name === '/static-page' && getSpanOp(span) === 'pageload' && span.is_segment; }); await page.goto(`/static-page`); - const transaction = await transactionPromise; - - expect(transaction).toMatchObject({ - contexts: { - trace: { - data: { - 'sentry.op': 'pageload', - 'sentry.origin': 'auto.pageload.nextjs.app_router_instrumentation', - 'sentry.segment.name.source': 'route', - 'url.template': '/static-page', - }, - op: 'pageload', - origin: 'auto.pageload.nextjs.app_router_instrumentation', - }, - }, - transaction: '/static-page', - transaction_info: { source: 'route' }, - type: 'transaction', + const span = await spanPromise; + + expect(span.name).toBe('/static-page'); + expect(span.attributes).toMatchObject({ + 'sentry.op': { value: 'pageload', type: 'string' }, + 'sentry.origin': { value: 'auto.pageload.nextjs.app_router_instrumentation', type: 'string' }, + 'sentry.segment.name.source': { value: 'route', type: 'string' }, + 'url.template': { value: '/static-page', type: 'string' }, }); }); -test('should create a correctly named pageload transaction for a parameterized route', async ({ page }) => { - const transactionPromise = waitForTransaction('nextjs-16-trailing-slash', async transactionEvent => { - return ( - transactionEvent.transaction === '/parameterized/:param' && transactionEvent.contexts?.trace?.op === 'pageload' - ); +test('should create a correctly named pageload span for a parameterized route', async ({ page }) => { + const spanPromise = waitForStreamedSpan('nextjs-16-trailing-slash', span => { + return span.name === '/parameterized/:param' && getSpanOp(span) === 'pageload' && span.is_segment; }); await page.goto(`/parameterized/some-value`); - const transaction = await transactionPromise; - - expect(transaction).toMatchObject({ - contexts: { - trace: { - data: { - 'sentry.op': 'pageload', - 'sentry.origin': 'auto.pageload.nextjs.app_router_instrumentation', - 'sentry.segment.name.source': 'route', - }, - op: 'pageload', - origin: 'auto.pageload.nextjs.app_router_instrumentation', - }, - }, - transaction: '/parameterized/:param', - transaction_info: { source: 'route' }, - type: 'transaction', + const span = await spanPromise; + + expect(span.name).toBe('/parameterized/:param'); + expect(span.attributes).toMatchObject({ + 'sentry.op': { value: 'pageload', type: 'string' }, + 'sentry.origin': { value: 'auto.pageload.nextjs.app_router_instrumentation', type: 'string' }, + 'sentry.segment.name.source': { value: 'route', type: 'string' }, }); }); -test('should create a correctly named pageload transaction for a static nested route under parameterized', async ({ +test('should create a correctly named pageload span for a static nested route under parameterized', async ({ page, }) => { - const transactionPromise = waitForTransaction('nextjs-16-trailing-slash', async transactionEvent => { - return ( - transactionEvent.transaction === '/parameterized/static' && transactionEvent.contexts?.trace?.op === 'pageload' - ); + const spanPromise = waitForStreamedSpan('nextjs-16-trailing-slash', span => { + return span.name === '/parameterized/static' && getSpanOp(span) === 'pageload' && span.is_segment; }); await page.goto(`/parameterized/static`); - const transaction = await transactionPromise; - - expect(transaction).toMatchObject({ - contexts: { - trace: { - data: { - 'sentry.op': 'pageload', - 'sentry.origin': 'auto.pageload.nextjs.app_router_instrumentation', - 'sentry.segment.name.source': 'route', - 'url.template': '/parameterized/static', - }, - op: 'pageload', - origin: 'auto.pageload.nextjs.app_router_instrumentation', - }, - }, - transaction: '/parameterized/static', - transaction_info: { source: 'route' }, - type: 'transaction', + const span = await spanPromise; + + expect(span.name).toBe('/parameterized/static'); + expect(span.attributes).toMatchObject({ + 'sentry.op': { value: 'pageload', type: 'string' }, + 'sentry.origin': { value: 'auto.pageload.nextjs.app_router_instrumentation', type: 'string' }, + 'sentry.segment.name.source': { value: 'route', type: 'string' }, + 'url.template': { value: '/parameterized/static', type: 'string' }, }); }); -test('should create a correctly named pageload transaction for the catch-all route', async ({ page }) => { - const transactionPromise = waitForTransaction('nextjs-16-trailing-slash', async transactionEvent => { - return transactionEvent.transaction === '/:slug*' && transactionEvent.contexts?.trace?.op === 'pageload'; +test('should create a correctly named pageload span for the catch-all route', async ({ page }) => { + const spanPromise = waitForStreamedSpan('nextjs-16-trailing-slash', span => { + return span.name === '/:slug*' && getSpanOp(span) === 'pageload' && span.is_segment; }); await page.goto(`/some/unmatched/path`); - const transaction = await transactionPromise; - - expect(transaction).toMatchObject({ - contexts: { - trace: { - data: { - 'sentry.op': 'pageload', - 'sentry.origin': 'auto.pageload.nextjs.app_router_instrumentation', - 'sentry.segment.name.source': 'route', - }, - op: 'pageload', - origin: 'auto.pageload.nextjs.app_router_instrumentation', - }, - }, - transaction: '/:slug*', - transaction_info: { source: 'route' }, - type: 'transaction', + const span = await spanPromise; + + expect(span.name).toBe('/:slug*'); + expect(span.attributes).toMatchObject({ + 'sentry.op': { value: 'pageload', type: 'string' }, + 'sentry.origin': { value: 'auto.pageload.nextjs.app_router_instrumentation', type: 'string' }, + 'sentry.segment.name.source': { value: 'route', type: 'string' }, }); }); -test('should create a correctly named pageload transaction for the home page', async ({ page }) => { - const transactionPromise = waitForTransaction('nextjs-16-trailing-slash', async transactionEvent => { - return transactionEvent.transaction === '/' && transactionEvent.contexts?.trace?.op === 'pageload'; +test('should create a correctly named pageload span for the home page', async ({ page }) => { + const spanPromise = waitForStreamedSpan('nextjs-16-trailing-slash', span => { + return span.name === '/' && getSpanOp(span) === 'pageload' && span.is_segment; }); await page.goto(`/`); - const transaction = await transactionPromise; - - expect(transaction).toMatchObject({ - contexts: { - trace: { - data: { - 'sentry.op': 'pageload', - 'sentry.origin': 'auto.pageload.nextjs.app_router_instrumentation', - 'sentry.segment.name.source': 'route', - 'url.template': '/', - }, - op: 'pageload', - origin: 'auto.pageload.nextjs.app_router_instrumentation', - }, - }, - transaction: '/', - transaction_info: { source: 'route' }, - type: 'transaction', + const span = await spanPromise; + + expect(span.name).toBe('/'); + expect(span.attributes).toMatchObject({ + 'sentry.op': { value: 'pageload', type: 'string' }, + 'sentry.origin': { value: 'auto.pageload.nextjs.app_router_instrumentation', type: 'string' }, + 'sentry.segment.name.source': { value: 'route', type: 'string' }, + 'url.template': { value: '/', type: 'string' }, }); });