import { describe, it, expect, vi, beforeEach } from 'vitest'; import { mount, flushPromises } from '@vue/test-utils'; import { createPinia, setActivePinia } from 'pinia'; import { createVuetify } from 'vuetify'; import axios from 'axios'; vi.mock('axios'); import ProjectsView from '../../resources/js/views/ProjectsView.vue'; /** * Инцидент 14.07.2026: клиент ЧЕТЫРЕ раза подряд пытался удалить проект из меню карточки * и каждый раз получал молчание — сервер отвечал «Нельзя удалить проект: по нему есть * сделки. Поставьте приём на паузу», а фронт ронял этот ответ в никуда (store.del без * catch, глобального перехватчика нет). Человек так и ушёл, ничего не поняв. * * Теперь причина показывается прямо там, где он нажал, и предлагается выход — пауза. */ const BLOCKED_MESSAGE = 'Нельзя удалить проект: по нему есть сделки. Поставьте приём на паузу, чтобы скрыть проект из работы.'; const factory = () => mount(ProjectsView, { global: { plugins: [createVuetify()], stubs: { VDialog: { template: '
', props: ['modelValue'], }, NewProjectDialog: true, EditProjectDialog: true, }, }, }); function mockProjects() { (axios.get as unknown as ReturnType).mockResolvedValue({ data: { data: [ { id: 46, name: 'дентал', signal_type: 'call', signal_identifier: '79221364286', daily_limit_target: 15, delivered_today: 0, is_active: false, sync_status: 'ok', }, ], meta: { total: 1, current_page: 1, per_page: 20 }, }, }); } function mockDeleteBlocked() { (axios.delete as unknown as ReturnType).mockRejectedValue({ response: { status: 422, data: { errors: { project: [BLOCKED_MESSAGE] } } }, }); } describe('ProjectsView — удаление проекта со сделками', () => { beforeEach(() => { setActivePinia(createPinia()); vi.clearAllMocks(); }); it('показывает причину отказа, а не молчит', async () => { mockProjects(); mockDeleteBlocked(); const wrapper = factory(); await flushPromises(); wrapper.findComponent({ name: 'ProjectCard' }).vm.$emit('delete', { id: 46, name: 'дентал', is_active: false }); await flushPromises(); expect(wrapper.text()).toContain('по нему есть сделки'); }); it('предлагает выход — кнопку «Поставить на паузу»', async () => { mockProjects(); mockDeleteBlocked(); (axios.patch as unknown as ReturnType).mockResolvedValue({ data: { data: {} } }); const wrapper = factory(); await flushPromises(); wrapper.findComponent({ name: 'ProjectCard' }).vm.$emit('delete', { id: 46, name: 'дентал', is_active: true }); await flushPromises(); const pauseBtn = wrapper.findAll('button').find((b) => b.text().includes('Поставить на паузу')); expect(pauseBtn).toBeDefined(); await pauseBtn!.trigger('click'); await flushPromises(); expect(axios.patch).toHaveBeenCalledWith('/api/projects/46/toggle-active', { is_active: false }); }); });