import { describe, it, expect, vi, beforeEach } 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: {
post: vi.fn(),
patch: vi.fn(),
},
ensureCsrfCookie: vi.fn().mockResolvedValue(undefined),
extractErrorMessage: vi.fn(() => 'Произошла ошибка.'),
}));
vi.mock('../../resources/js/api/requisites', () => ({
getRequisites: vi.fn().mockResolvedValue({
subject_type: 'individual',
contact_name: 'Денис',
contact_phone: '+79221364286',
inn: null,
}),
updateRequisites: vi.fn().mockResolvedValue({}),
}));
import { apiClient } from '../../resources/js/api/client';
import NewProjectDialog from '../../resources/js/views/projects/NewProjectDialog.vue';
/**
* Инцидент 14.07.2026: клиент дважды нажал «Создать» — и НИЧЕГО не произошло. Сервер
* отказал по полю delivery_days_mask (человек снял все дни недели), а в форме это поле
* не привязано ни к какому месту показа ошибки: ответ раскладывался в errors и исчезал.
* Клиент тыкал вслепую 5 минут.
*
* Правило: любая причина отказа, которой негде показаться у поля, обязана появиться
* общим баннером. Форма не имеет права молчать.
*/
const factory = () =>
mount(NewProjectDialog, {
props: { modelValue: true, mode: 'create' as const },
global: {
plugins: [createVuetify(), createPinia()],
stubs: {
VDialog: {
template: '
',
props: ['modelValue'],
},
},
},
});
type Vm = {
submit: () => Promise;
confirmVsyaRf: () => void;
form: { name: string; signal_identifier: string };
};
beforeEach(() => {
setActivePinia(createPinia());
vi.clearAllMocks();
});
describe('NewProjectDialog — форма не молчит при отказе сервера', () => {
it('показывает причину отказа по дням недели', async () => {
vi.mocked(apiClient.post).mockRejectedValue({
response: {
status: 422,
data: {
message: 'Ошибка',
errors: { delivery_days_mask: ['Выберите хотя бы один день недели приёма.'] },
},
},
});
const wrapper = factory();
await flushPromises();
const vm = wrapper.vm as unknown as Vm;
vm.confirmVsyaRf();
vm.form.name = 'Сибдент тел';
vm.form.signal_identifier = 'sibdent.ru';
await vm.submit();
await flushPromises();
expect(wrapper.text()).toContain('Выберите хотя бы один день недели приёма.');
});
it('не отправляет проект без единого дня недели — объясняет сразу, не гоняя на сервер', async () => {
const wrapper = factory();
await flushPromises();
const vm = wrapper.vm as unknown as Vm & { selectedDays: number[] };
vm.confirmVsyaRf();
vm.form.name = 'Сибдент тел';
vm.form.signal_identifier = 'sibdent.ru';
vm.selectedDays.splice(0, vm.selectedDays.length); // человек снял все дни
await vm.submit();
await flushPromises();
expect(apiClient.post).not.toHaveBeenCalled();
expect(wrapper.text()).toContain('день недели');
});
it('показывает причину отказа по полю, которого нет в форме', async () => {
vi.mocked(apiClient.post).mockRejectedValue({
response: {
status: 422,
data: {
message: 'Ошибка',
errors: { some_unknown_field: ['Что-то не так с этим полем.'] },
},
},
});
const wrapper = factory();
await flushPromises();
const vm = wrapper.vm as unknown as Vm;
vm.confirmVsyaRf();
vm.form.name = 'Сибдент тел';
vm.form.signal_identifier = 'sibdent.ru';
await vm.submit();
await flushPromises();
expect(wrapper.text()).toContain('Что-то не так с этим полем.');
});
});