test(credentials): 完成 B-04 迁移恢复验收

This commit is contained in:
2026-09-09 19:59:10 +08:00
parent e39bf7b02b
commit db101085d9
6 changed files with 844 additions and 30 deletions
@@ -35,3 +35,18 @@ it('clears the restore password and treats native cancellation as no change', as
expect(wrapper.text()).not.toContain('已恢复')
wrapper.unmount()
})
it('runs native-confirmed cleanup without exposing a path or confirmation to the WebView', async () => {
vi.mocked(hostInvoke).mockImplementation(async command => {
if (command === 'credentials_status') return { locked: false }
if (command === 'credentials_cleanup') return { count: 3, already_clean: false }
return null
})
const wrapper = mount(CredentialVaultSettings)
await flushPromises()
await wrapper.findAll('button').find(button => button.text().includes('清理已迁移'))!.trigger('click')
await flushPromises()
expect(hostInvoke).toHaveBeenCalledWith('credentials_cleanup')
expect(wrapper.text()).toContain('已清理 3 条已迁移凭据的旧文件')
wrapper.unmount()
})
@@ -26,6 +26,15 @@ async function importLegacy() {
} catch (error) { message.value = failureMessage(error, 'MIGRATION_FAILED') }
finally { busy.value = false }
}
async function cleanupLegacy() {
busy.value = true; message.value = ''
try {
const result = await hostInvoke<{ count: number; already_clean: boolean } | null>('credentials_cleanup')
if (result?.already_clean) message.value = t('这次迁移的旧凭据已清理。', 'Legacy credentials for this migration are already cleaned up.')
else if (result) message.value = t(`已清理 ${result.count} 条已迁移凭据的旧文件。`, `Cleaned up legacy files for ${result.count} migrated credentials.`)
} catch (error) { message.value = failureMessage(error, 'MIGRATION_CLEANUP_FAILED') }
finally { busy.value = false }
}
async function backup() {
busy.value = true; message.value = ''
try {
@@ -85,6 +94,7 @@ onUnmounted(() => clearInterval(statusTimer))
<button class="button-primary" type="submit" :disabled="busy">{{ busy ? t('处理中…', 'Working…') : locked ? t('解锁', 'Unlock') : t('更改口令', 'Change password') }}</button>
<button v-if="!locked" class="button-secondary" type="button" :disabled="busy" @click="act('lock')">{{ t('立即锁定', 'Lock now') }}</button>
<button v-if="!locked" class="button-secondary" type="button" :disabled="busy" @click="importLegacy">{{ t('迁移旧凭据', 'Import legacy credentials') }}</button>
<button v-if="!locked" class="button-secondary" type="button" :disabled="busy" @click="cleanupLegacy">{{ t('清理已迁移旧凭据', 'Clean up migrated credentials') }}</button>
<button v-if="!locked" class="button-secondary" type="button" :disabled="busy" @click="backup">{{ t('导出加密备份', 'Export encrypted backup') }}</button>
<button v-if="locked" class="button-secondary" type="button" :disabled="busy" @click="restore">{{ t('使用此口令恢复备份', 'Restore backup with this password') }}</button>
</div>