forked from cardosofelipe/fast-next-template
test(forms): add unit tests for FormTextarea and FormSelect components
- Add comprehensive test coverage for FormTextarea and FormSelect components to validate rendering, accessibility, props forwarding, error handling, and behavior. - Introduced function-scoped fixtures in e2e tests to ensure test isolation and address event loop issues with pytest-asyncio and SQLAlchemy.
This commit is contained in:
@@ -368,3 +368,9 @@ async def e2e_org_with_members(e2e_client, e2e_superuser):
|
||||
"user_id": member_id,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# NOTE: Class-scoped fixtures for E2E tests were attempted but have fundamental
|
||||
# issues with pytest-asyncio + SQLAlchemy/asyncpg event loop management.
|
||||
# The function-scoped fixtures above provide proper test isolation.
|
||||
# Performance optimization would require significant infrastructure changes.
|
||||
|
||||
448
frontend/tests/components/forms/FormSelect.test.tsx
Normal file
448
frontend/tests/components/forms/FormSelect.test.tsx
Normal file
@@ -0,0 +1,448 @@
|
||||
/**
|
||||
* Tests for FormSelect Component
|
||||
* Verifies select field rendering, accessibility, and error handling
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
import { FormSelect, type SelectOption } from '@/components/forms/FormSelect';
|
||||
|
||||
// Polyfill for Radix UI Select - jsdom doesn't support these browser APIs
|
||||
beforeAll(() => {
|
||||
Element.prototype.hasPointerCapture = jest.fn(() => false);
|
||||
Element.prototype.setPointerCapture = jest.fn();
|
||||
Element.prototype.releasePointerCapture = jest.fn();
|
||||
Element.prototype.scrollIntoView = jest.fn();
|
||||
window.HTMLElement.prototype.scrollIntoView = jest.fn();
|
||||
});
|
||||
|
||||
// Helper wrapper component to provide form context
|
||||
interface TestFormValues {
|
||||
model: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
function TestWrapper({
|
||||
children,
|
||||
defaultValues = { model: '', category: '' },
|
||||
}: {
|
||||
children: (props: {
|
||||
control: ReturnType<typeof useForm<TestFormValues>>['control'];
|
||||
}) => React.ReactNode;
|
||||
defaultValues?: Partial<TestFormValues>;
|
||||
}) {
|
||||
const form = useForm<TestFormValues>({
|
||||
defaultValues: { model: '', category: '', ...defaultValues },
|
||||
});
|
||||
|
||||
return <FormProvider {...form}>{children({ control: form.control })}</FormProvider>;
|
||||
}
|
||||
|
||||
const mockOptions: SelectOption[] = [
|
||||
{ value: 'claude-opus', label: 'Claude Opus' },
|
||||
{ value: 'claude-sonnet', label: 'Claude Sonnet' },
|
||||
{ value: 'claude-haiku', label: 'Claude Haiku' },
|
||||
];
|
||||
|
||||
describe('FormSelect', () => {
|
||||
describe('Basic Rendering', () => {
|
||||
it('renders with label and select trigger', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Primary Model')).toBeInTheDocument();
|
||||
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with description', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
description="Main model used for this agent"
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Main model used for this agent')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with custom placeholder', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
placeholder="Choose a model"
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Choose a model')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders default placeholder when none provided', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Select primary model')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Required Field', () => {
|
||||
it('shows asterisk when required is true', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(screen.getByText('*')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show asterisk when required is false', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
required={false}
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(screen.queryByText('*')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Options Rendering', () => {
|
||||
it('renders all options when opened', async () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
// Open the select using fireEvent (works better with Radix UI)
|
||||
fireEvent.click(screen.getByRole('combobox'));
|
||||
|
||||
// Check all options are rendered
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('option', { name: 'Claude Opus' })).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByRole('option', { name: 'Claude Sonnet' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('option', { name: 'Claude Haiku' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('selects option when clicked', async () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
// Open the select and choose an option
|
||||
fireEvent.click(screen.getByRole('combobox'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('option', { name: 'Claude Sonnet' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole('option', { name: 'Claude Sonnet' }));
|
||||
|
||||
// The selected value should now be displayed
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('combobox')).toHaveTextContent('Claude Sonnet');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Disabled State', () => {
|
||||
it('disables select when disabled prop is true', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
disabled
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(screen.getByRole('combobox')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('enables select when disabled prop is false', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
disabled={false}
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(screen.getByRole('combobox')).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pre-selected Value', () => {
|
||||
it('displays pre-selected value', () => {
|
||||
render(
|
||||
<TestWrapper defaultValues={{ model: 'claude-opus' }}>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(screen.getByRole('combobox')).toHaveTextContent('Claude Opus');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('links label to select via htmlFor/id', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
const label = screen.getByText('Primary Model');
|
||||
const select = screen.getByRole('combobox');
|
||||
|
||||
expect(label).toHaveAttribute('for', 'model');
|
||||
expect(select).toHaveAttribute('id', 'model');
|
||||
});
|
||||
|
||||
it('sets aria-describedby with description ID when description exists', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
description="Choose the main model"
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
const select = screen.getByRole('combobox');
|
||||
expect(select).toHaveAttribute('aria-describedby', 'model-description');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom ClassName', () => {
|
||||
it('applies custom className to wrapper', () => {
|
||||
const { container } = render(
|
||||
<TestWrapper>
|
||||
{({ control }) => (
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
className="custom-class"
|
||||
/>
|
||||
)}
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
expect(container.querySelector('.custom-class')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Handling', () => {
|
||||
it('displays error message when field has error', () => {
|
||||
function TestComponent() {
|
||||
const form = useForm<TestFormValues>({
|
||||
defaultValues: { model: '', category: '' },
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
form.setError('model', { type: 'required', message: 'Model is required' });
|
||||
}, [form]);
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={form.control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
/>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
render(<TestComponent />);
|
||||
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('Model is required');
|
||||
});
|
||||
|
||||
it('sets aria-invalid when error exists', () => {
|
||||
function TestComponent() {
|
||||
const form = useForm<TestFormValues>({
|
||||
defaultValues: { model: '', category: '' },
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
form.setError('model', { type: 'required', message: 'Model is required' });
|
||||
}, [form]);
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={form.control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
/>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
render(<TestComponent />);
|
||||
|
||||
expect(screen.getByRole('combobox')).toHaveAttribute('aria-invalid', 'true');
|
||||
});
|
||||
|
||||
it('sets aria-describedby with error ID when error exists', () => {
|
||||
function TestComponent() {
|
||||
const form = useForm<TestFormValues>({
|
||||
defaultValues: { model: '', category: '' },
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
form.setError('model', { type: 'required', message: 'Model is required' });
|
||||
}, [form]);
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={form.control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
/>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
render(<TestComponent />);
|
||||
|
||||
expect(screen.getByRole('combobox')).toHaveAttribute('aria-describedby', 'model-error');
|
||||
});
|
||||
|
||||
it('combines error and description IDs in aria-describedby', () => {
|
||||
function TestComponent() {
|
||||
const form = useForm<TestFormValues>({
|
||||
defaultValues: { model: '', category: '' },
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
form.setError('model', { type: 'required', message: 'Model is required' });
|
||||
}, [form]);
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<FormSelect
|
||||
name="model"
|
||||
control={form.control}
|
||||
label="Primary Model"
|
||||
options={mockOptions}
|
||||
description="Choose the main model"
|
||||
/>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
render(<TestComponent />);
|
||||
|
||||
expect(screen.getByRole('combobox')).toHaveAttribute(
|
||||
'aria-describedby',
|
||||
'model-error model-description'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
281
frontend/tests/components/forms/FormTextarea.test.tsx
Normal file
281
frontend/tests/components/forms/FormTextarea.test.tsx
Normal file
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* Tests for FormTextarea Component
|
||||
* Verifies textarea field rendering, accessibility, and error handling
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { FormTextarea } from '@/components/forms/FormTextarea';
|
||||
import type { FieldError } from 'react-hook-form';
|
||||
|
||||
describe('FormTextarea', () => {
|
||||
describe('Basic Rendering', () => {
|
||||
it('renders with label and textarea', () => {
|
||||
render(<FormTextarea label="Description" name="description" />);
|
||||
|
||||
expect(screen.getByLabelText('Description')).toBeInTheDocument();
|
||||
expect(screen.getByRole('textbox')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with description', () => {
|
||||
render(
|
||||
<FormTextarea
|
||||
label="Personality Prompt"
|
||||
name="personality"
|
||||
description="Define the agent's personality and behavior"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Define the agent's personality and behavior")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders description before textarea', () => {
|
||||
const { container } = render(
|
||||
<FormTextarea label="Description" name="description" description="Helper text" />
|
||||
);
|
||||
|
||||
const description = container.querySelector('#description-description');
|
||||
const textarea = container.querySelector('textarea');
|
||||
|
||||
// Get positions
|
||||
const descriptionRect = description?.getBoundingClientRect();
|
||||
const textareaRect = textarea?.getBoundingClientRect();
|
||||
|
||||
// Description should appear (both should exist)
|
||||
expect(description).toBeInTheDocument();
|
||||
expect(textarea).toBeInTheDocument();
|
||||
|
||||
// In the DOM order, description comes before textarea
|
||||
expect(descriptionRect).toBeDefined();
|
||||
expect(textareaRect).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Required Field', () => {
|
||||
it('shows asterisk when required is true', () => {
|
||||
render(<FormTextarea label="Description" name="description" required />);
|
||||
|
||||
expect(screen.getByText('*')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show asterisk when required is false', () => {
|
||||
render(<FormTextarea label="Description" name="description" required={false} />);
|
||||
|
||||
expect(screen.queryByText('*')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Handling', () => {
|
||||
it('displays error message when error prop is provided', () => {
|
||||
const error: FieldError = {
|
||||
type: 'required',
|
||||
message: 'Description is required',
|
||||
};
|
||||
|
||||
render(<FormTextarea label="Description" name="description" error={error} />);
|
||||
|
||||
expect(screen.getByText('Description is required')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('sets aria-invalid when error exists', () => {
|
||||
const error: FieldError = {
|
||||
type: 'required',
|
||||
message: 'Description is required',
|
||||
};
|
||||
|
||||
render(<FormTextarea label="Description" name="description" error={error} />);
|
||||
|
||||
const textarea = screen.getByRole('textbox');
|
||||
expect(textarea).toHaveAttribute('aria-invalid', 'true');
|
||||
});
|
||||
|
||||
it('sets aria-describedby with error ID when error exists', () => {
|
||||
const error: FieldError = {
|
||||
type: 'required',
|
||||
message: 'Description is required',
|
||||
};
|
||||
|
||||
render(<FormTextarea label="Description" name="description" error={error} />);
|
||||
|
||||
const textarea = screen.getByRole('textbox');
|
||||
expect(textarea).toHaveAttribute('aria-describedby', 'description-error');
|
||||
});
|
||||
|
||||
it('renders error with role="alert"', () => {
|
||||
const error: FieldError = {
|
||||
type: 'required',
|
||||
message: 'Description is required',
|
||||
};
|
||||
|
||||
render(<FormTextarea label="Description" name="description" error={error} />);
|
||||
|
||||
const errorElement = screen.getByRole('alert');
|
||||
expect(errorElement).toHaveTextContent('Description is required');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('links label to textarea via htmlFor/id', () => {
|
||||
render(<FormTextarea label="Description" name="description" />);
|
||||
|
||||
const label = screen.getByText('Description');
|
||||
const textarea = screen.getByRole('textbox');
|
||||
|
||||
expect(label).toHaveAttribute('for', 'description');
|
||||
expect(textarea).toHaveAttribute('id', 'description');
|
||||
});
|
||||
|
||||
it('sets aria-describedby with description ID when description exists', () => {
|
||||
render(
|
||||
<FormTextarea
|
||||
label="Description"
|
||||
name="description"
|
||||
description="Enter a detailed description"
|
||||
/>
|
||||
);
|
||||
|
||||
const textarea = screen.getByRole('textbox');
|
||||
expect(textarea).toHaveAttribute('aria-describedby', 'description-description');
|
||||
});
|
||||
|
||||
it('combines error and description IDs in aria-describedby', () => {
|
||||
const error: FieldError = {
|
||||
type: 'required',
|
||||
message: 'Description is required',
|
||||
};
|
||||
|
||||
render(
|
||||
<FormTextarea
|
||||
label="Description"
|
||||
name="description"
|
||||
description="Enter a detailed description"
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
|
||||
const textarea = screen.getByRole('textbox');
|
||||
expect(textarea).toHaveAttribute(
|
||||
'aria-describedby',
|
||||
'description-error description-description'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Textarea Props Forwarding', () => {
|
||||
it('forwards textarea props correctly', () => {
|
||||
render(
|
||||
<FormTextarea
|
||||
label="Description"
|
||||
name="description"
|
||||
placeholder="Enter description"
|
||||
rows={5}
|
||||
disabled
|
||||
/>
|
||||
);
|
||||
|
||||
const textarea = screen.getByRole('textbox');
|
||||
expect(textarea).toHaveAttribute('placeholder', 'Enter description');
|
||||
expect(textarea).toHaveAttribute('rows', '5');
|
||||
expect(textarea).toBeDisabled();
|
||||
});
|
||||
|
||||
it('accepts register() props via registration', () => {
|
||||
const registerProps = {
|
||||
name: 'description',
|
||||
onChange: jest.fn(),
|
||||
onBlur: jest.fn(),
|
||||
ref: jest.fn(),
|
||||
};
|
||||
|
||||
render(<FormTextarea label="Description" registration={registerProps} />);
|
||||
|
||||
const textarea = screen.getByRole('textbox');
|
||||
expect(textarea).toBeInTheDocument();
|
||||
expect(textarea).toHaveAttribute('id', 'description');
|
||||
});
|
||||
|
||||
it('extracts name from spread props', () => {
|
||||
const spreadProps = {
|
||||
name: 'content',
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
|
||||
render(<FormTextarea label="Content" {...spreadProps} />);
|
||||
|
||||
const textarea = screen.getByRole('textbox');
|
||||
expect(textarea).toHaveAttribute('id', 'content');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Cases', () => {
|
||||
it('throws error when name is not provided', () => {
|
||||
// Suppress console.error for this test
|
||||
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
expect(() => {
|
||||
render(<FormTextarea label="Description" />);
|
||||
}).toThrow('FormTextarea: name must be provided either explicitly or via register()');
|
||||
|
||||
consoleError.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Layout and Styling', () => {
|
||||
it('applies correct spacing classes', () => {
|
||||
const { container } = render(<FormTextarea label="Description" name="description" />);
|
||||
|
||||
const wrapper = container.firstChild as HTMLElement;
|
||||
expect(wrapper).toHaveClass('space-y-2');
|
||||
});
|
||||
|
||||
it('applies correct error styling', () => {
|
||||
const error: FieldError = {
|
||||
type: 'required',
|
||||
message: 'Description is required',
|
||||
};
|
||||
|
||||
render(<FormTextarea label="Description" name="description" error={error} />);
|
||||
|
||||
const errorElement = screen.getByRole('alert');
|
||||
expect(errorElement).toHaveClass('text-sm', 'text-destructive');
|
||||
});
|
||||
|
||||
it('applies correct description styling', () => {
|
||||
const { container } = render(
|
||||
<FormTextarea label="Description" name="description" description="Helper text" />
|
||||
);
|
||||
|
||||
const description = container.querySelector('#description-description');
|
||||
expect(description).toHaveClass('text-sm', 'text-muted-foreground');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Name Priority', () => {
|
||||
it('uses explicit name over registration name', () => {
|
||||
const registerProps = {
|
||||
name: 'fromRegister',
|
||||
onChange: jest.fn(),
|
||||
onBlur: jest.fn(),
|
||||
ref: jest.fn(),
|
||||
};
|
||||
|
||||
render(<FormTextarea label="Content" name="explicit" registration={registerProps} />);
|
||||
|
||||
const textarea = screen.getByRole('textbox');
|
||||
expect(textarea).toHaveAttribute('id', 'explicit');
|
||||
});
|
||||
|
||||
it('uses registration name when explicit name not provided', () => {
|
||||
const registerProps = {
|
||||
name: 'fromRegister',
|
||||
onChange: jest.fn(),
|
||||
onBlur: jest.fn(),
|
||||
ref: jest.fn(),
|
||||
};
|
||||
|
||||
render(<FormTextarea label="Content" registration={registerProps} />);
|
||||
|
||||
const textarea = screen.getByRole('textbox');
|
||||
expect(textarea).toHaveAttribute('id', 'fromRegister');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user