@oclif/test 4.0.1-beta.1 → 4.0.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -225
- package/lib/index.d.ts +44 -14
- package/lib/index.js +98 -54
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -6,230 +6,33 @@ test helpers for oclif CLIs
|
|
|
6
6
|
[](https://npmjs.org/package/@oclif/test)
|
|
7
7
|
[](https://github.com/oclif/test/blob/main/package.json)
|
|
8
8
|
|
|
9
|
+
## Migration
|
|
10
|
+
|
|
11
|
+
See the [V4 Migration Guide](./MIGRATION.md) if you are migrating from v3 or older.
|
|
12
|
+
|
|
9
13
|
## Usage
|
|
10
14
|
|
|
11
|
-
`@oclif/test`
|
|
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
|
-
If you would like to run the same test without using `@oclif/test`:
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
import {Config, ux} from '@oclif/core'
|
|
40
|
-
import {expect} from 'chai'
|
|
41
|
-
import {join} from 'node:path'
|
|
42
|
-
import {SinonSandbox, SinonStub, createSandbox} from 'sinon'
|
|
43
|
-
|
|
44
|
-
const root = join(__dirname, 'fixtures/test-cli')
|
|
45
|
-
describe('non-fancy test', () => {
|
|
46
|
-
let sandbox: SinonSandbox
|
|
47
|
-
let config: Config
|
|
48
|
-
let stdoutStub: SinonStub
|
|
49
|
-
|
|
50
|
-
beforeEach(async () => {
|
|
51
|
-
sandbox = createSandbox()
|
|
52
|
-
stdoutStub = sandbox.stub(ux.write, 'stdout')
|
|
53
|
-
config = await Config.load({root})
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
afterEach(async () => {
|
|
57
|
-
sandbox.restore()
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('should run command from the given directory', async () => {
|
|
61
|
-
const {name} = await config.runCommand<{name: string}>('foo:bar')
|
|
62
|
-
expect(stdoutStub.calledWith('hello world!\n')).to.be.true
|
|
63
|
-
expect(config.root).to.equal(root)
|
|
64
|
-
expect(name).to.equal('world')
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### `.command()`
|
|
70
|
-
|
|
71
|
-
`.command()` let's you run a command from your CLI.
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
import {expect, test} from '@oclif/test'
|
|
75
|
-
|
|
76
|
-
describe('hello world', () => {
|
|
77
|
-
test
|
|
78
|
-
.stdout()
|
|
79
|
-
.command(['hello:world'])
|
|
80
|
-
.it('runs hello world cmd', (ctx) => {
|
|
81
|
-
expect(ctx.stdout).to.contain('hello world!')
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
For a [single command cli](https://oclif.io/docs/single_command_cli) you would provide `'.'` as the command. For instance:
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
import {expect, test} from '@oclif/test'
|
|
90
|
-
|
|
91
|
-
describe('hello world', () => {
|
|
92
|
-
test
|
|
93
|
-
.stdout()
|
|
94
|
-
.command(['.'])
|
|
95
|
-
.it('runs hello world cmd', (ctx) => {
|
|
96
|
-
expect(ctx.stdout).to.contain('hello world!')
|
|
97
|
-
})
|
|
98
|
-
})
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
If you would like to run the same test without using `@oclif/test`:
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
import {Config, ux} from '@oclif/core'
|
|
105
|
-
import {expect} from 'chai'
|
|
106
|
-
import {SinonSandbox, SinonStub, createSandbox} from 'sinon'
|
|
107
|
-
|
|
108
|
-
describe('non-fancy test', () => {
|
|
109
|
-
let sandbox: SinonSandbox
|
|
110
|
-
let config: Config
|
|
111
|
-
let stdoutStub: SinonStub
|
|
112
|
-
|
|
113
|
-
beforeEach(async () => {
|
|
114
|
-
sandbox = createSandbox()
|
|
115
|
-
stdoutStub = sandbox.stub(ux.write, 'stdout')
|
|
116
|
-
config = await Config.load({root: process.cwd()})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
afterEach(async () => {
|
|
120
|
-
sandbox.restore()
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('should run command', async () => {
|
|
124
|
-
// use '.' for a single command CLI
|
|
125
|
-
const {name} = await config.runCommand<{name: string}>('hello:world')
|
|
126
|
-
expect(stdoutStub.calledWith('hello world!\n')).to.be.true
|
|
127
|
-
expect(name).to.equal('world')
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### `.exit()`
|
|
133
|
-
|
|
134
|
-
`.exit()` let's you test that a command exited with a certain exit code.
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
import {join} from 'node:path'
|
|
138
|
-
import {expect, test} from '@oclif/test'
|
|
139
|
-
|
|
140
|
-
describe('exit', () => {
|
|
141
|
-
test
|
|
142
|
-
.loadConfig()
|
|
143
|
-
.stdout()
|
|
144
|
-
.command(['hello:world', '--code=101'])
|
|
145
|
-
.exit(101)
|
|
146
|
-
.do((output) => expect(output.stdout).to.equal('exiting with code 101\n'))
|
|
147
|
-
.it('should exit with code 101')
|
|
148
|
-
})
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
If you would like to run the same test without using `@oclif/test`:
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
import {Config, Errors, ux} from '@oclif/core'
|
|
155
|
-
import {expect} from 'chai'
|
|
156
|
-
import {SinonSandbox, createSandbox} from 'sinon'
|
|
157
|
-
|
|
158
|
-
describe('non-fancy test', () => {
|
|
159
|
-
let sandbox: SinonSandbox
|
|
160
|
-
let config: Config
|
|
161
|
-
|
|
162
|
-
beforeEach(async () => {
|
|
163
|
-
sandbox = createSandbox()
|
|
164
|
-
sandbox.stub(ux.write, 'stdout')
|
|
165
|
-
config = await Config.load({root: process.cwd()})
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
afterEach(async () => {
|
|
169
|
-
sandbox.restore()
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
it('should run command from the given directory', async () => {
|
|
173
|
-
try {
|
|
174
|
-
await config.runCommand('.')
|
|
175
|
-
throw new Error('Expected CLIError to be thrown')
|
|
176
|
-
} catch (error) {
|
|
177
|
-
if (error instanceof Errors.CLIError) {
|
|
178
|
-
expect(error.oclif.exit).to.equal(101)
|
|
179
|
-
} else {
|
|
180
|
-
throw error
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
})
|
|
184
|
-
})
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### `.hook()`
|
|
188
|
-
|
|
189
|
-
`.hook()` let's you test a hook in your CLI.
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
import {join} from 'node:path'
|
|
193
|
-
|
|
194
|
-
import {expect, test} from '@oclif/test'
|
|
195
|
-
|
|
196
|
-
const root = join(__dirname, 'fixtures/test-cli')
|
|
197
|
-
|
|
198
|
-
describe('hooks', () => {
|
|
199
|
-
test
|
|
200
|
-
.loadConfig({root})
|
|
201
|
-
.stdout()
|
|
202
|
-
.hook('foo', {argv: ['arg']}, {root})
|
|
203
|
-
.do((output) => expect(output.stdout).to.equal('foo hook args: arg\n'))
|
|
204
|
-
.it('should run hook')
|
|
205
|
-
})
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
If you would like to run the same test without using `@oclif/test`:
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
import {Config, ux} from '@oclif/core'
|
|
212
|
-
import {expect} from 'chai'
|
|
213
|
-
import {SinonSandbox, SinonStub, createSandbox} from 'sinon'
|
|
214
|
-
|
|
215
|
-
describe('non-fancy test', () => {
|
|
216
|
-
let sandbox: SinonSandbox
|
|
217
|
-
let config: Config
|
|
218
|
-
let stdoutStub: SinonStub
|
|
219
|
-
|
|
220
|
-
beforeEach(async () => {
|
|
221
|
-
sandbox = createSandbox()
|
|
222
|
-
stdoutStub = sandbox.stub(ux.write, 'stdout')
|
|
223
|
-
config = await Config.load({root: process.cwd()})
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
afterEach(async () => {
|
|
227
|
-
sandbox.restore()
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
it('should run hook', async () => {
|
|
231
|
-
const {name} = await config.runHook('foo', {argv: ['arg']})
|
|
232
|
-
expect(stdoutStub.calledWith('foo hook args: arg\n')).to.be.true
|
|
233
|
-
})
|
|
234
|
-
})
|
|
235
|
-
```
|
|
15
|
+
`@oclif/test` provides a handful of utilities that make it easy to test your [oclif](https://oclif.io) CLI.
|
|
16
|
+
|
|
17
|
+
### `captureOutput`
|
|
18
|
+
|
|
19
|
+
`captureOutput` allows you to get the stdout, stderr, return value, and error of the callback you provide it. This makes it possible to assert that certain strings were printed to stdout and stderr or that the callback failed with the expected error or succeeded with the expected result.
|
|
20
|
+
|
|
21
|
+
**Options**
|
|
22
|
+
|
|
23
|
+
- `print` - Print everything that goes to stdout and stderr.
|
|
24
|
+
- `stripAnsi` - Strip ansi codes from everything that goes to stdout and stderr. Defaults to true.
|
|
25
|
+
|
|
26
|
+
See the [tests](./test/capture-output.test.ts) for example usage.
|
|
27
|
+
|
|
28
|
+
### `runCommand`
|
|
29
|
+
|
|
30
|
+
`runCommand` allows you to get the stdout, stderr, return value, and error of a command in your CLI.
|
|
31
|
+
|
|
32
|
+
See the [tests](./test/run-command.test.ts) for example usage.
|
|
33
|
+
|
|
34
|
+
### `runHook`
|
|
35
|
+
|
|
36
|
+
`runHook` allows you to get the stdout, stderr, return value, and error of a hook in your CLI.
|
|
37
|
+
|
|
38
|
+
See the [tests](./test/run-hook.test.ts) for example usage.
|
package/lib/index.d.ts
CHANGED
|
@@ -3,22 +3,52 @@ type CaptureOptions = {
|
|
|
3
3
|
print?: boolean;
|
|
4
4
|
stripAnsi?: boolean;
|
|
5
5
|
};
|
|
6
|
-
|
|
6
|
+
type CaptureResult<T> = {
|
|
7
7
|
error?: Error & Partial<Errors.CLIError>;
|
|
8
8
|
result?: T;
|
|
9
9
|
stderr: string;
|
|
10
10
|
stdout: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Capture the stderr and stdout output of a function
|
|
14
|
+
* @param fn async function to run
|
|
15
|
+
* @param opts options
|
|
16
|
+
* - print: Whether to print the output to the console
|
|
17
|
+
* - stripAnsi: Whether to strip ANSI codes from the output
|
|
18
|
+
* @returns {Promise<CaptureResult<T>>} Captured output
|
|
19
|
+
* - error: Error object if the function throws an error
|
|
20
|
+
* - result: Result of the function if it returns a value and succeeds
|
|
21
|
+
* - stderr: Captured stderr output
|
|
22
|
+
* - stdout: Captured stdout output
|
|
23
|
+
*/
|
|
24
|
+
export declare function captureOutput<T>(fn: () => Promise<unknown>, opts?: CaptureOptions): Promise<CaptureResult<T>>;
|
|
25
|
+
/**
|
|
26
|
+
* Capture the stderr and stdout output of a command in your CLI
|
|
27
|
+
* @param args Command arguments, e.g. `['my:command', '--flag']` or `'my:command --flag'`
|
|
28
|
+
* @param loadOpts options for loading oclif `Config`
|
|
29
|
+
* @param captureOpts options for capturing the output
|
|
30
|
+
* - print: Whether to print the output to the console
|
|
31
|
+
* - stripAnsi: Whether to strip ANSI codes from the output
|
|
32
|
+
* @returns {Promise<CaptureResult<T>>} Captured output
|
|
33
|
+
* - error: Error object if the command throws an error
|
|
34
|
+
* - result: Result of the command if it returns a value and succeeds
|
|
35
|
+
* - stderr: Captured stderr output
|
|
36
|
+
* - stdout: Captured stdout output
|
|
37
|
+
*/
|
|
38
|
+
export declare function runCommand<T>(args: string | string[], loadOpts?: Interfaces.LoadOptions, captureOpts?: CaptureOptions): Promise<CaptureResult<T>>;
|
|
39
|
+
/**
|
|
40
|
+
* Capture the stderr and stdout output of a hook in your CLI
|
|
41
|
+
* @param hook Hook name
|
|
42
|
+
* @param options options to pass to the hook
|
|
43
|
+
* @param loadOpts options for loading oclif `Config`
|
|
44
|
+
* @param captureOpts options for capturing the output
|
|
45
|
+
* - print: Whether to print the output to the console
|
|
46
|
+
* - stripAnsi: Whether to strip ANSI codes from the output
|
|
47
|
+
* @returns {Promise<CaptureResult<T>>} Captured output
|
|
48
|
+
* - error: Error object if the hook throws an error
|
|
49
|
+
* - result: Result of the hook if it returns a value and succeeds
|
|
50
|
+
* - stderr: Captured stderr output
|
|
51
|
+
* - stdout: Captured stdout output
|
|
52
|
+
*/
|
|
53
|
+
export declare function runHook<T>(hook: string, options: Record<string, unknown>, loadOpts?: Interfaces.LoadOptions, captureOpts?: CaptureOptions): Promise<CaptureResult<T>>;
|
|
24
54
|
export {};
|
package/lib/index.js
CHANGED
|
@@ -1,57 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
27
|
};
|
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
29
|
exports.runHook = exports.runCommand = exports.captureOutput = void 0;
|
|
7
30
|
const core_1 = require("@oclif/core");
|
|
8
|
-
const ansis_1 = __importDefault(require("ansis"));
|
|
9
31
|
const debug_1 = __importDefault(require("debug"));
|
|
10
32
|
const node_path_1 = require("node:path");
|
|
11
33
|
const debug = (0, debug_1.default)('oclif-test');
|
|
12
|
-
const RECORD_OPTIONS = {
|
|
13
|
-
print: false,
|
|
14
|
-
stripAnsi: true,
|
|
15
|
-
};
|
|
16
|
-
const originals = {
|
|
17
|
-
NODE_ENV: process.env.NODE_ENV,
|
|
18
|
-
stderr: process.stderr.write,
|
|
19
|
-
stdout: process.stdout.write,
|
|
20
|
-
};
|
|
21
|
-
const output = {
|
|
22
|
-
stderr: [],
|
|
23
|
-
stdout: [],
|
|
24
|
-
};
|
|
25
|
-
function mockedStdout(str, encoding, cb) {
|
|
26
|
-
output.stdout.push(str);
|
|
27
|
-
if (!RECORD_OPTIONS.print)
|
|
28
|
-
return true;
|
|
29
|
-
if (typeof encoding === 'string') {
|
|
30
|
-
return originals.stdout.bind(process.stdout)(str, encoding, cb);
|
|
31
|
-
}
|
|
32
|
-
return originals.stdout.bind(process.stdout)(str, cb);
|
|
33
|
-
}
|
|
34
|
-
function mockedStderr(str, encoding, cb) {
|
|
35
|
-
output.stderr.push(str);
|
|
36
|
-
if (!RECORD_OPTIONS.print)
|
|
37
|
-
return true;
|
|
38
|
-
if (typeof encoding === 'string') {
|
|
39
|
-
return originals.stdout.bind(process.stderr)(str, encoding, cb);
|
|
40
|
-
}
|
|
41
|
-
return originals.stdout.bind(process.stderr)(str, cb);
|
|
42
|
-
}
|
|
43
|
-
const restore = () => {
|
|
44
|
-
process.stderr.write = originals.stderr;
|
|
45
|
-
process.stdout.write = originals.stdout;
|
|
46
|
-
process.env.NODE_ENV = originals.NODE_ENV;
|
|
47
|
-
};
|
|
48
|
-
const reset = () => {
|
|
49
|
-
output.stderr = [];
|
|
50
|
-
output.stdout = [];
|
|
51
|
-
};
|
|
52
|
-
const toString = (str) => RECORD_OPTIONS.stripAnsi ? ansis_1.default.strip(str.toString()) : str.toString();
|
|
53
|
-
const getStderr = () => output.stderr.map((b) => toString(b)).join('');
|
|
54
|
-
const getStdout = () => output.stdout.map((b) => toString(b)).join('');
|
|
55
34
|
function traverseFilePathUntil(filename, predicate) {
|
|
56
35
|
let current = filename;
|
|
57
36
|
while (!predicate(current)) {
|
|
@@ -70,11 +49,48 @@ function findRoot() {
|
|
|
70
49
|
function makeLoadOptions(loadOpts) {
|
|
71
50
|
return loadOpts ?? { root: findRoot() };
|
|
72
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Capture the stderr and stdout output of a function
|
|
54
|
+
* @param fn async function to run
|
|
55
|
+
* @param opts options
|
|
56
|
+
* - print: Whether to print the output to the console
|
|
57
|
+
* - stripAnsi: Whether to strip ANSI codes from the output
|
|
58
|
+
* @returns {Promise<CaptureResult<T>>} Captured output
|
|
59
|
+
* - error: Error object if the function throws an error
|
|
60
|
+
* - result: Result of the function if it returns a value and succeeds
|
|
61
|
+
* - stderr: Captured stderr output
|
|
62
|
+
* - stdout: Captured stdout output
|
|
63
|
+
*/
|
|
73
64
|
async function captureOutput(fn, opts) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
const print = opts?.print ?? false;
|
|
66
|
+
const originals = {
|
|
67
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
68
|
+
stderr: process.stderr.write,
|
|
69
|
+
stdout: process.stdout.write,
|
|
70
|
+
};
|
|
71
|
+
const output = {
|
|
72
|
+
stderr: [],
|
|
73
|
+
stdout: [],
|
|
74
|
+
};
|
|
75
|
+
const { default: stripAnsi } = (opts?.stripAnsi ?? true) ? await Promise.resolve().then(() => __importStar(require('strip-ansi'))) : { default: (str) => str };
|
|
76
|
+
const toString = (str) => stripAnsi(str.toString());
|
|
77
|
+
const getStderr = () => output.stderr.map((b) => toString(b)).join('');
|
|
78
|
+
const getStdout = () => output.stdout.map((b) => toString(b)).join('');
|
|
79
|
+
const mock = (std) => (str, encoding, cb) => {
|
|
80
|
+
output[std].push(str);
|
|
81
|
+
if (print) {
|
|
82
|
+
if (encoding !== null && typeof encoding === 'function') {
|
|
83
|
+
cb = encoding;
|
|
84
|
+
encoding = undefined;
|
|
85
|
+
}
|
|
86
|
+
originals[std].apply(process[std], [str, encoding, cb]);
|
|
87
|
+
}
|
|
88
|
+
else if (typeof cb === 'function')
|
|
89
|
+
cb();
|
|
90
|
+
return true;
|
|
91
|
+
};
|
|
92
|
+
process.stdout.write = mock('stdout');
|
|
93
|
+
process.stderr.write = mock('stderr');
|
|
78
94
|
process.env.NODE_ENV = 'test';
|
|
79
95
|
try {
|
|
80
96
|
const result = await fn();
|
|
@@ -86,18 +102,32 @@ async function captureOutput(fn, opts) {
|
|
|
86
102
|
}
|
|
87
103
|
catch (error) {
|
|
88
104
|
return {
|
|
89
|
-
...(error instanceof core_1.Errors.CLIError && { error }),
|
|
90
|
-
...(error instanceof Error && { error }),
|
|
105
|
+
...(error instanceof core_1.Errors.CLIError && { error: { ...error, message: toString(error.message) } }),
|
|
106
|
+
...(error instanceof Error && { error: { ...error, message: toString(error.message) } }),
|
|
91
107
|
stderr: getStderr(),
|
|
92
108
|
stdout: getStdout(),
|
|
93
109
|
};
|
|
94
110
|
}
|
|
95
111
|
finally {
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
process.stderr.write = originals.stderr;
|
|
113
|
+
process.stdout.write = originals.stdout;
|
|
114
|
+
process.env.NODE_ENV = originals.NODE_ENV;
|
|
98
115
|
}
|
|
99
116
|
}
|
|
100
117
|
exports.captureOutput = captureOutput;
|
|
118
|
+
/**
|
|
119
|
+
* Capture the stderr and stdout output of a command in your CLI
|
|
120
|
+
* @param args Command arguments, e.g. `['my:command', '--flag']` or `'my:command --flag'`
|
|
121
|
+
* @param loadOpts options for loading oclif `Config`
|
|
122
|
+
* @param captureOpts options for capturing the output
|
|
123
|
+
* - print: Whether to print the output to the console
|
|
124
|
+
* - stripAnsi: Whether to strip ANSI codes from the output
|
|
125
|
+
* @returns {Promise<CaptureResult<T>>} Captured output
|
|
126
|
+
* - error: Error object if the command throws an error
|
|
127
|
+
* - result: Result of the command if it returns a value and succeeds
|
|
128
|
+
* - stderr: Captured stderr output
|
|
129
|
+
* - stdout: Captured stdout output
|
|
130
|
+
*/
|
|
101
131
|
async function runCommand(args, loadOpts, captureOpts) {
|
|
102
132
|
const loadOptions = makeLoadOptions(loadOpts);
|
|
103
133
|
const argsArray = (Array.isArray(args) ? args : [args]).join(' ').split(' ');
|
|
@@ -108,12 +138,26 @@ async function runCommand(args, loadOpts, captureOpts) {
|
|
|
108
138
|
return captureOutput(async () => (0, core_1.run)(finalArgs, loadOptions), captureOpts);
|
|
109
139
|
}
|
|
110
140
|
exports.runCommand = runCommand;
|
|
111
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Capture the stderr and stdout output of a hook in your CLI
|
|
143
|
+
* @param hook Hook name
|
|
144
|
+
* @param options options to pass to the hook
|
|
145
|
+
* @param loadOpts options for loading oclif `Config`
|
|
146
|
+
* @param captureOpts options for capturing the output
|
|
147
|
+
* - print: Whether to print the output to the console
|
|
148
|
+
* - stripAnsi: Whether to strip ANSI codes from the output
|
|
149
|
+
* @returns {Promise<CaptureResult<T>>} Captured output
|
|
150
|
+
* - error: Error object if the hook throws an error
|
|
151
|
+
* - result: Result of the hook if it returns a value and succeeds
|
|
152
|
+
* - stderr: Captured stderr output
|
|
153
|
+
* - stdout: Captured stdout output
|
|
154
|
+
*/
|
|
155
|
+
async function runHook(hook, options, loadOpts, captureOpts) {
|
|
112
156
|
const loadOptions = makeLoadOptions(loadOpts);
|
|
113
157
|
debug('loadOpts: %O', loadOptions);
|
|
114
158
|
return captureOutput(async () => {
|
|
115
159
|
const config = await core_1.Config.load(loadOptions);
|
|
116
160
|
return config.runHook(hook, options);
|
|
117
|
-
},
|
|
161
|
+
}, captureOpts);
|
|
118
162
|
}
|
|
119
163
|
exports.runHook = runHook;
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oclif/test",
|
|
3
3
|
"description": "test helpers for oclif components",
|
|
4
|
-
"version": "4.0.1-beta.
|
|
4
|
+
"version": "4.0.1-beta.3",
|
|
5
5
|
"author": "Salesforce",
|
|
6
6
|
"bugs": "https://github.com/oclif/test/issues",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
8
|
+
"debug": "^4.3.4",
|
|
9
|
+
"strip-ansi": "^7.1.0"
|
|
10
10
|
},
|
|
11
11
|
"peerDependencies": {
|
|
12
|
-
"@oclif/core": "
|
|
12
|
+
"@oclif/core": ">= 3.0.0"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@commitlint/config-conventional": "^18.6.3",
|