refactor(web): migrate to Vitest and esm (#29974)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
Stephen Zhou
2025-12-22 16:35:22 +08:00
committed by GitHub
parent 42f7ecda12
commit eabdc5f0eb
268 changed files with 5455 additions and 6307 deletions

View File

@ -11,7 +11,7 @@ enum MockCredentialTypeEnum {
}
// Mock plugin-auth module to avoid deep import chain issues
jest.mock('@/app/components/plugins/plugin-auth', () => ({
vi.mock('@/app/components/plugins/plugin-auth', () => ({
CredentialTypeEnum: {
OAUTH2: 'oauth2',
API_KEY: 'api_key',
@ -19,7 +19,7 @@ jest.mock('@/app/components/plugins/plugin-auth', () => ({
}))
// Mock portal-to-follow-elem - use React state to properly handle open/close
jest.mock('@/app/components/base/portal-to-follow-elem', () => {
vi.mock('@/app/components/base/portal-to-follow-elem', () => {
const MockPortalToFollowElem = ({ children, open }: any) => {
return (
<div data-testid="portal-root" data-open={open}>
@ -85,14 +85,14 @@ const createMockCredentials = (count: number = 3): DataSourceCredential[] =>
const createDefaultProps = (overrides?: Partial<CredentialSelectorProps>): CredentialSelectorProps => ({
currentCredentialId: 'cred-1',
onCredentialChange: jest.fn(),
onCredentialChange: vi.fn(),
credentials: createMockCredentials(),
...overrides,
})
describe('CredentialSelector', () => {
beforeEach(() => {
jest.clearAllMocks()
vi.clearAllMocks()
})
// ==========================================
@ -277,7 +277,7 @@ describe('CredentialSelector', () => {
describe('onCredentialChange prop', () => {
it('should be called when selecting a credential', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
render(<CredentialSelector {...props} />)
@ -298,7 +298,7 @@ describe('CredentialSelector', () => {
['cred-3', 'Credential 3'],
])('should call onCredentialChange with %s when selecting %s', (credId, credentialName) => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
render(<CredentialSelector {...props} />)
@ -317,7 +317,7 @@ describe('CredentialSelector', () => {
it('should call onCredentialChange with cred-1 when selecting Credential 1 in dropdown', () => {
// Arrange - Start with cred-2 selected so cred-1 is only in dropdown
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({
onCredentialChange: mockOnChange,
currentCredentialId: 'cred-2',
@ -359,7 +359,7 @@ describe('CredentialSelector', () => {
it('should call onCredentialChange when clicking a credential item', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
render(<CredentialSelector {...props} />)
@ -376,7 +376,7 @@ describe('CredentialSelector', () => {
it('should close dropdown after selecting a credential', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
render(<CredentialSelector {...props} />)
@ -410,7 +410,7 @@ describe('CredentialSelector', () => {
it('should allow selecting credentials multiple times', () => {
// Arrange - Start with cred-2 selected so we can select other credentials
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({
onCredentialChange: mockOnChange,
currentCredentialId: 'cred-2',
@ -435,7 +435,7 @@ describe('CredentialSelector', () => {
describe('Side Effects and Cleanup', () => {
it('should auto-select first credential when currentCredential is not found and credentials exist', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({
currentCredentialId: 'non-existent-id',
onCredentialChange: mockOnChange,
@ -450,7 +450,7 @@ describe('CredentialSelector', () => {
it('should not call onCredentialChange when currentCredential is found', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({
currentCredentialId: 'cred-2',
onCredentialChange: mockOnChange,
@ -465,7 +465,7 @@ describe('CredentialSelector', () => {
it('should not call onCredentialChange when credentials array is empty', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({
currentCredentialId: 'cred-1',
credentials: [],
@ -481,7 +481,7 @@ describe('CredentialSelector', () => {
it('should auto-select when credentials change and currentCredential becomes invalid', async () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const initialCredentials = createMockCredentials(3)
const props = createDefaultProps({
currentCredentialId: 'cred-1',
@ -512,7 +512,7 @@ describe('CredentialSelector', () => {
it('should not trigger auto-select effect on every render with same props', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
// Act - Render and rerender with same props
@ -531,7 +531,7 @@ describe('CredentialSelector', () => {
describe('Callback Stability and Memoization', () => {
it('should have stable handleCredentialChange callback', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
render(<CredentialSelector {...props} />)
@ -547,8 +547,8 @@ describe('CredentialSelector', () => {
it('should update handleCredentialChange when onCredentialChange changes', () => {
// Arrange
const mockOnChange1 = jest.fn()
const mockOnChange2 = jest.fn()
const mockOnChange1 = vi.fn()
const mockOnChange2 = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange1 })
const { rerender } = render(<CredentialSelector {...props} />)
@ -618,7 +618,7 @@ describe('CredentialSelector', () => {
it('should return undefined currentCredential when id not found', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({
currentCredentialId: 'non-existent',
onCredentialChange: mockOnChange,
@ -643,9 +643,9 @@ describe('CredentialSelector', () => {
it('should not re-render when props remain the same', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
const renderSpy = jest.fn()
const renderSpy = vi.fn()
const TrackedCredentialSelector: React.FC<CredentialSelectorProps> = (trackedProps) => {
renderSpy()
@ -693,8 +693,8 @@ describe('CredentialSelector', () => {
it('should re-render when onCredentialChange reference changes', () => {
// Arrange
const mockOnChange1 = jest.fn()
const mockOnChange2 = jest.fn()
const mockOnChange1 = vi.fn()
const mockOnChange2 = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange1 })
const { rerender } = render(<CredentialSelector {...props} />)
@ -845,7 +845,7 @@ describe('CredentialSelector', () => {
it('should handle credential selection with duplicate names', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const duplicateCredentials = [
createMockCredential({ id: 'cred-1', name: 'Same Name' }),
createMockCredential({ id: 'cred-2', name: 'Same Name' }),
@ -875,7 +875,7 @@ describe('CredentialSelector', () => {
it('should not crash when clicking credential after unmount', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
const { unmount } = render(<CredentialSelector {...props} />)
@ -1017,7 +1017,7 @@ describe('CredentialSelector', () => {
it('should pass handleCredentialChange to List component', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
render(<CredentialSelector {...props} />)

View File

@ -10,7 +10,7 @@ enum MockCredentialTypeEnum {
}
// Mock plugin-auth module to avoid deep import chain issues
jest.mock('@/app/components/plugins/plugin-auth', () => ({
vi.mock('@/app/components/plugins/plugin-auth', () => ({
CredentialTypeEnum: {
OAUTH2: 'oauth2',
API_KEY: 'api_key',
@ -18,7 +18,7 @@ jest.mock('@/app/components/plugins/plugin-auth', () => ({
}))
// Mock portal-to-follow-elem - required for CredentialSelector
jest.mock('@/app/components/base/portal-to-follow-elem', () => {
vi.mock('@/app/components/base/portal-to-follow-elem', () => {
const MockPortalToFollowElem = ({ children, open }: any) => {
return (
<div data-testid="portal-root" data-open={open}>
@ -84,14 +84,14 @@ const createDefaultProps = (overrides?: Partial<HeaderProps>): HeaderProps => ({
docLink: 'https://docs.example.com',
pluginName: 'Test Plugin',
currentCredentialId: 'cred-1',
onCredentialChange: jest.fn(),
onCredentialChange: vi.fn(),
credentials: createMockCredentials(),
...overrides,
})
describe('Header', () => {
beforeEach(() => {
jest.clearAllMocks()
vi.clearAllMocks()
})
// ==========================================
@ -266,7 +266,7 @@ describe('Header', () => {
describe('onClickConfiguration prop', () => {
it('should call onClickConfiguration when configuration icon is clicked', () => {
// Arrange
const mockOnClick = jest.fn()
const mockOnClick = vi.fn()
const props = createDefaultProps({ onClickConfiguration: mockOnClick })
render(<Header {...props} />)
@ -328,7 +328,7 @@ describe('Header', () => {
it('should pass onCredentialChange to CredentialSelector', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
render(<Header {...props} />)
@ -363,7 +363,7 @@ describe('Header', () => {
it('should allow credential selection through CredentialSelector', () => {
// Arrange
const mockOnChange = jest.fn()
const mockOnChange = vi.fn()
const props = createDefaultProps({ onCredentialChange: mockOnChange })
render(<Header {...props} />)
@ -377,7 +377,7 @@ describe('Header', () => {
it('should trigger configuration callback when clicking config icon', () => {
// Arrange
const mockOnConfig = jest.fn()
const mockOnConfig = vi.fn()
const props = createDefaultProps({ onClickConfiguration: mockOnConfig })
const { container } = render(<Header {...props} />)
@ -402,7 +402,7 @@ describe('Header', () => {
it('should not re-render when props remain the same', () => {
// Arrange
const props = createDefaultProps()
const renderSpy = jest.fn()
const renderSpy = vi.fn()
const TrackedHeader: React.FC<HeaderProps> = (trackedProps) => {
renderSpy()
@ -573,7 +573,7 @@ describe('Header', () => {
describe('Integration', () => {
it('should work with full credential workflow', () => {
// Arrange
const mockOnCredentialChange = jest.fn()
const mockOnCredentialChange = vi.fn()
const props = createDefaultProps({
onCredentialChange: mockOnCredentialChange,
currentCredentialId: 'cred-1',
@ -597,7 +597,7 @@ describe('Header', () => {
it('should display all components together correctly', () => {
// Arrange
const mockOnConfig = jest.fn()
const mockOnConfig = vi.fn()
const props = createDefaultProps({
docTitle: 'Integration Test Docs',
docLink: 'https://test.com/docs',