From a80679ae720ace8e9ed2f1d0d22e614b5702d50d Mon Sep 17 00:00:00 2001 From: Gros Frumos Date: Mon, 16 Mar 2026 11:08:02 +0200 Subject: [PATCH] =?UTF-8?q?kin:=20KIN-077=20=D0=9D=D0=B0=D0=B6=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B8=20Review=20?= =?UTF-8?q?--=20Auto=20=D0=BF=D0=BE=20=D0=BF=D1=80=D0=B5=D0=B6=D0=BD=D0=B5?= =?UTF-8?q?=D0=BC=D1=83=20=D0=BF=D1=80=D0=B8=D0=B2=D0=BE=D0=B4=D0=B8=D1=82?= =?UTF-8?q?=20=D0=BA=20400=20Bad=20Request?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../execution-mode-unification.test.ts | 162 +++++++++++++++++- 1 file changed, 159 insertions(+), 3 deletions(-) diff --git a/web/frontend/src/__tests__/execution-mode-unification.test.ts b/web/frontend/src/__tests__/execution-mode-unification.test.ts index 3beec72..6b526cf 100644 --- a/web/frontend/src/__tests__/execution-mode-unification.test.ts +++ b/web/frontend/src/__tests__/execution-mode-unification.test.ts @@ -144,7 +144,7 @@ describe('KIN-FIX-002: execution_mode унификация на "auto_complete"' const router = makeRouter() await router.push('/task/KIN-001') - const wrapper = mount(TaskDetail, { + mount(TaskDetail, { props: { id: 'KIN-001' }, global: { plugins: [router] }, }) @@ -191,7 +191,7 @@ describe('KIN-FIX-002: execution_mode унификация на "auto_complete"' const router = makeRouter() await router.push('/project/KIN') - const wrapper = mount(ProjectView, { + mount(ProjectView, { props: { id: 'KIN' }, global: { plugins: [router] }, }) @@ -278,7 +278,7 @@ describe('KIN-FIX-002: execution_mode унификация на "auto_complete"' const router = makeRouter() await router.push('/task/KIN-001') - const wrapper = mount(TaskDetail, { + mount(TaskDetail, { props: { id: 'KIN-001' }, global: { plugins: [router] }, }) @@ -291,3 +291,159 @@ describe('KIN-FIX-002: execution_mode унификация на "auto_complete"' }) }) }) + +describe('KIN-077: кнопка Review/Auto — regression (400 Bad Request fix)', () => { + describe('ProjectView — patchProject вызывается с корректным enum-значением', () => { + it('при переключении review→auto отправляет "auto_complete", не "auto"', async () => { + const projectReview = { ...MOCK_PROJECT, execution_mode: 'review' } + vi.mocked(api.project).mockResolvedValue(projectReview as any) + 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('Review') || b.text().includes('Auto') + ) + expect(toggleBtn, 'кнопка тоггла должна быть найдена').toBeDefined() + + await toggleBtn!.trigger('click') + await flushPromises() + + // Главная проверка: patchProject вызван с 'auto_complete', не 'auto' (причина 400) + expect(vi.mocked(api.patchProject)).toHaveBeenCalledWith('KIN', { + execution_mode: 'auto_complete', + }) + const callArg = vi.mocked(api.patchProject).mock.calls[0][1] as { execution_mode: string } + expect(callArg.execution_mode).not.toBe('auto') + }) + + it('при переключении auto→review отправляет "review"', async () => { + const projectAuto = { ...MOCK_PROJECT, execution_mode: 'auto_complete' } + vi.mocked(api.project).mockResolvedValue(projectAuto as any) + vi.mocked(api.patchProject).mockResolvedValue({ execution_mode: 'review' } 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') + ) + expect(toggleBtn).toBeDefined() + + await toggleBtn!.trigger('click') + await flushPromises() + + expect(vi.mocked(api.patchProject)).toHaveBeenCalledWith('KIN', { + execution_mode: 'review', + }) + }) + }) + + describe('ProjectView — кнопка отображает актуальный режим', () => { + it('когда проект в режиме "review" — кнопка показывает "Review"', async () => { + const projectReview = { ...MOCK_PROJECT, execution_mode: 'review' } + vi.mocked(api.project).mockResolvedValue(projectReview 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('Review') || b.text().includes('Auto') + ) + expect(toggleBtn).toBeDefined() + expect(toggleBtn!.text()).toContain('Review') + }) + + it('когда проект в режиме "auto_complete" — кнопка показывает "Auto"', async () => { + const projectAuto = { ...MOCK_PROJECT, execution_mode: 'auto_complete' } + vi.mocked(api.project).mockResolvedValue(projectAuto 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') + ) + expect(toggleBtn).toBeDefined() + expect(toggleBtn!.text()).toContain('Auto') + }) + + it('после клика review→auto кнопка меняет текст на "Auto"', async () => { + const projectReview = { ...MOCK_PROJECT, execution_mode: 'review' } + vi.mocked(api.project).mockResolvedValue(projectReview as any) + 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 findToggleBtn = () => + wrapper.findAll('button').find(b => b.text().includes('Auto') || b.text().includes('Review')) + + expect(findToggleBtn()!.text()).toContain('Review') + + await findToggleBtn()!.trigger('click') + await flushPromises() + + expect(findToggleBtn()!.text()).toContain('Auto') + }) + + it('двойной клик возвращает кнопку обратно в "Review"', async () => { + const projectReview = { ...MOCK_PROJECT, execution_mode: 'review' } + vi.mocked(api.project).mockResolvedValue(projectReview as any) + vi.mocked(api.patchProject) + .mockResolvedValueOnce({ execution_mode: 'auto_complete' } as any) + .mockResolvedValueOnce({ execution_mode: 'review' } as any) + + const router = makeRouter() + await router.push('/project/KIN') + + const wrapper = mount(ProjectView, { + props: { id: 'KIN' }, + global: { plugins: [router] }, + }) + await flushPromises() + + const findToggleBtn = () => + wrapper.findAll('button').find(b => b.text().includes('Auto') || b.text().includes('Review')) + + await findToggleBtn()!.trigger('click') + await flushPromises() + expect(findToggleBtn()!.text()).toContain('Auto') + + await findToggleBtn()!.trigger('click') + await flushPromises() + expect(findToggleBtn()!.text()).toContain('Review') + }) + }) +})