This commit is contained in:
Stephen Zhou
2026-05-25 21:13:04 +08:00
parent 4531d93114
commit e21be9e8fe
4 changed files with 109 additions and 1 deletions

View File

@ -0,0 +1,84 @@
import { render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { AccessChannelsSection } from '../channels-section'
type QueryOptions = {
queryKey?: string[]
}
type QueryResult = {
data?: unknown
isLoading: boolean
isError: boolean
}
const mockUseQuery = vi.fn<(options: QueryOptions) => QueryResult>()
let mockUpdateAccessChannelsPending = false
vi.mock('@tanstack/react-query', () => ({
useMutation: () => ({
mutate: vi.fn(),
isPending: mockUpdateAccessChannelsPending,
}),
useQuery: (options: QueryOptions) => mockUseQuery(options),
}))
vi.mock('@/service/client', () => ({
consoleQuery: {
enterprise: {
accessService: {
getAccessChannels: {
queryOptions: () => ({ queryKey: ['access-channels'] }),
},
updateAccessChannels: {
mutationOptions: () => ({ mutationKey: ['update-access-channels'] }),
},
},
deploymentService: {
listEnvironmentDeployments: {
queryOptions: () => ({ queryKey: ['environment-deployments'] }),
},
},
},
},
}))
function queryResult(overrides: Partial<QueryResult> = {}): QueryResult {
return {
data: undefined,
isLoading: false,
isError: false,
...overrides,
}
}
describe('AccessChannelsSection', () => {
beforeEach(() => {
vi.clearAllMocks()
mockUpdateAccessChannelsPending = false
mockUseQuery.mockImplementation((options: QueryOptions) => {
switch (options.queryKey?.[0]) {
case 'access-channels':
return queryResult({ data: { accessChannels: { webAppEnabled: false } } })
case 'environment-deployments':
return queryResult({ data: { data: [] } })
default:
return queryResult()
}
})
})
it('should show loading feedback on the access channel switch while toggling', () => {
// Arrange
mockUpdateAccessChannelsPending = true
// Act
const { container } = render(<AccessChannelsSection appInstanceId="instance-1" />)
// Assert
const switchElement = screen.getByRole('switch')
expect(switchElement).toHaveAttribute('aria-busy', 'true')
expect(switchElement).toHaveAttribute('data-disabled', '')
expect(container.querySelector('.i-ri-loader-2-line')).toBeInTheDocument()
})
})

View File

@ -2,7 +2,7 @@ import type { ReactNode } from 'react'
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
import { createStore, Provider as JotaiProvider } from 'jotai'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { DeveloperApiHeaderActions, DeveloperApiSection } from '../developer-api-section'
import { DeveloperApiHeaderActions, DeveloperApiHeaderSwitch, DeveloperApiSection } from '../developer-api-section'
type QueryOptions = {
queryKey?: string[]
@ -19,6 +19,7 @@ type MutationOptions = {
}
const mockUseQuery = vi.fn<(options: QueryOptions) => QueryResult>()
let mockUpdateAccessChannelsPending = false
vi.mock('@tanstack/react-query', () => ({
useMutation: (options: MutationOptions) => ({
@ -26,6 +27,9 @@ vi.mock('@tanstack/react-query', () => ({
if (options.mutationKey?.[0] === 'create-api-key')
callbacks?.onSuccess?.({ token: 'app-created-token' })
}),
isPending: options.mutationKey?.[0] === 'update-access-channels'
? mockUpdateAccessChannelsPending
: false,
}),
useQueries: () => [],
useQuery: (options: QueryOptions) => mockUseQuery(options),
@ -80,6 +84,24 @@ function renderWithJotai(node: ReactNode) {
describe('DeveloperApiSection', () => {
beforeEach(() => {
vi.clearAllMocks()
mockUpdateAccessChannelsPending = false
})
describe('Header switch', () => {
it('should show loading feedback while toggling developer API access', () => {
// Arrange
mockUpdateAccessChannelsPending = true
mockUseQuery.mockReturnValue(queryResult({ data: { accessChannels: { developerApiEnabled: false } } }))
// Act
const { container } = render(<DeveloperApiHeaderSwitch appInstanceId="instance-1" />)
// Assert
const switchElement = screen.getByRole('switch')
expect(switchElement).toHaveAttribute('aria-busy', 'true')
expect(switchElement).toHaveAttribute('data-disabled', '')
expect(container.querySelector('.i-ri-loader-2-line')).toBeInTheDocument()
})
})
// Loading should reserve the same shape as the enabled API key list.

View File

@ -29,6 +29,7 @@ function AccessChannelsSwitch({ appInstanceId, checked, disabled }: {
<Switch
checked={checked}
disabled={disabled}
loading={toggleAccessChannel.isPending}
onCheckedChange={(enabled) => {
toggleAccessChannel.mutate({
params: { appInstanceId },

View File

@ -104,6 +104,7 @@ function DeveloperApiSwitch({ appInstanceId, checked, disabled }: {
<Switch
checked={checked}
disabled={disabled}
loading={toggleDeveloperAPI.isPending}
onCheckedChange={(enabled) => {
toggleDeveloperAPI.mutate({
params: { appInstanceId },