import { readFileSync } from 'node:fs'; import React from 'react'; import { act, create } from 'react-test-renderer'; import { describe, expect, it, vi } from 'vitest'; import { I18nProvider } from '../i18n/provider'; import type { SecurityUpdateStatus } from '../types'; import type { OverlayWorkbenchTheme } from '../utils/overlayWorkbenchTheme'; import SecurityUpdateSettingsModal from './SecurityUpdateSettingsModal'; vi.mock('../i18n/runtime', () => ({ syncLanguageRuntime: vi.fn(async () => undefined), })); vi.mock('antd', async () => { const React = await import('react'); return { Button: ({ children, }: { children?: React.ReactNode; }) => React.createElement('button', null, children), Empty: ({ description, }: { description?: React.ReactNode; }) => React.createElement('div', null, description), Modal: ({ children, footer, open, title, }: { children?: React.ReactNode; footer?: React.ReactNode; open?: boolean; title?: React.ReactNode; }) => (open ? React.createElement('section', null, title, footer, children) : null), Tag: ({ children, }: { children?: React.ReactNode; }) => React.createElement('span', null, children), }; }); vi.mock('@ant-design/icons', async () => { const React = await import('react'); return { SafetyCertificateOutlined: () => React.createElement('span', null, 'certificate'), }; }); const source = readFileSync(new URL('./SecurityUpdateSettingsModal.tsx', import.meta.url), 'utf8'); const overlayTheme: OverlayWorkbenchTheme = { isDark: false, shellBg: '#fff', shellBorder: '1px solid #eee', shellShadow: 'none', shellBackdropFilter: 'none', sectionBg: '#fff', sectionBorder: '1px solid #eee', mutedText: '#666', titleText: '#111', iconBg: '#f5f5f5', iconColor: '#1677ff', hoverBg: '#f5f5f5', selectedBg: '#e6f4ff', selectedText: '#1677ff', divider: '#eee', }; const baseStatus: SecurityUpdateStatus = { overallStatus: 'needs_attention', summary: { total: 2, updated: 1, pending: 1, skipped: 0, failed: 0, }, issues: [ { id: 'issue-1', title: 'RAW issue title', message: 'RAW issue message', severity: 'high', status: 'needs_attention', action: 'open_connection', }, ], backupPath: 'C:\\raw\\backup.zip', lastError: 'RAW system error', }; const renderSettingsModalText = async (status: SecurityUpdateStatus = baseStatus) => { let renderer: ReturnType; await act(async () => { renderer = create( undefined} > undefined} onStart={() => undefined} onRetry={() => undefined} onRestart={() => undefined} onIssueAction={() => undefined} /> , ); }); return JSON.stringify(renderer!.toJSON()); }; describe('SecurityUpdateSettingsModal i18n source guards', () => { it('uses settings i18n keys instead of legacy Chinese shell copy', () => { expect(source).toContain('security_update.settings.title'); expect(source).toContain('security_update.settings.current_status'); expect(source).toContain('security_update.settings.item_default_message'); expect(source).not.toContain('安全更新'); expect(source).not.toContain('管理已保存配置的安全更新状态与待处理项。'); expect(source).not.toContain('当前状态:'); expect(source).not.toContain('影响范围'); expect(source).not.toContain('待处理清单'); expect(source).not.toContain('当前项需要进一步处理后才能完成安全更新。'); }); it('lets the tool center provide the title when embedded', () => { expect(source).toContain('title={embedded ? null : ('); expect(source).toContain('closable={embedded ? false : undefined}'); }); it('localizes settings chrome while preserving raw issue details, backup path and error text', async () => { const modalText = await renderSettingsModalText(); expect(modalText).toContain('Security Update'); expect(modalText).toContain('Manage the security update status and pending items for saved configurations.'); expect(modalText).toContain('Check Again'); expect(modalText).toContain('Restart Update'); expect(modalText).toContain('Close'); expect(modalText).toContain('Current status: Needs Attention'); expect(modalText).toContain('Affected Scope'); expect(modalText).toContain('Pending Items'); expect(modalText).toContain('Status: Needs Attention'); expect(modalText).toContain('Level: High Risk'); expect(modalText).toContain('Latest Result'); expect(modalText).toContain('Backup location: '); expect(modalText).toContain('Latest error: '); expect(modalText).toContain('RAW issue title'); expect(modalText).toContain('RAW issue message'); expect(modalText).toContain('C:\\\\raw\\\\backup.zip'); expect(modalText).toContain('RAW system error'); }); it('localizes the empty pending state and issue default message fallback', async () => { const emptyText = await renderSettingsModalText({ ...baseStatus, issues: [], backupPath: undefined, lastError: undefined, }); expect(emptyText).toContain('No pending items'); const fallbackText = await renderSettingsModalText({ ...baseStatus, issues: [{ id: 'issue-without-message', severity: 'medium', status: 'pending', }], }); expect(fallbackText).toContain('This item needs more attention before the security update can be completed.'); }); });