Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Prerender in Emulated Serverless Mode #10660

Merged
merged 4 commits into from Feb 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
91 changes: 89 additions & 2 deletions test/integration/prerender/server.js
@@ -1,15 +1,102 @@
const http = require('http')
const url = require('url')

const fs = require('fs')
const path = require('path')
const server = http.createServer((req, res) => {
let { pathname } = url.parse(req.url)
pathname = pathname.replace(/\/$/, '')
let isDataReq = false
if (pathname.startsWith('/_next/data')) {
isDataReq = true
pathname = pathname
.replace(`/_next/data/${process.env.BUILD_ID}/`, '/')
.replace(/\.json$/, '')
}
console.log('serving', pathname)
require(`./.next/serverless/pages${pathname}`).render(req, res)

if (pathname === '/favicon.ico') {
res.statusCode = 404
return res.end()
}

if (pathname.startsWith('/_next/static/')) {
res.write(
fs.readFileSync(
path.join(
__dirname,
'./.next/static/',
decodeURI(pathname.slice('/_next/static/'.length))
),
'utf8'
)
)
return res.end()
} else {
const ext = isDataReq ? 'json' : 'html'
if (
fs.existsSync(
path.join(__dirname, `./.next/serverless/pages${pathname}.${ext}`)
)
) {
res.write(
fs.readFileSync(
path.join(__dirname, `./.next/serverless/pages${pathname}.${ext}`),
'utf8'
)
)
return res.end()
}

let re
try {
re = require(`./.next/serverless/pages${pathname}`)
} catch {
const d = decodeURI(pathname)
if (
fs.existsSync(
path.join(__dirname, `./.next/serverless/pages${d}.${ext}`)
)
) {
res.write(
fs.readFileSync(
path.join(__dirname, `./.next/serverless/pages${d}.${ext}`),
'utf8'
)
)
return res.end()
}

const routesManifest = require('./.next/routes-manifest.json')
const { dynamicRoutes } = routesManifest
dynamicRoutes.some(({ page, regex }) => {
if (new RegExp(regex).test(pathname)) {
if (
fs.existsSync(
path.join(__dirname, `./.next/serverless/pages${page}.${ext}`)
)
) {
res.write(
fs.readFileSync(
path.join(__dirname, `./.next/serverless/pages${page}.${ext}`),
'utf8'
)
)
res.end()
return true
}

re = require(`./.next/serverless/pages${page}`)
return true
}
return false
})
}
if (!res.finished) {
return typeof re.render === 'function'
? re.render(req, res)
: re.default(req, res)
}
}
})

server.listen(process.env.PORT, () => {
Expand Down
144 changes: 93 additions & 51 deletions test/integration/prerender/test/index.test.js
Expand Up @@ -245,7 +245,7 @@ const navigateTest = (dev = false) => {
})
}

