mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-22 21:27:11 +08:00
- 新增共享六语言词典、前端 i18n 运行时与语言设置入口 - 推进连接、驱动、数据网格、查询、AI、Redis、表设计等模块文案本地化 - 补充 raw 边界、SQL/驱动/更新场景测试与 i18n 扫描工具
806 lines
30 KiB
TypeScript
806 lines
30 KiB
TypeScript
import React from 'react';
|
||
import { readFileSync } from 'node:fs';
|
||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||
import { act, create, type ReactTestRenderer } from 'react-test-renderer';
|
||
import { t } from '../i18n';
|
||
|
||
const storeState = {
|
||
theme: 'light',
|
||
languagePreference: 'zh-CN',
|
||
setLanguagePreference: vi.fn(async (languagePreference: 'zh-CN' | 'en-US') => {
|
||
storeState.languagePreference = languagePreference;
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage(languagePreference);
|
||
notifyStoreSubscribers();
|
||
}),
|
||
appearance: { uiVersion: 'legacy', opacity: 1 },
|
||
};
|
||
|
||
const storeSubscribers = new Set<() => void>();
|
||
const notifyStoreSubscribers = () => {
|
||
storeSubscribers.forEach((subscriber) => subscriber());
|
||
};
|
||
|
||
const backendApp = {
|
||
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 textContent = (node: any): string => {
|
||
if (node === null || node === undefined) return '';
|
||
if (typeof node === 'string') return node;
|
||
if (Array.isArray(node)) return node.map((item) => textContent(item)).join('');
|
||
return textContent(node.children || []);
|
||
};
|
||
|
||
const findButton = (renderer: ReactTestRenderer, text: string) =>
|
||
renderer.root.findAll((node) => node.type === 'button' && textContent(node).includes(text))[0];
|
||
|
||
const findInputByPlaceholder = (renderer: ReactTestRenderer, placeholder: string) =>
|
||
renderer.root.findAll((node) => node.type === 'input' && node.props?.placeholder === placeholder)[0];
|
||
|
||
const buildNetworkStatusResult = (overrides: Record<string, unknown> = {}) => ({
|
||
success: true,
|
||
data: {
|
||
reachable: true,
|
||
summary: 'reachable',
|
||
downloadChainReachable: true,
|
||
downloadRequiredHosts: [],
|
||
recommendedProxy: false,
|
||
proxyConfigured: false,
|
||
proxyEnv: {},
|
||
checks: [],
|
||
logPath: 'D:/logs/driver-network.log',
|
||
...overrides,
|
||
},
|
||
});
|
||
|
||
vi.mock('../store', () => ({
|
||
useStore: (selector: (state: typeof storeState) => unknown) =>
|
||
React.useSyncExternalStore(
|
||
(subscriber) => {
|
||
storeSubscribers.add(subscriber);
|
||
return () => {
|
||
storeSubscribers.delete(subscriber);
|
||
};
|
||
},
|
||
() => selector(storeState),
|
||
() => selector(storeState),
|
||
),
|
||
}));
|
||
|
||
vi.mock('../../wailsjs/go/app/App', () => backendApp);
|
||
|
||
vi.mock('../../wailsjs/runtime/runtime', () => ({
|
||
EventsOn: vi.fn(() => vi.fn()),
|
||
}));
|
||
|
||
vi.mock('../utils/driverManagerWorkbenchTheme', () => ({
|
||
buildDriverManagerWorkbenchTheme: () => ({
|
||
pageBg: '#fff',
|
||
titleText: '#111',
|
||
sectionBorder: '1px solid #eee',
|
||
sectionBg: '#fff',
|
||
statBorder: '1px solid #eee',
|
||
statBg: '#fff',
|
||
updateNoteBorder: '1px solid #eee',
|
||
updateNoteBg: '#fff',
|
||
cardWarningBorder: '1px solid #faad14',
|
||
cardReadyBorder: '1px solid #52c41a',
|
||
cardBorder: '1px solid #eee',
|
||
cardBg: '#fff',
|
||
warningText: '#faad14',
|
||
isDark: false,
|
||
}),
|
||
}));
|
||
|
||
vi.mock('@ant-design/icons', () => {
|
||
const Icon = () => <span />;
|
||
return {
|
||
DeleteOutlined: Icon,
|
||
DownloadOutlined: Icon,
|
||
FileSearchOutlined: Icon,
|
||
FolderOpenOutlined: Icon,
|
||
InfoCircleFilled: Icon,
|
||
ReloadOutlined: Icon,
|
||
};
|
||
});
|
||
|
||
vi.mock('antd', () => {
|
||
const Button = ({ children, disabled, loading, onClick, ...rest }: any) => (
|
||
<button type="button" disabled={disabled || loading} onClick={onClick} {...rest}>
|
||
{children}
|
||
</button>
|
||
);
|
||
|
||
const Modal: any = ({ title, children, footer, open }: any) =>
|
||
open ? (
|
||
<section>
|
||
<div>{title}</div>
|
||
<div>{children}</div>
|
||
<div>{footer}</div>
|
||
</section>
|
||
) : null;
|
||
Modal.confirm = vi.fn();
|
||
|
||
const Collapse = ({ items }: any) => (
|
||
<div>
|
||
{items?.map((item: any) => (
|
||
<div key={item.key}>
|
||
<div>{item.label}</div>
|
||
<div>{item.children}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
|
||
const Input: any = ({ value, onChange, placeholder, ...rest }: any) => (
|
||
<input value={value} onChange={onChange} placeholder={placeholder} {...rest} />
|
||
);
|
||
Input.Search = ({ value, onChange, placeholder, ...rest }: any) => (
|
||
<input value={value} onChange={onChange} placeholder={placeholder} {...rest} />
|
||
);
|
||
|
||
const Space = ({ children }: any) => <div>{children}</div>;
|
||
const Tag = ({ children }: any) => <span>{children}</span>;
|
||
const Switch = ({ checked, onChange, ...rest }: any) => (
|
||
<button type="button" data-checked={checked} onClick={() => onChange?.(!checked)} {...rest}>
|
||
switch
|
||
</button>
|
||
);
|
||
const Progress = ({ percent }: any) => <div>{percent}</div>;
|
||
const Select = ({ placeholder }: any) => <div>{placeholder}</div>;
|
||
const Empty: any = ({ description }: any) => <div>{description}</div>;
|
||
Empty.PRESENTED_IMAGE_SIMPLE = 'empty';
|
||
const Alert = ({ message, description }: any) => (
|
||
<div>
|
||
<div>{message}</div>
|
||
<div>{description}</div>
|
||
</div>
|
||
);
|
||
|
||
const Typography = {
|
||
Text: ({ children }: any) => <span>{children}</span>,
|
||
Paragraph: ({ children }: any) => <div>{children}</div>,
|
||
};
|
||
|
||
const message = {
|
||
error: vi.fn(),
|
||
warning: vi.fn(),
|
||
success: vi.fn(),
|
||
info: vi.fn(),
|
||
};
|
||
|
||
return {
|
||
Alert,
|
||
Button,
|
||
Collapse,
|
||
Empty,
|
||
Input,
|
||
Modal,
|
||
Progress,
|
||
Select,
|
||
Space,
|
||
Switch,
|
||
Tag,
|
||
Typography,
|
||
message,
|
||
};
|
||
});
|
||
|
||
describe('DriverManagerModal i18n', () => {
|
||
it('keeps backend cancel sentinel checks centralized outside UI branches', () => {
|
||
const source = readFileSync(new URL('./DriverManagerModal.tsx', import.meta.url), 'utf8');
|
||
|
||
expect(source).not.toContain("String(fileRes?.message || '') !== '已取消'");
|
||
expect(source).not.toContain("String(directoryRes?.message || '') !== '已取消'");
|
||
expect(source).toContain('isBackendCancelledResult(fileRes)');
|
||
expect(source).toContain('isBackendCancelledResult(directoryRes)');
|
||
});
|
||
|
||
beforeEach(() => {
|
||
vi.resetModules();
|
||
storeState.theme = 'light';
|
||
storeState.languagePreference = 'zh-CN';
|
||
storeState.setLanguagePreference.mockClear();
|
||
storeState.appearance.uiVersion = 'legacy';
|
||
storeState.appearance.opacity = 1;
|
||
backendApp.GetDriverStatusList.mockResolvedValue({
|
||
success: true,
|
||
data: {
|
||
downloadDir: 'D:/drivers',
|
||
drivers: [
|
||
{
|
||
type: 'clickhouse',
|
||
name: 'ClickHouse',
|
||
builtIn: false,
|
||
pinnedVersion: 'v1.2.3',
|
||
installedVersion: 'v1.2.3',
|
||
packageSizeText: '12 MB',
|
||
runtimeAvailable: true,
|
||
packageInstalled: true,
|
||
connectable: true,
|
||
installDir: 'D:/drivers/clickhouse',
|
||
executablePath: 'D:/drivers/clickhouse/clickhouse-driver-agent.exe',
|
||
message: 'HTTP 403 from GitHub release asset',
|
||
},
|
||
],
|
||
},
|
||
});
|
||
backendApp.CheckDriverNetworkStatus.mockResolvedValue({
|
||
success: true,
|
||
data: {
|
||
reachable: true,
|
||
summary: 'HTTP 403 from GitHub release asset',
|
||
downloadChainReachable: true,
|
||
downloadRequiredHosts: [],
|
||
recommendedProxy: false,
|
||
proxyConfigured: false,
|
||
proxyEnv: {},
|
||
checks: [
|
||
{
|
||
probeCode: 'github_api',
|
||
name: 'GitHub API',
|
||
url: 'https://api.github.com',
|
||
reachable: false,
|
||
httpStatus: 403,
|
||
error: 'HTTP 403',
|
||
},
|
||
],
|
||
logPath: 'D:/logs/driver-network.log',
|
||
},
|
||
});
|
||
backendApp.GetDriverVersionList.mockResolvedValue({ success: true, data: { versions: [] } });
|
||
backendApp.GetDriverVersionPackageSize.mockResolvedValue({ success: true, data: { packageSizeText: '12 MB' } });
|
||
backendApp.DownloadDriverPackage.mockResolvedValue({ success: true });
|
||
backendApp.InstallLocalDriverPackage.mockResolvedValue({ success: true });
|
||
backendApp.OpenDriverDownloadDirectory.mockResolvedValue({ success: true });
|
||
backendApp.RemoveDriverPackage.mockResolvedValue({ success: true });
|
||
backendApp.SelectDriverPackageDirectory.mockResolvedValue({ success: false, message: '已取消' });
|
||
backendApp.SelectDriverPackageFile.mockResolvedValue({ success: false, message: '已取消' });
|
||
});
|
||
|
||
it('updates visible copy when languagePreference changes while the modal stays open', async () => {
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('zh-CN');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
expect(textContent(renderer!.toJSON())).toContain('驱动管理');
|
||
|
||
await act(async () => {
|
||
await storeState.setLanguagePreference('en-US');
|
||
});
|
||
|
||
expect(storeState.setLanguagePreference).toHaveBeenCalledWith('en-US');
|
||
expect(textContent(renderer!.toJSON())).toContain('Driver Manager');
|
||
expect(textContent(renderer!.toJSON())).not.toContain('驱动管理');
|
||
});
|
||
|
||
it('reuses the shared backend cancel sentinel helper instead of local Chinese string checks', async () => {
|
||
const source = readFileSync(new URL('./DriverManagerModal.tsx', import.meta.url), 'utf8');
|
||
|
||
expect(source).toContain('isBackendCancelledResult(');
|
||
expect(source).not.toContain("String(fileRes?.message || '') !== '已取消'");
|
||
expect(source).not.toContain("String(directoryRes?.message || '') !== '已取消'");
|
||
});
|
||
|
||
it('uses driver_manager network catalog keys instead of legacy driver.modal network keys', () => {
|
||
const source = readFileSync(new URL('./DriverManagerModal.tsx', import.meta.url), 'utf8');
|
||
|
||
expect(source).toContain('driver_manager.network.summary.reachable');
|
||
expect(source).toContain('driver_manager.network.not_checked');
|
||
expect(source).toContain('driver_manager.network.alert.download_chain_unreachable');
|
||
expect(source).toContain('driver_manager.punctuation.list_separator');
|
||
expect(source).not.toContain('driver.modal.network.');
|
||
expect(source).not.toContain('driver.modal.punctuation.listSeparator');
|
||
});
|
||
|
||
it('uses driver_manager log catalog keys instead of legacy driver.modal log keys', () => {
|
||
const source = readFileSync(new URL('./DriverManagerModal.tsx', import.meta.url), 'utf8');
|
||
|
||
expect(source).toContain("t('driver_manager.action.logs')");
|
||
expect(source).toContain("t('driver_manager.log_modal.title', { name: activeLogRow?.name || logDriverType })");
|
||
expect(source).toContain("t('driver_manager.log_modal.install_dir', { path: activeLogRow.installDir })");
|
||
expect(source).toContain("t('driver_manager.log_modal.executable_path', { path: activeLogRow.executablePath })");
|
||
expect(source).toContain("t('driver_manager.log_modal.empty')");
|
||
expect(source).not.toContain("t('driver.modal.card.logs')");
|
||
expect(source).not.toContain("t('driver.modal.log.");
|
||
});
|
||
|
||
it('uses driver_manager directory info keys and shared import guidance helpers instead of legacy driver.modal directory keys', () => {
|
||
const source = readFileSync(new URL('./DriverManagerModal.tsx', import.meta.url), 'utf8');
|
||
|
||
expect(source).toContain("t('driver_manager.directory_info.title')");
|
||
expect(source).toContain("t('driver_manager.directory_info.reuse_help')");
|
||
expect(source).toContain("t('driver_manager.directory_info.root_dir', { path: downloadDir || '-' })");
|
||
expect(source).toContain("t('driver_manager.directory_info.log_file', { path: networkStatus.logPath })");
|
||
expect(source).toContain('getDriverLocalImportDirectoryHelp()');
|
||
expect(source).toContain('getDriverLocalImportSingleFileHelp()');
|
||
expect(source).not.toContain("t('driver.modal.directory.title')");
|
||
expect(source).not.toContain("t('driver.modal.directory.description')");
|
||
expect(source).not.toContain("t('driver.modal.directory.root'");
|
||
expect(source).not.toContain("t('driver.modal.directory.logPath'");
|
||
});
|
||
|
||
it('uses structured local source codes internally instead of Chinese labels', () => {
|
||
const source = readFileSync(new URL('./DriverManagerModal.tsx', import.meta.url), 'utf8');
|
||
|
||
expect(source).toContain("type DriverLocalSourceCode = 'file' | 'directory'");
|
||
expect(source).toContain("await installDriverFromLocalPath(row, filePath, 'file');");
|
||
expect(source).toContain("await installDriverFromLocalPath(row, directoryPath, 'directory', { silentToast: true, skipRefresh: true });");
|
||
expect(source).not.toContain("await installDriverFromLocalPath(row, filePath, '文件');");
|
||
expect(source).not.toContain("await installDriverFromLocalPath(row, directoryPath, '目录', { silentToast: true, skipRefresh: true });");
|
||
});
|
||
|
||
it.each([
|
||
['legacy', 'zh-CN', '驱动管理', '安装所有驱动', '搜索驱动名称/类型(如 DuckDB、clickhouse)', '驱动日志 - ClickHouse', '安装目录:', '驱动可执行文件:', '当前驱动暂无操作日志。'],
|
||
['v2', 'en-US', 'Driver Manager', 'Install all drivers', 'Search driver name/type (for example DuckDB, clickhouse)', 'Driver Logs - ClickHouse', 'Install directory:', 'Driver executable:', 'This driver has no operation logs yet.'],
|
||
] as const)(
|
||
'renders localized chrome and preserves raw network summary for %s %s',
|
||
async (uiVersion, language, titleText, toolbarText, searchText, logTitleText, logInstallDirText, logExecutableText, emptyLogText) => {
|
||
storeState.appearance.uiVersion = uiVersion;
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage(language);
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
expect(textContent(renderer!.toJSON())).toContain(titleText);
|
||
expect(textContent(renderer!.toJSON())).toContain(toolbarText);
|
||
expect(findInputByPlaceholder(renderer!, searchText)).toBeTruthy();
|
||
expect(textContent(renderer!.toJSON())).toContain('HTTP 403 from GitHub release asset');
|
||
|
||
await act(async () => {
|
||
findButton(renderer!, language === 'en-US' ? 'Logs' : '日志').props.onClick();
|
||
});
|
||
|
||
expect(textContent(renderer!.toJSON())).toContain(logTitleText);
|
||
expect(textContent(renderer!.toJSON())).toContain(logInstallDirText);
|
||
expect(textContent(renderer!.toJSON())).toContain(logExecutableText);
|
||
expect(textContent(renderer!.toJSON())).toContain(emptyLogText);
|
||
},
|
||
);
|
||
|
||
it.each([
|
||
['legacy', 'zh-CN', '暂无驱动数据'],
|
||
['v2', 'en-US', 'No drivers available'],
|
||
] as const)('renders localized empty state for %s %s', async (uiVersion, language, emptyText) => {
|
||
storeState.appearance.uiVersion = uiVersion;
|
||
backendApp.GetDriverStatusList.mockResolvedValueOnce({
|
||
success: true,
|
||
data: {
|
||
downloadDir: 'D:/drivers',
|
||
drivers: [],
|
||
},
|
||
});
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage(language);
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
expect(textContent(renderer!.toJSON())).toContain(emptyText);
|
||
});
|
||
|
||
it('renders localized card metadata and actions for en-US v2 while preserving raw driver detail', async () => {
|
||
storeState.appearance.uiVersion = 'v2';
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('en-US');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
const content = textContent(renderer!.toJSON());
|
||
expect(content).toContain('Size: 12 MB');
|
||
expect(content).toContain('Version: v1.2.3');
|
||
expect(content).toContain('Driver version');
|
||
expect(content).toContain('Status progress');
|
||
expect(content).toContain('v1.2.3 (installed)');
|
||
expect(findButton(renderer!, 'Remove')).toBeTruthy();
|
||
expect(content).toContain('HTTP 403 from GitHub release asset');
|
||
});
|
||
|
||
it('renders en-US driver card status shell without exposing backend Chinese wrappers', async () => {
|
||
storeState.appearance.uiVersion = 'v2';
|
||
backendApp.GetDriverStatusList.mockResolvedValue({
|
||
success: true,
|
||
data: {
|
||
downloadDir: 'D:/drivers',
|
||
drivers: [
|
||
{
|
||
type: 'clickhouse',
|
||
name: 'ClickHouse',
|
||
builtIn: false,
|
||
pinnedVersion: 'v2.0.0',
|
||
installedVersion: 'v1.0.0',
|
||
packageSizeText: '12 MB',
|
||
runtimeAvailable: true,
|
||
packageInstalled: true,
|
||
connectable: true,
|
||
needsUpdate: true,
|
||
affectedConnections: 3,
|
||
agentRevision: 'rev-old',
|
||
expectedRevision: 'rev-new',
|
||
updateReason: '驱动代理需要重装后才能应用当前版本的驱动侧更新',
|
||
message: 'raw runtime reason: checksum mismatch abc123',
|
||
},
|
||
],
|
||
},
|
||
});
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('en-US');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
const content = textContent(renderer!.toJSON());
|
||
expect(content).toContain('Reinstall required to apply driver updates.');
|
||
expect(content).toContain('Affects 3 saved connections');
|
||
expect(content).toContain('installed revision rev-old');
|
||
expect(content).toContain('expected revision rev-new');
|
||
expect(content).toContain('raw runtime reason: checksum mismatch abc123');
|
||
expect(content).not.toContain('驱动代理需要重装');
|
||
});
|
||
|
||
it('renders en-US network summary from structured fields instead of backend Chinese summary', async () => {
|
||
storeState.appearance.uiVersion = 'v2';
|
||
backendApp.CheckDriverNetworkStatus.mockResolvedValueOnce({
|
||
success: true,
|
||
data: {
|
||
reachable: true,
|
||
summary: '驱动下载网络检测通过,可直接安装驱动',
|
||
downloadChainReachable: true,
|
||
downloadRequiredHosts: [],
|
||
recommendedProxy: false,
|
||
proxyConfigured: false,
|
||
proxyEnv: {},
|
||
checks: [
|
||
{
|
||
probeCode: 'github_release',
|
||
name: 'GitHub driver release',
|
||
url: 'https://github.com/releases/latest/download/GoNavi-DriverAgents.zip',
|
||
reachable: true,
|
||
httpStatus: 200,
|
||
httpLatencyMs: 88,
|
||
error: 'HTTP 200',
|
||
},
|
||
],
|
||
logPath: 'D:/logs/driver-network.log',
|
||
},
|
||
});
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('en-US');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
const content = textContent(renderer!.toJSON());
|
||
expect(content).toContain('Driver download network is available. You can install drivers directly.');
|
||
expect(content).toContain('reachable, 88ms, HTTP 200');
|
||
expect(content).not.toContain('驱动下载网络检测通过');
|
||
});
|
||
|
||
it('renders checking copy while the network status request is pending', async () => {
|
||
storeState.appearance.uiVersion = 'v2';
|
||
backendApp.CheckDriverNetworkStatus.mockImplementationOnce(() => new Promise(() => {}));
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('en-US');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
await Promise.resolve();
|
||
});
|
||
|
||
const content = textContent(renderer!.toJSON());
|
||
expect(content).toContain('Checking driver download network...');
|
||
expect(content).not.toContain('Network check has not completed');
|
||
});
|
||
|
||
it.each([
|
||
[
|
||
'reachable_with_proxy',
|
||
buildNetworkStatusResult({
|
||
reachable: true,
|
||
proxyConfigured: true,
|
||
proxyEnv: {
|
||
HTTPS_PROXY: 'http://127.0.0.1:7890',
|
||
},
|
||
}),
|
||
'Driver download network is available through the configured proxy.',
|
||
'Driver download network is available. You can install drivers directly.',
|
||
],
|
||
[
|
||
'unreachable_proxy_configured',
|
||
buildNetworkStatusResult({
|
||
reachable: false,
|
||
summary: 'proxy unreachable',
|
||
proxyConfigured: true,
|
||
}),
|
||
'Some driver download endpoints are unreachable. Check that the configured proxy is working and retry.',
|
||
'Configure an HTTP/HTTPS/SOCKS5 proxy before installing drivers.',
|
||
],
|
||
[
|
||
'proxy_recommended',
|
||
buildNetworkStatusResult({
|
||
reachable: false,
|
||
summary: 'proxy recommended',
|
||
recommendedProxy: true,
|
||
}),
|
||
'Some driver download endpoints are unreachable. Configure an HTTP/HTTPS/SOCKS5 proxy before installing drivers.',
|
||
'Check that the configured proxy is working and retry.',
|
||
],
|
||
[
|
||
'unreachable',
|
||
buildNetworkStatusResult({
|
||
reachable: false,
|
||
summary: 'network unreachable',
|
||
}),
|
||
'Some driver download endpoints are unreachable. Check your network and retry.',
|
||
'Configure an HTTP/HTTPS/SOCKS5 proxy before installing drivers.',
|
||
],
|
||
] as const)(
|
||
'renders en-US network summary branch %s from structured status',
|
||
async (_caseName, networkResult, expectedText, unexpectedText) => {
|
||
storeState.appearance.uiVersion = 'v2';
|
||
backendApp.CheckDriverNetworkStatus.mockResolvedValueOnce(networkResult);
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('en-US');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
const content = textContent(renderer!.toJSON());
|
||
expect(content).toContain(expectedText);
|
||
expect(content).not.toContain(unexpectedText);
|
||
},
|
||
);
|
||
|
||
it('renders en-US network punctuation while preserving raw GitHub error text', async () => {
|
||
storeState.appearance.uiVersion = 'v2';
|
||
backendApp.CheckDriverNetworkStatus.mockResolvedValueOnce({
|
||
success: true,
|
||
data: {
|
||
reachable: true,
|
||
summary: 'reachable',
|
||
downloadChainReachable: true,
|
||
downloadRequiredHosts: [
|
||
'github.com',
|
||
'release-assets.githubusercontent.com',
|
||
],
|
||
recommendedProxy: false,
|
||
proxyConfigured: true,
|
||
proxyEnv: {
|
||
HTTP_PROXY: 'http://127.0.0.1:7890',
|
||
HTTPS_PROXY: 'http://127.0.0.1:7890',
|
||
},
|
||
checks: [
|
||
{
|
||
probeCode: 'github_api',
|
||
name: 'GitHub API',
|
||
url: 'https://api.github.com',
|
||
reachable: true,
|
||
httpStatus: 403,
|
||
httpLatencyMs: 123,
|
||
error: 'HTTP 403',
|
||
},
|
||
],
|
||
logPath: 'D:/logs/driver-network.log',
|
||
},
|
||
});
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('en-US');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
const content = textContent(renderer!.toJSON());
|
||
expect(content).toContain('Detected proxy environment variables: HTTP_PROXY, HTTPS_PROXY');
|
||
expect(content).toContain('reachable, 123ms, HTTP 403');
|
||
expect(content).toContain('HTTP 403');
|
||
expect(content).not.toContain('HTTP_PROXY、HTTPS_PROXY');
|
||
expect(content).not.toContain('reachable,123ms,HTTP 403');
|
||
|
||
backendApp.CheckDriverNetworkStatus.mockResolvedValueOnce({
|
||
success: true,
|
||
data: {
|
||
reachable: false,
|
||
summary: 'download chain blocked',
|
||
downloadChainReachable: false,
|
||
downloadRequiredHosts: [
|
||
'github.com',
|
||
'release-assets.githubusercontent.com',
|
||
],
|
||
recommendedProxy: true,
|
||
proxyConfigured: true,
|
||
proxyEnv: {},
|
||
checks: [],
|
||
logPath: 'D:/logs/driver-network.log',
|
||
},
|
||
});
|
||
|
||
await act(async () => {
|
||
findButton(renderer!, 'Network check').props.onClick();
|
||
});
|
||
|
||
const unreachableContent = textContent(renderer!.toJSON());
|
||
expect(unreachableContent).toContain('allow these hosts in the proxy rules: github.com, release-assets.githubusercontent.com.');
|
||
expect(unreachableContent).not.toContain('github.com、release-assets.githubusercontent.com');
|
||
});
|
||
|
||
it('uses structured slim-build reason code instead of raw Chinese message when importing a directory', async () => {
|
||
storeState.appearance.uiVersion = 'v2';
|
||
backendApp.GetDriverStatusList.mockResolvedValue({
|
||
success: true,
|
||
data: {
|
||
downloadDir: 'D:/drivers',
|
||
drivers: [
|
||
{
|
||
type: 'clickhouse',
|
||
name: 'ClickHouse',
|
||
builtIn: false,
|
||
pinnedVersion: 'v1.2.3',
|
||
runtimeAvailable: false,
|
||
packageInstalled: false,
|
||
connectable: false,
|
||
reasonCode: 'slim_build_missing_driver',
|
||
message: 'ClickHouse is unavailable in this slim build',
|
||
},
|
||
],
|
||
},
|
||
});
|
||
backendApp.SelectDriverPackageDirectory.mockResolvedValue({
|
||
success: true,
|
||
data: { path: 'D:/manual/drivers' },
|
||
});
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('en-US');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
await act(async () => {
|
||
findButton(renderer!, 'Import driver directory').props.onClick();
|
||
});
|
||
|
||
expect(backendApp.InstallLocalDriverPackage).not.toHaveBeenCalled();
|
||
expect(textContent(renderer!.toJSON())).toContain('ClickHouse is unavailable in this slim build');
|
||
});
|
||
|
||
it('uses structured GitHub release probe code instead of raw Chinese probe name', async () => {
|
||
storeState.appearance.uiVersion = 'v2';
|
||
backendApp.CheckDriverNetworkStatus.mockResolvedValueOnce({
|
||
success: true,
|
||
data: {
|
||
reachable: true,
|
||
summary: 'reachable',
|
||
downloadChainReachable: true,
|
||
downloadRequiredHosts: [],
|
||
recommendedProxy: false,
|
||
proxyConfigured: false,
|
||
proxyEnv: {},
|
||
checks: [
|
||
{
|
||
probeCode: 'github_release',
|
||
name: 'GitHub driver release',
|
||
url: 'https://github.com/releases/latest/download/GoNavi-DriverAgents.zip',
|
||
reachable: false,
|
||
httpStatus: 403,
|
||
httpLatencyMs: 321,
|
||
error: 'HTTP 403',
|
||
},
|
||
],
|
||
logPath: 'D:/logs/driver-network.log',
|
||
},
|
||
});
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('en-US');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
const content = textContent(renderer!.toJSON());
|
||
expect(content).toContain('unreachable, 321ms, HTTP 403');
|
||
});
|
||
|
||
it('renders en-US frontend-generated operation log shell while preserving raw local import details', async () => {
|
||
storeState.appearance.uiVersion = 'v2';
|
||
backendApp.GetDriverStatusList.mockResolvedValue({
|
||
success: true,
|
||
data: {
|
||
downloadDir: 'D:/drivers',
|
||
drivers: [
|
||
{
|
||
type: 'clickhouse',
|
||
name: 'ClickHouse',
|
||
builtIn: false,
|
||
pinnedVersion: 'v9.8.7',
|
||
installedVersion: '',
|
||
packageSizeText: '12 MB',
|
||
runtimeAvailable: false,
|
||
packageInstalled: false,
|
||
connectable: false,
|
||
installDir: 'D:/drivers/clickhouse',
|
||
executablePath: 'D:/drivers/clickhouse/clickhouse-driver-agent.exe',
|
||
message: 'HTTP 403 from GitHub release asset',
|
||
},
|
||
],
|
||
},
|
||
});
|
||
backendApp.SelectDriverPackageFile.mockResolvedValue({
|
||
success: true,
|
||
data: { path: 'D:/manual/GoNavi-DriverAgents.zip' },
|
||
});
|
||
backendApp.InstallLocalDriverPackage.mockResolvedValueOnce({
|
||
success: false,
|
||
message: 'raw system unzip error: HTTP 500',
|
||
}).mockResolvedValueOnce({ success: true });
|
||
const { setCurrentLanguage } = await import('../i18n');
|
||
setCurrentLanguage('en-US');
|
||
const { default: DriverManagerModal } = await import('./DriverManagerModal');
|
||
|
||
let renderer: ReactTestRenderer;
|
||
await act(async () => {
|
||
renderer = create(<DriverManagerModal open onClose={vi.fn()} />);
|
||
});
|
||
|
||
await act(async () => {
|
||
findButton(renderer!, t('driver_manager.action.import_package')).props.onClick();
|
||
});
|
||
await act(async () => {
|
||
findButton(renderer!, t('driver_manager.action.import_package')).props.onClick();
|
||
});
|
||
await act(async () => {
|
||
findButton(renderer!, 'Logs').props.onClick();
|
||
});
|
||
|
||
const content = textContent(renderer!.toJSON());
|
||
expect(content).toContain('[START] Starting local import (v9.8.7) (file): D:/manual/GoNavi-DriverAgents.zip');
|
||
expect(content).toContain('[ERROR] raw system unzip error: HTTP 500');
|
||
expect(content).toContain('[DONE] Local import installation completed (v9.8.7)');
|
||
expect(content).not.toContain('开始本地导入');
|
||
expect(content).not.toContain('本地导入安装完成');
|
||
});
|
||
});
|