Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
enhanceFetchErrorMessages: false,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Based on possible TypeError exceptions from https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch

// Network error (e.g. ad-blocked, offline, page does not exist, ...)
window.networkError = () => {
fetch('http://sentry-test-external.io/does-not-exist');
};

window.networkErrorSubdomain = () => {
fetch('http://subdomain.sentry-test-external.io/does-not-exist');
};

window.networkErrorWithPort = () => {
fetch('http://sentry-test-external.io:3000/does-not-exist');
};

// Invalid header also produces TypeError
window.invalidHeaderName = () => {
fetch('http://sentry-test-external.io/invalid-header-name', { headers: { 'C ontent-Type': 'text/xml' } });
};

// Invalid header value also produces TypeError
window.invalidHeaderValue = () => {
fetch('http://sentry-test-external.io/invalid-header-value', { headers: ['Content-Type', 'text/html', 'extra'] });
};

// Invalid URL scheme
window.invalidUrlScheme = () => {
fetch('blub://sentry-test-external.io/invalid-scheme');
};

// URL includes credentials
window.credentialsInUrl = () => {
fetch('https://user:password@sentry-test-external.io/credentials-in-url');
};

// Invalid mode
window.invalidMode = () => {
fetch('https://sentry-test-external.io/invalid-mode', { mode: 'navigate' });
};

// Invalid request method
window.invalidMethod = () => {
fetch('http://sentry-test-external.io/invalid-method', { method: 'CONNECT' });
};

