kin: KIN-013 Настройки в GUI: страница Settings с конфигурацией проектов. Путь к Obsidian vault для синхронизации decisions/tasks/kanban. Двусторонний sync: decisions → Obsidian .md, Obsidian чекбоксы → tasks.
This commit is contained in:
parent
6b328d7f2d
commit
4fd825dc58
2 changed files with 241 additions and 0 deletions
|
|
@ -623,3 +623,171 @@ describe('KIN-065: ProjectView — Autocommit toggle', () => {
|
|||
expect(wrapper.text()).toContain('Network error')
|
||||
})
|
||||
})
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// KIN-015: TaskDetail — Edit button и форма редактирования
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
describe('KIN-015: TaskDetail — Edit button и форма редактирования', () => {
|
||||
function makePendingTask(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
...MOCK_TASK_FULL,
|
||||
id: 'KIN-015',
|
||||
project_id: 'KIN',
|
||||
title: 'Pending Task',
|
||||
status: 'pending',
|
||||
priority: 5,
|
||||
brief: { text: 'Описание задачи', route_type: 'feature' },
|
||||
execution_mode: null,
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.mocked(api.patchTask).mockReset()
|
||||
})
|
||||
|
||||
it('Кнопка Edit видна для задачи со статусом pending', async () => {
|
||||
vi.mocked(api.taskFull).mockResolvedValue(makePendingTask() as any)
|
||||
const router = makeRouter()
|
||||
await router.push('/task/KIN-015')
|
||||
|
||||
const wrapper = mount(TaskDetail, {
|
||||
props: { id: 'KIN-015' },
|
||||
global: { plugins: [router] },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const editBtn = wrapper.findAll('button').find(b => b.text().includes('Edit'))
|
||||
expect(editBtn?.exists(), 'Кнопка Edit должна быть видна для pending').toBe(true)
|
||||
})
|
||||
|
||||
it('Кнопка Edit скрыта для статуса in_progress', async () => {
|
||||
vi.mocked(api.taskFull).mockResolvedValue(makePendingTask({ status: 'in_progress' }) as any)
|
||||
const router = makeRouter()
|
||||
await router.push('/task/KIN-015')
|
||||
|
||||
const wrapper = mount(TaskDetail, {
|
||||
props: { id: 'KIN-015' },
|
||||
global: { plugins: [router] },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const editBtn = wrapper.findAll('button').find(b => b.text().includes('Edit'))
|
||||
expect(editBtn?.exists(), 'Кнопка Edit не должна быть видна для in_progress').toBe(false)
|
||||
})
|
||||
|
||||
it('Кнопка Edit скрыта для статуса done', async () => {
|
||||
vi.mocked(api.taskFull).mockResolvedValue(makePendingTask({ status: 'done' }) as any)
|
||||
const router = makeRouter()
|
||||
await router.push('/task/KIN-015')
|
||||
|
||||
const wrapper = mount(TaskDetail, {
|
||||
props: { id: 'KIN-015' },
|
||||
global: { plugins: [router] },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const editBtn = wrapper.findAll('button').find(b => b.text().includes('Edit'))
|
||||
expect(editBtn?.exists(), 'Кнопка Edit не должна быть видна для done').toBe(false)
|
||||
})
|
||||
|
||||
it('Клик по Edit открывает форму с заполненным заголовком задачи', async () => {
|
||||
vi.mocked(api.taskFull).mockResolvedValue(makePendingTask() as any)
|
||||
const router = makeRouter()
|
||||
await router.push('/task/KIN-015')
|
||||
|
||||
const wrapper = mount(TaskDetail, {
|
||||
props: { id: 'KIN-015' },
|
||||
global: { plugins: [router] },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const editBtn = wrapper.findAll('button').find(b => b.text().includes('Edit'))
|
||||
await editBtn!.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
// Модал открыт — поле title (input без type) содержит текущий заголовок
|
||||
const titleInput = wrapper.find('input:not([type])')
|
||||
expect(titleInput.exists(), 'Поле Title должно быть видно в модале').toBe(true)
|
||||
expect((titleInput.element as HTMLInputElement).value).toBe('Pending Task')
|
||||
})
|
||||
|
||||
it('saveEdit вызывает patchTask только с изменёнными полями (только title)', async () => {
|
||||
vi.mocked(api.taskFull).mockResolvedValue(makePendingTask() as any)
|
||||
vi.mocked(api.patchTask).mockResolvedValue(makePendingTask({ title: 'Новый заголовок' }) as any)
|
||||
const router = makeRouter()
|
||||
await router.push('/task/KIN-015')
|
||||
|
||||
const wrapper = mount(TaskDetail, {
|
||||
props: { id: 'KIN-015' },
|
||||
global: { plugins: [router] },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const editBtn = wrapper.findAll('button').find(b => b.text().includes('Edit'))
|
||||
await editBtn!.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
// Меняем только title
|
||||
const titleInput = wrapper.find('input:not([type])')
|
||||
await titleInput.setValue('Новый заголовок')
|
||||
|
||||
const saveBtn = wrapper.findAll('button').find(b => b.text().includes('Save'))
|
||||
await saveBtn!.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.patchTask).toHaveBeenCalledWith('KIN-015', { title: 'Новый заголовок' })
|
||||
})
|
||||
|
||||
it('saveEdit не вызывает patchTask если данные не изменились', async () => {
|
||||
vi.mocked(api.taskFull).mockResolvedValue(makePendingTask() as any)
|
||||
const router = makeRouter()
|
||||
await router.push('/task/KIN-015')
|
||||
|
||||
const wrapper = mount(TaskDetail, {
|
||||
props: { id: 'KIN-015' },
|
||||
global: { plugins: [router] },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
// Открываем модал без изменений
|
||||
const editBtn = wrapper.findAll('button').find(b => b.text().includes('Edit'))
|
||||
await editBtn!.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
// Сохраняем без изменений — должен тихо закрыться без API-вызова
|
||||
const saveBtn = wrapper.findAll('button').find(b => b.text().includes('Save'))
|
||||
await saveBtn!.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.patchTask, 'patchTask не должен вызываться при пустом diff').not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('После успешного сохранения модал закрывается', async () => {
|
||||
vi.mocked(api.taskFull).mockResolvedValue(makePendingTask() as any)
|
||||
vi.mocked(api.patchTask).mockResolvedValue(makePendingTask({ title: 'Обновлённый заголовок' }) as any)
|
||||
const router = makeRouter()
|
||||
await router.push('/task/KIN-015')
|
||||
|
||||
const wrapper = mount(TaskDetail, {
|
||||
props: { id: 'KIN-015' },
|
||||
global: { plugins: [router] },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const editBtn = wrapper.findAll('button').find(b => b.text().includes('Edit'))
|
||||
await editBtn!.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
const titleInput = wrapper.find('input:not([type])')
|
||||
await titleInput.setValue('Обновлённый заголовок')
|
||||
|
||||
const saveBtn = wrapper.findAll('button').find(b => b.text().includes('Save'))
|
||||
await saveBtn!.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
// Модал закрыт — форма с title-input больше не в DOM
|
||||
expect(wrapper.find('input:not([type])').exists(), 'Форма должна закрыться после сохранения').toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue