Skip to content

Commit

Permalink
Test Prerender in Emulated Serverless Mode (#10660)
Browse files Browse the repository at this point in the history
* Test Prerender in Emulated Serverless Mode

* Clean up between runs
  • Loading branch information
Timer committed Feb 24, 2020
1 parent 18ecaa9 commit 51b35f1
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 53 deletions.
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

0 comments on commit 51b35f1

Please sign in to comment.