Rename process.env.TURBOPACK -> process.env.TURBOPACK_DEV in test skips (#63665)

## What?

Follow-up to #63653.

<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Adding or Updating Examples

- The "examples guidelines" are followed from our contributing doc
https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See
https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md


## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->


Closes NEXT-2911
This commit is contained in:
Tim Neutkens 2024-03-25 14:17:56 +01:00 committed by GitHub
parent f9d73cc2fa
commit 1e710ab73c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
305 changed files with 13796 additions and 12666 deletions

View file

@ -14,7 +14,7 @@ createNextDescribe(
},
({ next, isNextStart }) => {
if (!isNextStart) {
it('skip test for dev mode', () => {})
it('skip test for development mode', () => {})
return
}

View file

@ -17,7 +17,7 @@ createNextDescribe(
},
({ next, isNextStart, isNextDeploy }) => {
if (!isNextStart) {
it('skip test for dev mode', () => {})
it('skip test for development mode', () => {})
return
}

View file

@ -80,7 +80,7 @@ createNextDescribe(
},
({ next, isNextDev }) => {
if (isNextDev) {
// since the router behavior is different in dev mode (no viewport prefetching + liberal revalidation)
// since the router behavior is different in development mode (no viewport prefetching + liberal revalidation)
// we only check the production behavior
it('should skip dev', () => {})
} else {

View file

@ -35,7 +35,7 @@ createNextDescribe(
},
({ next, isNextDev }) => {
if (isNextDev) {
it.skip('should skip test in dev mode', () => {})
it.skip('should skip test in development mode', () => {})
} else {
it('should avoid double-fetching when optimistic navigation fails', async () => {
const browser = await next.browser('/foo')

View file

@ -3056,7 +3056,7 @@ createNextDescribe(
})
}
})
// Don't run these tests in dev mode since they won't be statically generated
// Don't run these tests in development mode since they won't be statically generated
if (!isDev) {
describe('server response', () => {
it('should bailout to client rendering - with suspense boundary', async () => {
@ -3215,7 +3215,7 @@ createNextDescribe(
})
}
if (!process.env.CUSTOM_CACHE_HANDLER && isDev) {
it('should not cache request if response data size is greater than 2MB and FetchCache is possible in Dev mode', async () => {
it('should not cache request if response data size is greater than 2MB and FetchCache is possible in development mode', async () => {
const cliOutputStart = next.cliOutput.length
const resp1 = await next.fetch('/force-cache/large-data')
const resp1Text = await resp1.text()
@ -3243,7 +3243,7 @@ createNextDescribe(
})
}
if (process.env.CUSTOM_CACHE_HANDLER && isDev) {
it('should cache request if response data size is greater than 2MB in Dev mode', async () => {
it('should cache request if response data size is greater than 2MB in development mode', async () => {
const cliOutputStart = next.cliOutput.length
const resp1 = await next.fetch('/force-cache/large-data')
const resp1Text = await resp1.text()

View file

@ -52,7 +52,7 @@ createNextDescribe(
withFullUrlFetches?: boolean
}) {
if (withFetchesLogging) {
it('should only log requests in dev mode', async () => {
it('should only log requests in development mode', async () => {
const outputIndex = next.cliOutput.length
await next.fetch('/default-cache')

View file

@ -12,7 +12,7 @@ createNextDescribe(
},
({ next, isNextDev }) => {
// If it's start mode, we get the whole logs since they're from build process.
// If it's dev mode, we get the logs after request
// If it's development mode, we get the logs after request
function getCliOutput(logStartPosition: number) {
return isNextDev ? next.cliOutput.slice(logStartPosition) : next.cliOutput
}

View file

@ -8,7 +8,7 @@ createNextDescribe(
},
({ next, isNextDev }) => {
if (isNextDev) {
it.skip('skip test for dev mode', () => {})
it.skip('skip test for development mode', () => {})
return
}

View file

@ -1,47 +1,50 @@
import { nextBuild } from 'next-test-utils'
describe('ppr build errors', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
let stderr: string
let stdout: string
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
let stderr: string
let stdout: string
beforeAll(async () => {
const output = await nextBuild(__dirname, [], {
stderr: true,
stdout: true,
beforeAll(async () => {
const output = await nextBuild(__dirname, [], {
stderr: true,
stdout: true,
})
stderr = output.stderr
stdout = output.stdout
})
stderr = output.stderr
stdout = output.stdout
})
describe('within a suspense boundary', () => {
it('should fail the build for uncaught prerender errors', async () => {
expect(stderr).toContain(
'Error occurred prerendering page "/regular-error-suspense-boundary".'
)
expect(stderr).toContain(
'Error occurred prerendering page "/re-throwing-error".'
)
describe('within a suspense boundary', () => {
it('should fail the build for uncaught prerender errors', async () => {
expect(stderr).toContain(
'Error occurred prerendering page "/regular-error-suspense-boundary".'
)
expect(stderr).toContain(
'Error occurred prerendering page "/re-throwing-error".'
)
})
})
})
describe('outside of a suspense boundary', () => {
it('should fail the build for uncaught errors', async () => {
expect(stderr).toContain(
'Error occurred prerendering page "/regular-error".'
)
expect(stderr).toContain(
'Error occurred prerendering page "/no-suspense-boundary-re-throwing-error".'
)
describe('outside of a suspense boundary', () => {
it('should fail the build for uncaught errors', async () => {
expect(stderr).toContain(
'Error occurred prerendering page "/regular-error".'
)
expect(stderr).toContain(
'Error occurred prerendering page "/no-suspense-boundary-re-throwing-error".'
)
})
})
})
describe('when a postpone call is caught and logged it should', () => {
it('should include a message telling why', async () => {
expect(stdout).toContain(
'User land logged error: Route /logging-error needs to bail out of prerendering at this point because it used cookies.'
)
describe('when a postpone call is caught and logged it should', () => {
it('should include a message telling why', async () => {
expect(stdout).toContain(
'User land logged error: Route /logging-error needs to bail out of prerendering at this point because it used cookies.'
)
})
})
})
})
}
)
})

View file

@ -16,7 +16,7 @@ const apiPath = 'pages/api/edge.js'
const api = new File(join(appDir, apiPath))
if ((global as any).isNextDev) {
describe('In dev mode', () => {
describe('In development mode', () => {
beforeAll(async () => {
next = await createNext({
files: new FileRef(appDir),

View file

@ -1076,7 +1076,7 @@ describe('Prerender', () => {
expect(JSON.parse($2('#__NEXT_DATA__').text()).isFallback).toBe(false)
})
it('should log error in console and browser in dev mode', async () => {
it('should log error in console and browser in development mode', async () => {
const indexPage = 'pages/index.js'
const origContent = await next.readFile(indexPage)

View file

@ -21,45 +21,48 @@ let appPort
let app
describe('404 Page Support with _app', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
afterAll(() => killApp(app))
it('should build successfully', async () => {
const { code, stderr, stdout } = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
it('should build successfully', async () => {
const { code, stderr, stdout } = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})
expect(code).toBe(0)
expect(stderr).not.toMatch(gip404Err)
expect(stdout).not.toMatch(gip404Err)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
expect(code).toBe(0)
expect(stderr).not.toMatch(gip404Err)
expect(stdout).not.toMatch(gip404Err)
it('should not output static 404 if _app has getInitialProps', async () => {
const browser = await webdriver(appPort, '/404')
const isAutoExported = await browser.eval('__NEXT_DATA__.autoExport')
expect(isAutoExported).toBeFalsy()
})
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
it('specify to use the 404 page still in the routes-manifest', async () => {
const manifest = await fs.readJSON(
join(appDir, '.next/routes-manifest.json')
)
expect(manifest.pages404).toBe(true)
})
it('should not output static 404 if _app has getInitialProps', async () => {
const browser = await webdriver(appPort, '/404')
const isAutoExported = await browser.eval('__NEXT_DATA__.autoExport')
expect(isAutoExported).toBeFalsy()
})
it('should still use 404 page', async () => {
const res = await fetchViaHTTP(appPort, '/abc')
expect(res.status).toBe(404)
const $ = cheerio.load(await res.text())
expect($('#404-title').text()).toBe('Hi There')
})
}
)
it('specify to use the 404 page still in the routes-manifest', async () => {
const manifest = await fs.readJSON(
join(appDir, '.next/routes-manifest.json')
)
expect(manifest.pages404).toBe(true)
})
it('should still use 404 page', async () => {
const res = await fetchViaHTTP(appPort, '/abc')
expect(res.status).toBe(404)
const $ = cheerio.load(await res.text())
expect($('#404-title').text()).toBe('Hi There')
})
})
describe('dev mode', () => {
describe('development mode', () => {
let stderr = ''
let stdout = ''

View file

@ -51,26 +51,29 @@ const runTests = (mode) => {
}
describe('Default 404 Page with custom _error', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
afterAll(() => killApp(app))
it('should build successfully', async () => {
const { code } = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
it('should build successfully', async () => {
const { code } = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})
expect(code).toBe(0)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
expect(code).toBe(0)
runTests('server')
}
)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
runTests('server')
})
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)

View file

@ -76,42 +76,45 @@ const runTests = (isDev) => {
}
describe('404 Page Support SSG', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
afterAll(() => killApp(app))
it('should build successfully', async () => {
const {
code,
stderr: buildStderr,
stdout: buildStdout,
} = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
it('should build successfully', async () => {
const {
code,
stderr: buildStderr,
stdout: buildStdout,
} = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})
expect(code).toBe(0)
expect(buildStderr).not.toMatch(gip404Err)
expect(buildStdout).not.toMatch(gip404Err)
appPort = await findPort()
stderr = ''
stdout = ''
app = await nextStart(appDir, appPort, {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
expect(code).toBe(0)
expect(buildStderr).not.toMatch(gip404Err)
expect(buildStdout).not.toMatch(gip404Err)
runTests()
}
)
appPort = await findPort()
stderr = ''
stdout = ''
app = await nextStart(appDir, appPort, {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
runTests()
})
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
stderr = ''

View file

@ -70,7 +70,7 @@ const runTests = (mode = 'server') => {
}
describe('404 Page Support', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -79,7 +79,7 @@ describe('404 Page Support', () => {
runTests('dev')
})
describe('dev mode 2', () => {
describe('development mode 2', () => {
it('falls back to _error correctly without pages/404', async () => {
await fs.move(pages404, `${pages404}.bak`)
appPort = await findPort()
@ -180,135 +180,141 @@ describe('404 Page Support', () => {
expect(stderr).toMatch(gip404Err)
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests('server')
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should not cache for custom 404 page with gssp and revalidate disabled', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
runTests('server')
}
)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should not cache for custom 404 page with gssp and revalidate disabled', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
const page = () => 'custom 404 page'
export async function getStaticProps() { return { props: {} } }
export default page
`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
const cache404 = await getCacheHeader(appPort, '/404')
const cacheNext = await getCacheHeader(appPort, '/_next/abc')
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
await killApp(app)
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
const cache404 = await getCacheHeader(appPort, '/404')
const cacheNext = await getCacheHeader(appPort, '/_next/abc')
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
await killApp(app)
expect(cache404).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
expect(cacheNext).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
})
expect(cache404).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
expect(cacheNext).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
})
it('should not cache for custom 404 page with gssp and revalidate enabled', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
it('should not cache for custom 404 page with gssp and revalidate enabled', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
const page = () => 'custom 404 page'
export async function getStaticProps() { return { props: {}, revalidate: 1 } }
export default page
`
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
const cache404 = await getCacheHeader(appPort, '/404')
const cacheNext = await getCacheHeader(appPort, '/_next/abc')
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
await killApp(app)
)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
const cache404 = await getCacheHeader(appPort, '/404')
const cacheNext = await getCacheHeader(appPort, '/_next/abc')
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
await killApp(app)
expect(cache404).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
expect(cacheNext).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
})
expect(cache404).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
expect(cacheNext).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
})
it('should not cache for custom 404 page without gssp', async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
const cache404 = await getCacheHeader(appPort, '/404')
const cacheNext = await getCacheHeader(appPort, '/_next/abc')
await killApp(app)
it('should not cache for custom 404 page without gssp', async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
const cache404 = await getCacheHeader(appPort, '/404')
const cacheNext = await getCacheHeader(appPort, '/_next/abc')
await killApp(app)
expect(cache404).toBe(null)
expect(cacheNext).toBe('no-cache, no-store, max-age=0, must-revalidate')
})
expect(cache404).toBe(null)
expect(cacheNext).toBe('no-cache, no-store, max-age=0, must-revalidate')
})
it('shows error with getInitialProps in pages/404 build', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
it('shows error with getInitialProps in pages/404 build', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
const page = () => 'custom 404 page'
page.getInitialProps = () => ({ a: 'b' })
export default page
`
)
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
)
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
expect(stderr).toMatch(gip404Err)
expect(code).toBe(1)
})
expect(stderr).toMatch(gip404Err)
expect(code).toBe(1)
})
it('does not show error with getStaticProps in pages/404 build', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
it('does not show error with getStaticProps in pages/404 build', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
const page = () => 'custom 404 page'
export const getStaticProps = () => ({ props: { a: 'b' } })
export default page
`
)
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
)
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
expect(stderr).not.toMatch(gip404Err)
expect(code).toBe(0)
})
expect(stderr).not.toMatch(gip404Err)
expect(code).toBe(0)
})
it('shows error with getServerSideProps in pages/404 build', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
it('shows error with getServerSideProps in pages/404 build', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
`
const page = () => 'custom 404 page'
export const getServerSideProps = () => ({ props: { a: 'b' } })
export default page
`
)
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
)
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)
expect(stderr).toMatch(gip404Err)
expect(code).toBe(1)
})
})
expect(stderr).toMatch(gip404Err)
expect(code).toBe(1)
})
}
)
})

View file

@ -23,163 +23,166 @@ let appPort
let app
describe('gsp-gssp', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('does not show error with getStaticProps in pages/500 build', async () => {
await fs.move(pages500, `${pages500}.bak`)
await fs.writeFile(
pages500,
`
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('does not show error with getStaticProps in pages/500 build', async () => {
await fs.move(pages500, `${pages500}.bak`)
await fs.writeFile(
pages500,
`
const page = () => 'custom 500 page'
export const getStaticProps = () => ({ props: { a: 'b' } })
export default page
`
)
await fs.remove(join(appDir, '.next'))
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages500)
await fs.move(`${pages500}.bak`, pages500)
)
await fs.remove(join(appDir, '.next'))
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages500)
await fs.move(`${pages500}.bak`, pages500)
expect(stderr).not.toMatch(gip500Err)
expect(code).toBe(0)
})
it('shows error with getServerSideProps in pages/500 build', async () => {
await fs.move(pages500, `${pages500}.bak`)
await fs.writeFile(
pages500,
`
expect(stderr).not.toMatch(gip500Err)
expect(code).toBe(0)
})
it('shows error with getServerSideProps in pages/500 build', async () => {
await fs.move(pages500, `${pages500}.bak`)
await fs.writeFile(
pages500,
`
const page = () => 'custom 500 page'
export const getServerSideProps = () => ({ props: { a: 'b' } })
export default page
`
)
await fs.remove(join(appDir, '.next'))
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages500)
await fs.move(`${pages500}.bak`, pages500)
)
await fs.remove(join(appDir, '.next'))
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages500)
await fs.move(`${pages500}.bak`, pages500)
expect(stderr).toMatch(gip500Err)
expect(code).toBe(1)
})
expect(stderr).toMatch(gip500Err)
expect(code).toBe(1)
})
it('does build 500 statically with getInitialProps in _app and getStaticProps in pages/500', async () => {
await fs.writeFile(
pagesApp,
`
it('does build 500 statically with getInitialProps in _app and getStaticProps in pages/500', async () => {
await fs.writeFile(
pagesApp,
`
import App from 'next/app'
const page = ({ Component, pageProps }) => <Component {...pageProps} />
page.getInitialProps = (ctx) => App.getInitialProps(ctx)
export default page
`
)
await fs.rename(pages500, `${pages500}.bak`)
await fs.writeFile(
pages500,
`
)
await fs.rename(pages500, `${pages500}.bak`)
await fs.writeFile(
pages500,
`
const page = () => {
console.log('rendered 500')
return 'custom 500 page'
}
export default page
export const getStaticProps = () => {
return {
props: {}
}
}
`
)
await fs.remove(join(appDir, '.next'))
const {
stderr,
stdout: buildStdout,
code,
} = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
)
await fs.remove(join(appDir, '.next'))
const {
stderr,
stdout: buildStdout,
code,
} = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})
await fs.remove(pagesApp)
await fs.remove(pages500)
await fs.rename(`${pages500}.bak`, pages500)
expect(stderr).not.toMatch(gip500Err)
expect(buildStdout).toContain('rendered 500')
expect(code).toBe(0)
expect(
await fs.pathExists(join(appDir, '.next/server/pages/500.html'))
).toBe(true)
let appStdout = ''
const appPort = await findPort()
const app = await nextStart(appDir, appPort, {
onStdout(msg) {
appStdout += msg || ''
},
onStderr(msg) {
appStdout += msg || ''
},
})
await renderViaHTTP(appPort, '/err')
await killApp(app)
expect(appStdout).not.toContain('rendered 500')
})
await fs.remove(pagesApp)
await fs.remove(pages500)
await fs.rename(`${pages500}.bak`, pages500)
expect(stderr).not.toMatch(gip500Err)
expect(buildStdout).toContain('rendered 500')
expect(code).toBe(0)
expect(
await fs.pathExists(join(appDir, '.next/server/pages/500.html'))
).toBe(true)
let appStdout = ''
const appPort = await findPort()
const app = await nextStart(appDir, appPort, {
onStdout(msg) {
appStdout += msg || ''
},
onStderr(msg) {
appStdout += msg || ''
},
})
await renderViaHTTP(appPort, '/err')
await killApp(app)
expect(appStdout).not.toContain('rendered 500')
})
it('does not build 500 statically with no pages/500 and getServerSideProps in _error', async () => {
await fs.rename(pages500, `${pages500}.bak`)
await fs.writeFile(
pagesError,
`
it('does not build 500 statically with no pages/500 and getServerSideProps in _error', async () => {
await fs.rename(pages500, `${pages500}.bak`)
await fs.writeFile(
pagesError,
`
function Error({ statusCode }) {
return <p>Error status: {statusCode}</p>
}
export const getServerSideProps = ({ req, res, err }) => {
console.error('called _error getServerSideProps')
if (req.url === '/500') {
throw new Error('should not export /500')
}
return {
props: {
statusCode: res && res.statusCode ? res.statusCode : err ? err.statusCode : 404
}
}
}
export default Error
`
)
await fs.remove(join(appDir, '.next'))
const { stderr: buildStderr, code } = await nextBuild(appDir, [], {
stderr: true,
)
await fs.remove(join(appDir, '.next'))
const { stderr: buildStderr, code } = await nextBuild(appDir, [], {
stderr: true,
})
await fs.rename(`${pages500}.bak`, pages500)
await fs.remove(pagesError)
console.log(buildStderr)
expect(buildStderr).not.toMatch(gip500Err)
expect(code).toBe(0)
expect(
await fs.pathExists(join(appDir, '.next/server/pages/500.html'))
).toBe(false)
let appStderr = ''
const appPort = await findPort()
const app = await nextStart(appDir, appPort, {
onStderr(msg) {
appStderr += msg || ''
},
})
await renderViaHTTP(appPort, '/err')
await killApp(app)
expect(appStderr).toContain('called _error getServerSideProps')
})
await fs.rename(`${pages500}.bak`, pages500)
await fs.remove(pagesError)
console.log(buildStderr)
expect(buildStderr).not.toMatch(gip500Err)
expect(code).toBe(0)
expect(
await fs.pathExists(join(appDir, '.next/server/pages/500.html'))
).toBe(false)
let appStderr = ''
const appPort = await findPort()
const app = await nextStart(appDir, appPort, {
onStderr(msg) {
appStderr += msg || ''
},
})
await renderViaHTTP(appPort, '/err')
await killApp(app)
expect(appStderr).toContain('called _error getServerSideProps')
})
})
}
)
describe('development mode', () => {
it('does not show error with getStaticProps in pages/500 dev', async () => {

View file

@ -60,7 +60,7 @@ const runTests = (mode = 'server') => {
}
describe('500 Page Support', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
appPort = await findPort()
@ -100,18 +100,21 @@ describe('500 Page Support', () => {
expect(stderr).toMatch(gip500Err)
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests('server')
})
;(process.env.TURBOPACK ? describe.skip : describe)(
runTests('server')
}
)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode 2',
() => {
it('does not build 500 statically with getInitialProps in _app', async () => {
@ -119,7 +122,7 @@ describe('500 Page Support', () => {
pagesApp,
`
import App from 'next/app'
const page = ({ Component, pageProps }) => <Component {...pageProps} />
page.getInitialProps = (ctx) => App.getInitialProps(ctx)
export default page
@ -233,19 +236,19 @@ describe('500 Page Support', () => {
function Error({ statusCode }) {
return <p>Error status: {statusCode}</p>
}
Error.getInitialProps = ({ req, res, err }) => {
console.error('called _error.getInitialProps')
if (req.url === '/500') {
throw new Error('should not export /500')
}
return {
statusCode: res && res.statusCode ? res.statusCode : err ? err.statusCode : 404
}
}
export default Error
`
)
@ -284,19 +287,19 @@ describe('500 Page Support', () => {
function Error({ statusCode }) {
return <p>Error status: {statusCode}</p>
}
Error.getInitialProps = ({ req, res, err }) => {
console.error('called _error.getInitialProps')
if (req.url === '/500') {
throw new Error('should not export /500')
}
return {
statusCode: res && res.statusCode ? res.statusCode : err ? err.statusCode : 404
}
}
export default Error
`
)
@ -306,18 +309,18 @@ describe('500 Page Support', () => {
function App({ pageProps, Component }) {
return <Component {...pageProps} />
}
App.getInitialProps = async ({ Component, ctx }) => {
// throw _app GIP err here
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
export default App
`
)

View file

@ -19,105 +19,108 @@ let cdnAccessLog = []
const nextConfig = new File(path.resolve(__dirname, '../next.config.js'))
describe('absolute assetPrefix with path prefix', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
cdnPort = await findPort()
// lightweight http proxy
cdn = http.createServer((clientReq, clientRes) => {
const proxyPath = clientReq.url.slice('/path-prefix'.length)
cdnAccessLog.push(proxyPath)
const proxyReq = http.request(
{
hostname: 'localhost',
port: appPort,
path: proxyPath,
method: clientReq.method,
headers: clientReq.headers,
},
(proxyRes) => {
// cdn must be configured to allow requests from this origin
proxyRes.headers[
'Access-Control-Allow-Origin'
] = `http://localhost:${appPort}`
clientRes.writeHead(proxyRes.statusCode, proxyRes.headers)
proxyRes.pipe(clientRes, { end: true })
}
)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
cdnPort = await findPort()
// lightweight http proxy
cdn = http.createServer((clientReq, clientRes) => {
const proxyPath = clientReq.url.slice('/path-prefix'.length)
cdnAccessLog.push(proxyPath)
const proxyReq = http.request(
{
hostname: 'localhost',
port: appPort,
path: proxyPath,
method: clientReq.method,
headers: clientReq.headers,
},
(proxyRes) => {
// cdn must be configured to allow requests from this origin
proxyRes.headers[
'Access-Control-Allow-Origin'
] = `http://localhost:${appPort}`
clientRes.writeHead(proxyRes.statusCode, proxyRes.headers)
proxyRes.pipe(clientRes, { end: true })
}
)
clientReq.pipe(proxyReq, { end: true })
clientReq.pipe(proxyReq, { end: true })
})
await new Promise((resolve) => cdn.listen(cdnPort, resolve))
nextConfig.replace('__CDN_PORT__', cdnPort)
await nextBuild(appDir)
buildId = await fs.readFile(
path.resolve(__dirname, '../.next/BUILD_ID'),
'utf8'
)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
await new Promise((resolve) => cdn.listen(cdnPort, resolve))
nextConfig.replace('__CDN_PORT__', cdnPort)
await nextBuild(appDir)
buildId = await fs.readFile(
path.resolve(__dirname, '../.next/BUILD_ID'),
'utf8'
)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterEach(() => {
cdnAccessLog = []
})
afterEach(() => {
cdnAccessLog = []
})
afterAll(() => killApp(app))
afterAll(() => cdn.close())
afterAll(() => nextConfig.restore())
afterAll(() => killApp(app))
afterAll(() => cdn.close())
afterAll(() => nextConfig.restore())
it('should not fetch static data from a CDN', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#about-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('hello')
expect(cdnAccessLog).not.toContain(`/_next/data/${buildId}/about.json`)
})
it('should not fetch static data from a CDN', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#about-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('hello')
expect(cdnAccessLog).not.toContain(`/_next/data/${buildId}/about.json`)
})
it('should fetch from cache correctly', async () => {
const browser = await webdriver(appPort, '/')
await browser.eval('window.clientSideNavigated = true')
await browser.waitForElementByCss('#about-link').click()
await browser.waitForElementByCss('#prop')
await browser.back()
await browser.waitForElementByCss('#about-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('hello')
expect(await browser.eval('window.clientSideNavigated')).toBe(true)
expect(
cdnAccessLog.filter(
(path) => path === `/_next/data/${buildId}/about.json`
it('should fetch from cache correctly', async () => {
const browser = await webdriver(appPort, '/')
await browser.eval('window.clientSideNavigated = true')
await browser.waitForElementByCss('#about-link').click()
await browser.waitForElementByCss('#prop')
await browser.back()
await browser.waitForElementByCss('#about-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('hello')
expect(await browser.eval('window.clientSideNavigated')).toBe(true)
expect(
cdnAccessLog.filter(
(path) => path === `/_next/data/${buildId}/about.json`
)
).toHaveLength(0)
})
it('should work with getStaticPaths prerendered', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#gsp-prerender-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('prerendered')
expect(cdnAccessLog).not.toContain(
`/_next/data/${buildId}/gsp-fallback/prerendered.json`
)
).toHaveLength(0)
})
})
it('should work with getStaticPaths prerendered', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#gsp-prerender-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('prerendered')
expect(cdnAccessLog).not.toContain(
`/_next/data/${buildId}/gsp-fallback/prerendered.json`
)
})
it('should work with getStaticPaths fallback', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#gsp-fallback-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('fallback')
expect(cdnAccessLog).not.toContain(
`/_next/data/${buildId}/gsp-fallback/fallback.json`
)
})
it('should work with getStaticPaths fallback', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#gsp-fallback-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('fallback')
expect(cdnAccessLog).not.toContain(
`/_next/data/${buildId}/gsp-fallback/fallback.json`
)
})
it('should work with getServerSideProps', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#gssp-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('foo')
expect(cdnAccessLog).not.toContain(
`/_next/data/${buildId}/gssp.json?prop=foo`
)
})
})
it('should work with getServerSideProps', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#gssp-link').click()
const prop = await browser.waitForElementByCss('#prop').text()
expect(prop).toBe('foo')
expect(cdnAccessLog).not.toContain(
`/_next/data/${buildId}/gssp.json?prop=foo`
)
})
}
)
})

View file

@ -13,106 +13,113 @@ const nextConfig = new File(join(appDir, 'next.config.js'))
let buildOutput
describe('AMP Validation on Export', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
const { stdout = '', stderr = '' } = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
const { stdout = '', stderr = '' } = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
})
buildOutput = stdout + stderr
})
buildOutput = stdout + stderr
})
it('should have shown errors during build', async () => {
expect(buildOutput).toMatch(
/error.*The mandatory attribute 'height' is missing in tag 'amp-video'\./
)
expect(existsSync(outDir)).toBeFalse()
})
it('should have shown errors during build', async () => {
expect(buildOutput).toMatch(
/error.*The mandatory attribute 'height' is missing in tag 'amp-video'\./
)
expect(existsSync(outDir)).toBeFalse()
})
// this is now an error instead of a warning
it.skip('shows AMP warning without throwing error', async () => {
nextConfig.replace(
'// exportPathMap',
`exportPathMap: function(defaultMap) {
// this is now an error instead of a warning
it.skip('shows AMP warning without throwing error', async () => {
nextConfig.replace(
'// exportPathMap',
`exportPathMap: function(defaultMap) {
return {
'/cat': { page: '/cat' },
}
},`
)
try {
const { stdout, stderr } = await runNextCommand(['export', appDir], {
stdout: true,
stderr: true,
})
expect(stdout).toMatch(
/error.*The mandatory attribute 'height' is missing in tag 'amp-video'\./
)
await expect(access(join(outDir, 'cat.html'))).resolves.toBe(undefined)
await expect(stderr).not.toMatch(
/Found conflicting amp tag "meta" with conflicting prop name="viewport"/
)
} finally {
nextConfig.restore()
}
})
// img instead of amp-img no longer shows a warning
it.skip('throws error on AMP error', async () => {
nextConfig.replace(
'// exportPathMap',
`exportPathMap: function(defaultMap) {
try {
const { stdout, stderr } = await runNextCommand(['export', appDir], {
stdout: true,
stderr: true,
})
expect(stdout).toMatch(
/error.*The mandatory attribute 'height' is missing in tag 'amp-video'\./
)
await expect(access(join(outDir, 'cat.html'))).resolves.toBe(
undefined
)
await expect(stderr).not.toMatch(
/Found conflicting amp tag "meta" with conflicting prop name="viewport"/
)
} finally {
nextConfig.restore()
}
})
// img instead of amp-img no longer shows a warning
it.skip('throws error on AMP error', async () => {
nextConfig.replace(
'// exportPathMap',
`exportPathMap: function(defaultMap) {
return {
'/dog': { page: '/dog' },
}
},`
)
try {
const { stdout, stderr } = await runNextCommand(['export', appDir], {
stdout: true,
stderr: true,
})
expect(stdout).toMatch(
/error.*The parent tag of tag 'img' is 'div', but it can only be 'i-amphtml-sizer-intrinsic'\./
)
await expect(access(join(outDir, 'dog.html'))).resolves.toBe(undefined)
await expect(stderr).not.toMatch(
/Found conflicting amp tag "meta" with conflicting prop name="viewport"/
)
} finally {
nextConfig.restore()
}
})
// img instead of amp-img no longer shows a warning
it.skip('shows warning and error when throwing error', async () => {
nextConfig.replace(
'// exportPathMap',
`exportPathMap: function(defaultMap) {
try {
const { stdout, stderr } = await runNextCommand(['export', appDir], {
stdout: true,
stderr: true,
})
expect(stdout).toMatch(
/error.*The parent tag of tag 'img' is 'div', but it can only be 'i-amphtml-sizer-intrinsic'\./
)
await expect(access(join(outDir, 'dog.html'))).resolves.toBe(
undefined
)
await expect(stderr).not.toMatch(
/Found conflicting amp tag "meta" with conflicting prop name="viewport"/
)
} finally {
nextConfig.restore()
}
})
// img instead of amp-img no longer shows a warning
it.skip('shows warning and error when throwing error', async () => {
nextConfig.replace(
'// exportPathMap',
`exportPathMap: function(defaultMap) {
return {
'/dog-cat': { page: '/dog-cat' },
}
},`
)
)
try {
const { stdout, stderr } = await runNextCommand(['export', appDir], {
stdout: true,
stderr: true,
})
expect(stdout).toMatch(
/error.*The parent tag of tag 'img' is 'div', but it can only be 'i-amphtml-sizer-intrinsic'\./
)
await expect(access(join(outDir, 'dog-cat.html'))).resolves.toBe(
undefined
)
await expect(stderr).not.toMatch(
/Found conflicting amp tag "meta" with conflicting prop name="viewport"/
)
} finally {
nextConfig.restore()
}
})
})
try {
const { stdout, stderr } = await runNextCommand(['export', appDir], {
stdout: true,
stderr: true,
})
expect(stdout).toMatch(
/error.*The parent tag of tag 'img' is 'div', but it can only be 'i-amphtml-sizer-intrinsic'\./
)
await expect(access(join(outDir, 'dog-cat.html'))).resolves.toBe(
undefined
)
await expect(stderr).not.toMatch(
/Found conflicting amp tag "meta" with conflicting prop name="viewport"/
)
} finally {
nextConfig.restore()
}
})
}
)
})

View file

@ -14,43 +14,46 @@ let appPort
const appDir = join(__dirname, '../')
describe('AMP Custom Optimizer', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should build and start for static page', async () => {
const { code } = await nextBuild(appDir)
expect(code).toBe(0)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should build and start for static page', async () => {
const { code } = await nextBuild(appDir)
expect(code).toBe(0)
appPort = await findPort()
app = await nextStart(appDir, appPort)
appPort = await findPort()
app = await nextStart(appDir, appPort)
const html = await renderViaHTTP(appPort, '/')
await killApp(app)
const html = await renderViaHTTP(appPort, '/')
await killApp(app)
expect(html).toContain(
'amp-twitter width="500" height="500" layout="responsive" data-tweetid="1159145442896166912"'
)
expect(html).toContain('i-amphtml-version="001515617716922"')
expect(html).toContain(
'script async src="https://cdn.ampproject.org/rtv/001515617716922/v0.mjs"'
)
})
expect(html).toContain(
'amp-twitter width="500" height="500" layout="responsive" data-tweetid="1159145442896166912"'
)
expect(html).toContain('i-amphtml-version="001515617716922"')
expect(html).toContain(
'script async src="https://cdn.ampproject.org/rtv/001515617716922/v0.mjs"'
)
})
it('should build and start for dynamic page', async () => {
const { code } = await nextBuild(appDir)
expect(code).toBe(0)
it('should build and start for dynamic page', async () => {
const { code } = await nextBuild(appDir)
expect(code).toBe(0)
appPort = await findPort()
app = await nextStart(appDir, appPort)
appPort = await findPort()
app = await nextStart(appDir, appPort)
const html = await renderViaHTTP(appPort, '/dynamic')
await killApp(app)
const html = await renderViaHTTP(appPort, '/dynamic')
await killApp(app)
expect(html).toContain(
'amp-img width="500" height="500" layout="responsive" src="https://amp.dev/static/samples/img/story_dog2_portrait.jpg"'
)
expect(html).toContain('i-amphtml-version="001515617716922"')
expect(html).toContain(
'script async src="https://cdn.ampproject.org/rtv/001515617716922/v0.mjs"'
)
})
})
expect(html).toContain(
'amp-img width="500" height="500" layout="responsive" src="https://amp.dev/static/samples/img/story_dog2_portrait.jpg"'
)
expect(html).toContain('i-amphtml-version="001515617716922"')
expect(html).toContain(
'script async src="https://cdn.ampproject.org/rtv/001515617716922/v0.mjs"'
)
})
}
)
})

View file

@ -18,7 +18,7 @@ const appDir = join(__dirname, '../')
;(process.env.TURBOPACK ? describe.skip : describe)(
'AMP Custom Validator',
() => {
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should build and start successfully', async () => {
@ -37,7 +37,7 @@ const appDir = join(__dirname, '../')
)
describe('development mode', () => {
it('should run in dev mode successfully', async () => {
it('should run in development mode successfully', async () => {
let stderr = ''
appPort = await findPort()

View file

@ -16,21 +16,24 @@ let appPort
let app
describe('AMP Fragment Styles', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir, [])
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir, [])
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
it('adds styles from fragment in AMP mode correctly', async () => {
const html = await renderViaHTTP(appPort, '/', { amp: 1 })
await validateAMP(html)
const $ = cheerio.load(html)
const styles = $('style[amp-custom]').text()
expect(styles).toMatch(/background:(.*|)hotpink/)
expect(styles).toMatch(/font-size:(.*|)16\.4px/)
})
})
it('adds styles from fragment in AMP mode correctly', async () => {
const html = await renderViaHTTP(appPort, '/', { amp: 1 })
await validateAMP(html)
const $ = cheerio.load(html)
const styles = $('style[amp-custom]').text()
expect(styles).toMatch(/background:(.*|)hotpink/)
expect(styles).toMatch(/font-size:(.*|)16\.4px/)
})
}
)
})

View file

@ -99,18 +99,21 @@ const runTests = (isDev = false) => {
}
describe('AMP SSG Support', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
// TODO: use browser instead to do checks that now need filesystem access
builtServerPagesDir = join(appDir, '.next', 'server', 'pages')
})
afterAll(() => killApp(app))
runTests()
})
describe('dev mode', () => {
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
// TODO: use browser instead to do checks that now need filesystem access
builtServerPagesDir = join(appDir, '.next', 'server', 'pages')
})
afterAll(() => killApp(app))
runTests()
}
)
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -119,7 +122,7 @@ describe('AMP SSG Support', () => {
runTests(true)
})
describe('export mode', () => {
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
let buildId

View file

@ -25,225 +25,228 @@ let app
const context = {}
describe('AMP Usage', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
let output = ''
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
let output = ''
beforeAll(async () => {
await rename(
join(appDir, 'pages/invalid-amp.js'),
join(appDir, 'pages/invalid-amp.js.bak')
)
const result = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
})
output = stripAnsi(result.stdout + result.stderr)
appPort = context.appPort = await findPort()
app = await nextStart(appDir, context.appPort)
})
afterAll(async () => {
await rename(
join(appDir, 'pages/invalid-amp.js.bak'),
join(appDir, 'pages/invalid-amp.js')
)
return killApp(app)
})
it('should have amp optimizer in trace', async () => {
const trace = JSON.parse(
readFileSync(join(appDir, '.next/next-server.js.nft.json'), 'utf8')
)
expect(
trace.files.some((file) =>
file.replace(/\\/g, '/').includes('@ampproject/toolbox-optimizer')
beforeAll(async () => {
await rename(
join(appDir, 'pages/invalid-amp.js'),
join(appDir, 'pages/invalid-amp.js.bak')
)
).toBe(true)
})
const result = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
})
it('should not contain missing files warning', async () => {
expect(output).toContain('Compiled successfully')
expect(output).not.toContain('Could not find files for')
})
output = stripAnsi(result.stdout + result.stderr)
describe('With basic usage', () => {
it('should render the page', async () => {
const html = await renderViaHTTP(appPort, '/')
expect(html).toMatch(/Hello World/)
appPort = context.appPort = await findPort()
app = await nextStart(appDir, context.appPort)
})
})
describe('With basic AMP usage', () => {
it('should render the page as valid AMP', async () => {
const html = await renderViaHTTP(appPort, '/?amp=1')
await validateAMP(html)
expect(html).toMatch(/Hello World/)
const $ = cheerio.load(html)
expect($('.abc')).toHaveLength(1)
})
it('should render the page without leaving render target', async () => {
const html = await renderViaHTTP(appPort, '/special-chars')
await validateAMP(html)
expect(html).not.toContain('__NEXT_AMP_RENDER_TARGET__')
})
it('should not output client pages for AMP only', async () => {
const browser = await webdriver(appPort, '/nav')
await browser.elementByCss('#only-amp-link').click()
const result = await browser.eval('window.NAV_PAGE_LOADED')
expect(result).toBeFalsy()
})
it('should not output client pages for AMP only with config exported after declaration', async () => {
const browser = await webdriver(appPort, '/nav')
await browser.elementByCss('#var-before-export-link').click()
const result = await browser.eval('window.NAV_PAGE_LOADED')
expect(result).toBeFalsy()
})
it('should drop custom scripts', async () => {
const html = await renderViaHTTP(appPort, '/custom-scripts')
expect(html).not.toMatch(/src='\/im-not-allowed\.js'/)
expect(html).not.toMatch(/console\.log("I'm not either :p")'/)
})
it('should not drop custom amp scripts', async () => {
const html = await renderViaHTTP(appPort, '/amp-script?amp=1')
await validateAMP(html)
})
it('should optimize clean', async () => {
const html = await renderViaHTTP(appPort, '/only-amp')
await validateAMP(html)
})
it('should auto import extensions', async () => {
const html = await renderViaHTTP(appPort, '/auto-import')
await validateAMP(html)
})
})
describe('With AMP context', () => {
it('should render the normal page that uses the AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook')
expect(html).toMatch(/Hello others/)
expect(html).toMatch(/no AMP for you\.\.\./)
})
it('should render the AMP page that uses the AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook?amp=1')
await validateAMP(html)
expect(html).toMatch(/Hello AMP/)
expect(html).toMatch(/AMP Power!!!/)
})
it('should render nested normal page with AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/nested')
expect(html).toMatch(/Hello others/)
})
it('should render nested AMP page with AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/nested?amp=1')
await validateAMP(html)
expect(html).toMatch(/Hello AMP/)
})
})
describe('canonical amphtml', () => {
it('should render link rel amphtml', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook')
const $ = cheerio.load(html)
expect($('link[rel=amphtml]').first().attr('href')).toBe(
'http://localhost:1234/use-amp-hook.amp'
afterAll(async () => {
await rename(
join(appDir, 'pages/invalid-amp.js.bak'),
join(appDir, 'pages/invalid-amp.js')
)
return killApp(app)
})
it('should render amphtml from provided rel link', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook.amp')
await validateAMP(html)
})
it('should render link rel amphtml with existing query', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook?hello=1')
expect(html).not.toMatch(/&amp;amp=1/)
})
it('should render the AMP page that uses the AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook?amp=1')
const $ = cheerio.load(html)
await validateAMP(html)
expect($('link[rel=canonical]').first().attr('href')).toBe(
'http://localhost:1234/use-amp-hook'
it('should have amp optimizer in trace', async () => {
const trace = JSON.parse(
readFileSync(join(appDir, '.next/next-server.js.nft.json'), 'utf8')
)
expect(
trace.files.some((file) =>
file.replace(/\\/g, '/').includes('@ampproject/toolbox-optimizer')
)
).toBe(true)
})
it('should render a canonical regardless of amp-only status (explicit)', async () => {
const html = await renderViaHTTP(appPort, '/only-amp')
const $ = cheerio.load(html)
await validateAMP(html)
expect($('link[rel=canonical]').first().attr('href')).toBe(
'http://localhost:1234/only-amp'
)
it('should not contain missing files warning', async () => {
expect(output).toContain('Compiled successfully')
expect(output).not.toContain('Could not find files for')
})
it('should not render amphtml link tag with no AMP page', async () => {
const html = await renderViaHTTP(appPort, '/normal')
const $ = cheerio.load(html)
expect($('link[rel=amphtml]').first().attr('href')).not.toBeTruthy()
describe('With basic usage', () => {
it('should render the page', async () => {
const html = await renderViaHTTP(appPort, '/')
expect(html).toMatch(/Hello World/)
})
})
it('should remove conflicting amp tags', async () => {
const html = await renderViaHTTP(appPort, '/conflicting-tag?amp=1')
const $ = cheerio.load(html)
await validateAMP(html)
expect($('meta[name=viewport]').attr('content')).not.toBe(
'something :p'
)
describe('With basic AMP usage', () => {
it('should render the page as valid AMP', async () => {
const html = await renderViaHTTP(appPort, '/?amp=1')
await validateAMP(html)
expect(html).toMatch(/Hello World/)
const $ = cheerio.load(html)
expect($('.abc')).toHaveLength(1)
})
it('should render the page without leaving render target', async () => {
const html = await renderViaHTTP(appPort, '/special-chars')
await validateAMP(html)
expect(html).not.toContain('__NEXT_AMP_RENDER_TARGET__')
})
it('should not output client pages for AMP only', async () => {
const browser = await webdriver(appPort, '/nav')
await browser.elementByCss('#only-amp-link').click()
const result = await browser.eval('window.NAV_PAGE_LOADED')
expect(result).toBeFalsy()
})
it('should not output client pages for AMP only with config exported after declaration', async () => {
const browser = await webdriver(appPort, '/nav')
await browser.elementByCss('#var-before-export-link').click()
const result = await browser.eval('window.NAV_PAGE_LOADED')
expect(result).toBeFalsy()
})
it('should drop custom scripts', async () => {
const html = await renderViaHTTP(appPort, '/custom-scripts')
expect(html).not.toMatch(/src='\/im-not-allowed\.js'/)
expect(html).not.toMatch(/console\.log("I'm not either :p")'/)
})
it('should not drop custom amp scripts', async () => {
const html = await renderViaHTTP(appPort, '/amp-script?amp=1')
await validateAMP(html)
})
it('should optimize clean', async () => {
const html = await renderViaHTTP(appPort, '/only-amp')
await validateAMP(html)
})
it('should auto import extensions', async () => {
const html = await renderViaHTTP(appPort, '/auto-import')
await validateAMP(html)
})
})
it('should allow manually setting canonical', async () => {
const html = await renderViaHTTP(appPort, '/manual-rels?amp=1')
const $ = cheerio.load(html)
await validateAMP(html)
expect($('link[rel=canonical]').attr('href')).toBe(
'/my-custom-canonical'
)
describe('With AMP context', () => {
it('should render the normal page that uses the AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook')
expect(html).toMatch(/Hello others/)
expect(html).toMatch(/no AMP for you\.\.\./)
})
it('should render the AMP page that uses the AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook?amp=1')
await validateAMP(html)
expect(html).toMatch(/Hello AMP/)
expect(html).toMatch(/AMP Power!!!/)
})
it('should render nested normal page with AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/nested')
expect(html).toMatch(/Hello others/)
})
it('should render nested AMP page with AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/nested?amp=1')
await validateAMP(html)
expect(html).toMatch(/Hello AMP/)
})
})
it('should allow manually setting amphtml rel', async () => {
const html = await renderViaHTTP(appPort, '/manual-rels')
const $ = cheerio.load(html)
expect($('link[rel=amphtml]').attr('href')).toBe('/my-custom-amphtml')
expect($('link[rel=amphtml]')).toHaveLength(1)
})
})
describe('canonical amphtml', () => {
it('should render link rel amphtml', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook')
const $ = cheerio.load(html)
expect($('link[rel=amphtml]').first().attr('href')).toBe(
'http://localhost:1234/use-amp-hook.amp'
)
})
describe('combined styles', () => {
it('should combine style tags', async () => {
const html = await renderViaHTTP(appPort, '/styled?amp=1')
const $ = cheerio.load(html)
expect($('style[amp-custom]').first().text()).toMatch(
/div.jsx-[a-zA-Z0-9]{1,}{color:red}span.jsx-[a-zA-Z0-9]{1,}{color:(?:blue|#00f)}body{background-color:green}/
)
it('should render amphtml from provided rel link', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook.amp')
await validateAMP(html)
})
it('should render link rel amphtml with existing query', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook?hello=1')
expect(html).not.toMatch(/&amp;amp=1/)
})
it('should render the AMP page that uses the AMP hook', async () => {
const html = await renderViaHTTP(appPort, '/use-amp-hook?amp=1')
const $ = cheerio.load(html)
await validateAMP(html)
expect($('link[rel=canonical]').first().attr('href')).toBe(
'http://localhost:1234/use-amp-hook'
)
})
it('should render a canonical regardless of amp-only status (explicit)', async () => {
const html = await renderViaHTTP(appPort, '/only-amp')
const $ = cheerio.load(html)
await validateAMP(html)
expect($('link[rel=canonical]').first().attr('href')).toBe(
'http://localhost:1234/only-amp'
)
})
it('should not render amphtml link tag with no AMP page', async () => {
const html = await renderViaHTTP(appPort, '/normal')
const $ = cheerio.load(html)
expect($('link[rel=amphtml]').first().attr('href')).not.toBeTruthy()
})
it('should remove conflicting amp tags', async () => {
const html = await renderViaHTTP(appPort, '/conflicting-tag?amp=1')
const $ = cheerio.load(html)
await validateAMP(html)
expect($('meta[name=viewport]').attr('content')).not.toBe(
'something :p'
)
})
it('should allow manually setting canonical', async () => {
const html = await renderViaHTTP(appPort, '/manual-rels?amp=1')
const $ = cheerio.load(html)
await validateAMP(html)
expect($('link[rel=canonical]').attr('href')).toBe(
'/my-custom-canonical'
)
})
it('should allow manually setting amphtml rel', async () => {
const html = await renderViaHTTP(appPort, '/manual-rels')
const $ = cheerio.load(html)
expect($('link[rel=amphtml]').attr('href')).toBe('/my-custom-amphtml')
expect($('link[rel=amphtml]')).toHaveLength(1)
})
})
it('should remove sourceMaps from styles', async () => {
const html = await renderViaHTTP(appPort, '/styled?amp=1')
const $ = cheerio.load(html)
const styles = $('style[amp-custom]').first().text()
describe('combined styles', () => {
it('should combine style tags', async () => {
const html = await renderViaHTTP(appPort, '/styled?amp=1')
const $ = cheerio.load(html)
expect($('style[amp-custom]').first().text()).toMatch(
/div.jsx-[a-zA-Z0-9]{1,}{color:red}span.jsx-[a-zA-Z0-9]{1,}{color:(?:blue|#00f)}body{background-color:green}/
)
})
expect(styles).not.toMatch(/\/\*@ sourceURL=.*?\*\//)
expect(styles).not.toMatch(/\/\*# sourceMappingURL=.*\*\//)
it('should remove sourceMaps from styles', async () => {
const html = await renderViaHTTP(appPort, '/styled?amp=1')
const $ = cheerio.load(html)
const styles = $('style[amp-custom]').first().text()
expect(styles).not.toMatch(/\/\*@ sourceURL=.*?\*\//)
expect(styles).not.toMatch(/\/\*# sourceMappingURL=.*\*\//)
})
})
})
})
}
)
describe('AMP dev no-warn', () => {
let dynamicAppPort
@ -269,7 +272,7 @@ describe('AMP Usage', () => {
})
})
describe('AMP dev mode', () => {
describe('AMP development mode', () => {
let dynamicAppPort
let ampDynamic
let output = ''

View file

@ -59,14 +59,17 @@ describe('API routes', () => {
runTests()
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
runTests()
}
)
})

View file

@ -547,7 +547,7 @@ function runTests(dev = false) {
expect(getPageFileFromPagesManifest(appDir, '/api/users')).toBeTruthy()
})
it('should show warning when the API resolves without ending the request in dev mode', async () => {
it('should show warning when the API resolves without ending the request in development mode', async () => {
const controller = new AbortController()
setTimeout(() => {
controller.abort()
@ -644,15 +644,18 @@ describe('API routes', () => {
runTests(true)
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
mode = 'server'
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
mode = 'server'
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
runTests()
}
)
})

View file

@ -11,79 +11,84 @@ import {
} from './utils'
describe('app dir - with output export (next dev / next build)', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should throw when exportPathMap configured', async () => {
nextConfig.replace(
'trailingSlash: true,',
`trailingSlash: true,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
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 error when running next export', async () => {
await fs.remove(distDir)
await fs.remove(exportDir)
nextConfig.delete()
try {
await nextBuild(appDir)
expect(await getFiles()).toEqual([])
let stdout = ''
let stderr = ''
let error = undefined
try {
await runNextCommand(['export', appDir], {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
} catch (e) {
error = e
}
expect(error).toBeDefined()
expect(stderr).toContain(
`\`next export\` has been removed in favor of '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(expectedWhenTrailingSlashTrue)
} finally {
nextConfig.restore()
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 error when running next export', async () => {
await fs.remove(distDir)
await fs.remove(exportDir)
nextConfig.delete()
try {
await nextBuild(appDir)
expect(await getFiles()).toEqual([])
let stdout = ''
let stderr = ''
let error = undefined
try {
await runNextCommand(['export', appDir], {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
} catch (e) {
error = e
}
expect(error).toBeDefined()
expect(stderr).toContain(
`\`next export\` has been removed in favor of '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(
expectedWhenTrailingSlashTrue
)
} finally {
nextConfig.restore()
await fs.remove(distDir)
await fs.remove(outputDir)
}
})
}
)
})

View file

@ -1,25 +1,28 @@
import { runTests } from './utils'
describe('app dir - with output export - dynamic missing gsp prod', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should error when dynamic route is missing generateStaticParams', async () => {
await runTests({
isDev: false,
dynamicPage: 'undefined',
generateStaticParamsOpt: 'set noop',
expectedErrMsg:
'Page "/another/[slug]" is missing "generateStaticParams()" so it cannot be used with "output: export" config.',
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should error when dynamic route is missing generateStaticParams', async () => {
await runTests({
isDev: false,
dynamicPage: 'undefined',
generateStaticParamsOpt: 'set noop',
expectedErrMsg:
'Page "/another/[slug]" is missing "generateStaticParams()" so it cannot be used with "output: export" config.',
})
})
})
it('should error when client component has generateStaticParams', async () => {
await runTests({
isDev: false,
dynamicPage: 'undefined',
generateStaticParamsOpt: 'set client',
expectedErrMsg:
'Page "/another/[slug]/page" cannot use both "use client" and export function "generateStaticParams()".',
it('should error when client component has generateStaticParams', async () => {
await runTests({
isDev: false,
dynamicPage: 'undefined',
generateStaticParamsOpt: 'set client',
expectedErrMsg:
'Page "/another/[slug]/page" cannot use both "use client" and export function "generateStaticParams()".',
})
})
})
})
}
)
})

View file

@ -1,21 +1,24 @@
import { runTests } from './utils'
describe('app dir - with output export - dynamic api route prod', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it.each([
{ dynamicApiRoute: 'undefined' },
{ dynamicApiRoute: "'error'" },
{ dynamicApiRoute: "'force-static'" },
{
dynamicApiRoute: "'force-dynamic'",
expectedErrMsg:
'export const dynamic = "force-dynamic" on page "/api/json" cannot be used with "output: export".',
},
])(
'should work in prod with dynamicApiRoute $dynamicApiRoute',
async ({ dynamicApiRoute, expectedErrMsg }) => {
await runTests({ isDev: false, dynamicApiRoute, expectedErrMsg })
}
)
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it.each([
{ dynamicApiRoute: 'undefined' },
{ dynamicApiRoute: "'error'" },
{ dynamicApiRoute: "'force-static'" },
{
dynamicApiRoute: "'force-dynamic'",
expectedErrMsg:
'export const dynamic = "force-dynamic" on page "/api/json" cannot be used with "output: export".',
},
])(
'should work in prod with dynamicApiRoute $dynamicApiRoute',
async ({ dynamicApiRoute, expectedErrMsg }) => {
await runTests({ isDev: false, dynamicApiRoute, expectedErrMsg })
}
)
}
)
})

View file

@ -1,21 +1,24 @@
import { runTests } from './utils'
describe('app dir - with output export - dynamic api route prod', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it.each([
{ dynamicPage: 'undefined' },
{ dynamicPage: "'error'" },
{ dynamicPage: "'force-static'" },
{
dynamicPage: "'force-dynamic'",
expectedErrMsg:
'Page with `dynamic = "force-dynamic"` couldn\'t be exported. `output: "export"` requires all pages be renderable statically',
},
])(
'should work in prod with dynamicPage $dynamicPage',
async ({ dynamicPage, expectedErrMsg }) => {
await runTests({ isDev: false, dynamicPage, expectedErrMsg })
}
)
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it.each([
{ dynamicPage: 'undefined' },
{ dynamicPage: "'error'" },
{ dynamicPage: "'force-static'" },
{
dynamicPage: "'force-dynamic'",
expectedErrMsg:
'Page with `dynamic = "force-dynamic"` couldn\'t be exported. `output: "export"` requires all pages be renderable statically',
},
])(
'should work in prod with dynamicPage $dynamicPage',
async ({ dynamicPage, expectedErrMsg }) => {
await runTests({ isDev: false, dynamicPage, expectedErrMsg })
}
)
}
)
})

View file

@ -18,45 +18,48 @@ const nextConfig = new File(join(appDir, 'next.config.js'))
let app
describe('app dir - with output export (next start)', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
afterEach(async () => {
await killApp(app)
nextConfig.restore()
await fs.remove(distDir)
await fs.remove(exportDir)
})
it('should error during next start with output export', async () => {
const { code } = await nextBuild(appDir)
expect(code).toBe(0)
const port = await findPort()
let stderr = ''
app = await nextStart(appDir, port, {
onStderr(msg: string) {
stderr += msg || ''
},
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
afterEach(async () => {
await killApp(app)
nextConfig.restore()
await fs.remove(distDir)
await fs.remove(exportDir)
})
await check(() => stderr, /error/i)
expect(stderr).toContain(
'"next start" does not work with "output: export" configuration. Use "npx serve@latest out" instead.'
)
})
it('should warn during next start with output standalone', async () => {
nextConfig.replace(`output: 'export'`, `output: 'standalone'`)
const { code } = await nextBuild(appDir)
expect(code).toBe(0)
const port = await findPort()
let stderr = ''
app = await nextStart(appDir, port, {
onStderr(msg: string) {
stderr += msg || ''
},
it('should error during next start with output export', async () => {
const { code } = await nextBuild(appDir)
expect(code).toBe(0)
const port = await findPort()
let stderr = ''
app = await nextStart(appDir, port, {
onStderr(msg: string) {
stderr += msg || ''
},
})
await check(() => stderr, /error/i)
expect(stderr).toContain(
'"next start" does not work with "output: export" configuration. Use "npx serve@latest out" instead.'
)
})
await check(() => stderr, /⚠/i)
expect(stderr).toContain(
`"next start" does not work with "output: standalone" configuration. Use "node .next/standalone/server.js" instead.`
)
})
})
it('should warn during next start with output standalone', async () => {
nextConfig.replace(`output: 'export'`, `output: 'standalone'`)
const { code } = await nextBuild(appDir)
expect(code).toBe(0)
const port = await findPort()
let stderr = ''
app = await nextStart(appDir, port, {
onStderr(msg: string) {
stderr += msg || ''
},
})
await check(() => stderr, /⚠/i)
expect(stderr).toContain(
`"next start" does not work with "output: standalone" configuration. Use "node .next/standalone/server.js" instead.`
)
})
}
)
})

View file

@ -1,12 +1,15 @@
import { runTests } from './utils'
describe('app dir - with output export - trailing slash prod', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it.each([{ trailingSlash: false }, { trailingSlash: true }])(
"should work in prod with trailingSlash '$trailingSlash'",
async ({ trailingSlash }) => {
await runTests({ isDev: false, trailingSlash })
}
)
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it.each([{ trailingSlash: false }, { trailingSlash: true }])(
"should work in prod with trailingSlash '$trailingSlash'",
async ({ trailingSlash }) => {
await runTests({ isDev: false, trailingSlash })
}
)
}
)
})

View file

@ -51,23 +51,26 @@ const respectsChunkAttachmentOrder = async () => {
}
describe('Root components import order', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
it(
'_app chunks should be attached to de dom before page chunks',
respectsChunkAttachmentOrder
)
it(
'root components should be imported in this order _document > _app > page in order to respect side effects',
respectsSideEffects
)
})
it(
'_app chunks should be attached to de dom before page chunks',
respectsChunkAttachmentOrder
)
it(
'root components should be imported in this order _document > _app > page in order to respect side effects',
respectsSideEffects
)
}
)
})
describe('development mode', () => {

View file

@ -16,27 +16,30 @@ let server
let app
describe('Custom Document Fragment Styles', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
app = nextServer({
dir: join(__dirname, '../'),
dev: false,
quiet: true,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
app = nextServer({
dir: join(__dirname, '../'),
dev: false,
quiet: true,
})
server = await startApp(app)
appPort = server.address().port
})
afterAll(() => stopApp(server))
server = await startApp(app)
appPort = server.address().port
})
afterAll(() => stopApp(server))
it('correctly adds styles from fragment styles key', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
it('correctly adds styles from fragment styles key', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
const styles = $('style').text()
expect(styles).toMatch(/background:(.*|)hotpink/)
expect(styles).toMatch(/font-size:(.*|)16\.4px/)
})
})
const styles = $('style').text()
expect(styles).toMatch(/background:(.*|)hotpink/)
expect(styles).toMatch(/font-size:(.*|)16\.4px/)
})
}
)
})

View file

@ -1,17 +1,20 @@
import { nextBuild } from 'next-test-utils'
import { join } from 'path'
describe('app-dynamic-error', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('throws an error when prerendering a page with config dynamic error', async () => {
const appDir = join(__dirname, '../../app-dynamic-error')
const { stderr, code } = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('throws an error when prerendering a page with config dynamic error', async () => {
const appDir = join(__dirname, '../../app-dynamic-error')
const { stderr, code } = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})
expect(stderr).toContain(
'Error occurred prerendering page "/dynamic-error"'
)
expect(code).toBe(1)
})
expect(stderr).toContain(
'Error occurred prerendering page "/dynamic-error"'
)
expect(code).toBe(1)
})
})
}
)
})

View file

@ -48,7 +48,7 @@ const runTests = () => {
}
describe('AppTree', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -56,13 +56,16 @@ describe('AppTree', () => {
afterAll(() => killApp(app))
runTests()
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
}
)
})

View file

@ -7,103 +7,106 @@ import { nextBuild } from 'next-test-utils'
const appDir = __dirname
describe('app type checking', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
let stderr, errors
beforeAll(async () => {
stderr = (await nextBuild(appDir, [], { stderr: true })).stderr
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
let stderr, errors
beforeAll(async () => {
stderr = (await nextBuild(appDir, [], { stderr: true })).stderr
errors = stderr.match(
/===== TS errors =====(.+)===== TS errors =====/s
)?.[1]
})
errors = stderr.match(
/===== TS errors =====(.+)===== TS errors =====/s
)?.[1]
})
it('should generate route types correctly and report link error', async () => {
// Make sure the d.ts file is generated
const dts = (
await fs.readFile(path.join(appDir, '.next', 'types', 'link.d.ts'))
).toString()
expect(dts.includes('`/dashboard/user/')).toBeTruthy()
expect(dts.includes('`/dashboard/another')).toBeTruthy()
it('should generate route types correctly and report link error', async () => {
// Make sure the d.ts file is generated
const dts = (
await fs.readFile(path.join(appDir, '.next', 'types', 'link.d.ts'))
).toString()
expect(dts.includes('`/dashboard/user/')).toBeTruthy()
expect(dts.includes('`/dashboard/another')).toBeTruthy()
// Check type checking errors
expect(errors).toContain(
'Type error: "/(newroot)/dashboard/another" is not an existing route. If it is intentional, please type it explicitly with `as Route`.'
)
// Check type checking errors
expect(errors).toContain(
'Type error: "/(newroot)/dashboard/another" is not an existing route. If it is intentional, please type it explicitly with `as Route`.'
)
// Make sure all errors were reported and other links passed type checking
const errorLines = [
...errors.matchAll(
/\.\/src\/app\/type-checks\/link\/page\.tsx:(\d+):/g
),
].map(([, line]) => +line)
// Make sure all errors were reported and other links passed type checking
const errorLines = [
...errors.matchAll(
/\.\/src\/app\/type-checks\/link\/page\.tsx:(\d+):/g
),
].map(([, line]) => +line)
const ST = 17
const ED = 34
expect(errorLines).toEqual(
Array.from({ length: ED - ST + 1 }, (_, i) => i + ST)
)
})
const ST = 17
const ED = 34
expect(errorLines).toEqual(
Array.from({ length: ED - ST + 1 }, (_, i) => i + ST)
)
})
it('should generate route types correctly and report router API errors', async () => {
// Make sure all errors were reported and other links passed type checking
const errorLines = [
...errors.matchAll(
/\.\/src\/app\/type-checks\/router\/page\.tsx:(\d+):/g
),
].map(([, line]) => +line)
it('should generate route types correctly and report router API errors', async () => {
// Make sure all errors were reported and other links passed type checking
const errorLines = [
...errors.matchAll(
/\.\/src\/app\/type-checks\/router\/page\.tsx:(\d+):/g
),
].map(([, line]) => +line)
const ST = 11
const ED = 13
expect(errorLines).toEqual(
Array.from({ length: ED - ST + 1 }, (_, i) => i + ST)
)
})
const ST = 11
const ED = 13
expect(errorLines).toEqual(
Array.from({ length: ED - ST + 1 }, (_, i) => i + ST)
)
})
it('should type check invalid entry exports', () => {
// Can't export arbitrary things.
expect(errors).toContain(`"foo" is not a valid Page export field.`)
it('should type check invalid entry exports', () => {
// Can't export arbitrary things.
expect(errors).toContain(`"foo" is not a valid Page export field.`)
// Can't export invalid fields.
expect(errors).toMatch(
/Invalid configuration "revalidate":\s+Expected "false | number (>= 0)", got "-1"/
)
// Can't export invalid fields.
expect(errors).toMatch(
/Invalid configuration "revalidate":\s+Expected "false | number (>= 0)", got "-1"/
)
// Avoid invalid argument types for exported functions.
expect(errors).toMatch(
/Page "src\/app\/type-checks\/config\/page\.tsx" has an invalid "default" export:\s+Type "{ foo: string; }" is not valid/
)
expect(errors).toMatch(
/Page "src\/app\/type-checks\/config\/page\.tsx" has an invalid "generateMetadata" export:\s+Type "{ s: number; }" is not valid/
)
expect(errors).toMatch(
/Page "src\/app\/type-checks\/config\/page\.tsx" has an invalid "generateStaticParams" export:\s+Type "string" is not valid/
)
// Avoid invalid argument types for exported functions.
expect(errors).toMatch(
/Page "src\/app\/type-checks\/config\/page\.tsx" has an invalid "default" export:\s+Type "{ foo: string; }" is not valid/
)
expect(errors).toMatch(
/Page "src\/app\/type-checks\/config\/page\.tsx" has an invalid "generateMetadata" export:\s+Type "{ s: number; }" is not valid/
)
expect(errors).toMatch(
/Page "src\/app\/type-checks\/config\/page\.tsx" has an invalid "generateStaticParams" export:\s+Type "string" is not valid/
)
// Avoid invalid return types for exported functions.
expect(errors).toContain(
`"Promise<number>" is not a valid generateStaticParams return type`
)
// Avoid invalid return types for exported functions.
expect(errors).toContain(
`"Promise<number>" is not a valid generateStaticParams return type`
)
// Can't export arbitrary things.
expect(errors).toContain(`"bar" is not a valid Route export field.`)
// Can't export arbitrary things.
expect(errors).toContain(`"bar" is not a valid Route export field.`)
// Can't export invalid fields.
expect(errors).toMatch(
/Invalid configuration "revalidate":\s+Expected "false | number (>= 0)", got "-1"/
)
// Can't export invalid fields.
expect(errors).toMatch(
/Invalid configuration "revalidate":\s+Expected "false | number (>= 0)", got "-1"/
)
// Avoid invalid argument types for exported functions.
expect(errors).toMatch(
/Route "src\/app\/type-checks\/route-handlers\/route\.ts" has an invalid "GET" export:\s+Type "boolean" is not a valid type for the function's first argument/
)
expect(errors).toMatch(
/Route "src\/app\/type-checks\/route-handlers\/route\.ts" has an invalid "generateStaticParams" export:\s+Type "string" is not valid/
)
// Avoid invalid argument types for exported functions.
expect(errors).toMatch(
/Route "src\/app\/type-checks\/route-handlers\/route\.ts" has an invalid "GET" export:\s+Type "boolean" is not a valid type for the function's first argument/
)
expect(errors).toMatch(
/Route "src\/app\/type-checks\/route-handlers\/route\.ts" has an invalid "generateStaticParams" export:\s+Type "string" is not valid/
)
// Avoid invalid return types for exported functions.
expect(errors).toContain(
`"Promise<boolean>" is not a valid generateStaticParams return type`
)
})
})
// Avoid invalid return types for exported functions.
expect(errors).toContain(
`"Promise<boolean>" is not a valid generateStaticParams return type`
)
})
}
)
})

View file

@ -104,7 +104,7 @@ function runTests(dev = false) {
}
describe('Async modules', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -115,16 +115,19 @@ describe('Async modules', () => {
runTests(true)
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
runTests()
})
runTests()
}
)
})

View file

@ -30,7 +30,10 @@ const runTests = () => {
}
describe('Auto Export _error bail', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
runTests()
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
runTests()
}
)
})

View file

@ -21,15 +21,22 @@ const runTests = () => {
}
describe('Auto Export', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
const { stderr: curStderr, code: curCode } = await nextBuild(appDir, [], {
stderr: true,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
const { stderr: curStderr, code: curCode } = await nextBuild(
appDir,
[],
{
stderr: true,
}
)
stderr = curStderr
exitCode = curCode
})
stderr = curStderr
exitCode = curCode
})
runTests()
})
runTests()
}
)
})

