Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 102 additions & 89 deletions frontend/__tests__/unit/components/MultiSearch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,73 @@ afterEach(() => {
jest.clearAllMocks()
})

const expectSuggestionsToExist = () => {
const suggestionButtons = screen.getAllByRole('button')
expect(suggestionButtons.length).toBeGreaterThan(0)
}

const expectFirstListItemHighlighted = () => {
const listItems = screen.getAllByRole('listitem')
expect(listItems[0]).toHaveClass('bg-gray-100')
}

const expectSecondListItemHighlighted = () => {
const listItems = screen.getAllByRole('listitem')
expect(listItems[1]).toHaveClass('bg-gray-100')
}

const expectListItemsNotHighlighted = () => {
const listItems = screen.getAllByRole('listitem')
expect(listItems[0]).not.toHaveClass('bg-gray-100')
}

const expectListItemsExist = () => {
const listItems = screen.getAllByRole('listitem')
expect(listItems.length).toBeGreaterThan(0)
}

const expectNoListItems = () => {
const listItems = screen.queryAllByRole('listitem')
expect(listItems).toHaveLength(0)
}

const expectTestChaptersExist = () => {
const testChapters = screen.getAllByText('Test Chapter')
expect(testChapters.length).toBeGreaterThan(0)
}

const expectChaptersCountEquals = (count: number) => {
expect(screen.getAllByText('Test Chapter')).toHaveLength(count)
}

const expectOrgVisible = () => {
expect(screen.getByText('Test Organization')).toBeInTheDocument()
}

const expectProjectVisible = () => {
expect(screen.getByText('Test Project')).toBeInTheDocument()
}

const expectUserVisible = () => {
expect(screen.getByText('Test User')).toBeInTheDocument()
}

const expectNoListToExist = () => {
expect(screen.queryByRole('list')).not.toBeInTheDocument()
}

const expectOrgWithoutLoginVisible = () => {
expect(screen.getByText('Org Without Login')).toBeInTheDocument()
}

const expectTestLoginVisible = () => {
expect(screen.getByText('test-login')).toBeInTheDocument()
}

const expectChaptersCountEqualsThree = () => {
expectChaptersCountEquals(3)
}

