Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1574f38
wip
simo6529 Apr 30, 2026
a77d74b
wip
simo6529 Apr 30, 2026
5e4e902
wip
simo6529 Apr 30, 2026
5816b48
wip
simo6529 Apr 30, 2026
4df1ea1
wip
simo6529 May 1, 2026
efc548d
wip
simo6529 May 1, 2026
c2fa4b8
wip
simo6529 May 1, 2026
066aa12
wip
simo6529 May 1, 2026
9b2cf5a
Merge branch 'main' into netwrok-request-failed-300426
simo6529 May 2, 2026
40ad363
wip
simo6529 May 4, 2026
f61b182
wip
simo6529 May 4, 2026
fdc39cc
wip
simo6529 May 4, 2026
b717bf0
wip
simo6529 May 4, 2026
9d89b15
wip
simo6529 May 4, 2026
6f7d9e4
wip
simo6529 May 4, 2026
873ab30
wip
simo6529 May 4, 2026
43e5ff0
wip
simo6529 May 4, 2026
252fbdd
wip
simo6529 May 4, 2026
5c66f93
wip
simo6529 May 4, 2026
3973a15
wip
simo6529 May 4, 2026
3d5f466
wip
simo6529 May 4, 2026
b57b5d8
wip
simo6529 May 5, 2026
66a0096
wip
simo6529 May 5, 2026
b56e529
Merge branch 'main' into netwrok-request-failed-300426
simo6529 May 5, 2026
64a304a
wip
simo6529 May 5, 2026
efda086
wip
simo6529 May 5, 2026
a98d42d
Merge branch 'main' into netwrok-request-failed-300426
simo6529 May 5, 2026
2f40da2
wip
simo6529 May 5, 2026
84b5383
wip
simo6529 May 5, 2026
7c24175
wip
simo6529 May 5, 2026
9a4c57f
wip
simo6529 May 5, 2026
a9fb0da
wip
simo6529 May 5, 2026
d2588e6
wip
simo6529 May 5, 2026
0993984
wip
simo6529 May 6, 2026
e1e6e2c
wip
simo6529 May 6, 2026
98ca9d5
wip
simo6529 May 6, 2026
5d46b8e
wip
simo6529 May 6, 2026
6b4e74e
wip
simo6529 May 6, 2026
f8704ab
wip
simo6529 May 6, 2026
6578bfa
wip
simo6529 May 6, 2026
c610fd2
Merge branch 'main' into netwrok-request-failed-300426
simo6529 May 7, 2026
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
300 changes: 298 additions & 2 deletions __tests__/instrumentation-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,44 @@ jest.mock("@sentry/nextjs", () => ({
captureRouterTransitionStart: mockCaptureRouterTransitionStart,
}));