View file

@ -57,19 +57,22 @@ const runTests = () => {
}
describe('Auto Export', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
afterAll(async () => {
await killApp(app)
})
runTests()
})
runTests()
}
)
describe('dev', () => {
beforeAll(async () => {

View file

@ -28,7 +28,7 @@ const runTests = () => {
}
// Skip as it runs `next build`, seems that is a bug.
;(process.env.TURBOPACK ? describe.skip : describe)('dev mode', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('development mode', () => {
beforeAll(async () => {
// TODO: This look like a bug, `nextBuild` shouldn't be required here.
await nextBuild(appDir)
@ -39,13 +39,16 @@ const runTests = () => {
afterAll(() => killApp(app))
runTests()
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
}
)

View file

@ -33,7 +33,7 @@ const runTests = () => {
}
describe('bigint API route support', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -42,15 +42,18 @@ describe('bigint API route support', () => {
runTests()
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await fs.remove(nextConfig)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await fs.remove(nextConfig)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
runTests()
}
)
})

View file

@ -10,40 +10,264 @@ const fixturesDir = join(__dirname, '..', 'fixtures')
const nextConfig = new File(join(fixturesDir, 'basic-app/next.config.js'))
describe('Build Output', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const configs = [{}, { gzipSize: false }]
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const configs = [{}, { gzipSize: false }]
for (const experimental of configs) {
describe(`Basic Application Output (experimental: ${JSON.stringify(
experimental
)})`, () => {
let stdout
const appDir = join(fixturesDir, 'basic-app')
for (const experimental of configs) {
describe(`Basic Application Output (experimental: ${JSON.stringify(
experimental
)})`, () => {
let stdout
const appDir = join(fixturesDir, 'basic-app')
const hasExperimentalConfig = Object.keys(experimental).length > 0
const hasExperimentalConfig = Object.keys(experimental).length > 0
beforeAll(async () => {
await remove(join(appDir, '.next'))
if (hasExperimentalConfig) {
nextConfig.write(
`module.exports = { experimental: ${JSON.stringify(
experimental
)} };`
)
}
;({ stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
if (hasExperimentalConfig) {
afterAll(async () => {
nextConfig.delete()
})
}
it('should not include internal pages', async () => {
expect(stdout).toMatch(/\/ (.* )?\d{1,} B/)
expect(stdout).toMatch(/\+ First Load JS shared by all [ 0-9.]* kB/)
expect(stdout).toMatch(/ chunks\/main-[0-9a-z]{16}\.js [ 0-9.]* kB/)
expect(stdout).toMatch(
/ chunks\/framework-[0-9a-z]{16}\.js [ 0-9. ]* kB/
)
expect(stdout).not.toContain(' /_document')
expect(stdout).not.toContain(' /_app')
expect(stdout).not.toContain(' /_error')
expect(stdout).not.toContain('<buildId>')
expect(stdout).toContain('○ /')
})
// TODO: change format of this test to be more reliable
it.skip('should not deviate from snapshot', async () => {
console.log(stdout)
if (process.env.NEXT_PRIVATE_SKIP_SIZE_TESTS) {
return
}
const parsePageSize = (page) =>
stdout.match(
new RegExp(` ${page} .*?((?:\\d|\\.){1,} (?:\\w{1,})) `)
)[1]
const parsePageFirstLoad = (page) =>
stdout.match(
new RegExp(
` ${page} .*?(?:(?:\\d|\\.){1,}) .*? ((?:\\d|\\.){1,} (?:\\w{1,}))`
)
)[1]
const parseSharedSize = (sharedPartName) => {
const matches = stdout.match(
new RegExp(
`${sharedPartName} .*? ((?:\\d|\\.){1,} (?:\\w{1,}))`
)
)
if (!matches) {
throw new Error(`Could not match ${sharedPartName}`)
}
return matches[1]
}
const indexSize = parsePageSize('/')
const indexFirstLoad = parsePageFirstLoad('/')
const err404Size = parsePageSize('/404')
const err404FirstLoad = parsePageFirstLoad('/404')
const sharedByAll = parseSharedSize('shared by all')
const _appSize = parseSharedSize('_app-.*?\\.js')
const webpackSize = parseSharedSize('webpack-.*?\\.js')
const mainSize = parseSharedSize('main-.*?\\.js')
const frameworkSize = parseSharedSize('framework-.*?\\.js')
for (const size of [
indexSize,
indexFirstLoad,
err404Size,
err404FirstLoad,
sharedByAll,
_appSize,
webpackSize,
mainSize,
frameworkSize,
]) {
expect(parseFloat(size)).toBeGreaterThan(0)
}
// const gz = experimental.gzipSize !== false
// expect(parseFloat(indexSize) / 1000).toBeCloseTo(
// gz ? 0.251 : 0.394,
// 2
// )
expect(indexSize.endsWith('B')).toBe(true)
// expect(parseFloat(indexFirstLoad)).toBeCloseTo(gz ? 64 : 196, 1)
expect(indexFirstLoad.endsWith('kB')).toBe(true)
// expect(parseFloat(err404Size)).toBeCloseTo(gz ? 3.17 : 8.51, 1)
expect(err404Size.endsWith('B')).toBe(true)
// expect(parseFloat(err404FirstLoad)).toBeCloseTo(gz ? 66.9 : 204, 1)
expect(err404FirstLoad.endsWith('kB')).toBe(true)
// expect(parseFloat(sharedByAll)).toBeCloseTo(gz ? 63.7 : 196, 1)
expect(sharedByAll.endsWith('kB')).toBe(true)
// const appSizeValue = _appSize.endsWith('kB')
// ? parseFloat(_appSize)
// : parseFloat(_appSize) / 1000
// expect(appSizeValue).toBeCloseTo(gz ? 0.799 : 1.63, 1)
expect(_appSize.endsWith('kB') || _appSize.endsWith(' B')).toBe(
true
)
// const webpackSizeValue = webpackSize.endsWith('kB')
// ? parseFloat(webpackSize)
// : parseFloat(webpackSize) / 1000
// expect(webpackSizeValue).toBeCloseTo(gz ? 0.766 : 1.46, 2)
expect(
webpackSize.endsWith('kB') || webpackSize.endsWith(' B')
).toBe(true)
// expect(parseFloat(mainSize)).toBeCloseTo(gz ? 20.1 : 62.7, 1)
expect(mainSize.endsWith('kB')).toBe(true)
// expect(parseFloat(frameworkSize)).toBeCloseTo(gz ? 42.0 : 130, 1)
expect(frameworkSize.endsWith('kB')).toBe(true)
})
it('should print duration when rendering or get static props takes long', () => {
const matches = stdout.match(
/ \/slow-static\/.+\/.+(?: \(\d+ ms\))?| \[\+\d+ more paths\]/g
)
for (const check of [
// summary
expect.stringMatching(
/\/\[propsDuration\]\/\[renderDuration\] \(\d+ ms\)/
),
// ordered by duration, includes duration
expect.stringMatching(/\/2000\/10 \(\d+ ms\)$/),
expect.stringMatching(/\/10\/1000 \(\d+ ms\)$/),
expect.stringMatching(/\/300\/10 \(\d+ ms\)$/),
// max of 7 preview paths
' [+2 more paths]',
]) {
// the order isn't guaranteed on the timing tests as while() is being
// used in the render so can block the thread of other renders sharing
// the same worker
expect(matches).toContainEqual(check)
}
})
it('should not emit extracted comments', async () => {
const files = await recursiveReadDir(join(appDir, '.next'), {
pathnameFilter: (f) => /\.txt|\.LICENSE\./.test(f),
})
expect(files).toEqual([])
})
})
}
describe('Custom App Output', () => {
const appDir = join(fixturesDir, 'with-app')
beforeAll(async () => {
await remove(join(appDir, '.next'))
if (hasExperimentalConfig) {
nextConfig.write(
`module.exports = { experimental: ${JSON.stringify(
experimental
)} };`
)
}
;({ stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
if (hasExperimentalConfig) {
afterAll(async () => {
nextConfig.delete()
it('should not include custom error', async () => {
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
}
it('should not include internal pages', async () => {
expect(stdout).toMatch(/\/ (.* )?\d{1,} B/)
expect(stdout).toMatch(/\/_app (.* )?\d{1,} B/)
expect(stdout).toMatch(/\+ First Load JS shared by all \s*[0-9.]+ kB/)
expect(stdout).toMatch(/ chunks\/main-[0-9a-z]{16}\.js \s*[0-9.]+ kB/)
expect(stdout).toMatch(
/ chunks\/framework-[0-9a-z]{16}\.js \s*[0-9.]+ kB/
)
expect(stdout).not.toContain(' /_document')
expect(stdout).not.toContain(' /_error')
expect(stdout).not.toContain('<buildId>')
expect(stdout).toContain(' /_app')
expect(stdout).toContain('○ /')
})
})
describe('With AMP Output', () => {
const appDir = join(fixturesDir, 'with-amp')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should not include custom error', async () => {
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(stdout).toMatch(/\/ (.* )?[0-9.]+ B \s*[0-9.]+ kB/)
expect(stdout).toMatch(/\/amp (.* )?AMP/)
expect(stdout).toMatch(/\/hybrid (.* )?[0-9.]+ B/)
expect(stdout).toMatch(/\+ First Load JS shared by all \s*[0-9.]+ kB/)
expect(stdout).toMatch(/ chunks\/main-[0-9a-z]{16}\.js \s*[0-9.]+ kB/)
expect(stdout).toMatch(
/ chunks\/framework-[0-9a-z]{16}\.js \s*[0-9.]+ kB/
)
expect(stdout).not.toContain(' /_document')
expect(stdout).not.toContain(' /_error')
expect(stdout).not.toContain('<buildId>')
expect(stdout).toContain('○ /')
})
})
describe('Custom Error Output', () => {
const appDir = join(fixturesDir, 'with-error')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should not include custom app', async () => {
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(stdout).toMatch(/\/ (.* )?\d{1,} B/)
expect(stdout).toMatch(/λ \/404 (.* )?\d{1,} B/)
expect(stdout).toMatch(/\+ First Load JS shared by all [ 0-9.]* kB/)
expect(stdout).toMatch(/ chunks\/main-[0-9a-z]{16}\.js [ 0-9.]* kB/)
expect(stdout).toMatch(
@ -52,258 +276,41 @@ describe('Build Output', () => {
expect(stdout).not.toContain(' /_document')
expect(stdout).not.toContain(' /_app')
expect(stdout).not.toContain(' /_error')
expect(stdout).not.toContain('<buildId>')
expect(stdout).not.toContain(' /_error')
expect(stdout).toContain('○ /')
})
})
// TODO: change format of this test to be more reliable
it.skip('should not deviate from snapshot', async () => {
console.log(stdout)
describe('Custom Static Error Output', () => {
const appDir = join(fixturesDir, 'with-error-static')
if (process.env.NEXT_PRIVATE_SKIP_SIZE_TESTS) {
return
}
const parsePageSize = (page) =>
stdout.match(
new RegExp(` ${page} .*?((?:\\d|\\.){1,} (?:\\w{1,})) `)
)[1]
const parsePageFirstLoad = (page) =>
stdout.match(
new RegExp(
` ${page} .*?(?:(?:\\d|\\.){1,}) .*? ((?:\\d|\\.){1,} (?:\\w{1,}))`
)
)[1]
const parseSharedSize = (sharedPartName) => {
const matches = stdout.match(
new RegExp(`${sharedPartName} .*? ((?:\\d|\\.){1,} (?:\\w{1,}))`)
)
if (!matches) {
throw new Error(`Could not match ${sharedPartName}`)
}
return matches[1]
}
const indexSize = parsePageSize('/')
const indexFirstLoad = parsePageFirstLoad('/')
const err404Size = parsePageSize('/404')
const err404FirstLoad = parsePageFirstLoad('/404')
const sharedByAll = parseSharedSize('shared by all')
const _appSize = parseSharedSize('_app-.*?\\.js')
const webpackSize = parseSharedSize('webpack-.*?\\.js')
const mainSize = parseSharedSize('main-.*?\\.js')
const frameworkSize = parseSharedSize('framework-.*?\\.js')
for (const size of [
indexSize,
indexFirstLoad,
err404Size,
err404FirstLoad,
sharedByAll,
_appSize,
webpackSize,
mainSize,
frameworkSize,
]) {
expect(parseFloat(size)).toBeGreaterThan(0)
}
// const gz = experimental.gzipSize !== false
// expect(parseFloat(indexSize) / 1000).toBeCloseTo(
// gz ? 0.251 : 0.394,
// 2
// )
expect(indexSize.endsWith('B')).toBe(true)
// expect(parseFloat(indexFirstLoad)).toBeCloseTo(gz ? 64 : 196, 1)
expect(indexFirstLoad.endsWith('kB')).toBe(true)
// expect(parseFloat(err404Size)).toBeCloseTo(gz ? 3.17 : 8.51, 1)
expect(err404Size.endsWith('B')).toBe(true)
// expect(parseFloat(err404FirstLoad)).toBeCloseTo(gz ? 66.9 : 204, 1)
expect(err404FirstLoad.endsWith('kB')).toBe(true)
// expect(parseFloat(sharedByAll)).toBeCloseTo(gz ? 63.7 : 196, 1)
expect(sharedByAll.endsWith('kB')).toBe(true)
// const appSizeValue = _appSize.endsWith('kB')
// ? parseFloat(_appSize)
// : parseFloat(_appSize) / 1000
// expect(appSizeValue).toBeCloseTo(gz ? 0.799 : 1.63, 1)
expect(_appSize.endsWith('kB') || _appSize.endsWith(' B')).toBe(true)
// const webpackSizeValue = webpackSize.endsWith('kB')
// ? parseFloat(webpackSize)
// : parseFloat(webpackSize) / 1000
// expect(webpackSizeValue).toBeCloseTo(gz ? 0.766 : 1.46, 2)
expect(webpackSize.endsWith('kB') || webpackSize.endsWith(' B')).toBe(
true
)
// expect(parseFloat(mainSize)).toBeCloseTo(gz ? 20.1 : 62.7, 1)
expect(mainSize.endsWith('kB')).toBe(true)
// expect(parseFloat(frameworkSize)).toBeCloseTo(gz ? 42.0 : 130, 1)
expect(frameworkSize.endsWith('kB')).toBe(true)
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should print duration when rendering or get static props takes long', () => {
const matches = stdout.match(
/ \/slow-static\/.+\/.+(?: \(\d+ ms\))?| \[\+\d+ more paths\]/g
)
for (const check of [
// summary
expect.stringMatching(
/\/\[propsDuration\]\/\[renderDuration\] \(\d+ ms\)/
),
// ordered by duration, includes duration
expect.stringMatching(/\/2000\/10 \(\d+ ms\)$/),
expect.stringMatching(/\/10\/1000 \(\d+ ms\)$/),
expect.stringMatching(/\/300\/10 \(\d+ ms\)$/),
// max of 7 preview paths
' [+2 more paths]',
]) {
// the order isn't guaranteed on the timing tests as while() is being
// used in the render so can block the thread of other renders sharing
// the same worker
expect(matches).toContainEqual(check)
}
})
it('should not emit extracted comments', async () => {
const files = await recursiveReadDir(join(appDir, '.next'), {
pathnameFilter: (f) => /\.txt|\.LICENSE\./.test(f),
it('should not specify /404 as lambda when static', async () => {
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(files).toEqual([])
expect(stdout).toContain('○ /404')
expect(stdout).not.toContain('λ /_error')
expect(stdout).not.toContain('<buildId>')
})
})
describe('With Parallel Routes', () => {
it('should not have duplicate paths that resolve to the same route', async () => {
const appDir = join(fixturesDir, 'with-parallel-routes')
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(stdout.match(/○ \/root-page /g).length).toBe(1)
})
})
}
describe('Custom App Output', () => {
const appDir = join(fixturesDir, 'with-app')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should not include custom error', async () => {
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(stdout).toMatch(/\/ (.* )?\d{1,} B/)
expect(stdout).toMatch(/\/_app (.* )?\d{1,} B/)
expect(stdout).toMatch(/\+ First Load JS shared by all \s*[0-9.]+ kB/)
expect(stdout).toMatch(/ chunks\/main-[0-9a-z]{16}\.js \s*[0-9.]+ kB/)
expect(stdout).toMatch(
/ chunks\/framework-[0-9a-z]{16}\.js \s*[0-9.]+ kB/
)
expect(stdout).not.toContain(' /_document')
expect(stdout).not.toContain(' /_error')
expect(stdout).not.toContain('<buildId>')
expect(stdout).toContain(' /_app')
expect(stdout).toContain('○ /')
})
})
describe('With AMP Output', () => {
const appDir = join(fixturesDir, 'with-amp')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should not include custom error', async () => {
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(stdout).toMatch(/\/ (.* )?[0-9.]+ B \s*[0-9.]+ kB/)
expect(stdout).toMatch(/\/amp (.* )?AMP/)
expect(stdout).toMatch(/\/hybrid (.* )?[0-9.]+ B/)
expect(stdout).toMatch(/\+ First Load JS shared by all \s*[0-9.]+ kB/)
expect(stdout).toMatch(/ chunks\/main-[0-9a-z]{16}\.js \s*[0-9.]+ kB/)
expect(stdout).toMatch(
/ chunks\/framework-[0-9a-z]{16}\.js \s*[0-9.]+ kB/
)
expect(stdout).not.toContain(' /_document')
expect(stdout).not.toContain(' /_error')
expect(stdout).not.toContain('<buildId>')
expect(stdout).toContain('○ /')
})
})
describe('Custom Error Output', () => {
const appDir = join(fixturesDir, 'with-error')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should not include custom app', async () => {
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(stdout).toMatch(/\/ (.* )?\d{1,} B/)
expect(stdout).toMatch(/λ \/404 (.* )?\d{1,} B/)
expect(stdout).toMatch(/\+ First Load JS shared by all [ 0-9.]* kB/)
expect(stdout).toMatch(/ chunks\/main-[0-9a-z]{16}\.js [ 0-9.]* kB/)
expect(stdout).toMatch(
/ chunks\/framework-[0-9a-z]{16}\.js [ 0-9. ]* kB/
)
expect(stdout).not.toContain(' /_document')
expect(stdout).not.toContain(' /_app')
expect(stdout).not.toContain('<buildId>')
expect(stdout).not.toContain(' /_error')
expect(stdout).toContain('○ /')
})
})
describe('Custom Static Error Output', () => {
const appDir = join(fixturesDir, 'with-error-static')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should not specify /404 as lambda when static', async () => {
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(stdout).toContain('○ /404')
expect(stdout).not.toContain('λ /_error')
expect(stdout).not.toContain('<buildId>')
})
})
describe('With Parallel Routes', () => {
it('should not have duplicate paths that resolve to the same route', async () => {
const appDir = join(fixturesDir, 'with-parallel-routes')
const { stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(stdout.match(/○ \/root-page /g).length).toBe(1)
})
})
})
)
})

View file

@ -7,88 +7,93 @@ import { nextBuild } from 'next-test-utils'
const appDir = join(__dirname, '../app')
describe('build trace with extra entries', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should build and trace correctly', async () => {
const result = await nextBuild(appDir, undefined, {
cwd: appDir,
stderr: true,
stdout: true,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should build and trace correctly', async () => {
const result = await nextBuild(appDir, undefined, {
cwd: appDir,
stderr: true,
stdout: true,
})
console.log(result)
expect(result.code).toBe(0)
const appTrace = await fs.readJSON(
join(appDir, '.next/server/pages/_app.js.nft.json')
)
const indexTrace = await fs.readJSON(
join(appDir, '.next/server/pages/index.js.nft.json')
)
const anotherTrace = await fs.readJSON(
join(appDir, '.next/server/pages/another.js.nft.json')
)
const imageTrace = await fs.readJSON(
join(appDir, '.next/server/pages/image-import.js.nft.json')
)
const appDirRoute1Trace = await fs.readJSON(
join(appDir, '.next/server/app/route1/route.js.nft.json')
)
expect(
appDirRoute1Trace.files.some(
(file) => file === '../../../../include-me/hello.txt'
)
).toBe(true)
expect(
appDirRoute1Trace.files.some(
(file) => file === '../../../../include-me/second.txt'
)
).toBe(true)
expect(
appDirRoute1Trace.files.some((file) => file.includes('exclude-me'))
).toBe(false)
const tracedFiles = [
...appTrace.files,
...indexTrace.files,
...anotherTrace.files,
...imageTrace.files,
]
expect(tracedFiles.some((file) => file.endsWith('hello.json'))).toBe(
true
)
expect(
tracedFiles.some((file) => file.includes('some-cms/index.js'))
).toBe(true)
expect(
tracedFiles.some((file) => file === '../../../include-me/hello.txt')
).toBe(true)
expect(
tracedFiles.some((file) => file === '../../../include-me/second.txt')
).toBe(true)
expect(
indexTrace.files.some((file) => file.includes('exclude-me'))
).toBe(false)
expect(
tracedFiles.some((file) =>
file.includes('nested-structure/constants/package.json')
)
).toBe(true)
expect(
tracedFiles.some((file) =>
file.includes('nested-structure/package.json')
)
).toBe(true)
expect(
tracedFiles.some((file) =>
file.includes('nested-structure/dist/constants.js')
)
).toBe(true)
expect(
tracedFiles.some((file) => file.includes('public/another.jpg'))
).toBe(true)
expect(
tracedFiles.some((file) => file.includes('public/test.jpg'))
).toBe(false)
})
console.log(result)
expect(result.code).toBe(0)
const appTrace = await fs.readJSON(
join(appDir, '.next/server/pages/_app.js.nft.json')
)
const indexTrace = await fs.readJSON(
join(appDir, '.next/server/pages/index.js.nft.json')
)
const anotherTrace = await fs.readJSON(
join(appDir, '.next/server/pages/another.js.nft.json')
)
const imageTrace = await fs.readJSON(
join(appDir, '.next/server/pages/image-import.js.nft.json')
)
const appDirRoute1Trace = await fs.readJSON(
join(appDir, '.next/server/app/route1/route.js.nft.json')
)
expect(
appDirRoute1Trace.files.some(
(file) => file === '../../../../include-me/hello.txt'
)
).toBe(true)
expect(
appDirRoute1Trace.files.some(
(file) => file === '../../../../include-me/second.txt'
)
).toBe(true)
expect(
appDirRoute1Trace.files.some((file) => file.includes('exclude-me'))
).toBe(false)
const tracedFiles = [
...appTrace.files,
...indexTrace.files,
...anotherTrace.files,
...imageTrace.files,
]
expect(tracedFiles.some((file) => file.endsWith('hello.json'))).toBe(true)
expect(
tracedFiles.some((file) => file.includes('some-cms/index.js'))
).toBe(true)
expect(
tracedFiles.some((file) => file === '../../../include-me/hello.txt')
).toBe(true)
expect(
tracedFiles.some((file) => file === '../../../include-me/second.txt')
).toBe(true)
expect(indexTrace.files.some((file) => file.includes('exclude-me'))).toBe(
false
)
expect(
tracedFiles.some((file) =>
file.includes('nested-structure/constants/package.json')
)
).toBe(true)
expect(
tracedFiles.some((file) =>
file.includes('nested-structure/package.json')
)
).toBe(true)
expect(
tracedFiles.some((file) =>
file.includes('nested-structure/dist/constants.js')
)
).toBe(true)
expect(
tracedFiles.some((file) => file.includes('public/another.jpg'))
).toBe(true)
expect(tracedFiles.some((file) => file.includes('public/test.jpg'))).toBe(
false
)
})
})
}
)
})

View file

@ -7,115 +7,118 @@ import { nextBuild } from 'next-test-utils'
const appDir = join(__dirname, '../app')
describe('build trace with extra entries', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should build and trace correctly', async () => {
const result = await nextBuild(appDir, undefined, {
cwd: appDir,
stderr: true,
stdout: true,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should build and trace correctly', async () => {
const result = await nextBuild(appDir, undefined, {
cwd: appDir,
stderr: true,
stdout: true,
})
console.log(result)
expect(result.code).toBe(0)
const appTrace = await fs.readJSON(
join(appDir, '.next/server/pages/_app.js.nft.json')
)
const indexTrace = await fs.readJSON(
join(appDir, '.next/server/pages/index.js.nft.json')
)
const anotherTrace = await fs.readJSON(
join(appDir, '.next/server/pages/another.js.nft.json')
)
const imageTrace = await fs.readJSON(
join(appDir, '.next/server/pages/image-import.js.nft.json')
)
const appDirRoute1Trace = await fs.readJSON(
join(appDir, '.next/server/app/route1/route.js.nft.json')
)
expect(
appDirRoute1Trace.files.some(
(file) => file === '../../../../include-me/hello.txt'
)
).toBe(true)
expect(
appDirRoute1Trace.files.some(
(file) => file === '../../../../include-me/second.txt'
)
).toBe(true)
expect(
appDirRoute1Trace.files.some((file) => file.includes('exclude-me'))
).toBe(false)
expect(appTrace.files.some((file) => file.endsWith('hello.json'))).toBe(
true
)
expect(
indexTrace.files.filter(
(file) => file.includes('chunks') && file.endsWith('.js')
).length
).toBeGreaterThan(
anotherTrace.files.filter(
(file) => file.includes('chunks') && file.endsWith('.js')
).length
)
expect(
appTrace.files.some((file) => file.endsWith('lib/get-data.js'))
).toBe(true)
expect(
indexTrace.files.some((file) => file.endsWith('hello.json'))
).toBeFalsy()
expect(
indexTrace.files.some((file) => file.endsWith('some-dir'))
).toBeFalsy()
expect(
indexTrace.files.some((file) =>
file.endsWith('.dot-folder/another-file.txt')
)
).toBe(true)
expect(
indexTrace.files.some((file) => file.endsWith('some-dir/file.txt'))
).toBe(true)
expect(
indexTrace.files.some((file) => file.includes('some-cms/index.js'))
).toBe(true)
expect(
indexTrace.files.some(
(file) => file === '../../../include-me/hello.txt'
)
).toBe(true)
expect(
indexTrace.files.some(
(file) => file === '../../../include-me/second.txt'
)
).toBe(true)
expect(
indexTrace.files.some((file) => file.includes('exclude-me'))
).toBe(false)
expect(
anotherTrace.files.some((file) =>
file.includes('nested-structure/constants/package.json')
)
).toBe(true)
expect(
anotherTrace.files.some((file) =>
file.includes('nested-structure/package.json')
)
).toBe(true)
expect(
anotherTrace.files.some((file) =>
file.includes('nested-structure/dist/constants.js')
)
).toBe(true)
expect(
imageTrace.files.some((file) => file.includes('public/another.jpg'))
).toBe(true)
expect(
imageTrace.files.some((file) => file.includes('public/test.jpg'))
).toBe(false)
})
console.log(result)
expect(result.code).toBe(0)
const appTrace = await fs.readJSON(
join(appDir, '.next/server/pages/_app.js.nft.json')
)
const indexTrace = await fs.readJSON(
join(appDir, '.next/server/pages/index.js.nft.json')
)
const anotherTrace = await fs.readJSON(
join(appDir, '.next/server/pages/another.js.nft.json')
)
const imageTrace = await fs.readJSON(
join(appDir, '.next/server/pages/image-import.js.nft.json')
)
const appDirRoute1Trace = await fs.readJSON(
join(appDir, '.next/server/app/route1/route.js.nft.json')
)
expect(
appDirRoute1Trace.files.some(
(file) => file === '../../../../include-me/hello.txt'
)
).toBe(true)
expect(
appDirRoute1Trace.files.some(
(file) => file === '../../../../include-me/second.txt'
)
).toBe(true)
expect(
appDirRoute1Trace.files.some((file) => file.includes('exclude-me'))
).toBe(false)
expect(appTrace.files.some((file) => file.endsWith('hello.json'))).toBe(
true
)
expect(
indexTrace.files.filter(
(file) => file.includes('chunks') && file.endsWith('.js')
).length
).toBeGreaterThan(
anotherTrace.files.filter(
(file) => file.includes('chunks') && file.endsWith('.js')
).length
)
expect(
appTrace.files.some((file) => file.endsWith('lib/get-data.js'))
).toBe(true)
expect(
indexTrace.files.some((file) => file.endsWith('hello.json'))
).toBeFalsy()
expect(
indexTrace.files.some((file) => file.endsWith('some-dir'))
).toBeFalsy()
expect(
indexTrace.files.some((file) =>
file.endsWith('.dot-folder/another-file.txt')
)
).toBe(true)
expect(
indexTrace.files.some((file) => file.endsWith('some-dir/file.txt'))
).toBe(true)
expect(
indexTrace.files.some((file) => file.includes('some-cms/index.js'))
).toBe(true)
expect(
indexTrace.files.some(
(file) => file === '../../../include-me/hello.txt'
)
).toBe(true)
expect(
indexTrace.files.some(
(file) => file === '../../../include-me/second.txt'
)
).toBe(true)
expect(indexTrace.files.some((file) => file.includes('exclude-me'))).toBe(
false
)
expect(
anotherTrace.files.some((file) =>
file.includes('nested-structure/constants/package.json')
)
).toBe(true)
expect(
anotherTrace.files.some((file) =>
file.includes('nested-structure/package.json')
)
).toBe(true)
expect(
anotherTrace.files.some((file) =>
file.includes('nested-structure/dist/constants.js')
)
).toBe(true)
expect(
imageTrace.files.some((file) => file.includes('public/another.jpg'))
).toBe(true)
expect(
imageTrace.files.some((file) => file.includes('public/test.jpg'))
).toBe(false)
})
})
}
)
})

