@moontra/moonui 3.2.0 → 3.4.0

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.
@@ -1,365 +1,181 @@
1
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
1
+ /**
2
+ * ColorPicker / SimpleColorPicker / GradientPicker testleri.
3
+ *
4
+ * ─── BU DOSYA NEDEN BAŞTAN YAZILDI (issue #263) ──────────────────────────────
5
+ * Önceki hâli var olmayan bir API'ye yazılmıştı: `color` / `onColorChange`
6
+ * prop'ları, `data-testid="color-display"`, `"color-area"`, `"opacity-slider"`
7
+ * gibi test-id'ler ve "Eyedropper", "Color Stops", "Add Stop", "Enter color..."
8
+ * gibi metinler. Bunların HİÇBİRİ kaynakta geçmiyordu (10/10 işaret için grep
9
+ * sonucu 0) — dolayısıyla 30 testin 22'si sürekli kırmızıydı ve dosya hiçbir
10
+ * regresyonu yakalayamıyordu.
11
+ *
12
+ * Gerçek API:
13
+ * ColorPicker { value, onChange, size, shape, showInput, showPresets,
14
+ * presets, allowOpacity, format, disableCopy, triggerMode,
15
+ * disabled, className }
16
+ * SimpleColorPicker { value, onChange, colors, size, className }
17
+ * GradientPicker { value: { start, end, angle }, onChange, className }
18
+ */
19
+ import { render, screen, fireEvent } from '@testing-library/react';
2
20
  import { ColorPicker, SimpleColorPicker, GradientPicker } from '../color-picker';
3
21
  import '@testing-library/jest-dom';
4
22
 
