Files
portal/app/tests/Frontend/KanbanColumn.spec.ts
T
Дмитрий d39934c8d9 phase2(kanban): KanbanView - 14 колонок по lead_statuses (БЕЗ DnD)
- KanbanCard: компактная карточка (name/phone/project/cost/manager-avatar),
  emit('open',id) на click для будущего DealDetailDrawer.
- KanbanColumn: header с border-top по colorHex статуса (--accent CSS-var) +
  name+count+total ₽; body с v-for карточек + empty-state «пусто».
- KanbanView: orchestrator, 14 колонок (по LEAD_STATUSES) с группировкой
  MOCK_DEALS по statusSlug, horizontal-scroll с custom scrollbar.
- Маршрут /kanban (meta.layout=app) в router + web.php.
- .gitleaks.toml: tests/Frontend/*.spec.ts в allowlist (assertion на mock-телефоны).
- cspell-words.txt: инлайн, vueuse.

DnD НЕ реализован на MVP - отдельный коммит после выбора библиотеки
(vue-draggable-next или @vueuse/integrations/useSortable).

Vitest +14 (всего 70/70 за 7.37s):
- KanbanCard 3 (data + initials + emit open)
- KanbanColumn 5 (header + total + empty + accent CSS-var case-insensitive +
  проброс openDeal)
- KanbanView 6 (заголовок + 14 columns + правильные status'ы + stats + кнопка +
  DnD-предупреждение)

Регресс: lint+type+format OK; vitest 70/70; vite build (KanbanView lazy-chunk);
story:build 14/20 за 31.17s; Pest 48/48 за 5.06s.

CLAUDE.md v1.23->v1.24, реестр Открытых_вопросов v1.32->v1.33.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:56:59 +03:00

51 lines
2.3 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 KanbanColumn from '../../resources/js/components/kanban/KanbanColumn.vue';
import { LEAD_STATUSES } from '../../resources/js/composables/leadStatuses';
import { MOCK_DEALS } from '../../resources/js/composables/mockDeals';
describe('KanbanColumn.vue', () => {
const status = LEAD_STATUSES[0]; // 'new'
const dealsForNew = MOCK_DEALS.filter((d) => d.statusSlug === 'new');
const factory = (props: { status: typeof status; deals: typeof MOCK_DEALS }) =>
mount(KanbanColumn, {
props,
global: { plugins: [createVuetify()] },
});
it('header содержит nameRu статуса и count', () => {
const wrapper = factory({ status, deals: dealsForNew });
const text = wrapper.text();
expect(text).toContain(status.nameRu);
expect(text).toContain(String(dealsForNew.length));
});
it('total = sum dealcost (форматированный «N ₽»)', () => {
const deals = MOCK_DEALS.slice(0, 2); // первые 2 — 1850 + 2400 = 4250
const wrapper = factory({ status, deals });
expect(wrapper.text()).toMatch(/4\s+250\s*₽/);
});
it('total = «—» при пустом списке', () => {
const wrapper = factory({ status, deals: [] });
expect(wrapper.text()).toContain('—');
expect(wrapper.text()).toContain('пусто');
});
it('применяет colorHex статуса как CSS var --accent', () => {
const wrapper = factory({ status, deals: dealsForNew });
const head = wrapper.find('.column-head');
// Vue 3 inline-style сохраняет как есть (case-preserve) — матчим case-insensitive.
expect(head.attributes('style')?.toLowerCase()).toContain(status.colorHex.toLowerCase());
});
it('пробрасывает open от карточки → openDeal с id', async () => {
const wrapper = factory({ status, deals: dealsForNew });
await wrapper.findComponent({ name: 'KanbanCard' }).vm.$emit('open', dealsForNew[0].id);
expect(wrapper.emitted('openDeal')).toBeTruthy();
expect(wrapper.emitted('openDeal')?.[0]).toEqual([dealsForNew[0].id]);
});
});