Refactor more tests to createNextDescribe (#44104)
This commit is contained in:
parent
b8ae447b0f
commit
7a2ec41601
39 changed files with 2752 additions and 3318 deletions
|
@ -5,11 +5,6 @@ import { NextInstance } from 'test/lib/next-modes/base'
|
|||
import path from 'path'
|
||||
|
||||
describe('ReactRefresh app', () => {
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -5,11 +5,6 @@ import path from 'path'
|
|||
|
||||
// TODO-APP: Investigate snapshot mismatch
|
||||
describe('ReactRefreshLogBox app', () => {
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -7,11 +7,6 @@ import path from 'path'
|
|||
// TODO: figure out why snapshots mismatch on GitHub actions
|
||||
// specifically but work in docker and locally
|
||||
describe.skip('ReactRefreshLogBox app', () => {
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -6,11 +6,6 @@ import { check } from 'next-test-utils'
|
|||
import path from 'path'
|
||||
|
||||
describe('ReactRefreshLogBox app', () => {
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -6,11 +6,6 @@ import path from 'path'
|
|||
// TODO: re-enable these tests after figuring out what is causing
|
||||
// them to be so unreliable in CI
|
||||
describe.skip('ReactRefreshLogBox app', () => {
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -4,11 +4,6 @@ import { NextInstance } from 'test/lib/next-modes/base'
|
|||
import { sandbox } from './helpers'
|
||||
|
||||
describe('ReactRefreshModule app', () => {
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -5,11 +5,6 @@ import { NextInstance } from 'test/lib/next-modes/base'
|
|||
import path from 'path'
|
||||
|
||||
describe('ReactRefreshRegression app', () => {
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -6,11 +6,6 @@ import path from 'path'
|
|||
import { check } from 'next-test-utils'
|
||||
|
||||
describe('Error Overlay for server components', () => {
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -532,14 +532,10 @@ describe('basic HMR', () => {
|
|||
)
|
||||
)
|
||||
|
||||
const isReact17 = process.env.NEXT_TEST_REACT_VERSION === '^17'
|
||||
|
||||
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: ${
|
||||
isReact17 ? '/search/' : '[object RegExp]'
|
||||
}). If you meant to render a collection of children, use an array instead.`
|
||||
`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)
|
||||
|
|
|
@ -597,14 +597,10 @@ describe('basic HMR', () => {
|
|||
)
|
||||
)
|
||||
|
||||
const isReact17 = process.env.NEXT_TEST_REACT_VERSION === '^17'
|
||||
|
||||
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: ${
|
||||
isReact17 ? '/search/' : '[object RegExp]'
|
||||
}). If you meant to render a collection of children, use an array instead.`
|
||||
`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)
|
||||
|
|
|
@ -1,56 +1,45 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { renderViaHTTP } from 'next-test-utils'
|
||||
import webdriver from 'next-webdriver'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import path from 'path'
|
||||
import { readJSON } from 'fs-extra'
|
||||
|
||||
describe('app-dir alias handling', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'app-alias')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
packageJson: {
|
||||
type: 'module',
|
||||
},
|
||||
createNextDescribe(
|
||||
'app-dir alias handling',
|
||||
{
|
||||
files: path.join(__dirname, 'app-alias'),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
packageJson: {
|
||||
type: 'module',
|
||||
},
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next, isNextDev }) => {
|
||||
it('should handle typescript paths alias correctly', async () => {
|
||||
const html = await next.render('/button')
|
||||
expect(html).toContain('click</button>')
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
it('should handle typescript paths alias correctly', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/button')
|
||||
expect(html).toContain('click</button>')
|
||||
})
|
||||
|
||||
it('should resolve css imports from outside with src folder presented', async () => {
|
||||
const browser = await webdriver(next.url, '/button')
|
||||
const fontSize = await browser
|
||||
.elementByCss('button')
|
||||
.getComputedCss('font-size')
|
||||
expect(fontSize).toBe('50px')
|
||||
})
|
||||
|
||||
if (!(global as any).isNextDev) {
|
||||
it('should generate app-build-manifest correctly', async () => {
|
||||
// Remove other page CSS files:
|
||||
const manifest = await readJSON(
|
||||
path.join(next.testDir, '.next', 'app-build-manifest.json')
|
||||
)
|
||||
|
||||
expect(manifest.pages).not.toBeEmptyObject()
|
||||
it('should resolve css imports from outside with src folder presented', async () => {
|
||||
const browser = await next.browser('/button')
|
||||
const fontSize = await browser
|
||||
.elementByCss('button')
|
||||
.getComputedCss('font-size')
|
||||
expect(fontSize).toBe('50px')
|
||||
})
|
||||
|
||||
if (!isNextDev) {
|
||||
it('should generate app-build-manifest correctly', async () => {
|
||||
// Remove other page CSS files:
|
||||
const manifest = await next.readJSON(
|
||||
path.join('.next', 'app-build-manifest.json')
|
||||
)
|
||||
|
||||
expect(manifest.pages).not.toBeEmptyObject()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,32 +1,23 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { renderViaHTTP } from 'next-test-utils'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import path from 'path'
|
||||
|
||||
describe('app-dir global edge configuration', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'app-edge-global')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
createNextDescribe(
|
||||
'app-dir global edge configuration',
|
||||
{
|
||||
files: path.join(__dirname, 'app-edge-global'),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
it('should handle edge only routes', async () => {
|
||||
const html = await next.render('/app-edge')
|
||||
expect(html).toContain('<p>Edge!</p>')
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
it('should handle edge only routes', async () => {
|
||||
const appHtml = await renderViaHTTP(next.url, '/app-edge')
|
||||
expect(appHtml).toContain('<p>Edge!</p>')
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,80 +1,72 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { check, renderViaHTTP } from 'next-test-utils'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import { check } from 'next-test-utils'
|
||||
import path from 'path'
|
||||
|
||||
describe('app-dir edge SSR', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
createNextDescribe(
|
||||
'app-dir edge SSR',
|
||||
{
|
||||
files: path.join(__dirname, 'app-edge'),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
it('should handle edge only routes', async () => {
|
||||
const appHtml = await next.render('/app-edge')
|
||||
expect(appHtml).toContain('<p>Edge!</p>')
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'app-edge')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
const pageHtml = await next.render('/pages-edge')
|
||||
expect(pageHtml).toContain('<p>pages-edge-ssr</p>')
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
it('should handle edge only routes', async () => {
|
||||
const appHtml = await renderViaHTTP(next.url, '/app-edge')
|
||||
expect(appHtml).toContain('<p>Edge!</p>')
|
||||
|
||||
const pageHtml = await renderViaHTTP(next.url, '/pages-edge')
|
||||
expect(pageHtml).toContain('<p>pages-edge-ssr</p>')
|
||||
})
|
||||
|
||||
if ((globalThis as any).isNextDev) {
|
||||
it('should resolve module without error in edge runtime', async () => {
|
||||
const logs = []
|
||||
next.on('stderr', (log) => {
|
||||
logs.push(log)
|
||||
if ((globalThis as any).isNextDev) {
|
||||
it('should resolve module without error in edge runtime', async () => {
|
||||
const logs = []
|
||||
next.on('stderr', (log) => {
|
||||
logs.push(log)
|
||||
})
|
||||
await next.render('app-edge')
|
||||
expect(
|
||||
logs.some((log) => log.includes(`Attempted import error:`))
|
||||
).toBe(false)
|
||||
})
|
||||
await renderViaHTTP(next.url, 'app-edge')
|
||||
expect(logs.some((log) => log.includes(`Attempted import error:`))).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle edge rsc hmr', async () => {
|
||||
const pageFile = 'app/app-edge/page.tsx'
|
||||
const content = await next.readFile(pageFile)
|
||||
it('should handle edge rsc hmr', async () => {
|
||||
const pageFile = 'app/app-edge/page.tsx'
|
||||
const content = await next.readFile(pageFile)
|
||||
|
||||
// Update rendered content
|
||||
const updatedContent = content.replace('Edge!', 'edge-hmr')
|
||||
await next.patchFile(pageFile, updatedContent)
|
||||
await check(async () => {
|
||||
const html = await renderViaHTTP(next.url, '/app-edge')
|
||||
return html
|
||||
}, /edge-hmr/)
|
||||
// Update rendered content
|
||||
const updatedContent = content.replace('Edge!', 'edge-hmr')
|
||||
await next.patchFile(pageFile, updatedContent)
|
||||
await check(async () => {
|
||||
const html = await next.render('/app-edge')
|
||||
return html
|
||||
}, /edge-hmr/)
|
||||
|
||||
// Revert
|
||||
await next.patchFile(pageFile, content)
|
||||
await check(async () => {
|
||||
const html = await renderViaHTTP(next.url, '/app-edge')
|
||||
return html
|
||||
}, /Edge!/)
|
||||
})
|
||||
} else {
|
||||
// Production tests
|
||||
it('should generate matchers correctly in middleware manifest', async () => {
|
||||
const manifest = JSON.parse(
|
||||
await next.readFile('.next/server/middleware-manifest.json')
|
||||
)
|
||||
expect(manifest.functions['/(group)/group/page'].matchers).toEqual([
|
||||
{
|
||||
regexp: '^/group$',
|
||||
},
|
||||
])
|
||||
})
|
||||
// Revert
|
||||
await next.patchFile(pageFile, content)
|
||||
await check(async () => {
|
||||
const html = await next.render('/app-edge')
|
||||
return html
|
||||
}, /Edge!/)
|
||||
})
|
||||
} else {
|
||||
// Production tests
|
||||
it('should generate matchers correctly in middleware manifest', async () => {
|
||||
const manifest = JSON.parse(
|
||||
await next.readFile('.next/server/middleware-manifest.json')
|
||||
)
|
||||
expect(manifest.functions['/(group)/group/page'].matchers).toEqual([
|
||||
{
|
||||
regexp: '^/group$',
|
||||
},
|
||||
])
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import path from 'path'
|
||||
import { renderViaHTTP, fetchViaHTTP } from 'next-test-utils'
|
||||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import webdriver from 'next-webdriver'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
|
||||
async function resolveStreamResponse(response: any, onData?: any) {
|
||||
let result = ''
|
||||
|
@ -18,183 +15,169 @@ async function resolveStreamResponse(response: any, onData?: any) {
|
|||
return result
|
||||
}
|
||||
|
||||
describe('app dir - external dependency', () => {
|
||||
let next: NextInstance
|
||||
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip for deploy mode for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, './app-external')),
|
||||
dependencies: {
|
||||
'@next/font': 'canary',
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
swr: '2.0.0-rc.0',
|
||||
createNextDescribe(
|
||||
'app dir - external dependency',
|
||||
{
|
||||
files: path.join(__dirname, './app-external'),
|
||||
dependencies: {
|
||||
'@next/font': 'canary',
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
swr: '2.0.0-rc.0',
|
||||
},
|
||||
packageJson: {
|
||||
scripts: {
|
||||
setup: `cp -r ./node_modules_bak/* ./node_modules`,
|
||||
build: 'yarn setup && next build',
|
||||
dev: 'yarn setup && next dev',
|
||||
start: 'next start',
|
||||
},
|
||||
packageJson: {
|
||||
scripts: {
|
||||
setup: `cp -r ./node_modules_bak/* ./node_modules`,
|
||||
build: 'yarn setup && next build',
|
||||
dev: 'yarn setup && next dev',
|
||||
start: 'next start',
|
||||
},
|
||||
},
|
||||
installCommand: 'yarn',
|
||||
startCommand: (global as any).isNextDev ? 'yarn dev' : 'yarn start',
|
||||
buildCommand: 'yarn build',
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
const { isNextDeploy } = global as any
|
||||
const isReact17 = process.env.NEXT_TEST_REACT_VERSION === '^17'
|
||||
if (isNextDeploy || isReact17) {
|
||||
it('should skip tests for next-deploy and react 17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
it('should be able to opt-out 3rd party packages being bundled in server components', async () => {
|
||||
await fetchViaHTTP(next.url, '/react-server/optout').then(
|
||||
async (response) => {
|
||||
},
|
||||
installCommand: 'yarn',
|
||||
startCommand: (global as any).isNextDev ? 'yarn dev' : 'yarn start',
|
||||
buildCommand: 'yarn build',
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
it('should be able to opt-out 3rd party packages being bundled in server components', async () => {
|
||||
await next.fetch('/react-server/optout').then(async (response) => {
|
||||
const result = await resolveStreamResponse(response)
|
||||
expect(result).toContain('Server: index.default')
|
||||
expect(result).toContain('Server subpath: subpath.default')
|
||||
expect(result).toContain('Client: index.default')
|
||||
expect(result).toContain('Client subpath: subpath.default')
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle external async module libraries correctly', async () => {
|
||||
const clientHtml = await renderViaHTTP(next.url, '/external-imports/client')
|
||||
const serverHtml = await renderViaHTTP(next.url, '/external-imports/server')
|
||||
const sharedHtml = await renderViaHTTP(next.url, '/shared-esm-dep')
|
||||
|
||||
const browser = await webdriver(next.url, '/external-imports/client')
|
||||
const browserClientText = await browser.elementByCss('#content').text()
|
||||
|
||||
function containClientContent(content) {
|
||||
expect(content).toContain('module type:esm-export')
|
||||
expect(content).toContain('export named:named')
|
||||
expect(content).toContain('export value:123')
|
||||
expect(content).toContain('export array:4,5,6')
|
||||
expect(content).toContain('export object:{x:1}')
|
||||
expect(content).toContain('swr-state')
|
||||
}
|
||||
|
||||
containClientContent(clientHtml)
|
||||
containClientContent(browserClientText)
|
||||
|
||||
// support esm module imports on server side, and indirect imports from shared components
|
||||
expect(serverHtml).toContain('pure-esm-module')
|
||||
expect(sharedHtml).toContain(
|
||||
'node_modules instance from client module pure-esm-module'
|
||||
)
|
||||
})
|
||||
|
||||
it('should transpile specific external packages with the `transpilePackages` option', async () => {
|
||||
const clientHtml = await renderViaHTTP(next.url, '/external-imports/client')
|
||||
expect(clientHtml).toContain('transpilePackages:5')
|
||||
})
|
||||
|
||||
it('should resolve the subset react in server components based on the react-server condition', async () => {
|
||||
await fetchViaHTTP(next.url, '/react-server').then(async (response) => {
|
||||
const result = await resolveStreamResponse(response)
|
||||
expect(result).toContain('Server: <!-- -->subset')
|
||||
expect(result).toContain('Client: <!-- -->full')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should resolve 3rd party package exports based on the react-server condition', async () => {
|
||||
await fetchViaHTTP(next.url, '/react-server/3rd-party-package').then(
|
||||
async (response) => {
|
||||
it('should handle external async module libraries correctly', async () => {
|
||||
const clientHtml = await next.render('/external-imports/client')
|
||||
const serverHtml = await next.render('/external-imports/server')
|
||||
const sharedHtml = await next.render('/shared-esm-dep')
|
||||
|
||||
const browser = await next.browser('/external-imports/client')
|
||||
const browserClientText = await browser.elementByCss('#content').text()
|
||||
|
||||
function containClientContent(content) {
|
||||
expect(content).toContain('module type:esm-export')
|
||||
expect(content).toContain('export named:named')
|
||||
expect(content).toContain('export value:123')
|
||||
expect(content).toContain('export array:4,5,6')
|
||||
expect(content).toContain('export object:{x:1}')
|
||||
expect(content).toContain('swr-state')
|
||||
}
|
||||
|
||||
containClientContent(clientHtml)
|
||||
containClientContent(browserClientText)
|
||||
|
||||
// support esm module imports on server side, and indirect imports from shared components
|
||||
expect(serverHtml).toContain('pure-esm-module')
|
||||
expect(sharedHtml).toContain(
|
||||
'node_modules instance from client module pure-esm-module'
|
||||
)
|
||||
})
|
||||
|
||||
it('should transpile specific external packages with the `transpilePackages` option', async () => {
|
||||
const clientHtml = await next.render('/external-imports/client')
|
||||
expect(clientHtml).toContain('transpilePackages:5')
|
||||
})
|
||||
|
||||
it('should resolve the subset react in server components based on the react-server condition', async () => {
|
||||
await next.fetch('/react-server').then(async (response) => {
|
||||
const result = await resolveStreamResponse(response)
|
||||
expect(result).toContain('Server: <!-- -->subset')
|
||||
expect(result).toContain('Client: <!-- -->full')
|
||||
})
|
||||
})
|
||||
|
||||
it('should resolve 3rd party package exports based on the react-server condition', async () => {
|
||||
await next
|
||||
.fetch('/react-server/3rd-party-package')
|
||||
.then(async (response) => {
|
||||
const result = await resolveStreamResponse(response)
|
||||
|
||||
// Package should be resolved based on the react-server condition,
|
||||
// as well as package's internal & external dependencies.
|
||||
expect(result).toContain(
|
||||
'Server: index.react-server:react.subset:dep.server'
|
||||
)
|
||||
expect(result).toContain(
|
||||
'Client: index.default:react.full:dep.default'
|
||||
)
|
||||
|
||||
// Subpath exports should be resolved based on the condition too.
|
||||
expect(result).toContain('Server subpath: subpath.react-server')
|
||||
expect(result).toContain('Client subpath: subpath.default')
|
||||
})
|
||||
})
|
||||
|
||||
it('should correctly collect global css imports and mark them as side effects', async () => {
|
||||
await next.fetch('/css/a').then(async (response) => {
|
||||
const result = await resolveStreamResponse(response)
|
||||
|
||||
// Package should be resolved based on the react-server condition,
|
||||
// as well as package's internal & external dependencies.
|
||||
expect(result).toContain(
|
||||
'Server: index.react-server:react.subset:dep.server'
|
||||
// It should include the global CSS import
|
||||
expect(result).toMatch(/\.css/)
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle external css modules', async () => {
|
||||
const browser = await next.browser('/css/modules')
|
||||
|
||||
expect(
|
||||
await browser.eval(
|
||||
`window.getComputedStyle(document.querySelector('h1')).color`
|
||||
)
|
||||
expect(result).toContain('Client: index.default:react.full:dep.default')
|
||||
|
||||
// Subpath exports should be resolved based on the condition too.
|
||||
expect(result).toContain('Server subpath: subpath.react-server')
|
||||
expect(result).toContain('Client subpath: subpath.default')
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should correctly collect global css imports and mark them as side effects', async () => {
|
||||
await fetchViaHTTP(next.url, '/css/a').then(async (response) => {
|
||||
const result = await resolveStreamResponse(response)
|
||||
|
||||
// It should include the global CSS import
|
||||
expect(result).toMatch(/\.css/)
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle external css modules', async () => {
|
||||
const browser = await webdriver(next.url, '/css/modules')
|
||||
|
||||
expect(
|
||||
await browser.eval(
|
||||
`window.getComputedStyle(document.querySelector('h1')).color`
|
||||
)
|
||||
).toBe('rgb(255, 0, 0)')
|
||||
})
|
||||
|
||||
it('should use the same export type for packages in both ssr and client', async () => {
|
||||
const browser = await webdriver(next.url, '/client-dep')
|
||||
expect(await browser.eval(`window.document.body.innerText`)).toBe('hello')
|
||||
})
|
||||
|
||||
it('should handle external css modules in pages', async () => {
|
||||
const browser = await webdriver(next.url, '/test-pages')
|
||||
|
||||
expect(
|
||||
await browser.eval(
|
||||
`window.getComputedStyle(document.querySelector('h1')).color`
|
||||
)
|
||||
).toBe('rgb(255, 0, 0)')
|
||||
})
|
||||
|
||||
it('should handle external @next/font', async () => {
|
||||
const browser = await webdriver(next.url, '/font')
|
||||
|
||||
expect(
|
||||
await browser.eval(
|
||||
`window.getComputedStyle(document.querySelector('p')).fontFamily`
|
||||
)
|
||||
).toMatch(/^__myFont_.{6}, __myFont_Fallback_.{6}$/)
|
||||
})
|
||||
|
||||
describe('react in external esm packages', () => {
|
||||
it('should use the same react in client app', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/esm/client')
|
||||
|
||||
const v1 = html.match(/App React Version: ([^<]+)</)[1]
|
||||
const v2 = html.match(/External React Version: ([^<]+)</)[1]
|
||||
expect(v1).toBe(v2)
|
||||
).toBe('rgb(255, 0, 0)')
|
||||
})
|
||||
|
||||
it('should use the same react in server app', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/esm/server')
|
||||
|
||||
const v1 = html.match(/App React Version: ([^<]+)</)[1]
|
||||
const v2 = html.match(/External React Version: ([^<]+)</)[1]
|
||||
expect(v1).toBe(v2)
|
||||
it('should use the same export type for packages in both ssr and client', async () => {
|
||||
const browser = await next.browser('/client-dep')
|
||||
expect(await browser.eval(`window.document.body.innerText`)).toBe('hello')
|
||||
})
|
||||
|
||||
it('should use the same react in pages', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/test-pages-esm')
|
||||
it('should handle external css modules in pages', async () => {
|
||||
const browser = await next.browser('/test-pages')
|
||||
|
||||
const v1 = html.match(/App React Version: ([^<]+)</)[1]
|
||||
const v2 = html.match(/External React Version: ([^<]+)</)[1]
|
||||
expect(v1).toBe(v2)
|
||||
expect(
|
||||
await browser.eval(
|
||||
`window.getComputedStyle(document.querySelector('h1')).color`
|
||||
)
|
||||
).toBe('rgb(255, 0, 0)')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle external @next/font', async () => {
|
||||
const browser = await next.browser('/font')
|
||||
|
||||
expect(
|
||||
await browser.eval(
|
||||
`window.getComputedStyle(document.querySelector('p')).fontFamily`
|
||||
)
|
||||
).toMatch(/^__myFont_.{6}, __myFont_Fallback_.{6}$/)
|
||||
})
|
||||
|
||||
describe('react in external esm packages', () => {
|
||||
it('should use the same react in client app', async () => {
|
||||
const html = await next.render('/esm/client')
|
||||
|
||||
const v1 = html.match(/App React Version: ([^<]+)</)[1]
|
||||
const v2 = html.match(/External React Version: ([^<]+)</)[1]
|
||||
expect(v1).toBe(v2)
|
||||
})
|
||||
|
||||
it('should use the same react in server app', async () => {
|
||||
const html = await next.render('/esm/server')
|
||||
|
||||
const v1 = html.match(/App React Version: ([^<]+)</)[1]
|
||||
const v2 = html.match(/External React Version: ([^<]+)</)[1]
|
||||
expect(v1).toBe(v2)
|
||||
})
|
||||
|
||||
it('should use the same react in pages', async () => {
|
||||
const html = await next.render('/test-pages-esm')
|
||||
|
||||
const v1 = html.match(/App React Version: ([^<]+)</)[1]
|
||||
const v2 = html.match(/External React Version: ([^<]+)</)[1]
|
||||
expect(v1).toBe(v2)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,158 +1,150 @@
|
|||
/* eslint-env jest */
|
||||
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils'
|
||||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { createNextDescribe, FileRef } from 'e2e-utils'
|
||||
import cheerio from 'cheerio'
|
||||
import path from 'path'
|
||||
|
||||
describe('app-dir with middleware', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
afterAll(() => next.destroy())
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'app-middleware')),
|
||||
})
|
||||
})
|
||||
|
||||
describe.each([
|
||||
{
|
||||
title: 'Serverless Functions',
|
||||
path: '/api/dump-headers-serverless',
|
||||
toJson: (res: Response) => res.json(),
|
||||
},
|
||||
{
|
||||
title: 'Edge Functions',
|
||||
path: '/api/dump-headers-edge',
|
||||
toJson: (res: Response) => res.json(),
|
||||
},
|
||||
{
|
||||
title: 'next/headers',
|
||||
path: '/headers',
|
||||
toJson: async (res: Response) => {
|
||||
const $ = cheerio.load(await res.text())
|
||||
return JSON.parse($('#headers').text())
|
||||
createNextDescribe(
|
||||
'app-dir with middleware',
|
||||
{
|
||||
files: path.join(__dirname, 'app-middleware'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
describe.each([
|
||||
{
|
||||
title: 'Serverless Functions',
|
||||
path: '/api/dump-headers-serverless',
|
||||
toJson: (res: Response) => res.json(),
|
||||
},
|
||||
},
|
||||
])('Mutate request headers for $title', ({ path, toJson }) => {
|
||||
it(`Adds new headers`, async () => {
|
||||
const res = await fetchViaHTTP(next.url, path, null, {
|
||||
headers: {
|
||||
{
|
||||
title: 'Edge Functions',
|
||||
path: '/api/dump-headers-edge',
|
||||
toJson: (res: Response) => res.json(),
|
||||
},
|
||||
{
|
||||
title: 'next/headers',
|
||||
path: '/headers',
|
||||
toJson: async (res: Response) => {
|
||||
const $ = cheerio.load(await res.text())
|
||||
return JSON.parse($('#headers').text())
|
||||
},
|
||||
},
|
||||
])('Mutate request headers for $title', ({ path, toJson }) => {
|
||||
it(`Adds new headers`, async () => {
|
||||
const res = await next.fetch(path, null, {
|
||||
headers: {
|
||||
'x-from-client': 'hello-from-client',
|
||||
},
|
||||
})
|
||||
expect(await toJson(res)).toMatchObject({
|
||||
'x-from-client': 'hello-from-client',
|
||||
},
|
||||
'x-from-middleware': 'hello-from-middleware',
|
||||
})
|
||||
})
|
||||
expect(await toJson(res)).toMatchObject({
|
||||
'x-from-client': 'hello-from-client',
|
||||
'x-from-middleware': 'hello-from-middleware',
|
||||
})
|
||||
})
|
||||
|
||||
it(`Deletes headers`, async () => {
|
||||
const res = await fetchViaHTTP(
|
||||
next.url,
|
||||
path,
|
||||
{
|
||||
'remove-headers': 'x-from-client1,x-from-client2',
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'x-from-client1': 'hello-from-client',
|
||||
'X-From-Client2': 'hello-from-client',
|
||||
it(`Deletes headers`, async () => {
|
||||
const res = await next.fetch(
|
||||
path,
|
||||
{
|
||||
'remove-headers': 'x-from-client1,x-from-client2',
|
||||
},
|
||||
}
|
||||
)
|
||||
{
|
||||
headers: {
|
||||
'x-from-client1': 'hello-from-client',
|
||||
'X-From-Client2': 'hello-from-client',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const json = await toJson(res)
|
||||
expect(json).not.toHaveProperty('x-from-client1')
|
||||
expect(json).not.toHaveProperty('X-From-Client2')
|
||||
expect(json).toMatchObject({
|
||||
'x-from-middleware': 'hello-from-middleware',
|
||||
const json = await toJson(res)
|
||||
expect(json).not.toHaveProperty('x-from-client1')
|
||||
expect(json).not.toHaveProperty('X-From-Client2')
|
||||
expect(json).toMatchObject({
|
||||
'x-from-middleware': 'hello-from-middleware',
|
||||
})
|
||||
|
||||
// Should not be included in response headers.
|
||||
expect(res.headers.get('x-middleware-override-headers')).toBeNull()
|
||||
expect(
|
||||
res.headers.get('x-middleware-request-x-from-middleware')
|
||||
).toBeNull()
|
||||
expect(
|
||||
res.headers.get('x-middleware-request-x-from-client1')
|
||||
).toBeNull()
|
||||
expect(
|
||||
res.headers.get('x-middleware-request-x-from-client2')
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
// Should not be included in response headers.
|
||||
expect(res.headers.get('x-middleware-override-headers')).toBeNull()
|
||||
expect(
|
||||
res.headers.get('x-middleware-request-x-from-middleware')
|
||||
).toBeNull()
|
||||
expect(res.headers.get('x-middleware-request-x-from-client1')).toBeNull()
|
||||
expect(res.headers.get('x-middleware-request-x-from-client2')).toBeNull()
|
||||
})
|
||||
|
||||
it(`Updates headers`, async () => {
|
||||
const res = await fetchViaHTTP(
|
||||
next.url,
|
||||
path,
|
||||
{
|
||||
'update-headers':
|
||||
'x-from-client1=new-value1,x-from-client2=new-value2',
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'x-from-client1': 'old-value1',
|
||||
'X-From-Client2': 'old-value2',
|
||||
'x-from-client3': 'old-value3',
|
||||
it(`Updates headers`, async () => {
|
||||
const res = await next.fetch(
|
||||
path,
|
||||
{
|
||||
'update-headers':
|
||||
'x-from-client1=new-value1,x-from-client2=new-value2',
|
||||
},
|
||||
}
|
||||
)
|
||||
expect(await toJson(res)).toMatchObject({
|
||||
'x-from-client1': 'new-value1',
|
||||
'x-from-client2': 'new-value2',
|
||||
'x-from-client3': 'old-value3',
|
||||
'x-from-middleware': 'hello-from-middleware',
|
||||
{
|
||||
headers: {
|
||||
'x-from-client1': 'old-value1',
|
||||
'X-From-Client2': 'old-value2',
|
||||
'x-from-client3': 'old-value3',
|
||||
},
|
||||
}
|
||||
)
|
||||
expect(await toJson(res)).toMatchObject({
|
||||
'x-from-client1': 'new-value1',
|
||||
'x-from-client2': 'new-value2',
|
||||
'x-from-client3': 'old-value3',
|
||||
'x-from-middleware': 'hello-from-middleware',
|
||||
})
|
||||
|
||||
// Should not be included in response headers.
|
||||
expect(res.headers.get('x-middleware-override-headers')).toBeNull()
|
||||
expect(
|
||||
res.headers.get('x-middleware-request-x-from-middleware')
|
||||
).toBeNull()
|
||||
expect(
|
||||
res.headers.get('x-middleware-request-x-from-client1')
|
||||
).toBeNull()
|
||||
expect(
|
||||
res.headers.get('x-middleware-request-x-from-client2')
|
||||
).toBeNull()
|
||||
expect(
|
||||
res.headers.get('x-middleware-request-x-from-client3')
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
// Should not be included in response headers.
|
||||
expect(res.headers.get('x-middleware-override-headers')).toBeNull()
|
||||
expect(
|
||||
res.headers.get('x-middleware-request-x-from-middleware')
|
||||
).toBeNull()
|
||||
expect(res.headers.get('x-middleware-request-x-from-client1')).toBeNull()
|
||||
expect(res.headers.get('x-middleware-request-x-from-client2')).toBeNull()
|
||||
expect(res.headers.get('x-middleware-request-x-from-client3')).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('app dir middleware without pages dir', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
)
|
||||
|
||||
let next: NextInstance
|
||||
createNextDescribe(
|
||||
'app dir middleware without pages dir',
|
||||
{
|
||||
files: {
|
||||
app: new FileRef(path.join(__dirname, 'app-middleware/app')),
|
||||
'next.config.js': new FileRef(
|
||||
path.join(__dirname, 'app-middleware/next.config.js')
|
||||
),
|
||||
'middleware.js': `
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
afterAll(() => next.destroy())
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: {
|
||||
app: new FileRef(path.join(__dirname, 'app-middleware/app')),
|
||||
'next.config.js': new FileRef(
|
||||
path.join(__dirname, 'app-middleware/next.config.js')
|
||||
),
|
||||
'middleware.js': `
|
||||
import { NextResponse } from 'next/server'
|
||||
export async function middleware(request) {
|
||||
return new NextResponse('redirected')
|
||||
}
|
||||
|
||||
export async function middleware(request) {
|
||||
return new NextResponse('redirected')
|
||||
}
|
||||
export const config = {
|
||||
matcher: '/headers'
|
||||
}
|
||||
`,
|
||||
},
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
// eslint-disable-next-line jest/no-identical-title
|
||||
it('Updates headers', async () => {
|
||||
const html = await next.render('/headers')
|
||||
|
||||
export const config = {
|
||||
matcher: '/headers'
|
||||
}
|
||||
`,
|
||||
},
|
||||
expect(html).toContain('redirected')
|
||||
})
|
||||
})
|
||||
|
||||
it(`Updates headers`, async () => {
|
||||
const html = await renderViaHTTP(next.url, '/headers')
|
||||
|
||||
expect(html).toContain('redirected')
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,62 +1,42 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import path from 'path'
|
||||
import cheerio from 'cheerio'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
describe('app-dir assetPrefix handling', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'asset-prefix')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
skipStart: true,
|
||||
createNextDescribe(
|
||||
'app-dir assetPrefix handling',
|
||||
{
|
||||
files: path.join(__dirname, 'asset-prefix'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
it('should redirect route when requesting it directly', async () => {
|
||||
const res = await next.fetch(
|
||||
'/a/',
|
||||
{},
|
||||
{
|
||||
redirect: 'manual',
|
||||
}
|
||||
)
|
||||
expect(res.status).toBe(308)
|
||||
expect(res.headers.get('location')).toBe(next.url + '/a')
|
||||
})
|
||||
|
||||
await next.start()
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
it('should render link', async () => {
|
||||
const $ = await next.render$('/')
|
||||
expect($('#to-a-trailing-slash').attr('href')).toBe('/a')
|
||||
})
|
||||
|
||||
it('should redirect route when requesting it directly', async () => {
|
||||
const res = await fetchViaHTTP(
|
||||
next.url,
|
||||
'/a/',
|
||||
{},
|
||||
{
|
||||
redirect: 'manual',
|
||||
}
|
||||
)
|
||||
expect(res.status).toBe(308)
|
||||
expect(res.headers.get('location')).toBe(next.url + '/a')
|
||||
})
|
||||
it('should redirect route when requesting it directly by browser', async () => {
|
||||
const browser = await next.browser('/a')
|
||||
expect(await browser.waitForElementByCss('#a-page').text()).toBe('A page')
|
||||
})
|
||||
|
||||
it('should render link', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/')
|
||||
const $ = cheerio.load(html)
|
||||
expect($('#to-a-trailing-slash').attr('href')).toBe('/a')
|
||||
})
|
||||
|
||||
it('should redirect route when requesting it directly by browser', async () => {
|
||||
const browser = await webdriver(next.url, '/a')
|
||||
expect(await browser.waitForElementByCss('#a-page').text()).toBe('A page')
|
||||
})
|
||||
|
||||
it('should redirect route when clicking link', async () => {
|
||||
const browser = await webdriver(next.url, '/')
|
||||
await browser
|
||||
.elementByCss('#to-a-trailing-slash')
|
||||
.click()
|
||||
.waitForElementByCss('#a-page')
|
||||
expect(await browser.waitForElementByCss('#a-page').text()).toBe('A page')
|
||||
})
|
||||
})
|
||||
it('should redirect route when clicking link', async () => {
|
||||
const browser = await next.browser('/')
|
||||
await browser
|
||||
.elementByCss('#to-a-trailing-slash')
|
||||
.click()
|
||||
.waitForElementByCss('#a-page')
|
||||
expect(await browser.waitForElementByCss('#a-page').text()).toBe('A page')
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,29 +1,18 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import webdriver from 'next-webdriver'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import path from 'path'
|
||||
|
||||
describe('async-component-preload', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'async-component-preload')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
createNextDescribe(
|
||||
'async-component-preload',
|
||||
{
|
||||
files: path.join(__dirname, 'async-component-preload'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
it('should handle redirect in an async page', async () => {
|
||||
const browser = await next.browser('/')
|
||||
expect(await browser.waitForElementByCss('#success').text()).toBe(
|
||||
'Success'
|
||||
)
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
it('should handle redirect in an async page', async () => {
|
||||
const browser = await webdriver(next.url, '/')
|
||||
expect(await browser.waitForElementByCss('#success').text()).toBe('Success')
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,42 +1,30 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import path from 'path'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
// TODO-APP: fix test as it's failing randomly
|
||||
describe.skip('app-dir back button download bug', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
createNextDescribe(
|
||||
'app-dir back button download bug',
|
||||
{
|
||||
files: path.join(__dirname, 'back-button-download-bug'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
it('should redirect route when clicking link', async () => {
|
||||
const browser = await next.browser('/')
|
||||
const text = await browser
|
||||
.elementByCss('#to-post-1')
|
||||
.click()
|
||||
.waitForElementByCss('#post-page')
|
||||
.text()
|
||||
expect(text).toBe('This is the post page')
|
||||
|
||||
let next: NextInstance
|
||||
await browser.back()
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'back-button-download-bug')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
skipStart: true,
|
||||
})
|
||||
|
||||
await next.start()
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
it('should redirect route when clicking link', async () => {
|
||||
const browser = await webdriver(next.url, '/')
|
||||
const text = await browser
|
||||
.elementByCss('#to-post-1')
|
||||
.click()
|
||||
.waitForElementByCss('#post-page')
|
||||
.text()
|
||||
expect(text).toBe('This is the post page')
|
||||
|
||||
await browser.back()
|
||||
|
||||
expect(await browser.waitForElementByCss('#home-page').text()).toBe('Home!')
|
||||
})
|
||||
expect(await browser.waitForElementByCss('#home-page').text()).toBe(
|
||||
'Home!'
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import path from 'path'
|
||||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import webdriver from 'next-webdriver'
|
||||
import { check } from 'next-test-utils'
|
||||
|
||||
describe('app-dir create root layout', () => {
|
||||
|
@ -12,10 +11,6 @@ describe('app-dir create root layout', () => {
|
|||
return
|
||||
}
|
||||
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
let next: NextInstance
|
||||
|
||||
if (isDev) {
|
||||
|
@ -39,7 +34,7 @@ describe('app-dir create root layout', () => {
|
|||
|
||||
it('create root layout', async () => {
|
||||
const outputIndex = next.cliOutput.length
|
||||
const browser = await webdriver(next.url, '/route')
|
||||
const browser = await next.browser('/route')
|
||||
|
||||
expect(await browser.elementById('page-text').text()).toBe(
|
||||
'Hello world!'
|
||||
|
@ -101,7 +96,7 @@ describe('app-dir create root layout', () => {
|
|||
|
||||
it('create root layout', async () => {
|
||||
const outputIndex = next.cliOutput.length
|
||||
const browser = await webdriver(next.url, '/')
|
||||
const browser = await next.browser('/')
|
||||
|
||||
expect(await browser.elementById('page-text').text()).toBe(
|
||||
'Hello world'
|
||||
|
@ -168,7 +163,7 @@ describe('app-dir create root layout', () => {
|
|||
|
||||
it('create root layout', async () => {
|
||||
const outputIndex = next.cliOutput.length
|
||||
const browser = await webdriver(next.url, '/route/second/inner')
|
||||
const browser = await next.browser('/route/second/inner')
|
||||
|
||||
expect(await browser.elementById('page-text').text()).toBe(
|
||||
'Hello world'
|
||||
|
@ -236,7 +231,7 @@ describe('app-dir create root layout', () => {
|
|||
|
||||
it('create root layout', async () => {
|
||||
const outputIndex = next.cliOutput.length
|
||||
const browser = await webdriver(next.url, '/')
|
||||
const browser = await next.browser('/')
|
||||
|
||||
expect(await browser.elementById('page-text').text()).toBe(
|
||||
'Hello world!'
|
||||
|
|
|
@ -1,75 +1,63 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import { getRedboxDescription, hasRedbox } from 'next-test-utils'
|
||||
import path from 'path'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
describe('dynamic-href', () => {
|
||||
const isDev = (global as any).isNextDev
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
createNextDescribe(
|
||||
'dynamic-href',
|
||||
{
|
||||
files: path.join(__dirname, 'dynamic-href'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ isNextDev: isDev, next }) => {
|
||||
if (isDev) {
|
||||
it('should error when using dynamic href.pathname in app dir', async () => {
|
||||
const browser = await next.browser('/object')
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'dynamic-href')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
if (isDev) {
|
||||
it('should error when using dynamic href.pathname in app dir', async () => {
|
||||
const browser = await webdriver(next.url, '/object')
|
||||
|
||||
// Error should show up
|
||||
expect(await hasRedbox(browser, true)).toBeTrue()
|
||||
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
||||
`"Error: Dynamic href \`/object/[slug]\` found in <Link> while using the \`/app\` router, this is not supported. Read more: https://nextjs.org/docs/messages/app-dir-dynamic-href"`
|
||||
)
|
||||
|
||||
// Fix error
|
||||
const pageContent = await next.readFile('app/object/page.js')
|
||||
await next.patchFile(
|
||||
'app/object/page.js',
|
||||
pageContent.replace(
|
||||
"pathname: '/object/[slug]'",
|
||||
"pathname: '/object/slug'"
|
||||
// Error should show up
|
||||
expect(await hasRedbox(browser, true)).toBeTrue()
|
||||
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
||||
`"Error: Dynamic href \`/object/[slug]\` found in <Link> while using the \`/app\` router, this is not supported. Read more: https://nextjs.org/docs/messages/app-dir-dynamic-href"`
|
||||
)
|
||||
)
|
||||
expect(await browser.waitForElementByCss('#link').text()).toBe('to slug')
|
||||
|
||||
// Navigate to new page
|
||||
await browser.elementByCss('#link').click()
|
||||
expect(await browser.waitForElementByCss('#pathname').text()).toBe(
|
||||
'/object/slug'
|
||||
)
|
||||
expect(await browser.elementByCss('#slug').text()).toBe('1')
|
||||
})
|
||||
// Fix error
|
||||
const pageContent = await next.readFile('app/object/page.js')
|
||||
await next.patchFile(
|
||||
'app/object/page.js',
|
||||
pageContent.replace(
|
||||
"pathname: '/object/[slug]'",
|
||||
"pathname: '/object/slug'"
|
||||
)
|
||||
)
|
||||
expect(await browser.waitForElementByCss('#link').text()).toBe(
|
||||
'to slug'
|
||||
)
|
||||
|
||||
it('should error when using dynamic href in app dir', async () => {
|
||||
const browser = await webdriver(next.url, '/string')
|
||||
// Navigate to new page
|
||||
await browser.elementByCss('#link').click()
|
||||
expect(await browser.waitForElementByCss('#pathname').text()).toBe(
|
||||
'/object/slug'
|
||||
)
|
||||
expect(await browser.elementByCss('#slug').text()).toBe('1')
|
||||
})
|
||||
|
||||
// Error should show up
|
||||
expect(await hasRedbox(browser, true)).toBeTrue()
|
||||
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
||||
`"Error: Dynamic href \`/object/[slug]\` found in <Link> while using the \`/app\` router, this is not supported. Read more: https://nextjs.org/docs/messages/app-dir-dynamic-href"`
|
||||
)
|
||||
})
|
||||
} else {
|
||||
it('should not error on /object in prod', async () => {
|
||||
const browser = await webdriver(next.url, '/object')
|
||||
expect(await browser.elementByCss('#link').text()).toBe('to slug')
|
||||
})
|
||||
it('should not error on /string in prod', async () => {
|
||||
const browser = await webdriver(next.url, '/string')
|
||||
expect(await browser.elementByCss('#link').text()).toBe('to slug')
|
||||
})
|
||||
it('should error when using dynamic href in app dir', async () => {
|
||||
const browser = await next.browser('/string')
|
||||
|
||||
// Error should show up
|
||||
expect(await hasRedbox(browser, true)).toBeTrue()
|
||||
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
||||
`"Error: Dynamic href \`/object/[slug]\` found in <Link> while using the \`/app\` router, this is not supported. Read more: https://nextjs.org/docs/messages/app-dir-dynamic-href"`
|
||||
)
|
||||
})
|
||||
} else {
|
||||
it('should not error on /object in prod', async () => {
|
||||
const browser = await next.browser('/object')
|
||||
expect(await browser.elementByCss('#link').text()).toBe('to slug')
|
||||
})
|
||||
it('should not error on /string in prod', async () => {
|
||||
const browser = await next.browser('/string')
|
||||
expect(await browser.elementByCss('#link').text()).toBe('to slug')
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,35 +1,29 @@
|
|||
import path from 'path'
|
||||
import { getRedboxHeader, hasRedbox } from 'next-test-utils'
|
||||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import webdriver from 'next-webdriver'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
|
||||
describe('app dir - global error', () => {
|
||||
let next: NextInstance
|
||||
const isDev = (global as any).isNextDev
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, './global-error')),
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
it('should trigger error component when an error happens during rendering', async () => {
|
||||
const browser = await webdriver(next.url, '/throw')
|
||||
await browser
|
||||
.waitForElementByCss('#error-trigger-button')
|
||||
.elementByCss('#error-trigger-button')
|
||||
.click()
|
||||
|
||||
if (isDev) {
|
||||
expect(await hasRedbox(browser)).toBe(true)
|
||||
expect(await getRedboxHeader(browser)).toMatch(/Error: Client error/)
|
||||
} else {
|
||||
createNextDescribe(
|
||||
'app dir - global error',
|
||||
{
|
||||
files: path.join(__dirname, './global-error'),
|
||||
},
|
||||
({ next, isNextDev }) => {
|
||||
it('should trigger error component when an error happens during rendering', async () => {
|
||||
const browser = await next.browser('/throw')
|
||||
await browser
|
||||
expect(await browser.elementByCss('#error').text()).toBe(
|
||||
'Error message: Client error'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
.waitForElementByCss('#error-trigger-button')
|
||||
.elementByCss('#error-trigger-button')
|
||||
.click()
|
||||
|
||||
if (isNextDev) {
|
||||
expect(await hasRedbox(browser)).toBe(true)
|
||||
expect(await getRedboxHeader(browser)).toMatch(/Error: Client error/)
|
||||
} else {
|
||||
await browser
|
||||
expect(await browser.elementByCss('#error').text()).toBe(
|
||||
'Error message: Client error'
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,38 +1,24 @@
|
|||
import path from 'path'
|
||||
import cheerio from 'cheerio'
|
||||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { renderViaHTTP } from 'next-test-utils'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
|
||||
describe('app dir imports', () => {
|
||||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
|
||||
it('should skip for react v17', () => {})
|
||||
return
|
||||
}
|
||||
let next: NextInstance
|
||||
|
||||
function runTests() {
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'import')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
createNextDescribe(
|
||||
'app dir imports',
|
||||
{
|
||||
files: path.join(__dirname, 'import'),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
},
|
||||
({ next }) => {
|
||||
;['js', 'jsx', 'ts', 'tsx'].forEach((ext) => {
|
||||
it(`we can import all components from .${ext}`, async () => {
|
||||
const html = await renderViaHTTP(next.url, `/${ext}`)
|
||||
const $ = cheerio.load(html)
|
||||
const $ = await next.render$(`/${ext}`)
|
||||
expect($('#js').text()).toBe('CompJs')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
runTests()
|
||||
})
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,99 +1,69 @@
|
|||
import path from 'path'
|
||||
import { renderViaHTTP } from 'next-test-utils'
|
||||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import cheerio from 'cheerio'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
|
||||
describe('app dir - layout params', () => {
|
||||
let next: NextInstance
|
||||
createNextDescribe(
|
||||
'app dir - layout params',
|
||||
{
|
||||
files: path.join(__dirname, './layout-params'),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
},
|
||||
({ next }) => {
|
||||
describe('basic params', () => {
|
||||
it('check layout without params get no params', async () => {
|
||||
const $ = await next.render$('/base/something/another')
|
||||
const ids = ['#root-layout', '#lvl1-layout']
|
||||
ids.forEach((divId) => {
|
||||
const params = $(`${divId} > div`)
|
||||
expect(params.length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, './layout-params')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
typescript: 'latest',
|
||||
'@types/react': 'latest',
|
||||
'@types/node': 'latest',
|
||||
},
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
it("check layout renders just it's params", async () => {
|
||||
const $ = await next.render$('/base/something/another')
|
||||
|
||||
const isReact17 = process.env.NEXT_TEST_REACT_VERSION === '^17'
|
||||
expect($('#lvl2-layout > div').length).toBe(1)
|
||||
expect($('#lvl2-param1').text()).toBe('"something"')
|
||||
})
|
||||
|
||||
if (isReact17) {
|
||||
it('should skip tests for react 17', () => {})
|
||||
return
|
||||
}
|
||||
it('check topmost layout renders all params', async () => {
|
||||
const $ = await next.render$('/base/something/another')
|
||||
|
||||
describe('basic params', () => {
|
||||
it('check layout without params get no params', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/base/something/another')
|
||||
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const ids = ['#root-layout', '#lvl1-layout']
|
||||
ids.forEach((divId) => {
|
||||
const params = $(`${divId} > div`)
|
||||
expect(params.length).toBe(0)
|
||||
expect($('#lvl3-layout > div').length).toBe(2)
|
||||
expect($('#lvl3-param1').text()).toBe('"something"')
|
||||
expect($('#lvl3-param2').text()).toBe('"another"')
|
||||
})
|
||||
})
|
||||
|
||||
it("check layout renders just it's params", async () => {
|
||||
const html = await renderViaHTTP(next.url, '/base/something/another')
|
||||
describe('catchall params', () => {
|
||||
it('should give catchall params just to last layout', async () => {
|
||||
const $ = await next.render$('/catchall/something/another')
|
||||
|
||||
const $ = cheerio.load(html)
|
||||
expect($(`#root-layout > div`).length).toBe(0)
|
||||
expect($('#lvl2-layout > div').length).toBe(1)
|
||||
expect($('#lvl2-params').text()).toBe('["something","another"]')
|
||||
})
|
||||
|
||||
expect($('#lvl2-layout > div').length).toBe(1)
|
||||
expect($('#lvl2-param1').text()).toBe('"something"')
|
||||
it('should give optional catchall params just to last layout', async () => {
|
||||
const $ = await next.render$('/optional-catchall/something/another')
|
||||
|
||||
expect($(`#root-layout > div`).length).toBe(0)
|
||||
|
||||
expect($('#lvl2-layout > div').length).toBe(1)
|
||||
expect($('#lvl2-params').text()).toBe('["something","another"]')
|
||||
})
|
||||
|
||||
it("should give empty optional catchall params won't give params to any layout", async () => {
|
||||
const $ = await next.render$('/optional-catchall')
|
||||
|
||||
expect($(`#root-layout > div`).length).toBe(0)
|
||||
expect($('#lvl2-layout > div').length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('check topmost layout renders all params', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/base/something/another')
|
||||
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
expect($('#lvl3-layout > div').length).toBe(2)
|
||||
expect($('#lvl3-param1').text()).toBe('"something"')
|
||||
expect($('#lvl3-param2').text()).toBe('"another"')
|
||||
})
|
||||
})
|
||||
|
||||
describe('catchall params', () => {
|
||||
it('should give catchall params just to last layout', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/catchall/something/another')
|
||||
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
expect($(`#root-layout > div`).length).toBe(0)
|
||||
|
||||
expect($('#lvl2-layout > div').length).toBe(1)
|
||||
expect($('#lvl2-params').text()).toBe('["something","another"]')
|
||||
})
|
||||
|
||||
it('should give optional catchall params just to last layout', async () => {
|
||||
const html = await renderViaHTTP(
|
||||
next.url,
|
||||
'/optional-catchall/something/another'
|
||||
)
|
||||
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
expect($(`#root-layout > div`).length).toBe(0)
|
||||
|
||||
expect($('#lvl2-layout > div').length).toBe(1)
|
||||
expect($('#lvl2-params').text()).toBe('["something","another"]')
|
||||
})
|
||||
|
||||
it("should give empty optional catchall params won't give params to any layout", async () => {
|
||||
const html = await renderViaHTTP(next.url, '/optional-catchall')
|
||||
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
expect($(`#root-layout > div`).length).toBe(0)
|
||||
expect($('#lvl2-layout > div').length).toBe(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,373 +1,354 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { getRedboxSource, hasRedbox, renderViaHTTP } from 'next-test-utils'
|
||||
import cheerio from 'cheerio'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import { getRedboxSource, hasRedbox } from 'next-test-utils'
|
||||
import path from 'path'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
describe('app dir next-font', () => {
|
||||
const isDev = (global as any).isNextDev
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
createNextDescribe(
|
||||
'app dir next-font',
|
||||
{
|
||||
files: path.join(__dirname, 'next-font'),
|
||||
dependencies: {
|
||||
'@next/font': 'canary',
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next, isNextDev: isDev }) => {
|
||||
describe('import values', () => {
|
||||
it('should have correct values at /', async () => {
|
||||
const $ = await next.render$('/')
|
||||
|
||||
// layout
|
||||
expect(JSON.parse($('#root-layout').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
variable: expect.stringMatching(/^__variable_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font1_.{6}'$/),
|
||||
},
|
||||
})
|
||||
// page
|
||||
expect(JSON.parse($('#root-page').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
variable: expect.stringMatching(/^__variable_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font2_.{6}'$/),
|
||||
},
|
||||
})
|
||||
// Comp
|
||||
expect(JSON.parse($('#root-comp').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font3_.{6}'$/),
|
||||
fontStyle: 'italic',
|
||||
fontWeight: 900,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should have correct values at /client', async () => {
|
||||
const $ = await next.render$('/client')
|
||||
|
||||
// root layout
|
||||
expect(JSON.parse($('#root-layout').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
variable: expect.stringMatching(/^__variable_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font1_.{6}'$/),
|
||||
},
|
||||
})
|
||||
|
||||
// layout
|
||||
expect(JSON.parse($('#client-layout').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font4_.{6}'$/),
|
||||
fontWeight: 100,
|
||||
},
|
||||
})
|
||||
// page
|
||||
expect(JSON.parse($('#client-page').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font5_.{6}'$/),
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
})
|
||||
// Comp
|
||||
expect(JSON.parse($('#client-comp').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font6_.{6}'$/),
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('computed styles', () => {
|
||||
it('should have correct styles at /', async () => {
|
||||
const browser = await next.browser('/')
|
||||
|
||||
// layout
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font1_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
|
||||
// page
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-page")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font2_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-page")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-page")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
|
||||
// Comp
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-comp")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font3_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-comp")).fontWeight'
|
||||
)
|
||||
).toBe('900')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-comp")).fontStyle'
|
||||
)
|
||||
).toBe('italic')
|
||||
})
|
||||
|
||||
it('should have correct styles at /client', async () => {
|
||||
const browser = await next.browser('/client')
|
||||
|
||||
// root layout
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font1_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
|
||||
// layout
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-layout")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font4_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-layout")).fontWeight'
|
||||
)
|
||||
).toBe('100')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-layout")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
|
||||
// page
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-page")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font5_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-page")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-page")).fontStyle'
|
||||
)
|
||||
).toBe('italic')
|
||||
|
||||
// Comp
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-comp")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font6_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-comp")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-comp")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
})
|
||||
})
|
||||
|
||||
if (!isDev) {
|
||||
describe('preload', () => {
|
||||
it('should preload correctly with server components', async () => {
|
||||
const $ = await next.render$('/')
|
||||
|
||||
// Preconnect
|
||||
expect($('link[rel="preconnect"]').length).toBe(0)
|
||||
|
||||
expect($('link[as="font"]').length).toBe(3)
|
||||
expect($('link[as="font"]').get(0).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e9b9dc0d8ba35f48.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
expect($('link[as="font"]').get(1).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/b61859a50be14c53.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
expect($('link[as="font"]').get(2).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/b2104791981359ae.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
})
|
||||
|
||||
it('should preload correctly with client components', async () => {
|
||||
const $ = await next.render$('/client')
|
||||
|
||||
// Preconnect
|
||||
expect($('link[rel="preconnect"]').length).toBe(0)
|
||||
|
||||
expect($('link[as="font"]').length).toBe(3)
|
||||
// From root layout
|
||||
expect($('link[as="font"]').get(0).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e9b9dc0d8ba35f48.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
|
||||
expect($('link[as="font"]').get(1).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e1053f04babc7571.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
expect($('link[as="font"]').get(2).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/feab2c68f2a8e9a4.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
})
|
||||
|
||||
it('should preload correctly with layout using fonts', async () => {
|
||||
const $ = await next.render$('/layout-with-fonts')
|
||||
|
||||
// Preconnect
|
||||
expect($('link[rel="preconnect"]').length).toBe(0)
|
||||
|
||||
expect($('link[as="font"]').length).toBe(2)
|
||||
// From root layout
|
||||
expect($('link[as="font"]').get(0).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e9b9dc0d8ba35f48.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
|
||||
expect($('link[as="font"]').get(1).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/75c5faeeb9c86969.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
})
|
||||
|
||||
it('should preload correctly with page using fonts', async () => {
|
||||
const $ = await next.render$('/page-with-fonts')
|
||||
|
||||
// Preconnect
|
||||
expect($('link[rel="preconnect"]').length).toBe(0)
|
||||
|
||||
expect($('link[as="font"]').length).toBe(2)
|
||||
// From root layout
|
||||
expect($('link[as="font"]').get(0).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e9b9dc0d8ba35f48.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
|
||||
expect($('link[as="font"]').get(1).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/568e4c6d8123c4d6.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (isDev) {
|
||||
describe('Dev errors', () => {
|
||||
it('should recover on font loader error', async () => {
|
||||
const browser = await next.browser('/')
|
||||
const font1Content = await next.readFile('fonts/index.js')
|
||||
|
||||
// Break file
|
||||
await next.patchFile(
|
||||
'fonts/index.js',
|
||||
font1Content.replace('./font1.woff2', './does-not-exist.woff2')
|
||||
)
|
||||
expect(await hasRedbox(browser, true)).toBeTrue()
|
||||
expect(await getRedboxSource(browser)).toInclude(
|
||||
"Can't resolve './does-not-exist.woff2'"
|
||||
)
|
||||
|
||||
// Fix file
|
||||
await next.patchFile('fonts/index.js', font1Content)
|
||||
await browser.waitForElementByCss('#root-page')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'next-font')),
|
||||
dependencies: {
|
||||
'@next/font': 'canary',
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
skipStart: true,
|
||||
})
|
||||
await next.start()
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
describe('import values', () => {
|
||||
it('should have correct values at /', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
// layout
|
||||
expect(JSON.parse($('#root-layout').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
variable: expect.stringMatching(/^__variable_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font1_.{6}'$/),
|
||||
},
|
||||
})
|
||||
// page
|
||||
expect(JSON.parse($('#root-page').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
variable: expect.stringMatching(/^__variable_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font2_.{6}'$/),
|
||||
},
|
||||
})
|
||||
// Comp
|
||||
expect(JSON.parse($('#root-comp').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font3_.{6}'$/),
|
||||
fontStyle: 'italic',
|
||||
fontWeight: 900,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should have correct values at /client', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/client')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
// root layout
|
||||
expect(JSON.parse($('#root-layout').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
variable: expect.stringMatching(/^__variable_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font1_.{6}'$/),
|
||||
},
|
||||
})
|
||||
|
||||
// layout
|
||||
expect(JSON.parse($('#client-layout').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font4_.{6}'$/),
|
||||
fontWeight: 100,
|
||||
},
|
||||
})
|
||||
// page
|
||||
expect(JSON.parse($('#client-page').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font5_.{6}'$/),
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
})
|
||||
// Comp
|
||||
expect(JSON.parse($('#client-comp').text())).toEqual({
|
||||
className: expect.stringMatching(/^__className_.{6}$/),
|
||||
style: {
|
||||
fontFamily: expect.stringMatching(/^'__font6_.{6}'$/),
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('computed styles', () => {
|
||||
it('should have correct styles at /', async () => {
|
||||
const browser = await webdriver(next.url, '/')
|
||||
|
||||
// layout
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font1_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
|
||||
// page
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-page")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font2_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-page")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-page")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
|
||||
// Comp
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-comp")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font3_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-comp")).fontWeight'
|
||||
)
|
||||
).toBe('900')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-comp")).fontStyle'
|
||||
)
|
||||
).toBe('italic')
|
||||
})
|
||||
|
||||
it('should have correct styles at /client', async () => {
|
||||
const browser = await webdriver(next.url, '/client')
|
||||
|
||||
// root layout
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font1_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#root-layout")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
|
||||
// layout
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-layout")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font4_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-layout")).fontWeight'
|
||||
)
|
||||
).toBe('100')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-layout")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
|
||||
// page
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-page")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font5_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-page")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-page")).fontStyle'
|
||||
)
|
||||
).toBe('italic')
|
||||
|
||||
// Comp
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-comp")).fontFamily'
|
||||
)
|
||||
).toMatch(/^__font6_.{6}$/)
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-comp")).fontWeight'
|
||||
)
|
||||
).toBe('400')
|
||||
expect(
|
||||
await browser.eval(
|
||||
'getComputedStyle(document.querySelector("#client-comp")).fontStyle'
|
||||
)
|
||||
).toBe('normal')
|
||||
})
|
||||
})
|
||||
|
||||
if (!isDev) {
|
||||
describe('preload', () => {
|
||||
it('should preload correctly with server components', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
// Preconnect
|
||||
expect($('link[rel="preconnect"]').length).toBe(0)
|
||||
|
||||
expect($('link[as="font"]').length).toBe(3)
|
||||
expect($('link[as="font"]').get(0).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e9b9dc0d8ba35f48.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
expect($('link[as="font"]').get(1).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/b61859a50be14c53.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
expect($('link[as="font"]').get(2).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/b2104791981359ae.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
})
|
||||
|
||||
it('should preload correctly with client components', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/client')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
// Preconnect
|
||||
expect($('link[rel="preconnect"]').length).toBe(0)
|
||||
|
||||
expect($('link[as="font"]').length).toBe(3)
|
||||
// From root layout
|
||||
expect($('link[as="font"]').get(0).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e9b9dc0d8ba35f48.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
|
||||
expect($('link[as="font"]').get(1).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e1053f04babc7571.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
expect($('link[as="font"]').get(2).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/feab2c68f2a8e9a4.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
})
|
||||
|
||||
it('should preload correctly with layout using fonts', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/layout-with-fonts')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
// Preconnect
|
||||
expect($('link[rel="preconnect"]').length).toBe(0)
|
||||
|
||||
expect($('link[as="font"]').length).toBe(2)
|
||||
// From root layout
|
||||
expect($('link[as="font"]').get(0).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e9b9dc0d8ba35f48.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
|
||||
expect($('link[as="font"]').get(1).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/75c5faeeb9c86969.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
})
|
||||
|
||||
it('should preload correctly with page using fonts', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/page-with-fonts')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
// Preconnect
|
||||
expect($('link[rel="preconnect"]').length).toBe(0)
|
||||
|
||||
expect($('link[as="font"]').length).toBe(2)
|
||||
// From root layout
|
||||
expect($('link[as="font"]').get(0).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/e9b9dc0d8ba35f48.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
|
||||
expect($('link[as="font"]').get(1).attribs).toEqual({
|
||||
as: 'font',
|
||||
crossorigin: '',
|
||||
href: '/_next/static/media/568e4c6d8123c4d6.p.woff2',
|
||||
rel: 'preload',
|
||||
type: 'font/woff2',
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (isDev) {
|
||||
describe('Dev errors', () => {
|
||||
it('should recover on font loader error', async () => {
|
||||
const browser = await webdriver(next.url, '/')
|
||||
const font1Content = await next.readFile('fonts/index.js')
|
||||
|
||||
// Break file
|
||||
await next.patchFile(
|
||||
'fonts/index.js',
|
||||
font1Content.replace('./font1.woff2', './does-not-exist.woff2')
|
||||
)
|
||||
expect(await hasRedbox(browser, true)).toBeTrue()
|
||||
expect(await getRedboxSource(browser)).toInclude(
|
||||
"Can't resolve './does-not-exist.woff2'"
|
||||
)
|
||||
|
||||
// Fix file
|
||||
await next.patchFile('fonts/index.js', font1Content)
|
||||
await browser.waitForElementByCss('#root-page')
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,314 +1,273 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils'
|
||||
import cheerio from 'cheerio'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import path from 'path'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
describe('app dir next-image', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
createNextDescribe(
|
||||
'app dir next-image',
|
||||
{
|
||||
files: path.join(__dirname, 'next-image'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
describe('ssr content', () => {
|
||||
it('should render images on / route', async () => {
|
||||
const $ = await next.render$('/')
|
||||
|
||||
const layout = $('#app-layout')
|
||||
expect(layout.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(layout.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const page = $('#app-page')
|
||||
expect(page.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90'
|
||||
)
|
||||
expect(page.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=90 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90 2x'
|
||||
)
|
||||
|
||||
const comp = $('#app-comp')
|
||||
expect(comp.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80'
|
||||
)
|
||||
expect(comp.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=80 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80 2x'
|
||||
)
|
||||
})
|
||||
|
||||
it('should render images on /client route', async () => {
|
||||
const $ = await next.render$('/client')
|
||||
|
||||
const root = $('#app-layout')
|
||||
expect(root.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(root.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const layout = $('#app-client-layout')
|
||||
expect(layout.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=55'
|
||||
)
|
||||
expect(layout.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=55 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=55 2x'
|
||||
)
|
||||
|
||||
const page = $('#app-client-page')
|
||||
expect(page.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=60'
|
||||
)
|
||||
expect(page.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=60 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=60 2x'
|
||||
)
|
||||
|
||||
const comp = $('#app-client-comp')
|
||||
expect(comp.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=50'
|
||||
)
|
||||
expect(comp.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=50 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=50 2x'
|
||||
)
|
||||
})
|
||||
|
||||
it('should render images nested under page dir on /nested route', async () => {
|
||||
const $ = await next.render$('/nested')
|
||||
|
||||
const root = $('#app-layout')
|
||||
expect(root.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(root.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const layout = $('#app-nested-layout')
|
||||
expect(layout.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=70'
|
||||
)
|
||||
expect(layout.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=70 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=70 2x'
|
||||
)
|
||||
|
||||
const page = $('#app-nested-page')
|
||||
expect(page.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=75'
|
||||
)
|
||||
expect(page.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=75 2x'
|
||||
)
|
||||
|
||||
const comp = $('#app-nested-comp')
|
||||
expect(comp.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=65'
|
||||
)
|
||||
expect(comp.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=65 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=65 2x'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('browser content', () => {
|
||||
it('should render images on / route', async () => {
|
||||
const browser = await next.browser('/')
|
||||
|
||||
const layout = await browser.elementById('app-layout')
|
||||
expect(await layout.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(await layout.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const page = await browser.elementById('app-page')
|
||||
expect(await page.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90'
|
||||
)
|
||||
expect(await page.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=90 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90 2x'
|
||||
)
|
||||
|
||||
const comp = await browser.elementById('app-comp')
|
||||
expect(await comp.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80'
|
||||
)
|
||||
expect(await comp.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=80 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80 2x'
|
||||
)
|
||||
})
|
||||
|
||||
it('should render images on /client route', async () => {
|
||||
const browser = await next.browser('/client')
|
||||
|
||||
const root = await browser.elementById('app-layout')
|
||||
expect(await root.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(await root.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const layout = await browser.elementById('app-client-layout')
|
||||
expect(await layout.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=55'
|
||||
)
|
||||
expect(await layout.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=55 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=55 2x'
|
||||
)
|
||||
|
||||
const page = await browser.elementById('app-client-page')
|
||||
expect(await page.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=60'
|
||||
)
|
||||
expect(await page.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=60 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=60 2x'
|
||||
)
|
||||
|
||||
const comp = await browser.elementById('app-client-comp')
|
||||
expect(await comp.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=50'
|
||||
)
|
||||
expect(await comp.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=50 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=50 2x'
|
||||
)
|
||||
})
|
||||
|
||||
it('should render images nested under page dir on /nested route', async () => {
|
||||
const browser = await next.browser('/nested')
|
||||
|
||||
const root = await browser.elementById('app-layout')
|
||||
expect(await root.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(await root.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const layout = await browser.elementById('app-nested-layout')
|
||||
expect(await layout.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=70'
|
||||
)
|
||||
expect(await layout.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=70 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=70 2x'
|
||||
)
|
||||
|
||||
const page = await browser.elementById('app-nested-page')
|
||||
expect(await page.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=75'
|
||||
)
|
||||
expect(await page.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=75 2x'
|
||||
)
|
||||
|
||||
const comp = await browser.elementById('app-nested-comp')
|
||||
expect(await comp.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=65'
|
||||
)
|
||||
expect(await comp.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=65 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=65 2x'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('image content', () => {
|
||||
it('should render images on / route', async () => {
|
||||
const $ = await next.render$('/')
|
||||
|
||||
const res1 = await next.fetch($('#app-layout').attr('src'))
|
||||
expect(res1.status).toBe(200)
|
||||
expect(res1.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res2 = await next.fetch($('#app-page').attr('src'))
|
||||
expect(res2.status).toBe(200)
|
||||
expect(res2.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res3 = await next.fetch($('#app-comp').attr('src'))
|
||||
expect(res3.status).toBe(200)
|
||||
expect(res3.headers.get('content-type')).toBe('image/png')
|
||||
})
|
||||
|
||||
it('should render images on /client route', async () => {
|
||||
const $ = await next.render$('/client')
|
||||
|
||||
const res1 = await next.fetch($('#app-layout').attr('src'))
|
||||
expect(res1.status).toBe(200)
|
||||
expect(res1.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res2 = await next.fetch($('#app-client-layout').attr('src'))
|
||||
expect(res2.status).toBe(200)
|
||||
expect(res2.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res3 = await next.fetch($('#app-client-page').attr('src'))
|
||||
expect(res3.status).toBe(200)
|
||||
expect(res3.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res4 = await next.fetch($('#app-client-comp').attr('src'))
|
||||
expect(res4.status).toBe(200)
|
||||
expect(res4.headers.get('content-type')).toBe('image/png')
|
||||
})
|
||||
|
||||
it('should render images nested under page dir on /nested route', async () => {
|
||||
const $ = await next.render$('/nested')
|
||||
|
||||
const res1 = await next.fetch($('#app-layout').attr('src'))
|
||||
expect(res1.status).toBe(200)
|
||||
expect(res1.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res2 = await next.fetch($('#app-nested-layout').attr('src'))
|
||||
expect(res2.status).toBe(200)
|
||||
expect(res2.headers.get('content-type')).toBe('image/jpeg')
|
||||
|
||||
const res3 = await next.fetch($('#app-nested-page').attr('src'))
|
||||
expect(res3.status).toBe(200)
|
||||
expect(res3.headers.get('content-type')).toBe('image/jpeg')
|
||||
|
||||
const res4 = await next.fetch($('#app-nested-comp').attr('src'))
|
||||
expect(res4.status).toBe(200)
|
||||
expect(res4.headers.get('content-type')).toBe('image/jpeg')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'next-image')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
skipStart: true,
|
||||
})
|
||||
await next.start()
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
describe('ssr content', () => {
|
||||
it('should render images on / route', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const layout = $('#app-layout')
|
||||
expect(layout.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(layout.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const page = $('#app-page')
|
||||
expect(page.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90'
|
||||
)
|
||||
expect(page.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=90 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90 2x'
|
||||
)
|
||||
|
||||
const comp = $('#app-comp')
|
||||
expect(comp.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80'
|
||||
)
|
||||
expect(comp.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=80 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80 2x'
|
||||
)
|
||||
})
|
||||
|
||||
it('should render images on /client route', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/client')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const root = $('#app-layout')
|
||||
expect(root.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(root.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const layout = $('#app-client-layout')
|
||||
expect(layout.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=55'
|
||||
)
|
||||
expect(layout.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=55 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=55 2x'
|
||||
)
|
||||
|
||||
const page = $('#app-client-page')
|
||||
expect(page.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=60'
|
||||
)
|
||||
expect(page.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=60 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=60 2x'
|
||||
)
|
||||
|
||||
const comp = $('#app-client-comp')
|
||||
expect(comp.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=50'
|
||||
)
|
||||
expect(comp.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=50 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=50 2x'
|
||||
)
|
||||
})
|
||||
|
||||
it('should render images nested under page dir on /nested route', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/nested')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const root = $('#app-layout')
|
||||
expect(root.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(root.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const layout = $('#app-nested-layout')
|
||||
expect(layout.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=70'
|
||||
)
|
||||
expect(layout.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=70 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=70 2x'
|
||||
)
|
||||
|
||||
const page = $('#app-nested-page')
|
||||
expect(page.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=75'
|
||||
)
|
||||
expect(page.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=75 2x'
|
||||
)
|
||||
|
||||
const comp = $('#app-nested-comp')
|
||||
expect(comp.attr('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=65'
|
||||
)
|
||||
expect(comp.attr('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=65 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=65 2x'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('browser content', () => {
|
||||
it('should render images on / route', async () => {
|
||||
const browser = await webdriver(next.url, '/')
|
||||
|
||||
const layout = await browser.elementById('app-layout')
|
||||
expect(await layout.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(await layout.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const page = await browser.elementById('app-page')
|
||||
expect(await page.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90'
|
||||
)
|
||||
expect(await page.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=90 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90 2x'
|
||||
)
|
||||
|
||||
const comp = await browser.elementById('app-comp')
|
||||
expect(await comp.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80'
|
||||
)
|
||||
expect(await comp.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=80 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80 2x'
|
||||
)
|
||||
})
|
||||
|
||||
it('should render images on /client route', async () => {
|
||||
const browser = await webdriver(next.url, '/client')
|
||||
|
||||
const root = await browser.elementById('app-layout')
|
||||
expect(await root.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(await root.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const layout = await browser.elementById('app-client-layout')
|
||||
expect(await layout.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=55'
|
||||
)
|
||||
expect(await layout.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=55 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=55 2x'
|
||||
)
|
||||
|
||||
const page = await browser.elementById('app-client-page')
|
||||
expect(await page.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=60'
|
||||
)
|
||||
expect(await page.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=60 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=60 2x'
|
||||
)
|
||||
|
||||
const comp = await browser.elementById('app-client-comp')
|
||||
expect(await comp.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=50'
|
||||
)
|
||||
expect(await comp.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=50 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=50 2x'
|
||||
)
|
||||
})
|
||||
|
||||
it('should render images nested under page dir on /nested route', async () => {
|
||||
const browser = await webdriver(next.url, '/nested')
|
||||
|
||||
const root = await browser.elementById('app-layout')
|
||||
expect(await root.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85'
|
||||
)
|
||||
expect(await root.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x'
|
||||
)
|
||||
|
||||
const layout = await browser.elementById('app-nested-layout')
|
||||
expect(await layout.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=70'
|
||||
)
|
||||
expect(await layout.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=70 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=70 2x'
|
||||
)
|
||||
|
||||
const page = await browser.elementById('app-nested-page')
|
||||
expect(await page.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=75'
|
||||
)
|
||||
expect(await page.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=75 2x'
|
||||
)
|
||||
|
||||
const comp = await browser.elementById('app-nested-comp')
|
||||
expect(await comp.getAttribute('src')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=65'
|
||||
)
|
||||
expect(await comp.getAttribute('srcset')).toBe(
|
||||
'/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=640&q=65 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.fab2915d.jpg&w=828&q=65 2x'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('image content', () => {
|
||||
it('should render images on / route', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const res1 = await fetchViaHTTP(next.url, $('#app-layout').attr('src'))
|
||||
expect(res1.status).toBe(200)
|
||||
expect(res1.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res2 = await fetchViaHTTP(next.url, $('#app-page').attr('src'))
|
||||
expect(res2.status).toBe(200)
|
||||
expect(res2.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res3 = await fetchViaHTTP(next.url, $('#app-comp').attr('src'))
|
||||
expect(res3.status).toBe(200)
|
||||
expect(res3.headers.get('content-type')).toBe('image/png')
|
||||
})
|
||||
|
||||
it('should render images on /client route', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/client')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const res1 = await fetchViaHTTP(next.url, $('#app-layout').attr('src'))
|
||||
expect(res1.status).toBe(200)
|
||||
expect(res1.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res2 = await fetchViaHTTP(
|
||||
next.url,
|
||||
$('#app-client-layout').attr('src')
|
||||
)
|
||||
expect(res2.status).toBe(200)
|
||||
expect(res2.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res3 = await fetchViaHTTP(
|
||||
next.url,
|
||||
$('#app-client-page').attr('src')
|
||||
)
|
||||
expect(res3.status).toBe(200)
|
||||
expect(res3.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res4 = await fetchViaHTTP(
|
||||
next.url,
|
||||
$('#app-client-comp').attr('src')
|
||||
)
|
||||
expect(res4.status).toBe(200)
|
||||
expect(res4.headers.get('content-type')).toBe('image/png')
|
||||
})
|
||||
|
||||
it('should render images nested under page dir on /nested route', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/nested')
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const res1 = await fetchViaHTTP(next.url, $('#app-layout').attr('src'))
|
||||
expect(res1.status).toBe(200)
|
||||
expect(res1.headers.get('content-type')).toBe('image/png')
|
||||
|
||||
const res2 = await fetchViaHTTP(
|
||||
next.url,
|
||||
$('#app-nested-layout').attr('src')
|
||||
)
|
||||
expect(res2.status).toBe(200)
|
||||
expect(res2.headers.get('content-type')).toBe('image/jpeg')
|
||||
|
||||
const res3 = await fetchViaHTTP(
|
||||
next.url,
|
||||
$('#app-nested-page').attr('src')
|
||||
)
|
||||
expect(res3.status).toBe(200)
|
||||
expect(res3.headers.get('content-type')).toBe('image/jpeg')
|
||||
|
||||
const res4 = await fetchViaHTTP(
|
||||
next.url,
|
||||
$('#app-nested-comp').attr('src')
|
||||
)
|
||||
expect(res4.status).toBe(200)
|
||||
expect(res4.headers.get('content-type')).toBe('image/jpeg')
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,63 +1,53 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import { waitFor } from 'next-test-utils'
|
||||
import path from 'path'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
describe('app dir prefetching', () => {
|
||||
// TODO: re-enable for dev after https://vercel.slack.com/archives/C035J346QQL/p1663822388387959 is resolved (Sep 22nd 2022)
|
||||
if ((global as any).isNextDeploy || (global as any).isNextDev) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
createNextDescribe(
|
||||
'app dir prefetching',
|
||||
{
|
||||
files: path.join(__dirname, 'app-prefetch'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next, isNextDev }) => {
|
||||
// TODO: re-enable for dev after https://vercel.slack.com/archives/C035J346QQL/p1663822388387959 is resolved (Sep 22nd 2022)
|
||||
if (isNextDev) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
it('should show layout eagerly when prefetched with loading one level down', async () => {
|
||||
const browser = await next.browser('/')
|
||||
// Ensure the page is prefetched
|
||||
await waitFor(1000)
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'app-prefetch')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
skipStart: true,
|
||||
const before = Date.now()
|
||||
await browser
|
||||
.elementByCss('#to-dashboard')
|
||||
.click()
|
||||
.waitForElementByCss('#dashboard-layout')
|
||||
const after = Date.now()
|
||||
const timeToComplete = after - before
|
||||
|
||||
expect(timeToComplete < 1000).toBe(true)
|
||||
|
||||
expect(await browser.elementByCss('#dashboard-layout').text()).toBe(
|
||||
'Dashboard Hello World'
|
||||
)
|
||||
|
||||
await browser.waitForElementByCss('#dashboard-page')
|
||||
|
||||
expect(await browser.waitForElementByCss('#dashboard-page').text()).toBe(
|
||||
'Welcome to the dashboard'
|
||||
)
|
||||
})
|
||||
await next.start()
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
it('should show layout eagerly when prefetched with loading one level down', async () => {
|
||||
const browser = await webdriver(next.url, '/')
|
||||
// Ensure the page is prefetched
|
||||
await waitFor(1000)
|
||||
|
||||
const before = Date.now()
|
||||
await browser
|
||||
.elementByCss('#to-dashboard')
|
||||
.click()
|
||||
.waitForElementByCss('#dashboard-layout')
|
||||
const after = Date.now()
|
||||
const timeToComplete = after - before
|
||||
|
||||
expect(timeToComplete < 1000).toBe(true)
|
||||
|
||||
expect(await browser.elementByCss('#dashboard-layout').text()).toBe(
|
||||
'Dashboard Hello World'
|
||||
)
|
||||
|
||||
await browser.waitForElementByCss('#dashboard-page')
|
||||
|
||||
expect(await browser.waitForElementByCss('#dashboard-page').text()).toBe(
|
||||
'Welcome to the dashboard'
|
||||
)
|
||||
})
|
||||
|
||||
it('should not have prefetch error for static path', async () => {
|
||||
const browser = await webdriver(next.url, '/')
|
||||
await browser.eval('window.nd.router.prefetch("/dashboard/123")')
|
||||
await waitFor(3000)
|
||||
await browser.eval('window.nd.router.push("/dashboard/123")')
|
||||
expect(next.cliOutput).not.toContain('ReferenceError')
|
||||
expect(next.cliOutput).not.toContain('is not defined')
|
||||
})
|
||||
})
|
||||
it('should not have prefetch error for static path', async () => {
|
||||
const browser = await next.browser('/')
|
||||
await browser.eval('window.nd.router.prefetch("/dashboard/123")')
|
||||
await waitFor(3000)
|
||||
await browser.eval('window.nd.router.push("/dashboard/123")')
|
||||
expect(next.cliOutput).not.toContain('ReferenceError')
|
||||
expect(next.cliOutput).not.toContain('is not defined')
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,147 +1,130 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { renderViaHTTP, fetchViaHTTP, waitFor } from 'next-test-utils'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import { waitFor } from 'next-test-utils'
|
||||
import path from 'path'
|
||||
import cheerio from 'cheerio'
|
||||
|
||||
describe('app dir rendering', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
createNextDescribe(
|
||||
'app dir rendering',
|
||||
{
|
||||
files: path.join(__dirname, 'app-rendering'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next, isNextDev: isDev }) => {
|
||||
it('should serve app/page.server.js at /', async () => {
|
||||
const html = await next.render('/')
|
||||
expect(html).toContain('app/page.server.js')
|
||||
})
|
||||
|
||||
describe('SSR only', () => {
|
||||
it('should run data in layout and page', async () => {
|
||||
const $ = await next.render$('/ssr-only/nested')
|
||||
expect($('#layout-message').text()).toBe('hello from layout')
|
||||
expect($('#page-message').text()).toBe('hello from page')
|
||||
})
|
||||
|
||||
it('should run data fetch in parallel', async () => {
|
||||
const startTime = Date.now()
|
||||
const $ = await next.render$('/ssr-only/slow')
|
||||
const endTime = Date.now()
|
||||
const duration = endTime - startTime
|
||||
// Each part takes 5 seconds so it should be below 10 seconds
|
||||
// Using 7 seconds to ensure external factors causing slight slowness don't fail the tests
|
||||
expect(duration < 7000).toBe(true)
|
||||
expect($('#slow-layout-message').text()).toBe('hello from slow layout')
|
||||
expect($('#slow-page-message').text()).toBe('hello from slow page')
|
||||
})
|
||||
})
|
||||
|
||||
describe('static only', () => {
|
||||
it('should run data in layout and page', async () => {
|
||||
const $ = await next.render$('/static-only/nested')
|
||||
expect($('#layout-message').text()).toBe('hello from layout')
|
||||
expect($('#page-message').text()).toBe('hello from page')
|
||||
})
|
||||
|
||||
it(`should run data in parallel ${
|
||||
isDev ? 'during development' : 'and use cached version for production'
|
||||
}`, async () => {
|
||||
// const startTime = Date.now()
|
||||
const $ = await next.render$('/static-only/slow')
|
||||
// const endTime = Date.now()
|
||||
// const duration = endTime - startTime
|
||||
// Each part takes 5 seconds so it should be below 10 seconds
|
||||
// Using 7 seconds to ensure external factors causing slight slowness don't fail the tests
|
||||
// TODO: cache static props in prod
|
||||
// expect(duration < (isDev ? 7000 : 2000)).toBe(true)
|
||||
// expect(duration < 7000).toBe(true)
|
||||
expect($('#slow-layout-message').text()).toBe('hello from slow layout')
|
||||
expect($('#slow-page-message').text()).toBe('hello from slow page')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ISR', () => {
|
||||
it('should revalidate the page when revalidate is configured', async () => {
|
||||
const getPage = async () => {
|
||||
const res = await next.fetch('isr-multiple/nested')
|
||||
const html = await res.text()
|
||||
|
||||
return {
|
||||
$: cheerio.load(html),
|
||||
cacheHeader: res.headers['x-nextjs-cache'],
|
||||
}
|
||||
}
|
||||
const { $ } = await getPage()
|
||||
expect($('#layout-message').text()).toBe('hello from layout')
|
||||
expect($('#page-message').text()).toBe('hello from page')
|
||||
|
||||
const layoutNow = $('#layout-now').text()
|
||||
const pageNow = $('#page-now').text()
|
||||
|
||||
await waitFor(2000)
|
||||
|
||||
// TODO: implement
|
||||
// Trigger revalidate
|
||||
// const { cacheHeader: revalidateCacheHeader } = await getPage()
|
||||
// expect(revalidateCacheHeader).toBe('STALE')
|
||||
|
||||
// TODO: implement
|
||||
const { $: $revalidated /* cacheHeader: revalidatedCacheHeader */ } =
|
||||
await getPage()
|
||||
// expect(revalidatedCacheHeader).toBe('REVALIDATED')
|
||||
|
||||
const layoutNowRevalidated = $revalidated('#layout-now').text()
|
||||
const pageNowRevalidated = $revalidated('#page-now').text()
|
||||
|
||||
// Expect that the `Date.now()` is different as the page have been regenerated
|
||||
expect(layoutNow).not.toBe(layoutNowRevalidated)
|
||||
expect(pageNow).not.toBe(pageNowRevalidated)
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: implement
|
||||
describe.skip('mixed static and dynamic', () => {
|
||||
it('should generate static data during build and use it', async () => {
|
||||
const getPage = async () => {
|
||||
const $ = await next.render$('isr-ssr-combined/nested')
|
||||
|
||||
return {
|
||||
$,
|
||||
}
|
||||
}
|
||||
const { $ } = await getPage()
|
||||
expect($('#layout-message').text()).toBe('hello from layout')
|
||||
expect($('#page-message').text()).toBe('hello from page')
|
||||
|
||||
const layoutNow = $('#layout-now').text()
|
||||
const pageNow = $('#page-now').text()
|
||||
|
||||
const { $: $second } = await getPage()
|
||||
|
||||
const layoutNowSecond = $second('#layout-now').text()
|
||||
const pageNowSecond = $second('#page-now').text()
|
||||
|
||||
// Expect that the `Date.now()` is different as it came from getServerSideProps
|
||||
expect(layoutNow).not.toBe(layoutNowSecond)
|
||||
// Expect that the `Date.now()` is the same as it came from getStaticProps
|
||||
expect(pageNow).toBe(pageNowSecond)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const isDev = (global as any).isNextDev
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'app-rendering')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
it('should serve app/page.server.js at /', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/')
|
||||
expect(html).toContain('app/page.server.js')
|
||||
})
|
||||
|
||||
describe('SSR only', () => {
|
||||
it('should run data in layout and page', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/ssr-only/nested')
|
||||
const $ = cheerio.load(html)
|
||||
expect($('#layout-message').text()).toBe('hello from layout')
|
||||
expect($('#page-message').text()).toBe('hello from page')
|
||||
})
|
||||
|
||||
it('should run data fetch in parallel', async () => {
|
||||
const startTime = Date.now()
|
||||
const html = await renderViaHTTP(next.url, '/ssr-only/slow')
|
||||
const endTime = Date.now()
|
||||
const duration = endTime - startTime
|
||||
// Each part takes 5 seconds so it should be below 10 seconds
|
||||
// Using 7 seconds to ensure external factors causing slight slowness don't fail the tests
|
||||
expect(duration < 7000).toBe(true)
|
||||
const $ = cheerio.load(html)
|
||||
expect($('#slow-layout-message').text()).toBe('hello from slow layout')
|
||||
expect($('#slow-page-message').text()).toBe('hello from slow page')
|
||||
})
|
||||
})
|
||||
|
||||
describe('static only', () => {
|
||||
it('should run data in layout and page', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/static-only/nested')
|
||||
const $ = cheerio.load(html)
|
||||
expect($('#layout-message').text()).toBe('hello from layout')
|
||||
expect($('#page-message').text()).toBe('hello from page')
|
||||
})
|
||||
|
||||
it(`should run data in parallel ${
|
||||
isDev ? 'during development' : 'and use cached version for production'
|
||||
}`, async () => {
|
||||
// const startTime = Date.now()
|
||||
const html = await renderViaHTTP(next.url, '/static-only/slow')
|
||||
// const endTime = Date.now()
|
||||
// const duration = endTime - startTime
|
||||
// Each part takes 5 seconds so it should be below 10 seconds
|
||||
// Using 7 seconds to ensure external factors causing slight slowness don't fail the tests
|
||||
// TODO: cache static props in prod
|
||||
// expect(duration < (isDev ? 7000 : 2000)).toBe(true)
|
||||
// expect(duration < 7000).toBe(true)
|
||||
const $ = cheerio.load(html)
|
||||
expect($('#slow-layout-message').text()).toBe('hello from slow layout')
|
||||
expect($('#slow-page-message').text()).toBe('hello from slow page')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ISR', () => {
|
||||
it('should revalidate the page when revalidate is configured', async () => {
|
||||
const getPage = async () => {
|
||||
const res = await fetchViaHTTP(next.url, 'isr-multiple/nested')
|
||||
const html = await res.text()
|
||||
|
||||
return {
|
||||
$: cheerio.load(html),
|
||||
cacheHeader: res.headers['x-nextjs-cache'],
|
||||
}
|
||||
}
|
||||
const { $ } = await getPage()
|
||||
expect($('#layout-message').text()).toBe('hello from layout')
|
||||
expect($('#page-message').text()).toBe('hello from page')
|
||||
|
||||
const layoutNow = $('#layout-now').text()
|
||||
const pageNow = $('#page-now').text()
|
||||
|
||||
await waitFor(2000)
|
||||
|
||||
// TODO: implement
|
||||
// Trigger revalidate
|
||||
// const { cacheHeader: revalidateCacheHeader } = await getPage()
|
||||
// expect(revalidateCacheHeader).toBe('STALE')
|
||||
|
||||
// TODO: implement
|
||||
const { $: $revalidated /* cacheHeader: revalidatedCacheHeader */ } =
|
||||
await getPage()
|
||||
// expect(revalidatedCacheHeader).toBe('REVALIDATED')
|
||||
|
||||
const layoutNowRevalidated = $revalidated('#layout-now').text()
|
||||
const pageNowRevalidated = $revalidated('#page-now').text()
|
||||
|
||||
// Expect that the `Date.now()` is different as the page have been regenerated
|
||||
expect(layoutNow).not.toBe(layoutNowRevalidated)
|
||||
expect(pageNow).not.toBe(pageNowRevalidated)
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: implement
|
||||
describe.skip('mixed static and dynamic', () => {
|
||||
it('should generate static data during build and use it', async () => {
|
||||
const getPage = async () => {
|
||||
const html = await renderViaHTTP(next.url, 'isr-ssr-combined/nested')
|
||||
|
||||
return {
|
||||
$: cheerio.load(html),
|
||||
}
|
||||
}
|
||||
const { $ } = await getPage()
|
||||
expect($('#layout-message').text()).toBe('hello from layout')
|
||||
expect($('#page-message').text()).toBe('hello from page')
|
||||
|
||||
const layoutNow = $('#layout-now').text()
|
||||
const pageNow = $('#page-now').text()
|
||||
|
||||
const { $: $second } = await getPage()
|
||||
|
||||
const layoutNowSecond = $second('#layout-now').text()
|
||||
const pageNowSecond = $second('#page-now').text()
|
||||
|
||||
// Expect that the `Date.now()` is different as it came from getServerSideProps
|
||||
expect(layoutNow).not.toBe(layoutNowSecond)
|
||||
// Expect that the `Date.now()` is the same as it came from getStaticProps
|
||||
expect(pageNow).toBe(pageNowSecond)
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,219 +1,199 @@
|
|||
import path from 'path'
|
||||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import webdriver from 'next-webdriver'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import { getRedboxSource, hasRedbox } from 'next-test-utils'
|
||||
|
||||
describe('app-dir root layout', () => {
|
||||
const isDev = (global as any).isNextDev
|
||||
createNextDescribe(
|
||||
'app-dir root layout',
|
||||
{
|
||||
files: path.join(__dirname, 'root-layout'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next, isNextDev: isDev }) => {
|
||||
if (isDev) {
|
||||
// TODO-APP: re-enable after reworking the error overlay.
|
||||
describe.skip('Missing required tags', () => {
|
||||
it('should error on page load', async () => {
|
||||
const browser = await next.browser('/missing-tags', {
|
||||
waitHydration: false,
|
||||
})
|
||||
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: {
|
||||
app: new FileRef(path.join(__dirname, 'root-layout/app')),
|
||||
'next.config.js': new FileRef(
|
||||
path.join(__dirname, 'root-layout/next.config.js')
|
||||
),
|
||||
},
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
if (isDev) {
|
||||
// TODO-APP: re-enable after reworking the error overlay.
|
||||
describe.skip('Missing required tags', () => {
|
||||
it('should error on page load', async () => {
|
||||
const browser = await webdriver(next.url, '/missing-tags', {
|
||||
waitHydration: false,
|
||||
})
|
||||
|
||||
expect(await hasRedbox(browser, true)).toBe(true)
|
||||
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
|
||||
expect(await hasRedbox(browser, true)).toBe(true)
|
||||
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
|
||||
"Please make sure to include the following tags in your root layout: <html>, <body>.
|
||||
|
||||
Missing required root layout tags: html, body"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should error on page navigation', async () => {
|
||||
const browser = await webdriver(next.url, '/has-tags', {
|
||||
waitHydration: false,
|
||||
})
|
||||
|
||||
it('should error on page navigation', async () => {
|
||||
const browser = await next.browser('/has-tags', {
|
||||
waitHydration: false,
|
||||
})
|
||||
await browser.elementByCss('a').click()
|
||||
|
||||
expect(await hasRedbox(browser, true)).toBe(true)
|
||||
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
|
||||
"Please make sure to include the following tags in your root layout: <html>, <body>.
|
||||
|
||||
Missing required root layout tags: html, body"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should error on page load on static generation', async () => {
|
||||
const browser = await next.browser('/static-missing-tags/slug', {
|
||||
waitHydration: false,
|
||||
})
|
||||
|
||||
expect(await hasRedbox(browser, true)).toBe(true)
|
||||
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
|
||||
"Please make sure to include the following tags in your root layout: <html>, <body>.
|
||||
|
||||
Missing required root layout tags: html, body"
|
||||
`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe('Should do a mpa navigation when switching root layout', () => {
|
||||
it('should work with basic routes', async () => {
|
||||
const browser = await next.browser('/basic-route')
|
||||
|
||||
expect(await browser.elementById('basic-route').text()).toBe(
|
||||
'Basic route'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#inner-basic-route').text()
|
||||
).toBe('Inner basic route')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
expect(await hasRedbox(browser, true)).toBe(true)
|
||||
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
|
||||
"Please make sure to include the following tags in your root layout: <html>, <body>.
|
||||
|
||||
Missing required root layout tags: html, body"
|
||||
`)
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.waitForElementByCss('#route-group').text()).toBe(
|
||||
'Route group'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should error on page load on static generation', async () => {
|
||||
const browser = await webdriver(next.url, '/static-missing-tags/slug', {
|
||||
waitHydration: false,
|
||||
})
|
||||
it('should work with route groups', async () => {
|
||||
const browser = await next.browser('/route-group')
|
||||
|
||||
expect(await hasRedbox(browser, true)).toBe(true)
|
||||
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
|
||||
"Please make sure to include the following tags in your root layout: <html>, <body>.
|
||||
expect(await browser.elementById('route-group').text()).toBe(
|
||||
'Route group'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
Missing required root layout tags: html, body"
|
||||
`)
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#nested-route-group').text()
|
||||
).toBe('Nested route group')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.waitForElementByCss('#parallel-one').text()).toBe(
|
||||
'One'
|
||||
)
|
||||
expect(await browser.waitForElementByCss('#parallel-two').text()).toBe(
|
||||
'Two'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should work with parallel routes', async () => {
|
||||
const browser = await next.browser('/with-parallel-routes')
|
||||
|
||||
expect(await browser.elementById('parallel-one').text()).toBe('One')
|
||||
expect(await browser.elementById('parallel-two').text()).toBe('Two')
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#parallel-one-inner').text()
|
||||
).toBe('One inner')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.waitForElementByCss('#dynamic-hello').text()).toBe(
|
||||
'dynamic hello'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should work with dynamic routes', async () => {
|
||||
const browser = await next.browser('/dynamic/first')
|
||||
|
||||
expect(await browser.elementById('dynamic-first').text()).toBe(
|
||||
'dynamic first'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#dynamic-first-second').text()
|
||||
).toBe('dynamic first second')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#inner-basic-route').text()
|
||||
).toBe('Inner basic route')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should work with dynamic catchall routes', async () => {
|
||||
const browser = await next.browser('/dynamic-catchall/slug')
|
||||
|
||||
expect(await browser.elementById('catchall-slug').text()).toBe(
|
||||
'catchall slug'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementById('to-next-url').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#catchall-slug-slug').text()
|
||||
).toBe('catchall slug slug')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementById('to-dynamic-first').click()
|
||||
expect(await browser.elementById('dynamic-first').text()).toBe(
|
||||
'dynamic first'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should work with static routes', async () => {
|
||||
const browser = await next.browser('/static-mpa-navigation/slug1')
|
||||
|
||||
expect(await browser.elementById('static-slug1').text()).toBe(
|
||||
'static slug1'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.waitForElementByCss('#static-slug2').text()).toBe(
|
||||
'static slug2'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.elementById('basic-route').text()).toBe(
|
||||
'Basic route'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe('Should do a mpa navigation when switching root layout', () => {
|
||||
it('should work with basic routes', async () => {
|
||||
const browser = await webdriver(next.url, '/basic-route')
|
||||
|
||||
expect(await browser.elementById('basic-route').text()).toBe(
|
||||
'Basic route'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#inner-basic-route').text()
|
||||
).toBe('Inner basic route')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.waitForElementByCss('#route-group').text()).toBe(
|
||||
'Route group'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should work with route groups', async () => {
|
||||
const browser = await webdriver(next.url, '/route-group')
|
||||
|
||||
expect(await browser.elementById('route-group').text()).toBe(
|
||||
'Route group'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#nested-route-group').text()
|
||||
).toBe('Nested route group')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.waitForElementByCss('#parallel-one').text()).toBe(
|
||||
'One'
|
||||
)
|
||||
expect(await browser.waitForElementByCss('#parallel-two').text()).toBe(
|
||||
'Two'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should work with parallel routes', async () => {
|
||||
const browser = await webdriver(next.url, '/with-parallel-routes')
|
||||
|
||||
expect(await browser.elementById('parallel-one').text()).toBe('One')
|
||||
expect(await browser.elementById('parallel-two').text()).toBe('Two')
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#parallel-one-inner').text()
|
||||
).toBe('One inner')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.waitForElementByCss('#dynamic-hello').text()).toBe(
|
||||
'dynamic hello'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should work with dynamic routes', async () => {
|
||||
const browser = await webdriver(next.url, '/dynamic/first')
|
||||
|
||||
expect(await browser.elementById('dynamic-first').text()).toBe(
|
||||
'dynamic first'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#dynamic-first-second').text()
|
||||
).toBe('dynamic first second')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#inner-basic-route').text()
|
||||
).toBe('Inner basic route')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should work with dynamic catchall routes', async () => {
|
||||
const browser = await webdriver(next.url, '/dynamic-catchall/slug')
|
||||
|
||||
expect(await browser.elementById('catchall-slug').text()).toBe(
|
||||
'catchall slug'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementById('to-next-url').click()
|
||||
expect(
|
||||
await browser.waitForElementByCss('#catchall-slug-slug').text()
|
||||
).toBe('catchall slug slug')
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementById('to-dynamic-first').click()
|
||||
expect(await browser.elementById('dynamic-first').text()).toBe(
|
||||
'dynamic first'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should work with static routes', async () => {
|
||||
const browser = await webdriver(next.url, '/static-mpa-navigation/slug1')
|
||||
|
||||
expect(await browser.elementById('static-slug1').text()).toBe(
|
||||
'static slug1'
|
||||
)
|
||||
await browser.eval('window.__TEST_NO_RELOAD = true')
|
||||
|
||||
// Navigate to page with same root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.waitForElementByCss('#static-slug2').text()).toBe(
|
||||
'static slug2'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeTrue()
|
||||
|
||||
// Navigate to page with different root layout
|
||||
await browser.elementByCss('a').click()
|
||||
expect(await browser.elementById('basic-route').text()).toBe(
|
||||
'Basic route'
|
||||
)
|
||||
expect(await browser.eval('window.__TEST_NO_RELOAD')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import webdriver from 'next-webdriver'
|
||||
import { renderViaHTTP, fetchViaHTTP, check } from 'next-test-utils'
|
||||
import { check } from 'next-test-utils'
|
||||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import cheerio from 'cheerio'
|
||||
|
||||
function getNodeBySelector(html, selector) {
|
||||
const $ = cheerio.load(html)
|
||||
return $(selector)
|
||||
}
|
||||
|
||||
async function resolveStreamResponse(response: any, onData?: any) {
|
||||
let result = ''
|
||||
onData = onData || (() => {})
|
||||
|
@ -58,53 +52,49 @@ describe('app dir - rsc basics', () => {
|
|||
afterAll(() => next.destroy())
|
||||
|
||||
const { isNextDeploy, isNextDev } = global as any
|
||||
const isReact17 = process.env.NEXT_TEST_REACT_VERSION === '^17'
|
||||
if (isNextDeploy || isReact17) {
|
||||
if (isNextDeploy) {
|
||||
it('should skip tests for next-deploy and react 17', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
it('should correctly render page returning null', async () => {
|
||||
const homeHTML = await renderViaHTTP(next.url, '/return-null/page')
|
||||
const homeHTML = await next.render('/return-null/page')
|
||||
const $ = cheerio.load(homeHTML)
|
||||
expect($('#return-null-layout').html()).toBeEmpty()
|
||||
})
|
||||
|
||||
it('should correctly render component returning null', async () => {
|
||||
const homeHTML = await renderViaHTTP(next.url, '/return-null/component')
|
||||
const homeHTML = await next.render('/return-null/component')
|
||||
const $ = cheerio.load(homeHTML)
|
||||
expect($('#return-null-layout').html()).toBeEmpty()
|
||||
})
|
||||
|
||||
it('should correctly render layout returning null', async () => {
|
||||
const homeHTML = await renderViaHTTP(next.url, '/return-null/layout')
|
||||
const homeHTML = await next.render('/return-null/layout')
|
||||
const $ = cheerio.load(homeHTML)
|
||||
expect($('#return-null-layout').html()).toBeEmpty()
|
||||
})
|
||||
|
||||
it('should correctly render page returning undefined', async () => {
|
||||
const homeHTML = await renderViaHTTP(next.url, '/return-undefined/page')
|
||||
const homeHTML = await next.render('/return-undefined/page')
|
||||
const $ = cheerio.load(homeHTML)
|
||||
expect($('#return-undefined-layout').html()).toBeEmpty()
|
||||
})
|
||||
|
||||
it('should correctly render component returning undefined', async () => {
|
||||
const homeHTML = await renderViaHTTP(
|
||||
next.url,
|
||||
'/return-undefined/component'
|
||||
)
|
||||
const homeHTML = await next.render('/return-undefined/component')
|
||||
const $ = cheerio.load(homeHTML)
|
||||
expect($('#return-undefined-layout').html()).toBeEmpty()
|
||||
})
|
||||
|
||||
it('should correctly render layout returning undefined', async () => {
|
||||
const homeHTML = await renderViaHTTP(next.url, '/return-undefined/layout')
|
||||
const homeHTML = await next.render('/return-undefined/layout')
|
||||
const $ = cheerio.load(homeHTML)
|
||||
expect($('#return-undefined-layout').html()).toBeEmpty()
|
||||
})
|
||||
|
||||
it('should render server components correctly', async () => {
|
||||
const homeHTML = await renderViaHTTP(next.url, '/', null, {
|
||||
const homeHTML = await next.render('/', null, {
|
||||
headers: {
|
||||
'x-next-test-client': 'test-util',
|
||||
},
|
||||
|
@ -142,7 +132,7 @@ describe('app dir - rsc basics', () => {
|
|||
it('should reuse the inline flight response without sending extra requests', async () => {
|
||||
let hasFlightRequest = false
|
||||
let requestsCount = 0
|
||||
await webdriver(next.url, '/root', {
|
||||
await next.browser('/root', {
|
||||
beforePageLoad(page) {
|
||||
page.on('request', (request) => {
|
||||
requestsCount++
|
||||
|
@ -164,13 +154,13 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should support multi-level server component imports', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/multi')
|
||||
const html = await next.render('/multi')
|
||||
expect(html).toContain('bar.server.js:')
|
||||
expect(html).toContain('foo.client')
|
||||
})
|
||||
|
||||
it('should be able to navigate between rsc routes', async () => {
|
||||
const browser = await webdriver(next.url, '/root')
|
||||
const browser = await next.browser('/root')
|
||||
|
||||
await browser.waitForElementByCss('#goto-next-link').click()
|
||||
await new Promise((res) => setTimeout(res, 1000))
|
||||
|
@ -192,7 +182,7 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should handle streaming server components correctly', async () => {
|
||||
const browser = await webdriver(next.url, '/streaming-rsc')
|
||||
const browser = await next.browser('/streaming-rsc')
|
||||
const content = await browser.eval(
|
||||
`document.querySelector('#content').innerText`
|
||||
)
|
||||
|
@ -200,12 +190,12 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should support next/link in server components', async () => {
|
||||
const linkHTML = await renderViaHTTP(next.url, '/next-api/link')
|
||||
const linkText = getNodeBySelector(linkHTML, 'body a[href="/root"]').text()
|
||||
const $ = await next.render$('/next-api/link')
|
||||
const linkText = $('body a[href="/root"]').text()
|
||||
|
||||
expect(linkText).toContain('home')
|
||||
|
||||
const browser = await webdriver(next.url, '/next-api/link')
|
||||
const browser = await next.browser('/next-api/link')
|
||||
|
||||
// We need to make sure the app is fully hydrated before clicking, otherwise
|
||||
// it will be a full redirection instead of being taken over by the next
|
||||
|
@ -228,7 +218,7 @@ describe('app dir - rsc basics', () => {
|
|||
it('should link correctly with next/link without mpa navigation to the page', async () => {
|
||||
// Select the button which is not hidden but rendered
|
||||
const selector = '#goto-next-link'
|
||||
const browser = await webdriver(next.url, '/root', {})
|
||||
const browser = await next.browser('/root', {})
|
||||
|
||||
await browser.eval('window.didNotReloadPage = true')
|
||||
await browser.elementByCss(selector).click().waitForElementByCss('#query')
|
||||
|
@ -240,13 +230,13 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should escape streaming data correctly', async () => {
|
||||
const browser = await webdriver(next.url, '/escaping-rsc')
|
||||
const browser = await next.browser('/escaping-rsc')
|
||||
const manipulated = await browser.eval(`window.__manipulated_by_injection`)
|
||||
expect(manipulated).toBe(undefined)
|
||||
})
|
||||
|
||||
it('should render built-in 404 page for missing route if pagesDir is not presented', async () => {
|
||||
const res = await fetchViaHTTP(next.url, '/does-not-exist')
|
||||
const res = await next.fetch('/does-not-exist')
|
||||
|
||||
expect(res.status).toBe(404)
|
||||
const html = await res.text()
|
||||
|
@ -254,28 +244,28 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should suspense next/legacy/image in server components', async () => {
|
||||
const imageHTML = await renderViaHTTP(next.url, '/next-api/image-legacy')
|
||||
const imageTag = getNodeBySelector(imageHTML, '#myimg')
|
||||
const $ = await next.render$('/next-api/image-legacy')
|
||||
const imageTag = $('#myimg')
|
||||
|
||||
expect(imageTag.attr('src')).toContain('data:image')
|
||||
})
|
||||
|
||||
it('should suspense next/image in server components', async () => {
|
||||
const imageHTML = await renderViaHTTP(next.url, '/next-api/image-new')
|
||||
const imageTag = getNodeBySelector(imageHTML, '#myimg')
|
||||
const $ = await next.render$('/next-api/image-new')
|
||||
const imageTag = $('#myimg')
|
||||
|
||||
expect(imageTag.attr('src')).toMatch(/test.+jpg/)
|
||||
})
|
||||
|
||||
it('should handle various kinds of exports correctly', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/various-exports')
|
||||
const content = getNodeBySelector(html, 'body').text()
|
||||
const $ = await next.render$('/various-exports')
|
||||
const content = $('body').text()
|
||||
|
||||
expect(content).toContain('abcde')
|
||||
expect(content).toContain('default-export-arrow.client')
|
||||
expect(content).toContain('named.client')
|
||||
|
||||
const browser = await webdriver(next.url, '/various-exports')
|
||||
const browser = await next.browser('/various-exports')
|
||||
const hydratedContent = await browser.waitForElementByCss('body').text()
|
||||
|
||||
expect(hydratedContent).toContain('abcde')
|
||||
|
@ -287,17 +277,17 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should support native modules in server component', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/native-module')
|
||||
const content = getNodeBySelector(html, 'body').text()
|
||||
const $ = await next.render$('/native-module')
|
||||
const content = $('body').text()
|
||||
|
||||
expect(content).toContain('fs: function')
|
||||
expect(content).toContain('foo.client')
|
||||
})
|
||||
|
||||
it('should resolve different kinds of components correctly', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/shared')
|
||||
const main = getNodeBySelector(html, '#main').html()
|
||||
const content = getNodeBySelector(html, '#bar').text()
|
||||
const $ = await next.render$('/shared')
|
||||
const main = $('#main').html()
|
||||
const content = $('#bar').text()
|
||||
|
||||
// Should have 5 occurrences of "client_component".
|
||||
expect(Array.from(main.matchAll(/client_component/g)).length).toBe(5)
|
||||
|
@ -317,8 +307,8 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should render initial styles of css-in-js in nodejs SSR correctly', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/css-in-js')
|
||||
const head = getNodeBySelector(html, 'head').html()
|
||||
const $ = await next.render$('/css-in-js')
|
||||
const head = $('head').html()
|
||||
|
||||
// from styled-jsx
|
||||
expect(head).toMatch(/{color:(\s*)purple;?}/) // styled-jsx/style
|
||||
|
@ -329,8 +319,8 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should render initial styles of css-in-js in edge SSR correctly', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/css-in-js/edge')
|
||||
const head = getNodeBySelector(html, 'head').html()
|
||||
const $ = await next.render$('/css-in-js/edge')
|
||||
const head = $('head').html()
|
||||
|
||||
// from styled-jsx
|
||||
expect(head).toMatch(/{color:(\s*)purple;?}/) // styled-jsx/style
|
||||
|
@ -341,28 +331,26 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should render css-in-js suspense boundary correctly', async () => {
|
||||
await fetchViaHTTP(next.url, '/css-in-js/suspense', null, {}).then(
|
||||
async (response) => {
|
||||
const results = []
|
||||
await next.fetch('/css-in-js/suspense', null, {}).then(async (response) => {
|
||||
const results = []
|
||||
|
||||
await resolveStreamResponse(response, (chunk: string) => {
|
||||
// check if rsc refresh script for suspense show up, the test content could change with react version
|
||||
const hasRCScript = /\$RC=function/.test(chunk)
|
||||
if (hasRCScript) results.push('refresh-script')
|
||||
await resolveStreamResponse(response, (chunk: string) => {
|
||||
// check if rsc refresh script for suspense show up, the test content could change with react version
|
||||
const hasRCScript = /\$RC=function/.test(chunk)
|
||||
if (hasRCScript) results.push('refresh-script')
|
||||
|
||||
const isSuspenseyDataResolved =
|
||||
/<style[^<>]*>(\s)*.+{padding:2px;(\s)*color:orange;}/.test(chunk)
|
||||
if (isSuspenseyDataResolved) results.push('data')
|
||||
const isSuspenseyDataResolved =
|
||||
/<style[^<>]*>(\s)*.+{padding:2px;(\s)*color:orange;}/.test(chunk)
|
||||
if (isSuspenseyDataResolved) results.push('data')
|
||||
|
||||
const isFallbackResolved = chunk.includes('fallback')
|
||||
if (isFallbackResolved) results.push('fallback')
|
||||
})
|
||||
const isFallbackResolved = chunk.includes('fallback')
|
||||
if (isFallbackResolved) results.push('fallback')
|
||||
})
|
||||
|
||||
expect(results).toEqual(['fallback', 'data', 'refresh-script'])
|
||||
}
|
||||
)
|
||||
expect(results).toEqual(['fallback', 'data', 'refresh-script'])
|
||||
})
|
||||
// // TODO-APP: fix streaming/suspense within browser for test suite
|
||||
// const browser = await webdriver(next.url, '/css-in-js', { waitHydration: false })
|
||||
// const browser = await next.browser( '/css-in-js', { waitHydration: false })
|
||||
// const footer = await browser.elementByCss('#footer')
|
||||
// expect(await footer.text()).toBe('wait for fallback')
|
||||
// expect(
|
||||
|
@ -390,7 +378,7 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should stick to the url without trailing /page suffix', async () => {
|
||||
const browser = await webdriver(next.url, '/edge/dynamic')
|
||||
const browser = await next.browser('/edge/dynamic')
|
||||
const indexUrl = await browser.url()
|
||||
|
||||
await browser.loadPage(`${next.url}/edge/dynamic/123`, {
|
||||
|
@ -404,51 +392,50 @@ describe('app dir - rsc basics', () => {
|
|||
})
|
||||
|
||||
it('should support streaming for flight response', async () => {
|
||||
await fetchViaHTTP(
|
||||
next.url,
|
||||
'/',
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
['RSC'.toString()]: '1',
|
||||
},
|
||||
}
|
||||
).then(async (response) => {
|
||||
const result = await resolveStreamResponse(response)
|
||||
expect(result).toContain('component:index.server')
|
||||
})
|
||||
await next
|
||||
.fetch(
|
||||
'/',
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
['RSC'.toString()]: '1',
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(async (response) => {
|
||||
const result = await resolveStreamResponse(response)
|
||||
expect(result).toContain('component:index.server')
|
||||
})
|
||||
})
|
||||
|
||||
it('should support partial hydration with inlined server data', async () => {
|
||||
await fetchViaHTTP(next.url, '/partial-hydration', null, {}).then(
|
||||
async (response) => {
|
||||
let gotFallback = false
|
||||
let gotData = false
|
||||
let gotInlinedData = false
|
||||
await next.fetch('/partial-hydration', null, {}).then(async (response) => {
|
||||
let gotFallback = false
|
||||
let gotData = false
|
||||
let gotInlinedData = false
|
||||
|
||||
await resolveStreamResponse(response, (_, result) => {
|
||||
gotInlinedData = result.includes('self.__next_f=')
|
||||
gotData = result.includes('next_streaming_data')
|
||||
if (!gotFallback) {
|
||||
gotFallback = result.includes('next_streaming_fallback')
|
||||
if (gotFallback) {
|
||||
expect(gotData).toBe(false)
|
||||
expect(gotInlinedData).toBe(false)
|
||||
}
|
||||
await resolveStreamResponse(response, (_, result) => {
|
||||
gotInlinedData = result.includes('self.__next_f=')
|
||||
gotData = result.includes('next_streaming_data')
|
||||
if (!gotFallback) {
|
||||
gotFallback = result.includes('next_streaming_fallback')
|
||||
if (gotFallback) {
|
||||
expect(gotData).toBe(false)
|
||||
expect(gotInlinedData).toBe(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
expect(gotFallback).toBe(true)
|
||||
expect(gotData).toBe(true)
|
||||
expect(gotInlinedData).toBe(true)
|
||||
}
|
||||
)
|
||||
expect(gotFallback).toBe(true)
|
||||
expect(gotData).toBe(true)
|
||||
expect(gotInlinedData).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
// disable this flaky test
|
||||
it.skip('should support partial hydration with inlined server data in browser', async () => {
|
||||
// Should end up with "next_streaming_data".
|
||||
const browser = await webdriver(next.url, '/partial-hydration', {
|
||||
const browser = await next.browser('/partial-hydration', {
|
||||
waitHydration: false,
|
||||
})
|
||||
const content = await browser.eval(`window.document.body.innerText`)
|
||||
|
|
|
@ -1,120 +1,98 @@
|
|||
import path from 'path'
|
||||
import { check, fetchViaHTTP, renderViaHTTP } from 'next-test-utils'
|
||||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { check } from 'next-test-utils'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
|
||||
describe('app dir - rsc errors', () => {
|
||||
let next: NextInstance
|
||||
if (!(globalThis as any).isNextDev) {
|
||||
it('should skip tests for next-start', () => {})
|
||||
} else {
|
||||
createNextDescribe(
|
||||
'app dir - rsc errors',
|
||||
{
|
||||
files: path.join(__dirname, './rsc-errors'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
it('should throw an error when getServerSideProps is used', async () => {
|
||||
const pageFile = 'app/client-with-errors/get-server-side-props/page.js'
|
||||
const content = await next.readFile(pageFile)
|
||||
const uncomment = content.replace(
|
||||
'// export function getServerSideProps',
|
||||
'export function getServerSideProps'
|
||||
)
|
||||
await next.patchFile(pageFile, uncomment)
|
||||
const res = await next.fetch(
|
||||
'/client-with-errors/get-server-side-props'
|
||||
)
|
||||
await next.patchFile(pageFile, content)
|
||||
|
||||
const { isNextDeploy, isNextDev } = global as any
|
||||
if (isNextDeploy) {
|
||||
it('should skip tests for next-deploy and react 17', () => {})
|
||||
return
|
||||
}
|
||||
if (!isNextDev) {
|
||||
it('should skip tests for next-start', () => {})
|
||||
return
|
||||
}
|
||||
await check(async () => {
|
||||
const { status } = await next.fetch(
|
||||
'/client-with-errors/get-server-side-props'
|
||||
)
|
||||
return status
|
||||
}, /200/)
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, './rsc-errors')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
expect(res.status).toBe(500)
|
||||
expect(await res.text()).toContain(
|
||||
'"getServerSideProps\\" is not supported in app/'
|
||||
)
|
||||
})
|
||||
|
||||
it('should throw an error when getServerSideProps is used', async () => {
|
||||
const pageFile = 'app/client-with-errors/get-server-side-props/page.js'
|
||||
const content = await next.readFile(pageFile)
|
||||
const uncomment = content.replace(
|
||||
'// export function getServerSideProps',
|
||||
'export function getServerSideProps'
|
||||
)
|
||||
await next.patchFile(pageFile, uncomment)
|
||||
const res = await fetchViaHTTP(
|
||||
next.url,
|
||||
'/client-with-errors/get-server-side-props'
|
||||
)
|
||||
await next.patchFile(pageFile, content)
|
||||
it('should throw an error when getStaticProps is used', async () => {
|
||||
const pageFile = 'app/client-with-errors/get-static-props/page.js'
|
||||
const content = await next.readFile(pageFile)
|
||||
const uncomment = content.replace(
|
||||
'// export function getStaticProps',
|
||||
'export function getStaticProps'
|
||||
)
|
||||
await next.patchFile(pageFile, uncomment)
|
||||
const res = await next.fetch('/client-with-errors/get-static-props')
|
||||
await next.patchFile(pageFile, content)
|
||||
await check(async () => {
|
||||
const { status } = await next.fetch(
|
||||
'/client-with-errors/get-static-props'
|
||||
)
|
||||
return status
|
||||
}, /200/)
|
||||
|
||||
await check(async () => {
|
||||
const { status } = await fetchViaHTTP(
|
||||
next.url,
|
||||
'/client-with-errors/get-server-side-props'
|
||||
)
|
||||
return status
|
||||
}, /200/)
|
||||
expect(res.status).toBe(500)
|
||||
expect(await res.text()).toContain(
|
||||
'"getStaticProps\\" is not supported in app/'
|
||||
)
|
||||
})
|
||||
|
||||
expect(res.status).toBe(500)
|
||||
expect(await res.text()).toContain(
|
||||
'"getServerSideProps\\" is not supported in app/'
|
||||
)
|
||||
})
|
||||
it('should error for styled-jsx imports on server side', async () => {
|
||||
const html = await next.render('/server-with-errors/styled-jsx')
|
||||
expect(html).toContain(
|
||||
'This module cannot be imported from a Server Component module. It should only be used from a Client Component.'
|
||||
)
|
||||
})
|
||||
|
||||
it('should throw an error when getStaticProps is used', async () => {
|
||||
const pageFile = 'app/client-with-errors/get-static-props/page.js'
|
||||
const content = await next.readFile(pageFile)
|
||||
const uncomment = content.replace(
|
||||
'// export function getStaticProps',
|
||||
'export function getStaticProps'
|
||||
)
|
||||
await next.patchFile(pageFile, uncomment)
|
||||
const res = await fetchViaHTTP(
|
||||
next.url,
|
||||
'/client-with-errors/get-static-props'
|
||||
)
|
||||
await next.patchFile(pageFile, content)
|
||||
await check(async () => {
|
||||
const { status } = await fetchViaHTTP(
|
||||
next.url,
|
||||
'/client-with-errors/get-static-props'
|
||||
)
|
||||
return status
|
||||
}, /200/)
|
||||
it('should error when page component export is not valid', async () => {
|
||||
const html = await next.render('/server-with-errors/page-export')
|
||||
expect(html).toContain(
|
||||
'The default export is not a React Component in page:'
|
||||
)
|
||||
})
|
||||
|
||||
expect(res.status).toBe(500)
|
||||
expect(await res.text()).toContain(
|
||||
'"getStaticProps\\" is not supported in app/'
|
||||
)
|
||||
})
|
||||
it('should throw an error when "use client" is on the top level but after other expressions', async () => {
|
||||
const pageFile = 'app/swc/use-client/page.js'
|
||||
const content = await next.readFile(pageFile)
|
||||
const uncomment = content.replace("// 'use client'", "'use client'")
|
||||
await next.patchFile(pageFile, uncomment)
|
||||
const res = await next.fetch('/swc/use-client')
|
||||
await next.patchFile(pageFile, content)
|
||||
|
||||
it('should error for styled-jsx imports on server side', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/server-with-errors/styled-jsx')
|
||||
expect(html).toContain(
|
||||
'This module cannot be imported from a Server Component module. It should only be used from a Client Component.'
|
||||
)
|
||||
})
|
||||
await check(async () => {
|
||||
const { status } = await next.fetch('/swc/use-client')
|
||||
return status
|
||||
}, /200/)
|
||||
|
||||
it('should error when page component export is not valid', async () => {
|
||||
const html = await renderViaHTTP(
|
||||
next.url,
|
||||
'/server-with-errors/page-export'
|
||||
)
|
||||
expect(html).toContain(
|
||||
'The default export is not a React Component in page:'
|
||||
)
|
||||
})
|
||||
|
||||
it('should throw an error when "use client" is on the top level but after other expressions', async () => {
|
||||
const pageFile = 'app/swc/use-client/page.js'
|
||||
const content = await next.readFile(pageFile)
|
||||
const uncomment = content.replace("// 'use client'", "'use client'")
|
||||
await next.patchFile(pageFile, uncomment)
|
||||
const res = await fetchViaHTTP(next.url, '/swc/use-client')
|
||||
await next.patchFile(pageFile, content)
|
||||
|
||||
await check(async () => {
|
||||
const { status } = await fetchViaHTTP(next.url, '/swc/use-client')
|
||||
return status
|
||||
}, /200/)
|
||||
|
||||
expect(res.status).toBe(500)
|
||||
expect(await res.text()).toContain(
|
||||
'directive must be placed before other expressions'
|
||||
)
|
||||
})
|
||||
})
|
||||
expect(res.status).toBe(500)
|
||||
expect(await res.text()).toContain(
|
||||
'directive must be placed before other expressions'
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,25 +1,20 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { createNextDescribe, FileRef } from 'e2e-utils'
|
||||
import fs from 'fs-extra'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import {
|
||||
fetchViaHTTP,
|
||||
findPort,
|
||||
initNextServerScript,
|
||||
killApp,
|
||||
fetchViaHTTP,
|
||||
} from 'next-test-utils'
|
||||
|
||||
describe('output: standalone with app dir', () => {
|
||||
let next: NextInstance
|
||||
|
||||
if (!(global as any).isNextStart) {
|
||||
it('should skip for non-next start', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
if (!(globalThis as any).isNextStart) {
|
||||
it('should skip for non-next start', () => {})
|
||||
} else {
|
||||
createNextDescribe(
|
||||
'output: standalone with app dir',
|
||||
{
|
||||
files: new FileRef(path.join(__dirname, 'app')),
|
||||
dependencies: {
|
||||
swr: '2.0.0-rc.0',
|
||||
|
@ -28,87 +23,88 @@ describe('output: standalone with app dir', () => {
|
|||
sass: 'latest',
|
||||
},
|
||||
skipStart: true,
|
||||
})
|
||||
},
|
||||
({ next }) => {
|
||||
beforeAll(async () => {
|
||||
await next.patchFile(
|
||||
'next.config.js',
|
||||
(await next.readFile('next.config.js')).replace('// output', 'output')
|
||||
)
|
||||
await next.start()
|
||||
})
|
||||
|
||||
await next.patchFile(
|
||||
'next.config.js',
|
||||
(await next.readFile('next.config.js')).replace('// output', 'output')
|
||||
)
|
||||
await next.start()
|
||||
})
|
||||
afterAll(async () => {
|
||||
await next.destroy()
|
||||
})
|
||||
it('should handle trace files correctly for route groups (nodejs only)', async () => {
|
||||
expect(next.cliOutput).not.toContain('Failed to copy traced files')
|
||||
const serverDirPath = path.join(
|
||||
next.testDir,
|
||||
'.next/standalone/.next/server'
|
||||
)
|
||||
for (const page of [
|
||||
'(newroot)/dashboard/another',
|
||||
'(newroot)/dashboard/project/[projectId]',
|
||||
'(rootonly)/dashboard/changelog',
|
||||
]) {
|
||||
const pagePath = path.join(serverDirPath, 'app', page)
|
||||
|
||||
if ((global as any).isNextStart) {
|
||||
it('should handle trace files correctly for route groups (nodejs only)', async () => {
|
||||
expect(next.cliOutput).not.toContain('Failed to copy traced files')
|
||||
const serverDirPath = path.join(
|
||||
next.testDir,
|
||||
'.next/standalone/.next/server'
|
||||
)
|
||||
for (const page of [
|
||||
'(newroot)/dashboard/another',
|
||||
'(newroot)/dashboard/project/[projectId]',
|
||||
'(rootonly)/dashboard/changelog',
|
||||
]) {
|
||||
const pagePath = path.join(serverDirPath, 'app', page)
|
||||
expect(
|
||||
await fs.pathExists(path.join(pagePath, 'page.js.nft.json'))
|
||||
).toBe(true)
|
||||
|
||||
expect(
|
||||
await fs.pathExists(path.join(pagePath, 'page.js.nft.json'))
|
||||
).toBe(true)
|
||||
const files = (
|
||||
await fs.readJSON(path.join(pagePath, 'page.js.nft.json'))
|
||||
).files as string[]
|
||||
|
||||
const files = (
|
||||
await fs.readJSON(path.join(pagePath, 'page.js.nft.json'))
|
||||
).files as string[]
|
||||
|
||||
for (const file of files) {
|
||||
expect(await fs.pathExists(path.join(pagePath, file))).toBe(true)
|
||||
for (const file of files) {
|
||||
expect(await fs.pathExists(path.join(pagePath, file))).toBe(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('should work correctly with output standalone', async () => {
|
||||
const tmpFolder = path.join(os.tmpdir(), 'next-standalone-' + Date.now())
|
||||
await fs.move(path.join(next.testDir, '.next/standalone'), tmpFolder)
|
||||
let server: any
|
||||
it('should work correctly with output standalone', async () => {
|
||||
const tmpFolder = path.join(
|
||||
os.tmpdir(),
|
||||
'next-standalone-' + Date.now()
|
||||
)
|
||||
await fs.move(path.join(next.testDir, '.next/standalone'), tmpFolder)
|
||||
let server: any
|
||||
|
||||
try {
|
||||
const testServer = path.join(tmpFolder, 'server.js')
|
||||
const appPort = await findPort()
|
||||
server = await initNextServerScript(
|
||||
testServer,
|
||||
/Listening on/,
|
||||
{
|
||||
...process.env,
|
||||
PORT: appPort,
|
||||
},
|
||||
undefined,
|
||||
{
|
||||
cwd: tmpFolder,
|
||||
try {
|
||||
const testServer = path.join(tmpFolder, 'server.js')
|
||||
const appPort = await findPort()
|
||||
server = await initNextServerScript(
|
||||
testServer,
|
||||
/Listening on/,
|
||||
{
|
||||
...process.env,
|
||||
PORT: appPort,
|
||||
},
|
||||
undefined,
|
||||
{
|
||||
cwd: tmpFolder,
|
||||
}
|
||||
)
|
||||
|
||||
for (const testPath of [
|
||||
'/',
|
||||
'/api/hello',
|
||||
'/blog/first',
|
||||
'/dashboard',
|
||||
'/dashboard/another',
|
||||
'/dashboard/changelog',
|
||||
'/dashboard/deployments/breakdown',
|
||||
'/dashboard/deployments/123',
|
||||
'/dashboard/hello',
|
||||
'/dashboard/project/123',
|
||||
'/catch-all/first',
|
||||
]) {
|
||||
const res = await fetchViaHTTP(appPort, testPath)
|
||||
expect(res.status).toBe(200)
|
||||
}
|
||||
} finally {
|
||||
if (server) await killApp(server)
|
||||
await fs.remove(tmpFolder)
|
||||
}
|
||||
)
|
||||
|
||||
for (const testPath of [
|
||||
'/',
|
||||
'/api/hello',
|
||||
'/blog/first',
|
||||
'/dashboard',
|
||||
'/dashboard/another',
|
||||
'/dashboard/changelog',
|
||||
'/dashboard/deployments/breakdown',
|
||||
'/dashboard/deployments/123',
|
||||
'/dashboard/hello',
|
||||
'/dashboard/project/123',
|
||||
'/catch-all/first',
|
||||
]) {
|
||||
const res = await fetchViaHTTP(appPort, testPath)
|
||||
expect(res.status).toBe(200)
|
||||
}
|
||||
} finally {
|
||||
if (server) await killApp(server)
|
||||
await fs.remove(tmpFolder)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,62 +1,42 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import path from 'path'
|
||||
import cheerio from 'cheerio'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
describe('app-dir trailingSlash handling', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'trailingslash')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
skipStart: true,
|
||||
createNextDescribe(
|
||||
'app-dir trailingSlash handling',
|
||||
{
|
||||
files: path.join(__dirname, 'trailingslash'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
it('should redirect route when requesting it directly', async () => {
|
||||
const res = await next.fetch(
|
||||
'/a',
|
||||
{},
|
||||
{
|
||||
redirect: 'manual',
|
||||
}
|
||||
)
|
||||
expect(res.status).toBe(308)
|
||||
expect(res.headers.get('location')).toBe(next.url + '/a/')
|
||||
})
|
||||
|
||||
await next.start()
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
it('should render link with trailing slash', async () => {
|
||||
const $ = await next.render$('/')
|
||||
expect($('#to-a-trailing-slash').attr('href')).toBe('/a/')
|
||||
})
|
||||
|
||||
it('should redirect route when requesting it directly', async () => {
|
||||
const res = await fetchViaHTTP(
|
||||
next.url,
|
||||
'/a',
|
||||
{},
|
||||
{
|
||||
redirect: 'manual',
|
||||
}
|
||||
)
|
||||
expect(res.status).toBe(308)
|
||||
expect(res.headers.get('location')).toBe(next.url + '/a/')
|
||||
})
|
||||
it('should redirect route when requesting it directly by browser', async () => {
|
||||
const browser = await next.browser('/a')
|
||||
expect(await browser.waitForElementByCss('#a-page').text()).toBe('A page')
|
||||
})
|
||||
|
||||
it('should render link with trailing slash', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/')
|
||||
const $ = cheerio.load(html)
|
||||
expect($('#to-a-trailing-slash').attr('href')).toBe('/a/')
|
||||
})
|
||||
|
||||
it('should redirect route when requesting it directly by browser', async () => {
|
||||
const browser = await webdriver(next.url, '/a')
|
||||
expect(await browser.waitForElementByCss('#a-page').text()).toBe('A page')
|
||||
})
|
||||
|
||||
it('should redirect route when clicking link', async () => {
|
||||
const browser = await webdriver(next.url, '/')
|
||||
await browser
|
||||
.elementByCss('#to-a-trailing-slash')
|
||||
.click()
|
||||
.waitForElementByCss('#a-page')
|
||||
expect(await browser.waitForElementByCss('#a-page').text()).toBe('A page')
|
||||
})
|
||||
})
|
||||
it('should redirect route when clicking link', async () => {
|
||||
const browser = await next.browser('/')
|
||||
await browser
|
||||
.elementByCss('#to-a-trailing-slash')
|
||||
.click()
|
||||
.waitForElementByCss('#a-page')
|
||||
expect(await browser.waitForElementByCss('#a-page').text()).toBe('A page')
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,7 +2,6 @@ import { createNext, FileRef } from 'e2e-utils'
|
|||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { check } from 'next-test-utils'
|
||||
import path from 'path'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
describe('vercel analytics', () => {
|
||||
const isDev = (global as any).isNextDev
|
||||
|
@ -48,7 +47,7 @@ describe('vercel analytics', () => {
|
|||
it('should send web vitals to Vercel analytics', async () => {
|
||||
let eventsCount = 0
|
||||
let countEvents = false
|
||||
const browser = await webdriver(next.url, '/client-nested', {
|
||||
const browser = await next.browser('/client-nested', {
|
||||
beforePageLoad(page) {
|
||||
page.route(
|
||||
'https://vitals.vercel-insights.com/v1/vitals',
|
||||
|
|
|
@ -1,31 +1,16 @@
|
|||
import { createNext, FileRef } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { renderViaHTTP } from 'next-test-utils'
|
||||
import { createNextDescribe } from 'e2e-utils'
|
||||
import path from 'path'
|
||||
import cheerio from 'cheerio'
|
||||
|
||||
describe('with babel', () => {
|
||||
if ((global as any).isNextDeploy) {
|
||||
it('should skip next deploy for now', () => {})
|
||||
return
|
||||
}
|
||||
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: new FileRef(path.join(__dirname, 'with-babel')),
|
||||
dependencies: {
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
createNextDescribe(
|
||||
'with babel',
|
||||
{
|
||||
files: path.join(__dirname, 'with-babel'),
|
||||
skipDeployment: true,
|
||||
},
|
||||
({ next }) => {
|
||||
it('should support babel in app dir', async () => {
|
||||
const $ = await next.render$('/')
|
||||
expect($('h1').text()).toBe('hello')
|
||||
})
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
it('should support babel in app dir', async () => {
|
||||
const html = await renderViaHTTP(next.url, '/')
|
||||
const $ = cheerio.load(html)
|
||||
expect($('h1').text()).toBe('hello')
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -35,8 +35,7 @@ describe('transpile packages', () => {
|
|||
afterAll(() => next.destroy())
|
||||
|
||||
const { isNextDeploy } = global as any
|
||||
const isReact17 = process.env.NEXT_TEST_REACT_VERSION === '^17'
|
||||
if (isNextDeploy || isReact17) {
|
||||
if (isNextDeploy) {
|
||||
it('should skip tests for next-deploy and react 17', () => {})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -221,7 +221,8 @@ export function createNextDescribe(
|
|||
|
||||
const nextProxy = new Proxy<NextInstance>({} as NextInstance, {
|
||||
get: function (_target, property) {
|
||||
return next[property]
|
||||
const prop = next[property]
|
||||
return typeof prop === 'function' ? prop.bind(next) : prop
|
||||
},
|
||||
})
|
||||
fn({
|
||||
|
|
|
@ -364,6 +364,9 @@ export class NextInstance {
|
|||
public async readFile(filename: string) {
|
||||
return fs.readFile(path.join(this.testDir, filename), 'utf8')
|
||||
}
|
||||
public async readJSON(filename: string) {
|
||||
return fs.readJSON(path.join(this.testDir, filename))
|
||||
}
|
||||
public async patchFile(filename: string, content: string) {
|
||||
const outputPath = path.join(this.testDir, filename)
|
||||
await fs.ensureDir(path.dirname(outputPath))
|
||||
|
|
Loading…
Reference in a new issue