1- import { categorizeError , ErrorCategory } from './error-categorizer.js'
1+ import { categorizeError , formatErrorMessage , ErrorCategory } from './error-categorizer.js'
22import { describe , test , expect } from 'vitest'
33
44describe ( 'categorizeError' , ( ) => {
@@ -36,7 +36,6 @@ describe('categorizeError', () => {
3636 const errors = [
3737 new Error ( 'ENOENT: no such file or directory' ) ,
3838 new Error ( 'EACCES: permission denied' ) ,
39- new Error ( 'File not found: theme.liquid' ) ,
4039 new Error ( 'Cannot read directory' ) ,
4140 new Error ( 'Invalid path provided' ) ,
4241 ]
@@ -117,9 +116,9 @@ describe('categorizeError', () => {
117116
118117 // When
119118 // Then
120- expect ( categorizeError ( errors [ 0 ] ) ) . toBe ( ErrorCategory . Parsing )
121- expect ( categorizeError ( errors [ 1 ] ) ) . toBe ( ErrorCategory . Parsing )
122- expect ( categorizeError ( errors [ 2 ] ) ) . toBe ( ErrorCategory . Parsing )
119+ expect ( categorizeError ( errors [ 0 ] ) ) . toBe ( ErrorCategory . Json )
120+ expect ( categorizeError ( errors [ 1 ] ) ) . toBe ( ErrorCategory . Json )
121+ expect ( categorizeError ( errors [ 2 ] ) ) . toBe ( ErrorCategory . Json )
123122 expect ( categorizeError ( errors [ 3 ] ) ) . toBe ( ErrorCategory . Authentication )
124123 } )
125124
@@ -134,7 +133,7 @@ describe('categorizeError', () => {
134133 // When
135134 // Then
136135 expect ( categorizeError ( errors [ 0 ] ) ) . toBe ( ErrorCategory . Validation )
137- expect ( categorizeError ( errors [ 1 ] ) ) . toBe ( ErrorCategory . Parsing )
136+ expect ( categorizeError ( errors [ 1 ] ) ) . toBe ( ErrorCategory . Validation )
138137 expect ( categorizeError ( errors [ 2 ] ) ) . toBe ( ErrorCategory . Validation )
139138 } )
140139
@@ -186,3 +185,341 @@ describe('categorizeError', () => {
186185 expect ( category ) . toBe ( ErrorCategory . Unknown )
187186 } )
188187} )
188+
189+ describe ( 'formatErrorMessage' , ( ) => {
190+ describe ( 'Network errors' , ( ) => {
191+ test ( 'preserves HTTP status codes' , ( ) => {
192+ // Given
193+ const error = new Error ( 'Request failed with status 404' )
194+ const category = ErrorCategory . Network
195+
196+ // When
197+ const formatted = formatErrorMessage ( error , category )
198+
199+ // Then
200+ expect ( formatted ) . toBe ( 'http-404-request-failed-with-status' )
201+ } )
202+
203+ test ( 'preserves GraphQL error codes' , ( ) => {
204+ // Given
205+ const error = new Error ( 'GraphQL Error (Code: 401): Unauthorized' )
206+ const category = ErrorCategory . Network
207+
208+ // When
209+ const formatted = formatErrorMessage ( error , category )
210+
211+ // Then
212+ expect ( formatted ) . toBe ( 'http-401-graphql-error-code-unauthorized' )
213+ } )
214+
215+ test ( 'preserves connection error codes' , ( ) => {
216+ // Given
217+ const error = new Error ( 'ECONNREFUSED: connection refused' )
218+ const category = ErrorCategory . Network
219+
220+ // When
221+ const formatted = formatErrorMessage ( error , category )
222+
223+ // Then
224+ expect ( formatted ) . toBe ( 'http-000-econnrefused-econnrefused-connection-refu' )
225+ } )
226+
227+ test ( 'handles network errors without specific codes' , ( ) => {
228+ // Given
229+ const error = new Error ( 'Network request failed' )
230+ const category = ErrorCategory . Network
231+
232+ // When
233+ const formatted = formatErrorMessage ( error , category )
234+
235+ // Then
236+ expect ( formatted ) . toBe ( 'http-000-network-request-failed' )
237+ } )
238+ } )
239+
240+ describe ( 'Authentication errors' , ( ) => {
241+ test ( 'uses generic formatting' , ( ) => {
242+ // Given
243+ const error = new Error ( 'Unauthorized access: 401' )
244+ const category = ErrorCategory . Authentication
245+
246+ // When
247+ const formatted = formatErrorMessage ( error , category )
248+
249+ // Then
250+ expect ( formatted ) . toBe ( 'unauthorized-access-401' )
251+ } )
252+
253+ test ( 'handles auth errors without status codes' , ( ) => {
254+ // Given
255+ const error = new Error ( 'Invalid credentials provided' )
256+ const category = ErrorCategory . Authentication
257+
258+ // When
259+ const formatted = formatErrorMessage ( error , category )
260+
261+ // Then
262+ expect ( formatted ) . toBe ( 'invalid-credentials-provided' )
263+ } )
264+ } )
265+
266+ describe ( 'File system errors' , ( ) => {
267+ test ( 'uses generic formatting' , ( ) => {
268+ // Given
269+ const error = new Error ( 'ENOENT: no such file or directory' )
270+ const category = ErrorCategory . FileSystem
271+
272+ // When
273+ const formatted = formatErrorMessage ( error , category )
274+
275+ // Then
276+ expect ( formatted ) . toBe ( 'enoent-no-such-file-or-directory' )
277+ } )
278+
279+ test ( 'handles file system errors without error codes' , ( ) => {
280+ // Given
281+ const error = new Error ( 'Cannot read directory' )
282+ const category = ErrorCategory . FileSystem
283+
284+ // When
285+ const formatted = formatErrorMessage ( error , category )
286+
287+ // Then
288+ expect ( formatted ) . toBe ( 'cannot-read-directory' )
289+ } )
290+ } )
291+
292+ describe ( 'Rate limit errors' , ( ) => {
293+ test ( 'uses generic formatting' , ( ) => {
294+ // Given
295+ const error = new Error ( 'Rate limit exceeded: 100 requests per minute' )
296+ const category = ErrorCategory . RateLimit
297+
298+ // When
299+ const formatted = formatErrorMessage ( error , category )
300+
301+ // Then
302+ expect ( formatted ) . toBe ( 'rate-limit-exceeded-100-requests-per-minute' )
303+ } )
304+
305+ test ( 'handles rate limit errors without numbers' , ( ) => {
306+ // Given
307+ const error = new Error ( 'Too many requests' )
308+ const category = ErrorCategory . RateLimit
309+
310+ // When
311+ const formatted = formatErrorMessage ( error , category )
312+
313+ // Then
314+ expect ( formatted ) . toBe ( 'too-many-requests' )
315+ } )
316+ } )
317+
318+ describe ( 'JSON errors' , ( ) => {
319+ test ( 'uses generic formatting' , ( ) => {
320+ // Given
321+ const error = new Error ( 'Syntax error at line 42: unexpected token' )
322+ const category = ErrorCategory . Json
323+
324+ // When
325+ const formatted = formatErrorMessage ( error , category )
326+
327+ // Then
328+ expect ( formatted ) . toBe ( 'syntax-error-at-line-42-unexpected-token' )
329+ } )
330+
331+ test ( 'handles JSON errors without position info' , ( ) => {
332+ // Given
333+ const error = new Error ( 'Invalid JSON received' )
334+ const category = ErrorCategory . Json
335+
336+ // When
337+ const formatted = formatErrorMessage ( error , category )
338+
339+ // Then
340+ expect ( formatted ) . toBe ( 'invalid-json-received' )
341+ } )
342+ } )
343+
344+ describe ( 'Validation errors' , ( ) => {
345+ test ( 'uses generic formatting' , ( ) => {
346+ // Given
347+ const error = new Error ( 'Validation failed for field: username' )
348+ const category = ErrorCategory . Validation
349+
350+ // When
351+ const formatted = formatErrorMessage ( error , category )
352+
353+ // Then
354+ expect ( formatted ) . toBe ( 'validation-failed-for-field-username' )
355+ } )
356+
357+ test ( 'handles validation errors without field names' , ( ) => {
358+ // Given
359+ const error = new Error ( 'Required field missing' )
360+ const category = ErrorCategory . Validation
361+
362+ // When
363+ const formatted = formatErrorMessage ( error , category )
364+
365+ // Then
366+ expect ( formatted ) . toBe ( 'required-field-missing' )
367+ } )
368+ } )
369+
370+ describe ( 'Permission errors' , ( ) => {
371+ test ( 'uses generic formatting' , ( ) => {
372+ // Given
373+ const error = new Error ( 'Permission denied to /etc/config' )
374+ const category = ErrorCategory . Permission
375+
376+ // When
377+ const formatted = formatErrorMessage ( error , category )
378+
379+ // Then
380+ expect ( formatted ) . toBe ( 'permission-denied-to-etc-config' )
381+ } )
382+
383+ test ( 'handles permission errors without resource names' , ( ) => {
384+ // Given
385+ const error = new Error ( 'Access denied' )
386+ const category = ErrorCategory . Permission
387+
388+ // When
389+ const formatted = formatErrorMessage ( error , category )
390+
391+ // Then
392+ expect ( formatted ) . toBe ( 'access-denied' )
393+ } )
394+ } )
395+
396+ describe ( 'Liquid errors' , ( ) => {
397+ test ( 'uses generic formatting' , ( ) => {
398+ // Given
399+ const error = new Error ( 'Liquid syntax error at line 15' )
400+ const category = ErrorCategory . Liquid
401+
402+ // When
403+ const formatted = formatErrorMessage ( error , category )
404+
405+ // Then
406+ expect ( formatted ) . toBe ( 'liquid-syntax-error-at-line-15' )
407+ } )
408+
409+ test ( 'handles Liquid errors without line numbers' , ( ) => {
410+ // Given
411+ const error = new Error ( 'Liquid template error' )
412+ const category = ErrorCategory . Liquid
413+
414+ // When
415+ const formatted = formatErrorMessage ( error , category )
416+
417+ // Then
418+ expect ( formatted ) . toBe ( 'liquid-template-error' )
419+ } )
420+ } )
421+
422+ describe ( 'Theme check errors' , ( ) => {
423+ test ( 'uses generic formatting' , ( ) => {
424+ // Given
425+ const error = new Error ( 'Theme check failed for rule: missing-alt-text' )
426+ const category = ErrorCategory . ThemeCheck
427+
428+ // When
429+ const formatted = formatErrorMessage ( error , category )
430+
431+ // Then
432+ expect ( formatted ) . toBe ( 'theme-check-failed-for-rule-missing-alt-text' )
433+ } )
434+
435+ test ( 'handles theme check errors without rule names' , ( ) => {
436+ // Given
437+ const error = new Error ( 'Theme validation failed' )
438+ const category = ErrorCategory . ThemeCheck
439+
440+ // When
441+ const formatted = formatErrorMessage ( error , category )
442+
443+ // Then
444+ expect ( formatted ) . toBe ( 'theme-validation-failed' )
445+ } )
446+ } )
447+
448+ describe ( 'Unknown errors' , ( ) => {
449+ test ( 'uses generic formatting' , ( ) => {
450+ // Given
451+ const error = new Error ( 'Something went wrong' )
452+ const category = ErrorCategory . Unknown
453+
454+ // When
455+ const formatted = formatErrorMessage ( error , category )
456+
457+ // Then
458+ expect ( formatted ) . toBe ( 'something-went-wrong' )
459+ } )
460+ } )
461+
462+ describe ( 'Edge cases' , ( ) => {
463+ test ( 'handles very long error messages' , ( ) => {
464+ // Given
465+ const longMessage = 'A' . repeat ( 100 )
466+ const error = new Error ( longMessage )
467+ const category = ErrorCategory . Network
468+
469+ // When
470+ const formatted = formatErrorMessage ( error , category )
471+
472+ // Then
473+ expect ( formatted . length ) . toBeLessThanOrEqual ( 50 )
474+ expect ( formatted ) . toBe ( `http-000-${ 'a' . repeat ( 41 ) } ` )
475+ } )
476+
477+ test ( 'handles non-Error objects' , ( ) => {
478+ // Given
479+ const error = 'String error message'
480+ const category = ErrorCategory . Unknown
481+
482+ // When
483+ const formatted = formatErrorMessage ( error , category )
484+
485+ // Then
486+ expect ( formatted ) . toBe ( 'string-error-message' )
487+ } )
488+
489+ test ( 'handles errors with special characters' , ( ) => {
490+ // Given
491+ const error = new Error ( 'Error: @#$%^&*()!' )
492+ const category = ErrorCategory . Unknown
493+
494+ // When
495+ const formatted = formatErrorMessage ( error , category )
496+
497+ // Then
498+ expect ( formatted ) . toBe ( 'error' )
499+ } )
500+
501+ test ( 'removes consecutive dashes' , ( ) => {
502+ // Given
503+ const error = new Error ( 'Error---with---many---dashes' )
504+ const category = ErrorCategory . Unknown
505+
506+ // When
507+ const formatted = formatErrorMessage ( error , category )
508+
509+ // Then
510+ expect ( formatted ) . toBe ( 'error-with-many-dashes' )
511+ } )
512+
513+ test ( 'trims leading and trailing dashes' , ( ) => {
514+ // Given
515+ const error = new Error ( '---Error message---' )
516+ const category = ErrorCategory . Unknown
517+
518+ // When
519+ const formatted = formatErrorMessage ( error , category )
520+
521+ // Then
522+ expect ( formatted ) . toBe ( 'error-message' )
523+ } )
524+ } )
525+ } )
0 commit comments