Skip to content

Commit 1097d99

Browse files
committed
chore(libnpmexec): refactor tests to use mock registry
1 parent 984bc55 commit 1097d99

File tree

12 files changed

+1164
-1754
lines changed

12 files changed

+1164
-1754
lines changed
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
2+
const fs = require('fs/promises')
3+
const { existsSync } = require('fs')
4+
const { resolve, extname, join } = require('path')
5+
const _binLinks = require('bin-links')
6+
const MockRegistry = require('@npmcli/mock-registry')
7+
const justExtend = require('just-extend')
8+
const set = require('just-safe-set')
9+
10+
const merge = (...args) => justExtend(true, ...args)
11+
12+
const DEFAULT_BIN_FILE = 'bin-file.js'
13+
14+
const createPkg = ({
15+
name = '@npmcli/create-index',
16+
bin,
17+
files,
18+
versions = [],
19+
version,
20+
localVersion,
21+
}) => {
22+
if (localVersion && !versions.includes(localVersion)) {
23+
versions = [...versions, localVersion]
24+
}
25+
if (version && !versions.includes(version)) {
26+
versions = [...versions, version]
27+
}
28+
29+
const defaultBinName = name.includes('/') ? name.split('/')[1] : name
30+
31+
if (!bin) {
32+
bin = {
33+
[defaultBinName]: `./${DEFAULT_BIN_FILE}`,
34+
}
35+
}
36+
37+
const pkgsArr = versions.map((v) => ({
38+
name,
39+
version: v,
40+
bin,
41+
}))
42+
43+
const pkgs = {}
44+
const tarballs = {}
45+
const fixtures = {}
46+
47+
for (const pkg of pkgsArr) {
48+
pkgs[pkg.version] = pkg
49+
}
50+
51+
if (localVersion) {
52+
set(fixtures, ['node_modules', ...name.split('/')], {
53+
'package.json': pkgs[localVersion],
54+
...files || {
55+
[DEFAULT_BIN_FILE]: { key: name, value: `local-${localVersion}` },
56+
},
57+
})
58+
fixtures['package.json'] = {
59+
name: 'pkg',
60+
version: '9.9.9',
61+
dependencies: {
62+
[name]: `^${localVersion}`,
63+
},
64+
}
65+
}
66+
67+
for (const pkg of pkgsArr) {
68+
const fixturePath = `${pkg.name}-${pkg.version}`.replace('/', '-')
69+
set(fixtures, ['packages', fixturePath], {
70+
'package.json': pkg,
71+
...files || {
72+
[DEFAULT_BIN_FILE]: { key: pkg.name, value: `packages-${pkg.version}` },
73+
},
74+
})
75+
tarballs[pkg.version] = join('packages', fixturePath)
76+
}
77+
78+
return {
79+
pkg: pkgsArr[0],
80+
pkgs,
81+
fixtures,
82+
package: ({ registry, path, tarballs: tgz = versions, ...opts }) => registry.package({
83+
times: 2,
84+
manifest: registry.manifest({ name: pkgsArr[0].name, packuments: pkgsArr }),
85+
tarballs: tgz.reduce((acc, v) => {
86+
acc[v] = resolve(path, tarballs[v])
87+
return acc
88+
}, {}),
89+
...opts,
90+
}),
91+
}
92+
}
93+
94+
const createTestdir = (...objs) => {
95+
const testdirHelper = (obj, ancestors = []) => {
96+
for (const [key, value] of Object.entries(obj)) {
97+
if (extname(key) === '.json') {
98+
obj[key] = JSON.stringify(value, null, 2)
99+
} else if (extname(key) === '.js' || ancestors.slice(-2).join('/') === 'node_modules/.bin') {
100+
// a js or bin file is converted to a bin script that writes a file
101+
obj[key] = `#!/usr/bin/env node\nrequire('fs').writeFileSync(
102+
'output-${value.key.replace('/', '-')}',
103+
JSON.stringify({
104+
value: '${value.value}',
105+
args: process.argv.slice(2),
106+
created: '${[...ancestors, key].join('/')}',
107+
})
108+
)`
109+
} else if (value && typeof value === 'object') {
110+
obj[key] = testdirHelper(value, [...ancestors, key])
111+
} else {
112+
obj[key] = value
113+
}
114+
}
115+
return obj
116+
}
117+
118+
return testdirHelper(merge(...objs))
119+
}
120+
121+
const setup = (t, {
122+
pkg,
123+
testdir: _testdir = {},
124+
mocks,
125+
global,
126+
debug,
127+
execPath,
128+
defaults = true,
129+
} = {}) => {
130+
const registry = new MockRegistry({
131+
tap: t,
132+
registry: 'http://smoke-test-registry.club/',
133+
strict: true,
134+
debug,
135+
})
136+
137+
if (debug) {
138+
process.on('log', console.error)
139+
t.teardown(() => process.off('log', console.error))
140+
}
141+
142+
const { node_modules: testdirNm, ...testdir } = _testdir
143+
const fullTestdir = createTestdir({
144+
cache: {},
145+
npxCache: {},
146+
...testdirNm ?
147+
global ? {
148+
global: {
149+
node_modules: {
150+
'.bin': {},
151+
...testdirNm,
152+
},
153+
},
154+
} : {
155+
node_modules: {
156+
'.bin': {},
157+
...testdirNm,
158+
},
159+
}
160+
: {},
161+
}, testdir)
162+
163+
// quick way to remove undefined and null values that we merged
164+
// in to not write certain directories
165+
const path = t.testdir(JSON.parse(JSON.stringify(fullTestdir, (_, v) => {
166+
if (v === null) {
167+
return
168+
}
169+
if (typeof v === 'string') {
170+
return v.replace(/\{REGISTRY\}/g, registry.origin)
171+
}
172+
return v
173+
}))
174+
)
175+
176+
const cache = resolve(path, 'cache')
177+
const npxCache = resolve(path, 'npxCache')
178+
const nodeModules = resolve(path, global ? 'global/node_modules' : 'node_modules')
179+
180+
const defaultOpts = {
181+
call: '',
182+
color: false,
183+
localBin: '',
184+
globalBin: '',
185+
packages: [],
186+
scriptShell: undefined,
187+
yes: true,
188+
path,
189+
runPath: path,
190+
}
191+
192+
const baseOpts = {
193+
audit: false,
194+
registry: registry.origin + '/',
195+
...existsSync(cache) ? { cache } : {},
196+
...existsSync(npxCache) ? { npxCache } : {},
197+
...global ? {
198+
globalBin: resolve(path, nodeModules, '.bin'),
199+
globalPath: resolve(path, 'global'),
200+
} : {},
201+
}
202+
203+
return {
204+
path,
205+
registry,
206+
chmod: async (chmodPath) => {
207+
if (!chmodPath) {
208+
for (const p of [].concat(pkg)) {
209+
await fs.chmod(resolve(path, nodeModules, p.name, DEFAULT_BIN_FILE), 0o775)
210+
}
211+
return
212+
}
213+
return fs.chmod(resolve(path, chmodPath), 0o775)
214+
},
215+
binLinks: async (binPkg) => {
216+
if (!binPkg) {
217+
for (const p of [].concat(pkg)) {
218+
await _binLinks({
219+
pkg: p,
220+
path: resolve(path, nodeModules, p.name),
221+
})
222+
}
223+
return
224+
}
225+
await _binLinks({
226+
pkg: binPkg,
227+
path: resolve(path, nodeModules, binPkg.name),
228+
})
229+
},
230+
readOutput: async (outputPath, { root = path } = {}) => {
231+
if (!outputPath) {
232+
outputPath = pkg.name.replace('/', '-')
233+
}
234+
return fs.readFile(resolve(root, `output-${outputPath}`), 'utf-8').then(r => JSON.parse(r))
235+
},
236+
rmOutput: (outputPath, { root = path } = {}) => {
237+
if (!outputPath) {
238+
outputPath = pkg.name.replace('/', '-')
239+
}
240+
return fs.rm(resolve(root, `output-${outputPath}`))
241+
},
242+
rimraf: (p) => fs.rm(resolve(path, p), { recursive: true, force: true }),
243+
exec: (opts) => t.mock(execPath || '../../lib/index.js', mocks)({
244+
...defaults ? {
245+
...defaultOpts,
246+
path,
247+
runPath: path,
248+
} : {},
249+
...baseOpts,
250+
...opts,
251+
}),
252+
}
253+
}
254+
255+
module.exports.setup = setup
256+
module.exports.createPkg = createPkg
257+
module.exports.merge = merge

0 commit comments

Comments
 (0)