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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

!*.d.ts

# Sentry
.sentryclirc

.vscode

test-results
event-dumps

.tmp_dev_server_logs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@sentry:registry=http://127.0.0.1:4873
@sentry-internal:registry=http://127.0.0.1:4873
public-hoist-pattern[]=*import-in-the-middle*
public-hoist-pattern[]=*require-in-the-middle*
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <p>hello world</p>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <p>hello world</p>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const dynamic = 'force-dynamic';

export default function Page() {
return <p>hello world</p>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const dynamic = 'force-dynamic';

export default function Page() {
return <p>hello world</p>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client';

import Link from 'next/link';
import { useRouter } from 'next/navigation';

export default function Page() {
const router = useRouter();

return (
<ul>
<li>
<button
onClick={() => {
router.push('/navigation/42/router-push');
}}
>
router.push()
</button>
</li>
<li>
<Link href="/navigation/42/link">Normal Link</Link>
</li>
</ul>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Page() {
return (
<div>
<h1>Nextjs basePath Test App</h1>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface Window {
recordedTransactions?: string[];
capturedExceptionId?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as Sentry from '@sentry/nextjs';

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1.0,
sendDefaultPii: true,
});

export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Sentry from '@sentry/nextjs';

export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('./sentry.server.config');
}

if (process.env.NEXT_RUNTIME === 'edge') {
await import('./sentry.edge.config');
}
}

export const onRequestError = Sentry.captureRequestError;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { withSentryConfig } = require('@sentry/nextjs');

/** @type {import('next').NextConfig} */
const nextConfig = {
basePath: '/my-app',
};

module.exports = withSentryConfig(nextConfig, {
silent: true,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "nextjs-15-basepath",
"version": "0.1.0",
"private": true,
"scripts": {
"build": "next build > .tmp_build_stdout 2> .tmp_build_stderr || (cat .tmp_build_stdout && cat .tmp_build_stderr && exit 1)",
"clean": "npx rimraf node_modules pnpm-lock.yaml .tmp_dev_server_logs",
"test:prod": "TEST_ENV=production playwright test",
"test:dev": "TEST_ENV=development playwright test",
"test:build": "pnpm install && pnpm build",
"test:assert": "pnpm test:prod && pnpm test:dev"
},
"dependencies": {
"@sentry/nextjs": "latest || *",
"@types/node": "^18.19.1",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
"next": "15.4.2-canary.1",
"react": "beta",
"react-dom": "beta",
"typescript": "~5.0.0"
},
"devDependencies": {
"@playwright/test": "~1.53.2",
"@sentry-internal/test-utils": "link:../../../test-utils"
},
"volta": {
"extends": "../../package.json"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
const testEnv = process.env.TEST_ENV;

if (!testEnv) {
throw new Error('No test env defined');
}

const getStartCommand = () => {
if (testEnv === 'development') {
return 'pnpm next dev -p 3030 2>&1 | tee .tmp_dev_server_logs';
}

if (testEnv === 'production') {
return 'pnpm next start -p 3030';
}

throw new Error(`Unknown test env: ${testEnv}`);
};

const config = getPlaywrightConfig({
startCommand: getStartCommand(),
port: 3030,
});

export default config;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Sentry from '@sentry/nextjs';

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1.0,
sendDefaultPii: true,
transportOptions: {
// We are doing a lot of events at once in this test
bufferSize: 1000,
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Sentry from '@sentry/nextjs';

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1.0,
sendDefaultPii: true,
transportOptions: {
// We are doing a lot of events at once in this test
bufferSize: 1000,
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as fs from 'fs';
import * as path from 'path';
import { startEventProxyServer } from '@sentry-internal/test-utils';

const packageJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json')));

startEventProxyServer({
port: 3031,
proxyServerName: 'nextjs-15-basepath',
envelopeDumpPath: path.join(
process.cwd(),
`event-dumps/next-15-basepath-v${packageJson.dependencies.next}-${process.env.TEST_ENV}.dump`,
),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';

test('Creates a pageload transaction for basePath root route with prefix', async ({ page }) => {
const clientPageloadTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => {
return transactionEvent?.transaction === '/my-app' && transactionEvent.contexts?.trace?.op === 'pageload';
});

await page.goto('/my-app');

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

test('Creates a dynamic pageload transaction for basePath dynamic route with prefix', async ({ page }) => {
const randomRoute = String(Math.random());

const clientPageloadTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => {
return (
transactionEvent?.transaction === '/my-app/dynamic/:parameter' &&
transactionEvent.contexts?.trace?.op === 'pageload'
);
});

await page.goto(`/my-app/dynamic/${randomRoute}`);

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

test('Creates a dynamic pageload transaction for basePath dynamic catch-all route with prefix', async ({ page }) => {
const randomRoute = String(Math.random());

const clientPageloadTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => {
return (
transactionEvent?.transaction === '/my-app/dynamic/:parameters*' &&
transactionEvent.contexts?.trace?.op === 'pageload'
);
});

await page.goto(`/my-app/dynamic/${randomRoute}/foo/bar/baz`);

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

test('Creates a navigation transaction for basePath router with prefix', async ({ page }) => {
const navigationTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => {
return (
transactionEvent?.transaction === '/my-app/navigation/:param/router-push' &&
transactionEvent.contexts?.trace?.op === 'navigation' &&
transactionEvent.contexts.trace.data?.['navigation.type'] === 'router.push'
);
});

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

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

test('Creates a navigation transaction for basePath <Link> with prefix', async ({ page }) => {
const navigationTransactionPromise = waitForTransaction('nextjs-15-basepath', transactionEvent => {
return (
transactionEvent?.transaction === '/my-app/navigation/:param/link' &&
transactionEvent.contexts?.trace?.op === 'navigation' &&
transactionEvent.contexts.trace.data?.['navigation.type'] === 'router.push'
);
});

await page.goto('/my-app/navigation');
await page.waitForTimeout(1000);
await page.getByText('Normal Link').click();

expect(await navigationTransactionPromise).toBeDefined();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es2018",
"allowImportingTsExtensions": true,
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"plugins": [
{
"name": "next"
}
],
"incremental": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next.config.js", ".next/types/**/*.ts"],
"exclude": ["node_modules", "playwright.config.ts"]
}
Loading
Loading