feat: P1 合盘/星座/问答与测测完整设计包及模拟器取证工具

落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-03 11:37:53 +08:00
co-authored by Cursor
parent 15a9db374a
commit bd22d9dddd
248 changed files with 26309 additions and 842 deletions
+84
View File
@@ -0,0 +1,84 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { defineComponent, h } from 'vue'
import AskPage from './AskPage.vue'
const { api } = vi.hoisted(() => ({
api: {
listProfiles: vi.fn(),
getAskQuota: vi.fn(),
createAskThread: vi.fn(),
sendAskMessage: vi.fn(),
},
}))
vi.mock('../api/client', () => ({ api }))
vi.mock('vue-router', () => ({
useRoute: () => ({ query: {} }),
useRouter: () => ({ back: vi.fn(), push: vi.fn() }),
}))
const RouterLinkStub = defineComponent({
props: ['to'],
setup(props, { slots }) {
return () => h('a', { href: String(props.to) }, slots.default?.())
},
})
describe('AskPage', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('shows empty state when no profiles', async () => {
api.listProfiles.mockResolvedValue({ items: [] })
api.getAskQuota.mockResolvedValue({ remaining: 3, free_limit: 3, active_membership: false, source: 'free' })
const w = mount(AskPage, { global: { stubs: { RouterLink: RouterLinkStub } } })
await flushPromises()
expect(w.text()).toContain('还没有个人档案')
expect(w.text()).toContain('去首页探索')
})
it('loads profiles and sends a message', async () => {
api.listProfiles.mockResolvedValue({
items: [{ id: 'p1', relation: 'self', display_name: '我', birth_date: '1990-01-01' }],
})
api.getAskQuota.mockResolvedValue({ remaining: 3, free_limit: 3, active_membership: false, source: 'free' })
api.createAskThread.mockResolvedValue({ id: 't1', profile_id: 'p1' })
api.sendAskMessage.mockResolvedValue({
user_message: { id: 'm1', role: 'user', content: '你好', thread_id: 't1', created_at: '' },
assistant_message: {
id: 'm2',
role: 'assistant',
content: '结合档案的回复',
thread_id: 't1',
created_at: '',
},
quota: { remaining: 2, free_limit: 3, active_membership: false, source: 'free' },
})
const w = mount(AskPage, { global: { stubs: { RouterLink: RouterLinkStub } } })
await flushPromises()
expect(w.text()).toContain('剩余 3 次')
await w.find('textarea').setValue('你好')
await w.find('form').trigger('submit')
await flushPromises()
expect(api.createAskThread).toHaveBeenCalled()
expect(w.text()).toContain('结合档案的回复')
expect(w.text()).toContain('剩余 2 次')
})
it('shows boot error with retry', async () => {
api.listProfiles.mockRejectedValueOnce(new Error('网络错误'))
api.getAskQuota.mockRejectedValueOnce(new Error('网络错误'))
const w = mount(AskPage, { global: { stubs: { RouterLink: RouterLinkStub } } })
await flushPromises()
expect(w.text()).toContain('网络错误')
api.listProfiles.mockResolvedValue({ items: [] })
api.getAskQuota.mockResolvedValue({ remaining: 3, free_limit: 3, active_membership: false, source: 'free' })
await w.find('button.link').trigger('click')
await flushPromises()
expect(w.text()).toContain('还没有个人档案')
})
})