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); +});