import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest'; import { mount, flushPromises } from '@vue/test-utils'; import { createPinia, setActivePinia } from 'pinia'; import { createVuetify } from 'vuetify'; vi.mock('axios'); vi.mock('../../resources/js/api/client', () => ({ apiClient: { get: vi.fn(), post: vi.fn().mockResolvedValue({ data: {} }), patch: vi.fn().mockResolvedValue({ data: {} }), }, ensureCsrfCookie: vi.fn().mockResolvedValue(undefined), extractErrorMessage: vi.fn(() => 'Произошла ошибка.'), })); // Косяк 04 / вариант C: управляем реквизитами по-тестово. vi.mock('../../resources/js/api/requisites', () => ({ getRequisites: vi.fn(), updateRequisites: vi.fn(), })); import { getRequisites, updateRequisites } from '../../resources/js/api/requisites'; import NewProjectDialog from '../../resources/js/views/projects/NewProjectDialog.vue'; const factory = () => mount(NewProjectDialog, { props: { modelValue: true, mode: 'create' as const }, global: { plugins: [createVuetify()], stubs: { VDialog: { template: '
', props: ['modelValue'], }, }, }, }); const lightComplete = { subject_type: 'individual', contact_name: 'Иван', contact_phone: '+79161234567', inn: null, }; beforeEach(() => { setActivePinia(createPinia()); vi.clearAllMocks(); (updateRequisites as unknown as Mock).mockResolvedValue({}); }); describe('NewProjectDialog — косяк 04 / вариант C: реквизиты первым шагом', () => { it('новый клиент без реквизитов видит шаг реквизитов, а не пустую форму проекта', async () => { (getRequisites as unknown as Mock).mockResolvedValue(null); const w = factory(); await flushPromises(); expect(w.find('[data-testid="req-step"]').exists()).toBe(true); expect(w.find('[data-testid="req-contact-phone"]').exists()).toBe(true); // форма проекта (вкладки источника) на шаге 1 ещё не показана expect(w.find('[data-testid="np-lead-banner"]').exists()).toBe(false); }); it('клиент с заполненными реквизитами сразу попадает на форму проекта', async () => { (getRequisites as unknown as Mock).mockResolvedValue(lightComplete); const w = factory(); await flushPromises(); expect(w.find('[data-testid="req-step"]').exists()).toBe(false); expect(w.find('[data-testid="np-lead-banner"]').exists()).toBe(true); }); it('сохранение реквизитов переводит на шаг проекта (без ухода со страницы)', async () => { (getRequisites as unknown as Mock).mockResolvedValue(null); const w = factory(); await flushPromises(); expect(w.find('[data-testid="req-step"]').exists()).toBe(true); const vm = w.vm as unknown as { reqForm: { subject_type: string; contact_name: string; contact_phone: string; inn: string }; saveRequisites: () => Promise; }; vm.reqForm.subject_type = 'individual'; vm.reqForm.contact_name = 'Иван Петров'; vm.reqForm.contact_phone = '+7 916 123 45 67'; await vm.saveRequisites(); await flushPromises(); expect(updateRequisites).toHaveBeenCalledTimes(1); expect(w.find('[data-testid="req-step"]').exists()).toBe(false); expect(w.find('[data-testid="np-lead-banner"]').exists()).toBe(true); }); it('ошибка валидации реквизитов остаётся на шаге 1 и не теряет форму проекта', async () => { (getRequisites as unknown as Mock).mockResolvedValue(null); (updateRequisites as unknown as Mock).mockRejectedValue({ response: { status: 422, data: { errors: { contact_phone: ['Некорректный телефон.'] } } }, }); const w = factory(); await flushPromises(); const vm = w.vm as unknown as { reqForm: { subject_type: string; contact_name: string; contact_phone: string; inn: string }; saveRequisites: () => Promise; }; vm.reqForm.subject_type = 'individual'; vm.reqForm.contact_name = 'Иван'; vm.reqForm.contact_phone = 'мусор'; await vm.saveRequisites(); await flushPromises(); // остались на шаге 1, ошибка показана expect(w.find('[data-testid="req-step"]').exists()).toBe(true); expect(w.text()).toContain('Некорректный телефон'); }); });