129 lines
4.8 KiB
TypeScript
129 lines
4.8 KiB
TypeScript
|
|
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: '<div class="dialog-stub" v-if="modelValue"><slot /></div>',
|
||
|
|
props: ['modelValue'],
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
type Vm = {
|
||
|
|
submit: () => Promise<void>;
|
||
|
|
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('Что-то не так с этим полем.');
|
||
|
|
});
|
||
|
|
});
|