rsnext/test/e2e/streaming-ssr/index.test.ts
Jiachi Liu 60dd6dfc9b
Fix module error for findDOMNode on edge (#43998)
## Bug

The `findDOMNode` will be exported from ReactDOM in esm mode, but it's
not defined for SSR since SSR is using react-dom server stub bundle
which doesn't contain any thing. So `import { findDOMNode } from
'react-dom'` will error in that case with bundling.

Since it's only being used on client, we import ReactDOM and call
`ReactDOM.findDOMNode` to avoid bundling error and adding a condition to
tree-shake it off on client

[slack
thread](https://vercel.slack.com/archives/C03KAR5DCKC/p1670608621289259)

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have a helpful link attached, see
[`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)
2022-12-13 09:16:01 -08:00

210 lines
5.9 KiB
TypeScript

import { join } from 'path'
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import {
check,
fetchViaHTTP,
findPort,
initNextServerScript,
killApp,
renderViaHTTP,
} from 'next-test-utils'
const isNextProd = !(global as any).isNextDev && !(global as any).isNextDeploy
describe('streaming SSR with custom next configs', () => {
let next: NextInstance
beforeAll(async () => {
next = await createNext({
files: {
'app/page.js': `
export default function Page() {
return 'fake-app' /* this should not enable appDir */
}
`,
pages: new FileRef(join(__dirname, 'streaming-ssr/pages')),
},
nextConfig: require(join(__dirname, 'streaming-ssr/next.config.js')),
installCommand: 'npm install',
})
})
afterAll(() => next.destroy())
it('should match more specific route along with dynamic routes', async () => {
const res1 = await fetchViaHTTP(next.url, '/api/user/login')
const res2 = await fetchViaHTTP(next.url, '/api/user/any')
expect(await res1.text()).toBe('login')
expect(await res2.text()).toBe('[id]')
})
it('should render styled-jsx styles in streaming', async () => {
const html = await renderViaHTTP(next.url, '/')
expect(html).toContain('color:blue')
})
it('should redirect paths without trailing-slash and render when slash is appended', async () => {
const page = '/hello'
const redirectRes = await fetchViaHTTP(
next.url,
page,
{},
{ redirect: 'manual' }
)
const res = await fetchViaHTTP(next.url, page + '/')
const html = await res.text()
expect(redirectRes.status).toBe(308)
expect(res.status).toBe(200)
expect(html).toContain('hello nextjs')
expect(html).toContain('home')
})
it('should render next/router correctly in edge runtime', async () => {
const html = await renderViaHTTP(next.url, '/router')
expect(html).toContain('link')
})
it('should render multi-byte characters correctly in streaming', async () => {
const html = await renderViaHTTP(next.url, '/multi-byte')
expect(html).toContain('マルチバイト'.repeat(28))
})
if ((global as any).isNextDev) {
it('should work with custom document', async () => {
await next.patchFile(
'pages/_document.js',
`
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
`
)
await check(async () => {
return await renderViaHTTP(next.url, '/')
}, /index/)
await next.deleteFile('pages/_document.js')
})
}
})
if (isNextProd) {
describe('streaming SSR with custom server', () => {
let next
let server
let appPort
beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'custom-server/pages')),
'server.js': new FileRef(join(__dirname, 'custom-server/server.js')),
},
nextConfig: require(join(__dirname, 'custom-server/next.config.js')),
})
await next.stop()
const testServer = join(next.testDir, 'server.js')
appPort = await findPort()
server = await initNextServerScript(
testServer,
/Listening/,
{
...process.env,
PORT: appPort,
},
undefined,
{
cwd: next.testDir,
}
)
})
afterAll(async () => {
await next.destroy()
if (server) await killApp(server)
})
it('should render page correctly under custom server', async () => {
const html = await renderViaHTTP(appPort, '/')
expect(html).toContain('streaming')
})
})
describe('react 18 streaming SSR in minimal mode with node runtime', () => {
let next: NextInstance
beforeAll(async () => {
if (isNextProd) {
process.env.NEXT_PRIVATE_MINIMAL_MODE = '1'
}
next = await createNext({
files: {
'pages/index.js': `
export default function Page() {
return <p>streaming</p>
}
export async function getServerSideProps() {
return { props: {} }
}`,
},
nextConfig: {
experimental: {
runtime: 'nodejs',
},
webpack(config, { nextRuntime }) {
const path = require('path')
const fs = require('fs')
const runtimeFilePath = path.join(__dirname, 'runtimes.txt')
let runtimeContent = ''
try {
runtimeContent = fs.readFileSync(runtimeFilePath, 'utf8')
runtimeContent += '\n'
} catch (_) {}
runtimeContent += nextRuntime || 'client'
fs.writeFileSync(runtimeFilePath, runtimeContent)
return config
},
},
})
})
afterAll(() => {
if (isNextProd) {
delete process.env.NEXT_PRIVATE_MINIMAL_MODE
}
next.destroy()
})
it('should pass correct nextRuntime values', async () => {
const content = await next.readFile('runtimes.txt')
expect(content.split('\n').sort()).toEqual(['client', 'edge', 'nodejs'])
})
it('should generate html response by streaming correctly', async () => {
const html = await renderViaHTTP(next.url, '/')
expect(html).toContain('streaming')
})
if (isNextProd) {
it('should have generated a static 404 page', async () => {
expect(await next.readFile('.next/server/pages/404.html')).toBeTruthy()
const res = await fetchViaHTTP(next.url, '/non-existent')
expect(res.status).toBe(404)
expect(await res.text()).toContain('This page could not be found')
})
}
})
}