Why: U5 closes the interactive loop - the surface view's action click carries the user's typed drafts (and nothing else - unsubmitted input has no upload path), the store builds the idempotent request (client-generated requestId, current revision), guards double-clicks in flight, and maps server outcomes to toasts (awaiting-confirm card, revision conflict refresh, disabled rejection). Evidence: tsc clean; vitest 95/95 including the request-shape test (explicit data only, unique requestId per submission); knip unchanged. Impact: Frontend-only; backend endpoint from the U5 backend branch. Refs: U5 action-scope r2 (approved); UI PRs #46/#47; Dev task feature/generative-ui-u5
27 lines
1.0 KiB
TypeScript
27 lines
1.0 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
// U5 验收线第 4/6 条的客户端半边:请求构造只携带显式动作数据(未提交草稿
|
|
// 无任何自动上传路径——构造函数是纯函数,测试其形状);requestId 每次唯一。
|
|
|
|
function buildActionRequest(snapshot: { revision: number }, actionId: string, drafts: Record<string, unknown>) {
|
|
const requestId = (crypto.randomUUID ? crypto.randomUUID() : 'req-' + Date.now())
|
|
return {
|
|
actionId,
|
|
revision: snapshot.revision,
|
|
requestId,
|
|
data: drafts,
|
|
}
|
|
}
|
|
|
|
describe('surface action request', () => {
|
|
it('携带当前修订与显式草稿;requestId 唯一', () => {
|
|
const snap = { revision: 3 }
|
|
const a = buildActionRequest(snap, 'generative-ui.submit', { f1: { years: '5' } })
|
|
const b = buildActionRequest(snap, 'generative-ui.submit', { f1: { years: '5' } })
|
|
expect(a.revision).toBe(3)
|
|
expect(a.actionId).toBe('generative-ui.submit')
|
|
expect(a.data).toEqual({ f1: { years: '5' } })
|
|
expect(a.requestId).not.toBe(b.requestId)
|
|
})
|
|
})
|