8000 test(e2e): Add `nextjs-13` e2e test app (#13154) · benjick/sentry-javascript@26c81e8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 26c81e8

Browse files
authored
test(e2e): Add nextjs-13 e2e test app (getsentry#13154)
1 parent 29f070f commit 26c81e8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1429
-3
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,7 @@ jobs:
10191019
'ember-classic',
10201020
'ember-embroider',
10211021
'nextjs-app-dir',
1022+
'nextjs-13',
10221023
'nextjs-14',
10231024
'nextjs-15',
10241025
'react-17',

.github/workflows/canary.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ jobs:
7878
- test-application: 'nextjs-app-dir'
7979
build-command: 'test:build-latest'
8080
label: 'nextjs-app-dir (latest)'
81+
- test-application: 'nextjs-13'
82+
build-command: 'test:build-canary'
83+
label: 'nextjs-13 (canary)'
84+
- test-application: 'nextjs-13'
85+
build-command: 'test:build-latest'
86+
label: 'nextjs-13 (latest)'
8187
- test-application: 'nextjs-14'
8288
build-command: 'test:build-canary'
8389
label: 'nextjs-14 (canary)'
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
.pnpm-debug.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts
37+
38+
!*.d.ts
39+
40+
test-results
41+
42+
.vscode
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@sentry:registry=http://127.0.0.1:4873
2+
@sentry-internal:registry=http://127.0.0.1:4873
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Layout({ children }: { children: React.ReactNode }) {
2+
return (
3+
<html lang="en">
4+
<body>{children}</body>
5+
</html>
6+
);
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function PageloadTransactionPage() {
2+
return <p>Pageload Transaction Page</p>;
3+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const dynamic = 'force-dynamic';
2+
3+
export default async function Page() {
4+
throw new Error('RSC error');
5+
return <p>Hello World</p>;
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
interface Window {
2+
recordedTransactions?: string[];
3+
capturedExceptionId?: string;
4+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as Sentry from '@sentry/nextjs';
2+
3+
export function register() {
4+
if (process.env.NEXT_RUNTIME === 'nodejs' || process.env.NEXT_RUNTIME === 'edge') {
5+
Sentry.init({
6+
environment: 'qa', // dynamic sampling bias to keep transactions
7+
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
8+
tunnel: `http://localhost:3031/`, // proxy server
9+
tracesSampleRate: 1,
10+
sendDefaultPii: true,
11+
transportOptions: {
12+
// We are doing a lot of events at once in this test app
13+
bufferSize: 1000,
14+
},
15+
});
16+
}
17+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
/// <reference types="next/navigation-types/compat/navigation" />
4+
5+
// NOTE: This file should not be edited
6+
// see https://nextjs.org/docs/basic-features/typescript for more information.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const { withSentryConfig } = require('@sentry/nextjs');
2+
3+
/** @type {import('next').NextConfig} */
4+
const moduleExports = {
5+
typescript: {
6+
ignoreBuildErrors: true, // TODO: Remove this
7+
},
8+
experimental: {
9+
appDir: true,
10+
},
11+
pageExtensions: ['jsx', 'js', 'tsx', 'ts', 'page.tsx'],
12+
};
13+
14+
module.exports = withSentryConfig(moduleExports, {
15+
silent: true,
16+
excludeServerRoutes: ['/api/endpoint-excluded-with-string', /\/api\/endpoint-excluded-with-regex/],
17+
});
Lines changed: 45 additions & 0 deletions
B93C
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "create-next-app",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"build": "next build > .tmp_build_stdout 2> .tmp_build_stderr || (cat .tmp_build_stdout && cat .tmp_build_stderr && exit 1)",
7+
"clean": "npx rimraf node_modules pnpm-lock.yaml .next",
8+
"test:prod": "TEST_ENV=production playwright test",
9+
"test:dev": "TEST_ENV=development playwright test",
10+
"test:build": "pnpm install && npx playwright install && pnpm build",
11+
"test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@beta && pnpm add react-dom@beta && npx playwright install && pnpm build",
12+
"test:build-latest": "pnpm install && pnpm add next@latest && npx playwright install && pnpm build",
13+
"test:assert": "pnpm test:prod && pnpm test:dev"
14+
},
15+
"dependencies": {
16+
"@sentry/nextjs": "latest || *",
17+
"@types/node": "18.11.17",
18+
"@types/react": "18.0.26",
19+
"@types/react-dom": "18.0.9",
20+
"next": "13.2.0",
21+
"react": "18.2.0",
22+
"react-dom": "18.2.0",
23+
"typescript": "4.9.5"
24+
},
25+
"devDependencies": {
26+
"@playwright/test": "^1.44.1",
27+
"@sentry-internal/test-utils": "link:../../../test-utils",
28+
"@sentry-internal/feedback": "latest || *",
29+
"@sentry-internal/replay-canvas": "latest || *",
30+
"@sentry-internal/browser-utils": "latest || *",
31+
"@sentry/browser": "latest || *",
32+
"@sentry/core": "latest || *",
33+
"@sentry/nextjs": "latest || *",
34+
"@sentry/node": "latest || *",
35+
"@sentry/opentelemetry": "latest || *",
36+
"@sentry/react": "latest || *",
37+
"@sentry-internal/replay": "latest || *",
38+
"@sentry/types": "latest || *",
39+
"@sentry/utils": "latest || *",
40+
"@sentry/vercel-edge": "latest || *"
41+
},
42+
"volta": {
43+
"extends": "../../package.json"
44+
}
45+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export default function ClickErrorPage() {
2+
return (
3+
<button
4+
id="error-button"
5+
onClick={() => {
6+
throw new Error('click error');
7+
}}
8+
>
9+
click to throw error
10+
</button>
11+
);
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Link from 'next/link';
2+
3+
export default function Page() {
4+
return (
5+
<Link href="/foo/navigation-target-page" id="navigation-link">
6+
Navigate
7+
</Link>
8+
);
9+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <p>arrived</p>;
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <p>pageload test page</p>;
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const WithInitialPropsPage = ({ data }: { data: string }) => <h1>WithInitialPropsPage {data}</h1>;
2+
3+
WithInitialPropsPage.getInitialProps = () => {
4+
return { data: '[some getInitialProps data]' };
5+
};
6+
7+
export default WithInitialPropsPage;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function WithServerSidePropsPage({ data }: { data: string }) {
2+
return <h1>WithServerSidePropsPage {data}</h1>;
3+
}
4+
5+
export async function getServerSideProps() {
6+
return { props: { data: '[some getServerSideProps data]' } };
7+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import App, { AppContext, AppProps } from 'next/app';
2+
3+
const MyApp = ({ Component, pageProps }: AppProps) => {
4+
// @ts-ignore I don't know why TS complains here
5+
return <Component {...pageProps} />;
6+
};
7+
8+
MyApp.getInitialProps = async (appContext: AppContext) => {
9+
// This simulates user misconfiguration. Users should always call `App.getInitialProps(appContext)`, but they don't,
10+
// so we have a test for this so we don't break their apps.
11+
if (appContext.ctx.pathname === '/misconfigured-_app-getInitialProps') {
12+
return {};
13+
}
14+
15+
const appProps = await App.getInitialProps(appContext);
16+
return { ...appProps };
17+
};
18+
19+
export default MyApp;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { NextApiRequest, NextApiResponse } from 'next';
2+
3+
export default async (_req: NextApiRequest, res: NextApiResponse) => {
4+
throw new Error('api route error');
5+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { NextApiRequest, NextApiResponse } from 'next';
2+
3+
export default async (_req: NextApiRequest, res: NextApiResponse) => {
4+
res.status(200).json({ success: true });
5+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { NextApiRequest, NextApiResponse } from 'next';
2+
3+
export default async (_req: NextApiRequest, res: NextApiResponse) => {
4+
res.status(200).json({ success: true });
5+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { NextApiRequest, NextApiResponse } from 'next';
2+
3+
if (process.env.NEXT_PUBLIC_SOME_FALSE_ENV_VAR === 'enabled') {
4+
require('../../tests/server/utils/throw'); // Should not throw unless the hoisting in the wrapping loader is messed up!
5+
}
6+
7+
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
8+
require('@sentry/nextjs').captureException; // Should not throw unless the wrapping loader messes up cjs imports
9+
// @ts-expect-error
10+
require.context('.'); // This is a webpack utility call. Should not throw unless the wrapping loader messes it up by mangling.
11+
res.status(200).json({ success: true });
12+
};
13+
14+
module.exports = handler;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { 10000 NextApiRequest, NextApiResponse } from 'next';
2+
3+
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
4+
res.status(200).json({ success: true });
5+
};
6+
7+
module.exports = handler;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { NextApiRequest, NextApiResponse } from 'next';
2+
3+
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
4+
res.status(200).json({ success: true });
5+
};
6+
7+
module.exports = handler;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { NextApiRequest, NextApiResponse } from 'next';
2+
3+
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
4+
res.status(200).json({ success: true });
5+
};
6+
7+
module.exports = handler;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { NextApiRequest, NextApiResponse } from 'next';
2+
3+
export default async (_req: NextApiRequest, res: NextApiResponse) => {
4+
res.status(200).json({ success: true });
5+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { NextApiRequest, NextApiResponse } from 'next';
< 10000 /code>2+
3+
export default async (_req: NextApiRequest, res: NextApiResponse) => {
4+
res.status(200).json({ success: true });
5+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default function CrashedPage() {
2+
// Magic to naively trigger onerror to make session crashed and allow for SSR
3+
try {
4+
if (typeof window !== 'undefined' && typeof window.onerror === 'function') {
5+
// Lovely oldschool browsers syntax with 5 arguments <3
6+
// @ts-expect-error
7+
window.onerror(null, null, null, null, new Error('Crashed'));
8+
}
9+
} catch (_e) {
10+
// no-empty
11+
}
12+
return <h1>Crashed</h1>;
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export default function BasicPage() {
2+
return (
3+
<h1>
4+
This page simply exists to test the compatibility of Next.js' `pageExtensions` option with our auto wrapping
5+
process. This file should be turned into a page by Next.js and our webpack loader should process it.
6+
</h1>
7+
);
8+
}
9+
10+
export async function getServerSideProps() {
11+
throw new Error('custom page extension error');
12+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function WithServerSidePropsPage({ data }: { data: string }) {
2+
return <h1>WithServerSidePropsPage {data}</h1>;
3+
}
4+
5+
export async function getServerSideProps() {
6+
throw new Error('getServerSideProps Error');
7+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { useEffect } from 'react';
2+
3+
export default function FetchPage() {
4+
useEffect(() => {
5+
// test that a span is created in the pageload transaction for this fetch request
6+
fetch('http://example.com').catch(() => {
7+
// no-empty
8+
});
9+
}, []);
10+
11+
return <p>Hello world!</p>;
12+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <p>healthy page</p>;
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// See _app.tsx for more information why this file exists.
2+
3+
export default function Page() {
4+
return <p>faulty _app getInitialProps</p>;
5+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { captureException, showReportDialog } from '@sentry/nextjs';
2+
3+
export default function ReportDialogPage() {
4+
return (
5+
<button
6+
id="open-report-dialog"
7+
onClick={() => {
8+
const eventId = captureException(new Error('show-report-dialog-error'));
9+
showReportDialog({ eventId });
10+
}}
11+
>
12+
Open Report Dialog
13+
</button>
14+
);
15+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This page simply exists to test the compatibility of Next.js' `pageExtensions` option with our auto wrapping
2+
process. This file should not be turned into a page by Next.js and our webpack loader also shouldn't process it.
3+
This page should not contain valid JavaScript.

0 commit comments

Comments
 (0)
0