Why: the settings page was mixing duty assignment with whole-document review and chapter objects. The three views need one draft and one save check. Evidence: SELF vitest run of the frontend suite, 278 tests passed, including duty-draft and design-guard. Impact: the duties tab assigns existing documents only. Submit, approve, reject, and final stay out of this page. Commands that are not ready keep the tab hidden. Refs: feature/rdm-duty-matrix Co-authored-by: Cursor <cursoragent@cursor.com>
62 lines
2.9 KiB
TypeScript
62 lines
2.9 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
absorbServerDocs, applyCell, cellDuty, completeness, editCell, replacementNote, saveIssues, savePayload,
|
|
type DutyDocDraft,
|
|
} from '../src/duties/draft'
|
|
|
|
function doc(over: Partial<DutyDocDraft> = {}): DutyDocDraft {
|
|
return {
|
|
docId: 'd1', title: '需求', code: 'UR-1', typeKey: 'design', stage: 'BD',
|
|
dutyVersion: 3, activeRound: 0, legacySectionOverrides: 0, dirty: false,
|
|
author: 'alice', reviewers: ['bob'], approver: 'carol',
|
|
...over,
|
|
}
|
|
}
|
|
|
|
describe('文档职责草稿', () => {
|
|
it('单元格由三权反推,三人视图读到同一份角色', () => {
|
|
const roles = { author: 'alice', reviewers: ['bob', 'dave'], approver: 'carol' }
|
|
expect(cellDuty(roles, 'alice')).toBe('author')
|
|
expect(cellDuty(roles, 'bob')).toBe('reviewer')
|
|
expect(cellDuty(roles, 'dave')).toBe('reviewer')
|
|
expect(cellDuty(roles, 'carol')).toBe('approver')
|
|
expect(cellDuty(roles, 'erin')).toBe('none')
|
|
expect(completeness(roles)).toEqual({ authors: 1, reviewers: 2, approvers: 1, conflicts: 0 })
|
|
})
|
|
|
|
it('替换编制前先记住原编制,并保证一人一责', () => {
|
|
const next = applyCell({ author: 'alice', reviewers: ['bob'], approver: 'carol' }, 'bob', 'author')
|
|
expect(next.replacedAuthor).toBe('alice')
|
|
expect(next.roles).toEqual({ author: 'bob', reviewers: [], approver: 'carol' })
|
|
expect(replacementNote({ author: 'alice', reviewers: ['bob'], approver: 'carol' }, 'bob', 'author')).toBe('将替换alice')
|
|
})
|
|
|
|
it('清掉唯一编制后允许留在草稿,但禁止保存', () => {
|
|
const cleared = editCell([doc()], 'd1', 'alice', 'none')
|
|
expect(cleared[0].author).toBe('')
|
|
expect(cleared[0].dirty).toBe(true)
|
|
expect(saveIssues(cleared[0])).toContain('需指定编制人')
|
|
expect(savePayload(cleared, false).issues.join(' ')).toContain('需指定编制人')
|
|
})
|
|
|
|
it('过期服务端页不覆盖未保存草稿', () => {
|
|
const dirty = doc({ author: 'erin', dirty: true, dutyVersion: 3 })
|
|
const absorbed = absorbServerDocs([dirty], [{
|
|
docId: 'd1', title: '需求', typeKey: 'design', dutyVersion: 4,
|
|
}], [{ docId: 'd1', owner: 'alice', duty: 'author' }, { docId: 'd1', owner: 'bob', duty: 'reviewer' }, { docId: 'd1', owner: 'carol', duty: 'approver' }])
|
|
expect(absorbed[0].author).toBe('erin')
|
|
expect(absorbed[0].dutyVersion).toBe(3)
|
|
})
|
|
|
|
it('保存载荷是每份脏文档的完整目标,超过 20 份被拒绝', () => {
|
|
const many = Array.from({ length: 21 }, (_, index) => doc({ docId: `d${index}`, dirty: true }))
|
|
const blocked = savePayload(many, true)
|
|
expect(blocked.issues).toContain('一次保存最多 20 份文档')
|
|
const one = savePayload([doc({ dirty: true, activeRound: 2 })], true)
|
|
expect(one.docs[0]).toMatchObject({
|
|
docId: 'd1', author: 'alice', reviewers: ['bob'], approver: 'carol',
|
|
expectedDutyVersion: 3, confirmInvalidation: true,
|
|
})
|
|
})
|
|
})
|