import type { Issue, IssueColumn } from '../types' import { describe, expect, it } from 'vitest' import { isColumnId, resolveKanbanDrag } from './kanbanDrag' function issue( number: number, column: IssueColumn, extra: Partial = {}, ): Issue { return { id: `repo#${number}`, number, title: `issue ${number}`, column, htmlUrl: '', ...extra, } } describe('isColumnId', () => { it('识别四列 id', () => { expect(isColumnId('todo')).toBe(true) expect(isColumnId('in-progress')).toBe(true) expect(isColumnId('review')).toBe(true) expect(isColumnId('done')).toBe(true) }) it('拒绝卡片 id', () => { expect(isColumnId('repo#1')).toBe(false) }) }) describe('resolveKanbanDrag', () => { it('active===over → none', () => { const issues = [issue(1, 'todo')] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'repo#1' })) .toEqual({ type: 'none' }) }) it('丢到另一张待办卡片 = set(over 为前置)', () => { const issues = [issue(1, 'todo'), issue(2, 'todo')] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'repo#2' })) .toEqual({ type: 'dependency-set', issueNumber: 1, prerequisiteNumber: 2 }) }) it('丢到待办列空白且已有依赖 = clear', () => { const issues = [issue(1, 'todo', { prerequisite: 2 }), issue(2, 'todo')] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'todo' })) .toEqual({ type: 'dependency-clear', issueNumber: 1, prerequisiteNumber: 2 }) }) it('over 是列且已在 todo 且无依赖 = none', () => { const issues = [issue(1, 'todo')] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'todo' })) .toEqual({ type: 'none' }) }) it('跨列仍是 column', () => { const issues = [issue(1, 'in-progress'), issue(2, 'todo')] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'todo' })) .toEqual({ type: 'column', issueNumber: 1, toColumn: 'todo' }) expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'repo#2' })) .toEqual({ type: 'column', issueNumber: 1, toColumn: 'todo' }) }) it('跨列带上 source / externalId', () => { const issues = [issue(1, 'review', { source: 'youtrack', externalId: 'LXF-1' })] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'todo' })) .toEqual({ type: 'column', issueNumber: 1, toColumn: 'todo', source: 'youtrack', externalId: 'LXF-1', }) }) it('环拒绝:不能把祖先挂到自己的后代下', () => { const issues = [ issue(1, 'todo'), issue(2, 'todo', { prerequisite: 1 }), issue(3, 'todo', { prerequisite: 2 }), ] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'repo#3' })) .toEqual({ type: 'none' }) }) it('已经是这个 parent → none', () => { const issues = [issue(1, 'todo', { prerequisite: 2 }), issue(2, 'todo')] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'repo#2' })) .toEqual({ type: 'none' }) }) it('换 parent 也是 dependency-set,不先 clear', () => { const issues = [ issue(1, 'todo', { prerequisite: 2 }), issue(2, 'todo'), issue(3, 'todo'), ] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'repo#3' })) .toEqual({ type: 'dependency-set', issueNumber: 1, prerequisiteNumber: 3 }) }) it('同列非 todo → reorder', () => { const issues = [issue(1, 'in-progress'), issue(2, 'in-progress')] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'repo#2' })) .toEqual({ type: 'reorder', fromIndex: 0, toIndex: 1 }) }) it('锁定:todo 且前置未完成,目标列不是 todo → none', () => { const issues = [ issue(1, 'todo', { prerequisite: 2 }), issue(2, 'in-progress'), ] expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'in-progress' })) .toEqual({ type: 'none' }) expect(resolveKanbanDrag({ issues, activeId: 'repo#1', overId: 'repo#2' })) .toEqual({ type: 'none' }) }) })