Files
portal/app/tests/Frontend/FunnelChart.spec.ts
T
Дмитрий 3cedf28f33
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
feat/ui: подсказки вопросик по карте — дашборд, мастер проекта, биллинг
Дашборд: вопросик у KPI Получено/Конверсия/Активные, у воронки и у прогноза хватит на N дней; pp на п.п.
Мастер проекта: подпись лимита заявок в день + вопросик у выбора источника Сайт/Звонок/СМС.
Биллинг: вопросик у Цены за лид 7 ступеней.
Тест FunnelChart 8/8, type-check чистый, затронутые спеки 58/60.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 13:41:27 +03:00

70 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import FunnelChart from '../../resources/js/components/charts/FunnelChart.vue';
import { LEAD_STATUSES } from '../../resources/js/composables/leadStatuses';
describe('FunnelChart.vue', () => {
const factory = (props?: Record<string, unknown>) =>
mount(FunnelChart, {
props,
global: { plugins: [createVuetify()] },
});
it('монтируется и содержит заголовок «Воронка»', () => {
const wrapper = factory();
expect(wrapper.text()).toContain('Воронка');
});
// UX-аудит 25.06: «?»-подсказка для новичка рядом с заголовком воронки.
it('содержит «?»-иконку подсказки у заголовка', () => {
const wrapper = factory();
expect(wrapper.find('.funnel-hint').exists()).toBe(true);
});
it('содержит ровно 5 сегментов в bar (по числу lead_statuses)', () => {
const wrapper = factory();
const segs = wrapper.findAll('.funnel-seg');
expect(segs).toHaveLength(5);
});
it('содержит ровно 5 list-items', () => {
const wrapper = factory();
const items = wrapper.findAll('.funnel-list-item');
expect(items).toHaveLength(5);
});
it('использует правильные slug-имена из schema (НЕ из BRANDBOOK)', () => {
const wrapper = factory();
const text = wrapper.text();
// Проверка что все 5 имён из lead_statuses присутствуют.
LEAD_STATUSES.forEach((s) => {
expect(text).toContain(s.nameRu);
});
// Гарантия что мы НЕ используем имена из BRANDBOOK §3.6 (расхождение #1).
// Например, "Думает" / "Не дозвон." / "Спам" / "КП" — handoff-only.
expect(text).not.toContain('Думает');
expect(text).not.toContain('Спам');
});
it('сортирует список по убыванию count (in_progress 96 — первый)', () => {
const wrapper = factory();
const names = wrapper.findAll('.funnel-list-item .name').map((n) => n.text());
expect(names[0]).toBe('В работе'); // count=96 — самый большой в DEFAULT_COUNTS.
});
it('применяет colorHex из lead_statuses к dots и сегментам', () => {
const wrapper = factory();
const segs = wrapper.findAll<HTMLElement>('.funnel-seg');
// Первый segment в bar — статус с sortOrder=1 (new, цвет #3B82F6).
expect(segs[0].element.style.background).toContain('rgb(59, 130, 246)');
});
it('считает total как сумму counts', () => {
const wrapper = factory({ counts: { new: 10, won: 20 } });
const text = wrapper.text();
// total = 10 + 20 = 30 (остальные слаги с counts={} → 0).
expect(text).toContain('30 лидов');
});
});