Skip to content

Commit 37bfcdc

Browse files
authored
test(nextjs): Extract pages routes tests from nextjs-app-dir e2e tests (#17175)
1 parent ac3e6e5 commit 37bfcdc

34 files changed

+574
-13
lines changed

dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "create-next-app",
2+
"name": "nextjs-app-dir",
33
"version": "0.1.0",
44
"private": true,
55
"scripts": {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
# Sentry
41+
.sentryclirc
42+
43+
.vscode
44+
45+
test-results
46+
event-dumps
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Without this file (or better said without the app directory), we run into a otel dependency issue on the edge runtime
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import * as assert from 'assert/strict';
4+
5+
const packageJson = require('./package.json');
6+
const nextjsVersion = packageJson.dependencies.next;
7+
8+
const buildStdout = fs.readFileSync('.tmp_build_stdout', 'utf-8');
9+
const buildStderr = fs.readFileSync('.tmp_build_stderr', 'utf-8');
10+
11+
const getLatestNextVersion = async () => {
12+
try {
13+
const response = await fetch('https://registry.npmjs.org/next/latest');
14+
const data = await response.json();
15+
return data.version as string;
16+
} catch {
17+
return '0.0.0';
18+
}
19+
};
20+
21+
(async () => {
22+
// Assert that there was no funky build time warning when we are on a stable (pinned) version
23+
if (
24+
!nextjsVersion.includes('-canary') &&
25+
!nextjsVersion.includes('-rc') &&
26+
// If we install latest we cannot assert on "latest" because the package json will contain the actual version number
27+
nextjsVersion !== (await getLatestNextVersion())
28+
) {
29+
assert.doesNotMatch(
30+
buildStderr,
31+
/Import trace for requested module/, // This is Next.js/Webpack speech for "something is off"
32+
`The E2E tests detected a build warning in the Next.js build output:\n\n--------------\n\n${buildStderr}\n\n--------------\n\n`,
33+
);
34+
}
35+
36+
// Read the contents of the directory
37+
const files = fs.readdirSync(path.join(process.cwd(), '.next', 'static'));
38+
const mapFiles = files.filter(file => path.extname(file) === '.map');
39+
if (mapFiles.length > 0) {
40+
throw new Error(
41+
'Client bundle .map files found even though `sourcemaps.deleteSourcemapsAfterUpload` option is set!',
42+
);
43+
}
44+
})();
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
'use client';
2+
3+
import { captureException } from '@sentry/nextjs';
4+
import { useContext, useState } from 'react';
5+
import { SpanContext } from './span-context';
6+
7+
export function ClientErrorDebugTools() {
8+
const spanContextValue = useContext(SpanContext);
9+
const [spanName, setSpanName] = useState<string>('');
10+
11+
const [isFetchingAPIRoute, setIsFetchingAPIRoute] = useState<boolean>();
12+
const [isFetchingEdgeAPIRoute, setIsFetchingEdgeAPIRoute] = useState<boolean>();
13+
const [isFetchingExternalAPIRoute, setIsFetchingExternalAPIRoute] = useState<boolean>();
14+
const [renderError, setRenderError] = useState<boolean>();
15+
16+
if (renderError) {
17+
throw new Error('Render Error');
18+
}
19+
20+
return (
21+
<div>
22+
{spanContextValue.spanActive ? (
23+
<button
24+
onClick={() => {
25+
spanContextValue.stop();
26+
setSpanName('');
27+
}}
28+
>
29+
Stop span
30+
</button>
31+
) : (
32+
<>
33+
<input
34+
type="text"
35+
placeholder="Span name"
36+
value={spanName}
37+
onChange={e => {
38+
setSpanName(e.target.value);
39+
}}
40+
/>
41+
<button
42+
onClick={() => {
43+
spanContextValue.start(spanName);
44+
}}
45+
>
46+
Start span
47+
</button>
48+
</>
49+
)}
50+
<br />
51+
<br />
52+
<button
53+
onClick={() => {
54+
throw new Error('Click Error');
55+
}}
56+
>
57+
Throw error
58+
</button>
59+
<br />
60+
<button
61+
onClick={() => {
62+
return Promise.reject('Promise Click Error');
63+
}}
64+
>
65+
Throw promise rejection
66+
</button>
67+
<br />
68+
<button
69+
onClick={() => {
70+
setRenderError(true);
71+
}}
72+
>
73+
Cause render error
74+
</button>
75+
<br />
76+
<br />
77+
<button
78+
onClick={async () => {
79+
setIsFetchingAPIRoute(true);
80+
try {
81+
await fetch('/api/endpoint');
82+
} catch (e) {
83+
captureException(e);
84+
}
85+
setIsFetchingAPIRoute(false);
86+
}}
87+
disabled={isFetchingAPIRoute}
88+
>
89+
Send request to Next.js API route
90+
</button>
91+
<br />
92+
<button
93+
onClick={async () => {
94+
setIsFetchingEdgeAPIRoute(true);
95+
try {
96+
await fetch('/api/edge-endpoint');
97+
} catch (e) {
98+
captureException(e);
99+
}
100+
setIsFetchingEdgeAPIRoute(false);
101+
}}
102+
disabled={isFetchingEdgeAPIRoute}
103+
>
104+
Send request to Next.js Edge API route
105+
</button>
106+
<br />
107+
<button
108+
onClick={async () => {
109+
setIsFetchingExternalAPIRoute(true);
110+
try {
111+
await fetch('https://example.com/', { mode: 'no-cors' });
112+
} catch (e) {
113+
captureException(e);
114+
}
115+
setIsFetchingExternalAPIRoute(false);
116+
}}
117+
disabled={isFetchingExternalAPIRoute}
118+
>
119+
Send request to external API route
120+
</button>
121+
<br />
122+
</div>
123+
);
124+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use client';
2+
3+
import { startInactiveSpan, Span } from '@sentry/nextjs';
4+
import { PropsWithChildren, createContext, useState } from 'react';
5+
6+
export const SpanContext = createContext<
7+
{ spanActive: false; start: (spanName: string) => void } | { spanActive: true; stop: () => void }
8+
>({
9+
spanActive: false,
10+
start: () => undefined,
11+
});
12+
13+
export function SpanContextProvider({ children }: PropsWithChildren) {
14+
const [span, setSpan] = useState<Span | undefined>(undefined);
15+
16+
return (
17+
<SpanContext.Provider
18+
value={
19+
span
20+
? {
21+
spanActive: true,
22+
stop: () => {
23+
span.end();
24+
setSpan(undefined);
25+
},
26+
}
27+
: {
28+
spanActive: false,
29+
start: (spanName: string) => {
30+
const span = startInactiveSpan({ name: spanName });
31+
setSpan(span);
32+
},
33+
}
34+
}
35+
>
36+
{children}
37+
</SpanContext.Provider>
38+
);
39+
}
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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as Sentry from '@sentry/nextjs';
2+
3+
Sentry.init({
4+
environment: 'qa', // dynamic sampling bias to keep transactions
5+
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
6+
tunnel: `http://localhost:3031/`, // proxy server
7+
tracesSampleRate: 1.0,
8+
sendDefaultPii: true,
9+
});
10+
11+
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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.0,
10+
sendDefaultPii: true,
11+
transportOptions: {
12+
// We are doing a lot of events at once in this test
13+
bufferSize: 1000,
14+
},
15+
});
16+
}
17+
}
18+
19+
export const onRequestError = Sentry.captureRequestError;

0 commit comments

Comments
 (0)