709478230f
Task 6.2b — SalesPerformanceView (начальник, #page-performance) поверх GET /api/sales/managers/performance. - Поиск по имени (debounce) + таблица: менеджер (имя/email), клиентов, активных, лидов пришло, оборот, выплачено, заработал, статус (Активен/Отпуск). HelpHint на Оборот/Заработал. Период из salesPeriod store. - Роут /sales/performance со заглушки на экран. Vitest 5/5, фронт-набор 1083 без регрессий, ESLint чист. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
133 lines
5.0 KiB
TypeScript
133 lines
5.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||
import { mount, flushPromises } from '@vue/test-utils';
|
||
import { createVuetify } from 'vuetify';
|
||
import { createPinia } from 'pinia';
|
||
import { createRouter, createMemoryHistory } from 'vue-router';
|
||
import SalesPerformanceView from '../../resources/js/views/sales/SalesPerformanceView.vue';
|
||
|
||
// ─── mock getSalesManagersPerformance ─────────────────────────────────────────
|
||
|
||
const perfMock = vi.fn().mockResolvedValue([
|
||
{
|
||
manager_id: 1,
|
||
name: 'Алексей Смирнов',
|
||
email: 'a.smirnov@liderra.ru',
|
||
clients_count: 7,
|
||
active_clients: 5,
|
||
leads_delivered: 1567,
|
||
oborot_rub: 235050,
|
||
paid_all_time_rub: 85000,
|
||
earned_rub: 13350,
|
||
status: 'active',
|
||
},
|
||
{
|
||
manager_id: 3,
|
||
name: 'Игорь Лебедев',
|
||
email: 'i.lebedev@liderra.ru',
|
||
clients_count: 4,
|
||
active_clients: 3,
|
||
leads_delivered: 724,
|
||
oborot_rub: 108600,
|
||
paid_all_time_rub: 40000,
|
||
earned_rub: 10000,
|
||
status: 'vacation',
|
||
},
|
||
]);
|
||
|
||
vi.mock('../../resources/js/api/sales', () => ({
|
||
getSalesManagersPerformance: (...args: unknown[]) => perfMock(...args),
|
||
}));
|
||
|
||
beforeEach(() => {
|
||
perfMock.mockClear();
|
||
});
|
||
|
||
// ─── helpers ──────────────────────────────────────────────────────────────────
|
||
|
||
function makeRouter() {
|
||
const router = createRouter({
|
||
history: createMemoryHistory(),
|
||
routes: [
|
||
{ path: '/sales/performance', name: 'sales-performance', component: SalesPerformanceView },
|
||
{ path: '/sales', name: 'sales-overview', component: { template: '<div/>' } },
|
||
],
|
||
});
|
||
return router.push('/sales/performance').then(() => router);
|
||
}
|
||
|
||
async function mountView() {
|
||
const router = await makeRouter();
|
||
const wrapper = mount(SalesPerformanceView, {
|
||
global: { plugins: [createVuetify(), createPinia(), router] },
|
||
});
|
||
await flushPromises();
|
||
return wrapper;
|
||
}
|
||
|
||
describe('SalesPerformanceView', () => {
|
||
it('отрисовывает заголовок и колонки', async () => {
|
||
const wrapper = await mountView();
|
||
const text = wrapper.text();
|
||
expect(text).toContain('Результативность менеджеров');
|
||
expect(text).toContain('Менеджер');
|
||
expect(text).toContain('Клиентов');
|
||
expect(text).toContain('Активных');
|
||
expect(text).toContain('Лидов пришло');
|
||
expect(text).toContain('Оборот клиентов');
|
||
expect(text).toContain('Выплачено');
|
||
expect(text).toContain('Заработал');
|
||
expect(text).toContain('Статус');
|
||
});
|
||
|
||
it('вызывает getSalesManagersPerformance при монтировании', async () => {
|
||
await mountView();
|
||
expect(perfMock).toHaveBeenCalled();
|
||
});
|
||
|
||
it('отрисовывает строки менеджеров из мока (имя + email)', async () => {
|
||
const wrapper = await mountView();
|
||
const rows = wrapper.findAll('[data-testid="perf-row"]');
|
||
expect(rows.length).toBe(2);
|
||
|
||
const text = wrapper.text();
|
||
expect(text).toContain('Алексей Смирнов');
|
||
expect(text).toContain('a.smirnov@liderra.ru');
|
||
expect(text).toContain('Игорь Лебедев');
|
||
expect(text).toContain('i.lebedev@liderra.ru');
|
||
});
|
||
|
||
it('статус active → чип «Активен», vacation → чип «Отпуск»', async () => {
|
||
const wrapper = await mountView();
|
||
const text = wrapper.text();
|
||
expect(text).toContain('Активен');
|
||
expect(text).toContain('Отпуск');
|
||
});
|
||
|
||
it('ввод в поиск вызывает getSalesManagersPerformance с параметром search (после debounce)', async () => {
|
||
vi.useFakeTimers();
|
||
try {
|
||
const router = await makeRouter();
|
||
const wrapper = mount(SalesPerformanceView, {
|
||
global: { plugins: [createVuetify(), createPinia(), router] },
|
||
});
|
||
// initial onMounted load
|
||
await vi.runAllTimersAsync();
|
||
await flushPromises();
|
||
perfMock.mockClear();
|
||
|
||
const input = wrapper.find('[data-testid="search-input"] input');
|
||
await input.setValue('Мария');
|
||
|
||
// Ждём завершения debounce.
|
||
await vi.runAllTimersAsync();
|
||
await flushPromises();
|
||
|
||
expect(perfMock).toHaveBeenCalled();
|
||
const lastCall = perfMock.mock.calls[perfMock.mock.calls.length - 1];
|
||
expect(lastCall[0]).toMatchObject({ search: 'Мария' });
|
||
} finally {
|
||
vi.useRealTimers();
|
||
}
|
||
});
|
||
});
|