Skip to content

Commit 90382ef

Browse files
committed
chore: add unit test for VexManagement, Dashboard, and ImageDetails
1 parent fb8f8b2 commit 90382ef

File tree

3 files changed

+674
-0
lines changed

3 files changed

+674
-0
lines changed
Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
import { mount } from '@vue/test-utils';
2+
import { createStore } from 'vuex';
3+
import ImageDetails from '../ImageDetails.vue';
4+
5+
// Define constants locally to avoid import issues
6+
const RESOURCE = {
7+
IMAGE: "sbombastic.rancher.io.image"
8+
};
9+
10+
describe('ImageDetails', () => {
11+
let store: any;
12+
let wrapper: any;
13+
14+
const mockImageData = {
15+
metadata: { name: 'test-image' },
16+
spec: {
17+
repository: 'test-repo',
18+
registry: 'test-registry',
19+
scanResult: {
20+
critical: 5,
21+
high: 10,
22+
medium: 15,
23+
low: 8,
24+
none: 2
25+
}
26+
}
27+
};
28+
29+
beforeEach(() => {
30+
store = createStore({
31+
modules: {
32+
cluster: {
33+
namespaced: true,
34+
getters: {
35+
'all': () => (type: string) => {
36+
if (type === RESOURCE.IMAGE) return [mockImageData];
37+
return [];
38+
}
39+
},
40+
actions: {
41+
'findAll': jest.fn()
42+
}
43+
}
44+
}
45+
});
46+
47+
wrapper = mount(ImageDetails, {
48+
global: {
49+
plugins: [store],
50+
mocks: {
51+
$route: {
52+
params: {
53+
cluster: 'test-cluster',
54+
id: 'test-image'
55+
}
56+
},
57+
$router: {
58+
push: jest.fn()
59+
},
60+
$t: (key: string) => key,
61+
$store: store
62+
},
63+
stubs: {
64+
RouterLink: {
65+
template: '<a><slot /></a>',
66+
props: ['to']
67+
},
68+
BadgeState: {
69+
template: '<span class="badge-state"><slot /></span>',
70+
props: ['color', 'label']
71+
},
72+
SortableTable: {
73+
template: '<div class="sortable-table"><slot name="header-left" /><slot name="search" /></div>',
74+
props: ['rows', 'headers', 'hasAdvancedFiltering', 'namespaced', 'rowActions', 'search']
75+
},
76+
ScoreBadge: {
77+
template: '<span class="score-badge"><slot /></span>',
78+
props: ['score', 'scoreType']
79+
},
80+
BarChart: {
81+
template: '<div class="bar-chart"></div>',
82+
props: ['chartData', 'description', 'colorPrefix']
83+
},
84+
Checkbox: {
85+
template: '<input type="checkbox" class="checkbox" />',
86+
props: ['value', 'label']
87+
}
88+
}
89+
}
90+
});
91+
});
92+
93+
afterEach(() => {
94+
wrapper.unmount();
95+
});
96+
97+
describe('Component Initialization', () => {
98+
it('should render the component', () => {
99+
expect(wrapper.exists()).toBe(true);
100+
});
101+
102+
it('should display the correct title with image name', () => {
103+
const title = wrapper.find('.title');
104+
expect(title.exists()).toBe(true);
105+
expect(title.text()).toContain('test-image');
106+
});
107+
108+
it('should display the severity badge', () => {
109+
const severityBadge = wrapper.find('.severity-badge');
110+
expect(severityBadge.exists()).toBe(true);
111+
});
112+
113+
it('should display the download report button', () => {
114+
const downloadButton = wrapper.find('.btn.role-primary');
115+
expect(downloadButton.exists()).toBe(true);
116+
expect(downloadButton.text()).toContain('imageScanner.images.downloadReport');
117+
});
118+
});
119+
120+
describe('Image Information Section', () => {
121+
it('should display image details in info grid', () => {
122+
const infoGrid = wrapper.find('.info-grid');
123+
expect(infoGrid.exists()).toBe(true);
124+
});
125+
126+
it('should show basic image properties', () => {
127+
const infoItems = wrapper.findAll('.info-item');
128+
expect(infoItems.length).toBeGreaterThan(0);
129+
});
130+
131+
it('should toggle show all properties when clicked', async () => {
132+
const showPropertiesLink = wrapper.find('.show-properties-link a');
133+
expect(showPropertiesLink.exists()).toBe(true);
134+
135+
await showPropertiesLink.trigger('click');
136+
expect(wrapper.vm.showAllProperties).toBe(true);
137+
138+
await showPropertiesLink.trigger('click');
139+
expect(wrapper.vm.showAllProperties).toBe(false);
140+
});
141+
142+
it('should show additional properties when showAllProperties is true', async () => {
143+
wrapper.vm.showAllProperties = true;
144+
await wrapper.vm.$nextTick();
145+
146+
// These should be visible when showAllProperties is true
147+
expect(wrapper.vm.showAllProperties).toBe(true);
148+
149+
// Check that additional properties are rendered
150+
const infoItems = wrapper.findAll('.info-item');
151+
expect(infoItems.length).toBeGreaterThan(10); // Should have more items when expanded
152+
});
153+
});
154+
155+
describe('Summary Section', () => {
156+
it('should render vulnerabilities section', () => {
157+
const vulnerabilitiesSection = wrapper.find('.vulnerabilities-section');
158+
expect(vulnerabilitiesSection.exists()).toBe(true);
159+
});
160+
161+
it('should render severity section with BarChart', () => {
162+
const severitySection = wrapper.find('.severity-section');
163+
expect(severitySection.exists()).toBe(true);
164+
165+
const barChart = wrapper.find('.bar-chart');
166+
expect(barChart.exists()).toBe(true);
167+
});
168+
169+
it('should display most severe vulnerabilities', () => {
170+
const vulnerabilitiesList = wrapper.find('.vulnerabilities-list');
171+
expect(vulnerabilitiesList.exists()).toBe(true);
172+
});
173+
});
174+
175+
describe('Vulnerability Table', () => {
176+
it('should render SortableTable component', () => {
177+
const sortableTable = wrapper.find('.sortable-table');
178+
expect(sortableTable.exists()).toBe(true);
179+
});
180+
181+
it('should display download custom report button', () => {
182+
const downloadCustomButton = wrapper.find('.table-header-actions .btn.role-primary');
183+
expect(downloadCustomButton.exists()).toBe(true);
184+
expect(downloadCustomButton.text()).toContain('imageScanner.images.buttons.downloadCustomReport');
185+
});
186+
187+
it('should show selected count when vulnerabilities are selected', async () => {
188+
wrapper.vm.selectedVulnerabilities = ['CVE-2017-5337', 'CVE-2018-1000007'];
189+
await wrapper.vm.$nextTick();
190+
191+
const selectedCount = wrapper.find('.selected-count');
192+
expect(selectedCount.exists()).toBe(true);
193+
expect(selectedCount.text()).toContain('2');
194+
});
195+
});
196+
197+
describe('Computed Properties', () => {
198+
it('should calculate total vulnerabilities correctly', () => {
199+
wrapper.vm.severityDistribution = {
200+
critical: 5,
201+
high: 10,
202+
medium: 15,
203+
low: 8,
204+
none: 2
205+
};
206+
207+
expect(wrapper.vm.totalVulnerabilities).toBe(40);
208+
});
209+
210+
it('should determine overall severity correctly', () => {
211+
wrapper.vm.severityDistribution = {
212+
critical: 5,
213+
high: 0,
214+
medium: 0,
215+
low: 0,
216+
none: 0
217+
};
218+
219+
expect(wrapper.vm.overallSeverity).toBe('critical');
220+
});
221+
222+
it('should return none when no vulnerabilities', () => {
223+
wrapper.vm.severityDistribution = {
224+
critical: 0,
225+
high: 0,
226+
medium: 0,
227+
low: 0,
228+
none: 0
229+
};
230+
231+
expect(wrapper.vm.overallSeverity).toBe('none');
232+
});
233+
234+
it('should calculate severity distribution with percentages', () => {
235+
wrapper.vm.severityDistribution = {
236+
critical: 10,
237+
high: 20,
238+
medium: 0,
239+
low: 0,
240+
none: 0
241+
};
242+
243+
const distribution = wrapper.vm.severityDistributionWithPercentages;
244+
expect(distribution.critical.percentage).toBe('33.3');
245+
expect(distribution.high.percentage).toBe('66.7');
246+
});
247+
248+
it('should filter vulnerabilities by CVE search', () => {
249+
wrapper.vm.filters.cveSearch = 'CVE-2017';
250+
const filtered = wrapper.vm.filteredVulnerabilities;
251+
expect(filtered.every(v => v.cveId.includes('CVE-2017'))).toBe(true);
252+
});
253+
254+
it('should filter vulnerabilities by score', () => {
255+
wrapper.vm.filters.score = '9.0';
256+
const filtered = wrapper.vm.filteredVulnerabilities;
257+
expect(filtered.every(v => {
258+
const score = parseFloat(v.score.split(' ')[0]);
259+
return score >= 9.0;
260+
})).toBe(true);
261+
});
262+
263+
it('should filter vulnerabilities by package search', () => {
264+
wrapper.vm.filters.packageSearch = 'tomcat';
265+
const filtered = wrapper.vm.filteredVulnerabilities;
266+
expect(filtered.every(v => v.package.toLowerCase().includes('tomcat'))).toBe(true);
267+
});
268+
269+
it('should filter vulnerabilities by fix availability', () => {
270+
wrapper.vm.filters.fixAvailable = 'available';
271+
const filtered = wrapper.vm.filteredVulnerabilities;
272+
expect(filtered.every(v => v.fixAvailable === true)).toBe(true);
273+
});
274+
275+
it('should filter vulnerabilities by severity', () => {
276+
wrapper.vm.filters.severity = 'critical';
277+
const filtered = wrapper.vm.filteredVulnerabilities;
278+
expect(filtered.every(v => v.severity === 'critical')).toBe(true);
279+
});
280+
281+
it('should filter vulnerabilities by exploitability', () => {
282+
wrapper.vm.filters.exploitability = 'affected';
283+
const filtered = wrapper.vm.filteredVulnerabilities;
284+
expect(filtered.every(v => v.exploitability === 'affected')).toBe(true);
285+
});
286+
});
287+
288+
describe('Methods', () => {
289+
it('should return correct severity color', () => {
290+
expect(wrapper.vm.getSeverityColor('critical')).toBe('critical-severity');
291+
expect(wrapper.vm.getSeverityColor('high')).toBe('bg-error');
292+
expect(wrapper.vm.getSeverityColor('medium')).toBe('bg-warning');
293+
expect(wrapper.vm.getSeverityColor('low')).toBe('bg-warning');
294+
expect(wrapper.vm.getSeverityColor('none')).toBe('bg-info');
295+
});
296+
297+
it('should return correct severity bar color', () => {
298+
expect(wrapper.vm.getSeverityBarColor('critical')).toBe('#850917');
299+
expect(wrapper.vm.getSeverityBarColor('high')).toBe('#DE2136');
300+
expect(wrapper.vm.getSeverityBarColor('medium')).toBe('#FF8533');
301+
expect(wrapper.vm.getSeverityBarColor('low')).toBe('#FDD835');
302+
expect(wrapper.vm.getSeverityBarColor('none')).toBe('#E0E0E0');
303+
});
304+
305+
it('should handle selection change', () => {
306+
const selected = ['CVE-2017-5337', 'CVE-2018-1000007'];
307+
wrapper.vm.onSelectionChange(selected);
308+
expect(wrapper.vm.selectedVulnerabilities).toEqual(selected);
309+
});
310+
311+
it('should handle download full report', () => {
312+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
313+
wrapper.vm.downloadFullReport();
314+
expect(consoleSpy).toHaveBeenCalledWith('Downloading full report for:', wrapper.vm.imageName);
315+
consoleSpy.mockRestore();
316+
});
317+
318+
it('should handle download custom report', () => {
319+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
320+
wrapper.vm.downloadCustomReport();
321+
expect(consoleSpy).toHaveBeenCalledWith('Downloading custom report with filters:', wrapper.vm.filters);
322+
consoleSpy.mockRestore();
323+
});
324+
325+
it('should calculate most severe vulnerabilities correctly', () => {
326+
// Ensure vulnerability details are loaded first
327+
wrapper.vm.vulnerabilityDetails = wrapper.vm.getMockVulnerabilityDetails();
328+
wrapper.vm.calculateMostSevereVulnerabilities();
329+
expect(wrapper.vm.mostSevereVulnerabilities).toHaveLength(5);
330+
331+
// Should be sorted by score descending
332+
const scores = wrapper.vm.mostSevereVulnerabilities.map(v => parseFloat(v.score.split(' ')[0]));
333+
for (let i = 1; i < scores.length; i++) {
334+
expect(scores[i-1]).toBeGreaterThanOrEqual(scores[i]);
335+
}
336+
});
337+
});
338+
339+
describe('Component Data', () => {
340+
it('should have correct initial data properties', () => {
341+
expect(wrapper.vm.PRODUCT_NAME).toBeDefined();
342+
expect(wrapper.vm.RESOURCE).toBeDefined();
343+
expect(wrapper.vm.PAGE).toBeDefined();
344+
expect(wrapper.vm.VULNERABILITY_DETAILS_TABLE).toBeDefined();
345+
expect(wrapper.vm.showAllProperties).toBe(false);
346+
expect(wrapper.vm.selectedVulnerabilities).toEqual([]);
347+
});
348+
349+
it('should have correct filter defaults', () => {
350+
expect(wrapper.vm.filters.cveSearch).toBe('');
351+
expect(wrapper.vm.filters.score).toBe('');
352+
expect(wrapper.vm.filters.packageSearch).toBe('');
353+
expect(wrapper.vm.filters.fixAvailable).toBe('any');
354+
expect(wrapper.vm.filters.severity).toBe('any');
355+
expect(wrapper.vm.filters.exploitability).toBe('any');
356+
});
357+
});
358+
359+
describe('Mock Data', () => {
360+
it('should generate mock vulnerability details', () => {
361+
const mockData = wrapper.vm.getMockVulnerabilityDetails();
362+
expect(Array.isArray(mockData)).toBe(true);
363+
expect(mockData.length).toBeGreaterThan(0);
364+
365+
// Check structure of first vulnerability
366+
const firstVuln = mockData[0];
367+
expect(firstVuln).toHaveProperty('cveId');
368+
expect(firstVuln).toHaveProperty('score');
369+
expect(firstVuln).toHaveProperty('package');
370+
expect(firstVuln).toHaveProperty('fixAvailable');
371+
expect(firstVuln).toHaveProperty('severity');
372+
expect(firstVuln).toHaveProperty('exploitability');
373+
});
374+
});
375+
});

0 commit comments

Comments
 (0)