1
1
import * as yargs from 'yargs' ;
2
- import { noop } from 'lodash' ;
3
2
import { DiagnosticSeverity } from '@stoplight/types' ;
4
3
import { IRuleResult } from '@stoplight/spectral-core' ;
4
+ import * as process from 'process' ;
5
+ import { ErrorWithCause } from 'pony-cause' ;
6
+ import AggregateError from 'es-aggregate-error' ;
5
7
6
8
import { lint } from '../../services/linter' ;
7
9
import { formatOutput , writeOutput } from '../../services/output' ;
8
10
import lintCommand from '../lint' ;
11
+ import chalk from 'chalk' ;
9
12
13
+ jest . mock ( 'process' ) ;
10
14
jest . mock ( '../../services/output' ) ;
11
15
jest . mock ( '../../services/linter' ) ;
12
16
@@ -24,9 +28,6 @@ function run(command: string) {
24
28
}
25
29
26
30
describe ( 'lint' , ( ) => {
27
- let errorSpy : jest . SpyInstance ;
28
- const { isTTY } = process . stdin ;
29
-
30
31
const results : IRuleResult [ ] = [
31
32
{
32
33
code : 'parser' ,
@@ -47,29 +48,26 @@ describe('lint', () => {
47
48
] ;
48
49
49
50
beforeEach ( ( ) => {
50
- ( lint as jest . Mock ) . mockClear ( ) ;
51
51
( lint as jest . Mock ) . mockResolvedValueOnce ( results ) ;
52
-
53
- ( formatOutput as jest . Mock ) . mockClear ( ) ;
54
52
( formatOutput as jest . Mock ) . mockReturnValueOnce ( '<formatted output>' ) ;
55
-
56
- ( writeOutput as jest . Mock ) . mockClear ( ) ;
57
53
( writeOutput as jest . Mock ) . mockResolvedValueOnce ( undefined ) ;
58
-
59
- errorSpy = jest . spyOn ( console , 'error' ) . mockImplementation ( noop ) ;
60
54
} ) ;
61
55
62
56
afterEach ( ( ) => {
63
- errorSpy . mockRestore ( ) ;
64
- process . stdin . isTTY = isTTY ;
57
+ process . stdin . isTTY = true ;
58
+ jest . clearAllMocks ( ) ;
59
+ jest . resetAllMocks ( ) ;
65
60
} ) ;
66
61
67
62
it ( 'shows help when no document and no STDIN are present' , ( ) => {
68
- process . stdin . isTTY = true ;
69
63
return expect ( run ( 'lint' ) ) . rejects . toContain ( 'documents Location of JSON/YAML documents' ) ;
70
64
} ) ;
71
65
72
66
describe ( 'when STDIN is present' , ( ) => {
67
+ beforeEach ( ( ) => {
68
+ process . stdin . isTTY = false ;
69
+ } ) ;
70
+
73
71
it ( 'does not show help when documents are missing' , async ( ) => {
74
72
const output = await run ( 'lint' ) ;
75
73
expect ( output ) . not . toContain ( 'documents Location of JSON/YAML documents' ) ;
@@ -150,35 +148,29 @@ describe('lint', () => {
150
148
151
149
it . each ( [ 'json' , 'stylish' ] ) ( 'calls formatOutput with %s format' , async format => {
152
150
await run ( `lint -f ${ format } ./__fixtures__/empty-oas2-document.json` ) ;
153
- await new Promise ( resolve => void process . nextTick ( resolve ) ) ;
154
151
expect ( formatOutput ) . toBeCalledWith ( results , format , { failSeverity : DiagnosticSeverity . Error } ) ;
155
152
} ) ;
156
153
157
154
it ( 'writes formatted output to a file' , async ( ) => {
158
155
await run ( `lint -o foo.json ./__fixtures__/empty-oas2-document.json` ) ;
159
- await new Promise ( resolve => void process . nextTick ( resolve ) ) ;
160
156
expect ( writeOutput ) . toBeCalledWith ( '<formatted output>' , 'foo.json' ) ;
161
157
} ) ;
162
158
163
159
it ( 'writes formatted output to multiple files when using format and output flags' , async ( ) => {
164
- ( formatOutput as jest . Mock ) . mockClear ( ) ;
165
160
( formatOutput as jest . Mock ) . mockReturnValue ( '<formatted output>' ) ;
166
161
167
162
await run (
168
163
`lint --format html --format json --output.json foo.json --output.html foo.html ./__fixtures__/empty-oas2-document.json` ,
169
164
) ;
170
- await new Promise ( resolve => void process . nextTick ( resolve ) ) ;
171
165
expect ( writeOutput ) . toBeCalledTimes ( 2 ) ;
172
166
expect ( writeOutput ) . nthCalledWith ( 1 , '<formatted output>' , 'foo.html' ) ;
173
167
expect ( writeOutput ) . nthCalledWith ( 2 , '<formatted output>' , 'foo.json' ) ;
174
168
} ) ;
175
169
176
170
it ( 'writes formatted output to multiple files and stdout when using format and output flags' , async ( ) => {
177
- ( formatOutput as jest . Mock ) . mockClear ( ) ;
178
171
( formatOutput as jest . Mock ) . mockReturnValue ( '<formatted output>' ) ;
179
172
180
173
await run ( `lint --format html --format json --output.json foo.json ./__fixtures__/empty-oas2-document.json` ) ;
181
- await new Promise ( resolve => void process . nextTick ( resolve ) ) ;
182
174
expect ( writeOutput ) . toBeCalledTimes ( 2 ) ;
183
175
expect ( writeOutput ) . nthCalledWith ( 1 , '<formatted output>' , '<stdout>' ) ;
184
176
expect ( writeOutput ) . nthCalledWith ( 2 , '<formatted output>' , 'foo.json' ) ;
@@ -216,8 +208,51 @@ describe('lint', () => {
216
208
const error = new Error ( 'Failure' ) ;
217
209
( lint as jest . Mock ) . mockReset ( ) ;
218
210
( lint as jest . Mock ) . mockRejectedValueOnce ( error ) ;
219
- await run ( `lint -o foo.json ./__fixtures__/empty-oas2-document.json` ) ;
220
- await new Promise ( resolve => void process . nextTick ( resolve ) ) ;
221
- expect ( errorSpy ) . toBeCalledWith ( 'Failure' ) ;
211
+ await run ( `lint ./__fixtures__/empty-oas2-document.json` ) ;
212
+ expect ( process . stderr . write ) . nthCalledWith ( 1 , chalk . red ( 'Error running Spectral!\n' ) ) ;
213
+ expect ( process . stderr . write ) . nthCalledWith ( 2 , chalk . red ( 'Use --verbose flag to print the error stack.\n' ) ) ;
214
+ expect ( process . stderr . write ) . nthCalledWith ( 3 , `Error #1: ${ chalk . red ( 'Failure' ) } \n` ) ;
215
+ } ) ;
216
+
217
+ it ( 'prints each error separately' , async ( ) => {
218
+ ( lint as jest . Mock ) . mockReset ( ) ;
219
+ ( lint as jest . Mock ) . mockRejectedValueOnce (
220
+ new AggregateError ( [
221
+ new Error ( 'some unhandled exception' ) ,
222
+ new TypeError ( 'another one' ) ,
223
+ new ErrorWithCause ( 'some error with cause' , { cause : 'original exception' } ) ,
224
+ ] ) ,
225
+ ) ;
226
+ await run ( `lint ./__fixtures__/empty-oas2-document.json` ) ;
227
+ expect ( process . stderr . write ) . nthCalledWith ( 3 , `Error #1: ${ chalk . red ( 'some unhandled exception' ) } \n` ) ;
228
+ expect ( process . stderr . write ) . nthCalledWith ( 4 , `Error #2: ${ chalk . red ( 'another one' ) } \n` ) ;
229
+ expect ( process . stderr . write ) . nthCalledWith ( 5 , `Error #3: ${ chalk . red ( 'original exception' ) } \n` ) ;
230
+ } ) ;
231
+
232
+ it ( 'given verbose flag, prints each error together with their stacks' , async ( ) => {
233
+ ( lint as jest . Mock ) . mockReset ( ) ;
234
+ ( lint as jest . Mock ) . mockRejectedValueOnce (
235
+ new AggregateError ( [
236
+ new Error ( 'some unhandled exception' ) ,
237
+ new TypeError ( 'another one' ) ,
238
+ new ErrorWithCause ( 'some error with cause' , { cause : 'original exception' } ) ,
239
+ ] ) ,
240
+ ) ;
241
+
242
+ await run ( `lint --verbose ./__fixtures__/empty-oas2-document.json` ) ;
243
+
244
+ expect ( process . stderr . write ) . nthCalledWith ( 2 , `Error #1: ${ chalk . red ( 'some unhandled exception' ) } \n` ) ;
245
+ expect ( process . stderr . write ) . nthCalledWith (
246
+ 3 ,
247
+ expect . stringContaining ( `packages/cli/src/commands/__tests__/lint.test.ts:236` ) ,
248
+ ) ;
249
+
250
+ expect ( process . stderr . write ) . nthCalledWith ( 4 , `Error #2: ${ chalk . red ( 'another one' ) } \n` ) ;
251
+ expect ( process . stderr . write ) . nthCalledWith (
252
+ 5 ,
253
+ expect . stringContaining ( `packages/cli/src/commands/__tests__/lint.test.ts:237` ) ,
254
+ ) ;
255
+
256
+ expect ( process . stderr . write ) . nthCalledWith ( 6 , `Error #3: ${ chalk . red ( 'original exception' ) } \n` ) ;
222
257
} ) ;
223
258
} ) ;
0 commit comments