Skip to content

Commit 697cf5e

Browse files
[scanner] šŸ› fix: update StorageOverview and MiniDashboard tests for useModalState migration (#19310)
Signed-off-by: Andy Anderson <andy@clubanderson.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent ffa2b94 commit 697cf5e

2 files changed

Lines changed: 226 additions & 217 deletions

File tree

Lines changed: 162 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,194 +1,240 @@
1-
import { describe, it, expect, vi, beforeEach } from 'vitest'
1+
import { beforeEach, describe, expect, it, vi } from 'vitest'
22
import { render, screen } from '@testing-library/react'
33
import { StorageOverview } from '../StorageOverview'
44

5-
// ── Mocks ────────────────────────────────────────────────────────────────────
5+
vi.mock('react-i18next', () => ({
6+
initReactI18next: { type: '3rdParty', init: () => {} },
7+
useTranslation: () => ({
8+
t: (key: string, opts?: Record<string, unknown>) => {
9+
if (opts && 'count' in opts) return `${opts.count}`
10+
if (opts && 'pvcs' in opts && 'clusters' in opts) return `${opts.pvcs} PVCs, ${opts.clusters} clusters`
11+
if (opts && 'error' in opts) return `Failed: ${opts.error}`
12+
return key
13+
},
14+
}),
15+
}))
616

17+
const mockUseClusters = vi.fn()
718
vi.mock('../../../hooks/useMCP', () => ({
8-
useClusters: () => ({
9-
deduplicatedClusters: [{ name: 'cluster-1', storageGB: 100, nodeCount: 3, reachable: true }],
10-
isLoading: false,
11-
isRefreshing: false,
12-
}),
19+
useClusters: () => mockUseClusters(),
1320
}))
1421

22+
const mockUseCachedPVCs = vi.fn()
1523
vi.mock('../../../hooks/useCachedData', () => ({
16-
useCachedPVCs: vi.fn(() => ({
17-
pvcs: [],
18-
isLoading: false,
19-
isRefreshing: false,
20-
isDemoFallback: false,
21-
isFailed: false,
22-
consecutiveFailures: 0,
23-
})),
24+
useCachedPVCs: () => mockUseCachedPVCs(),
2425
}))
2526

27+
const mockUseGlobalFilters = vi.fn()
2628
vi.mock('../../../hooks/useGlobalFilters', () => ({
27-
useGlobalFilters: () => ({ selectedClusters: [], isAllClustersSelected: true }),
28-
}))
29-
30-
vi.mock('../../../hooks/useDrillDown', () => ({
31-
useDrillDownActions: () => ({}),
32-
}))
33-
34-
vi.mock('../CardDataContext', () => ({
35-
useCardLoadingState: vi.fn(() => ({ showSkeleton: false, showEmptyState: false })),
29+
useGlobalFilters: () => mockUseGlobalFilters(),
3630
}))
3731

32+
const mockUseDemoMode = vi.fn()
3833
vi.mock('../../../hooks/useDemoMode', () => ({
39-
useDemoMode: () => ({ isDemoMode: false }),
40-
getDemoMode: () => false, default: () => false,
41-
hasRealToken: () => false, isDemoModeForced: false, isNetlifyDeployment: false,
42-
canToggleDemoMode: () => true, isDemoToken: () => true, setDemoToken: vi.fn(),
43-
setGlobalDemoMode: vi.fn(),
34+
useDemoMode: () => mockUseDemoMode(),
4435
}))
4536

46-
vi.mock('react-i18next', () => ({
47-
initReactI18next: { type: '3rdParty', init: () => {} },
48-
useTranslation: () => ({
49-
t: (k: string, opts?: Record<string, unknown>) => {
50-
if (opts?.count !== undefined) return `${k}:${opts.count}`
51-
return k
52-
},
53-
}),
37+
const mockUseCardLoadingState = vi.fn()
38+
vi.mock('../CardDataContext', () => ({
39+
useCardLoadingState: (args: Record<string, unknown>) => mockUseCardLoadingState(args),
5440
}))
5541

42+
const mockUseChartFilters = vi.fn()
5643
vi.mock('../../../lib/cards/cardHooks', () => ({
57-
useChartFilters: () => ({
58-
localClusterFilter: [],
59-
toggleClusterFilter: vi.fn(),
60-
clearClusterFilter: vi.fn(),
61-
availableClusters: [{ name: 'cluster-1' }],
62-
showClusterFilter: false,
63-
setShowClusterFilter: vi.fn(),
64-
clusterFilterRef: { current: null },
65-
}),
44+
useChartFilters: () => mockUseChartFilters(),
6645
}))
6746

6847
vi.mock('../../../lib/cards/CardComponents', () => ({
69-
CardClusterFilter: () => <div data-testid="cluster-filter" />,
48+
CardClusterFilter: ({ availableClusters }: { availableClusters: Array<{ name: string }> }) => (
49+
<div data-testid="cluster-filter" data-count={availableClusters.length} />
50+
),
7051
}))
7152

7253
vi.mock('../../../lib/formatStats', () => ({
73-
formatStat: (n: number) => String(n),
74-
formatStorageStat: (n: number, real?: boolean) => (real ? `${n}GB` : 'N/A'),
54+
formatStat: (value: number) => String(value),
55+
formatStorageStat: (value: number, hasRealData?: boolean) => (hasRealData === false ? 'N/A' : `${value}GB`),
7556
}))
7657

77-
// ── Tests ────────────────────────────────────────────────────────────────────
58+
vi.mock('../../ui/Skeleton', () => ({
59+
Skeleton: ({ width, height }: { width?: number; height?: number }) => (
60+
<div data-testid="skeleton" style={{ width, height }} />
61+
),
62+
SkeletonStats: ({ className }: { className?: string }) => (
63+
<div data-testid="skeleton-stats" className={className} />
64+
),
65+
SkeletonList: ({ items, className }: { items?: number; className?: string }) => (
66+
<div data-testid="skeleton-list" data-items={items} className={className} />
67+
),
68+
}))
69+
70+
type MockPVC = {
71+
status: string
72+
cluster: string
73+
namespace: string
74+
name: string
75+
storageClass: string
76+
}
77+
78+
const makePVC = (
79+
status: string,
80+
cluster = 'cluster-1',
81+
storageClass = 'standard',
82+
name = `pvc-${status.toLowerCase()}`
83+
): MockPVC => ({
84+
status,
85+
cluster,
86+
namespace: 'default',
87+
name,
88+
storageClass,
89+
})
90+
91+
const defaultClustersReturn = {
92+
deduplicatedClusters: [{ name: 'cluster-1', storageGB: 100, nodeCount: 3, reachable: true }],
93+
isLoading: false,
94+
isRefreshing: false,
95+
}
96+
97+
const defaultPVCsReturn = {
98+
pvcs: [makePVC('Bound'), makePVC('Pending'), makePVC('Lost')],
99+
isLoading: false,
100+
isRefreshing: false,
101+
isDemoFallback: false,
102+
isFailed: false,
103+
consecutiveFailures: 0,
104+
error: null,
105+
}
106+
107+
const defaultGlobalFilters = {
108+
selectedClusters: [],
109+
isAllClustersSelected: true,
110+
}
111+
112+
const defaultChartFilters = {
113+
localClusterFilter: [],
114+
toggleClusterFilter: vi.fn(),
115+
clearClusterFilter: vi.fn(),
116+
availableClusters: [{ name: 'cluster-1' }],
117+
showClusterFilter: false,
118+
setShowClusterFilter: vi.fn(),
119+
clusterFilterRef: { current: null },
120+
}
121+
122+
function setup(): void {
123+
mockUseClusters.mockReturnValue(defaultClustersReturn)
124+
mockUseCachedPVCs.mockReturnValue(defaultPVCsReturn)
125+
mockUseGlobalFilters.mockReturnValue(defaultGlobalFilters)
126+
mockUseDemoMode.mockReturnValue({ isDemoMode: false })
127+
mockUseChartFilters.mockReturnValue(defaultChartFilters)
128+
mockUseCardLoadingState.mockReturnValue({ showSkeleton: false, showEmptyState: false })
129+
}
78130

79131
describe('StorageOverview', () => {
80-
beforeEach(async () => {
132+
beforeEach(() => {
81133
vi.clearAllMocks()
82-
const { useCardLoadingState } = await import('../CardDataContext')
83-
vi.mocked(useCardLoadingState).mockReturnValue({ showSkeleton: false, showEmptyState: false } as never)
134+
setup()
84135
})
85136

86137
describe('Skeleton / empty states', () => {
87-
it('renders loading spinner when showSkeleton', async () => {
88-
const { useCardLoadingState } = await import('../CardDataContext')
89-
vi.mocked(useCardLoadingState).mockReturnValue({ showSkeleton: true, showEmptyState: false } as never)
138+
it('renders loading spinner when showSkeleton', () => {
139+
mockUseCardLoadingState.mockReturnValue({ showSkeleton: true, showEmptyState: false })
140+
90141
render(<StorageOverview />)
91-
expect(screen.getByText('storageOverview.loading')).toBeTruthy()
142+
143+
expect(screen.getByText('storageOverview.loading')).toBeInTheDocument()
144+
expect(screen.getByTestId('skeleton-stats')).toBeInTheDocument()
145+
expect(screen.getByTestId('skeleton-list')).toBeInTheDocument()
92146
})
93147

94-
it('renders no data message when showEmptyState', async () => {
95-
const { useCardLoadingState } = await import('../CardDataContext')
96-
vi.mocked(useCardLoadingState).mockReturnValue({ showSkeleton: false, showEmptyState: true } as never)
148+
it('renders no data message when showEmptyState', () => {
149+
mockUseCardLoadingState.mockReturnValue({ showSkeleton: false, showEmptyState: true })
150+
mockUseCachedPVCs.mockReturnValue({ ...defaultPVCsReturn, pvcs: [] })
151+
97152
render(<StorageOverview />)
98-
expect(screen.getByText('storageOverview.noData')).toBeTruthy()
153+
154+
expect(screen.getByText('storageOverview.noData')).toBeInTheDocument()
99155
})
100156
})
101157

102158
describe('Main stats', () => {
103159
it('renders total capacity and PVCs tiles', () => {
104160
render(<StorageOverview />)
105-
expect(screen.getByText('storageOverview.totalCapacity')).toBeTruthy()
106-
expect(screen.getByText('storageOverview.pvcs')).toBeTruthy()
161+
162+
expect(screen.getByText('storageOverview.totalCapacity')).toBeInTheDocument()
163+
expect(screen.getByText('storageOverview.pvcs')).toBeInTheDocument()
164+
expect(screen.getByText('100GB')).toBeInTheDocument()
165+
expect(screen.getAllByText('3').length).toBeGreaterThan(0)
107166
})
108167

109168
it('renders bound, pending, failed PVC breakdown', () => {
110169
render(<StorageOverview />)
111-
expect(screen.getByText('storageOverview.bound')).toBeTruthy()
112-
expect(screen.getByText('common:common.pending')).toBeTruthy()
113-
expect(screen.getByText('common:common.failed')).toBeTruthy()
170+
171+
expect(screen.getByText('storageOverview.bound')).toBeInTheDocument()
172+
expect(screen.getByText('common:common.pending')).toBeInTheDocument()
173+
expect(screen.getByText('common:common.failed')).toBeInTheDocument()
114174
})
115175
})
116176

117177
describe('PVC counts', () => {
118-
it('counts bound/pending/failed PVCs correctly', async () => {
119-
const { useCachedPVCs } = await import('../../../hooks/useCachedData')
120-
vi.mocked(useCachedPVCs).mockReturnValue({
178+
it('counts bound/pending/failed PVCs correctly', () => {
179+
mockUseCachedPVCs.mockReturnValue({
180+
...defaultPVCsReturn,
121181
pvcs: [
122-
{ cluster: 'cluster-1', namespace: 'default', name: 'pvc-1', status: 'Bound', storageClass: 'gp2' },
123-
{ cluster: 'cluster-1', namespace: 'default', name: 'pvc-2', status: 'Pending', storageClass: 'gp2' },
124-
{ cluster: 'cluster-1', namespace: 'default', name: 'pvc-3', status: 'Lost', storageClass: 'gp2' },
182+
makePVC('Bound', 'cluster-1', 'gp2', 'pvc-1'),
183+
makePVC('Pending', 'cluster-1', 'gp2', 'pvc-2'),
184+
makePVC('Lost', 'cluster-1', 'gp2', 'pvc-3'),
125185
],
126-
isLoading: false,
127-
isRefreshing: false,
128-
isDemoFallback: false,
129-
isFailed: false,
130-
consecutiveFailures: 0,
131-
} as never)
186+
})
187+
132188
render(<StorageOverview />)
133-
// bound=1, pending=1, failed=1 — all rendered as "1"
189+
134190
const ones = screen.getAllByText('1')
135191
expect(ones.length).toBeGreaterThanOrEqual(3)
136192
})
137193
})
138194

139-
describe('Storage classes', () => {
140-
it('renders storage class list when PVCs have classes', async () => {
141-
const { useCachedPVCs } = await import('../../../hooks/useCachedData')
142-
vi.mocked(useCachedPVCs).mockReturnValue({
143-
pvcs: [
144-
{ cluster: 'cluster-1', namespace: 'default', name: 'p1', status: 'Bound', storageClass: 'gp2' },
145-
{ cluster: 'cluster-1', namespace: 'default', name: 'p2', status: 'Bound', storageClass: 'standard' },
146-
],
147-
isLoading: false,
148-
isRefreshing: false,
149-
isDemoFallback: false,
150-
isFailed: false,
151-
consecutiveFailures: 0,
152-
} as never)
195+
describe('PVC tiles', () => {
196+
it('PVC status tiles are not clickable (no drilldown view)', () => {
153197
render(<StorageOverview />)
154-
expect(screen.getByText('storageOverview.storageClasses')).toBeTruthy()
155-
expect(screen.getByText('gp2')).toBeTruthy()
156-
expect(screen.getByText('standard')).toBeTruthy()
198+
199+
const boundLabel = screen.getByText('storageOverview.bound')
200+
const tile = boundLabel.closest('[class*="border"]')
201+
expect(tile).not.toBeNull()
202+
expect(tile?.className).toContain('cursor-default')
203+
expect(tile?.className).not.toContain('cursor-pointer')
157204
})
158205
})
159206

160-
describe('PVC tiles', () => {
161-
it('PVC status tiles are not clickable (no drilldown view)', async () => {
162-
const { useCachedPVCs } = await import('../../../hooks/useCachedData')
163-
vi.mocked(useCachedPVCs).mockReturnValue({
164-
pvcs: [{ cluster: 'cluster-1', namespace: 'default', name: 'pvc-1', status: 'Bound', storageClass: 'gp2' }],
165-
isLoading: false,
166-
isRefreshing: false,
167-
isDemoFallback: false,
168-
isFailed: false,
169-
consecutiveFailures: 0,
170-
} as never)
207+
describe('Storage classes', () => {
208+
it('renders storage class list when PVCs have classes', () => {
209+
mockUseCachedPVCs.mockReturnValue({
210+
...defaultPVCsReturn,
211+
pvcs: [
212+
makePVC('Bound', 'cluster-1', 'gp2', 'p1'),
213+
makePVC('Bound', 'cluster-1', 'standard', 'p2'),
214+
],
215+
})
216+
171217
render(<StorageOverview />)
172-
// The text is inside a nested div; walk up to the tile container that carries the cursor class
173-
const boundLabel = screen.getByText('storageOverview.bound')
174-
// The tile div is the one with border/bg classes — two levels up from the label span
175-
const tileDivs = boundLabel.closest('[class*="border"]')!
176-
expect(tileDivs.className).toContain('cursor-default')
177-
expect(tileDivs.className).not.toContain('cursor-pointer')
218+
219+
expect(screen.getByText('storageOverview.storageClasses')).toBeInTheDocument()
220+
expect(screen.getByText('gp2')).toBeInTheDocument()
221+
expect(screen.getByText('standard')).toBeInTheDocument()
178222
})
179223
})
180224

181225
describe('Cluster filter', () => {
182226
it('renders cluster filter dropdown', () => {
183227
render(<StorageOverview />)
184-
expect(screen.getByTestId('cluster-filter')).toBeTruthy()
228+
229+
expect(screen.getByTestId('cluster-filter')).toBeInTheDocument()
185230
})
186231
})
187232

188233
describe('Footer', () => {
189234
it('renders footer with PVC and cluster count', () => {
190235
render(<StorageOverview />)
191-
expect(screen.getByText(/storageOverview.footer/)).toBeTruthy()
236+
237+
expect(screen.getByText(/PVCs, 1 clusters/)).toBeInTheDocument()
192238
})
193239
})
194-
})
240+
})

0 commit comments

Comments
Ā (0)