11import React from 'react' ;
2- import { screen } from '@testing-library/react' ;
3- import { rtlHelpers } from 'foremanReact/common/rtlTestHelpers' ;
4- import { API } from 'foremanReact/redux/API' ;
2+ import { render , screen } from '@testing-library/react' ;
3+ import '@testing-library/jest-dom' ;
4+ import { Provider } from 'react-redux' ;
5+ import { ConnectedRouter } from 'connected-react-router' ;
6+ import { createMemoryHistory } from 'history' ;
7+ import configureMockStore from 'redux-mock-store' ;
8+ import { STATUS } from 'foremanReact/constants' ;
9+ import * as APIHooks from 'foremanReact/common/hooks/API/APIHooks' ;
10+ import * as ConfigHooks from '../../common/Hooks/ConfigHooks' ;
511import { CVECountCell } from '../CVECountCell' ;
612
7- jest . mock ( 'foremanReact/redux/ API' ) ;
13+ jest . mock ( 'foremanReact/common/hooks/ API/APIHooks ' ) ;
814jest . mock ( '../../common/Hooks/ConfigHooks' ) ;
915
10- const { renderWithStore } = rtlHelpers ;
11-
12- API . get . mockImplementation ( async ( ) => ( {
13- data : [
14- {
15- attributes : {
16- cve_count : 1 ,
17- } ,
16+ const mockStore = configureMockStore ( ) ;
17+ const history = createMemoryHistory ( ) ;
18+ const store = mockStore ( {
19+ router : {
20+ location : {
21+ pathname : '/' ,
22+ search : '' ,
23+ hash : '' ,
24+ state : null ,
1825 } ,
19- ] ,
20- } ) ) ;
26+ action : 'POP' ,
27+ } ,
28+ } ) ;
2129
2230const hostDetailsMock = {
2331 name : 'test-host.example.com' ,
@@ -26,104 +34,121 @@ const hostDetailsMock = {
2634 } ,
2735} ;
2836
37+ const renderComponent = ( props = { } ) => {
38+ const allProps = {
39+ hostDetails : hostDetailsMock ,
40+ ...props ,
41+ } ;
42+
43+ return render (
44+ < Provider store = { store } >
45+ < ConnectedRouter history = { history } >
46+ < CVECountCell { ...allProps } />
47+ </ ConnectedRouter >
48+ </ Provider >
49+ ) ;
50+ } ;
51+
2952describe ( 'CVECountCell' , ( ) => {
30- afterEach ( ( ) => {
53+ beforeEach ( ( ) => {
54+ store . clearActions ( ) ;
3155 jest . clearAllMocks ( ) ;
3256 } ) ;
3357
3458 it ( 'renders an empty cves count column when no subscription UUID' , ( ) => {
35- const hostDetailsMockIoP = {
59+ ConfigHooks . useIopConfig . mockReturnValue ( true ) ;
60+ APIHooks . useAPI . mockReturnValue ( {
61+ status : STATUS . RESOLVED ,
62+ response : null ,
63+ } ) ;
64+
65+ const hostDetailsMockNoUuid = {
3666 name : 'test-host.example.com' ,
3767 subscription_facet_attributes : {
3868 uuid : null , // no subscription
3969 } ,
4070 } ;
41- renderWithStore ( < CVECountCell hostDetails = { hostDetailsMockIoP } /> , {
42- router : {
43- location : {
44- pathname : '/' ,
45- search : '' ,
46- hash : '' ,
47- query : { } ,
48- } ,
49- } ,
50- API : {
51- ADVISOR_ENGINE_CONFIG : {
52- response : { use_iop_mode : true } ,
53- status : 'RESOLVED' ,
54- } ,
55- } ,
71+
72+ renderComponent ( { hostDetails : hostDetailsMockNoUuid } ) ;
73+ expect ( screen . getByText ( '—' ) ) . toBeInTheDocument ( ) ;
74+ } ) ;
75+
76+ it ( 'renders — when IoP is not enabled' , ( ) => {
77+ ConfigHooks . useIopConfig . mockReturnValue ( false ) ;
78+ APIHooks . useAPI . mockReturnValue ( {
79+ status : STATUS . RESOLVED ,
80+ response : null ,
5681 } ) ;
57- expect ( screen . getByRole ( 'img' , { hidden : true } ) ) . toBeTruthy ( ) ;
82+
83+ renderComponent ( ) ;
84+ expect ( screen . getByText ( '—' ) ) . toBeInTheDocument ( ) ;
5885 } ) ;
5986
60- it ( 'renders UnknownIcon when IoP is not enabled' , ( ) => {
61- renderWithStore ( < CVECountCell hostDetails = { hostDetailsMock } /> , {
62- router : {
63- location : {
64- pathname : '/' ,
65- search : '' ,
66- hash : '' ,
67- query : { } ,
68- } ,
69- } ,
70- API : {
71- ADVISOR_ENGINE_CONFIG : {
72- response : { use_iop_mode : false } ,
73- status : 'RESOLVED' ,
74- } ,
75- } ,
87+ it ( 'renders — when IoP is enabled but CVE API call fails' , ( ) => {
88+ ConfigHooks . useIopConfig . mockReturnValue ( true ) ;
89+ APIHooks . useAPI . mockReturnValue ( {
90+ status : STATUS . ERROR ,
91+ response : null ,
7692 } ) ;
77- expect ( screen . getByRole ( 'img' , { hidden : true } ) ) . toBeTruthy ( ) ;
93+
94+ renderComponent ( ) ;
95+ expect ( screen . getByText ( '—' ) ) . toBeInTheDocument ( ) ;
7896 } ) ;
7997
80- it ( 'renders UnknownIcon when IoP is enabled but CVE API call fails' , ( ) => {
81- // Mock CVE API failure - override the global mock for this test
82- API . get . mockImplementationOnce ( async ( ) => {
83- throw new Error ( 'CVE API call failed' ) ;
98+ it ( 'renders — when IoP is undefined (API call pending)' , ( ) => {
99+ ConfigHooks . useIopConfig . mockReturnValue ( undefined ) ;
100+ APIHooks . useAPI . mockReturnValue ( {
101+ status : STATUS . PENDING ,
102+ response : null ,
84103 } ) ;
85104
86- renderWithStore ( < CVECountCell hostDetails = { hostDetailsMock } /> , {
87- router : {
88- location : {
89- pathname : '/' ,
90- search : '' ,
91- hash : '' ,
92- query : { } ,
93- } ,
94- } ,
95- API : {
96- ADVISOR_ENGINE_CONFIG : {
97- response : { use_iop_mode : true } ,
98- status : 'RESOLVED' ,
99- } ,
100- // Mock the API failure state for the CVE endpoint
101- [ `HOST_CVE_COUNT_${ hostDetailsMock . subscription_facet_attributes . uuid } ` ] : {
102- status : 'ERROR' ,
103- error : 'CVE API call failed' ,
104- } ,
105+ renderComponent ( ) ;
106+ expect ( screen . getByText ( '—' ) ) . toBeInTheDocument ( ) ;
107+ } ) ;
108+
109+ it ( 'renders "Analysis disabled" when opt_out is true' , ( ) => {
110+ ConfigHooks . useIopConfig . mockReturnValue ( true ) ;
111+ APIHooks . useAPI . mockReturnValue ( {
112+ status : STATUS . RESOLVED ,
113+ response : {
114+ data : [
115+ {
116+ attributes : {
117+ opt_out : true ,
118+ } ,
119+ } ,
120+ ] ,
105121 } ,
106122 } ) ;
107- // Should render UnknownIcon when CVE API fails
108- expect ( screen . getByRole ( 'img' , { hidden : true } ) ) . toBeTruthy ( ) ;
123+
124+ renderComponent ( ) ;
125+ expect ( screen . getByText ( 'Analysis disabled' ) ) . toBeInTheDocument ( ) ;
109126 } ) ;
110127
111- it ( 'renders UnknownIcon when IoP is undefined (API call pending)' , ( ) => {
112- renderWithStore ( < CVECountCell hostDetails = { hostDetailsMock } /> , {
113- router : {
114- location : {
115- pathname : '/' ,
116- search : '' ,
117- hash : '' ,
118- query : { } ,
119- } ,
120- } ,
121- API : {
122- ADVISOR_ENGINE_CONFIG : {
123- status : 'PENDING' ,
124- } ,
128+ it ( 'renders CVE count link when valid count is returned' , ( ) => {
129+ ConfigHooks . useIopConfig . mockReturnValue ( true ) ;
130+ APIHooks . useAPI . mockReturnValue ( {
131+ status : STATUS . RESOLVED ,
132+ response : {
133+ data : [
134+ {
135+ attributes : {
136+ cve_count : 1 ,
137+ } ,
138+ } ,
139+ ] ,
125140 } ,
126141 } ) ;
127- expect ( screen . getByRole ( 'img' , { hidden : true } ) ) . toBeTruthy ( ) ;
142+
143+ renderComponent ( ) ;
144+
145+ // Should render a link with the CVE count
146+ const link = screen . getByRole ( 'link' ) ;
147+ expect ( link ) . toBeInTheDocument ( ) ;
148+ expect ( link ) . toHaveTextContent ( '1' ) ;
149+ expect ( link ) . toHaveAttribute (
150+ 'href' ,
151+ `/hosts/${ hostDetailsMock . name } #/Vulnerabilities`
152+ ) ;
128153 } ) ;
129154} ) ;
0 commit comments