73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
import { cleanup, render, screen, fireEvent } from '@testing-library/react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import { RuleBuilder } from './RuleBuilder';
|
|
import type { RuleField } from './fieldCatalog';
|
|
import type { Group } from './types';
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
const FIELDS: RuleField[] = [
|
|
{ name: 'genre', label: 'Genre', type: 'text', group: 'General', values: [] },
|
|
{ name: 'type', label: 'Item type', type: 'enum', group: 'General', values: ['movie', 'episode'] }
|
|
];
|
|
|
|
function setup(initial: Group) {
|
|
const onChange = vi.fn();
|
|
const utils = render(<RuleBuilder value={initial} onChange={onChange} fields={FIELDS} />);
|
|
return { onChange, ...utils };
|
|
}
|
|
|
|
describe('RuleBuilder', () => {
|
|
it('adds a rule', () => {
|
|
const { onChange } = setup({ match: 'all', children: [] });
|
|
fireEvent.click(screen.getByText('Add rule'));
|
|
expect(onChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
match: 'all',
|
|
children: expect.arrayContaining([expect.objectContaining({ field: 'genre' })])
|
|
})
|
|
);
|
|
});
|
|
|
|
it('removes a rule', () => {
|
|
const { onChange } = setup({ match: 'all', children: [{ field: 'genre', operator: 'is', value: 'Horror' }] });
|
|
fireEvent.click(screen.getByLabelText('Remove rule'));
|
|
expect(onChange).toHaveBeenCalledWith({ match: 'all', children: [] });
|
|
});
|
|
|
|
it('adds a nested group only at top level', () => {
|
|
const { onChange } = setup({ match: 'all', children: [] });
|
|
fireEvent.click(screen.getByText('Add group'));
|
|
expect(onChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({ children: expect.arrayContaining([expect.objectContaining({ match: 'any' })]) })
|
|
);
|
|
});
|
|
|
|
it('shows enum values as a dropdown', () => {
|
|
setup({ match: 'all', children: [{ field: 'type', operator: 'is', value: 'movie' }] });
|
|
expect(screen.getByRole('option', { name: 'episode' })).toBeInTheDocument();
|
|
});
|
|
|
|
it('resets operator and drops value/value2 when the field type changes', () => {
|
|
const onChange = vi.fn();
|
|
const fieldsWithNumber = [
|
|
{ name: 'genre', label: 'Genre', type: 'text' as const, group: 'General', values: [] },
|
|
{ name: 'minutes', label: 'Duration', type: 'number' as const, group: 'Technical', values: [] }
|
|
];
|
|
render(
|
|
<RuleBuilder
|
|
value={{ match: 'all', children: [{ field: 'minutes', operator: 'between', value: '10', value2: '90' }] }}
|
|
onChange={onChange}
|
|
fields={fieldsWithNumber}
|
|
/>
|
|
);
|
|
fireEvent.change(screen.getByLabelText('Field'), { target: { value: 'genre' } });
|
|
expect(onChange).toHaveBeenCalledWith({
|
|
match: 'all',
|
|
children: [{ field: 'genre', operator: 'is', value: '' }]
|
|
});
|
|
});
|
|
});
|