Migrate basic-basepath tests into basic (#44776)

Removes `test/development/basic-basepath` and migrates those tests into
`test/development/basic` instead. Currently the tests are duplicated but
`/basic-basepath` is just a subset of `/basic`.

## Bug

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

## Feature

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

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm build && pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing
doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
This commit is contained in:
Hannes Bornö 2023-01-11 17:13:19 +01:00 committed by GitHub
parent 7fe9e035d8
commit ee0f483159
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 1210 additions and 2521 deletions

View file

@ -1,704 +0,0 @@
import { join } from 'path'
import cheerio from 'cheerio'
import webdriver from 'next-webdriver'
import {
check,
getBrowserBodyText,
getRedboxHeader,
getRedboxSource,
hasRedbox,
renderViaHTTP,
waitFor,
} from 'next-test-utils'
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
describe('basic HMR', () => {
let next: NextInstance
beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'hmr/pages')),
components: new FileRef(join(__dirname, 'hmr/components')),
},
nextConfig: {
basePath: '/docs',
},
})
})
afterAll(() => next.destroy())
describe('Hot Module Reloading', () => {
describe('delete a page and add it back', () => {
it('should load the page properly', async () => {
const contactPagePath = join('pages', 'hmr', 'contact.js')
const newContactPagePath = join('pages', 'hmr', '_contact.js')
let browser
try {
browser = await webdriver(next.url, '/docs/hmr/contact')
const text = await browser.elementByCss('p').text()
expect(text).toBe('This is the contact page.')
// Rename the file to mimic a deleted page
await next.renameFile(contactPagePath, newContactPagePath)
await check(
() => getBrowserBodyText(browser),
/This page could not be found/
)
// Rename the file back to the original filename
await next.renameFile(newContactPagePath, contactPagePath)
// wait until the page comes back
await check(
() => getBrowserBodyText(browser),
/This is the contact page/
)
} finally {
if (browser) {
await browser.close()
}
await next
.renameFile(newContactPagePath, contactPagePath)
.catch(() => {})
}
})
})
describe('editing a page', () => {
it('should detect the changes and display it', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/hmr/about')
const text = await browser.elementByCss('p').text()
expect(text).toBe('This is the about page.')
const aboutPagePath = join('pages', 'hmr', 'about.js')
const originalContent = await next.readFile(aboutPagePath)
const editedContent = originalContent.replace(
'This is the about page',
'COOL page'
)
// change the content
try {
await next.patchFile(aboutPagePath, editedContent)
await check(() => getBrowserBodyText(browser), /COOL page/)
} finally {
// add the original content
await next.patchFile(aboutPagePath, originalContent)
}
await check(
() => getBrowserBodyText(browser),
/This is the about page/
)
} finally {
if (browser) {
await browser.close()
}
}
})
it('should not reload unrelated pages', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/hmr/counter')
const text = await browser
.elementByCss('button')
.click()
.elementByCss('button')
.click()
.elementByCss('p')
.text()
expect(text).toBe('COUNT: 2')
const aboutPagePath = join('pages', 'hmr', 'about.js')
const originalContent = await next.readFile(aboutPagePath)
const editedContent = originalContent.replace(
'This is the about page',
'COOL page'
)
try {
// Change the about.js page
await next.patchFile(aboutPagePath, editedContent)
// Check whether the this page has reloaded or not.
await check(() => browser.elementByCss('p').text(), /COUNT: 2/)
} finally {
// restore the about page content.
await next.patchFile(aboutPagePath, originalContent)
}
} finally {
if (browser) {
await browser.close()
}
}
})
// Added because of a regression in react-hot-loader, see issues: #4246 #4273
// Also: https://github.com/vercel/styled-jsx/issues/425
it('should update styles correctly', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/hmr/style')
const pTag = await browser.elementByCss('.hmr-style-page p')
const initialFontSize = await pTag.getComputedCss('font-size')
expect(initialFontSize).toBe('100px')
const pagePath = join('pages', 'hmr', 'style.js')
const originalContent = await next.readFile(pagePath)
const editedContent = originalContent.replace('100px', '200px')
// Change the page
await next.patchFile(pagePath, editedContent)
try {
// Check whether the this page has reloaded or not.
await check(async () => {
const editedPTag = await browser.elementByCss('.hmr-style-page p')
return editedPTag.getComputedCss('font-size')
}, /200px/)
} finally {
// Finally is used so that we revert the content back to the original regardless of the test outcome
// restore the about page content.
await next.patchFile(pagePath, originalContent)
}
} finally {
if (browser) {
await browser.close()
}
}
})
// Added because of a regression in react-hot-loader, see issues: #4246 #4273
// Also: https://github.com/vercel/styled-jsx/issues/425
it('should update styles in a stateful component correctly', async () => {
let browser
const pagePath = join('pages', 'hmr', 'style-stateful-component.js')
const originalContent = await next.readFile(pagePath)
try {
browser = await webdriver(
next.url,
'/docs/hmr/style-stateful-component'
)
const pTag = await browser.elementByCss('.hmr-style-page p')
const initialFontSize = await pTag.getComputedCss('font-size')
expect(initialFontSize).toBe('100px')
const editedContent = originalContent.replace('100px', '200px')
// Change the page
await next.patchFile(pagePath, editedContent)
// Check whether the this page has reloaded or not.
await check(async () => {
const editedPTag = await browser.elementByCss('.hmr-style-page p')
return editedPTag.getComputedCss('font-size')
}, /200px/)
} finally {
if (browser) {
await browser.close()
}
await next.patchFile(pagePath, originalContent)
}
})
// Added because of a regression in react-hot-loader, see issues: #4246 #4273
// Also: https://github.com/vercel/styled-jsx/issues/425
it('should update styles in a dynamic component correctly', async () => {
let browser = null
let secondBrowser = null
const pagePath = join('components', 'hmr', 'dynamic.js')
const originalContent = await next.readFile(pagePath)
try {
browser = await webdriver(
next.url,
'/docs/hmr/style-dynamic-component'
)
const div = await browser.elementByCss('#dynamic-component')
const initialClientClassName = await div.getAttribute('class')
const initialFontSize = await div.getComputedCss('font-size')
expect(initialFontSize).toBe('100px')
const initialHtml = await renderViaHTTP(
next.url,
'/docs/hmr/style-dynamic-component'
)
expect(initialHtml.includes('100px')).toBeTruthy()
const $initialHtml = cheerio.load(initialHtml)
const initialServerClassName =
$initialHtml('#dynamic-component').attr('class')
expect(initialClientClassName === initialServerClassName).toBeTruthy()
const editedContent = originalContent.replace('100px', '200px')
// Change the page
await next.patchFile(pagePath, editedContent)
// wait for 5 seconds
await waitFor(5000)
secondBrowser = await webdriver(
next.url,
'/docs/hmr/style-dynamic-component'
)
// Check whether the this page has reloaded or not.
const editedDiv = await secondBrowser.elementByCss(
'#dynamic-component'
)
const editedClientClassName = await editedDiv.getAttribute('class')
const editedFontSize = await editedDiv.getComputedCss('font-size')
const browserHtml = await secondBrowser.eval(
'document.documentElement.innerHTML'
)
expect(editedFontSize).toBe('200px')
expect(browserHtml.includes('font-size:200px')).toBe(true)
expect(browserHtml.includes('font-size:100px')).toBe(false)
const editedHtml = await renderViaHTTP(
next.url,
'/docs/hmr/style-dynamic-component'
)
expect(editedHtml.includes('200px')).toBeTruthy()
const $editedHtml = cheerio.load(editedHtml)
const editedServerClassName =
$editedHtml('#dynamic-component').attr('class')
expect(editedClientClassName === editedServerClassName).toBe(true)
} finally {
// Finally is used so that we revert the content back to the original regardless of the test outcome
// restore the about page content.
await next.patchFile(pagePath, originalContent)
if (browser) {
await browser.close()
}
if (secondBrowser) {
secondBrowser.close()
}
}
})
})
})
describe('Error Recovery', () => {
it('should recover from 404 after a page has been added', async () => {
let browser
const newPage = join('pages', 'hmr', 'new-page.js')
try {
browser = await webdriver(next.url, '/docs/hmr/new-page')
expect(await browser.elementByCss('body').text()).toMatch(
/This page could not be found/
)
// Add the page
await next.patchFile(
newPage,
'export default () => (<div id="new-page">the-new-page</div>)'
)
await check(() => getBrowserBodyText(browser), /the-new-page/)
await next.deleteFile(newPage)
await check(
() => getBrowserBodyText(browser),
/This page could not be found/
)
} catch (err) {
await next.deleteFile(newPage)
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should detect syntax errors and recover', async () => {
let browser
const aboutPage = join('pages', 'hmr', 'about2.js')
const aboutContent = await next.readFile(aboutPage)
try {
browser = await webdriver(next.url, '/docs/hmr/about2')
await check(() => getBrowserBodyText(browser), /This is the about page/)
await next.patchFile(aboutPage, aboutContent.replace('</div>', 'div'))
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxSource(browser)).toMatch(/Unexpected eof/)
await next.patchFile(aboutPage, aboutContent)
await check(() => getBrowserBodyText(browser), /This is the about page/)
} catch (err) {
await next.patchFile(aboutPage, aboutContent)
if (browser) {
await check(
() => getBrowserBodyText(browser),
/This is the about page/
)
}
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should show the error on all pages', async () => {
const aboutPage = join('pages', 'hmr', 'about2.js')
const aboutContent = await next.readFile(aboutPage)
let browser
try {
await renderViaHTTP(next.url, '/docs/hmr/about2')
await next.patchFile(aboutPage, aboutContent.replace('</div>', 'div'))
// Ensure dev server has time to break:
await new Promise((resolve) => setTimeout(resolve, 2000))
browser = await webdriver(next.url, '/docs/hmr/contact')
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxSource(browser)).toMatch(/Unexpected eof/)
await next.patchFile(aboutPage, aboutContent)
await check(
() => getBrowserBodyText(browser),
/This is the contact page/
)
} catch (err) {
await next.patchFile(aboutPage, aboutContent)
if (browser) {
await check(
() => getBrowserBodyText(browser),
/This is the contact page/
)
}
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should detect runtime errors on the module scope', async () => {
let browser
const aboutPage = join('pages', 'hmr', 'about3.js')
const aboutContent = await next.readFile(aboutPage)
try {
browser = await webdriver(next.url, '/docs/hmr/about3')
await check(() => getBrowserBodyText(browser), /This is the about page/)
await next.patchFile(
aboutPage,
aboutContent.replace('export', 'aa=20;\nexport')
)
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatch(/aa is not defined/)
await next.patchFile(aboutPage, aboutContent)
await check(() => getBrowserBodyText(browser), /This is the about page/)
} finally {
await next.patchFile(aboutPage, aboutContent)
if (browser) {
await browser.close()
}
}
})
it('should recover from errors in the render function', async () => {
let browser
const aboutPage = join('pages', 'hmr', 'about4.js')
const aboutContent = await next.readFile(aboutPage)
try {
browser = await webdriver(next.url, '/docs/hmr/about4')
await check(() => getBrowserBodyText(browser), /This is the about page/)
await next.patchFile(
aboutPage,
aboutContent.replace(
'return',
'throw new Error("an-expected-error");\nreturn'
)
)
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxSource(browser)).toMatch(/an-expected-error/)
await next.patchFile(aboutPage, aboutContent)
await check(() => getBrowserBodyText(browser), /This is the about page/)
} catch (err) {
await next.patchFile(aboutPage, aboutContent)
if (browser) {
await check(
() => getBrowserBodyText(browser),
/This is the about page/
)
}
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should recover after exporting an invalid page', async () => {
let browser
const aboutPage = join('pages', 'hmr', 'about5.js')
const aboutContent = await next.readFile(aboutPage)
try {
browser = await webdriver(next.url, '/docs/hmr/about5')
await check(() => getBrowserBodyText(browser), /This is the about page/)
await next.patchFile(
aboutPage,
aboutContent.replace(
'export default',
'export default {};\nexport const fn ='
)
)
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(`
" 1 of 1 unhandled error
Server Error
Error: The default export is not a React Component in page: \\"/hmr/about5\\"
This error happened while generating the page. Any console logs will be displayed in the terminal window."
`)
await next.patchFile(aboutPage, aboutContent)
await check(() => getBrowserBodyText(browser), /This is the about page/)
} catch (err) {
await next.patchFile(aboutPage, aboutContent)
if (browser) {
await check(
() => getBrowserBodyText(browser),
/This is the about page/
)
}
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should recover after a bad return from the render function', async () => {
let browser
const aboutPage = join('pages', 'hmr', 'about6.js')
const aboutContent = await next.readFile(aboutPage)
try {
browser = await webdriver(next.url, '/docs/hmr/about6')
await check(() => getBrowserBodyText(browser), /This is the about page/)
await next.patchFile(
aboutPage,
aboutContent.replace(
'export default',
'export default () => /search/;\nexport const fn ='
)
)
expect(await hasRedbox(browser)).toBe(true)
// TODO: Replace this when webpack 5 is the default
expect(await getRedboxHeader(browser)).toMatch(
`Objects are not valid as a React child (found: [object RegExp]). If you meant to render a collection of children, use an array instead.`
)
await next.patchFile(aboutPage, aboutContent)
await check(() => getBrowserBodyText(browser), /This is the about page/)
} catch (err) {
await next.patchFile(aboutPage, aboutContent)
if (browser) {
await check(
() => getBrowserBodyText(browser),
/This is the about page/
)
}
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should recover after undefined exported as default', async () => {
let browser
const aboutPage = join('pages', 'hmr', 'about7.js')
const aboutContent = await next.readFile(aboutPage)
try {
browser = await webdriver(next.url, '/docs/hmr/about7')
await check(() => getBrowserBodyText(browser), /This is the about page/)
await next.patchFile(
aboutPage,
aboutContent.replace(
'export default',
'export default undefined;\nexport const fn ='
)
)
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(`
" 1 of 1 unhandled error
Server Error
Error: The default export is not a React Component in page: \\"/hmr/about7\\"
This error happened while generating the page. Any console logs will be displayed in the terminal window."
`)
await next.patchFile(aboutPage, aboutContent)
await check(() => getBrowserBodyText(browser), /This is the about page/)
expect(await hasRedbox(browser, false)).toBe(false)
} catch (err) {
await next.patchFile(aboutPage, aboutContent)
if (browser) {
await check(
() => getBrowserBodyText(browser),
/This is the about page/
)
}
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should recover from errors in getInitialProps in client', async () => {
let browser
const erroredPage = join('pages', 'hmr', 'error-in-gip.js')
const errorContent = await next.readFile(erroredPage)
try {
browser = await webdriver(next.url, '/docs/hmr')
await browser.elementByCss('#error-in-gip-link').click()
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(`
" 1 of 1 unhandled error
Unhandled Runtime Error
Error: an-expected-error-in-gip"
`)
await next.patchFile(
erroredPage,
errorContent.replace('throw error', 'return {}')
)
await check(() => getBrowserBodyText(browser), /Hello/)
await next.patchFile(erroredPage, errorContent)
await check(async () => {
await browser.refresh()
await waitFor(2000)
const text = await browser.elementByCss('body').text()
if (text.includes('Hello')) {
throw new Error('waiting')
}
return getRedboxSource(browser)
}, /an-expected-error-in-gip/)
} catch (err) {
await next.patchFile(erroredPage, errorContent)
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should recover after an error reported via SSR', async () => {
let browser
const erroredPage = join('pages', 'hmr', 'error-in-gip.js')
const errorContent = await next.readFile(erroredPage)
try {
browser = await webdriver(next.url, '/docs/hmr/error-in-gip')
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(`
" 1 of 1 unhandled error
Server Error
Error: an-expected-error-in-gip
This error happened while generating the page. Any console logs will be displayed in the terminal window."
`)
const erroredPage = join('pages', 'hmr', 'error-in-gip.js')
await next.patchFile(
erroredPage,
errorContent.replace('throw error', 'return {}')
)
await check(() => getBrowserBodyText(browser), /Hello/)
await next.patchFile(erroredPage, errorContent)
await check(async () => {
await browser.refresh()
await waitFor(2000)
const text = await getBrowserBodyText(browser)
if (text.includes('Hello')) {
throw new Error('waiting')
}
return getRedboxSource(browser)
}, /an-expected-error-in-gip/)
} catch (err) {
await next.patchFile(erroredPage, errorContent)
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
})
})

View file

@ -1,12 +0,0 @@
export default () => {
return (
<div id="dynamic-component">
Dynamic Component
<style jsx>{`
div {
font-size: 100px;
}
`}</style>
</div>
)
}

View file

@ -1,7 +0,0 @@
export default function About() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -1,7 +0,0 @@
export default () => {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -1,7 +0,0 @@
export default function About2() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -1,7 +0,0 @@
export default () => {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -1,7 +0,0 @@
export default function About4() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -1,7 +0,0 @@
export default () => {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -1,7 +0,0 @@
export default () => {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -1,7 +0,0 @@
export default () => {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -1,5 +0,0 @@
export default () => (
<div className="hmr-contact-page">
<p>This is the contact page.</p>
</div>
)

View file

@ -1,19 +0,0 @@
import React from 'react'
export default class Counter extends React.Component {
state = { count: 0 }
incr() {
const { count } = this.state
this.setState({ count: count + 1 })
}
render() {
return (
<div>
<p>COUNT: {this.state.count}</p>
<button onClick={() => this.incr()}>Increment</button>
</div>
)
}
}

View file

@ -1,11 +0,0 @@
import React from 'react'
export default class extends React.Component {
static getInitialProps() {
const error = new Error('an-expected-error-in-gip')
throw error
}
render() {
return <div>Hello</div>
}
}

View file

@ -1,9 +0,0 @@
import Link from 'next/link'
export default () => (
<div>
<Link href="/hmr/error-in-gip" id="error-in-gip-link">
Bad Page
</Link>
</div>
)

View file

@ -1,8 +0,0 @@
import React from 'react'
import dynamic from 'next/dynamic'
const HmrDynamic = dynamic(import('../../components/hmr/dynamic'))
export default () => {
return <HmrDynamic />
}

View file

@ -1,20 +0,0 @@
import React, { Component } from 'react'
export default class StyleStateFul extends Component {
render() {
return (
<React.Fragment>
<div className="hmr-style-page">
<p>
This is the style page.
<style jsx>{`
p {
font-size: 100px;
}
`}</style>
</p>
</div>
</React.Fragment>
)
}
}

View file

@ -1,17 +0,0 @@
import React from 'react'
export default function Style() {
return (
<React.Fragment>
<div className="hmr-style-page">
<p>
This is the style page.
<style jsx>{`
p {
font-size: 100px;
}
`}</style>
</p>
</div>
</React.Fragment>
)
}

View file

@ -1,113 +0,0 @@
import url from 'url'
import { join } from 'path'
import webdriver from 'next-webdriver'
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils'
describe('misc basic dev tests', () => {
let next: NextInstance
beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'misc/pages')),
public: new FileRef(join(__dirname, 'misc/public')),
},
nextConfig: {
basePath: '/docs',
},
})
})
afterAll(() => next.destroy())
it('should set process.env.NODE_ENV in development', async () => {
const browser = await webdriver(next.url, '/docs/process-env')
const nodeEnv = await browser.elementByCss('#node-env').text()
expect(nodeEnv).toBe('development')
await browser.close()
})
it('should allow access to public files', async () => {
const data = await renderViaHTTP(next.url, '/docs/data/data.txt')
expect(data).toBe('data')
const legacy = await renderViaHTTP(next.url, '/docs/static/legacy.txt')
expect(legacy).toMatch(`new static folder`)
})
describe('With Security Related Issues', () => {
it('should not allow accessing files outside .next/static and .next/server directory', async () => {
const pathsToCheck = [
`/docs/_next/static/../BUILD_ID`,
`/docs/_next/static/../routes-manifest.json`,
]
for (const path of pathsToCheck) {
const res = await fetchViaHTTP(next.url, path)
const text = await res.text()
try {
expect(res.status).toBe(404)
expect(text).toMatch(/This page could not be found/)
} catch (err) {
throw new Error(`Path ${path} accessible from the browser`)
}
}
})
it('should handle encoded / value for trailing slash correctly', async () => {
const res = await fetchViaHTTP(
next.url,
'/docs/%2fexample.com/',
undefined,
{ redirect: 'manual' }
)
const { pathname, hostname } = url.parse(
res.headers.get('location') || ''
)
expect(res.status).toBe(308)
expect(pathname).toBe('/docs/%2fexample.com')
expect(hostname).not.toBe('example.com')
const text = await res.text()
expect(text).toEqual('/docs/%2fexample.com')
})
})
async function getLogs$(path) {
let foundLog = false
let browser
try {
browser = await webdriver(next.url, path)
const browserLogs = await browser.log('browser')
browserLogs.forEach((log) => {
if (log.message.includes('Next.js auto-prefetches automatically')) {
foundLog = true
}
})
} finally {
if (browser) {
await browser.close()
}
}
return foundLog
}
describe('Development Logs', () => {
it('should warn when prefetch is true', async () => {
const foundLog = await getLogs$('/docs/development-logs')
expect(foundLog).toBe(true)
})
it('should not warn when prefetch is false', async () => {
const foundLog = await getLogs$(
'/docs/development-logs/link-with-prefetch-false'
)
expect(foundLog).toBe(false)
})
it('should not warn when prefetch is not specified', async () => {
const foundLog = await getLogs$(
'/docs/development-logs/link-with-no-prefetch'
)
expect(foundLog).toBe(false)
})
})
})

View file

@ -1 +0,0 @@
export default () => <div className="about-page">About Page</div>

View file

@ -1,11 +0,0 @@
import Link from 'next/link'
export default function IndexPage() {
return (
<div>
<Link href="/about" prefetch>
To About Page
</Link>
</div>
)
}

View file

@ -1,9 +0,0 @@
import Link from 'next/link'
export default function NoPrefetchPage() {
return (
<div>
<Link href="/about">No prefetch</Link>
</div>
)
}

View file

@ -1,11 +0,0 @@
import Link from 'next/link'
export default function PrefetchFalsePage() {
return (
<div>
<Link href="/about" prefetch={false}>
Prefetch set to false
</Link>
</div>
)
}

View file

@ -1 +0,0 @@
export default () => <div id="node-env">{process.env.NODE_ENV}</div>

View file

@ -1 +0,0 @@
new static folder

View file

@ -1,253 +0,0 @@
import { join } from 'path'
import cheerio from 'cheerio'
import webdriver from 'next-webdriver'
import { createNext, FileRef } from 'e2e-utils'
import { renderViaHTTP, check } from 'next-test-utils'
import { NextInstance } from 'test/lib/next-modes/base'
describe('basic next/dynamic usage', () => {
let next: NextInstance
beforeAll(async () => {
next = await createNext({
files: {
components: new FileRef(join(__dirname, 'next-dynamic/components')),
pages: new FileRef(join(__dirname, 'next-dynamic/pages')),
},
nextConfig: {
basePath: '/docs',
},
})
})
afterAll(() => next.destroy())
async function get$(path, query?: any) {
const html = await renderViaHTTP(next.url, path, query)
return cheerio.load(html)
}
describe('Dynamic import', () => {
describe('default behavior', () => {
it('should render dynamic import components', async () => {
const $ = await get$('/docs/dynamic/ssr')
// Make sure the client side knows it has to wait for the bundle
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
'dynamic/ssr.js -> ../../components/hello1'
)
expect($('body').text()).toMatch(/Hello World 1/)
})
it('should render dynamic import components using a function as first parameter', async () => {
const $ = await get$('/docs/dynamic/function')
// Make sure the client side knows it has to wait for the bundle
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
'dynamic/function.js -> ../../components/hello1'
)
expect($('body').text()).toMatch(/Hello World 1/)
})
it('should render even there are no physical chunk exists', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/dynamic/no-chunk')
await check(
() => browser.elementByCss('body').text(),
/Welcome, normal/
)
await check(
() => browser.elementByCss('body').text(),
/Welcome, dynamic/
)
} finally {
if (browser) {
await browser.close()
}
}
})
it('should hydrate nested chunks', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/dynamic/nested')
await check(() => browser.elementByCss('body').text(), /Nested 1/)
await check(() => browser.elementByCss('body').text(), /Nested 2/)
await check(
() => browser.elementByCss('body').text(),
/Browser hydrated/
)
if ((global as any).browserName === 'chrome') {
const logs = await browser.log('browser')
logs.forEach((logItem) => {
expect(logItem.message).not.toMatch(
/Expected server HTML to contain/
)
})
}
} finally {
if (browser) {
await browser.close()
}
}
})
it('should render the component Head content', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/dynamic/head')
await check(() => browser.elementByCss('body').text(), /test/)
const backgroundColor = await browser
.elementByCss('.dynamic-style')
.getComputedCss('background-color')
const height = await browser
.elementByCss('.dynamic-style')
.getComputedCss('height')
expect(height).toBe('200px')
expect(backgroundColor).toMatch(/rgba?\(0, 128, 0/)
} finally {
if (browser) {
await browser.close()
}
}
})
})
describe('ssr:false option', () => {
it('should not render loading on the server side', async () => {
const $ = await get$('/docs/dynamic/no-ssr')
expect($('body').html()).not.toContain('"dynamicIds"')
expect($('body').text()).not.toMatch('loading...')
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/dynamic/no-ssr')
await check(
() => browser.elementByCss('body').text(),
/Hello World 1/
)
} finally {
if (browser) {
await browser.close()
}
}
})
if (!(global as any).isNextDev) {
it('should not include ssr:false imports to server trace', async () => {
const trace = JSON.parse(
await next.readFile('.next/server/pages/dynamic/no-ssr.js.nft.json')
) as { files: string[] }
expect(trace).not.toContain('hello1')
})
}
})
describe('ssr:true option', () => {
it('Should render the component on the server side', async () => {
const $ = await get$('/docs/dynamic/ssr-true')
expect($('body').html()).toContain('"dynamicIds"')
expect($('p').text()).toBe('Hello World 1')
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/dynamic/ssr-true')
await check(
() => browser.elementByCss('body').text(),
/Hello World 1/
)
} finally {
if (browser) {
await browser.close()
}
}
})
})
describe('custom chunkfilename', () => {
it('should render the correct filename', async () => {
const $ = await get$('/docs/dynamic/chunkfilename')
expect($('body').text()).toMatch(/test chunkfilename/)
expect($('html').html()).toMatch(/hello-world\.js/)
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/dynamic/chunkfilename')
await check(
() => browser.elementByCss('body').text(),
/test chunkfilename/
)
} finally {
if (browser) {
await browser.close()
}
}
})
})
describe('custom loading', () => {
it('should render custom loading on the server side when `ssr:false` and `loading` is provided', async () => {
const $ = await get$('/docs/dynamic/no-ssr-custom-loading')
expect($('p').text()).toBe('LOADING')
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(
next.url,
'/docs/dynamic/no-ssr-custom-loading'
)
await check(
() => browser.elementByCss('body').text(),
/Hello World 1/
)
} finally {
if (browser) {
await browser.close()
}
}
})
})
describe('Multiple modules', () => {
it('should only include the rendered module script tag', async () => {
const $ = await get$('/docs/dynamic/multiple-modules')
const html = $('html').html()
expect(html).toMatch(/hello1\.js/)
expect(html).not.toMatch(/hello2\.js/)
})
it('should only load the rendered module in the browser', async () => {
let browser
try {
browser = await webdriver(next.url, '/docs/dynamic/multiple-modules')
const html = await browser.eval('document.documentElement.innerHTML')
expect(html).toMatch(/hello1\.js/)
expect(html).not.toMatch(/hello2\.js/)
} finally {
if (browser) {
await browser.close()
}
}
})
it('should only render one bundle if component is used multiple times', async () => {
const $ = await get$('/docs/dynamic/multiple-modules')
const html = $('html').html()
try {
expect(html.match(/chunks[\\/]hello1\.js/g).length).toBe(1)
expect(html).not.toMatch(/hello2\.js/)
} catch (err) {
console.error(html)
throw err
}
})
})
})
})

View file

@ -1 +0,0 @@
export default () => <div>test chunkfilename</div>

View file

@ -1,13 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
export default class extends React.Component {
static contextTypes = {
data: PropTypes.object,
}
render() {
const { data } = this.context
return <div>{data.title}</div>
}
}

View file

@ -1 +0,0 @@
export default () => <p>Hello World 1</p>

View file

@ -1 +0,0 @@
export default () => <p>Hello World 2</p>

View file

@ -1 +0,0 @@
export default () => <p>Hello World 1</p>

View file

@ -1 +0,0 @@
export default () => <p>Hello World 2</p>

View file

@ -1,10 +0,0 @@
import dynamic from 'next/dynamic'
const Nested2 = dynamic(() => import('./nested2'))
export default () => (
<div>
Nested 1
<Nested2 />
</div>
)

View file

@ -1,12 +0,0 @@
import dynamic from 'next/dynamic'
const BrowserLoaded = dynamic(async () => () => <div>Browser hydrated</div>, {
ssr: false,
})
export default () => (
<div>
<div>Nested 2</div>
<BrowserLoaded />
</div>
)

View file

@ -1,17 +0,0 @@
import React from 'react'
export default class Welcome extends React.Component {
state = { name: null }
componentDidMount() {
const { name } = this.props
this.setState({ name })
}
render() {
const { name } = this.state
if (!name) return null
return <p>Welcome, {name}</p>
}
}

View file

@ -1,9 +0,0 @@
import dynamic from 'next/dynamic'
const Hello = dynamic(
import(
/* webpackChunkName: 'hello-world' */ '../../components/hello-chunkfilename'
)
)
export default Hello

View file

@ -1,5 +0,0 @@
import dynamic from 'next/dynamic'
const Hello = dynamic(() => import('../../components/hello1'))
export default Hello

View file

@ -1,30 +0,0 @@
import dynamic from 'next/dynamic'
import Head from 'next/head'
const Test = dynamic({
loader: async () => {
// component
return () => {
return (
<div className="dynamic-style">
<Head>
<style
dangerouslySetInnerHTML={{
__html: `
.dynamic-style {
background-color: green;
height: 200px;
}
`,
}}
/>
</Head>
test
</div>
)
}
},
ssr: false,
})
export default Test

View file

@ -1,7 +0,0 @@
import Link from 'next/link'
export default () => (
<div>
<Link href="/dynamic/no-chunk">No Chunk</Link>
</div>
)

View file

@ -1,23 +0,0 @@
/* eslint-disable */
import dynamic from 'next/dynamic'
const Hello = dynamic(
import(/* webpackChunkName: 'hello1' */ '../../components/hello3')
)
const Hello2 = dynamic(
import(/* webpackChunkName: 'hello2' */ '../../components/hello4')
)
export default () => {
return (
<div>
<Hello />
<Hello />
<Hello />
<Hello />
<Hello />
<Hello />
<Hello />
</div>
)
}

View file

@ -1,5 +0,0 @@
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(() => import('../../components/nested1'))
export default DynamicComponent

View file

@ -1,11 +0,0 @@
import dynamic from 'next/dynamic'
import Welcome from '../../components/welcome'
const Welcome2 = dynamic(import('../../components/welcome'))
export default () => (
<div>
<Welcome name="normal" />
<Welcome2 name="dynamic" />
</div>
)

View file

@ -1,8 +0,0 @@
import dynamic from 'next/dynamic'
const Hello = dynamic(import('../../components/hello1'), {
ssr: false,
loading: () => <p>LOADING</p>,
})
export default Hello

View file

@ -1,5 +0,0 @@
import dynamic from 'next/dynamic'
const Hello = dynamic(import('../../components/hello1'), { ssr: false })
export default Hello

View file

@ -1,5 +0,0 @@
import dynamic from 'next/dynamic'
const Hello = dynamic(import('../../components/hello1'), { ssr: true })
export default Hello

View file

@ -1,5 +0,0 @@
import dynamic from 'next/dynamic'
const Hello = dynamic(import('../../components/hello1'))
export default Hello

File diff suppressed because it is too large Load diff

View file

@ -5,101 +5,117 @@ import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils'
describe('misc basic dev tests', () => {
let next: NextInstance
describe.each([[''], ['/docs']])(
'misc basic dev tests, basePath: %p',
(basePath: string) => {
let next: NextInstance
beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'misc/pages')),
public: new FileRef(join(__dirname, 'misc/public')),
},
beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'misc/pages')),
public: new FileRef(join(__dirname, 'misc/public')),
},
nextConfig: {
basePath,
},
})
})
})
afterAll(() => next.destroy())
afterAll(() => next.destroy())
it('should set process.env.NODE_ENV in development', async () => {
const browser = await webdriver(next.url, '/process-env')
const nodeEnv = await browser.elementByCss('#node-env').text()
expect(nodeEnv).toBe('development')
await browser.close()
})
it('should set process.env.NODE_ENV in development', async () => {
const browser = await webdriver(next.url, basePath + '/process-env')
const nodeEnv = await browser.elementByCss('#node-env').text()
expect(nodeEnv).toBe('development')
await browser.close()
})
it('should allow access to public files', async () => {
const data = await renderViaHTTP(next.url, '/data/data.txt')
expect(data).toBe('data')
it('should allow access to public files', async () => {
const data = await renderViaHTTP(next.url, basePath + '/data/data.txt')
expect(data).toBe('data')
const legacy = await renderViaHTTP(next.url, '/static/legacy.txt')
expect(legacy).toMatch(`new static folder`)
})
const legacy = await renderViaHTTP(
next.url,
basePath + '/static/legacy.txt'
)
expect(legacy).toMatch(`new static folder`)
})
describe('With Security Related Issues', () => {
it('should not allow accessing files outside .next/static and .next/server directory', async () => {
const pathsToCheck = [
`/_next/static/../BUILD_ID`,
`/_next/static/../routes-manifest.json`,
]
for (const path of pathsToCheck) {
const res = await fetchViaHTTP(next.url, path)
describe('With Security Related Issues', () => {
it('should not allow accessing files outside .next/static and .next/server directory', async () => {
const pathsToCheck = [
basePath + '/_next/static/../BUILD_ID',
basePath + '/_next/static/../routes-manifest.json',
]
for (const path of pathsToCheck) {
const res = await fetchViaHTTP(next.url, path)
const text = await res.text()
try {
expect(res.status).toBe(404)
expect(text).toMatch(/This page could not be found/)
} catch (err) {
throw new Error(`Path ${path} accessible from the browser`)
}
}
})
it('should handle encoded / value for trailing slash correctly', async () => {
const res = await fetchViaHTTP(
next.url,
basePath + '/%2fexample.com/',
undefined,
{
redirect: 'manual',
}
)
const { pathname, hostname } = url.parse(
res.headers.get('location') || ''
)
expect(res.status).toBe(308)
expect(pathname).toBe(basePath + '/%2fexample.com')
expect(hostname).not.toBe('example.com')
const text = await res.text()
try {
expect(res.status).toBe(404)
expect(text).toMatch(/This page could not be found/)
} catch (err) {
throw new Error(`Path ${path} accessible from the browser`)
}
}
expect(text).toEqual(basePath + '/%2fexample.com')
})
})
it('should handle encoded / value for trailing slash correctly', async () => {
const res = await fetchViaHTTP(next.url, '/%2fexample.com/', undefined, {
redirect: 'manual',
})
async function getLogs$(path) {
let foundLog = false
let browser
try {
browser = await webdriver(next.url, path)
const browserLogs = await browser.log('browser')
const { pathname, hostname } = url.parse(
res.headers.get('location') || ''
)
expect(res.status).toBe(308)
expect(pathname).toBe('/%2fexample.com')
expect(hostname).not.toBe('example.com')
const text = await res.text()
expect(text).toEqual('/%2fexample.com')
})
})
async function getLogs$(path) {
let foundLog = false
let browser
try {
browser = await webdriver(next.url, path)
const browserLogs = await browser.log('browser')
browserLogs.forEach((log) => {
if (log.message.includes('Next.js auto-prefetches automatically')) {
foundLog = true
browserLogs.forEach((log) => {
if (log.message.includes('Next.js auto-prefetches automatically')) {
foundLog = true
}
})
} finally {
if (browser) {
await browser.close()
}
})
} finally {
if (browser) {
await browser.close()
}
return foundLog
}
return foundLog
describe('Development Logs', () => {
it('should warn when prefetch is true', async () => {
const foundLog = await getLogs$(basePath + '/development-logs')
expect(foundLog).toBe(true)
})
it('should not warn when prefetch is false', async () => {
const foundLog = await getLogs$(
basePath + '/development-logs/link-with-prefetch-false'
)
expect(foundLog).toBe(false)
})
it('should not warn when prefetch is not specified', async () => {
const foundLog = await getLogs$(
basePath + '/development-logs/link-with-no-prefetch'
)
expect(foundLog).toBe(false)
})
})
}
describe('Development Logs', () => {
it('should warn when prefetch is true', async () => {
const foundLog = await getLogs$('/development-logs')
expect(foundLog).toBe(true)
})
it('should not warn when prefetch is false', async () => {
const foundLog = await getLogs$(
'/development-logs/link-with-prefetch-false'
)
expect(foundLog).toBe(false)
})
it('should not warn when prefetch is not specified', async () => {
const foundLog = await getLogs$('/development-logs/link-with-no-prefetch')
expect(foundLog).toBe(false)
})
})
})
)

View file

@ -5,244 +5,263 @@ import { createNext, FileRef } from 'e2e-utils'
import { renderViaHTTP, check, hasRedbox } from 'next-test-utils'
import { NextInstance } from 'test/lib/next-modes/base'
describe('basic next/dynamic usage', () => {
let next: NextInstance
describe.each([[''], ['/docs']])(
'basic next/dynamic usage, basePath: %p',
(basePath: string) => {
let next: NextInstance
beforeAll(async () => {
next = await createNext({
files: {
components: new FileRef(join(__dirname, 'next-dynamic/components')),
pages: new FileRef(join(__dirname, 'next-dynamic/pages')),
},
})
})
afterAll(() => next.destroy())
async function get$(path, query?: any) {
const html = await renderViaHTTP(next.url, path, query)
return cheerio.load(html)
}
describe('Dynamic import', () => {
describe('default behavior', () => {
it('should render dynamic import components', async () => {
const $ = await get$('/dynamic/ssr')
// Make sure the client side knows it has to wait for the bundle
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
'dynamic/ssr.js -> ../../components/hello1'
)
expect($('body').text()).toMatch(/Hello World 1/)
})
it('should render dynamic import components using a function as first parameter', async () => {
const $ = await get$('/dynamic/function')
// Make sure the client side knows it has to wait for the bundle
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
'dynamic/function.js -> ../../components/hello1'
)
expect($('body').text()).toMatch(/Hello World 1/)
})
it('should render even there are no physical chunk exists', async () => {
let browser
try {
browser = await webdriver(next.url, '/dynamic/no-chunk')
await check(
() => browser.elementByCss('body').text(),
/Welcome, normal/
)
await check(
() => browser.elementByCss('body').text(),
/Welcome, dynamic/
)
} finally {
if (browser) {
await browser.close()
}
}
})
it('should hydrate nested chunks', async () => {
let browser
try {
browser = await webdriver(next.url, '/dynamic/nested')
await check(() => browser.elementByCss('body').text(), /Nested 1/)
await check(() => browser.elementByCss('body').text(), /Nested 2/)
await check(
() => browser.elementByCss('body').text(),
/Browser hydrated/
)
if ((global as any).browserName === 'chrome') {
const logs = await browser.log('browser')
logs.forEach((logItem) => {
expect(logItem.message).not.toMatch(
/Expected server HTML to contain/
)
})
}
} finally {
if (browser) {
await browser.close()
}
}
})
it('should render the component Head content', async () => {
let browser
try {
browser = await webdriver(next.url, '/dynamic/head')
await check(() => browser.elementByCss('body').text(), /test/)
const backgroundColor = await browser
.elementByCss('.dynamic-style')
.getComputedCss('background-color')
const height = await browser
.elementByCss('.dynamic-style')
.getComputedCss('height')
expect(height).toBe('200px')
expect(backgroundColor).toMatch(/rgba?\(0, 128, 0/)
} finally {
if (browser) {
await browser.close()
}
}
beforeAll(async () => {
next = await createNext({
files: {
components: new FileRef(join(__dirname, 'next-dynamic/components')),
pages: new FileRef(join(__dirname, 'next-dynamic/pages')),
},
nextConfig: {
basePath,
},
})
})
describe('ssr:false option', () => {
it('should not render loading on the server side', async () => {
const $ = await get$('/dynamic/no-ssr')
expect($('body').html()).not.toContain('"dynamicIds"')
expect($('body').text()).not.toMatch('loading...')
})
afterAll(() => next.destroy())
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(next.url, '/dynamic/no-ssr')
await check(
() => browser.elementByCss('body').text(),
/Hello World 1/
async function get$(path, query?: any) {
const html = await renderViaHTTP(next.url, path, query)
return cheerio.load(html)
}
describe('Dynamic import', () => {
describe('default behavior', () => {
it('should render dynamic import components', async () => {
const $ = await get$(basePath + '/dynamic/ssr')
// Make sure the client side knows it has to wait for the bundle
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
'dynamic/ssr.js -> ../../components/hello1'
)
expect(await hasRedbox(browser)).toBe(false)
} finally {
if (browser) {
await browser.close()
}
}
})
})
describe('ssr:true option', () => {
it('Should render the component on the server side', async () => {
const $ = await get$('/dynamic/ssr-true')
expect($('body').html()).toContain('"dynamicIds"')
expect($('p').text()).toBe('Hello World 1')
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(next.url, '/dynamic/ssr-true')
await check(
() => browser.elementByCss('body').text(),
/Hello World 1/
)
} finally {
if (browser) {
await browser.close()
}
}
})
if (!(global as any).isNextDev) {
it('should not include ssr:false imports to server trace', async () => {
const trace = JSON.parse(
await next.readFile('.next/server/pages/dynamic/no-ssr.js.nft.json')
) as { files: string[] }
expect(trace).not.toContain('hello1')
expect($('body').text()).toMatch(/Hello World 1/)
})
}
})
describe('custom chunkfilename', () => {
it('should render the correct filename', async () => {
const $ = await get$('/dynamic/chunkfilename')
expect($('body').text()).toMatch(/test chunkfilename/)
expect($('html').html()).toMatch(/hello-world\.js/)
it('should render dynamic import components using a function as first parameter', async () => {
const $ = await get$(basePath + '/dynamic/function')
// Make sure the client side knows it has to wait for the bundle
expect(JSON.parse($('#__NEXT_DATA__').html()).dynamicIds).toContain(
'dynamic/function.js -> ../../components/hello1'
)
expect($('body').text()).toMatch(/Hello World 1/)
})
it('should render even there are no physical chunk exists', async () => {
let browser
try {
browser = await webdriver(next.url, basePath + '/dynamic/no-chunk')
await check(
() => browser.elementByCss('body').text(),
/Welcome, normal/
)
await check(
() => browser.elementByCss('body').text(),
/Welcome, dynamic/
)
} finally {
if (browser) {
await browser.close()
}
}
})
it('should hydrate nested chunks', async () => {
let browser
try {
browser = await webdriver(next.url, basePath + '/dynamic/nested')
await check(() => browser.elementByCss('body').text(), /Nested 1/)
await check(() => browser.elementByCss('body').text(), /Nested 2/)
await check(
() => browser.elementByCss('body').text(),
/Browser hydrated/
)
if ((global as any).browserName === 'chrome') {
const logs = await browser.log('browser')
logs.forEach((logItem) => {
expect(logItem.message).not.toMatch(
/Expected server HTML to contain/
)
})
}
} finally {
if (browser) {
await browser.close()
}
}
})
it('should render the component Head content', async () => {
let browser
try {
browser = await webdriver(next.url, basePath + '/dynamic/head')
await check(() => browser.elementByCss('body').text(), /test/)
const backgroundColor = await browser
.elementByCss('.dynamic-style')
.getComputedCss('background-color')
const height = await browser
.elementByCss('.dynamic-style')
.getComputedCss('height')
expect(height).toBe('200px')
expect(backgroundColor).toMatch(/rgba?\(0, 128, 0/)
} finally {
if (browser) {
await browser.close()
}
}
})
})
describe('ssr:false option', () => {
it('should not render loading on the server side', async () => {
const $ = await get$(basePath + '/dynamic/no-ssr')
expect($('body').html()).not.toContain('"dynamicIds"')
expect($('body').text()).not.toMatch('loading...')
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(next.url, basePath + '/dynamic/no-ssr')
await check(
() => browser.elementByCss('body').text(),
/Hello World 1/
)
expect(await hasRedbox(browser)).toBe(false)
} finally {
if (browser) {
await browser.close()
}
}
})
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(next.url, '/dynamic/chunkfilename')
await check(
() => browser.elementByCss('body').text(),
/test chunkfilename/
)
} finally {
if (browser) {
await browser.close()
describe('ssr:true option', () => {
it('Should render the component on the server side', async () => {
const $ = await get$(basePath + '/dynamic/ssr-true')
expect($('body').html()).toContain('"dynamicIds"')
expect($('p').text()).toBe('Hello World 1')
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(next.url, basePath + '/dynamic/ssr-true')
await check(
() => browser.elementByCss('body').text(),
/Hello World 1/
)
} finally {
if (browser) {
await browser.close()
}
}
})
if (!(global as any).isNextDev) {
it('should not include ssr:false imports to server trace', async () => {
const trace = JSON.parse(
await next.readFile(
'.next/server/pages/dynamic/no-ssr.js.nft.json'
)
) as { files: string[] }
expect(trace).not.toContain('hello1')
})
}
})
})
describe('custom loading', () => {
it('should render custom loading on the server side when `ssr:false` and `loading` is provided', async () => {
const $ = await get$('/dynamic/no-ssr-custom-loading')
expect($('p').text()).toBe('LOADING')
})
describe('custom chunkfilename', () => {
it('should render the correct filename', async () => {
const $ = await get$(basePath + '/dynamic/chunkfilename')
expect($('body').text()).toMatch(/test chunkfilename/)
expect($('html').html()).toMatch(/hello-world\.js/)
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(next.url, '/dynamic/no-ssr-custom-loading')
await check(
() => browser.elementByCss('body').text(),
/Hello World 1/
)
} finally {
if (browser) {
await browser.close()
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(
next.url,
basePath + '/dynamic/chunkfilename'
)
await check(
() => browser.elementByCss('body').text(),
/test chunkfilename/
)
} finally {
if (browser) {
await browser.close()
}
}
}
})
})
describe('Multiple modules', () => {
it('should only include the rendered module script tag', async () => {
const $ = await get$('/dynamic/multiple-modules')
const html = $('html').html()
expect(html).toMatch(/hello1\.js/)
expect(html).not.toMatch(/hello2\.js/)
})
})
it('should only load the rendered module in the browser', async () => {
let browser
try {
browser = await webdriver(next.url, '/dynamic/multiple-modules')
const html = await browser.eval('document.documentElement.innerHTML')
describe('custom loading', () => {
it('should render custom loading on the server side when `ssr:false` and `loading` is provided', async () => {
const $ = await get$(basePath + '/dynamic/no-ssr-custom-loading')
expect($('p').text()).toBe('LOADING')
})
it('should render the component on client side', async () => {
let browser
try {
browser = await webdriver(
next.url,
basePath + '/dynamic/no-ssr-custom-loading'
)
await check(
() => browser.elementByCss('body').text(),
/Hello World 1/
)
} finally {
if (browser) {
await browser.close()
}
}
})
})
describe('Multiple modules', () => {
it('should only include the rendered module script tag', async () => {
const $ = await get$(basePath + '/dynamic/multiple-modules')
const html = $('html').html()
expect(html).toMatch(/hello1\.js/)
expect(html).not.toMatch(/hello2\.js/)
} finally {
if (browser) {
await browser.close()
}
}
})
})
it('should only render one bundle if component is used multiple times', async () => {
const $ = await get$('/dynamic/multiple-modules')
const html = $('html').html()
try {
expect(html.match(/chunks[\\/]hello1\.js/g).length).toBe(1)
expect(html).not.toMatch(/hello2\.js/)
} catch (err) {
console.error(html)
throw err
}
it('should only load the rendered module in the browser', async () => {
let browser
try {
browser = await webdriver(
next.url,
basePath + '/dynamic/multiple-modules'
)
const html = await browser.eval(
'document.documentElement.innerHTML'
)
expect(html).toMatch(/hello1\.js/)
expect(html).not.toMatch(/hello2\.js/)
} finally {
if (browser) {
await browser.close()
}
}
})
it('should only render one bundle if component is used multiple times', async () => {
const $ = await get$(basePath + '/dynamic/multiple-modules')
const html = $('html').html()
try {
expect(html.match(/chunks[\\/]hello1\.js/g).length).toBe(1)
expect(html).not.toMatch(/hello2\.js/)
} catch (err) {
console.error(html)
throw err
}
})
})
})
})
})
}
)