const runTests = (dev = false) => {
const runTests = (dev = false, looseMode = false) => {
navigateTest(dev)

it('should SSR normal page correctly', async () => {
Expand Down Expand Up @@ -518,14 +518,16 @@ const runTests = (dev = false) => {
expect(curRandom).toBe(initialRandom + '')
})
} else {
it('should should use correct caching headers for a no-revalidate page', async () => {
const initialRes = await fetchViaHTTP(appPort, '/something')
expect(initialRes.headers.get('cache-control')).toBe(
's-maxage=31536000, stale-while-revalidate'
)
const initialHtml = await initialRes.text()
expect(initialHtml).toMatch(/hello.*?world/)
})
if (!looseMode) {
it('should should use correct caching headers for a no-revalidate page', async () => {
const initialRes = await fetchViaHTTP(appPort, '/something')
expect(initialRes.headers.get('cache-control')).toBe(
's-maxage=31536000, stale-while-revalidate'
)
const initialHtml = await initialRes.text()
expect(initialHtml).toMatch(/hello.*?world/)
})
}

it('outputs a prerender-manifest correctly', async () => {
const manifest = JSON.parse(
Expand Down Expand Up @@ -600,20 +602,22 @@ const runTests = (dev = false) => {
}
})

it('should handle de-duping correctly', async () => {
let vals = new Array(10).fill(null)
if (!looseMode) {
it('should handle de-duping correctly', async () => {
let vals = new Array(10).fill(null)

// use data route so we don't get the fallback
vals = await Promise.all(
vals.map(() =>
renderViaHTTP(appPort, `/_next/data/${buildId}/blog/post-10.json`)
// use data route so we don't get the fallback
vals = await Promise.all(
vals.map(() =>
renderViaHTTP(appPort, `/_next/data/${buildId}/blog/post-10.json`)
)
)
)
const val = vals[0]
const val = vals[0]

expect(JSON.parse(val).pageProps.post).toBe('post-10')
expect(new Set(vals).size).toBe(1)
})
expect(JSON.parse(val).pageProps.post).toBe('post-10')
expect(new Set(vals).size).toBe(1)
})
}

it('should not revalidate when set to false', async () => {
const route = '/something'
Expand All @@ -628,43 +632,45 @@ const runTests = (dev = false) => {
expect(initialHtml).toBe(newHtml)
})

it('should handle revalidating HTML correctly', async () => {
const route = '/blog/post-2/comment-2'
const initialHtml = await renderViaHTTP(appPort, route)
expect(initialHtml).toMatch(/Post:.*?post-2/)
expect(initialHtml).toMatch(/Comment:.*?comment-2/)
if (!looseMode) {
it('should handle revalidating HTML correctly', async () => {
const route = '/blog/post-2/comment-2'
const initialHtml = await renderViaHTTP(appPort, route)
expect(initialHtml).toMatch(/Post:.*?post-2/)
expect(initialHtml).toMatch(/Comment:.*?comment-2/)

let newHtml = await renderViaHTTP(appPort, route)
expect(newHtml).toBe(initialHtml)
let newHtml = await renderViaHTTP(appPort, route)
expect(newHtml).toBe(initialHtml)

await waitFor(2 * 1000)
await renderViaHTTP(appPort, route)
await waitFor(2 * 1000)
await renderViaHTTP(appPort, route)

await waitFor(2 * 1000)
newHtml = await renderViaHTTP(appPort, route)
expect(newHtml === initialHtml).toBe(false)
expect(newHtml).toMatch(/Post:.*?post-2/)
expect(newHtml).toMatch(/Comment:.*?comment-2/)
})
await waitFor(2 * 1000)
newHtml = await renderViaHTTP(appPort, route)
expect(newHtml === initialHtml).toBe(false)
expect(newHtml).toMatch(/Post:.*?post-2/)
expect(newHtml).toMatch(/Comment:.*?comment-2/)
})

it('should handle revalidating JSON correctly', async () => {
const route = `/_next/data/${buildId}/blog/post-2/comment-3.json`
const initialJson = await renderViaHTTP(appPort, route)
expect(initialJson).toMatch(/post-2/)
expect(initialJson).toMatch(/comment-3/)
it('should handle revalidating JSON correctly', async () => {
const route = `/_next/data/${buildId}/blog/post-2/comment-3.json`
const initialJson = await renderViaHTTP(appPort, route)
expect(initialJson).toMatch(/post-2/)
expect(initialJson).toMatch(/comment-3/)

let newJson = await renderViaHTTP(appPort, route)
expect(newJson).toBe(initialJson)
let newJson = await renderViaHTTP(appPort, route)
expect(newJson).toBe(initialJson)

await waitFor(2 * 1000)
await renderViaHTTP(appPort, route)
await waitFor(2 * 1000)
await renderViaHTTP(appPort, route)

await waitFor(2 * 1000)
newJson = await renderViaHTTP(appPort, route)
expect(newJson === initialJson).toBe(false)
expect(newJson).toMatch(/post-2/)
expect(newJson).toMatch(/comment-3/)
})
await waitFor(2 * 1000)
newJson = await renderViaHTTP(appPort, route)
expect(newJson === initialJson).toBe(false)
expect(newJson).toMatch(/post-2/)
expect(newJson).toMatch(/comment-3/)
})
}

it('should not fetch prerender data on mount', async () => {
const browser = await webdriver(appPort, '/blog/post-100')
Expand All @@ -682,7 +688,7 @@ const runTests = (dev = false) => {
}
}

describe('SPR Prerender', () => {
describe('SSG Prerender', () => {
afterAll(() => fs.remove(nextConfig))

describe('dev mode', () => {
Expand Down Expand Up @@ -724,6 +730,7 @@ describe('SPR Prerender', () => {
`module.exports = { target: 'serverless' }`,
'utf8'
)
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)
stderr = ''
appPort = await findPort()
Expand Down Expand Up @@ -766,6 +773,7 @@ describe('SPR Prerender', () => {
export default () => 'hello world'
`
)
await fs.remove(join(appDir, '.next'))
const { stderr } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(brokenPage)
expect(stderr).toContain(
Expand All @@ -777,10 +785,43 @@ describe('SPR Prerender', () => {
})
})

describe('enumlated serverless mode', () => {
beforeAll(async () => {
const startServerlessEmulator = async (dir, port, buildId) => {
const scriptPath = join(dir, 'server.js')
const env = Object.assign(
{},
{ ...process.env },
{ PORT: port, BUILD_ID: buildId }
)
return initNextServerScript(scriptPath, /ready on/i, env)
}

await fs.writeFile(
nextConfig,
`module.exports = { target: 'experimental-serverless-trace' }`,
'utf8'
)
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)

distPagesDir = join(appDir, '.next/serverless/pages')
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')

stderr = ''
appPort = await findPort()
app = await startServerlessEmulator(appDir, appPort, buildId)
})
afterAll(() => killApp(app))

runTests(false, true)
})

describe('production mode', () => {
let buildOutput = ''
beforeAll(async () => {
await fs.remove(nextConfig)
await fs.remove(join(appDir, '.next'))
const { stdout } = await nextBuild(appDir, [], { stdout: true })
buildOutput = stdout

Expand Down Expand Up @@ -820,6 +861,7 @@ describe('SPR Prerender', () => {
},
}`
)
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)
await nextExport(appDir, { outdir: exportDir })
app = await startStaticServer(exportDir)
Expand Down