// No-cors mode with cors-required method
window.noCorsMethod = () => {
fetch('http://sentry-test-external.io/no-cors-method', { mode: 'no-cors', method: 'PUT' });
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../utils/helpers';

sentryTest(
'enhanceFetchErrorMessages: false: enhances error for Sentry while preserving original @firefox',
async ({ getLocalTestUrl, page, browserName }) => {
const url = await getLocalTestUrl({ testDir: __dirname });
const reqPromise = waitForErrorRequest(page);
const pageErrorPromise = new Promise<string>(resolve => {
page.on('pageerror', error => {
resolve(error.message);
});
});

await page.goto(url);
await page.evaluate('networkError()');

const [req, pageErrorMessage] = await Promise.all([reqPromise, pageErrorPromise]);
const eventData = envelopeRequestParser(req);
const originalErrorMap: Record<string, string> = {
chromium: 'Failed to fetch',
webkit: 'Load failed',
firefox: 'NetworkError when attempting to fetch resource.',
};

const originalError = originalErrorMap[browserName];

expect(pageErrorMessage).toContain(originalError);
expect(pageErrorMessage).not.toContain('sentry-test-external.io');

expect(eventData.exception?.values).toHaveLength(1);
expect(eventData.exception?.values?.[0]).toMatchObject({
type: 'TypeError',
value: originalError,
mechanism: {
handled: false,
type: 'auto.browser.global_handlers.onunhandledrejection',
},
});
},
);

sentryTest(
'enhanceFetchErrorMessages: false: enhances subdomain errors @firefox',
async ({ getLocalTestUrl, page, browserName }) => {
const url = await getLocalTestUrl({ testDir: __dirname });
const reqPromise = waitForErrorRequest(page);
const pageErrorPromise = new Promise<string>(resolve => page.on('pageerror', error => resolve(error.message)));

await page.goto(url);
await page.evaluate('networkErrorSubdomain()');

const [req, pageErrorMessage] = await Promise.all([reqPromise, pageErrorPromise]);
const eventData = envelopeRequestParser(req);

const originalErrorMap: Record<string, string> = {
chromium: 'Failed to fetch',
webkit: 'Load failed',
firefox: 'NetworkError when attempting to fetch resource.',
};

const originalError = originalErrorMap[browserName];

expect(pageErrorMessage).toContain(originalError);
expect(pageErrorMessage).not.toContain('subdomain.sentry-test-external.io');
expect(eventData.exception?.values).toHaveLength(1);
expect(eventData.exception?.values?.[0]).toMatchObject({
type: 'TypeError',
value: originalError,
mechanism: {
handled: false,
type: 'auto.browser.global_handlers.onunhandledrejection',
},
});
},
);

sentryTest(
'enhanceFetchErrorMessages: false: includes port in hostname @firefox',
async ({ getLocalTestUrl, page, browserName }) => {
const url = await getLocalTestUrl({ testDir: __dirname });
const reqPromise = waitForErrorRequest(page);

const pageErrorPromise = new Promise<string>(resolve => page.on('pageerror', error => resolve(error.message)));

await page.goto(url);
await page.evaluate('networkErrorWithPort()');

const [req, pageErrorMessage] = await Promise.all([reqPromise, pageErrorPromise]);
const eventData = envelopeRequestParser(req);

const originalErrorMap: Record<string, string> = {
chromium: 'Failed to fetch',
webkit: 'Load failed',
firefox: 'NetworkError when attempting to fetch resource.',
};

const originalError = originalErrorMap[browserName];

expect(pageErrorMessage).toContain(originalError);
expect(pageErrorMessage).not.toContain('sentry-test-external.io:3000');
expect(eventData.exception?.values).toHaveLength(1);
expect(eventData.exception?.values?.[0]).toMatchObject({
type: 'TypeError',
value: originalError,
mechanism: {
handled: false,
type: 'auto.browser.global_handlers.onunhandledrejection',
},
});
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
enhanceFetchErrorMessages: 'report-only',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Based on possible TypeError exceptions from https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch

// Network error (e.g. ad-blocked, offline, page does not exist, ...)
window.networkError = () => {
fetch('http://sentry-test-external.io/does-not-exist');
};

window.networkErrorSubdomain = () => {
fetch('http://subdomain.sentry-test-external.io/does-not-exist');
};

window.networkErrorWithPort = () => {
fetch('http://sentry-test-external.io:3000/does-not-exist');
};

// Invalid header also produces TypeError
window.invalidHeaderName = () => {
fetch('http://sentry-test-external.io/invalid-header-name', { headers: { 'C ontent-Type': 'text/xml' } });
};

// Invalid header value also produces TypeError
window.invalidHeaderValue = () => {
fetch('http://sentry-test-external.io/invalid-header-value', { headers: ['Content-Type', 'text/html', 'extra'] });
};

// Invalid URL scheme
window.invalidUrlScheme = () => {
fetch('blub://sentry-test-external.io/invalid-scheme');
};

// URL includes credentials
window.credentialsInUrl = () => {
fetch('https://user:password@sentry-test-external.io/credentials-in-url');
};

// Invalid mode
window.invalidMode = () => {
fetch('https://sentry-test-external.io/invalid-mode', { mode: 'navigate' });
};

// Invalid request method
window.invalidMethod = () => {
fetch('http://sentry-test-external.io/invalid-method', { method: 'CONNECT' });
};

// No-cors mode with cors-required method
window.noCorsMethod = () => {
fetch('http://sentry-test-external.io/no-cors-method', { mode: 'no-cors', method: 'PUT' });
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../utils/helpers';

sentryTest(
'enhanceFetchErrorMessages: report-only: enhances error for Sentry while preserving original @firefox',
async ({ getLocalTestUrl, page, browserName }) => {
const url = await getLocalTestUrl({ testDir: __dirname });
const reqPromise = waitForErrorRequest(page);
const pageErrorPromise = new Promise<string>(resolve => page.on('pageerror', error => resolve(error.message)));

await page.goto(url);
await page.evaluate('networkError()');

const [req, pageErrorMessage] = await Promise.all([reqPromise, pageErrorPromise]);
const eventData = envelopeRequestParser(req);
const originalErrorMap: Record<string, string> = {
chromium: 'Failed to fetch',
webkit: 'Load failed',
firefox: 'NetworkError when attempting to fetch resource.',
};

const enhancedErrorMap: Record<string, string> = {
chromium: 'Failed to fetch (sentry-test-external.io)',
webkit: 'Load failed (sentry-test-external.io)',
firefox: 'NetworkError when attempting to fetch resource. (sentry-test-external.io)',
};

const originalError = originalErrorMap[browserName];
const enhancedError = enhancedErrorMap[browserName];

expect(pageErrorMessage).toContain(originalError);
expect(pageErrorMessage).not.toContain('sentry-test-external.io');

// Verify Sentry received the enhanced message
// Note: In report-only mode, the original error message remains unchanged
// at the JavaScript level (for third-party package compatibility),
// but Sentry gets the enhanced version via __sentry_fetch_url_host__
expect(eventData.exception?.values).toHaveLength(1);
expect(eventData.exception?.values?.[0]).toMatchObject({
type: 'TypeError',
value: enhancedError,
mechanism: {
handled: false,
type: 'auto.browser.global_handlers.onunhandledrejection',
},
});
},
);

sentryTest(
'enhanceFetchErrorMessages: report-only: enhances subdomain errors @firefox',
async ({ getLocalTestUrl, page, browserName }) => {
const url = await getLocalTestUrl({ testDir: __dirname });
const reqPromise = waitForErrorRequest(page);
const pageErrorPromise = new Promise<string>(resolve => page.on('pageerror', error => resolve(error.message)));

await page.goto(url);
await page.evaluate('networkErrorSubdomain()');

const [req, pageErrorMessage] = await Promise.all([reqPromise, pageErrorPromise]);
const eventData = envelopeRequestParser(req);

const originalErrorMap: Record<string, string> = {
chromium: 'Failed to fetch',
webkit: 'Load failed',
firefox: 'NetworkError when attempting to fetch resource.',
};

const enhancedErrorMap: Record<string, string> = {
chromium: 'Failed to fetch (subdomain.sentry-test-external.io)',
webkit: 'Load failed (subdomain.sentry-test-external.io)',
firefox: 'NetworkError when attempting to fetch resource. (subdomain.sentry-test-external.io)',
};

const originalError = originalErrorMap[browserName];
const enhancedError = enhancedErrorMap[browserName];

expect(pageErrorMessage).toContain(originalError);
expect(pageErrorMessage).not.toContain('subdomain.sentry-test-external.io');
expect(eventData.exception?.values).toHaveLength(1);
expect(eventData.exception?.values?.[0]).toMatchObject({
type: 'TypeError',
value: enhancedError,
mechanism: {
handled: false,
type: 'auto.browser.global_handlers.onunhandledrejection',
},
});
},
);

sentryTest(
'enhanceFetchErrorMessages: report-only: includes port in hostname @firefox',
async ({ getLocalTestUrl, page, browserName }) => {
const url = await getLocalTestUrl({ testDir: __dirname });
const reqPromise = waitForErrorRequest(page);

const pageErrorPromise = new Promise<string>(resolve => page.on('pageerror', error => resolve(error.message)));

await page.goto(url);
await page.evaluate('networkErrorWithPort()');

const [req, pageErrorMessage] = await Promise.all([reqPromise, pageErrorPromise]);
const eventData = envelopeRequestParser(req);

const originalErrorMap: Record<string, string> = {
chromium: 'Failed to fetch',
webkit: 'Load failed',
firefox: 'NetworkError when attempting to fetch resource.',
};

const enhancedErrorMap: Record<string, string> = {
chromium: 'Failed to fetch (sentry-test-external.io:3000)',
webkit: 'Load failed (sentry-test-external.io:3000)',
firefox: 'NetworkError when attempting to fetch resource. (sentry-test-external.io:3000)',
};

const originalError = originalErrorMap[browserName];
const enhancedError = enhancedErrorMap[browserName];

expect(pageErrorMessage).toContain(originalError);
expect(pageErrorMessage).not.toContain('sentry-test-external.io:3000');
expect(eventData.exception?.values).toHaveLength(1);
expect(eventData.exception?.values?.[0]).toMatchObject({
type: 'TypeError',
value: enhancedError,
mechanism: {
handled: false,
type: 'auto.browser.global_handlers.onunhandledrejection',
},
});
},
);
Loading
Loading