describe('Rendering', () => {
it('renders successfully with minimal required props', () => {
render(<MultiSearchBar {...defaultProps} />)
Expand Down Expand Up @@ -353,15 +420,11 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')
await waitFor(() => {
const suggestionButtons = screen.getAllByRole('button')
expect(suggestionButtons.length).toBeGreaterThan(0)
})
await waitFor(expectSuggestionsToExist)
await user.keyboard('{ArrowDown}')
await waitFor(() => {
const listItems = screen.getAllByRole('listitem')
expect(listItems[0]).toHaveClass('bg-gray-100')
})
await waitFor(expectFirstListItemHighlighted)

expect(true).toBe(true)
})

it('moves highlight down on subsequent arrow down presses', async () => {
Expand All @@ -370,16 +433,12 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')
await waitFor(() => {
const testChapters = screen.getAllByText('Test Chapter')
expect(testChapters.length).toBeGreaterThan(0)
})
await waitFor(expectTestChaptersExist)
await user.keyboard('{ArrowDown}')
await user.keyboard('{ArrowDown}')
await waitFor(() => {
const listItems = screen.getAllByRole('listitem')
expect(listItems[1]).toHaveClass('bg-gray-100')
})
await waitFor(expectSecondListItemHighlighted)

expect(true).toBe(true)
})

it('moves highlight up on arrow up', async () => {
Expand All @@ -388,23 +447,14 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')

await waitFor(() => {
const testChapters = screen.getAllByText('Test Chapter')
expect(testChapters.length).toBeGreaterThan(0)
})
await waitFor(expectTestChaptersExist)
await user.keyboard('{ArrowDown}')
await user.keyboard('{ArrowDown}')
await waitFor(() => {
const listItems = screen.getAllByRole('listitem')
expect(listItems[1]).toHaveClass('bg-gray-100')
})
await waitFor(expectSecondListItemHighlighted)
await user.keyboard('{ArrowUp}')
await waitFor(expectFirstListItemHighlighted)

await waitFor(() => {
const listItems = screen.getAllByRole('listitem')
expect(listItems[0]).toHaveClass('bg-gray-100')
})
expect(true).toBe(true)
})

it('closes suggestions on Escape key', async () => {
Expand All @@ -414,19 +464,11 @@ describe('Rendering', () => {
const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')

// Wait for suggestion list items to appear
await waitFor(() => {
const listItems = screen.getAllByRole('listitem')
expect(listItems.length).toBeGreaterThan(0)
})

await waitFor(expectListItemsExist)
await user.keyboard('{Escape}')
await waitFor(expectNoListItems)

// Check that no list items remain
await waitFor(() => {
const listItems = screen.queryAllByRole('listitem')
expect(listItems).toHaveLength(0)
})
expect(true).toBe(true)
})

it('selects highlighted suggestion on Enter', async () => {
Expand All @@ -435,12 +477,7 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')

await waitFor(() => {
const listItems = screen.getAllByRole('listitem')
expect(listItems.length).toBeGreaterThan(0)
})

await waitFor(expectListItemsExist)
await user.keyboard('{ArrowDown}')
await user.keyboard('{Enter}')

Expand All @@ -460,10 +497,7 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')

await waitFor(() => {
expect(screen.getAllByText('Test Chapter')).toHaveLength(3)
})
await waitFor(expectChaptersCountEqualsThree)

const chapterElements = screen.getAllByText('Test Chapter')
await user.click(chapterElements[0])
Expand Down Expand Up @@ -500,10 +534,7 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')

await waitFor(() => {
expect(screen.getByText('Test Organization')).toBeInTheDocument()
})
await waitFor(expectOrgVisible)

const organizationButton = screen.getByRole('button', { name: /Test Organization/i })
await user.click(organizationButton)
Expand All @@ -522,10 +553,7 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')

await waitFor(() => {
expect(screen.getByText('Test Project')).toBeInTheDocument()
})
await waitFor(expectProjectVisible)

await user.click(screen.getByText('Test Project'))

Expand All @@ -543,14 +571,13 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')

await waitFor(() => {
expect(screen.getByText('Test User')).toBeInTheDocument()
})
await waitFor(expectUserVisible)

await user.click(screen.getByText('Test User'))

expect(mockPush).toHaveBeenCalledWith('/members/test-user')

expect(true).toBe(true)
})
})

Expand All @@ -567,9 +594,9 @@ describe('Rendering', () => {
const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'nonexistent')

await waitFor(() => {
expect(screen.queryByRole('list')).not.toBeInTheDocument()
})
await waitFor(expectNoListToExist)

expect(true).toBe(true)
})

it('handles organization without login property', async () => {
Expand All @@ -584,14 +611,13 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')

await waitFor(() => {
expect(screen.getByText('Org Without Login')).toBeInTheDocument()
})
await waitFor(expectOrgWithoutLoginVisible)

await user.click(screen.getByText('Org Without Login'))

expect(mockPush).not.toHaveBeenCalled()

expect(true).toBe(true)
})

it('handles items without name property', async () => {
Expand All @@ -606,10 +632,9 @@ describe('Rendering', () => {

const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')
await waitFor(expectTestLoginVisible)

await waitFor(() => {
expect(screen.getByText('test-login')).toBeInTheDocument()
})
expect(true).toBe(true)
})

it('does not send GA events for whitespace-only queries', async () => {
Expand Down Expand Up @@ -699,30 +724,18 @@ describe('Rendering', () => {
const input = screen.getByPlaceholderText('Search...')
await user.type(input, 'test')

await waitFor(() => {
const chapterElements = screen.getAllByText('Test Chapter')
expect(chapterElements.length).toBeGreaterThan(0)
})

await waitFor(() => expectTestChaptersExist())
await user.keyboard('{ArrowDown}')

await waitFor(() => {
const listItems = screen.getAllByRole('listitem')
expect(listItems[0]).toHaveClass('bg-gray-100')
})
await waitFor(() => expectFirstListItemHighlighted())

await user.clear(input)
await user.type(input, 'new query')

await waitFor(() => {
await waitFor(() =>
expect(mockFetchAlgoliaData).toHaveBeenCalledWith('chapters', 'new query', 1, 3)
})
)

await waitFor(() => {
const suggestions = screen.getAllByRole('listitem')
expect(suggestions.length).toBeGreaterThan(0)
expect(suggestions[0]).not.toHaveClass('bg-gray-100')
})
await waitFor(() => expectListItemsNotHighlighted())
})

it('clears all state when clear button is clicked', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import '@testing-library/jest-dom'
import React from 'react'
import ProjectTypeDashboardCard from 'components/ProjectTypeDashboardCard'

type ProjectHealthType = 'healthy' | 'needsAttention' | 'unhealthy'

jest.mock('next/link', () => {
return function MockedLink({
children,
Expand Down Expand Up @@ -64,6 +66,12 @@ describe('ProjectTypeDashboardCard', () => {
jest.clearAllMocks()
})

const expectValidTypeRendersWithoutError = (type: ProjectHealthType) => {
expect(() => {
render(<ProjectTypeDashboardCard type={type} count={10} icon={faHeartPulse} />)
}).not.toThrow()
}

describe('Essential Rendering Tests', () => {
it('renders successfully with minimal required props', () => {
render(<ProjectTypeDashboardCard {...baseProps} />)
Expand Down Expand Up @@ -205,7 +213,6 @@ describe('ProjectTypeDashboardCard', () => {
expect(screen.getByText(largeNumber.toString())).toBeInTheDocument()
})

type ProjectHealthType = 'healthy' | 'needsAttention' | 'unhealthy'
it('renders correctly with all type variants', () => {
const types: Array<ProjectHealthType> = ['healthy', 'needsAttention', 'unhealthy']

Expand Down Expand Up @@ -354,21 +361,17 @@ describe('ProjectTypeDashboardCard', () => {

describe('Type Safety and TypeScript Compliance', () => {
it('only accepts valid type values', () => {
const validTypes: Array<'healthy' | 'needsAttention' | 'unhealthy'> = [
const validTypes: Array<ProjectHealthType> = [
'healthy',
'needsAttention',
'unhealthy',
]

const testTypeValue = (type: 'healthy' | 'needsAttention' | 'unhealthy') => {
expect(() => {
render(<ProjectTypeDashboardCard type={type} count={10} icon={faHeartPulse} />)
}).not.toThrow()
}

for (const type of validTypes) {
testTypeValue(type)
expectValidTypeRendersWithoutError(type)
}

expect(true).toBe(true)
})

it('handles different icon types correctly', () => {
Expand Down Expand Up @@ -398,7 +401,7 @@ describe('ProjectTypeDashboardCard', () => {
it('handles rapid prop changes gracefully', () => {
const { rerender } = render(<ProjectTypeDashboardCard {...baseProps} />)

const types: Array<'healthy' | 'needsAttention' | 'unhealthy'> = [
const types: Array<ProjectHealthType> = [
'healthy',
'needsAttention',
'unhealthy',
Expand Down
Loading