@object-ui/fields 3.4.0 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/package.json +7 -7
- package/dist/packages/fields/src/__tests__/boolean-checkbox.test.d.ts +0 -0
- package/dist/packages/fields/src/__tests__/cell-renderers.test.d.ts +0 -0
- package/dist/packages/fields/src/__tests__/datetime-cell.test.d.ts +0 -0
- package/dist/packages/fields/src/__tests__/datetime-timezone.test.d.ts +0 -0
- package/dist/packages/fields/src/__tests__/extreme-inputs.test.d.ts +0 -0
- package/dist/packages/fields/src/__tests__/form-variants.test.d.ts +0 -0
- package/dist/packages/fields/src/lookup-depends-on.test.d.ts +0 -1
- package/dist/packages/fields/src/new-widgets.test.d.ts +0 -8
- package/dist/packages/fields/src/record-picker.test.d.ts +0 -1
- package/dist/packages/fields/src/remaining-widgets.test.d.ts +0 -1
- package/dist/packages/fields/src/text-rich-widgets.test.d.ts +0 -1
- package/dist/packages/fields/src/visual-widgets.test.d.ts +0 -1
- package/src/__tests__/boolean-checkbox.test.tsx +0 -74
- package/src/__tests__/cell-renderers.test.tsx +0 -1193
- package/src/__tests__/datetime-cell.test.tsx +0 -46
- package/src/__tests__/datetime-timezone.test.tsx +0 -770
- package/src/__tests__/extreme-inputs.test.tsx +0 -567
- package/src/__tests__/form-variants.test.tsx +0 -306
- package/src/lookup-depends-on.test.tsx +0 -130
- package/src/new-widgets.test.tsx +0 -302
- package/src/record-picker.test.tsx +0 -1703
- package/src/remaining-widgets.test.tsx +0 -211
- package/src/text-rich-widgets.test.tsx +0 -104
- package/src/visual-widgets.test.tsx +0 -97
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
4
|
-
import { AutoNumberField } from './widgets/AutoNumberField';
|
|
5
|
-
import { FormulaField } from './widgets/FormulaField';
|
|
6
|
-
import { SummaryField } from './widgets/SummaryField';
|
|
7
|
-
import { PercentField } from './widgets/PercentField';
|
|
8
|
-
import { ImageField } from './widgets/ImageField';
|
|
9
|
-
import { LocationField } from './widgets/LocationField';
|
|
10
|
-
import { ObjectField } from './widgets/ObjectField';
|
|
11
|
-
import { UserField } from './widgets/UserField';
|
|
12
|
-
import { VectorField } from './widgets/VectorField';
|
|
13
|
-
import type { FieldWidgetProps } from './widgets/types';
|
|
14
|
-
|
|
15
|
-
// ------------- Mocks & Setup -------------
|
|
16
|
-
|
|
17
|
-
const mockField = {
|
|
18
|
-
name: 'test_field',
|
|
19
|
-
label: 'Test Field',
|
|
20
|
-
} as any;
|
|
21
|
-
|
|
22
|
-
const baseProps: FieldWidgetProps<any> = {
|
|
23
|
-
field: mockField,
|
|
24
|
-
value: undefined,
|
|
25
|
-
onChange: vi.fn(),
|
|
26
|
-
readonly: false,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
describe('Remaining Field Widgets', () => {
|
|
30
|
-
|
|
31
|
-
// 1. AutoNumberField
|
|
32
|
-
describe('AutoNumberField', () => {
|
|
33
|
-
it('should render value as string', () => {
|
|
34
|
-
render(<AutoNumberField {...baseProps} value={1001} />);
|
|
35
|
-
expect(screen.getByText('1001')).toBeInTheDocument();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should render dash for null value', () => {
|
|
39
|
-
render(<AutoNumberField {...baseProps} value={null as any} />);
|
|
40
|
-
expect(screen.getByText('—')).toBeInTheDocument();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// 2. FormulaField
|
|
45
|
-
describe('FormulaField', () => {
|
|
46
|
-
it('should format return_type=currency', () => {
|
|
47
|
-
const field = { ...mockField, return_type: 'currency' };
|
|
48
|
-
render(<FormulaField {...baseProps} field={field} value={123.456} />);
|
|
49
|
-
// Checks for 2 decimal places
|
|
50
|
-
expect(screen.getByText('123.46')).toBeInTheDocument();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should format return_type=boolean', () => {
|
|
54
|
-
const field = { ...mockField, return_type: 'boolean' };
|
|
55
|
-
render(<FormulaField {...baseProps} field={field} value={true} />);
|
|
56
|
-
expect(screen.getByText('Yes')).toBeInTheDocument();
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// 3. SummaryField
|
|
61
|
-
describe('SummaryField', () => {
|
|
62
|
-
it('should render count as integer', () => {
|
|
63
|
-
const field = { ...mockField, summary_type: 'count' };
|
|
64
|
-
render(<SummaryField {...baseProps} field={field} value={42} />);
|
|
65
|
-
expect(screen.getByText('42')).toBeInTheDocument();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should render sum with decimals', () => {
|
|
69
|
-
const field = { ...mockField, summary_type: 'sum' };
|
|
70
|
-
render(<SummaryField {...baseProps} field={field} value={10.566} />);
|
|
71
|
-
expect(screen.getByText('10.57')).toBeInTheDocument();
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// 4. PercentField
|
|
76
|
-
describe('PercentField', () => {
|
|
77
|
-
it('should display value * 100 in input', () => {
|
|
78
|
-
render(<PercentField {...baseProps} value={0.5} />);
|
|
79
|
-
const input = screen.getByRole('spinbutton') as HTMLInputElement;
|
|
80
|
-
expect(input.value).toBe('50');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should display formatted value in readonly', () => {
|
|
84
|
-
render(<PercentField {...baseProps} value={0.125} readonly={true} />);
|
|
85
|
-
expect(screen.getByText('12.50%')).toBeInTheDocument();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should call onChange with value / 100', () => {
|
|
89
|
-
const handleChange = vi.fn();
|
|
90
|
-
render(<PercentField {...baseProps} onChange={handleChange} value={0} />);
|
|
91
|
-
const input = screen.getByRole('spinbutton');
|
|
92
|
-
fireEvent.change(input, { target: { value: '75' } });
|
|
93
|
-
expect(handleChange).toHaveBeenCalledWith(0.75);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('slider renders and syncs with input value', () => {
|
|
97
|
-
render(<PercentField {...baseProps} value={0.5} />);
|
|
98
|
-
const slider = screen.getByTestId('percent-slider');
|
|
99
|
-
expect(slider).toBeInTheDocument();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('slider does not fire onChange when disabled', () => {
|
|
103
|
-
const handleChange = vi.fn();
|
|
104
|
-
render(<PercentField {...baseProps} onChange={handleChange} value={0.5} disabled={true} />);
|
|
105
|
-
const slider = screen.getByTestId('percent-slider');
|
|
106
|
-
expect(slider).toBeInTheDocument();
|
|
107
|
-
// The slider should be disabled, so no changes should fire
|
|
108
|
-
expect(handleChange).not.toHaveBeenCalled();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('slider is not rendered in readonly mode', () => {
|
|
112
|
-
render(<PercentField {...baseProps} value={0.5} readonly={true} />);
|
|
113
|
-
expect(screen.queryByTestId('percent-slider')).not.toBeInTheDocument();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('handles null value gracefully for slider', () => {
|
|
117
|
-
render(<PercentField {...baseProps} value={null as any} />);
|
|
118
|
-
const slider = screen.getByTestId('percent-slider');
|
|
119
|
-
expect(slider).toBeInTheDocument();
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// 5. ImageField
|
|
124
|
-
describe('ImageField', () => {
|
|
125
|
-
const images = [
|
|
126
|
-
{ url: 'http://example.com/1.jpg', name: 'Img 1' },
|
|
127
|
-
{ url: 'http://example.com/2.jpg', name: 'Img 2' }
|
|
128
|
-
];
|
|
129
|
-
|
|
130
|
-
it('should render images in readonly mode', () => {
|
|
131
|
-
render(<ImageField {...baseProps} value={images} readonly={true} />);
|
|
132
|
-
const imgs = screen.getAllByRole('img');
|
|
133
|
-
expect(imgs).toHaveLength(2);
|
|
134
|
-
expect(imgs[0]).toHaveAttribute('src', 'http://example.com/1.jpg');
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('renders a Crop button per image when crop is enabled (default)', () => {
|
|
138
|
-
render(<ImageField {...baseProps} value={images} field={{ ...baseProps.field, multiple: true } as any} />);
|
|
139
|
-
// Two crop buttons (one per image) with stable test ids — opacity is hover-controlled
|
|
140
|
-
// but the buttons are still present in the DOM for a11y/keyboard users.
|
|
141
|
-
expect(screen.getByTestId('image-field-crop-0')).toBeInTheDocument();
|
|
142
|
-
expect(screen.getByTestId('image-field-crop-1')).toBeInTheDocument();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('hides the Crop button when field.crop = false', () => {
|
|
146
|
-
render(<ImageField {...baseProps} value={images} field={{ ...baseProps.field, multiple: true, crop: false } as any} />);
|
|
147
|
-
expect(screen.queryByTestId('image-field-crop-0')).not.toBeInTheDocument();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Complex interaction omitted for brevity, focusing on render contracts
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// 6. LocationField
|
|
154
|
-
describe('LocationField', () => {
|
|
155
|
-
it('should render lat,long string', () => {
|
|
156
|
-
const loc = { latitude: 34.05, longitude: -118.25 };
|
|
157
|
-
render(<LocationField {...baseProps} value={loc} />);
|
|
158
|
-
const input = screen.getByRole('textbox') as HTMLInputElement;
|
|
159
|
-
expect(input.value).toContain('34.05, -118.25');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should parse string input to object', () => {
|
|
163
|
-
const handleChange = vi.fn();
|
|
164
|
-
render(<LocationField {...baseProps} onChange={handleChange} />);
|
|
165
|
-
const input = screen.getByRole('textbox');
|
|
166
|
-
fireEvent.change(input, { target: { value: '10.5, 20.5' } });
|
|
167
|
-
expect(handleChange).toHaveBeenCalledWith({ latitude: 10.5, longitude: 20.5 });
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
// 7. ObjectField
|
|
172
|
-
describe('ObjectField', () => {
|
|
173
|
-
const obj = { foo: 'bar', num: 1 };
|
|
174
|
-
|
|
175
|
-
it('should render JSON string in readonly', () => {
|
|
176
|
-
render(<ObjectField {...baseProps} value={obj} readonly={true} />);
|
|
177
|
-
// pre tag usually contains formatted JSON
|
|
178
|
-
expect(screen.getByText(/"foo": "bar"/)).toBeInTheDocument();
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('should parse valid JSON input', () => {
|
|
182
|
-
const handleChange = vi.fn();
|
|
183
|
-
render(<ObjectField {...baseProps} onChange={handleChange} value={obj} />);
|
|
184
|
-
const textarea = screen.getByRole('textbox');
|
|
185
|
-
const newJson = '{"test": true}';
|
|
186
|
-
fireEvent.change(textarea, { target: { value: newJson } });
|
|
187
|
-
expect(handleChange).toHaveBeenCalledWith({ test: true });
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// 8. UserField
|
|
192
|
-
describe('UserField', () => {
|
|
193
|
-
const user = { name: 'Alice Smith', id: 'u1' };
|
|
194
|
-
|
|
195
|
-
it('should render user avatar in readonly', () => {
|
|
196
|
-
render(<UserField {...baseProps} value={user} readonly={true} />);
|
|
197
|
-
// Avatar fallback logic splits name
|
|
198
|
-
expect(screen.getByText('AS')).toBeInTheDocument();
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// 9. VectorField
|
|
203
|
-
describe('VectorField', () => {
|
|
204
|
-
it('should render vector values', () => {
|
|
205
|
-
const vec = [0.12345, 0.67891, 0.00001];
|
|
206
|
-
render(<VectorField {...baseProps} value={vec} />);
|
|
207
|
-
expect(screen.getByText(/0.1235/)).toBeInTheDocument(); // toFixed(4)
|
|
208
|
-
expect(screen.getByText('(3D)')).toBeInTheDocument();
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
});
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
-
import { TextAreaField } from './widgets/TextAreaField';
|
|
4
|
-
import { RichTextField } from './widgets/RichTextField';
|
|
5
|
-
import { CodeField } from './widgets/CodeField';
|
|
6
|
-
import { PasswordField } from './widgets/PasswordField';
|
|
7
|
-
import type { FieldWidgetProps } from './widgets/types';
|
|
8
|
-
|
|
9
|
-
const mockField = {
|
|
10
|
-
name: 'test_field',
|
|
11
|
-
label: 'Test Field',
|
|
12
|
-
} as any;
|
|
13
|
-
|
|
14
|
-
const baseProps: FieldWidgetProps<string> = {
|
|
15
|
-
field: mockField,
|
|
16
|
-
value: '',
|
|
17
|
-
onChange: vi.fn(),
|
|
18
|
-
readonly: false,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
describe('Text & Rich Content Widgets', () => {
|
|
22
|
-
describe('TextAreaField', () => {
|
|
23
|
-
it('renders textarea in edit mode', () => {
|
|
24
|
-
render(<TextAreaField {...baseProps} />);
|
|
25
|
-
expect(screen.getByRole('textbox')).toBeInTheDocument();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('handles max length indicator', () => {
|
|
29
|
-
const propsWithMax = {
|
|
30
|
-
...baseProps,
|
|
31
|
-
field: { ...mockField, max_length: 100 }
|
|
32
|
-
};
|
|
33
|
-
render(<TextAreaField {...propsWithMax} value="testing" />);
|
|
34
|
-
expect(screen.getByLabelText('Character count: 7 of 100')).toBeInTheDocument();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('renders whitespace-pre-wrap in readonly mode', () => {
|
|
38
|
-
const text = 'Line 1\nLine 2';
|
|
39
|
-
render(<TextAreaField {...baseProps} readonly value={text} />);
|
|
40
|
-
// Check existence logic
|
|
41
|
-
const display = screen.getByText((content) =>
|
|
42
|
-
content.includes('Line 1') && content.includes('Line 2')
|
|
43
|
-
);
|
|
44
|
-
expect(display).toHaveClass('whitespace-pre-wrap');
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe('RichTextField', () => {
|
|
49
|
-
// Current implementation is a simpler textarea wrapper
|
|
50
|
-
it('renders textarea with format indicator', () => {
|
|
51
|
-
render(<RichTextField {...baseProps} />);
|
|
52
|
-
expect(screen.getByRole('textbox')).toBeInTheDocument();
|
|
53
|
-
expect(screen.getByText((content) => content.includes('Format: markdown'))).toBeInTheDocument();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('renders prose in readonly mode', () => {
|
|
57
|
-
render(<RichTextField {...baseProps} readonly value="# Heading" />);
|
|
58
|
-
const display = screen.getByText('# Heading');
|
|
59
|
-
expect(display).toHaveClass('prose');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe('CodeField', () => {
|
|
64
|
-
it('renders textarea with monospace font', () => {
|
|
65
|
-
render(<CodeField {...baseProps} />);
|
|
66
|
-
const input = screen.getByRole('textbox');
|
|
67
|
-
expect(input).toHaveClass('font-mono');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('renders code block in readonly mode', () => {
|
|
71
|
-
render(<CodeField {...baseProps} readonly value="const x = 1;" />);
|
|
72
|
-
// Check for pre > code structure
|
|
73
|
-
const pre = document.querySelector('pre');
|
|
74
|
-
expect(pre).toBeInTheDocument();
|
|
75
|
-
expect(pre).toHaveTextContent('const x = 1;');
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe('PasswordField', () => {
|
|
80
|
-
it('renders password input by default', () => {
|
|
81
|
-
const { container } = render(<PasswordField {...baseProps} />);
|
|
82
|
-
const input = container.querySelector('input[type="password"]');
|
|
83
|
-
expect(input).toBeInTheDocument();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('toggles visibility', () => {
|
|
87
|
-
const { container } = render(<PasswordField {...baseProps} />);
|
|
88
|
-
const button = screen.getByRole('button'); // The eye icon button
|
|
89
|
-
const input = container.querySelector('input') as HTMLInputElement;
|
|
90
|
-
|
|
91
|
-
expect(input.type).toBe('password');
|
|
92
|
-
fireEvent.click(button);
|
|
93
|
-
expect(input.type).toBe('text');
|
|
94
|
-
fireEvent.click(button);
|
|
95
|
-
expect(input.type).toBe('password');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('renders masked bullets in readonly mode', () => {
|
|
99
|
-
render(<PasswordField {...baseProps} readonly value="secret" />);
|
|
100
|
-
expect(screen.getByText('••••••••')).toBeInTheDocument();
|
|
101
|
-
expect(screen.queryByText('secret')).not.toBeInTheDocument();
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
});
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
-
import { ColorField } from './widgets/ColorField';
|
|
4
|
-
import { RatingField } from './widgets/RatingField';
|
|
5
|
-
import { SliderField } from './widgets/SliderField';
|
|
6
|
-
import { QRCodeField } from './widgets/QRCodeField'; // Intentionally simpler than Image/Avatar
|
|
7
|
-
import type { FieldWidgetProps } from './widgets/types';
|
|
8
|
-
|
|
9
|
-
const mockField = {
|
|
10
|
-
name: 'test_field',
|
|
11
|
-
label: 'Test Field',
|
|
12
|
-
} as any;
|
|
13
|
-
|
|
14
|
-
const baseProps: FieldWidgetProps<any> = {
|
|
15
|
-
field: mockField,
|
|
16
|
-
value: undefined,
|
|
17
|
-
onChange: vi.fn(),
|
|
18
|
-
readonly: false,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
describe('Visual & Display Widgets', () => {
|
|
22
|
-
describe('ColorField', () => {
|
|
23
|
-
it('renders color picker + text input in edit mode', () => {
|
|
24
|
-
const { container } = render(<ColorField {...baseProps} value="#00ff00" />);
|
|
25
|
-
const colorInput = container.querySelector('input[type="color"]');
|
|
26
|
-
const textInput = screen.getByRole('textbox');
|
|
27
|
-
|
|
28
|
-
expect(colorInput).toBeInTheDocument();
|
|
29
|
-
expect(textInput).toBeInTheDocument();
|
|
30
|
-
expect(textInput).toHaveValue('#00ff00');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('syncs change from color picker', () => {
|
|
34
|
-
const { container } = render(<ColorField {...baseProps} />);
|
|
35
|
-
const colorInput = container.querySelector('input[type="color"]') as HTMLInputElement;
|
|
36
|
-
fireEvent.change(colorInput, { target: { value: '#ff0000' } });
|
|
37
|
-
expect(baseProps.onChange).toHaveBeenCalledWith('#ff0000');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('shows preview box in readonly mode', () => {
|
|
41
|
-
const { container } = render(<ColorField {...baseProps} readonly value="#0000ff" />);
|
|
42
|
-
// Check for the style attribute on the div
|
|
43
|
-
// We search for a div that has the style background-color
|
|
44
|
-
// This is a bit fragile but standard for inline styles
|
|
45
|
-
const preview = container.querySelector('div[style="background-color: #0000ff;"]');
|
|
46
|
-
// Or looser check:
|
|
47
|
-
const divs = container.querySelectorAll('div');
|
|
48
|
-
const found = Array.from(divs).some(d => d.style.backgroundColor === 'rgb(0, 0, 255)' || d.style.backgroundColor === '#0000ff');
|
|
49
|
-
expect(found).toBe(true);
|
|
50
|
-
expect(screen.getByText('#0000ff')).toBeInTheDocument();
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe('RatingField', () => {
|
|
55
|
-
it('renders stars', () => {
|
|
56
|
-
render(<RatingField {...baseProps} value={3} />);
|
|
57
|
-
// Should see 5 buttons (default max)
|
|
58
|
-
const buttons = screen.getAllByRole('button');
|
|
59
|
-
expect(buttons).toHaveLength(5);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('highlights correct number of stars in readonly', () => {
|
|
63
|
-
const { container } = render(<RatingField {...baseProps} readonly value={3} />);
|
|
64
|
-
// Should have 3 filled stars, 2 empty.
|
|
65
|
-
// Logic: fill-yellow-400
|
|
66
|
-
const filled = container.querySelectorAll('.fill-yellow-400');
|
|
67
|
-
expect(filled).toHaveLength(3);
|
|
68
|
-
expect(screen.getByText('3 / 5')).toBeInTheDocument();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('calls onChange when clicking star', () => {
|
|
72
|
-
render(<RatingField {...baseProps} />);
|
|
73
|
-
const buttons = screen.getAllByRole('button');
|
|
74
|
-
fireEvent.click(buttons[3]); // 4th star (index 3) -> value 4
|
|
75
|
-
expect(baseProps.onChange).toHaveBeenCalledWith(4);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe('SliderField', () => {
|
|
80
|
-
it('renders slider', () => {
|
|
81
|
-
const { container } = render(<SliderField {...baseProps} value={50} />);
|
|
82
|
-
// Radix slider usually has role="slider"
|
|
83
|
-
const slider = screen.getByRole('slider');
|
|
84
|
-
expect(slider).toBeInTheDocument();
|
|
85
|
-
// Check text display
|
|
86
|
-
expect(screen.getByText('50')).toBeInTheDocument();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('renders readonly view', () => {
|
|
90
|
-
render(<SliderField {...baseProps} readonly value={75} />);
|
|
91
|
-
expect(screen.getByText('75')).toBeInTheDocument();
|
|
92
|
-
expect(screen.getByText('/ 100')).toBeInTheDocument();
|
|
93
|
-
expect(screen.queryByRole('slider')).not.toBeInTheDocument();
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
});
|