feat(i18n): add multi-language support (ja/ko/vi/ru/zh-TW) with system auto-detection
- Extend AppLanguage to 7 locales: zh-CN, zh-TW, en, ja, ko, vi, ru - Auto-detect system language via navigator.languages; fallback to English - Add BCP47 locale mapping for date formatting and sorting - Create 50 translation JSON files (5 languages × 10 namespaces, 1137 keys each) - Update language selector with native language names - Extend locale key parity test to cover all 7 languages (70 assertions) - Add VNC/RDP desktop, serial terminal, AI agent, port forwarding, and SFTP features - Add credential encryption, host metrics, session backup crypto, and zmodem bridge - Update Rust backend: desktop services, host metrics, SSH/SFTP enhancements
This commit is contained in:
1 parent
fea6d8db04
commit
b0d0e3a5f3
301 files changed
+28002
-2705
No files matched your search
@@ -25,13 +25,13 @@ describe('Session workflow acceptance', () => {
|
||||
const onClose = vi.fn()
|
||||
render(<SessionDialog isOpen onClose={onClose} editSession={null} />)
|
||||
|
||||
await user.type(screen.getByPlaceholderText('自动用 user@host'), 'OpsBase 验收')
|
||||
await user.clear(screen.getByPlaceholderText('root@192.168.1.10'))
|
||||
await user.type(screen.getByPlaceholderText('root@192.168.1.10'), 'root@opsbase.cn:55555')
|
||||
await user.type(screen.getByPlaceholderText('自动用 user@host 或 vnc:host'), 'OpsBase 验收')
|
||||
await user.clear(screen.getByPlaceholderText('192.168.1.10'))
|
||||
await user.type(screen.getByPlaceholderText('192.168.1.10'), 'root@opsbase.cn:55555')
|
||||
expect(screen.getByLabelText('端口')).toHaveValue(55555)
|
||||
await user.click(screen.getByRole('button', { name: /高级选项/ }))
|
||||
await user.clear(screen.getByPlaceholderText('default'))
|
||||
await user.type(screen.getByPlaceholderText('default'), '验收环境')
|
||||
await user.selectOptions(screen.getByLabelText('分组'), '__new__')
|
||||
await user.type(screen.getByPlaceholderText('如:生产环境'), '验收环境')
|
||||
await user.click(screen.getByRole('button', { name: '创建' }))
|
||||
await user.click(screen.getByRole('button', { name: '创建并保存' }))
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -64,8 +64,8 @@ describe('Session workflow acceptance', () => {
|
||||
expect(screen.getByRole('button', { name: '密钥' })).toBeVisible()
|
||||
expect(screen.queryByRole('button', { name: 'Agent' })).not.toBeInTheDocument()
|
||||
|
||||
await user.clear(screen.getByLabelText('目标'))
|
||||
await user.type(screen.getByLabelText('目标'), 'admin@example.com')
|
||||
await user.clear(screen.getByLabelText('主机'))
|
||||
await user.type(screen.getByLabelText('主机'), 'admin@example.com')
|
||||
await user.type(screen.getByLabelText('密码'), 'session-secret')
|
||||
await user.click(screen.getByRole('button', { name: '创建并保存' }))
|
||||
|
||||
@@ -88,9 +88,11 @@ describe('Session workflow acceptance', () => {
|
||||
const user = userEvent.setup()
|
||||
render(<SessionDialog isOpen onClose={vi.fn()} editSession={null} />)
|
||||
|
||||
await user.clear(screen.getByLabelText('目标'))
|
||||
await user.type(screen.getByLabelText('目标'), 'root@opsbase.cn')
|
||||
await user.clear(screen.getByLabelText('主机'))
|
||||
await user.type(screen.getByLabelText('主机'), 'root@opsbase.cn')
|
||||
await user.click(screen.getByRole('button', { name: '密钥' }))
|
||||
expect(screen.getByRole('button', { name: '文件' })).toBeVisible()
|
||||
expect(screen.getByRole('button', { name: '文本' })).toBeVisible()
|
||||
await user.clear(screen.getByLabelText('私钥路径'))
|
||||
await user.type(screen.getByLabelText('私钥路径'), '~/.ssh/id_ed25519')
|
||||
await user.type(screen.getByLabelText('密钥口令(可选)'), 'key-secret')
|
||||
@@ -109,12 +111,39 @@ describe('Session workflow acceptance', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('saves pasted PEM private key as text content', async () => {
|
||||
const user = userEvent.setup()
|
||||
const pem = [
|
||||
'-----BEGIN OPENSSH PRIVATE KEY-----',
|
||||
'b3BlbnNzaC1rZXktdjEAAAAABG5vbmU=',
|
||||
'-----END OPENSSH PRIVATE KEY-----',
|
||||
].join('\n')
|
||||
render(<SessionDialog isOpen onClose={vi.fn()} editSession={null} />)
|
||||
|
||||
await user.clear(screen.getByLabelText('主机'))
|
||||
await user.type(screen.getByLabelText('主机'), 'root@opsbase.cn')
|
||||
await user.click(screen.getByRole('button', { name: '密钥' }))
|
||||
await user.click(screen.getByRole('button', { name: '文本' }))
|
||||
await user.click(screen.getByLabelText('私钥内容'))
|
||||
await user.paste(pem)
|
||||
await user.click(screen.getByRole('button', { name: '创建并保存' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockDesktopApi.sessionCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
authType: 'key',
|
||||
privateKey: pem,
|
||||
password: undefined,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
it('saves a SOCKS5 connection proxy with optional credentials', async () => {
|
||||
const user = userEvent.setup()
|
||||
render(<SessionDialog isOpen onClose={vi.fn()} editSession={null} />)
|
||||
|
||||
await user.clear(screen.getByLabelText('目标'))
|
||||
await user.type(screen.getByLabelText('目标'), 'admin@internal.example.com')
|
||||
await user.clear(screen.getByLabelText('主机'))
|
||||
await user.type(screen.getByLabelText('主机'), 'admin@internal.example.com')
|
||||
await user.click(screen.getByRole('button', { name: /高级选项/ }))
|
||||
await user.click(screen.getByRole('button', { name: 'SOCKS5' }))
|
||||
await user.type(screen.getByPlaceholderText('代理地址'), '127.0.0.1')
|
||||
@@ -153,8 +182,8 @@ describe('Session workflow acceptance', () => {
|
||||
})
|
||||
render(<SessionDialog isOpen onClose={vi.fn()} editSession={null} />)
|
||||
|
||||
await user.clear(screen.getByLabelText('目标'))
|
||||
await user.type(screen.getByLabelText('目标'), 'root@internal.example.com')
|
||||
await user.clear(screen.getByLabelText('主机'))
|
||||
await user.type(screen.getByLabelText('主机'), 'root@internal.example.com')
|
||||
await user.click(screen.getByRole('button', { name: /高级选项/ }))
|
||||
await user.selectOptions(screen.getByLabelText('跳板机(可选)'), 'jump-1')
|
||||
await user.click(screen.getByRole('button', { name: '创建并保存' }))
|
||||
@@ -201,7 +230,7 @@ describe('Session workflow acceptance', () => {
|
||||
|
||||
quick.unmount()
|
||||
render(<SessionDialog isOpen onClose={vi.fn()} editSession={null} />)
|
||||
const sessionTarget = screen.getByLabelText('目标')
|
||||
const sessionTarget = screen.getByLabelText('主机')
|
||||
await user.clear(sessionTarget)
|
||||
await user.paste('admin@example.com:2200')
|
||||
expect(sessionTarget).toHaveValue('admin@example.com:2200')
|
||||
|
||||
Reference in new issue
Block a user