import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import KanbanBoard from '../src/components/KanbanBoard.vue' function makeItem(overrides = {}) { return { id: 1, modality: 'bh_email', sender_id: 'jane@example.com', raw_content: 'I have some books to donate.', type: null, stage: 'new', direction: 'inbound', follow_up_date: '', ...overrides, } } describe('KanbanBoard', () => { it('buckets items into the correct board and shows counts on tabs', () => { const items = [ makeItem({ id: 1, type: 'donation', direction: 'inbound', stage: 'new' }), makeItem({ id: 2, type: 'other', direction: 'inbound', stage: 'new' }), makeItem({ id: 3, type: null, direction: 'outbound', stage: 'new' }), makeItem({ id: 4, type: null, direction: 'inbound', stage: 'new' }), ] const wrapper = mount(KanbanBoard, { props: { items } }) const tabs = wrapper.findAll('[role="tab"]').map((t) => t.text()) expect(tabs).toEqual([ 'Donations (1)', 'Other (1)', 'Outbound Leads (1)', 'Unsorted (1)', ]) }) it('defaults to the Donations board and its first stage', () => { const wrapper = mount(KanbanBoard, { props: { items: [] } }) const activeTab = wrapper.find('[role="tab"][aria-selected="true"]') expect(activeTab.text()).toContain('Donations') }) it('shows humanized stage labels, not raw stage strings, on column headers', () => { const items = [makeItem({ id: 1, type: 'donation', stage: 'pickup_scheduled' })] const wrapper = mount(KanbanBoard, { props: { items } }) expect(wrapper.text()).toContain('Pickup Scheduled') expect(wrapper.text()).not.toContain('pickup_scheduled') }) it('switching board tabs resets the active stage to that board\'s first stage', async () => { const wrapper = mount(KanbanBoard, { props: { items: [] } }) const outboundTab = wrapper.findAll('[role="tab"]').find((t) => t.text().includes('Outbound Leads')) await outboundTab.trigger('click') const breadcrumb = wrapper.find('.mobile-breadcrumb') expect(breadcrumb.text()).toContain('New') expect(breadcrumb.text()).toContain('Outbound Leads') }) it('clicking a stage-strip button switches the active stage', async () => { const items = [makeItem({ id: 1, type: 'donation', stage: 'replied' })] const wrapper = mount(KanbanBoard, { props: { items } }) const repliedButton = wrapper.findAll('.stage-strip-button').find((b) => b.text().includes('Replied')) await repliedButton.trigger('click') expect(wrapper.find('.mobile-breadcrumb').text()).toContain('Replied') }) it('clicking a card emits select-item with the item id', async () => { const items = [makeItem({ id: 42, type: 'donation', stage: 'new' })] const wrapper = mount(KanbanBoard, { props: { items } }) await wrapper.find('.card').trigger('click') expect(wrapper.emitted('select-item')).toEqual([[42]]) }) it('renders an empty-state message for a column with no items', () => { const wrapper = mount(KanbanBoard, { props: { items: [] } }) expect(wrapper.text()).toContain('Nothing here') }) it('applies the is-active-mobile class only to the currently active stage column', () => { const items = [ makeItem({ id: 1, type: 'donation', stage: 'new' }), makeItem({ id: 2, type: 'donation', stage: 'replied' }), ] const wrapper = mount(KanbanBoard, { props: { items } }) const columns = wrapper.findAll('.column') const activeColumns = columns.filter((c) => c.classes().includes('is-active-mobile')) expect(activeColumns).toHaveLength(1) expect(activeColumns[0].text()).toContain('New') }) it('ArrowRight on a board tab moves selection and focus to the next tab', async () => { const wrapper = mount(KanbanBoard, { props: { items: [] }, attachTo: document.body }) const tabs = wrapper.findAll('[role="tab"]') tabs[0].element.focus() await tabs[0].trigger('keydown', { key: 'ArrowRight' }) const updatedTabs = wrapper.findAll('[role="tab"]') expect(updatedTabs[1].attributes('aria-selected')).toBe('true') expect(document.activeElement).toBe(updatedTabs[1].element) wrapper.unmount() }) it('ArrowLeft wraps from the first tab to the last tab', async () => { const wrapper = mount(KanbanBoard, { props: { items: [] }, attachTo: document.body }) const tabs = wrapper.findAll('[role="tab"]') await tabs[0].trigger('keydown', { key: 'ArrowLeft' }) const updatedTabs = wrapper.findAll('[role="tab"]') expect(updatedTabs[3].attributes('aria-selected')).toBe('true') wrapper.unmount() }) })