c5ca3c4387
Промт называл один тест, который печатал сетевую ошибку. Сторож нашёл 93 теста в 14 файлах: остальные 92 глушились чужими catch внутри рабочего кода и были невидимы вообще. Запрета на выход наружу в тестах интерфейса не было ни одного за всю историю — тот же класс, что закрыли 01.08 на PHP. Сторож в app/tests/Frontend/setup.ts делает две вещи, и обе нужны: обрывает запрос и запоминает адрес, роняя тест в afterEach. Без второй половины код, который глушит отказ своим catch, снова всё прячет. Там же поимённо заглушены четыре фоновые точки оболочки — счётчик сделок, состояние автоподбора, статус баланса, отметка о посещении. Их не спрашивает ни один тест разметки, а отказ у всех четырёх и так проглатывается. Остальные семь файлов глушат свои вызовы у себя; общий ответ статусов воронки лежит в tests/Frontend/support/fixtures.ts. Заглушки повторяют то состояние, которое тесты видели при отказе сети, поэтому ни одна проверка не переписывалась. track.spec.ts снимает общую заглушку через vi.unmock — он проверяет сам заглушенный код. Приёмка: сторож проверен красным трижды — обычный запрос, запрос с проглоченной ошибкой, fetch. Полный набор интерфейса 233 файла / 1750 тестов / 3 пропущено / 0 падений / 0 ошибок. Линтер интерфейса 0, проверка типов 5 — те же унаследованные, что были до смены. Рабочий код не тронут. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
71 lines
3.6 KiB
TypeScript
71 lines
3.6 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { mount } from '@vue/test-utils';
|
|
import { createPinia, setActivePinia } from 'pinia';
|
|
import { createVuetify } from 'vuetify';
|
|
import { reactive, ref } from 'vue';
|
|
|
|
// Мокаем только сетевые вызовы; autopodborErrorMessage (разбор причины) — настоящая,
|
|
// именно её работу мы и проверяем.
|
|
vi.mock('../../resources/js/api/autopodbor', async (importOriginal) => ({
|
|
...(await importOriginal<typeof import('../../resources/js/api/autopodbor')>()),
|
|
startStudyBatch: vi.fn(),
|
|
}));
|
|
import FieldWorkspaceScreen from '../../resources/js/views/autopodbor/screens/FieldWorkspaceScreen.vue';
|
|
import { useAutopodborStore } from '../../resources/js/stores/autopodborStore';
|
|
|
|
/**
|
|
* Инцидент 14.07.2026: клиент трижды нажал «Добавить в поле» и трижды увидел одинаковое
|
|
* «Не удалось добавить конкурента» — форма ловила ошибку голым catch {} и выбрасывала
|
|
* объяснение сервера в мусор. Человек не понял, что чинить, и бросил функцию совсем.
|
|
*
|
|
* Теперь форма обязана показать ПРИЧИНУ, присланную сервером.
|
|
*/
|
|
const vuetify = createVuetify();
|
|
|
|
function makeNav() {
|
|
return { go: vi.fn(), ctx: reactive({ competitorId: null }), screen: ref('field') };
|
|
}
|
|
|
|
function mountWs(nav: unknown) {
|
|
return mount(FieldWorkspaceScreen, { global: { plugins: [vuetify], provide: { autopodborNav: nav } } });
|
|
}
|
|
|
|
describe('FieldWorkspaceScreen — «Добавить вручную» объясняет отказ', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia());
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('показывает причину от сервера, а не безликое «Не удалось»', async () => {
|
|
const store = useAutopodborStore();
|
|
vi.spyOn(store, 'loadField').mockImplementation(async () => {
|
|
store.field = [] as never;
|
|
});
|
|
// Экран при открытии тянет ДВА списка сразу (см. reload() в FieldWorkspaceScreen).
|
|
// Без этой заглушки тест физически слал запрос наружу на /api/autopodbor/proposals
|
|
// и всё равно был зелёным — ошибка вылетала мимо проверок. Ловит сторож сети
|
|
// в tests/Frontend/setup.ts.
|
|
vi.spyOn(store, 'loadProposalGroups').mockResolvedValue(undefined);
|
|
vi.spyOn(store, 'addFieldCompetitor').mockRejectedValue({
|
|
isAxiosError: true, // как настоящая ошибка axios
|
|
response: {
|
|
status: 422,
|
|
data: { errors: { 'elements.0.cards.0': ['Ссылка на карточку слишком длинная — максимум 2000 символов.'] } },
|
|
},
|
|
});
|
|
|
|
const w = mountWs(makeNav());
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
|
|
const vm = w.vm as unknown as {
|
|
addComp: { open: boolean; name: string; note: string; elements: unknown[] };
|
|
saveAdd: () => Promise<void>;
|
|
};
|
|
vm.addComp.name = 'Сиб Дент';
|
|
await vm.saveAdd();
|
|
|
|
expect(vm.addComp.note).toContain('слишком длинная');
|
|
expect(vm.addComp.note).not.toBe('Не удалось добавить конкурента.');
|
|
});
|
|
});
|