JJ Kasper 2023-04-07 17:54:17 -07:00 committed by GitHub
parent 71a29a17b8
commit 4e3f135fd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 211 additions and 188 deletions

View file

@ -486,20 +486,18 @@ describe('Middleware Runtime', () => {
const json = readMiddlewareJSON(res)
delete json.process.env['JEST_WORKER_ID']
expect(json).toEqual({
process: {
env: {
ANOTHER_MIDDLEWARE_TEST: 'asdf2',
STRING_ENV_VAR: 'asdf3',
MIDDLEWARE_TEST: 'asdf',
...((global as any).isNextDeploy
? {}
: {
NEXT_RUNTIME: 'edge',
}),
},
},
})
for (const [key, value] of Object.entries({
ANOTHER_MIDDLEWARE_TEST: 'asdf2',
STRING_ENV_VAR: 'asdf3',
MIDDLEWARE_TEST: 'asdf',
...((global as any).isNextDeploy
? {}
: {
NEXT_RUNTIME: 'edge',
}),
})) {
expect(json.process.env[key]).toBe(value)
}
})
it(`should contain \`globalThis\``, async () => {

View file

@ -0,0 +1,116 @@
import fs from 'fs-extra'
import { nextBuild, nextExport } from 'next-test-utils'
import { join } from 'path'
import {
appDir,
distDir,
expectedFiles,
exportDir,
getFiles,
nextConfig,
} from './utils'
describe('app dir with output export (next dev / next build)', () => {
it('should throw when exportPathMap configured', async () => {
nextConfig.replace(
'trailingSlash: true,',
`trailingSlash: true,
exportPathMap: async function (map) {
return map
},`
)
await fs.remove(distDir)
await fs.remove(exportDir)
let result = { code: 0, stderr: '' }
try {
result = await nextBuild(appDir, [], { stderr: true })
} finally {
nextConfig.restore()
}
expect(result.code).toBe(1)
expect(result.stderr).toContain(
'The "exportPathMap" configuration cannot be used with the "app" directory. Please use generateStaticParams() instead.'
)
})
it('should warn about "next export" is no longer needed with config', async () => {
await fs.remove(distDir)
await fs.remove(exportDir)
await nextBuild(appDir)
expect(await getFiles()).toEqual(expectedFiles)
let stdout = ''
let stderr = ''
await nextExport(
appDir,
{ outdir: exportDir },
{
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
}
)
expect(stderr).toContain(
'warn - "next export" is no longer needed when "output: export" is configured in next.config.js'
)
expect(stdout).toContain('Export successful. Files written to')
expect(await getFiles()).toEqual(expectedFiles)
})
it('should error when no config.output detected for next export', async () => {
await fs.remove(distDir)
await fs.remove(exportDir)
nextConfig.replace(`output: 'export',`, '')
try {
await nextBuild(appDir)
expect(await getFiles()).toEqual([])
let stdout = ''
let stderr = ''
let error = undefined
try {
await nextExport(
appDir,
{ outdir: exportDir },
{
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
}
)
} catch (e) {
error = e
}
expect(error).toBeDefined()
expect(stderr).toContain(
'error - "next export" does not work with App Router. Please use "output: export" in next.config.js'
)
expect(stdout).not.toContain('Export successful. Files written to')
expect(await getFiles()).toEqual([])
} finally {
nextConfig.restore()
await fs.remove(distDir)
await fs.remove(exportDir)
}
})
it('should correctly emit exported assets to config.distDir', async () => {
const outputDir = join(appDir, 'output')
await fs.remove(distDir)
await fs.remove(outputDir)
nextConfig.replace(
'trailingSlash: true,',
`trailingSlash: true,
distDir: 'output',`
)
try {
await nextBuild(appDir)
expect(await getFiles(outputDir)).toEqual(expectedFiles)
} finally {
nextConfig.restore()
await fs.remove(distDir)
await fs.remove(outputDir)
}
})
})

View file

@ -0,0 +1,2 @@
process.env.TEST_DEV = '1'
require('./dynamic')

View file

@ -0,0 +1 @@
require('./dynamic')

View file

@ -0,0 +1,54 @@
import { runTests } from './utils'
it.each([
{ isDev: true, dynamic: 'undefined' },
{ isDev: true, dynamic: "'error'" },
{ isDev: true, dynamic: "'force-static'" },
{
isDev: true,
dynamic: "'force-dynamic'",
expectedErrMsg:
'Page with `dynamic = "force-dynamic"` couldn\'t be rendered statically because it used `output: export`.',
},
{ isDev: false, dynamic: 'undefined' },
{ isDev: false, dynamic: "'error'" },
{ isDev: false, dynamic: "'force-static'" },
// TODO: re-enable when flakey behavior is investigated
// {
// isDev: false,
// dynamic: "'force-dynamic'",
// expectedErrMsg:
// 'Page with `dynamic = "force-dynamic"` couldn\'t be rendered statically because it used `output: export`.',
// },
])(
"should work with with isDev '$isDev' and dynamic $dynamic on page",
async ({ isDev, dynamic, expectedErrMsg }) => {
await runTests({ isDev, dynamicPage: dynamic, expectedErrMsg })
}
)
it.each([
{ isDev: true, dynamic: 'undefined' },
{ isDev: true, dynamic: "'error'" },
{ isDev: true, dynamic: "'force-static'" },
{
isDev: true,
dynamic: "'force-dynamic'",
expectedErrMsg:
'export const dynamic = "force-dynamic" on page "/api/json" cannot be used with "output: export".',
},
{ isDev: false, dynamic: 'undefined' },
{ isDev: false, dynamic: "'error'" },
{ isDev: false, dynamic: "'force-static'" },
{
isDev: false,
dynamic: "'force-dynamic'",
expectedErrMsg:
'export const dynamic = "force-dynamic" on page "/api/json" cannot be used with "output: export".',
},
])(
"should work with with isDev '$isDev' and dynamic $dynamic on route handler",
async ({ isDev, dynamic, expectedErrMsg }) => {
await runTests({ isDev, dynamicApiRoute: dynamic, expectedErrMsg })
}
)

View file

@ -0,0 +1,2 @@
process.env.TEST_DEV = '1'
require('./trailing-slash')

View file

@ -0,0 +1 @@
require('./trailing-slash')

View file

@ -0,0 +1,13 @@
import { runTests } from './utils'
it.each([
{ isDev: true, trailingSlash: false },
{ isDev: true, trailingSlash: true },
{ isDev: false, trailingSlash: false },
{ isDev: false, trailingSlash: true },
])(
"should work with isDev '$isDev' and trailingSlash '$trailingSlash'",
async ({ isDev, trailingSlash }) => {
await runTests({ isDev, trailingSlash })
}
)

View file

@ -15,20 +15,19 @@ import {
killApp,
launchApp,
nextBuild,
nextExport,
startStaticServer,
stopApp,
} from 'next-test-utils'
const glob = promisify(globOrig)
const appDir = join(__dirname, '..')
const distDir = join(appDir, '.next')
const exportDir = join(appDir, 'out')
const nextConfig = new File(join(appDir, 'next.config.js'))
export const appDir = join(__dirname, '..')
export const distDir = join(appDir, '.next')
export const exportDir = join(appDir, 'out')
export const nextConfig = new File(join(appDir, 'next.config.js'))
const slugPage = new File(join(appDir, 'app/another/[slug]/page.js'))
const apiJson = new File(join(appDir, 'app/api/json/route.js'))
const expectedFiles = [
export const expectedFiles = [
'404.html',
'404/index.html',
'_next/static/media/test.3f1a293b.png',
@ -50,7 +49,7 @@ const expectedFiles = [
'robots.txt',
]
async function getFiles(cwd = exportDir) {
export async function getFiles(cwd = exportDir) {
const opts = { cwd, nodir: true }
const files = ((await glob('**/*', opts)) as string[])
.filter(
@ -62,7 +61,7 @@ async function getFiles(cwd = exportDir) {
.sort()
return files
}
async function runTests({
export async function runTests({
isDev = false,
trailingSlash = true,
dynamicPage,
@ -75,6 +74,9 @@ async function runTests({
dynamicApiRoute?: string
expectedErrMsg?: string
}) {
if ((isDev && !process.env.TEST_DEV) || (!isDev && process.env.TEST_DEV)) {
return
}
if (trailingSlash) {
nextConfig.replace(
'trailingSlash: true,',
@ -200,169 +202,3 @@ async function runTests({
apiJson.restore()
}
}
describe('app dir with output export (next dev / next build)', () => {
it.each([
{ isDev: true, trailingSlash: false },
{ isDev: true, trailingSlash: true },
{ isDev: false, trailingSlash: false },
{ isDev: false, trailingSlash: true },
])(
"should work with isDev '$isDev' and trailingSlash '$trailingSlash'",
async ({ isDev, trailingSlash }) => {
await runTests({ isDev, trailingSlash })
}
)
it.each([
{ isDev: true, dynamic: 'undefined' },
{ isDev: true, dynamic: "'error'" },
{ isDev: true, dynamic: "'force-static'" },
{
isDev: true,
dynamic: "'force-dynamic'",
expectedErrMsg:
'Page with `dynamic = "force-dynamic"` couldn\'t be rendered statically because it used `output: export`.',
},
{ isDev: false, dynamic: 'undefined' },
{ isDev: false, dynamic: "'error'" },
{ isDev: false, dynamic: "'force-static'" },
{
isDev: false,
dynamic: "'force-dynamic'",
expectedErrMsg:
'Page with `dynamic = "force-dynamic"` couldn\'t be rendered statically because it used `output: export`.',
},
])(
"should work with with isDev '$isDev' and dynamic $dynamic on page",
async ({ isDev, dynamic, expectedErrMsg }) => {
await runTests({ isDev, dynamicPage: dynamic, expectedErrMsg })
}
)
it.each([
{ isDev: true, dynamic: 'undefined' },
{ isDev: true, dynamic: "'error'" },
{ isDev: true, dynamic: "'force-static'" },
{
isDev: true,
dynamic: "'force-dynamic'",
expectedErrMsg:
'export const dynamic = "force-dynamic" on page "/api/json" cannot be used with "output: export".',
},
{ isDev: false, dynamic: 'undefined' },
{ isDev: false, dynamic: "'error'" },
{ isDev: false, dynamic: "'force-static'" },
{
isDev: false,
dynamic: "'force-dynamic'",
expectedErrMsg:
'export const dynamic = "force-dynamic" on page "/api/json" cannot be used with "output: export".',
},
])(
"should work with with isDev '$isDev' and dynamic $dynamic on route handler",
async ({ isDev, dynamic, expectedErrMsg }) => {
await runTests({ isDev, dynamicApiRoute: dynamic, expectedErrMsg })
}
)
it('should throw when exportPathMap configured', async () => {
nextConfig.replace(
'trailingSlash: true,',
`trailingSlash: true,
exportPathMap: async function (map) {
return map
},`
)
await fs.remove(distDir)
await fs.remove(exportDir)
let result = { code: 0, stderr: '' }
try {
result = await nextBuild(appDir, [], { stderr: true })
} finally {
nextConfig.restore()
}
expect(result.code).toBe(1)
expect(result.stderr).toContain(
'The "exportPathMap" configuration cannot be used with the "app" directory. Please use generateStaticParams() instead.'
)
})
it('should warn about "next export" is no longer needed with config', async () => {
await fs.remove(distDir)
await fs.remove(exportDir)
await nextBuild(appDir)
expect(await getFiles()).toEqual(expectedFiles)
let stdout = ''
let stderr = ''
await nextExport(
appDir,
{ outdir: exportDir },
{
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
}
)
expect(stderr).toContain(
'warn - "next export" is no longer needed when "output: export" is configured in next.config.js'
)
expect(stdout).toContain('Export successful. Files written to')
expect(await getFiles()).toEqual(expectedFiles)
})
it('should error when no config.output detected for next export', async () => {
await fs.remove(distDir)
await fs.remove(exportDir)
nextConfig.replace(`output: 'export',`, '')
try {
await nextBuild(appDir)
expect(await getFiles()).toEqual([])
let stdout = ''
let stderr = ''
let error = undefined
try {
await nextExport(
appDir,
{ outdir: exportDir },
{
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
}
)
} catch (e) {
error = e
}
expect(error).toBeDefined()
expect(stderr).toContain(
'error - "next export" does not work with App Router. Please use "output: export" in next.config.js'
)
expect(stdout).not.toContain('Export successful. Files written to')
expect(await getFiles()).toEqual([])
} finally {
nextConfig.restore()
await fs.remove(distDir)
await fs.remove(exportDir)
}
})
it('should correctly emit exported assets to config.distDir', async () => {
const outputDir = join(appDir, 'output')
await fs.remove(distDir)
await fs.remove(outputDir)
nextConfig.replace(
'trailingSlash: true,',
`trailingSlash: true,
distDir: 'output',`
)
try {
await nextBuild(appDir)
expect(await getFiles(outputDir)).toEqual(expectedFiles)
} finally {
nextConfig.restore()
await fs.remove(distDir)
await fs.remove(outputDir)
}
})
})