From 1e2eadbe6b0a5ff0ed87eac4fa5a5386d7bad6ec Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Fri, 6 Mar 2026 17:48:23 +0100 Subject: [PATCH 01/12] Add .rum-ai-toolkit/ to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cc7c5d0379..bba633f1c0 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ docs/ /developer-extension/.wxt .env* !.env.example +.rum-ai-toolkit/ # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored .pnp.* From 372af36fc24d135acaf8442c23d93c15de73b117 Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Sat, 7 Mar 2026 16:26:53 +0100 Subject: [PATCH 02/12] Add datadogPagesRouter, unit and e2e tests, e2e test app. --- .prettierignore | 1 + eslint.config.mjs | 1 + packages/rum-nextjs/README.md | 39 +- .../src/domain/datadogPagesRouter.tsx | 18 + packages/rum-nextjs/src/entries/main.ts | 1 + test/apps/nextjs-app-router/next-env.d.ts | 2 +- test/apps/nextjs-app-router/yarn.lock | 12 +- test/apps/nextjs-pages-router/.gitignore | 3 + .../instrumentation-client.js | 13 + test/apps/nextjs-pages-router/next-env.d.ts | 6 + test/apps/nextjs-pages-router/next.config.js | 8 + test/apps/nextjs-pages-router/package.json | 33 + test/apps/nextjs-pages-router/pages/_app.tsx | 19 + .../pages/guides/[...slug].tsx | 16 + test/apps/nextjs-pages-router/pages/index.tsx | 17 + .../nextjs-pages-router/pages/user/[id].tsx | 15 + test/apps/nextjs-pages-router/tsconfig.json | 21 + test/apps/nextjs-pages-router/yarn.lock | 705 ++++++++++++++++++ test/e2e/lib/framework/createTest.ts | 16 +- test/e2e/lib/helpers/playwright.ts | 1 + test/e2e/playwright.base.config.ts | 8 + .../nextjsPagesRouterPlugin.scenario.ts | 77 ++ 22 files changed, 1022 insertions(+), 10 deletions(-) create mode 100644 packages/rum-nextjs/src/domain/datadogPagesRouter.tsx create mode 100644 test/apps/nextjs-pages-router/.gitignore create mode 100644 test/apps/nextjs-pages-router/instrumentation-client.js create mode 100644 test/apps/nextjs-pages-router/next-env.d.ts create mode 100644 test/apps/nextjs-pages-router/next.config.js create mode 100644 test/apps/nextjs-pages-router/package.json create mode 100644 test/apps/nextjs-pages-router/pages/_app.tsx create mode 100644 test/apps/nextjs-pages-router/pages/guides/[...slug].tsx create mode 100644 test/apps/nextjs-pages-router/pages/index.tsx create mode 100644 test/apps/nextjs-pages-router/pages/user/[id].tsx create mode 100644 test/apps/nextjs-pages-router/tsconfig.json create mode 100644 test/apps/nextjs-pages-router/yarn.lock create mode 100644 test/e2e/scenario/nextjsPagesRouterPlugin.scenario.ts diff --git a/.prettierignore b/.prettierignore index 9cd8ea477e..8878e234c7 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,6 +7,7 @@ rum-events-format test/**/dist test/**/.next test/apps/nextjs-app-router/next-env.d.ts +test/apps/nextjs-pages-router/next-env.d.ts yarn.lock /docs /developer-extension/.output diff --git a/eslint.config.mjs b/eslint.config.mjs index 493f485793..065af2c3bd 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -31,6 +31,7 @@ export default tseslint.config( 'test/apps/react-heavy-spa', 'test/apps/react-shopist-like', 'test/apps/nextjs-app-router', + 'test/apps/nextjs-pages-router', 'sandbox', 'coverage', 'rum-events-format', diff --git a/packages/rum-nextjs/README.md b/packages/rum-nextjs/README.md index 6fa07ad35d..e003a461ed 100644 --- a/packages/rum-nextjs/README.md +++ b/packages/rum-nextjs/README.md @@ -1,12 +1,12 @@ # RUM Browser Monitoring - NEXTJS integration -This package provides NextJS App Router integration for Datadog Browser RUM. +This package provides NextJS integration for Datadog Browser RUM, supporting both the App Router and Pages Router. Requires Next.js v15.3+, which supports the [`instrumentation-client`][1] file convention. [1]: https://nextjs.org/docs/app/api-reference/file-conventions/instrumentation-client -# Usage +# App Router Usage ## 1. Create an `instrumentation-client.js` file in the root of your Next.js project @@ -43,3 +43,38 @@ export default function RootLayout({ children }: { children: React.ReactNode }) ) } ``` + +# Pages Router Usage + +## 1. Create an `instrumentation-client.js` file in the root of your Next.js project + +Initialize the Datadog RUM SDK with the `nextjsPlugin`. The `onRouterTransitionStart` export is **not needed** for Pages Router. + +```js +import { datadogRum } from '@datadog/browser-rum' +import { nextjsPlugin } from '@datadog/browser-rum-nextjs' + +datadogRum.init({ + applicationId: '', + clientToken: '', + site: 'datadoghq.com', + plugins: [nextjsPlugin()], +}) +``` + +## 2. Call the DatadogPagesRouter component from your custom App. + +```tsx +// pages/_app.tsx +import type { AppProps } from 'next/app' +import { DatadogPagesRouter } from '@datadog/browser-rum-nextjs' + +export default function MyApp({ Component, pageProps }: AppProps) { + return ( + <> + + + + ) +} +``` diff --git a/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx b/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx new file mode 100644 index 0000000000..1ce5c43b17 --- /dev/null +++ b/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx @@ -0,0 +1,18 @@ +'use client' + +import { useRef } from 'react' +import { useRouter } from 'next/router' +import { startNextjsView } from './nextjsPlugin' + +export function DatadogPagesRouter() { + const router = useRouter() + const previousPathname = useRef(null) + + if (previousPathname.current !== router.pathname) { + previousPathname.current = router.pathname + // router.pathname already contains the route pattern (e.g., "/user/[id]") + startNextjsView(router.pathname) + } + + return null +} diff --git a/packages/rum-nextjs/src/entries/main.ts b/packages/rum-nextjs/src/entries/main.ts index f1e347ed1b..1d347b2273 100644 --- a/packages/rum-nextjs/src/entries/main.ts +++ b/packages/rum-nextjs/src/entries/main.ts @@ -1,3 +1,4 @@ export { nextjsPlugin, onRouterTransitionStart } from '../domain/nextjsPlugin' export type { NextjsPlugin } from '../domain/nextjsPlugin' export { DatadogAppRouter } from '../domain/datadogAppRouter' +export { DatadogPagesRouter } from '../domain/datadogPagesRouter' diff --git a/test/apps/nextjs-app-router/next-env.d.ts b/test/apps/nextjs-app-router/next-env.d.ts index 9edff1c7ca..c4b7818fbb 100644 --- a/test/apps/nextjs-app-router/next-env.d.ts +++ b/test/apps/nextjs-app-router/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/test/apps/nextjs-app-router/yarn.lock b/test/apps/nextjs-app-router/yarn.lock index 4f3041d852..6b0a08d9da 100644 --- a/test/apps/nextjs-app-router/yarn.lock +++ b/test/apps/nextjs-app-router/yarn.lock @@ -7,8 +7,8 @@ __metadata: "@datadog/browser-core@file:../../../packages/core/package.tgz::locator=nextjs-app-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=e62f42&locator=nextjs-app-router%40workspace%3A." - checksum: 10c0/3569d76a2863c745ad9804953972f7ca3cc236a8239b53cb095c44e54290fde89bf880776a4baad096306744ffcee9bba28beddac4b0593e0c9f3a5d19998488 + resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=c45ebb&locator=nextjs-app-router%40workspace%3A." + checksum: 10c0/19a0212b06a2d6d1bf74a47c3c3be3e1bf8ce6c78c403e6968a317d4e5da95901d78297beafa025fa2e97376bfc0a81ad23c9c677994b2912139fdafc6aead44 languageName: node linkType: hard @@ -23,7 +23,7 @@ __metadata: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-app-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=2c2ac0&locator=nextjs-app-router%40workspace%3A." + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=67a10f&locator=nextjs-app-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -35,7 +35,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/b72cc517a492ee6082eefb2a665201bee5c3f38a8436f6a6e5882bdc49923985ef0a3873e0611390c695669309589af8074bde7c50582ccc4e4c391a7a95c079 + checksum: 10c0/ba5dcc216f2bb5760e5c95f6c9193044e7c18e405afae4a41a523eb0c1f7602d55f4f93cb8640c5c7b1fd0d991a823f5b44da2f6511206833bdb6e1506eaf227 languageName: node linkType: hard @@ -66,7 +66,7 @@ __metadata: "@datadog/browser-rum@file:../../../packages/rum/package.tgz::locator=nextjs-app-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=03a1d8&locator=nextjs-app-router%40workspace%3A." + resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=9cfd3b&locator=nextjs-app-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -75,7 +75,7 @@ __metadata: peerDependenciesMeta: "@datadog/browser-logs": optional: true - checksum: 10c0/4b82022e8aec51284cd4c7436999e63a5f944fed37330dd13b27827fe9db1acb1ca1491cfc45454ebef089145d902bc86c3202984709c8f369383fc340c33790 + checksum: 10c0/95237d8e152fffc25e395f8b417eff467bd90c367ab9017d7bab90eb77f626dcc75820e7163354e154175c82c091318cbf3dd5ea5bbd5a0d903660c2f939fb4e languageName: node linkType: hard diff --git a/test/apps/nextjs-pages-router/.gitignore b/test/apps/nextjs-pages-router/.gitignore new file mode 100644 index 0000000000..20e9477154 --- /dev/null +++ b/test/apps/nextjs-pages-router/.gitignore @@ -0,0 +1,3 @@ +.next +node_modules +.yarn/* \ No newline at end of file diff --git a/test/apps/nextjs-pages-router/instrumentation-client.js b/test/apps/nextjs-pages-router/instrumentation-client.js new file mode 100644 index 0000000000..e735bbf74e --- /dev/null +++ b/test/apps/nextjs-pages-router/instrumentation-client.js @@ -0,0 +1,13 @@ +import { datadogRum } from '@datadog/browser-rum' +import { nextjsPlugin } from '@datadog/browser-rum-nextjs' + +const params = new URLSearchParams(window.location.search) +const config = params.get('rum-config') +const context = params.get('rum-context') + +if (config && !datadogRum.getInitConfiguration()) { + datadogRum.init({ ...JSON.parse(config), plugins: [nextjsPlugin()] }) + if (context) { + datadogRum.setGlobalContext(JSON.parse(context)) + } +} diff --git a/test/apps/nextjs-pages-router/next-env.d.ts b/test/apps/nextjs-pages-router/next-env.d.ts new file mode 100644 index 0000000000..7996d352f4 --- /dev/null +++ b/test/apps/nextjs-pages-router/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/dev/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/test/apps/nextjs-pages-router/next.config.js b/test/apps/nextjs-pages-router/next.config.js new file mode 100644 index 0000000000..c77754a91e --- /dev/null +++ b/test/apps/nextjs-pages-router/next.config.js @@ -0,0 +1,8 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + turbopack: { + root: __dirname, + }, +} + +module.exports = nextConfig diff --git a/test/apps/nextjs-pages-router/package.json b/test/apps/nextjs-pages-router/package.json new file mode 100644 index 0000000000..26f29e2b73 --- /dev/null +++ b/test/apps/nextjs-pages-router/package.json @@ -0,0 +1,33 @@ +{ + "name": "nextjs-pages-router", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "@datadog/browser-rum": "file:../../../packages/rum/package.tgz", + "@datadog/browser-rum-nextjs": "file:../../../packages/rum-nextjs/package.tgz", + "next": "16.1.6", + "react": "19.2.3", + "react-dom": "19.2.3" + }, + "resolutions": { + "@datadog/browser-rum-core": "file:../../../packages/rum-core/package.tgz", + "@datadog/browser-core": "file:../../../packages/core/package.tgz", + "@datadog/browser-rum": "file:../../../packages/rum/package.tgz", + "@datadog/browser-rum-nextjs": "file:../../../packages/rum-nextjs/package.tgz", + "@datadog/browser-rum-slim": "file:../../../packages/rum-slim/package.tgz", + "@datadog/browser-worker": "file:../../../packages/worker/package.tgz" + }, + "devDependencies": { + "@types/node": "22.16.0", + "@types/react": "19.2.8", + "@types/react-dom": "19.2.3", + "typescript": "5.9.3" + }, + "volta": { + "extends": "../../../package.json" + } +} diff --git a/test/apps/nextjs-pages-router/pages/_app.tsx b/test/apps/nextjs-pages-router/pages/_app.tsx new file mode 100644 index 0000000000..037e13005d --- /dev/null +++ b/test/apps/nextjs-pages-router/pages/_app.tsx @@ -0,0 +1,19 @@ +import type { AppProps } from 'next/app' +import Link from 'next/link' +import { DatadogPagesRouter } from '@datadog/browser-rum-nextjs' + +export default function MyApp({ Component, pageProps }: AppProps) { + return ( + <> + + +
+ +
+ + ) +} diff --git a/test/apps/nextjs-pages-router/pages/guides/[...slug].tsx b/test/apps/nextjs-pages-router/pages/guides/[...slug].tsx new file mode 100644 index 0000000000..8a7ba73103 --- /dev/null +++ b/test/apps/nextjs-pages-router/pages/guides/[...slug].tsx @@ -0,0 +1,16 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' + +export default function GuidesPage() { + const router = useRouter() + const { slug } = router.query + const slugParts = Array.isArray(slug) ? slug : slug ? [slug] : [] + + return ( +
+ ← Back to Home +

Guides: {slugParts.join('/')}

+

This is a catch-all route testing slug normalization.

+
+ ) +} diff --git a/test/apps/nextjs-pages-router/pages/index.tsx b/test/apps/nextjs-pages-router/pages/index.tsx new file mode 100644 index 0000000000..3f4ad125a5 --- /dev/null +++ b/test/apps/nextjs-pages-router/pages/index.tsx @@ -0,0 +1,17 @@ +import Link from 'next/link' + +export default function HomePage() { + return ( +
+

Home

+
    +
  • + Go to User 42 +
  • +
  • + Go to Guides +
  • +
+
+ ) +} diff --git a/test/apps/nextjs-pages-router/pages/user/[id].tsx b/test/apps/nextjs-pages-router/pages/user/[id].tsx new file mode 100644 index 0000000000..05af0ef7d5 --- /dev/null +++ b/test/apps/nextjs-pages-router/pages/user/[id].tsx @@ -0,0 +1,15 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' + +export default function UserPage() { + const router = useRouter() + const { id } = router.query + + return ( +
+ ← Back to Home +

User {id}

+

This is a dynamic route testing view name normalization.

