@@ -97,6 +97,132 @@ describe('validateApp', () => {
9797 expect ( renderSuccess ) . not . toHaveBeenCalled ( )
9898 } )
9999
100+ test ( 'outputs only structured issues when the rendered message matches them exactly' , async ( ) => {
101+ // Given
102+ const errors = new AppErrors ( )
103+ errors . addError ( '/path/to/shopify.app.toml' , '• [client_id]: Required' , [
104+ {
105+ filePath : '/path/to/shopify.app.toml' ,
106+ path : [ 'client_id' ] ,
107+ pathString : 'client_id' ,
108+ message : 'Required' ,
109+ code : 'invalid_type' ,
110+ } ,
111+ ] )
112+ const app = testAppLinked ( )
113+ app . errors = errors
114+
115+ // When / Then
116+ await expect ( validateApp ( app , { json : true } ) ) . rejects . toThrow ( AbortSilentError )
117+ expect ( outputResult ) . toHaveBeenCalledWith (
118+ JSON . stringify (
119+ {
120+ valid : false ,
121+ issues : [
122+ {
123+ filePath : '/path/to/shopify.app.toml' ,
124+ path : [ 'client_id' ] ,
125+ pathString : 'client_id' ,
126+ message : 'Required' ,
127+ code : 'invalid_type' ,
128+ } ,
129+ ] ,
130+ } ,
131+ null ,
132+ 2 ,
133+ ) ,
134+ )
135+ } )
136+
137+ test ( 'outputs only structured issues when the rendered message is a validation wrapper around them' , async ( ) => {
138+ // Given
139+ const errors = new AppErrors ( )
140+ errors . addError (
141+ '/path/to/shopify.app.toml' ,
142+ 'Validation errors in /path/to/shopify.app.toml:\n\n• [client_id]: Required' ,
143+ [
144+ {
145+ filePath : '/path/to/shopify.app.toml' ,
146+ path : [ 'client_id' ] ,
147+ pathString : 'client_id' ,
148+ message : 'Required' ,
149+ code : 'invalid_type' ,
150+ } ,
151+ ] ,
152+ )
153+ const app = testAppLinked ( )
154+ app . errors = errors
155+
156+ // When / Then
157+ await expect ( validateApp ( app , { json : true } ) ) . rejects . toThrow ( AbortSilentError )
158+ expect ( outputResult ) . toHaveBeenCalledWith (
159+ JSON . stringify (
160+ {
161+ valid : false ,
162+ issues : [
163+ {
164+ filePath : '/path/to/shopify.app.toml' ,
165+ path : [ 'client_id' ] ,
166+ pathString : 'client_id' ,
167+ message : 'Required' ,
168+ code : 'invalid_type' ,
169+ } ,
170+ ] ,
171+ } ,
172+ null ,
173+ 2 ,
174+ ) ,
175+ )
176+ } )
177+
178+ test ( 'adds a root issue when the rendered message includes extra context beyond the structured issues' , async ( ) => {
179+ // Given
180+ const errors = new AppErrors ( )
181+ errors . addError (
182+ '/path/to/shopify.app.toml' ,
183+ 'Validation errors in /path/to/shopify.app.toml:\n\n• [client_id]: Required\n\nFix the app config before continuing.' ,
184+ [
185+ {
186+ filePath : '/path/to/shopify.app.toml' ,
187+ path : [ 'client_id' ] ,
188+ pathString : 'client_id' ,
189+ message : 'Required' ,
190+ code : 'invalid_type' ,
191+ } ,
192+ ] ,
193+ )
194+ const app = testAppLinked ( )
195+ app . errors = errors
196+
197+ // When / Then
198+ await expect ( validateApp ( app , { json : true } ) ) . rejects . toThrow ( AbortSilentError )
199+ expect ( outputResult ) . toHaveBeenCalledWith (
200+ JSON . stringify (
201+ {
202+ valid : false ,
203+ issues : [
204+ {
205+ filePath : '/path/to/shopify.app.toml' ,
206+ path : [ 'client_id' ] ,
207+ pathString : 'client_id' ,
208+ message : 'Required' ,
209+ code : 'invalid_type' ,
210+ } ,
211+ {
212+ filePath : '/path/to/shopify.app.toml' ,
213+ path : [ ] ,
214+ pathString : 'root' ,
215+ message :
216+ 'Validation errors in /path/to/shopify.app.toml:\n\n• [client_id]: Required\n\nFix the app config before continuing.' ,
217+ } ,
218+ ] ,
219+ } ,
220+ null ,
221+ 2 ,
222+ ) ,
223+ )
224+ } )
225+
100226 test ( 'renders success when errors object exists but is empty' , async ( ) => {
101227 // Given
102228 const errors = new AppErrors ( )
0 commit comments