@@ -6,6 +6,8 @@ jest.mock('chalk', () => ({
66 yellow : ( text : string ) => `[YELLOW]${ text } [/YELLOW]` ,
77 red : ( text : string ) => `[RED]${ text } [/RED]` ,
88 cyan : ( text : string ) => `[CYAN]${ text } [/CYAN]` ,
9+ dim : ( text : string ) => `[DIM]${ text } [/DIM]` ,
10+ bold : ( text : string ) => `[BOLD]${ text } [/BOLD]` ,
911} ) ) ;
1012
1113const mockOraInstance = {
@@ -173,6 +175,185 @@ describe('TerminalUI', () => {
173175 } ) ;
174176 } ) ;
175177
178+ describe ( 'table()' , ( ) => {
179+ beforeEach ( ( ) => {
180+ // Need to mock chalk for table tests
181+ jest . mock ( 'chalk' , ( ) => ( {
182+ blue : ( text : string ) => `[BLUE]${ text } [/BLUE]` ,
183+ green : ( text : string ) => `[GREEN]${ text } [/GREEN]` ,
184+ yellow : ( text : string ) => `[YELLOW]${ text } [/YELLOW]` ,
185+ red : ( text : string ) => `[RED]${ text } [/RED]` ,
186+ cyan : ( text : string ) => `[CYAN]${ text } [/CYAN]` ,
187+ dim : ( text : string ) => `[DIM]${ text } [/DIM]` ,
188+ bold : ( text : string ) => `[BOLD]${ text } [/BOLD]` ,
189+ } ) ) ;
190+ } ) ;
191+
192+ it ( 'should display table with headers and rows' , ( ) => {
193+ ui . table ( {
194+ headers : [ 'Name' , 'Status' ] ,
195+ rows : [
196+ [ 'skill-1' , 'active' ] ,
197+ [ 'skill-2' , 'inactive' ]
198+ ]
199+ } ) ;
200+
201+ expect ( consoleLogSpy ) . toHaveBeenCalled ( ) ;
202+ // Should have header, separator, and 2 rows = 4 calls
203+ expect ( consoleLogSpy ) . toHaveBeenCalledTimes ( 4 ) ;
204+ } ) ;
205+
206+ it ( 'should apply column styles when provided' , ( ) => {
207+ const chalk = require ( 'chalk' ) ;
208+
209+ ui . table ( {
210+ headers : [ 'Name' , 'Type' ] ,
211+ rows : [ [ 'test' , 'demo' ] ] ,
212+ columnStyles : [ chalk . cyan , chalk . green ]
213+ } ) ;
214+
215+ expect ( consoleLogSpy ) . toHaveBeenCalled ( ) ;
216+ } ) ;
217+
218+ it ( 'should use default indent of 2 spaces' , ( ) => {
219+ ui . table ( {
220+ headers : [ 'Col1' ] ,
221+ rows : [ [ 'data' ] ]
222+ } ) ;
223+
224+ const calls = consoleLogSpy . mock . calls ;
225+ // Check that rows start with indent
226+ expect ( calls [ 2 ] [ 0 ] ) . toMatch ( / ^ / ) ;
227+ } ) ;
228+
229+ it ( 'should use custom indent when provided' , ( ) => {
230+ ui . table ( {
231+ headers : [ 'Col1' ] ,
232+ rows : [ [ 'data' ] ] ,
233+ indent : ' '
234+ } ) ;
235+
236+ const calls = consoleLogSpy . mock . calls ;
237+ // Check that rows start with custom indent
238+ expect ( calls [ 2 ] [ 0 ] ) . toMatch ( / ^ / ) ;
239+ } ) ;
240+
241+ it ( 'should handle empty rows' , ( ) => {
242+ ui . table ( {
243+ headers : [ 'Name' , 'Status' ] ,
244+ rows : [ ]
245+ } ) ;
246+
247+ // Should still display headers and separator
248+ expect ( consoleLogSpy ) . toHaveBeenCalledTimes ( 2 ) ;
249+ } ) ;
250+
251+ it ( 'should pad columns correctly' , ( ) => {
252+ ui . table ( {
253+ headers : [ 'Short' , 'VeryLongHeader' ] ,
254+ rows : [
255+ [ 'a' , 'b' ] ,
256+ [ 'longer' , 'c' ]
257+ ]
258+ } ) ;
259+
260+ expect ( consoleLogSpy ) . toHaveBeenCalled ( ) ;
261+ // Verify padding is applied (all calls should have content)
262+ consoleLogSpy . mock . calls . forEach ( ( call : any [ ] ) => {
263+ expect ( call [ 0 ] ) . toBeTruthy ( ) ;
264+ } ) ;
265+ } ) ;
266+ } ) ;
267+
268+ describe ( 'summary()' , ( ) => {
269+ it ( 'should display summary with title and items' , ( ) => {
270+ ui . summary ( {
271+ items : [
272+ { type : 'success' , count : 5 , label : 'updated' } ,
273+ { type : 'warning' , count : 2 , label : 'skipped' } ,
274+ { type : 'error' , count : 1 , label : 'failed' }
275+ ]
276+ } ) ;
277+
278+ expect ( consoleLogSpy ) . toHaveBeenCalled ( ) ;
279+ // Should have title + 3 items = 4 calls
280+ expect ( consoleLogSpy ) . toHaveBeenCalledTimes ( 4 ) ;
281+ } ) ;
282+
283+ it ( 'should use custom title when provided' , ( ) => {
284+ ui . summary ( {
285+ title : 'Custom Summary' ,
286+ items : [
287+ { type : 'success' , count : 1 , label : 'done' }
288+ ]
289+ } ) ;
290+
291+ const calls = consoleLogSpy . mock . calls ;
292+ expect ( calls [ 0 ] [ 0 ] ) . toContain ( 'Custom Summary' ) ;
293+ } ) ;
294+
295+ it ( 'should skip items with count 0' , ( ) => {
296+ ui . summary ( {
297+ items : [
298+ { type : 'success' , count : 0 , label : 'updated' } ,
299+ { type : 'error' , count : 1 , label : 'failed' }
300+ ]
301+ } ) ;
302+
303+ // Should only display title + 1 item (skipping the 0 count)
304+ expect ( consoleLogSpy ) . toHaveBeenCalledTimes ( 2 ) ;
305+ } ) ;
306+
307+ it ( 'should display details section when provided' , ( ) => {
308+ ui . summary ( {
309+ items : [
310+ { type : 'error' , count : 1 , label : 'failed' }
311+ ] ,
312+ details : {
313+ title : 'Errors' ,
314+ items : [
315+ { message : 'Error 1' , tip : 'Fix this' } ,
316+ { message : 'Error 2' }
317+ ]
318+ }
319+ } ) ;
320+
321+ expect ( consoleLogSpy ) . toHaveBeenCalled ( ) ;
322+ // Title + 1 item + details title + 2 errors (with 1 tip) = 6 calls
323+ expect ( consoleLogSpy ) . toHaveBeenCalledTimes ( 6 ) ;
324+ } ) ;
325+
326+ it ( 'should not display details section when items are empty' , ( ) => {
327+ ui . summary ( {
328+ items : [
329+ { type : 'success' , count : 1 , label : 'done' }
330+ ] ,
331+ details : {
332+ title : 'Errors' ,
333+ items : [ ]
334+ }
335+ } ) ;
336+
337+ // Should only display summary, not details
338+ expect ( consoleLogSpy ) . toHaveBeenCalledTimes ( 2 ) ;
339+ } ) ;
340+
341+ it ( 'should apply correct colors for different types' , ( ) => {
342+ ui . summary ( {
343+ items : [
344+ { type : 'success' , count : 1 , label : 'ok' } ,
345+ { type : 'warning' , count : 1 , label : 'warn' } ,
346+ { type : 'error' , count : 1 , label : 'err' } ,
347+ { type : 'info' , count : 1 , label : 'info' }
348+ ]
349+ } ) ;
350+
351+ expect ( consoleLogSpy ) . toHaveBeenCalled ( ) ;
352+ // Verify all types were displayed
353+ expect ( consoleLogSpy ) . toHaveBeenCalledTimes ( 5 ) ; // title + 4 items
354+ } ) ;
355+ } ) ;
356+
176357 describe ( 'Edge cases' , ( ) => {
177358 it ( 'should handle very long messages' , ( ) => {
178359 const longMessage = 'a' . repeat ( 1000 ) ;
0 commit comments