View file

@ -7,85 +7,88 @@ import { join } from 'path'
const appDir = join(__dirname, '../')
describe('Build warnings', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should not shown warning about minification without any modification', async () => {
const { stderr } = await nextBuild(appDir, undefined, { stderr: true })
expect(stderr).not.toContain('optimization has been disabled')
})
it('should shown warning about minification for minimize', async () => {
const nextConfig = new File(join(appDir, 'next.config.js'))
await waitFor(500)
nextConfig.replace('true', 'false')
const { stderr } = await nextBuild(appDir, undefined, { stderr: true })
expect(stderr).toContain('optimization has been disabled')
nextConfig.restore()
})
it('should shown warning about minification for minimizer', async () => {
const nextConfig = new File(join(appDir, 'next.config.js'))
await waitFor(500)
nextConfig.replace(
'config.optimization.minimize = true',
'config.optimization.minimizer = []'
)
const { stderr } = await nextBuild(appDir, undefined, { stderr: true })
expect(stderr).toContain('optimization has been disabled')
nextConfig.restore()
})
it('should not warn about missing cache in non-CI', async () => {
await remove(join(appDir, '.next'))
const { stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: {
CI: '',
CIRCLECI: '',
TRAVIS: '',
SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: '',
GITHUB_ACTIONS: '',
GITHUB_EVENT_NAME: '',
},
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should not shown warning about minification without any modification', async () => {
const { stderr } = await nextBuild(appDir, undefined, { stderr: true })
expect(stderr).not.toContain('optimization has been disabled')
})
expect(stdout).not.toContain('no-cache')
})
it('should not warn about missing cache on supported platforms', async () => {
await remove(join(appDir, '.next'))
it('should shown warning about minification for minimize', async () => {
const nextConfig = new File(join(appDir, 'next.config.js'))
const { stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: { CI: '1', NOW_BUILDER: '1' },
await waitFor(500)
nextConfig.replace('true', 'false')
const { stderr } = await nextBuild(appDir, undefined, { stderr: true })
expect(stderr).toContain('optimization has been disabled')
nextConfig.restore()
})
expect(stdout).not.toContain('no-cache')
})
it('should warn about missing cache in CI', async () => {
await remove(join(appDir, '.next'))
it('should shown warning about minification for minimizer', async () => {
const nextConfig = new File(join(appDir, 'next.config.js'))
let { stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: { CI: '1' },
await waitFor(500)
nextConfig.replace(
'config.optimization.minimize = true',
'config.optimization.minimizer = []'
)
const { stderr } = await nextBuild(appDir, undefined, { stderr: true })
expect(stderr).toContain('optimization has been disabled')
nextConfig.restore()
})
expect(stdout).toContain('no-cache')
// Do not warn after cache is present
;({ stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: { CI: '1' },
}))
expect(stdout).not.toContain('no-cache')
})
})
it('should not warn about missing cache in non-CI', async () => {
await remove(join(appDir, '.next'))
const { stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: {
CI: '',
CIRCLECI: '',
TRAVIS: '',
SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: '',
GITHUB_ACTIONS: '',
GITHUB_EVENT_NAME: '',
},
})
expect(stdout).not.toContain('no-cache')
})
it('should not warn about missing cache on supported platforms', async () => {
await remove(join(appDir, '.next'))
const { stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: { CI: '1', NOW_BUILDER: '1' },
})
expect(stdout).not.toContain('no-cache')
})
it('should warn about missing cache in CI', async () => {
await remove(join(appDir, '.next'))
let { stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: { CI: '1' },
})
expect(stdout).toContain('no-cache')
// Do not warn after cache is present
;({ stdout } = await nextBuild(appDir, undefined, {
stdout: true,
env: { CI: '1' },
}))
expect(stdout).not.toContain('no-cache')
})
}
)
})

