mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-31 16:33:34 +08:00
fix(desktop): save IM settings without binding fields #1137
This commit is contained in:
parent
b21c63cff3
commit
d0267132eb
@ -793,6 +793,7 @@ Row 9, all 8 cells: continuing from straight down, turning left through lower-le
|
||||
'settings.adapters.waAllowedUsersPlaceholder': 'e.g. 15551234567@s.whatsapp.net',
|
||||
'settings.adapters.defaultProject': 'Default Project',
|
||||
'settings.adapters.defaultProjectHint': 'Default working directory for new IM sessions. If empty, the bot uses the current user working directory.',
|
||||
'settings.adapters.clearDefaultProject': 'Clear default project',
|
||||
'settings.adapters.streamingCard': 'Streaming Card Mode',
|
||||
'settings.adapters.streamingCardDesc': 'Real-time card updates for better experience',
|
||||
'settings.adapters.serverUrl': 'Server URL',
|
||||
|
||||
@ -795,6 +795,7 @@ export const jp: Record<TranslationKey, string> = {
|
||||
'settings.adapters.waAllowedUsersPlaceholder': '例: 15551234567@s.whatsapp.net',
|
||||
'settings.adapters.defaultProject': 'デフォルトプロジェクト',
|
||||
'settings.adapters.defaultProjectHint': '新しい IM セッションのデフォルト作業ディレクトリ。空欄の場合、ボットは現在のユーザーの作業ディレクトリを使用します。',
|
||||
'settings.adapters.clearDefaultProject': 'デフォルトプロジェクトをクリア',
|
||||
'settings.adapters.streamingCard': 'ストリーミングカードモード',
|
||||
'settings.adapters.streamingCardDesc': 'より快適な体験のためにカードをリアルタイムで更新します',
|
||||
'settings.adapters.serverUrl': 'サーバー URL',
|
||||
|
||||
@ -795,6 +795,7 @@ export const kr: Record<TranslationKey, string> = {
|
||||
'settings.adapters.waAllowedUsersPlaceholder': '예: 15551234567@s.whatsapp.net',
|
||||
'settings.adapters.defaultProject': '기본 프로젝트',
|
||||
'settings.adapters.defaultProjectHint': '새 IM 세션의 기본 작업 디렉터리입니다. 비어 있으면 봇은 현재 사용자 작업 디렉터리를 사용합니다.',
|
||||
'settings.adapters.clearDefaultProject': '기본 프로젝트 지우기',
|
||||
'settings.adapters.streamingCard': '스트리밍 카드 모드',
|
||||
'settings.adapters.streamingCardDesc': '더 나은 환경을 위해 카드를 실시간으로 업데이트합니다',
|
||||
'settings.adapters.serverUrl': '서버 URL',
|
||||
|
||||
@ -794,6 +794,7 @@ export const zh: Record<TranslationKey, string> = {
|
||||
'settings.adapters.waAllowedUsersPlaceholder': '如 15551234567@s.whatsapp.net',
|
||||
'settings.adapters.defaultProject': '預設專案',
|
||||
'settings.adapters.defaultProjectHint': '新 IM 會話的預設工作目錄。留空則使用當前使用者工作目錄。',
|
||||
'settings.adapters.clearDefaultProject': '清除預設專案',
|
||||
'settings.adapters.streamingCard': '流式卡片模式',
|
||||
'settings.adapters.streamingCardDesc': '實時更新訊息內容,體驗更好',
|
||||
'settings.adapters.serverUrl': '伺服器地址',
|
||||
|
||||
@ -794,6 +794,7 @@ export const zh: Record<TranslationKey, string> = {
|
||||
'settings.adapters.waAllowedUsersPlaceholder': '如 15551234567@s.whatsapp.net',
|
||||
'settings.adapters.defaultProject': '默认项目',
|
||||
'settings.adapters.defaultProjectHint': '新 IM 会话的默认工作目录。留空则使用当前用户工作目录。',
|
||||
'settings.adapters.clearDefaultProject': '清空默认项目',
|
||||
'settings.adapters.streamingCard': '流式卡片模式',
|
||||
'settings.adapters.streamingCardDesc': '实时更新消息内容,体验更好',
|
||||
'settings.adapters.serverUrl': '服务器地址',
|
||||
|
||||
@ -89,6 +89,135 @@ describe('AdapterSettings Feishu onboarding', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('AdapterSettings config saving', () => {
|
||||
it('does not send WeChat binding-owned fields when saving editable settings', async () => {
|
||||
const updateConfig = vi.fn(async (_patch: Partial<AdapterFileConfig>) => {})
|
||||
renderAdapterSettings(
|
||||
{
|
||||
wechat: {
|
||||
accountId: 'wx-account',
|
||||
botToken: '****oken',
|
||||
baseUrl: 'https://ilinkai.weixin.qq.com',
|
||||
userId: 'wx-user',
|
||||
allowedUsers: ['wx-allowed'],
|
||||
pairedUsers: [{ userId: 'wx-user', displayName: 'WeChat User', pairedAt: 1 }],
|
||||
},
|
||||
},
|
||||
{ updateConfig },
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Save' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateConfig).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
const patch = updateConfig.mock.calls[0]![0]
|
||||
expect(patch).toMatchObject({
|
||||
wechat: {
|
||||
allowedUsers: ['wx-allowed'],
|
||||
},
|
||||
})
|
||||
expect(patch.wechat).toEqual({
|
||||
allowedUsers: ['wx-allowed'],
|
||||
})
|
||||
})
|
||||
|
||||
it('does not send WhatsApp binding-owned fields when saving editable settings', async () => {
|
||||
const updateConfig = vi.fn(async (_patch: Partial<AdapterFileConfig>) => {})
|
||||
renderAdapterSettings(
|
||||
{
|
||||
whatsapp: {
|
||||
accountJid: '15551234567@s.whatsapp.net',
|
||||
authDir: '/tmp/whatsapp-auth',
|
||||
allowedUsers: ['15550000000@s.whatsapp.net'],
|
||||
pairedUsers: [{
|
||||
userId: '15551234567@s.whatsapp.net',
|
||||
displayName: 'WhatsApp User',
|
||||
pairedAt: 1,
|
||||
}],
|
||||
},
|
||||
},
|
||||
{ updateConfig },
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Save' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateConfig).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
const patch = updateConfig.mock.calls[0]![0]
|
||||
expect(patch.whatsapp).toEqual({
|
||||
allowedUsers: ['15550000000@s.whatsapp.net'],
|
||||
})
|
||||
})
|
||||
|
||||
it('submits empty strings when clearing editable configuration', async () => {
|
||||
const updateConfig = vi.fn(async (_patch: Partial<AdapterFileConfig>) => {})
|
||||
renderAdapterSettings(
|
||||
{
|
||||
defaultProjectDir: '/tmp/existing-project',
|
||||
telegram: { botToken: '****oken' },
|
||||
feishu: {
|
||||
appId: 'cli_existing',
|
||||
appSecret: '****cret',
|
||||
encryptKey: '****-key',
|
||||
verificationToken: '****oken',
|
||||
},
|
||||
dingtalk: {
|
||||
clientId: 'ding-client',
|
||||
clientSecret: '****cret',
|
||||
endpoint: 'https://custom.example.com',
|
||||
permissionCardTemplateId: 'permission-template',
|
||||
},
|
||||
},
|
||||
{ updateConfig },
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Clear default project' }))
|
||||
fireEvent.change(screen.getByLabelText('Bot Token'), { target: { value: '' } })
|
||||
|
||||
fireEvent.click(screen.getByRole('tab', { name: 'Feishu' }))
|
||||
fireEvent.change(screen.getByLabelText('App ID'), { target: { value: '' } })
|
||||
fireEvent.change(screen.getByLabelText('App Secret'), { target: { value: '' } })
|
||||
fireEvent.change(screen.getByLabelText('Encrypt Key'), { target: { value: '' } })
|
||||
fireEvent.change(screen.getByLabelText('Verification Token'), { target: { value: '' } })
|
||||
|
||||
fireEvent.click(screen.getByRole('tab', { name: 'DingTalk' }))
|
||||
fireEvent.change(screen.getByLabelText('Client ID'), { target: { value: '' } })
|
||||
fireEvent.change(screen.getByLabelText('Client Secret'), { target: { value: '' } })
|
||||
fireEvent.change(screen.getByLabelText('Stream Endpoint'), { target: { value: '' } })
|
||||
fireEvent.change(screen.getByLabelText('Permission Card Template ID'), { target: { value: '' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Save' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateConfig).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
const patch = updateConfig.mock.calls[0]![0]
|
||||
expect(patch).toMatchObject({
|
||||
defaultProjectDir: '',
|
||||
telegram: {
|
||||
botToken: '',
|
||||
allowedUsers: [],
|
||||
},
|
||||
feishu: {
|
||||
appId: '',
|
||||
appSecret: '',
|
||||
encryptKey: '',
|
||||
verificationToken: '',
|
||||
allowedUsers: [],
|
||||
streamingCard: false,
|
||||
},
|
||||
dingtalk: {
|
||||
clientId: '',
|
||||
clientSecret: '',
|
||||
allowedUsers: [],
|
||||
endpoint: '',
|
||||
permissionCardTemplateId: '',
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('AdapterSettings account unbind confirmation', () => {
|
||||
it('confirms before unbinding a WeChat account', async () => {
|
||||
const unbindWechatAccount = vi.fn(async () => {})
|
||||
|
||||
@ -257,9 +257,9 @@ export function AdapterSettings() {
|
||||
setSaveStatus('idle')
|
||||
setSaveError('')
|
||||
try {
|
||||
const patch: Record<string, unknown> = {}
|
||||
|
||||
if (defaultProjectDir) patch.defaultProjectDir = defaultProjectDir
|
||||
const patch: Record<string, unknown> = {
|
||||
defaultProjectDir,
|
||||
}
|
||||
|
||||
const tgUsers = tgAllowedUsers
|
||||
.split(',')
|
||||
@ -269,7 +269,7 @@ export function AdapterSettings() {
|
||||
.filter((n) => !isNaN(n))
|
||||
|
||||
patch.telegram = {
|
||||
botToken: tgBotToken || undefined,
|
||||
botToken: tgBotToken,
|
||||
allowedUsers: tgUsers.length ? tgUsers : [],
|
||||
}
|
||||
|
||||
@ -279,10 +279,10 @@ export function AdapterSettings() {
|
||||
.filter(Boolean)
|
||||
|
||||
patch.feishu = {
|
||||
appId: fsAppId || undefined,
|
||||
appSecret: fsAppSecret || undefined,
|
||||
encryptKey: fsEncryptKey || undefined,
|
||||
verificationToken: fsVerificationToken || undefined,
|
||||
appId: fsAppId,
|
||||
appSecret: fsAppSecret,
|
||||
encryptKey: fsEncryptKey,
|
||||
verificationToken: fsVerificationToken,
|
||||
allowedUsers: fsUsers.length ? fsUsers : [],
|
||||
streamingCard: fsStreamingCard,
|
||||
}
|
||||
@ -293,7 +293,6 @@ export function AdapterSettings() {
|
||||
.filter(Boolean)
|
||||
|
||||
patch.wechat = {
|
||||
...config.wechat,
|
||||
allowedUsers: wcUsers.length ? wcUsers : [],
|
||||
}
|
||||
|
||||
@ -303,7 +302,6 @@ export function AdapterSettings() {
|
||||
.filter(Boolean)
|
||||
|
||||
patch.whatsapp = {
|
||||
...config.whatsapp,
|
||||
allowedUsers: waUsers.length ? waUsers : [],
|
||||
}
|
||||
|
||||
@ -313,11 +311,11 @@ export function AdapterSettings() {
|
||||
.filter(Boolean)
|
||||
|
||||
patch.dingtalk = {
|
||||
clientId: dtClientId || undefined,
|
||||
clientSecret: dtClientSecret || undefined,
|
||||
clientId: dtClientId,
|
||||
clientSecret: dtClientSecret,
|
||||
allowedUsers: dtUsers.length ? dtUsers : [],
|
||||
endpoint: dtEndpoint || undefined,
|
||||
permissionCardTemplateId: dtPermissionCardTemplateId || undefined,
|
||||
endpoint: dtEndpoint,
|
||||
permissionCardTemplateId: dtPermissionCardTemplateId,
|
||||
}
|
||||
|
||||
await updateConfig(patch)
|
||||
@ -602,7 +600,14 @@ export function AdapterSettings() {
|
||||
<label className="text-sm font-medium text-[var(--color-text-primary)]">
|
||||
{t('settings.adapters.defaultProject')}
|
||||
</label>
|
||||
<DirectoryPicker value={defaultProjectDir} onChange={setDefaultProjectDir} />
|
||||
<div className="flex items-center gap-2">
|
||||
<DirectoryPicker value={defaultProjectDir} onChange={setDefaultProjectDir} />
|
||||
{defaultProjectDir && (
|
||||
<Button variant="ghost" size="sm" onClick={() => setDefaultProjectDir('')}>
|
||||
{t('settings.adapters.clearDefaultProject')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-[var(--color-text-tertiary)]">
|
||||
{t('settings.adapters.defaultProjectHint')}
|
||||
</p>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user