Skip to content

Commit d0840e9

Browse files
authored
feat(nuxt): Detect development environment and add dev E2E test (#18671)
Nuxt shows if it is running in a dev environment in the variable `import.meta.dev` ([docs](https://nuxt.com/docs/4.x/api/advanced/import-meta#runtime-app-properties)) - the Vite variable `import.meta.env.DEV` ([docs](https://vite.dev/guide/env-and-mode#built-in-constants)) is not available. To test this, a dev E2E test environment has been added. This includes a bash script which runs `nuxt dev` first to generate the necessary files and then starts the dev command again with the `NODE_OPTIONS` env. Right now, the development environment tests only test this specific feature (detecting the dev environment) because a bunch of existing tests are failing in dev mode. This is probably because the dev test setup needs to be a bit different (like waiting for `networkidle`) and a lot of tests would need some adaption (can be done in another PR). Added an entry to contributors as there was already a PR for that a while ago: #15179 Closes #15623 Closes #15147
1 parent a6a4f7b commit d0840e9

File tree

13 files changed

+166
-8
lines changed

13 files changed

+166
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7-
Work in this release was contributed by @xgedev, @Mohataseem89, @sebws, @G-Rath, and @gianpaj. Thank you for your contributions!
7+
Work in this release was contributed by @xgedev, @Mohataseem89, @sebws, @G-Rath, @maximepvrt, and @gianpaj. Thank you for your contributions!
88

99
- ref(nextjs): Drop `resolve` dependency from the Next.js SDK ([#18618](https://github.com/getsentry/sentry-javascript/pull/18618))
1010

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/bin/bash
2+
# To enable Sentry in Nuxt dev, it needs the sentry.server.config.mjs file from the .nuxt folder.
3+
# First, we need to start 'nuxt dev' to generate the file, and then start 'nuxt dev' again with the NODE_OPTIONS to have Sentry enabled.
4+
5+
# Using a different port to avoid playwright already starting with the tests for port 3030
6+
TEMP_PORT=3035
7+
8+
# 1. Start dev in background - this generates .nuxt folder
9+
pnpm dev -p $TEMP_PORT &
10+
DEV_PID=$!
11+
12+
# 2. Wait for the sentry.server.config.mjs file to appear
13+
echo "Waiting for .nuxt/dev/sentry.server.config.mjs file..."
14+
COUNTER=0
15+
while [ ! -f ".nuxt/dev/sentry.server.config.mjs" ] && [ $COUNTER -lt 30 ]; do
16+
sleep 1
17+
((COUNTER++))
18+
done
19+
20+
if [ ! -f ".nuxt/dev/sentry.server.config.mjs" ]; then
21+
echo "ERROR: .nuxt/dev/sentry.server.config.mjs file never appeared!"
22+
echo "This usually means the Nuxt dev server failed to start or generate the file. Try to rerun the test."
23+
pkill -P $DEV_PID || kill $DEV_PID
24+
exit 1
25+
fi
26+
27+
# 3. Cleanup
28+
echo "Found .nuxt/dev/sentry.server.config.mjs, stopping 'nuxt dev' process..."
29+
pkill -P $DEV_PID || kill $DEV_PID
30+
31+
# Wait for port to be released
32+
echo "Waiting for port $TEMP_PORT to be released..."
33+
COUNTER=0
34+
# Check if port is still in use
35+
while lsof -i :$TEMP_PORT > /dev/null 2>&1 && [ $COUNTER -lt 10 ]; do
36+
sleep 1
37+
((COUNTER++))
38+
done
39+
40+
if lsof -i :$TEMP_PORT > /dev/null 2>&1; then
41+
echo "WARNING: Port $TEMP_PORT still in use after 10 seconds, proceeding anyway..."
42+
else
43+
echo "Port $TEMP_PORT released successfully"
44+
fi
45+
46+
echo "Starting nuxt dev with Sentry server config..."
47+
48+
# 4. Start the actual dev command which should be used for the tests
49+
NODE_OPTIONS='--import ./.nuxt/dev/sentry.server.config.mjs' nuxt dev

dev-packages/e2e-tests/test-applications/nuxt-4/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
"start:import": "node --import ./.output/server/sentry.server.config.mjs .output/server/index.mjs",
1212
"clean": "npx nuxi cleanup",
1313
"test": "playwright test",
14+
"test:prod": "TEST_ENV=production playwright test",
15+
"test:dev": "TEST_ENV=development playwright test environment",
1416
"test:build": "pnpm install && pnpm build",
1517
"test:build-canary": "pnpm add nuxt@npm:nuxt-nightly@latest && pnpm add nitropack@npm:nitropack-nightly@latest && pnpm install --force && pnpm build",
16-
"test:assert": "pnpm test"
18+
"test:assert": "pnpm test:prod && pnpm test:dev"
1719
},
1820
"dependencies": {
1921
"@pinia/nuxt": "^0.5.5",
Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
11
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
22

3+
const testEnv = process.env.TEST_ENV;
4+
5+
if (!testEnv) {
6+
throw new Error('No test env defined');
7+
}
8+
9+
const getStartCommand = () => {
10+
if (testEnv === 'development') {
11+
return 'bash ./nuxt-start-dev-server.bash';
12+
}
13+
14+
if (testEnv === 'production') {
15+
return 'pnpm start:import';
16+
}
17+
18+
throw new Error(`Unknown test env: ${testEnv}`);
19+
};
20+
321
const config = getPlaywrightConfig({
4-
startCommand: `pnpm start:import`,
22+
startCommand: getStartCommand(),
523
});
624

725
export default config;

dev-packages/e2e-tests/test-applications/nuxt-4/sentry.client.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as Sentry from '@sentry/nuxt';
22
import { usePinia, useRuntimeConfig } from '#imports';
33

44
Sentry.init({
5-
environment: 'qa', // dynamic sampling bias to keep transactions
65
dsn: useRuntimeConfig().public.sentry.dsn,
76
tunnel: `http://localhost:3031/`, // proxy server
87
tracesSampleRate: 1.0,

dev-packages/e2e-tests/test-applications/nuxt-4/sentry.server.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as Sentry from '@sentry/nuxt';
22

33
Sentry.init({
44
dsn: 'https://[email protected]/1337',
5-
environment: 'qa', // dynamic sampling bias to keep transactions
65
tracesSampleRate: 1.0, // Capture 100% of the transactions
76
tunnel: 'http://localhost:3031/', // proxy server
87
});
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
3+
import { isDevMode } from './isDevMode';
4+
5+
test.describe('environment detection', async () => {
6+
test('sets correct environment for client-side errors', async ({ page }) => {
7+
const errorPromise = waitForError('nuxt-4', async errorEvent => {
8+
return errorEvent?.exception?.values?.[0]?.value === 'Error thrown from Nuxt-4 E2E test app';
9+
});
10+
11+
// We have to wait for networkidle in dev mode because clicking the button is a no-op otherwise (network requests are blocked during page load)
12+
await page.goto(`/client-error`, isDevMode ? { waitUntil: 'networkidle' } : {});
13+
await page.locator('#errorBtn').click();
14+
15+
const error = await errorPromise;
16+
17+
if (isDevMode) {
18+
expect(error.environment).toBe('development');
19+
} else {
20+
expect(error.environment).toBe('production');
21+
}
22+
});
23+
24+
test('sets correct environment for client-side transactions', async ({ page }) => {
25+
const transactionPromise = waitForTransaction('nuxt-4', async transactionEvent => {
26+
return transactionEvent.transaction === '/test-param/:param()';
27+
});
28+
29+
await page.goto(`/test-param/1234`);
30+
31+
const transaction = await transactionPromise;
32+
33+
if (isDevMode) {
34+
expect(transaction.environment).toBe('development');
35+
} else {
36+
expect(transaction.environment).toBe('production');
37+
}
38+
});
39+
40+
test('sets correct environment for server-side errors', async ({ page }) => {
41+
const errorPromise = waitForError('nuxt-4', async errorEvent => {
42+
return errorEvent?.exception?.values?.[0]?.value === 'Nuxt 4 Server error';
43+
});
44+
45+
await page.goto(`/fetch-server-routes`, isDevMode ? { waitUntil: 'networkidle' } : {});
46+
await page.getByText('Fetch Server API Error', { exact: true }).click();
47+
48+
const error = await errorPromise;
49+
50+
expect(error.transaction).toBe('GET /api/server-error');
51+
52+
if (isDevMode) {
53+
expect(error.environment).toBe('development');
54+
} else {
55+
expect(error.environment).toBe('production');
56+
}
57+
});
58+
59+
test('sets correct environment for server-side transactions', async ({ page }) => {
60+
const transactionPromise = waitForTransaction('nuxt-4', async transactionEvent => {
61+
return transactionEvent.transaction === 'GET /api/nitro-fetch';
62+
});
63+
64+
await page.goto(`/fetch-server-routes`, isDevMode ? { waitUntil: 'networkidle' } : {});
65+
await page.getByText('Fetch Nitro $fetch', { exact: true }).click();
66+
67+
const transaction = await transactionPromise;
68+
69+
expect(transaction.contexts.trace.op).toBe('http.server');
70+
71+
if (isDevMode) {
72+
expect(transaction.environment).toBe('development');
73+
} else {
74+
expect(transaction.environment).toBe('production');
75+
}
76+
});
77+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const isDevMode = !!process.env.TEST_ENV && process.env.TEST_ENV.includes('development');

packages/core/src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export const DEFAULT_ENVIRONMENT = 'production';
2+
export const DEV_ENVIRONMENT = 'development';

packages/core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export {
101101
headersToDict,
102102
httpHeadersToSpanAttributes,
103103
} from './utils/request';
104-
export { DEFAULT_ENVIRONMENT } from './constants';
104+
export { DEFAULT_ENVIRONMENT, DEV_ENVIRONMENT } from './constants';
105105
export { addBreadcrumb } from './breadcrumbs';
106106
export { functionToStringIntegration } from './integrations/functiontostring';
107107
// eslint-disable-next-line deprecation/deprecation

0 commit comments

Comments
 (0)