View file

@ -14,7 +14,7 @@ const errorRegex = /getStaticPaths was added without a getStaticProps in/
describe('Catches Missing getStaticProps', () => {
describe('development mode', () => {
it('should catch it in dev mode', async () => {
it('should catch it in development mode', async () => {
const appPort = await findPort()
const app = await launchApp(appDir, appPort)
const html = await renderViaHTTP(appPort, '/hello')
@ -23,12 +23,15 @@ describe('Catches Missing getStaticProps', () => {
expect(html).toMatch(errorRegex)
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should catch it in server build mode', async () => {
const { stderr } = await nextBuild(appDir, [], {
stderr: true,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should catch it in server build mode', async () => {
const { stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(stderr).toMatch(errorRegex)
})
expect(stderr).toMatch(errorRegex)
})
})
}
)
})

View file

@ -24,112 +24,117 @@ const existsChunkNamed = (name) => {
// Skipped as it uses webpack internals / stats.json.
;(process.env.TURBOPACK ? describe.skip : describe)('Chunking', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
try {
// If a previous build has left chunks behind, delete them
const oldChunks = await readdir(
join(appDir, '.next', 'static', 'chunks')
)
await Promise.all(
oldChunks.map((chunk) => {
return unlink(join(appDir, '.next', 'static', 'chunks', chunk))
})
)
} catch (e) {
// Error here means old chunks don't exist, so we don't need to do anything
}
await nextBuild(appDir, [])
stats = (await readFile(join(appDir, '.next', 'stats.json'), 'utf8'))
// fixes backslashes in keyNames not being escaped on windows
.replace(/"static\\(.*?":?)/g, (match) => match.replace(/\\/g, '\\\\'))
stats = JSON.parse(stats)
appPort = await findPort()
app = await nextStart(appDir, appPort)
chunks = await readdir(join(appDir, '.next', 'static', 'chunks'))
})
afterAll(() => killApp(app))
it('should use all url friendly names', () => {
expect(chunks).toEqual(chunks.map((name) => encodeURIComponent(name)))
})
it('should create a framework chunk', () => {
expect(existsChunkNamed('framework')).toBe(true)
})
it('should not create a commons chunk', () => {
// This app has no dependency that is required by ALL pages, and should
// therefore not have a commons chunk
expect(existsChunkNamed('commons')).toBe(false)
})
it('should not create a lib chunk for react or react-dom', () => {
// These large dependencies would become lib chunks, except that they
// are preemptively moved into the framework chunk.
expect(existsChunkNamed('react|react-dom')).toBe(false)
})
it('should not preload the build manifest', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
expect(
[].slice
.call($('link[rel="preload"][as="script"]'))
.map((e) => e.attribs.href)
.some((entry) => entry.includes('_buildManifest'))
).toBe(false)
})
it('should execute the build manifest', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
expect(
Array.from($('script'))
.map((e) => e.attribs.src)
.some((entry) => entry && entry.includes('_buildManifest'))
).toBe(true)
})
it('should not include more than one instance of react-dom', async () => {
const misplacedReactDom = stats.chunks.some((chunk) => {
if (chunk.names.includes('framework')) {
// disregard react-dom in framework--it's supposed to be there
return false
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
try {
// If a previous build has left chunks behind, delete them
const oldChunks = await readdir(
join(appDir, '.next', 'static', 'chunks')
)
await Promise.all(
oldChunks.map((chunk) => {
return unlink(join(appDir, '.next', 'static', 'chunks', chunk))
})
)
} catch (e) {
// Error here means old chunks don't exist, so we don't need to do anything
}
return chunk.modules.some((module) => {
return /react-dom/.test(module.name)
await nextBuild(appDir, [])
stats = (await readFile(join(appDir, '.next', 'stats.json'), 'utf8'))
// fixes backslashes in keyNames not being escaped on windows
.replace(/"static\\(.*?":?)/g, (match) =>
match.replace(/\\/g, '\\\\')
)
stats = JSON.parse(stats)
appPort = await findPort()
app = await nextStart(appDir, appPort)
chunks = await readdir(join(appDir, '.next', 'static', 'chunks'))
})
afterAll(() => killApp(app))
it('should use all url friendly names', () => {
expect(chunks).toEqual(chunks.map((name) => encodeURIComponent(name)))
})
it('should create a framework chunk', () => {
expect(existsChunkNamed('framework')).toBe(true)
})
it('should not create a commons chunk', () => {
// This app has no dependency that is required by ALL pages, and should
// therefore not have a commons chunk
expect(existsChunkNamed('commons')).toBe(false)
})
it('should not create a lib chunk for react or react-dom', () => {
// These large dependencies would become lib chunks, except that they
// are preemptively moved into the framework chunk.
expect(existsChunkNamed('react|react-dom')).toBe(false)
})
it('should not preload the build manifest', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
expect(
[].slice
.call($('link[rel="preload"][as="script"]'))
.map((e) => e.attribs.href)
.some((entry) => entry.includes('_buildManifest'))
).toBe(false)
})
it('should execute the build manifest', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
expect(
Array.from($('script'))
.map((e) => e.attribs.src)
.some((entry) => entry && entry.includes('_buildManifest'))
).toBe(true)
})
it('should not include more than one instance of react-dom', async () => {
const misplacedReactDom = stats.chunks.some((chunk) => {
if (chunk.names.includes('framework')) {
// disregard react-dom in framework--it's supposed to be there
return false
}
return chunk.modules.some((module) => {
return /react-dom/.test(module.name)
})
})
expect(misplacedReactDom).toBe(false)
})
describe('Serving', () => {
it('should hydrate with aggressive chunking', async () => {
const browser = await webdriver(appPort, '/page2')
const text = await browser.elementByCss('#padded-str').text()
expect(text).toBe('__rad__')
await browser.close()
})
it('should load chunks when navigating', async () => {
const browser = await webdriver(appPort, '/page3')
const text = await browser
.elementByCss('#page2-link')
.click()
.waitForElementByCss('#padded-str')
.elementByCss('#padded-str')
.text()
expect(text).toBe('__rad__')
await browser.close()
})
})
expect(misplacedReactDom).toBe(false)
})
describe('Serving', () => {
it('should hydrate with aggressive chunking', async () => {
const browser = await webdriver(appPort, '/page2')
const text = await browser.elementByCss('#padded-str').text()
expect(text).toBe('__rad__')
await browser.close()
})
it('should load chunks when navigating', async () => {
const browser = await webdriver(appPort, '/page3')
const text = await browser
.elementByCss('#page2-link')
.click()
.waitForElementByCss('#padded-str')
.elementByCss('#padded-str')
.text()
expect(text).toBe('__rad__')
await browser.close()
})
})
})
}
)
})

View file

@ -29,28 +29,31 @@ const runTests = () => {
}
describe('Cleaning distDir', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
runTests()
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
runTests()
describe('disabled write', () => {
beforeAll(async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`
describe('disabled write', () => {
beforeAll(async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`
module.exports = {
cleanDistDir: false
}
`
)
})
afterAll(async () => {
await fs.writeFile(nextConfig, nextConfigContent)
})
)
})
afterAll(async () => {
await fs.writeFile(nextConfig, nextConfigContent)
})
it('should not clean up .next before build start', async () => {
await checkFileWrite(true)
it('should not clean up .next before build start', async () => {
await checkFileWrite(true)
})
})
})
})
}
)
})

View file

@ -80,279 +80,286 @@ const testExitSignal = async (
}
describe('CLI Usage', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
describe('start', () => {
test('should exit when SIGINT is signalled', async () => {
require('console').log('before build')
await fs.remove(join(dirBasic, '.next'))
await nextBuild(dirBasic, undefined, {
onStdout(msg) {
console.log(msg)
},
onStderr(msg) {
console.log(msg)
},
})
require('console').log('build finished')
const port = await findPort()
await testExitSignal(
'SIGINT',
['start', dirBasic, '-p', port],
/- Local:/
)
})
test('should exit when SIGTERM is signalled', async () => {
await fs.remove(join(dirBasic, '.next'))
await nextBuild(dirBasic, undefined, {
onStdout(msg) {
console.log(msg)
},
onStderr(msg) {
console.log(msg)
},
})
const port = await findPort()
await testExitSignal(
'SIGTERM',
['start', dirBasic, '-p', port],
/- Local:/
)
})
test('--help', async () => {
const help = await runNextCommand(['start', '--help'], {
stdout: true,
})
expect(help.stdout).toMatch(/Starts Next.js in production mode/)
})
test('-h', async () => {
const help = await runNextCommand(['start', '-h'], {
stdout: true,
})
expect(help.stdout).toMatch(/Starts Next.js in production mode/)
})
test('should format IPv6 addresses correctly', async () => {
await nextBuild(dirBasic)
const port = await findPort()
let stdout = ''
const app = await runNextCommandDev(
['start', dirBasic, '--hostname', '::', '--port', port],
undefined,
{
nextStart: true,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
describe('start', () => {
test('should exit when SIGINT is signalled', async () => {
require('console').log('before build')
await fs.remove(join(dirBasic, '.next'))
await nextBuild(dirBasic, undefined, {
onStdout(msg) {
stdout += msg
console.log(msg)
},
onStderr(msg) {
console.log(msg)
},
}
)
try {
await check(() => {
// Only display when hostname is provided
expect(stdout).toMatch(
new RegExp(`Network:\\s*http://\\[::\\]:${port}`)
)
expect(stdout).toMatch(new RegExp(`http://\\[::1\\]:${port}`))
})
} finally {
await killApp(app)
}
})
require('console').log('build finished')
test('should warn when unknown argument provided', async () => {
const { stderr } = await runNextCommand(['start', '--random'], {
stderr: true,
const port = await findPort()
await testExitSignal(
'SIGINT',
['start', dirBasic, '-p', port],
/- Local:/
)
})
expect(stderr).toEqual(`error: unknown option '--random'\n`)
})
test('should not throw UnhandledPromiseRejectionWarning', async () => {
const { stderr } = await runNextCommand(['start', '--random'], {
stderr: true,
})
expect(stderr).not.toContain('UnhandledPromiseRejectionWarning')
})
test('duplicate sass deps', async () => {
const port = await findPort()
let stderr = ''
let instance = await launchApp(dirDuplicateSass, port, {
stderr: true,
onStderr(msg) {
stderr += msg
},
test('should exit when SIGTERM is signalled', async () => {
await fs.remove(join(dirBasic, '.next'))
await nextBuild(dirBasic, undefined, {
onStdout(msg) {
console.log(msg)
},
onStderr(msg) {
console.log(msg)
},
})
const port = await findPort()
await testExitSignal(
'SIGTERM',
['start', dirBasic, '-p', port],
/- Local:/
)
})
try {
await check(() => stderr, /both `sass` and `node-sass` installed/)
} finally {
await killApp(instance).catch(() => {})
}
})
test('invalid directory', async () => {
const output = await runNextCommand(['start', 'non-existent'], {
stderr: true,
test('--help', async () => {
const help = await runNextCommand(['start', '--help'], {
stdout: true,
})
expect(help.stdout).toMatch(/Starts Next.js in production mode/)
})
expect(output.stderr).toContain(
'Invalid project directory provided, no such directory'
)
})
test('--keepAliveTimeout string arg', async () => {
const { stderr } = await runNextCommand(
['start', '--keepAliveTimeout', 'string'],
{
stderr: true,
test('-h', async () => {
const help = await runNextCommand(['start', '-h'], {
stdout: true,
})
expect(help.stdout).toMatch(/Starts Next.js in production mode/)
})
test('should format IPv6 addresses correctly', async () => {
await nextBuild(dirBasic)
const port = await findPort()
let stdout = ''
const app = await runNextCommandDev(
['start', dirBasic, '--hostname', '::', '--port', port],
undefined,
{
nextStart: true,
onStdout(msg) {
stdout += msg
},
}
)
try {
await check(() => {
// Only display when hostname is provided
expect(stdout).toMatch(
new RegExp(`Network:\\s*http://\\[::\\]:${port}`)
)
expect(stdout).toMatch(new RegExp(`http://\\[::1\\]:${port}`))
})
} finally {
await killApp(app)
}
)
expect(stderr).toContain(
`error: option '--keepAliveTimeout <keepAliveTimeout>' argument 'string' is invalid. 'string' is not a non-negative number.`
)
})
})
test('--keepAliveTimeout negative number', async () => {
const { stderr } = await runNextCommand(
['start', '--keepAliveTimeout=-100'],
{
test('should warn when unknown argument provided', async () => {
const { stderr } = await runNextCommand(['start', '--random'], {
stderr: true,
}
)
expect(stderr).toContain(
`error: option '--keepAliveTimeout <keepAliveTimeout>' argument '-100' is invalid. '-100' is not a non-negative number.`
)
})
test('--keepAliveTimeout Infinity', async () => {
const { stderr } = await runNextCommand(
['start', '--keepAliveTimeout', 'Infinity'],
{
})
expect(stderr).toEqual(`error: unknown option '--random'\n`)
})
test('should not throw UnhandledPromiseRejectionWarning', async () => {
const { stderr } = await runNextCommand(['start', '--random'], {
stderr: true,
}
)
expect(stderr).toContain(
`error: option '--keepAliveTimeout <keepAliveTimeout>' argument 'Infinity' is invalid. 'Infinity' is not a non-negative number.`
)
})
})
expect(stderr).not.toContain('UnhandledPromiseRejectionWarning')
})
test('--keepAliveTimeout happy path', async () => {
const { stderr } = await runNextCommand(
['start', '--keepAliveTimeout', '100'],
{
test('duplicate sass deps', async () => {
const port = await findPort()
let stderr = ''
let instance = await launchApp(dirDuplicateSass, port, {
stderr: true,
}
)
expect(stderr).not.toContain(
`error: option '--keepAliveTimeout <keepAliveTimeout>' argument '100' is invalid. '100' is not a non-negative number.`
)
})
onStderr(msg) {
stderr += msg
},
})
test('should not start on a port out of range', async () => {
const invalidPort = '300001'
const { stderr } = await runNextCommand(
['start', '--port', invalidPort],
{
try {
await check(() => stderr, /both `sass` and `node-sass` installed/)
} finally {
await killApp(instance).catch(() => {})
}
})
test('invalid directory', async () => {
const output = await runNextCommand(['start', 'non-existent'], {
stderr: true,
}
)
})
expect(output.stderr).toContain(
'Invalid project directory provided, no such directory'
)
})
expect(stderr).toContain(`options.port should be >= 0 and < 65536.`)
test('--keepAliveTimeout string arg', async () => {
const { stderr } = await runNextCommand(
['start', '--keepAliveTimeout', 'string'],
{
stderr: true,
}
)
expect(stderr).toContain(
`error: option '--keepAliveTimeout <keepAliveTimeout>' argument 'string' is invalid. 'string' is not a non-negative number.`
)
})
test('--keepAliveTimeout negative number', async () => {
const { stderr } = await runNextCommand(
['start', '--keepAliveTimeout=-100'],
{
stderr: true,
}
)
expect(stderr).toContain(
`error: option '--keepAliveTimeout <keepAliveTimeout>' argument '-100' is invalid. '-100' is not a non-negative number.`
)
})
test('--keepAliveTimeout Infinity', async () => {
const { stderr } = await runNextCommand(
['start', '--keepAliveTimeout', 'Infinity'],
{
stderr: true,
}
)
expect(stderr).toContain(
`error: option '--keepAliveTimeout <keepAliveTimeout>' argument 'Infinity' is invalid. 'Infinity' is not a non-negative number.`
)
})
test('--keepAliveTimeout happy path', async () => {
const { stderr } = await runNextCommand(
['start', '--keepAliveTimeout', '100'],
{
stderr: true,
}
)
expect(stderr).not.toContain(
`error: option '--keepAliveTimeout <keepAliveTimeout>' argument '100' is invalid. '100' is not a non-negative number.`
)
})
test('should not start on a port out of range', async () => {
const invalidPort = '300001'
const { stderr } = await runNextCommand(
['start', '--port', invalidPort],
{
stderr: true,
}
)
expect(stderr).toContain(`options.port should be >= 0 and < 65536.`)
})
test('should not start on a reserved port', async () => {
const reservedPort = '4045'
const { stderr } = await runNextCommand(
['start', '--port', reservedPort],
{
stderr: true,
}
)
expect(stderr).toContain(
`Bad port: "${reservedPort}" is reserved for npp`
)
})
})
test('should not start on a reserved port', async () => {
const reservedPort = '4045'
const { stderr } = await runNextCommand(
['start', '--port', reservedPort],
{
describe('telemetry', () => {
test('--help', async () => {
const help = await runNextCommand(['telemetry', '--help'], {
stdout: true,
})
expect(help.stdout).toMatch(
/Allows you to enable or disable Next\.js'/
)
})
test('-h', async () => {
const help = await runNextCommand(['telemetry', '-h'], {
stdout: true,
})
expect(help.stdout).toMatch(
/Allows you to enable or disable Next\.js'/
)
})
test('should warn when unknown argument provided', async () => {
const { stderr } = await runNextCommand(['telemetry', '--random'], {
stderr: true,
}
)
expect(stderr).toContain(
`Bad port: "${reservedPort}" is reserved for npp`
)
})
})
describe('telemetry', () => {
test('--help', async () => {
const help = await runNextCommand(['telemetry', '--help'], {
stdout: true,
})
expect(stderr).toEqual(`error: unknown option '--random'\n`)
})
test('should not throw UnhandledPromiseRejectionWarning', async () => {
const { stderr } = await runNextCommand(['telemetry', '--random'], {
stderr: true,
})
expect(stderr).not.toContain('UnhandledPromiseRejectionWarning')
})
expect(help.stdout).toMatch(/Allows you to enable or disable Next\.js'/)
})
test('-h', async () => {
const help = await runNextCommand(['telemetry', '-h'], {
stdout: true,
describe('build', () => {
test('--help', async () => {
const help = await runNextCommand(['build', '--help'], {
stdout: true,
})
expect(help.stdout).toMatch(/Creates an optimized production build/)
})
expect(help.stdout).toMatch(/Allows you to enable or disable Next\.js'/)
})
test('should warn when unknown argument provided', async () => {
const { stderr } = await runNextCommand(['telemetry', '--random'], {
stderr: true,
test('-h', async () => {
const help = await runNextCommand(['build', '-h'], {
stdout: true,
})
expect(help.stdout).toMatch(/Creates an optimized production build/)
})
expect(stderr).toEqual(`error: unknown option '--random'\n`)
})
test('should not throw UnhandledPromiseRejectionWarning', async () => {
const { stderr } = await runNextCommand(['telemetry', '--random'], {
stderr: true,
})
expect(stderr).not.toContain('UnhandledPromiseRejectionWarning')
})
})
describe('build', () => {
test('--help', async () => {
const help = await runNextCommand(['build', '--help'], {
stdout: true,
test('should warn when unknown argument provided', async () => {
const { stderr } = await runNextCommand(['build', '--random'], {
stderr: true,
})
expect(stderr).toEqual(`error: unknown option '--random'\n`)
})
expect(help.stdout).toMatch(/Creates an optimized production build/)
})
test('-h', async () => {
const help = await runNextCommand(['build', '-h'], {
stdout: true,
test('should not throw UnhandledPromiseRejectionWarning', async () => {
const { stderr } = await runNextCommand(['build', '--random'], {
stderr: true,
})
expect(stderr).not.toContain('UnhandledPromiseRejectionWarning')
})
expect(help.stdout).toMatch(/Creates an optimized production build/)
})
test('should warn when unknown argument provided', async () => {
const { stderr } = await runNextCommand(['build', '--random'], {
stderr: true,
test('should exit when SIGINT is signalled', async () => {
await testExitSignal('SIGINT', ['build', dirBasic])
})
expect(stderr).toEqual(`error: unknown option '--random'\n`)
})
test('should not throw UnhandledPromiseRejectionWarning', async () => {
const { stderr } = await runNextCommand(['build', '--random'], {
stderr: true,
test('should exit when SIGTERM is signalled', async () => {
await testExitSignal('SIGTERM', ['build', dirBasic])
})
expect(stderr).not.toContain('UnhandledPromiseRejectionWarning')
})
test('should exit when SIGINT is signalled', async () => {
await testExitSignal('SIGINT', ['build', dirBasic])
})
test('should exit when SIGTERM is signalled', async () => {
await testExitSignal('SIGTERM', ['build', dirBasic])
})
test('invalid directory', async () => {
const output = await runNextCommand(['build', 'non-existent'], {
stderr: true,
test('invalid directory', async () => {
const output = await runNextCommand(['build', 'non-existent'], {
stderr: true,
})
expect(output.stderr).toContain(
'Invalid project directory provided, no such directory'
)
})
expect(output.stderr).toContain(
'Invalid project directory provided, no such directory'
)
})
})
})
}
)
describe('no command', () => {
test('--help', async () => {

View file

@ -23,7 +23,7 @@ const runTests = (isProd = false) => {
}
describe('Client 404', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
context.appPort = await findPort()
context.server = await launchApp(appDir, context.appPort)
@ -35,23 +35,26 @@ describe('Client 404', () => {
runTests()
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
context.appPort = await findPort()
context.server = await nextStart(appDir, context.appPort)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
context.appPort = await findPort()
context.server = await nextStart(appDir, context.appPort)
const manifest = await getBuildManifest(appDir)
const files = manifest.pages['/missing'].filter((d) =>
/static[\\/]chunks[\\/]pages/.test(d)
)
if (files.length < 1) {
throw new Error('oops!')
}
await Promise.all(files.map((f) => fs.remove(join(appDir, '.next', f))))
})
afterAll(() => killApp(context.server))
const manifest = await getBuildManifest(appDir)
const files = manifest.pages['/missing'].filter((d) =>
/static[\\/]chunks[\\/]pages/.test(d)
)
if (files.length < 1) {
throw new Error('oops!')
}
await Promise.all(files.map((f) => fs.remove(join(appDir, '.next', f))))
})
afterAll(() => killApp(context.server))
runTests(true)
})
runTests(true)
}
)
})

View file

@ -88,7 +88,7 @@ const runTests = () => {
}
describe('Client Shallow Routing', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -97,14 +97,17 @@ describe('Client Shallow Routing', () => {
runTests()
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
runTests()
}
)
})

View file

@ -130,9 +130,11 @@ describe('Config Experimental Warning', () => {
expect(stdout).toContain(' · workerThreads')
expect(stdout).toContain(' · scrollRestoration')
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should not show next app info in next start', async () => {
configFile.write(`
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should not show next app info in next start', async () => {
configFile.write(`
module.exports = {
experimental: {
workerThreads: true,
@ -143,19 +145,19 @@ describe('Config Experimental Warning', () => {
}
`)
await collectStdoutFromBuild(appDir)
const port = await findPort()
let stdout = ''
app = await nextStart(appDir, port, {
onStdout(msg) {
stdout += msg
},
await collectStdoutFromBuild(appDir)
const port = await findPort()
let stdout = ''
app = await nextStart(appDir, port, {
onStdout(msg) {
stdout += msg
},
})
expect(stdout).not.toMatch(experimentalHeader)
})
expect(stdout).not.toMatch(experimentalHeader)
})
it('should show next app info with all experimental features in next build', async () => {
configFile.write(`
it('should show next app info with all experimental features in next build', async () => {
configFile.write(`
module.exports = {
experimental: {
workerThreads: true,
@ -165,16 +167,16 @@ describe('Config Experimental Warning', () => {
}
}
`)
const stdout = await collectStdoutFromBuild(appDir)
expect(stdout).toMatch(experimentalHeader)
expect(stdout).toMatch(' · cpus')
expect(stdout).toMatch(' · workerThreads')
expect(stdout).toMatch(' · scrollRestoration')
expect(stdout).toMatch(' · instrumentationHook')
})
const stdout = await collectStdoutFromBuild(appDir)
expect(stdout).toMatch(experimentalHeader)
expect(stdout).toMatch(' · cpus')
expect(stdout).toMatch(' · workerThreads')
expect(stdout).toMatch(' · scrollRestoration')
expect(stdout).toMatch(' · instrumentationHook')
})
it('should show unrecognized experimental features in warning but not in start log experiments section', async () => {
configFile.write(`
it('should show unrecognized experimental features in warning but not in start log experiments section', async () => {
configFile.write(`
module.exports = {
experimental: {
appDir: true
@ -182,27 +184,28 @@ describe('Config Experimental Warning', () => {
}
`)
await collectStdoutFromBuild(appDir)
const port = await findPort()
let stdout = ''
let stderr = ''
app = await nextStart(appDir, port, {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
await collectStdoutFromBuild(appDir)
const port = await findPort()
let stdout = ''
let stderr = ''
app = await nextStart(appDir, port, {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
await check(() => {
const cliOutput = stripAnsi(stdout)
const cliOutputErr = stripAnsi(stderr)
expect(cliOutput).not.toContain(experimentalHeader)
expect(cliOutputErr).toContain(
`Unrecognized key(s) in object: 'appDir' at "experimental"`
)
await check(() => {
const cliOutput = stripAnsi(stdout)
const cliOutputErr = stripAnsi(stderr)
expect(cliOutput).not.toContain(experimentalHeader)
expect(cliOutputErr).toContain(
`Unrecognized key(s) in object: 'appDir' at "experimental"`
)
})
})
})
})
}
)
})

View file

@ -7,13 +7,15 @@ import { nextBuild } from 'next-test-utils'
const appDir = join(__dirname, '..')
describe('Promise in next config', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
afterEach(() => fs.remove(join(appDir, 'next.config.js')))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
afterEach(() => fs.remove(join(appDir, 'next.config.js')))
it('should warn when a promise is returned on webpack', async () => {
fs.writeFile(
join(appDir, 'next.config.js'),
`
it('should warn when a promise is returned on webpack', async () => {
fs.writeFile(
join(appDir, 'next.config.js'),
`
module.exports = (phase, { isServer }) => {
return {
webpack: async (config) => {
@ -22,15 +24,16 @@ describe('Promise in next config', () => {
}
}
`
)
)
const { stderr, stdout } = await nextBuild(appDir, undefined, {
stderr: true,
stdout: true,
const { stderr, stdout } = await nextBuild(appDir, undefined, {
stderr: true,
stdout: true,
})
expect(stderr + stdout).toMatch(
/> Promise returned in next config\. https:\/\//
)
})
expect(stderr + stdout).toMatch(
/> Promise returned in next config\. https:\/\//
)
})
})
}
)
})

View file

@ -8,46 +8,49 @@ const nextConfigJS = join(appDir, 'next.config.js')
const nextConfigMJS = join(appDir, 'next.config.mjs')
describe('Invalid config syntax', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should error when next.config.js contains syntax error', async () => {
await fs.writeFile(
nextConfigJS,
`
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should error when next.config.js contains syntax error', async () => {
await fs.writeFile(
nextConfigJS,
`
module.exports = {
reactStrictMode: true,,
}
`
)
const { stderr } = await nextBuild(appDir, undefined, {
stderr: true,
)
const { stderr } = await nextBuild(appDir, undefined, {
stderr: true,
})
await fs.remove(nextConfigJS)
expect(stderr).toContain(
'Failed to load next.config.js, see more info here https://nextjs.org/docs/messages/next-config-error'
)
expect(stderr).toContain('SyntaxError')
})
await fs.remove(nextConfigJS)
expect(stderr).toContain(
'Failed to load next.config.js, see more info here https://nextjs.org/docs/messages/next-config-error'
)
expect(stderr).toContain('SyntaxError')
})
it('should error when next.config.mjs contains syntax error', async () => {
await fs.writeFile(
nextConfigMJS,
`
it('should error when next.config.mjs contains syntax error', async () => {
await fs.writeFile(
nextConfigMJS,
`
const config = {
reactStrictMode: true,,
}
export default config
`
)
const { stderr } = await nextBuild(appDir, undefined, {
stderr: true,
})
await fs.remove(nextConfigMJS)
)
const { stderr } = await nextBuild(appDir, undefined, {
stderr: true,
})
await fs.remove(nextConfigMJS)
expect(stderr).toContain(
'Failed to load next.config.mjs, see more info here https://nextjs.org/docs/messages/next-config-error'
)
expect(stderr).toContain('SyntaxError')
})
})
expect(stderr).toContain(
'Failed to load next.config.mjs, see more info here https://nextjs.org/docs/messages/next-config-error'
)
expect(stderr).toContain('SyntaxError')
})
}
)
})

View file

@ -5,11 +5,13 @@ import fs from 'fs-extra'
const nextConfigPath = path.join(__dirname, '../next.config.js')
describe('next.config.js validation', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it.each([
{
name: 'invalid config types',
configContent: `
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it.each([
{
name: 'invalid config types',
configContent: `
module.exports = {
swcMinify: 'hello',
rewrites: true,
@ -18,15 +20,15 @@ describe('next.config.js validation', () => {
}
}
`,
outputs: [
`received 'something' at "images.loader"`,
'Expected function, received boolean at "rewrites"',
'Expected boolean, received string at "swcMinify"',
],
},
{
name: 'unexpected config fields',
configContent: `
outputs: [
`received 'something' at "images.loader"`,
'Expected function, received boolean at "rewrites"',
'Expected boolean, received string at "swcMinify"',
],
},
{
name: 'unexpected config fields',
configContent: `
module.exports = {
nonExistent: true,
experimental: {
@ -34,29 +36,33 @@ describe('next.config.js validation', () => {
}
}
`,
outputs: [
`Unrecognized key(s) in object: 'nonExistent'`,
`Unrecognized key(s) in object: 'anotherNonExistent' at "experimental"`,
],
},
])(
'it should validate correctly for $name',
async ({ outputs, configContent }) => {
await fs.writeFile(nextConfigPath, configContent)
const result = await nextBuild(path.join(__dirname, '../'), undefined, {
stderr: true,
stdout: true,
})
await fs.remove(nextConfigPath)
outputs: [
`Unrecognized key(s) in object: 'nonExistent'`,
`Unrecognized key(s) in object: 'anotherNonExistent' at "experimental"`,
],
},
])(
'it should validate correctly for $name',
async ({ outputs, configContent }) => {
await fs.writeFile(nextConfigPath, configContent)
const result = await nextBuild(
path.join(__dirname, '../'),
undefined,
{
stderr: true,
stdout: true,
}
)
await fs.remove(nextConfigPath)
for (const output of outputs) {
expect(result.stdout + result.stderr).toContain(output)
for (const output of outputs) {
expect(result.stdout + result.stderr).toContain(output)
}
}
}
)
)
it('should allow undefined environment variables', async () => {
const configContent = `
it('should allow undefined environment variables', async () => {
const configContent = `
module.exports = {
env: {
FOO: 'bar',
@ -65,17 +71,18 @@ describe('next.config.js validation', () => {
}
`
await fs.writeFile(nextConfigPath, configContent)
const result = await nextBuild(path.join(__dirname, '../'), undefined, {
stderr: true,
stdout: true,
await fs.writeFile(nextConfigPath, configContent)
const result = await nextBuild(path.join(__dirname, '../'), undefined, {
stderr: true,
stdout: true,
})
await fs.remove(nextConfigPath)
expect(result.stdout + result.stderr).not.toContain(
'"env.QUX" is missing'
)
})
await fs.remove(nextConfigPath)
expect(result.stdout + result.stderr).not.toContain(
'"env.QUX" is missing'
)
})
})
}
)
})

View file

@ -27,19 +27,22 @@ describe('Errors on conflict between public file and page file', () => {
await killApp(app)
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('Throws error during build', async () => {
const conflicts = ['/another/conflict', '/another', '/hello']
const results = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
})
const output = results.stdout + results.stderr
expect(output).toMatch(/Conflicting public and page files were found/)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('Throws error during build', async () => {
const conflicts = ['/another/conflict', '/another', '/hello']
const results = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
})
const output = results.stdout + results.stderr
expect(output).toMatch(/Conflicting public and page files were found/)
for (const conflict of conflicts) {
expect(output.indexOf(conflict) > 0).toBe(true)
}
})
})
for (const conflict of conflicts) {
expect(output.indexOf(conflict) > 0).toBe(true)
}
})
}
)
})

View file

@ -8,14 +8,16 @@ const appDir = join(__dirname, '../')
const pagesDir = join(appDir, 'pages')
describe('Conflicting SSG paths', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
afterEach(() => fs.remove(pagesDir))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
afterEach(() => fs.remove(pagesDir))
it('should show proper error when two dynamic SSG routes have conflicting paths', async () => {
await fs.ensureDir(join(pagesDir, 'blog'))
await fs.writeFile(
join(pagesDir, 'blog/[slug].js'),
`
it('should show proper error when two dynamic SSG routes have conflicting paths', async () => {
await fs.ensureDir(join(pagesDir, 'blog'))
await fs.writeFile(
join(pagesDir, 'blog/[slug].js'),
`
export const getStaticProps = () => {
return {
props: {}
@ -36,11 +38,11 @@ describe('Conflicting SSG paths', () => {
return '/blog/[slug]'
}
`
)
)
await fs.writeFile(
join(pagesDir, '[...catchAll].js'),
`
await fs.writeFile(
join(pagesDir, '[...catchAll].js'),
`
export const getStaticProps = () => {
return {
props: {}
@ -61,39 +63,39 @@ describe('Conflicting SSG paths', () => {
return '/[catchAll]'
}
`
)
)
const result = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
const result = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
})
const output = result.stdout + result.stderr
expect(output).toContain(
'Conflicting paths returned from getStaticPaths, paths must be unique per page'
)
expect(output).toContain(
'https://nextjs.org/docs/messages/conflicting-ssg-paths'
)
expect(output).toContain(
`path: "/blog/conflicting" from page: "/[...catchAll]"`
)
expect(output).toContain(`conflicts with path: "/blog/conflicting"`)
})
const output = result.stdout + result.stderr
expect(output).toContain(
'Conflicting paths returned from getStaticPaths, paths must be unique per page'
)
expect(output).toContain(
'https://nextjs.org/docs/messages/conflicting-ssg-paths'
)
expect(output).toContain(
`path: "/blog/conflicting" from page: "/[...catchAll]"`
)
expect(output).toContain(`conflicts with path: "/blog/conflicting"`)
})
it('should show proper error when a dynamic SSG route conflicts with normal route', async () => {
await fs.ensureDir(join(pagesDir, 'hello'))
await fs.writeFile(
join(pagesDir, 'hello/world.js'),
`
it('should show proper error when a dynamic SSG route conflicts with normal route', async () => {
await fs.ensureDir(join(pagesDir, 'hello'))
await fs.writeFile(
join(pagesDir, 'hello/world.js'),
`
export default function Page() {
return '/hello/world'
}
`
)
)
await fs.writeFile(
join(pagesDir, '[...catchAll].js'),
`
await fs.writeFile(
join(pagesDir, '[...catchAll].js'),
`
export const getStaticProps = () => {
return {
props: {}
@ -114,40 +116,40 @@ describe('Conflicting SSG paths', () => {
return '/[catchAll]'
}
`
)
)
const result = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
const result = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
})
const output = result.stdout + result.stderr
expect(output).toContain(
'Conflicting paths returned from getStaticPaths, paths must be unique per page'
)
expect(output).toContain(
'https://nextjs.org/docs/messages/conflicting-ssg-paths'
)
expect(output).toContain(
`path: "/hellO/world" from page: "/[...catchAll]" conflicts with path: "/hello/world"`
)
})
const output = result.stdout + result.stderr
expect(output).toContain(
'Conflicting paths returned from getStaticPaths, paths must be unique per page'
)
expect(output).toContain(
'https://nextjs.org/docs/messages/conflicting-ssg-paths'
)
expect(output).toContain(
`path: "/hellO/world" from page: "/[...catchAll]" conflicts with path: "/hello/world"`
)
})
it('should show proper error when a dynamic SSG route conflicts with SSR route', async () => {
await fs.ensureDir(join(pagesDir, 'hello'))
await fs.writeFile(
join(pagesDir, 'hello/world.js'),
`
it('should show proper error when a dynamic SSG route conflicts with SSR route', async () => {
await fs.ensureDir(join(pagesDir, 'hello'))
await fs.writeFile(
join(pagesDir, 'hello/world.js'),
`
export const getServerSideProps = () => ({ props: {} })
export default function Page() {
return '/hello/world'
}
`
)
)
await fs.writeFile(
join(pagesDir, '[...catchAll].js'),
`
await fs.writeFile(
join(pagesDir, '[...catchAll].js'),
`
export const getStaticProps = () => {
return {
props: {}
@ -168,22 +170,23 @@ describe('Conflicting SSG paths', () => {
return '/[catchAll]'
}
`
)
)
const result = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
const result = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
})
const output = result.stdout + result.stderr
expect(output).toContain(
'Conflicting paths returned from getStaticPaths, paths must be unique per page'
)
expect(output).toContain(
'https://nextjs.org/docs/messages/conflicting-ssg-paths'
)
expect(output).toContain(
`path: "/hellO/world" from page: "/[...catchAll]" conflicts with path: "/hello/world"`
)
})
const output = result.stdout + result.stderr
expect(output).toContain(
'Conflicting paths returned from getStaticPaths, paths must be unique per page'
)
expect(output).toContain(
'https://nextjs.org/docs/messages/conflicting-ssg-paths'
)
expect(output).toContain(
`path: "/hellO/world" from page: "/[...catchAll]" conflicts with path: "/hello/world"`
)
})
})
}
)
})

View file

@ -57,25 +57,28 @@ function runTests() {
}
describe('CSS optimization for SSR apps', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { experimental: {optimizeCss: true} }`,
'utf8'
)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { experimental: {optimizeCss: true} }`,
'utf8'
)
if (fs.pathExistsSync(join(appDir, '.next'))) {
await fs.remove(join(appDir, '.next'))
}
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
})
runTests()
})
if (fs.pathExistsSync(join(appDir, '.next'))) {
await fs.remove(join(appDir, '.next'))
}
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
})
runTests()
}
)
})

View file

@ -150,75 +150,78 @@ function runTests(dev) {
}
describe('CSS Module client-side navigation', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await remove(join(appDir, '.next'))
await nextBuild(appDir)
const port = await findPort()
app = await nextStart(appDir, port)
appPort = await findPort()
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await remove(join(appDir, '.next'))
await nextBuild(appDir)
const port = await findPort()
app = await nextStart(appDir, port)
appPort = await findPort()
const proxy = httpProxy.createProxyServer({
target: `http://localhost:${port}`,
const proxy = httpProxy.createProxyServer({
target: `http://localhost:${port}`,
})
proxyServer = http.createServer(async (req, res) => {
if (stallCss && req.url.endsWith('.css')) {
console.log('stalling request for', req.url)
await new Promise((resolve) => setTimeout(resolve, 5 * 1000))
}
proxy.web(req, res)
})
proxy.on('error', (err) => {
console.warn('Failed to proxy', err)
})
await new Promise((resolve) => {
proxyServer.listen(appPort, () => resolve())
})
})
afterAll(async () => {
proxyServer.close()
await killApp(app)
})
proxyServer = http.createServer(async (req, res) => {
if (stallCss && req.url.endsWith('.css')) {
console.log('stalling request for', req.url)
await new Promise((resolve) => setTimeout(resolve, 5 * 1000))
it('should time out and hard navigate for stalled CSS request', async () => {
let browser
stallCss = true
try {
browser = await webdriver(appPort, '/red')
await browser.eval('window.beforeNav = "hello"')
const redColor = await browser.eval(
`window.getComputedStyle(document.querySelector('#verify-red')).color`
)
expect(redColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`)
expect(await browser.eval('window.beforeNav')).toBe('hello')
await browser.elementByCss('#link-blue').click()
await browser.waitForElementByCss('#verify-blue')
const blueColor = await browser.eval(
`window.getComputedStyle(document.querySelector('#verify-blue')).color`
)
expect(blueColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`)
// the timeout should have been reached and we did a hard
// navigation
expect(await browser.eval('window.beforeNav')).toBeFalsy()
} finally {
stallCss = false
if (browser) {
await browser.close()
}
}
proxy.web(req, res)
})
proxy.on('error', (err) => {
console.warn('Failed to proxy', err)
})
await new Promise((resolve) => {
proxyServer.listen(appPort, () => resolve())
})
})
afterAll(async () => {
proxyServer.close()
await killApp(app)
})
it('should time out and hard navigate for stalled CSS request', async () => {
let browser
stallCss = true
try {
browser = await webdriver(appPort, '/red')
await browser.eval('window.beforeNav = "hello"')
const redColor = await browser.eval(
`window.getComputedStyle(document.querySelector('#verify-red')).color`
)
expect(redColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`)
expect(await browser.eval('window.beforeNav')).toBe('hello')
await browser.elementByCss('#link-blue').click()
await browser.waitForElementByCss('#verify-blue')
const blueColor = await browser.eval(
`window.getComputedStyle(document.querySelector('#verify-blue')).color`
)
expect(blueColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`)
// the timeout should have been reached and we did a hard
// navigation
expect(await browser.eval('window.beforeNav')).toBeFalsy()
} finally {
stallCss = false
if (browser) {
await browser.close()
}
}
})
runTests()
})
runTests()
}
)
describe('dev', () => {
beforeAll(async () => {

View file

@ -8,53 +8,61 @@ import escapeStringRegexp from 'escape-string-regexp'
const fixturesDir = join(__dirname, '../..', 'css-fixtures')
describe('CSS Customization', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'custom-configuration')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'custom-configuration')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
describe('Basic CSS', () => {
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
describe('Basic CSS', () => {
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've compiled and prefixed`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've compiled and prefixed`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`"@media (480px <= width < 768px){::placeholder{color:green}}.video{max-width:400px;max-height:300px}"`
)
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`"@media (480px <= width < 768px){::placeholder{color:green}}.video{max-width:400px;max-height:300px}"`
)
// Contains a source map
expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//)
})
// Contains a source map
expect(cssContent).toMatch(
/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//
)
})
it(`should've emitted a source map`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a source map`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f))
const files = await readdir(cssFolder)
const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f))
expect(cssMapFiles.length).toBe(1)
const cssMapContent = (
await readFile(join(cssFolder, cssMapFiles[0]), 'utf8')
).trim()
expect(cssMapFiles.length).toBe(1)
const cssMapContent = (
await readFile(join(cssFolder, cssMapFiles[0]), 'utf8')
).trim()
const { version, mappings, sourcesContent } = JSON.parse(cssMapContent)
expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(`
const { version, mappings, sourcesContent } =
JSON.parse(cssMapContent)
expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(`
{
"mappings": "AACA,gCACE,cACE,WACF,CACF,CAGA,OACE,eAA0B,CAA1B,gBACF",
"sourcesContent": [
@ -74,55 +82,61 @@ describe('CSS Customization', () => {
"version": 3,
}
`)
})
})
describe('Correct CSS Customization Array', () => {
const appDir = join(fixturesDir, 'custom-configuration-arr')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've compiled and prefixed`, async () => {
const cssFolder = join(appDir, '.next/static/css')
describe('Correct CSS Customization Array', () => {
const appDir = join(fixturesDir, 'custom-configuration-arr')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`"@media (480px <= width < 768px){a:before{content:""}::placeholder{color:green}}.video{max-width:6400px;max-height:4800px;max-width:400rem;max-height:300rem}"`
)
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
// Contains a source map
expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//)
})
it(`should've compiled and prefixed`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a source map`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`"@media (480px <= width < 768px){a:before{content:""}::placeholder{color:green}}.video{max-width:6400px;max-height:4800px;max-width:400rem;max-height:300rem}"`
)
expect(cssMapFiles.length).toBe(1)
const cssMapContent = (
await readFile(join(cssFolder, cssMapFiles[0]), 'utf8')
).trim()
// Contains a source map
expect(cssContent).toMatch(
/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//
)
})
const { version, mappings, sourcesContent } = JSON.parse(cssMapContent)
expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(`
it(`should've emitted a source map`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f))
expect(cssMapFiles.length).toBe(1)
const cssMapContent = (
await readFile(join(cssFolder, cssMapFiles[0]), 'utf8')
).trim()
const { version, mappings, sourcesContent } =
JSON.parse(cssMapContent)
expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(`
{
"mappings": "AACA,gCACE,SACE,UACF,CACA,cACE,WACF,CACF,CAGA,OACE,gBAA4B,CAA5B,iBAA4B,CAA5B,gBAA4B,CAA5B,iBACF",
"sourcesContent": [
@ -145,246 +159,252 @@ describe('CSS Customization', () => {
"version": 3,
}
`)
})
})
describe('Correct CSS Customization custom loader', () => {
const appDir = join(fixturesDir, 'custom-configuration-loader')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout, stderr } = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
})
expect(code).toBe(0)
expect(stderr).toMatch(/Built-in CSS support is being disabled/)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've applied style`, async () => {
const pagesFolder = join(appDir, '.next/static/chunks/pages')
describe('Correct CSS Customization custom loader', () => {
const appDir = join(fixturesDir, 'custom-configuration-loader')
const files = await readdir(pagesFolder)
const indexFiles = files.filter((f) => /^index.+\.js$/.test(f))
expect(indexFiles.length).toBe(1)
const indexContent = await readFile(
join(pagesFolder, indexFiles[0]),
'utf8'
)
expect(indexContent).toMatch(/\.my-text\.jsx-[0-9a-z]+{color:red}/)
})
})
describe('Bad CSS Customization', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { stdout, stderr } = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(stdout).toMatch(/Compiled successfully/)
expect(stderr).toMatch(/field which is not supported.*?sourceMap/)
;[
'postcss-modules-values',
'postcss-modules-scope',
'postcss-modules-extract-imports',
'postcss-modules-local-by-default',
'postcss-modules',
].forEach((plugin) => {
expect(stderr).toMatch(
new RegExp(`Please remove the.*?${escapeStringRegexp(plugin)}`)
it('should compile successfully', async () => {
const { code, stdout, stderr } = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
})
expect(code).toBe(0)
expect(stderr).toMatch(/Built-in CSS support is being disabled/)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've applied style`, async () => {
const pagesFolder = join(appDir, '.next/static/chunks/pages')
const files = await readdir(pagesFolder)
const indexFiles = files.filter((f) => /^index.+\.js$/.test(f))
expect(indexFiles.length).toBe(1)
const indexContent = await readFile(
join(pagesFolder, indexFiles[0]),
'utf8'
)
expect(indexContent).toMatch(/\.my-text\.jsx-[0-9a-z]+{color:red}/)
})
})
describe('Bad CSS Customization', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { stdout, stderr } = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
})
expect(stdout).toMatch(/Compiled successfully/)
expect(stderr).toMatch(/field which is not supported.*?sourceMap/)
;[
'postcss-modules-values',
'postcss-modules-scope',
'postcss-modules-extract-imports',
'postcss-modules-local-by-default',
'postcss-modules',
].forEach((plugin) => {
expect(stderr).toMatch(
new RegExp(`Please remove the.*?${escapeStringRegexp(plugin)}`)
)
})
})
it(`should've compiled and prefixed`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".video{max-width:400px;max-height:300px}"`)
// Contains a source map
expect(cssContent).toMatch(
/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//
)
})
it(`should've emitted a source map`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f))
expect(cssMapFiles.length).toBe(1)
})
})
it(`should've compiled and prefixed`, async () => {
const cssFolder = join(appDir, '.next/static/css')
describe('Bad CSS Customization Array (1)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-1')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".video{max-width:400px;max-height:300px}"`)
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
// Contains a source map
expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//)
expect(stderr).toMatch(
/A PostCSS Plugin was passed as an array but did not provide its configuration \('postcss-trolling'\)/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
it(`should've emitted a source map`, async () => {
const cssFolder = join(appDir, '.next/static/css')
describe('Bad CSS Customization Array (2)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-2')
const files = await readdir(cssFolder)
const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f))
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(cssMapFiles.length).toBe(1)
})
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
describe('Bad CSS Customization Array (1)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-1')
beforeAll(async () => {
await remove(join(appDir, '.next'))
expect(stderr).toMatch(
/Error: Your PostCSS configuration for 'postcss-trolling' cannot have null configuration./
)
expect(stderr).toMatch(
/To disable 'postcss-trolling', pass false, otherwise, pass true or a configuration object./
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
describe('Bad CSS Customization Array (3)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-3')
expect(stderr).toMatch(
/A PostCSS Plugin was passed as an array but did not provide its configuration \('postcss-trolling'\)/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
describe('Bad CSS Customization Array (2)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-2')
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
beforeAll(async () => {
await remove(join(appDir, '.next'))
expect(stderr).toMatch(
/A PostCSS Plugin must be provided as a string. Instead, we got: '5'/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
describe('Bad CSS Customization Array (4)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-4')
expect(stderr).toMatch(
/Error: Your PostCSS configuration for 'postcss-trolling' cannot have null configuration./
)
expect(stderr).toMatch(
/To disable 'postcss-trolling', pass false, otherwise, pass true or a configuration object./
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
describe('Bad CSS Customization Array (3)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-3')
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
beforeAll(async () => {
await remove(join(appDir, '.next'))
expect(stderr).toMatch(/An unknown PostCSS plugin was provided \(5\)/)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
describe('Bad CSS Customization Array (5)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-5')
expect(stderr).toMatch(
/A PostCSS Plugin must be provided as a string. Instead, we got: '5'/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
describe('Bad CSS Customization Array (4)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-4')
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
beforeAll(async () => {
await remove(join(appDir, '.next'))
expect(stderr).toMatch(
/Your custom PostCSS configuration must export a `plugins` key./
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
describe('Bad CSS Customization Array (6)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-6')
expect(stderr).toMatch(/An unknown PostCSS plugin was provided \(5\)/)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
describe('Bad CSS Customization Array (5)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-5')
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
beforeAll(async () => {
await remove(join(appDir, '.next'))
expect(stderr).toMatch(
/Your custom PostCSS configuration must export a `plugins` key./
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
describe('Bad CSS Customization Array (7)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-7')
expect(stderr).toMatch(
/Your custom PostCSS configuration must export a `plugins` key./
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
describe('Bad CSS Customization Array (6)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-6')
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
beforeAll(async () => {
await remove(join(appDir, '.next'))
expect(stderr).toMatch(
/A PostCSS Plugin was passed as an array but did not provide its configuration \('postcss-trolling'\)/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
describe('Bad CSS Customization Array (8)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-8')
expect(stderr).toMatch(
/Your custom PostCSS configuration must export a `plugins` key./
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
describe('Bad CSS Customization Array (7)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-7')
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
beforeAll(async () => {
await remove(join(appDir, '.next'))
expect(stderr).toMatch(
/A PostCSS Plugin was passed as a function using require\(\), but it must be provided as a string/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
describe('Bad CSS Customization Function', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-func')
expect(stderr).toMatch(
/A PostCSS Plugin was passed as an array but did not provide its configuration \('postcss-trolling'\)/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
expect(stderr).toMatch(
/Your custom PostCSS configuration may not export a function/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
})
describe('Bad CSS Customization Array (8)', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-arr-8')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
expect(stderr).toMatch(
/A PostCSS Plugin was passed as a function using require\(\), but it must be provided as a string/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
describe('Bad CSS Customization Function', () => {
const appDir = join(fixturesDir, 'bad-custom-configuration-func')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail the build', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })
expect(stderr).toMatch(
/Your custom PostCSS configuration may not export a function/
)
expect(stderr).toMatch(/Build failed because of webpack errors/)
})
})
})
}
)
})

View file

@ -7,73 +7,79 @@ import { join } from 'path'
const fixturesDir = join(__dirname, '../fixtures')
describe('Browserslist: Old', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'browsers-old')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'browsers-old')
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`"a{-webkit-animation:none 0s ease 0s 1 normal none running;animation:none 0s ease 0s 1 normal none running;-webkit-backface-visibility:visible;backface-visibility:visible;background:transparent none repeat 0 0/auto auto padding-box border-box scroll;border:none;border-collapse:separate;-webkit-border-image:none;border-image:none;-webkit-border-radius:0;border-radius:0;border-spacing:0;bottom:auto;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:content-box;box-sizing:content-box;caption-side:top;clear:none;clip:auto;color:#000;-webkit-columns:auto;-webkit-column-count:auto;-webkit-column-fill:balance;column-fill:balance;-webkit-column-gap:normal;column-gap:normal;-webkit-column-rule:medium none currentColor;column-rule:medium none currentColor;-webkit-column-span:1;column-span:1;-webkit-column-width:auto;columns:auto;content:normal;counter-increment:none;counter-reset:none;cursor:auto;direction:ltr;display:inline;empty-cells:show;float:none;font-family:serif;font-size:medium;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;line-height:normal;height:auto;-ms-hyphens:none;hyphens:none;left:auto;letter-spacing:normal;list-style:disc none outside;margin:0;max-height:none;max-width:none;min-height:0;min-width:0;opacity:1;orphans:2;outline:medium none invert;overflow:visible;overflow-x:visible;overflow-y:visible;padding:0;page-break-after:auto;page-break-before:auto;page-break-inside:auto;-webkit-perspective:none;perspective:none;-webkit-perspective-origin:50% 50%;perspective-origin:50% 50%;position:static;right:auto;tab-size:8;table-layout:auto;text-align:left;text-align-last:auto;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;top:auto;-webkit-transform:none;transform:none;-webkit-transform-origin:50% 50% 0;transform-origin:50% 50% 0;-webkit-transform-style:flat;transform-style:flat;-webkit-transition:none 0s ease 0s;transition:none 0s ease 0s;unicode-bidi:normal;vertical-align:baseline;visibility:visible;white-space:normal;widows:2;width:auto;word-spacing:normal;z-index:auto;all:initial}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:2dppx){.image{background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}}"`
)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`"a{-webkit-animation:none 0s ease 0s 1 normal none running;animation:none 0s ease 0s 1 normal none running;-webkit-backface-visibility:visible;backface-visibility:visible;background:transparent none repeat 0 0/auto auto padding-box border-box scroll;border:none;border-collapse:separate;-webkit-border-image:none;border-image:none;-webkit-border-radius:0;border-radius:0;border-spacing:0;bottom:auto;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:content-box;box-sizing:content-box;caption-side:top;clear:none;clip:auto;color:#000;-webkit-columns:auto;-webkit-column-count:auto;-webkit-column-fill:balance;column-fill:balance;-webkit-column-gap:normal;column-gap:normal;-webkit-column-rule:medium none currentColor;column-rule:medium none currentColor;-webkit-column-span:1;column-span:1;-webkit-column-width:auto;columns:auto;content:normal;counter-increment:none;counter-reset:none;cursor:auto;direction:ltr;display:inline;empty-cells:show;float:none;font-family:serif;font-size:medium;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;line-height:normal;height:auto;-ms-hyphens:none;hyphens:none;left:auto;letter-spacing:normal;list-style:disc none outside;margin:0;max-height:none;max-width:none;min-height:0;min-width:0;opacity:1;orphans:2;outline:medium none invert;overflow:visible;overflow-x:visible;overflow-y:visible;padding:0;page-break-after:auto;page-break-before:auto;page-break-inside:auto;-webkit-perspective:none;perspective:none;-webkit-perspective-origin:50% 50%;perspective-origin:50% 50%;position:static;right:auto;tab-size:8;table-layout:auto;text-align:left;text-align-last:auto;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;top:auto;-webkit-transform:none;transform:none;-webkit-transform-origin:50% 50% 0;transform-origin:50% 50% 0;-webkit-transform-style:flat;transform-style:flat;-webkit-transition:none 0s ease 0s;transition:none 0s ease 0s;unicode-bidi:normal;vertical-align:baseline;visibility:visible;white-space:normal;widows:2;width:auto;word-spacing:normal;z-index:auto;all:initial}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:2dppx){.image{background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}}"`
)
})
}
)
})
describe('Browserslist: New', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'browsers-new')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'browsers-new')
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`"a{all:initial}@media (min-resolution:2dppx){.image{background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}}"`
)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`"a{all:initial}@media (min-resolution:2dppx){.image{background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}}"`
)
})
}
)
})

View file

@ -7,129 +7,144 @@ import { join } from 'path'
const fixturesDir = join(__dirname, '../fixtures')
describe('Custom Properties: Fail for :root {} in CSS Modules', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'cp-global-modules')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'cp-global-modules')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('pages/styles.module.css')
expect(stderr).toContain('Selector ":root" is not pure')
})
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('pages/styles.module.css')
expect(stderr).toContain('Selector ":root" is not pure')
})
}
)
})
describe('Custom Properties: Fail for global element in CSS Modules', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'cp-el-modules')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'cp-el-modules')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('pages/styles.module.css')
expect(stderr).toContain('Selector "h1" is not pure')
})
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('pages/styles.module.css')
expect(stderr).toContain('Selector "h1" is not pure')
})
}
)
})
describe('CSS Modules: Import Global CSS', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'module-import-global')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'module-import-global')
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`"a .styles_foo__G5630{all:initial}"`)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`"a .styles_foo__G5630{all:initial}"`)
})
}
)
})
describe('CSS Modules: Importing Invalid Global CSS', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'module-import-global-invalid')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'module-import-global-invalid')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('pages/styles.css')
expect(stderr).toContain('Selector "a" is not pure')
})
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('pages/styles.css')
expect(stderr).toContain('Selector "a" is not pure')
})
}
)
})
describe('CSS Modules: Import Exports', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'module-import-exports')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'module-import-exports')
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".styles_blk__480DC{color:#000}"`)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".styles_blk__480DC{color:#000}"`)
})
}
)
})