+
+ ) +} diff --git a/test/apps/nextjs-pages-router/tsconfig.json b/test/apps/nextjs-pages-router/tsconfig.json new file mode 100644 index 0000000000..6bc6207a59 --- /dev/null +++ b/test/apps/nextjs-pages-router/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [{ "name": "next" }], + "paths": { "@/*": ["./*"] }, + "target": "ES2017" + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/test/apps/nextjs-pages-router/yarn.lock b/test/apps/nextjs-pages-router/yarn.lock new file mode 100644 index 0000000000..8538339d92 --- /dev/null +++ b/test/apps/nextjs-pages-router/yarn.lock @@ -0,0 +1,705 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@datadog/browser-core@file:../../../packages/core/package.tgz::locator=nextjs-pages-router%40workspace%3A.": + version: 6.30.1 + resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=c45ebb&locator=nextjs-pages-router%40workspace%3A." + checksum: 10c0/19a0212b06a2d6d1bf74a47c3c3be3e1bf8ce6c78c403e6968a317d4e5da95901d78297beafa025fa2e97376bfc0a81ad23c9c677994b2912139fdafc6aead44 + languageName: node + linkType: hard + +"@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz::locator=nextjs-pages-router%40workspace%3A.": + version: 6.30.1 + resolution: "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz#../../../packages/rum-core/package.tgz::hash=acbd3f&locator=nextjs-pages-router%40workspace%3A." + dependencies: + "@datadog/browser-core": "npm:6.30.1" + checksum: 10c0/4e2fe3cf78c7cbe16530bb4cf732922ae7866c8df233dfcbf1bd14380c348336c4abcf703b7f159fde344bbee0e5382169c58150e64f940a7949ded3e7cf6f81 + languageName: node + linkType: hard + +"@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-pages-router%40workspace%3A.": + version: 6.30.1 + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=67a10f&locator=nextjs-pages-router%40workspace%3A." + dependencies: + "@datadog/browser-core": "npm:6.30.1" + "@datadog/browser-rum-core": "npm:6.30.1" + peerDependencies: + next: ">=13.0.0" + react: ">=18.0.0" + peerDependenciesMeta: + next: + optional: true + react: + optional: true + checksum: 10c0/ba5dcc216f2bb5760e5c95f6c9193044e7c18e405afae4a41a523eb0c1f7602d55f4f93cb8640c5c7b1fd0d991a823f5b44da2f6511206833bdb6e1506eaf227 + languageName: node + linkType: hard + +"@datadog/browser-rum@file:../../../packages/rum/package.tgz::locator=nextjs-pages-router%40workspace%3A.": + version: 6.30.1 + resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=9cfd3b&locator=nextjs-pages-router%40workspace%3A." + dependencies: + "@datadog/browser-core": "npm:6.30.1" + "@datadog/browser-rum-core": "npm:6.30.1" + peerDependencies: + "@datadog/browser-logs": 6.30.1 + peerDependenciesMeta: + "@datadog/browser-logs": + optional: true + checksum: 10c0/95237d8e152fffc25e395f8b417eff467bd90c367ab9017d7bab90eb77f626dcc75820e7163354e154175c82c091318cbf3dd5ea5bbd5a0d903660c2f939fb4e + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.7.0": + version: 1.8.1 + resolution: "@emnapi/runtime@npm:1.8.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/f4929d75e37aafb24da77d2f58816761fe3f826aad2e37fa6d4421dac9060cbd5098eea1ac3c9ecc4526b89deb58153852fa432f87021dc57863f2ff726d713f + languageName: node + linkType: hard + +"@img/colour@npm:^1.0.0": + version: 1.1.0 + resolution: "@img/colour@npm:1.1.0" + checksum: 10c0/2ebea2c0bbaee73b99badcefa04e1e71d83f36e5369337d3121dca841f4569533c4e2faddda6d62dd247f0d5cca143711f9446c59bcce81e427ba433a7a94a17 + languageName: node + linkType: hard + +"@img/sharp-darwin-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-arm64": + optional: true + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-darwin-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-x64": + optional: true + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm@npm:1.2.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-ppc64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-riscv64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-riscv64@npm:1.2.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-s390x@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.4" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linux-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-arm@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm": + optional: true + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-ppc64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-ppc64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-ppc64": + optional: true + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-riscv64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-riscv64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-riscv64": + optional: true + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-s390x@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-s390x@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-s390x": + optional: true + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-wasm32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-wasm32@npm:0.34.5" + dependencies: + "@emnapi/runtime": "npm:^1.7.0" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@img/sharp-win32-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-arm64@npm:0.34.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-win32-ia32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-ia32@npm:0.34.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@img/sharp-win32-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-x64@npm:0.34.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@next/env@npm:16.1.6": + version: 16.1.6 + resolution: "@next/env@npm:16.1.6" + checksum: 10c0/ed7023edb94b9b2e5da3f9c99d08b614da9757c1edd0ecec792fce4d336b4f0c64db1a84955e07cfbd848b9e61c4118fff28f4098cd7b0a7f97814a90565ebe6 + languageName: node + linkType: hard + +"@next/swc-darwin-arm64@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-darwin-arm64@npm:16.1.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-darwin-x64@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-darwin-x64@npm:16.1.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-linux-arm64-gnu@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-arm64-gnu@npm:16.1.6" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-arm64-musl@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-arm64-musl@npm:16.1.6" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-x64-gnu@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-x64-gnu@npm:16.1.6" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-x64-musl@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-x64-musl@npm:16.1.6" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-win32-arm64-msvc@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-win32-arm64-msvc@npm:16.1.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-win32-x64-msvc@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-win32-x64-msvc@npm:16.1.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@swc/helpers@npm:0.5.15": + version: 0.5.15 + resolution: "@swc/helpers@npm:0.5.15" + dependencies: + tslib: "npm:^2.8.0" + checksum: 10c0/33002f74f6f885f04c132960835fdfc474186983ea567606db62e86acd0680ca82f34647e8e610f4e1e422d1c16fce729dde22cd3b797ab1fd9061a825dabca4 + languageName: node + linkType: hard + +"@types/node@npm:22.16.0": + version: 22.16.0 + resolution: "@types/node@npm:22.16.0" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/6219b521062f6c38d4d85ebd25807bd7f2bc703a5acba24e2c6716938d9d6cefd6fafd7b5156f61580eb58a0d82e8921751b778655675389631d813e5f261c03 + languageName: node + linkType: hard + +"@types/react-dom@npm:19.2.3": + version: 19.2.3 + resolution: "@types/react-dom@npm:19.2.3" + peerDependencies: + "@types/react": ^19.2.0 + checksum: 10c0/b486ebe0f4e2fb35e2e108df1d8fc0927ca5d6002d5771e8a739de11239fe62d0e207c50886185253c99eb9dedfeeb956ea7429e5ba17f6693c7acb4c02f8cd1 + languageName: node + linkType: hard + +"@types/react@npm:19.2.8": + version: 19.2.8 + resolution: "@types/react@npm:19.2.8" + dependencies: + csstype: "npm:^3.2.2" + checksum: 10c0/832834998c4ee971fca72ecf1eb95dc924ad3931a2112c687a4dae498aabd115c5fa4db09186853e34a646226b0223808c8f867df03d17601168f9cf119448de + languageName: node + linkType: hard + +"baseline-browser-mapping@npm:^2.8.3": + version: 2.10.0 + resolution: "baseline-browser-mapping@npm:2.10.0" + bin: + baseline-browser-mapping: dist/cli.cjs + checksum: 10c0/da9c3ec0fcd7f325226a47d2142794d41706b6e0a405718a2c15410bbdb72aacadd65738bedef558c6f1b106ed19458cb25b06f63b66df2c284799905dbbd003 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001579": + version: 1.0.30001777 + resolution: "caniuse-lite@npm:1.0.30001777" + checksum: 10c0/e35443fa7c470edc06e315297cca706790840e96983fff12dfe502a4b123d6e4a64b9b4e8e35fb2f5bb60c31b24fbda93d76b2f700ce183df474671236fa7a4a + languageName: node + linkType: hard + +"client-only@npm:0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 10c0/9d6cfd0c19e1c96a434605added99dff48482152af791ec4172fb912a71cff9027ff174efd8cdb2160cc7f377543e0537ffc462d4f279bc4701de3f2a3c4b358 + languageName: node + linkType: hard + +"csstype@npm:^3.2.2": + version: 3.2.3 + resolution: "csstype@npm:3.2.3" + checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce + languageName: node + linkType: hard + +"detect-libc@npm:^2.1.2": + version: 2.1.2 + resolution: "detect-libc@npm:2.1.2" + checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.6": + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b + languageName: node + linkType: hard + +"next@npm:16.1.6": + version: 16.1.6 + resolution: "next@npm:16.1.6" + dependencies: + "@next/env": "npm:16.1.6" + "@next/swc-darwin-arm64": "npm:16.1.6" + "@next/swc-darwin-x64": "npm:16.1.6" + "@next/swc-linux-arm64-gnu": "npm:16.1.6" + "@next/swc-linux-arm64-musl": "npm:16.1.6" + "@next/swc-linux-x64-gnu": "npm:16.1.6" + "@next/swc-linux-x64-musl": "npm:16.1.6" + "@next/swc-win32-arm64-msvc": "npm:16.1.6" + "@next/swc-win32-x64-msvc": "npm:16.1.6" + "@swc/helpers": "npm:0.5.15" + baseline-browser-mapping: "npm:^2.8.3" + caniuse-lite: "npm:^1.0.30001579" + postcss: "npm:8.4.31" + sharp: "npm:^0.34.4" + styled-jsx: "npm:5.1.6" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.51.1 + babel-plugin-react-compiler: "*" + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + sharp: + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/543766bf879bb5a5d454dc18cb302953270a92efba1d01dd028ea83c64b69573ce7d6e6c3759ecbaabec0a84131b0237263c24d1ccd7c8a97205e776dcd34e0b + languageName: node + linkType: hard + +"nextjs-pages-router@workspace:.": + version: 0.0.0-use.local + resolution: "nextjs-pages-router@workspace:." + dependencies: + "@datadog/browser-rum": "file:../../../packages/rum/package.tgz" + "@datadog/browser-rum-nextjs": "file:../../../packages/rum-nextjs/package.tgz" + "@types/node": "npm:22.16.0" + "@types/react": "npm:19.2.8" + "@types/react-dom": "npm:19.2.3" + next: "npm:16.1.6" + react: "npm:19.2.3" + react-dom: "npm:19.2.3" + typescript: "npm:5.9.3" + languageName: unknown + linkType: soft + +"picocolors@npm:^1.0.0": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"postcss@npm:8.4.31": + version: 8.4.31 + resolution: "postcss@npm:8.4.31" + dependencies: + nanoid: "npm:^3.3.6" + picocolors: "npm:^1.0.0" + source-map-js: "npm:^1.0.2" + checksum: 10c0/748b82e6e5fc34034dcf2ae88ea3d11fd09f69b6c50ecdd3b4a875cfc7cdca435c958b211e2cb52355422ab6fccb7d8f2f2923161d7a1b281029e4a913d59acf + languageName: node + linkType: hard + +"react-dom@npm:19.2.3": + version: 19.2.3 + resolution: "react-dom@npm:19.2.3" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.3 + checksum: 10c0/dc43f7ede06f46f3acc16ee83107c925530de9b91d1d0b3824583814746ff4c498ea64fd65cd83aba363205268adff52e2827c582634ae7b15069deaeabc4892 + languageName: node + linkType: hard + +"react@npm:19.2.3": + version: 19.2.3 + resolution: "react@npm:19.2.3" + checksum: 10c0/094220b3ba3a76c1b668f972ace1dd15509b157aead1b40391d1c8e657e720c201d9719537375eff08f5e0514748c0319063392a6f000e31303aafc4471f1436 + languageName: node + linkType: hard + +"scheduler@npm:^0.27.0": + version: 0.27.0 + resolution: "scheduler@npm:0.27.0" + checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 + languageName: node + linkType: hard + +"semver@npm:^7.7.3": + version: 7.7.4 + resolution: "semver@npm:7.7.4" + bin: + semver: bin/semver.js + checksum: 10c0/5215ad0234e2845d4ea5bb9d836d42b03499546ddafb12075566899fc617f68794bb6f146076b6881d755de17d6c6cc73372555879ec7dce2c2feee947866ad2 + languageName: node + linkType: hard + +"sharp@npm:^0.34.4": + version: 0.34.5 + resolution: "sharp@npm:0.34.5" + dependencies: + "@img/colour": "npm:^1.0.0" + "@img/sharp-darwin-arm64": "npm:0.34.5" + "@img/sharp-darwin-x64": "npm:0.34.5" + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + "@img/sharp-linux-arm": "npm:0.34.5" + "@img/sharp-linux-arm64": "npm:0.34.5" + "@img/sharp-linux-ppc64": "npm:0.34.5" + "@img/sharp-linux-riscv64": "npm:0.34.5" + "@img/sharp-linux-s390x": "npm:0.34.5" + "@img/sharp-linux-x64": "npm:0.34.5" + "@img/sharp-linuxmusl-arm64": "npm:0.34.5" + "@img/sharp-linuxmusl-x64": "npm:0.34.5" + "@img/sharp-wasm32": "npm:0.34.5" + "@img/sharp-win32-arm64": "npm:0.34.5" + "@img/sharp-win32-ia32": "npm:0.34.5" + "@img/sharp-win32-x64": "npm:0.34.5" + detect-libc: "npm:^2.1.2" + semver: "npm:^7.7.3" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-libvips-darwin-arm64": + optional: true + "@img/sharp-libvips-darwin-x64": + optional: true + "@img/sharp-libvips-linux-arm": + optional: true + "@img/sharp-libvips-linux-arm64": + optional: true + "@img/sharp-libvips-linux-ppc64": + optional: true + "@img/sharp-libvips-linux-riscv64": + optional: true + "@img/sharp-libvips-linux-s390x": + optional: true + "@img/sharp-libvips-linux-x64": + optional: true + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + "@img/sharp-libvips-linuxmusl-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-ppc64": + optional: true + "@img/sharp-linux-riscv64": + optional: true + "@img/sharp-linux-s390x": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-linuxmusl-arm64": + optional: true + "@img/sharp-linuxmusl-x64": + optional: true + "@img/sharp-wasm32": + optional: true + "@img/sharp-win32-arm64": + optional: true + "@img/sharp-win32-ia32": + optional: true + "@img/sharp-win32-x64": + optional: true + checksum: 10c0/fd79e29df0597a7d5704b8461c51f944ead91a5243691697be6e8243b966402beda53ddc6f0a53b96ea3cb8221f0b244aa588114d3ebf8734fb4aefd41ab802f + languageName: node + linkType: hard + +"source-map-js@npm:^1.0.2": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + +"styled-jsx@npm:5.1.6": + version: 5.1.6 + resolution: "styled-jsx@npm:5.1.6" + dependencies: + client-only: "npm:0.0.1" + peerDependencies: + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + peerDependenciesMeta: + "@babel/core": + optional: true + babel-plugin-macros: + optional: true + checksum: 10c0/ace50e7ea5ae5ae6a3b65a50994c51fca6ae7df9c7ecfd0104c36be0b4b3a9c5c1a2374d16e2a11e256d0b20be6d47256d768ecb4f91ab390f60752a075780f5 + languageName: node + linkType: hard + +"tslib@npm:^2.4.0, tslib@npm:^2.8.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + +"typescript@npm:5.9.3": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A5.9.3#optional!builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 + languageName: node + linkType: hard + +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index c23fe8abc1..2ebd9a2ffd 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -9,7 +9,7 @@ import { BrowserLogsManager, deleteAllCookies, getBrowserName, sendXhr } from '. import { DEFAULT_LOGS_CONFIGURATION, DEFAULT_RUM_CONFIGURATION } from '../helpers/configuration' import { validateRumFormat } from '../helpers/validation' import type { BrowserConfiguration } from '../../../browsers.conf' -import { NEXTJS_APP_ROUTER_PORT } from '../helpers/playwright' +import { NEXTJS_APP_ROUTER_PORT, NEXTJS_PAGES_ROUTER_PORT } from '../helpers/playwright' import { IntakeRegistry } from './intakeRegistry' import { flushEvents } from './flushEvents' import type { Servers } from './httpServers' @@ -125,6 +125,20 @@ class TestBuilder { return this } + withNextjsPagesApp() { + this.baseUrlHooks.push((baseUrl, servers, { rum, context }) => { + baseUrl.port = NEXTJS_PAGES_ROUTER_PORT + if (rum) { + baseUrl.searchParams.set('rum-config', formatConfiguration(rum, servers)) + } + if (context) { + baseUrl.searchParams.set('rum-context', JSON.stringify(context)) + } + }) + this.setups = [{ factory: () => '' }] + return this + } + withBasePath(newBasePath: string) { this.baseUrlHooks.push((baseUrl) => { const parsed = new URL(newBasePath, baseUrl.href) diff --git a/test/e2e/lib/helpers/playwright.ts b/test/e2e/lib/helpers/playwright.ts index abd3220242..433e7bdd1c 100644 --- a/test/e2e/lib/helpers/playwright.ts +++ b/test/e2e/lib/helpers/playwright.ts @@ -5,6 +5,7 @@ import packageJson from '../../../../package.json' with { type: 'json' } export const DEV_SERVER_BASE_URL = `http://localhost:${process.env.DEV_SERVER_PORT}` export const NEXTJS_APP_ROUTER_PORT = process.env.NEXTJS_APP_ROUTER_PORT! +export const NEXTJS_PAGES_ROUTER_PORT = process.env.NEXTJS_PAGES_ROUTER_PORT! export function getPlaywrightConfigBrowserName(name: string): PlaywrightWorkerOptions['browserName'] { if (name.includes('firefox')) { diff --git a/test/e2e/playwright.base.config.ts b/test/e2e/playwright.base.config.ts index e31730b62c..2edd191557 100644 --- a/test/e2e/playwright.base.config.ts +++ b/test/e2e/playwright.base.config.ts @@ -51,5 +51,13 @@ export const config: Config = { stdout: /- Local:\s+http:\/\/localhost:(?\d+)/, }, }, + { + stdout: 'pipe' as const, + cwd: path.join(__dirname, '../apps/nextjs-pages-router'), + command: isLocal ? 'yarn dev' : 'yarn start', + wait: { + stdout: /- Local:\s+http:\/\/localhost:(?\d+)/, + }, + }, ], } diff --git a/test/e2e/scenario/nextjsPagesRouterPlugin.scenario.ts b/test/e2e/scenario/nextjsPagesRouterPlugin.scenario.ts new file mode 100644 index 0000000000..69b764e3e2 --- /dev/null +++ b/test/e2e/scenario/nextjsPagesRouterPlugin.scenario.ts @@ -0,0 +1,77 @@ +import { test, expect } from '@playwright/test' +import { createTest } from '../lib/framework' + +test.describe('nextjs pages router', () => { + createTest('should track initial home view') + .withRum() + .withNextjsPagesApp() + .run(async ({ flushEvents, intakeRegistry, page }) => { + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + const homeView = viewEvents.find((e) => e.view.name === '/' && e.view.loading_type === 'initial_load') + expect(homeView).toBeDefined() + }) + + createTest('should normalize dynamic routes and preserve real URLs and referrers') + .withRum() + .withNextjsPagesApp() + .run(async ({ page, flushEvents, intakeRegistry, baseUrl }) => { + const baseOrigin = new URL(baseUrl).origin + + // Home → Guides → Home → User (link includes ?admin=true) → Home + await page.click('text=Go to Guides') + await page.waitForURL('**/guides/getting-started/intro') + + await page.click('text=Back to Home') + await page.waitForURL('**/') + + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') + + await page.click('text=Back to Home') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + + const homeView = viewEvents.find((e) => e.view.name === '/') + expect(homeView).toBeDefined() + + const guidesView = viewEvents.find((e) => e.view.name === '/guides/[...slug]') + expect(guidesView).toBeDefined() + expect(guidesView?.view.loading_type).toBe('route_change') + expect(guidesView?.view.url).toContain('/guides/getting-started/intro') + expect(guidesView?.view.referrer).toBe(baseUrl) + + const userView = viewEvents.find((e) => e.view.name === '/user/[id]') + expect(userView).toBeDefined() + expect(userView?.view.loading_type).toBe('route_change') + expect(userView?.view.url).toBe(`${baseOrigin}/user/42?admin=true`) + expect(userView?.view.referrer).toBe(`${baseOrigin}/`) + }) + + createTest('should track SPA navigation with loading_time') + .withRum() + .withNextjsPagesApp() + .run(async ({ page, flushEvents, intakeRegistry }) => { + await page.waitForLoadState('networkidle') + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') + + await page.click('text=Back to Home') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + const homeView = viewEvents.find( + (e) => e.view.name === '/' && e.view.loading_type === 'initial_load' && e.view.loading_time !== undefined + ) + expect(homeView).toBeDefined() + expect(homeView?.view.loading_time).toBeDefined() + expect(homeView?.view.loading_time).toBeGreaterThan(0) + }) +}) From dc498c0be0d8daa64cd96aff55365f05eb0a8390 Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Sat, 7 Mar 2026 16:57:11 +0100 Subject: [PATCH 03/12] Build pages router e2e test app, merge tests scenarios for nextjs --- scripts/build/build-test-apps.ts | 1 + test/apps/nextjs-app-router/next-env.d.ts | 2 +- test/apps/nextjs-app-router/yarn.lock | 4 +- test/apps/nextjs-pages-router/next-env.d.ts | 2 +- test/apps/nextjs-pages-router/pages/index.tsx | 2 +- test/apps/nextjs-pages-router/yarn.lock | 4 +- .../nextjsPagesRouterPlugin.scenario.ts | 77 --------- test/e2e/scenario/nextjsPlugin.scenario.ts | 150 +++++++++--------- 8 files changed, 84 insertions(+), 158 deletions(-) delete mode 100644 test/e2e/scenario/nextjsPagesRouterPlugin.scenario.ts diff --git a/scripts/build/build-test-apps.ts b/scripts/build/build-test-apps.ts index a40349c503..f69051c00a 100644 --- a/scripts/build/build-test-apps.ts +++ b/scripts/build/build-test-apps.ts @@ -21,6 +21,7 @@ runMain(async () => { await buildReactRouterv7App() await buildExtensions() buildApp('test/apps/nextjs-app-router') + buildApp('test/apps/nextjs-pages-router') printLog('Test apps and extensions built successfully.') }) diff --git a/test/apps/nextjs-app-router/next-env.d.ts b/test/apps/nextjs-app-router/next-env.d.ts index c4b7818fbb..9edff1c7ca 100644 --- a/test/apps/nextjs-app-router/next-env.d.ts +++ b/test/apps/nextjs-app-router/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/test/apps/nextjs-app-router/yarn.lock b/test/apps/nextjs-app-router/yarn.lock index 6b0a08d9da..12a2a88dfd 100644 --- a/test/apps/nextjs-app-router/yarn.lock +++ b/test/apps/nextjs-app-router/yarn.lock @@ -23,7 +23,7 @@ __metadata: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-app-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=67a10f&locator=nextjs-app-router%40workspace%3A." + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=85a82c&locator=nextjs-app-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -35,7 +35,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/ba5dcc216f2bb5760e5c95f6c9193044e7c18e405afae4a41a523eb0c1f7602d55f4f93cb8640c5c7b1fd0d991a823f5b44da2f6511206833bdb6e1506eaf227 + checksum: 10c0/127455b8244c464bb566220a94cb8773136c65d2346f913bc56a65299077b39acc3fd86bbedd59bbe63cf7d9d397ffd79e8b9c0c6daf6850b571f1718cd2238e languageName: node linkType: hard diff --git a/test/apps/nextjs-pages-router/next-env.d.ts b/test/apps/nextjs-pages-router/next-env.d.ts index 7996d352f4..19709046af 100644 --- a/test/apps/nextjs-pages-router/next-env.d.ts +++ b/test/apps/nextjs-pages-router/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/test/apps/nextjs-pages-router/pages/index.tsx b/test/apps/nextjs-pages-router/pages/index.tsx index 3f4ad125a5..f0497463c9 100644 --- a/test/apps/nextjs-pages-router/pages/index.tsx +++ b/test/apps/nextjs-pages-router/pages/index.tsx @@ -9,7 +9,7 @@ export default function HomePage() { Go to User 42
  • - Go to Guides + Go to Guides 123
  • diff --git a/test/apps/nextjs-pages-router/yarn.lock b/test/apps/nextjs-pages-router/yarn.lock index 8538339d92..5182adbc68 100644 --- a/test/apps/nextjs-pages-router/yarn.lock +++ b/test/apps/nextjs-pages-router/yarn.lock @@ -23,7 +23,7 @@ __metadata: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-pages-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=67a10f&locator=nextjs-pages-router%40workspace%3A." + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=85a82c&locator=nextjs-pages-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -35,7 +35,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/ba5dcc216f2bb5760e5c95f6c9193044e7c18e405afae4a41a523eb0c1f7602d55f4f93cb8640c5c7b1fd0d991a823f5b44da2f6511206833bdb6e1506eaf227 + checksum: 10c0/127455b8244c464bb566220a94cb8773136c65d2346f913bc56a65299077b39acc3fd86bbedd59bbe63cf7d9d397ffd79e8b9c0c6daf6850b571f1718cd2238e languageName: node linkType: hard diff --git a/test/e2e/scenario/nextjsPagesRouterPlugin.scenario.ts b/test/e2e/scenario/nextjsPagesRouterPlugin.scenario.ts deleted file mode 100644 index 69b764e3e2..0000000000 --- a/test/e2e/scenario/nextjsPagesRouterPlugin.scenario.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { test, expect } from '@playwright/test' -import { createTest } from '../lib/framework' - -test.describe('nextjs pages router', () => { - createTest('should track initial home view') - .withRum() - .withNextjsPagesApp() - .run(async ({ flushEvents, intakeRegistry, page }) => { - await page.click('text=Go to User 42') - await page.waitForURL('**/user/42?admin=true') - - await flushEvents() - - const viewEvents = intakeRegistry.rumViewEvents - const homeView = viewEvents.find((e) => e.view.name === '/' && e.view.loading_type === 'initial_load') - expect(homeView).toBeDefined() - }) - - createTest('should normalize dynamic routes and preserve real URLs and referrers') - .withRum() - .withNextjsPagesApp() - .run(async ({ page, flushEvents, intakeRegistry, baseUrl }) => { - const baseOrigin = new URL(baseUrl).origin - - // Home → Guides → Home → User (link includes ?admin=true) → Home - await page.click('text=Go to Guides') - await page.waitForURL('**/guides/getting-started/intro') - - await page.click('text=Back to Home') - await page.waitForURL('**/') - - await page.click('text=Go to User 42') - await page.waitForURL('**/user/42?admin=true') - - await page.click('text=Back to Home') - - await flushEvents() - - const viewEvents = intakeRegistry.rumViewEvents - - const homeView = viewEvents.find((e) => e.view.name === '/') - expect(homeView).toBeDefined() - - const guidesView = viewEvents.find((e) => e.view.name === '/guides/[...slug]') - expect(guidesView).toBeDefined() - expect(guidesView?.view.loading_type).toBe('route_change') - expect(guidesView?.view.url).toContain('/guides/getting-started/intro') - expect(guidesView?.view.referrer).toBe(baseUrl) - - const userView = viewEvents.find((e) => e.view.name === '/user/[id]') - expect(userView).toBeDefined() - expect(userView?.view.loading_type).toBe('route_change') - expect(userView?.view.url).toBe(`${baseOrigin}/user/42?admin=true`) - expect(userView?.view.referrer).toBe(`${baseOrigin}/`) - }) - - createTest('should track SPA navigation with loading_time') - .withRum() - .withNextjsPagesApp() - .run(async ({ page, flushEvents, intakeRegistry }) => { - await page.waitForLoadState('networkidle') - await page.click('text=Go to User 42') - await page.waitForURL('**/user/42?admin=true') - - await page.click('text=Back to Home') - - await flushEvents() - - const viewEvents = intakeRegistry.rumViewEvents - const homeView = viewEvents.find( - (e) => e.view.name === '/' && e.view.loading_type === 'initial_load' && e.view.loading_time !== undefined - ) - expect(homeView).toBeDefined() - expect(homeView?.view.loading_time).toBeDefined() - expect(homeView?.view.loading_time).toBeGreaterThan(0) - }) -}) diff --git a/test/e2e/scenario/nextjsPlugin.scenario.ts b/test/e2e/scenario/nextjsPlugin.scenario.ts index a427537840..51c1f9c817 100644 --- a/test/e2e/scenario/nextjsPlugin.scenario.ts +++ b/test/e2e/scenario/nextjsPlugin.scenario.ts @@ -1,58 +1,83 @@ import { test, expect } from '@playwright/test' import { createTest } from '../lib/framework' +;[ + { name: 'nextjs app router', withApp: (t: ReturnType) => t.withNextjsApp() }, + { name: 'nextjs pages router', withApp: (t: ReturnType) => t.withNextjsPagesApp() }, +].forEach(({ name, withApp }) => { + test.describe(name, () => { + withApp(createTest('should track initial home view').withRum()).run( + async ({ flushEvents, intakeRegistry, page }) => { + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + const homeView = viewEvents.find((e) => e.view.name === '/' && e.view.loading_type === 'initial_load') + expect(homeView).toBeDefined() + } + ) + + withApp(createTest('should normalize dynamic routes and preserve real URLs and referrers').withRum()).run( + async ({ page, flushEvents, intakeRegistry, baseUrl }) => { + const baseOrigin = new URL(baseUrl).origin + + // Home → Guides → Home → User (link includes ?admin=true) → Home + await page.click('text=Go to Guides 123') + await page.waitForURL('**/guides/123') + + await page.click('text=Back to Home') + await page.waitForURL('**/') + + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') + + await page.click('text=Back to Home') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + + const homeView = viewEvents.find((e) => e.view.name === '/') + expect(homeView).toBeDefined() + + const guidesView = viewEvents.find((e) => e.view.name === '/guides/[...slug]') + expect(guidesView).toBeDefined() + expect(guidesView?.view.loading_type).toBe('route_change') + expect(guidesView?.view.url).toContain('/guides/123') + expect(guidesView?.view.referrer).toBe(baseUrl) + + const userView = viewEvents.find((e) => e.view.name === '/user/[id]') + expect(userView).toBeDefined() + expect(userView?.view.loading_type).toBe('route_change') + expect(userView?.view.url).toBe(`${baseOrigin}/user/42?admin=true`) + expect(userView?.view.referrer).toBe(`${baseOrigin}/`) + } + ) + + withApp(createTest('should track SPA navigation with loading_time').withRum()).run( + async ({ page, flushEvents, intakeRegistry }) => { + await page.waitForLoadState('networkidle') + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') + + await page.click('text=Back to Home') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + const homeView = viewEvents.find( + (e) => e.view.name === '/' && e.view.loading_type === 'initial_load' && e.view.loading_time !== undefined + ) + expect(homeView).toBeDefined() + expect(homeView?.view.loading_time).toBeDefined() + expect(homeView?.view.loading_time).toBeGreaterThan(0) + } + ) + }) +}) test.describe('nextjs app router', () => { - createTest('should track initial home view') - .withRum() - .withNextjsApp() - .run(async ({ flushEvents, intakeRegistry, page }) => { - await page.click('text=Go to User 42') - await page.waitForURL('**/user/42?admin=true') - - await flushEvents() - - const viewEvents = intakeRegistry.rumViewEvents - const homeView = viewEvents.find((e) => e.view.name === '/' && e.view.loading_type === 'initial_load') - expect(homeView).toBeDefined() - }) - createTest('should normalize dynamic routes and preserve real URLs and referrers') - .withRum() - .withNextjsApp() - .run(async ({ page, flushEvents, intakeRegistry, baseUrl }) => { - const baseOrigin = new URL(baseUrl).origin - - // Home → Guides → Home → User (link includes ?admin=true) → Home - await page.click('text=Go to Guides 123') - await page.waitForURL('**/guides/123') - - await page.click('text=Back to Home') - await page.waitForURL('**/') - - await page.click('text=Go to User 42') - await page.waitForURL('**/user/42?admin=true') - - await page.click('text=Back to Home') - - await flushEvents() - - const viewEvents = intakeRegistry.rumViewEvents - - const homeView = viewEvents.find((e) => e.view.name === '/') - expect(homeView).toBeDefined() - - const guidesView = viewEvents.find((e) => e.view.name === '/guides/[...slug]') - expect(guidesView).toBeDefined() - expect(guidesView?.view.loading_type).toBe('route_change') - expect(guidesView?.view.url).toContain('/guides/123') - expect(guidesView?.view.referrer).toBe(baseUrl) - - const userView = viewEvents.find((e) => e.view.name === '/user/[id]') - expect(userView).toBeDefined() - expect(userView?.view.loading_type).toBe('route_change') - expect(userView?.view.url).toBe(`${baseOrigin}/user/42?admin=true`) - expect(userView?.view.referrer).toBe(`${baseOrigin}/`) - }) - createTest('should not be affected by parallel routes') .withRum() .withNextjsApp() @@ -65,7 +90,6 @@ test.describe('nextjs app router', () => { await page.click('text=Go to User 42') await page.waitForURL('**/user/42?admin=true') - // Test that the sidebar content is shown on the page expect(await page.textContent('[data-testid="sidebar"]')).toContain('Sidebar: User 42') await page.click('text=Back to Home') @@ -74,7 +98,6 @@ test.describe('nextjs app router', () => { const viewEvents = intakeRegistry.rumViewEvents - // View names should reflect the page route, not the parallel slot const homeView = viewEvents.find((e) => e.view.name === '/') expect(homeView).toBeDefined() @@ -84,25 +107,4 @@ test.describe('nextjs app router', () => { // No view should have @sidebar in the name expect(viewEvents.every((e) => !e.view.name?.includes('@sidebar'))).toBe(true) }) - - createTest('should track SPA navigation with loading_time') - .withRum() - .withNextjsApp() - .run(async ({ page, flushEvents, intakeRegistry }) => { - await page.waitForLoadState('networkidle') - await page.click('text=Go to User 42') - await page.waitForURL('**/user/42?admin=true') - - await page.click('text=Back to Home') - - await flushEvents() - - const viewEvents = intakeRegistry.rumViewEvents - const homeView = viewEvents.find( - (e) => e.view.name === '/' && e.view.loading_type === 'initial_load' && e.view.loading_time !== undefined - ) - expect(homeView).toBeDefined() - expect(homeView?.view.loading_time).toBeDefined() - expect(homeView?.view.loading_time).toBeGreaterThan(0) - }) }) From d36bc66839271f8c548870b05ac7c2be430a5669 Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Sat, 7 Mar 2026 17:13:12 +0100 Subject: [PATCH 04/12] simplify nextjs e2e setup, choose random port --- test/apps/nextjs-pages-router/package.json | 4 +-- test/e2e/lib/framework/createTest.ts | 19 ++---------- test/e2e/scenario/nextjsPlugin.scenario.ts | 35 ++++++++++++---------- 3 files changed, 24 insertions(+), 34 deletions(-) diff --git a/test/apps/nextjs-pages-router/package.json b/test/apps/nextjs-pages-router/package.json index 26f29e2b73..ac8603b64f 100644 --- a/test/apps/nextjs-pages-router/package.json +++ b/test/apps/nextjs-pages-router/package.json @@ -2,9 +2,9 @@ "name": "nextjs-pages-router", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev -p 0", "build": "next build", - "start": "next start" + "start": "next start -p 0" }, "dependencies": { "@datadog/browser-rum": "file:../../../packages/rum/package.tgz", diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index 2ebd9a2ffd..4a9b96d62b 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -111,23 +111,10 @@ class TestBuilder { return this } - withNextjsApp() { + withNextjsApp(router: 'app' | 'pages') { + const port = router === 'app' ? NEXTJS_APP_ROUTER_PORT : NEXTJS_PAGES_ROUTER_PORT this.baseUrlHooks.push((baseUrl, servers, { rum, context }) => { - baseUrl.port = NEXTJS_APP_ROUTER_PORT - if (rum) { - baseUrl.searchParams.set('rum-config', formatConfiguration(rum, servers)) - } - if (context) { - baseUrl.searchParams.set('rum-context', JSON.stringify(context)) - } - }) - this.setups = [{ factory: () => '' }] - return this - } - - withNextjsPagesApp() { - this.baseUrlHooks.push((baseUrl, servers, { rum, context }) => { - baseUrl.port = NEXTJS_PAGES_ROUTER_PORT + baseUrl.port = port if (rum) { baseUrl.searchParams.set('rum-config', formatConfiguration(rum, servers)) } diff --git a/test/e2e/scenario/nextjsPlugin.scenario.ts b/test/e2e/scenario/nextjsPlugin.scenario.ts index 51c1f9c817..4c11fbc25a 100644 --- a/test/e2e/scenario/nextjsPlugin.scenario.ts +++ b/test/e2e/scenario/nextjsPlugin.scenario.ts @@ -1,12 +1,14 @@ import { test, expect } from '@playwright/test' import { createTest } from '../lib/framework' ;[ - { name: 'nextjs app router', withApp: (t: ReturnType) => t.withNextjsApp() }, - { name: 'nextjs pages router', withApp: (t: ReturnType) => t.withNextjsPagesApp() }, -].forEach(({ name, withApp }) => { + { name: 'nextjs app router', router: 'app' as const }, + { name: 'nextjs pages router', router: 'pages' as const }, +].forEach(({ name, router }) => { test.describe(name, () => { - withApp(createTest('should track initial home view').withRum()).run( - async ({ flushEvents, intakeRegistry, page }) => { + createTest('should track initial home view') + .withRum() + .withNextjsApp(router) + .run(async ({ flushEvents, intakeRegistry, page }) => { await page.click('text=Go to User 42') await page.waitForURL('**/user/42?admin=true') @@ -15,11 +17,12 @@ import { createTest } from '../lib/framework' const viewEvents = intakeRegistry.rumViewEvents const homeView = viewEvents.find((e) => e.view.name === '/' && e.view.loading_type === 'initial_load') expect(homeView).toBeDefined() - } - ) + }) - withApp(createTest('should normalize dynamic routes and preserve real URLs and referrers').withRum()).run( - async ({ page, flushEvents, intakeRegistry, baseUrl }) => { + createTest('should normalize dynamic routes and preserve real URLs and referrers') + .withRum() + .withNextjsApp(router) + .run(async ({ page, flushEvents, intakeRegistry, baseUrl }) => { const baseOrigin = new URL(baseUrl).origin // Home → Guides → Home → User (link includes ?admin=true) → Home @@ -52,11 +55,12 @@ import { createTest } from '../lib/framework' expect(userView?.view.loading_type).toBe('route_change') expect(userView?.view.url).toBe(`${baseOrigin}/user/42?admin=true`) expect(userView?.view.referrer).toBe(`${baseOrigin}/`) - } - ) + }) - withApp(createTest('should track SPA navigation with loading_time').withRum()).run( - async ({ page, flushEvents, intakeRegistry }) => { + createTest('should track SPA navigation with loading_time') + .withRum() + .withNextjsApp(router) + .run(async ({ page, flushEvents, intakeRegistry }) => { await page.waitForLoadState('networkidle') await page.click('text=Go to User 42') await page.waitForURL('**/user/42?admin=true') @@ -72,15 +76,14 @@ import { createTest } from '../lib/framework' expect(homeView).toBeDefined() expect(homeView?.view.loading_time).toBeDefined() expect(homeView?.view.loading_time).toBeGreaterThan(0) - } - ) + }) }) }) test.describe('nextjs app router', () => { createTest('should not be affected by parallel routes') .withRum() - .withNextjsApp() + .withNextjsApp('app') .run(async ({ page, flushEvents, intakeRegistry }) => { // The @sidebar parallel route renders alongside the main content // but should not affect view names or URL structure From 23eedb3c1bee71541efaac0d4b655811e5c81c8f Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Sat, 7 Mar 2026 17:17:18 +0100 Subject: [PATCH 05/12] fix e2e ci tests --- test/apps/nextjs-app-router/next-env.d.ts | 2 +- test/apps/nextjs-pages-router/next-env.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/apps/nextjs-app-router/next-env.d.ts b/test/apps/nextjs-app-router/next-env.d.ts index 9edff1c7ca..c4b7818fbb 100644 --- a/test/apps/nextjs-app-router/next-env.d.ts +++ b/test/apps/nextjs-app-router/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/test/apps/nextjs-pages-router/next-env.d.ts b/test/apps/nextjs-pages-router/next-env.d.ts index 19709046af..7996d352f4 100644 --- a/test/apps/nextjs-pages-router/next-env.d.ts +++ b/test/apps/nextjs-pages-router/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. From ed231d8b5c3b73c210b62775a3ac04c26364c344 Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Sat, 7 Mar 2026 18:10:46 +0100 Subject: [PATCH 06/12] add empty favicon --- test/apps/nextjs-pages-router/public/favicon.ico | Bin 0 -> 70 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/apps/nextjs-pages-router/public/favicon.ico diff --git a/test/apps/nextjs-pages-router/public/favicon.ico b/test/apps/nextjs-pages-router/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bdeadc4abcd87b8f6ce1d73fca4ddc53a16c5e86 GIT binary patch literal 70 pcmZQzU<5%%1|TWHV8Fn@AO^%5KnxUOU;@(;KoUFn|NlQ&0suBD1El}} literal 0 HcmV?d00001 From 81a79a2b194bae2cfa2922abf8f8d093b842d664 Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Sat, 7 Mar 2026 19:01:36 +0100 Subject: [PATCH 07/12] use path instead of pathname for view change --- .../src/domain/datadogPagesRouter.tsx | 10 ++++++---- test/apps/nextjs-app-router/yarn.lock | 4 ++-- .../nextjs-pages-router/pages/user/[id].tsx | 1 + test/apps/nextjs-pages-router/yarn.lock | 4 ++-- test/e2e/scenario/nextjsPlugin.scenario.ts | 20 +++++++++++++++++++ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx b/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx index 1ce5c43b17..4eba459ab1 100644 --- a/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx +++ b/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx @@ -6,11 +6,13 @@ import { startNextjsView } from './nextjsPlugin' export function DatadogPagesRouter() { const router = useRouter() - const previousPathname = useRef(null) + const previousAsPath = useRef(null) - if (previousPathname.current !== router.pathname) { - previousPathname.current = router.pathname - // router.pathname already contains the route pattern (e.g., "/user/[id]") + if (previousAsPath.current !== router.asPath) { + previousAsPath.current = router.asPath + // router.pathname is the route pattern (e.g., "/user/[id]") — used as the view name + // router.asPath is the actual URL (e.g., "/user/42") — used to detect navigations between + // different concrete URLs of the same dynamic route (e.g., /user/42 → /user/43) startNextjsView(router.pathname) } diff --git a/test/apps/nextjs-app-router/yarn.lock b/test/apps/nextjs-app-router/yarn.lock index 12a2a88dfd..6f741ac4f3 100644 --- a/test/apps/nextjs-app-router/yarn.lock +++ b/test/apps/nextjs-app-router/yarn.lock @@ -23,7 +23,7 @@ __metadata: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-app-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=85a82c&locator=nextjs-app-router%40workspace%3A." + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=710fdb&locator=nextjs-app-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -35,7 +35,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/127455b8244c464bb566220a94cb8773136c65d2346f913bc56a65299077b39acc3fd86bbedd59bbe63cf7d9d397ffd79e8b9c0c6daf6850b571f1718cd2238e + checksum: 10c0/393eee84c705a43aaad0d6be282815da5d2c3953a812aee5e8b9deb36d9d929bd95f650b027318b44af6ad04e7632fcf63980a00fd80d5bb5f7cbbd04ab2cc68 languageName: node linkType: hard diff --git a/test/apps/nextjs-pages-router/pages/user/[id].tsx b/test/apps/nextjs-pages-router/pages/user/[id].tsx index 05af0ef7d5..91bd4dcb51 100644 --- a/test/apps/nextjs-pages-router/pages/user/[id].tsx +++ b/test/apps/nextjs-pages-router/pages/user/[id].tsx @@ -10,6 +10,7 @@ export default function UserPage() { ← Back to Home

    User {id}

    This is a dynamic route testing view name normalization.

    + Go to User 999 ) } diff --git a/test/apps/nextjs-pages-router/yarn.lock b/test/apps/nextjs-pages-router/yarn.lock index 5182adbc68..ec42ab5b93 100644 --- a/test/apps/nextjs-pages-router/yarn.lock +++ b/test/apps/nextjs-pages-router/yarn.lock @@ -23,7 +23,7 @@ __metadata: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-pages-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=85a82c&locator=nextjs-pages-router%40workspace%3A." + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=710fdb&locator=nextjs-pages-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -35,7 +35,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/127455b8244c464bb566220a94cb8773136c65d2346f913bc56a65299077b39acc3fd86bbedd59bbe63cf7d9d397ffd79e8b9c0c6daf6850b571f1718cd2238e + checksum: 10c0/393eee84c705a43aaad0d6be282815da5d2c3953a812aee5e8b9deb36d9d929bd95f650b027318b44af6ad04e7632fcf63980a00fd80d5bb5f7cbbd04ab2cc68 languageName: node linkType: hard diff --git a/test/e2e/scenario/nextjsPlugin.scenario.ts b/test/e2e/scenario/nextjsPlugin.scenario.ts index 4c11fbc25a..77c554cbbb 100644 --- a/test/e2e/scenario/nextjsPlugin.scenario.ts +++ b/test/e2e/scenario/nextjsPlugin.scenario.ts @@ -80,6 +80,26 @@ import { createTest } from '../lib/framework' }) }) +test.describe('nextjs pages router', () => { + createTest('should track navigations between different concrete URLs of the same dynamic route') + .withRum() + .withNextjsApp('pages') + .run(async ({ page, flushEvents, intakeRegistry }) => { + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') + + // Navigate directly to another user — same route pattern, different URL + await page.click('text=Go to User 999') + await page.waitForURL('**/user/999?admin=true') + + await flushEvents() + + const userViews = intakeRegistry.rumViewEvents.filter((e) => e.view.name === '/user/[id]') + expect(userViews[0].view.url).toContain('/user/42') + expect(userViews[1].view.url).toContain('/user/999') + }) +}) + test.describe('nextjs app router', () => { createTest('should not be affected by parallel routes') .withRum() From 2a3bb4f6cc48ded0e0d6d4d65776df39564428f9 Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Sat, 7 Mar 2026 19:12:03 +0100 Subject: [PATCH 08/12] Fix test --- test/e2e/scenario/nextjsPlugin.scenario.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/e2e/scenario/nextjsPlugin.scenario.ts b/test/e2e/scenario/nextjsPlugin.scenario.ts index 77c554cbbb..c228de346b 100644 --- a/test/e2e/scenario/nextjsPlugin.scenario.ts +++ b/test/e2e/scenario/nextjsPlugin.scenario.ts @@ -94,9 +94,15 @@ test.describe('nextjs pages router', () => { await flushEvents() - const userViews = intakeRegistry.rumViewEvents.filter((e) => e.view.name === '/user/[id]') - expect(userViews[0].view.url).toContain('/user/42') - expect(userViews[1].view.url).toContain('/user/999') + const user42View = intakeRegistry.rumViewEvents.find( + (e) => e.view.name === '/user/[id]' && e.view.url?.includes('/user/42') + ) + const user999View = intakeRegistry.rumViewEvents.find( + (e) => e.view.name === '/user/[id]' && e.view.url?.includes('/user/999') + ) + expect(user42View).toBeDefined() + expect(user999View).toBeDefined() + expect(user999View?.view.referrer).toContain('/user/42') }) }) From 99b888fd567193d5b5b0fb93726ec7581fe647aa Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Mon, 9 Mar 2026 17:42:34 +0100 Subject: [PATCH 09/12] Add e2e test for change in query params, wait until router is ready in datadogPagesRouter, fixes for comments --- .../src/domain/datadogPagesRouter.tsx | 15 ++- .../nextjs-app-router/app/user/[id]/page.tsx | 1 + test/apps/nextjs-app-router/yarn.lock | 4 +- .../nextjs-pages-router/pages/user/[id].tsx | 1 + test/apps/nextjs-pages-router/yarn.lock | 4 +- test/e2e/scenario/nextjsPlugin.scenario.ts | 114 ++++++++++-------- 6 files changed, 82 insertions(+), 57 deletions(-) diff --git a/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx b/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx index 4eba459ab1..458017cf80 100644 --- a/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx +++ b/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx @@ -1,18 +1,23 @@ -'use client' - import { useRef } from 'react' import { useRouter } from 'next/router' import { startNextjsView } from './nextjsPlugin' export function DatadogPagesRouter() { const router = useRouter() - const previousAsPath = useRef(null) + const previousPath = useRef(null) + + if (!router.isReady) { + return null + } + + // Extract the path portion of asPath (without query params or hash) to detect navigations. + const path = router.asPath.split(/[?#]/)[0] - if (previousAsPath.current !== router.asPath) { - previousAsPath.current = router.asPath + if (previousPath.current !== path) { // router.pathname is the route pattern (e.g., "/user/[id]") — used as the view name // router.asPath is the actual URL (e.g., "/user/42") — used to detect navigations between // different concrete URLs of the same dynamic route (e.g., /user/42 → /user/43) + previousPath.current = path startNextjsView(router.pathname) } diff --git a/test/apps/nextjs-app-router/app/user/[id]/page.tsx b/test/apps/nextjs-app-router/app/user/[id]/page.tsx index 5a496c9ba4..48e1947ae7 100644 --- a/test/apps/nextjs-app-router/app/user/[id]/page.tsx +++ b/test/apps/nextjs-app-router/app/user/[id]/page.tsx @@ -8,6 +8,7 @@ export default async function UserPage({ params }: { params: Promise<{ id: strin ← Back to Home

    User {id}

    This is a dynamic route testing view name normalization.

    + Change query params ) } diff --git a/test/apps/nextjs-app-router/yarn.lock b/test/apps/nextjs-app-router/yarn.lock index 6f741ac4f3..b28da17773 100644 --- a/test/apps/nextjs-app-router/yarn.lock +++ b/test/apps/nextjs-app-router/yarn.lock @@ -23,7 +23,7 @@ __metadata: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-app-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=710fdb&locator=nextjs-app-router%40workspace%3A." + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=8376d1&locator=nextjs-app-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -35,7 +35,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/393eee84c705a43aaad0d6be282815da5d2c3953a812aee5e8b9deb36d9d929bd95f650b027318b44af6ad04e7632fcf63980a00fd80d5bb5f7cbbd04ab2cc68 + checksum: 10c0/7e8ab4f2a420d8b721034ab6c9c15e8b64d9cfbbc18950d1e22ec81f862a386bd499472096700006432ce9a2ce5a4042a21a914a7ad66933d16553339db283b7 languageName: node linkType: hard diff --git a/test/apps/nextjs-pages-router/pages/user/[id].tsx b/test/apps/nextjs-pages-router/pages/user/[id].tsx index 91bd4dcb51..8a5a744d73 100644 --- a/test/apps/nextjs-pages-router/pages/user/[id].tsx +++ b/test/apps/nextjs-pages-router/pages/user/[id].tsx @@ -11,6 +11,7 @@ export default function UserPage() {

    User {id}

    This is a dynamic route testing view name normalization.

    Go to User 999 + Change query params ) } diff --git a/test/apps/nextjs-pages-router/yarn.lock b/test/apps/nextjs-pages-router/yarn.lock index ec42ab5b93..ea2a02faef 100644 --- a/test/apps/nextjs-pages-router/yarn.lock +++ b/test/apps/nextjs-pages-router/yarn.lock @@ -23,7 +23,7 @@ __metadata: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-pages-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=710fdb&locator=nextjs-pages-router%40workspace%3A." + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=8376d1&locator=nextjs-pages-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -35,7 +35,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/393eee84c705a43aaad0d6be282815da5d2c3953a812aee5e8b9deb36d9d929bd95f650b027318b44af6ad04e7632fcf63980a00fd80d5bb5f7cbbd04ab2cc68 + checksum: 10c0/7e8ab4f2a420d8b721034ab6c9c15e8b64d9cfbbc18950d1e22ec81f862a386bd499472096700006432ce9a2ce5a4042a21a914a7ad66933d16553339db283b7 languageName: node linkType: hard diff --git a/test/e2e/scenario/nextjsPlugin.scenario.ts b/test/e2e/scenario/nextjsPlugin.scenario.ts index c228de346b..85866e7a24 100644 --- a/test/e2e/scenario/nextjsPlugin.scenario.ts +++ b/test/e2e/scenario/nextjsPlugin.scenario.ts @@ -77,63 +77,81 @@ import { createTest } from '../lib/framework' expect(homeView?.view.loading_time).toBeDefined() expect(homeView?.view.loading_time).toBeGreaterThan(0) }) + + createTest('should not create a new view when only query params change') + .withRum() + .withNextjsApp(router) + .run(async ({ page, flushEvents, intakeRegistry }) => { + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') + + await page.click('text=Change query params') + await page.waitForURL('**/user/42?admin=false') + + await flushEvents() + + const userView = intakeRegistry.rumViewEvents.find((e) => e.view.name === '/user/[id]') + expect(userView).toBeDefined() + + // No view should have been created for the query-param-only navigation + const spuriousView = intakeRegistry.rumViewEvents.find((e) => e.view.url?.includes('admin=false')) + expect(spuriousView).toBeUndefined() + }) }) -}) -test.describe('nextjs pages router', () => { - createTest('should track navigations between different concrete URLs of the same dynamic route') - .withRum() - .withNextjsApp('pages') - .run(async ({ page, flushEvents, intakeRegistry }) => { - await page.click('text=Go to User 42') - await page.waitForURL('**/user/42?admin=true') - - // Navigate directly to another user — same route pattern, different URL - await page.click('text=Go to User 999') - await page.waitForURL('**/user/999?admin=true') - - await flushEvents() - - const user42View = intakeRegistry.rumViewEvents.find( - (e) => e.view.name === '/user/[id]' && e.view.url?.includes('/user/42') - ) - const user999View = intakeRegistry.rumViewEvents.find( - (e) => e.view.name === '/user/[id]' && e.view.url?.includes('/user/999') - ) - expect(user42View).toBeDefined() - expect(user999View).toBeDefined() - expect(user999View?.view.referrer).toContain('/user/42') - }) -}) + if (router === 'pages') { + createTest('should track navigations between different concrete URLs of the same dynamic route') + .withRum() + .withNextjsApp('pages') + .run(async ({ page, flushEvents, intakeRegistry }) => { + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') -test.describe('nextjs app router', () => { - createTest('should not be affected by parallel routes') - .withRum() - .withNextjsApp('app') - .run(async ({ page, flushEvents, intakeRegistry }) => { - // The @sidebar parallel route renders alongside the main content - // but should not affect view names or URL structure - await page.waitForSelector('[data-testid="sidebar"]') - expect(await page.textContent('[data-testid="sidebar"]')).toContain('Sidebar: Home') + // Navigate directly to another user — same route pattern, different URL + await page.click('text=Go to User 999') + await page.waitForURL('**/user/999?admin=true') - await page.click('text=Go to User 42') - await page.waitForURL('**/user/42?admin=true') + await flushEvents() + + const user42View = intakeRegistry.rumViewEvents.find( + (e) => e.view.name === '/user/[id]' && e.view.url?.includes('/user/42') + ) + const user999View = intakeRegistry.rumViewEvents.find( + (e) => e.view.name === '/user/[id]' && e.view.url?.includes('/user/999') + ) + expect(user42View).toBeDefined() + expect(user999View).toBeDefined() + expect(user999View?.view.referrer).toContain('/user/42') + }) + } else { + createTest('should not be affected by parallel routes') + .withRum() + .withNextjsApp('app') + .run(async ({ page, flushEvents, intakeRegistry }) => { + // The @sidebar parallel route renders alongside the main content + // but should not affect view names or URL structure + await page.waitForSelector('[data-testid="sidebar"]') + expect(await page.textContent('[data-testid="sidebar"]')).toContain('Sidebar: Home') + + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42?admin=true') - expect(await page.textContent('[data-testid="sidebar"]')).toContain('Sidebar: User 42') + expect(await page.textContent('[data-testid="sidebar"]')).toContain('Sidebar: User 42') - await page.click('text=Back to Home') + await page.click('text=Back to Home') - await flushEvents() + await flushEvents() - const viewEvents = intakeRegistry.rumViewEvents + const viewEvents = intakeRegistry.rumViewEvents - const homeView = viewEvents.find((e) => e.view.name === '/') - expect(homeView).toBeDefined() + const homeView = viewEvents.find((e) => e.view.name === '/') + expect(homeView).toBeDefined() - const userView = viewEvents.find((e) => e.view.name === '/user/[id]') - expect(userView).toBeDefined() + const userView = viewEvents.find((e) => e.view.name === '/user/[id]') + expect(userView).toBeDefined() - // No view should have @sidebar in the name - expect(viewEvents.every((e) => !e.view.name?.includes('@sidebar'))).toBe(true) - }) + // No view should have @sidebar in the name + expect(viewEvents.every((e) => !e.view.name?.includes('@sidebar'))).toBe(true) + }) + } }) From 11afd177a996516615634a3fed4c993f10fdd183 Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Mon, 9 Mar 2026 17:44:50 +0100 Subject: [PATCH 10/12] Fix README --- packages/rum-nextjs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rum-nextjs/README.md b/packages/rum-nextjs/README.md index e003a461ed..87559ed87f 100644 --- a/packages/rum-nextjs/README.md +++ b/packages/rum-nextjs/README.md @@ -2,7 +2,7 @@ This package provides NextJS integration for Datadog Browser RUM, supporting both the App Router and Pages Router. -Requires Next.js v15.3+, which supports the [`instrumentation-client`][1] file convention. +Both routers require **Next.js v15.3+**, which supports the [`instrumentation-client`][1] file convention. [1]: https://nextjs.org/docs/app/api-reference/file-conventions/instrumentation-client From 4cb9c39dd1f9b689f36c9c7a83bc99dd67f05310 Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Tue, 10 Mar 2026 13:29:09 +0100 Subject: [PATCH 11/12] Refactor e2e test app. --- .prettierignore | 1 - eslint.config.mjs | 1 - scripts/build/build-test-apps.ts | 1 - test/apps/nextjs-app-router/next-env.d.ts | 1 + .../pages/_app.tsx | 2 +- .../pages/pages-router}/guides/[...slug].tsx | 2 +- .../pages/pages-router}/index.tsx | 4 +- .../pages/pages-router}/user/[id].tsx | 6 +- test/apps/nextjs-app-router/yarn.lock | 4 +- test/apps/nextjs-pages-router/.gitignore | 3 - .../instrumentation-client.js | 13 - test/apps/nextjs-pages-router/next-env.d.ts | 6 - test/apps/nextjs-pages-router/next.config.js | 8 - test/apps/nextjs-pages-router/package.json | 33 - .../nextjs-pages-router/public/favicon.ico | Bin 70 -> 0 bytes test/apps/nextjs-pages-router/tsconfig.json | 21 - test/apps/nextjs-pages-router/yarn.lock | 705 ------------------ test/e2e/lib/framework/createTest.ts | 9 +- test/e2e/lib/helpers/playwright.ts | 1 - test/e2e/playwright.base.config.ts | 8 - test/e2e/scenario/nextjsPlugin.scenario.ts | 36 +- 21 files changed, 38 insertions(+), 827 deletions(-) rename test/apps/{nextjs-pages-router => nextjs-app-router}/pages/_app.tsx (85%) rename test/apps/{nextjs-pages-router/pages => nextjs-app-router/pages/pages-router}/guides/[...slug].tsx (87%) rename test/apps/{nextjs-pages-router/pages => nextjs-app-router/pages/pages-router}/index.tsx (57%) rename test/apps/{nextjs-pages-router/pages => nextjs-app-router/pages/pages-router}/user/[id].tsx (58%) delete mode 100644 test/apps/nextjs-pages-router/.gitignore delete mode 100644 test/apps/nextjs-pages-router/instrumentation-client.js delete mode 100644 test/apps/nextjs-pages-router/next-env.d.ts delete mode 100644 test/apps/nextjs-pages-router/next.config.js delete mode 100644 test/apps/nextjs-pages-router/package.json delete mode 100644 test/apps/nextjs-pages-router/public/favicon.ico delete mode 100644 test/apps/nextjs-pages-router/tsconfig.json delete mode 100644 test/apps/nextjs-pages-router/yarn.lock diff --git a/.prettierignore b/.prettierignore index 8878e234c7..9cd8ea477e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,7 +7,6 @@ rum-events-format test/**/dist test/**/.next test/apps/nextjs-app-router/next-env.d.ts -test/apps/nextjs-pages-router/next-env.d.ts yarn.lock /docs /developer-extension/.output diff --git a/eslint.config.mjs b/eslint.config.mjs index 065af2c3bd..493f485793 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -31,7 +31,6 @@ export default tseslint.config( 'test/apps/react-heavy-spa', 'test/apps/react-shopist-like', 'test/apps/nextjs-app-router', - 'test/apps/nextjs-pages-router', 'sandbox', 'coverage', 'rum-events-format', diff --git a/scripts/build/build-test-apps.ts b/scripts/build/build-test-apps.ts index f69051c00a..a40349c503 100644 --- a/scripts/build/build-test-apps.ts +++ b/scripts/build/build-test-apps.ts @@ -21,7 +21,6 @@ runMain(async () => { await buildReactRouterv7App() await buildExtensions() buildApp('test/apps/nextjs-app-router') - buildApp('test/apps/nextjs-pages-router') printLog('Test apps and extensions built successfully.') }) diff --git a/test/apps/nextjs-app-router/next-env.d.ts b/test/apps/nextjs-app-router/next-env.d.ts index c4b7818fbb..0c7fad710c 100644 --- a/test/apps/nextjs-app-router/next-env.d.ts +++ b/test/apps/nextjs-app-router/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited diff --git a/test/apps/nextjs-pages-router/pages/_app.tsx b/test/apps/nextjs-app-router/pages/_app.tsx similarity index 85% rename from test/apps/nextjs-pages-router/pages/_app.tsx rename to test/apps/nextjs-app-router/pages/_app.tsx index 037e13005d..0f46b64b43 100644 --- a/test/apps/nextjs-pages-router/pages/_app.tsx +++ b/test/apps/nextjs-app-router/pages/_app.tsx @@ -7,7 +7,7 @@ export default function MyApp({ Component, pageProps }: AppProps) { <> diff --git a/test/apps/nextjs-pages-router/pages/guides/[...slug].tsx b/test/apps/nextjs-app-router/pages/pages-router/guides/[...slug].tsx similarity index 87% rename from test/apps/nextjs-pages-router/pages/guides/[...slug].tsx rename to test/apps/nextjs-app-router/pages/pages-router/guides/[...slug].tsx index 8a7ba73103..17dea646f4 100644 --- a/test/apps/nextjs-pages-router/pages/guides/[...slug].tsx +++ b/test/apps/nextjs-app-router/pages/pages-router/guides/[...slug].tsx @@ -8,7 +8,7 @@ export default function GuidesPage() { return (
    - ← Back to Home + ← Back to Home

    Guides: {slugParts.join('/')}

    This is a catch-all route testing slug normalization.

    diff --git a/test/apps/nextjs-pages-router/pages/index.tsx b/test/apps/nextjs-app-router/pages/pages-router/index.tsx similarity index 57% rename from test/apps/nextjs-pages-router/pages/index.tsx rename to test/apps/nextjs-app-router/pages/pages-router/index.tsx index f0497463c9..cc03d59a47 100644 --- a/test/apps/nextjs-pages-router/pages/index.tsx +++ b/test/apps/nextjs-app-router/pages/pages-router/index.tsx @@ -6,10 +6,10 @@ export default function HomePage() {

    Home

    • - Go to User 42 + Go to User 42
    • - Go to Guides 123 + Go to Guides 123
    diff --git a/test/apps/nextjs-pages-router/pages/user/[id].tsx b/test/apps/nextjs-app-router/pages/pages-router/user/[id].tsx similarity index 58% rename from test/apps/nextjs-pages-router/pages/user/[id].tsx rename to test/apps/nextjs-app-router/pages/pages-router/user/[id].tsx index 8a5a744d73..c750218817 100644 --- a/test/apps/nextjs-pages-router/pages/user/[id].tsx +++ b/test/apps/nextjs-app-router/pages/pages-router/user/[id].tsx @@ -7,11 +7,11 @@ export default function UserPage() { return (
    - ← Back to Home + ← Back to Home

    User {id}

    This is a dynamic route testing view name normalization.

    - Go to User 999 - Change query params + Go to User 999 + Change query params
    ) } diff --git a/test/apps/nextjs-app-router/yarn.lock b/test/apps/nextjs-app-router/yarn.lock index b28da17773..d4df8dfa3d 100644 --- a/test/apps/nextjs-app-router/yarn.lock +++ b/test/apps/nextjs-app-router/yarn.lock @@ -23,7 +23,7 @@ __metadata: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-app-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=8376d1&locator=nextjs-app-router%40workspace%3A." + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=9f1c94&locator=nextjs-app-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -35,7 +35,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/7e8ab4f2a420d8b721034ab6c9c15e8b64d9cfbbc18950d1e22ec81f862a386bd499472096700006432ce9a2ce5a4042a21a914a7ad66933d16553339db283b7 + checksum: 10c0/996e75cf3a5881735a3ec9a41b34d054beef9634d7f843f16c896c4a491b4bb08012b1008268151f5992f49d327d388784d5adc8f487c6f37375f8cd099dea5a languageName: node linkType: hard diff --git a/test/apps/nextjs-pages-router/.gitignore b/test/apps/nextjs-pages-router/.gitignore deleted file mode 100644 index 20e9477154..0000000000 --- a/test/apps/nextjs-pages-router/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.next -node_modules -.yarn/* \ No newline at end of file diff --git a/test/apps/nextjs-pages-router/instrumentation-client.js b/test/apps/nextjs-pages-router/instrumentation-client.js deleted file mode 100644 index e735bbf74e..0000000000 --- a/test/apps/nextjs-pages-router/instrumentation-client.js +++ /dev/null @@ -1,13 +0,0 @@ -import { datadogRum } from '@datadog/browser-rum' -import { nextjsPlugin } from '@datadog/browser-rum-nextjs' - -const params = new URLSearchParams(window.location.search) -const config = params.get('rum-config') -const context = params.get('rum-context') - -if (config && !datadogRum.getInitConfiguration()) { - datadogRum.init({ ...JSON.parse(config), plugins: [nextjsPlugin()] }) - if (context) { - datadogRum.setGlobalContext(JSON.parse(context)) - } -} diff --git a/test/apps/nextjs-pages-router/next-env.d.ts b/test/apps/nextjs-pages-router/next-env.d.ts deleted file mode 100644 index 7996d352f4..0000000000 --- a/test/apps/nextjs-pages-router/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -import "./.next/dev/types/routes.d.ts"; - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/test/apps/nextjs-pages-router/next.config.js b/test/apps/nextjs-pages-router/next.config.js deleted file mode 100644 index c77754a91e..0000000000 --- a/test/apps/nextjs-pages-router/next.config.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - turbopack: { - root: __dirname, - }, -} - -module.exports = nextConfig diff --git a/test/apps/nextjs-pages-router/package.json b/test/apps/nextjs-pages-router/package.json deleted file mode 100644 index ac8603b64f..0000000000 --- a/test/apps/nextjs-pages-router/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "nextjs-pages-router", - "private": true, - "scripts": { - "dev": "next dev -p 0", - "build": "next build", - "start": "next start -p 0" - }, - "dependencies": { - "@datadog/browser-rum": "file:../../../packages/rum/package.tgz", - "@datadog/browser-rum-nextjs": "file:../../../packages/rum-nextjs/package.tgz", - "next": "16.1.6", - "react": "19.2.3", - "react-dom": "19.2.3" - }, - "resolutions": { - "@datadog/browser-rum-core": "file:../../../packages/rum-core/package.tgz", - "@datadog/browser-core": "file:../../../packages/core/package.tgz", - "@datadog/browser-rum": "file:../../../packages/rum/package.tgz", - "@datadog/browser-rum-nextjs": "file:../../../packages/rum-nextjs/package.tgz", - "@datadog/browser-rum-slim": "file:../../../packages/rum-slim/package.tgz", - "@datadog/browser-worker": "file:../../../packages/worker/package.tgz" - }, - "devDependencies": { - "@types/node": "22.16.0", - "@types/react": "19.2.8", - "@types/react-dom": "19.2.3", - "typescript": "5.9.3" - }, - "volta": { - "extends": "../../../package.json" - } -} diff --git a/test/apps/nextjs-pages-router/public/favicon.ico b/test/apps/nextjs-pages-router/public/favicon.ico deleted file mode 100644 index bdeadc4abcd87b8f6ce1d73fca4ddc53a16c5e86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70 pcmZQzU<5%%1|TWHV8Fn@AO^%5KnxUOU;@(;KoUFn|NlQ&0suBD1El}} diff --git a/test/apps/nextjs-pages-router/tsconfig.json b/test/apps/nextjs-pages-router/tsconfig.json deleted file mode 100644 index 6bc6207a59..0000000000 --- a/test/apps/nextjs-pages-router/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "incremental": true, - "plugins": [{ "name": "next" }], - "paths": { "@/*": ["./*"] }, - "target": "ES2017" - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/test/apps/nextjs-pages-router/yarn.lock b/test/apps/nextjs-pages-router/yarn.lock deleted file mode 100644 index ea2a02faef..0000000000 --- a/test/apps/nextjs-pages-router/yarn.lock +++ /dev/null @@ -1,705 +0,0 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 8 - cacheKey: 10c0 - -"@datadog/browser-core@file:../../../packages/core/package.tgz::locator=nextjs-pages-router%40workspace%3A.": - version: 6.30.1 - resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=c45ebb&locator=nextjs-pages-router%40workspace%3A." - checksum: 10c0/19a0212b06a2d6d1bf74a47c3c3be3e1bf8ce6c78c403e6968a317d4e5da95901d78297beafa025fa2e97376bfc0a81ad23c9c677994b2912139fdafc6aead44 - languageName: node - linkType: hard - -"@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz::locator=nextjs-pages-router%40workspace%3A.": - version: 6.30.1 - resolution: "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz#../../../packages/rum-core/package.tgz::hash=acbd3f&locator=nextjs-pages-router%40workspace%3A." - dependencies: - "@datadog/browser-core": "npm:6.30.1" - checksum: 10c0/4e2fe3cf78c7cbe16530bb4cf732922ae7866c8df233dfcbf1bd14380c348336c4abcf703b7f159fde344bbee0e5382169c58150e64f940a7949ded3e7cf6f81 - languageName: node - linkType: hard - -"@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-pages-router%40workspace%3A.": - version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=8376d1&locator=nextjs-pages-router%40workspace%3A." - dependencies: - "@datadog/browser-core": "npm:6.30.1" - "@datadog/browser-rum-core": "npm:6.30.1" - peerDependencies: - next: ">=13.0.0" - react: ">=18.0.0" - peerDependenciesMeta: - next: - optional: true - react: - optional: true - checksum: 10c0/7e8ab4f2a420d8b721034ab6c9c15e8b64d9cfbbc18950d1e22ec81f862a386bd499472096700006432ce9a2ce5a4042a21a914a7ad66933d16553339db283b7 - languageName: node - linkType: hard - -"@datadog/browser-rum@file:../../../packages/rum/package.tgz::locator=nextjs-pages-router%40workspace%3A.": - version: 6.30.1 - resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=9cfd3b&locator=nextjs-pages-router%40workspace%3A." - dependencies: - "@datadog/browser-core": "npm:6.30.1" - "@datadog/browser-rum-core": "npm:6.30.1" - peerDependencies: - "@datadog/browser-logs": 6.30.1 - peerDependenciesMeta: - "@datadog/browser-logs": - optional: true - checksum: 10c0/95237d8e152fffc25e395f8b417eff467bd90c367ab9017d7bab90eb77f626dcc75820e7163354e154175c82c091318cbf3dd5ea5bbd5a0d903660c2f939fb4e - languageName: node - linkType: hard - -"@emnapi/runtime@npm:^1.7.0": - version: 1.8.1 - resolution: "@emnapi/runtime@npm:1.8.1" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/f4929d75e37aafb24da77d2f58816761fe3f826aad2e37fa6d4421dac9060cbd5098eea1ac3c9ecc4526b89deb58153852fa432f87021dc57863f2ff726d713f - languageName: node - linkType: hard - -"@img/colour@npm:^1.0.0": - version: 1.1.0 - resolution: "@img/colour@npm:1.1.0" - checksum: 10c0/2ebea2c0bbaee73b99badcefa04e1e71d83f36e5369337d3121dca841f4569533c4e2faddda6d62dd247f0d5cca143711f9446c59bcce81e427ba433a7a94a17 - languageName: node - linkType: hard - -"@img/sharp-darwin-arm64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-darwin-arm64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-darwin-arm64": - optional: true - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@img/sharp-darwin-x64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-darwin-x64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-darwin-x64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-darwin-x64": - optional: true - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@img/sharp-libvips-darwin-arm64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.4" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@img/sharp-libvips-darwin-x64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.4" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-arm64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.4" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-arm@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-arm@npm:1.2.4" - conditions: os=linux & cpu=arm & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-ppc64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.4" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-riscv64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-riscv64@npm:1.2.4" - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-s390x@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.4" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linux-x64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linux-x64@npm:1.2.4" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@img/sharp-libvips-linuxmusl-x64@npm:1.2.4": - version: 1.2.4 - resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.4" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@img/sharp-linux-arm64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-arm64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-arm64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-arm64": - optional: true - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-arm@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-arm@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-arm": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-arm": - optional: true - conditions: os=linux & cpu=arm & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-ppc64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-ppc64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-ppc64": - optional: true - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-riscv64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-riscv64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-riscv64": - optional: true - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-s390x@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-s390x@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-s390x": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-s390x": - optional: true - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linux-x64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linux-x64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linux-x64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linux-x64": - optional: true - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@img/sharp-linuxmusl-arm64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linuxmusl-arm64": - optional: true - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@img/sharp-linuxmusl-x64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-linuxmusl-x64@npm:0.34.5" - dependencies: - "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" - dependenciesMeta: - "@img/sharp-libvips-linuxmusl-x64": - optional: true - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@img/sharp-wasm32@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-wasm32@npm:0.34.5" - dependencies: - "@emnapi/runtime": "npm:^1.7.0" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@img/sharp-win32-arm64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-win32-arm64@npm:0.34.5" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@img/sharp-win32-ia32@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-win32-ia32@npm:0.34.5" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@img/sharp-win32-x64@npm:0.34.5": - version: 0.34.5 - resolution: "@img/sharp-win32-x64@npm:0.34.5" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@next/env@npm:16.1.6": - version: 16.1.6 - resolution: "@next/env@npm:16.1.6" - checksum: 10c0/ed7023edb94b9b2e5da3f9c99d08b614da9757c1edd0ecec792fce4d336b4f0c64db1a84955e07cfbd848b9e61c4118fff28f4098cd7b0a7f97814a90565ebe6 - languageName: node - linkType: hard - -"@next/swc-darwin-arm64@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-darwin-arm64@npm:16.1.6" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@next/swc-darwin-x64@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-darwin-x64@npm:16.1.6" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@next/swc-linux-arm64-gnu@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-linux-arm64-gnu@npm:16.1.6" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@next/swc-linux-arm64-musl@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-linux-arm64-musl@npm:16.1.6" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@next/swc-linux-x64-gnu@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-linux-x64-gnu@npm:16.1.6" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@next/swc-linux-x64-musl@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-linux-x64-musl@npm:16.1.6" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@next/swc-win32-arm64-msvc@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-win32-arm64-msvc@npm:16.1.6" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@next/swc-win32-x64-msvc@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-win32-x64-msvc@npm:16.1.6" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@swc/helpers@npm:0.5.15": - version: 0.5.15 - resolution: "@swc/helpers@npm:0.5.15" - dependencies: - tslib: "npm:^2.8.0" - checksum: 10c0/33002f74f6f885f04c132960835fdfc474186983ea567606db62e86acd0680ca82f34647e8e610f4e1e422d1c16fce729dde22cd3b797ab1fd9061a825dabca4 - languageName: node - linkType: hard - -"@types/node@npm:22.16.0": - version: 22.16.0 - resolution: "@types/node@npm:22.16.0" - dependencies: - undici-types: "npm:~6.21.0" - checksum: 10c0/6219b521062f6c38d4d85ebd25807bd7f2bc703a5acba24e2c6716938d9d6cefd6fafd7b5156f61580eb58a0d82e8921751b778655675389631d813e5f261c03 - languageName: node - linkType: hard - -"@types/react-dom@npm:19.2.3": - version: 19.2.3 - resolution: "@types/react-dom@npm:19.2.3" - peerDependencies: - "@types/react": ^19.2.0 - checksum: 10c0/b486ebe0f4e2fb35e2e108df1d8fc0927ca5d6002d5771e8a739de11239fe62d0e207c50886185253c99eb9dedfeeb956ea7429e5ba17f6693c7acb4c02f8cd1 - languageName: node - linkType: hard - -"@types/react@npm:19.2.8": - version: 19.2.8 - resolution: "@types/react@npm:19.2.8" - dependencies: - csstype: "npm:^3.2.2" - checksum: 10c0/832834998c4ee971fca72ecf1eb95dc924ad3931a2112c687a4dae498aabd115c5fa4db09186853e34a646226b0223808c8f867df03d17601168f9cf119448de - languageName: node - linkType: hard - -"baseline-browser-mapping@npm:^2.8.3": - version: 2.10.0 - resolution: "baseline-browser-mapping@npm:2.10.0" - bin: - baseline-browser-mapping: dist/cli.cjs - checksum: 10c0/da9c3ec0fcd7f325226a47d2142794d41706b6e0a405718a2c15410bbdb72aacadd65738bedef558c6f1b106ed19458cb25b06f63b66df2c284799905dbbd003 - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001579": - version: 1.0.30001777 - resolution: "caniuse-lite@npm:1.0.30001777" - checksum: 10c0/e35443fa7c470edc06e315297cca706790840e96983fff12dfe502a4b123d6e4a64b9b4e8e35fb2f5bb60c31b24fbda93d76b2f700ce183df474671236fa7a4a - languageName: node - linkType: hard - -"client-only@npm:0.0.1": - version: 0.0.1 - resolution: "client-only@npm:0.0.1" - checksum: 10c0/9d6cfd0c19e1c96a434605added99dff48482152af791ec4172fb912a71cff9027ff174efd8cdb2160cc7f377543e0537ffc462d4f279bc4701de3f2a3c4b358 - languageName: node - linkType: hard - -"csstype@npm:^3.2.2": - version: 3.2.3 - resolution: "csstype@npm:3.2.3" - checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce - languageName: node - linkType: hard - -"detect-libc@npm:^2.1.2": - version: 2.1.2 - resolution: "detect-libc@npm:2.1.2" - checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 - languageName: node - linkType: hard - -"nanoid@npm:^3.3.6": - version: 3.3.11 - resolution: "nanoid@npm:3.3.11" - bin: - nanoid: bin/nanoid.cjs - checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b - languageName: node - linkType: hard - -"next@npm:16.1.6": - version: 16.1.6 - resolution: "next@npm:16.1.6" - dependencies: - "@next/env": "npm:16.1.6" - "@next/swc-darwin-arm64": "npm:16.1.6" - "@next/swc-darwin-x64": "npm:16.1.6" - "@next/swc-linux-arm64-gnu": "npm:16.1.6" - "@next/swc-linux-arm64-musl": "npm:16.1.6" - "@next/swc-linux-x64-gnu": "npm:16.1.6" - "@next/swc-linux-x64-musl": "npm:16.1.6" - "@next/swc-win32-arm64-msvc": "npm:16.1.6" - "@next/swc-win32-x64-msvc": "npm:16.1.6" - "@swc/helpers": "npm:0.5.15" - baseline-browser-mapping: "npm:^2.8.3" - caniuse-lite: "npm:^1.0.30001579" - postcss: "npm:8.4.31" - sharp: "npm:^0.34.4" - styled-jsx: "npm:5.1.6" - peerDependencies: - "@opentelemetry/api": ^1.1.0 - "@playwright/test": ^1.51.1 - babel-plugin-react-compiler: "*" - react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - sass: ^1.3.0 - dependenciesMeta: - "@next/swc-darwin-arm64": - optional: true - "@next/swc-darwin-x64": - optional: true - "@next/swc-linux-arm64-gnu": - optional: true - "@next/swc-linux-arm64-musl": - optional: true - "@next/swc-linux-x64-gnu": - optional: true - "@next/swc-linux-x64-musl": - optional: true - "@next/swc-win32-arm64-msvc": - optional: true - "@next/swc-win32-x64-msvc": - optional: true - sharp: - optional: true - peerDependenciesMeta: - "@opentelemetry/api": - optional: true - "@playwright/test": - optional: true - babel-plugin-react-compiler: - optional: true - sass: - optional: true - bin: - next: dist/bin/next - checksum: 10c0/543766bf879bb5a5d454dc18cb302953270a92efba1d01dd028ea83c64b69573ce7d6e6c3759ecbaabec0a84131b0237263c24d1ccd7c8a97205e776dcd34e0b - languageName: node - linkType: hard - -"nextjs-pages-router@workspace:.": - version: 0.0.0-use.local - resolution: "nextjs-pages-router@workspace:." - dependencies: - "@datadog/browser-rum": "file:../../../packages/rum/package.tgz" - "@datadog/browser-rum-nextjs": "file:../../../packages/rum-nextjs/package.tgz" - "@types/node": "npm:22.16.0" - "@types/react": "npm:19.2.8" - "@types/react-dom": "npm:19.2.3" - next: "npm:16.1.6" - react: "npm:19.2.3" - react-dom: "npm:19.2.3" - typescript: "npm:5.9.3" - languageName: unknown - linkType: soft - -"picocolors@npm:^1.0.0": - version: 1.1.1 - resolution: "picocolors@npm:1.1.1" - checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 - languageName: node - linkType: hard - -"postcss@npm:8.4.31": - version: 8.4.31 - resolution: "postcss@npm:8.4.31" - dependencies: - nanoid: "npm:^3.3.6" - picocolors: "npm:^1.0.0" - source-map-js: "npm:^1.0.2" - checksum: 10c0/748b82e6e5fc34034dcf2ae88ea3d11fd09f69b6c50ecdd3b4a875cfc7cdca435c958b211e2cb52355422ab6fccb7d8f2f2923161d7a1b281029e4a913d59acf - languageName: node - linkType: hard - -"react-dom@npm:19.2.3": - version: 19.2.3 - resolution: "react-dom@npm:19.2.3" - dependencies: - scheduler: "npm:^0.27.0" - peerDependencies: - react: ^19.2.3 - checksum: 10c0/dc43f7ede06f46f3acc16ee83107c925530de9b91d1d0b3824583814746ff4c498ea64fd65cd83aba363205268adff52e2827c582634ae7b15069deaeabc4892 - languageName: node - linkType: hard - -"react@npm:19.2.3": - version: 19.2.3 - resolution: "react@npm:19.2.3" - checksum: 10c0/094220b3ba3a76c1b668f972ace1dd15509b157aead1b40391d1c8e657e720c201d9719537375eff08f5e0514748c0319063392a6f000e31303aafc4471f1436 - languageName: node - linkType: hard - -"scheduler@npm:^0.27.0": - version: 0.27.0 - resolution: "scheduler@npm:0.27.0" - checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 - languageName: node - linkType: hard - -"semver@npm:^7.7.3": - version: 7.7.4 - resolution: "semver@npm:7.7.4" - bin: - semver: bin/semver.js - checksum: 10c0/5215ad0234e2845d4ea5bb9d836d42b03499546ddafb12075566899fc617f68794bb6f146076b6881d755de17d6c6cc73372555879ec7dce2c2feee947866ad2 - languageName: node - linkType: hard - -"sharp@npm:^0.34.4": - version: 0.34.5 - resolution: "sharp@npm:0.34.5" - dependencies: - "@img/colour": "npm:^1.0.0" - "@img/sharp-darwin-arm64": "npm:0.34.5" - "@img/sharp-darwin-x64": "npm:0.34.5" - "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" - "@img/sharp-libvips-darwin-x64": "npm:1.2.4" - "@img/sharp-libvips-linux-arm": "npm:1.2.4" - "@img/sharp-libvips-linux-arm64": "npm:1.2.4" - "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" - "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" - "@img/sharp-libvips-linux-s390x": "npm:1.2.4" - "@img/sharp-libvips-linux-x64": "npm:1.2.4" - "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" - "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" - "@img/sharp-linux-arm": "npm:0.34.5" - "@img/sharp-linux-arm64": "npm:0.34.5" - "@img/sharp-linux-ppc64": "npm:0.34.5" - "@img/sharp-linux-riscv64": "npm:0.34.5" - "@img/sharp-linux-s390x": "npm:0.34.5" - "@img/sharp-linux-x64": "npm:0.34.5" - "@img/sharp-linuxmusl-arm64": "npm:0.34.5" - "@img/sharp-linuxmusl-x64": "npm:0.34.5" - "@img/sharp-wasm32": "npm:0.34.5" - "@img/sharp-win32-arm64": "npm:0.34.5" - "@img/sharp-win32-ia32": "npm:0.34.5" - "@img/sharp-win32-x64": "npm:0.34.5" - detect-libc: "npm:^2.1.2" - semver: "npm:^7.7.3" - dependenciesMeta: - "@img/sharp-darwin-arm64": - optional: true - "@img/sharp-darwin-x64": - optional: true - "@img/sharp-libvips-darwin-arm64": - optional: true - "@img/sharp-libvips-darwin-x64": - optional: true - "@img/sharp-libvips-linux-arm": - optional: true - "@img/sharp-libvips-linux-arm64": - optional: true - "@img/sharp-libvips-linux-ppc64": - optional: true - "@img/sharp-libvips-linux-riscv64": - optional: true - "@img/sharp-libvips-linux-s390x": - optional: true - "@img/sharp-libvips-linux-x64": - optional: true - "@img/sharp-libvips-linuxmusl-arm64": - optional: true - "@img/sharp-libvips-linuxmusl-x64": - optional: true - "@img/sharp-linux-arm": - optional: true - "@img/sharp-linux-arm64": - optional: true - "@img/sharp-linux-ppc64": - optional: true - "@img/sharp-linux-riscv64": - optional: true - "@img/sharp-linux-s390x": - optional: true - "@img/sharp-linux-x64": - optional: true - "@img/sharp-linuxmusl-arm64": - optional: true - "@img/sharp-linuxmusl-x64": - optional: true - "@img/sharp-wasm32": - optional: true - "@img/sharp-win32-arm64": - optional: true - "@img/sharp-win32-ia32": - optional: true - "@img/sharp-win32-x64": - optional: true - checksum: 10c0/fd79e29df0597a7d5704b8461c51f944ead91a5243691697be6e8243b966402beda53ddc6f0a53b96ea3cb8221f0b244aa588114d3ebf8734fb4aefd41ab802f - languageName: node - linkType: hard - -"source-map-js@npm:^1.0.2": - version: 1.2.1 - resolution: "source-map-js@npm:1.2.1" - checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf - languageName: node - linkType: hard - -"styled-jsx@npm:5.1.6": - version: 5.1.6 - resolution: "styled-jsx@npm:5.1.6" - dependencies: - client-only: "npm:0.0.1" - peerDependencies: - react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - peerDependenciesMeta: - "@babel/core": - optional: true - babel-plugin-macros: - optional: true - checksum: 10c0/ace50e7ea5ae5ae6a3b65a50994c51fca6ae7df9c7ecfd0104c36be0b4b3a9c5c1a2374d16e2a11e256d0b20be6d47256d768ecb4f91ab390f60752a075780f5 - languageName: node - linkType: hard - -"tslib@npm:^2.4.0, tslib@npm:^2.8.0": - version: 2.8.1 - resolution: "tslib@npm:2.8.1" - checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 - languageName: node - linkType: hard - -"typescript@npm:5.9.3": - version: 5.9.3 - resolution: "typescript@npm:5.9.3" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 - languageName: node - linkType: hard - -"typescript@patch:typescript@npm%3A5.9.3#optional!builtin": - version: 5.9.3 - resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 - languageName: node - linkType: hard - -"undici-types@npm:~6.21.0": - version: 6.21.0 - resolution: "undici-types@npm:6.21.0" - checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 - languageName: node - linkType: hard diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index 4a9b96d62b..53beddde22 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -9,7 +9,7 @@ import { BrowserLogsManager, deleteAllCookies, getBrowserName, sendXhr } from '. import { DEFAULT_LOGS_CONFIGURATION, DEFAULT_RUM_CONFIGURATION } from '../helpers/configuration' import { validateRumFormat } from '../helpers/validation' import type { BrowserConfiguration } from '../../../browsers.conf' -import { NEXTJS_APP_ROUTER_PORT, NEXTJS_PAGES_ROUTER_PORT } from '../helpers/playwright' +import { NEXTJS_APP_ROUTER_PORT } from '../helpers/playwright' import { IntakeRegistry } from './intakeRegistry' import { flushEvents } from './flushEvents' import type { Servers } from './httpServers' @@ -112,9 +112,12 @@ class TestBuilder { } withNextjsApp(router: 'app' | 'pages') { - const port = router === 'app' ? NEXTJS_APP_ROUTER_PORT : NEXTJS_PAGES_ROUTER_PORT + const basePath = router === 'pages' ? '/pages-router' : '' this.baseUrlHooks.push((baseUrl, servers, { rum, context }) => { - baseUrl.port = port + baseUrl.port = NEXTJS_APP_ROUTER_PORT + if (basePath) { + baseUrl.pathname = basePath + (baseUrl.pathname === '/' ? '' : baseUrl.pathname) + } if (rum) { baseUrl.searchParams.set('rum-config', formatConfiguration(rum, servers)) } diff --git a/test/e2e/lib/helpers/playwright.ts b/test/e2e/lib/helpers/playwright.ts index 433e7bdd1c..abd3220242 100644 --- a/test/e2e/lib/helpers/playwright.ts +++ b/test/e2e/lib/helpers/playwright.ts @@ -5,7 +5,6 @@ import packageJson from '../../../../package.json' with { type: 'json' } export const DEV_SERVER_BASE_URL = `http://localhost:${process.env.DEV_SERVER_PORT}` export const NEXTJS_APP_ROUTER_PORT = process.env.NEXTJS_APP_ROUTER_PORT! -export const NEXTJS_PAGES_ROUTER_PORT = process.env.NEXTJS_PAGES_ROUTER_PORT! export function getPlaywrightConfigBrowserName(name: string): PlaywrightWorkerOptions['browserName'] { if (name.includes('firefox')) { diff --git a/test/e2e/playwright.base.config.ts b/test/e2e/playwright.base.config.ts index 2edd191557..e31730b62c 100644 --- a/test/e2e/playwright.base.config.ts +++ b/test/e2e/playwright.base.config.ts @@ -51,13 +51,5 @@ export const config: Config = { stdout: /- Local:\s+http:\/\/localhost:(?\d+)/, }, }, - { - stdout: 'pipe' as const, - cwd: path.join(__dirname, '../apps/nextjs-pages-router'), - command: isLocal ? 'yarn dev' : 'yarn start', - wait: { - stdout: /- Local:\s+http:\/\/localhost:(?\d+)/, - }, - }, ], } diff --git a/test/e2e/scenario/nextjsPlugin.scenario.ts b/test/e2e/scenario/nextjsPlugin.scenario.ts index 85866e7a24..a806c15d3b 100644 --- a/test/e2e/scenario/nextjsPlugin.scenario.ts +++ b/test/e2e/scenario/nextjsPlugin.scenario.ts @@ -1,9 +1,16 @@ import { test, expect } from '@playwright/test' import { createTest } from '../lib/framework' ;[ - { name: 'nextjs app router', router: 'app' as const }, - { name: 'nextjs pages router', router: 'pages' as const }, -].forEach(({ name, router }) => { + { name: 'nextjs app router', router: 'app' as const, viewPrefix: '', homeUrlPattern: '**/' }, + { + name: 'nextjs pages router', + router: 'pages' as const, + viewPrefix: '/pages-router', + homeUrlPattern: /\/pages-router(\?|$)/, + }, +].forEach(({ name, router, viewPrefix, homeUrlPattern }) => { + const homeViewName = viewPrefix || '/' + test.describe(name, () => { createTest('should track initial home view') .withRum() @@ -15,7 +22,7 @@ import { createTest } from '../lib/framework' await flushEvents() const viewEvents = intakeRegistry.rumViewEvents - const homeView = viewEvents.find((e) => e.view.name === '/' && e.view.loading_type === 'initial_load') + const homeView = viewEvents.find((e) => e.view.name === homeViewName && e.view.loading_type === 'initial_load') expect(homeView).toBeDefined() }) @@ -30,7 +37,7 @@ import { createTest } from '../lib/framework' await page.waitForURL('**/guides/123') await page.click('text=Back to Home') - await page.waitForURL('**/') + await page.waitForURL(homeUrlPattern) await page.click('text=Go to User 42') await page.waitForURL('**/user/42?admin=true') @@ -41,20 +48,20 @@ import { createTest } from '../lib/framework' const viewEvents = intakeRegistry.rumViewEvents - const homeView = viewEvents.find((e) => e.view.name === '/') + const homeView = viewEvents.find((e) => e.view.name === homeViewName) expect(homeView).toBeDefined() - const guidesView = viewEvents.find((e) => e.view.name === '/guides/[...slug]') + const guidesView = viewEvents.find((e) => e.view.name === `${viewPrefix}/guides/[...slug]`) expect(guidesView).toBeDefined() expect(guidesView?.view.loading_type).toBe('route_change') expect(guidesView?.view.url).toContain('/guides/123') expect(guidesView?.view.referrer).toBe(baseUrl) - const userView = viewEvents.find((e) => e.view.name === '/user/[id]') + const userView = viewEvents.find((e) => e.view.name === `${viewPrefix}/user/[id]`) expect(userView).toBeDefined() expect(userView?.view.loading_type).toBe('route_change') - expect(userView?.view.url).toBe(`${baseOrigin}/user/42?admin=true`) - expect(userView?.view.referrer).toBe(`${baseOrigin}/`) + expect(userView?.view.url).toBe(`${baseOrigin}${viewPrefix}/user/42?admin=true`) + expect(userView?.view.referrer).toBe(`${baseOrigin}${homeViewName}`) }) createTest('should track SPA navigation with loading_time') @@ -71,7 +78,8 @@ import { createTest } from '../lib/framework' const viewEvents = intakeRegistry.rumViewEvents const homeView = viewEvents.find( - (e) => e.view.name === '/' && e.view.loading_type === 'initial_load' && e.view.loading_time !== undefined + (e) => + e.view.name === homeViewName && e.view.loading_type === 'initial_load' && e.view.loading_time !== undefined ) expect(homeView).toBeDefined() expect(homeView?.view.loading_time).toBeDefined() @@ -90,7 +98,7 @@ import { createTest } from '../lib/framework' await flushEvents() - const userView = intakeRegistry.rumViewEvents.find((e) => e.view.name === '/user/[id]') + const userView = intakeRegistry.rumViewEvents.find((e) => e.view.name === `${viewPrefix}/user/[id]`) expect(userView).toBeDefined() // No view should have been created for the query-param-only navigation @@ -114,10 +122,10 @@ import { createTest } from '../lib/framework' await flushEvents() const user42View = intakeRegistry.rumViewEvents.find( - (e) => e.view.name === '/user/[id]' && e.view.url?.includes('/user/42') + (e) => e.view.name === `${viewPrefix}/user/[id]` && e.view.url?.includes('/user/42') ) const user999View = intakeRegistry.rumViewEvents.find( - (e) => e.view.name === '/user/[id]' && e.view.url?.includes('/user/999') + (e) => e.view.name === `${viewPrefix}/user/[id]` && e.view.url?.includes('/user/999') ) expect(user42View).toBeDefined() expect(user999View).toBeDefined() From 3c6b98b3085f23a9176f152cfafcd3a662919be3 Mon Sep 17 00:00:00 2001 From: "beltran.bulbarella" Date: Tue, 10 Mar 2026 14:17:18 +0100 Subject: [PATCH 12/12] Add unit tests for nextJSRouter --- .../computeViewNameFromParams.spec.ts | 0 .../computeViewNameFromParams.ts | 0 .../{ => nextJSRouter}/datadogAppRouter.tsx | 9 +- .../{ => nextJSRouter}/datadogPagesRouter.tsx | 7 +- .../nextJSRouter/datadogRouters.spec.ts | 158 ++++++++++++++++++ packages/rum-nextjs/src/entries/main.ts | 4 +- .../rum-nextjs/test/initializeNextjsPlugin.ts | 14 ++ .../rum-nextjs/test/stubs/nextNavigation.ts | 9 + packages/rum-nextjs/test/stubs/nextRouter.ts | 5 + test/apps/nextjs-app-router/next-env.d.ts | 2 +- test/apps/nextjs-app-router/yarn.lock | 4 +- test/unit/karma.base.conf.js | 10 +- 12 files changed, 209 insertions(+), 13 deletions(-) rename packages/rum-nextjs/src/domain/{ => nextJSRouter}/computeViewNameFromParams.spec.ts (100%) rename packages/rum-nextjs/src/domain/{ => nextJSRouter}/computeViewNameFromParams.ts (100%) rename packages/rum-nextjs/src/domain/{ => nextJSRouter}/datadogAppRouter.tsx (61%) rename packages/rum-nextjs/src/domain/{ => nextJSRouter}/datadogPagesRouter.tsx (77%) create mode 100644 packages/rum-nextjs/src/domain/nextJSRouter/datadogRouters.spec.ts create mode 100644 packages/rum-nextjs/test/initializeNextjsPlugin.ts create mode 100644 packages/rum-nextjs/test/stubs/nextNavigation.ts create mode 100644 packages/rum-nextjs/test/stubs/nextRouter.ts diff --git a/packages/rum-nextjs/src/domain/computeViewNameFromParams.spec.ts b/packages/rum-nextjs/src/domain/nextJSRouter/computeViewNameFromParams.spec.ts similarity index 100% rename from packages/rum-nextjs/src/domain/computeViewNameFromParams.spec.ts rename to packages/rum-nextjs/src/domain/nextJSRouter/computeViewNameFromParams.spec.ts diff --git a/packages/rum-nextjs/src/domain/computeViewNameFromParams.ts b/packages/rum-nextjs/src/domain/nextJSRouter/computeViewNameFromParams.ts similarity index 100% rename from packages/rum-nextjs/src/domain/computeViewNameFromParams.ts rename to packages/rum-nextjs/src/domain/nextJSRouter/computeViewNameFromParams.ts diff --git a/packages/rum-nextjs/src/domain/datadogAppRouter.tsx b/packages/rum-nextjs/src/domain/nextJSRouter/datadogAppRouter.tsx similarity index 61% rename from packages/rum-nextjs/src/domain/datadogAppRouter.tsx rename to packages/rum-nextjs/src/domain/nextJSRouter/datadogAppRouter.tsx index 3d0ee3603a..50bc025ead 100644 --- a/packages/rum-nextjs/src/domain/datadogAppRouter.tsx +++ b/packages/rum-nextjs/src/domain/nextJSRouter/datadogAppRouter.tsx @@ -2,13 +2,14 @@ import { useRef } from 'react' import { usePathname, useParams } from 'next/navigation' +import { mockable } from '@datadog/browser-core' +import { startNextjsView } from '../nextjsPlugin' import { computeViewNameFromParams } from './computeViewNameFromParams' -import { startNextjsView } from './nextjsPlugin' export function DatadogAppRouter() { - const pathname = usePathname() - const params = useParams() - const previousPathname = useRef(null) + const pathname = mockable(usePathname)() + const params = mockable(useParams)() + const previousPathname = mockable(useRef)(null) if (previousPathname.current !== pathname) { previousPathname.current = pathname diff --git a/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx b/packages/rum-nextjs/src/domain/nextJSRouter/datadogPagesRouter.tsx similarity index 77% rename from packages/rum-nextjs/src/domain/datadogPagesRouter.tsx rename to packages/rum-nextjs/src/domain/nextJSRouter/datadogPagesRouter.tsx index 458017cf80..e320dfe951 100644 --- a/packages/rum-nextjs/src/domain/datadogPagesRouter.tsx +++ b/packages/rum-nextjs/src/domain/nextJSRouter/datadogPagesRouter.tsx @@ -1,10 +1,11 @@ import { useRef } from 'react' import { useRouter } from 'next/router' -import { startNextjsView } from './nextjsPlugin' +import { mockable } from '@datadog/browser-core' +import { startNextjsView } from '../nextjsPlugin' export function DatadogPagesRouter() { - const router = useRouter() - const previousPath = useRef(null) + const router = mockable(useRouter)() + const previousPath = mockable(useRef)(null) if (!router.isReady) { return null diff --git a/packages/rum-nextjs/src/domain/nextJSRouter/datadogRouters.spec.ts b/packages/rum-nextjs/src/domain/nextJSRouter/datadogRouters.spec.ts new file mode 100644 index 0000000000..8e2f03e535 --- /dev/null +++ b/packages/rum-nextjs/src/domain/nextJSRouter/datadogRouters.spec.ts @@ -0,0 +1,158 @@ +import { useRef } from 'react' +import { usePathname, useParams } from 'next/navigation' +import { useRouter } from 'next/router' +import { replaceMockable } from '@datadog/browser-core/test' +import { initializeNextjsPlugin } from '../../../test/initializeNextjsPlugin' +import { DatadogAppRouter } from './datadogAppRouter' +import { DatadogPagesRouter } from './datadogPagesRouter' + +function mockRouter(partial: { isReady: boolean; asPath: string; pathname: string }): ReturnType { + return partial as unknown as ReturnType +} + +;[ + { + name: 'DatadogAppRouter', + component: DatadogAppRouter, + setupBeforeEach: () => { + // usePathname() returns path only — strip query/hash to match real Next.js behavior + let asPath = '' + let params: Record = {} + replaceMockable(usePathname, () => asPath.split(/[?#]/)[0]) + replaceMockable(useParams, () => params) + return (newAsPath: string, newParams: Record = {}) => { + asPath = newAsPath + params = newParams + } + }, + }, + { + name: 'DatadogPagesRouter', + component: DatadogPagesRouter, + setupBeforeEach: () => { + let asPath = '' + let routePattern = '' + replaceMockable(useRouter, () => mockRouter({ isReady: true, asPath, pathname: routePattern })) + return (newAsPath: string, newRoutePattern: string) => { + asPath = newAsPath + routePattern = newRoutePattern + } + }, + }, +].forEach(({ name, component, setupBeforeEach }) => { + describe(name, () => { + let ref: { current: string | null } + let setupRoute: (asPath: string, routePattern: string, params?: Record) => void + + beforeEach(() => { + ref = { current: null } + replaceMockable(useRef, () => ref) + const setup = setupBeforeEach() + // Normalize: AppRouter derives the view name from params, PagesRouter from the route pattern. + // Wrap each setup so the shared tests can pass (asPath, routePattern, params) uniformly. + if (name === 'DatadogAppRouter') { + setupRoute = (asPath: string, _routePattern: string, params?: Record) => { + ;(setup as (asPath: string, params?: Record) => void)(asPath, params) + } + } else { + setupRoute = (asPath: string, routePattern: string) => { + ;(setup as (asPath: string, routePattern: string) => void)(asPath, routePattern) + } + } + }) + + it('starts a view on first render', () => { + const startViewSpy = initializeNextjsPlugin() + setupRoute('/about', '/about') + + component() + + expect(startViewSpy).toHaveBeenCalledOnceWith({ name: '/about', url: undefined }) + }) + + it('does not start a new view when re-rendered with the same path', () => { + const startViewSpy = initializeNextjsPlugin() + setupRoute('/about', '/about') + + component() + component() + + expect(startViewSpy).toHaveBeenCalledTimes(1) + }) + + it('starts a new view when path changes', () => { + const startViewSpy = initializeNextjsPlugin() + setupRoute('/about', '/about') + + component() + setupRoute('/contact', '/contact') + component() + + expect(startViewSpy).toHaveBeenCalledTimes(2) + expect(startViewSpy.calls.mostRecent().args[0]).toEqual({ name: '/contact', url: undefined }) + }) + + it('uses the route pattern as the view name for dynamic routes', () => { + const startViewSpy = initializeNextjsPlugin() + setupRoute('/user/42', '/user/[id]', { id: '42' }) + + component() + + expect(startViewSpy).toHaveBeenCalledOnceWith({ name: '/user/[id]', url: undefined }) + }) + + it('starts a new view for the same route pattern with different dynamic segments', () => { + const startViewSpy = initializeNextjsPlugin() + setupRoute('/user/42', '/user/[id]', { id: '42' }) + + component() + startViewSpy.calls.reset() + setupRoute('/user/43', '/user/[id]', { id: '43' }) + component() + + expect(startViewSpy).toHaveBeenCalledOnceWith({ name: '/user/[id]', url: undefined }) + }) + + it('does not start a new view when only query params change', () => { + const startViewSpy = initializeNextjsPlugin() + setupRoute('/about', '/about') + + component() + startViewSpy.calls.reset() + setupRoute('/about?foo=bar', '/about') + component() + + expect(startViewSpy).not.toHaveBeenCalled() + }) + + it('does not start a new view when only the hash changes', () => { + const startViewSpy = initializeNextjsPlugin() + setupRoute('/about', '/about') + + component() + startViewSpy.calls.reset() + setupRoute('/about#section', '/about') + component() + + expect(startViewSpy).not.toHaveBeenCalled() + }) + }) +}) + +describe('DatadogPagesRouter', () => { + let ref: { current: string | null } + + beforeEach(() => { + ref = { current: null } + replaceMockable(useRef, () => ref) + }) + + it('does not start a view when router is not ready', () => { + const startViewSpy = initializeNextjsPlugin() + replaceMockable(useRouter, () => mockRouter({ isReady: false, asPath: '/', pathname: '/' })) + + DatadogPagesRouter() + + expect(startViewSpy).not.toHaveBeenCalled() + }) +}) diff --git a/packages/rum-nextjs/src/entries/main.ts b/packages/rum-nextjs/src/entries/main.ts index 1d347b2273..7b2612ef9a 100644 --- a/packages/rum-nextjs/src/entries/main.ts +++ b/packages/rum-nextjs/src/entries/main.ts @@ -1,4 +1,4 @@ export { nextjsPlugin, onRouterTransitionStart } from '../domain/nextjsPlugin' export type { NextjsPlugin } from '../domain/nextjsPlugin' -export { DatadogAppRouter } from '../domain/datadogAppRouter' -export { DatadogPagesRouter } from '../domain/datadogPagesRouter' +export { DatadogAppRouter } from '../domain/nextJSRouter/datadogAppRouter' +export { DatadogPagesRouter } from '../domain/nextJSRouter/datadogPagesRouter' diff --git a/packages/rum-nextjs/test/initializeNextjsPlugin.ts b/packages/rum-nextjs/test/initializeNextjsPlugin.ts new file mode 100644 index 0000000000..58cee98950 --- /dev/null +++ b/packages/rum-nextjs/test/initializeNextjsPlugin.ts @@ -0,0 +1,14 @@ +import type { RumInitConfiguration, RumPublicApi } from '@datadog/browser-rum-core' +import { registerCleanupTask } from '../../core/test' +import { nextjsPlugin, resetNextjsPlugin } from '../src/domain/nextjsPlugin' + +export function initializeNextjsPlugin() { + const startViewSpy = jasmine.createSpy('startView') + const plugin = nextjsPlugin() + plugin.onInit({ + publicApi: { startView: startViewSpy } as unknown as RumPublicApi, + initConfiguration: {} as RumInitConfiguration, + }) + registerCleanupTask(() => resetNextjsPlugin()) + return startViewSpy +} diff --git a/packages/rum-nextjs/test/stubs/nextNavigation.ts b/packages/rum-nextjs/test/stubs/nextNavigation.ts new file mode 100644 index 0000000000..862066dcf3 --- /dev/null +++ b/packages/rum-nextjs/test/stubs/nextNavigation.ts @@ -0,0 +1,9 @@ +// Stub for next/navigation used in unit tests. +// The real implementations are always replaced via replaceMockable() in specs. +export function usePathname(): string { + return '' +} + +export function useParams(): Record { + return {} +} diff --git a/packages/rum-nextjs/test/stubs/nextRouter.ts b/packages/rum-nextjs/test/stubs/nextRouter.ts new file mode 100644 index 0000000000..0a63fe7475 --- /dev/null +++ b/packages/rum-nextjs/test/stubs/nextRouter.ts @@ -0,0 +1,5 @@ +// Stub for next/router used in unit tests. +// The real implementations are always replaced via replaceMockable() in specs. +export function useRouter() { + return {} +} diff --git a/test/apps/nextjs-app-router/next-env.d.ts b/test/apps/nextjs-app-router/next-env.d.ts index 0c7fad710c..2d5420ebae 100644 --- a/test/apps/nextjs-app-router/next-env.d.ts +++ b/test/apps/nextjs-app-router/next-env.d.ts @@ -1,7 +1,7 @@ /// /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/test/apps/nextjs-app-router/yarn.lock b/test/apps/nextjs-app-router/yarn.lock index d4df8dfa3d..de6ea0dcc3 100644 --- a/test/apps/nextjs-app-router/yarn.lock +++ b/test/apps/nextjs-app-router/yarn.lock @@ -23,7 +23,7 @@ __metadata: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz::locator=nextjs-app-router%40workspace%3A.": version: 6.30.1 - resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=9f1c94&locator=nextjs-app-router%40workspace%3A." + resolution: "@datadog/browser-rum-nextjs@file:../../../packages/rum-nextjs/package.tgz#../../../packages/rum-nextjs/package.tgz::hash=304479&locator=nextjs-app-router%40workspace%3A." dependencies: "@datadog/browser-core": "npm:6.30.1" "@datadog/browser-rum-core": "npm:6.30.1" @@ -35,7 +35,7 @@ __metadata: optional: true react: optional: true - checksum: 10c0/996e75cf3a5881735a3ec9a41b34d054beef9634d7f843f16c896c4a491b4bb08012b1008268151f5992f49d327d388784d5adc8f487c6f37375f8cd099dea5a + checksum: 10c0/7e8e72b9cd4ac5ecfbc3a4f52ae3e38a54bb762d92e8e933db273f5fce1c8f7c697c56bed5bbb438e44434e81cc164be8d806074a9b65e6215be5ff70ee88b89 languageName: node linkType: hard diff --git a/test/unit/karma.base.conf.js b/test/unit/karma.base.conf.js index 480210acd5..628ee829a7 100644 --- a/test/unit/karma.base.conf.js +++ b/test/unit/karma.base.conf.js @@ -1,3 +1,4 @@ +import path from 'node:path' import { parseArgs } from 'node:util' import webpackBase from '../../webpack.base.ts' @@ -78,7 +79,14 @@ export default { webpack: { stats: 'minimal', module: overrideTsLoaderRule(webpackConfig.module), - resolve: webpackConfig.resolve, + resolve: { + ...webpackConfig.resolve, + alias: { + ...webpackConfig.resolve.alias, + 'next/navigation': path.resolve('packages/rum-nextjs/test/stubs/nextNavigation.ts'), + 'next/router': path.resolve('packages/rum-nextjs/test/stubs/nextRouter.ts'), + }, + }, target: webpackConfig.target, devtool: false, mode: 'development',