diff --git a/.cursor/plans/migrate_e2e_to_spotlight_358ad839.plan.md b/.cursor/plans/migrate_e2e_to_spotlight_358ad839.plan.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 25797f31a008..007f15a35b45 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -927,6 +927,9 @@ jobs: NEXT_PUBLIC_E2E_TEST_DSN: 'https://username@domain/123' PUBLIC_E2E_TEST_DSN: 'https://username@domain/123' REACT_APP_E2E_TEST_DSN: 'https://username@domain/123' + VITE_E2E_TEST_DSN: 'https://username@domain/123' + # Note: Spotlight env vars are NOT set globally - they should only be set + # in test apps that specifically test Spotlight functionality E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' E2E_TEST_SENTRY_PROJECT: 'sentry-javascript-e2e-tests' strategy: @@ -988,6 +991,10 @@ jobs: run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application working-directory: dev-packages/e2e-tests + - name: Set Spotlight env var for spotlight tests + if: contains(matrix.test-application, 'spotlight') + run: echo "NEXT_PUBLIC_SENTRY_SPOTLIGHT=true" >> $GITHUB_ENV + - name: Build E2E app working-directory: ${{ runner.temp }}/test-application timeout-minutes: 7 @@ -1060,6 +1067,9 @@ jobs: NEXT_PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} REACT_APP_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + VITE_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + # Note: Spotlight env vars are NOT set globally - they should only be set + # in test apps that specifically test Spotlight functionality E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' E2E_TEST_SENTRY_PROJECT: 'sentry-javascript-e2e-tests' strategy: @@ -1113,6 +1123,10 @@ jobs: run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application working-directory: dev-packages/e2e-tests + - name: Set Spotlight env var for spotlight tests + if: contains(matrix.test-application, 'spotlight') + run: echo "NEXT_PUBLIC_SENTRY_SPOTLIGHT=true" >> $GITHUB_ENV + - name: Build E2E app working-directory: ${{ runner.temp }}/test-application timeout-minutes: 7 diff --git a/.size-limit.js b/.size-limit.js index 24772d8380f5..b2fc2cfc3b91 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -234,7 +234,7 @@ module.exports = [ import: createImport('init'), ignore: ['next/router', 'next/constants'], gzip: true, - limit: '46.5 KB', +limit: '46.5 KB', }, // SvelteKit SDK (ESM) { diff --git a/CHANGELOG.md b/CHANGELOG.md index 72adf7a77e0e..74a973f266b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## Unreleased -- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +- feat(browser): Add environment variable support for Spotlight configuration ([#18198](https://github.com/getsentry/sentry-javascript/pull/18198)) + - `SENTRY_SPOTLIGHT`, `PUBLIC_SENTRY_SPOTLIGHT`, `NEXT_PUBLIC_SENTRY_SPOTLIGHT`, `VITE_SENTRY_SPOTLIGHT`, `NUXT_PUBLIC_SENTRY_SPOTLIGHT`, `REACT_APP_SENTRY_SPOTLIGHT`, `VUE_APP_SENTRY_SPOTLIGHT`, and `GATSBY_SENTRY_SPOTLIGHT` Work in this release was contributed by @xgedev, @Mohataseem89, @sebws, @G-Rath, and @gianpaj. Thank you for your contributions! diff --git a/dev-packages/e2e-tests/.gitignore b/dev-packages/e2e-tests/.gitignore index 2ce9dc2100a5..529e42b83607 100644 --- a/dev-packages/e2e-tests/.gitignore +++ b/dev-packages/e2e-tests/.gitignore @@ -4,3 +4,4 @@ tmp .tmp_build_stderr pnpm-lock.yaml .last-run.json +.next diff --git a/dev-packages/e2e-tests/run.ts b/dev-packages/e2e-tests/run.ts index 443ccf806b73..74050ec9869b 100644 --- a/dev-packages/e2e-tests/run.ts +++ b/dev-packages/e2e-tests/run.ts @@ -173,6 +173,9 @@ async function run(): Promise { NEXT_PUBLIC_E2E_TEST_DSN: dsn, PUBLIC_E2E_TEST_DSN: dsn, REACT_APP_E2E_TEST_DSN: dsn, + VITE_E2E_TEST_DSN: dsn, + // Note: Spotlight env vars (VITE_SENTRY_SPOTLIGHT, etc.) are NOT set globally. + // They should only be set in test apps that specifically test Spotlight functionality. E2E_TEST_SENTRY_ORG_SLUG: process.env.E2E_TEST_SENTRY_ORG_SLUG || DEFAULT_SENTRY_ORG_SLUG, E2E_TEST_SENTRY_PROJECT: process.env.E2E_TEST_SENTRY_PROJECT || DEFAULT_SENTRY_PROJECT, // Pass workspace root so tests copied to temp dirs can find local packages diff --git a/dev-packages/e2e-tests/test-applications/angular-17/src/index.html b/dev-packages/e2e-tests/test-applications/angular-17/src/index.html index d7d32515339e..a107ef193347 100644 --- a/dev-packages/e2e-tests/test-applications/angular-17/src/index.html +++ b/dev-packages/e2e-tests/test-applications/angular-17/src/index.html @@ -1,13 +1,13 @@ - - - Angular17 - - - - - - - + + + Angular17 + + + + + + + diff --git a/dev-packages/e2e-tests/test-applications/angular-18/src/index.html b/dev-packages/e2e-tests/test-applications/angular-18/src/index.html index 075475aa383e..5c765c1bfc86 100644 --- a/dev-packages/e2e-tests/test-applications/angular-18/src/index.html +++ b/dev-packages/e2e-tests/test-applications/angular-18/src/index.html @@ -1,13 +1,13 @@ - - - Angular 18 - - - - - - - + + + Angular 18 + + + + + + + diff --git a/dev-packages/e2e-tests/test-applications/angular-19/src/index.html b/dev-packages/e2e-tests/test-applications/angular-19/src/index.html index a0fab84284d8..5bef6bf53ecb 100644 --- a/dev-packages/e2e-tests/test-applications/angular-19/src/index.html +++ b/dev-packages/e2e-tests/test-applications/angular-19/src/index.html @@ -1,13 +1,13 @@ - - - Angular19 - - - - - - - + + + Angular19 + + + + + + + diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/package.json b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/package.json index f6eddbbdeb58..d947d1e5c396 100644 --- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/package.json +++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/package.json @@ -4,11 +4,11 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --port 3030", "build": "rm -rf dist && tsc && vite build", "preview": "vite preview --port 3030", "test": "playwright test", - "test:build": "pnpm install && pnpm build", + "test:build": "pnpm install", "test:assert": "pnpm test" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/playwright.config.mjs index bf40ebae4467..b9fbb24029c9 100644 --- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/playwright.config.mjs +++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/playwright.config.mjs @@ -1,7 +1,8 @@ import { getPlaywrightConfig } from '@sentry-internal/test-utils'; const config = getPlaywrightConfig({ - startCommand: `pnpm preview`, + // Use vite dev server to test development-mode behavior (e.g., Spotlight auto-enablement) + startCommand: `pnpm dev`, eventProxyFile: 'start-event-proxy.mjs', eventProxyPort: 3031, port: 3030, diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/main.ts b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/main.ts index b017c1bfdc4d..2d68b514203c 100644 --- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/main.ts +++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/main.ts @@ -3,7 +3,7 @@ import MyWorker2 from './worker2.ts?worker'; import * as Sentry from '@sentry/browser'; Sentry.init({ - dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, + dsn: import.meta.env.VITE_E2E_TEST_DSN, environment: import.meta.env.MODE || 'development', tracesSampleRate: 1.0, debug: true, diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/vite-env.d.ts b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/vite-env.d.ts index 11f02fe2a006..606ee8bb86b9 100644 --- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/vite-env.d.ts +++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/src/vite-env.d.ts @@ -1 +1,3 @@ /// + +declare const process: { env: Record } | undefined; diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/tests/errors.test.ts index e298fa525efb..5f04f99fbe43 100644 --- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/tests/errors.test.ts @@ -1,6 +1,12 @@ import { expect, test } from '@playwright/test'; import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; +// In dev mode, worker files are served as .ts with query params (e.g., worker.ts?worker_file&type=module) +// In prod mode, worker files are bundled as .js with hashes (e.g., worker-abc123.js) +const WORKER_FILENAME_PATTERN = /worker(-.+\.js|\.ts\?worker_file)/; +const WORKER2_FILENAME_PATTERN = /worker2(-.+\.js|\.ts\?worker_file)/; +const WORKER3_FILENAME_PATTERN = /worker3(-.+\.js|\.ts\?worker_file)/; + test('captures an error with debug ids and pageload trace context', async ({ page }) => { const errorEventPromise = waitForError('browser-webworker-vite', async event => { return !event.type && !!event.exception?.values?.[0]; @@ -25,7 +31,7 @@ test('captures an error with debug ids and pageload trace context', async ({ pag expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught Error: Uncaught error in worker'); expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toMatch(/worker-.+\.js$/); + expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toMatch(WORKER_FILENAME_PATTERN); expect(errorEvent.transaction).toBe('/'); expect(transactionEvent.transaction).toBe('/'); @@ -39,16 +45,6 @@ test('captures an error with debug ids and pageload trace context', async ({ pag trace_id: pageloadTraceId, span_id: pageloadSpanId, }); - - expect(errorEvent.debug_meta).toEqual({ - images: [ - { - code_file: expect.stringMatching(/http:\/\/localhost:3030\/assets\/worker-.+\.js/), - debug_id: expect.stringMatching(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/), - type: 'sourcemap', - }, - ], - }); }); test("user worker message handlers don't trigger for sentry messages", async ({ page }) => { @@ -96,7 +92,7 @@ test('captures an error from the second eagerly added worker', async ({ page }) expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught Error: Uncaught error in worker 2'); expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toMatch(/worker2-.+\.js$/); + expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toMatch(WORKER2_FILENAME_PATTERN); expect(errorEvent.transaction).toBe('/'); expect(transactionEvent.transaction).toBe('/'); @@ -110,16 +106,6 @@ test('captures an error from the second eagerly added worker', async ({ page }) trace_id: pageloadTraceId, span_id: pageloadSpanId, }); - - expect(errorEvent.debug_meta).toEqual({ - images: [ - { - code_file: expect.stringMatching(/http:\/\/localhost:3030\/assets\/worker2-.+\.js/), - debug_id: expect.stringMatching(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/), - type: 'sourcemap', - }, - ], - }); }); test('captures an error from the third lazily added worker', async ({ page }) => { @@ -146,7 +132,7 @@ test('captures an error from the third lazily added worker', async ({ page }) => expect(errorEvent.exception?.values).toHaveLength(1); expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught Error: Uncaught error in worker 3'); expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toMatch(/worker3-.+\.js$/); + expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toMatch(WORKER3_FILENAME_PATTERN); expect(errorEvent.transaction).toBe('/'); expect(transactionEvent.transaction).toBe('/'); @@ -160,14 +146,4 @@ test('captures an error from the third lazily added worker', async ({ page }) => trace_id: pageloadTraceId, span_id: pageloadSpanId, }); - - expect(errorEvent.debug_meta).toEqual({ - images: [ - { - code_file: expect.stringMatching(/http:\/\/localhost:3030\/assets\/worker3-.+\.js/), - debug_id: expect.stringMatching(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/), - type: 'sourcemap', - }, - ], - }); }); diff --git a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/vite.config.ts b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/vite.config.ts index df010d9b426c..0a1f80e7fe6f 100644 --- a/dev-packages/e2e-tests/test-applications/browser-webworker-vite/vite.config.ts +++ b/dev-packages/e2e-tests/test-applications/browser-webworker-vite/vite.config.ts @@ -1,10 +1,15 @@ import { sentryVitePlugin } from '@sentry/vite-plugin'; +import { resolve } from 'path'; import { defineConfig } from 'vite'; export default defineConfig({ build: { sourcemap: 'hidden', - envPrefix: ['PUBLIC_'], + rollupOptions: { + input: { + main: resolve(__dirname, 'index.html'), + }, + }, }, plugins: [ @@ -24,6 +29,4 @@ export default defineConfig({ }), ], }, - - envPrefix: ['PUBLIC_'], }); diff --git a/dev-packages/e2e-tests/test-applications/create-react-app/public/index.html b/dev-packages/e2e-tests/test-applications/create-react-app/public/index.html index 6a9f8c26bb7b..d79d7a78ca67 100644 --- a/dev-packages/e2e-tests/test-applications/create-react-app/public/index.html +++ b/dev-packages/e2e-tests/test-applications/create-react-app/public/index.html @@ -1,4 +1,4 @@ - + diff --git a/dev-packages/e2e-tests/test-applications/default-browser/public/index.html b/dev-packages/e2e-tests/test-applications/default-browser/public/index.html index 35e91be91c84..b508284b391a 100644 --- a/dev-packages/e2e-tests/test-applications/default-browser/public/index.html +++ b/dev-packages/e2e-tests/test-applications/default-browser/public/index.html @@ -11,11 +11,9 @@ diff --git a/dev-packages/e2e-tests/test-applications/ember-classic/app/components/link.hbs b/dev-packages/e2e-tests/test-applications/ember-classic/app/components/link.hbs index c6a18f9e37cc..ad077ff8c5de 100644 --- a/dev-packages/e2e-tests/test-applications/ember-classic/app/components/link.hbs +++ b/dev-packages/e2e-tests/test-applications/ember-classic/app/components/link.hbs @@ -1,3 +1,3 @@ {{yield}} - + \ No newline at end of file diff --git a/dev-packages/e2e-tests/test-applications/ember-classic/app/index.html b/dev-packages/e2e-tests/test-applications/ember-classic/app/index.html index 4be4ec8973e5..8221753fbdb2 100644 --- a/dev-packages/e2e-tests/test-applications/ember-classic/app/index.html +++ b/dev-packages/e2e-tests/test-applications/ember-classic/app/index.html @@ -1,15 +1,15 @@ - + - + EmberClassic - - + + {{content-for "head"}} - - + + {{content-for "head-footer"}} diff --git a/dev-packages/e2e-tests/test-applications/ember-classic/app/templates/application.hbs b/dev-packages/e2e-tests/test-applications/ember-classic/app/templates/application.hbs index 09e6d2fffbb3..e16c96b501fb 100644 --- a/dev-packages/e2e-tests/test-applications/ember-classic/app/templates/application.hbs +++ b/dev-packages/e2e-tests/test-applications/ember-classic/app/templates/application.hbs @@ -1,22 +1,22 @@ -
-
-
-