View file

@ -7,103 +7,112 @@ import { join } from 'path'
const fixturesDir = join(__dirname, '../fixtures')
describe('Custom Properties: Pass-Through IE11', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'cp-ie-11')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'cp-ie-11')
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`":root{--color:red}h1{color:var(--color)}"`)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`":root{--color:red}h1{color:var(--color)}"`)
})
}
)
})
describe('Custom Properties: Pass-Through Modern', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'cp-modern')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'cp-modern')
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`":root{--color:red}h1{color:var(--color)}"`)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`":root{--color:red}h1{color:var(--color)}"`)
})
}
)
})
describe('Inline Comments: Minify', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'inline-comments')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'inline-comments')
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`"*{box-sizing:border-box}"`)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`"*{box-sizing:border-box}"`)
})
}
)
})

View file

@ -28,15 +28,18 @@ function runTests() {
}
describe('css-minify', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
runTests()
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
runTests()
}
)
})

View file

@ -18,122 +18,132 @@ import { join } from 'path'
const fixturesDir = join(__dirname, '../../css-fixtures')
describe('Basic CSS Module Support', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'basic-module')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'basic-module')
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
expect(stdout).toContain('.css')
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
expect(stdout).toContain('.css')
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".index_redText__honUV{color:red}"`)
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".index_redText__honUV{color:red}"`)
})
it(`should've injected the CSS on server render`, async () => {
const content = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(content)
it(`should've injected the CSS on server render`, async () => {
const content = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(content)
const cssPreload = $('link[rel="preload"][as="style"]')
expect(cssPreload.length).toBe(1)
expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/)
const cssPreload = $('link[rel="preload"][as="style"]')
expect(cssPreload.length).toBe(1)
expect(cssPreload.attr('href')).toMatch(
/^\/_next\/static\/css\/.*\.css$/
)
const cssSheet = $('link[rel="stylesheet"]')
expect(cssSheet.length).toBe(1)
expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/)
const cssSheet = $('link[rel="stylesheet"]')
expect(cssSheet.length).toBe(1)
expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/)
expect($('#verify-red').attr('class')).toMatchInlineSnapshot(
`"index_redText__honUV"`
)
})
})
expect($('#verify-red').attr('class')).toMatchInlineSnapshot(
`"index_redText__honUV"`
)
})
}
)
})
describe('3rd Party CSS Module Support', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, '3rd-party-module')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, '3rd-party-module')
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".index_foo__6TgnK{position:relative}.index_foo__6TgnK .bar,.index_foo__6TgnK .baz{height:100%;overflow:hidden}.index_foo__6TgnK .lol,.index_foo__6TgnK>.lel{width:80%}"`
)
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".index_foo__6TgnK{position:relative}.index_foo__6TgnK .bar,.index_foo__6TgnK .baz{height:100%;overflow:hidden}.index_foo__6TgnK .lol,.index_foo__6TgnK>.lel{width:80%}"`
)
})
it(`should've injected the CSS on server render`, async () => {
const content = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(content)
it(`should've injected the CSS on server render`, async () => {
const content = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(content)
const cssPreload = $('link[rel="preload"][as="style"]')
expect(cssPreload.length).toBe(1)
expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/)
const cssPreload = $('link[rel="preload"][as="style"]')
expect(cssPreload.length).toBe(1)
expect(cssPreload.attr('href')).toMatch(
/^\/_next\/static\/css\/.*\.css$/
)
const cssSheet = $('link[rel="stylesheet"]')
expect(cssSheet.length).toBe(1)
expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/)
const cssSheet = $('link[rel="stylesheet"]')
expect(cssSheet.length).toBe(1)
expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/)
expect($('#verify-div').attr('class')).toMatchInlineSnapshot(
`"index_foo__6TgnK"`
)
})
})
expect($('#verify-div').attr('class')).toMatchInlineSnapshot(
`"index_foo__6TgnK"`
)
})
}
)
})
describe('Has CSS Module in computed styles in Development', () => {
@ -161,39 +171,42 @@ describe('Has CSS Module in computed styles in Development', () => {
})
describe('Has CSS Module in computed styles in Production', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'prod-module')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'prod-module')
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have CSS for page', async () => {
const browser = await webdriver(appPort, '/')
it('should have CSS for page', async () => {
const browser = await webdriver(appPort, '/')
const currentColor = await browser.eval(
`window.getComputedStyle(document.querySelector('#verify-red')).color`
)
expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`)
})
})
const currentColor = await browser.eval(
`window.getComputedStyle(document.querySelector('#verify-red')).color`
)
expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`)
})
}
)
})
describe('Can hot reload CSS Module without losing state', () => {
@ -243,341 +256,365 @@ describe('Can hot reload CSS Module without losing state', () => {
})
describe.skip('Invalid CSS Module Usage in node_modules', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'invalid-module')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'invalid-module')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('node_modules/example/index.module.css')
expect(stderr).toMatch(
/CSS Modules.*cannot.*be imported from within.*node_modules/
)
expect(stderr).toMatch(
/Location:.*node_modules[\\/]example[\\/]index\.mjs/
)
})
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('node_modules/example/index.module.css')
expect(stderr).toMatch(
/CSS Modules.*cannot.*be imported from within.*node_modules/
)
expect(stderr).toMatch(
/Location:.*node_modules[\\/]example[\\/]index\.mjs/
)
})
}
)
})
describe.skip('Invalid Global CSS Module Usage in node_modules', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'invalid-global-module')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'invalid-global-module')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('node_modules/example/index.css')
expect(stderr).toMatch(
/Global CSS.*cannot.*be imported from within.*node_modules/
)
expect(stderr).toMatch(
/Location:.*node_modules[\\/]example[\\/]index\.mjs/
)
})
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('node_modules/example/index.css')
expect(stderr).toMatch(
/Global CSS.*cannot.*be imported from within.*node_modules/
)
expect(stderr).toMatch(
/Location:.*node_modules[\\/]example[\\/]index\.mjs/
)
})
}
)
})
describe('Valid CSS Module Usage from within node_modules', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'nm-module')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'nm-module')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've prerendered with relevant data`, async () => {
const content = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(content)
it(`should've prerendered with relevant data`, async () => {
const content = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(content)
const cssPreload = $('#nm-div')
expect(cssPreload.text()).toMatchInlineSnapshot(
`"{"message":"Why hello there"} {"redText":"example_redText__0ctGB"}"`
)
})
const cssPreload = $('#nm-div')
expect(cssPreload.text()).toMatchInlineSnapshot(
`"{"message":"Why hello there"} {"redText":"example_redText__0ctGB"}"`
)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".example_redText__0ctGB{color:red}"`)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".example_redText__0ctGB{color:red}"`)
})
}
)
})
describe('Valid Nested CSS Module Usage from within node_modules', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'nm-module-nested')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'nm-module-nested')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've prerendered with relevant data`, async () => {
const content = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(content)
it(`should've prerendered with relevant data`, async () => {
const content = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(content)
const cssPreload = $('#nm-div')
expect(cssPreload.text()).toMatchInlineSnapshot(
`"{"message":"Why hello there"} {"subClass":"example_subClass__m6Tyy other_className__OA8dV"}"`
)
})
const cssPreload = $('#nm-div')
expect(cssPreload.text()).toMatchInlineSnapshot(
`"{"message":"Why hello there"} {"subClass":"example_subClass__m6Tyy other_className__OA8dV"}"`
)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".other2_other2__dYPgz{color:red}.other3_other3__7hgUE{color:violet}.other_className__OA8dV{background:red;color:#ff0}.example_subClass__m6Tyy{background:blue}"`
)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".other2_other2__dYPgz{color:red}.other3_other3__7hgUE{color:violet}.other_className__OA8dV{background:red;color:#ff0}.example_subClass__m6Tyy{background:blue}"`
)
})
}
)
})
describe('CSS Module Composes Usage (Basic)', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
// This is a very bad feature. Do not use it.
const appDir = join(fixturesDir, 'composes-basic')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
// This is a very bad feature. Do not use it.
const appDir = join(fixturesDir, 'composes-basic')
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".index_className__jjcZ1{background:red;color:#ff0}.index_subClass__eDzaW{background:blue}"`
)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".index_className__jjcZ1{background:red;color:#ff0}.index_subClass__eDzaW{background:blue}"`
)
})
}
)
})
describe('CSS Module Composes Usage (External)', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
// This is a very bad feature. Do not use it.
const appDir = join(fixturesDir, 'composes-external')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
// This is a very bad feature. Do not use it.
const appDir = join(fixturesDir, 'composes-external')
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".other_className__eZV4M{background:red;color:#ff0}.index_subClass__eDzaW{background:blue}"`
)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".other_className__eZV4M{background:red;color:#ff0}.index_subClass__eDzaW{background:blue}"`
)
})
}
)
})
describe('Dynamic Route CSS Module Usage', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'dynamic-route-module')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'dynamic-route-module')
let stdout
let code
let app
let appPort
let stdout
let code
let app
let appPort
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should apply styles correctly', async () => {
const browser = await webdriver(appPort, '/post-1')
it('should apply styles correctly', async () => {
const browser = await webdriver(appPort, '/post-1')
const background = await browser
.elementByCss('#my-div')
.getComputedCss('background-color')
const background = await browser
.elementByCss('#my-div')
.getComputedCss('background-color')
expect(background).toMatch(/rgb(a|)\(255, 0, 0/)
})
expect(background).toMatch(/rgb(a|)\(255, 0, 0/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`"._post__home__yRmHz{background:red}"`)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`"._post__home__yRmHz{background:red}"`)
})
}
)
})
describe('Catch-all Route CSS Module Usage', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'catch-all-module')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'catch-all-module')
let stdout
let code
let app
let appPort
let stdout
let code
let app
let appPort
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should apply styles correctly', async () => {
const browser = await webdriver(appPort, '/post-1')
it('should apply styles correctly', async () => {
const browser = await webdriver(appPort, '/post-1')
const bg = await browser
.elementByCss('#my-div')
.getComputedCss('background-color')
expect(bg).toMatch(/rgb(a|)\(255, 0, 0/)
const bg = await browser
.elementByCss('#my-div')
.getComputedCss('background-color')
expect(bg).toMatch(/rgb(a|)\(255, 0, 0/)
const fg = await browser.elementByCss('#my-div').getComputedCss('color')
expect(fg).toMatch(/rgb(a|)\(0, 128, 0/)
})
const fg = await browser.elementByCss('#my-div').getComputedCss('color')
expect(fg).toMatch(/rgb(a|)\(0, 128, 0/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".___post__home__e4zfx{background:red}.__55css_home__r8Rnq{color:green}"`
)
})
})
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(
`".___post__home__e4zfx{background:red}.__55css_home__r8Rnq{color:green}"`
)
})
}
)
})

View file

@ -6,14 +6,16 @@ import { join } from 'path'
const fixturesDir = join(__dirname, '../..', 'css-fixtures')
describe('Basic Global Support', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'single-global')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'single-global')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -21,45 +23,52 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
expect(
await readFile(join(cssFolder, cssFiles[0]), 'utf8')
).toContain('color:red')
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain(
'color:red'
)
})
})
})
}
)
})
describe('Basic Global Support with special characters in path', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'single-global-special-characters', 'a+b')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(
fixturesDir,
'single-global-special-characters',
'a+b'
)
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../../next.config.js');
module.exports = {
...config,
@ -67,45 +76,48 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
expect(
await readFile(join(cssFolder, cssFiles[0]), 'utf8')
).toContain('color:red')
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain(
'color:red'
)
})
})
})
}
)
})
describe('Basic Global Support with src/ dir', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'single-global-src')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'single-global-src')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -113,45 +125,48 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
expect(
await readFile(join(cssFolder, cssFiles[0]), 'utf8')
).toContain('color:red')
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
expect(await readFile(join(cssFolder, cssFiles[0]), 'utf8')).toContain(
'color:red'
)
})
})
})
}
)
})
describe('Multi Global Support', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'multi-global')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'multi-global')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -159,44 +174,52 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchSnapshot()
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchSnapshot()
})
})
})
}
)
})
describe('Nested @import() Global Support', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'nested-global')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'nested-global')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -204,45 +227,53 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchSnapshot()
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchSnapshot()
})
})
})
}
)
})
// Tests css ordering
describe('Multi Global Support (reversed)', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'multi-global-reversed')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'multi-global-reversed')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -250,44 +281,52 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchSnapshot()
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchSnapshot()
})
})
})
}
)
})
describe('CSS URL via `file-loader`', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'url-global')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'url-global')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -295,8 +334,63 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted expected files`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const mediaFolder = join(appDir, '.next/static/media')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchSnapshot()
const mediaFiles = await readdir(mediaFolder)
expect(mediaFiles.length).toBe(3)
expect(
mediaFiles
.map((fileName) =>
/^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.')
)
.sort()
).toMatchInlineSnapshot(`
[
"dark.svg",
"dark2.svg",
"light.svg",
]
`)
})
})
}
)
})
describe('CSS URL via `file-loader` and asset prefix (1)', () => {
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'url-global-asset-prefix-1')
beforeAll(async () => {
await remove(join(appDir, '.next'))
@ -319,7 +413,9 @@ module.exports = {
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchSnapshot()
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(
/^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/
)
const mediaFiles = await readdir(mediaFolder)
expect(mediaFiles.length).toBe(3)
@ -330,109 +426,64 @@ module.exports = {
)
.sort()
).toMatchInlineSnapshot(`
[
"dark.svg",
"dark2.svg",
"light.svg",
]
`)
})
})
})
})
describe('CSS URL via `file-loader` and asset prefix (1)', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'url-global-asset-prefix-1')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted expected files`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const mediaFolder = join(appDir, '.next/static/media')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(
/^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/
)
const mediaFiles = await readdir(mediaFolder)
expect(mediaFiles.length).toBe(3)
expect(
mediaFiles
.map((fileName) =>
/^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.')
)
.sort()
).toMatchInlineSnapshot(`
[
"dark.svg",
"dark2.svg",
"light.svg",
]
`)
})
})
})
}
)
})
describe('CSS URL via `file-loader` and asset prefix (2)', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'url-global-asset-prefix-2')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'url-global-asset-prefix-2')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted expected files`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const mediaFolder = join(appDir, '.next/static/media')
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
it(`should've emitted expected files`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const mediaFolder = join(appDir, '.next/static/media')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(
/^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/
)
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const mediaFiles = await readdir(mediaFolder)
expect(mediaFiles.length).toBe(3)
expect(
mediaFiles
.map((fileName) =>
/^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.')
)
.sort()
).toMatchInlineSnapshot(`
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(
/^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/
)
const mediaFiles = await readdir(mediaFolder)
expect(mediaFiles.length).toBe(3)
expect(
mediaFiles
.map((fileName) =>
/^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.')
)
.sort()
).toMatchInlineSnapshot(`
[
"dark.svg",
"dark2.svg",
"light.svg",
]
`)
})
})
})
}
)
})

View file

@ -37,37 +37,40 @@ describe('Ordering with styled-jsx (dev)', () => {
})
describe('Ordering with styled-jsx (prod)', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'with-styled-jsx')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'with-styled-jsx')
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have the correct color (css ordering)', async () => {
const browser = await webdriver(appPort, '/')
it('should have the correct color (css ordering)', async () => {
const browser = await webdriver(appPort, '/')
const currentColor = await browser.eval(
`window.getComputedStyle(document.querySelector('.my-text')).color`
)
expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 128, 0)"`)
})
})
const currentColor = await browser.eval(
`window.getComputedStyle(document.querySelector('.my-text')).color`
)
expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 128, 0)"`)
})
}
)
})

