1616 * SPDX-License-Identifier: Apache-2.0
1717 ***********************************************************************/
1818
19- import { render } from '@testing-library/svelte' ;
20- import { RemoteMocks } from '/@/tests/remote-mocks' ;
21- import { API_POD_LOGS , type PodLogsChunk , type PodLogsApi } from '@kubernetes-dashboard/channels' ;
22- import { StreamsMocks } from '/@/tests/stream-mocks' ;
23- import { FakeStreamObject } from '/@/stream/util/fake-stream-object.svelte' ;
24- import PodLogs from './PodLogs.svelte' ;
19+ import { API_POD_LOGS , type PodLogsApi , type PodLogsChunk } from '@kubernetes-dashboard/channels' ;
2520import type { V1Pod } from '@kubernetes/client-node' ;
26- import TerminalWindow from '/@/component/terminal/TerminalWindow.svelte' ;
27- import type { Terminal } from '@xterm/xterm' ;
2821import { EmptyScreen } from '@podman-desktop/ui-svelte' ;
22+ import { render } from '@testing-library/svelte' ;
23+ import type { Terminal } from '@xterm/xterm' ;
2924import { beforeEach , describe , expect , test , vi } from 'vitest' ;
25+ import PodLogs from './PodLogs.svelte' ;
26+ import TerminalWindow from '/@/component/terminal/TerminalWindow.svelte' ;
27+ import { FakeStreamObject } from '/@/stream/util/fake-stream-object.svelte' ;
28+ import { RemoteMocks } from '/@/tests/remote-mocks' ;
29+ import { StreamsMocks } from '/@/tests/stream-mocks' ;
3030
3131vi . mock ( import ( '../terminal/TerminalWindow.svelte' ) ) ;
3232vi . mock ( import ( '@podman-desktop/ui-svelte' ) ) ;
@@ -36,6 +36,36 @@ const streamMocks = new StreamsMocks();
3636
3737const streamPodLogsMock = new FakeStreamObject < PodLogsChunk > ( ) ;
3838
39+ // Helper to create a mock terminal
40+ function createMockTerminal ( ) : Terminal {
41+ return {
42+ write : vi . fn ( ) ,
43+ dispose : vi . fn ( ) ,
44+ clear : vi . fn ( ) ,
45+ } as unknown as Terminal ;
46+ }
47+
48+ // Helper to create a pod with the specified containers
49+ function createPod ( containerNames : string [ ] ) : V1Pod {
50+ return {
51+ metadata : {
52+ name : 'podName' ,
53+ namespace : 'namespace' ,
54+ } ,
55+ spec : {
56+ containers : containerNames . map ( name => ( { name } ) ) ,
57+ } ,
58+ } as V1Pod ;
59+ }
60+
61+ // Helper to setup terminal mock with binding
62+ function setupTerminalMock ( terminal : Terminal ) : void {
63+ vi . mocked ( TerminalWindow ) . mockImplementation ( ( _ , props ) => {
64+ props . terminal = terminal ;
65+ return { } ;
66+ } ) ;
67+ }
68+
3969beforeEach ( ( ) => {
4070 vi . resetAllMocks ( ) ;
4171 streamMocks . reset ( ) ;
@@ -45,123 +75,161 @@ beforeEach(() => {
4575 remoteMocks . mock ( API_POD_LOGS , { } as unknown as PodLogsApi ) ;
4676} ) ;
4777
48- describe ( 'pod with one container' , async ( ) => {
49- let pod : V1Pod ;
50- beforeEach ( ( ) => {
51- pod = {
52- metadata : {
53- name : 'podName' ,
78+ describe ( 'PodLogs' , ( ) => {
79+ describe ( 'EmptyScreen display' , ( ) => {
80+ test . each ( [
81+ { containerCount : 1 , containers : [ 'containerName' ] } ,
82+ { containerCount : 2 , containers : [ 'cnt1' , 'containerName2' ] } ,
83+ ] ) ( 'should display "No Log" with $containerCount container(s) when no logs received' , ( { containers } ) => {
84+ const pod = createPod ( containers ) ;
85+ render ( PodLogs , { object : pod } ) ;
86+
87+ expect ( EmptyScreen ) . toHaveBeenCalledWith (
88+ expect . anything ( ) ,
89+ expect . objectContaining ( {
90+ hidden : false ,
91+ } ) ,
92+ ) ;
93+ } ) ;
94+
95+ test ( 'should hide EmptyScreen when logs are received' , ( ) => {
96+ const pod = createPod ( [ 'containerName' ] ) ;
97+ const mockedTerminal = createMockTerminal ( ) ;
98+ setupTerminalMock ( mockedTerminal ) ;
99+
100+ render ( PodLogs , { object : pod } ) ;
101+
102+ streamPodLogsMock . sendData ( {
103+ podName : 'podName' ,
54104 namespace : 'namespace' ,
55- } ,
56- spec : {
57- containers : [
58- {
59- name : 'containerName' ,
60- } ,
61- ] ,
62- } ,
63- } as V1Pod ;
64- } ) ;
105+ containerName : 'containerName' ,
106+ data : 'some logs' ,
107+ } ) ;
65108
66- test ( 'display No Logwith no logs' , async ( ) => {
67- render ( PodLogs , { object : pod } ) ;
68- expect ( EmptyScreen ) . toHaveBeenCalledWith (
69- expect . anything ( ) ,
70- expect . objectContaining ( {
71- hidden : false ,
72- } ) ,
73- ) ;
109+ expect ( EmptyScreen ) . toHaveBeenLastCalledWith (
110+ expect . anything ( ) ,
111+ expect . objectContaining ( {
112+ hidden : true ,
113+ } ) ,
114+ ) ;
115+ } ) ;
74116 } ) ;
75117
76- test ( 'write received logs to the terminal' , async ( ) => {
77- const mockedTerminal : Terminal = {
78- write : vi . fn ( ) ,
79- dispose : vi . fn ( ) ,
80- clear : vi . fn ( ) ,
81- } as unknown as Terminal ;
82- vi . mocked ( TerminalWindow ) . mockImplementation ( ( _ , props ) => {
83- props . terminal = mockedTerminal ;
84- return { } ;
118+ describe ( 'log output formatting' , ( ) => {
119+ test ( 'should format single container logs without prefix' , ( ) => {
120+ const pod = createPod ( [ 'containerName' ] ) ;
121+ const mockedTerminal = createMockTerminal ( ) ;
122+ setupTerminalMock ( mockedTerminal ) ;
123+
124+ render ( PodLogs , { object : pod } ) ;
125+
126+ streamPodLogsMock . sendData ( {
127+ podName : 'podName' ,
128+ namespace : 'namespace' ,
129+ containerName : 'containerName' ,
130+ data : 'simple log line' ,
131+ } ) ;
132+
133+ expect ( mockedTerminal . write ) . toHaveBeenCalled ( ) ;
134+ const writtenLog = vi . mocked ( mockedTerminal . write ) . mock . calls [ 0 ] [ 0 ] as string ;
135+ expect ( writtenLog ) . toContain ( 'simple log line\r' ) ;
136+ expect ( writtenLog ) . not . toContain ( '|' ) ;
85137 } ) ;
86- render ( PodLogs , { object : pod } ) ;
87- expect ( mockedTerminal . write ) . not . toHaveBeenCalledWith ( ) ;
88- expect ( mockedTerminal . clear ) . toHaveBeenCalled ( ) ;
89- expect ( TerminalWindow ) . toHaveBeenCalled ( ) ;
90138
91- streamPodLogsMock . sendData ( {
92- podName : 'podName' ,
93- namespace : 'namespace' ,
94- containerName : 'containerName' ,
95- data : 'some logs' ,
139+ test ( 'should format multi-container logs with colored prefix and padding' , ( ) => {
140+ const pod = createPod ( [ 'cnt1' , 'containerName2' ] ) ;
141+ const mockedTerminal = createMockTerminal ( ) ;
142+ setupTerminalMock ( mockedTerminal ) ;
143+
144+ render ( PodLogs , { object : pod } ) ;
145+
146+ streamPodLogsMock . sendData ( {
147+ podName : 'podName' ,
148+ namespace : 'namespace' ,
149+ containerName : 'cnt1' ,
150+ data : 'log from cnt1' ,
151+ } ) ;
152+
153+ expect ( mockedTerminal . write ) . toHaveBeenCalled ( ) ;
154+ const writtenLog = vi . mocked ( mockedTerminal . write ) . mock . calls [ 0 ] [ 0 ] as string ;
155+ expect ( writtenLog ) . toContain ( '\u001b[36mcnt1\u001b[0m|log from cnt1' ) ;
96156 } ) ;
97- expect ( mockedTerminal . write ) . toHaveBeenCalledWith ( 'some logs\r' ) ;
98- expect ( EmptyScreen ) . toHaveBeenCalledWith (
99- expect . anything ( ) ,
100- expect . objectContaining ( {
101- hidden : true ,
102- } ) ,
103- ) ;
104- } ) ;
105- } ) ;
106157
107- describe ( 'pod with two containers' , async ( ) => {
108- let pod : V1Pod ;
109- beforeEach ( ( ) => {
110- pod = {
111- metadata : {
112- name : 'podName' ,
158+ test ( 'should apply log level colorization' , ( ) => {
159+ const pod = createPod ( [ 'containerName' ] ) ;
160+ const mockedTerminal = createMockTerminal ( ) ;
161+ setupTerminalMock ( mockedTerminal ) ;
162+
163+ render ( PodLogs , { object : pod } ) ;
164+
165+ streamPodLogsMock . sendData ( {
166+ podName : 'podName' ,
113167 namespace : 'namespace' ,
114- } ,
115- spec : {
116- containers : [
117- {
118- name : 'cnt1' ,
119- } ,
120- {
121- name : 'containerName2' ,
122- } ,
123- ] ,
124- } ,
125- } as V1Pod ;
126- } ) ;
168+ containerName : 'containerName' ,
169+ data : 'info: Application started' ,
170+ } ) ;
171+
172+ const writtenLog = vi . mocked ( mockedTerminal . write ) . mock . calls [ 0 ] [ 0 ] as string ;
173+ // Should contain ANSI color code for info: (cyan)
174+ expect ( writtenLog ) . toContain ( '\u001b[36m' ) ;
175+ expect ( writtenLog ) . toContain ( '\u001b[0m' ) ;
176+ expect ( writtenLog ) . toContain ( 'Application started' ) ;
177+ } ) ;
127178
128- test ( 'display No Logwith no logs' , async ( ) => {
129- render ( PodLogs , { object : pod } ) ;
130- expect ( EmptyScreen ) . toHaveBeenCalledWith (
131- expect . anything ( ) ,
132- expect . objectContaining ( {
133- hidden : false ,
134- } ) ,
135- ) ;
179+ test ( 'should apply JSON colorization when JSON logs detected' , ( ) => {
180+ const pod = createPod ( [ 'containerName' ] ) ;
181+ const mockedTerminal = createMockTerminal ( ) ;
182+ setupTerminalMock ( mockedTerminal ) ;
183+
184+ render ( PodLogs , { object : pod } ) ;
185+
186+ // Send JSON log data
187+ streamPodLogsMock . sendData ( {
188+ podName : 'podName' ,
189+ namespace : 'namespace' ,
190+ containerName : 'containerName' ,
191+ data : '{"level":"info","message":"test","count":42}' ,
192+ } ) ;
193+
194+ const writtenLog = vi . mocked ( mockedTerminal . write ) . mock . calls [ 0 ] [ 0 ] as string ;
195+ // Should contain ANSI color codes for JSON elements (braces in yellow, numbers in green)
196+ expect ( writtenLog ) . toContain ( '\u001b[33m{\u001b[0m' ) ; // yellow brace
197+ expect ( writtenLog ) . toContain ( '\u001b[32m42\u001b[0m' ) ; // green number
198+ } ) ;
136199 } ) ;
137200
138- test ( 'write received logs to the terminal v2' , async ( ) => {
139- const mockedTerminal : Terminal = {
140- write : vi . fn ( ) ,
141- dispose : vi . fn ( ) ,
142- clear : vi . fn ( ) ,
143- } as unknown as Terminal ;
144- vi . mocked ( TerminalWindow ) . mockImplementation ( ( _ , props ) => {
145- props . terminal = mockedTerminal ;
146- return { } ;
201+ describe ( 'terminal initialization' , ( ) => {
202+ test ( 'should clear terminal and create TerminalWindow on mount' , ( ) => {
203+ const pod = createPod ( [ 'containerName' ] ) ;
204+ const mockedTerminal = createMockTerminal ( ) ;
205+ setupTerminalMock ( mockedTerminal ) ;
206+
207+ render ( PodLogs , { object : pod } ) ;
208+
209+ expect ( mockedTerminal . clear ) . toHaveBeenCalled ( ) ;
210+ expect ( TerminalWindow ) . toHaveBeenCalled ( ) ;
147211 } ) ;
148- render ( PodLogs , { object : pod } ) ;
149- expect ( mockedTerminal . write ) . not . toHaveBeenCalledWith ( ) ;
150- expect ( mockedTerminal . clear ) . toHaveBeenCalled ( ) ;
151- expect ( TerminalWindow ) . toHaveBeenCalled ( ) ;
212+ } ) ;
152213
153- streamPodLogsMock . sendData ( {
154- podName : 'podName' ,
155- namespace : 'namespace' ,
156- containerName : 'cnt1' ,
157- data : 'some logs' ,
214+ describe ( 'multi-container prefix colors' , ( ) => {
215+ test ( 'should apply colored prefixes for each container' , ( ) => {
216+ const pod = createPod ( [ 'container1' , 'container2' , 'container3' ] ) ;
217+ const mockedTerminal = createMockTerminal ( ) ;
218+ setupTerminalMock ( mockedTerminal ) ;
219+
220+ render ( PodLogs , { object : pod } ) ;
221+
222+ // Send log from first container
223+ streamPodLogsMock . sendData ( {
224+ podName : 'podName' ,
225+ namespace : 'namespace' ,
226+ containerName : 'container1' ,
227+ data : 'log message' ,
228+ } ) ;
229+
230+ const calls = vi . mocked ( mockedTerminal . write ) . mock . calls ;
231+ // First container should have cyan prefix
232+ expect ( calls [ 0 ] [ 0 ] ) . toContain ( '\u001b[36mcontainer1\u001b[0m|' ) ;
158233 } ) ;
159- expect ( mockedTerminal . write ) . toHaveBeenCalledWith ( ' \u001b[36mcnt1\u001b[0m|some logs\r' ) ;
160- expect ( EmptyScreen ) . toHaveBeenCalledWith (
161- expect . anything ( ) ,
162- expect . objectContaining ( {
163- hidden : true ,
164- } ) ,
165- ) ;
166234 } ) ;
167235} ) ;
0 commit comments