/
mock-npm.js
131 lines (116 loc) · 3.54 KB
/
mock-npm.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
const npmlog = require('npmlog')
const procLog = require('../../lib/utils/proc-log-listener.js')
procLog.reset()
// In theory we shouldn't have to do this if all the tests were tearing down
// their listeners properly, we're still getting warnings even though
// perfStop() and procLog.reset() is in the teardown script. This silences the
// warnings for now
require('events').defaultMaxListeners = Infinity
const realLog = {}
for (const level in npmlog.levels) {
realLog[level] = npmlog[level]
}
const { title, execPath } = process
// Eventually this should default to having a prefix of an empty testdir, and
// awaiting npm.load() unless told not to (for npm tests for example). Ideally
// the prefix of an empty dir is inferred rather than explicitly set
const RealMockNpm = (t, otherMocks = {}) => {
const mock = {}
mock.logs = []
mock.outputs = []
mock.joinedOutput = () => {
return mock.outputs.map(o => o.join(' ')).join('\n')
}
mock.filteredLogs = title => mock.logs.filter(([t]) => t === title).map(([, , msg]) => msg)
const Npm = t.mock('../../lib/npm.js', otherMocks)
class MockNpm extends Npm {
constructor () {
super()
for (const level in npmlog.levels) {
npmlog[level] = (...msg) => {
mock.logs.push([level, ...msg])
const l = npmlog.level
npmlog.level = 'silent'
realLog[level](...msg)
npmlog.level = l
}
}
// npm.js tests need this restored to actually test this function!
mock.npmOutput = this.output
this.output = (...msg) => mock.outputs.push(msg)
}
}
mock.Npm = MockNpm
t.afterEach(() => {
mock.outputs.length = 0
mock.logs.length = 0
})
t.teardown(() => {
process.removeAllListeners('time')
process.removeAllListeners('timeEnd')
npmlog.record.length = 0
for (const level in npmlog.levels) {
npmlog[level] = realLog[level]
}
procLog.reset()
process.title = title
process.execPath = execPath
delete process.env.npm_command
delete process.env.COLOR
})
return mock
}
const realConfig = require('../../lib/utils/config')
// Basic npm fixture that you can give a config object that acts like
// npm.config You still need a separate flatOptions. Tests should migrate to
// using the real npm mock above
class MockNpm {
constructor (base = {}) {
this._mockOutputs = []
this.isMockNpm = true
this.base = base
const config = base.config || {}
for (const attr in base) {
if (attr !== 'config') {
this[attr] = base[attr]
}
}
this.flatOptions = base.flatOptions || {}
this.config = {
// for now just set `find` to what config.find should return
// this works cause `find` is not an existing config entry
find: (k) => ({ ...realConfig.defaults, ...config })[k],
get: (k) => ({ ...realConfig.defaults, ...config })[k],
set: (k, v) => config[k] = v,
list: [{ ...realConfig.defaults, ...config }],
}
if (!this.log) {
this.log = {
clearProgress: () => {},
disableProgress: () => {},
enableProgress: () => {},
http: () => {},
info: () => {},
levels: [],
notice: () => {},
pause: () => {},
silly: () => {},
verbose: () => {},
warn: () => {},
}
}
}
output (...msg) {
if (this.base.output) {
return this.base.output(msg)
}
this._mockOutputs.push(msg)
}
}
const FakeMockNpm = (base = {}) => {
return new MockNpm(base)
}
module.exports = {
fake: FakeMockNpm,
real: RealMockNpm,
}