@oclif/test 4.0.1-beta.2 → 4.0.1-beta.4
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 +41 -2
- package/package.json +2 -2
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
|
@@ -27,6 +27,18 @@ function findRoot() {
|
|
|
27
27
|
function makeLoadOptions(loadOpts) {
|
|
28
28
|
return loadOpts ?? { root: findRoot() };
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Capture the stderr and stdout output of a function
|
|
32
|
+
* @param fn async function to run
|
|
33
|
+
* @param opts options
|
|
34
|
+
* - print: Whether to print the output to the console
|
|
35
|
+
* - stripAnsi: Whether to strip ANSI codes from the output
|
|
36
|
+
* @returns {Promise<CaptureResult<T>>} Captured output
|
|
37
|
+
* - error: Error object if the function throws an error
|
|
38
|
+
* - result: Result of the function if it returns a value and succeeds
|
|
39
|
+
* - stderr: Captured stderr output
|
|
40
|
+
* - stdout: Captured stdout output
|
|
41
|
+
*/
|
|
30
42
|
async function captureOutput(fn, opts) {
|
|
31
43
|
const print = opts?.print ?? false;
|
|
32
44
|
const stripAnsi = opts?.stripAnsi ?? true;
|
|
@@ -81,6 +93,19 @@ async function captureOutput(fn, opts) {
|
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
exports.captureOutput = captureOutput;
|
|
96
|
+
/**
|
|
97
|
+
* Capture the stderr and stdout output of a command in your CLI
|
|
98
|
+
* @param args Command arguments, e.g. `['my:command', '--flag']` or `'my:command --flag'`
|
|
99
|
+
* @param loadOpts options for loading oclif `Config`
|
|
100
|
+
* @param captureOpts options for capturing the output
|
|
101
|
+
* - print: Whether to print the output to the console
|
|
102
|
+
* - stripAnsi: Whether to strip ANSI codes from the output
|
|
103
|
+
* @returns {Promise<CaptureResult<T>>} Captured output
|
|
104
|
+
* - error: Error object if the command throws an error
|
|
105
|
+
* - result: Result of the command if it returns a value and succeeds
|
|
106
|
+
* - stderr: Captured stderr output
|
|
107
|
+
* - stdout: Captured stdout output
|
|
108
|
+
*/
|
|
84
109
|
async function runCommand(args, loadOpts, captureOpts) {
|
|
85
110
|
const loadOptions = makeLoadOptions(loadOpts);
|
|
86
111
|
const argsArray = (Array.isArray(args) ? args : [args]).join(' ').split(' ');
|
|
@@ -91,12 +116,26 @@ async function runCommand(args, loadOpts, captureOpts) {
|
|
|
91
116
|
return captureOutput(async () => (0, core_1.run)(finalArgs, loadOptions), captureOpts);
|
|
92
117
|
}
|
|
93
118
|
exports.runCommand = runCommand;
|
|
94
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Capture the stderr and stdout output of a hook in your CLI
|
|
121
|
+
* @param hook Hook name
|
|
122
|
+
* @param options options to pass to the hook
|
|
123
|
+
* @param loadOpts options for loading oclif `Config`
|
|
124
|
+
* @param captureOpts options for capturing the output
|
|
125
|
+
* - print: Whether to print the output to the console
|
|
126
|
+
* - stripAnsi: Whether to strip ANSI codes from the output
|
|
127
|
+
* @returns {Promise<CaptureResult<T>>} Captured output
|
|
128
|
+
* - error: Error object if the hook throws an error
|
|
129
|
+
* - result: Result of the hook if it returns a value and succeeds
|
|
130
|
+
* - stderr: Captured stderr output
|
|
131
|
+
* - stdout: Captured stdout output
|
|
132
|
+
*/
|
|
133
|
+
async function runHook(hook, options, loadOpts, captureOpts) {
|
|
95
134
|
const loadOptions = makeLoadOptions(loadOpts);
|
|
96
135
|
debug('loadOpts: %O', loadOptions);
|
|
97
136
|
return captureOutput(async () => {
|
|
98
137
|
const config = await core_1.Config.load(loadOptions);
|
|
99
138
|
return config.runHook(hook, options);
|
|
100
|
-
},
|
|
139
|
+
}, captureOpts);
|
|
101
140
|
}
|
|
102
141
|
exports.runHook = runHook;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.4",
|
|
5
5
|
"author": "Salesforce",
|
|
6
6
|
"bugs": "https://github.com/oclif/test/issues",
|
|
7
7
|
"dependencies": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"debug": "^4.3.4"
|
|
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",
|