describe("instrumentation-client beforeSendTransaction", () => {
const loadBeforeSendTransaction = () => {
describe("instrumentation-client", () => {
const wrappedNetworkMessage =
"Network request failed. Please check your connection and try again. (/api/waves-overview)";

type BeforeSendResult = {
tags?: Record<string, unknown> | undefined;
fingerprint?: string[] | undefined;
exception?:
| {
values?: Array<{ value?: string | undefined } | undefined>;
}
| undefined;
message?: string | undefined;
} | null;

const loadSentryConfig = () => {
jest.isolateModules(() => {
require("@/instrumentation-client");
});

const config = mockInit.mock.calls[0]?.[0];
expect(config).toBeDefined();

return config;
};

const loadBeforeSend = () => {
const config = loadSentryConfig();
expect(typeof config.beforeSend).toBe("function");

return config.beforeSend as (
event: Record<string, unknown>,
hint?: Record<string, unknown>
) => BeforeSendResult;
};

const loadBeforeSendTransaction = () => {
const config = loadSentryConfig();
expect(typeof config.beforeSendTransaction).toBe("function");

return config.beforeSendTransaction as (event: Record<string, unknown>) => {
Expand All @@ -37,6 +67,272 @@ describe("instrumentation-client beforeSendTransaction", () => {
mockCaptureRouterTransitionStart.mockReset();
});

it("drops sampled-out first-party browser transport network errors", () => {
const beforeSend = loadBeforeSend();
const event = {
event_id: "network-drop-event",
exception: {
values: [
{
type: "TypeError",
value: "Load failed",
},
],
},
breadcrumbs: [
{
type: "http",
category: "fetch",
data: {
status_code: 0,
url: "/api/waves-overview",
"url.is_first_party": true,
},
},
],
};

const result = beforeSend(event, {
originalException: new TypeError("Load failed"),
});

expect(result).toBeNull();
});

it("uses the latest failed fetch breadcrumb for raw browser network errors", () => {
const beforeSend = loadBeforeSend();
const event = {
event_id: "network-drop-event",
exception: {
values: [
{
type: "TypeError",
value: "Load failed",
},
],
},
breadcrumbs: [
{
type: "http",
category: "fetch",
data: {
status_code: 0,
url: "/api/a",
"url.is_first_party": true,
},
},
{
type: "http",
category: "fetch",
data: {
status_code: 0,
url: "/api/b",
"url.is_first_party": true,
},
},
],
};

const result = beforeSend(event, {
originalException: new TypeError("Load failed"),
});

expect(result).toBeNull();
expect(event.exception.values[0]?.value).toBe(
"Network request failed. Please check your connection and try again. (/api/b)"
);
});

it("keeps and tags sampled-in first-party browser transport network errors", () => {
const beforeSend = loadBeforeSend();
const event = {
event_id: "event-200",
exception: {
values: [
{
type: "TypeError",
value: "Load failed",
},
],
},
breadcrumbs: [
{
type: "http",
category: "fetch",
data: {
status_code: 0,
url: "/api/waves-overview",
"url.is_first_party": true,
},
},
],
};

const result = beforeSend(event, {
originalException: new TypeError("Load failed"),
});

expect(result).not.toBeNull();
expect(result?.tags).toEqual(
expect.objectContaining({
errorType: "network",
handled: true,
network_failure_kind: "browser_transport",
network_noise_sampled: "true",
})
);
expect(result?.fingerprint).toEqual(["network-error"]);
});

it("drops sampled-out app-wrapped first-party browser transport network errors", () => {
const beforeSend = loadBeforeSend();
const event = {
event_id: "network-drop-event",
exception: {
values: [
{
type: "Error",
value: wrappedNetworkMessage,
},
],
},
breadcrumbs: [
{
type: "http",
category: "fetch",
data: {
status_code: 0,
url: "/api/waves-overview",
"url.is_first_party": true,
},
},
],
};

const result = beforeSend(event, {
originalException: new Error(wrappedNetworkMessage),
});

expect(result).toBeNull();
});

it("keeps and tags sampled-in app-wrapped first-party browser transport network errors without rewriting the message", () => {
const beforeSend = loadBeforeSend();
const event = {
event_id: "event-200",
message: wrappedNetworkMessage,
fingerprint: ["drop-reaction", "network"],
exception: {
values: [
{
type: "Error",
value: wrappedNetworkMessage,
},
],
},
breadcrumbs: [
{
type: "http",
category: "fetch",
data: {
status_code: 0,
url: "/api/waves-overview",
"url.is_first_party": true,
},
},
],
};

const result = beforeSend(event, {
originalException: new Error(wrappedNetworkMessage),
});

expect(result).not.toBeNull();
expect(result?.tags).toEqual(
expect.objectContaining({
errorType: "network",
handled: true,
network_failure_kind: "browser_transport",
network_noise_sampled: "true",
})
);
expect(result?.exception?.values?.[0]?.value).toBe(wrappedNetworkMessage);
expect(result?.message).toBe(wrappedNetworkMessage);
expect(result?.fingerprint).toEqual(["drop-reaction", "network"]);
});

it("does not tag unrelated plain errors that mention network", () => {
const beforeSend = loadBeforeSend();
const event = {
event_id: "event-200",
exception: {
values: [
{
type: "Error",
value: "network switch failed",
},
],
},
breadcrumbs: [
{
type: "http",
category: "fetch",
data: {
status_code: 0,
url: "/api/waves-overview",
"url.is_first_party": true,
},
},
],
};

const result = beforeSend(event, {
originalException: new Error("network switch failed"),
});

expect(result).not.toBeNull();
expect(result?.tags?.["errorType"]).toBeUndefined();
});

it("keeps browser network errors with a real HTTP status", () => {
const beforeSend = loadBeforeSend();
const event = {
event_id: "network-drop-event",
exception: {
values: [
{
type: "TypeError",
value: "Load failed",
},
],
},
breadcrumbs: [
{
type: "http",
category: "fetch",
data: {
status_code: 500,
url: "/api/waves-overview",
"url.is_first_party": true,
},
},
],
};

const result = beforeSend(event, {
originalException: new TypeError("Load failed"),
});

expect(result).not.toBeNull();
expect(result?.tags).toEqual(
expect.objectContaining({
errorType: "network",
handled: true,
})
);
expect(result?.tags?.["network_noise_sampled"]).toBeUndefined();
});

it("removes only the known noisy third-party telemetry spans", () => {
const beforeSendTransaction = loadBeforeSendTransaction();
const event = {
Expand Down
Loading
Loading