View file

@ -15,19 +15,23 @@ import { join } from 'path'
const fixturesDir = join(__dirname, '../..', 'css-fixtures')
describe('CSS Support', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
describe('CSS Compilation and Prefixing', () => {
const appDir = join(fixturesDir, 'compilation-and-prefixing')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
describe('CSS Compilation and Prefixing', () => {
const appDir = join(fixturesDir, 'compilation-and-prefixing')
const nextConfig = new File(join(appDir, 'next.config.js'))
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
await remove(join(appDir, '.next'))
})
describe.each([true, false])(
`useLightnincsss(%s)`,
(useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -35,127 +39,74 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
)
})
afterAll(async () => {
nextConfig.delete()
})
afterAll(async () => {
nextConfig.delete()
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've compiled and prefixed`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've compiled and prefixed`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchSnapshot()
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchSnapshot()
// Contains a source map
expect(cssContent).toMatch(
/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//
)
})
// Contains a source map
expect(cssContent).toMatch(
/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//
)
})
it(`should've emitted a source map`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a source map`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f))
const files = await readdir(cssFolder)
const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f))
expect(cssMapFiles.length).toBe(1)
const cssMapContent = (
await readFile(join(cssFolder, cssMapFiles[0]), 'utf8')
).trim()
expect(cssMapFiles.length).toBe(1)
const cssMapContent = (
await readFile(join(cssFolder, cssMapFiles[0]), 'utf8')
).trim()
const { version, mappings, sourcesContent } =
JSON.parse(cssMapContent)
expect({ version, mappings, sourcesContent }).toMatchSnapshot()
})
})
})
describe('React Lifecyce Order (production)', () => {
const appDir = join(fixturesDir, 'transition-react')
const nextConfig = new File(join(appDir, 'next.config.js'))
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
experimental: {
useLightningcss: ${useLightningcss}
}
}`
)
})
let appPort
let app
let code
let stdout
beforeAll(async () => {
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have the correct color on mount after navigation', async () => {
let browser
try {
browser = await webdriver(appPort, '/')
// Navigate to other:
await browser.waitForElementByCss('#link-other').click()
const text = await browser.waitForElementByCss('#red-title').text()
expect(text).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`)
} finally {
if (browser) {
await browser.close()
}
const { version, mappings, sourcesContent } =
JSON.parse(cssMapContent)
expect({ version, mappings, sourcesContent }).toMatchSnapshot()
})
}
})
)
})
})
describe('Has CSS in computed styles in Production', () => {
const appDir = join(fixturesDir, 'multi-page')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
describe('React Lifecyce Order (production)', () => {
const appDir = join(fixturesDir, 'transition-react')
const nextConfig = new File(join(appDir, 'next.config.js'))
beforeAll(async () => {
nextConfig.write(
`
await remove(join(appDir, '.next'))
})
describe.each([true, false])(
`useLightnincsss(%s)`,
(useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -163,73 +114,60 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
let appPort
let app
let code
let stdout
beforeAll(async () => {
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have CSS for page', async () => {
const browser = await webdriver(appPort, '/page2')
it('should have the correct color on mount after navigation', async () => {
let browser
try {
browser = await webdriver(appPort, '/')
const currentColor = await browser.eval(
`window.getComputedStyle(document.querySelector('.blue-text')).color`
)
expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`)
})
it(`should've preloaded the CSS file and injected it in <head>`, async () => {
const content = await renderViaHTTP(appPort, '/page2')
const $ = cheerio.load(content)
const cssPreload = $('link[rel="preload"][as="style"]')
expect(cssPreload.length).toBe(1)
expect(cssPreload.attr('href')).toMatch(
/^\/_next\/static\/css\/.*\.css$/
)
const cssSheet = $('link[rel="stylesheet"]')
expect(cssSheet.length).toBe(1)
expect(cssSheet.attr('href')).toMatch(
/^\/_next\/static\/css\/.*\.css$/
)
/* ensure CSS preloaded first */
const allPreloads = [].slice.call($('link[rel="preload"]'))
const styleIndexes = allPreloads.flatMap((p, i) =>
p.attribs.as === 'style' ? i : []
)
expect(styleIndexes).toEqual([0])
})
// Navigate to other:
await browser.waitForElementByCss('#link-other').click()
const text = await browser
.waitForElementByCss('#red-title')
.text()
expect(text).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`)
} finally {
if (browser) {
await browser.close()
}
}
})
}
)
})
})
describe('Good CSS Import from node_modules', () => {
const appDir = join(fixturesDir, 'npm-import')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe('Has CSS in computed styles in Production', () => {
const appDir = join(fixturesDir, 'multi-page')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(
`useLightnincsss(%s)`,
(useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -237,47 +175,76 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it('should have CSS for page', async () => {
const browser = await webdriver(appPort, '/page2')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const currentColor = await browser.eval(
`window.getComputedStyle(document.querySelector('.blue-text')).color`
)
expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`)
})
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(
/nprogress/
)
})
it(`should've preloaded the CSS file and injected it in <head>`, async () => {
const content = await renderViaHTTP(appPort, '/page2')
const $ = cheerio.load(content)
const cssPreload = $('link[rel="preload"][as="style"]')
expect(cssPreload.length).toBe(1)
expect(cssPreload.attr('href')).toMatch(
/^\/_next\/static\/css\/.*\.css$/
)
const cssSheet = $('link[rel="stylesheet"]')
expect(cssSheet.length).toBe(1)
expect(cssSheet.attr('href')).toMatch(
/^\/_next\/static\/css\/.*\.css$/
)
/* ensure CSS preloaded first */
const allPreloads = [].slice.call($('link[rel="preload"]'))
const styleIndexes = allPreloads.flatMap((p, i) =>
p.attribs.as === 'style' ? i : []
)
expect(styleIndexes).toEqual([0])
})
}
)
})
})
describe('Good Nested CSS Import from node_modules', () => {
const appDir = join(fixturesDir, 'npm-import-nested')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe('Good CSS Import from node_modules', () => {
const appDir = join(fixturesDir, 'npm-import')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(
`useLightnincsss(%s)`,
(useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -285,51 +252,106 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchSnapshot()
})
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(
/nprogress/
)
})
}
)
})
})
})
describe('Good Nested CSS Import from node_modules', () => {
const appDir = join(fixturesDir, 'npm-import-nested')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(
`useLightnincsss(%s)`,
(useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
experimental: {
useLightningcss: ${useLightningcss}
}
}`
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(
join(cssFolder, cssFiles[0]),
'utf8'
)
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchSnapshot()
})
}
)
})
}
)
})
// https://github.com/vercel/next.js/issues/15468
describe('CSS Property Ordering', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'next-issue-15468')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'next-issue-15468')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -337,43 +359,44 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have the border width (property ordering)', async () => {
const browser = await webdriver(appPort, '/')
const width1 = await browser.eval(
`window.getComputedStyle(document.querySelector('.test1')).borderWidth`
)
expect(width1).toMatchInlineSnapshot(`"0px"`)
const width2 = await browser.eval(
`window.getComputedStyle(document.querySelector('.test2')).borderWidth`
)
expect(width2).toMatchInlineSnapshot(`"5px"`)
})
})
it('should have the border width (property ordering)', async () => {
const browser = await webdriver(appPort, '/')
const width1 = await browser.eval(
`window.getComputedStyle(document.querySelector('.test1')).borderWidth`
)
expect(width1).toMatchInlineSnapshot(`"0px"`)
const width2 = await browser.eval(
`window.getComputedStyle(document.querySelector('.test2')).borderWidth`
)
expect(width2).toMatchInlineSnapshot(`"5px"`)
})
})
})
}
)
})

View file

@ -133,62 +133,69 @@ useLightningcss: ${useLightningcss}
})
describe('should handle unresolved files gracefully', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const workDir = join(fixturesDir, 'unresolved-css-url')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const workDir = join(fixturesDir, 'unresolved-css-url')
it('should build correctly', async () => {
await remove(join(workDir, '.next'))
const { code } = await nextBuild(workDir)
expect(code).toBe(0)
})
it('should build correctly', async () => {
await remove(join(workDir, '.next'))
const { code } = await nextBuild(workDir)
expect(code).toBe(0)
})
it('should have correct file references in CSS output', async () => {
const cssFiles = await readdir(join(workDir, '.next/static/css'))
it('should have correct file references in CSS output', async () => {
const cssFiles = await readdir(join(workDir, '.next/static/css'))
for (const file of cssFiles) {
if (file.endsWith('.css.map')) continue
for (const file of cssFiles) {
if (file.endsWith('.css.map')) continue
const content = await readFile(
join(workDir, '.next/static/css', file),
'utf8'
)
console.log(file, content)
const content = await readFile(
join(workDir, '.next/static/css', file),
'utf8'
)
console.log(file, content)
// if it is the combined global CSS file there are double the expected
// results
const howMany = content.includes('p{') || content.includes('p,') ? 2 : 1
// if it is the combined global CSS file there are double the expected
// results
const howMany =
content.includes('p{') || content.includes('p,') ? 2 : 1
expect(content.match(/\(\/vercel\.svg/g).length).toBe(howMany)
// expect(content.match(/\(vercel\.svg/g).length).toBe(howMany)
expect(content.match(/\(\/_next\/static\/media/g).length).toBe(1)
expect(content.match(/\(https:\/\//g).length).toBe(howMany)
}
})
})
expect(content.match(/\(\/vercel\.svg/g).length).toBe(howMany)
// expect(content.match(/\(vercel\.svg/g).length).toBe(howMany)
expect(content.match(/\(\/_next\/static\/media/g).length).toBe(1)
expect(content.match(/\(https:\/\//g).length).toBe(howMany)
}
})
}
)
})
describe('Data URLs', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const workDir = join(fixturesDir, 'data-url')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const workDir = join(fixturesDir, 'data-url')
it('should compile successfully', async () => {
await remove(join(workDir, '.next'))
const { code } = await nextBuild(workDir)
expect(code).toBe(0)
})
it('should compile successfully', async () => {
await remove(join(workDir, '.next'))
const { code } = await nextBuild(workDir)
expect(code).toBe(0)
})
it('should have emitted expected files', async () => {
const cssFolder = join(workDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
it('should have emitted expected files', async () => {
const cssFolder = join(workDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(
/background:url\("data:[^"]+"\)/
)
})
})
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch(
/background:url\("data:[^"]+"\)/
)
})
}
)
})
describe('Ordering with Global CSS and Modules (dev)', () => {
@ -291,14 +298,16 @@ useLightningcss: ${useLightningcss}
})
describe('Ordering with Global CSS and Modules (prod)', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'global-and-module-ordering')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'global-and-module-ordering')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -306,40 +315,41 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
let appPort
let app
let stdout
let code
beforeAll(async () => {
await remove(join(appDir, '.next'))
;({ code, stdout } = await nextBuild(appDir, [], {
stdout: true,
}))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have compiled successfully', () => {
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should have the correct color (css ordering)', async () => {
const browser = await webdriver(appPort, '/')
it('should have the correct color (css ordering)', async () => {
const browser = await webdriver(appPort, '/')
const currentColor = await browser.eval(
`window.getComputedStyle(document.querySelector('#blueText')).color`
)
expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`)
const currentColor = await browser.eval(
`window.getComputedStyle(document.querySelector('#blueText')).color`
)
expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`)
})
})
})
})
}
)
})
// https://github.com/vercel/next.js/issues/12445
@ -513,7 +523,7 @@ module.exports = {
tests(true)
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {

View file

@ -15,15 +15,19 @@ import { join } from 'path'
const fixturesDir = join(__dirname, '../..', 'css-fixtures')
describe('CSS Support', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
describe('CSS Import from node_modules', () => {
const appDir = join(fixturesDir, 'npm-import-bad')
const nextConfig = new File(join(appDir, 'next.config.js'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
describe('CSS Import from node_modules', () => {
const appDir = join(fixturesDir, 'npm-import-bad')
const nextConfig = new File(join(appDir, 'next.config.js'))
describe.each([true, false])(`useLightnincsss(%s)`, (useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
describe.each([true, false])(
`useLightnincsss(%s)`,
(useLightningcss) => {
beforeAll(async () => {
nextConfig.write(
`
const config = require('../next.config.js');
module.exports = {
...config,
@ -31,22 +35,28 @@ module.exports = {
useLightningcss: ${useLightningcss}
}
}`
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
)
})
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail the build', async () => {
const { code, stderr } = await nextBuild(appDir, [], { stderr: true })
it('should fail the build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).toBe(0)
expect(stderr).not.toMatch(/Can't resolve '[^']*?nprogress[^']*?'/)
expect(stderr).not.toMatch(/Build error occurred/)
})
expect(code).toBe(0)
expect(stderr).not.toMatch(
/Can't resolve '[^']*?nprogress[^']*?'/
)
expect(stderr).not.toMatch(/Build error occurred/)
})
}
)
})
})
})
}
)
// https://github.com/vercel/next.js/issues/18557
describe('CSS page transition inject <style> with nonce so it works with CSP header', () => {
@ -185,7 +195,7 @@ module.exports = {
})
}
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
@ -267,7 +277,7 @@ module.exports = {
})
}
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
@ -335,7 +345,7 @@ module.exports = {
})
}
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
@ -450,7 +460,7 @@ module.exports = {
})
}
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {

View file

@ -6,122 +6,139 @@ import { join } from 'path'
const fixturesDir = join(__dirname, '../..', 'css-fixtures')
describe('Invalid CSS in _document', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'invalid-module-document')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'invalid-module-document')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('styles.module.css')
expect(stderr).toMatch(
/CSS.*cannot.*be imported within.*pages[\\/]_document\.js/
)
expect(stderr).toMatch(/Location:.*pages[\\/]_document\.js/)
})
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('styles.module.css')
expect(stderr).toMatch(
/CSS.*cannot.*be imported within.*pages[\\/]_document\.js/
)
expect(stderr).toMatch(/Location:.*pages[\\/]_document\.js/)
})
}
)
})
describe('Invalid Global CSS', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'invalid-global')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'invalid-global')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('styles/global.css')
expect(stderr).toMatch(
/Please move all first-party global CSS imports.*?pages(\/|\\)_app/
)
expect(stderr).toMatch(/Location:.*pages[\\/]index\.js/)
})
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('styles/global.css')
expect(stderr).toMatch(
/Please move all first-party global CSS imports.*?pages(\/|\\)_app/
)
expect(stderr).toMatch(/Location:.*pages[\\/]index\.js/)
})
}
)
})
describe('Valid Global CSS from npm', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'import-global-from-module')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'import-global-from-module')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
it('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
it(`should've emitted a single CSS file`, async () => {
const cssFolder = join(appDir, '.next/static/css')
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".red-text{color:"red"}"`)
})
})
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(
cssContent.replace(/\/\*.*?\*\//g, '').trim()
).toMatchInlineSnapshot(`".red-text{color:"red"}"`)
})
}
)
})
describe('Invalid Global CSS with Custom App', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'invalid-global-with-app')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'invalid-global-with-app')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('styles/global.css')
expect(stderr).toMatch(
/Please move all first-party global CSS imports.*?pages(\/|\\)_app/
)
expect(stderr).toMatch(/Location:.*pages[\\/]index\.js/)
})
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('styles/global.css')
expect(stderr).toMatch(
/Please move all first-party global CSS imports.*?pages(\/|\\)_app/
)
expect(stderr).toMatch(/Location:.*pages[\\/]index\.js/)
})
}
)
})
describe('Valid and Invalid Global CSS with Custom App', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const appDir = join(fixturesDir, 'valid-and-invalid-global')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const appDir = join(fixturesDir, 'valid-and-invalid-global')
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('styles/global.css')
expect(stderr).toContain('Please move all first-party global CSS imports')
expect(stderr).toMatch(/Location:.*pages[\\/]index\.js/)
})
})
it('should fail to build', async () => {
const { code, stderr } = await nextBuild(appDir, [], {
stderr: true,
})
expect(code).not.toBe(0)
expect(stderr).toContain('Failed to compile')
expect(stderr).toContain('styles/global.css')
expect(stderr).toContain(
'Please move all first-party global CSS imports'
)
expect(stderr).toMatch(/Location:.*pages[\\/]index\.js/)
})
}
)
})

View file

@ -11,26 +11,29 @@ let app
// TODO: re-enable with React 18
describe.skip('Custom error page exception', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir, undefined, {
nodeArgs,
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir, undefined, {
nodeArgs,
})
appPort = await findPort()
app = await nextStart(appDir, appPort, {
nodeArgs,
})
})
appPort = await findPort()
app = await nextStart(appDir, appPort, {
nodeArgs,
})
})
afterAll(() => killApp(app))
it('should handle errors from _error render', async () => {
const navSel = '#nav'
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss(navSel).elementByCss(navSel).click()
afterAll(() => killApp(app))
it('should handle errors from _error render', async () => {
const navSel = '#nav'
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss(navSel).elementByCss(navSel).click()
await check(
() => browser.eval('document.documentElement.innerHTML'),
/Application error: a client-side exception has occurred/
)
})
})
await check(
() => browser.eval('document.documentElement.innerHTML'),
/Application error: a client-side exception has occurred/
)
})
}
)
})

View file

@ -27,7 +27,7 @@ const customErrNo404Match =
/You have added a custom \/_error page without a custom \/404 page/
describe('Custom _error', () => {
describe('dev mode 1', () => {
describe('development mode 1', () => {
let stderr = ''
beforeAll(async () => {
@ -50,7 +50,7 @@ describe('Custom _error', () => {
})
})
describe('dev mode 2', () => {
describe('development mode 2', () => {
let stderr = ''
beforeAll(async () => {
@ -79,25 +79,28 @@ describe('Custom _error', () => {
expect(html).toContain('An error 404 occurred on server')
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
let buildOutput = ''
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
let buildOutput = ''
beforeAll(async () => {
const { stdout, stderr } = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
beforeAll(async () => {
const { stdout, stderr } = await nextBuild(appDir, undefined, {
stdout: true,
stderr: true,
})
buildOutput = (stdout || '') + (stderr || '')
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
buildOutput = (stdout || '') + (stderr || '')
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
afterAll(() => killApp(app))
it('should not contain /_error in build output', async () => {
expect(buildOutput).toMatch(/λ .*?\/404/)
expect(buildOutput).not.toMatch(/λ .*?\/_error/)
})
it('should not contain /_error in build output', async () => {
expect(buildOutput).toMatch(/λ .*?\/404/)
expect(buildOutput).not.toMatch(/λ .*?\/_error/)
})
runTests()
})
runTests()
}
)
})

View file

@ -27,7 +27,7 @@ const runTests = () => {
}
describe('Custom page extension', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -35,13 +35,16 @@ describe('Custom page extension', () => {
afterAll(() => killApp(app))
runTests()
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
}
)
})

View file

@ -44,7 +44,7 @@ const runTests = () => {
}
describe('Custom routes', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -53,14 +53,17 @@ describe('Custom routes', () => {
afterAll(() => killApp(app))
runTests(true)
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(() => killApp(app))
runTests()
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(() => killApp(app))
runTests()
}
)
})

View file

@ -61,7 +61,7 @@ describe('Custom routes i18n with index redirect', () => {
server.close()
})
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -69,13 +69,16 @@ describe('Custom routes i18n with index redirect', () => {
afterAll(() => killApp(app))
runTests(true)
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
}
)
})

View file

@ -168,7 +168,7 @@ describe('Custom routes i18n', () => {
nextConfig.restore()
})
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -176,13 +176,16 @@ describe('Custom routes i18n', () => {
afterAll(() => killApp(app))
runTests(true)
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
}
)
})

View file

@ -2636,7 +2636,7 @@ describe('Custom routes', () => {
await fs.writeFile(nextConfigPath, nextConfigRestoreContent)
})
describe('dev mode', () => {
describe('development mode', () => {
let nextConfigContent
beforeAll(async () => {
@ -2686,31 +2686,34 @@ describe('Custom routes', () => {
)
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
const { stdout: buildStdout, stderr: buildStderr } = await nextBuild(
appDir,
['-d'],
{
stdout: true,
stderr: true,
}
)
stdout = buildStdout
stderr = buildStderr
appPort = await findPort()
app = await nextStart(appDir, appPort)
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(() => killApp(app))
runTests()
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
const { stdout: buildStdout, stderr: buildStderr } = await nextBuild(
appDir,
['-d'],
{
stdout: true,
stderr: true,
}
)
stdout = buildStdout
stderr = buildStderr
appPort = await findPort()
app = await nextStart(appDir, appPort)
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(() => killApp(app))
runTests()
it('should not show warning for custom routes when not next export', async () => {
expect(stderr).not.toContain(
`rewrites, redirects, and headers are not applied when exporting your application detected`
)
})
})
it('should not show warning for custom routes when not next export', async () => {
expect(stderr).not.toContain(
`rewrites, redirects, and headers are not applied when exporting your application detected`
)
})
}
)
describe('should load custom routes when only one type is used', () => {
const runSoloTests = (isDev) => {
@ -2807,10 +2810,10 @@ describe('Custom routes', () => {
})
}
describe('dev mode', () => {
describe('development mode', () => {
runSoloTests(true)
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
runSoloTests()
@ -2820,27 +2823,30 @@ describe('Custom routes', () => {
})
describe('export', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
nextConfig.replace('// REPLACEME', `output: 'export',`)
const { stdout: buildStdout, stderr: buildStderr } = await nextBuild(
appDir,
['-d'],
{
stdout: true,
stderr: true,
}
)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
nextConfig.replace('// REPLACEME', `output: 'export',`)
const { stdout: buildStdout, stderr: buildStderr } = await nextBuild(
appDir,
['-d'],
{
stdout: true,
stderr: true,
}
)
stdout = buildStdout
stderr = buildStderr
})
afterAll(() => nextConfig.restore())
stdout = buildStdout
stderr = buildStderr
})
afterAll(() => nextConfig.restore())
it('should not show warning for custom routes when not next export', async () => {
expect(stderr).not.toContain(
`rewrites, redirects, and headers are not applied when exporting your application detected`
)
})
})
it('should not show warning for custom routes when not next export', async () => {
expect(stderr).not.toContain(
`rewrites, redirects, and headers are not applied when exporting your application detected`
)
})
}
)
})

View file

@ -141,7 +141,7 @@ describe.each([
})
describe('with generateEtags enabled', () => {
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
@ -211,7 +211,7 @@ describe.each([
describe('Error when rendering without starting slash', () => {
afterEach(() => killApp(server))
it('should warn in dev mode', async () => {
it('should warn in development mode', async () => {
let stderr = ''
await startServer(
{},
@ -227,7 +227,7 @@ describe.each([
expect(html).toContain('made it to dashboard')
expect(stderr).toContain('Cannot render page with path "dashboard"')
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should warn in production mode', async () => {

View file

@ -129,17 +129,19 @@ describe('GS(S)P Page Errors', () => {
})
afterAll(() => fs.writeFile(indexPage, origIndexPage))
describe('dev mode', () => {
describe('development mode', () => {
runTests(true)
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
runTests()
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
runTests()
it('Error stack printed to stderr', async () => {
try {
await fs.writeFile(
indexPage,
`export default function Page() {
it('Error stack printed to stderr', async () => {
try {
await fs.writeFile(
indexPage,
`export default function Page() {
return <div/>
}
export function getStaticProps() {
@ -147,31 +149,32 @@ describe('GS(S)P Page Errors', () => {
if(process.env.NEXT_PHASE === "${PHASE_PRODUCTION_BUILD}") {
return { props: { foo: 'bar' }, revalidate: 1 }
}
throw new Error("Oops")
}
`
)
)
await nextBuild(appDir)
await nextBuild(appDir)
appPort = await findPort()
appPort = await findPort()
let stderr = ''
app = await nextStart(appDir, appPort, {
onStderr: (msg) => {
stderr += msg || ''
},
})
await check(async () => {
await renderViaHTTP(appPort, '/')
return stderr
}, /error: oops/i)
let stderr = ''
app = await nextStart(appDir, appPort, {
onStderr: (msg) => {
stderr += msg || ''
},
})
await check(async () => {
await renderViaHTTP(appPort, '/')
return stderr
}, /error: oops/i)
expect(stderr).toContain('Error: Oops')
} finally {
await killApp(app)
}
})
})
expect(stderr).toContain('Error: Oops')
} finally {
await killApp(app)
}
})
}
)
})

