mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
[Flakiness metric](https://app.datadoghq.com/ci/test/runs?query=test_level%3Atest%20%40git.repository.id%3A%22github.com%2Fvercel%2Fnext.js%22%20%40test.name%3A%22server-components-hmr-cache%20should%20support%20reading%20from%20an%20infinite%20streaming%20fetch%22%20%40test.status%3Afail%20%40duration%3A%3E%3D1ns&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&index=citest&start=1773310422894&end=1773915222894&paused=false) This test times out frequently on slow CI machines. To fix this, we're removing the low custom 10 second test timeout (using the default 60 second timeout instead).
238 lines
8.6 KiB
TypeScript
238 lines
8.6 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import { retry } from 'next-test-utils'
|
|
|
|
const isCacheComponentsEnabled = process.env.__NEXT_CACHE_COMPONENTS === 'true'
|
|
|
|
describe('server-components-hmr-cache', () => {
|
|
const { next } = nextTestSetup({ files: __dirname, patchFileDelay: 1000 })
|
|
const loggedAfterValueRegexp = /After: (\d\.\d+)/
|
|
let cliOutputLength: number
|
|
|
|
const getLoggedAfterValue = () => {
|
|
const match = next.cliOutput
|
|
.slice(cliOutputLength)
|
|
.match(loggedAfterValueRegexp)
|
|
|
|
if (!match) {
|
|
throw new Error('No logs from after() found')
|
|
}
|
|
return match[1]
|
|
}
|
|
|
|
// Edge runtime is not supported with Cache Components.
|
|
const runtimes = isCacheComponentsEnabled ? ['node'] : ['edge', 'node']
|
|
|
|
beforeEach(() => {
|
|
cliOutputLength = next.cliOutput.length
|
|
})
|
|
|
|
describe.each(runtimes)('%s runtime', (runtime) => {
|
|
it('should use cached fetch calls for fast refresh requests', async () => {
|
|
const browser = await next.browser(`/${runtime}`)
|
|
const valueBeforePatch = await browser.elementById('value').text()
|
|
|
|
await next.patchFile(
|
|
'components/shared-page.tsx',
|
|
(content) => content.replace('foo', 'bar'),
|
|
async () => {
|
|
await retry(async () => {
|
|
const updatedContent = await browser.elementById('content').text()
|
|
expect(updatedContent).toBe('bar')
|
|
// TODO: remove custom duration in case we increase the default.
|
|
}, 5000)
|
|
|
|
const valueAfterPatch = await browser.elementById('value').text()
|
|
expect(valueBeforePatch).toEqual(valueAfterPatch)
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should not use cached fetch calls for intentional refresh requests', async () => {
|
|
const browser = await next.browser(`/${runtime}`)
|
|
const valueBeforeRefresh = await browser.elementById('value').text()
|
|
await browser.elementByCss(`button`).click().waitForIdleNetwork()
|
|
|
|
await retry(async () => {
|
|
const valueAfterRefresh = await browser.elementById('value').text()
|
|
expect(valueBeforeRefresh).not.toEqual(valueAfterRefresh)
|
|
// TODO: remove custom duration in case we increase the default.
|
|
}, 5000)
|
|
})
|
|
|
|
describe('in after()', () => {
|
|
it('should use cached fetch calls for fast refresh requests', async () => {
|
|
const browser = await next.browser(`/${runtime}`)
|
|
const valueBeforePatch = await retry(() => getLoggedAfterValue())
|
|
cliOutputLength = next.cliOutput.length
|
|
|
|
await next.patchFile(
|
|
'components/shared-page.tsx',
|
|
(content) => content.replace('foo', 'bar'),
|
|
async () => {
|
|
await retry(async () => {
|
|
const updatedContent = await browser.elementById('content').text()
|
|
expect(updatedContent).toBe('bar')
|
|
// TODO: remove custom duration in case we increase the default.
|
|
}, 5000)
|
|
|
|
const valueAfterPatch = await retry(() => getLoggedAfterValue())
|
|
expect(valueBeforePatch).toEqual(valueAfterPatch)
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should not use cached fetch calls for intentional refresh requests', async () => {
|
|
const browser = await next.browser(`/${runtime}`)
|
|
const valueBeforeRefresh = await retry(() => getLoggedAfterValue())
|
|
cliOutputLength = next.cliOutput.length
|
|
|
|
await browser.elementByCss(`button`).click().waitForIdleNetwork()
|
|
|
|
await retry(async () => {
|
|
const valueAfterRefresh = getLoggedAfterValue()
|
|
expect(valueBeforeRefresh).not.toEqual(valueAfterRefresh)
|
|
// TODO: remove custom duration in case we increase the default.
|
|
}, 5000)
|
|
})
|
|
})
|
|
|
|
describe('with cacheMaxMemorySize set to 0', () => {
|
|
beforeAll(async () => {
|
|
await next.patchFile('next.config.js', (content) =>
|
|
content.replace('// cacheMaxMemorySize: 0,', 'cacheMaxMemorySize: 0,')
|
|
)
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await next.patchFile('next.config.js', (content) =>
|
|
content.replace('cacheMaxMemorySize: 0,', '// cacheMaxMemorySize: 0,')
|
|
)
|
|
})
|
|
|
|
it('should not warn about "Single item size exceeds maxSize"', async () => {
|
|
const initialOutputLength = next.cliOutput.length
|
|
const browser = await next.browser(`/${runtime}`)
|
|
await browser.elementById('value').text()
|
|
|
|
await next.patchFile(
|
|
'components/shared-page.tsx',
|
|
(content) => content.replace('foo', 'bar'),
|
|
async () => {
|
|
await retry(async () => {
|
|
const updatedContent = await browser.elementById('content').text()
|
|
expect(updatedContent).toBe('bar')
|
|
}, 5000)
|
|
|
|
// Verify the warning does not appear
|
|
const newOutput = next.cliOutput.slice(initialOutputLength)
|
|
expect(newOutput).not.toContain('Single item size exceeds maxSize')
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should still use cached fetch calls for fast refresh requests', async () => {
|
|
const browser = await next.browser(`/${runtime}`)
|
|
const valueBeforePatch = await browser.elementById('value').text()
|
|
|
|
await next.patchFile(
|
|
'components/shared-page.tsx',
|
|
(content) => content.replace('foo', 'bar'),
|
|
async () => {
|
|
await retry(async () => {
|
|
const updatedContent = await browser.elementById('content').text()
|
|
expect(updatedContent).toBe('bar')
|
|
}, 5000)
|
|
|
|
// HMR cache should still work even with cacheMaxMemorySize: 0
|
|
const valueAfterPatch = await browser.elementById('value').text()
|
|
expect(valueBeforePatch).toEqual(valueAfterPatch)
|
|
}
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('with experimental.serverComponentsHmrCache disabled', () => {
|
|
beforeAll(async () => {
|
|
// Wait for server to be ready
|
|
await next.fetch('/404')
|
|
await next.patchFile('next.config.js', (content) =>
|
|
content.replace(
|
|
'// serverComponentsHmrCache: false,',
|
|
'serverComponentsHmrCache: false,'
|
|
)
|
|
)
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await next.patchFile('next.config.js', (content) =>
|
|
content.replace(
|
|
'serverComponentsHmrCache: false,',
|
|
'// serverComponentsHmrCache: false,'
|
|
)
|
|
)
|
|
})
|
|
|
|
it('should not use cached fetch calls for fast refresh requests', async () => {
|
|
const browser = await next.browser(`/${runtime}`)
|
|
const valueBeforePatch = await browser.elementById('value').text()
|
|
|
|
await next.patchFile(
|
|
'components/shared-page.tsx',
|
|
(content) => content.replace('foo', 'bar'),
|
|
async () => {
|
|
await retry(async () => {
|
|
const updatedContent = await browser.elementById('content').text()
|
|
expect(updatedContent).toBe('bar')
|
|
// TODO: remove custom duration in case we increase the default.
|
|
}, 5000)
|
|
|
|
const valueAfterPatch = await browser.elementById('value').text()
|
|
expect(valueBeforePatch).not.toEqual(valueAfterPatch)
|
|
}
|
|
)
|
|
})
|
|
|
|
describe('in after()', () => {
|
|
beforeEach(() => {
|
|
cliOutputLength = next.cliOutput.length
|
|
})
|
|
|
|
it('should not use cached fetch calls for fast refresh requests', async () => {
|
|
const browser = await next.browser(`/${runtime}`)
|
|
const valueBeforePatch = await retry(() => getLoggedAfterValue())
|
|
cliOutputLength = next.cliOutput.length
|
|
|
|
await next.patchFile(
|
|
'components/shared-page.tsx',
|
|
(content) => content.replace('foo', 'bar'),
|
|
async () => {
|
|
await retry(async () => {
|
|
const updatedContent = await browser
|
|
.elementById('content')
|
|
.text()
|
|
expect(updatedContent).toBe('bar')
|
|
// TODO: remove custom duration in case we increase the default.
|
|
}, 5000)
|
|
|
|
const valueAfterPatch = await retry(() => getLoggedAfterValue())
|
|
expect(valueBeforePatch).not.toEqual(valueAfterPatch)
|
|
}
|
|
)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should support reading from an infinite streaming fetch', async () => {
|
|
const browser = await next.browser('/infinite-stream')
|
|
const text = await browser.elementByCss('p').text()
|
|
expect(text).toBe('data: chunk-1')
|
|
|
|
// The fetch is aborted after reading the first chunk, which is intentional,
|
|
// so we shouldn't warn about failing to cache it.
|
|
expect(next.cliOutput.slice(cliOutputLength)).not.toContain(
|
|
'Failed to set fetch cache'
|
|
)
|
|
})
|
|
})
|