kin: KIN-FIX-002 Унифицировать localStorage значения execution_mode с 'auto_complete'
This commit is contained in:
parent
7f8e0e2238
commit
9764d1b414
2 changed files with 295 additions and 1 deletions
293
web/frontend/src/__tests__/execution-mode-unification.test.ts
Normal file
293
web/frontend/src/__tests__/execution-mode-unification.test.ts
Normal file
|
|
@ -0,0 +1,293 @@
|
||||||
|
/**
|
||||||
|
* KIN-FIX-002: Унифицировать localStorage значения execution_mode с 'auto_complete'
|
||||||
|
*
|
||||||
|
* Acceptance Criteria:
|
||||||
|
* 1. Строки 46 и 53 в TaskDetail.vue содержат 'auto_complete' в localStorage операциях
|
||||||
|
* 2. Все вхождения режимов execution_mode используют 'auto_complete', не 'auto'
|
||||||
|
* 3. Grep по всему frontend не находит standalone 'auto' как значение execution_mode
|
||||||
|
* 4. Существующие тесты filter-persistence.test.ts пройдены успешно
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||||
|
import { mount, flushPromises } from '@vue/test-utils'
|
||||||
|
import { createRouter, createMemoryHistory } from 'vue-router'
|
||||||
|
import ProjectView from '../views/ProjectView.vue'
|
||||||
|
import TaskDetail from '../views/TaskDetail.vue'
|
||||||
|
|
||||||
|
// Mock api
|
||||||
|
vi.mock('../api', () => ({
|
||||||
|
api: {
|
||||||
|
project: vi.fn(),
|
||||||
|
taskFull: vi.fn(),
|
||||||
|
patchTask: vi.fn(),
|
||||||
|
patchProject: vi.fn(),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
import { api } from '../api'
|
||||||
|
|
||||||
|
const Stub = { template: '<div />' }
|
||||||
|
|
||||||
|
const MOCK_PROJECT = {
|
||||||
|
id: 'KIN',
|
||||||
|
name: 'Kin',
|
||||||
|
path: '/projects/kin',
|
||||||
|
status: 'active',
|
||||||
|
priority: 5,
|
||||||
|
tech_stack: ['python', 'vue'],
|
||||||
|
created_at: '2024-01-01',
|
||||||
|
total_tasks: 1,
|
||||||
|
done_tasks: 0,
|
||||||
|
active_tasks: 1,
|
||||||
|
blocked_tasks: 0,
|
||||||
|
review_tasks: 0,
|
||||||
|
tasks: [],
|
||||||
|
decisions: [],
|
||||||
|
modules: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
const localStorageMock = (() => {
|
||||||
|
let store: Record<string, string> = {}
|
||||||
|
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 })
|
||||||
|
|
||||||
|
function makeRouter() {
|
||||||
|
return createRouter({
|
||||||
|
history: createMemoryHistory(),
|
||||||
|
routes: [
|
||||||
|
{ path: '/', component: Stub },
|
||||||
|
{ path: '/project/:id', component: ProjectView, props: true },
|
||||||
|
{ path: '/task/:id', component: TaskDetail, props: true },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
localStorageMock.clear()
|
||||||
|
vi.mocked(api.project).mockResolvedValue(MOCK_PROJECT as any)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('KIN-FIX-002: execution_mode унификация на "auto_complete"', () => {
|
||||||
|
describe('TaskDetail.vue — localStorage операции (lines 46, 53)', () => {
|
||||||
|
it('toggleMode в TaskDetail сохраняет "auto_complete" в localStorage', async () => {
|
||||||
|
const task = {
|
||||||
|
id: 'KIN-001',
|
||||||
|
project_id: 'KIN',
|
||||||
|
title: 'Test Task',
|
||||||
|
status: 'pending',
|
||||||
|
priority: 5,
|
||||||
|
assigned_role: null,
|
||||||
|
parent_task_id: null,
|
||||||
|
brief: null,
|
||||||
|
spec: null,
|
||||||
|
execution_mode: null,
|
||||||
|
created_at: '2024-01-01',
|
||||||
|
updated_at: '2024-01-01',
|
||||||
|
pipeline_steps: [],
|
||||||
|
related_decisions: [],
|
||||||
|
}
|
||||||
|
vi.mocked(api.taskFull).mockResolvedValue(task as any)
|
||||||
|
vi.mocked(api.patchTask).mockResolvedValue({ execution_mode: 'auto_complete' } as any)
|
||||||
|
|
||||||
|
const router = makeRouter()
|
||||||
|
await router.push('/task/KIN-001')
|
||||||
|
|
||||||
|
const wrapper = mount(TaskDetail, {
|
||||||
|
props: { id: 'KIN-001' },
|
||||||
|
global: { plugins: [router] },
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
// Найти и кликнуть кнопку тоггла режима (Auto/Review)
|
||||||
|
const toggleBtn = wrapper.findAll('button').find(b =>
|
||||||
|
b.text().includes('Auto') || b.text().includes('Review')
|
||||||
|
)
|
||||||
|
|
||||||
|
if (toggleBtn) {
|
||||||
|
await toggleBtn.trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
// Проверяем, что localStorage содержит 'auto_complete', не 'auto'
|
||||||
|
const stored = localStorageMock.getItem('kin-mode-KIN')
|
||||||
|
expect(stored, 'localStorage должен содержать "auto_complete"').toBe('auto_complete')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('loadMode в TaskDetail использует "auto_complete" при чтении из localStorage', async () => {
|
||||||
|
// Сначала установим значение в localStorage
|
||||||
|
localStorageMock.setItem('kin-mode-KIN', 'auto_complete')
|
||||||
|
|
||||||
|
const task = {
|
||||||
|
id: 'KIN-001',
|
||||||
|
project_id: 'KIN',
|
||||||
|
title: 'Test Task',
|
||||||
|
status: 'pending',
|
||||||
|
priority: 5,
|
||||||
|
assigned_role: null,
|
||||||
|
parent_task_id: null,
|
||||||
|
brief: null,
|
||||||
|
spec: null,
|
||||||
|
execution_mode: null,
|
||||||
|
created_at: '2024-01-01',
|
||||||
|
updated_at: '2024-01-01',
|
||||||
|
pipeline_steps: [],
|
||||||
|
related_decisions: [],
|
||||||
|
}
|
||||||
|
vi.mocked(api.taskFull).mockResolvedValue(task as any)
|
||||||
|
|
||||||
|
const router = makeRouter()
|
||||||
|
await router.push('/task/KIN-001')
|
||||||
|
|
||||||
|
const wrapper = mount(TaskDetail, {
|
||||||
|
props: { id: 'KIN-001' },
|
||||||
|
global: { plugins: [router] },
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
// Проверяем, что значение из localStorage прочитано как 'auto_complete'
|
||||||
|
const stored = localStorageMock.getItem('kin-mode-KIN')
|
||||||
|
expect(stored).toBe('auto_complete')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('ProjectView.vue — localStorage операции (lines 171, 173, 179, 181, 182)', () => {
|
||||||
|
it('toggleMode в ProjectView сохраняет "auto_complete" в localStorage', async () => {
|
||||||
|
vi.mocked(api.patchProject).mockResolvedValue({ execution_mode: 'auto_complete' } as any)
|
||||||
|
|
||||||
|
const router = makeRouter()
|
||||||
|
await router.push('/project/KIN')
|
||||||
|
|
||||||
|
const wrapper = mount(ProjectView, {
|
||||||
|
props: { id: 'KIN' },
|
||||||
|
global: { plugins: [router] },
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
// Найти и кликнуть кнопку тоггла режима
|
||||||
|
const toggleBtn = wrapper.findAll('button').find(b =>
|
||||||
|
b.text().includes('Auto') || b.text().includes('Review')
|
||||||
|
)
|
||||||
|
|
||||||
|
if (toggleBtn) {
|
||||||
|
await toggleBtn.trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
// Проверяем, что localStorage содержит 'auto_complete', не 'auto'
|
||||||
|
const stored = localStorageMock.getItem('kin-mode-KIN')
|
||||||
|
expect(stored, 'localStorage должен содержать "auto_complete" в ProjectView').toBe('auto_complete')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('loadMode в ProjectView использует "auto_complete" при чтении из localStorage', async () => {
|
||||||
|
// Установим значение в localStorage
|
||||||
|
localStorageMock.setItem('kin-mode-KIN', 'auto_complete')
|
||||||
|
|
||||||
|
const router = makeRouter()
|
||||||
|
await router.push('/project/KIN')
|
||||||
|
|
||||||
|
const wrapper = mount(ProjectView, {
|
||||||
|
props: { id: 'KIN' },
|
||||||
|
global: { plugins: [router] },
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
// Проверяем, что значение из localStorage прочитано корректно
|
||||||
|
const stored = localStorageMock.getItem('kin-mode-KIN')
|
||||||
|
expect(stored).toBe('auto_complete')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Унификация: все значения используют "auto_complete"', () => {
|
||||||
|
it('execution_mode никогда не использует standalone "auto"', async () => {
|
||||||
|
// Проверяем, что при сохранении режима, используется ТОЛЬКО 'auto_complete' или 'review'
|
||||||
|
const task = {
|
||||||
|
id: 'KIN-001',
|
||||||
|
project_id: 'KIN',
|
||||||
|
title: 'Test Task',
|
||||||
|
status: 'pending',
|
||||||
|
priority: 5,
|
||||||
|
assigned_role: null,
|
||||||
|
parent_task_id: null,
|
||||||
|
brief: null,
|
||||||
|
spec: null,
|
||||||
|
execution_mode: 'auto_complete',
|
||||||
|
created_at: '2024-01-01',
|
||||||
|
updated_at: '2024-01-01',
|
||||||
|
pipeline_steps: [],
|
||||||
|
related_decisions: [],
|
||||||
|
}
|
||||||
|
vi.mocked(api.taskFull).mockResolvedValue(task as any)
|
||||||
|
vi.mocked(api.patchTask).mockResolvedValue({ execution_mode: 'review' } as any)
|
||||||
|
|
||||||
|
const router = makeRouter()
|
||||||
|
await router.push('/task/KIN-001')
|
||||||
|
|
||||||
|
const wrapper = mount(TaskDetail, {
|
||||||
|
props: { id: 'KIN-001' },
|
||||||
|
global: { plugins: [router] },
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
// Переключаемся в режим review
|
||||||
|
const toggleBtn = wrapper.findAll('button').find(b =>
|
||||||
|
b.text().includes('Auto') || b.text().includes('Review')
|
||||||
|
)
|
||||||
|
if (toggleBtn) {
|
||||||
|
await toggleBtn.trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
const stored = localStorageMock.getItem('kin-mode-KIN')
|
||||||
|
// Проверяем, что используется только 'auto_complete' или 'review'
|
||||||
|
expect(
|
||||||
|
stored === 'auto_complete' || stored === 'review',
|
||||||
|
`localStorage должен содержать 'auto_complete' или 'review', получено: "${stored}"`
|
||||||
|
).toBe(true)
|
||||||
|
// Проверяем, что НЕ используется 'auto'
|
||||||
|
expect(stored).not.toBe('auto')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Сравнение в коде используется "auto_complete", не "auto"', async () => {
|
||||||
|
// Установим 'auto_complete' и проверим, что компонент корректно определяет режим
|
||||||
|
localStorageMock.setItem('kin-mode-KIN', 'auto_complete')
|
||||||
|
|
||||||
|
const task = {
|
||||||
|
id: 'KIN-001',
|
||||||
|
project_id: 'KIN',
|
||||||
|
title: 'Test Task',
|
||||||
|
status: 'pending',
|
||||||
|
priority: 5,
|
||||||
|
assigned_role: null,
|
||||||
|
parent_task_id: null,
|
||||||
|
brief: null,
|
||||||
|
spec: null,
|
||||||
|
execution_mode: null,
|
||||||
|
created_at: '2024-01-01',
|
||||||
|
updated_at: '2024-01-01',
|
||||||
|
pipeline_steps: [],
|
||||||
|
related_decisions: [],
|
||||||
|
}
|
||||||
|
vi.mocked(api.taskFull).mockResolvedValue(task as any)
|
||||||
|
|
||||||
|
const router = makeRouter()
|
||||||
|
await router.push('/task/KIN-001')
|
||||||
|
|
||||||
|
const wrapper = mount(TaskDetail, {
|
||||||
|
props: { id: 'KIN-001' },
|
||||||
|
global: { plugins: [router] },
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
// После загрузки, компонент должен прочитать 'auto_complete' из localStorage
|
||||||
|
// и корректно применить режим (это видно по наличию или отсутствию кнопок Approve/Reject)
|
||||||
|
const stored = localStorageMock.getItem('kin-mode-KIN')
|
||||||
|
expect(stored).toBe('auto_complete')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -115,7 +115,8 @@ function makeRouter() {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorageMock.clear()
|
localStorageMock.clear()
|
||||||
vi.mocked(api.project).mockResolvedValue(MOCK_PROJECT as any)
|
vi.clearAllMocks()
|
||||||
|
vi.mocked(api.project).mockResolvedValue(JSON.parse(JSON.stringify(MOCK_PROJECT)) as any)
|
||||||
vi.mocked(api.getPhases).mockResolvedValue([])
|
vi.mocked(api.getPhases).mockResolvedValue([])
|
||||||
vi.mocked(api.patchTask).mockResolvedValue(makeTask('KIN-001', 'in_progress') as any)
|
vi.mocked(api.patchTask).mockResolvedValue(makeTask('KIN-001', 'in_progress') as any)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue