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]); }); });