diff --git a/frontend/__tests__/a11y/components/ContributionHeatmap.a11y.test.tsx b/frontend/__tests__/a11y/components/ContributionHeatmap.a11y.test.tsx
index 2ee9d3adec..84620e7d00 100644
--- a/frontend/__tests__/a11y/components/ContributionHeatmap.a11y.test.tsx
+++ b/frontend/__tests__/a11y/components/ContributionHeatmap.a11y.test.tsx
@@ -78,7 +78,7 @@ describe.each([
it('should not have any accessibility violations', async () => {
const { container } = render()
- await screen.findByTestId('mock-heatmap-chart')
+ await screen.findAllByTestId('mock-heatmap-chart')
const results = await axe(container)
diff --git a/frontend/__tests__/a11y/components/RepositoryCard.a11y.test.tsx b/frontend/__tests__/a11y/components/RepositoryCard.a11y.test.tsx
index 427e07caaa..238051e381 100644
--- a/frontend/__tests__/a11y/components/RepositoryCard.a11y.test.tsx
+++ b/frontend/__tests__/a11y/components/RepositoryCard.a11y.test.tsx
@@ -47,7 +47,7 @@ describe.each([
})
it('should not have any accessibility violations when expanded', async () => {
- const repositories = Array.from({ length: 6 }, (_, i) => createMockRepository(i))
+ const repositories = Array.from({ length: 7 }, (_, i) => createMockRepository(i))
const { container } = render()
const showMoreButton = screen.getByRole('button', { name: /show more/i })
diff --git a/frontend/__tests__/unit/components/ContributionHeatmap.test.tsx b/frontend/__tests__/unit/components/ContributionHeatmap.test.tsx
index 7a55210905..8eebc45794 100644
--- a/frontend/__tests__/unit/components/ContributionHeatmap.test.tsx
+++ b/frontend/__tests__/unit/components/ContributionHeatmap.test.tsx
@@ -85,7 +85,9 @@ describe('ContributionHeatmap', () => {
describe('Rendering & Props', () => {
it('renders with minimal and all optional props', async () => {
const { rerender } = renderWithTheme()
- await waitFor(() => expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument())
+ await waitFor(() =>
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
+ )
expect(screen.queryByRole('heading')).not.toBeInTheDocument()
rerender(
@@ -99,16 +101,16 @@ describe('ContributionHeatmap', () => {
it('renders all 7 day series and correct chart type', () => {
renderWithTheme()
;['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].forEach((day) =>
- expect(screen.getByTestId(`series-${day}`)).toBeInTheDocument()
+ expect(screen.getAllByTestId(`series-${day}`)[0]).toBeInTheDocument()
)
- expect(screen.getByTestId('mock-heatmap-chart')).toHaveAttribute('data-type', 'heatmap')
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toHaveAttribute('data-type', 'heatmap')
})
it('applies custom unit and handles undefined title', () => {
renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
expect(screen.queryByRole('heading')).not.toBeInTheDocument()
})
})
@@ -124,7 +126,7 @@ describe('ContributionHeatmap', () => {
const { unmount } = renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
unmount()
})
})
@@ -139,7 +141,7 @@ describe('ContributionHeatmap', () => {
const { unmount } = renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
unmount()
})
})
@@ -151,7 +153,7 @@ describe('ContributionHeatmap', () => {
endDate: '2024-01-31',
}
renderWithTheme()
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles large datasets (365 days)', () => {
@@ -168,29 +170,35 @@ describe('ContributionHeatmap', () => {
endDate="2024-12-31"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles missing startDate by using default dates', () => {
renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
// Should render with default date range (1 year)
- expect(screen.getByTestId('mock-heatmap-chart')).toHaveAttribute('data-series-length', '7')
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toHaveAttribute(
+ 'data-series-length',
+ '7'
+ )
})
it('handles missing endDate by using default dates', () => {
renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
- expect(screen.getByTestId('mock-heatmap-chart')).toHaveAttribute('data-series-length', '7')
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toHaveAttribute(
+ 'data-series-length',
+ '7'
+ )
})
it('handles both missing startDate and endDate', () => {
renderWithTheme()
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles invalid startDate by using default dates', () => {
@@ -201,7 +209,7 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-31"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles invalid endDate by using default dates', () => {
@@ -212,7 +220,7 @@ describe('ContributionHeatmap', () => {
endDate="not-a-date"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles both invalid startDate and endDate', () => {
@@ -223,7 +231,7 @@ describe('ContributionHeatmap', () => {
endDate="also-garbage"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles swapped dates (startDate > endDate) by swapping them', () => {
@@ -234,8 +242,11 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-01"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
- expect(screen.getByTestId('mock-heatmap-chart')).toHaveAttribute('data-series-length', '7')
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toHaveAttribute(
+ 'data-series-length',
+ '7'
+ )
})
it('handles startDate after endDate and swaps them correctly', () => {
@@ -246,7 +257,7 @@ describe('ContributionHeatmap', () => {
renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
})
@@ -299,7 +310,7 @@ describe('ContributionHeatmap', () => {
describe('Chart Configuration & Performance', () => {
it('sets correct dimensions and series count', () => {
renderWithTheme()
- const chart = screen.getByTestId('mock-heatmap-chart')
+ const chart = screen.getAllByTestId('mock-heatmap-chart')[0]
expect(chart).toHaveAttribute('data-height', '195')
expect(chart).toHaveAttribute('data-series-length', '7')
})
@@ -316,24 +327,26 @@ describe('ContributionHeatmap', () => {
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles dynamic import with SSR disabled', async () => {
renderWithTheme()
- await waitFor(() => expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument())
+ await waitFor(() =>
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
+ )
})
})
describe('Tooltip Behavior', () => {
it('generates correct tooltip with date formatting', () => {
renderWithTheme()
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles singular and plural unit labels in tooltip', () => {
const { rerender } = renderWithTheme()
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
const singleData = { '2024-01-01': 1 }
rerender(
@@ -346,26 +359,26 @@ describe('ContributionHeatmap', () => {
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('tooltip respects theme colors', () => {
const { rerender } = renderWithTheme(, 'light')
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
;(useTheme as jest.Mock).mockReturnValue({ theme: 'dark', setTheme: jest.fn() })
rerender(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles missing data in tooltip gracefully', () => {
const { container } = renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
const styleTag = container.querySelector('style')
expect(styleTag).toBeInTheDocument()
@@ -381,7 +394,7 @@ describe('ContributionHeatmap', () => {
renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles week transitions correctly', () => {
@@ -392,7 +405,7 @@ describe('ContributionHeatmap', () => {
renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
})
@@ -412,7 +425,7 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-31"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles boundary values in color ranges', () => {
@@ -434,7 +447,7 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-31"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
})
@@ -452,7 +465,7 @@ describe('ContributionHeatmap', () => {
endDate="2024-03-01"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles year boundaries correctly', () => {
@@ -469,7 +482,7 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-02"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles dates in reverse chronological order in data object', () => {
@@ -486,7 +499,7 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-31"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
})
@@ -503,7 +516,7 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-31"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles floating point contribution values', () => {
@@ -518,7 +531,7 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-31"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles extremely large contribution counts', () => {
@@ -533,7 +546,7 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-31"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
})
@@ -554,14 +567,14 @@ describe('ContributionHeatmap', () => {
)
}
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles unmounting and remounting', () => {
const { unmount } = renderWithTheme()
unmount()
renderWithTheme()
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
})
@@ -578,38 +591,38 @@ describe('ContributionHeatmap', () => {
endDate="2024-01-31"
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('handles custom unit strings with special characters', () => {
const { unmount } = renderWithTheme(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
unmount()
const { rerender } = renderWithTheme()
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
rerender(
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
})
describe('Chart Options Validation', () => {
it('configures chart with correct options structure', () => {
renderWithTheme()
- const chart = screen.getByTestId('mock-heatmap-chart')
+ const chart = screen.getAllByTestId('mock-heatmap-chart')[0]
expect(chart).toHaveAttribute('data-type', 'heatmap')
})
it('renders heatmap chart', () => {
renderWithTheme()
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
})
@@ -654,9 +667,9 @@ describe('ContributionHeatmap', () => {
)
expect(screen.getByText('GitHub Contributions')).toBeInTheDocument()
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
;['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].forEach((day) =>
- expect(screen.getByTestId(`series-${day}`)).toBeInTheDocument()
+ expect(screen.getAllByTestId(`series-${day}`)[0]).toBeInTheDocument()
)
})
@@ -671,7 +684,7 @@ describe('ContributionHeatmap', () => {
)
expect(screen.getByText('No Activity')).toBeInTheDocument()
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
it('renders correctly with all edge cases combined', () => {
@@ -692,62 +705,35 @@ describe('ContributionHeatmap', () => {
/>
)
- expect(screen.getByTestId('mock-heatmap-chart')).toBeInTheDocument()
+ expect(screen.getAllByTestId('mock-heatmap-chart')[0]).toBeInTheDocument()
})
})
describe('Variants', () => {
it('renders default variant with full dimensions', () => {
renderWithTheme()
- const chart = screen.getByTestId('mock-heatmap-chart')
+ const chart = screen.getAllByTestId('mock-heatmap-chart')[0]
// Verify full-size dimensions (195px height for default variant)
expect(chart).toHaveAttribute('data-height', '195')
})
it('renders medium variant with medium dimensions', () => {
renderWithTheme()
- const chart = screen.getByTestId('mock-heatmap-chart')
+ const chart = screen.getAllByTestId('mock-heatmap-chart')[0]
// Verify medium dimensions (172px height for medium variant)
expect(chart).toHaveAttribute('data-height', '172')
})
it('renders compact variant with smaller dimensions', () => {
renderWithTheme()
- const chart = screen.getByTestId('mock-heatmap-chart')
+ const chart = screen.getAllByTestId('mock-heatmap-chart')[0]
// Verify compact dimensions (150px height for compact variant)
expect(chart).toHaveAttribute('data-height', '150')
})
- it('applies compact-specific container styling when variant is compact', () => {
- const { container } = renderWithTheme(
-
- )
- // Verify compact variant uses inline-block class
- const chartContainer = container.querySelector('.inline-block')
- expect(chartContainer).toBeInTheDocument()
- })
-
- it('applies default variant container styling when variant is default', () => {
- const { container } = renderWithTheme(
-
- )
- // Verify default variant uses inline-block class
- const chartContainer = container.querySelector('.inline-block')
- expect(chartContainer).toBeInTheDocument()
- })
-
- it('applies medium variant container styling', () => {
- const { container } = renderWithTheme(
-
- )
- // Verify medium variant uses inline-block class
- const chartContainer = container.querySelector('.inline-block')
- expect(chartContainer).toBeInTheDocument()
- })
-
it('defaults to default variant when no variant is specified', () => {
renderWithTheme()
- const chart = screen.getByTestId('mock-heatmap-chart')
+ const chart = screen.getAllByTestId('mock-heatmap-chart')[0]
// Should render with default variant dimensions
expect(chart).toHaveAttribute('data-height', '195')
})
diff --git a/frontend/__tests__/unit/components/InfoBlock.test.tsx b/frontend/__tests__/unit/components/InfoBlock.test.tsx
index 7186e0b326..f2baacfeac 100644
--- a/frontend/__tests__/unit/components/InfoBlock.test.tsx
+++ b/frontend/__tests__/unit/components/InfoBlock.test.tsx
@@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event'
import '@testing-library/jest-dom'
import millify from 'millify'
import React from 'react'
-import { FaUser, FaStar, FaCode } from 'react-icons/fa6'
+import { FaUser, FaStar } from 'react-icons/fa6'
import { pluralize } from 'utils/pluralize'
import InfoBlock from 'components/InfoBlock'
@@ -73,37 +73,16 @@ describe('InfoBlock Component', () => {
unit="contributor"
pluralizedName="contributors"
precision={2}
- label="Team Members"
className="custom-class"
/>
)
expect(screen.getByTestId('icon-star')).toBeInTheDocument()
- expect(screen.getByText('Team Members')).toBeInTheDocument()
expect(screen.getByText('1.5k contributors')).toBeInTheDocument()
})
})
describe('Conditional rendering logic', () => {
- it('should show label when provided', () => {
- mockMillify.mockReturnValue('50')
- mockPluralize.mockReturnValue('projects')
-
- render()
-
- expect(screen.getByText('Active Projects')).toBeInTheDocument()
- expect(screen.getByText('Active Projects')).toHaveClass('text-sm', 'font-medium')
- })
-
- it('should not show label when not provided', () => {
- mockMillify.mockReturnValue('50')
- mockPluralize.mockReturnValue('projects')
-
- render()
-
- expect(screen.queryByText('Active Projects')).not.toBeInTheDocument()
- })
-
it('should show "No" prefix when value is 0', () => {
mockMillify.mockReturnValue('0')
mockPluralize.mockReturnValue('items')
@@ -233,20 +212,6 @@ describe('InfoBlock Component', () => {
})
describe('Default values and fallbacks', () => {
- it('should not render a label element when label prop is not provided', () => {
- mockMillify.mockReturnValue('100')
- mockPluralize.mockReturnValue('items')
-
- render()
-
- // Verify only the formatted value is rendered, no label
- expect(screen.getByText('100 items')).toBeInTheDocument()
-
- // Verify no element with label styling exists
- const labelElements = document.querySelectorAll('.text-sm.font-medium')
- expect(labelElements).toHaveLength(0)
- })
-
it('should use empty string as default className', () => {
mockMillify.mockReturnValue('100')
mockPluralize.mockReturnValue('items')
@@ -286,16 +251,6 @@ describe('InfoBlock Component', () => {
expect(screen.getByText('2.5k stars')).toBeInTheDocument()
})
- it('should render label with correct styling', () => {
- mockMillify.mockReturnValue('100')
- mockPluralize.mockReturnValue('repos')
-
- render()
-
- const label = screen.getByText('Repositories')
- expect(label).toHaveClass('text-sm', 'font-medium')
- })
-
it('should render tooltip content correctly for positive values', () => {
mockMillify.mockReturnValue('1.5k')
mockPluralize.mockReturnValue('contributors')
@@ -379,10 +334,10 @@ describe('InfoBlock Component', () => {
expect(wrapper).toHaveClass('flex')
const icon = screen.getByTestId('icon-user')
- expect(icon).toHaveClass('mr-3', 'mt-1', 'w-5')
+ expect(icon).toHaveClass('w-5', 'text-gray-500')
const contentDiv = wrapper.children[1] as HTMLElement
- expect(contentDiv.children[0]).toHaveClass('text-sm', 'md:text-base')
+ expect(contentDiv).toHaveClass('text-base', 'text-gray-600')
})
it('should apply custom className correctly', () => {
@@ -404,20 +359,7 @@ describe('InfoBlock Component', () => {
render()
const icon = screen.getByTestId('icon-user')
- expect(icon).toHaveClass('mr-3', 'mt-1', 'w-5')
- })
-
- it('should have correct text container styling', () => {
- mockMillify.mockReturnValue('100')
- mockPluralize.mockReturnValue('items')
-
- render()
-
- const textContainer = screen.getByText('100 items').closest('div')
- expect(textContainer).toHaveClass('text-sm', 'md:text-base')
-
- const label = screen.getByText('Test Label')
- expect(label).toHaveClass('text-sm', 'font-medium')
+ expect(icon).toHaveClass('w-5', 'text-gray-500')
})
})
diff --git a/frontend/__tests__/unit/components/Milestones.test.tsx b/frontend/__tests__/unit/components/Milestones.test.tsx
index 0dfd45001e..8b3d990105 100644
--- a/frontend/__tests__/unit/components/Milestones.test.tsx
+++ b/frontend/__tests__/unit/components/Milestones.test.tsx
@@ -193,7 +193,6 @@ describe('Milestones', () => {
render()
expect(screen.getByText('Jan 1, 2023')).toBeInTheDocument()
- expect(screen.getByText('10 closed')).toBeInTheDocument()
expect(screen.getByText('5 open')).toBeInTheDocument()
expect(screen.getByTestId('truncated-text')).toHaveTextContent('awesome-repo')
})
@@ -246,9 +245,6 @@ describe('Milestones', () => {
expect(screen.getByTestId('milestone-0')).toBeInTheDocument()
expect(screen.getByTestId('milestone-1')).toBeInTheDocument()
expect(screen.getByTestId('milestone-2')).toBeInTheDocument()
- expect(screen.getByText('5 closed')).toBeInTheDocument()
- expect(screen.getByText('10 closed')).toBeInTheDocument()
- expect(screen.getByText('15 closed')).toBeInTheDocument()
})
it('handles edge case with zero counts', () => {
@@ -256,7 +252,6 @@ describe('Milestones', () => {
)
- expect(screen.getByText('0 closed')).toBeInTheDocument()
expect(screen.getByText('0 open')).toBeInTheDocument()
})
@@ -265,7 +260,6 @@ describe('Milestones', () => {
)
- expect(screen.getByText('999 closed')).toBeInTheDocument()
expect(screen.getByText('1000 open')).toBeInTheDocument()
})
diff --git a/frontend/__tests__/unit/components/RecentRelease.test.tsx b/frontend/__tests__/unit/components/RecentRelease.test.tsx
index a7e04d76a2..8b17abbef5 100644
--- a/frontend/__tests__/unit/components/RecentRelease.test.tsx
+++ b/frontend/__tests__/unit/components/RecentRelease.test.tsx
@@ -382,10 +382,6 @@ describe('RecentReleases Component', () => {
})
// Check for main card structure - look for the card wrapper
- const cardElement = container.querySelector(
- String.raw`.mb-4.w-full.rounded-lg.bg-gray-200.p-4.dark\:bg-gray-700`
- )
- expect(cardElement).toBeInTheDocument()
// Check for proper grid layout
const gridElement = container.querySelector('.grid')
diff --git a/frontend/__tests__/unit/components/RepositoryCard.test.tsx b/frontend/__tests__/unit/components/RepositoryCard.test.tsx
index a46b483cad..c3e943604f 100644
--- a/frontend/__tests__/unit/components/RepositoryCard.test.tsx
+++ b/frontend/__tests__/unit/components/RepositoryCard.test.tsx
@@ -82,65 +82,67 @@ describe('RepositoryCard', () => {
expect(container.querySelector('.grid')).toBeNull()
})
- it('shows first 4 repositories initially when there are more than 4', () => {
- const repositories = Array.from({ length: 6 }, (_, i) => createMockRepository(i))
+ it('shows first 6 repositories initially when there are more than 6', () => {
+ const repositories = Array.from({ length: 8 }, (_, i) => createMockRepository(i))
render()
expect(screen.getByText('Repository 0')).toBeInTheDocument()
- expect(screen.getByText('Repository 3')).toBeInTheDocument()
- expect(screen.queryByText('Repository 4')).not.toBeInTheDocument()
- expect(screen.queryByText('Repository 5')).not.toBeInTheDocument()
+ expect(screen.getByText('Repository 5')).toBeInTheDocument()
+ expect(screen.queryByText('Repository 6')).not.toBeInTheDocument()
+ expect(screen.queryByText('Repository 7')).not.toBeInTheDocument()
})
- it('shows all repositories when there are 4 or fewer', () => {
- const repositories = Array.from({ length: 3 }, (_, i) => createMockRepository(i))
+ it('shows all repositories when there are 6 or fewer', () => {
+ const repositories = Array.from({ length: 5 }, (_, i) => createMockRepository(i))
render()
expect(screen.getByText('Repository 0')).toBeInTheDocument()
expect(screen.getByText('Repository 1')).toBeInTheDocument()
expect(screen.getByText('Repository 2')).toBeInTheDocument()
+ expect(screen.getByText('Repository 3')).toBeInTheDocument()
+ expect(screen.getByText('Repository 4')).toBeInTheDocument()
})
- it('displays ShowMoreButton when there are more than 4 repositories', () => {
- const repositories = Array.from({ length: 6 }, (_, i) => createMockRepository(i))
+ it('displays ShowMoreButton when there are more than 6 repositories', () => {
+ const repositories = Array.from({ length: 8 }, (_, i) => createMockRepository(i))
render()
expect(screen.getByRole('button', { name: /Show/ })).toBeInTheDocument()
})
- it('does not display ShowMoreButton when there are 4 or fewer repositories', () => {
- const repositories = Array.from({ length: 4 }, (_, i) => createMockRepository(i))
+ it('does not display ShowMoreButton when there are 6 or fewer repositories', () => {
+ const repositories = Array.from({ length: 6 }, (_, i) => createMockRepository(i))
render()
expect(screen.queryByRole('button', { name: /Show/ })).not.toBeInTheDocument()
})
- it('toggles between showing 4 and all repositories when clicked', () => {
- const repositories = Array.from({ length: 6 }, (_, i) => createMockRepository(i))
+ it('toggles between showing 6 and all repositories when clicked', () => {
+ const repositories = Array.from({ length: 8 }, (_, i) => createMockRepository(i))
render()
- // Initially shows first 4
+ // Initially shows first 6
expect(screen.getByText('Repository 0')).toBeInTheDocument()
- expect(screen.queryByText('Repository 4')).not.toBeInTheDocument()
+ expect(screen.queryByText('Repository 6')).not.toBeInTheDocument()
// Click show more
fireEvent.click(screen.getByRole('button', { name: /Show/ }))
// Now shows all repositories
- expect(screen.getByText('Repository 4')).toBeInTheDocument()
- expect(screen.getByText('Repository 5')).toBeInTheDocument()
+ expect(screen.getByText('Repository 6')).toBeInTheDocument()
+ expect(screen.getByText('Repository 7')).toBeInTheDocument()
// Click show less
fireEvent.click(screen.getByRole('button', { name: /Show/ }))
- // Back to showing first 4
- expect(screen.queryByText('Repository 4')).not.toBeInTheDocument()
- expect(screen.queryByText('Repository 5')).not.toBeInTheDocument()
+ // Back to showing first 6
+ expect(screen.queryByText('Repository 6')).not.toBeInTheDocument()
+ expect(screen.queryByText('Repository 7')).not.toBeInTheDocument()
})
it('renders repository items with correct information', () => {
@@ -311,22 +313,22 @@ describe('RepositoryCard', () => {
})
it('archived badge persists when toggling show more/less', () => {
- const repositories = Array.from({ length: 6 }, (_, i) => ({
+ const repositories = Array.from({ length: 8 }, (_, i) => ({
...createMockRepository(i),
isArchived: i % 2 === 0,
}))
render()
- expect(screen.getAllByText('Archived')).toHaveLength(2)
+ expect(screen.getAllByText('Archived')).toHaveLength(3)
fireEvent.click(screen.getByRole('button', { name: /Show/ }))
- expect(screen.getAllByText('Archived')).toHaveLength(3)
+ expect(screen.getAllByText('Archived')).toHaveLength(4)
fireEvent.click(screen.getByRole('button', { name: /Show/ }))
- expect(screen.getAllByText('Archived')).toHaveLength(2)
+ expect(screen.getAllByText('Archived')).toHaveLength(3)
})
it('clicking archived repository still navigates correctly', () => {
diff --git a/frontend/__tests__/unit/pages/Home.test.tsx b/frontend/__tests__/unit/pages/Home.test.tsx
index 0f0d3e9232..06960c6331 100644
--- a/frontend/__tests__/unit/pages/Home.test.tsx
+++ b/frontend/__tests__/unit/pages/Home.test.tsx
@@ -213,7 +213,6 @@ describe('Home', () => {
expect(screen.getByText(milestone.title)).toBeInTheDocument()
expect(screen.getByText(milestone.repositoryName)).toBeInTheDocument()
expect(screen.getByText(`${milestone.openIssuesCount} open`)).toBeInTheDocument()
- expect(screen.getByText(`${milestone.closedIssuesCount} closed`)).toBeInTheDocument()
}
})
})
diff --git a/frontend/__tests__/unit/pages/OrganizationDetails.test.tsx b/frontend/__tests__/unit/pages/OrganizationDetails.test.tsx
index 70f832992a..c851c3e850 100644
--- a/frontend/__tests__/unit/pages/OrganizationDetails.test.tsx
+++ b/frontend/__tests__/unit/pages/OrganizationDetails.test.tsx
@@ -118,7 +118,6 @@ describe('OrganizationDetailsPage', () => {
expect(screen.getByText(milestone.title)).toBeInTheDocument()
expect(screen.getByText(milestone.repositoryName)).toBeInTheDocument()
expect(screen.getByText(`${milestone.openIssuesCount} open`)).toBeInTheDocument()
- expect(screen.getByText(`${milestone.closedIssuesCount} closed`)).toBeInTheDocument()
}
})
})
diff --git a/frontend/__tests__/unit/pages/ProjectDetails.test.tsx b/frontend/__tests__/unit/pages/ProjectDetails.test.tsx
index 4afd72e511..7753332299 100644
--- a/frontend/__tests__/unit/pages/ProjectDetails.test.tsx
+++ b/frontend/__tests__/unit/pages/ProjectDetails.test.tsx
@@ -260,7 +260,6 @@ describe('ProjectDetailsPage', () => {
expect(screen.getByText(milestone.title)).toBeInTheDocument()
expect(screen.getByText(milestone.repositoryName)).toBeInTheDocument()
expect(screen.getByText(`${milestone.openIssuesCount} open`)).toBeInTheDocument()
- expect(screen.getByText(`${milestone.closedIssuesCount} closed`)).toBeInTheDocument()
}
})
})
diff --git a/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx b/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx
index c28c160b8f..db71bb4397 100644
--- a/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx
+++ b/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx
@@ -188,7 +188,6 @@ describe('RepositoryDetailsPage', () => {
expect(screen.getByText(milestone.title)).toBeInTheDocument()
expect(screen.getByText(milestone.repositoryName)).toBeInTheDocument()
expect(screen.getByText(`${milestone.openIssuesCount} open`)).toBeInTheDocument()
- expect(screen.getByText(`${milestone.closedIssuesCount} closed`)).toBeInTheDocument()
}
})
})
diff --git a/frontend/__tests__/unit/pages/UserDetails.test.tsx b/frontend/__tests__/unit/pages/UserDetails.test.tsx
index 154dbd1f77..4ce7410edf 100644
--- a/frontend/__tests__/unit/pages/UserDetails.test.tsx
+++ b/frontend/__tests__/unit/pages/UserDetails.test.tsx
@@ -12,32 +12,6 @@ jest.mock('@apollo/client/react', () => ({
useQuery: jest.fn(),
}))
-// Mock Badges component
-jest.mock('components/Badges', () => {
- const MockBadges = ({
- name,
- cssClass,
- showTooltip,
- }: {
- name: string
- cssClass: string
- showTooltip?: boolean
- }) => (
-
-
-
- )
- MockBadges.displayName = 'MockBadges'
- return {
- __esModule: true,
- default: MockBadges,
- }
-})
-
const mockRouter = {
push: jest.fn(),
}
@@ -72,13 +46,11 @@ describe('UserSummary', () => {
name: 'John Doe',
avatarUrl: 'https://example.com/avatar.png',
url: 'https://github.com/johndoe',
+ bio: 'Bio text',
}
render(
[0]['user']}
formattedBio={Bio text}
/>
)
@@ -90,25 +62,6 @@ describe('UserSummary', () => {
expect(screen.getByText('Bio text')).toBeInTheDocument()
})
- test('renders contribution heatmap when hasContributionData is true', () => {
- const user = {
- login: 'jane',
- name: 'Jane',
- avatarUrl: '/avatar.png',
- url: 'https://github.com/jane',
- }
- render(
-
- )
- expect(screen.getByTestId('contribution-heatmap')).toBeInTheDocument()
- })
-
test('uses login as avatar alt when user has no name', () => {
const user = {
login: 'nologin',
@@ -117,50 +70,10 @@ describe('UserSummary', () => {
url: 'https://github.com/nologin',
}
render(
-
+ [0]['user']} formattedBio={null} />
)
expect(screen.getByRole('img', { name: 'nologin' })).toBeInTheDocument()
})
-
- test('uses placeholder avatar and fallback alt when user is null', () => {
- render(
-
- )
- const img = screen.getByRole('img', { name: 'User Avatar' })
- expect(img).toHaveAttribute('src', expect.stringContaining('placeholder.svg'))
- })
-
- test('renders badges when user has badges', () => {
- const user = {
- login: 'badged',
- name: 'Badged User',
- avatarUrl: '/a.png',
- url: 'https://github.com/badged',
- badges: [{ id: 'b1', name: 'Star', cssClass: 'fa-star', description: 'Star', weight: 1 }],
- }
- render(
-
- )
- expect(screen.getByTestId('badge-star')).toBeInTheDocument()
- })
})
describe('UserDetailsPage', () => {
@@ -176,21 +89,6 @@ describe('UserDetailsPage', () => {
jest.clearAllMocks()
})
- // Helper functions to reduce nesting depth
- const getBadgeElements = () => {
- return screen.getAllByTestId(/^badge-/)
- }
-
- const getBadgeTestIds = () => {
- const badgeElements = getBadgeElements()
- return badgeElements.map((element) => element.dataset.testid)
- }
-
- const expectBadgesInCorrectOrder = (expectedOrder: string[]) => {
- const badgeTestIds = getBadgeTestIds()
- expect(badgeTestIds).toEqual(expectedOrder)
- }
-
test('renders loading state', async () => {
;(useQuery as unknown as jest.Mock).mockReturnValue({
data: null,
@@ -222,7 +120,6 @@ describe('UserDetailsPage', () => {
expect(screen.getByText('Test User')).toBeInTheDocument()
})
- expect(screen.getByText('Statistics')).toBeInTheDocument()
expect(screen.getByText('Test Company')).toBeInTheDocument()
expect(screen.getByText('Test Location')).toBeInTheDocument()
expect(screen.getByText('10 Followers')).toBeInTheDocument()
@@ -305,7 +202,6 @@ describe('UserDetailsPage', () => {
expect(screen.getByText(milestone.title)).toBeInTheDocument()
expect(screen.getByText(milestone.repositoryName)).toBeInTheDocument()
expect(screen.getByText(`${milestone.openIssuesCount} open`)).toBeInTheDocument()
- expect(screen.getByText(`${milestone.closedIssuesCount} closed`)).toBeInTheDocument()
}
})
})
@@ -344,9 +240,6 @@ describe('UserDetailsPage', () => {
render()
await waitFor(() => {
- const statisticsTitle = screen.getByText('Statistics')
- expect(statisticsTitle).toBeInTheDocument()
-
const followersCount = screen.getByText('10 Followers')
expect(followersCount).toBeInTheDocument()
@@ -413,13 +306,11 @@ describe('UserDetailsPage', () => {
expect(userName).toBeInTheDocument()
const avatar = screen.getByAltText('Test User')
expect(avatar).toHaveClass('rounded-full')
- expect(avatar).toHaveClass('h-[200px]')
- expect(avatar).toHaveClass('w-[200px]')
+ expect(avatar).toHaveClass('size-32')
// Check for responsive classes
const summaryContainer = avatar.closest('div.flex')
expect(summaryContainer).toHaveClass('flex-col')
- expect(summaryContainer).toHaveClass('lg:flex-row')
})
})
@@ -516,8 +407,7 @@ describe('UserDetailsPage', () => {
render()
await waitFor(() => {
- expect(screen.getByText('Recent Issues')).toBeInTheDocument()
- expect(screen.getByText('No recent releases.')).toBeInTheDocument()
+ expect(screen.queryByText('Recent Issues')).not.toBeInTheDocument()
})
})
@@ -534,8 +424,7 @@ describe('UserDetailsPage', () => {
render()
await waitFor(() => {
- expect(screen.getByText('Recent Pull Requests')).toBeInTheDocument()
- expect(screen.queryByText('Test Pull Request')).not.toBeInTheDocument()
+ expect(screen.queryByText('Recent Pull Requests')).not.toBeInTheDocument()
})
})
@@ -551,8 +440,7 @@ describe('UserDetailsPage', () => {
})
render()
await waitFor(() => {
- expect(screen.getByText('Recent Releases')).toBeInTheDocument()
- expect(screen.queryByText('Test v1.0.0')).not.toBeInTheDocument()
+ expect(screen.queryByText('Recent Releases')).not.toBeInTheDocument()
})
})
@@ -568,8 +456,7 @@ describe('UserDetailsPage', () => {
})
render()
await waitFor(() => {
- expect(screen.getByText('Recent Milestones')).toBeInTheDocument()
- expect(screen.queryByText('v2.0.0 Release')).not.toBeInTheDocument()
+ expect(screen.queryByText('Recent Milestones')).not.toBeInTheDocument()
})
})
@@ -619,8 +506,7 @@ describe('UserDetailsPage', () => {
await waitFor(() => {
expect(screen.getAllByText('N/A').length).toBe(3)
const bioContainer = screen.getByText('@testuser').closest('div')
- expect(bioContainer).toHaveClass('text-center')
- expect(bioContainer).toHaveClass('lg:text-left')
+ expect(bioContainer).toHaveClass('text-left')
})
})
@@ -635,274 +521,6 @@ describe('UserDetailsPage', () => {
})
})
- describe('Badge Display Tests', () => {
- test('renders badges section when user has badges', async () => {
- ;(useQuery as unknown as jest.Mock).mockReturnValue({
- data: mockUserDetailsData,
- loading: false,
- error: null,
- })
-
- render()
- await waitFor(() => {
- expect(screen.getByTestId('badge-contributor')).toBeInTheDocument()
- expect(screen.getByTestId('badge-security-expert')).toBeInTheDocument()
- })
- })
-
- test('renders badges with correct props', async () => {
- ;(useQuery as unknown as jest.Mock).mockReturnValue({
- data: mockUserDetailsData,
- loading: false,
- error: null,
- })
-
- render()
- await waitFor(() => {
- const contributorBadge = screen.getByTestId('badge-contributor')
- expect(contributorBadge).toHaveAttribute('data-css-class', 'fa-medal')
- expect(contributorBadge).toHaveAttribute('data-show-tooltip', 'true')
-
- const securityBadge = screen.getByTestId('badge-security-expert')
- expect(securityBadge).toHaveAttribute('data-css-class', 'fa-shield-alt')
- expect(securityBadge).toHaveAttribute('data-show-tooltip', 'true')
- })
- })
-
- test('does not render badges section when user has no badges', async () => {
- const dataWithoutBadges = {
- ...mockUserDetailsData,
- user: {
- ...mockUserDetailsData.user,
- badges: [],
- badgeCount: 0,
- },
- }
-
- ;(useQuery as unknown as jest.Mock).mockReturnValue({
- data: dataWithoutBadges,
- loading: false,
- error: null,
- })
-
- render()
- await waitFor(() => {
- expect(screen.queryByTestId(/^badge-/)).not.toBeInTheDocument()
- })
- })
-
- test('does not render badges section when badges is undefined', async () => {
- const dataWithoutBadges = {
- ...mockUserDetailsData,
- user: {
- ...mockUserDetailsData.user,
- badges: undefined,
- badgeCount: 0,
- },
- }
-
- ;(useQuery as unknown as jest.Mock).mockReturnValue({
- data: dataWithoutBadges,
- loading: false,
- error: null,
- })
-
- render()
- await waitFor(() => {
- expect(screen.queryByTestId(/^badge-/)).not.toBeInTheDocument()
- })
- })
-
- test('renders badges with fallback cssClass when not provided', async () => {
- const dataWithIncompleteBadges = {
- ...mockUserDetailsData,
- user: {
- ...mockUserDetailsData.user,
- badges: [
- {
- id: '1',
- name: 'Test Badge',
- cssClass: undefined,
- description: 'Test description',
- weight: 1,
- },
- ],
- },
- }
-
- ;(useQuery as unknown as jest.Mock).mockReturnValue({
- data: dataWithIncompleteBadges,
- loading: false,
- error: null,
- })
-
- render()
- await waitFor(() => {
- const badge = screen.getByTestId('badge-test-badge')
- expect(badge).toHaveAttribute('data-css-class', 'medal')
- })
- })
-
- test('renders badges with empty cssClass fallback', async () => {
- const dataWithEmptyCssClass = {
- ...mockUserDetailsData,
- user: {
- ...mockUserDetailsData.user,
- badges: [
- {
- id: '1',
- name: 'Test Badge',
- cssClass: '',
- description: 'Test description',
- weight: 1,
- },
- ],
- },
- }
-
- ;(useQuery as unknown as jest.Mock).mockReturnValue({
- data: dataWithEmptyCssClass,
- loading: false,
- error: null,
- })
-
- render()
- await waitFor(() => {
- const badge = screen.getByTestId('badge-test-badge')
- expect(badge).toHaveAttribute('data-css-class', 'medal')
- })
- })
-
- test('handles badges with special characters in names', async () => {
- const dataWithSpecialBadges = {
- ...mockUserDetailsData,
- user: {
- ...mockUserDetailsData.user,
- badges: [
- {
- id: '1',
- name: 'Badge & More!',
- cssClass: 'fa-star',
- description: 'Special badge',
- weight: 1,
- },
- ],
- },
- }
- ;(useQuery as unknown as jest.Mock).mockReturnValue({
- data: dataWithSpecialBadges,
- loading: false,
- error: null,
- })
-
- render()
- await waitFor(() => {
- expect(screen.getByTestId('badge-badge-&-more!')).toBeInTheDocument()
- })
- })
-
- test('handles badges with long names', async () => {
- const dataWithLongNameBadge = {
- ...mockUserDetailsData,
- user: {
- ...mockUserDetailsData.user,
- badges: [
- {
- id: '1',
- name: 'Very Long Badge Name That Exceeds Normal Length',
- cssClass: 'fa-trophy',
- description: 'Long name badge',
- weight: 1,
- },
- ],
- },
- }
-
- ;(useQuery as unknown as jest.Mock).mockReturnValue({
- data: dataWithLongNameBadge,
- loading: false,
- error: null,
- })
-
- render()
- await waitFor(() => {
- expect(
- screen.getByTestId('badge-very-long-badge-name-that-exceeds-normal-length')
- ).toBeInTheDocument()
- })
- })
-
- // eslint-disable-next-line jest/expect-expect
- test('renders badges in correct order as returned by backend (weight ASC then name ASC)', async () => {
- // Backend returns badges sorted by weight ASC, then name ASC
- // This test verifies the frontend preserves the backend ordering
- const dataWithOrderedBadges = {
- ...mockUserDetailsData,
- user: {
- ...mockUserDetailsData.user,
- badges: [
- // Backend returns badges in this order: weight ASC, then name ASC
- {
- id: '3',
- name: 'Alpha Badge',
- cssClass: 'fa-star',
- description: 'Alpha badge with weight 1',
- weight: 1,
- },
- {
- id: '4',
- name: 'Beta Badge',
- cssClass: 'fa-trophy',
- description: 'Beta badge with weight 1',
- weight: 1,
- },
- {
- id: '1',
- name: 'Contributor',
- cssClass: 'medal',
- description: 'Active contributor',
- weight: 1,
- },
- {
- id: '2',
- name: 'Security Expert',
- cssClass: 'fa-shield-alt',
- description: 'Security expertise',
- weight: 2,
- },
- {
- id: '5',
- name: 'Top Contributor',
- cssClass: 'fa-crown',
- description: 'Highest weight badge',
- weight: 3,
- },
- ],
- badgeCount: 5,
- },
- }
-
- ;(useQuery as unknown as jest.Mock).mockReturnValue({
- data: dataWithOrderedBadges,
- loading: false,
- error: null,
- })
-
- render()
- await waitFor(() => {
- // Expected order matches backend contract: weight ASC (1, 1, 1, 2, 3), then name ASC for equal weights
- const expectedOrder = [
- 'badge-alpha-badge', // weight 1, name ASC
- 'badge-beta-badge', // weight 1, name ASC
- 'badge-contributor', // weight 1, name ASC
- 'badge-security-expert', // weight 2
- 'badge-top-contributor', // weight 3
- ]
- expectBadgesInCorrectOrder(expectedOrder)
- })
- })
- })
-
describe('Contribution Heatmap', () => {
test('does not render heatmap when user has empty contribution data', async () => {
const dataWithEmptyContributions = {
@@ -1121,7 +739,7 @@ describe('UserDetailsPage', () => {
render()
await waitFor(() => {
- expect(screen.getByText('Joined:')).toBeInTheDocument()
+ expect(screen.getByText('Joined :')).toBeInTheDocument()
expect(screen.getByText('Not available')).toBeInTheDocument()
})
})
diff --git a/frontend/src/app/members/[memberKey]/page.tsx b/frontend/src/app/members/[memberKey]/page.tsx
index 15e69f3ef6..8981c9e863 100644
--- a/frontend/src/app/members/[memberKey]/page.tsx
+++ b/frontend/src/app/members/[memberKey]/page.tsx
@@ -1,89 +1,30 @@
'use client'
import { useQuery } from '@apollo/client/react'
-import Image from 'next/image'
import Link from 'next/link'
import { useParams } from 'next/navigation'
import React, { useEffect, useMemo } from 'react'
-import { FaCodeMerge, FaFolderOpen, FaPersonWalkingArrowRight, FaUserPlus } from 'react-icons/fa6'
import { handleAppError, ErrorDisplay } from 'app/global-error'
import { GetUserDataDocument } from 'types/__generated__/userQueries.generated'
-import { Badge } from 'types/badge'
import { User } from 'types/user'
-import { formatDate } from 'utils/dateFormatter'
-import Badges from 'components/Badges'
-import Contributions from 'components/cards/Contributions'
-import Contributors from 'components/cards/Contributors'
-import Header from 'components/cards/Header'
-import IssuesMilestones from 'components/cards/IssuesMilestones'
-import Metadata from 'components/cards/Metadata'
+import MemberDetailSidebar from 'components/cards/MemberDetailSidebar'
import PageWrapper from 'components/cards/PageWrapper'
import RepositoriesModules from 'components/cards/RepositoriesModules'
-import Summary from 'components/cards/Summary'
import ContributionHeatmap from 'components/ContributionHeatmap'
-import MemberDetailsPageSkeleton from 'components/skeletons/MemberDetailsPageSkeleton'
+import Milestones from 'components/Milestones'
+import RecentIssues from 'components/RecentIssues'
+import RecentPullRequests from 'components/RecentPullRequests'
+import RecentReleases from 'components/RecentReleases'
-type DateRange = { startDate: string; endDate: string }
+import MemberDetailsPageSkeleton from 'components/skeletons/MemberDetailsPageSkeleton'
interface UserSummaryProps {
- user: User | null
- contributionData: Record
- dateRange: DateRange
- hasContributionData: boolean
+ user: User
formattedBio: React.ReactNode
}
-export const UserSummary: React.FC = ({
- user,
- contributionData,
- dateRange,
- hasContributionData,
- formattedBio,
-}) => (
-
-
-
-
-
-
- @{user?.login}
-
- {user?.badges && user.badges.length > 0 && (
-
- {user.badges.slice().map((badge: Badge) => (
-
-
-
- ))}
-
- )}
-
-
{formattedBio}
-
- {hasContributionData && dateRange.startDate && dateRange.endDate && (
-
- )}
-
-
+export const UserSummary: React.FC = ({ user, formattedBio }) => (
+
)
const UserDetailsPage: React.FC = () => {
@@ -181,56 +122,59 @@ const UserDetailsPage: React.FC = () => {
)
}
- const userDetails = [
- { label: 'Joined', value: user?.createdAt ? formatDate(user.createdAt) : 'Not available' },
- { label: 'Email', value: user?.email || 'N/A' },
- { label: 'Company', value: user?.company || 'N/A' },
- { label: 'Location', value: user?.location || 'N/A' },
- ]
-
- const userStats = [
- { icon: FaPersonWalkingArrowRight, value: user?.followersCount || 0, unit: 'Follower' },
- { icon: FaUserPlus, value: user?.followingCount || 0, unit: 'Following' },
- {
- icon: FaFolderOpen,
- pluralizedName: 'Repositories',
- unit: 'Repository',
- value: user?.publicRepositoriesCount ?? 0,
- },
- { icon: FaCodeMerge, value: user?.contributionsCount || 0, unit: 'Contribution' },
- ]
-
return (
-
-
-
- }
- />
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ {hasContributionData && dateRange.startDate && dateRange.endDate && (
+
+ )}
+
+ {topRepositories.length > 0 && (
+
+
+
+ )}
+
+
+ {issues.length > 0 && (
+
+
+
+ )}
+ {pullRequests.length > 0 && (
+
+
+
+ )}
+ {milestones.length > 0 && (
+
+
+
+ )}
+ {releases.length > 0 && (
+
+
+
+ )}
+
+
+
+
)
}
diff --git a/frontend/src/components/ContributionHeatmap.tsx b/frontend/src/components/ContributionHeatmap.tsx
index 95a7e2338c..9ff859d089 100644
--- a/frontend/src/components/ContributionHeatmap.tsx
+++ b/frontend/src/components/ContributionHeatmap.tsx
@@ -282,21 +282,21 @@ const ContributionHeatmap: React.FC = ({
const weeksCount = heatmapSeries[0]?.data?.length || 0
if (variant === 'compact') {
- const pixelPerWeek = 13.4
- const padding = 40
+ const pixelPerWeek = 14
+ const padding = 60
const calculatedWidth = weeksCount * pixelPerWeek + padding
return Math.max(400, calculatedWidth)
}
if (variant === 'medium') {
- const pixelPerWeek = 16
- const padding = 45
+ const pixelPerWeek = 17
+ const padding = 70
const calculatedWidth = weeksCount * pixelPerWeek + padding
return Math.max(500, calculatedWidth)
}
- const pixelPerWeek = 19.5
- const padding = 50
+ const pixelPerWeek = 20
+ const padding = 80
const calculatedWidth = weeksCount * pixelPerWeek + padding
return Math.max(600, calculatedWidth)
}, [heatmapSeries, variant])
@@ -316,7 +316,7 @@ const ContributionHeatmap: React.FC = ({
)}
{/* scroll wrapper for small screens */}
-
+
-
+
= ({
width={chartWidth}
/>
+
+
+
)
diff --git a/frontend/src/components/InfoBlock.tsx b/frontend/src/components/InfoBlock.tsx
index 281e9777f1..3787d7723d 100644
--- a/frontend/src/components/InfoBlock.tsx
+++ b/frontend/src/components/InfoBlock.tsx
@@ -7,14 +7,13 @@ import { pluralize } from 'utils/pluralize'
const InfoBlock = ({
className = '',
icon,
- label = '',
pluralizedName,
precision = 1,
unit = '',
value = 0,
}: {
className?: string
- icon: IconType
+ icon?: IconType
label?: string
pluralizedName?: string
precision?: number
@@ -26,15 +25,12 @@ const InfoBlock = ({
const tooltipValue = value ? `${value.toLocaleString()} ${name}` : `No ${name}`
return (
-
-
-
-
- {label &&
{label}
}
-
- {formattedValue}
-
-
+
+ {icon &&
}
+
+
+ {formattedValue}
+
)
diff --git a/frontend/src/components/ItemCardList.tsx b/frontend/src/components/ItemCardList.tsx
index 1bf4a997dc..93a3c58f4c 100644
--- a/frontend/src/components/ItemCardList.tsx
+++ b/frontend/src/components/ItemCardList.tsx
@@ -90,7 +90,7 @@ const ItemCardList = ({
{data && data.length > 0 ? (
{data.map((item, index) => {
const getItemKey = (i: ItemCardData, idx: number): string => {
@@ -107,7 +107,7 @@ const ItemCardList = ({
return (
diff --git a/frontend/src/components/Milestones.tsx b/frontend/src/components/Milestones.tsx
index 5d708718f9..cab31f37e3 100644
--- a/frontend/src/components/Milestones.tsx
+++ b/frontend/src/components/Milestones.tsx
@@ -1,12 +1,6 @@
import { useRouter } from 'next/navigation'
import React from 'react'
-import {
- FaCalendar,
- FaFolderOpen,
- FaSignsPost,
- FaCircleCheck,
- FaCircleExclamation,
-} from 'react-icons/fa6'
+import { FaCalendar, FaFolderOpen, FaSignsPost, FaCircleExclamation } from 'react-icons/fa6'
import type { Milestone } from 'types/milestone'
import { formatDate } from 'utils/dateFormatter'
import AnchorTitle from 'components/AnchorTitle'
@@ -43,10 +37,6 @@ const Milestones: React.FC
= ({
{item.createdAt ? formatDate(item.createdAt) : 'N/A'}
-
-
- {item.closedIssuesCount} closed
-
{item.openIssuesCount} open
diff --git a/frontend/src/components/RecentReleases.tsx b/frontend/src/components/RecentReleases.tsx
index d1c02d6aff..e736b6a8e8 100644
--- a/frontend/src/components/RecentReleases.tsx
+++ b/frontend/src/components/RecentReleases.tsx
@@ -27,7 +27,7 @@ const RecentReleases: React.FC
= ({
>
{data && data.length > 0 ? (
{data.map((item) => (
= ({
}
return (
-
+
{showAvatar && release?.author && (
diff --git a/frontend/src/components/RepositoryCard.tsx b/frontend/src/components/RepositoryCard.tsx
index 03c0b5a661..985ec273b2 100644
--- a/frontend/src/components/RepositoryCard.tsx
+++ b/frontend/src/components/RepositoryCard.tsx
@@ -11,7 +11,7 @@ import StatusBadge from 'components/StatusBadge'
import { TruncatedText } from 'components/TruncatedText'
const RepositoryCard: React.FC
= ({
- maxInitialDisplay = 4,
+ maxInitialDisplay = 6,
repositories,
}) => {
const [showAllRepositories, setShowAllRepositories] = useState(false)
@@ -25,7 +25,7 @@ const RepositoryCard: React.FC = ({
: repositories.slice(0, maxInitialDisplay)
return (
-
+
{displayedRepositories.map((repository) => {
return (
{
+ const details: Detail[] = [
+ { label: 'Joined', value: user.createdAt ? formatDate(user.createdAt) : 'Not available' },
+ { label: 'Email', value: user.email || 'N/A' },
+ { label: 'Company', value: user.company || 'N/A' },
+ { label: 'Location', value: user.location || 'N/A' },
+ ]
+
+ const stats: Stat[] = [
+ { icon: FaPersonWalkingArrowRight, value: user.followersCount || 0, unit: 'Follower' },
+ { icon: FaUserPlus, value: user.followingCount || 0, unit: 'Following' },
+ {
+ icon: FaFolderOpen,
+ pluralizedName: 'Repositories',
+ unit: 'Repository',
+ value: user.publicRepositoriesCount ?? 0,
+ },
+ { icon: FaCodeMerge, value: user.contributionsCount || 0, unit: 'Contribution' },
+ ]
+
+ return (
+
+
+
+
+
+ {user.name || user.login}
+
+
+ @{user.login}
+
+ {user.bio && (
+
+ {formattedBio}
+
+ )}
+
+
+
+
+
+ {details.map((detail) => (
+
+ {detail.label} :
+ {detail.value}
+
+ ))}
+
+
+
+ {stats.map((stat) => (
+
+ ))}
+
+
+
+ )
+}
+
+export default MemberDetailSidebar
diff --git a/frontend/src/components/cards/RepositoriesModules.tsx b/frontend/src/components/cards/RepositoriesModules.tsx
index d93bed8d54..9a1c5b7993 100644
--- a/frontend/src/components/cards/RepositoriesModules.tsx
+++ b/frontend/src/components/cards/RepositoriesModules.tsx
@@ -25,7 +25,7 @@ const RepositoriesModules = ({
<>
{repositories.length > 0 && (
}>
-
+
)}
{modules &&
diff --git a/frontend/src/components/skeletons/MemberDetailsPageSkeleton.tsx b/frontend/src/components/skeletons/MemberDetailsPageSkeleton.tsx
index 387b295924..a1c6ce924f 100644
--- a/frontend/src/components/skeletons/MemberDetailsPageSkeleton.tsx
+++ b/frontend/src/components/skeletons/MemberDetailsPageSkeleton.tsx
@@ -4,158 +4,139 @@ import {
CardSection,
PageWrapper,
SectionHeader,
- TitleSection,
TwoColumnSection,
} from 'components/skeletons/sharedSkeletons'
const MemberDetailsPageSkeleton: React.FC = () => {
return (
-
-
- {/* User Summary Card - bg-gray-100 like SecondaryCard */}
-
-
- {/* Avatar */}
-
-
- {/* User Info and Heatmap */}
-
-
- {/* Username with badge */}
-
-
+
+
+ {/* Sidebar */}
+
-
-
-
- {/* Main Grid - User Details and Statistics */}
-
- {/* User Details Card */}
-
-
-
- {[1, 2, 3, 4].map((i) => (
-
-
-
-
+
+
+ {[1, 2, 3, 4].map((i) => (
+
+
+
+
+ ))}
- ))}
-
-
- {/* Statistics Card */}
-
-
-
- {[1, 2, 3, 4].map((i) => (
-
-
-
-
-
-
-
+
+
+ {[1, 2, 3, 4].map((i) => (
+
+ ))}
- ))}
-
-
-
-
- {/* Two Column Layout - Issues and Milestones */}
-
+
+
- {/* Two Column Layout - Pull Requests and Releases */}
-
+ {/* Main content */}
+
+ {/* Heatmap skeleton */}
+
+
+
- {/* Repositories Section */}
-
-
-
-
- {[1, 2, 3, 4].map((i) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ {/* Repositories Section */}
+
+
+
+
+ {[1, 2, 3, 4, 5, 6].map((i) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
- ))}
-
-
-
+
+
+ {/* Two Column Layout - Issues and Pull Requests */}
+
+
+ {/* Two Column Layout - Milestones and Releases */}
+
-
+
)
}