cadfa70e60
* #708 apply strictier linting rules and fix simple eslint bugs * #708 fix eslint errors relate to promise * #708 fix eslint import/no-extraneous-dependencies * #708 fix eslint errors of react-hook * #708 enable eslint check for typescript --------- Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
345 lines
10 KiB
TypeScript
345 lines
10 KiB
TypeScript
import SearchBar from '@/components/Menubar/EventSearchBar'
|
|
import * as searchThunk from '@/features/Search/SearchSlice'
|
|
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import { renderWithProviders } from '../../utils/Renderwithproviders'
|
|
|
|
describe('EventSearchBar', () => {
|
|
const today = new Date()
|
|
const start = new Date(today)
|
|
start.setHours(10, 0, 0, 0)
|
|
const end = new Date(today)
|
|
end.setHours(11, 0, 0, 0)
|
|
const preloadedState = {
|
|
user: {
|
|
userData: {
|
|
sub: 'test',
|
|
email: 'test@test.com',
|
|
sid: 'mockSid',
|
|
openpaasId: 'user1'
|
|
},
|
|
tokens: { accessToken: 'token' }
|
|
},
|
|
calendars: {
|
|
list: {
|
|
'user1/cal1': {
|
|
name: 'Calendar personal',
|
|
id: 'user1/cal1',
|
|
color: { light: '#FF0000', dark: '#000' },
|
|
owner: { emails: ['alice@example.com'] },
|
|
events: {
|
|
event1: {
|
|
id: 'event1',
|
|
calId: 'user1/cal1',
|
|
uid: 'event1',
|
|
title: 'Test Event',
|
|
start: start.toISOString(),
|
|
end: end.toISOString(),
|
|
partstat: 'ACCEPTED',
|
|
organizer: {
|
|
cn: 'Alice',
|
|
cal_address: 'alice@example.com'
|
|
},
|
|
attendee: [
|
|
{
|
|
cn: 'Alice',
|
|
partstat: 'ACCEPTED',
|
|
rsvp: 'TRUE',
|
|
role: 'REQ-PARTICIPANT',
|
|
cutype: 'INDIVIDUAL',
|
|
cal_address: 'alice@example.com'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
'user2/cal1': {
|
|
name: 'Calendar delegated',
|
|
delegated: true,
|
|
id: 'user2/cal1',
|
|
color: { light: '#FF0000', dark: '#000' },
|
|
owner: { emails: ['alice@example.com'] },
|
|
events: {
|
|
event1: {
|
|
id: 'event1',
|
|
calId: 'user2/cal1',
|
|
uid: 'event1',
|
|
title: 'Test Event',
|
|
start: start.toISOString(),
|
|
end: end.toISOString(),
|
|
partstat: 'ACCEPTED',
|
|
organizer: {
|
|
cn: 'Alice',
|
|
cal_address: 'alice@example.com'
|
|
},
|
|
attendee: [
|
|
{
|
|
cn: 'Alice',
|
|
partstat: 'ACCEPTED',
|
|
rsvp: 'TRUE',
|
|
role: 'REQ-PARTICIPANT',
|
|
cutype: 'INDIVIDUAL',
|
|
cal_address: 'alice@example.com'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
'user3/cal1': {
|
|
name: 'Calendar shared',
|
|
id: 'user3/cal1',
|
|
color: { light: '#FF0000', dark: '#000' },
|
|
owner: { emails: ['alice@example.com'] },
|
|
events: {
|
|
event1: {
|
|
id: 'event1',
|
|
calId: 'user3/cal1',
|
|
uid: 'event1',
|
|
title: 'Test Event',
|
|
start: start.toISOString(),
|
|
end: end.toISOString(),
|
|
partstat: 'ACCEPTED',
|
|
organizer: {
|
|
cn: 'Alice',
|
|
cal_address: 'alice@example.com'
|
|
},
|
|
attendee: [
|
|
{
|
|
cn: 'Alice',
|
|
partstat: 'ACCEPTED',
|
|
rsvp: 'TRUE',
|
|
role: 'REQ-PARTICIPANT',
|
|
cutype: 'INDIVIDUAL',
|
|
cal_address: 'alice@example.com'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
pending: false
|
|
}
|
|
}
|
|
|
|
it('should render search icon button initially', () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
const searchButton = screen.getByRole('button')
|
|
expect(searchButton).toBeInTheDocument()
|
|
})
|
|
|
|
it('should expand search bar when icon is clicked', () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
const searchButton = screen.getByRole('button')
|
|
fireEvent.click(searchButton)
|
|
|
|
const searchInput = screen.getByPlaceholderText('common.search')
|
|
expect(searchInput).toBeInTheDocument()
|
|
})
|
|
|
|
it('should unexpand search bar when field is unfocused and empty', () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
const searchButton = screen.getByRole('button')
|
|
fireEvent.click(searchButton)
|
|
|
|
const searchInput = screen.getByPlaceholderText('common.search')
|
|
fireEvent.mouseDown(document.body)
|
|
expect(searchInput).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should not unexpand search bar when field is unfocused but not empty', () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
const searchButton = screen.getByRole('button')
|
|
fireEvent.click(searchButton)
|
|
|
|
const searchInput = screen.getByPlaceholderText('common.search')
|
|
userEvent.type(searchInput, 'test')
|
|
fireEvent.blur(searchInput)
|
|
expect(searchInput).toBeInTheDocument()
|
|
expect(searchInput).toHaveValue('test')
|
|
})
|
|
|
|
it('should update search value on input', () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
const searchButton = screen.getByRole('button')
|
|
fireEvent.click(searchButton)
|
|
|
|
const searchInput = screen.getByPlaceholderText('common.search')
|
|
fireEvent.change(searchInput, { target: { value: 'meeting' } })
|
|
|
|
expect(searchInput).toHaveValue('meeting')
|
|
})
|
|
|
|
it('should open filter popover when tune icon is clicked', async () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
// Expand search bar
|
|
const searchButton = screen.getByRole('button')
|
|
fireEvent.click(searchButton)
|
|
|
|
// Click tune icon
|
|
const tuneButton = screen.getByTestId('TuneIcon')
|
|
if (tuneButton) fireEvent.click(tuneButton)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('search.searchIn')).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
it('should clear search value when clear button is clicked', () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
const searchButton = screen.getByRole('button')
|
|
fireEvent.click(searchButton)
|
|
|
|
const searchInput = screen.getByPlaceholderText('common.search')
|
|
fireEvent.change(searchInput, { target: { value: 'meeting' } })
|
|
|
|
const clearButton = screen.getByTestId('HighlightOffIcon')
|
|
fireEvent.click(clearButton)
|
|
|
|
expect(searchInput).toHaveValue('')
|
|
})
|
|
|
|
it('should trigger search on Enter key', async () => {
|
|
const searchSpy = jest.spyOn(searchThunk, 'searchEventsAsync')
|
|
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
const searchButton = screen.getByRole('button')
|
|
fireEvent.click(searchButton)
|
|
|
|
const searchInput = screen.getByPlaceholderText('common.search')
|
|
fireEvent.change(searchInput, { target: { value: 'test' } })
|
|
fireEvent.keyDown(searchInput, { key: 'Enter' })
|
|
|
|
await waitFor(() => {
|
|
expect(searchSpy).toHaveBeenCalledWith({
|
|
filters: {
|
|
keywords: '',
|
|
organizers: [],
|
|
attendees: [],
|
|
searchIn: ['user1/cal1']
|
|
},
|
|
search: 'test'
|
|
})
|
|
})
|
|
})
|
|
it('should not trigger search on Enter key when search is empty', async () => {
|
|
const searchSpy = jest.spyOn(searchThunk, 'searchEventsAsync')
|
|
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
const searchButton = screen.getByRole('button')
|
|
fireEvent.click(searchButton)
|
|
|
|
const searchInput = screen.getByPlaceholderText('common.search')
|
|
fireEvent.keyDown(searchInput, { key: 'Enter' })
|
|
|
|
await waitFor(() => {
|
|
expect(searchSpy).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
it('should not trigger search when filter keywords is empty', async () => {
|
|
const searchSpy = jest.spyOn(searchThunk, 'searchEventsAsync')
|
|
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
const searchButton = screen.getByRole('button')
|
|
fireEvent.click(searchButton)
|
|
|
|
// Click tune icon
|
|
const tuneButton = screen.getByTestId('TuneIcon')
|
|
if (tuneButton) fireEvent.click(tuneButton)
|
|
|
|
fireEvent.click(screen.getByText('common.search'))
|
|
|
|
await waitFor(() => {
|
|
expect(searchSpy).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
it('keeps search bar expanded when popover opens', () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
fireEvent.click(screen.getByRole('button'))
|
|
const tuneBtn = screen
|
|
.getAllByRole('button')
|
|
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
|
|
|
fireEvent.click(tuneBtn!)
|
|
|
|
expect(screen.getByPlaceholderText('common.search')).toBeInTheDocument()
|
|
})
|
|
|
|
it('does NOT collapse search when clicking inside popover', () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
fireEvent.click(screen.getByRole('button'))
|
|
const tuneBtn = screen
|
|
.getAllByRole('button')
|
|
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
|
|
|
fireEvent.click(tuneBtn!)
|
|
|
|
fireEvent.mouseDown(screen.getByText('search.searchIn'))
|
|
|
|
expect(screen.getByPlaceholderText('common.search')).toBeInTheDocument()
|
|
})
|
|
|
|
it('clicking Cancel closes popover and search collapses if empty', async () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
fireEvent.click(screen.getByRole('button'))
|
|
const tuneBtn = screen
|
|
.getAllByRole('button')
|
|
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
|
|
|
fireEvent.click(tuneBtn!)
|
|
|
|
fireEvent.click(screen.getByText('common.cancel'))
|
|
|
|
await waitFor(() =>
|
|
expect(screen.queryByText('search.searchIn')).not.toBeInTheDocument()
|
|
)
|
|
expect(screen.queryByPlaceholderText('common.search')).toBeNull()
|
|
})
|
|
|
|
it('does not collapse when selecting a filter value', async () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
fireEvent.click(screen.getByRole('button'))
|
|
|
|
const tuneBtn = screen
|
|
.getAllByRole('button')
|
|
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
|
|
|
fireEvent.click(tuneBtn!)
|
|
|
|
fireEvent.mouseDown(screen.getByText('search.searchIn')) // simulate interaction inside menu
|
|
|
|
expect(screen.getByPlaceholderText('common.search')).toBeInTheDocument()
|
|
})
|
|
|
|
it('keeps input value after popover closes if not empty', () => {
|
|
renderWithProviders(<SearchBar />, preloadedState)
|
|
|
|
fireEvent.click(screen.getByRole('button'))
|
|
userEvent.type(screen.getByPlaceholderText('common.search'), 'hello')
|
|
|
|
const tuneBtn = screen
|
|
.getAllByRole('button')
|
|
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
|
|
|
fireEvent.click(tuneBtn!)
|
|
fireEvent.click(document.body)
|
|
|
|
const inputAfter = screen.getByPlaceholderText('common.search')
|
|
expect(inputAfter).toHaveValue('hello')
|
|
})
|
|
})
|