1+ import fs from 'node:fs' ;
2+
13import { sync } from 'cross-spawn' ;
24import { vi } from 'vitest' ;
35
6+ import { ConfigError } from '../../../error' ;
47import { postprocessOutput } from '../postprocess' ;
58
69vi . mock ( 'cross-spawn' ) ;
10+ vi . mock ( 'node:fs' ) ;
711
812const mockSync = vi . mocked ( sync ) ;
13+ const mockExistsSync = vi . mocked ( fs . existsSync ) ;
14+ const mockReaddirSync = vi . mocked ( fs . readdirSync ) ;
915
1016const baseConfig = {
1117 path : '/output' ,
@@ -17,13 +23,39 @@ const noopPostProcessors = {};
1723describe ( 'postprocessOutput' , ( ) => {
1824 beforeEach ( ( ) => {
1925 vi . clearAllMocks ( ) ;
26+ mockExistsSync . mockReturnValue ( true ) ;
27+ mockReaddirSync . mockReturnValue ( [ 'index.ts' ] as any ) ;
2028 } ) ;
2129
2230 it ( 'should not call sync when postProcess is empty' , ( ) => {
2331 postprocessOutput ( baseConfig , noopPostProcessors , '' ) ;
2432 expect ( mockSync ) . not . toHaveBeenCalled ( ) ;
2533 } ) ;
2634
35+ it ( 'should not call sync when output directory does not exist' , ( ) => {
36+ mockExistsSync . mockReturnValue ( false ) ;
37+
38+ postprocessOutput (
39+ { ...baseConfig , postProcess : [ { args : [ '{{path}}' ] , command : 'prettier' } ] } ,
40+ noopPostProcessors ,
41+ '' ,
42+ ) ;
43+
44+ expect ( mockSync ) . not . toHaveBeenCalled ( ) ;
45+ } ) ;
46+
47+ it ( 'should not call sync when output directory is empty' , ( ) => {
48+ mockReaddirSync . mockReturnValue ( [ ] as any ) ;
49+
50+ postprocessOutput (
51+ { ...baseConfig , postProcess : [ { args : [ '{{path}}' ] , command : 'prettier' } ] } ,
52+ noopPostProcessors ,
53+ '' ,
54+ ) ;
55+
56+ expect ( mockSync ) . not . toHaveBeenCalled ( ) ;
57+ } ) ;
58+
2759 it ( 'should call sync with command and resolved args' , ( ) => {
2860 mockSync . mockReturnValue ( { error : undefined , status : 0 } as any ) ;
2961
@@ -48,7 +80,20 @@ describe('postprocessOutput', () => {
4880 expect ( mockSync ) . toHaveBeenCalledWith ( 'prettier' , [ '/my/output' , '--write' ] ) ;
4981 } ) ;
5082
51- it ( 'should throw when the process fails to spawn (e.g., ENOENT)' , ( ) => {
83+ it ( 'should throw ConfigError when the process fails to spawn (e.g., ENOENT)' , ( ) => {
84+ const spawnError = new Error ( 'spawnSync oxfmt ENOENT' ) ;
85+ mockSync . mockReturnValue ( { error : spawnError , status : null } as any ) ;
86+
87+ expect ( ( ) =>
88+ postprocessOutput (
89+ { ...baseConfig , postProcess : [ { args : [ '{{path}}' ] , command : 'oxfmt' } ] } ,
90+ noopPostProcessors ,
91+ '' ,
92+ ) ,
93+ ) . toThrow ( ConfigError ) ;
94+ } ) ;
95+
96+ it ( 'should include the error message when the process fails to spawn' , ( ) => {
5297 const spawnError = new Error ( 'spawnSync oxfmt ENOENT' ) ;
5398 mockSync . mockReturnValue ( { error : spawnError , status : null } as any ) ;
5499
@@ -77,7 +122,19 @@ describe('postprocessOutput', () => {
77122 ) . toThrow ( 'Post-processor "My Formatter" failed to run: spawnSync my-formatter ENOENT' ) ;
78123 } ) ;
79124
80- it ( 'should throw when the process exits with a non-zero status code' , ( ) => {
125+ it ( 'should throw ConfigError when the process exits with a non-zero status code' , ( ) => {
126+ mockSync . mockReturnValue ( { error : undefined , status : 1 , stderr : Buffer . from ( '' ) } as any ) ;
127+
128+ expect ( ( ) =>
129+ postprocessOutput (
130+ { ...baseConfig , postProcess : [ { args : [ '{{path}}' ] , command : 'prettier' } ] } ,
131+ noopPostProcessors ,
132+ '' ,
133+ ) ,
134+ ) . toThrow ( ConfigError ) ;
135+ } ) ;
136+
137+ it ( 'should include exit code in error message' , ( ) => {
81138 mockSync . mockReturnValue ( { error : undefined , status : 1 , stderr : Buffer . from ( '' ) } as any ) ;
82139
83140 expect ( ( ) =>
@@ -105,6 +162,18 @@ describe('postprocessOutput', () => {
105162 ) . toThrow ( 'Post-processor "biome" exited with code 2:\nerror: file not found' ) ;
106163 } ) ;
107164
165+ it ( 'should not throw when the process is killed by a signal (null status)' , ( ) => {
166+ mockSync . mockReturnValue ( { error : undefined , signal : 'SIGTERM' , status : null } as any ) ;
167+
168+ expect ( ( ) =>
169+ postprocessOutput (
170+ { ...baseConfig , postProcess : [ { args : [ '{{path}}' ] , command : 'prettier' } ] } ,
171+ noopPostProcessors ,
172+ '' ,
173+ ) ,
174+ ) . not . toThrow ( ) ;
175+ } ) ;
176+
108177 it ( 'should skip unknown string preset processors' , ( ) => {
109178 postprocessOutput ( { ...baseConfig , postProcess : [ 'unknown-preset' ] } , noopPostProcessors , '' ) ;
110179 expect ( mockSync ) . not . toHaveBeenCalled ( ) ;
0 commit comments