- 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
112 lines
4.0 KiB
TypeScript
112 lines
4.0 KiB
TypeScript
import { render, screen, waitFor, within } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import App from '../../src/app/App'
|
|
import QuickLauncher from '../../src/components/sessions/QuickLauncher'
|
|
import SerialConnectDialog from '../../src/components/serial/SerialConnectDialog'
|
|
import { useSessionStore } from '../../src/stores/sessionStore'
|
|
import { useTabStore } from '../../src/stores/tabStore'
|
|
import { mockDesktopApi } from '../setup'
|
|
import { unlockProForTests } from '../helpers/entitlement'
|
|
|
|
const serialMocks = vi.hoisted(() => ({
|
|
listSerialPorts: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('../../src/platform/serial', () => ({
|
|
listSerialPorts: serialMocks.listSerialPorts,
|
|
}))
|
|
|
|
describe('Quick launcher and serial acceptance', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
unlockProForTests()
|
|
window.opsxApi = mockDesktopApi
|
|
serialMocks.listSerialPorts.mockResolvedValue([
|
|
{ path: '/dev/tty.debug-console', portType: 'Serial' },
|
|
{ path: '/dev/tty.Bluetooth-Incoming-Port', portType: 'Bluetooth' },
|
|
])
|
|
useSessionStore.setState({ sessions: [] })
|
|
useTabStore.setState({
|
|
tabs: [{ id: 'welcome', title: '欢迎', type: 'welcome', isActive: true }],
|
|
activeTabId: 'welcome',
|
|
})
|
|
})
|
|
|
|
it('opens the unified launcher from the tab-bar plus button', async () => {
|
|
const user = userEvent.setup()
|
|
render(<App />)
|
|
|
|
await user.click(screen.getByRole('button', { name: '快速启动' }))
|
|
|
|
expect(await screen.findByRole('heading', { name: '快速启动' })).toBeVisible()
|
|
const launcher = within(screen.getByRole('dialog'))
|
|
expect(launcher.getByRole('button', { name: /串口终端/ })).toBeVisible()
|
|
expect(launcher.getByRole('button', { name: /快速连接/ })).toBeVisible()
|
|
})
|
|
|
|
it('starts the system-default zsh directly from the launcher', async () => {
|
|
const user = userEvent.setup()
|
|
render(
|
|
<QuickLauncher
|
|
isOpen
|
|
onClose={vi.fn()}
|
|
onNewSession={vi.fn()}
|
|
onQuickConnect={vi.fn()}
|
|
onSerialConnect={vi.fn()}
|
|
/>
|
|
)
|
|
|
|
const zshButton = await screen.findByRole('button', {
|
|
name: /Zsh.*\/bin\/zsh.*系统默认/,
|
|
})
|
|
await user.click(zshButton)
|
|
|
|
expect(useTabStore.getState().tabs).toContainEqual(
|
|
expect.objectContaining({
|
|
title: 'Zsh',
|
|
type: 'terminal',
|
|
isLocal: true,
|
|
localShellPath: '/bin/zsh',
|
|
localShellKind: 'zsh',
|
|
})
|
|
)
|
|
})
|
|
|
|
it('maps the selected serial device and line settings into a serial config', async () => {
|
|
const user = userEvent.setup()
|
|
const onConnect = vi.fn()
|
|
render(<SerialConnectDialog isOpen onClose={vi.fn()} onConnect={onConnect} />)
|
|
|
|
await user.click(
|
|
await screen.findByRole('button', { name: /\/dev\/tty\.Bluetooth-Incoming-Port/ })
|
|
)
|
|
await user.selectOptions(screen.getByLabelText('波特率'), '921600')
|
|
await user.selectOptions(screen.getByLabelText('数据位'), '7')
|
|
await user.selectOptions(screen.getByLabelText('停止位'), '2')
|
|
await user.selectOptions(screen.getByLabelText('校验'), 'even')
|
|
await user.selectOptions(screen.getByLabelText('流控'), 'hardware')
|
|
await user.click(screen.getByRole('button', { name: '打开串口' }))
|
|
|
|
expect(onConnect).toHaveBeenCalledWith({
|
|
path: '/dev/tty.Bluetooth-Incoming-Port',
|
|
baudRate: 921600,
|
|
dataBits: 7,
|
|
stopBits: 2,
|
|
parity: 'even',
|
|
flowControl: 'hardware',
|
|
lineEnding: 'lf',
|
|
localEcho: false,
|
|
})
|
|
})
|
|
|
|
it('keeps the platform default serial path when enumeration returns no devices', async () => {
|
|
serialMocks.listSerialPorts.mockResolvedValue([])
|
|
render(<SerialConnectDialog isOpen onClose={vi.fn()} onConnect={vi.fn()} />)
|
|
|
|
await waitFor(() => expect(serialMocks.listSerialPorts).toHaveBeenCalled())
|
|
expect(screen.getByLabelText('设备')).toHaveValue('/dev/tty.usbserial')
|
|
expect(screen.queryByRole('button', { name: /Bluetooth-Incoming-Port/ })).toBeNull()
|
|
})
|
|
})
|