mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
By accident, the responses for server actions that fetch something and don't revalidate are currently not streamed to the browser until the `fetch` calls are fully resolved. In the simplest case this happens when a server action returns the response body of a `fetch` call. This is especially critical if that response is itself streaming, for example for a chat app. In that case the client didn't see any updates until the whole stream was resolved. It affected mostly dev mode (due to the HMR fetch cache), but also prod if the fetch was explicitly marked as cacheable. The cause of the issue is that we are awaiting `pendingRevalidates` (in which the cache-writing promise is stored) after executing the action and before rendering. However, when we skip rendering and return the action directly this is unnecessary and blocks the response. Instead, we can defer awaiting the `pendingRevalidates` using `waitUntil`, as we do after rendering. This is executed at the very end, before the response is closed.
44 lines
1.2 KiB
TypeScript
44 lines
1.2 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import { retry, waitFor } from 'next-test-utils'
|
|
|
|
describe('actions-streaming', () => {
|
|
const { next } = nextTestSetup({
|
|
files: __dirname,
|
|
})
|
|
|
|
describe('actions returning a ReadableStream', () => {
|
|
it('should properly stream the response without buffering', async () => {
|
|
const browser = await next.browser('/readable-stream')
|
|
await browser.elementById('stream-button').click()
|
|
|
|
expect(await browser.elementById('stream-button').text()).toBe(
|
|
'Streaming...'
|
|
)
|
|
|
|
// If we're streaming properly, we should see the first chunks arrive
|
|
// quickly.
|
|
expect(await browser.elementByCss('h3').text()).toMatch(
|
|
/Received \d+ chunks/
|
|
)
|
|
expect(await browser.elementById('chunks').text()).toInclude(
|
|
'Lorem ipsum dolor sit'
|
|
)
|
|
|
|
// Finally, wait for the response to finish streaming.
|
|
await waitFor(5000)
|
|
await retry(
|
|
async () => {
|
|
expect(await browser.elementByCss('h3').text()).toBe(
|
|
'Received 50 chunks'
|
|
)
|
|
expect(await browser.elementById('stream-button').text()).toBe(
|
|
'Start Stream'
|
|
)
|
|
},
|
|
10000,
|
|
1000
|
|
)
|
|
})
|
|
})
|
|
})
|