From 50f2e2056cb996dbfc5929b6c2cbac2046f5f234 Mon Sep 17 00:00:00 2001 From: clubanderson Date: Sun, 28 Jun 2026 17:34:42 -0400 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=8C=B1=20Replace=20'any'=20types=20wi?= =?UTF-8?q?th=20proper=20type=20annotations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces 25 uses of the 'any' type with proper type annotations to improve type safety: Test files (using specific interfaces and unknown): - EnterpriseSidebar.test.tsx: Mock component props with interfaces - EnterpriseLayout.test.tsx: Mock customizer props with interface - ClusterGrid.common.test.tsx: React.KeyboardEvent types for event handlers - CreateDashboardModal.test.tsx: Specific modal props interfaces - ui-data-components.test.tsx: Removed unnecessary 'any' casts - WorkloadsExtended.test.tsx: Proper mock data interfaces Production code: - VClusterActionBanner.tsx: Removed 'any' cast for i18n translation keys All changes maintain existing functionality while improving type safety. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: clubanderson --- .../__tests__/ClusterGrid.common.test.tsx | 6 +-- .../__tests__/CreateDashboardModal.test.tsx | 20 ++++++-- .../__tests__/EnterpriseLayout.test.tsx | 8 ++- .../__tests__/EnterpriseSidebar.test.tsx | 11 +++- .../__tests__/ui-data-components.test.tsx | 6 +-- .../sections/VClusterActionBanner.tsx | 6 +-- .../__tests__/WorkloadsExtended.test.tsx | 51 ++++++++++++++----- 7 files changed, 79 insertions(+), 29 deletions(-) diff --git a/web/src/components/clusters/components/__tests__/ClusterGrid.common.test.tsx b/web/src/components/clusters/components/__tests__/ClusterGrid.common.test.tsx index 1fe07afb4a..53420221a8 100644 --- a/web/src/components/clusters/components/__tests__/ClusterGrid.common.test.tsx +++ b/web/src/components/clusters/components/__tests__/ClusterGrid.common.test.tsx @@ -130,7 +130,7 @@ describe('ClusterGrid.common utilities', () => { const event = new KeyboardEvent('keydown', { key: 'Enter' }) Object.defineProperty(event, 'preventDefault', { value: vi.fn() }) - handler(event as any) + handler(event as unknown as React.KeyboardEvent) expect(callback).toHaveBeenCalledTimes(1) expect(event.preventDefault).toHaveBeenCalled() }) @@ -141,7 +141,7 @@ describe('ClusterGrid.common utilities', () => { const event = new KeyboardEvent('keydown', { key: ' ' }) Object.defineProperty(event, 'preventDefault', { value: vi.fn() }) - handler(event as any) + handler(event as unknown as React.KeyboardEvent) expect(callback).toHaveBeenCalledTimes(1) expect(event.preventDefault).toHaveBeenCalled() }) @@ -152,7 +152,7 @@ describe('ClusterGrid.common utilities', () => { const event = new KeyboardEvent('keydown', { key: 'a' }) Object.defineProperty(event, 'preventDefault', { value: vi.fn() }) - handler(event as any) + handler(event as unknown as React.KeyboardEvent) expect(callback).not.toHaveBeenCalled() expect(event.preventDefault).not.toHaveBeenCalled() }) diff --git a/web/src/components/dashboard/__tests__/CreateDashboardModal.test.tsx b/web/src/components/dashboard/__tests__/CreateDashboardModal.test.tsx index c33b9e1605..979ce34276 100644 --- a/web/src/components/dashboard/__tests__/CreateDashboardModal.test.tsx +++ b/web/src/components/dashboard/__tests__/CreateDashboardModal.test.tsx @@ -40,6 +40,18 @@ vi.mock('react-i18next', () => ({ }), })) +interface MockModalHeaderProps { + title: string + onClose: () => void + disabled?: boolean +} + +interface MockModalFooterProps { + children: React.ReactNode + disabled?: boolean + loading?: boolean +} + vi.mock('../../../lib/modals', () => ({ BaseModal: Object.assign( ({ children, isOpen }: { children: React.ReactNode; isOpen: boolean }) => { @@ -47,17 +59,17 @@ vi.mock('../../../lib/modals', () => ({ return
{children}
}, { - Header: ({ title, onClose, ...rest }: { title: string; onClose: () => void; [key: string]: unknown }) => ( + Header: ({ title, onClose, disabled }: MockModalHeaderProps) => (
{title} - +
), Content: ({ children }: { children: React.ReactNode }) => (
{children}
), - Footer: ({ children, ...rest }: { children: React.ReactNode; [key: string]: unknown }) => ( -
{children}
+ Footer: ({ children, disabled, loading }: MockModalFooterProps) => ( +
{children}
), } ), diff --git a/web/src/components/enterprise/__tests__/EnterpriseLayout.test.tsx b/web/src/components/enterprise/__tests__/EnterpriseLayout.test.tsx index e47362b807..ac6aa4a05a 100644 --- a/web/src/components/enterprise/__tests__/EnterpriseLayout.test.tsx +++ b/web/src/components/enterprise/__tests__/EnterpriseLayout.test.tsx @@ -46,8 +46,14 @@ vi.mock('../../dashboard/FloatingDashboardActions', () => ({ ), })) +interface MockDashboardCustomizerProps { + existingCardTypes: string[] + onAddCards: (cards: Array<{ type: string; title: string; config: Record }>) => void + isOpen: boolean +} + vi.mock('../../dashboard/customizer/DashboardCustomizer', () => ({ - DashboardCustomizer: ({ existingCardTypes, onAddCards, isOpen }: any) => ( + DashboardCustomizer: ({ existingCardTypes, onAddCards, isOpen }: MockDashboardCustomizerProps) => (
{String(isOpen)}
{existingCardTypes.join(',')}
diff --git a/web/src/components/enterprise/__tests__/EnterpriseSidebar.test.tsx b/web/src/components/enterprise/__tests__/EnterpriseSidebar.test.tsx index 4733133140..63439521c5 100644 --- a/web/src/components/enterprise/__tests__/EnterpriseSidebar.test.tsx +++ b/web/src/components/enterprise/__tests__/EnterpriseSidebar.test.tsx @@ -5,12 +5,19 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' const mockOpenAddCardModal = vi.fn() +interface MockSidebarShellProps { + navSections: Array<{ items: Array<{ id: string; href: string; label: string }> }> + branding: { title: string; subtitle: string } + onAddCard: () => void + onAddMore: () => void +} + vi.mock('../../layout/SidebarShell', () => ({ - SidebarShell: ({ navSections, branding, onAddCard, onAddMore }: any) => ( + SidebarShell: ({ navSections, branding, onAddCard, onAddMore }: MockSidebarShellProps) => (

{branding.title}

{branding.subtitle}

- {navSections.flatMap((section: any) => section.items).map((item: any) => ( + {navSections.flatMap((section) => section.items).map((item) => ( {item.label} ))} diff --git a/web/src/components/mission-control/__tests__/ui-data-components.test.tsx b/web/src/components/mission-control/__tests__/ui-data-components.test.tsx index 3b4bc7cede..cd46fa315f 100644 --- a/web/src/components/mission-control/__tests__/ui-data-components.test.tsx +++ b/web/src/components/mission-control/__tests__/ui-data-components.test.tsx @@ -224,7 +224,7 @@ describe('ClusterReadinessCard', () => { projectNames: ['falco'], readiness: { overallScore: 85, cpuHeadroomPercent: 75, memHeadroomPercent: 75, storageHeadroomPercent: 100 }, warnings: [] - } as unknown as any} + }} /> ) @@ -245,7 +245,7 @@ describe('ClusterReadinessCard', () => { projectNames: [], readiness: { overallScore: 85 }, warnings: [] - } as unknown as any} + }} /> ) @@ -393,7 +393,7 @@ describe('FixerDefinitionPanel', () => { onRemoveProject={vi.fn()} onUpdatePriority={vi.fn()} aiStreaming={true} - planningMission={{ status: 'running', messages: [] } as unknown as any} + planningMission={{ status: 'running', messages: [] }} /> ) diff --git a/web/src/components/settings/sections/VClusterActionBanner.tsx b/web/src/components/settings/sections/VClusterActionBanner.tsx index de5c235d8b..9e248a210d 100644 --- a/web/src/components/settings/sections/VClusterActionBanner.tsx +++ b/web/src/components/settings/sections/VClusterActionBanner.tsx @@ -12,12 +12,10 @@ function getVClusterActionMessage(feedback: VClusterActionFeedback, t: TFunction if (feedback.state === 'error') { return feedback.message ? friendlyErrorMessage(feedback.message) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - : String(t(`${keyBase}Fallback` as any, { name: feedback.name, namespace: feedback.namespace })) + : String(t(`${keyBase}Fallback`, { name: feedback.name, namespace: feedback.namespace })) } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return String(t(keyBase as any, { name: feedback.name, namespace: feedback.namespace })) + return String(t(keyBase, { name: feedback.name, namespace: feedback.namespace })) } /** Inline feedback banner for vCluster create/connect/disconnect/delete operations. */ diff --git a/web/src/components/workloads/__tests__/WorkloadsExtended.test.tsx b/web/src/components/workloads/__tests__/WorkloadsExtended.test.tsx index 6d9968a1e5..b08c3c2783 100644 --- a/web/src/components/workloads/__tests__/WorkloadsExtended.test.tsx +++ b/web/src/components/workloads/__tests__/WorkloadsExtended.test.tsx @@ -44,10 +44,37 @@ vi.mock('../../../lib/dashboards/DashboardPage', () => ({ ), })) -let mockPodIssues: any[] = [] -let mockDeploymentIssues: any[] = [] -let mockDeployments: any[] = [] -const mockClusters: any[] = [] +interface MockPodIssue { + name: string + namespace: string + cluster: string + reason: string +} + +interface MockDeploymentIssue { + name: string + namespace: string + cluster: string + reason?: string +} + +interface MockDeployment { + name: string + namespace: string + cluster: string + status: string + replicas: number + readyReplicas: number +} + +interface MockCluster { + name: string +} + +let mockPodIssues: MockPodIssue[] = [] +let mockDeploymentIssues: MockDeploymentIssue[] = [] +let mockDeployments: MockDeployment[] = [] +const mockClusters: MockCluster[] = [] let mockIsLoading = false let mockIsDemoMode = true @@ -65,7 +92,7 @@ vi.mock('../../../hooks/useGlobalFilters', () => ({ selectedClusters: [], isAllClustersSelected: true, customFilter: '', - filterByCluster: (items: any[]) => items, + filterByCluster: (items: T[]) => items, })), })) @@ -115,7 +142,7 @@ vi.mock('../../ui/Toast', () => ({ const kubectlExecSpy = vi.fn().mockResolvedValue({ output: 'deployment.apps/test restarted', exitCode: 0 }) vi.mock('../../../lib/kubectlProxy', () => ({ kubectlProxy: { - exec: (...args: any[]) => kubectlExecSpy(...args), + exec: (...args: unknown[]) => kubectlExecSpy(...args), }, })) @@ -141,8 +168,8 @@ const setupDeploymentView = () => { selectedClusters: [], isAllClustersSelected: true, customFilter: 'nginx', - filterByCluster: (items: any[]) => items, - } as any) + filterByCluster: (items: T[]) => items, + }) mockDeployments = [ { name: 'nginx-web', namespace: 'production', cluster: 'ctx/prod-east', status: 'running', replicas: 3, readyReplicas: 3 }, @@ -228,8 +255,8 @@ describe('Namespace-grouped view (#12479)', () => { selectedClusters: [], isAllClustersSelected: true, customFilter: '', - filterByCluster: (items: any[]) => items, - } as any) + filterByCluster: (items: T[]) => items, + }) }) it('groups deployments by namespace/cluster when no filter is applied', () => { @@ -435,8 +462,8 @@ describe('Loading skeleton and agent-offline states (#12481)', () => { selectedClusters: [], isAllClustersSelected: true, customFilter: '', - filterByCluster: (items: any[]) => items, - } as any) + filterByCluster: (items: T[]) => items, + }) }) it('shows loading skeletons when data is loading and no data exists', () => { From d62b2b2fbd700f84aec875f09641482dca95afdd Mon Sep 17 00:00:00 2001 From: Andy Anderson Date: Sun, 28 Jun 2026 18:13:19 -0400 Subject: [PATCH 2/2] fix: restore type assertions for dynamic i18n keys in VClusterActionBanner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dynamic translation keys built at runtime cannot match the static union of known keys, so `as any` is the necessary escape hatch. The other 24 `any` removals in test files remain — this only restores the 2 production-code assertions that broke the build. Signed-off-by: clubanderson --- .../components/settings/sections/VClusterActionBanner.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/src/components/settings/sections/VClusterActionBanner.tsx b/web/src/components/settings/sections/VClusterActionBanner.tsx index 9e248a210d..4fc69c3d1b 100644 --- a/web/src/components/settings/sections/VClusterActionBanner.tsx +++ b/web/src/components/settings/sections/VClusterActionBanner.tsx @@ -12,10 +12,13 @@ function getVClusterActionMessage(feedback: VClusterActionFeedback, t: TFunction if (feedback.state === 'error') { return feedback.message ? friendlyErrorMessage(feedback.message) - : String(t(`${keyBase}Fallback`, { name: feedback.name, namespace: feedback.namespace })) + // Dynamic i18n keys require type assertion — the key is built at runtime + // eslint-disable-next-line @typescript-eslint/no-explicit-any + : String(t(`${keyBase}Fallback` as any, { name: feedback.name, namespace: feedback.namespace })) } - return String(t(keyBase, { name: feedback.name, namespace: feedback.namespace })) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return String(t(keyBase as any, { name: feedback.name, namespace: feedback.namespace })) } /** Inline feedback banner for vCluster create/connect/disconnect/delete operations. */