mirror of
https://github.com/langgenius/dify.git
synced 2026-05-23 02:18:23 +08:00
update
This commit is contained in:
@ -0,0 +1,77 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { DeveloperApiSection } from '../developer-api-section'
|
||||
|
||||
type QueryOptions = {
|
||||
queryKey?: string[]
|
||||
}
|
||||
|
||||
type QueryResult = {
|
||||
data?: unknown
|
||||
isLoading: boolean
|
||||
isError: boolean
|
||||
}
|
||||
|
||||
const mockUseQuery = vi.fn<(options: QueryOptions) => QueryResult>()
|
||||
|
||||
vi.mock('@tanstack/react-query', () => ({
|
||||
useMutation: () => ({ mutate: vi.fn() }),
|
||||
useQuery: (options: QueryOptions) => mockUseQuery(options),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/client', () => ({
|
||||
consoleQuery: {
|
||||
enterprise: {
|
||||
appDeployAccessService: {
|
||||
createDeveloperApiKey: {
|
||||
mutationOptions: () => ({ mutationFn: vi.fn() }),
|
||||
},
|
||||
deleteDeveloperApiKey: {
|
||||
mutationOptions: () => ({ mutationFn: vi.fn() }),
|
||||
},
|
||||
getAppInstanceAccess: {
|
||||
queryOptions: () => ({ queryKey: ['app-instance-access'] }),
|
||||
},
|
||||
updateDeveloperApi: {
|
||||
mutationOptions: () => ({ mutationFn: vi.fn() }),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
function queryResult(overrides: Partial<QueryResult> = {}): QueryResult {
|
||||
return {
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
describe('DeveloperApiSection', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// Loading should reserve the same shape as the enabled API page: endpoint copy row plus API key table.
|
||||
describe('Loading state', () => {
|
||||
it('should render the updated API tab skeleton while access config is loading', () => {
|
||||
// Arrange
|
||||
mockUseQuery.mockReturnValue(queryResult({ isLoading: true }))
|
||||
|
||||
// Act
|
||||
const { container } = render(<DeveloperApiSection appInstanceId="instance-1" />)
|
||||
|
||||
// Assert
|
||||
expect(screen.getByText('deployments.access.api.endpoint')).toBeInTheDocument()
|
||||
expect(screen.getByRole('columnheader', { name: 'deployments.access.api.table.name' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('columnheader', { name: 'deployments.access.api.table.environment' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('columnheader', { name: 'deployments.access.api.table.key' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('columnheader', { name: 'deployments.access.api.table.action' })).toBeInTheDocument()
|
||||
expect(container.querySelectorAll('[data-slot="deployment-developer-api-skeleton"]')).toHaveLength(1)
|
||||
expect(container.querySelectorAll('[data-slot="deployment-developer-api-desktop-row-skeleton"]')).toHaveLength(2)
|
||||
expect(container.querySelectorAll('[data-slot="deployment-developer-api-mobile-row-skeleton"]')).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -8,9 +8,20 @@ import { Switch, SwitchSkeleton } from '@langgenius/dify-ui/switch'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { atom, useAtom, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SkeletonRectangle, SkeletonRow } from '@/app/components/base/skeleton'
|
||||
import { SkeletonRectangle } from '@/app/components/base/skeleton'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
import { SectionState } from '../../common'
|
||||
import {
|
||||
DetailTable,
|
||||
DetailTableBody,
|
||||
DetailTableCard,
|
||||
DetailTableCardList,
|
||||
DetailTableCell,
|
||||
DetailTableHead,
|
||||
DetailTableHeader,
|
||||
DetailTableRow,
|
||||
} from '../../table'
|
||||
import { API_KEY_DETAIL_TABLE_COLUMN_CLASS_NAMES } from '../../table-styles'
|
||||
import { ApiKeyGenerateMenu, ApiKeyList } from './api-keys'
|
||||
import { CopyPill } from './common'
|
||||
|
||||
@ -127,31 +138,95 @@ function CreatedApiTokenCard({ token, onDismiss }: {
|
||||
}
|
||||
|
||||
function DeveloperApiSkeleton() {
|
||||
const { t } = useTranslation('deployments')
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<SkeletonRectangle className="my-0 h-8 w-full animate-pulse rounded-lg" />
|
||||
<SkeletonRow className="items-center justify-between gap-3 rounded-lg border border-divider-subtle bg-background-default-subtle px-4 py-3">
|
||||
<div className="flex min-w-0 flex-col gap-1.5">
|
||||
<SkeletonRectangle className="h-3.5 w-28 animate-pulse" />
|
||||
<SkeletonRectangle className="h-3 w-40 animate-pulse" />
|
||||
<div className="flex flex-col gap-4" data-slot="deployment-developer-api-skeleton">
|
||||
<div className="flex h-8 items-center gap-1 rounded-lg border border-components-input-border-active bg-components-input-bg-normal pr-1 pl-1.5">
|
||||
<div className="flex h-5 shrink-0 items-center rounded-md border border-divider-subtle px-1.5 system-2xs-medium text-text-tertiary">
|
||||
{t('access.api.endpoint')}
|
||||
</div>
|
||||
<SkeletonRectangle className="my-0 h-8 w-24 animate-pulse rounded-lg" />
|
||||
</SkeletonRow>
|
||||
<div className="flex flex-col gap-2">
|
||||
{DEVELOPER_API_KEY_SKELETON_KEYS.map(key => (
|
||||
<SkeletonRow key={key} className="items-center gap-3 rounded-lg border border-divider-subtle bg-background-default-subtle px-3 py-3">
|
||||
<div className="flex min-w-35 flex-col gap-1.5">
|
||||
<SkeletonRectangle className="h-3 w-24 animate-pulse" />
|
||||
<SkeletonRectangle className="h-2.5 w-18 animate-pulse" />
|
||||
</div>
|
||||
<SkeletonRectangle className="my-0 h-8 min-w-0 flex-1 animate-pulse rounded-lg" />
|
||||
</SkeletonRow>
|
||||
))}
|
||||
<SkeletonRectangle className="my-0 h-3 min-w-0 flex-1 animate-pulse" />
|
||||
<div className="h-3.5 w-px shrink-0 bg-divider-regular" />
|
||||
<SkeletonRectangle className="my-0 size-6 shrink-0 animate-pulse rounded-md" />
|
||||
</div>
|
||||
<ApiKeyTableSkeleton />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ApiKeyTableSkeleton() {
|
||||
return (
|
||||
<>
|
||||
<DetailTableCardList className="pc:hidden">
|
||||
{DEVELOPER_API_KEY_SKELETON_KEYS.map(key => (
|
||||
<DetailTableCard key={key} data-slot="deployment-developer-api-mobile-row-skeleton">
|
||||
<div className="flex flex-col gap-3 p-4">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<SkeletonRectangle className="my-0 h-3.5 w-32 animate-pulse" />
|
||||
<SkeletonRectangle className="mt-2 h-5 w-20 animate-pulse rounded-md" />
|
||||
</div>
|
||||
<SkeletonRectangle className="my-0 size-8 shrink-0 animate-pulse rounded-md" />
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-col gap-1">
|
||||
<SkeletonRectangle className="my-0 h-2.5 w-14 animate-pulse" />
|
||||
<SkeletonRectangle className="my-0 h-8 w-full animate-pulse rounded-lg" />
|
||||
</div>
|
||||
</div>
|
||||
</DetailTableCard>
|
||||
))}
|
||||
</DetailTableCardList>
|
||||
<div className="hidden pc:block">
|
||||
<DetailTable>
|
||||
<ApiKeyTableHeaderSkeleton />
|
||||
<DetailTableBody>
|
||||
{DEVELOPER_API_KEY_SKELETON_KEYS.map(key => (
|
||||
<ApiKeyDesktopRowSkeleton key={key} />
|
||||
))}
|
||||
</DetailTableBody>
|
||||
</DetailTable>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function ApiKeyTableHeaderSkeleton() {
|
||||
const { t } = useTranslation('deployments')
|
||||
|
||||
return (
|
||||
<DetailTableHeader>
|
||||
<DetailTableRow>
|
||||
<DetailTableHead className={API_KEY_DETAIL_TABLE_COLUMN_CLASS_NAMES.name}>{t('access.api.table.name')}</DetailTableHead>
|
||||
<DetailTableHead className={API_KEY_DETAIL_TABLE_COLUMN_CLASS_NAMES.environment}>{t('access.api.table.environment')}</DetailTableHead>
|
||||
<DetailTableHead className={API_KEY_DETAIL_TABLE_COLUMN_CLASS_NAMES.key}>{t('access.api.table.key')}</DetailTableHead>
|
||||
<DetailTableHead className={`${API_KEY_DETAIL_TABLE_COLUMN_CLASS_NAMES.action} text-right`}>{t('access.api.table.action')}</DetailTableHead>
|
||||
</DetailTableRow>
|
||||
</DetailTableHeader>
|
||||
)
|
||||
}
|
||||
|
||||
function ApiKeyDesktopRowSkeleton() {
|
||||
return (
|
||||
<DetailTableRow data-slot="deployment-developer-api-desktop-row-skeleton">
|
||||
<DetailTableCell>
|
||||
<SkeletonRectangle className="my-0 h-3.5 w-32 animate-pulse" />
|
||||
</DetailTableCell>
|
||||
<DetailTableCell>
|
||||
<SkeletonRectangle className="my-0 h-5 w-20 animate-pulse rounded-md" />
|
||||
</DetailTableCell>
|
||||
<DetailTableCell>
|
||||
<SkeletonRectangle className="my-0 h-8 w-full animate-pulse rounded-lg" />
|
||||
</DetailTableCell>
|
||||
<DetailTableCell>
|
||||
<div className="flex justify-end">
|
||||
<SkeletonRectangle className="my-0 size-8 animate-pulse rounded-md" />
|
||||
</div>
|
||||
</DetailTableCell>
|
||||
</DetailTableRow>
|
||||
)
|
||||
}
|
||||
|
||||
export function DeveloperApiSection({
|
||||
appInstanceId,
|
||||
}: {
|
||||
|
||||
Reference in New Issue
Block a user