forked from vercel/next.js
/
index.test.js
172 lines (151 loc) · 5.79 KB
/
index.test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/* eslint-env jest */
import execa from 'execa'
import fs from 'fs-extra'
import os from 'os'
import path from 'path'
import { findPort, killProcess, renderViaHTTP, waitFor } from 'next-test-utils'
import webdriver from 'next-webdriver'
const packagesDir = path.join(__dirname, '..', '..', '..', '..', 'packages')
const APP_DIRS = {
app: path.join(__dirname, '..', 'app'),
'app-multi-page': path.join(__dirname, '..', 'app-multi-page'),
}
const runNpm = (cwd, ...args) => execa('npm', [...args], { cwd })
const runPnpm = (cwd, ...args) => execa('npx', ['pnpm', ...args], { cwd })
async function usingTempDir(fn) {
const folder = path.join(os.tmpdir(), Math.random().toString(36).substring(2))
await fs.mkdirp(folder)
try {
return await fn(folder)
} finally {
await fs.remove(folder)
}
}
/**
* Using 'npm pack', create a tarball of the given package in
* directory `pkg` and write it to `cwd`.
*
* `pkg` is relative to the monorepo 'packages/' directory.
*
* Return the absolute path to the tarball.
*/
async function pack(cwd, pkg) {
const pkgDir = path.join(packagesDir, pkg)
const { stdout } = await runNpm(
cwd,
'pack',
'--ignore-scripts', // Skip the prepublish script
path.join(packagesDir, pkg)
)
const tarballFilename = stdout.match(/.*\.tgz/)[0]
if (!tarballFilename) {
throw new Error(
`npm failed to pack "next" package tarball in directory ${pkgDir}.`
)
}
return path.join(cwd, tarballFilename)
}
/**
* Create a Next.js app in a temporary directory, and install dependencies with pnpm.
*
* "next" and its monorepo dependencies are installed by `npm pack`-ing tarballs from
* 'next.js/packages/*', because installing "next" directly via
* `pnpm add path/to/next.js/packages/next` results in a symlink:
* 'app/node_modules/next' -> 'path/to/next.js/packages/next'.
* This is undesired since modules inside "next" would be resolved to the
* next.js monorepo 'node_modules' and lead to false test results;
* installing from a tarball avoids this issue.
*
* The "next" package's monorepo dependencies (e.g. "@next/env", "@next/polyfill-module")
* are not bundled with `npm pack next.js/packages/next`,
* so they need to be packed individually.
* To ensure that they are installed upon installing "next", a package.json "pnpm.overrides"
* field is used to override these dependency paths at install time.
*/
async function usingPnpmCreateNextApp(appDir, fn) {
await usingTempDir(async (tempDir) => {
const nextTarballPath = await pack(tempDir, 'next')
const dependencyTarballPaths = {
'@next/env': await pack(tempDir, 'next-env'),
'@next/polyfill-module': await pack(tempDir, 'next-polyfill-module'),
'@next/polyfill-nomodule': await pack(tempDir, 'next-polyfill-nomodule'),
'@next/react-dev-overlay': await pack(tempDir, 'react-dev-overlay'),
'@next/react-refresh-utils': await pack(tempDir, 'react-refresh-utils'),
}
const tempAppDir = path.join(tempDir, 'app')
await fs.copy(appDir, tempAppDir)
// Inject dependency tarball paths into a "pnpm.overrides" field in package.json,
// so that they are installed from packed tarballs rather than from the npm registry.
const packageJsonPath = path.join(tempAppDir, 'package.json')
const overrides = {}
for (const [dependency, tarballPath] of Object.entries(
dependencyTarballPaths
)) {
overrides[dependency] = `file:${tarballPath}`
}
const packageJsonWithOverrides = {
...(await fs.readJson(packageJsonPath)),
pnpm: { overrides },
}
await fs.writeFile(
packageJsonPath,
JSON.stringify(packageJsonWithOverrides, null, 2)
)
await runPnpm(tempAppDir, 'install')
await runPnpm(tempAppDir, 'add', `next@${nextTarballPath}`)
await fs.copy(
path.join(__dirname, '../../../../packages/next/native'),
path.join(tempAppDir, 'node_modules/next/native')
)
await fn(tempAppDir)
})
}
describe('pnpm support', () => {
it('should build with dependencies installed via pnpm', async () => {
await usingPnpmCreateNextApp(APP_DIRS['app'], async (appDir) => {
expect(
await fs.pathExists(path.join(appDir, 'pnpm-lock.yaml'))
).toBeTruthy()
const packageJsonPath = path.join(appDir, 'package.json')
const packageJson = await fs.readJson(packageJsonPath)
expect(packageJson.dependencies['next']).toMatch(/^file:/)
for (const dependency of [
'@next/env',
'@next/polyfill-module',
'@next/polyfill-nomodule',
'@next/react-dev-overlay',
'@next/react-refresh-utils',
]) {
expect(packageJson.pnpm.overrides[dependency]).toMatch(/^file:/)
}
const { stdout, stderr } = await runPnpm(appDir, 'run', 'build')
console.log(stdout, stderr)
expect(stdout).toMatch(/Compiled successfully/)
})
})
it('should execute client-side JS on each page', async () => {
await usingPnpmCreateNextApp(APP_DIRS['app-multi-page'], async (appDir) => {
const { stdout, stderr } = await runPnpm(appDir, 'run', 'build')
console.log(stdout, stderr)
expect(stdout).toMatch(/Compiled successfully/)
let appPort
let appProcess
let browser
try {
appPort = await findPort()
appProcess = runPnpm(appDir, 'run', 'start', '--', '--port', appPort)
await waitFor(5000)
await renderViaHTTP(appPort, '/')
browser = await webdriver(appPort, '/', false)
expect(await browser.waitForElementByCss('#world').text()).toBe('World')
await browser.close()
browser = await webdriver(appPort, '/about', false)
expect(await browser.waitForElementByCss('#world').text()).toBe('World')
await browser.close()
} finally {
await killProcess(appProcess.pid)
await waitFor(5000)
}
})
})
})