diff --git a/frontend/__tests__/unit/components/IssuesTable.test.tsx b/frontend/__tests__/unit/components/IssuesTable.test.tsx index d6f7a05835..f9c805d183 100644 --- a/frontend/__tests__/unit/components/IssuesTable.test.tsx +++ b/frontend/__tests__/unit/components/IssuesTable.test.tsx @@ -2,6 +2,7 @@ import { render, screen, fireEvent, within } from '@testing-library/react' import '@testing-library/jest-dom' import React from 'react' import IssuesTable, { type IssueRow } from 'components/IssuesTable' +import { LabelList } from 'components/LabelList' jest.mock('next/navigation', () => ({ useRouter: () => ({ @@ -47,6 +48,36 @@ jest.mock('@heroui/tooltip', () => ({ }, })) +interface MockLabelListProps { + entityKey: string + labels: string[] + maxVisible?: number + className?: string +} + +const MockLabelList = (props: MockLabelListProps) => { + const { entityKey, labels, maxVisible = 5, className } = props + if (!labels || labels.length === 0) return null + const visibleLabels = labels.slice(0, maxVisible) + const remainingCount = labels.length - maxVisible + return ( +
+ {visibleLabels.map((label) => ( + + {label} + + ))} + {remainingCount > 0 && +{remainingCount} more} +
+ ) +} + +jest.mock('components/LabelList', () => ({ + // Must match the module export name for the mock to be used by IssuesTable + // eslint-disable-next-line @typescript-eslint/naming-convention -- component export name + LabelList: jest.fn((props: MockLabelListProps) => ), +})) + const mockIssues: IssueRow[] = [ { objectID: '1', @@ -102,6 +133,10 @@ describe('', () => { issues: mockIssues, } + beforeEach(() => { + jest.mocked(LabelList).mockClear() + }) + describe('Rendering', () => { it('renders table view', () => { render() @@ -200,6 +235,83 @@ describe('', () => { render() expect(screen.getByText('+2 more')).toBeInTheDocument() }) + + it('uses LabelList with entityKey derived from issue objectID', () => { + render() + expect(LabelList).toHaveBeenCalledTimes(1) + expect(LabelList).toHaveBeenCalledWith( + expect.objectContaining({ + entityKey: 'issue-1', + labels: ['bug', 'enhancement'], + maxVisible: 5, + }), + undefined + ) + }) + + it('passes maxVisibleLabels to LabelList as maxVisible', () => { + render() + expect(LabelList).toHaveBeenCalledTimes(1) + expect(LabelList).toHaveBeenCalledWith( + expect.objectContaining({ + entityKey: 'issue-1', + labels: ['bug', 'enhancement'], + maxVisible: 3, + }), + undefined + ) + }) + + it('passes empty array to LabelList when issue has no labels', () => { + render() + expect(LabelList).toHaveBeenCalledTimes(1) + expect(LabelList).toHaveBeenCalledWith( + expect.objectContaining({ + entityKey: 'issue-3', + labels: [], + maxVisible: 5, + }), + undefined + ) + }) + + it('passes empty array to LabelList when issue.labels is undefined', () => { + const issueWithUndefinedLabels = { + ...mockIssues[0], + objectID: 'undefined-labels', + labels: undefined, + } as IssueRow + render() + expect(LabelList).toHaveBeenCalledTimes(1) + expect(LabelList).toHaveBeenCalledWith( + expect.objectContaining({ + entityKey: 'issue-undefined-labels', + labels: [], + maxVisible: 5, + }), + undefined + ) + }) + + it('calls LabelList once per issue row with correct labels', () => { + render() + expect(LabelList).toHaveBeenCalledTimes(3) + expect(LabelList).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ entityKey: 'issue-1', labels: ['bug', 'enhancement'] }), + undefined + ) + expect(LabelList).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ entityKey: 'issue-2', labels: ['documentation'] }), + undefined + ) + expect(LabelList).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ entityKey: 'issue-3', labels: [] }), + undefined + ) + }) }) describe('Assignee Column', () => { diff --git a/frontend/src/components/IssuesTable.tsx b/frontend/src/components/IssuesTable.tsx index d0c2f53d97..1e4805ca5e 100644 --- a/frontend/src/components/IssuesTable.tsx +++ b/frontend/src/components/IssuesTable.tsx @@ -5,6 +5,8 @@ import Image from 'next/image' import { useRouter } from 'next/navigation' import type React from 'react' +import { LabelList } from 'components/LabelList' + export type IssueRow = { objectID: string number: number @@ -141,23 +143,12 @@ const IssuesTable: React.FC = ({ {/* Labels */} - {issue.labels && issue.labels.length > 0 ? ( -
- {issue.labels.slice(0, maxVisibleLabels).map((label) => ( - - {label} - - ))} - {issue.labels.length > maxVisibleLabels && ( - - +{issue.labels.length - maxVisibleLabels} more - - )} -
- ) : null} + {/* Assignee */}