diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/server/app.js b/dev-packages/e2e-tests/test-applications/react-router-6/server/app.js
index d0ece10b907b..5a8cdb3929a1 100644
--- a/dev-packages/e2e-tests/test-applications/react-router-6/server/app.js
+++ b/dev-packages/e2e-tests/test-applications/react-router-6/server/app.js
@@ -11,7 +11,7 @@ const wait = time => {
});
};
-async function eventsHandler(request, response) {
+async function sseHandler(request, response, timeout = false) {
response.headers = {
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
@@ -30,12 +30,17 @@ async function eventsHandler(request, response) {
for (let index = 0; index < 10; index++) {
response.write(`data: ${new Date().toISOString()}\n\n`);
+ if (timeout) {
+ await wait(10000);
+ }
}
response.end();
}
-app.get('/sse', eventsHandler);
+app.get('/sse', (req, res) => sseHandler(req, res));
+
+app.get('/sse-timeout', (req, res) => sseHandler(req, res, true));
app.listen(PORT, () => {
console.log(`SSE service listening at http://localhost:${PORT}`);
diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/SSE.tsx b/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/SSE.tsx
index 877d9f4298c8..49e53b09cfa2 100644
--- a/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/SSE.tsx
+++ b/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/SSE.tsx
@@ -2,10 +2,11 @@ import * as Sentry from '@sentry/react';
// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX
import * as React from 'react';
-const fetchSSE = async () => {
+const fetchSSE = async ({ timeout }: { timeout: boolean }) => {
Sentry.startSpanManual({ name: 'sse stream using fetch' }, async span => {
const res = await Sentry.startSpan({ name: 'sse fetch call' }, async () => {
- return await fetch('http://localhost:8080/sse');
+ const endpoint = `http://localhost:8080/${timeout ? 'sse-timeout' : 'sse'}`;
+ return await fetch(endpoint);
});
const stream = res.body;
@@ -34,9 +35,14 @@ const fetchSSE = async () => {
const SSE = () => {
return (
-
+ <>
+
+
+ >
);
};
diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/tests/sse.test.ts b/dev-packages/e2e-tests/test-applications/react-router-6/tests/sse.test.ts
index a27db8264d6d..44612566051c 100644
--- a/dev-packages/e2e-tests/test-applications/react-router-6/tests/sse.test.ts
+++ b/dev-packages/e2e-tests/test-applications/react-router-6/tests/sse.test.ts
@@ -33,3 +33,37 @@ test('Waits for sse streaming when creating spans', async ({ page }) => {
expect(resolveDuration).toBe(0);
expect(resolveBodyDuration).toBe(2);
});
+
+test('Aborts when stream takes longer than 5s', async ({ page }) => {
+ await page.goto('/sse');
+
+ const transactionPromise = waitForTransaction('react-router-6', async transactionEvent => {
+ return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
+ });
+
+ const fetchButton = page.locator('id=fetch-timeout-button');
+ await fetchButton.click();
+
+ const rootSpan = await transactionPromise;
+ const sseFetchCall = rootSpan.spans?.filter(span => span.description === 'sse fetch call')[0] as SpanJSON;
+ const httpGet = rootSpan.spans?.filter(
+ span => span.description === 'GET http://localhost:8080/sse-timeout',
+ )[0] as SpanJSON;
+
+ expect(sseFetchCall).not.toBeUndefined();
+ expect(httpGet).not.toBeUndefined();
+
+ expect(sseFetchCall?.timestamp).not.toBeUndefined();
+ expect(sseFetchCall?.start_timestamp).not.toBeUndefined();
+ expect(httpGet?.timestamp).not.toBeUndefined();
+ expect(httpGet?.start_timestamp).not.toBeUndefined();
+
+ // http headers get sent instantly from the server
+ const resolveDuration = Math.round((sseFetchCall.timestamp as number) - sseFetchCall.start_timestamp);
+
+ // body streams after 10s but client should abort reading after 5s
+ const resolveBodyDuration = Math.round((httpGet.timestamp as number) - httpGet.start_timestamp);
+
+ expect(resolveDuration).toBe(0);
+ expect(resolveBodyDuration).toBe(7);
+});