diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml
new file mode 100644
index 000000000000..8cb80ac7440e
--- /dev/null
+++ b/.github/actions/install-dependencies/action.yml
@@ -0,0 +1,29 @@
+name: "Install yarn dependencies"
+description: "Installs yarn dependencies and caches them."
+
+outputs:
+ cache_key:
+ description: "The dependency cache key"
+ value: ${{ steps.compute_lockfile_hash.outputs.hash }}
+
+runs:
+ using: "composite"
+ steps:
+ # we use a hash of yarn.lock as our cache key, because if it hasn't changed, our dependencies haven't changed,
+ # so no need to reinstall them
+ - name: Compute dependency cache key
+ id: compute_lockfile_hash
+ run: echo "hash=dependencies-${{ hashFiles('yarn.lock', 'packages/*/package.json', 'dev-packages/*/package.json') }}" >> "$GITHUB_OUTPUT"
+ shell: bash
+
+ - name: Check dependency cache
+ uses: actions/cache@v4
+ id: cache_dependencies
+ with:
+ path: ${{ env.CACHED_DEPENDENCY_PATHS }}
+ key: ${{ steps.compute_lockfile_hash.outputs.hash }}
+
+ - name: Install dependencies
+ if: steps.cache_dependencies.outputs.cache-hit != 'true'
+ run: yarn install --ignore-engines --frozen-lockfile
+ shell: bash
diff --git a/.github/actions/install-playwright/action.yml b/.github/actions/install-playwright/action.yml
index 7f85f5e743ba..9de6e1a2b104 100644
--- a/.github/actions/install-playwright/action.yml
+++ b/.github/actions/install-playwright/action.yml
@@ -13,14 +13,23 @@ runs:
run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> $GITHUB_OUTPUT
shell: bash
- - name: Cache playwright binaries
- uses: actions/cache@v4
+ - name: Restore cached playwright binaries
+ uses: actions/cache/restore@v4
id: playwright-cache
with:
path: |
~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
+ # Only store cache on develop branch
+ - name: Store cached playwright binaries
+ uses: actions/cache/save@v4
+ if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
+ with:
+ path: |
+ ~/.cache/ms-playwright
+ key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
+
# We always install all browsers, if uncached
- name: Install Playwright dependencies (uncached)
run: npx playwright install chromium webkit firefox --with-deps
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a956f585b414..516b1d47b624 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -146,22 +146,9 @@ jobs:
with:
node-version-file: 'package.json'
- # we use a hash of yarn.lock as our cache key, because if it hasn't changed, our dependencies haven't changed,
- # so no need to reinstall them
- - name: Compute dependency cache key
- id: compute_lockfile_hash
- run: echo "hash=${{ hashFiles('yarn.lock', '**/package.json') }}" >> "$GITHUB_OUTPUT"
-
- - name: Check dependency cache
- uses: actions/cache@v4
- id: cache_dependencies
- with:
- path: ${{ env.CACHED_DEPENDENCY_PATHS }}
- key: ${{ steps.compute_lockfile_hash.outputs.hash }}
-
- - name: Install dependencies
- if: steps.cache_dependencies.outputs.cache-hit != 'true'
- run: yarn install --ignore-engines --frozen-lockfile
+ - name: Install Dependencies
+ uses: ./.github/actions/install-dependencies
+ id: install_dependencies
- name: Check for Affected Nx Projects
uses: dkhunt27/action-nx-affected-list@v5.3
@@ -200,7 +187,7 @@ jobs:
run: yarn build
outputs:
- dependency_cache_key: ${{ steps.compute_lockfile_hash.outputs.hash }}
+ dependency_cache_key: ${{ steps.install_dependencies.outputs.cache_key }}
changed_node_integration: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry-internal/node-integration-tests') }}
changed_remix: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/remix') }}
changed_node: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/node') }}
@@ -293,22 +280,9 @@ jobs:
with:
node-version-file: 'package.json'
- # we use a hash of yarn.lock as our cache key, because if it hasn't changed, our dependencies haven't changed,
- # so no need to reinstall them
- - name: Compute dependency cache key
- id: compute_lockfile_hash
- run: echo "hash=${{ hashFiles('yarn.lock', '**/package.json') }}" >> "$GITHUB_OUTPUT"
-
- - name: Check dependency cache
- uses: actions/cache@v4
- id: cache_dependencies
- with:
- path: ${{ env.CACHED_DEPENDENCY_PATHS }}
- key: ${{ steps.compute_lockfile_hash.outputs.hash }}
-
- - name: Install dependencies
- if: steps.cache_dependencies.outputs.cache-hit != 'true'
- run: yarn install --ignore-engines --frozen-lockfile
+ - name: Install Dependencies
+ uses: ./.github/actions/install-dependencies
+ id: install_dependencies
- name: Check file formatting
run: yarn lint:prettier && yarn lint:biome
@@ -873,6 +847,7 @@ jobs:
[
'angular-17',
'angular-18',
+ 'astro-4',
'aws-lambda-layer-cjs',
'aws-serverless-esm',
'node-express',
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 931d1462da47..7864efac6871 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,60 @@
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
+Work in this release was contributed by @charpeni. Thank you for your contribution!
+
+## 8.26.0
+
+### Important Changes
+
+- **feat(node): Add `fsInstrumentation` (#13291)**
+
+ This release adds `fsIntegration`, an integration that instruments the `fs` API to the Sentry Node SDK. The
+ integration creates spans with naming patterns of `fs.readFile`, `fs.unlink`, and so on.
+
+ This integration is not enabled by default and needs to be registered in your `Sentry.init` call. You can configure
+ via options whether to include path arguments or error messages as span attributes when an fs call fails:
+
+ ```js
+ Sentry.init({
+ integrations: [
+ Sentry.fsIntegration({
+ recordFilePaths: true,
+ recordErrorMessagesAsSpanAttributes: true,
+ }),
+ ],
+ });
+ ```
+
+ **WARNING:** This integration may add significant overhead to your application. Especially in scenarios with a lot of
+ file I/O, like for example when running a framework dev server, including this integration can massively slow down
+ your application.
+
+### Other Changes
+
+- feat(browser): Add spotlightBrowser integration (#13263)
+- feat(browser): Allow sentry in safari extension background page (#13209)
+- feat(browser): Send CLS as standalone span (experimental) (#13056)
+- feat(core): Add OpenTelemetry-specific `getTraceData` implementation (#13281)
+- feat(nextjs): Always add `browserTracingIntegration` (#13324)
+- feat(nextjs): Always transmit trace data to the client (#13337)
+- feat(nextjs): export SentryBuildOptions (#13296)
+- feat(nextjs): Update `experimental_captureRequestError` to reflect `RequestInfo.path` change in Next.js canary
+ (#13344)
+
+- feat(nuxt): Always add tracing meta tags (#13273)
+- feat(nuxt): Set transaction name for server error (#13292)
+- feat(replay): Add a replay-specific logger (#13256)
+- feat(sveltekit): Add bundle size optimizations to plugin options (#13318)
+- feat(sveltekit): Always add browserTracingIntegration (#13322)
+- feat(tracing): Make long animation frames opt-out (#13255)
+- fix(astro): Correctly extract request data (#13315)
+- fix(astro): Only track access request headers in dynamic page requests (#13306)
+- fix(nuxt): Add import line for disabled `autoImport` (#13342)
+- fix(nuxt): Add vue to excludeEsmLoaderHooks array (#13346)
+- fix(opentelemetry): Do not overwrite http span name if kind is internal (#13282)
+- fix(remix): Ensure `origin` is correctly set for remix server spans (#13305)
+
Work in this release was contributed by @MonstraG, @undead-voron and @Zen-cronic. Thank you for your contributions!
## 8.25.0
diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/sdkLoadedInMeanwhile/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/sdkLoadedInMeanwhile/test.ts
index 44eca49888be..62456093a9a6 100644
--- a/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/sdkLoadedInMeanwhile/test.ts
+++ b/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/sdkLoadedInMeanwhile/test.ts
@@ -1,5 +1,5 @@
-import fs from 'fs';
-import path from 'path';
+import * as fs from 'fs';
+import * as path from 'path';
import { expect } from '@playwright/test';
import { TEST_HOST, sentryTest } from '../../../../utils/fixtures';
@@ -28,6 +28,8 @@ sentryTest('it does not download the SDK if the SDK was loaded in the meanwhile'
});
});
+ const tmpDir = await getLocalTestUrl({ testDir: __dirname, skipRouteHandler: true });
+
await page.route(`${TEST_HOST}/*.*`, route => {
const file = route.request().url().split('/').pop();
@@ -35,12 +37,13 @@ sentryTest('it does not download the SDK if the SDK was loaded in the meanwhile'
cdnLoadedCount++;
}
- const filePath = path.resolve(__dirname, `./dist/${file}`);
+ const filePath = path.resolve(tmpDir, `./${file}`);
return fs.existsSync(filePath) ? route.fulfill({ path: filePath }) : route.continue();
});
- const url = await getLocalTestUrl({ testDir: __dirname, skipRouteHandler: true });
+ const url = `${TEST_HOST}/index.html`;
+
const req = await waitForErrorRequestOnUrl(page, url);
const eventData = envelopeRequestParser(req);
diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json
index 84b90b6045a9..2e2e6cab12ab 100644
--- a/dev-packages/browser-integration-tests/package.json
+++ b/dev-packages/browser-integration-tests/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/browser-integration-tests",
- "version": "8.25.0",
+ "version": "8.26.0",
"main": "index.js",
"license": "MIT",
"engines": {
@@ -43,7 +43,7 @@
"@babel/preset-typescript": "^7.16.7",
"@playwright/test": "^1.44.1",
"@sentry-internal/rrweb": "2.11.0",
- "@sentry/browser": "8.25.0",
+ "@sentry/browser": "8.26.0",
"axios": "1.6.7",
"babel-loader": "^8.2.2",
"html-webpack-plugin": "^5.5.0",
diff --git a/dev-packages/browser-integration-tests/playwright.config.ts b/dev-packages/browser-integration-tests/playwright.config.ts
index b03f758e11dc..45a548311eef 100644
--- a/dev-packages/browser-integration-tests/playwright.config.ts
+++ b/dev-packages/browser-integration-tests/playwright.config.ts
@@ -31,6 +31,7 @@ const config: PlaywrightTestConfig = {
],
globalSetup: require.resolve('./playwright.setup.ts'),
+ globalTeardown: require.resolve('./playwright.teardown.ts'),
};
export default config;
diff --git a/dev-packages/browser-integration-tests/playwright.teardown.ts b/dev-packages/browser-integration-tests/playwright.teardown.ts
new file mode 100644
index 000000000000..3afbf63f0aa1
--- /dev/null
+++ b/dev-packages/browser-integration-tests/playwright.teardown.ts
@@ -0,0 +1,5 @@
+import * as childProcess from 'child_process';
+
+export default function globalTeardown(): void {
+ childProcess.execSync('yarn clean', { stdio: 'inherit', cwd: process.cwd() });
+}
diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js
new file mode 100644
index 000000000000..067dbec23fd4
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js
@@ -0,0 +1,19 @@
+import * as Sentry from '@sentry/browser';
+// Import this separately so that generatePlugin can handle it for CDN scenarios
+import { feedbackIntegration } from '@sentry/browser';
+
+window.Sentry = Sentry;
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ integrations: [
+ feedbackIntegration({ tags: { from: 'integration init' }, styleNonce: 'foo1234', scriptNonce: 'foo1234' }),
+ ],
+});
+
+document.addEventListener('securitypolicyviolation', () => {
+ const container = document.querySelector('#csp-violation');
+ if (container) {
+ container.innerText = 'CSP Violation';
+ }
+});
diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html
new file mode 100644
index 000000000000..919f372ef468
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts
new file mode 100644
index 000000000000..95a8a2eacee8
--- /dev/null
+++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts
@@ -0,0 +1,84 @@
+import { expect } from '@playwright/test';
+
+import { TEST_HOST, sentryTest } from '../../../utils/fixtures';
+import { envelopeRequestParser, getEnvelopeType, shouldSkipFeedbackTest } from '../../../utils/helpers';
+
+sentryTest('should capture feedback', async ({ getLocalTestUrl, page }) => {
+ if (shouldSkipFeedbackTest()) {
+ sentryTest.skip();
+ }
+
+ const feedbackRequestPromise = page.waitForResponse(res => {
+ const req = res.request();
+
+ const postData = req.postData();
+ if (!postData) {
+ return false;
+ }
+
+ try {
+ return getEnvelopeType(req) === 'feedback';
+ } catch (err) {
+ return false;
+ }
+ });
+
+ await page.route('https://dsn.ingest.sentry.io/**/*', route => {
+ return route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ body: JSON.stringify({ id: 'test-id' }),
+ });
+ });
+
+ const url = await getLocalTestUrl({ testDir: __dirname });
+
+ await page.goto(url);
+ await page.getByText('Report a Bug').click();
+ expect(await page.locator(':visible:text-is("Report a Bug")').count()).toEqual(1);
+ await page.locator('[name="name"]').fill('Jane Doe');
+ await page.locator('[name="email"]').fill('janedoe@example.org');
+ await page.locator('[name="message"]').fill('my example feedback');
+ await page.locator('[data-sentry-feedback] .btn--primary').click();
+
+ const feedbackEvent = envelopeRequestParser((await feedbackRequestPromise).request());
+ expect(feedbackEvent).toEqual({
+ type: 'feedback',
+ breadcrumbs: expect.any(Array),
+ contexts: {
+ feedback: {
+ contact_email: 'janedoe@example.org',
+ message: 'my example feedback',
+ name: 'Jane Doe',
+ source: 'widget',
+ url: `${TEST_HOST}/index.html`,
+ },
+ trace: {
+ trace_id: expect.stringMatching(/\w{32}/),
+ span_id: expect.stringMatching(/\w{16}/),
+ },
+ },
+ level: 'info',
+ tags: {
+ from: 'integration init',
+ },
+ timestamp: expect.any(Number),
+ event_id: expect.stringMatching(/\w{32}/),
+ environment: 'production',
+ sdk: {
+ integrations: expect.arrayContaining(['Feedback']),
+ version: expect.any(String),
+ name: 'sentry.javascript.browser',
+ packages: expect.anything(),
+ },
+ request: {
+ url: `${TEST_HOST}/index.html`,
+ headers: {
+ 'User-Agent': expect.stringContaining(''),
+ },
+ },
+ platform: 'javascript',
+ });
+ const cspContainer = await page.locator('#csp-violation');
+ expect(cspContainer).not.toContainText('CSP Violation');
+});
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js
index f6e1e21e4611..1cbadc6e36e6 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js
@@ -1,3 +1,3 @@
-fetch('http://localhost:7654/foo').then(() => {
+fetch('http://sentry-test.io/foo').then(() => {
Sentry.captureException('test error');
});
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts
index f4e173940bc4..9ecf29c31014 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts
@@ -31,7 +31,7 @@ sentryTest('captures Breadcrumb for basic GET request', async ({ getLocalTestUrl
data: {
method: 'GET',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
});
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/subject.js
index 0ca20f1b5acb..2c014f821a43 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/subject.js
@@ -1,3 +1,3 @@
-fetch(new Request('http://localhost:7654/foo')).then(() => {
+fetch(new Request('http://sentry-test.io/foo')).then(() => {
Sentry.captureException('test error');
});
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/test.ts
index 3ffa68776fd2..8d545ea6f566 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/test.ts
@@ -31,7 +31,7 @@ sentryTest('captures Breadcrumb for basic GET request that uses request object',
data: {
method: 'GET',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
});
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js
index ea1bf44bc905..e1545e8060c6 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js
@@ -1,4 +1,4 @@
-fetch('http://localhost:7654/foo', {
+fetch('http://sentry-test.io/foo', {
method: 'POST',
body: '{"my":"body"}',
headers: {
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts
index 9267c1099bfd..fd27c71e6ad7 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts
@@ -31,7 +31,7 @@ sentryTest('captures Breadcrumb for POST request', async ({ getLocalTestUrl, pag
data: {
method: 'POST',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
});
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js
index f95bcb45c166..8202bb03803b 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js
@@ -1,6 +1,6 @@
const xhr = new XMLHttpRequest();
-xhr.open('GET', 'http://localhost:7654/foo');
+xhr.open('GET', 'http://sentry-test.io/foo');
xhr.send();
xhr.addEventListener('readystatechange', function () {
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts
index 858392b38444..9c4b14e89033 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts
@@ -32,7 +32,7 @@ sentryTest('captures Breadcrumb for basic GET request', async ({ getLocalTestUrl
data: {
method: 'GET',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
});
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js
index f8779b681d58..888d961ae9c5 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js
@@ -1,6 +1,6 @@
const xhr = new XMLHttpRequest();
-xhr.open('POST', 'http://localhost:7654/foo');
+xhr.open('POST', 'http://sentry-test.io/foo');
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send('{"my":"body"}');
diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts
index 8df0e468a12b..985e7e316244 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts
@@ -31,7 +31,7 @@ sentryTest('captures Breadcrumb for POST request', async ({ getLocalTestUrl, pag
data: {
method: 'POST',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
});
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/axios/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/axios/subject.js
index 5cac9d385e15..1e881c5f7181 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/axios/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/axios/subject.js
@@ -1,7 +1,7 @@
import axios from 'axios';
axios
- .get('http://localhost:7654/foo', {
+ .get('http://sentry-test.io/foo', {
headers: { Accept: 'application/json', 'Content-Type': 'application/json', Cache: 'no-cache' },
})
.then(response => {
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/axios/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpclient/axios/test.ts
index 3560a2598160..6318b337c165 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/axios/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/axios/test.ts
@@ -44,7 +44,7 @@ sentryTest(
],
},
request: {
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
method: 'GET',
headers: {
accept: 'application/json',
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/simple/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/simple/subject.js
index 94ab60d92ed9..93da60f0e2e6 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/simple/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/simple/subject.js
@@ -1,4 +1,4 @@
-fetch('http://localhost:7654/foo', {
+fetch('http://sentry-test.io/foo', {
method: 'GET',
credentials: 'include',
headers: {
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/simple/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/simple/test.ts
index bc56277c0f71..6cf8d7c7187f 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/simple/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/simple/test.ts
@@ -46,7 +46,7 @@ sentryTest(
],
},
request: {
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
method: 'GET',
headers: {
accept: 'application/json',
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withAbortController/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withAbortController/subject.js
index 78028b473ad7..6cb0fe3156fe 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withAbortController/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withAbortController/subject.js
@@ -10,7 +10,7 @@ const startFetch = e => {
forceTransaction: true,
},
async () => {
- await fetch('http://localhost:7654/foo', { signal })
+ await fetch('http://sentry-test.io/foo', { signal })
.then(response => response.json())
.then(data => {
console.log('Fetch succeeded:', data);
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequest/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequest/subject.js
index 07b538291b73..af5a031c9a61 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequest/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequest/subject.js
@@ -1,4 +1,4 @@
-const request = new Request('http://localhost:7654/foo', {
+const request = new Request('http://sentry-test.io/foo', {
method: 'POST',
credentials: 'include',
headers: {
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequest/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequest/test.ts
index 3caa6bd19652..e2dcd308417a 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequest/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequest/test.ts
@@ -42,7 +42,7 @@ sentryTest('works with a Request passed in', async ({ getLocalTestPath, page })
],
},
request: {
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
method: 'POST',
headers: {
accept: 'application/json',
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndBodyAndOptions/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndBodyAndOptions/subject.js
index 4659addc56d2..7972dd9eb291 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndBodyAndOptions/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndBodyAndOptions/subject.js
@@ -1,4 +1,4 @@
-const request = new Request('http://localhost:7654/foo', {
+const request = new Request('http://sentry-test.io/foo', {
method: 'POST',
credentials: 'include',
headers: {
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndBodyAndOptions/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndBodyAndOptions/test.ts
index 1ae88e8c5f5b..98519cd32129 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndBodyAndOptions/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndBodyAndOptions/test.ts
@@ -44,7 +44,7 @@ sentryTest(
],
},
request: {
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
method: 'POST',
headers: {
accept: 'application/json',
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndOptions/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndOptions/subject.js
index 96e3194dcfe2..8c2f763d2352 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndOptions/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndOptions/subject.js
@@ -1,4 +1,4 @@
-const request = new Request('http://localhost:7654/foo', {
+const request = new Request('http://sentry-test.io/foo', {
method: 'POST',
credentials: 'include',
headers: {
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndOptions/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndOptions/test.ts
index 9840f91ba272..4d3fe8458bac 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndOptions/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/fetch/withRequestAndOptions/test.ts
@@ -42,7 +42,7 @@ sentryTest('works with a Request (without body) & options passed in', async ({ g
],
},
request: {
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
method: 'POST',
headers: {
accept: 'application/json',
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js
index 7a2e3cdd28c0..563b069e66cc 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js
@@ -1,6 +1,6 @@
const xhr = new XMLHttpRequest();
-xhr.open('GET', 'http://localhost:7654/foo', true);
+xhr.open('GET', 'http://sentry-test.io/foo', true);
xhr.withCredentials = true;
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json');
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts
index 8bf8efa34cc4..f064a8652b48 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts
@@ -42,7 +42,7 @@ sentryTest('works with httpClientIntegration', async ({ getLocalTestPath, page }
],
},
request: {
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
method: 'GET',
headers: {
accept: 'application/json',
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/xhr/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/xhr/subject.js
index 7a2e3cdd28c0..563b069e66cc 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/xhr/subject.js
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/xhr/subject.js
@@ -1,6 +1,6 @@
const xhr = new XMLHttpRequest();
-xhr.open('GET', 'http://localhost:7654/foo', true);
+xhr.open('GET', 'http://sentry-test.io/foo', true);
xhr.withCredentials = true;
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json');
diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/xhr/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpclient/xhr/test.ts
index a3283be9cc00..6595fc0a7399 100644
--- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/xhr/test.ts
+++ b/dev-packages/browser-integration-tests/suites/integrations/httpclient/xhr/test.ts
@@ -44,7 +44,7 @@ sentryTest(
],
},
request: {
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
method: 'GET',
headers: {
accept: 'application/json',
diff --git a/dev-packages/browser-integration-tests/suites/public-api/init/built-pkg/test.ts b/dev-packages/browser-integration-tests/suites/public-api/init/built-pkg/test.ts
index 82b08fad52b6..a33a138927f5 100644
--- a/dev-packages/browser-integration-tests/suites/public-api/init/built-pkg/test.ts
+++ b/dev-packages/browser-integration-tests/suites/public-api/init/built-pkg/test.ts
@@ -6,14 +6,14 @@ import { expect } from '@playwright/test';
import { sentryTest } from '../../../../utils/fixtures';
// Regression test against https://github.com/getsentry/sentry-javascript/pull/1896
-sentryTest('should not contain tslib_1__default', async ({ getLocalTestPath }) => {
- await getLocalTestPath({ testDir: __dirname });
+sentryTest('should not contain tslib_1__default', async ({ getLocalTestUrl }) => {
+ const tmpDir = await getLocalTestUrl({ testDir: __dirname, skipRouteHandler: true });
- const initBundle = fs.readFileSync(path.join(__dirname, 'dist', 'init.bundle.js'), 'utf-8');
+ const initBundle = fs.readFileSync(path.join(tmpDir, 'init.bundle.js'), 'utf-8');
expect(initBundle.length).toBeGreaterThan(0);
expect(initBundle).not.toContain('tslib_1__default');
- const subjectBundle = fs.readFileSync(path.join(__dirname, 'dist', 'subject.bundle.js'), 'utf-8');
+ const subjectBundle = fs.readFileSync(path.join(tmpDir, 'subject.bundle.js'), 'utf-8');
expect(subjectBundle.length).toBeGreaterThan(0);
expect(subjectBundle).not.toContain('tslib_1__default');
});
diff --git a/dev-packages/browser-integration-tests/suites/replay/captureReplay/test.ts b/dev-packages/browser-integration-tests/suites/replay/captureReplay/test.ts
index 0b1e056123e8..b2a7fa6dc3ac 100644
--- a/dev-packages/browser-integration-tests/suites/replay/captureReplay/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/captureReplay/test.ts
@@ -34,7 +34,7 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
timestamp: expect.any(Number),
error_ids: [],
trace_ids: [],
- urls: [expect.stringContaining('/dist/index.html')],
+ urls: [expect.stringMatching(/\/dist\/([\w-]+)\/index\.html$/)],
replay_id: expect.stringMatching(/\w{32}/),
replay_start_timestamp: expect.any(Number),
segment_id: 0,
@@ -57,7 +57,7 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
name: 'sentry.javascript.browser',
},
request: {
- url: expect.stringContaining('/dist/index.html'),
+ url: expect.stringMatching(/\/dist\/([\w-]+)\/index\.html$/),
headers: {
'User-Agent': expect.stringContaining(''),
},
@@ -94,7 +94,7 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
name: 'sentry.javascript.browser',
},
request: {
- url: expect.stringContaining('/dist/index.html'),
+ url: expect.stringMatching(/\/dist\/([\w-]+)\/index\.html$/),
headers: {
'User-Agent': expect.stringContaining(''),
},
diff --git a/dev-packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts b/dev-packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts
index 58260c4c577f..6267413ff84e 100644
--- a/dev-packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts
@@ -34,7 +34,7 @@ sentryTest('should capture replays (@sentry-internal/replay export)', async ({ g
timestamp: expect.any(Number),
error_ids: [],
trace_ids: [],
- urls: [expect.stringContaining('/dist/index.html')],
+ urls: [expect.stringMatching(/\/dist\/([\w-]+)\/index\.html$/)],
replay_id: expect.stringMatching(/\w{32}/),
replay_start_timestamp: expect.any(Number),
segment_id: 0,
@@ -57,7 +57,7 @@ sentryTest('should capture replays (@sentry-internal/replay export)', async ({ g
name: 'sentry.javascript.browser',
},
request: {
- url: expect.stringContaining('/dist/index.html'),
+ url: expect.stringMatching(/\/dist\/([\w-]+)\/index\.html$/),
headers: {
'User-Agent': expect.stringContaining(''),
},
@@ -94,7 +94,7 @@ sentryTest('should capture replays (@sentry-internal/replay export)', async ({ g
name: 'sentry.javascript.browser',
},
request: {
- url: expect.stringContaining('/dist/index.html'),
+ url: expect.stringMatching(/\/dist\/([\w-]+)\/index\.html$/),
headers: {
'User-Agent': expect.stringContaining(''),
},
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestBody/init.js b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestBody/init.js
index c43d5713f173..9db232f536cd 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestBody/init.js
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestBody/init.js
@@ -6,7 +6,7 @@ window.Replay = Sentry.replayIntegration({
flushMaxDelay: 200,
minReplayDuration: 0,
- networkDetailAllowUrls: ['http://localhost:7654/foo', 'http://sentry-test.io/foo'],
+ networkDetailAllowUrls: ['http://sentry-test.io/foo'],
networkCaptureBodies: true,
});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestBody/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestBody/test.ts
index bd8050b740aa..f8b48ffec598 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestBody/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestBody/test.ts
@@ -8,14 +8,14 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures text request body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures text request body', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -34,19 +34,17 @@ sentryTest('captures text request body', async ({ getLocalTestPath, page, browse
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
body: 'input body',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -63,7 +61,7 @@ sentryTest('captures text request body', async ({ getLocalTestPath, page, browse
method: 'POST',
request_body_size: 10,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -80,7 +78,7 @@ sentryTest('captures text request body', async ({ getLocalTestPath, page, browse
},
response: additionalHeaders ? { headers: additionalHeaders } : undefined,
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -88,14 +86,14 @@ sentryTest('captures text request body', async ({ getLocalTestPath, page, browse
]);
});
-sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures JSON request body', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -114,19 +112,17 @@ sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browse
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
body: '{"foo":"bar"}',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -143,7 +139,7 @@ sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browse
method: 'POST',
request_body_size: 13,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -160,7 +156,7 @@ sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browse
},
response: additionalHeaders ? { headers: additionalHeaders } : undefined,
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -168,14 +164,14 @@ sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browse
]);
});
-sentryTest('captures non-text request body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures non-text request body', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -194,7 +190,7 @@ sentryTest('captures non-text request body', async ({ getLocalTestPath, page, br
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
@@ -202,15 +198,13 @@ sentryTest('captures non-text request body', async ({ getLocalTestPath, page, br
body.append('name', 'Anne');
body.append('age', '32');
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
body: body,
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -227,7 +221,7 @@ sentryTest('captures non-text request body', async ({ getLocalTestPath, page, br
method: 'POST',
request_body_size: 16,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -244,7 +238,7 @@ sentryTest('captures non-text request body', async ({ getLocalTestPath, page, br
},
response: additionalHeaders ? { headers: additionalHeaders } : undefined,
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -259,7 +253,7 @@ sentryTest('captures text request body when matching relative URL', async ({ get
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -282,7 +276,6 @@ sentryTest('captures text request body when matching relative URL', async ({ get
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
fetch('/foo', {
method: 'POST',
body: 'input body',
@@ -290,7 +283,6 @@ sentryTest('captures text request body when matching relative URL', async ({ get
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -332,12 +324,12 @@ sentryTest('captures text request body when matching relative URL', async ({ get
]);
});
-sentryTest('does not capture request body when URL does not match', async ({ getLocalTestPath, page }) => {
+sentryTest('does not capture request body when URL does not match', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
- await page.route('**/bar', route => {
+ await page.route('http://sentry-test.io/bar', route => {
return route.fulfill({
status: 200,
});
@@ -356,19 +348,17 @@ sentryTest('does not capture request body when URL does not match', async ({ get
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/bar', {
+ fetch('http://sentry-test.io/bar', {
method: 'POST',
body: 'input body',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -385,7 +375,7 @@ sentryTest('does not capture request body when URL does not match', async ({ get
method: 'POST',
request_body_size: 10,
status_code: 200,
- url: 'http://localhost:7654/bar',
+ url: 'http://sentry-test.io/bar',
},
});
@@ -409,7 +399,7 @@ sentryTest('does not capture request body when URL does not match', async ({ get
},
},
},
- description: 'http://localhost:7654/bar',
+ description: 'http://sentry-test.io/bar',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/init.js b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/init.js
index 8e8006759bee..5a97a87c2571 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/init.js
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/init.js
@@ -6,7 +6,7 @@ window.Replay = Sentry.replayIntegration({
flushMaxDelay: 200,
minReplayDuration: 0,
- networkDetailAllowUrls: ['http://localhost:7654/foo'],
+ networkDetailAllowUrls: ['http://sentry-test.io/foo'],
networkRequestHeaders: ['X-Test-Header'],
});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/test.ts
index 19e85e8b03db..f1952da08f0a 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/test.ts
@@ -8,14 +8,14 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('handles empty/missing request headers', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('handles empty/missing request headers', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -32,19 +32,17 @@ sentryTest('handles empty/missing request headers', async ({ getLocalTestPath, p
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request, { replayRecordingSnapshots }] = await Promise.all([
page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
replayRequestPromise,
@@ -62,7 +60,7 @@ sentryTest('handles empty/missing request headers', async ({ getLocalTestPath, p
data: {
method: 'POST',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -73,7 +71,7 @@ sentryTest('handles empty/missing request headers', async ({ getLocalTestPath, p
statusCode: 200,
response: additionalHeaders ? { headers: additionalHeaders } : undefined,
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -81,14 +79,14 @@ sentryTest('handles empty/missing request headers', async ({ getLocalTestPath, p
]);
});
-sentryTest('captures request headers as POJO', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures request headers as POJO', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -107,13 +105,12 @@ sentryTest('captures request headers as POJO', async ({ getLocalTestPath, page,
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request, { replayRecordingSnapshots }] = await Promise.all([
page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
headers: {
Accept: 'application/json',
@@ -126,7 +123,6 @@ sentryTest('captures request headers as POJO', async ({ getLocalTestPath, page,
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
replayRequestPromise,
@@ -144,7 +140,7 @@ sentryTest('captures request headers as POJO', async ({ getLocalTestPath, page,
data: {
method: 'POST',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -162,7 +158,7 @@ sentryTest('captures request headers as POJO', async ({ getLocalTestPath, page,
},
response: additionalHeaders ? { headers: additionalHeaders } : undefined,
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -170,14 +166,14 @@ sentryTest('captures request headers as POJO', async ({ getLocalTestPath, page,
]);
});
-sentryTest('captures request headers on Request', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures request headers on Request', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -194,12 +190,12 @@ sentryTest('captures request headers on Request', async ({ getLocalTestPath, pag
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request, { replayRecordingSnapshots }] = await Promise.all([
page.evaluate(() => {
- const request = new Request('http://localhost:7654/foo', {
+ const request = new Request('http://sentry-test.io/foo', {
method: 'POST',
headers: {
Accept: 'application/json',
@@ -208,12 +204,11 @@ sentryTest('captures request headers on Request', async ({ getLocalTestPath, pag
'X-Custom-Header': 'foo',
},
});
- /* eslint-disable */
+
fetch(request).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
replayRequestPromise,
@@ -231,7 +226,7 @@ sentryTest('captures request headers on Request', async ({ getLocalTestPath, pag
data: {
method: 'POST',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -248,7 +243,7 @@ sentryTest('captures request headers on Request', async ({ getLocalTestPath, pag
},
response: additionalHeaders ? { headers: additionalHeaders } : undefined,
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -256,14 +251,14 @@ sentryTest('captures request headers on Request', async ({ getLocalTestPath, pag
]);
});
-sentryTest('captures request headers as Headers instance', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures request headers as Headers instance', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -280,7 +275,7 @@ sentryTest('captures request headers as Headers instance', async ({ getLocalTest
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
@@ -292,15 +287,13 @@ sentryTest('captures request headers as Headers instance', async ({ getLocalTest
headers.append('Cache', 'no-cache');
headers.append('X-Custom-Header', 'foo');
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
headers,
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
replayRequestPromise,
@@ -318,7 +311,7 @@ sentryTest('captures request headers as Headers instance', async ({ getLocalTest
data: {
method: 'POST',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -335,7 +328,7 @@ sentryTest('captures request headers as Headers instance', async ({ getLocalTest
},
response: additionalHeaders ? { headers: additionalHeaders } : undefined,
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -343,12 +336,12 @@ sentryTest('captures request headers as Headers instance', async ({ getLocalTest
]);
});
-sentryTest('does not captures request headers if URL does not match', async ({ getLocalTestPath, page }) => {
+sentryTest('does not captures request headers if URL does not match', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
- await page.route('**/bar', route => {
+ await page.route('http://sentry-test.io/bar', route => {
return route.fulfill({
status: 200,
});
@@ -367,13 +360,12 @@ sentryTest('does not captures request headers if URL does not match', async ({ g
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request, { replayRecordingSnapshots }] = await Promise.all([
page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/bar', {
+ fetch('http://sentry-test.io/bar', {
method: 'POST',
headers: {
Accept: 'application/json',
@@ -386,7 +378,6 @@ sentryTest('does not captures request headers if URL does not match', async ({ g
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
replayRequestPromise,
@@ -404,7 +395,7 @@ sentryTest('does not captures request headers if URL does not match', async ({ g
data: {
method: 'POST',
status_code: 200,
- url: 'http://localhost:7654/bar',
+ url: 'http://sentry-test.io/bar',
},
});
@@ -426,7 +417,7 @@ sentryTest('does not captures request headers if URL does not match', async ({ g
},
},
},
- description: 'http://localhost:7654/bar',
+ description: 'http://sentry-test.io/bar',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestSize/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestSize/test.ts
index dafa896f2cbd..4da3c7142996 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestSize/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestSize/test.ts
@@ -8,12 +8,12 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures request body size when body is sent', async ({ getLocalTestPath, page }) => {
+sentryTest('captures request body size when body is sent', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -32,20 +32,18 @@ sentryTest('captures request body size when body is sent', async ({ getLocalTest
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request, { replayRecordingSnapshots }] = await Promise.all([
page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
body: '{"foo":"bar"}',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
replayRequestPromise,
@@ -64,7 +62,7 @@ sentryTest('captures request body size when body is sent', async ({ getLocalTest
method: 'POST',
request_body_size: 13,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -87,7 +85,7 @@ sentryTest('captures request body size when body is sent', async ({ getLocalTest
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -95,7 +93,7 @@ sentryTest('captures request body size when body is sent', async ({ getLocalTest
]);
});
-sentryTest('captures request size from non-text request body', async ({ getLocalTestPath, page }) => {
+sentryTest('captures request size from non-text request body', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
@@ -119,22 +117,20 @@ sentryTest('captures request size from non-text request body', async ({ getLocal
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request, { replayRecordingSnapshots }] = await Promise.all([
page.evaluate(() => {
- /* eslint-disable */
const blob = new Blob(['Hello world!!'], { type: 'text/html' });
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
body: blob,
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
replayRequestPromise,
@@ -153,7 +149,7 @@ sentryTest('captures request size from non-text request body', async ({ getLocal
method: 'POST',
request_body_size: 26,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -176,7 +172,7 @@ sentryTest('captures request size from non-text request body', async ({ getLocal
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseBody/init.js b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseBody/init.js
index f2eb62b8c051..9db232f536cd 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseBody/init.js
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseBody/init.js
@@ -6,7 +6,7 @@ window.Replay = Sentry.replayIntegration({
flushMaxDelay: 200,
minReplayDuration: 0,
- networkDetailAllowUrls: ['http://localhost:7654/foo'],
+ networkDetailAllowUrls: ['http://sentry-test.io/foo'],
networkCaptureBodies: true,
});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseBody/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseBody/test.ts
index 5e58b63218ef..7ce8ecb87748 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseBody/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseBody/test.ts
@@ -8,14 +8,14 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures text response body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures text response body', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
body: 'response body',
@@ -35,18 +35,16 @@ sentryTest('captures text response body', async ({ getLocalTestPath, page, brows
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -63,7 +61,7 @@ sentryTest('captures text response body', async ({ getLocalTestPath, page, brows
method: 'POST',
response_body_size: 13,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -82,7 +80,7 @@ sentryTest('captures text response body', async ({ getLocalTestPath, page, brows
body: 'response body',
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -90,14 +88,14 @@ sentryTest('captures text response body', async ({ getLocalTestPath, page, brows
]);
});
-sentryTest('captures JSON response body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures JSON response body', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({ res: 'this' }),
@@ -117,18 +115,16 @@ sentryTest('captures JSON response body', async ({ getLocalTestPath, page, brows
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -145,7 +141,7 @@ sentryTest('captures JSON response body', async ({ getLocalTestPath, page, brows
method: 'POST',
response_body_size: 14,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -164,7 +160,7 @@ sentryTest('captures JSON response body', async ({ getLocalTestPath, page, brows
body: { res: 'this' },
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -172,14 +168,14 @@ sentryTest('captures JSON response body', async ({ getLocalTestPath, page, brows
]);
});
-sentryTest('captures non-text response body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures non-text response body', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'application/octet-stream' } : {};
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
body: Buffer.from('Hello world'),
@@ -199,18 +195,16 @@ sentryTest('captures non-text response body', async ({ getLocalTestPath, page, b
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -227,7 +221,7 @@ sentryTest('captures non-text response body', async ({ getLocalTestPath, page, b
method: 'POST',
response_body_size: 24,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -246,7 +240,7 @@ sentryTest('captures non-text response body', async ({ getLocalTestPath, page, b
body: 'Hello world',
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -256,12 +250,12 @@ sentryTest('captures non-text response body', async ({ getLocalTestPath, page, b
// This test is flaky
// See: https://github.com/getsentry/sentry-javascript/issues/11136
-sentryTest.skip('does not capture response body when URL does not match', async ({ getLocalTestPath, page }) => {
+sentryTest.skip('does not capture response body when URL does not match', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
- await page.route('**/bar', route => {
+ await page.route('http://sentry-test.io/bar', route => {
return route.fulfill({
status: 200,
body: 'response body',
@@ -281,18 +275,16 @@ sentryTest.skip('does not capture response body when URL does not match', async
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/bar', {
+ fetch('http://sentry-test.io/bar', {
method: 'POST',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -309,7 +301,7 @@ sentryTest.skip('does not capture response body when URL does not match', async
method: 'POST',
response_body_size: 13,
status_code: 200,
- url: 'http://localhost:7654/bar',
+ url: 'http://sentry-test.io/bar',
},
});
@@ -333,7 +325,7 @@ sentryTest.skip('does not capture response body when URL does not match', async
},
},
},
- description: 'http://localhost:7654/bar',
+ description: 'http://sentry-test.io/bar',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/init.js b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/init.js
index a2a7a1680123..82bc5a045870 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/init.js
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/init.js
@@ -6,7 +6,7 @@ window.Replay = Sentry.replayIntegration({
flushMaxDelay: 200,
minReplayDuration: 0,
- networkDetailAllowUrls: ['http://localhost:7654/foo'],
+ networkDetailAllowUrls: ['http://sentry-test.io/foo'],
networkResponseHeaders: ['X-Test-Header'],
});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/test.ts
index 8486cf541a18..ec4a26dd199f 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/test.ts
@@ -8,14 +8,14 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('handles empty headers', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('handles empty headers', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
const additionalHeaders = browserName === 'webkit' ? { 'content-type': 'text/plain' } : undefined;
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -34,12 +34,12 @@ sentryTest('handles empty headers', async ({ getLocalTestPath, page, browserName
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request] = await Promise.all([
page.evaluate(() => {
- fetch('http://localhost:7654/foo').then(() => {
+ fetch('http://sentry-test.io/foo').then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
@@ -59,7 +59,7 @@ sentryTest('handles empty headers', async ({ getLocalTestPath, page, browserName
data: {
method: 'GET',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -71,7 +71,7 @@ sentryTest('handles empty headers', async ({ getLocalTestPath, page, browserName
statusCode: 200,
response: additionalHeaders ? { headers: additionalHeaders } : undefined,
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -79,12 +79,12 @@ sentryTest('handles empty headers', async ({ getLocalTestPath, page, browserName
]);
});
-sentryTest('captures response headers', async ({ getLocalTestPath, page }) => {
+sentryTest('captures response headers', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
headers: {
@@ -110,12 +110,12 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page }) => {
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request] = await Promise.all([
page.evaluate(() => {
- fetch('http://localhost:7654/foo').then(() => {
+ fetch('http://sentry-test.io/foo').then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
@@ -135,7 +135,7 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page }) => {
data: {
method: 'GET',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -153,7 +153,7 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page }) => {
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -161,12 +161,12 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page }) => {
]);
});
-sentryTest('does not capture response headers if URL does not match', async ({ getLocalTestPath, page }) => {
+sentryTest('does not capture response headers if URL does not match', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
- await page.route('**/bar', route => {
+ await page.route('http://sentry-test.io/bar', route => {
return route.fulfill({
status: 200,
headers: {
@@ -192,12 +192,12 @@ sentryTest('does not capture response headers if URL does not match', async ({ g
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request] = await Promise.all([
page.evaluate(() => {
- fetch('http://localhost:7654/bar').then(() => {
+ fetch('http://sentry-test.io/bar').then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
@@ -217,7 +217,7 @@ sentryTest('does not capture response headers if URL does not match', async ({ g
data: {
method: 'GET',
status_code: 200,
- url: 'http://localhost:7654/bar',
+ url: 'http://sentry-test.io/bar',
},
});
@@ -236,7 +236,7 @@ sentryTest('does not capture response headers if URL does not match', async ({ g
_meta: { warnings: ['URL_SKIPPED'] },
},
},
- description: 'http://localhost:7654/bar',
+ description: 'http://sentry-test.io/bar',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseSize/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseSize/test.ts
index 63e3611d9d8f..0191617d46da 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseSize/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseSize/test.ts
@@ -8,12 +8,12 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures response size from Content-Length header if available', async ({ getLocalTestPath, page }) => {
+sentryTest('captures response size from Content-Length header if available', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({
@@ -40,17 +40,15 @@ sentryTest('captures response size from Content-Length header if available', asy
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request] = await Promise.all([
page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo').then(() => {
+ fetch('http://sentry-test.io/foo').then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
]);
@@ -68,7 +66,7 @@ sentryTest('captures response size from Content-Length header if available', asy
method: 'GET',
response_body_size: 789,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -92,7 +90,7 @@ sentryTest('captures response size from Content-Length header if available', asy
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -100,12 +98,12 @@ sentryTest('captures response size from Content-Length header if available', asy
]);
});
-sentryTest('captures response size without Content-Length header', async ({ getLocalTestPath, page }) => {
+sentryTest('captures response size without Content-Length header', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({
@@ -132,17 +130,15 @@ sentryTest('captures response size without Content-Length header', async ({ getL
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request] = await Promise.all([
page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo').then(() => {
+ fetch('http://sentry-test.io/foo').then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
]);
@@ -160,7 +156,7 @@ sentryTest('captures response size without Content-Length header', async ({ getL
method: 'GET',
status_code: 200,
// NOT set here from body, as this would be async
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -184,7 +180,7 @@ sentryTest('captures response size without Content-Length header', async ({ getL
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
@@ -192,7 +188,7 @@ sentryTest('captures response size without Content-Length header', async ({ getL
]);
});
-sentryTest('captures response size from non-text response body', async ({ getLocalTestPath, page }) => {
+sentryTest('captures response size from non-text response body', async ({ getLocalTestUrl, page }) => {
if (shouldSkipReplayTest()) {
sentryTest.skip();
}
@@ -221,19 +217,17 @@ sentryTest('captures response size from non-text response body', async ({ getLoc
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request, { replayRecordingSnapshots }] = await Promise.all([
page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
}),
requestPromise,
replayRequestPromise,
@@ -251,7 +245,7 @@ sentryTest('captures response size from non-text response body', async ({ getLoc
data: {
method: 'POST',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -274,7 +268,7 @@ sentryTest('captures response size from non-text response body', async ({ getLoc
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.fetch',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureTimestamps/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureTimestamps/test.ts
index cce931062770..d77796f92fc2 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureTimestamps/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureTimestamps/test.ts
@@ -8,13 +8,13 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures correct timestamps', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures correct timestamps', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -34,19 +34,17 @@ sentryTest('captures correct timestamps', async ({ getLocalTestPath, page, brows
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.fetch');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
- fetch('http://localhost:7654/foo', {
+ fetch('http://sentry-test.io/foo', {
method: 'POST',
body: '{"foo":"bar"}',
}).then(() => {
// @ts-expect-error Sentry is a global
Sentry.captureException('test error');
});
- /* eslint-enable */
});
const request = await requestPromise;
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestBody/init.js b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestBody/init.js
index c43d5713f173..9db232f536cd 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestBody/init.js
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestBody/init.js
@@ -6,7 +6,7 @@ window.Replay = Sentry.replayIntegration({
flushMaxDelay: 200,
minReplayDuration: 0,
- networkDetailAllowUrls: ['http://localhost:7654/foo', 'http://sentry-test.io/foo'],
+ networkDetailAllowUrls: ['http://sentry-test.io/foo'],
networkCaptureBodies: true,
});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestBody/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestBody/test.ts
index cd19ba50dd99..d12d74381fc2 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestBody/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestBody/test.ts
@@ -8,13 +8,13 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures text request body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures text request body', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -33,14 +33,13 @@ sentryTest('captures text request body', async ({ getLocalTestPath, page, browse
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
void page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.send('input body');
xhr.addEventListener('readystatechange', function () {
@@ -49,7 +48,6 @@ sentryTest('captures text request body', async ({ getLocalTestPath, page, browse
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -66,7 +64,7 @@ sentryTest('captures text request body', async ({ getLocalTestPath, page, browse
method: 'POST',
request_body_size: 10,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -82,7 +80,7 @@ sentryTest('captures text request body', async ({ getLocalTestPath, page, browse
body: 'input body',
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -90,13 +88,13 @@ sentryTest('captures text request body', async ({ getLocalTestPath, page, browse
]);
});
-sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures JSON request body', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -115,14 +113,13 @@ sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browse
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
void page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.send('{"foo":"bar"}');
xhr.addEventListener('readystatechange', function () {
@@ -131,7 +128,6 @@ sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browse
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -148,7 +144,7 @@ sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browse
method: 'POST',
request_body_size: 13,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -164,7 +160,7 @@ sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browse
body: { foo: 'bar' },
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -172,7 +168,7 @@ sentryTest('captures JSON request body', async ({ getLocalTestPath, page, browse
]);
});
-sentryTest('captures non-text request body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures non-text request body', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
@@ -197,18 +193,17 @@ sentryTest('captures non-text request body', async ({ getLocalTestPath, page, br
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
const body = new URLSearchParams();
body.append('name', 'Anne');
body.append('age', '32');
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.send(body);
xhr.addEventListener('readystatechange', function () {
@@ -217,7 +212,6 @@ sentryTest('captures non-text request body', async ({ getLocalTestPath, page, br
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -234,7 +228,7 @@ sentryTest('captures non-text request body', async ({ getLocalTestPath, page, br
method: 'POST',
request_body_size: 16,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -250,7 +244,7 @@ sentryTest('captures non-text request body', async ({ getLocalTestPath, page, br
body: 'name=Anne&age=32',
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -264,7 +258,7 @@ sentryTest('captures text request body when matching relative URL', async ({ get
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -287,7 +281,6 @@ sentryTest('captures text request body when matching relative URL', async ({ get
await page.goto(url);
void page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
xhr.open('POST', '/foo');
@@ -299,7 +292,6 @@ sentryTest('captures text request body when matching relative URL', async ({ get
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -340,13 +332,13 @@ sentryTest('captures text request body when matching relative URL', async ({ get
]);
});
-sentryTest('does not capture request body when URL does not match', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('does not capture request body when URL does not match', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/bar', route => {
+ await page.route('http://sentry-test.io/bar', route => {
return route.fulfill({
status: 200,
});
@@ -365,14 +357,13 @@ sentryTest('does not capture request body when URL does not match', async ({ get
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
void page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/bar');
+ xhr.open('POST', 'http://sentry-test.io/bar');
xhr.send('input body');
xhr.addEventListener('readystatechange', function () {
@@ -381,7 +372,6 @@ sentryTest('does not capture request body when URL does not match', async ({ get
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -398,7 +388,7 @@ sentryTest('does not capture request body when URL does not match', async ({ get
method: 'POST',
request_body_size: 10,
status_code: 200,
- url: 'http://localhost:7654/bar',
+ url: 'http://sentry-test.io/bar',
},
});
@@ -422,7 +412,7 @@ sentryTest('does not capture request body when URL does not match', async ({ get
},
},
},
- description: 'http://localhost:7654/bar',
+ description: 'http://sentry-test.io/bar',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestHeaders/init.js b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestHeaders/init.js
index 8e8006759bee..5a97a87c2571 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestHeaders/init.js
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestHeaders/init.js
@@ -6,7 +6,7 @@ window.Replay = Sentry.replayIntegration({
flushMaxDelay: 200,
minReplayDuration: 0,
- networkDetailAllowUrls: ['http://localhost:7654/foo'],
+ networkDetailAllowUrls: ['http://sentry-test.io/foo'],
networkRequestHeaders: ['X-Test-Header'],
});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestHeaders/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestHeaders/test.ts
index c9dd8c455b41..be6ac82d4b5c 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestHeaders/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestHeaders/test.ts
@@ -8,13 +8,13 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures request headers', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures request headers', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -33,14 +33,13 @@ sentryTest('captures request headers', async ({ getLocalTestPath, page, browserN
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
void page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Cache', 'no-cache');
@@ -53,7 +52,6 @@ sentryTest('captures request headers', async ({ getLocalTestPath, page, browserN
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -69,7 +67,7 @@ sentryTest('captures request headers', async ({ getLocalTestPath, page, browserN
data: {
method: 'POST',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -87,7 +85,7 @@ sentryTest('captures request headers', async ({ getLocalTestPath, page, browserN
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -95,98 +93,93 @@ sentryTest('captures request headers', async ({ getLocalTestPath, page, browserN
]);
});
-sentryTest(
- 'does not capture request headers if URL does not match',
- async ({ getLocalTestPath, page, browserName }) => {
- // These are a bit flaky on non-chromium browsers
- if (shouldSkipReplayTest() || browserName !== 'chromium') {
- sentryTest.skip();
- }
-
- await page.route('**/bar', route => {
- return route.fulfill({
- status: 200,
- });
- });
+sentryTest('does not capture request headers if URL does not match', async ({ getLocalTestUrl, page, browserName }) => {
+ // These are a bit flaky on non-chromium browsers
+ if (shouldSkipReplayTest() || browserName !== 'chromium') {
+ sentryTest.skip();
+ }
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
+ await page.route('http://sentry-test.io/bar', route => {
+ return route.fulfill({
+ status: 200,
});
+ });
- const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise = collectReplayRequests(page, recordingEvents => {
- return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
+ await page.route('https://dsn.ingest.sentry.io/**/*', route => {
+ return route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ body: JSON.stringify({ id: 'test-id' }),
});
+ });
+
+ const requestPromise = waitForErrorRequest(page);
+ const replayRequestPromise = collectReplayRequests(page, recordingEvents => {
+ return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
+ });
+
+ const url = await getLocalTestUrl({ testDir: __dirname });
+ await page.goto(url);
+
+ void page.evaluate(() => {
+ const xhr = new XMLHttpRequest();
- const url = await getLocalTestPath({ testDir: __dirname });
- await page.goto(url);
-
- void page.evaluate(() => {
- /* eslint-disable */
- const xhr = new XMLHttpRequest();
-
- xhr.open('POST', 'http://localhost:7654/bar');
- xhr.setRequestHeader('Accept', 'application/json');
- xhr.setRequestHeader('Content-Type', 'application/json');
- xhr.setRequestHeader('Cache', 'no-cache');
- xhr.setRequestHeader('X-Test-Header', 'test-value');
- xhr.send();
-
- xhr.addEventListener('readystatechange', function () {
- if (xhr.readyState === 4) {
- // @ts-expect-error Sentry is a global
- setTimeout(() => Sentry.captureException('test error', 0));
- }
- });
- /* eslint-enable */
+ xhr.open('POST', 'http://sentry-test.io/bar');
+ xhr.setRequestHeader('Accept', 'application/json');
+ xhr.setRequestHeader('Content-Type', 'application/json');
+ xhr.setRequestHeader('Cache', 'no-cache');
+ xhr.setRequestHeader('X-Test-Header', 'test-value');
+ xhr.send();
+
+ xhr.addEventListener('readystatechange', function () {
+ if (xhr.readyState === 4) {
+ // @ts-expect-error Sentry is a global
+ setTimeout(() => Sentry.captureException('test error', 0));
+ }
});
+ });
- const [request] = await Promise.all([requestPromise]);
+ const [request] = await Promise.all([requestPromise]);
- const eventData = envelopeRequestParser(request);
+ const eventData = envelopeRequestParser(request);
+
+ expect(eventData.exception?.values).toHaveLength(1);
- expect(eventData.exception?.values).toHaveLength(1);
+ expect(eventData?.breadcrumbs?.length).toBe(1);
+ expect(eventData!.breadcrumbs![0]).toEqual({
+ timestamp: expect.any(Number),
+ category: 'xhr',
+ type: 'http',
+ data: {
+ method: 'POST',
+ status_code: 200,
+ url: 'http://sentry-test.io/bar',
+ },
+ });
- expect(eventData?.breadcrumbs?.length).toBe(1);
- expect(eventData!.breadcrumbs![0]).toEqual({
- timestamp: expect.any(Number),
- category: 'xhr',
- type: 'http',
+ const { replayRecordingSnapshots } = await replayRequestPromise;
+ expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.xhr')).toEqual([
+ {
data: {
method: 'POST',
- status_code: 200,
- url: 'http://localhost:7654/bar',
- },
- });
-
- const { replayRecordingSnapshots } = await replayRequestPromise;
- expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.xhr')).toEqual([
- {
- data: {
- method: 'POST',
- statusCode: 200,
- request: {
- headers: {},
- _meta: {
- warnings: ['URL_SKIPPED'],
- },
+ statusCode: 200,
+ request: {
+ headers: {},
+ _meta: {
+ warnings: ['URL_SKIPPED'],
},
- response: {
- headers: {},
- _meta: {
- warnings: ['URL_SKIPPED'],
- },
+ },
+ response: {
+ headers: {},
+ _meta: {
+ warnings: ['URL_SKIPPED'],
},
},
- description: 'http://localhost:7654/bar',
- endTimestamp: expect.any(Number),
- op: 'resource.xhr',
- startTimestamp: expect.any(Number),
},
- ]);
- },
-);
+ description: 'http://sentry-test.io/bar',
+ endTimestamp: expect.any(Number),
+ op: 'resource.xhr',
+ startTimestamp: expect.any(Number),
+ },
+ ]);
+});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestSize/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestSize/test.ts
index 50709dc37705..73d063ea1b47 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestSize/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureRequestSize/test.ts
@@ -8,13 +8,13 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures request body size when body is sent', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures request body size when body is sent', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -33,15 +33,14 @@ sentryTest('captures request body size when body is sent', async ({ getLocalTest
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
const [, request] = await Promise.all([
page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.send('{"foo":"bar"}');
xhr.addEventListener('readystatechange', function () {
@@ -50,7 +49,6 @@ sentryTest('captures request body size when body is sent', async ({ getLocalTest
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
}),
requestPromise,
]);
@@ -68,7 +66,7 @@ sentryTest('captures request body size when body is sent', async ({ getLocalTest
method: 'POST',
request_body_size: 13,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -92,7 +90,7 @@ sentryTest('captures request body size when body is sent', async ({ getLocalTest
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -100,7 +98,7 @@ sentryTest('captures request body size when body is sent', async ({ getLocalTest
]);
});
-sentryTest('captures request size from non-text request body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures request size from non-text request body', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
@@ -125,16 +123,15 @@ sentryTest('captures request size from non-text request body', async ({ getLocal
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
const blob = new Blob(['Hello world!!'], { type: 'text/html' });
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.send(blob);
xhr.addEventListener('readystatechange', function () {
@@ -143,7 +140,6 @@ sentryTest('captures request size from non-text request body', async ({ getLocal
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -160,7 +156,7 @@ sentryTest('captures request size from non-text request body', async ({ getLocal
method: 'POST',
request_body_size: 26,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -184,7 +180,7 @@ sentryTest('captures request size from non-text request body', async ({ getLocal
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseBody/init.js b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseBody/init.js
index f2eb62b8c051..9db232f536cd 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseBody/init.js
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseBody/init.js
@@ -6,7 +6,7 @@ window.Replay = Sentry.replayIntegration({
flushMaxDelay: 200,
minReplayDuration: 0,
- networkDetailAllowUrls: ['http://localhost:7654/foo'],
+ networkDetailAllowUrls: ['http://sentry-test.io/foo'],
networkCaptureBodies: true,
});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseBody/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseBody/test.ts
index 97e9bcd749fa..afa2c669f204 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseBody/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseBody/test.ts
@@ -8,13 +8,13 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures text response body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures text response body', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
body: 'response body',
@@ -37,14 +37,13 @@ sentryTest('captures text response body', async ({ getLocalTestPath, page, brows
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
void page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.send();
xhr.addEventListener('readystatechange', function () {
@@ -53,7 +52,6 @@ sentryTest('captures text response body', async ({ getLocalTestPath, page, brows
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -70,7 +68,7 @@ sentryTest('captures text response body', async ({ getLocalTestPath, page, brows
method: 'POST',
response_body_size: 13,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -86,7 +84,7 @@ sentryTest('captures text response body', async ({ getLocalTestPath, page, brows
body: 'response body',
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -94,13 +92,13 @@ sentryTest('captures text response body', async ({ getLocalTestPath, page, brows
]);
});
-sentryTest('captures JSON response body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures JSON response body', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({ res: 'this' }),
@@ -123,14 +121,13 @@ sentryTest('captures JSON response body', async ({ getLocalTestPath, page, brows
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
void page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.send();
xhr.addEventListener('readystatechange', function () {
@@ -139,7 +136,6 @@ sentryTest('captures JSON response body', async ({ getLocalTestPath, page, brows
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -156,7 +152,7 @@ sentryTest('captures JSON response body', async ({ getLocalTestPath, page, brows
method: 'POST',
response_body_size: 14,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -172,7 +168,7 @@ sentryTest('captures JSON response body', async ({ getLocalTestPath, page, brows
body: { res: 'this' },
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -180,13 +176,13 @@ sentryTest('captures JSON response body', async ({ getLocalTestPath, page, brows
]);
});
-sentryTest('captures JSON response body when responseType=json', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures JSON response body when responseType=json', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({ res: 'this' }),
@@ -209,14 +205,13 @@ sentryTest('captures JSON response body when responseType=json', async ({ getLoc
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
void page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
// Setting this to json ensures that xhr.response returns a POJO
xhr.responseType = 'json';
xhr.send();
@@ -227,7 +222,6 @@ sentryTest('captures JSON response body when responseType=json', async ({ getLoc
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -244,7 +238,7 @@ sentryTest('captures JSON response body when responseType=json', async ({ getLoc
method: 'POST',
response_body_size: 14,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -260,7 +254,7 @@ sentryTest('captures JSON response body when responseType=json', async ({ getLoc
body: { res: 'this' },
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -268,13 +262,13 @@ sentryTest('captures JSON response body when responseType=json', async ({ getLoc
]);
});
-sentryTest('captures non-text response body', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures non-text response body', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', async route => {
+ await page.route('http://sentry-test.io/foo', async route => {
return route.fulfill({
status: 200,
body: Buffer.from('Hello world'),
@@ -297,14 +291,13 @@ sentryTest('captures non-text response body', async ({ getLocalTestPath, page, b
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.send();
xhr.addEventListener('readystatechange', function () {
@@ -313,7 +306,6 @@ sentryTest('captures non-text response body', async ({ getLocalTestPath, page, b
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -330,7 +322,7 @@ sentryTest('captures non-text response body', async ({ getLocalTestPath, page, b
method: 'POST',
response_body_size: 24,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -346,7 +338,7 @@ sentryTest('captures non-text response body', async ({ getLocalTestPath, page, b
body: 'Hello world',
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -354,99 +346,94 @@ sentryTest('captures non-text response body', async ({ getLocalTestPath, page, b
]);
});
-sentryTest(
- 'does not capture response body when URL does not match',
- async ({ getLocalTestPath, page, browserName }) => {
- // These are a bit flaky on non-chromium browsers
- if (shouldSkipReplayTest() || browserName !== 'chromium') {
- sentryTest.skip();
- }
-
- await page.route('**/bar', route => {
- return route.fulfill({
- status: 200,
- body: 'response body',
- headers: {
- 'Content-Length': '',
- },
- });
- });
+sentryTest('does not capture response body when URL does not match', async ({ getLocalTestUrl, page, browserName }) => {
+ // These are a bit flaky on non-chromium browsers
+ if (shouldSkipReplayTest() || browserName !== 'chromium') {
+ sentryTest.skip();
+ }
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
+ await page.route('http://sentry-test.io/bar', route => {
+ return route.fulfill({
+ status: 200,
+ body: 'response body',
+ headers: {
+ 'Content-Length': '',
+ },
});
+ });
- const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise = collectReplayRequests(page, recordingEvents => {
- return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
+ await page.route('https://dsn.ingest.sentry.io/**/*', route => {
+ return route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ body: JSON.stringify({ id: 'test-id' }),
});
+ });
- const url = await getLocalTestPath({ testDir: __dirname });
- await page.goto(url);
+ const requestPromise = waitForErrorRequest(page);
+ const replayRequestPromise = collectReplayRequests(page, recordingEvents => {
+ return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
+ });
- void page.evaluate(() => {
- /* eslint-disable */
- const xhr = new XMLHttpRequest();
+ const url = await getLocalTestUrl({ testDir: __dirname });
+ await page.goto(url);
- xhr.open('POST', 'http://localhost:7654/bar');
- xhr.send();
+ void page.evaluate(() => {
+ const xhr = new XMLHttpRequest();
- xhr.addEventListener('readystatechange', function () {
- if (xhr.readyState === 4) {
- // @ts-expect-error Sentry is a global
- setTimeout(() => Sentry.captureException('test error', 0));
- }
- });
- /* eslint-enable */
+ xhr.open('POST', 'http://sentry-test.io/bar');
+ xhr.send();
+
+ xhr.addEventListener('readystatechange', function () {
+ if (xhr.readyState === 4) {
+ // @ts-expect-error Sentry is a global
+ setTimeout(() => Sentry.captureException('test error', 0));
+ }
});
+ });
- const request = await requestPromise;
- const eventData = envelopeRequestParser(request);
+ const request = await requestPromise;
+ const eventData = envelopeRequestParser(request);
- expect(eventData.exception?.values).toHaveLength(1);
+ expect(eventData.exception?.values).toHaveLength(1);
- expect(eventData?.breadcrumbs?.length).toBe(1);
- expect(eventData!.breadcrumbs![0]).toEqual({
- timestamp: expect.any(Number),
- category: 'xhr',
- type: 'http',
+ expect(eventData?.breadcrumbs?.length).toBe(1);
+ expect(eventData!.breadcrumbs![0]).toEqual({
+ timestamp: expect.any(Number),
+ category: 'xhr',
+ type: 'http',
+ data: {
+ method: 'POST',
+ response_body_size: 13,
+ status_code: 200,
+ url: 'http://sentry-test.io/bar',
+ },
+ });
+
+ const { replayRecordingSnapshots } = await replayRequestPromise;
+ expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.xhr')).toEqual([
+ {
data: {
method: 'POST',
- response_body_size: 13,
- status_code: 200,
- url: 'http://localhost:7654/bar',
- },
- });
-
- const { replayRecordingSnapshots } = await replayRequestPromise;
- expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.xhr')).toEqual([
- {
- data: {
- method: 'POST',
- statusCode: 200,
- request: {
- headers: {},
- _meta: {
- warnings: ['URL_SKIPPED'],
- },
+ statusCode: 200,
+ request: {
+ headers: {},
+ _meta: {
+ warnings: ['URL_SKIPPED'],
},
- response: {
- size: 13,
- headers: {},
- _meta: {
- warnings: ['URL_SKIPPED'],
- },
+ },
+ response: {
+ size: 13,
+ headers: {},
+ _meta: {
+ warnings: ['URL_SKIPPED'],
},
},
- description: 'http://localhost:7654/bar',
- endTimestamp: expect.any(Number),
- op: 'resource.xhr',
- startTimestamp: expect.any(Number),
},
- ]);
- },
-);
+ description: 'http://sentry-test.io/bar',
+ endTimestamp: expect.any(Number),
+ op: 'resource.xhr',
+ startTimestamp: expect.any(Number),
+ },
+ ]);
+});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseHeaders/init.js b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseHeaders/init.js
index a2a7a1680123..82bc5a045870 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseHeaders/init.js
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseHeaders/init.js
@@ -6,7 +6,7 @@ window.Replay = Sentry.replayIntegration({
flushMaxDelay: 200,
minReplayDuration: 0,
- networkDetailAllowUrls: ['http://localhost:7654/foo'],
+ networkDetailAllowUrls: ['http://sentry-test.io/foo'],
networkResponseHeaders: ['X-Test-Header'],
});
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseHeaders/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseHeaders/test.ts
index 754c2adf588f..4726f69c641d 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseHeaders/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseHeaders/test.ts
@@ -8,13 +8,13 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures response headers', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures response headers', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
headers: {
@@ -40,14 +40,13 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page, browser
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('GET', 'http://localhost:7654/foo');
+ xhr.open('GET', 'http://sentry-test.io/foo');
xhr.send();
xhr.addEventListener('readystatechange', function () {
@@ -56,7 +55,6 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page, browser
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -72,7 +70,7 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page, browser
data: {
method: 'GET',
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -90,7 +88,7 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page, browser
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -100,13 +98,13 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page, browser
sentryTest(
'does not capture response headers if URL does not match',
- async ({ getLocalTestPath, page, browserName }) => {
+ async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/bar', route => {
+ await page.route('http://sentry-test.io/bar', route => {
return route.fulfill({
status: 200,
headers: {
@@ -132,14 +130,13 @@ sentryTest(
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('GET', 'http://localhost:7654/bar');
+ xhr.open('GET', 'http://sentry-test.io/bar');
xhr.send();
xhr.addEventListener('readystatechange', function () {
@@ -148,7 +145,6 @@ sentryTest(
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -164,7 +160,7 @@ sentryTest(
data: {
method: 'GET',
status_code: 200,
- url: 'http://localhost:7654/bar',
+ url: 'http://sentry-test.io/bar',
},
});
@@ -187,7 +183,7 @@ sentryTest(
},
},
},
- description: 'http://localhost:7654/bar',
+ description: 'http://sentry-test.io/bar',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseSize/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseSize/test.ts
index 5024e65741e9..dda9e4e642c1 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseSize/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureResponseSize/test.ts
@@ -10,13 +10,13 @@ import {
sentryTest(
'captures response size from Content-Length header if available',
- async ({ getLocalTestPath, page, browserName }) => {
+ async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
headers: {
@@ -38,14 +38,13 @@ sentryTest(
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('GET', 'http://localhost:7654/foo');
+ xhr.open('GET', 'http://sentry-test.io/foo');
xhr.send();
xhr.addEventListener('readystatechange', function () {
@@ -54,7 +53,6 @@ sentryTest(
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -71,7 +69,7 @@ sentryTest(
method: 'GET',
response_body_size: 789,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -95,7 +93,7 @@ sentryTest(
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -104,13 +102,13 @@ sentryTest(
},
);
-sentryTest('captures response size without Content-Length header', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures response size without Content-Length header', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({
@@ -135,14 +133,13 @@ sentryTest('captures response size without Content-Length header', async ({ getL
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('GET', 'http://localhost:7654/foo');
+ xhr.open('GET', 'http://sentry-test.io/foo');
xhr.send();
xhr.addEventListener('readystatechange', function () {
@@ -151,7 +148,6 @@ sentryTest('captures response size without Content-Length header', async ({ getL
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -159,8 +155,8 @@ sentryTest('captures response size without Content-Length header', async ({ getL
expect(eventData.exception?.values).toHaveLength(1);
- expect(eventData?.breadcrumbs?.length).toBe(1);
- expect(eventData!.breadcrumbs![0]).toEqual({
+ expect(eventData.breadcrumbs?.length).toBe(1);
+ expect(eventData.breadcrumbs![0]).toEqual({
timestamp: expect.any(Number),
category: 'xhr',
type: 'http',
@@ -168,7 +164,7 @@ sentryTest('captures response size without Content-Length header', async ({ getL
method: 'GET',
response_body_size: 29,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -192,7 +188,7 @@ sentryTest('captures response size without Content-Length header', async ({ getL
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
@@ -200,13 +196,13 @@ sentryTest('captures response size without Content-Length header', async ({ getL
]);
});
-sentryTest('captures response size for non-string bodies', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures response size for non-string bodies', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', async route => {
+ await page.route('http://sentry-test.io/foo', async route => {
return route.fulfill({
status: 200,
body: Buffer.from('Hello world'),
@@ -229,14 +225,13 @@ sentryTest('captures response size for non-string bodies', async ({ getLocalTest
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.send();
xhr.addEventListener('readystatechange', function () {
@@ -245,7 +240,6 @@ sentryTest('captures response size for non-string bodies', async ({ getLocalTest
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
@@ -262,7 +256,7 @@ sentryTest('captures response size for non-string bodies', async ({ getLocalTest
method: 'POST',
response_body_size: 24,
status_code: 200,
- url: 'http://localhost:7654/foo',
+ url: 'http://sentry-test.io/foo',
},
});
@@ -286,7 +280,7 @@ sentryTest('captures response size for non-string bodies', async ({ getLocalTest
},
},
},
- description: 'http://localhost:7654/foo',
+ description: 'http://sentry-test.io/foo',
endTimestamp: expect.any(Number),
op: 'resource.xhr',
startTimestamp: expect.any(Number),
diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureTimestamps/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureTimestamps/test.ts
index d5d065f83ec5..e89fa6e4a24e 100644
--- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureTimestamps/test.ts
+++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/captureTimestamps/test.ts
@@ -8,13 +8,13 @@ import {
shouldSkipReplayTest,
} from '../../../../../utils/replayHelpers';
-sentryTest('captures correct timestamps', async ({ getLocalTestPath, page, browserName }) => {
+sentryTest('captures correct timestamps', async ({ getLocalTestUrl, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
- await page.route('**/foo', route => {
+ await page.route('http://sentry-test.io/foo', route => {
return route.fulfill({
status: 200,
});
@@ -34,14 +34,13 @@ sentryTest('captures correct timestamps', async ({ getLocalTestPath, page, brows
return getReplayPerformanceSpans(recordingEvents).some(span => span.op === 'resource.xhr');
});
- const url = await getLocalTestPath({ testDir: __dirname });
+ const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
void page.evaluate(() => {
- /* eslint-disable */
const xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://localhost:7654/foo');
+ xhr.open('POST', 'http://sentry-test.io/foo');
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Cache', 'no-cache');
@@ -54,7 +53,6 @@ sentryTest('captures correct timestamps', async ({ getLocalTestPath, page, brows
setTimeout(() => Sentry.captureException('test error', 0));
}
});
- /* eslint-enable */
});
const request = await requestPromise;
diff --git a/dev-packages/browser-integration-tests/suites/sessions/initial-scope/test.ts b/dev-packages/browser-integration-tests/suites/sessions/initial-scope/test.ts
index 8a9ba80cdbc4..9d7b07f1cfd1 100644
--- a/dev-packages/browser-integration-tests/suites/sessions/initial-scope/test.ts
+++ b/dev-packages/browser-integration-tests/suites/sessions/initial-scope/test.ts
@@ -16,14 +16,10 @@ sentryTest('should start a new session on pageload.', async ({ getLocalTestPath,
expect(session.did).toBe('1337');
});
-sentryTest('should start a new session with navigation.', async ({ getLocalTestPath, page, browserName }) => {
- // Navigations get CORS error on Firefox and WebKit as we're using `file://` protocol.
- if (browserName !== 'chromium') {
- sentryTest.skip();
- }
+sentryTest('should start a new session with navigation.', async ({ getLocalTestUrl, page }) => {
+ const url = await getLocalTestUrl({ testDir: __dirname });
- const url = await getLocalTestPath({ testDir: __dirname });
- await page.route('**/foo', (route: Route) => route.fulfill({ path: `${__dirname}/dist/index.html` }));
+ await page.route('**/foo', (route: Route) => route.continue({ url }));
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({
diff --git a/dev-packages/browser-integration-tests/suites/sessions/start-session/test.ts b/dev-packages/browser-integration-tests/suites/sessions/start-session/test.ts
index 62c969d2e76d..65f9eef8e9ae 100644
--- a/dev-packages/browser-integration-tests/suites/sessions/start-session/test.ts
+++ b/dev-packages/browser-integration-tests/suites/sessions/start-session/test.ts
@@ -15,14 +15,9 @@ sentryTest('should start a new session on pageload.', async ({ getLocalTestPath,
expect(session.status).toBe('ok');
});
-sentryTest('should start a new session with navigation.', async ({ getLocalTestPath, page, browserName }) => {
- // Navigations get CORS error on Firefox and WebKit as we're using `file://` protocol.
- if (browserName !== 'chromium') {
- sentryTest.skip();
- }
-
- const url = await getLocalTestPath({ testDir: __dirname });
- await page.route('**/foo', (route: Route) => route.fulfill({ path: `${__dirname}/dist/index.html` }));
+sentryTest('should start a new session with navigation.', async ({ getLocalTestUrl, page }) => {
+ const url = await getLocalTestUrl({ testDir: __dirname });
+ await page.route('**/foo', (route: Route) => route.continue({ url }));
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({
diff --git a/dev-packages/browser-integration-tests/suites/sessions/v7-hub-start-session/test.ts b/dev-packages/browser-integration-tests/suites/sessions/v7-hub-start-session/test.ts
index 62c969d2e76d..65f9eef8e9ae 100644
--- a/dev-packages/browser-integration-tests/suites/sessions/v7-hub-start-session/test.ts
+++ b/dev-packages/browser-integration-tests/suites/sessions/v7-hub-start-session/test.ts
@@ -15,14 +15,9 @@ sentryTest('should start a new session on pageload.', async ({ getLocalTestPath,
expect(session.status).toBe('ok');
});
-sentryTest('should start a new session with navigation.', async ({ getLocalTestPath, page, browserName }) => {
- // Navigations get CORS error on Firefox and WebKit as we're using `file://` protocol.
- if (browserName !== 'chromium') {
- sentryTest.skip();
- }
-
- const url = await getLocalTestPath({ testDir: __dirname });
- await page.route('**/foo', (route: Route) => route.fulfill({ path: `${__dirname}/dist/index.html` }));
+sentryTest('should start a new session with navigation.', async ({ getLocalTestUrl, page }) => {
+ const url = await getLocalTestUrl({ testDir: __dirname });
+ await page.route('**/foo', (route: Route) => route.continue({ url }));
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({
diff --git a/dev-packages/browser-integration-tests/utils/fixtures.ts b/dev-packages/browser-integration-tests/utils/fixtures.ts
index cf34c9b7e693..e154ddc25988 100644
--- a/dev-packages/browser-integration-tests/utils/fixtures.ts
+++ b/dev-packages/browser-integration-tests/utils/fixtures.ts
@@ -1,3 +1,4 @@
+import crypto from 'crypto';
import fs from 'fs';
import path from 'path';
/* eslint-disable no-empty-pattern */
@@ -11,16 +12,19 @@ export const TEST_HOST = 'http://sentry-test.io';
const getAsset = (assetDir: string, asset: string): string => {
const assetPath = `${assetDir}/${asset}`;
+ // Try to find the asset in the same directory
if (fs.existsSync(assetPath)) {
return assetPath;
}
+ // Else, try to find it in the parent directory
const parentDirAssetPath = `${path.dirname(assetDir)}/${asset}`;
if (fs.existsSync(parentDirAssetPath)) {
return parentDirAssetPath;
}
+ // Else use a static asset
return `utils/defaults/${asset}`;
};
@@ -54,44 +58,50 @@ const sentryTest = base.extend({
return use(async ({ testDir, skipRouteHandler = false }) => {
const pagePath = `${TEST_HOST}/index.html`;
- await build(testDir);
-
- // Serve all assets under
- if (!skipRouteHandler) {
- await page.route(`${TEST_HOST}/*.*`, route => {
- const file = route.request().url().split('/').pop();
- const filePath = path.resolve(testDir, `./dist/${file}`);
-
- return fs.existsSync(filePath) ? route.fulfill({ path: filePath }) : route.continue();
- });
-
- // Ensure feedback can be lazy loaded
- await page.route(`https://browser.sentry-cdn.com/${SDK_VERSION}/feedback-modal.min.js`, route => {
- const filePath = path.resolve(testDir, './dist/feedback-modal.bundle.js');
- if (!fs.existsSync(filePath)) {
- throw new Error(`Feedback modal bundle (${filePath}) not found`);
- }
- return route.fulfill({ path: filePath });
- });
-
- await page.route(`https://browser.sentry-cdn.com/${SDK_VERSION}/feedback-screenshot.min.js`, route => {
- const filePath = path.resolve(testDir, './dist/feedback-screenshot.bundle.js');
- if (!fs.existsSync(filePath)) {
- throw new Error(`Feedback screenshot bundle (${filePath}) not found`);
- }
- return route.fulfill({ path: filePath });
- });
+ const tmpDir = path.join(testDir, 'dist', crypto.randomUUID());
+
+ await build(testDir, tmpDir);
+
+ // If skipping route handlers we return the tmp dir instead of adding the handler
+ // This way, this can be handled by the caller manually
+ if (skipRouteHandler) {
+ return tmpDir;
}
+ await page.route(`${TEST_HOST}/*.*`, route => {
+ const file = route.request().url().split('/').pop();
+ const filePath = path.resolve(tmpDir, `./${file}`);
+
+ return fs.existsSync(filePath) ? route.fulfill({ path: filePath }) : route.continue();
+ });
+
+ // Ensure feedback can be lazy loaded
+ await page.route(`https://browser.sentry-cdn.com/${SDK_VERSION}/feedback-modal.min.js`, route => {
+ const filePath = path.resolve(tmpDir, './feedback-modal.bundle.js');
+ if (!fs.existsSync(filePath)) {
+ throw new Error(`Feedback modal bundle (${filePath}) not found`);
+ }
+ return route.fulfill({ path: filePath });
+ });
+
+ await page.route(`https://browser.sentry-cdn.com/${SDK_VERSION}/feedback-screenshot.min.js`, route => {
+ const filePath = path.resolve(tmpDir, './feedback-screenshot.bundle.js');
+ if (!fs.existsSync(filePath)) {
+ throw new Error(`Feedback screenshot bundle (${filePath}) not found`);
+ }
+ return route.fulfill({ path: filePath });
+ });
+
return pagePath;
});
},
getLocalTestPath: ({}, use) => {
return use(async ({ testDir }) => {
- const pagePath = `file:///${path.resolve(testDir, './dist/index.html')}`;
+ const tmpDir = path.join(testDir, 'dist', crypto.randomUUID());
+ const pagePath = `file:///${path.resolve(tmpDir, './index.html')}`;
- await build(testDir);
+ await build(testDir, tmpDir);
return pagePath;
});
@@ -139,12 +149,12 @@ const sentryTest = base.extend({
export { sentryTest };
-async function build(testDir: string): Promise {
+async function build(testDir: string, tmpDir: string): Promise {
const subject = getAsset(testDir, 'subject.js');
const template = getAsset(testDir, 'template.html');
const init = getAsset(testDir, 'init.js');
- await generatePage(init, subject, template, testDir);
+ await generatePage(init, subject, template, tmpDir);
const additionalPages = fs
.readdirSync(testDir)
@@ -155,6 +165,6 @@ async function build(testDir: string): Promise {
const subject = getAsset(testDir, 'subject.js');
const pageFile = getAsset(testDir, pageFilename);
const init = getAsset(testDir, 'init.js');
- await generatePage(init, subject, pageFile, testDir, pageFilename);
+ await generatePage(init, subject, pageFile, tmpDir, pageFilename);
}
}
diff --git a/dev-packages/browser-integration-tests/utils/generatePage.ts b/dev-packages/browser-integration-tests/utils/generatePage.ts
index 47be50c707e4..987bb4e3d178 100644
--- a/dev-packages/browser-integration-tests/utils/generatePage.ts
+++ b/dev-packages/browser-integration-tests/utils/generatePage.ts
@@ -1,4 +1,4 @@
-import { existsSync, mkdirSync } from 'fs';
+import { mkdirSync } from 'fs';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import webpack from 'webpack';
@@ -12,49 +12,42 @@ export async function generatePage(
outPath: string,
outPageName: string = 'index.html',
): Promise {
- const localPath = `${outPath}/dist`;
- const bundlePath = `${localPath}/${outPageName}}`;
+ mkdirSync(outPath, { recursive: true });
- if (!existsSync(localPath)) {
- mkdirSync(localPath, { recursive: true });
- }
+ await new Promise((resolve, reject) => {
+ const compiler = webpack(
+ webpackConfig({
+ entry: {
+ init: initPath,
+ subject: subjectPath,
+ },
+ output: {
+ path: outPath,
+ filename: '[name].bundle.js',
+ },
+ plugins: [
+ new SentryScenarioGenerationPlugin(outPath),
+ new HtmlWebpackPlugin({
+ filename: outPageName,
+ template: templatePath,
+ inject: 'body',
+ }),
+ ],
+ }),
+ );
- if (!existsSync(bundlePath)) {
- await new Promise((resolve, reject) => {
- const compiler = webpack(
- webpackConfig({
- entry: {
- init: initPath,
- subject: subjectPath,
- },
- output: {
- path: localPath,
- filename: '[name].bundle.js',
- },
- plugins: [
- new SentryScenarioGenerationPlugin(localPath),
- new HtmlWebpackPlugin({
- filename: outPageName,
- template: templatePath,
- inject: 'body',
- }),
- ],
- }),
- );
+ compiler.run(err => {
+ if (err) {
+ reject(err);
+ }
- compiler.run(err => {
+ compiler.close(err => {
if (err) {
reject(err);
}
- compiler.close(err => {
- if (err) {
- reject(err);
- }
-
- resolve();
- });
+ resolve();
});
});
- }
+ });
}
diff --git a/dev-packages/browser-integration-tests/utils/staticAssets.ts b/dev-packages/browser-integration-tests/utils/staticAssets.ts
index 4b13159c58a4..447a3ad337f7 100644
--- a/dev-packages/browser-integration-tests/utils/staticAssets.ts
+++ b/dev-packages/browser-integration-tests/utils/staticAssets.ts
@@ -27,16 +27,7 @@ export function addStaticAssetSymlink(localOutPath: string, originalPath: string
// Only copy files once
if (!fs.existsSync(newPath)) {
- try {
- fs.symlinkSync(originalPath, newPath);
- } catch (error) {
- // There must be some race condition here as some of our tests flakey
- // because the file already exists. Let's catch and ignore
- // only ignore these kind of errors
- if (!`${error}`.includes('file already exists')) {
- throw error;
- }
- }
+ fs.symlinkSync(originalPath, newPath);
}
symlinkAsset(newPath, path.join(localOutPath, fileName));
@@ -49,12 +40,5 @@ function symlinkAsset(originalPath: string, targetPath: string): void {
// ignore errors here
}
- try {
- fs.linkSync(originalPath, targetPath);
- } catch (error) {
- // only ignore these kind of errors
- if (!`${error}`.includes('file already exists')) {
- throw error;
- }
- }
+ fs.linkSync(originalPath, targetPath);
}
diff --git a/dev-packages/bundle-analyzer-scenarios/package.json b/dev-packages/bundle-analyzer-scenarios/package.json
index bcf269754bf9..a9f2c9265e72 100644
--- a/dev-packages/bundle-analyzer-scenarios/package.json
+++ b/dev-packages/bundle-analyzer-scenarios/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/bundle-analyzer-scenarios",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Scenarios to test bundle analysis with",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/dev-packages/bundle-analyzer-scenarios",
diff --git a/dev-packages/e2e-tests/package.json b/dev-packages/e2e-tests/package.json
index 2f4bd429ca17..69211cd141a8 100644
--- a/dev-packages/e2e-tests/package.json
+++ b/dev-packages/e2e-tests/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/e2e-tests",
- "version": "8.25.0",
+ "version": "8.26.0",
"license": "MIT",
"private": true,
"scripts": {
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/.gitignore b/dev-packages/e2e-tests/test-applications/astro-4/.gitignore
new file mode 100644
index 000000000000..560782d47d98
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/.gitignore
@@ -0,0 +1,26 @@
+# build output
+dist/
+
+# generated types
+.astro/
+
+# dependencies
+node_modules/
+
+# logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# environment variables
+.env
+.env.production
+
+# macOS-specific files
+.DS_Store
+
+# jetbrains setting folder
+.idea/
+
+test-results
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/.npmrc b/dev-packages/e2e-tests/test-applications/astro-4/.npmrc
new file mode 100644
index 000000000000..070f80f05092
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/.npmrc
@@ -0,0 +1,2 @@
+@sentry:registry=http://127.0.0.1:4873
+@sentry-internal:registry=http://127.0.0.1:4873
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/README.md b/dev-packages/e2e-tests/test-applications/astro-4/README.md
new file mode 100644
index 000000000000..28e41344b910
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/README.md
@@ -0,0 +1,6 @@
+# Astro 4 E2E test app
+
+- Astro 4.x
+- Output mode `hybrid` (== opt into SSR routes)
+- Node adapter
+- Configured for Tracing and Performance
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs b/dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs
new file mode 100644
index 000000000000..96db58759ba2
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs
@@ -0,0 +1,29 @@
+import node from '@astrojs/node';
+import sentry from '@sentry/astro';
+import { defineConfig } from 'astro/config';
+
+import spotlightjs from '@spotlightjs/astro';
+
+// https://astro.build/config
+export default defineConfig({
+ output: 'hybrid',
+ integrations: [
+ sentry({
+ debug: true,
+ sourceMapsUploadOptions: {
+ enabled: false,
+ },
+ }),
+ spotlightjs(),
+ ],
+ adapter: node({
+ mode: 'standalone',
+ }),
+ vite: {
+ build: {
+ rollupOptions: {
+ external: ['https'],
+ },
+ },
+ },
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/package.json b/dev-packages/e2e-tests/test-applications/astro-4/package.json
new file mode 100644
index 000000000000..5bc5233ef7cc
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "astro-hybrid-with-static-routes",
+ "type": "module",
+ "version": "0.0.1",
+ "scripts": {
+ "dev": "astro dev --force",
+ "start": "astro dev",
+ "build": "astro check && astro build",
+ "preview": "astro preview",
+ "astro": "astro",
+ "test:build": "pnpm install && npx playwright install && pnpm build",
+ "test:assert": "TEST_ENV=production playwright test"
+ },
+ "dependencies": {
+ "@astrojs/check": "^0.9.2",
+ "@astrojs/node": "^8.3.2",
+ "@playwright/test": "^1.46.0",
+ "@sentry/astro": "* || latest",
+ "@sentry-internal/test-utils": "link:../../../test-utils",
+ "@spotlightjs/astro": "^2.1.6",
+ "astro": "^4.13.3",
+ "typescript": "^5.5.4"
+ },
+ "devDependencies": {
+ "@astrojs/internal-helpers": "^0.4.1"
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/astro-4/playwright.config.mjs
new file mode 100644
index 000000000000..cd6ed611fb4a
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/playwright.config.mjs
@@ -0,0 +1,13 @@
+import { getPlaywrightConfig } from '@sentry-internal/test-utils';
+
+const testEnv = process.env.TEST_ENV;
+
+if (!testEnv) {
+ throw new Error('No test env defined');
+}
+
+const config = getPlaywrightConfig({
+ startCommand: 'node ./dist/server/entry.mjs',
+});
+
+export default config;
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/public/favicon.svg b/dev-packages/e2e-tests/test-applications/astro-4/public/favicon.svg
new file mode 100644
index 000000000000..f157bd1c5e28
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/public/favicon.svg
@@ -0,0 +1,9 @@
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js b/dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js
new file mode 100644
index 000000000000..2b79ec0ed337
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js
@@ -0,0 +1,8 @@
+import * as Sentry from '@sentry/astro';
+
+Sentry.init({
+ dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
+ environment: 'qa',
+ tracesSampleRate: 1.0,
+ tunnel: 'http://localhost:3031/', // proxy server
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/sentry.server.config.js b/dev-packages/e2e-tests/test-applications/astro-4/sentry.server.config.js
new file mode 100644
index 000000000000..0662d678dc7c
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/sentry.server.config.js
@@ -0,0 +1,9 @@
+import * as Sentry from '@sentry/astro';
+
+Sentry.init({
+ dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
+ environment: 'qa',
+ tracesSampleRate: 1.0,
+ spotlight: true,
+ tunnel: 'http://localhost:3031/', // proxy server
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts b/dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts
new file mode 100644
index 000000000000..f964fe0cffd8
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro
new file mode 100644
index 000000000000..c4e54b834656
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro
@@ -0,0 +1,39 @@
+---
+interface Props {
+ title: string;
+}
+
+const { title } = Astro.props;
+---
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/client-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/client-error/index.astro
new file mode 100644
index 000000000000..facd6f077a6e
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/client-error/index.astro
@@ -0,0 +1,11 @@
+---
+import Layout from "../../layouts/Layout.astro";
+---
+
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/api.ts b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/api.ts
new file mode 100644
index 000000000000..a76accdba010
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/api.ts
@@ -0,0 +1,15 @@
+import type { APIRoute } from 'astro';
+
+export const prerender = false;
+
+export const GET: APIRoute = ({ request, url }) => {
+ if (url.searchParams.has('error')) {
+ throw new Error('Endpoint Error');
+ }
+ return new Response(
+ JSON.stringify({
+ search: url.search,
+ sp: url.searchParams,
+ }),
+ );
+};
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/index.astro
new file mode 100644
index 000000000000..f025c76f8365
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/index.astro
@@ -0,0 +1,9 @@
+---
+import Layout from "../../layouts/Layout.astro";
+
+export const prerender = false;
+---
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/index.astro
new file mode 100644
index 000000000000..088205fc4028
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/index.astro
@@ -0,0 +1,36 @@
+---
+import Layout from '../layouts/Layout.astro';
+---
+
+
+
+ Astro E2E Test App
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/ssr-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/ssr-error/index.astro
new file mode 100644
index 000000000000..4ecb7466de70
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/ssr-error/index.astro
@@ -0,0 +1,13 @@
+---
+import Layout from "../../layouts/Layout.astro";
+
+const a = {} as any;
+console.log(a.foo.x);
+export const prerender = false;
+---
+
+
+
+ Page with SSR error
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-ssr/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-ssr/index.astro
new file mode 100644
index 000000000000..58f5d80198d7
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-ssr/index.astro
@@ -0,0 +1,15 @@
+---
+import Layout from "../../layouts/Layout.astro"
+
+export const prerender = false
+---
+
+
+
+
+ This is a server page
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-static/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-static/index.astro
new file mode 100644
index 000000000000..f71bf00c9adf
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-static/index.astro
@@ -0,0 +1,15 @@
+---
+import Layout from "../../layouts/Layout.astro";
+
+export const prerender = true;
+---
+
+
+
+
+ This is a static page
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/astro-4/start-event-proxy.mjs
new file mode 100644
index 000000000000..a657dae0f425
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/start-event-proxy.mjs
@@ -0,0 +1,6 @@
+import { startEventProxyServer } from '@sentry-internal/test-utils';
+
+startEventProxyServer({
+ port: 3031,
+ proxyServerName: 'astro-4',
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts
new file mode 100644
index 000000000000..4cbf4bf36604
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts
@@ -0,0 +1,79 @@
+import { expect, test } from '@playwright/test';
+import { waitForError } from '@sentry-internal/test-utils';
+
+test.describe('client-side errors', () => {
+ test('captures error thrown on click', async ({ page }) => {
+ const errorEventPromise = waitForError('astro-4', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'client error';
+ });
+
+ await page.goto('/client-error');
+
+ await page.getByText('Throw Error').click();
+
+ const errorEvent = await errorEventPromise;
+
+ const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames;
+
+ expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
+ expect.objectContaining({
+ colno: expect.any(Number),
+ lineno: expect.any(Number),
+ filename: expect.stringContaining('/client-error'),
+ function: 'HTMLButtonElement.onclick',
+ in_app: true,
+ }),
+ );
+
+ expect(errorEvent).toMatchObject({
+ exception: {
+ values: [
+ {
+ mechanism: {
+ handled: false,
+ type: 'onerror',
+ },
+ type: 'Error',
+ value: 'client error',
+ stacktrace: expect.any(Object), // detailed check above
+ },
+ ],
+ },
+ level: 'error',
+ platform: 'javascript',
+ request: {
+ url: expect.stringContaining('/client-error'),
+ headers: {
+ 'User-Agent': expect.any(String),
+ },
+ },
+ event_id: expect.stringMatching(/[a-f0-9]{32}/),
+ timestamp: expect.any(Number),
+ sdk: {
+ integrations: expect.arrayContaining([
+ 'InboundFilters',
+ 'FunctionToString',
+ 'BrowserApiErrors',
+ 'Breadcrumbs',
+ 'GlobalHandlers',
+ 'LinkedErrors',
+ 'Dedupe',
+ 'HttpContext',
+ 'BrowserTracing',
+ ]),
+ name: 'sentry.javascript.astro',
+ version: expect.any(String),
+ packages: expect.any(Array),
+ },
+ transaction: '/client-error',
+ contexts: {
+ trace: {
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ },
+ },
+ environment: 'qa',
+ });
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts
new file mode 100644
index 000000000000..d5f07ebe239a
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts
@@ -0,0 +1,115 @@
+import { expect, test } from '@playwright/test';
+import { waitForError } from '@sentry-internal/test-utils';
+
+test.describe('server-side errors', () => {
+ test('captures SSR error', async ({ page }) => {
+ const errorEventPromise = waitForError('astro-4', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === "Cannot read properties of undefined (reading 'x')";
+ });
+
+ await page.goto('/ssr-error');
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent).toMatchObject({
+ contexts: {
+ app: expect.any(Object),
+ cloud_resource: expect.any(Object),
+ culture: expect.any(Object),
+ device: expect.any(Object),
+ os: expect.any(Object),
+ runtime: expect.any(Object),
+ trace: {
+ span_id: '', //TODO: This is a bug! We should expect.stringMatching(/[a-f0-9]{16}/) instead of ''
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ environment: 'qa',
+ event_id: expect.stringMatching(/[a-f0-9]{32}/),
+ exception: {
+ values: [
+ {
+ mechanism: {
+ data: {
+ function: 'astroMiddleware',
+ },
+ handled: false,
+ type: 'astro',
+ },
+ stacktrace: expect.any(Object),
+ type: 'TypeError',
+ value: "Cannot read properties of undefined (reading 'x')",
+ },
+ ],
+ },
+ platform: 'node',
+ request: {
+ cookies: {},
+ headers: expect.objectContaining({
+ // demonstrates that requestData integration is getting data
+ host: 'localhost:3030',
+ 'user-agent': expect.any(String),
+ }),
+ method: 'GET',
+ url: expect.stringContaining('/ssr-error'),
+ },
+ sdk: {
+ integrations: expect.any(Array),
+ name: 'sentry.javascript.astro',
+ packages: expect.any(Array),
+ version: expect.any(String),
+ },
+ server_name: expect.any(String),
+ timestamp: expect.any(Number),
+ transaction: 'GET /ssr-error',
+ });
+ });
+
+ test('captures endpoint error', async ({ page }) => {
+ const errorEventPromise = waitForError('astro-4', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'Endpoint Error';
+ });
+
+ await page.goto('/endpoint-error');
+ await page.getByText('Get Data').click();
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent).toMatchObject({
+ contexts: {
+ trace: {
+ parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ exception: {
+ values: [
+ {
+ mechanism: {
+ data: {
+ function: 'astroMiddleware',
+ },
+ handled: false,
+ type: 'astro',
+ },
+ stacktrace: expect.any(Object),
+ type: 'Error',
+ value: 'Endpoint Error',
+ },
+ ],
+ },
+ platform: 'node',
+ request: {
+ cookies: {},
+ headers: expect.objectContaining({
+ accept: expect.any(String),
+ }),
+ method: 'GET',
+ query_string: 'error=1',
+ url: expect.stringContaining('endpoint-error/api?error=1'),
+ },
+ transaction: 'GET /endpoint-error/api',
+ });
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.dynamic.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.dynamic.test.ts
new file mode 100644
index 000000000000..9a295f677d96
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.dynamic.test.ts
@@ -0,0 +1,123 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/test-utils';
+
+test.describe('tracing in dynamically rendered (ssr) routes', () => {
+ test('sends server and client pageload spans with the same trace id', async ({ page }) => {
+ const clientPageloadTxnPromise = waitForTransaction('astro-4', txnEvent => {
+ return txnEvent?.transaction === '/test-ssr';
+ });
+
+ const serverPageRequestTxnPromise = waitForTransaction('astro-4', txnEvent => {
+ return txnEvent?.transaction === 'GET /test-ssr';
+ });
+
+ await page.goto('/test-ssr');
+
+ const clientPageloadTxn = await clientPageloadTxnPromise;
+ const serverPageRequestTxn = await serverPageRequestTxnPromise;
+
+ const clientPageloadTraceId = clientPageloadTxn.contexts?.trace?.trace_id;
+ const clientPageloadParentSpanId = clientPageloadTxn.contexts?.trace?.parent_span_id;
+
+ const serverPageRequestTraceId = serverPageRequestTxn.contexts?.trace?.trace_id;
+ const serverPageloadSpanId = serverPageRequestTxn.contexts?.trace?.span_id;
+
+ expect(clientPageloadTraceId).toEqual(serverPageRequestTraceId);
+ expect(clientPageloadParentSpanId).toEqual(serverPageloadSpanId);
+
+ expect(clientPageloadTxn).toMatchObject({
+ contexts: {
+ trace: {
+ data: expect.objectContaining({
+ 'sentry.op': 'pageload',
+ 'sentry.origin': 'auto.pageload.browser',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'url',
+ }),
+ op: 'pageload',
+ origin: 'auto.pageload.browser',
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ environment: 'qa',
+ event_id: expect.stringMatching(/[a-f0-9]{32}/),
+ measurements: expect.any(Object),
+ platform: 'javascript',
+ request: expect.any(Object),
+ sdk: {
+ integrations: expect.any(Array),
+ name: 'sentry.javascript.astro',
+ packages: expect.any(Array),
+ version: expect.any(String),
+ },
+ spans: expect.any(Array),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ transaction: '/test-ssr',
+ transaction_info: {
+ source: 'url',
+ },
+ type: 'transaction',
+ });
+
+ expect(serverPageRequestTxn).toMatchObject({
+ breadcrumbs: expect.any(Array),
+ contexts: {
+ app: expect.any(Object),
+ cloud_resource: expect.any(Object),
+ culture: expect.any(Object),
+ device: expect.any(Object),
+ os: expect.any(Object),
+ otel: expect.any(Object),
+ runtime: expect.any(Object),
+ trace: {
+ data: {
+ 'http.response.status_code': 200,
+ method: 'GET',
+ 'sentry.op': 'http.server',
+ 'sentry.origin': 'auto.http.astro',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'route',
+ url: expect.stringContaining('/test-ssr'),
+ },
+ op: 'http.server',
+ origin: 'auto.http.astro',
+ status: 'ok',
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ environment: 'qa',
+ event_id: expect.stringMatching(/[a-f0-9]{32}/),
+ platform: 'node',
+ request: {
+ cookies: {},
+ headers: expect.objectContaining({
+ // demonstrates that request data integration can extract headers
+ accept: expect.any(String),
+ 'accept-encoding': expect.any(String),
+ 'user-agent': expect.any(String),
+ }),
+ method: 'GET',
+ url: expect.stringContaining('/test-ssr'),
+ },
+ sdk: {
+ integrations: expect.any(Array),
+ name: 'sentry.javascript.astro',
+ packages: expect.any(Array),
+ version: expect.any(String),
+ },
+ server_name: expect.any(String),
+ spans: expect.any(Array),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ transaction: 'GET /test-ssr',
+ transaction_info: {
+ source: 'route',
+ },
+ type: 'transaction',
+ });
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.static.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.static.test.ts
new file mode 100644
index 000000000000..8817b2b22aa7
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.static.test.ts
@@ -0,0 +1,62 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/test-utils';
+
+test.describe('tracing in static/pre-rendered routes', () => {
+ test('only sends client pageload span with traceId from pre-rendered tags', async ({ page }) => {
+ const clientPageloadTxnPromise = waitForTransaction('astro-4', txnEvent => {
+ return txnEvent?.transaction === '/test-static';
+ });
+
+ waitForTransaction('astro-4', evt => {
+ if (evt.platform !== 'javascript') {
+ throw new Error('Server transaction should not be sent');
+ }
+ return false;
+ });
+
+ await page.goto('/test-static');
+
+ const clientPageloadTxn = await clientPageloadTxnPromise;
+
+ const clientPageloadTraceId = clientPageloadTxn.contexts?.trace?.trace_id;
+ const clientPageloadParentSpanId = clientPageloadTxn.contexts?.trace?.parent_span_id;
+
+ const sentryTraceMetaTagContent = await page.locator('meta[name="sentry-trace"]').getAttribute('content');
+ const baggageMetaTagContent = await page.locator('meta[name="baggage"]').getAttribute('content');
+
+ const [metaTraceId, metaParentSpanId, metaSampled] = sentryTraceMetaTagContent?.split('-') || [];
+
+ expect(clientPageloadTraceId).toMatch(/[a-f0-9]{32}/);
+ expect(clientPageloadParentSpanId).toMatch(/[a-f0-9]{16}/);
+ expect(metaSampled).toBe('1');
+
+ expect(clientPageloadTxn).toMatchObject({
+ contexts: {
+ trace: {
+ data: expect.objectContaining({
+ 'sentry.op': 'pageload',
+ 'sentry.origin': 'auto.pageload.browser',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'url',
+ }),
+ op: 'pageload',
+ origin: 'auto.pageload.browser',
+ parent_span_id: metaParentSpanId,
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: metaTraceId,
+ },
+ },
+ platform: 'javascript',
+ transaction: '/test-static',
+ transaction_info: {
+ source: 'url',
+ },
+ type: 'transaction',
+ });
+
+ expect(baggageMetaTagContent).toContain('sentry-transaction=GET%20%2Ftest-static%2F'); // URL-encoded for 'GET /test-static/'
+ expect(baggageMetaTagContent).toContain('sentry-sampled=true');
+
+ await page.waitForTimeout(1000); // wait another sec to ensure no server transaction is sent
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tsconfig.json b/dev-packages/e2e-tests/test-applications/astro-4/tsconfig.json
new file mode 100644
index 000000000000..77da9dd00982
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "astro/tsconfigs/strict"
+}
\ No newline at end of file
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/default-browser/tests/performance.test.ts
index 7013fb43ecef..0407d9965389 100644
--- a/dev-packages/e2e-tests/test-applications/default-browser/tests/performance.test.ts
+++ b/dev-packages/e2e-tests/test-applications/default-browser/tests/performance.test.ts
@@ -10,7 +10,7 @@ test('captures a pageload transaction', async ({ page }) => {
const pageLoadTransaction = await transactionPromise;
- expect(pageLoadTransaction).toEqual({
+ expect(pageLoadTransaction).toMatchObject({
contexts: {
trace: {
data: expect.objectContaining({
@@ -28,32 +28,7 @@ test('captures a pageload transaction', async ({ page }) => {
},
environment: 'qa',
event_id: expect.stringMatching(/[a-f0-9]{32}/),
- measurements: {
- 'connection.rtt': {
- unit: 'millisecond',
- value: expect.any(Number),
- },
- fcp: {
- unit: 'millisecond',
- value: expect.any(Number),
- },
- fp: {
- unit: 'millisecond',
- value: expect.any(Number),
- },
- lcp: {
- unit: 'millisecond',
- value: expect.any(Number),
- },
- ttfb: {
- unit: 'millisecond',
- value: expect.any(Number),
- },
- 'ttfb.requestTime': {
- unit: 'millisecond',
- value: expect.any(Number),
- },
- },
+ measurements: expect.any(Object),
platform: 'javascript',
release: 'e2e-test',
request: {
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts
index ec0a921da2c4..9cda3c96f9a6 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts
@@ -1,10 +1,14 @@
-import { Controller, Get, Param, ParseIntPipe, UseGuards, UseInterceptors } from '@nestjs/common';
+import { Controller, Get, Param, ParseIntPipe, UseFilters, UseGuards, UseInterceptors } from '@nestjs/common';
import { flush } from '@sentry/nestjs';
import { AppService } from './app.service';
+import { ExampleExceptionGlobalFilter } from './example-global-filter.exception';
+import { ExampleExceptionLocalFilter } from './example-local-filter.exception';
+import { ExampleLocalFilter } from './example-local.filter';
import { ExampleGuard } from './example.guard';
import { ExampleInterceptor } from './example.interceptor';
@Controller()
+@UseFilters(ExampleLocalFilter)
export class AppController {
constructor(private readonly appService: AppService) {}
@@ -74,4 +78,14 @@ export class AppController {
async flush() {
await flush();
}
+
+ @Get('example-exception-global-filter')
+ async exampleExceptionGlobalFilter() {
+ throw new ExampleExceptionGlobalFilter();
+ }
+
+ @Get('example-exception-local-filter')
+ async exampleExceptionLocalFilter() {
+ throw new ExampleExceptionLocalFilter();
+ }
}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.module.ts
index b2aad014c745..3de3c82dc925 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.module.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.module.ts
@@ -1,14 +1,26 @@
import { MiddlewareConsumer, Module } from '@nestjs/common';
+import { APP_FILTER } from '@nestjs/core';
import { ScheduleModule } from '@nestjs/schedule';
-import { SentryModule } from '@sentry/nestjs/setup';
+import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup';
import { AppController } from './app.controller';
import { AppService } from './app.service';
+import { ExampleGlobalFilter } from './example-global.filter';
import { ExampleMiddleware } from './example.middleware';
@Module({
imports: [SentryModule.forRoot(), ScheduleModule.forRoot()],
controllers: [AppController],
- providers: [AppService],
+ providers: [
+ AppService,
+ {
+ provide: APP_FILTER,
+ useClass: SentryGlobalFilter,
+ },
+ {
+ provide: APP_FILTER,
+ useClass: ExampleGlobalFilter,
+ },
+ ],
})
export class AppModule {
configure(consumer: MiddlewareConsumer): void {
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-global-filter.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-global-filter.exception.ts
new file mode 100644
index 000000000000..41981ba748fe
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-global-filter.exception.ts
@@ -0,0 +1,5 @@
+export class ExampleExceptionGlobalFilter extends Error {
+ constructor() {
+ super('Original global example exception!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-global.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-global.filter.ts
new file mode 100644
index 000000000000..988696d0e13d
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-global.filter.ts
@@ -0,0 +1,19 @@
+import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
+import { Request, Response } from 'express';
+import { ExampleExceptionGlobalFilter } from './example-global-filter.exception';
+
+@Catch(ExampleExceptionGlobalFilter)
+export class ExampleGlobalFilter implements ExceptionFilter {
+ catch(exception: BadRequestException, host: ArgumentsHost): void {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+
+ response.status(400).json({
+ statusCode: 400,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ message: 'Example exception was handled by global filter!',
+ });
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-local-filter.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-local-filter.exception.ts
new file mode 100644
index 000000000000..8f76520a3b94
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-local-filter.exception.ts
@@ -0,0 +1,5 @@
+export class ExampleExceptionLocalFilter extends Error {
+ constructor() {
+ super('Original local example exception!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-local.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-local.filter.ts
new file mode 100644
index 000000000000..505217f5dcbd
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-local.filter.ts
@@ -0,0 +1,19 @@
+import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
+import { Request, Response } from 'express';
+import { ExampleExceptionLocalFilter } from './example-local-filter.exception';
+
+@Catch(ExampleExceptionLocalFilter)
+export class ExampleLocalFilter implements ExceptionFilter {
+ catch(exception: BadRequestException, host: ArgumentsHost): void {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+
+ response.status(400).json({
+ statusCode: 400,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ message: 'Example exception was handled by local filter!',
+ });
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts
index 34e626cb8c52..ee7d12dd22ca 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts
@@ -94,3 +94,73 @@ test('Does not send RpcExceptions to Sentry', async ({ baseURL }) => {
expect(errorEventOccurred).toBe(false);
});
+
+test('Global exception filter registered in main module is applied and exception is not sent to Sentry', async ({
+ baseURL,
+}) => {
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-basic', event => {
+ if (!event.type && event.exception?.values?.[0]?.value === 'Example exception was handled by global filter!') {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-exception-global-filter';
+ });
+
+ const transactionEventPromise = waitForTransaction('nestjs-basic', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-exception-global-filter';
+ });
+
+ const response = await fetch(`${baseURL}/example-exception-global-filter`);
+ const responseBody = await response.json();
+
+ expect(response.status).toBe(400);
+ expect(responseBody).toEqual({
+ statusCode: 400,
+ timestamp: expect.any(String),
+ path: '/example-exception-global-filter',
+ message: 'Example exception was handled by global filter!',
+ });
+
+ await transactionEventPromise;
+
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
+});
+
+test('Local exception filter registered in main module is applied and exception is not sent to Sentry', async ({
+ baseURL,
+}) => {
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-basic', event => {
+ if (!event.type && event.exception?.values?.[0]?.value === 'Example exception was handled by local filter!') {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-exception-local-filter';
+ });
+
+ const transactionEventPromise = waitForTransaction('nestjs-basic', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-exception-local-filter';
+ });
+
+ const response = await fetch(`${baseURL}/example-exception-local-filter`);
+ const responseBody = await response.json();
+
+ expect(response.status).toBe(400);
+ expect(responseBody).toEqual({
+ statusCode: 400,
+ timestamp: expect.any(String),
+ path: '/example-exception-local-filter',
+ message: 'Example exception was handled by local filter!',
+ });
+
+ await transactionEventPromise;
+
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
+});
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.gitignore b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.gitignore
new file mode 100644
index 000000000000..4b56acfbebf4
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.gitignore
@@ -0,0 +1,56 @@
+# compiled output
+/dist
+/node_modules
+/build
+
+# Logs
+logs
+*.log
+npm-debug.log*
+pnpm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# OS
+.DS_Store
+
+# Tests
+/coverage
+/.nyc_output
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# temp directory
+.temp
+.tmp
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.npmrc
new file mode 100644
index 000000000000..070f80f05092
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/.npmrc
@@ -0,0 +1,2 @@
+@sentry:registry=http://127.0.0.1:4873
+@sentry-internal:registry=http://127.0.0.1:4873
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/nest-cli.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/nest-cli.json
new file mode 100644
index 000000000000..f9aa683b1ad5
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/nest-cli.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "https://json.schemastore.org/nest-cli",
+ "collection": "@nestjs/schematics",
+ "sourceRoot": "src",
+ "compilerOptions": {
+ "deleteOutDir": true
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/package.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/package.json
new file mode 100644
index 000000000000..9cc3641d2322
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "nestjs-with-submodules-decorator",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "build": "nest build",
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
+ "start": "nest start",
+ "start:dev": "nest start --watch",
+ "start:debug": "nest start --debug --watch",
+ "start:prod": "node dist/main",
+ "clean": "npx rimraf node_modules pnpm-lock.yaml",
+ "test": "playwright test",
+ "test:build": "pnpm install",
+ "test:assert": "pnpm test"
+ },
+ "dependencies": {
+ "@nestjs/common": "^10.0.0",
+ "@nestjs/core": "^10.0.0",
+ "@nestjs/platform-express": "^10.0.0",
+ "@sentry/nestjs": "latest || *",
+ "@sentry/types": "latest || *",
+ "reflect-metadata": "^0.2.0",
+ "rxjs": "^7.8.1"
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.44.1",
+ "@sentry-internal/test-utils": "link:../../../test-utils",
+ "@nestjs/cli": "^10.0.0",
+ "@nestjs/schematics": "^10.0.0",
+ "@nestjs/testing": "^10.0.0",
+ "@types/express": "^4.17.17",
+ "@types/node": "18.15.1",
+ "@types/supertest": "^6.0.0",
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
+ "@typescript-eslint/parser": "^6.0.0",
+ "eslint": "^8.42.0",
+ "eslint-config-prettier": "^9.0.0",
+ "eslint-plugin-prettier": "^5.0.0",
+ "prettier": "^3.0.0",
+ "source-map-support": "^0.5.21",
+ "supertest": "^6.3.3",
+ "ts-loader": "^9.4.3",
+ "tsconfig-paths": "^4.2.0",
+ "typescript": "^4.9.5"
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/playwright.config.mjs
new file mode 100644
index 000000000000..31f2b913b58b
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/playwright.config.mjs
@@ -0,0 +1,7 @@
+import { getPlaywrightConfig } from '@sentry-internal/test-utils';
+
+const config = getPlaywrightConfig({
+ startCommand: `pnpm start`,
+});
+
+export default config;
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/app.controller.ts
new file mode 100644
index 000000000000..efce824a20c3
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/app.controller.ts
@@ -0,0 +1,37 @@
+import { Controller, Get, Param, UseFilters } from '@nestjs/common';
+import { flush } from '@sentry/nestjs';
+import { AppService } from './app.service';
+import { ExampleExceptionLocalFilter } from './example-local.exception';
+import { ExampleLocalFilter } from './example-local.filter';
+import { ExampleExceptionSpecificFilter } from './example-specific.exception';
+
+@Controller()
+@UseFilters(ExampleLocalFilter)
+export class AppController {
+ constructor(private readonly appService: AppService) {}
+
+ @Get('test-exception/:id')
+ async testException(@Param('id') id: string) {
+ return this.appService.testException(id);
+ }
+
+ @Get('test-expected-exception/:id')
+ async testExpectedException(@Param('id') id: string) {
+ return this.appService.testExpectedException(id);
+ }
+
+ @Get('flush')
+ async flush() {
+ await flush();
+ }
+
+ @Get('example-exception-specific-filter')
+ async exampleExceptionGlobalFilter() {
+ throw new ExampleExceptionSpecificFilter();
+ }
+
+ @Get('example-exception-local-filter')
+ async exampleExceptionLocalFilter() {
+ throw new ExampleExceptionLocalFilter();
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/app.module.ts
new file mode 100644
index 000000000000..77cf85a4fa9c
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/app.module.ts
@@ -0,0 +1,32 @@
+import { Module } from '@nestjs/common';
+import { APP_FILTER } from '@nestjs/core';
+import { SentryModule } from '@sentry/nestjs/setup';
+import { AppController } from './app.controller';
+import { AppService } from './app.service';
+import { ExampleWrappedGlobalFilter } from './example-global.filter';
+import { ExampleModuleGlobalFilterRegisteredFirst } from './example-module-global-filter-registered-first/example.module';
+import { ExampleModuleGlobalFilter } from './example-module-global-filter/example.module';
+import { ExampleModuleLocalFilter } from './example-module-local-filter/example.module';
+import { ExampleSpecificFilter } from './example-specific.filter';
+
+@Module({
+ imports: [
+ ExampleModuleGlobalFilterRegisteredFirst,
+ SentryModule.forRoot(),
+ ExampleModuleGlobalFilter,
+ ExampleModuleLocalFilter,
+ ],
+ controllers: [AppController],
+ providers: [
+ AppService,
+ {
+ provide: APP_FILTER,
+ useClass: ExampleWrappedGlobalFilter,
+ },
+ {
+ provide: APP_FILTER,
+ useClass: ExampleSpecificFilter,
+ },
+ ],
+})
+export class AppModule {}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/app.service.ts
new file mode 100644
index 000000000000..242408023586
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/app.service.ts
@@ -0,0 +1,14 @@
+import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
+
+@Injectable()
+export class AppService {
+ constructor() {}
+
+ testException(id: string) {
+ throw new Error(`This is an exception with id ${id}`);
+ }
+
+ testExpectedException(id: string) {
+ throw new HttpException(`This is an expected exception with id ${id}`, HttpStatus.FORBIDDEN);
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-global.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-global.filter.ts
new file mode 100644
index 000000000000..cee50d0d2c7c
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-global.filter.ts
@@ -0,0 +1,20 @@
+import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
+import { WithSentry } from '@sentry/nestjs';
+import { Request, Response } from 'express';
+
+@Catch()
+export class ExampleWrappedGlobalFilter implements ExceptionFilter {
+ @WithSentry()
+ catch(exception: BadRequestException, host: ArgumentsHost): void {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+
+ response.status(501).json({
+ statusCode: 501,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ message: 'Example exception was handled by global filter!',
+ });
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-local.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-local.exception.ts
new file mode 100644
index 000000000000..8f76520a3b94
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-local.exception.ts
@@ -0,0 +1,5 @@
+export class ExampleExceptionLocalFilter extends Error {
+ constructor() {
+ super('Original local example exception!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-local.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-local.filter.ts
new file mode 100644
index 000000000000..0e93e5f7fac2
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-local.filter.ts
@@ -0,0 +1,19 @@
+import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
+import { Request, Response } from 'express';
+import { ExampleExceptionLocalFilter } from './example-local.exception';
+
+@Catch(ExampleExceptionLocalFilter)
+export class ExampleLocalFilter implements ExceptionFilter {
+ catch(exception: BadRequestException, host: ArgumentsHost): void {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+
+ response.status(400).json({
+ statusCode: 400,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ message: 'Example exception was handled by local filter!',
+ });
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.controller.ts
similarity index 63%
rename from dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.controller.ts
rename to dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.controller.ts
index 028af4a43f87..967886948a25 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.controller.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.controller.ts
@@ -1,13 +1,13 @@
import { Controller, Get } from '@nestjs/common';
-import { ExampleExceptionWrongRegistrationOrder } from './example.exception';
+import { ExampleExceptionRegisteredFirst } from './example.exception';
-@Controller('example-module-wrong-order')
+@Controller('example-module-registered-first')
export class ExampleController {
constructor() {}
@Get('/expected-exception')
getCaughtException(): string {
- throw new ExampleExceptionWrongRegistrationOrder();
+ throw new ExampleExceptionRegisteredFirst();
}
@Get('/unexpected-exception')
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.exception.ts
similarity index 54%
rename from dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.exception.ts
rename to dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.exception.ts
index 0e4f58314fa2..9bb3b5948343 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.exception.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.exception.ts
@@ -1,4 +1,4 @@
-export class ExampleExceptionWrongRegistrationOrder extends Error {
+export class ExampleExceptionRegisteredFirst extends Error {
constructor() {
super('Something went wrong in the example module!');
}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.filter.ts
similarity index 52%
rename from dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.filter.ts
rename to dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.filter.ts
index 6ecdf88937aa..6c3b81d395b5 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.filter.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.filter.ts
@@ -1,11 +1,11 @@
import { ArgumentsHost, BadRequestException, Catch } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
-import { ExampleExceptionWrongRegistrationOrder } from './example.exception';
+import { ExampleExceptionRegisteredFirst } from './example.exception';
-@Catch(ExampleExceptionWrongRegistrationOrder)
-export class ExampleExceptionFilterWrongRegistrationOrder extends BaseExceptionFilter {
+@Catch(ExampleExceptionRegisteredFirst)
+export class ExampleExceptionFilterRegisteredFirst extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
- if (exception instanceof ExampleExceptionWrongRegistrationOrder) {
+ if (exception instanceof ExampleExceptionRegisteredFirst) {
return super.catch(new BadRequestException(exception.message), host);
}
return super.catch(exception, host);
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.module.ts
similarity index 56%
rename from dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.module.ts
rename to dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.module.ts
index c98a5757b01c..8751856f99cd 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-wrong-registration-order/example.module.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter-registered-first/example.module.ts
@@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { ExampleController } from './example.controller';
-import { ExampleExceptionFilterWrongRegistrationOrder } from './example.filter';
+import { ExampleExceptionFilterRegisteredFirst } from './example.filter';
@Module({
imports: [],
@@ -9,8 +9,8 @@ import { ExampleExceptionFilterWrongRegistrationOrder } from './example.filter';
providers: [
{
provide: APP_FILTER,
- useClass: ExampleExceptionFilterWrongRegistrationOrder,
+ useClass: ExampleExceptionFilterRegisteredFirst,
},
],
})
-export class ExampleModuleGlobalFilterWrongRegistrationOrder {}
+export class ExampleModuleGlobalFilterRegisteredFirst {}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.controller.ts
new file mode 100644
index 000000000000..53356e906130
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.controller.ts
@@ -0,0 +1,25 @@
+import { Controller, Get } from '@nestjs/common';
+import * as Sentry from '@sentry/nestjs';
+import { ExampleException } from './example.exception';
+
+@Controller('example-module')
+export class ExampleController {
+ constructor() {}
+
+ @Get('/expected-exception')
+ getCaughtException(): string {
+ throw new ExampleException();
+ }
+
+ @Get('/unexpected-exception')
+ getUncaughtException(): string {
+ throw new Error(`This is an uncaught exception!`);
+ }
+
+ @Get('/transaction')
+ testTransaction() {
+ Sentry.startSpan({ name: 'test-span' }, () => {
+ Sentry.startSpan({ name: 'child-span' }, () => {});
+ });
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.exception.ts
new file mode 100644
index 000000000000..ac43dddfa8dc
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.exception.ts
@@ -0,0 +1,5 @@
+export class ExampleException extends Error {
+ constructor() {
+ super('Something went wrong in the example module!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.filter.ts
new file mode 100644
index 000000000000..848441caf855
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.filter.ts
@@ -0,0 +1,13 @@
+import { ArgumentsHost, BadRequestException, Catch } from '@nestjs/common';
+import { BaseExceptionFilter } from '@nestjs/core';
+import { ExampleException } from './example.exception';
+
+@Catch(ExampleException)
+export class ExampleExceptionFilter extends BaseExceptionFilter {
+ catch(exception: unknown, host: ArgumentsHost) {
+ if (exception instanceof ExampleException) {
+ return super.catch(new BadRequestException(exception.message), host);
+ }
+ return super.catch(exception, host);
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.module.ts
new file mode 100644
index 000000000000..8052cb137aac
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-global-filter/example.module.ts
@@ -0,0 +1,16 @@
+import { Module } from '@nestjs/common';
+import { APP_FILTER } from '@nestjs/core';
+import { ExampleController } from './example.controller';
+import { ExampleExceptionFilter } from './example.filter';
+
+@Module({
+ imports: [],
+ controllers: [ExampleController],
+ providers: [
+ {
+ provide: APP_FILTER,
+ useClass: ExampleExceptionFilter,
+ },
+ ],
+})
+export class ExampleModuleGlobalFilter {}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.controller.ts
new file mode 100644
index 000000000000..11a0470ace17
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.controller.ts
@@ -0,0 +1,19 @@
+import { Controller, Get, UseFilters } from '@nestjs/common';
+import { LocalExampleException } from './example.exception';
+import { LocalExampleExceptionFilter } from './example.filter';
+
+@Controller('example-module-local-filter')
+@UseFilters(LocalExampleExceptionFilter)
+export class ExampleControllerLocalFilter {
+ constructor() {}
+
+ @Get('/expected-exception')
+ getCaughtException() {
+ throw new LocalExampleException();
+ }
+
+ @Get('/unexpected-exception')
+ getUncaughtException(): string {
+ throw new Error(`This is an uncaught exception!`);
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.exception.ts
new file mode 100644
index 000000000000..85a64d26d75e
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.exception.ts
@@ -0,0 +1,5 @@
+export class LocalExampleException extends Error {
+ constructor() {
+ super('Something went wrong in the example module with local filter!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.filter.ts
new file mode 100644
index 000000000000..9b6797c95e44
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.filter.ts
@@ -0,0 +1,13 @@
+import { ArgumentsHost, BadRequestException, Catch } from '@nestjs/common';
+import { BaseExceptionFilter } from '@nestjs/core';
+import { LocalExampleException } from './example.exception';
+
+@Catch(LocalExampleException)
+export class LocalExampleExceptionFilter extends BaseExceptionFilter {
+ catch(exception: unknown, host: ArgumentsHost) {
+ if (exception instanceof LocalExampleException) {
+ return super.catch(new BadRequestException(exception.message), host);
+ }
+ return super.catch(exception, host);
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.module.ts
new file mode 100644
index 000000000000..91d229a553c1
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-module-local-filter/example.module.ts
@@ -0,0 +1,9 @@
+import { Module } from '@nestjs/common';
+import { ExampleControllerLocalFilter } from './example.controller';
+
+@Module({
+ imports: [],
+ controllers: [ExampleControllerLocalFilter],
+ providers: [],
+})
+export class ExampleModuleLocalFilter {}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-specific.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-specific.exception.ts
new file mode 100644
index 000000000000..a26cb1a26d52
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-specific.exception.ts
@@ -0,0 +1,5 @@
+export class ExampleExceptionSpecificFilter extends Error {
+ constructor() {
+ super('Original specific example exception!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-specific.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-specific.filter.ts
new file mode 100644
index 000000000000..bf85385a9a86
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-specific.filter.ts
@@ -0,0 +1,19 @@
+import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
+import { Request, Response } from 'express';
+import { ExampleExceptionSpecificFilter } from './example-specific.exception';
+
+@Catch(ExampleExceptionSpecificFilter)
+export class ExampleSpecificFilter implements ExceptionFilter {
+ catch(exception: BadRequestException, host: ArgumentsHost): void {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+
+ response.status(400).json({
+ statusCode: 400,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ message: 'Example exception was handled by specific filter!',
+ });
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/instrument.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/instrument.ts
new file mode 100644
index 000000000000..4f16ebb36d11
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/instrument.ts
@@ -0,0 +1,12 @@
+import * as Sentry from '@sentry/nestjs';
+
+Sentry.init({
+ environment: 'qa', // dynamic sampling bias to keep transactions
+ dsn: process.env.E2E_TEST_DSN,
+ tunnel: `http://localhost:3031/`, // proxy server
+ tracesSampleRate: 1,
+ transportOptions: {
+ // We expect the app to send a lot of events in a short time
+ bufferSize: 1000,
+ },
+});
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/main.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/main.ts
new file mode 100644
index 000000000000..71ce685f4d61
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/main.ts
@@ -0,0 +1,15 @@
+// Import this first
+import './instrument';
+
+// Import other modules
+import { NestFactory } from '@nestjs/core';
+import { AppModule } from './app.module';
+
+const PORT = 3030;
+
+async function bootstrap() {
+ const app = await NestFactory.create(AppModule);
+ await app.listen(PORT);
+}
+
+bootstrap();
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/start-event-proxy.mjs
new file mode 100644
index 000000000000..3c205b59a2d1
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/start-event-proxy.mjs
@@ -0,0 +1,6 @@
+import { startEventProxyServer } from '@sentry-internal/test-utils';
+
+startEventProxyServer({
+ port: 3031,
+ proxyServerName: 'nestjs-with-submodules-decorator',
+});
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/errors.test.ts
new file mode 100644
index 000000000000..94c742dd210a
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/errors.test.ts
@@ -0,0 +1,266 @@
+import { expect, test } from '@playwright/test';
+import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
+
+test('Sends unexpected exception to Sentry if thrown in module with global filter', async ({ baseURL }) => {
+ const errorEventPromise = waitForError('nestjs-with-submodules-decorator', event => {
+ return !event.type && event.exception?.values?.[0]?.value === 'This is an uncaught exception!';
+ });
+
+ const response = await fetch(`${baseURL}/example-module/unexpected-exception`);
+ const responseBody = await response.json();
+
+ expect(response.status).toBe(501);
+ expect(responseBody).toEqual({
+ statusCode: 501,
+ timestamp: expect.any(String),
+ path: '/example-module/unexpected-exception',
+ message: 'Example exception was handled by global filter!',
+ });
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent.exception?.values).toHaveLength(1);
+ expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!');
+
+ expect(errorEvent.request).toEqual({
+ method: 'GET',
+ cookies: {},
+ headers: expect.any(Object),
+ url: 'http://localhost:3030/example-module/unexpected-exception',
+ });
+
+ expect(errorEvent.transaction).toEqual('GET /example-module/unexpected-exception');
+
+ expect(errorEvent.contexts?.trace).toEqual({
+ trace_id: expect.any(String),
+ span_id: expect.any(String),
+ });
+});
+
+test('Sends unexpected exception to Sentry if thrown in module with local filter', async ({ baseURL }) => {
+ const errorEventPromise = waitForError('nestjs-with-submodules-decorator', event => {
+ return !event.type && event.exception?.values?.[0]?.value === 'This is an uncaught exception!';
+ });
+
+ const response = await fetch(`${baseURL}/example-module-local-filter/unexpected-exception`);
+ const responseBody = await response.json();
+
+ expect(response.status).toBe(501);
+ expect(responseBody).toEqual({
+ statusCode: 501,
+ timestamp: expect.any(String),
+ path: '/example-module-local-filter/unexpected-exception',
+ message: 'Example exception was handled by global filter!',
+ });
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent.exception?.values).toHaveLength(1);
+ expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!');
+
+ expect(errorEvent.request).toEqual({
+ method: 'GET',
+ cookies: {},
+ headers: expect.any(Object),
+ url: 'http://localhost:3030/example-module-local-filter/unexpected-exception',
+ });
+
+ expect(errorEvent.transaction).toEqual('GET /example-module-local-filter/unexpected-exception');
+
+ expect(errorEvent.contexts?.trace).toEqual({
+ trace_id: expect.any(String),
+ span_id: expect.any(String),
+ });
+});
+
+test('Sends unexpected exception to Sentry if thrown in module that was registered before Sentry', async ({
+ baseURL,
+}) => {
+ const errorEventPromise = waitForError('nestjs-with-submodules-decorator', event => {
+ return !event.type && event.exception?.values?.[0]?.value === 'This is an uncaught exception!';
+ });
+
+ const response = await fetch(`${baseURL}/example-module-registered-first/unexpected-exception`);
+ const responseBody = await response.json();
+
+ expect(response.status).toBe(501);
+ expect(responseBody).toEqual({
+ statusCode: 501,
+ timestamp: expect.any(String),
+ path: '/example-module-registered-first/unexpected-exception',
+ message: 'Example exception was handled by global filter!',
+ });
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent.exception?.values).toHaveLength(1);
+ expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!');
+
+ expect(errorEvent.request).toEqual({
+ method: 'GET',
+ cookies: {},
+ headers: expect.any(Object),
+ url: 'http://localhost:3030/example-module-registered-first/unexpected-exception',
+ });
+
+ expect(errorEvent.transaction).toEqual('GET /example-module-registered-first/unexpected-exception');
+
+ expect(errorEvent.contexts?.trace).toEqual({
+ trace_id: expect.any(String),
+ span_id: expect.any(String),
+ });
+});
+
+test('Does not send exception to Sentry if user-defined global exception filter already catches the exception', async ({
+ baseURL,
+}) => {
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-with-submodules-decorator', event => {
+ if (!event.type && event.exception?.values?.[0]?.value === 'Something went wrong in the example module!') {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-module/expected-exception';
+ });
+
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules-decorator', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-module/expected-exception';
+ });
+
+ const response = await fetch(`${baseURL}/example-module/expected-exception`);
+ expect(response.status).toBe(400);
+
+ await transactionEventPromise;
+
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
+});
+
+test('Does not send exception to Sentry if user-defined local exception filter already catches the exception', async ({
+ baseURL,
+}) => {
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-with-submodules-decorator', event => {
+ if (
+ !event.type &&
+ event.exception?.values?.[0]?.value === 'Something went wrong in the example module with local filter!'
+ ) {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-module-local-filter/expected-exception';
+ });
+
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules-decorator', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-module-local-filter/expected-exception';
+ });
+
+ const response = await fetch(`${baseURL}/example-module-local-filter/expected-exception`);
+ expect(response.status).toBe(400);
+
+ await transactionEventPromise;
+
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
+});
+
+test('Does not send expected exception to Sentry if exception is thrown in module registered before Sentry', async ({
+ baseURL,
+}) => {
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-with-submodules-decorator', event => {
+ if (!event.type && event.exception?.values?.[0].value === 'Something went wrong in the example module!') {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-module-registered-first/expected-exception';
+ });
+
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules-decorator', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-module-registered-first/expected-exception';
+ });
+
+ const response = await fetch(`${baseURL}/example-module-registered-first/expected-exception`);
+ expect(response.status).toBe(400);
+
+ await transactionEventPromise;
+
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
+});
+
+test('Global specific exception filter registered in main module is applied and exception is not sent to Sentry', async ({
+ baseURL,
+}) => {
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-with-submodules-decorator', event => {
+ if (!event.type && event.exception?.values?.[0]?.value === 'Example exception was handled by specific filter!') {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-exception-specific-filter';
+ });
+
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules-decorator', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-exception-specific-filter';
+ });
+
+ const response = await fetch(`${baseURL}/example-exception-specific-filter`);
+ const responseBody = await response.json();
+
+ expect(response.status).toBe(400);
+ expect(responseBody).toEqual({
+ statusCode: 400,
+ timestamp: expect.any(String),
+ path: '/example-exception-specific-filter',
+ message: 'Example exception was handled by specific filter!',
+ });
+
+ await transactionEventPromise;
+
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
+});
+
+test('Local specific exception filter registered in main module is applied and exception is not sent to Sentry', async ({
+ baseURL,
+}) => {
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-with-submodules-decorator', event => {
+ if (!event.type && event.exception?.values?.[0]?.value === 'Example exception was handled by local filter!') {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-exception-local-filter';
+ });
+
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules-decorator', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-exception-local-filter';
+ });
+
+ const response = await fetch(`${baseURL}/example-exception-local-filter`);
+ const responseBody = await response.json();
+
+ expect(response.status).toBe(400);
+ expect(responseBody).toEqual({
+ statusCode: 400,
+ timestamp: expect.any(String),
+ path: '/example-exception-local-filter',
+ message: 'Example exception was handled by local filter!',
+ });
+
+ await transactionEventPromise;
+
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
+});
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/transactions.test.ts
new file mode 100644
index 000000000000..740ab6f5650c
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tests/transactions.test.ts
@@ -0,0 +1,240 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/test-utils';
+
+test('Sends an API route transaction from module', async ({ baseURL }) => {
+ const pageloadTransactionEventPromise = waitForTransaction('nestjs-with-submodules-decorator', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'http.server' &&
+ transactionEvent?.transaction === 'GET /example-module/transaction'
+ );
+ });
+
+ await fetch(`${baseURL}/example-module/transaction`);
+
+ const transactionEvent = await pageloadTransactionEventPromise;
+
+ expect(transactionEvent.contexts?.trace).toEqual({
+ data: {
+ 'sentry.source': 'route',
+ 'sentry.origin': 'auto.http.otel.http',
+ 'sentry.op': 'http.server',
+ 'sentry.sample_rate': 1,
+ url: 'http://localhost:3030/example-module/transaction',
+ 'otel.kind': 'SERVER',
+ 'http.response.status_code': 200,
+ 'http.url': 'http://localhost:3030/example-module/transaction',
+ 'http.host': 'localhost:3030',
+ 'net.host.name': 'localhost',
+ 'http.method': 'GET',
+ 'http.scheme': 'http',
+ 'http.target': '/example-module/transaction',
+ 'http.user_agent': 'node',
+ 'http.flavor': '1.1',
+ 'net.transport': 'ip_tcp',
+ 'net.host.ip': expect.any(String),
+ 'net.host.port': expect.any(Number),
+ 'net.peer.ip': expect.any(String),
+ 'net.peer.port': expect.any(Number),
+ 'http.status_code': 200,
+ 'http.status_text': 'OK',
+ 'http.route': '/example-module/transaction',
+ },
+ op: 'http.server',
+ span_id: expect.any(String),
+ status: 'ok',
+ trace_id: expect.any(String),
+ origin: 'auto.http.otel.http',
+ });
+
+ expect(transactionEvent).toEqual(
+ expect.objectContaining({
+ spans: expect.arrayContaining([
+ {
+ data: {
+ 'express.name': '/example-module/transaction',
+ 'express.type': 'request_handler',
+ 'http.route': '/example-module/transaction',
+ 'sentry.origin': 'auto.http.otel.express',
+ 'sentry.op': 'request_handler.express',
+ },
+ op: 'request_handler.express',
+ description: '/example-module/transaction',
+ parent_span_id: expect.any(String),
+ span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ status: 'ok',
+ timestamp: expect.any(Number),
+ trace_id: expect.any(String),
+ origin: 'auto.http.otel.express',
+ },
+ {
+ data: {
+ 'sentry.origin': 'manual',
+ },
+ description: 'test-span',
+ parent_span_id: expect.any(String),
+ span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ status: 'ok',
+ timestamp: expect.any(Number),
+ trace_id: expect.any(String),
+ origin: 'manual',
+ },
+ {
+ data: {
+ 'sentry.origin': 'manual',
+ },
+ description: 'child-span',
+ parent_span_id: expect.any(String),
+ span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ status: 'ok',
+ timestamp: expect.any(Number),
+ trace_id: expect.any(String),
+ origin: 'manual',
+ },
+ {
+ span_id: expect.any(String),
+ trace_id: expect.any(String),
+ data: {
+ 'sentry.origin': 'auto.http.otel.nestjs',
+ 'sentry.op': 'handler.nestjs',
+ component: '@nestjs/core',
+ 'nestjs.version': expect.any(String),
+ 'nestjs.type': 'handler',
+ 'nestjs.callback': 'testTransaction',
+ },
+ description: 'testTransaction',
+ parent_span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ status: 'ok',
+ origin: 'auto.http.otel.nestjs',
+ op: 'handler.nestjs',
+ },
+ ]),
+ transaction: 'GET /example-module/transaction',
+ type: 'transaction',
+ transaction_info: {
+ source: 'route',
+ },
+ }),
+ );
+});
+
+test('API route transaction includes exception filter span for global filter in module registered after Sentry', async ({
+ baseURL,
+}) => {
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules-decorator', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'http.server' &&
+ transactionEvent?.transaction === 'GET /example-module/expected-exception' &&
+ transactionEvent?.request?.url?.includes('/example-module/expected-exception')
+ );
+ });
+
+ const response = await fetch(`${baseURL}/example-module/expected-exception`);
+ expect(response.status).toBe(400);
+
+ const transactionEvent = await transactionEventPromise;
+
+ expect(transactionEvent).toEqual(
+ expect.objectContaining({
+ spans: expect.arrayContaining([
+ {
+ span_id: expect.any(String),
+ trace_id: expect.any(String),
+ data: {
+ 'sentry.op': 'middleware.nestjs',
+ 'sentry.origin': 'auto.middleware.nestjs',
+ },
+ description: 'ExampleExceptionFilter',
+ parent_span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ status: 'ok',
+ op: 'middleware.nestjs',
+ origin: 'auto.middleware.nestjs',
+ },
+ ]),
+ }),
+ );
+});
+
+test('API route transaction includes exception filter span for local filter in module registered after Sentry', async ({
+ baseURL,
+}) => {
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules-decorator', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'http.server' &&
+ transactionEvent?.transaction === 'GET /example-module-local-filter/expected-exception' &&
+ transactionEvent?.request?.url?.includes('/example-module-local-filter/expected-exception')
+ );
+ });
+
+ const response = await fetch(`${baseURL}/example-module-local-filter/expected-exception`);
+ expect(response.status).toBe(400);
+
+ const transactionEvent = await transactionEventPromise;
+
+ expect(transactionEvent).toEqual(
+ expect.objectContaining({
+ spans: expect.arrayContaining([
+ {
+ span_id: expect.any(String),
+ trace_id: expect.any(String),
+ data: {
+ 'sentry.op': 'middleware.nestjs',
+ 'sentry.origin': 'auto.middleware.nestjs',
+ },
+ description: 'LocalExampleExceptionFilter',
+ parent_span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ status: 'ok',
+ op: 'middleware.nestjs',
+ origin: 'auto.middleware.nestjs',
+ },
+ ]),
+ }),
+ );
+});
+
+test('API route transaction includes exception filter span for global filter in module registered before Sentry', async ({
+ baseURL,
+}) => {
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules-decorator', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'http.server' &&
+ transactionEvent?.transaction === 'GET /example-module-registered-first/expected-exception' &&
+ transactionEvent?.request?.url?.includes('/example-module-registered-first/expected-exception')
+ );
+ });
+
+ const response = await fetch(`${baseURL}/example-module-registered-first/expected-exception`);
+ expect(response.status).toBe(400);
+
+ const transactionEvent = await transactionEventPromise;
+
+ expect(transactionEvent).toEqual(
+ expect.objectContaining({
+ spans: expect.arrayContaining([
+ {
+ span_id: expect.any(String),
+ trace_id: expect.any(String),
+ data: {
+ 'sentry.op': 'middleware.nestjs',
+ 'sentry.origin': 'auto.middleware.nestjs',
+ },
+ description: 'ExampleExceptionFilterRegisteredFirst',
+ parent_span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ status: 'ok',
+ op: 'middleware.nestjs',
+ origin: 'auto.middleware.nestjs',
+ },
+ ]),
+ }),
+ );
+});
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tsconfig.build.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tsconfig.build.json
new file mode 100644
index 000000000000..26c30d4eddf2
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tsconfig.build.json
@@ -0,0 +1,4 @@
+{
+ "extends": "./tsconfig.json",
+ "exclude": ["node_modules", "test", "dist"]
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tsconfig.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tsconfig.json
new file mode 100644
index 000000000000..95f5641cf7f3
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "declaration": true,
+ "removeComments": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "allowSyntheticDefaultImports": true,
+ "target": "ES2021",
+ "sourceMap": true,
+ "outDir": "./dist",
+ "baseUrl": "./",
+ "incremental": true,
+ "skipLibCheck": true,
+ "strictNullChecks": false,
+ "noImplicitAny": false,
+ "strictBindCallApply": false,
+ "forceConsistentCasingInFileNames": false,
+ "noFallthroughCasesInSwitch": false
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.controller.ts
index 0d2c46e90da2..efce824a20c3 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.controller.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.controller.ts
@@ -1,8 +1,12 @@
-import { Controller, Get, Param } from '@nestjs/common';
+import { Controller, Get, Param, UseFilters } from '@nestjs/common';
import { flush } from '@sentry/nestjs';
import { AppService } from './app.service';
+import { ExampleExceptionLocalFilter } from './example-local.exception';
+import { ExampleLocalFilter } from './example-local.filter';
+import { ExampleExceptionSpecificFilter } from './example-specific.exception';
@Controller()
+@UseFilters(ExampleLocalFilter)
export class AppController {
constructor(private readonly appService: AppService) {}
@@ -20,4 +24,14 @@ export class AppController {
async flush() {
await flush();
}
+
+ @Get('example-exception-specific-filter')
+ async exampleExceptionGlobalFilter() {
+ throw new ExampleExceptionSpecificFilter();
+ }
+
+ @Get('example-exception-local-filter')
+ async exampleExceptionLocalFilter() {
+ throw new ExampleExceptionLocalFilter();
+ }
}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.module.ts
index 212b17a3556b..2a93747ac075 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.module.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.module.ts
@@ -1,19 +1,31 @@
import { Module } from '@nestjs/common';
-import { SentryModule } from '@sentry/nestjs/setup';
+import { APP_FILTER } from '@nestjs/core';
+import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup';
import { AppController } from './app.controller';
import { AppService } from './app.service';
-import { ExampleModuleGlobalFilterWrongRegistrationOrder } from './example-module-global-filter-wrong-registration-order/example.module';
+import { ExampleModuleGlobalFilterRegisteredFirst } from './example-module-global-filter-registered-first/example.module';
import { ExampleModuleGlobalFilter } from './example-module-global-filter/example.module';
import { ExampleModuleLocalFilter } from './example-module-local-filter/example.module';
+import { ExampleSpecificFilter } from './example-specific.filter';
@Module({
imports: [
- ExampleModuleGlobalFilterWrongRegistrationOrder,
+ ExampleModuleGlobalFilterRegisteredFirst,
SentryModule.forRoot(),
ExampleModuleGlobalFilter,
ExampleModuleLocalFilter,
],
controllers: [AppController],
- providers: [AppService],
+ providers: [
+ AppService,
+ {
+ provide: APP_FILTER,
+ useClass: SentryGlobalFilter,
+ },
+ {
+ provide: APP_FILTER,
+ useClass: ExampleSpecificFilter,
+ },
+ ],
})
export class AppModule {}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-local.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-local.exception.ts
new file mode 100644
index 000000000000..8f76520a3b94
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-local.exception.ts
@@ -0,0 +1,5 @@
+export class ExampleExceptionLocalFilter extends Error {
+ constructor() {
+ super('Original local example exception!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-local.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-local.filter.ts
new file mode 100644
index 000000000000..0e93e5f7fac2
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-local.filter.ts
@@ -0,0 +1,19 @@
+import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
+import { Request, Response } from 'express';
+import { ExampleExceptionLocalFilter } from './example-local.exception';
+
+@Catch(ExampleExceptionLocalFilter)
+export class ExampleLocalFilter implements ExceptionFilter {
+ catch(exception: BadRequestException, host: ArgumentsHost): void {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+
+ response.status(400).json({
+ statusCode: 400,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ message: 'Example exception was handled by local filter!',
+ });
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.controller.ts
new file mode 100644
index 000000000000..967886948a25
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.controller.ts
@@ -0,0 +1,17 @@
+import { Controller, Get } from '@nestjs/common';
+import { ExampleExceptionRegisteredFirst } from './example.exception';
+
+@Controller('example-module-registered-first')
+export class ExampleController {
+ constructor() {}
+
+ @Get('/expected-exception')
+ getCaughtException(): string {
+ throw new ExampleExceptionRegisteredFirst();
+ }
+
+ @Get('/unexpected-exception')
+ getUncaughtException(): string {
+ throw new Error(`This is an uncaught exception!`);
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.exception.ts
new file mode 100644
index 000000000000..9bb3b5948343
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.exception.ts
@@ -0,0 +1,5 @@
+export class ExampleExceptionRegisteredFirst extends Error {
+ constructor() {
+ super('Something went wrong in the example module!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.filter.ts
new file mode 100644
index 000000000000..6c3b81d395b5
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.filter.ts
@@ -0,0 +1,13 @@
+import { ArgumentsHost, BadRequestException, Catch } from '@nestjs/common';
+import { BaseExceptionFilter } from '@nestjs/core';
+import { ExampleExceptionRegisteredFirst } from './example.exception';
+
+@Catch(ExampleExceptionRegisteredFirst)
+export class ExampleExceptionFilterRegisteredFirst extends BaseExceptionFilter {
+ catch(exception: unknown, host: ArgumentsHost) {
+ if (exception instanceof ExampleExceptionRegisteredFirst) {
+ return super.catch(new BadRequestException(exception.message), host);
+ }
+ return super.catch(exception, host);
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.module.ts
new file mode 100644
index 000000000000..8751856f99cd
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-global-filter-registered-first/example.module.ts
@@ -0,0 +1,16 @@
+import { Module } from '@nestjs/common';
+import { APP_FILTER } from '@nestjs/core';
+import { ExampleController } from './example.controller';
+import { ExampleExceptionFilterRegisteredFirst } from './example.filter';
+
+@Module({
+ imports: [],
+ controllers: [ExampleController],
+ providers: [
+ {
+ provide: APP_FILTER,
+ useClass: ExampleExceptionFilterRegisteredFirst,
+ },
+ ],
+})
+export class ExampleModuleGlobalFilterRegisteredFirst {}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-local-filter/example.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-local-filter/example.controller.ts
index 41d75d6eaf89..11a0470ace17 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-local-filter/example.controller.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module-local-filter/example.controller.ts
@@ -11,4 +11,9 @@ export class ExampleControllerLocalFilter {
getCaughtException() {
throw new LocalExampleException();
}
+
+ @Get('/unexpected-exception')
+ getUncaughtException(): string {
+ throw new Error(`This is an uncaught exception!`);
+ }
}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-specific.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-specific.exception.ts
new file mode 100644
index 000000000000..a26cb1a26d52
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-specific.exception.ts
@@ -0,0 +1,5 @@
+export class ExampleExceptionSpecificFilter extends Error {
+ constructor() {
+ super('Original specific example exception!');
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-specific.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-specific.filter.ts
new file mode 100644
index 000000000000..bf85385a9a86
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-specific.filter.ts
@@ -0,0 +1,19 @@
+import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
+import { Request, Response } from 'express';
+import { ExampleExceptionSpecificFilter } from './example-specific.exception';
+
+@Catch(ExampleExceptionSpecificFilter)
+export class ExampleSpecificFilter implements ExceptionFilter {
+ catch(exception: BadRequestException, host: ArgumentsHost): void {
+ const ctx = host.switchToHttp();
+ const response = ctx.getResponse();
+ const request = ctx.getRequest();
+
+ response.status(400).json({
+ statusCode: 400,
+ timestamp: new Date().toISOString(),
+ path: request.url,
+ message: 'Example exception was handled by specific filter!',
+ });
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts
index 6fbc9f2c1f32..03d4679fa3c0 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts
@@ -29,6 +29,34 @@ test('Sends unexpected exception to Sentry if thrown in module with global filte
});
});
+test('Sends unexpected exception to Sentry if thrown in module with local filter', async ({ baseURL }) => {
+ const errorEventPromise = waitForError('nestjs-with-submodules', event => {
+ return !event.type && event.exception?.values?.[0]?.value === 'This is an uncaught exception!';
+ });
+
+ const response = await fetch(`${baseURL}/example-module-local-filter/unexpected-exception`);
+ expect(response.status).toBe(500);
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent.exception?.values).toHaveLength(1);
+ expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an uncaught exception!');
+
+ expect(errorEvent.request).toEqual({
+ method: 'GET',
+ cookies: {},
+ headers: expect.any(Object),
+ url: 'http://localhost:3030/example-module-local-filter/unexpected-exception',
+ });
+
+ expect(errorEvent.transaction).toEqual('GET /example-module-local-filter/unexpected-exception');
+
+ expect(errorEvent.contexts?.trace).toEqual({
+ trace_id: expect.any(String),
+ span_id: expect.any(String),
+ });
+});
+
test('Sends unexpected exception to Sentry if thrown in module that was registered before Sentry', async ({
baseURL,
}) => {
@@ -36,7 +64,7 @@ test('Sends unexpected exception to Sentry if thrown in module that was register
return !event.type && event.exception?.values?.[0]?.value === 'This is an uncaught exception!';
});
- const response = await fetch(`${baseURL}/example-module-wrong-order/unexpected-exception`);
+ const response = await fetch(`${baseURL}/example-module-registered-first/unexpected-exception`);
expect(response.status).toBe(500);
const errorEvent = await errorEventPromise;
@@ -48,10 +76,10 @@ test('Sends unexpected exception to Sentry if thrown in module that was register
method: 'GET',
cookies: {},
headers: expect.any(Object),
- url: 'http://localhost:3030/example-module-wrong-order/unexpected-exception',
+ url: 'http://localhost:3030/example-module-registered-first/unexpected-exception',
});
- expect(errorEvent.transaction).toEqual('GET /example-module-wrong-order/unexpected-exception');
+ expect(errorEvent.transaction).toEqual('GET /example-module-registered-first/unexpected-exception');
expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.any(String),
@@ -116,33 +144,99 @@ test('Does not send exception to Sentry if user-defined local exception filter a
expect(errorEventOccurred).toBe(false);
});
-test('Does not handle expected exception if exception is thrown in module registered before Sentry', async ({
+test('Does not send expected exception to Sentry if exception is thrown in module registered before Sentry', async ({
baseURL,
}) => {
- const errorEventPromise = waitForError('nestjs-with-submodules', event => {
- return !event.type && event.exception?.values?.[0]?.value === 'Something went wrong in the example module!';
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-with-submodules', event => {
+ if (!event.type && event.exception?.values?.[0].value === 'Something went wrong in the example module!') {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-module-registered-first/expected-exception';
});
- const response = await fetch(`${baseURL}/example-module-wrong-order/expected-exception`);
- expect(response.status).toBe(500); // should be 400
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-module-registered-first/expected-exception';
+ });
- // should never arrive, but does because the exception is not handled properly
- const errorEvent = await errorEventPromise;
+ const response = await fetch(`${baseURL}/example-module-registered-first/expected-exception`);
+ expect(response.status).toBe(400);
- expect(errorEvent.exception?.values).toHaveLength(1);
- expect(errorEvent.exception?.values?.[0]?.value).toBe('Something went wrong in the example module!');
+ await transactionEventPromise;
- expect(errorEvent.request).toEqual({
- method: 'GET',
- cookies: {},
- headers: expect.any(Object),
- url: 'http://localhost:3030/example-module-wrong-order/expected-exception',
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
+});
+
+test('Global specific exception filter registered in main module is applied and exception is not sent to Sentry', async ({
+ baseURL,
+}) => {
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-with-submodules', event => {
+ if (!event.type && event.exception?.values?.[0]?.value === 'Example exception was handled by specific filter!') {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-exception-specific-filter';
});
- expect(errorEvent.transaction).toEqual('GET /example-module-wrong-order/expected-exception');
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-exception-specific-filter';
+ });
- expect(errorEvent.contexts?.trace).toEqual({
- trace_id: expect.any(String),
- span_id: expect.any(String),
+ const response = await fetch(`${baseURL}/example-exception-specific-filter`);
+ const responseBody = await response.json();
+
+ expect(response.status).toBe(400);
+ expect(responseBody).toEqual({
+ statusCode: 400,
+ timestamp: expect.any(String),
+ path: '/example-exception-specific-filter',
+ message: 'Example exception was handled by specific filter!',
});
+
+ await transactionEventPromise;
+
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
+});
+
+test('Local specific exception filter registered in main module is applied and exception is not sent to Sentry', async ({
+ baseURL,
+}) => {
+ let errorEventOccurred = false;
+
+ waitForError('nestjs-with-submodules', event => {
+ if (!event.type && event.exception?.values?.[0]?.value === 'Example exception was handled by local filter!') {
+ errorEventOccurred = true;
+ }
+
+ return event?.transaction === 'GET /example-exception-local-filter';
+ });
+
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules', transactionEvent => {
+ return transactionEvent?.transaction === 'GET /example-exception-local-filter';
+ });
+
+ const response = await fetch(`${baseURL}/example-exception-local-filter`);
+ const responseBody = await response.json();
+
+ expect(response.status).toBe(400);
+ expect(responseBody).toEqual({
+ statusCode: 400,
+ timestamp: expect.any(String),
+ path: '/example-exception-local-filter',
+ message: 'Example exception was handled by local filter!',
+ });
+
+ await transactionEventPromise;
+
+ (await fetch(`${baseURL}/flush`)).text();
+
+ expect(errorEventOccurred).toBe(false);
});
diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/transactions.test.ts
index 9217249faad0..a2ea1db9c506 100644
--- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/transactions.test.ts
@@ -122,7 +122,9 @@ test('Sends an API route transaction from module', async ({ baseURL }) => {
);
});
-test('API route transaction includes exception filter span for global filter', async ({ baseURL }) => {
+test('API route transaction includes exception filter span for global filter in module registered after Sentry', async ({
+ baseURL,
+}) => {
const transactionEventPromise = waitForTransaction('nestjs-with-submodules', transactionEvent => {
return (
transactionEvent?.contexts?.trace?.op === 'http.server' &&
@@ -159,7 +161,9 @@ test('API route transaction includes exception filter span for global filter', a
);
});
-test('API route transaction includes exception filter span for local filter', async ({ baseURL }) => {
+test('API route transaction includes exception filter span for local filter in module registered after Sentry', async ({
+ baseURL,
+}) => {
const transactionEventPromise = waitForTransaction('nestjs-with-submodules', transactionEvent => {
return (
transactionEvent?.contexts?.trace?.op === 'http.server' &&
@@ -195,3 +199,42 @@ test('API route transaction includes exception filter span for local filter', as
}),
);
});
+
+test('API route transaction includes exception filter span for global filter in module registered before Sentry', async ({
+ baseURL,
+}) => {
+ const transactionEventPromise = waitForTransaction('nestjs-with-submodules', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'http.server' &&
+ transactionEvent?.transaction === 'GET /example-module-registered-first/expected-exception' &&
+ transactionEvent?.request?.url?.includes('/example-module-registered-first/expected-exception')
+ );
+ });
+
+ const response = await fetch(`${baseURL}/example-module-registered-first/expected-exception`);
+ expect(response.status).toBe(400);
+
+ const transactionEvent = await transactionEventPromise;
+
+ expect(transactionEvent).toEqual(
+ expect.objectContaining({
+ spans: expect.arrayContaining([
+ {
+ span_id: expect.any(String),
+ trace_id: expect.any(String),
+ data: {
+ 'sentry.op': 'middleware.nestjs',
+ 'sentry.origin': 'auto.middleware.nestjs',
+ },
+ description: 'ExampleExceptionFilterRegisteredFirst',
+ parent_span_id: expect.any(String),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ status: 'ok',
+ op: 'middleware.nestjs',
+ origin: 'auto.middleware.nestjs',
+ },
+ ]),
+ }),
+ );
+});
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/back-navigation.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/back-navigation.tsx
new file mode 100644
index 000000000000..ddd970944bf3
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/back-navigation.tsx
@@ -0,0 +1,9 @@
+import { A } from '@solidjs/router';
+
+export default function BackNavigation() {
+ return (
+
+ User 6
+
+ );
+}
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx
index 873d542e4bae..eed722cba4e3 100644
--- a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx
+++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx
@@ -20,9 +20,7 @@ export default function Home() {
-
- User 6
-
+ Test back navigation
>
diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts
index 6e5f43e016c8..52d9cb219401 100644
--- a/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts
+++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts
@@ -54,8 +54,8 @@ test('updates the transaction when using the back button', async ({ page }) => {
return transactionEvent?.transaction === '/users/6' && transactionEvent.contexts?.trace?.op === 'navigation';
});
- await page.goto(`/`);
- await page.locator('#navLinkUserBack').click();
+ await page.goto(`/back-navigation`);
+ await page.locator('#navLink').click();
const navigationTxn = await navigationTxnPromise;
expect(navigationTxn).toMatchObject({
@@ -72,7 +72,9 @@ test('updates the transaction when using the back button', async ({ page }) => {
});
const backNavigationTxnPromise = waitForTransaction('solidstart', async transactionEvent => {
- return transactionEvent?.transaction === '/' && transactionEvent.contexts?.trace?.op === 'navigation';
+ return (
+ transactionEvent?.transaction === '/back-navigation' && transactionEvent.contexts?.trace?.op === 'navigation'
+ );
});
await page.goBack();
@@ -85,7 +87,7 @@ test('updates the transaction when using the back button', async ({ page }) => {
origin: 'auto.navigation.solidstart.solidrouter',
},
},
- transaction: '/',
+ transaction: '/back-navigation',
transaction_info: {
source: 'url',
},
diff --git a/dev-packages/external-contributor-gh-action/package.json b/dev-packages/external-contributor-gh-action/package.json
index c5502135c384..3d81148be51a 100644
--- a/dev-packages/external-contributor-gh-action/package.json
+++ b/dev-packages/external-contributor-gh-action/package.json
@@ -1,7 +1,7 @@
{
"name": "@sentry-internal/external-contributor-gh-action",
"description": "An internal Github Action to add external contributors to the CHANGELOG.md file.",
- "version": "8.25.0",
+ "version": "8.26.0",
"license": "MIT",
"engines": {
"node": ">=18"
diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json
index c9ddd75d6402..e381dc99e5d6 100644
--- a/dev-packages/node-integration-tests/package.json
+++ b/dev-packages/node-integration-tests/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/node-integration-tests",
- "version": "8.25.0",
+ "version": "8.26.0",
"license": "MIT",
"engines": {
"node": ">=14.18"
@@ -31,10 +31,10 @@
"@nestjs/core": "^10.3.3",
"@nestjs/platform-express": "^10.3.3",
"@prisma/client": "5.9.1",
- "@sentry/aws-serverless": "8.25.0",
- "@sentry/node": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0",
+ "@sentry/aws-serverless": "8.26.0",
+ "@sentry/node": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0",
"@types/mongodb": "^3.6.20",
"@types/mysql": "^2.15.21",
"@types/pg": "^8.6.5",
diff --git a/dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js b/dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js
new file mode 100644
index 000000000000..74d5f73693f5
--- /dev/null
+++ b/dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js
@@ -0,0 +1,71 @@
+const { loggingTransport } = require('@sentry-internal/node-integration-tests');
+const Sentry = require('@sentry/node');
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ release: '1.0',
+ tracesSampleRate: 1.0,
+ transport: loggingTransport,
+});
+
+// Stop the process from exiting before the transaction is sent
+setInterval(() => {}, 1000);
+
+const mysql = require('mysql');
+const genericPool = require('generic-pool');
+
+const factory = {
+ create: function () {
+ return mysql.createConnection({
+ user: 'root',
+ password: 'docker',
+ });
+ },
+ destroy: function (client) {
+ client.end(err => {
+ if (err) {
+ // eslint-disable-next-line no-console
+ console.error('Error while disconnecting MySQL:', err);
+ }
+ });
+ },
+};
+
+const opts = {
+ max: 10,
+ min: 2,
+};
+
+const myPool = genericPool.createPool(factory, opts);
+
+async function run() {
+ await Sentry.startSpan(
+ {
+ op: 'transaction',
+ name: 'Test Transaction',
+ },
+ async () => {
+ try {
+ const client1 = await myPool.acquire();
+ const client2 = await myPool.acquire();
+
+ client1.query('SELECT NOW()', function () {
+ myPool.release(client1);
+ });
+
+ client2.query('SELECT 1 + 1 AS solution', function () {
+ myPool.release(client2);
+ });
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.error('Error while pooling MySQL:', err);
+ } finally {
+ await myPool.drain();
+ await myPool.clear();
+ }
+ },
+ );
+}
+
+// eslint-disable-next-line @typescript-eslint/no-floating-promises
+run();
diff --git a/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts b/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts
new file mode 100644
index 000000000000..129e142f2808
--- /dev/null
+++ b/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts
@@ -0,0 +1,34 @@
+import { cleanupChildProcesses, createRunner } from '../../../utils/runner';
+
+describe('genericPool auto instrumentation', () => {
+ afterAll(() => {
+ cleanupChildProcesses();
+ });
+
+ test('should auto-instrument `genericPool` package when calling pool.require()', done => {
+ const EXPECTED_TRANSACTION = {
+ transaction: 'Test Transaction',
+ spans: expect.arrayContaining([
+ expect.objectContaining({
+ description: 'generic-pool.aquire',
+ origin: 'auto.db.otel.generic-pool',
+ data: {
+ 'sentry.origin': 'auto.db.otel.generic-pool',
+ },
+ status: 'ok',
+ }),
+
+ expect.objectContaining({
+ description: 'generic-pool.aquire',
+ origin: 'auto.db.otel.generic-pool',
+ data: {
+ 'sentry.origin': 'auto.db.otel.generic-pool',
+ },
+ status: 'ok',
+ }),
+ ]),
+ };
+
+ createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done);
+ });
+});
diff --git a/dev-packages/overhead-metrics/package.json b/dev-packages/overhead-metrics/package.json
index 724ffad496a8..4b4825b185df 100644
--- a/dev-packages/overhead-metrics/package.json
+++ b/dev-packages/overhead-metrics/package.json
@@ -1,6 +1,6 @@
{
"private": true,
- "version": "8.25.0",
+ "version": "8.26.0",
"name": "@sentry-internal/overhead-metrics",
"main": "index.js",
"author": "Sentry",
diff --git a/dev-packages/rollup-utils/package.json b/dev-packages/rollup-utils/package.json
index 5b591020aaeb..6ac6f0a1a729 100644
--- a/dev-packages/rollup-utils/package.json
+++ b/dev-packages/rollup-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/rollup-utils",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Rollup utilities used at Sentry for the Sentry JavaScript SDK",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/rollup-utils",
diff --git a/dev-packages/size-limit-gh-action/index.mjs b/dev-packages/size-limit-gh-action/index.mjs
index c42e9c523e94..3dbb8aa22127 100644
--- a/dev-packages/size-limit-gh-action/index.mjs
+++ b/dev-packages/size-limit-gh-action/index.mjs
@@ -31,12 +31,27 @@ class SizeLimit {
return bytes.format(size, { unitSeparator: ' ' });
}
- formatTime(seconds) {
- if (seconds >= 1) {
- return `${Math.ceil(seconds * 10) / 10} s`;
+ formatPercentageChange(base = 0, current = 0) {
+ if (base === 0) {
+ return 'added';
+ }
+
+ if (current === 0) {
+ return 'removed';
+ }
+
+ const value = ((current - base) / base) * 100;
+ const formatted = (Math.sign(value) * Math.ceil(Math.abs(value) * 100)) / 100;
+
+ if (value > 0) {
+ return `+${formatted}%`;
+ }
+
+ if (value === 0) {
+ return '-';
}
- return `${Math.ceil(seconds * 1000)} ms`;
+ return `${formatted}%`;
}
formatChange(base = 0, current = 0) {
@@ -48,18 +63,18 @@ class SizeLimit {
return 'removed';
}
- const value = ((current - base) / base) * 100;
- const formatted = (Math.sign(value) * Math.ceil(Math.abs(value) * 100)) / 100;
+ const value = current - base;
+ const formatted = this.formatBytes(value);
if (value > 0) {
- return `+${formatted}% 🔺`;
+ return `+${formatted} 🔺`;
}
if (value === 0) {
- return `${formatted}%`;
+ return '-';
}
- return `${formatted}% 🔽`;
+ return `${formatted} 🔽`;
}
formatLine(value, change) {
@@ -67,16 +82,11 @@ class SizeLimit {
}
formatSizeResult(name, base, current) {
- return [name, this.formatLine(this.formatBytes(current.size), this.formatChange(base.size, current.size))];
- }
-
- formatTimeResult(name, base, current) {
return [
name,
- this.formatLine(this.formatBytes(current.size), this.formatChange(base.size, current.size)),
- this.formatLine(this.formatTime(current.loading), this.formatChange(base.loading, current.loading)),
- this.formatLine(this.formatTime(current.running), this.formatChange(base.running, current.running)),
- this.formatTime(current.total),
+ this.formatBytes(current.size),
+ this.formatPercentageChange(base.size, current.size),
+ this.formatChange(base.size, current.size),
];
}
@@ -84,26 +94,12 @@ class SizeLimit {
const results = JSON.parse(output);
return results.reduce((current, result) => {
- let time = {};
-
- if (result.loading !== undefined && result.running !== undefined) {
- const loading = +result.loading;
- const running = +result.running;
-
- time = {
- running,
- loading,
- total: loading + running,
- };
- }
-
return {
// biome-ignore lint/performance/noAccumulatingSpread:
...current,
[result.name]: {
name: result.name,
size: +result.size,
- ...time,
},
};
}, {});
@@ -111,12 +107,6 @@ class SizeLimit {
hasSizeChanges(base, current, threshold = 0) {
const names = [...new Set([...(base ? Object.keys(base) : []), ...Object.keys(current)])];
- const isSize = names.some(name => current[name] && current[name].total === undefined);
-
- // Always return true if time results are present
- if (!isSize) {
- return true;
- }
return !!names.find(name => {
const baseResult = base?.[name] || EmptyResult;
@@ -132,16 +122,12 @@ class SizeLimit {
formatResults(base, current) {
const names = [...new Set([...(base ? Object.keys(base) : []), ...Object.keys(current)])];
- const isSize = names.some(name => current[name] && current[name].total === undefined);
- const header = isSize ? SIZE_RESULTS_HEADER : TIME_RESULTS_HEADER;
+ const header = SIZE_RESULTS_HEADER;
const fields = names.map(name => {
const baseResult = base?.[name] || EmptyResult;
const currentResult = current[name] || EmptyResult;
- if (isSize) {
- return this.formatSizeResult(name, baseResult, currentResult);
- }
- return this.formatTimeResult(name, baseResult, currentResult);
+ return this.formatSizeResult(name, baseResult, currentResult);
});
return [header, ...fields];
@@ -165,15 +151,11 @@ async function execSizeLimit() {
return { status, output };
}
-const SIZE_RESULTS_HEADER = ['Path', 'Size'];
-const TIME_RESULTS_HEADER = ['Path', 'Size', 'Loading time (3g)', 'Running time (snapdragon)', 'Total time'];
+const SIZE_RESULTS_HEADER = ['Path', 'Size', '% Change', 'Change'];
const EmptyResult = {
name: '-',
size: 0,
- running: 0,
- loading: 0,
- total: 0,
};
async function run() {
@@ -227,6 +209,8 @@ async function run() {
// Else, we run size limit for the current branch, AND fetch it for the comparison branch
let base;
let current;
+ let baseIsNotLatest = false;
+ let baseWorkflowRun;
try {
const artifacts = await getArtifactsForBranchAndWorkflow(octokit, {
@@ -240,6 +224,8 @@ async function run() {
throw new Error('No artifacts found');
}
+ baseWorkflowRun = artifacts.workflowRun;
+
await downloadOtherWorkflowArtifact(octokit, {
...repo,
artifactName: ARTIFACT_NAME,
@@ -248,6 +234,11 @@ async function run() {
});
base = JSON.parse(await fs.readFile(resultsFilePath, { encoding: 'utf8' }));
+
+ if (!artifacts.isLatest) {
+ baseIsNotLatest = true;
+ core.info('Base artifact is not the latest one. This may lead to incorrect results.');
+ }
} catch (error) {
core.startGroup('Warning, unable to find base results');
core.error(error);
@@ -271,7 +262,22 @@ async function run() {
isNaN(thresholdNumber) || limit.hasSizeChanges(base, current, thresholdNumber) || sizeLimitComment;
if (shouldComment) {
- const body = [SIZE_LIMIT_HEADING, markdownTable(limit.formatResults(base, current))].join('\r\n');
+ const bodyParts = [SIZE_LIMIT_HEADING];
+
+ if (baseIsNotLatest) {
+ bodyParts.push(
+ '⚠️ **Warning:** Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.',
+ );
+ }
+
+ bodyParts.push(markdownTable(limit.formatResults(base, current)));
+
+ if (baseWorkflowRun) {
+ bodyParts.push('');
+ bodyParts.push(`[View base workflow run](${baseWorkflowRun.html_url})`);
+ }
+
+ const body = bodyParts.join('\r\n');
try {
if (!sizeLimitComment) {
@@ -320,7 +326,7 @@ const DEFAULT_PAGE_LIMIT = 10;
* This is a bit hacky since GitHub Actions currently does not directly
* support downloading artifacts from other workflows
*/
-export async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, workflowName, branch, artifactName }) {
+async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, workflowName, branch, artifactName }) {
core.startGroup(`getArtifactsForBranchAndWorkflow - workflow:"${workflowName}", branch:"${branch}"`);
let repositoryWorkflow = null;
@@ -361,14 +367,13 @@ export async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, w
const workflow_id = repositoryWorkflow.id;
let currentPage = 0;
- const completedWorkflowRuns = [];
+ let latestWorkflowRun = null;
for await (const response of octokit.paginate.iterator(octokit.rest.actions.listWorkflowRuns, {
owner,
repo,
workflow_id,
branch,
- status: 'completed',
per_page: DEFAULT_PAGE_LIMIT,
event: 'push',
})) {
@@ -379,12 +384,47 @@ export async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, w
}
// Do not allow downloading artifacts from a fork.
- completedWorkflowRuns.push(
- ...response.data.filter(workflowRun => workflowRun.head_repository.full_name === `${owner}/${repo}`),
- );
+ const filtered = response.data.filter(workflowRun => workflowRun.head_repository.full_name === `${owner}/${repo}`);
+
+ // Sort to ensure the latest workflow run is the first
+ filtered.sort((a, b) => {
+ return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
+ });
+
+ // Store the first workflow run, to determine if this is the latest one...
+ if (!latestWorkflowRun) {
+ latestWorkflowRun = filtered[0];
+ }
+
+ // Search through workflow artifacts until we find a workflow run w/ artifact name that we are looking for
+ for (const workflowRun of filtered) {
+ core.info(`Checking artifacts for workflow run: ${workflowRun.html_url}`);
- if (completedWorkflowRuns.length) {
- break;
+ const {
+ data: { artifacts },
+ } = await octokit.rest.actions.listWorkflowRunArtifacts({
+ owner,
+ repo,
+ run_id: workflowRun.id,
+ });
+
+ if (!artifacts) {
+ core.warning(
+ `Unable to fetch artifacts for branch: ${branch}, workflow: ${workflow_id}, workflowRunId: ${workflowRun.id}`,
+ );
+ } else {
+ const foundArtifact = artifacts.find(({ name }) => name === artifactName);
+ if (foundArtifact) {
+ core.info(`Found suitable artifact: ${foundArtifact.url}`);
+ return {
+ artifact: foundArtifact,
+ workflowRun,
+ isLatest: latestWorkflowRun.id === workflowRun.id,
+ };
+ } else {
+ core.info(`No artifact found for ${artifactName}, trying next workflow run...`);
+ }
+ }
}
if (currentPage > DEFAULT_MAX_PAGES) {
@@ -396,34 +436,6 @@ export async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, w
currentPage++;
}
- // Search through workflow artifacts until we find a workflow run w/ artifact name that we are looking for
- for (const workflowRun of completedWorkflowRuns) {
- core.info(`Checking artifacts for workflow run: ${workflowRun.html_url}`);
-
- const {
- data: { artifacts },
- } = await octokit.rest.actions.listWorkflowRunArtifacts({
- owner,
- repo,
- run_id: workflowRun.id,
- });
-
- if (!artifacts) {
- core.warning(
- `Unable to fetch artifacts for branch: ${branch}, workflow: ${workflow_id}, workflowRunId: ${workflowRun.id}`,
- );
- } else {
- const foundArtifact = artifacts.find(({ name }) => name === artifactName);
- if (foundArtifact) {
- core.info(`Found suitable artifact: ${foundArtifact.url}`);
- return {
- artifact: foundArtifact,
- workflowRun,
- };
- }
- }
- }
-
core.warning(`Artifact not found: ${artifactName}`);
core.endGroup();
return null;
diff --git a/dev-packages/size-limit-gh-action/package.json b/dev-packages/size-limit-gh-action/package.json
index 7179980f6f32..7d76088b54b6 100644
--- a/dev-packages/size-limit-gh-action/package.json
+++ b/dev-packages/size-limit-gh-action/package.json
@@ -1,7 +1,7 @@
{
"name": "@sentry-internal/size-limit-gh-action",
"description": "An internal Github Action to compare the current size of a PR against the one on develop.",
- "version": "8.25.0",
+ "version": "8.26.0",
"license": "MIT",
"engines": {
"node": ">=18"
diff --git a/dev-packages/test-utils/package.json b/dev-packages/test-utils/package.json
index 7fd73360538c..a0b1688f4142 100644
--- a/dev-packages/test-utils/package.json
+++ b/dev-packages/test-utils/package.json
@@ -1,6 +1,6 @@
{
"private": true,
- "version": "8.25.0",
+ "version": "8.26.0",
"name": "@sentry-internal/test-utils",
"author": "Sentry",
"license": "MIT",
@@ -45,8 +45,8 @@
},
"devDependencies": {
"@playwright/test": "^1.44.1",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0"
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0"
},
"volta": {
"extends": "../../package.json"
diff --git a/docs/changelog/v7.md b/docs/changelog/v7.md
index 16072a8f8121..e784702015e0 100644
--- a/docs/changelog/v7.md
+++ b/docs/changelog/v7.md
@@ -1,7 +1,7 @@
# Changelog for Sentry SDK 7.x
Support for Sentry SDK v7 will be dropped soon. We recommend migrating to the latest version of the SDK. You can migrate
-from `v7` to `v8 of the SDK by following the [migration guide](../../MIGRATION.md).
+from `v7` of the SDK to `v8` by following the [migration guide](../../MIGRATION.md#upgrading-from-7x-to-8x).
## 7.118.0
diff --git a/docs/new-sdk-release-checklist.md b/docs/new-sdk-release-checklist.md
index 1292c5363fb0..7f1811e53d8f 100644
--- a/docs/new-sdk-release-checklist.md
+++ b/docs/new-sdk-release-checklist.md
@@ -45,8 +45,8 @@ differ slightly for other SDKs depending on how they are structured and how they
- [ ] Make sure `build.yml` CI script is correctly set up to cover tests for the new package
- - [ ] Ensure dependent packages are correctly set for “Determine changed packages”
- [ ] Ensure unit tests run correctly
+ - [ ] If it is a browser SDK, add it to `BROWSER_TEST_PACKAGES` in `scripts/ci-unit-tests.ts`
- [ ] Make sure the file paths in the
["Upload Artifacts" job](https://github.com/getsentry/sentry-javascript/blob/e5c1486eed236b878f2c49d6a04be86093816ac9/.github/workflows/build.yml#L314-L349)
diff --git a/lerna.json b/lerna.json
index ed95619c70a2..e3c3f83f3096 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
- "version": "8.25.0",
+ "version": "8.26.0",
"npmClient": "yarn"
}
diff --git a/package.json b/package.json
index ebf4021a7a6a..beba7d79d284 100644
--- a/package.json
+++ b/package.json
@@ -35,15 +35,15 @@
"test:unit": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\" test:unit",
"test:update-snapshots": "lerna run test:update-snapshots",
"test:pr": "nx affected -t test --exclude \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\"",
- "test:pr:browser": "yarn test:pr --exclude \"@sentry/{core,utils,opentelemetry,bun,deno,node,profiling-node,aws-serverless,google-cloud-serverless,nextjs,nestjs,astro,cloudflare,solidstart,nuxt,remix,gatsby,sveltekit,vercel-edge}\"",
- "test:pr:node": "ts-node ./scripts/node-unit-tests.ts --affected",
- "test:ci:browser": "lerna run test --ignore \"@sentry/{core,utils,opentelemetry,bun,deno,node,profiling-node,aws-serverless,google-cloud-serverless,nextjs,nestjs,astro,cloudflare,solidstart,nuxt,remix,gatsby,sveltekit,vercel-edge}\" --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\"",
- "test:ci:node": "ts-node ./scripts/node-unit-tests.ts",
+ "test:pr:browser": "UNIT_TEST_ENV=browser ts-node ./scripts/ci-unit-tests.ts --affected",
+ "test:pr:node": "UNIT_TEST_ENV=node ts-node ./scripts/ci-unit-tests.ts --affected",
+ "test:ci:browser": "UNIT_TEST_ENV=browser ts-node ./scripts/ci-unit-tests.ts",
+ "test:ci:node": "UNIT_TEST_ENV=node ts-node ./scripts/ci-unit-tests.ts",
"test:ci:bun": "lerna run test --scope @sentry/bun",
"yalc:publish": "lerna run yalc:publish"
},
"volta": {
- "node": "22.5.1",
+ "node": "18.20.3",
"yarn": "1.22.22"
},
"workspaces": [
diff --git a/packages/angular/package.json b/packages/angular/package.json
index 5f22dc37ac12..a6c6bb06ef94 100644
--- a/packages/angular/package.json
+++ b/packages/angular/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry/angular",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Official Sentry SDK for Angular",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular",
@@ -21,10 +21,10 @@
"rxjs": "^6.5.5 || ^7.x"
},
"dependencies": {
- "@sentry/browser": "8.25.0",
- "@sentry/core": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0",
+ "@sentry/browser": "8.26.0",
+ "@sentry/core": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0",
"tslib": "^2.4.1"
},
"devDependencies": {
diff --git a/packages/astro/package.json b/packages/astro/package.json
index d857fd048877..b5fd94e25d3a 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry/astro",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Official Sentry SDK for Astro",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/astro",
@@ -56,11 +56,11 @@
"astro": ">=3.x || >=4.0.0-beta"
},
"dependencies": {
- "@sentry/browser": "8.25.0",
- "@sentry/core": "8.25.0",
- "@sentry/node": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0",
+ "@sentry/browser": "8.26.0",
+ "@sentry/core": "8.26.0",
+ "@sentry/node": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0",
"@sentry/vite-plugin": "^2.20.1"
},
"devDependencies": {
diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts
index 3752bd30d448..95d099ff0526 100644
--- a/packages/astro/src/server/middleware.ts
+++ b/packages/astro/src/server/middleware.ts
@@ -147,7 +147,7 @@ async function instrumentRequest(
async span => {
const originalResponse = await next();
- if (span && originalResponse.status) {
+ if (originalResponse.status) {
setHttpStatus(span, originalResponse.status);
}
diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json
index be6fe9f16c4d..881976a8db34 100644
--- a/packages/aws-serverless/package.json
+++ b/packages/aws-serverless/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry/aws-serverless",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Official Sentry SDK for AWS Lambda and AWS Serverless Environments",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless",
@@ -66,10 +66,10 @@
"dependencies": {
"@opentelemetry/instrumentation-aws-lambda": "0.43.0",
"@opentelemetry/instrumentation-aws-sdk": "0.43.1",
- "@sentry/core": "8.25.0",
- "@sentry/node": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0",
+ "@sentry/core": "8.26.0",
+ "@sentry/node": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0",
"@types/aws-lambda": "^8.10.62"
},
"devDependencies": {
diff --git a/packages/browser-utils/package.json b/packages/browser-utils/package.json
index 8fc2497527a2..235032606e5d 100644
--- a/packages/browser-utils/package.json
+++ b/packages/browser-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/browser-utils",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Browser Utilities for all Sentry JavaScript SDKs",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser-utils",
@@ -39,9 +39,9 @@
"access": "public"
},
"dependencies": {
- "@sentry/core": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0"
+ "@sentry/core": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0"
},
"scripts": {
"build": "run-p build:transpile build:types",
diff --git a/packages/browser/package.json b/packages/browser/package.json
index 2131cefd692c..07322e621598 100644
--- a/packages/browser/package.json
+++ b/packages/browser/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry/browser",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Official Sentry SDK for browsers",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser",
@@ -39,16 +39,16 @@
"access": "public"
},
"dependencies": {
- "@sentry-internal/browser-utils": "8.25.0",
- "@sentry-internal/feedback": "8.25.0",
- "@sentry-internal/replay": "8.25.0",
- "@sentry-internal/replay-canvas": "8.25.0",
- "@sentry/core": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0"
+ "@sentry-internal/browser-utils": "8.26.0",
+ "@sentry-internal/feedback": "8.26.0",
+ "@sentry-internal/replay": "8.26.0",
+ "@sentry-internal/replay-canvas": "8.26.0",
+ "@sentry/core": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0"
},
"devDependencies": {
- "@sentry-internal/integration-shims": "8.25.0",
+ "@sentry-internal/integration-shims": "8.26.0",
"fake-indexeddb": "^4.0.1"
},
"scripts": {
diff --git a/packages/browser/src/utils/lazyLoadIntegration.ts b/packages/browser/src/utils/lazyLoadIntegration.ts
index b023bc18aa95..168d1fd1013b 100644
--- a/packages/browser/src/utils/lazyLoadIntegration.ts
+++ b/packages/browser/src/utils/lazyLoadIntegration.ts
@@ -31,7 +31,10 @@ const WindowWithMaybeIntegration = WINDOW as {
* Lazy load an integration from the CDN.
* Rejects if the integration cannot be loaded.
*/
-export async function lazyLoadIntegration(name: keyof typeof LazyLoadableIntegrations): Promise {
+export async function lazyLoadIntegration(
+ name: keyof typeof LazyLoadableIntegrations,
+ scriptNonce?: string,
+): Promise {
const bundle = LazyLoadableIntegrations[name];
// `window.Sentry` is only set when using a CDN bundle, but this method can also be used via the NPM package
@@ -56,6 +59,10 @@ export async function lazyLoadIntegration(name: keyof typeof LazyLoadableIntegra
script.crossOrigin = 'anonymous';
script.referrerPolicy = 'origin';
+ if (scriptNonce) {
+ script.setAttribute('nonce', scriptNonce);
+ }
+
const waitForLoad = new Promise((resolve, reject) => {
script.addEventListener('load', () => resolve());
script.addEventListener('error', reject);
diff --git a/packages/bun/package.json b/packages/bun/package.json
index 71fcf3c36310..fe0c181bf70a 100644
--- a/packages/bun/package.json
+++ b/packages/bun/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry/bun",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Official Sentry SDK for bun",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/bun",
@@ -39,11 +39,11 @@
"access": "public"
},
"dependencies": {
- "@sentry/core": "8.25.0",
- "@sentry/node": "8.25.0",
- "@sentry/opentelemetry": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0"
+ "@sentry/core": "8.26.0",
+ "@sentry/node": "8.26.0",
+ "@sentry/opentelemetry": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0"
},
"devDependencies": {
"bun-types": "latest"
diff --git a/packages/cloudflare/README.md b/packages/cloudflare/README.md
index f7de52a56e88..398153563f1c 100644
--- a/packages/cloudflare/README.md
+++ b/packages/cloudflare/README.md
@@ -114,16 +114,16 @@ Currently only ESM handlers are supported.
import * as Sentry from '@sentry/cloudflare';
export default withSentry(
- (env) => ({
- dsn: env.SENTRY_DSN,
+ env => ({
+ dsn: env.SENTRY_DSN,
// Set tracesSampleRate to 1.0 to capture 100% of spans for tracing.
- tracesSampleRate: 1.0,
- }),
- {
- async fetch(request, env, ctx) {
- return new Response('Hello World!');
- },
- } satisfies ExportedHandler
+ tracesSampleRate: 1.0,
+ }),
+ {
+ async fetch(request, env, ctx) {
+ return new Response('Hello World!');
+ },
+ } satisfies ExportedHandler,
);
```
diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json
index 43803985c7bb..fd9d9bcffe36 100644
--- a/packages/cloudflare/package.json
+++ b/packages/cloudflare/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry/cloudflare",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Offical Sentry SDK for Cloudflare Workers and Pages",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/cloudflare",
@@ -39,9 +39,9 @@
"access": "public"
},
"dependencies": {
- "@sentry/core": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0"
+ "@sentry/core": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0"
},
"optionalDependencies": {
"@cloudflare/workers-types": "^4.x"
diff --git a/packages/core/package.json b/packages/core/package.json
index e37131d2c479..8f140bfbb09c 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry/core",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Base implementation for all Sentry JavaScript SDKs",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core",
@@ -39,8 +39,8 @@
"access": "public"
},
"dependencies": {
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0"
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0"
},
"scripts": {
"build": "run-p build:transpile build:types",
diff --git a/packages/deno/package.json b/packages/deno/package.json
index 2c70255bff45..4178c41cff65 100644
--- a/packages/deno/package.json
+++ b/packages/deno/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry/deno",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Official Sentry SDK for Deno",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/deno",
@@ -24,9 +24,9 @@
"/build"
],
"dependencies": {
- "@sentry/core": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0"
+ "@sentry/core": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0"
},
"devDependencies": {
"@rollup/plugin-typescript": "^11.1.5",
diff --git a/packages/deno/src/client.ts b/packages/deno/src/client.ts
index 1db45a5cb960..6f4e37f1ed74 100644
--- a/packages/deno/src/client.ts
+++ b/packages/deno/src/client.ts
@@ -4,6 +4,11 @@ import { SDK_VERSION, ServerRuntimeClient } from '@sentry/core';
import type { DenoClientOptions } from './types';
function getHostName(): string | undefined {
+ // Deno.permissions.querySync is not available on Deno Deploy
+ if (!Deno.permissions.querySync) {
+ return undefined;
+ }
+
const result = Deno.permissions.querySync({ name: 'sys', kind: 'hostname' });
return result.state === 'granted' ? Deno.hostname() : undefined;
}
diff --git a/packages/deno/src/integrations/context.ts b/packages/deno/src/integrations/context.ts
index 69ef164bb32d..926fcd065f1b 100644
--- a/packages/deno/src/integrations/context.ts
+++ b/packages/deno/src/integrations/context.ts
@@ -16,8 +16,8 @@ function getOSName(): string {
}
}
-function getOSRelease(): string | undefined {
- return Deno.permissions.querySync({ name: 'sys', kind: 'osRelease' }).state === 'granted'
+async function getOSRelease(): Promise {
+ return (await Deno.permissions.query({ name: 'sys', kind: 'osRelease' })).state === 'granted'
? Deno.osRelease()
: undefined;
}
@@ -35,7 +35,7 @@ async function addDenoRuntimeContext(event: Event): Promise {
},
os: {
name: getOSName(),
- version: getOSRelease(),
+ version: await getOSRelease(),
},
v8: {
name: 'v8',
diff --git a/packages/deno/src/integrations/normalizepaths.ts b/packages/deno/src/integrations/normalizepaths.ts
index f984d5bebc33..07e0f872338b 100644
--- a/packages/deno/src/integrations/normalizepaths.ts
+++ b/packages/deno/src/integrations/normalizepaths.ts
@@ -42,6 +42,11 @@ function appRootFromErrorStack(error: Error): string | undefined {
}
function getCwd(): string | undefined {
+ // Deno.permissions.querySync is not available on Deno Deploy
+ if (!Deno.permissions.querySync) {
+ return undefined;
+ }
+
// We don't want to prompt for permissions so we only get the cwd if
// permissions are already granted
const permission = Deno.permissions.querySync({ name: 'read', path: './' });
diff --git a/packages/deno/src/transports/index.ts b/packages/deno/src/transports/index.ts
index 964c1a9347af..c678688c2462 100644
--- a/packages/deno/src/transports/index.ts
+++ b/packages/deno/src/transports/index.ts
@@ -1,6 +1,6 @@
import { createTransport } from '@sentry/core';
import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types';
-import { consoleSandbox, rejectedSyncPromise } from '@sentry/utils';
+import { consoleSandbox, logger, rejectedSyncPromise } from '@sentry/utils';
export interface DenoTransportOptions extends BaseTransportOptions {
/** Custom headers for the transport. Used by the XHRTransport and FetchTransport */
@@ -13,13 +13,20 @@ export interface DenoTransportOptions extends BaseTransportOptions {
export function makeFetchTransport(options: DenoTransportOptions): Transport {
const url = new URL(options.url);
- if (Deno.permissions.querySync({ name: 'net', host: url.host }).state !== 'granted') {
- consoleSandbox(() => {
- // eslint-disable-next-line no-console
- console.warn(`Sentry SDK requires 'net' permission to send events.
- Run with '--allow-net=${url.host}' to grant the requires permissions.`);
+ Deno.permissions
+ .query({ name: 'net', host: url.host })
+ .then(({ state }) => {
+ if (state !== 'granted') {
+ consoleSandbox(() => {
+ // eslint-disable-next-line no-console
+ console.warn(`Sentry SDK requires 'net' permission to send events.
+ Run with '--allow-net=${url.host}' to grant the requires permissions.`);
+ });
+ }
+ })
+ .catch(() => {
+ logger.warn('Failed to read the "net" permission.');
});
- }
function makeRequest(request: TransportRequest): PromiseLike {
const requestOptions: RequestInit = {
diff --git a/packages/ember/package.json b/packages/ember/package.json
index 2682d65f9244..046d187d8da3 100644
--- a/packages/ember/package.json
+++ b/packages/ember/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry/ember",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Official Sentry SDK for Ember.js",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember",
@@ -33,10 +33,10 @@
"dependencies": {
"@babel/core": "^7.24.4",
"@embroider/macros": "^1.16.0",
- "@sentry/browser": "8.25.0",
- "@sentry/core": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0",
+ "@sentry/browser": "8.26.0",
+ "@sentry/core": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0",
"ember-auto-import": "^2.7.2",
"ember-cli-babel": "^8.2.0",
"ember-cli-htmlbars": "^6.1.1",
diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json
index e2630e1038c3..f7d26ab13625 100644
--- a/packages/eslint-config-sdk/package.json
+++ b/packages/eslint-config-sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/eslint-config-sdk",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Official Sentry SDK eslint config",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk",
@@ -22,8 +22,8 @@
"access": "public"
},
"dependencies": {
- "@sentry-internal/eslint-plugin-sdk": "8.25.0",
- "@sentry-internal/typescript": "8.25.0",
+ "@sentry-internal/eslint-plugin-sdk": "8.26.0",
+ "@sentry-internal/typescript": "8.26.0",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
"eslint-config-prettier": "^6.11.0",
diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json
index b0f74252abf8..6f01f96eee3e 100644
--- a/packages/eslint-plugin-sdk/package.json
+++ b/packages/eslint-plugin-sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/eslint-plugin-sdk",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Official Sentry SDK eslint plugin",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk",
diff --git a/packages/feedback/package.json b/packages/feedback/package.json
index def2f283dc81..ff4c10fcad8a 100644
--- a/packages/feedback/package.json
+++ b/packages/feedback/package.json
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/feedback",
- "version": "8.25.0",
+ "version": "8.26.0",
"description": "Sentry SDK integration for user feedback",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/feedback",
@@ -39,9 +39,9 @@
"access": "public"
},
"dependencies": {
- "@sentry/core": "8.25.0",
- "@sentry/types": "8.25.0",
- "@sentry/utils": "8.25.0"
+ "@sentry/core": "8.26.0",
+ "@sentry/types": "8.26.0",
+ "@sentry/utils": "8.26.0"
},
"devDependencies": {
"preact": "^10.19.4"
diff --git a/packages/feedback/src/core/components/Actor.css.ts b/packages/feedback/src/core/components/Actor.css.ts
index 60ae7cebd08e..8a8b2c4efa29 100644
--- a/packages/feedback/src/core/components/Actor.css.ts
+++ b/packages/feedback/src/core/components/Actor.css.ts
@@ -3,7 +3,7 @@ import { DOCUMENT } from '../../constants';
/**
* Creates