diff --git a/web/frontend/src/__tests__/gate-blocked-review-buttons.test.ts b/web/frontend/src/__tests__/gate-blocked-review-buttons.test.ts deleted file mode 100644 index 8c66803..0000000 --- a/web/frontend/src/__tests__/gate-blocked-review-buttons.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -/** - * KIN-134: Кнопки Approve/Revise/Reject видны для задач со статусом 'blocked' - * - * Когда gate-агент блокирует задачу в auto_complete-режиме: - * - task.status → 'blocked' - * - task.execution_mode остаётся 'auto_complete' (pipeline не сбрасывает его) - * Раньше кнопки проверяли только status === 'review' && !autoMode, - * поэтому для blocked-задач кнопки не рендерились вообще. - */ - -import { describe, it, expect, vi, beforeEach } from 'vitest' -import { mount, flushPromises } from '@vue/test-utils' -import { createRouter, createMemoryHistory } from 'vue-router' -import TaskDetail from '../views/TaskDetail.vue' - -vi.mock('../api', () => ({ - api: { - taskFull: vi.fn(), - patchTask: vi.fn(), - runTask: vi.fn(), - approveTask: vi.fn(), - rejectTask: vi.fn(), - reviseTask: vi.fn(), - followupTask: vi.fn(), - deployProject: vi.fn(), - getAttachments: vi.fn(), - resolveAction: vi.fn(), - }, -})) - -import { api } from '../api' - -const localStorageMock = (() => { - let store: Record = {} - return { - getItem: (k: string) => store[k] ?? null, - setItem: (k: string, v: string) => { store[k] = v }, - removeItem: (k: string) => { delete store[k] }, - clear: () => { store = {} }, - } -})() -Object.defineProperty(globalThis, 'localStorage', { value: localStorageMock, configurable: true }) - -const Stub = { template: '
' } - -function makeRouter() { - return createRouter({ - history: createMemoryHistory(), - routes: [ - { path: '/', component: Stub }, - { path: '/task/:id', component: TaskDetail, props: true }, - ], - }) -} - -function makeBlockedTask(execution_mode: string | null = 'auto_complete') { - return { - id: 'KIN-134', - project_id: 'KIN', - title: 'Gate-blocked task', - status: 'blocked', - priority: 5, - assigned_role: 'reviewer', - parent_task_id: null, - brief: null, - spec: null, - execution_mode, - blocked_reason: 'Reviewer: задача не соответствует критериям', - dangerously_skipped: null, - category: null, - acceptance_criteria: null, - created_at: '2026-01-01', - updated_at: '2026-01-01', - pipeline_steps: [], - related_decisions: [], - pending_actions: [], - pipeline_id: null, - project_deploy_command: null, - project_deploy_runtime: null, - } -} - -beforeEach(() => { - localStorageMock.clear() - vi.clearAllMocks() - vi.mocked(api.getAttachments).mockResolvedValue([]) -}) - -describe('KIN-134: кнопки Approve/Revise/Reject для status=blocked', () => { - it('Approve-кнопка видна когда статус blocked и execution_mode=auto_complete', async () => { - vi.mocked(api.taskFull).mockResolvedValue(makeBlockedTask('auto_complete') as any) - const router = makeRouter() - await router.push('/task/KIN-134') - const wrapper = mount(TaskDetail, { - props: { id: 'KIN-134' }, - global: { plugins: [router] }, - }) - await flushPromises() - - const buttons = wrapper.findAll('button') - const approveBtn = buttons.find(b => b.text().includes('Approve')) - expect(approveBtn?.exists(), 'Кнопка Approve должна быть видна для blocked-задачи').toBe(true) - }) - - it('Revise-кнопка видна когда статус blocked и execution_mode=auto_complete', async () => { - vi.mocked(api.taskFull).mockResolvedValue(makeBlockedTask('auto_complete') as any) - const router = makeRouter() - await router.push('/task/KIN-134') - const wrapper = mount(TaskDetail, { - props: { id: 'KIN-134' }, - global: { plugins: [router] }, - }) - await flushPromises() - - const buttons = wrapper.findAll('button') - const reviseBtn = buttons.find(b => b.text().includes('Revise')) - expect(reviseBtn?.exists(), 'Кнопка Revise должна быть видна для blocked-задачи').toBe(true) - }) - - it('Reject-кнопка видна когда статус blocked и execution_mode=auto_complete', async () => { - vi.mocked(api.taskFull).mockResolvedValue(makeBlockedTask('auto_complete') as any) - const router = makeRouter() - await router.push('/task/KIN-134') - const wrapper = mount(TaskDetail, { - props: { id: 'KIN-134' }, - global: { plugins: [router] }, - }) - await flushPromises() - - const buttons = wrapper.findAll('button') - const rejectBtn = buttons.find(b => b.text().includes('Reject')) - expect(rejectBtn?.exists(), 'Кнопка Reject должна быть видна для blocked-задачи').toBe(true) - }) - - it('Все три кнопки видны для blocked-задачи без execution_mode', async () => { - vi.mocked(api.taskFull).mockResolvedValue(makeBlockedTask(null) as any) - const router = makeRouter() - await router.push('/task/KIN-134') - const wrapper = mount(TaskDetail, { - props: { id: 'KIN-134' }, - global: { plugins: [router] }, - }) - await flushPromises() - - const buttons = wrapper.findAll('button') - const texts = buttons.map(b => b.text()) - expect(texts.some(t => t.includes('Approve')), 'Approve должен быть').toBe(true) - expect(texts.some(t => t.includes('Revise')), 'Revise должен быть').toBe(true) - expect(texts.some(t => t.includes('Reject')), 'Reject должен быть').toBe(true) - }) -}) diff --git a/web/frontend/src/views/TaskDetail.vue b/web/frontend/src/views/TaskDetail.vue index 359d9ad..59025aa 100644 --- a/web/frontend/src/views/TaskDetail.vue +++ b/web/frontend/src/views/TaskDetail.vue @@ -571,17 +571,17 @@ async function saveEdit() { {{ t('taskDetail.autopilot_active') }}
- - -