2022-05-02 12:18:16 +02:00
|
|
|
import { createNext, FileRef } from 'e2e-utils'
|
|
|
|
import { NextInstance } from 'test/lib/next-modes/base'
|
|
|
|
import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils'
|
|
|
|
import path from 'path'
|
|
|
|
import cheerio from 'cheerio'
|
|
|
|
import webdriver from 'next-webdriver'
|
|
|
|
|
2022-05-03 15:12:06 +02:00
|
|
|
describe('root dir', () => {
|
2022-05-02 12:18:16 +02:00
|
|
|
let next: NextInstance
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
|
|
|
next = await createNext({
|
|
|
|
files: {
|
|
|
|
public: new FileRef(path.join(__dirname, 'app/public')),
|
|
|
|
pages: new FileRef(path.join(__dirname, 'app/pages')),
|
|
|
|
root: new FileRef(path.join(__dirname, 'app/root')),
|
|
|
|
'root.server.js': new FileRef(
|
|
|
|
path.join(__dirname, 'app/root.server.js')
|
|
|
|
),
|
|
|
|
'next.config.js': new FileRef(
|
|
|
|
path.join(__dirname, 'app/next.config.js')
|
|
|
|
),
|
|
|
|
},
|
|
|
|
dependencies: {
|
|
|
|
react: '18.0.0-rc.2',
|
|
|
|
'react-dom': '18.0.0-rc.2',
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
afterAll(() => next.destroy())
|
|
|
|
|
|
|
|
it('should serve from pages', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/')
|
|
|
|
expect(html).toContain('hello from pages/index')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should serve dynamic route from pages', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/blog/first')
|
|
|
|
expect(html).toContain('hello from pages/blog/[slug]')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should serve from public', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/hello.txt')
|
|
|
|
expect(html).toContain('hello world')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should serve from root', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/dashboard')
|
|
|
|
expect(html).toContain('hello from root/dashboard')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should include layouts when no direct parent layout', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/dashboard/integrations')
|
|
|
|
const $ = cheerio.load(html)
|
|
|
|
// Should not be nested in dashboard
|
|
|
|
expect($('h1').text()).toBe('Dashboard')
|
|
|
|
// Should include the page text
|
|
|
|
expect($('p').text()).toBe('hello from root/dashboard/integrations')
|
|
|
|
})
|
|
|
|
|
|
|
|
// TODO: why is this routable but /should-not-serve-server.server.js
|
|
|
|
it('should not include parent when not in parent directory with route in directory', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/dashboard/rootonly/hello')
|
|
|
|
const $ = cheerio.load(html)
|
|
|
|
|
|
|
|
// Should be nested in /root.js
|
|
|
|
expect($('html').hasClass('this-is-the-document-html')).toBeTruthy()
|
|
|
|
expect($('body').hasClass('this-is-the-document-body')).toBeTruthy()
|
|
|
|
|
|
|
|
// Should not be nested in dashboard
|
|
|
|
expect($('h1').text()).toBeFalsy()
|
|
|
|
|
|
|
|
// Should render the page text
|
|
|
|
expect($('p').text()).toBe('hello from root/dashboard/rootonly/hello')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should include parent document when no direct parent layout', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/dashboard/integrations')
|
|
|
|
const $ = cheerio.load(html)
|
|
|
|
|
|
|
|
// Root has to provide it's own document
|
|
|
|
expect($('html').hasClass('this-is-the-document-html')).toBeTruthy()
|
|
|
|
expect($('body').hasClass('this-is-the-document-body')).toBeTruthy()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not include parent when not in parent directory', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/dashboard/changelog')
|
|
|
|
const $ = cheerio.load(html)
|
|
|
|
// Should not be nested in dashboard
|
|
|
|
expect($('h1').text()).toBeFalsy()
|
|
|
|
// Should include the page text
|
|
|
|
expect($('p').text()).toBe('hello from root/dashboard/changelog')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should serve nested parent', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/dashboard/deployments/123')
|
|
|
|
const $ = cheerio.load(html)
|
|
|
|
// Should be nested in dashboard
|
|
|
|
expect($('h1').text()).toBe('Dashboard')
|
|
|
|
// Should be nested in deployments
|
|
|
|
expect($('h2').text()).toBe('Deployments hello')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should serve dynamic parameter', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/dashboard/deployments/123')
|
|
|
|
const $ = cheerio.load(html)
|
|
|
|
// Should include the page text with the parameter
|
|
|
|
expect($('p').text()).toBe(
|
|
|
|
'hello from root/dashboard/deployments/[id]. ID is: 123'
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should include document html and body', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/dashboard')
|
|
|
|
const $ = cheerio.load(html)
|
|
|
|
|
|
|
|
expect($('html').hasClass('this-is-the-document-html')).toBeTruthy()
|
|
|
|
expect($('body').hasClass('this-is-the-document-body')).toBeTruthy()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not serve when layout is provided but no folder index', async () => {
|
|
|
|
const res = await fetchViaHTTP(next.url, '/dashboard/deployments')
|
|
|
|
expect(res.status).toBe(404)
|
|
|
|
expect(await res.text()).toContain('This page could not be found')
|
|
|
|
})
|
|
|
|
|
|
|
|
// TODO: do we want to make this only work for /root or is it allowed
|
|
|
|
// to work for /pages as well?
|
2022-05-03 15:12:06 +02:00
|
|
|
it.skip('should match partial parameters', async () => {
|
2022-05-02 12:18:16 +02:00
|
|
|
const html = await renderViaHTTP(next.url, '/partial-match-123')
|
|
|
|
expect(html).toContain('hello from root/partial-match-[id]. ID is: 123')
|
|
|
|
})
|
|
|
|
|
2022-05-03 15:12:06 +02:00
|
|
|
// TODO: Implement
|
|
|
|
describe.skip('parallel routes', () => {
|
2022-05-02 12:18:16 +02:00
|
|
|
describe('conditional routes', () => {
|
|
|
|
it('should serve user page', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/conditional/tim')
|
|
|
|
expect(html).toContain('hello from user homepage')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should serve user teams page', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/conditional/tim/teams')
|
|
|
|
expect(html).toContain('hello from user/teams')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not serve teams page to user', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/conditional/tim/members')
|
|
|
|
expect(html).not.toContain('hello from team/members')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should serve team page', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/conditional/vercel')
|
|
|
|
expect(html).toContain('hello from team homepage')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should serve team members page', async () => {
|
|
|
|
const html = await renderViaHTTP(
|
|
|
|
next.url,
|
|
|
|
'/conditional/vercel/members'
|
|
|
|
)
|
|
|
|
expect(html).toContain('hello from team/members')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should provide both matches if both paths match', async () => {
|
|
|
|
const html = await renderViaHTTP(next.url, '/conditional/both')
|
|
|
|
expect(html).toContain('hello from team homepage')
|
|
|
|
expect(html).toContain('hello from user homepage')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should 404 based on getServerSideProps', async () => {
|
|
|
|
const res = await fetchViaHTTP(next.url, '/conditional/nonexistent')
|
|
|
|
expect(res.status).toBe(404)
|
|
|
|
expect(await res.text()).toContain('This page could not be found')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('server components', () => {
|
|
|
|
// TODO: why is this not servable but /dashboard+rootonly/hello.server.js
|
|
|
|
// should be? Seems like they both either should be servable or not
|
|
|
|
it('should not serve .server.js as a path', async () => {
|
|
|
|
// Without .server.js should serve
|
|
|
|
const html = await renderViaHTTP(next.url, '/should-not-serve-server')
|
|
|
|
expect(html).toContain('hello from root/should-not-serve-server')
|
|
|
|
|
|
|
|
// Should not serve `.server`
|
|
|
|
const res = await fetchViaHTTP(
|
|
|
|
next.url,
|
|
|
|
'/should-not-serve-server.server'
|
|
|
|
)
|
|
|
|
expect(res.status).toBe(404)
|
|
|
|
expect(await res.text()).toContain('This page could not be found')
|
|
|
|
|
|
|
|
// Should not serve `.server.js`
|
|
|
|
const res2 = await fetchViaHTTP(
|
|
|
|
next.url,
|
|
|
|
'/should-not-serve-server.server.js'
|
|
|
|
)
|
|
|
|
expect(res2.status).toBe(404)
|
|
|
|
expect(await res2.text()).toContain('This page could not be found')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not serve .client.js as a path', async () => {
|
|
|
|
// Without .client.js should serve
|
|
|
|
const html = await renderViaHTTP(next.url, '/should-not-serve-client')
|
|
|
|
expect(html).toContain('hello from root/should-not-serve-client')
|
|
|
|
|
|
|
|
// Should not serve `.client`
|
|
|
|
const res = await fetchViaHTTP(
|
|
|
|
next.url,
|
|
|
|
'/should-not-serve-client.client'
|
|
|
|
)
|
|
|
|
expect(res.status).toBe(404)
|
|
|
|
expect(await res.text()).toContain('This page could not be found')
|
|
|
|
|
|
|
|
// Should not serve `.client.js`
|
|
|
|
const res2 = await fetchViaHTTP(
|
|
|
|
next.url,
|
|
|
|
'/should-not-serve-client.client.js'
|
|
|
|
)
|
|
|
|
expect(res2.status).toBe(404)
|
|
|
|
expect(await res2.text()).toContain('This page could not be found')
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should serve shared component', async () => {
|
|
|
|
// Without .client.js should serve
|
|
|
|
const html = await renderViaHTTP(next.url, '/shared-component-route')
|
|
|
|
expect(html).toContain('hello from root/shared-component-route')
|
|
|
|
})
|
|
|
|
|
2022-05-03 15:12:06 +02:00
|
|
|
// TODO: implement
|
|
|
|
it.skip('should serve client component', async () => {
|
2022-05-02 12:18:16 +02:00
|
|
|
const html = await renderViaHTTP(next.url, '/client-component-route')
|
|
|
|
expect(html).toContain('hello from root/client-component-route. count: 0')
|
|
|
|
|
|
|
|
const browser = await webdriver(next.url, '/client-component-route')
|
|
|
|
// After hydration count should be 1
|
|
|
|
expect(await browser.elementByCss('p').text()).toBe(
|
|
|
|
'hello from root/client-component-route. count: 1'
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2022-05-03 15:12:06 +02:00
|
|
|
// TODO: implement
|
|
|
|
it.skip('should include client component layout with server component route', async () => {
|
2022-05-02 12:18:16 +02:00
|
|
|
const html = await renderViaHTTP(next.url, '/client-nested')
|
|
|
|
const $ = cheerio.load(html)
|
|
|
|
// Should not be nested in dashboard
|
|
|
|
expect($('h1').text()).toBe('Client Nested. Count: 0')
|
|
|
|
// Should include the page text
|
|
|
|
expect($('p').text()).toBe('hello from root/client-nested')
|
|
|
|
|
|
|
|
const browser = await webdriver(next.url, '/client-nested')
|
|
|
|
// After hydration count should be 1
|
|
|
|
expect(await browser.elementByCss('h1').text()).toBe(
|
|
|
|
'Client Nested. Count: 0'
|
|
|
|
)
|
|
|
|
|
|
|
|
// After hydration count should be 1
|
|
|
|
expect(await browser.elementByCss('h1').text()).toBe(
|
|
|
|
'hello from root/client-nested'
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|