diff --git a/e2e-tests/test-applications/nuxt-3-test-app/.gitignore b/e2e-tests/test-applications/nuxt-3-test-app/.gitignore new file mode 100644 index 00000000..4a7f73a2 --- /dev/null +++ b/e2e-tests/test-applications/nuxt-3-test-app/.gitignore @@ -0,0 +1,24 @@ +# Nuxt dev/build outputs +.output +.data +.nuxt +.nitro +.cache +dist + +# Node dependencies +node_modules + +# Logs +logs +*.log + +# Misc +.DS_Store +.fleet +.idea + +# Local env files +.env +.env.* +!.env.example diff --git a/e2e-tests/test-applications/nuxt-3-test-app/README.md b/e2e-tests/test-applications/nuxt-3-test-app/README.md new file mode 100644 index 00000000..25b58212 --- /dev/null +++ b/e2e-tests/test-applications/nuxt-3-test-app/README.md @@ -0,0 +1,75 @@ +# Nuxt Minimal Starter + +Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. + +## Setup + +Make sure to install dependencies: + +```bash +# npm +npm install + +# pnpm +pnpm install + +# yarn +yarn install + +# bun +bun install +``` + +## Development Server + +Start the development server on `http://localhost:3000`: + +```bash +# npm +npm run dev + +# pnpm +pnpm dev + +# yarn +yarn dev + +# bun +bun run dev +``` + +## Production + +Build the application for production: + +```bash +# npm +npm run build + +# pnpm +pnpm build + +# yarn +yarn build + +# bun +bun run build +``` + +Locally preview production build: + +```bash +# npm +npm run preview + +# pnpm +pnpm preview + +# yarn +yarn preview + +# bun +bun run preview +``` + +Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. diff --git a/e2e-tests/test-applications/nuxt-3-test-app/nuxt.config.ts b/e2e-tests/test-applications/nuxt-3-test-app/nuxt.config.ts new file mode 100644 index 00000000..8ae12e6c --- /dev/null +++ b/e2e-tests/test-applications/nuxt-3-test-app/nuxt.config.ts @@ -0,0 +1,5 @@ +// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + compatibilityDate: '2024-11-01', + devtools: { enabled: true } +}) diff --git a/e2e-tests/test-applications/nuxt-3-test-app/package.json b/e2e-tests/test-applications/nuxt-3-test-app/package.json new file mode 100644 index 00000000..3ca1ecf9 --- /dev/null +++ b/e2e-tests/test-applications/nuxt-3-test-app/package.json @@ -0,0 +1,18 @@ +{ + "name": "nuxt-app", + "private": true, + "type": "module", + "scripts": { + "build": "nuxt build", + "dev": "nuxt dev", + "generate": "nuxt generate", + "preview": "nuxt preview", + "postinstall": "nuxt prepare", + "start": "node .output/server/index.mjs" + }, + "dependencies": { + "nuxt": "^3.14.1592", + "vue": "latest", + "vue-router": "latest" + } +} diff --git a/e2e-tests/test-applications/nuxt-3-test-app/public/favicon.ico b/e2e-tests/test-applications/nuxt-3-test-app/public/favicon.ico new file mode 100644 index 00000000..18993ad9 Binary files /dev/null and b/e2e-tests/test-applications/nuxt-3-test-app/public/favicon.ico differ diff --git a/e2e-tests/test-applications/nuxt-3-test-app/public/robots.txt b/e2e-tests/test-applications/nuxt-3-test-app/public/robots.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/e2e-tests/test-applications/nuxt-3-test-app/public/robots.txt @@ -0,0 +1 @@ + diff --git a/e2e-tests/test-applications/nuxt-3-test-app/server/tsconfig.json b/e2e-tests/test-applications/nuxt-3-test-app/server/tsconfig.json new file mode 100644 index 00000000..b9ed69c1 --- /dev/null +++ b/e2e-tests/test-applications/nuxt-3-test-app/server/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../.nuxt/tsconfig.server.json" +} diff --git a/e2e-tests/test-applications/nuxt-3-test-app/tsconfig.json b/e2e-tests/test-applications/nuxt-3-test-app/tsconfig.json new file mode 100644 index 00000000..a746f2a7 --- /dev/null +++ b/e2e-tests/test-applications/nuxt-3-test-app/tsconfig.json @@ -0,0 +1,4 @@ +{ + // https://nuxt.com/docs/guide/concepts/typescript + "extends": "./.nuxt/tsconfig.json" +} diff --git a/e2e-tests/test-applications/nuxt-4-test-app b/e2e-tests/test-applications/nuxt-4-test-app new file mode 160000 index 00000000..c949347d --- /dev/null +++ b/e2e-tests/test-applications/nuxt-4-test-app @@ -0,0 +1 @@ +Subproject commit c949347d3e2b8c163e5da1ff7d7bdb5d2c5658ce diff --git a/e2e-tests/tests/nuxt-3.test.ts b/e2e-tests/tests/nuxt-3.test.ts new file mode 100644 index 00000000..7c9fa498 --- /dev/null +++ b/e2e-tests/tests/nuxt-3.test.ts @@ -0,0 +1,169 @@ +import * as path from 'path'; +import { + checkEnvBuildPlugin, + checkFileContents, + checkFileExists, + checkIfBuilds, + checkIfRunsOnProdMode, + checkPackageJson, + cleanupGit, + KEYS, + revertLocalChanges, + startWizardInstance, + TEST_ARGS, +} from '../utils'; +import { Integration } from '../../lib/Constants'; + +describe('Nuxt-3', () => { + const projectDir = path.resolve( + __dirname, + '../test-applications/nuxt-3-test-app', + ); + + beforeAll(async () => { + await runWizardOnNuxtProject(projectDir); + }); + + afterAll(() => { + revertLocalChanges(projectDir); + cleanupGit(projectDir); + }); + + testNuxtProjectSetup(projectDir); + + testNuxtProjectConfigs(projectDir); + + testNuxtProjectBuildsAndRuns(projectDir); +}); + +async function runWizardOnNuxtProject(projectDir: string): Promise { + const integration = Integration.nuxt; + + const wizardInstance = startWizardInstance(integration, projectDir); + const packageManagerPrompted = await wizardInstance.waitForOutput( + 'Please select your package manager.', + ); + + const tracingOptionPrompted = + packageManagerPrompted && + (await wizardInstance.sendStdinAndWaitForOutput( + // Selecting `yarn` as the package manager + [KEYS.DOWN, KEYS.ENTER], + // "Do you want to enable Tracing", sometimes doesn't work as `Tracing` can be printed in bold. + 'to track the performance of your application?', + { + timeout: 240_000, + }, + )); + + const replayOptionPrompted = + tracingOptionPrompted && + (await wizardInstance.sendStdinAndWaitForOutput( + [KEYS.ENTER], + // "Do you want to enable Sentry Session Replay", sometimes doesn't work as `Sentry Session Replay` can be printed in bold. + 'to get a video-like reproduction of errors during a user session?', + )); + + replayOptionPrompted && + (await wizardInstance.sendStdinAndWaitForOutput( + [KEYS.ENTER], + 'Do you want to create an example page', + { + optional: true, + }, + )); + + await wizardInstance.sendStdinAndWaitForOutput( + [KEYS.ENTER, KEYS.ENTER], + 'Successfully installed the Sentry Nuxt SDK!', + ); + + wizardInstance.kill(); +} + +function testNuxtProjectSetup(projectDir: string) { + const integration = Integration.nuxt; + + test('package.json is updated correctly', () => { + checkPackageJson(projectDir, integration); + }); + + test('.env-sentry-build-plugin is created and contains the auth token', () => { + checkEnvBuildPlugin(projectDir); + }); + + test('config files created', () => { + checkFileExists(`${projectDir}/sentry.server.config.ts`); + checkFileExists(`${projectDir}/sentry.client.config.ts`); + }); + + test('example page exists', () => { + checkFileExists(`${projectDir}/pages/sentry-example-page.vue`); + checkFileExists(`${projectDir}/server/api/sentry-example-api.ts`); + }); +} + +function testNuxtProjectConfigs(projectDir: string) { + test('nuxt config contains sentry module', () => { + checkFileContents(path.resolve(projectDir, 'nuxt.config.ts'), [ + "modules: ['@sentry/nuxt/module'],", + 'sentry: {', + ' sourceMapsUploadOptions: {', + ` org: '${TEST_ARGS.ORG_SLUG}',`, + ` project: '${TEST_ARGS.PROJECT_SLUG}'`, + ' }', + '},', + 'sourcemap: {', + ' client: true', + '}', + ]); + }); + + test('sentry.client.config.ts contents', () => { + checkFileContents(path.resolve(projectDir, 'sentry.client.config.ts'), [ + 'import * as Sentry from "@sentry/nuxt";', + 'Sentry.init({', + ' // If set up, you can use your runtime config here', + ' // dsn: useRuntimeConfig().public.sentry.dsn,', + ` dsn: "${TEST_ARGS.PROJECT_DSN}",`, + ' // We recommend adjusting this value in production, or using tracesSampler', + ' // for finer control', + ' tracesSampleRate: 1.0,', + ' // This sets the sample rate to be 10%. You may want this to be 100% while', + ' // in development and sample at a lower rate in production', + ' replaysSessionSampleRate: 0.1,', + ' // If the entire session is not sampled, use the below sample rate to sample', + ' // sessions when an error occurs.', + ' replaysOnErrorSampleRate: 1.0,', + " // If you don't want to use Session Replay, just remove the line below:", + ' integrations: [Sentry.replayIntegration()],', + " // Setting this option to true will print useful information to the console while you're setting up Sentry.", + ' debug: false,', + '});', + ]); + }); + + test('sentry.server.config.ts contents', () => { + checkFileContents(path.resolve(projectDir, 'sentry.server.config.ts'), [ + 'import * as Sentry from "@sentry/nuxt";', + 'Sentry.init({', + ` dsn: "${TEST_ARGS.PROJECT_DSN}",`, + ' // We recommend adjusting this value in production, or using tracesSampler', + ' // for finer control', + ' tracesSampleRate: 1.0,', + " // Setting this option to true will print useful information to the console while you're setting up Sentry.", + ' debug: false,', + '});', + ]); + }); +} + +function testNuxtProjectBuildsAndRuns(projectDir: string) { + test('builds successfully', async () => { + await checkIfBuilds(projectDir, 'preview this build'); + }); + + test('runs on prod mode correctly', async () => { + await checkIfRunsOnProdMode(projectDir, 'Listening on'); + }); +} diff --git a/e2e-tests/tests/nuxt-4.test.ts b/e2e-tests/tests/nuxt-4.test.ts new file mode 100644 index 00000000..cf30ea9c --- /dev/null +++ b/e2e-tests/tests/nuxt-4.test.ts @@ -0,0 +1,168 @@ +import * as path from 'path'; +import { Integration } from '../../lib/Constants'; +import { cleanupGit, revertLocalChanges } from '../utils'; +import { + checkEnvBuildPlugin, + checkFileContents, + checkFileExists, + checkIfBuilds, + checkIfRunsOnProdMode, + checkPackageJson, + KEYS, + startWizardInstance, + TEST_ARGS, +} from '../utils'; + +describe('Nuxt-4', () => { + const projectDir = path.resolve( + __dirname, + '../test-applications/nuxt-4-test-app', + ); + + beforeAll(async () => { + await runWizardOnNuxtProject(projectDir); + }); + + afterAll(() => { + revertLocalChanges(projectDir); + cleanupGit(projectDir); + }); + + testNuxtProjectSetup(projectDir); + + testNuxtProjectConfigs(projectDir); + + testNuxtProjectBuildsAndRuns(projectDir); +}); + +async function runWizardOnNuxtProject(projectDir: string): Promise { + const integration = Integration.nuxt; + + const wizardInstance = startWizardInstance(integration, projectDir); + const packageManagerPrompted = await wizardInstance.waitForOutput( + 'Please select your package manager.', + ); + + const tracingOptionPrompted = + packageManagerPrompted && + (await wizardInstance.sendStdinAndWaitForOutput( + // Selecting `yarn` as the package manager + [KEYS.DOWN, KEYS.ENTER], + // "Do you want to enable Tracing", sometimes doesn't work as `Tracing` can be printed in bold. + 'to track the performance of your application?', + { + timeout: 240_000, + }, + )); + + const replayOptionPrompted = + tracingOptionPrompted && + (await wizardInstance.sendStdinAndWaitForOutput( + [KEYS.ENTER], + // "Do you want to enable Sentry Session Replay", sometimes doesn't work as `Sentry Session Replay` can be printed in bold. + 'to get a video-like reproduction of errors during a user session?', + )); + + replayOptionPrompted && + (await wizardInstance.sendStdinAndWaitForOutput( + [KEYS.ENTER], + 'Do you want to create an example page', + { + optional: true, + }, + )); + + await wizardInstance.sendStdinAndWaitForOutput( + [KEYS.ENTER, KEYS.ENTER], + 'Successfully installed the Sentry Nuxt SDK!', + ); + + wizardInstance.kill(); +} + +function testNuxtProjectSetup(projectDir: string) { + const integration = Integration.nuxt; + + test('package.json is updated correctly', () => { + checkPackageJson(projectDir, integration); + }); + + test('.env-sentry-build-plugin is created and contains the auth token', () => { + checkEnvBuildPlugin(projectDir); + }); + + test('config files created', () => { + checkFileExists(`${projectDir}/sentry.server.config.ts`); + checkFileExists(`${projectDir}/sentry.client.config.ts`); + }); + + test('example page exists', () => { + checkFileExists(`${projectDir}'/app/pages/sentry-example-page.vue`); + checkFileExists(`${projectDir}/server/api/sentry-example-api.ts`); + }); +} + +function testNuxtProjectConfigs(projectDir: string) { + test('nuxt config contains sentry module', () => { + checkFileContents(path.resolve(projectDir, 'nuxt.config.ts'), [ + "modules: ['@sentry/nuxt/module'],", + 'sentry: {', + ' sourceMapsUploadOptions: {', + ` org: '${TEST_ARGS.ORG_SLUG}',`, + ` project: '${TEST_ARGS.PROJECT_SLUG}'`, + ' }', + '},', + 'sourcemap: {', + ' client: true', + '}', + ]); + }); + + test('sentry.client.config.ts contents', () => { + checkFileContents(path.resolve(projectDir, 'sentry.client.config.ts'), [ + 'import * as Sentry from "@sentry/nuxt";', + 'Sentry.init({', + ' // If set up, you can use your runtime config here', + ' // dsn: useRuntimeConfig().public.sentry.dsn,', + ` dsn: "${TEST_ARGS.PROJECT_DSN}",`, + ' // We recommend adjusting this value in production, or using tracesSampler', + ' // for finer control', + ' tracesSampleRate: 1.0,', + ' // This sets the sample rate to be 10%. You may want this to be 100% while', + ' // in development and sample at a lower rate in production', + ' replaysSessionSampleRate: 0.1,', + ' // If the entire session is not sampled, use the below sample rate to sample', + ' // sessions when an error occurs.', + ' replaysOnErrorSampleRate: 1.0,', + " // If you don't want to use Session Replay, just remove the line below:", + ' integrations: [Sentry.replayIntegration()],', + " // Setting this option to true will print useful information to the console while you're setting up Sentry.", + ' debug: false,', + '});', + ]); + }); + + test('sentry.server.config.ts contents', () => { + checkFileContents(path.resolve(projectDir, 'sentry.server.config.ts'), [ + 'import * as Sentry from "@sentry/nuxt";', + 'Sentry.init({', + ` dsn: "${TEST_ARGS.PROJECT_DSN}",`, + ' // We recommend adjusting this value in production, or using tracesSampler', + ' // for finer control', + ' tracesSampleRate: 1.0,', + " // Setting this option to true will print useful information to the console while you're setting up Sentry.", + ' debug: false,', + '});', + ]); + }); +} + +function testNuxtProjectBuildsAndRuns(projectDir: string) { + test('builds successfully', async () => { + await checkIfBuilds(projectDir, 'preview this build'); + }); + + test('runs on prod mode correctly', async () => { + await checkIfRunsOnProdMode(projectDir, 'Listening on'); + }); +}