Files
portal/app/tests/Frontend/AdminLeads.spec.ts
T
Дмитрий 6536c19c96 feat(дашборд): Этап A — сквозная вложенность Лиды до источника
Экран «Лиды» (/admin/leads): серверный список с фильтрами (дата/канал/поставщик/
статус/поиск) + пагинация (масштаб 10⁴+ лидов). Карточка лида (/admin/leads/{id}):
полная цепочка — ОТКУДА (поставщик B1/B2/B3 + канал + источник + регион) → КОМУ
(сделки клиентов через deals.source_crm_id = supplier_leads.vid). Дашборд: drill
Лиды +топ-10 последних + «Открыть все лиды →». Nav-пункт «Лиды». ПДн-телефон
маскируется (152-ФЗ). Тесты: backend 3 + FE 5 (38 FE всего зелёные).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 10:14:47 +03:00

72 lines
3.9 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import { createRouter, createMemoryHistory } from 'vue-router';
import AdminLeadsView from '../../resources/js/views/admin/AdminLeadsView.vue';
import AdminLeadDetailView from '../../resources/js/views/admin/AdminLeadDetailView.vue';
vi.mock('../../resources/js/api/adminLeads', () => ({
getLeads: vi.fn().mockResolvedValue({
data: [
{ id: 501, received_at: '2026-06-28 07:55', platform: 'B1', channel: 'site', source: 'okna.ru', region_code: 77, phone_masked: '79***07', deals_created_count: 2, status: 'delivered' },
],
total: 1, page: 1, per_page: 25,
}),
getLead: vi.fn().mockResolvedValue({
lead: { id: 501, platform: 'B1', phone_masked: '79***07', received_at: '2026-06-28 07:55', processed_at: '2026-06-28 07:56', error: null, region_code: 77, region_source: 'dadata', phone_operator: 'МТС', deals_created_count: 1, status: 'delivered' },
source: { platform: 'B1', channel: 'site', identifier: 'okna.ru', supplier_project_id: 9 },
deals: [{ id: 1, tenant_id: 2, tenant_name: 'Компания 1', subdomain: 'c1', status: 'new', project_id: 5, received_at: '2026-06-28 07:56' }],
}),
}));
beforeEach(() => vi.clearAllMocks());
function routerWith(path: string) {
const router = createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/admin/leads', name: 'admin-leads', component: AdminLeadsView },
{ path: '/admin/leads/:id', name: 'admin-lead-detail', component: AdminLeadDetailView },
{ path: '/admin/tenants/:code', name: 'admin-tenant-detail', component: { template: '<div/>' } },
{ path: '/admin/supplier-projects', name: 'admin-supplier-projects', component: { template: '<div/>' } },
{ path: '/admin/dashboard', name: 'admin-dashboard', component: { template: '<div/>' } },
],
});
return router.push(path).then(() => router);
}
describe('AdminLeadsView', () => {
it('грузит список и показывает строку лида', async () => {
const router = await routerWith('/admin/leads');
const wrapper = mount(AdminLeadsView, { global: { plugins: [createVuetify(), router] } });
await flushPromises();
expect(wrapper.text()).toContain('okna.ru');
expect(wrapper.text()).toContain('доставлен');
const api = await import('../../resources/js/api/adminLeads');
expect(api.getLeads).toHaveBeenCalled();
});
it('фильтр по каналу шлёт channel и сбрасывает на 1 страницу', async () => {
const router = await routerWith('/admin/leads');
const wrapper = mount(AdminLeadsView, { global: { plugins: [createVuetify(), router] } });
await flushPromises();
const api = await import('../../resources/js/api/adminLeads');
wrapper.vm.filters.channel = 'call';
await wrapper.find('[data-testid="apply-filters"]').trigger('click');
await flushPromises();
expect(vi.mocked(api.getLeads).mock.calls.at(-1)?.[0]).toMatchObject({ channel: 'call', page: 1 });
});
});
describe('AdminLeadDetailView', () => {
it('показывает цепочку: откуда (источник) и кому (сделки)', async () => {
const router = await routerWith('/admin/leads/501');
const wrapper = mount(AdminLeadDetailView, { global: { plugins: [createVuetify(), router] } });
await flushPromises();
expect(wrapper.find('[data-testid="lead-source"]').exists()).toBe(true);
expect(wrapper.text()).toContain('okna.ru'); // источник
expect(wrapper.find('[data-testid="lead-deals"]').exists()).toBe(true);
expect(wrapper.text()).toContain('Компания 1'); // кому ушёл
});
});