Files
portal/app/tests/Frontend/SalesProspectsView.spec.ts
T
Дмитрий 9ee4e9a79f feat(воронка продаж): корзина, дробь у «Отказа», фильтр по датам — экраны
- 12-я колонка «Корзина», результат «В корзину» просит только причину;
- в шапке «Отказа» дробь «4/2» с подсказкой «из них 2 после ручного
  тестирования»; при нуле дробь не рисуется — «69/0» это шум;
- у «Выслано КП» поле даты подписано «если договорились» и необязательно;
- орган фильтра по датам на ОБОИХ экранах, два режима + период.

Приёмка глазами пройдена живьём по всем семи пунктам, включая «Воронку отдела»
(снимки в docs/superpowers/screens/2026-08-01-korzina-filtry/priemka/).
Браузер поймал то, чего не видели тесты: при смене режима оставался прежний
период, и «что менялось за завтра» давало пустую доску — теперь период
возвращается к «Сегодня», на это заведён отдельный тест.

Прогон: сервер 1245/1245, фронт 1567/1567. Журнал схемы — v9.31.
2026-08-01 13:29:10 +03:00

101 lines
3.8 KiB
TypeScript

import { mount, flushPromises } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import { describe, expect, it, vi, beforeEach } from 'vitest';
function card(overrides: Record<string, unknown> = {}) {
return {
id: 1,
sales_user_id: 1,
stage: 'new',
source: 'search',
firm_name: 'ООО Демо',
city: 'Ростов',
phone: null,
site: null,
inn: null,
rating_label: 'горячая',
payload: {},
next_call_at: null,
reason: null,
registered_email: null,
notes: null,
...overrides,
};
}
vi.mock('../../resources/js/api/sales', () => ({
listProspects: vi.fn().mockResolvedValue({ prospects: [], by_stage: {}, stages: [] }),
updateProspect: vi.fn().mockResolvedValue({ id: 1, stage: 'in_work' }),
createProspect: vi.fn().mockResolvedValue({ id: 2, stage: 'new', source: 'manager' }),
extractSalesErrorMessage: () => 'err',
}));
import SalesProspectsView from '../../resources/js/views/sales/SalesProspectsView.vue';
import { listProspects, updateProspect, createProspect } from '../../resources/js/api/sales';
const vuetify = createVuetify();
interface ViewVm {
open: (p: unknown) => Promise<void> | void;
createCandidate: (p: Record<string, unknown>) => Promise<void>;
applyDateFilter: (v: unknown) => Promise<void>;
}
describe('SalesProspectsView', () => {
beforeEach(() => {
vi.clearAllMocks();
(listProspects as ReturnType<typeof vi.fn>).mockResolvedValue({
prospects: [card()],
by_stage: {},
stages: [],
});
});
it('загружает и показывает карточки менеджера', async () => {
const w = mount(SalesProspectsView, { global: { plugins: [vuetify] } });
await flushPromises();
expect(listProspects).toHaveBeenCalledWith(undefined, undefined, undefined, null);
expect(w.text()).toContain('ООО Демо');
});
it('фильтр по датам уходит в запрос и у менеджера тоже', async () => {
const w = mount(SalesProspectsView, { global: { plugins: [vuetify] } });
await flushPromises();
const changed = { mode: 'changed', period: 'today', from: null, to: null };
await (w.vm as unknown as ViewVm).applyDateFilter(changed);
expect(listProspects).toHaveBeenLastCalledWith(undefined, undefined, undefined, changed);
});
it('открытие карточки из «Новые» помечает её взятой в работу (action=opened)', async () => {
const w = mount(SalesProspectsView, { global: { plugins: [vuetify] } });
await flushPromises();
await (w.vm as unknown as ViewVm).open(card({ id: 7, stage: 'new' }));
await flushPromises();
expect(updateProspect).toHaveBeenCalledWith(7, { action: 'opened' });
});
it('открытие карточки НЕ из «Новые» ничего не помечает', async () => {
const w = mount(SalesProspectsView, { global: { plugins: [vuetify] } });
await flushPromises();
await (w.vm as unknown as ViewVm).open(card({ id: 8, stage: 'negotiation' }));
await flushPromises();
expect(updateProspect).not.toHaveBeenCalled();
});
it('создаёт своего кандидата через createProspect', async () => {
const w = mount(SalesProspectsView, { global: { plugins: [vuetify] } });
await flushPromises();
await (w.vm as unknown as ViewVm).createCandidate({ firm_name: 'ООО Своя', city: 'Омск' });
await flushPromises();
expect(createProspect).toHaveBeenCalledWith({ firm_name: 'ООО Своя', city: 'Омск' });
});
});