refactor(ECR-004): Synastry 再拆、Scale 测、Membership 守卫、OpenAPI 与 CI
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s

压合盘贴线文件;补选答单测与 nil 守卫;对齐关键 OpenAPI schemas;加 GitHub Actions 门禁。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-05 21:06:14 +08:00
co-authored by Cursor
parent 67701d1f7b
commit 7034c92de7
22 changed files with 1317 additions and 543 deletions
+106
View File
@@ -0,0 +1,106 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import ScalePage from './ScalePage.vue'
const { api } = vi.hoisted(() => ({
api: {
getScale: vi.fn(),
listProfiles: vi.fn(),
submitScale: vi.fn(),
},
}))
vi.mock('../api/client', () => ({ api }))
vi.mock('../lib/scaleDraft', () => ({
loadScaleDraft: () => null,
saveScaleDraft: vi.fn(),
clearScaleDraft: vi.fn(),
}))
vi.mock('vue-router', () => ({
useRoute: () => ({ params: { slug: 'mbti-lite' } }),
}))
const scaleDetail = {
slug: 'mbti-lite',
title: 'MBTI 轻测',
description: '探索向',
questions: [
{
id: 'q1',
sort: 1,
body: {
prompt: '第一题?',
options: [
{ key: 'a', text: '选项A' },
{ key: 'b', text: '选项B' },
],
},
},
{
id: 'q2',
sort: 2,
body: {
prompt: '第二题?',
options: [
{ key: 'a', text: '选项A' },
{ key: 'b', text: '选项B' },
],
},
},
],
}
describe('ScalePage', () => {
beforeEach(() => {
vi.clearAllMocks()
api.getScale.mockResolvedValue(scaleDetail)
api.listProfiles.mockResolvedValue({
items: [{ id: 'p1', relation: 'self', display_name: '我', birth_date: '1990-01-01' }],
})
api.submitScale.mockResolvedValue({
id: 'res1',
result: { label: '探索结果', share_line: '分享一句', summary: '摘要' },
})
})
it('selects answers via emit then submits on last next', async () => {
const w = mount(ScalePage, {
global: { stubs: { RouterLink: true, ShareSheet: true, HomeToolIcon: true, BackButton: true } },
})
await flushPromises()
expect(w.text()).toContain('MBTI 轻测')
await w.find('button.intro-start').trigger('click')
await flushPromises()
expect(w.text()).toContain('第一题?')
const radios = w.findAll('input[type="radio"]')
expect(radios.length).toBeGreaterThan(0)
await radios[0].setValue()
await radios[0].trigger('change')
await flushPromises()
const next = w.findAll('button').find((b) => b.text().includes('下一题'))
expect(next).toBeTruthy()
await next!.trigger('click')
await flushPromises()
expect(w.text()).toContain('第二题?')
const radios2 = w.findAll('input[type="radio"]')
await radios2[0].setValue()
await radios2[0].trigger('change')
await flushPromises()
const submitBtn = w.findAll('button').find((b) => b.text().includes('查看探索结果'))
expect(submitBtn).toBeTruthy()
await submitBtn!.trigger('click')
await flushPromises()
expect(api.submitScale).toHaveBeenCalledWith(
'mbti-lite',
'p1',
expect.objectContaining({ q1: 'a', q2: 'a' }),
)
expect(w.text()).toContain('探索结果')
})
})