feat: 前端集成 Prettier 代码格式化
This commit is contained in:
@@ -1,18 +1,24 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { ProviderTable } from '@/pages/Providers/ProviderTable';
|
||||
import type { Provider } from '@/types';
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { ProviderTable } from '@/pages/Providers/ProviderTable'
|
||||
import type { Provider } from '@/types'
|
||||
|
||||
const mockModelsData = [
|
||||
{ id: 'model-1', providerId: 'openai', modelName: 'gpt-4o', enabled: true, unifiedId: 'openai/gpt-4o' },
|
||||
{ id: 'model-2', providerId: 'openai', modelName: 'gpt-3.5-turbo', enabled: false, unifiedId: 'openai/gpt-3.5-turbo' },
|
||||
];
|
||||
{
|
||||
id: 'model-2',
|
||||
providerId: 'openai',
|
||||
modelName: 'gpt-3.5-turbo',
|
||||
enabled: false,
|
||||
unifiedId: 'openai/gpt-3.5-turbo',
|
||||
},
|
||||
]
|
||||
|
||||
vi.mock('@/hooks/useModels', () => ({
|
||||
useModels: vi.fn(() => ({ data: mockModelsData, isLoading: false })),
|
||||
useDeleteModel: vi.fn(() => ({ mutate: vi.fn() })),
|
||||
}));
|
||||
}))
|
||||
|
||||
const mockProviders: Provider[] = [
|
||||
{
|
||||
@@ -35,7 +41,7 @@ const mockProviders: Provider[] = [
|
||||
createdAt: '2024-01-02T00:00:00Z',
|
||||
updatedAt: '2024-01-02T00:00:00Z',
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
const defaultProps = {
|
||||
providers: mockProviders,
|
||||
@@ -45,36 +51,36 @@ const defaultProps = {
|
||||
onDelete: vi.fn(),
|
||||
onAddModel: vi.fn(),
|
||||
onEditModel: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
describe('ProviderTable', () => {
|
||||
it('renders provider list with name, baseUrl, apiKey, and status tags', () => {
|
||||
render(<ProviderTable {...defaultProps} />);
|
||||
render(<ProviderTable {...defaultProps} />)
|
||||
|
||||
expect(screen.getByText('供应商列表')).toBeInTheDocument();
|
||||
expect(screen.getByText('供应商列表')).toBeInTheDocument()
|
||||
|
||||
expect(screen.getAllByText('OpenAI').length).toBeGreaterThan(0);
|
||||
expect(screen.getByText('https://api.openai.com/v1')).toBeInTheDocument();
|
||||
expect(screen.getByText('sk-abcdefgh12345678')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('OpenAI').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('https://api.openai.com/v1')).toBeInTheDocument()
|
||||
expect(screen.getByText('sk-abcdefgh12345678')).toBeInTheDocument()
|
||||
|
||||
expect(screen.getAllByText('Anthropic').length).toBeGreaterThan(0);
|
||||
expect(screen.getByText('https://api.anthropic.com')).toBeInTheDocument();
|
||||
expect(screen.getByText('sk-ant-test')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('Anthropic').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('https://api.anthropic.com')).toBeInTheDocument()
|
||||
expect(screen.getByText('sk-ant-test')).toBeInTheDocument()
|
||||
|
||||
const enabledTags = screen.getAllByText('启用');
|
||||
const disabledTags = screen.getAllByText('禁用');
|
||||
expect(enabledTags.length).toBeGreaterThanOrEqual(1);
|
||||
expect(disabledTags.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
const enabledTags = screen.getAllByText('启用')
|
||||
const disabledTags = screen.getAllByText('禁用')
|
||||
expect(enabledTags.length).toBeGreaterThanOrEqual(1)
|
||||
expect(disabledTags.length).toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
|
||||
it('renders within a Card component', () => {
|
||||
const { container } = render(<ProviderTable {...defaultProps} />);
|
||||
const { container } = render(<ProviderTable {...defaultProps} />)
|
||||
|
||||
// TDesign Card component
|
||||
expect(container.querySelector('.t-card')).toBeInTheDocument();
|
||||
expect(container.querySelector('.t-card__header')).toBeInTheDocument();
|
||||
expect(container.querySelector('.t-card__body')).toBeInTheDocument();
|
||||
});
|
||||
expect(container.querySelector('.t-card')).toBeInTheDocument()
|
||||
expect(container.querySelector('.t-card__header')).toBeInTheDocument()
|
||||
expect(container.querySelector('.t-card__body')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders short api keys directly', () => {
|
||||
const shortKeyProvider: Provider[] = [
|
||||
@@ -84,99 +90,99 @@ describe('ProviderTable', () => {
|
||||
name: 'ShortKey',
|
||||
apiKey: 'ab',
|
||||
},
|
||||
];
|
||||
render(<ProviderTable {...defaultProps} providers={shortKeyProvider} />);
|
||||
]
|
||||
render(<ProviderTable {...defaultProps} providers={shortKeyProvider} />)
|
||||
|
||||
expect(screen.getByText('ab')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('ab')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('calls onAdd when clicking "添加供应商" button', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onAdd = vi.fn();
|
||||
render(<ProviderTable {...defaultProps} onAdd={onAdd} />);
|
||||
const user = userEvent.setup()
|
||||
const onAdd = vi.fn()
|
||||
render(<ProviderTable {...defaultProps} onAdd={onAdd} />)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '添加供应商' }));
|
||||
expect(onAdd).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
await user.click(screen.getByRole('button', { name: '添加供应商' }))
|
||||
expect(onAdd).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('calls onEdit with correct provider when clicking "编辑"', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onEdit = vi.fn();
|
||||
render(<ProviderTable {...defaultProps} onEdit={onEdit} />);
|
||||
const user = userEvent.setup()
|
||||
const onEdit = vi.fn()
|
||||
render(<ProviderTable {...defaultProps} onEdit={onEdit} />)
|
||||
|
||||
const editButtons = screen.getAllByRole('button', { name: /编 ?辑/ });
|
||||
await user.click(editButtons[0]);
|
||||
const editButtons = screen.getAllByRole('button', { name: /编 ?辑/ })
|
||||
await user.click(editButtons[0])
|
||||
|
||||
expect(onEdit).toHaveBeenCalledTimes(1);
|
||||
expect(onEdit).toHaveBeenCalledWith(mockProviders[0]);
|
||||
});
|
||||
expect(onEdit).toHaveBeenCalledTimes(1)
|
||||
expect(onEdit).toHaveBeenCalledWith(mockProviders[0])
|
||||
})
|
||||
|
||||
it('calls onDelete with correct provider ID when delete is confirmed', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onDelete = vi.fn();
|
||||
render(<ProviderTable {...defaultProps} onDelete={onDelete} />);
|
||||
const user = userEvent.setup()
|
||||
const onDelete = vi.fn()
|
||||
render(<ProviderTable {...defaultProps} onDelete={onDelete} />)
|
||||
|
||||
// Find and click the delete button for the first row
|
||||
const deleteButtons = screen.getAllByRole('button', { name: '删除' });
|
||||
await user.click(deleteButtons[0]);
|
||||
const deleteButtons = screen.getAllByRole('button', { name: '删除' })
|
||||
await user.click(deleteButtons[0])
|
||||
|
||||
// TDesign Popconfirm renders confirmation popup with "确定" button
|
||||
const confirmButton = await screen.findByRole('button', { name: '确定' });
|
||||
await user.click(confirmButton);
|
||||
const confirmButton = await screen.findByRole('button', { name: '确定' })
|
||||
await user.click(confirmButton)
|
||||
|
||||
// Assert that onDelete was called with the correct provider ID
|
||||
expect(onDelete).toHaveBeenCalledTimes(1);
|
||||
expect(onDelete).toHaveBeenCalledWith('openai');
|
||||
}, 10000);
|
||||
expect(onDelete).toHaveBeenCalledTimes(1)
|
||||
expect(onDelete).toHaveBeenCalledWith('openai')
|
||||
}, 10000)
|
||||
|
||||
it('shows loading state', () => {
|
||||
const { container } = render(<ProviderTable {...defaultProps} loading={true} />);
|
||||
const { container } = render(<ProviderTable {...defaultProps} loading={true} />)
|
||||
// TDesign Table loading indicator
|
||||
const loadingElement = container.querySelector('.t-table__loading') || container.querySelector('.t-loading');
|
||||
expect(loadingElement).toBeInTheDocument();
|
||||
});
|
||||
const loadingElement = container.querySelector('.t-table__loading') || container.querySelector('.t-loading')
|
||||
expect(loadingElement).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders expandable ModelTable when row is expanded', async () => {
|
||||
const user = userEvent.setup();
|
||||
const { container } = render(<ProviderTable {...defaultProps} />);
|
||||
const user = userEvent.setup()
|
||||
const { container } = render(<ProviderTable {...defaultProps} />)
|
||||
|
||||
// TDesign Table expand icon is rendered as a button with specific class
|
||||
const expandIcon = container.querySelector('.t-table__expandable-icon');
|
||||
const expandIcon = container.querySelector('.t-table__expandable-icon')
|
||||
if (expandIcon) {
|
||||
await user.click(expandIcon);
|
||||
await user.click(expandIcon)
|
||||
|
||||
// Verify that ModelTable content is rendered with data from mocked useModels
|
||||
expect(await screen.findByText('gpt-4o')).toBeInTheDocument();
|
||||
expect(screen.getByText('gpt-3.5-turbo')).toBeInTheDocument();
|
||||
expect(await screen.findByText('gpt-4o')).toBeInTheDocument()
|
||||
expect(screen.getByText('gpt-3.5-turbo')).toBeInTheDocument()
|
||||
} else {
|
||||
// If no expand icon found, the test should still pass as expandable rows are optional
|
||||
expect(true).toBe(true);
|
||||
expect(true).toBe(true)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
it('sets fixed width and ellipsis on name column', () => {
|
||||
const { container } = render(<ProviderTable {...defaultProps} />);
|
||||
const { container } = render(<ProviderTable {...defaultProps} />)
|
||||
// TDesign Table
|
||||
const table = container.querySelector('.t-table');
|
||||
expect(table).toBeInTheDocument();
|
||||
});
|
||||
const table = container.querySelector('.t-table')
|
||||
expect(table).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows custom empty text when providers list is empty', () => {
|
||||
render(<ProviderTable {...defaultProps} providers={[]} />);
|
||||
expect(screen.getByText('暂无供应商,点击上方按钮添加')).toBeInTheDocument();
|
||||
});
|
||||
render(<ProviderTable {...defaultProps} providers={[]} />)
|
||||
expect(screen.getByText('暂无供应商,点击上方按钮添加')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders protocol column with correct tags', () => {
|
||||
const { container } = render(<ProviderTable {...defaultProps} />);
|
||||
const { container } = render(<ProviderTable {...defaultProps} />)
|
||||
|
||||
// Check that protocol tags are displayed in the table
|
||||
const protocolCells = container.querySelectorAll('[data-colkey="protocol"]');
|
||||
expect(protocolCells.length).toBeGreaterThan(0);
|
||||
const protocolCells = container.querySelectorAll('[data-colkey="protocol"]')
|
||||
expect(protocolCells.length).toBeGreaterThan(0)
|
||||
|
||||
// Verify protocol tags exist
|
||||
const tags = container.querySelectorAll('.t-tag');
|
||||
expect(tags.length).toBeGreaterThan(0);
|
||||
});
|
||||
const tags = container.querySelectorAll('.t-tag')
|
||||
expect(tags.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it('displays protocol tag for each provider', () => {
|
||||
const singleProvider: Provider[] = [
|
||||
@@ -190,11 +196,11 @@ describe('ProviderTable', () => {
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
updatedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
];
|
||||
const { container } = render(<ProviderTable {...defaultProps} providers={singleProvider} />);
|
||||
]
|
||||
const { container } = render(<ProviderTable {...defaultProps} providers={singleProvider} />)
|
||||
|
||||
// Should display protocol column
|
||||
const protocolCell = container.querySelector('[data-colkey="protocol"]');
|
||||
expect(protocolCell).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
const protocolCell = container.querySelector('[data-colkey="protocol"]')
|
||||
expect(protocolCell).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user