rsnext/test/unit/isolated/require-page.test.ts
Javi Velasco 0de109baab
Refactor Page Paths utils and Middleware Plugin (#36576)
This PR brings some significant refactoring in preparation for upcoming middleware changes. Each commit can be reviewed independently, here is a summary of what each one does and the reasoning behind it:
- [Move pagesDir to next-dev-server](f2fe154c00) simply moves the `pagesDir` property to the dev server which is the only place where it is needed. Having it for every server is misleading.
- [Move (de)normalize page path utils to a file page-path-utils.ts](27cedf0871) Moves the functions to normalize and denormalize page paths to a single file that is intended to hold every utility function that transforms page paths. Since those are complementary it makes sense to have them together. I also added explanatory comments on why they are not idempotent and examples for input -> output that I find very useful.
- [Extract removePagePathTail](6b121332aa) This extracts a function to remove the tail on a page path (absolute or relative). I'm sure there will be other contexts where we can use it.
- [Extract getPagePaths and refactor findPageFile](cf2c7b842e) This extracts a function `getPagePaths` that is used to generate an array of paths to inspect when looking for a page file from `findPageFile`. Then it refactors such function to use it parallelizing lookups. This will allow us to print every path we look at when looking for a file which can be useful for debugging. It also adds a `flatten` helper. 
- [Refactor onDemandEntryHandler](4be685c37e) I've found this one quite difficult to understand so it is refactored to use some of the previously mentioned functions and make it easier to read.
- [Extract absolutePagePath util](3bc0783474) Extracts yet another util from the `next-dev-server` that transforms an absolute path into a page name. Of course it adds comments, parameters and examples.
- [Refactor MiddlewarePlugin](c595a2cc62) This is the most significant change. The logic here was very hard to understand so it is totally redistributed with comments. This also removes a global variable `ssrEntries` that was deprecated in favour of module metadata added to Webpack from loaders keeping less dependencies. It also adds types and makes a clear distinction between phases where we statically analyze the code, find metadata and generate the manifest file cc @shuding @huozhi 

EDIT: 
- [Split page path utils](158fb002d0) After seeing one of the utils was being used by the client while it was defined originally in the server, with this PR we are splitting the util into multiple files and moving it to `shared/lib` in order to make explicit that those can be also imported from client.
2022-04-30 11:19:27 +00:00

119 lines
3.3 KiB
TypeScript

/* eslint-env jest */
import { join } from 'path'
import { SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH } from 'next/constants'
import { normalizePagePath } from 'next/dist/shared/lib/page-path/normalize-page-path'
import {
requirePage,
getPagePath,
pageNotFoundError,
} from 'next/dist/server/require'
const sep = '/'
const distDir = join(__dirname, '_resolvedata')
const pathToBundles = join(
distDir,
SERVER_DIRECTORY,
CLIENT_STATIC_FILES_PATH,
'development',
'pages'
)
describe('pageNotFoundError', () => {
it('Should throw error with ENOENT code', () => {
expect.assertions(1)
try {
throw pageNotFoundError('test')
} catch (err) {
// eslint-disable-next-line jest/no-try-expect
expect(err.code).toBe('ENOENT')
}
})
})
describe('normalizePagePath', () => {
it('Should turn / into /index', () => {
expect(normalizePagePath('/')).toBe(`${sep}index`)
})
it('Should turn _error into /_error', () => {
expect(normalizePagePath('_error')).toBe(`${sep}_error`)
})
it('Should turn /abc into /abc', () => {
expect(normalizePagePath('/abc')).toBe(`${sep}abc`)
})
it('Should turn /abc/def into /abc/def', () => {
expect(normalizePagePath('/abc/def')).toBe(`${sep}abc${sep}def`)
})
it('Should throw on /../../test.js', () => {
expect(() => normalizePagePath('/../../test.js')).toThrow()
})
})
describe('getPagePath', () => {
it('Should not append /index to the / page', () => {
expect(() => getPagePath('/', distDir, false)).toThrow(
'Cannot find module for page: /'
)
})
it('Should prepend / when a page does not have it', () => {
const pagePath = getPagePath('_error', distDir, false)
expect(pagePath).toBe(join(pathToBundles, `${sep}_error.js`))
})
it('Should throw with paths containing ../', () => {
expect(() => getPagePath('/../../package.json', distDir, false)).toThrow()
})
})
describe('requirePage', () => {
it('Should not find page /index when using /', async () => {
await expect(() => requirePage('/', distDir, false)).toThrow(
'Cannot find module for page: /'
)
})
it('Should require /index.js when using /index', async () => {
const page = await requirePage('/index', distDir, false)
expect(page.test).toBe('hello')
})
it('Should require /world.js when using /world', async () => {
const page = await requirePage('/world', distDir, false)
expect(page.test).toBe('world')
})
it('Should throw when using /../../test.js', async () => {
expect.assertions(1)
try {
await requirePage('/../../test', distDir, false)
} catch (err) {
// eslint-disable-next-line jest/no-try-expect
expect(err.code).toBe('ENOENT')
}
})
it('Should throw when using non existent pages like /non-existent.js', async () => {
expect.assertions(1)
try {
await requirePage('/non-existent', distDir, false)
} catch (err) {
// eslint-disable-next-line jest/no-try-expect
expect(err.code).toBe('ENOENT')
}
})
it('Should bubble up errors in the child component', async () => {
expect.assertions(1)
try {
await requirePage('/non-existent-child', distDir, false)
} catch (err) {
// eslint-disable-next-line jest/no-try-expect
expect(err.code).toBe('MODULE_NOT_FOUND')
}
})
})