-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
prv-proton
committed
Jan 7, 2025
1 parent
747384f
commit 3c988de
Showing
3 changed files
with
338 additions
and
3 deletions.
There are no files selected for viewing
168 changes: 168 additions & 0 deletions
168
frontend/src/components/__tests__/BCDateFloatingFilter.test.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import { describe, it, expect, vi, beforeEach } from 'vitest' | ||
import { render, screen, fireEvent, waitFor } from '@testing-library/react' | ||
import { BCDateFloatingFilter } from './BCDateFloatingFilter' | ||
import { format } from 'date-fns' | ||
|
||
// Mock Material-UI components | ||
vi.mock('@mui/material', () => ({ | ||
FormControl: ({ children, ...props }) => <div {...props}>{children}</div>, | ||
IconButton: ({ children, onClick, ...props }) => ( | ||
<button onClick={onClick} {...props}> | ||
{children} | ||
</button> | ||
), | ||
InputAdornment: ({ children }) => <div>{children}</div> | ||
})) | ||
|
||
vi.mock('@mui/x-date-pickers', () => ({ | ||
DatePicker: ({ value, onChange, onOpen, onClose, slotProps, ...props }) => ( | ||
<div> | ||
<input | ||
type="text" | ||
value={value ? format(value, 'yyyy-MM-dd') : ''} | ||
onChange={(e) => onChange(new Date(e.target.value))} | ||
{...props} | ||
/> | ||
{slotProps.textField.InputProps.startAdornment} | ||
{slotProps.textField.InputProps.endAdornment} | ||
</div> | ||
) | ||
})) | ||
|
||
describe('BCDateFloatingFilter', () => { | ||
const mockOnModelChange = vi.fn() | ||
const defaultProps = { | ||
model: null, | ||
onModelChange: mockOnModelChange, | ||
disabled: false, | ||
minDate: '2013-01-01', | ||
maxDate: '2040-01-01', | ||
initialFilterType: 'equals', | ||
label: 'Select Date' | ||
} | ||
|
||
beforeEach(() => { | ||
mockOnModelChange.mockClear() | ||
}) | ||
|
||
it('renders with default props', () => { | ||
render(<BCDateFloatingFilter {...defaultProps} />) | ||
|
||
expect(screen.getByRole('group')).toBeInTheDocument() | ||
expect(screen.getByLabelText('Open calendar')).toBeInTheDocument() | ||
expect(screen.getByLabelText('Date Picker')).toBeInTheDocument() | ||
}) | ||
|
||
it('handles date selection', async () => { | ||
render(<BCDateFloatingFilter {...defaultProps} />) | ||
|
||
const input = screen.getByLabelText('Date Picker') | ||
const testDate = '2024-01-01' | ||
|
||
fireEvent.change(input, { target: { value: testDate } }) | ||
|
||
expect(mockOnModelChange).toHaveBeenCalledWith({ | ||
type: 'equals', | ||
dateFrom: testDate, | ||
dateTo: null, | ||
filterType: 'date' | ||
}) | ||
}) | ||
|
||
it('handles date clearing', async () => { | ||
const initialModel = { | ||
type: 'equals', | ||
dateFrom: '2024-01-01', | ||
dateTo: null, | ||
filterType: 'date' | ||
} | ||
|
||
render(<BCDateFloatingFilter {...defaultProps} model={initialModel} />) | ||
|
||
const clearButton = screen.getByLabelText('Clear date') | ||
fireEvent.click(clearButton) | ||
|
||
expect(mockOnModelChange).toHaveBeenCalledWith(null) | ||
}) | ||
|
||
it('initializes with model date when provided', () => { | ||
const modelWithDate = { | ||
type: 'equals', | ||
dateFrom: '2024-01-01', | ||
dateTo: null, | ||
filterType: 'date' | ||
} | ||
|
||
render(<BCDateFloatingFilter {...defaultProps} model={modelWithDate} />) | ||
|
||
const input = screen.getByLabelText('Date Picker') | ||
expect(input).toHaveValue('2024-01-01') | ||
}) | ||
|
||
it('disables input when disabled prop is true', () => { | ||
render(<BCDateFloatingFilter {...defaultProps} disabled={true} />) | ||
|
||
const input = screen.getByLabelText('Date Picker') | ||
expect(input).toBeDisabled() | ||
}) | ||
|
||
it('handles invalid date input', async () => { | ||
render(<BCDateFloatingFilter {...defaultProps} />) | ||
|
||
const input = screen.getByLabelText('Date Picker') | ||
fireEvent.change(input, { target: { value: 'invalid-date' } }) | ||
|
||
expect(mockOnModelChange).toHaveBeenCalledWith(null) | ||
}) | ||
|
||
it('opens calendar on calendar icon click', () => { | ||
render(<BCDateFloatingFilter {...defaultProps} />) | ||
|
||
const calendarButton = screen.getByLabelText('Open calendar') | ||
fireEvent.click(calendarButton) | ||
|
||
// Since we're mocking the DatePicker, we can't directly test if the calendar opens, | ||
// but we can verify the click handler was called | ||
expect(calendarButton).toBeInTheDocument() | ||
}) | ||
|
||
it('maintains proper ARIA attributes', () => { | ||
render(<BCDateFloatingFilter {...defaultProps} />) | ||
|
||
const container = screen.getByRole('group') | ||
expect(container).toHaveAttribute('aria-labelledby', 'date-picker-label') | ||
|
||
const datePicker = screen.getByLabelText('Date Picker') | ||
expect(datePicker).toHaveAttribute('aria-describedby', 'date-picker-description') | ||
}) | ||
|
||
it('respects min and max date constraints', () => { | ||
const customProps = { | ||
...defaultProps, | ||
minDate: '2023-01-01', | ||
maxDate: '2025-01-01' | ||
} | ||
|
||
render(<BCDateFloatingFilter {...customProps} />) | ||
|
||
const input = screen.getByLabelText('Date Picker') | ||
expect(input).toBeInTheDocument() | ||
// Note: Actual min/max date validation would be handled by the DatePicker component | ||
}) | ||
|
||
it('updates when model changes externally', () => { | ||
const { rerender } = render(<BCDateFloatingFilter {...defaultProps} />) | ||
|
||
const newModel = { | ||
type: 'equals', | ||
dateFrom: '2024-02-01', | ||
dateTo: null, | ||
filterType: 'date' | ||
} | ||
|
||
rerender(<BCDateFloatingFilter {...defaultProps} model={newModel} />) | ||
|
||
const input = screen.getByLabelText('Date Picker') | ||
expect(input).toHaveValue('2024-02-01') | ||
}) | ||
}) |
166 changes: 166 additions & 0 deletions
166
frontend/src/components/__tests__/BCSelectFloatingFilter.test.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import { describe, it, expect, vi, beforeEach } from 'vitest' | ||
import { render, screen, fireEvent, waitFor } from '@testing-library/react' | ||
import { BCSelectFloatingFilter } from '../BCDataGrid/components' | ||
|
||
describe('BCSelectFloatingFilter', () => { | ||
const mockOnModelChange = vi.fn() | ||
const defaultProps = { | ||
model: null, | ||
onModelChange: mockOnModelChange, | ||
optionsQuery: () => ({ | ||
data: [ | ||
{ value: '1', label: 'Option 1' }, | ||
{ value: '2', label: 'Option 2' }, | ||
{ value: '3', label: 'Option 3' } | ||
], | ||
isLoading: false, | ||
isError: false, | ||
error: null | ||
}), | ||
valueKey: 'value', | ||
labelKey: 'label', | ||
disabled: false, | ||
params: {}, | ||
initialFilterType: 'equals', | ||
multiple: false, | ||
initialSelectedValues: [] | ||
} | ||
|
||
beforeEach(() => { | ||
mockOnModelChange.mockClear() | ||
}) | ||
|
||
it('renders with default props', () => { | ||
render(<BCSelectFloatingFilter {...defaultProps} />) | ||
|
||
const select = screen.getByRole('combobox') | ||
expect(select).toBeInTheDocument() | ||
expect(screen.getByText('Select')).toBeInTheDocument() | ||
expect(screen.getAllByRole('option')).toHaveLength(4) // Including the default "Select" option | ||
}) | ||
|
||
it('handles single selection', async () => { | ||
render(<BCSelectFloatingFilter {...defaultProps} />) | ||
|
||
const select = screen.getByRole('combobox') | ||
fireEvent.change(select, { target: { value: '1' } }) | ||
|
||
expect(mockOnModelChange).toHaveBeenCalledWith({ | ||
type: 'equals', | ||
filter: '1' | ||
}) | ||
}) | ||
|
||
it('handles multiple selection when multiple prop is true', () => { | ||
render(<BCSelectFloatingFilter {...defaultProps} multiple={true} />) | ||
|
||
const select = screen.getByRole('combobox') | ||
fireEvent.change(select, { | ||
target: { | ||
options: [ | ||
{ selected: true, value: '1' }, | ||
{ selected: true, value: '2' } | ||
] | ||
} | ||
}) | ||
|
||
expect(mockOnModelChange).toHaveBeenCalledWith({ | ||
type: 'equals', | ||
filter: ['1', '2'] | ||
}) | ||
}) | ||
|
||
it('shows loading state', () => { | ||
const loadingProps = { | ||
...defaultProps, | ||
optionsQuery: () => ({ | ||
data: null, | ||
isLoading: true, | ||
isError: false, | ||
error: null | ||
}) | ||
} | ||
|
||
render(<BCSelectFloatingFilter {...loadingProps} />) | ||
expect(screen.getByText('Loading...')).toBeInTheDocument() | ||
}) | ||
|
||
it('shows error state', () => { | ||
const errorProps = { | ||
...defaultProps, | ||
optionsQuery: () => ({ | ||
data: null, | ||
isLoading: false, | ||
isError: true, | ||
error: { message: 'Failed to load' } | ||
}) | ||
} | ||
|
||
render(<BCSelectFloatingFilter {...errorProps} />) | ||
expect( | ||
screen.getByText('Error loading options: Failed to load') | ||
).toBeInTheDocument() | ||
}) | ||
|
||
it('clears selection when clear button is clicked', async () => { | ||
render(<BCSelectFloatingFilter {...defaultProps} />) | ||
|
||
// First select a value | ||
const select = screen.getByRole('combobox') | ||
fireEvent.change(select, { target: { value: '1' } }) | ||
|
||
// Then clear it | ||
const clearButton = screen.getByLabelText('Clear selection') | ||
fireEvent.click(clearButton) | ||
|
||
expect(mockOnModelChange).toHaveBeenLastCalledWith(null) | ||
}) | ||
|
||
it('initializes with model value when provided', () => { | ||
const modelProps = { | ||
...defaultProps, | ||
model: { type: 'equals', filter: '2' } | ||
} | ||
|
||
render(<BCSelectFloatingFilter {...modelProps} />) | ||
const select = screen.getByRole('combobox') | ||
expect(select).toHaveValue('2') | ||
}) | ||
|
||
it('initializes with initialSelectedValues when provided', () => { | ||
const initialValueProps = { | ||
...defaultProps, | ||
initialSelectedValues: ['1'] | ||
} | ||
|
||
render(<BCSelectFloatingFilter {...initialValueProps} />) | ||
const select = screen.getByRole('combobox') | ||
expect(select).toHaveValue('1') | ||
}) | ||
|
||
it('disables select when disabled prop is true', () => { | ||
render(<BCSelectFloatingFilter {...defaultProps} disabled={true} />) | ||
const select = screen.getByRole('combobox') | ||
expect(select).toBeDisabled() | ||
}) | ||
|
||
it('handles empty/null selection in single select mode', () => { | ||
render(<BCSelectFloatingFilter {...defaultProps} />) | ||
|
||
const select = screen.getByRole('combobox') | ||
fireEvent.change(select, { target: { value: '0' } }) | ||
|
||
expect(mockOnModelChange).toHaveBeenCalledWith(null) | ||
}) | ||
|
||
it('maintains proper ARIA attributes', () => { | ||
render(<BCSelectFloatingFilter {...defaultProps} />) | ||
|
||
const container = screen.getByRole('group') | ||
expect(container).toHaveAttribute('aria-labelledby', 'select-filter-label') | ||
|
||
const combobox = screen.getByRole('combobox') | ||
expect(combobox).toHaveAttribute('aria-controls', 'select-filter') | ||
expect(combobox).toHaveAttribute('aria-expanded', 'false') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters