import React from 'react'; import { readFileSync } from 'node:fs'; import { act, create, type ReactTestRenderer } from 'react-test-renderer'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import DriverManagerModal from './DriverManagerModal'; const connectionModalSource = readFileSync(new URL('./ConnectionModal.tsx', import.meta.url), 'utf8'); const driverManagerModalSource = readFileSync(new URL('./DriverManagerModal.tsx', import.meta.url), 'utf8'); const sidebarSource = readFileSync(new URL('./Sidebar.tsx', import.meta.url), 'utf8'); const storeState = vi.hoisted(() => ({ theme: 'light', appearance: { enabled: true, opacity: 1, blur: 0, uiVersion: 'legacy', }, })); const backendApp = vi.hoisted(() => ({ CheckDriverNetworkStatus: vi.fn(), DownloadDriverPackage: vi.fn(), GetDriverVersionList: vi.fn(), GetDriverVersionPackageSize: vi.fn(), GetDriverStatusList: vi.fn(), InstallLocalDriverPackage: vi.fn(), OpenDriverDownloadDirectory: vi.fn(), RemoveDriverPackage: vi.fn(), SelectDriverPackageDirectory: vi.fn(), SelectDriverPackageFile: vi.fn(), })); const runtimeApi = vi.hoisted(() => ({ EventsOn: vi.fn(() => vi.fn()), })); const messageApi = vi.hoisted(() => ({ error: vi.fn(), success: vi.fn(), warning: vi.fn(), info: vi.fn(), })); vi.mock('../store', () => ({ useStore: (selector: (state: typeof storeState) => any) => selector(storeState), })); vi.mock('../../wailsjs/go/app/App', () => backendApp); vi.mock('../../wailsjs/runtime/runtime', () => runtimeApi); vi.mock('@ant-design/icons', () => { const Icon = () => ; return { DeleteOutlined: Icon, DownloadOutlined: Icon, FileSearchOutlined: Icon, FolderOpenOutlined: Icon, InfoCircleFilled: Icon, ReloadOutlined: Icon, }; }); describe('driver-agent update prompt placement', () => { it('keeps revision mismatch prompts inside driver manager only', () => { expect(driverManagerModalSource).toContain('需要重装'); expect(driverManagerModalSource).toContain('row.needsUpdate'); expect(connectionModalSource).not.toContain('当前数据源驱动代理建议重装'); expect(connectionModalSource).not.toContain('去驱动管理重装'); expect(sidebarSource).not.toContain('warnIfConnectionDriverAgentNeedsUpdate'); expect(sidebarSource).not.toContain('driver-agent-update-'); expect(sidebarSource).not.toContain('驱动代理需要重装:'); }); }); vi.mock('antd', () => { const Button: any = ({ children, disabled, loading, onClick, ...rest }: any) => ( ); const Input: any = ({ value, onChange, placeholder }: any) => ( ); Input.Search = ({ value, onChange, placeholder }: any) => ( ); const Select = ({ value, options, disabled, loading, placeholder, onOpenChange, onChange }: any) => ( ); const Progress = () =>
; const Tag = ({ children }: any) => {children}; const Switch = ({ checked, onChange, disabled }: any) => ( ); const Space = ({ children }: any) =>
{children}
; const Text = ({ children }: any) => {children}; const Paragraph = ({ children }: any) =>
{children}
; const Typography = { Paragraph, Text }; const Alert = ({ children, message, description }: any) =>
{children}{message}{description}
; const Empty: any = ({ description }: any) =>
{description}
; Empty.PRESENTED_IMAGE_SIMPLE = null; const Collapse = ({ items }: any) => (
{items?.map((item: any) =>
{item.label}{item.children}
)}
); const Modal: any = ({ children, open, footer, title }: any) => (open ? (
{children}
{footer}
) : null); Modal.confirm = vi.fn(); return { Alert, Button, Collapse, Empty, Input, Modal, Progress, Select, Space, Switch, Tag, Typography, message: messageApi, }; }); const flushPromises = async () => { await act(async () => { await Promise.resolve(); await Promise.resolve(); }); }; const textContent = (node: any): string => (node.children || []) .map((item: any) => (typeof item === 'string' ? item : textContent(item))) .join(''); const findButton = (renderer: ReactTestRenderer, text: string) => renderer.root.findAll((node) => node.type === 'button' && textContent(node).includes(text))[0]; describe('DriverManagerModal toolbar actions', () => { beforeEach(() => { vi.clearAllMocks(); backendApp.GetDriverStatusList.mockResolvedValue({ success: true, data: { downloadDir: 'D:/drivers', drivers: [ { type: 'duckdb', name: 'DuckDB', builtIn: false, pinnedVersion: '2.5.6', runtimeAvailable: false, packageInstalled: false, connectable: false, defaultDownloadUrl: 'builtin://activate/duckdb', message: '未启用', }, ], }, }); backendApp.CheckDriverNetworkStatus.mockResolvedValue({ success: true, data: { reachable: true, summary: 'ok', recommendedProxy: false, proxyConfigured: false, checks: [], }, }); backendApp.GetDriverVersionList.mockResolvedValue({ success: true, data: { versions: [{ version: '2.5.6', downloadUrl: 'builtin://activate/duckdb', recommended: true }], }, }); backendApp.DownloadDriverPackage.mockImplementation(() => new Promise(() => {})); backendApp.OpenDriverDownloadDirectory.mockResolvedValue({ success: true }); backendApp.SelectDriverPackageDirectory.mockResolvedValue({ success: true, data: { path: 'D:/drivers/import' } }); }); it('keeps directory tools enabled while a single driver install is running', async () => { let renderer: ReactTestRenderer; await act(async () => { renderer = create(); }); await flushPromises(); const installButton = findButton(renderer!, '安装启用'); const openDirButtonBefore = findButton(renderer!, '打开驱动目录'); const importDirButtonBefore = findButton(renderer!, '导入驱动目录'); const installAllButtonBefore = findButton(renderer!, '安装所有驱动'); expect(openDirButtonBefore.props.disabled).toBeFalsy(); expect(importDirButtonBefore.props.disabled).toBeFalsy(); expect(installAllButtonBefore.props.disabled).toBeFalsy(); await act(async () => { installButton.props.onClick(); await Promise.resolve(); }); const openDirButtonAfter = findButton(renderer!, '打开驱动目录'); const importDirButtonAfter = findButton(renderer!, '导入驱动目录'); const installAllButtonAfter = findButton(renderer!, '安装所有驱动'); expect(openDirButtonAfter.props.disabled).toBeFalsy(); expect(importDirButtonAfter.props.disabled).toBeFalsy(); expect(installAllButtonAfter.props.disabled).toBe(true); }); it('releases install action when the driver install watchdog expires', async () => { vi.useFakeTimers(); try { let renderer: ReactTestRenderer; await act(async () => { renderer = create(); }); await flushPromises(); const installButton = findButton(renderer!, '安装启用'); await act(async () => { installButton.props.onClick(); await Promise.resolve(); }); expect(findButton(renderer!, '安装启用').props.disabled).toBe(true); await act(async () => { vi.advanceTimersByTime(12 * 60 * 1000); await Promise.resolve(); }); expect(findButton(renderer!, '安装启用').props.disabled).toBeFalsy(); expect(messageApi.error).toHaveBeenCalledWith(expect.stringContaining('超过 12 分钟仍未完成')); } finally { vi.useRealTimers(); } }); it('reinstalls stale MongoDB v2 drivers with the v1 compatibility default', async () => { vi.useFakeTimers({ shouldAdvanceTime: true }); try { vi.setSystemTime(new Date(Date.now() + 2 * 60 * 1000)); backendApp.GetDriverStatusList.mockResolvedValue({ success: true, data: { downloadDir: 'D:/drivers', drivers: [ { type: 'mongodb', name: 'MongoDB', builtIn: false, pinnedVersion: '1.17.9', installedVersion: '2.5.0', runtimeAvailable: true, packageInstalled: true, connectable: true, needsUpdate: true, defaultDownloadUrl: 'builtin://activate/mongodb', message: '建议重装', }, ], }, }); backendApp.GetDriverVersionList.mockResolvedValue({ success: true, data: { versions: [ { version: '2.5.0', downloadUrl: 'builtin://activate/mongodb?version=2.5.0' }, { version: '1.17.9', downloadUrl: 'builtin://activate/mongodb', recommended: true }, ], }, }); backendApp.DownloadDriverPackage.mockResolvedValue({ success: true }); let renderer: ReactTestRenderer; await act(async () => { renderer = create(); }); await flushPromises(); const reinstallButton = findButton(renderer!, '重装驱动'); await act(async () => { await reinstallButton.props.onClick(); }); expect(backendApp.DownloadDriverPackage).toHaveBeenCalledWith( 'mongodb', '1.17.9', 'builtin://activate/mongodb', 'D:/drivers', ); } finally { vi.useRealTimers(); } }); it('allows switching installed TDengine drivers to a historical compatible version', async () => { backendApp.GetDriverStatusList.mockResolvedValue({ success: true, data: { downloadDir: 'D:/drivers', drivers: [ { type: 'tdengine', name: 'TDengine', builtIn: false, pinnedVersion: '3.7.8', installedVersion: '3.7.8', runtimeAvailable: true, packageInstalled: true, connectable: true, defaultDownloadUrl: 'builtin://activate/tdengine', message: '已启用', }, ], }, }); backendApp.GetDriverVersionList.mockResolvedValue({ success: true, data: { versions: [ { version: '3.7.8', downloadUrl: 'builtin://activate/tdengine', recommended: true }, { version: '3.3.1', downloadUrl: 'builtin://activate/tdengine?channel=history&version=3.3.1' }, ], }, }); backendApp.DownloadDriverPackage.mockResolvedValue({ success: true }); let renderer: ReactTestRenderer; await act(async () => { renderer = create(); }); await flushPromises(); const refreshButton = findButton(renderer!, '刷新'); await act(async () => { await refreshButton.props.onClick(); }); await flushPromises(); const versionSelect = renderer!.root.findByType('select'); await act(async () => { versionSelect.props.onFocus(); }); await flushPromises(); expect(backendApp.GetDriverVersionList).toHaveBeenCalledWith('tdengine', ''); const reloadedVersionSelect = renderer!.root.findByType('select'); await act(async () => { reloadedVersionSelect.props.onChange({ target: { value: '3.3.1@@builtin://activate/tdengine?channel=history&version=3.3.1' } }); }); await flushPromises(); const switchButtons = renderer!.root.findAll((node) => node.type === 'button' && textContent(node).includes('切换版本')); expect(switchButtons).toHaveLength(1); const switchButton = switchButtons[0]; await act(async () => { await switchButton.props.onClick(); }); expect(backendApp.DownloadDriverPackage).toHaveBeenCalledWith( 'tdengine', '3.3.1', 'builtin://activate/tdengine?channel=history&version=3.3.1', 'D:/drivers', ); }); });