@arkyn/components 3.0.1-beta.122 → 3.0.1-beta.124

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/TESTING_MAP.md ADDED
@@ -0,0 +1,613 @@
1
+ # 🧪 Mapa de Geração de Testes para Componentes React
2
+
3
+ Este documento serve como guia padronizado para IAs gerarem testes de componentes React utilizando **Vitest** e **React Testing Library**.
4
+
5
+ ---
6
+
7
+ ## 📋 Índice
8
+
9
+ 1. [Pré-requisitos](#1-pré-requisitos)
10
+ 2. [Estrutura de Arquivos](#2-estrutura-de-arquivos)
11
+ 3. [Anatomia de um Teste](#3-anatomia-de-um-teste)
12
+ 4. [Categorias de Testes](#4-categorias-de-testes)
13
+ 5. [Padrões de Nomenclatura](#5-padrões-de-nomenclatura)
14
+ 6. [Checklist de Cobertura](#6-checklist-de-cobertura)
15
+ 7. [Exemplos Práticos](#7-exemplos-práticos)
16
+ 8. [Boas Práticas](#8-boas-práticas)
17
+
18
+ ---
19
+
20
+ ## 1. Pré-requisitos
21
+
22
+ ### Dependências necessárias:
23
+
24
+ ```json
25
+ {
26
+ "devDependencies": {
27
+ "vitest": "^4.x",
28
+ "@testing-library/react": "^16.x",
29
+ "@testing-library/jest-dom": "^6.x",
30
+ "@testing-library/user-event": "^14.x",
31
+ "jsdom": "^28.x"
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### Configuração do `vitest.config.ts`:
37
+
38
+ ```typescript
39
+ import { defineConfig } from "vitest/config";
40
+ import react from "@vitejs/plugin-react";
41
+
42
+ export default defineConfig({
43
+ plugins: [react()],
44
+ test: {
45
+ environment: "jsdom",
46
+ globals: true,
47
+ setupFiles: ["./vitest.setup.ts"],
48
+ include: ["src/**/*.{test,spec}.{ts,tsx}"],
49
+ },
50
+ });
51
+ ```
52
+
53
+ ### Configuração do `vitest.setup.ts`:
54
+
55
+ ```typescript
56
+ import "@testing-library/jest-dom/vitest";
57
+ ```
58
+
59
+ ---
60
+
61
+ ## 2. Estrutura de Arquivos
62
+
63
+ ### Localização dos testes:
64
+
65
+ ```
66
+ src/
67
+ └── components/
68
+ └── [componentName]/
69
+ ├── index.tsx # Componente
70
+ ├── styles.css # Estilos
71
+ └── index.test.tsx # Testes (mesmo diretório)
72
+ ```
73
+
74
+ **OU** centralizado:
75
+
76
+ ```
77
+ src/
78
+ └── components/
79
+ ├── __test__/
80
+ │ └── [componentName].test.tsx
81
+ └── [componentName]/
82
+ ├── index.tsx
83
+ └── styles.css
84
+ ```
85
+
86
+ ---
87
+
88
+ ## 3. Anatomia de um Teste
89
+
90
+ ### Estrutura básica do arquivo de teste:
91
+
92
+ ```typescript
93
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
94
+ import { render, screen, fireEvent, waitFor } from "@testing-library/react";
95
+ import userEvent from "@testing-library/user-event";
96
+
97
+ import { ComponentName } from "./index";
98
+
99
+ // ============================================
100
+ // MOCKS (se necessário)
101
+ // ============================================
102
+ vi.mock("../dependency", () => ({
103
+ dependency: vi.fn(),
104
+ }));
105
+
106
+ // ============================================
107
+ // HELPERS/FIXTURES
108
+ // ============================================
109
+ const defaultProps = {
110
+ // props padrão para os testes
111
+ };
112
+
113
+ const renderComponent = (props = {}) => {
114
+ return render(<ComponentName {...defaultProps} {...props} />);
115
+ };
116
+
117
+ // ============================================
118
+ // TESTES
119
+ // ============================================
120
+ describe("ComponentName", () => {
121
+ beforeEach(() => {
122
+ vi.clearAllMocks();
123
+ });
124
+
125
+ // Grupo 1: Renderização
126
+ describe("Rendering", () => {
127
+ it("should render correctly with default props", () => {});
128
+ it("should render children when provided", () => {});
129
+ });
130
+
131
+ // Grupo 2: Props
132
+ describe("Props", () => {
133
+ it("should apply custom className", () => {});
134
+ it("should handle variant prop correctly", () => {});
135
+ });
136
+
137
+ // Grupo 3: Interações
138
+ describe("Interactions", () => {
139
+ it("should call onClick when clicked", () => {});
140
+ it("should handle keyboard navigation", () => {});
141
+ });
142
+
143
+ // Grupo 4: Estados
144
+ describe("States", () => {
145
+ it("should display loading state", () => {});
146
+ it("should display disabled state", () => {});
147
+ });
148
+
149
+ // Grupo 5: Acessibilidade
150
+ describe("Accessibility", () => {
151
+ it("should have correct ARIA attributes", () => {});
152
+ it("should be focusable", () => {});
153
+ });
154
+ });
155
+ ```
156
+
157
+ ---
158
+
159
+ ## 4. Categorias de Testes
160
+
161
+ ### 4.1 Testes de Renderização
162
+
163
+ Verificam se o componente renderiza corretamente.
164
+
165
+ ```typescript
166
+ describe("Rendering", () => {
167
+ it("should render correctly with default props", () => {
168
+ renderComponent();
169
+ expect(screen.getByRole("button")).toBeInTheDocument();
170
+ });
171
+
172
+ it("should render children content", () => {
173
+ renderComponent({ children: "Click me" });
174
+ expect(screen.getByText("Click me")).toBeInTheDocument();
175
+ });
176
+
177
+ it("should not render when condition is false", () => {
178
+ renderComponent({ show: false });
179
+ expect(screen.queryByRole("button")).not.toBeInTheDocument();
180
+ });
181
+ });
182
+ ```
183
+
184
+ ### 4.2 Testes de Props
185
+
186
+ Verificam se as props são aplicadas corretamente.
187
+
188
+ ```typescript
189
+ describe("Props", () => {
190
+ it("should apply size prop correctly", () => {
191
+ renderComponent({ size: "lg" });
192
+ expect(screen.getByRole("button")).toHaveClass("lg");
193
+ });
194
+
195
+ it("should apply variant prop correctly", () => {
196
+ renderComponent({ variant: "outline" });
197
+ expect(screen.getByRole("button")).toHaveClass("outline");
198
+ });
199
+
200
+ it("should merge custom className with base classes", () => {
201
+ renderComponent({ className: "custom-class" });
202
+ expect(screen.getByRole("button")).toHaveClass("custom-class");
203
+ });
204
+
205
+ it("should spread additional HTML attributes", () => {
206
+ renderComponent({ "data-testid": "custom-button", id: "btn-1" });
207
+ expect(screen.getByTestId("custom-button")).toHaveAttribute("id", "btn-1");
208
+ });
209
+ });
210
+ ```
211
+
212
+ ### 4.3 Testes de Interação
213
+
214
+ Verificam eventos e comportamentos do usuário.
215
+
216
+ ```typescript
217
+ describe("Interactions", () => {
218
+ it("should call onClick handler when clicked", async () => {
219
+ const handleClick = vi.fn();
220
+ renderComponent({ onClick: handleClick });
221
+
222
+ await userEvent.click(screen.getByRole("button"));
223
+
224
+ expect(handleClick).toHaveBeenCalledTimes(1);
225
+ });
226
+
227
+ it("should not call onClick when disabled", async () => {
228
+ const handleClick = vi.fn();
229
+ renderComponent({ onClick: handleClick, disabled: true });
230
+
231
+ await userEvent.click(screen.getByRole("button"));
232
+
233
+ expect(handleClick).not.toHaveBeenCalled();
234
+ });
235
+
236
+ it("should handle double click", async () => {
237
+ const handleDoubleClick = vi.fn();
238
+ renderComponent({ onDoubleClick: handleDoubleClick });
239
+
240
+ await userEvent.dblClick(screen.getByRole("button"));
241
+
242
+ expect(handleDoubleClick).toHaveBeenCalledTimes(1);
243
+ });
244
+
245
+ it("should handle keyboard events", async () => {
246
+ const handleKeyDown = vi.fn();
247
+ renderComponent({ onKeyDown: handleKeyDown });
248
+
249
+ screen.getByRole("button").focus();
250
+ await userEvent.keyboard("{Enter}");
251
+
252
+ expect(handleKeyDown).toHaveBeenCalled();
253
+ });
254
+ });
255
+ ```
256
+
257
+ ### 4.4 Testes de Estado
258
+
259
+ Verificam estados visuais e comportamentais.
260
+
261
+ ```typescript
262
+ describe("States", () => {
263
+ it("should show loading spinner when isLoading is true", () => {
264
+ renderComponent({ isLoading: true });
265
+ expect(screen.getByRole("button")).toHaveClass("loadingTrue");
266
+ });
267
+
268
+ it("should display loading text when provided", () => {
269
+ renderComponent({ isLoading: true, loadingText: "Loading..." });
270
+ expect(screen.getByText("Loading...")).toBeInTheDocument();
271
+ });
272
+
273
+ it("should be disabled when disabled prop is true", () => {
274
+ renderComponent({ disabled: true });
275
+ expect(screen.getByRole("button")).toBeDisabled();
276
+ });
277
+
278
+ it("should be disabled when loading", () => {
279
+ renderComponent({ isLoading: true });
280
+ expect(screen.getByRole("button")).toBeDisabled();
281
+ });
282
+ });
283
+ ```
284
+
285
+ ### 4.5 Testes de Acessibilidade
286
+
287
+ Verificam conformidade com padrões de acessibilidade.
288
+
289
+ ```typescript
290
+ describe("Accessibility", () => {
291
+ it("should have correct role", () => {
292
+ renderComponent();
293
+ expect(screen.getByRole("button")).toBeInTheDocument();
294
+ });
295
+
296
+ it("should support aria-label", () => {
297
+ renderComponent({ "aria-label": "Close modal" });
298
+ expect(screen.getByLabelText("Close modal")).toBeInTheDocument();
299
+ });
300
+
301
+ it("should support aria-describedby", () => {
302
+ renderComponent({ "aria-describedby": "help-text" });
303
+ expect(screen.getByRole("button")).toHaveAttribute(
304
+ "aria-describedby",
305
+ "help-text",
306
+ );
307
+ });
308
+
309
+ it("should be focusable when not disabled", () => {
310
+ renderComponent();
311
+ const button = screen.getByRole("button");
312
+ button.focus();
313
+ expect(button).toHaveFocus();
314
+ });
315
+
316
+ it("should have type button by default to prevent form submission", () => {
317
+ renderComponent({ type: "button" });
318
+ expect(screen.getByRole("button")).toHaveAttribute("type", "button");
319
+ });
320
+ });
321
+ ```
322
+
323
+ ### 4.6 Testes de Variações/Combinações
324
+
325
+ Verificam diferentes combinações de props.
326
+
327
+ ```typescript
328
+ describe("Variations", () => {
329
+ const sizes = ["xs", "sm", "md", "lg"] as const;
330
+ const variants = ["solid", "outline", "ghost"] as const;
331
+ const schemes = ["primary", "success", "danger"] as const;
332
+
333
+ sizes.forEach((size) => {
334
+ it(`should render correctly with size="${size}"`, () => {
335
+ renderComponent({ size });
336
+ expect(screen.getByRole("button")).toHaveClass(size);
337
+ });
338
+ });
339
+
340
+ variants.forEach((variant) => {
341
+ it(`should render correctly with variant="${variant}"`, () => {
342
+ renderComponent({ variant });
343
+ expect(screen.getByRole("button")).toHaveClass(variant);
344
+ });
345
+ });
346
+
347
+ schemes.forEach((scheme) => {
348
+ it(`should render correctly with scheme="${scheme}"`, () => {
349
+ renderComponent({ scheme });
350
+ expect(screen.getByRole("button")).toHaveClass(scheme);
351
+ });
352
+ });
353
+ });
354
+ ```
355
+
356
+ ### 4.7 Testes de Ícones (se aplicável)
357
+
358
+ Verificam renderização de ícones.
359
+
360
+ ```typescript
361
+ describe("Icons", () => {
362
+ it("should render left icon when provided", () => {
363
+ const MockIcon = () => <svg data-testid="left-icon" />;
364
+ renderComponent({ leftIcon: MockIcon });
365
+ expect(screen.getByTestId("left-icon")).toBeInTheDocument();
366
+ });
367
+
368
+ it("should render right icon when provided", () => {
369
+ const MockIcon = () => <svg data-testid="right-icon" />;
370
+ renderComponent({ rightIcon: MockIcon });
371
+ expect(screen.getByTestId("right-icon")).toBeInTheDocument();
372
+ });
373
+
374
+ it("should render both icons simultaneously", () => {
375
+ const LeftMock = () => <svg data-testid="left-icon" />;
376
+ const RightMock = () => <svg data-testid="right-icon" />;
377
+ renderComponent({ leftIcon: LeftMock, rightIcon: RightMock });
378
+
379
+ expect(screen.getByTestId("left-icon")).toBeInTheDocument();
380
+ expect(screen.getByTestId("right-icon")).toBeInTheDocument();
381
+ });
382
+ });
383
+ ```
384
+
385
+ ---
386
+
387
+ ## 5. Padrões de Nomenclatura
388
+
389
+ ### Formato do `describe`:
390
+
391
+ - Use o nome do componente como primeiro `describe`
392
+ - Agrupe testes relacionados em `describe` aninhados
393
+
394
+ ### Formato do `it`:
395
+
396
+ Use o padrão: `should [comportamento esperado] when [condição]`
397
+
398
+ **Exemplos:**
399
+
400
+ - ✅ `should render correctly with default props`
401
+ - ✅ `should call onClick when button is clicked`
402
+ - ✅ `should be disabled when isLoading is true`
403
+ - ✅ `should apply custom className when provided`
404
+ - ❌ `test button click` (muito vago)
405
+ - ❌ `onClick works` (não descreve o cenário)
406
+
407
+ ---
408
+
409
+ ## 6. Checklist de Cobertura
410
+
411
+ ### Antes de considerar os testes completos, verifique:
412
+
413
+ #### Renderização
414
+
415
+ - [ ] Renderiza com props padrão
416
+ - [ ] Renderiza children corretamente
417
+ - [ ] Aplica className customizada
418
+ - [ ] Propaga atributos HTML adicionais (data-\*, id, etc.)
419
+
420
+ #### Props
421
+
422
+ - [ ] Cada prop documentada tem pelo menos 1 teste
423
+ - [ ] Valores default são testados
424
+ - [ ] Diferentes valores de cada prop são testados
425
+
426
+ #### Interações
427
+
428
+ - [ ] onClick é chamado corretamente
429
+ - [ ] Eventos de teclado funcionam (se aplicável)
430
+ - [ ] Comportamento quando disabled
431
+ - [ ] Foco e blur (se aplicável)
432
+
433
+ #### Estados
434
+
435
+ - [ ] Estado de loading (se aplicável)
436
+ - [ ] Estado disabled
437
+ - [ ] Estados visuais (hover, active, focus via classes)
438
+
439
+ #### Acessibilidade
440
+
441
+ - [ ] Role correto
442
+ - [ ] Suporte a aria-label
443
+ - [ ] Focusable quando apropriado
444
+ - [ ] Anúncio de estados para screen readers
445
+
446
+ #### Edge Cases
447
+
448
+ - [ ] Props undefined ou null
449
+ - [ ] Strings vazias
450
+ - [ ] Combinações incomuns de props
451
+
452
+ ---
453
+
454
+ ## 7. Exemplos Práticos
455
+
456
+ ### Exemplo completo de teste para um Button:
457
+
458
+ Veja o arquivo `/packages/components/src/components/__test__/button.test.tsx` para um exemplo completo e real de como os testes devem ser estruturados.
459
+
460
+ ---
461
+
462
+ ## 8. Boas Práticas
463
+
464
+ ### ✅ FAÇA:
465
+
466
+ 1. **Teste comportamento, não implementação**
467
+
468
+ ```typescript
469
+ // ✅ Bom - testa o comportamento
470
+ expect(screen.getByRole("button")).toBeDisabled();
471
+
472
+ // ❌ Ruim - testa implementação
473
+ expect(component.state.disabled).toBe(true);
474
+ ```
475
+
476
+ 2. **Use queries apropriadas**
477
+
478
+ ```typescript
479
+ // Prioridade de queries:
480
+ // 1. getByRole (mais acessível)
481
+ // 2. getByLabelText
482
+ // 3. getByText
483
+ // 4. getByTestId (último recurso)
484
+ ```
485
+
486
+ 3. **Aguarde operações assíncronas**
487
+
488
+ ```typescript
489
+ await userEvent.click(button);
490
+ await waitFor(() => expect(mockFn).toHaveBeenCalled());
491
+ ```
492
+
493
+ 4. **Use userEvent ao invés de fireEvent**
494
+
495
+ ```typescript
496
+ // ✅ Simula comportamento real do usuário
497
+ await userEvent.click(button);
498
+
499
+ // ❌ Dispara evento diretamente
500
+ fireEvent.click(button);
501
+ ```
502
+
503
+ 5. **Limpe mocks entre testes**
504
+ ```typescript
505
+ beforeEach(() => {
506
+ vi.clearAllMocks();
507
+ });
508
+ ```
509
+
510
+ ### ❌ NÃO FAÇA:
511
+
512
+ 1. **Não teste bibliotecas externas**
513
+ - Não teste se o React renderiza
514
+ - Não teste comportamento do browser
515
+
516
+ 2. **Não use snapshots como teste principal**
517
+ - Snapshots são frágeis e difíceis de manter
518
+
519
+ 3. **Não duplique testes**
520
+ - Cada comportamento deve ser testado uma vez
521
+
522
+ 4. **Não ignore erros do console**
523
+ - Warnings e errors indicam problemas reais
524
+
525
+ 5. **Não dependa de timeouts arbitrários**
526
+
527
+ ```typescript
528
+ // ❌ Ruim
529
+ await new Promise((r) => setTimeout(r, 1000));
530
+
531
+ // ✅ Bom
532
+ await waitFor(() => expect(element).toBeVisible());
533
+ ```
534
+
535
+ ---
536
+
537
+ ## 📝 Template Rápido
538
+
539
+ ```typescript
540
+ import { describe, it, expect, vi, beforeEach } from "vitest";
541
+ import { render, screen } from "@testing-library/react";
542
+ import userEvent from "@testing-library/user-event";
543
+
544
+ import { ComponentName } from "./index";
545
+
546
+ const defaultProps = {};
547
+
548
+ const renderComponent = (props = {}) => {
549
+ return render(<ComponentName {...defaultProps} {...props} />);
550
+ };
551
+
552
+ describe("ComponentName", () => {
553
+ beforeEach(() => {
554
+ vi.clearAllMocks();
555
+ });
556
+
557
+ describe("Rendering", () => {
558
+ it("should render correctly with default props", () => {
559
+ renderComponent();
560
+ // adicione assertions
561
+ });
562
+ });
563
+
564
+ describe("Props", () => {
565
+ // testes de props
566
+ });
567
+
568
+ describe("Interactions", () => {
569
+ // testes de interação
570
+ });
571
+
572
+ describe("States", () => {
573
+ // testes de estado
574
+ });
575
+
576
+ describe("Accessibility", () => {
577
+ // testes de acessibilidade
578
+ });
579
+ });
580
+ ```
581
+
582
+ ---
583
+
584
+ ## 🔄 Fluxo de Trabalho para IAs
585
+
586
+ Ao receber um componente para testar, siga este fluxo:
587
+
588
+ 1. **Analise o componente**
589
+ - Identifique todas as props e seus tipos
590
+ - Identifique valores default
591
+ - Identifique estados possíveis
592
+ - Identifique eventos/handlers
593
+
594
+ 2. **Crie a estrutura do teste**
595
+ - Use o template fornecido
596
+ - Organize em grupos lógicos
597
+
598
+ 3. **Implemente os testes**
599
+ - Comece pelos testes de renderização
600
+ - Siga com props, interações, estados
601
+ - Finalize com acessibilidade e edge cases
602
+
603
+ 4. **Valide a cobertura**
604
+ - Use o checklist da seção 6
605
+ - Garanta que cada prop/comportamento está coberto
606
+
607
+ 5. **Revise a nomenclatura**
608
+ - Todos os `it` seguem o padrão `should...when...`?
609
+ - Os grupos fazem sentido?
610
+
611
+ ---
612
+
613
+ **Última atualização:** Março 2026