View file

@ -15,30 +15,33 @@ let appPort
let app
describe('De-dedupes scripts in _document', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
appPort = await findPort()
await nextBuild(appDir)
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
appPort = await findPort()
await nextBuild(appDir)
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
it('Does not have duplicate script references', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
let foundDuplicate = false
const srcs = new Set()
it('Does not have duplicate script references', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
let foundDuplicate = false
const srcs = new Set()
for (const script of $('script').toArray()) {
const { src } = script.attribs
if (!src || !src.startsWith('/_next/static')) continue
if (srcs.has(src)) {
console.error(`Found duplicate script ${src}`)
foundDuplicate = true
for (const script of $('script').toArray()) {
const { src } = script.attribs
if (!src || !src.startsWith('/_next/static')) continue
if (srcs.has(src)) {
console.error(`Found duplicate script ${src}`)
foundDuplicate = true
}
srcs.add(src)
}
srcs.add(src)
}
expect(foundDuplicate).toBe(false)
})
})
expect(foundDuplicate).toBe(false)
})
}
)
})

View file

@ -18,43 +18,46 @@ let app
const context = {}
describe('disabled runtime JS', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
appPort = await findPort()
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
appPort = await findPort()
await nextBuild(appDir)
app = await nextStart(appDir, appPort)
await nextBuild(appDir)
app = await nextStart(appDir, appPort)
context.appPort = appPort
})
afterAll(() => killApp(app))
context.appPort = appPort
})
afterAll(() => killApp(app))
it('should render the page', async () => {
const html = await renderViaHTTP(appPort, '/')
expect(html).toMatch(/Hello World/)
})
it('should render the page', async () => {
const html = await renderViaHTTP(appPort, '/')
expect(html).toMatch(/Hello World/)
})
it('should not have __NEXT_DATA__ script', async () => {
const html = await renderViaHTTP(appPort, '/')
it('should not have __NEXT_DATA__ script', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
expect($('script#__NEXT_DATA__').length).toBe(0)
})
const $ = cheerio.load(html)
expect($('script#__NEXT_DATA__').length).toBe(0)
})
it('should not have scripts', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
expect($('script[src]').length).toBe(0)
})
it('should not have scripts', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
expect($('script[src]').length).toBe(0)
})
it('should not have preload links', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
expect($('link[rel=preload]').length).toBe(0)
})
})
it('should not have preload links', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
expect($('link[rel=preload]').length).toBe(0)
})
}
)
describe('dev mode', () => {
describe('development mode', () => {
let appPort
let app

View file

@ -19,7 +19,7 @@ let app
describe('distDir', () => {
describe('With basic usage', () => {
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
@ -48,7 +48,7 @@ describe('distDir', () => {
)
})
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
await fs.remove(join(appDir, 'dist'))
@ -71,28 +71,31 @@ describe('distDir', () => {
expect(await fs.exists(join(__dirname, '/../.next'))).toBeFalsy()
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
it('should throw error with invalid distDir', async () => {
const origNextConfig = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(nextConfig, `module.exports = { distDir: '' }`)
const { stderr } = await nextBuild(appDir, [], { stderr: true })
await fs.writeFile(nextConfig, origNextConfig)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('should throw error with invalid distDir', async () => {
const origNextConfig = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(nextConfig, `module.exports = { distDir: '' }`)
const { stderr } = await nextBuild(appDir, [], { stderr: true })
await fs.writeFile(nextConfig, origNextConfig)
expect(stderr).toContain(
'Invalid distDir provided, distDir can not be an empty string. Please remove this config or set it to undefined'
)
})
expect(stderr).toContain(
'Invalid distDir provided, distDir can not be an empty string. Please remove this config or set it to undefined'
)
})
it('should handle undefined distDir', async () => {
const origNextConfig = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`module.exports = { distDir: undefined, eslint: { ignoreDuringBuilds: true } }`
)
const { stderr } = await nextBuild(appDir, [], { stderr: true })
await fs.writeFile(nextConfig, origNextConfig)
it('should handle undefined distDir', async () => {
const origNextConfig = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`module.exports = { distDir: undefined, eslint: { ignoreDuringBuilds: true } }`
)
const { stderr } = await nextBuild(appDir, [], { stderr: true })
await fs.writeFile(nextConfig, origNextConfig)
expect(stderr.length).toBe(0)
})
})
expect(stderr.length).toBe(0)
})
}
)
})

View file

@ -10,67 +10,70 @@ let appPort
let app
describe('File Dependencies', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
appPort = await findPort()
await nextBuild(appDir)
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
it('should apply styles defined in global and module css files in a standard page.', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#index')
const styles = await browser.eval(() => {
const computed = getComputedStyle(document.getElementById('index'))
return {
color: computed.color,
backgroundColor: computed.backgroundColor,
}
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
appPort = await findPort()
await nextBuild(appDir)
app = await nextStart(appDir, appPort)
})
expect(styles).toEqual({
color: 'rgb(0, 0, 255)',
backgroundColor: 'rgb(200, 200, 200)',
})
})
afterAll(() => killApp(app))
it('should apply styles defined in global and module css files in 404 page', async () => {
const browser = await webdriver(appPort, '/__not_found__')
await browser.waitForElementByCss('#notFound')
it('should apply styles defined in global and module css files in a standard page.', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#index')
const styles = await browser.eval(() => {
const computed = getComputedStyle(document.getElementById('notFound'))
return {
color: computed.color,
backgroundColor: computed.backgroundColor,
}
const styles = await browser.eval(() => {
const computed = getComputedStyle(document.getElementById('index'))
return {
color: computed.color,
backgroundColor: computed.backgroundColor,
}
})
expect(styles).toEqual({
color: 'rgb(0, 0, 255)',
backgroundColor: 'rgb(200, 200, 200)',
})
})
expect(styles).toEqual({
color: 'rgb(0, 255, 0)',
backgroundColor: 'rgb(200, 200, 200)',
})
})
it('should apply styles defined in global and module css files in 404 page', async () => {
const browser = await webdriver(appPort, '/__not_found__')
await browser.waitForElementByCss('#notFound')
it('should apply styles defined in global and module css files in error page', async () => {
const browser = await webdriver(appPort, '/error-trigger')
await browser.waitForElementByCss('#error')
const styles = await browser.eval(() => {
const computed = getComputedStyle(document.getElementById('notFound'))
return {
color: computed.color,
backgroundColor: computed.backgroundColor,
}
})
const styles = await browser.eval(() => {
const computed = getComputedStyle(document.getElementById('error'))
return {
color: computed.color,
backgroundColor: computed.backgroundColor,
}
expect(styles).toEqual({
color: 'rgb(0, 255, 0)',
backgroundColor: 'rgb(200, 200, 200)',
})
})
expect(styles).toEqual({
color: 'rgb(255, 0, 0)',
backgroundColor: 'rgb(200, 200, 200)',
it('should apply styles defined in global and module css files in error page', async () => {
const browser = await webdriver(appPort, '/error-trigger')
await browser.waitForElementByCss('#error')
const styles = await browser.eval(() => {
const computed = getComputedStyle(document.getElementById('error'))
return {
color: computed.color,
backgroundColor: computed.backgroundColor,
}
})
expect(styles).toEqual({
color: 'rgb(255, 0, 0)',
backgroundColor: 'rgb(200, 200, 200)',
})
})
})
})
}
)
})

View file

@ -117,132 +117,135 @@ describe('Test Draft Mode', () => {
await killApp(app)
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
let appPort, app, cookieString, initialRand
const getOpts = () => ({ headers: { Cookie: cookieString } })
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
let appPort, app, cookieString, initialRand
const getOpts = () => ({ headers: { Cookie: cookieString } })
it('should compile successfully', async () => {
await fs.remove(join(appDir, '.next'))
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
it('should compile successfully', async () => {
await fs.remove(join(appDir, '.next'))
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
it('should start production application', async () => {
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
it('should return prerendered page on first request', async () => {
const html = await renderViaHTTP(appPort, '/')
const { nextData, draft, rand } = getData(html)
expect(nextData).toMatchObject({ isFallback: false })
expect(draft).toBe('false')
initialRand = rand
})
it('should return prerendered page on second request', async () => {
const html = await renderViaHTTP(appPort, '/')
const { nextData, draft, rand } = getData(html)
expect(nextData).toMatchObject({ isFallback: false })
expect(draft).toBe('false')
expect(rand).toBe(initialRand)
})
it('should enable draft mode', async () => {
const res = await fetchViaHTTP(appPort, '/api/enable')
expect(res.status).toBe(200)
const originalCookies = res.headers.get('set-cookie').split(',')
const cookies = originalCookies.map((c) => cookie.parse(c))
expect(cookies.length).toBe(1)
expect(cookies[0]).toBeTruthy()
expect(cookies[0]).toMatchObject({ Path: '/', SameSite: 'None' })
expect(cookies[0]).toHaveProperty('__prerender_bypass')
//expect(cookies[0]).toHaveProperty('Secure')
expect(cookies[0]).not.toHaveProperty('Max-Age')
cookieString = cookie.serialize(
'__prerender_bypass',
cookies[0].__prerender_bypass
)
})
it('should return dynamic response when draft mode enabled', async () => {
const html = await renderViaHTTP(appPort, '/', {}, getOpts())
const { nextData, draft, rand } = getData(html)
expect(nextData).toMatchObject({ isFallback: false })
expect(draft).toBe('true')
expect(rand).not.toBe(initialRand)
})
it('should not return fallback page on draft request', async () => {
const res = await fetchViaHTTP(appPort, '/ssp', {}, getOpts())
const html = await res.text()
const { nextData, draft } = getData(html)
expect(res.headers.get('cache-control')).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
expect(nextData).toMatchObject({ isFallback: false })
expect(draft).toBe('true')
})
it('should return correct caching headers for draft mode request', async () => {
const url = `/_next/data/${encodeURI(await getBuildId())}/index.json`
const res = await fetchViaHTTP(appPort, url, {}, getOpts())
const json = await res.json()
expect(res.headers.get('cache-control')).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
expect(json).toMatchObject({
pageProps: {
draftMode: 'true',
},
it('should start production application', async () => {
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
})
it('should return cookies to be expired on disable request', async () => {
const res = await fetchViaHTTP(appPort, '/api/disable', {}, getOpts())
expect(res.status).toBe(200)
const cookies = res.headers
.get('set-cookie')
.replace(/(=(?!Lax)\w{3}),/g, '$1')
.split(',')
.map((c) => cookie.parse(c))
expect(cookies[0]).toBeTruthy()
expect(cookies[0]).toMatchObject({
Path: '/',
SameSite: 'None',
Expires: 'Thu 01 Jan 1970 00:00:00 GMT',
it('should return prerendered page on first request', async () => {
const html = await renderViaHTTP(appPort, '/')
const { nextData, draft, rand } = getData(html)
expect(nextData).toMatchObject({ isFallback: false })
expect(draft).toBe('false')
initialRand = rand
})
expect(cookies[0]).toHaveProperty('__prerender_bypass')
expect(cookies[0]).not.toHaveProperty('Max-Age')
})
it('should pass undefined to API routes when not in draft mode', async () => {
const res = await fetchViaHTTP(appPort, `/api/read`)
const json = await res.json()
expect(json).toMatchObject({})
})
it('should pass draft mode to API routes', async () => {
const res = await fetchViaHTTP(appPort, '/api/read', {}, getOpts())
const json = await res.json()
expect(json).toMatchObject({
draftMode: true,
it('should return prerendered page on second request', async () => {
const html = await renderViaHTTP(appPort, '/')
const { nextData, draft, rand } = getData(html)
expect(nextData).toMatchObject({ isFallback: false })
expect(draft).toBe('false')
expect(rand).toBe(initialRand)
})
})
afterAll(async () => {
await killApp(app)
})
})
it('should enable draft mode', async () => {
const res = await fetchViaHTTP(appPort, '/api/enable')
expect(res.status).toBe(200)
const originalCookies = res.headers.get('set-cookie').split(',')
const cookies = originalCookies.map((c) => cookie.parse(c))
expect(cookies.length).toBe(1)
expect(cookies[0]).toBeTruthy()
expect(cookies[0]).toMatchObject({ Path: '/', SameSite: 'None' })
expect(cookies[0]).toHaveProperty('__prerender_bypass')
//expect(cookies[0]).toHaveProperty('Secure')
expect(cookies[0]).not.toHaveProperty('Max-Age')
cookieString = cookie.serialize(
'__prerender_bypass',
cookies[0].__prerender_bypass
)
})
it('should return dynamic response when draft mode enabled', async () => {
const html = await renderViaHTTP(appPort, '/', {}, getOpts())
const { nextData, draft, rand } = getData(html)
expect(nextData).toMatchObject({ isFallback: false })
expect(draft).toBe('true')
expect(rand).not.toBe(initialRand)
})
it('should not return fallback page on draft request', async () => {
const res = await fetchViaHTTP(appPort, '/ssp', {}, getOpts())
const html = await res.text()
const { nextData, draft } = getData(html)
expect(res.headers.get('cache-control')).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
expect(nextData).toMatchObject({ isFallback: false })
expect(draft).toBe('true')
})
it('should return correct caching headers for draft mode request', async () => {
const url = `/_next/data/${encodeURI(await getBuildId())}/index.json`
const res = await fetchViaHTTP(appPort, url, {}, getOpts())
const json = await res.json()
expect(res.headers.get('cache-control')).toBe(
'private, no-cache, no-store, max-age=0, must-revalidate'
)
expect(json).toMatchObject({
pageProps: {
draftMode: 'true',
},
})
})
it('should return cookies to be expired on disable request', async () => {
const res = await fetchViaHTTP(appPort, '/api/disable', {}, getOpts())
expect(res.status).toBe(200)
const cookies = res.headers
.get('set-cookie')
.replace(/(=(?!Lax)\w{3}),/g, '$1')
.split(',')
.map((c) => cookie.parse(c))
expect(cookies[0]).toBeTruthy()
expect(cookies[0]).toMatchObject({
Path: '/',
SameSite: 'None',
Expires: 'Thu 01 Jan 1970 00:00:00 GMT',
})
expect(cookies[0]).toHaveProperty('__prerender_bypass')
expect(cookies[0]).not.toHaveProperty('Max-Age')
})
it('should pass undefined to API routes when not in draft mode', async () => {
const res = await fetchViaHTTP(appPort, `/api/read`)
const json = await res.json()
expect(json).toMatchObject({})
})
it('should pass draft mode to API routes', async () => {
const res = await fetchViaHTTP(appPort, '/api/read', {}, getOpts())
const json = await res.json()
expect(json).toMatchObject({
draftMode: true,
})
})
afterAll(async () => {
await killApp(app)
})
}
)
})

View file

@ -51,7 +51,7 @@ function runTests() {
const nextConfig = join(appDir, 'next.config.js')
describe('Dynamic Optional Routing Root Fallback', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
@ -62,22 +62,25 @@ describe('Dynamic Optional Routing Root Fallback', () => {
runTests()
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
const curConfig = await fs.readFile(nextConfig, 'utf8')
const curConfig = await fs.readFile(nextConfig, 'utf8')
if (curConfig.includes('target')) {
await fs.writeFile(nextConfig, `module.exports = {}`)
}
await nextBuild(appDir)
if (curConfig.includes('target')) {
await fs.writeFile(nextConfig, `module.exports = {}`)
}
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
runTests()
}
)
})

View file

@ -39,7 +39,7 @@ function runTests() {
const nextConfig = join(appDir, 'next.config.js')
describe('Dynamic Optional Routing', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -48,20 +48,23 @@ describe('Dynamic Optional Routing', () => {
runTests()
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
const curConfig = await fs.readFile(nextConfig, 'utf8')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
const curConfig = await fs.readFile(nextConfig, 'utf8')
if (curConfig.includes('target')) {
await fs.writeFile(nextConfig, `module.exports = {}`)
}
await nextBuild(appDir)
if (curConfig.includes('target')) {
await fs.writeFile(nextConfig, `module.exports = {}`)
}
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
runTests()
}
)
})

View file

@ -237,7 +237,7 @@ function runInvalidPagesTests(buildFn) {
}
describe('Dynamic Optional Routing', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
@ -255,32 +255,34 @@ describe('Dynamic Optional Routing', () => {
})
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
const curConfig = await fs.readFile(nextConfig, 'utf8')
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
const curConfig = await fs.readFile(nextConfig, 'utf8')
if (curConfig.includes('target')) {
await fs.writeFile(nextConfig, `module.exports = {}`)
}
await nextBuild(appDir)
if (curConfig.includes('target')) {
await fs.writeFile(nextConfig, `module.exports = {}`)
}
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
runTests()
runInvalidPagesTests(async (appDir) => {
;({ stderr } = await nextBuild(appDir, [], { stderr: true }))
})
runInvalidPagesTests(async (appDir) => {
;({ stderr } = await nextBuild(appDir, [], { stderr: true }))
})
it('should fail to build when param is not explicitly defined', async () => {
const invalidRoute = appDir + 'pages/invalid/[[...slug]].js'
try {
await fs.outputFile(
invalidRoute,
`
it('should fail to build when param is not explicitly defined', async () => {
const invalidRoute = appDir + 'pages/invalid/[[...slug]].js'
try {
await fs.outputFile(
invalidRoute,
`
export async function getStaticPaths() {
return {
paths: [
@ -300,15 +302,16 @@ describe('Dynamic Optional Routing', () => {
)
}
`,
'utf-8'
)
const { stderr } = await nextBuild(appDir, [], { stderr: true })
await expect(stderr).toMatch(
'A required parameter (slug) was not provided as an array received undefined in getStaticPaths for /invalid/[[...slug]]'
)
} finally {
await fs.unlink(invalidRoute)
}
})
})
'utf-8'
)
const { stderr } = await nextBuild(appDir, [], { stderr: true })
await expect(stderr).toMatch(
'A required parameter (slug) was not provided as an array received undefined in getStaticPaths for /invalid/[[...slug]]'
)
} finally {
await fs.unlink(invalidRoute)
}
})
}
)
})

View file

@ -1579,7 +1579,7 @@ describe('Dynamic Routing', () => {
afterAll(() => fs.remove(middlewarePath))
}
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
await fs.remove(nextConfig)
@ -1591,18 +1591,21 @@ describe('Dynamic Routing', () => {
runTests({ dev: true })
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await fs.remove(nextConfig)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await fs.remove(nextConfig)
await nextBuild(appDir)
buildId = await fs.readFile(buildIdPath, 'utf8')
await nextBuild(appDir)
buildId = await fs.readFile(buildIdPath, 'utf8')
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests({ dev: false })
})
runTests({ dev: false })
}
)
})

View file

@ -102,7 +102,7 @@ describe('Edge runtime configurable guards', () => {
)
})
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('fails to build because of unallowed code', async () => {
@ -306,7 +306,7 @@ describe('Edge runtime configurable guards', () => {
},
])('$title with allowed, unused dynamic code', ({ init, url }) => {
beforeEach(() => init())
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('build and does not warn at runtime', async () => {
@ -391,7 +391,7 @@ describe('Edge runtime configurable guards', () => {
`Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime`
)
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('fails to build because of dynamic code evaluation', async () => {
@ -449,7 +449,7 @@ describe('Edge runtime configurable guards', () => {
`Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime`
)
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
// eslint-disable-next-line jest/no-identical-title

View file

@ -22,7 +22,7 @@ const context = {
appDir: join(__dirname, '../'),
}
describe('Page using eval in dev mode', () => {
describe('Page using eval in development mode', () => {
let output = ''
beforeAll(async () => {
@ -74,7 +74,7 @@ describe.each([
])(
'$title usage of dynamic code evaluation',
({ extractValue, computeRoute }) => {
describe('dev mode', () => {
describe('development mode', () => {
let output = ''
beforeAll(async () => {
@ -158,7 +158,7 @@ describe.each([
expect(output).not.toContain('DynamicWasmCodeGenerationWarning')
})
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
let buildResult

View file

@ -101,7 +101,7 @@ describe('Edge runtime code with imports', () => {
})
})
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('throws unsupported module error in production at runtime and prints error on logs', async () => {
@ -187,7 +187,7 @@ describe('Edge runtime code with imports', () => {
}, 'success')
})
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('throws unsupported module error in production at runtime and prints error on logs', async () => {
@ -258,7 +258,7 @@ describe('Edge runtime code with imports', () => {
return 'success'
}, 'success')
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('does not build and reports', async () => {
@ -320,7 +320,7 @@ describe('Edge runtime code with imports', () => {
expect(res.headers.get('x-from-runtime')).toBeDefined()
expectNoError(moduleName)
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('does not throw in production at runtime', async () => {
@ -389,7 +389,7 @@ describe('Edge runtime code with imports', () => {
expect(res.headers.get('x-from-runtime')).toBe('false')
expectNoError(moduleName)
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('does not throw in production at runtime', async () => {

View file

@ -95,7 +95,7 @@ describe('Edge runtime code with imports', () => {
return 'success'
}, 'success')
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('throws unsupported module error in production at runtime and prints error on logs', async () => {
@ -126,7 +126,7 @@ describe('Edge runtime code with imports', () => {
new (${importStatement})()
return Response.json({ ok: true })
}
export const config = { runtime: 'edge' }
`)
},
@ -137,7 +137,7 @@ describe('Edge runtime code with imports', () => {
init(importStatement) {
context.middleware.write(`
import { NextResponse } from 'next/server'
export async function middleware(request) {
new (${importStatement})()
return NextResponse.next()
@ -165,7 +165,7 @@ describe('Edge runtime code with imports', () => {
return 'success'
}, 'success')
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('does not build and reports module not found error', async () => {
@ -193,7 +193,7 @@ describe('Edge runtime code with imports', () => {
}
return Response.json({ ok: true })
}
export const config = { runtime: 'edge' }
`)
},
@ -204,7 +204,7 @@ describe('Edge runtime code with imports', () => {
init(importStatement) {
context.middleware.write(`
import { NextResponse } from 'next/server'
export async function middleware(request) {
if (process.env === 'production') {
new (${importStatement})()
@ -233,7 +233,7 @@ describe('Edge runtime code with imports', () => {
return 'success'
}, 'success')
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('does not build and reports module not found error', async () => {
@ -262,7 +262,7 @@ describe('Edge runtime code with imports', () => {
}
return Response.json({ ok: true })
}
export const config = { runtime: 'edge' }
`)
},
@ -273,7 +273,7 @@ describe('Edge runtime code with imports', () => {
init(importStatement) {
context.middleware.write(`
import { NextResponse } from 'next/server'
export async function middleware(request) {
if (process.env === 'production') {
(${importStatement}).spawn('ls', ['-lh', '/usr'])
@ -295,7 +295,7 @@ describe('Edge runtime code with imports', () => {
expect(res.status).toBe(200)
expectNoError(moduleName)
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it('does not throw in production at runtime', async () => {

View file

@ -72,7 +72,7 @@ describe('Edge runtime code with imports', () => {
)
expect(res.status).toBe(500)
})
;(process.env.TURBOPACK ? describe.skip : describe)(
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
it(`${title} build test Response`, async () => {

View file

@ -50,7 +50,7 @@ function createContext() {
return ctx
}
describe('dev mode', () => {
describe('development mode', () => {
const context = createContext()
beforeAll(async () => {
@ -65,20 +65,23 @@ describe('dev mode', () => {
it('logs the error correctly', test(context))
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
const context = createContext()
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
const context = createContext()
beforeAll(async () => {
await remove(path.join(appDir, '.next'))
await nextBuild(appDir, undefined, {
stderr: true,
stdout: true,
beforeAll(async () => {
await remove(path.join(appDir, '.next'))
await nextBuild(appDir, undefined, {
stderr: true,
stdout: true,
})
context.appPort = await findPort()
context.app = await nextStart(appDir, context.appPort, {
...context.handler,
})
})
context.appPort = await findPort()
context.app = await nextStart(appDir, context.appPort, {
...context.handler,
})
})
afterAll(() => killApp(context.app))
it('logs the error correctly', test(context))
})
afterAll(() => killApp(context.app))
it('logs the error correctly', test(context))
}
)

View file

@ -60,7 +60,7 @@ describe.each([
])('$title using Node.js API', ({ computeRoute }) => {
const appDir = join(__dirname, '..')
describe('dev mode', () => {
describe('development mode', () => {
let output = ''
let appPort: number
let app = null
@ -110,30 +110,33 @@ Learn more: https://nextjs.org/docs/api-reference/edge-runtime`)
expect(stripAnsi(output)).toInclude(errorHighlight)
})
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
let buildResult
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
let buildResult
beforeAll(async () => {
await remove(join(appDir, '.next'))
buildResult = await nextBuild(appDir, undefined, {
stderr: true,
stdout: true,
beforeAll(async () => {
await remove(join(appDir, '.next'))
buildResult = await nextBuild(appDir, undefined, {
stderr: true,
stdout: true,
})
})
})
it.each(
[...unsupportedFunctions, ...unsupportedClasses].map((api, index) => ({
api,
}))
)(`warns for $api during build`, ({ api }) => {
expect(buildResult.stderr).toContain(`A Node.js API is used (${api}`)
})
it.each([...undefinedProperties].map((api) => ({ api })))(
'does not warn on using $api',
({ api }) => {
it.each(
[...unsupportedFunctions, ...unsupportedClasses].map((api, index) => ({
api,
}))
)(`warns for $api during build`, ({ api }) => {
expect(buildResult.stderr).toContain(`A Node.js API is used (${api}`)
}
)
})
})
it.each([...undefinedProperties].map((api) => ({ api })))(
'does not warn on using $api',
({ api }) => {
expect(buildResult.stderr).toContain(`A Node.js API is used (${api}`)
}
)
}
)
})

View file

@ -132,7 +132,7 @@ const runTests = (mode = 'dev', didReload = false) => {
}
describe('Env Config', () => {
describe('dev mode', () => {
describe('development mode', () => {
beforeAll(async () => {
output = ''
appPort = await findPort()
@ -358,25 +358,28 @@ describe('Env Config', () => {
runTests('test')
})
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
const { code } = await nextBuild(appDir, [], {
env: {
PROCESS_ENV_KEY: 'processenvironment',
ENV_FILE_PROCESS_ENV: 'env-cli',
},
})
if (code !== 0) throw new Error(`Build failed with exit code ${code}`)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
const { code } = await nextBuild(appDir, [], {
env: {
PROCESS_ENV_KEY: 'processenvironment',
ENV_FILE_PROCESS_ENV: 'env-cli',
},
})
if (code !== 0) throw new Error(`Build failed with exit code ${code}`)
appPort = await findPort()
app = await nextStart(appDir, appPort, {
env: {
ENV_FILE_PROCESS_ENV: 'env-cli',
},
appPort = await findPort()
app = await nextStart(appDir, appPort, {
env: {
ENV_FILE_PROCESS_ENV: 'env-cli',
},
})
})
})
afterAll(() => killApp(app))
afterAll(() => killApp(app))
runTests('server')
})
runTests('server')
}
)
})

View file

@ -16,25 +16,28 @@ let app
let port
describe('Handles an Error in _error', () => {
;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
port = await findPort()
app = await nextStart(appDir, port)
})
afterAll(() => killApp(app))
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
beforeAll(async () => {
await nextBuild(appDir)
port = await findPort()
app = await nextStart(appDir, port)
})
afterAll(() => killApp(app))
it('Handles error during SSR', async () => {
const html = await renderViaHTTP(port, '/some-404-page')
expect(html).toMatch(/Internal Server Error/i)
})
it('Handles error during SSR', async () => {
const html = await renderViaHTTP(port, '/some-404-page')
expect(html).toMatch(/Internal Server Error/i)
})
it('Handles error during client transition', async () => {
const browser = await webdriver(port, '/')
await browser.waitForElementByCss('a').click()
await waitFor(1000)
const html = await browser.eval('document.body.innerHTML')
expect(html).toMatch(/Internal Server Error/i)
})
})
it('Handles error during client transition', async () => {
const browser = await webdriver(port, '/')
await browser.waitForElementByCss('a').click()
await waitFor(1000)
const html = await browser.eval('document.body.innerHTML')
expect(html).toMatch(/Internal Server Error/i)
})
}
)
})

Some files were not shown because too many files have changed in this diff Show more