Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('CreateNamespaceModal', () => {
expect(screen.getAllByRole('combobox').length).toBeGreaterThan(0)
})

it('initializes with first cluster selected', () => {
it('initializes with cluster placeholder selected', () => {
render(
<CreateNamespaceModal
clusters={clusters}
Expand All @@ -74,7 +74,7 @@ describe('CreateNamespaceModal', () => {

const comboboxes = screen.getAllByRole('combobox')
expect(comboboxes.length).toBeGreaterThan(0)
expect((comboboxes[0] as HTMLSelectElement).value).toBe('cluster-1')
expect((comboboxes[0] as HTMLSelectElement).value).toBe('')
})

it('allows cluster selection change', async () => {
Expand Down Expand Up @@ -124,8 +124,10 @@ describe('CreateNamespaceModal', () => {

const nameInput = screen.getByPlaceholderText('my-namespace')
const teamInput = screen.getByPlaceholderText('platform-team')
const clusterSelect = screen.getAllByRole('combobox')[0]
const createBtn = screen.getByRole('button', { name: /create/i })

await user.selectOptions(clusterSelect, 'cluster-1')
await user.type(nameInput, 'test-ns')
await user.type(teamInput, 'my-team')
Comment on lines +127 to 132
await user.click(createBtn)
Expand Down Expand Up @@ -153,8 +155,10 @@ describe('CreateNamespaceModal', () => {
)

const nameInput = screen.getByPlaceholderText('my-namespace')
const clusterSelect = screen.getAllByRole('combobox')[0]
const createBtn = screen.getByRole('button', { name: /create/i })

await user.selectOptions(clusterSelect, 'cluster-1')
await user.type(nameInput, 'existing-ns')
await user.click(createBtn)

Expand Down Expand Up @@ -229,8 +233,10 @@ describe('CreateNamespaceModal', () => {

const nameInput = screen.getByPlaceholderText('my-namespace')
const teamInput = screen.getByPlaceholderText('platform-team')
const clusterSelect = screen.getAllByRole('combobox')[0]
const createBtn = screen.getByRole('button', { name: /create/i })

await user.selectOptions(clusterSelect, 'cluster-1')
await user.type(nameInput, 'test-ns')
await user.type(teamInput, 'platform-team')
await user.click(createBtn)
Expand All @@ -256,8 +262,10 @@ describe('CreateNamespaceModal', () => {
)

const nameInput = screen.getByPlaceholderText('my-namespace')
const clusterSelect = screen.getAllByRole('combobox')[0]
const createBtn = screen.getByRole('button', { name: /create/i })

await user.selectOptions(clusterSelect, 'cluster-1')
await user.type(nameInput, 'test-ns')
await user.click(createBtn)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,9 @@ describe('NamespaceAccessPanel', () => {
expect(screen.getByText('alice')).toBeInTheDocument()
})

// Verify first call with original namespace
expect(api.get).toHaveBeenCalledWith(
expect.stringContaining('test-namespace')
)
// Verify first call with original namespace. api.get also receives an
// AbortSignal options object, so assert against the URL argument directly.
expect(vi.mocked(api.get).mock.calls[0]?.[0]).toContain('test-namespace')
Comment on lines +293 to +295

const newNamespace: NamespaceDetails = {
...mockNamespace,
Expand Down
54 changes: 42 additions & 12 deletions web/src/components/rbac/__tests__/CanIChecker.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { CanIChecker } from '../CanIChecker'
Expand All @@ -23,8 +23,11 @@ vi.mock('../../../hooks/usePermissions', () => ({
}),
}))

let mockClusters = [{ name: 'cluster-a' }, { name: 'cluster-b' }]
let mockNamespaces = ['default', 'kube-system', 'kube-public']
const defaultMockClusters = [{ name: 'cluster-a' }, { name: 'cluster-b' }]
const defaultMockNamespaces = ['default', 'kube-system', 'kube-public']

let mockClusters = [...defaultMockClusters]
let mockNamespaces = [...defaultMockNamespaces]

vi.mock('../../../hooks/useMCP', () => ({
useClusters: () => ({
Expand Down Expand Up @@ -59,6 +62,20 @@ vi.mock('../../PageErrorBoundary', () => ({

/* ---------- Tests ---------- */

function selectCluster(cluster = 'cluster-a') {
fireEvent.change(screen.getByTestId('can-i-cluster'), { target: { value: cluster } })
}

beforeEach(() => {
vi.clearAllMocks()
mockClusters = [...defaultMockClusters]
mockNamespaces = [...defaultMockNamespaces]
mockChecking = false
mockResult = null
mockError = null
})


describe('CanIChecker — Initial Rendering', () => {
beforeEach(() => {
vi.clearAllMocks()
Expand All @@ -85,9 +102,10 @@ describe('CanIChecker — Initial Rendering', () => {
const clusterSelect = screen.getByTestId('can-i-cluster') as HTMLSelectElement
const options = Array.from(clusterSelect.options)

expect(options).toHaveLength(2)
expect(options[0].value).toBe('cluster-a')
expect(options[1].value).toBe('cluster-b')
expect(options).toHaveLength(3)
expect(options[0].value).toBe('')
expect(options[1].value).toBe('cluster-a')
expect(options[2].value).toBe('cluster-b')
})

it('populates namespace dropdown with fetched namespaces', () => {
Expand All @@ -102,11 +120,11 @@ describe('CanIChecker — Initial Rendering', () => {
expect(options.some(opt => opt.value === 'kube-system')).toBe(true)
})

it('defaults to first cluster', () => {
it('starts with no cluster selected', () => {
render(<CanIChecker />)

const clusterSelect = screen.getByTestId('can-i-cluster') as HTMLSelectElement
expect(clusterSelect.value).toBe('cluster-a')
expect(clusterSelect.value).toBe('')
})

it('defaults verb and resource to common values', () => {
Expand All @@ -130,6 +148,7 @@ describe('CanIChecker — Form Interactions', () => {

it('calls checkPermission with defaults when Check button is clicked', async () => {
render(<CanIChecker />)
selectCluster()

const checkBtn = screen.getByTestId('can-i-check')
await userEvent.click(checkBtn)
Expand Down Expand Up @@ -161,6 +180,7 @@ describe('CanIChecker — Form Interactions', () => {

it('allows changing verb selection', async () => {
render(<CanIChecker />)
selectCluster()

const verbSelect = screen.getByTestId('can-i-verb')
fireEvent.change(verbSelect, { target: { value: 'list' } })
Expand All @@ -177,6 +197,7 @@ describe('CanIChecker — Form Interactions', () => {

it('allows changing resource selection', async () => {
render(<CanIChecker />)
selectCluster()

const resourceSelect = screen.getByTestId('can-i-resource')
fireEvent.change(resourceSelect, { target: { value: 'deployments' } })
Expand All @@ -193,6 +214,7 @@ describe('CanIChecker — Form Interactions', () => {

it('allows changing namespace selection', async () => {
render(<CanIChecker />)
selectCluster()

const nsSelect = screen.getByTestId('can-i-namespace')
fireEvent.change(nsSelect, { target: { value: 'kube-system' } })
Expand All @@ -209,6 +231,7 @@ describe('CanIChecker — Form Interactions', () => {

it('sends undefined namespace when "all namespaces" is selected', async () => {
render(<CanIChecker />)
selectCluster()

const nsSelect = screen.getByTestId('can-i-namespace')
// First option is "all namespaces"
Expand All @@ -235,6 +258,7 @@ describe('CanIChecker — Form Interactions', () => {

it('uses custom verb value when submitted', async () => {
render(<CanIChecker />)
selectCluster()

const verbSelect = screen.getByTestId('can-i-verb')
fireEvent.change(verbSelect, { target: { value: 'custom' } })
Expand Down Expand Up @@ -263,6 +287,7 @@ describe('CanIChecker — Form Interactions', () => {

it('uses custom resource value when submitted', async () => {
render(<CanIChecker />)
selectCluster()

const resourceSelect = screen.getByTestId('can-i-resource')
fireEvent.change(resourceSelect, { target: { value: 'custom' } })
Expand Down Expand Up @@ -291,6 +316,7 @@ describe('CanIChecker — Form Interactions', () => {

it('uses custom API group value when submitted', async () => {
render(<CanIChecker />)
selectCluster()

const apiGroupSelect = screen.getByTestId('can-i-api-group')
fireEvent.change(apiGroupSelect, { target: { value: 'custom' } })
Expand Down Expand Up @@ -384,6 +410,7 @@ describe('CanIChecker — Result Display (Allowed)', () => {
mockError = null

render(<CanIChecker />)
selectCluster()

await userEvent.click(screen.getByTestId('can-i-check'))

Expand Down Expand Up @@ -426,6 +453,7 @@ describe('CanIChecker — Result Display (Denied)', () => {
mockError = null

render(<CanIChecker />)
selectCluster()

await userEvent.click(screen.getByTestId('can-i-check'))

Expand Down Expand Up @@ -480,10 +508,6 @@ describe('CanIChecker — No Clusters Available', () => {
mockError = null
})

afterEach(() => {
// Restore default clusters
mockClusters = [{ name: 'cluster-a' }, { name: 'cluster-b' }]
})

it('shows warning and disables check button when no clusters available', () => {
render(<CanIChecker />)
Expand Down Expand Up @@ -546,6 +570,7 @@ describe('CanIChecker — Edge Cases', () => {

it('handles wildcard resources', async () => {
render(<CanIChecker />)
selectCluster()

const resourceSelect = screen.getByTestId('can-i-resource')
fireEvent.change(resourceSelect, { target: { value: 'custom' } })
Expand Down Expand Up @@ -577,6 +602,7 @@ describe('CanIChecker — Edge Cases', () => {

it('handles special characters in custom inputs', async () => {
render(<CanIChecker />)
selectCluster()

const resourceSelect = screen.getByTestId('can-i-resource')
fireEvent.change(resourceSelect, { target: { value: 'custom' } })
Expand All @@ -596,6 +622,7 @@ describe('CanIChecker — Edge Cases', () => {

it('handles multiple rapid clicks on check button', async () => {
render(<CanIChecker />)
selectCluster()

const checkBtn = screen.getByTestId('can-i-check')

Expand All @@ -618,6 +645,7 @@ describe('CanIChecker — API Group Handling', () => {

it('sends correct API group for apps resources', async () => {
render(<CanIChecker />)
selectCluster()

const resourceSelect = screen.getByTestId('can-i-resource')
fireEvent.change(resourceSelect, { target: { value: 'deployments' } })
Expand All @@ -635,6 +663,7 @@ describe('CanIChecker — API Group Handling', () => {

it('sends empty API group for core resources', async () => {
render(<CanIChecker />)
selectCluster()

const resourceSelect = screen.getByTestId('can-i-resource')
fireEvent.change(resourceSelect, { target: { value: 'pods' } })
Expand All @@ -652,6 +681,7 @@ describe('CanIChecker — API Group Handling', () => {

it('allows overriding automatic API group selection', async () => {
render(<CanIChecker />)
selectCluster()

const resourceSelect = screen.getByTestId('can-i-resource')
fireEvent.change(resourceSelect, { target: { value: 'deployments' } })
Expand Down
Loading