5
- describe('ColorPicker Components', () => {
6
- describe('ColorPicker', () => {
7
- it('renders correctly with default props', () => {
8
- const mockOnChange = jest.fn();
9
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
10
-
11
- expect(screen.getByRole('button')).toBeInTheDocument();
12
- expect(screen.getByText('#ffffff')).toBeInTheDocument();
13
- });
14
-
15
- it('displays selected color', () => {
16
- const mockOnChange = jest.fn();
17
- render(<ColorPicker color="#ff0000" onColorChange={mockOnChange} />);
18
-
19
- expect(screen.getByText('#ff0000')).toBeInTheDocument();
20
- const colorDisplay = screen.getByTestId('color-display');
21
- expect(colorDisplay).toHaveStyle('background-color: #ff0000');
22
- });
23
-
24
- it('opens color picker when button is clicked', async () => {
25
- const mockOnChange = jest.fn();
26
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
27
-
28
- const button = screen.getByRole('button');
29
- fireEvent.click(button);
30
-
31
- await waitFor(() => {
32
- expect(screen.getByRole('dialog')).toBeInTheDocument();
33
- });
34
- });
35
-
36
- it('calls onColorChange when color is selected', async () => {
37
- const mockOnChange = jest.fn();
38
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
39
-
40
- const button = screen.getByRole('button');
41
- fireEvent.click(button);
42
-
43
- await waitFor(() => {
44
- const colorArea = screen.getByTestId('color-area');
45
- fireEvent.click(colorArea);
46
- expect(mockOnChange).toHaveBeenCalled();
47
- });
48
- });
49
-
50
- it('handles HSL color format', () => {
51
- const mockOnChange = jest.fn();
52
- render(<ColorPicker color="hsl(120, 100%, 50%)" onColorChange={mockOnChange} />);
53
-
54
- expect(screen.getByText('hsl(120, 100%, 50%)')).toBeInTheDocument();
55
- });
56
-
57
- it('handles RGB color format', () => {
58
- const mockOnChange = jest.fn();
59
- render(<ColorPicker color="rgb(255, 0, 0)" onColorChange={mockOnChange} />);
60
-
61
- expect(screen.getByText('rgb(255, 0, 0)')).toBeInTheDocument();
62
- });
23
+ /** ColorPicker'ı render edip Popover panelini açar. */
24
+ function renderOpen(props: Partial<React.ComponentProps<typeof ColorPicker>> = {}) {
25
+ const utils = render(<ColorPicker {...props} />);
26
+ fireEvent.click(screen.getByRole('button', { name: 'Pick a color' }));
27
+ return utils;
28
+ }
29
+
30
+ /**
31
+ * Radix TabsTrigger seçimi `onMouseDown` üzerinden yapar — `fireEvent.click`
32
+ * mouseDown üretmediği için sekme DEĞİŞMEZ (ölçülerek doğrulandı).
33
+ */
34
+ function activateTab(name: string) {
35
+ fireEvent.mouseDown(screen.getByRole('tab', { name }));
36
+ }
37
+
38
+ describe('ColorPicker', () => {
39
+ it('tetikleyici buton erişilebilir ad taşır ve seçili rengi gösterir', () => {
40
+ render(<ColorPicker value="#ff0000" />);
41
+
42
+ const trigger = screen.getByRole('button', { name: 'Pick a color' });
43
+ expect(trigger).toBeInTheDocument();
44
+ // Renk arka planda taşınır (buton içeriği sr-only metinden ibarettir).
45
+ expect(trigger).toHaveStyle({ backgroundColor: '#ff0000' });
46
+ });
63
47
 
64
- it('handles alpha channel', async () => {
65
- const mockOnChange = jest.fn();
66
- render(<ColorPicker color="rgba(255, 0, 0, 0.5)" onColorChange={mockOnChange} />);
67
-
68
- const button = screen.getByRole('button');
69
- fireEvent.click(button);
70
-
71
- await waitFor(() => {
72
- expect(screen.getByText('Opacity')).toBeInTheDocument();
73
- const opacitySlider = screen.getByTestId('opacity-slider');
74
- expect(opacitySlider).toBeInTheDocument();
75
- });
76
- });
48
+ it('disabled iken tetikleyici devre dışıdır', () => {
49
+ render(<ColorPicker value="#ffffff" disabled />);
50
+ expect(screen.getByRole('button', { name: 'Pick a color' })).toBeDisabled();
51
+ });
77
52
 
78
- it('shows eyedropper tool when available', async () => {
79
- // Mock the EyeDropper API
80
- Object.defineProperty(window, 'EyeDropper', {
81
- value: class MockEyeDropper {
82
- async open() {
83
- return { sRGBHex: '#ff0000' };
84
- }
85
- }
86
- });
53
+ it('özel className tetikleyiciye uygulanır', () => {
54
+ render(<ColorPicker value="#ffffff" className="custom-class" />);
55
+ expect(screen.getByRole('button', { name: 'Pick a color' })).toHaveClass('custom-class');
56
+ });
87
57
 
88
- const mockOnChange = jest.fn();
89
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
90
-
91
- const button = screen.getByRole('button');
92
- fireEvent.click(button);
93
-
94
- await waitFor(() => {
95
- expect(screen.getByText('Eyedropper')).toBeInTheDocument();
96
- });
97
- });
58
+ it('tıklanınca panel açılır ve üç sekme sunar', () => {
59
+ renderOpen({ value: '#ffffff' });
98
60
 
99
- it('handles disabled state', () => {
100
- const mockOnChange = jest.fn();
101
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} disabled />);
102
-
103
- const button = screen.getByRole('button');
104
- expect(button).toBeDisabled();
105
- });
61
+ expect(screen.getByRole('tab', { name: 'Picker' })).toBeInTheDocument();
62
+ expect(screen.getByRole('tab', { name: 'Sliders' })).toBeInTheDocument();
63
+ expect(screen.getByRole('tab', { name: 'Input' })).toBeInTheDocument();
106
64
  });
107
65
 
108
- describe('SimpleColorPicker', () => {
109
- it('renders correctly with preset colors', () => {
110
- const mockOnChange = jest.fn();
111
- render(<SimpleColorPicker color="#ffffff" onColorChange={mockOnChange} />);
112
-
113
- expect(screen.getByRole('button')).toBeInTheDocument();
114
-
115
- // Check for preset color buttons
116
- const colorButtons = screen.getAllByRole('button');
117
- expect(colorButtons.length).toBeGreaterThan(1);
118
- });
66
+ it('Input sekmesinde hex girişi bulunur', () => {
67
+ renderOpen({ value: '#ff0000' });
119
68
 
120
- it('displays selected color', () => {
121
- const mockOnChange = jest.fn();
122
- render(<SimpleColorPicker color="#ff0000" onColorChange={mockOnChange} />);
123
-
124
- const selectedButton = screen.getByRole('button', { pressed: true });
125
- expect(selectedButton).toHaveStyle('background-color: #ff0000');
126
- });
69
+ activateTab('Input');
70
+ expect(screen.getByPlaceholderText('#000000')).toBeInTheDocument();
71
+ });
127
72
 
128
- it('calls onColorChange when preset color is clicked', () => {
129
- const mockOnChange = jest.fn();
130
- render(<SimpleColorPicker color="#ffffff" onColorChange={mockOnChange} />);
131
-
132
- const colorButtons = screen.getAllByRole('button');
133
- fireEvent.click(colorButtons[1]); // Click second color button
134
-
135
- expect(mockOnChange).toHaveBeenCalled();
136
- });
73
+ it('hex girişine geçerli değer yazılınca onChange tetiklenir', () => {
74
+ const onChange = jest.fn();
75
+ renderOpen({ value: '#ff0000', onChange });
137
76
 
138
- it('shows custom color input', () => {
139
- const mockOnChange = jest.fn();
140
- render(<SimpleColorPicker color="#ffffff" onColorChange={mockOnChange} />);
141
-
142
- expect(screen.getByPlaceholderText('Enter color...')).toBeInTheDocument();
77
+ activateTab('Input');
78
+ fireEvent.change(screen.getByPlaceholderText('#000000'), {
79
+ target: { value: '#00ff00' },
143
80
  });
144
81
 
145
- it('handles custom color input', () => {
146
- const mockOnChange = jest.fn();
147
- render(<SimpleColorPicker color="#ffffff" onColorChange={mockOnChange} />);
148
-
149
- const input = screen.getByPlaceholderText('Enter color...');
150
- fireEvent.change(input, { target: { value: '#ff0000' } });
151
- fireEvent.blur(input);
152
-
153
- expect(mockOnChange).toHaveBeenCalledWith('#ff0000');
154
- });
82
+ expect(onChange).toHaveBeenCalledWith('#00ff00');
155
83
  });
156
84
 
157
- describe('GradientPicker', () => {
158
- it('renders correctly with default gradient', () => {
159
- const mockOnChange = jest.fn();
160
- render(<GradientPicker gradient="linear-gradient(to right, #ff0000, #0000ff)" onGradientChange={mockOnChange} />);
161
-
162
- expect(screen.getByRole('button')).toBeInTheDocument();
163
- expect(screen.getByText('linear-gradient(to right, #ff0000, #0000ff)')).toBeInTheDocument();
164
- });
165
-
166
- it('opens gradient picker when button is clicked', async () => {
167
- const mockOnChange = jest.fn();
168
- render(<GradientPicker gradient="linear-gradient(to right, #ff0000, #0000ff)" onGradientChange={mockOnChange} />);
169
-
170
- const button = screen.getByRole('button');
171
- fireEvent.click(button);
172
-
173
- await waitFor(() => {
174
- expect(screen.getByRole('dialog')).toBeInTheDocument();
175
- });
176
- });
85
+ it('preset swatch tıklanınca o renkle onChange tetiklenir', () => {
86
+ const onChange = jest.fn();
87
+ renderOpen({ value: '#ffffff', onChange, presets: ['#ef4444', '#22c55e'] });
177
88
 
178
- it('shows gradient type selector', async () => {
179
- const mockOnChange = jest.fn();
180
- render(<GradientPicker gradient="linear-gradient(to right, #ff0000, #0000ff)" onGradientChange={mockOnChange} />);
181
-
182
- const button = screen.getByRole('button');
183
- fireEvent.click(button);
184
-
185
- await waitFor(() => {
186
- expect(screen.getByText('Linear')).toBeInTheDocument();
187
- expect(screen.getByText('Radial')).toBeInTheDocument();
188
- });
189
- });
89
+ fireEvent.click(screen.getByRole('button', { name: 'Select color #22c55e' }));
90
+ expect(onChange).toHaveBeenCalledWith('#22c55e');
91
+ });
190
92
 
191
- it('shows color stops', async () => {
192
- const mockOnChange = jest.fn();
193
- render(<GradientPicker gradient="linear-gradient(to right, #ff0000, #0000ff)" onGradientChange={mockOnChange} />);
194
-
195
- const button = screen.getByRole('button');
196
- fireEvent.click(button);
197
-
198
- await waitFor(() => {
199
- expect(screen.getByText('Color Stops')).toBeInTheDocument();
200
- });
201
- });
93
+ it('preset swatch butonları erişilebilir ad taşır', () => {
94
+ // Regresyon: swatch'ın tek içeriği arka plan rengiydi; aria-label olmadan
95
+ // ekran okuyucu yalnızca "button" diyordu.
96
+ renderOpen({ value: '#ef4444', presets: ['#ef4444', '#22c55e'] });
97
+
98
+ expect(screen.getByRole('button', { name: 'Select color #ef4444' })).toHaveAttribute(
99
+ 'aria-pressed',
100
+ 'true'
101
+ );
102
+ expect(screen.getByRole('button', { name: 'Select color #22c55e' })).toHaveAttribute(
103
+ 'aria-pressed',
104
+ 'false'
105
+ );
106
+ });
202
107
 
203
- it('allows adding color stops', async () => {
204
- const mockOnChange = jest.fn();
205
- render(<GradientPicker gradient="linear-gradient(to right, #ff0000, #0000ff)" onGradientChange={mockOnChange} />);
206
-
207
- const button = screen.getByRole('button');
208
- fireEvent.click(button);
209
-
210
- await waitFor(() => {
211
- const addButton = screen.getByText('Add Stop');
212
- fireEvent.click(addButton);
213
- expect(mockOnChange).toHaveBeenCalled();
214
- });
215
- });
108
+ it('showPresets=false iken preset bölümü render edilmez', () => {
109
+ renderOpen({ value: '#ffffff', showPresets: false, presets: ['#ef4444'] });
110
+ expect(screen.queryByRole('button', { name: 'Select color #ef4444' })).not.toBeInTheDocument();
111
+ });
112
+ });
216
113
 
217
- it('handles gradient direction change', async () => {
218
- const mockOnChange = jest.fn();
219
- render(<GradientPicker gradient="linear-gradient(to right, #ff0000, #0000ff)" onGradientChange={mockOnChange} />);
220
-
221
- const button = screen.getByRole('button');
222
- fireEvent.click(button);
223
-
224
- await waitFor(() => {
225
- const directionSelect = screen.getByText('to right');
226
- fireEvent.click(directionSelect);
227
-
228
- const toBottomOption = screen.getByText('to bottom');
229
- fireEvent.click(toBottomOption);
230
-
231
- expect(mockOnChange).toHaveBeenCalled();
232
- });
233
- });
114
+ describe('SimpleColorPicker', () => {
115
+ it('verilen renkler için birer swatch render eder', () => {
116
+ render(<SimpleColorPicker value="#ffffff" colors={['#ef4444', '#22c55e', '#3b82f6']} />);
117
+ expect(screen.getAllByRole('button')).toHaveLength(3);
234
118
  });
235
119
 
236
- describe('Common functionality', () => {
237
- it('handles keyboard navigation', async () => {
238
- const mockOnChange = jest.fn();
239
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
240
-
241
- const button = screen.getByRole('button');
242
- fireEvent.keyDown(button, { key: 'Enter' });
243
-
244
- await waitFor(() => {
245
- expect(screen.getByRole('dialog')).toBeInTheDocument();
246
- });
247
- });
120
+ it('swatch tıklanınca o renkle onChange tetiklenir', () => {
121
+ const onChange = jest.fn();
122
+ render(
123
+ <SimpleColorPicker value="#ffffff" onChange={onChange} colors={['#ef4444', '#22c55e']} />
124
+ );
248
125
 
249
- it('closes picker when clicking outside', async () => {
250
- const mockOnChange = jest.fn();
251
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
252
-
253
- const button = screen.getByRole('button');
254
- fireEvent.click(button);
255
-
256
- await waitFor(() => {
257
- expect(screen.getByRole('dialog')).toBeInTheDocument();
258
- });
259
-
260
- fireEvent.click(document.body);
261
-
262
- await waitFor(() => {
263
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
264
- });
265
- });
126
+ fireEvent.click(screen.getByRole('button', { name: 'Select color #22c55e' }));
127
+ expect(onChange).toHaveBeenCalledWith('#22c55e');
128
+ });
266
129
 
267
- it('handles ESC key to close picker', async () => {
268
- const mockOnChange = jest.fn();
269
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
270
-
271
- const button = screen.getByRole('button');
272
- fireEvent.click(button);
273
-
274
- await waitFor(() => {
275
- expect(screen.getByRole('dialog')).toBeInTheDocument();
276
- });
277
-
278
- fireEvent.keyDown(document, { key: 'Escape' });
279
-
280
- await waitFor(() => {
281
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
282
- });
283
- });
130
+ it('seçili swatch aria-pressed ile işaretlenir', () => {
131
+ render(<SimpleColorPicker value="#ef4444" colors={['#ef4444', '#22c55e']} />);
132
+
133
+ expect(screen.getByRole('button', { name: 'Select color #ef4444' })).toHaveAttribute(
134
+ 'aria-pressed',
135
+ 'true'
136
+ );
137
+ expect(screen.getByRole('button', { name: 'Select color #22c55e' })).toHaveAttribute(
138
+ 'aria-pressed',
139
+ 'false'
140
+ );
141
+ });
284
142
 
285
- it('applies custom className', () => {
286
- const mockOnChange = jest.fn();
287
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} className="custom-class" />);
288
-
289
- const button = screen.getByRole('button');
290
- expect(button).toHaveClass('custom-class');
291
- });
143
+ it('onChange verilmese de tıklama hata vermez', () => {
144
+ render(<SimpleColorPicker value="#ffffff" colors={['#ef4444']} />);
145
+ expect(() =>
146
+ fireEvent.click(screen.getByRole('button', { name: 'Select color #ef4444' }))
147
+ ).not.toThrow();
292
148
  });
149
+ });
293
150
 
294
- describe('Accessibility', () => {
295
- it('has proper ARIA attributes', () => {
296
- const mockOnChange = jest.fn();
297
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
298
-
299
- const button = screen.getByRole('button');
300
- expect(button).toHaveAttribute('aria-haspopup', 'dialog');
301
- expect(button).toHaveAttribute('aria-expanded', 'false');
302
- });
151
+ describe('GradientPicker', () => {
152
+ const gradient = { start: '#ff0000', end: '#0000ff', angle: 90 };
303
153
 
304
- it('updates aria-expanded when picker is open', async () => {
305
- const mockOnChange = jest.fn();
306
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
307
-
308
- const button = screen.getByRole('button');
309
- fireEvent.click(button);
310
-
311
- await waitFor(() => {
312
- expect(button).toHaveAttribute('aria-expanded', 'true');
313
- });
314
- });
154
+ it('başlangıç/bitiş rengi ve açı alanlarını render eder', () => {
155
+ render(<GradientPicker value={gradient} />);
315
156
 
316
- it('has proper labels for screen readers', () => {
317
- const mockOnChange = jest.fn();
318
- render(<ColorPicker color="#ffffff" onColorChange={mockOnChange} />);
319
-
320
- const button = screen.getByRole('button');
321
- expect(button).toHaveAttribute('aria-label', 'Open color picker');
322
- });
157
+ expect(screen.getByText('Start Color')).toBeInTheDocument();
158
+ expect(screen.getByText('End Color')).toBeInTheDocument();
159
+ expect(screen.getByText('Angle')).toBeInTheDocument();
160
+ });
323
161
 
324
- it('provides color value to screen readers', () => {
325
- const mockOnChange = jest.fn();
326
- render(<ColorPicker color="#ff0000" onColorChange={mockOnChange} />);
327
-
328
- const button = screen.getByRole('button');
329
- expect(button).toHaveAttribute('aria-describedby');
330
- expect(screen.getByText('#ff0000')).toBeInTheDocument();
331
- });
162
+ it('başlangıç ve bitiş için birer ColorPicker tetikleyicisi sunar', () => {
163
+ render(<GradientPicker value={gradient} />);
164
+ expect(screen.getAllByRole('button', { name: 'Pick a color' })).toHaveLength(2);
332
165
  });
333
166
 
334
- describe('Form integration', () => {
335
- it('works with form validation', () => {
336
- const mockOnChange = jest.fn();
337
-
338
- render(
339
- <form>
340
- <ColorPicker color="#ffffff" onColorChange={mockOnChange} required />
341
- </form>
342
- );
343
-
344
- const button = screen.getByRole('button');
345
- expect(button).toBeValid();
346
- });
167
+ it('başlangıç rengi değişince onChange tüm gradient nesnesiyle çağrılır', () => {
168
+ const onChange = jest.fn();
169
+ render(<GradientPicker value={gradient} onChange={onChange} />);
347
170
 
348
- it('handles form reset', () => {
349
- const mockOnChange = jest.fn();
350
-
351
- render(
352
- <form>
353
- <ColorPicker color="#ff0000" onColorChange={mockOnChange} />
354
- <button type="reset">Reset</button>
355
- </form>
356
- );
357
-
358
- const resetButton = screen.getByText('Reset');
359
- fireEvent.click(resetButton);
360
-
361
- // Form reset should trigger appropriate behavior
362
- expect(screen.getAllByRole('button')).toHaveLength(2);
363
- });
171
+ // İlk tetikleyici "Start Color"; panelden bir preset seçilir.
172
+ fireEvent.click(screen.getAllByRole('button', { name: 'Pick a color' })[0]);
173
+ const swatch = screen.getAllByRole('button', { name: /^Select color / })[0];
174
+ const picked = swatch.getAttribute('aria-label')!.replace('Select color ', '');
175
+ fireEvent.click(swatch);
176
+
177
+ expect(onChange).toHaveBeenCalledWith(
178
+ expect.objectContaining({ start: picked, end: gradient.end, angle: gradient.angle })
179
+ );
364
180
  });
365
- });
181
+ });
@@ -552,4 +552,64 @@ describe('RadioGroup Components', () => {
552
552
  expect(option2).toBeDisabled()
553
553
  })
554
554
  })
555
+
556
+ describe('Uncontrolled mod ve klavye focus izi', () => {
557
+ it('defaultValue ile uncontrolled çalışır', () => {
558
+ render(
559
+ <RadioGroup defaultValue="option2">
560
+ <RadioGroupItem value="option1" data-testid="r1" />
561
+ <RadioGroupItem value="option2" data-testid="r2" />
562
+ </RadioGroup>
563
+ )
564
+
565
+ expect(screen.getByTestId('r2')).toBeChecked()
566
+ expect(screen.getByTestId('r1')).not.toBeChecked()
567
+
568
+ // Tüketici onValueChange vermeden de seçim değişebilmeli.
569
+ fireEvent.click(screen.getByTestId('r1'))
570
+ expect(screen.getByTestId('r1')).toBeChecked()
571
+ expect(screen.getByTestId('r2')).not.toBeChecked()
572
+ })
573
+
574
+ it('controlled mod defaultValue tarafından ezilmez', () => {
575
+ render(
576
+ <RadioGroup value="option1" defaultValue="option2">
577
+ <RadioGroupItem value="option1" data-testid="r1" />
578
+ <RadioGroupItem value="option2" data-testid="r2" />
579
+ </RadioGroup>
580
+ )
581
+
582
+ expect(screen.getByTestId('r1')).toBeChecked()
583
+ expect(screen.getByTestId('r2')).not.toBeChecked()
584
+ })
585
+
586
+ it('uncontrolled modda onValueChange yine de çağrılır', () => {
587
+ const onValueChange = jest.fn()
588
+ render(
589
+ <RadioGroup defaultValue="option1" onValueChange={onValueChange}>
590
+ <RadioGroupItem value="option1" data-testid="r1" />
591
+ <RadioGroupItem value="option2" data-testid="r2" />
592
+ </RadioGroup>
593
+ )
594
+
595
+ fireEvent.click(screen.getByTestId('r2'))
596
+ expect(onValueChange).toHaveBeenCalledWith('option2')
597
+ })
598
+
599
+ it('gizli input peer sınıfını taşır — focus izi görünür label\'a taşınabilsin', () => {
600
+ render(
601
+ <RadioGroup>
602
+ <RadioGroupItem value="option1" data-testid="r1" />
603
+ </RadioGroup>
604
+ )
605
+
606
+ // Gerçekte odaklanan eleman sr-only input; `peer` olmadan görünür label
607
+ // hiçbir focus izi gösteremez (klavye kullanıcısı nerede olduğunu göremez).
608
+ const input = screen.getByTestId('r1')
609
+ expect(input).toHaveClass('peer', 'sr-only')
610
+
611
+ const label = input.parentElement?.querySelector('label')
612
+ expect(label?.className).toContain('peer-focus-visible:ring-2')
613
+ })
614
+ })
555
615
  })