@hd-agent-kit/cli 1.0.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.
- package/README.md +83 -0
- package/bin/cli.js +52 -0
- package/package.json +27 -0
- package/src/index.js +151 -0
- package/templates/.agents/skills/analyze/SKILL.md +328 -0
- package/templates/.agents/skills/commit/SKILL.md +257 -0
- package/templates/.agents/skills/document/SKILL.md +322 -0
- package/templates/.agents/skills/performance-check/SKILL.md +250 -0
- package/templates/.agents/skills/refactor/SKILL.md +240 -0
- package/templates/.agents/skills/security-scan/SKILL.md +319 -0
- package/templates/.agents/skills/write-tests/SKILL.md +389 -0
- package/templates/AGENTS.md +823 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# /security-scan — Güvenlik Tarama Skill'i
|
|
2
|
+
|
|
3
|
+
> **Tetikleyici:** `/security-scan`
|
|
4
|
+
> **Amaç:** Dependency audit, XSS/CSRF risk tespiti, env variable kontrolü ve güvenlik best practice'lerine uyum raporu oluşturmak.
|
|
5
|
+
> **Çıktı:** Kategorize edilmiş güvenlik raporu + düzeltme önerileri
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Temel İlkeler
|
|
10
|
+
|
|
11
|
+
- **Defense in depth.** Tek bir güvenlik katmanına güvenme; çok katmanlı savunma kur.
|
|
12
|
+
- **Principle of least privilege.** Her modül/kullanıcı sadece gereken minimum yetkiye sahip olsun.
|
|
13
|
+
- **Fail secure.** Hata durumunda güvenli tarafa düş (erişimi kapat, default deny).
|
|
14
|
+
- **Input'a asla güvenme.** Tüm kullanıcı girdilerini her zaman doğrula ve sanitize et.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Kullanım Formatları
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
/security-scan ← Tüm proje taraması
|
|
22
|
+
/security-scan src/services/auth.ts ← Belirli dosya taraması
|
|
23
|
+
/security-scan dependencies ← Sadece dependency audit
|
|
24
|
+
/security-scan env ← Sadece env variable kontrolü
|
|
25
|
+
/security-scan auth ← Auth katmanı güvenlik kontrolü
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Tarama Kategorileri
|
|
31
|
+
|
|
32
|
+
### Kategori 1: Dependency Güvenliği
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
📦 Dependency Audit:
|
|
36
|
+
|
|
37
|
+
1. Bilinen Güvenlik Açıkları:
|
|
38
|
+
├── npm audit / pnpm audit sonucu:
|
|
39
|
+
│ ├── Critical: [N adet]
|
|
40
|
+
│ ├── High: [N adet]
|
|
41
|
+
│ ├── Moderate: [N adet]
|
|
42
|
+
│ └── Low: [N adet]
|
|
43
|
+
├── Etkilenen paketler:
|
|
44
|
+
│ ├── [paket@versiyon] → [CVE-XXXX-XXXX] — [açıklama] — Fix: [güncel versiyon]
|
|
45
|
+
│ └── ...
|
|
46
|
+
└── Transitive dependency riskleri: [dolaylı bağımlılıklardaki açıklar]
|
|
47
|
+
|
|
48
|
+
2. Eski/Bakımsız Dependency'ler:
|
|
49
|
+
├── Son güncellemesi 1+ yıl: [liste]
|
|
50
|
+
├── Deprecated paketler: [liste]
|
|
51
|
+
├── Bilinen alternatifler: [mevcut → önerilen]
|
|
52
|
+
└── Önemsiz/gereksiz dependency'ler: [kaldırılabilecekler]
|
|
53
|
+
|
|
54
|
+
3. License Kontrolü:
|
|
55
|
+
├── Ticari kullanımda sorunlu lisanslar: [GPL, AGPL vs.]
|
|
56
|
+
├── Lisans uyumsuzlukları: [var/yok]
|
|
57
|
+
└── Lisansı belirsiz paketler: [liste]
|
|
58
|
+
|
|
59
|
+
4. Supply Chain Riskleri:
|
|
60
|
+
├── Lockfile güncel mi? (package-lock.json / pnpm-lock.yaml)
|
|
61
|
+
├── Exact versioning kullanılıyor mu?
|
|
62
|
+
├── .npmrc güvenlik ayarları: [var/yok]
|
|
63
|
+
└── Typosquatting riski: [benzer isimli paketler]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Kategori 2: XSS (Cross-Site Scripting) Taraması
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
🛡️ XSS Risk Analizi:
|
|
70
|
+
|
|
71
|
+
1. dangerouslySetInnerHTML Kullanımı:
|
|
72
|
+
├── Kullanım yerleri: [dosya:satır listesi]
|
|
73
|
+
├── Sanitization: [DOMPurify kullanılıyor mu?]
|
|
74
|
+
├── Kaynak güvenli mi: [kullanıcı girdisi mi, statik veri mi?]
|
|
75
|
+
└── Risk seviyesi: [Kritik/Yüksek/Orta/Düşük]
|
|
76
|
+
|
|
77
|
+
2. URL İşleme:
|
|
78
|
+
├── href'e kullanıcı girdisi aktarılıyor mu?
|
|
79
|
+
├── javascript: protocol kontrolü: [var/yok]
|
|
80
|
+
├── URL sanitization: [var/yok]
|
|
81
|
+
└── window.location manipülasyonu: [var/yok]
|
|
82
|
+
|
|
83
|
+
3. DOM Manipülasyonu:
|
|
84
|
+
├── innerHTML kullanımı: [var/yok]
|
|
85
|
+
├── document.write kullanımı: [var/yok]
|
|
86
|
+
├── eval() kullanımı: [var/yok]
|
|
87
|
+
├── new Function() kullanımı: [var/yok]
|
|
88
|
+
└── template literal'da HTML: [var/yok]
|
|
89
|
+
|
|
90
|
+
4. Üçüncü Parti Script'ler:
|
|
91
|
+
├── Harici script tag'ları: [liste]
|
|
92
|
+
├── SRI (Subresource Integrity): [var/yok]
|
|
93
|
+
├── Sandbox attribute: [iframe'lerde var/yok]
|
|
94
|
+
└── Postmessage origin kontrolü: [var/yok]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Kategori 3: CSRF & İstek Güvenliği
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
🔐 CSRF & Request Security:
|
|
101
|
+
|
|
102
|
+
1. CSRF Koruması:
|
|
103
|
+
├── CSRF token implementasyonu: [var/yok]
|
|
104
|
+
├── SameSite cookie attribute: [var/yok, değeri]
|
|
105
|
+
├── Custom header kontrolü: [var/yok]
|
|
106
|
+
└── State-changing GET request'ler: [var/yok — olmamalı]
|
|
107
|
+
|
|
108
|
+
2. CORS Yapılandırması:
|
|
109
|
+
├── Access-Control-Allow-Origin: [değer]
|
|
110
|
+
├── Wildcard (*) kullanımı: [var/yok — riskli]
|
|
111
|
+
├── Credentials ile CORS: [var/yok]
|
|
112
|
+
├── Allowed methods: [liste]
|
|
113
|
+
└── Exposed headers: [liste]
|
|
114
|
+
|
|
115
|
+
3. HTTP Güvenlik Header'ları:
|
|
116
|
+
├── Content-Security-Policy: [var/yok, değer]
|
|
117
|
+
├── X-Content-Type-Options: nosniff [var/yok]
|
|
118
|
+
├── X-Frame-Options: [var/yok, değer]
|
|
119
|
+
├── Strict-Transport-Security: [var/yok]
|
|
120
|
+
├── Referrer-Policy: [var/yok, değer]
|
|
121
|
+
├── Permissions-Policy: [var/yok]
|
|
122
|
+
└── X-XSS-Protection: [var/yok — modern tarayıcılarda gereksiz]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Kategori 4: Authentication & Authorization
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
🔑 Auth Güvenliği:
|
|
129
|
+
|
|
130
|
+
1. Token Yönetimi:
|
|
131
|
+
├── Token storage: [localStorage / sessionStorage / httpOnly cookie]
|
|
132
|
+
├── Token tipi: [JWT / opaque / session ID]
|
|
133
|
+
├── Token expiry: [var/yok, süre]
|
|
134
|
+
├── Refresh token mekanizması: [var/yok]
|
|
135
|
+
├── Token rotation: [var/yok]
|
|
136
|
+
└── Logout'ta token invalidation: [var/yok]
|
|
137
|
+
|
|
138
|
+
2. Oturum Güvenliği:
|
|
139
|
+
├── Session fixation koruması: [var/yok]
|
|
140
|
+
├── Concurrent session kontrolü: [var/yok]
|
|
141
|
+
├── Idle timeout: [var/yok, süre]
|
|
142
|
+
├── Absolute timeout: [var/yok, süre]
|
|
143
|
+
└── Secure/HttpOnly/SameSite cookie flags: [var/yok]
|
|
144
|
+
|
|
145
|
+
3. Yetkilendirme:
|
|
146
|
+
├── Route-level auth check: [var/yok]
|
|
147
|
+
├── API-level auth check: [var/yok]
|
|
148
|
+
├── Role-based access control: [var/yok]
|
|
149
|
+
├── Client-side route guard: [var/yok]
|
|
150
|
+
├── Server-side middleware: [var/yok]
|
|
151
|
+
└── Privilege escalation riski: [var/yok]
|
|
152
|
+
|
|
153
|
+
4. Şifre/Credential Güvenliği:
|
|
154
|
+
├── Şifre karmaşıklık kuralları: [var/yok]
|
|
155
|
+
├── Rate limiting (brute force): [var/yok]
|
|
156
|
+
├── Account lockout: [var/yok]
|
|
157
|
+
├── Password hash algoritması: [bcrypt/argon2/scrypt]
|
|
158
|
+
└── 2FA desteği: [var/yok]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Kategori 5: Environment & Secret Yönetimi
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
🔒 Environment & Secrets:
|
|
165
|
+
|
|
166
|
+
1. Env Variable Güvenliği:
|
|
167
|
+
├── Client-side'da erişilebilen secret'lar: [RISK — liste]
|
|
168
|
+
├── .env dosyası gitignore'da mı?: [Evet/Hayır]
|
|
169
|
+
├── .env.example mevcut mu?: [Evet/Hayır]
|
|
170
|
+
├── Hardcoded secret/API key: [var/yok — dosya:satır]
|
|
171
|
+
└── Secret'lar sadece server-side'da mı?: [Evet/Hayır]
|
|
172
|
+
|
|
173
|
+
2. Git Geçmişi:
|
|
174
|
+
├── Geçmişte commit'lenmiş secret: [tespit edilebiliyorsa]
|
|
175
|
+
├── .gitignore kapsamı yeterli mi?: [Evet/Hayır]
|
|
176
|
+
└── Git hook'lar (secret scanning): [var/yok]
|
|
177
|
+
|
|
178
|
+
3. Üçüncü Parti Servis Key'leri:
|
|
179
|
+
├── API key'ler rotation'a uygun mu?: [Evet/Hayır]
|
|
180
|
+
├── Minimum yetki prensibi: [Evet/Hayır]
|
|
181
|
+
├── Key scope/restriction: [var/yok]
|
|
182
|
+
└── Ayrı dev/prod key'ler: [Evet/Hayır]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Kategori 6: Input Validation & Data Sanitization
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
🧹 Input Validation:
|
|
189
|
+
|
|
190
|
+
1. Form Validation:
|
|
191
|
+
├── Client-side validation: [var/yok, kütüphane]
|
|
192
|
+
├── Server-side validation: [var/yok, kütüphane]
|
|
193
|
+
├── Validation schema paylaşımı (client-server): [var/yok]
|
|
194
|
+
└── Zod/Yup/Valibot kullanımı: [var/yok]
|
|
195
|
+
|
|
196
|
+
2. SQL/NoSQL Injection:
|
|
197
|
+
├── Parameterized query kullanımı: [var/yok]
|
|
198
|
+
├── ORM kullanımı: [var/yok — Prisma/Drizzle vs.]
|
|
199
|
+
├── Raw query'ler: [var/yok — dosya:satır]
|
|
200
|
+
└── User input'un query'de doğrudan kullanımı: [var/yok]
|
|
201
|
+
|
|
202
|
+
3. File Upload:
|
|
203
|
+
├── File type validation: [var/yok]
|
|
204
|
+
├── File size limiti: [var/yok, değer]
|
|
205
|
+
├── File name sanitization: [var/yok]
|
|
206
|
+
├── Magic number validation: [var/yok]
|
|
207
|
+
└── Upload dizini web root dışında mı?: [Evet/Hayır]
|
|
208
|
+
|
|
209
|
+
4. API Input:
|
|
210
|
+
├── Request body validation: [var/yok]
|
|
211
|
+
├── Query parameter validation: [var/yok]
|
|
212
|
+
├── Path parameter validation: [var/yok]
|
|
213
|
+
├── Content-Type kontrolü: [var/yok]
|
|
214
|
+
└── Rate limiting: [var/yok]
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Kategori 7: Frontend-Specific Güvenlik
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
🖥️ Frontend Güvenlik:
|
|
221
|
+
|
|
222
|
+
1. Dış Linkler:
|
|
223
|
+
├── rel="noopener noreferrer" kullanımı: [var/yok]
|
|
224
|
+
├── Target="_blank" güvenliği: [var/yok]
|
|
225
|
+
└── Outbound link kontrolü: [var/yok]
|
|
226
|
+
|
|
227
|
+
2. Sensitive Data Handling:
|
|
228
|
+
├── Console.log'da sensitive veri: [var/yok]
|
|
229
|
+
├── localStorage'da sensitive veri: [var/yok]
|
|
230
|
+
├── URL'de sensitive veri (query param): [var/yok]
|
|
231
|
+
├── Error mesajlarında sensitive veri leak: [var/yok]
|
|
232
|
+
└── Source map production'da kapalı mı?: [Evet/Hayır]
|
|
233
|
+
|
|
234
|
+
3. Content Injection:
|
|
235
|
+
├── User-generated content sanitization: [var/yok]
|
|
236
|
+
├── Markdown rendering güvenliği: [var/yok]
|
|
237
|
+
├── Rich text editor güvenliği: [var/yok]
|
|
238
|
+
└── SVG upload XSS riski: [var/yok]
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Puanlama Sistemi
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
🔒 Güvenlik Raporu:
|
|
247
|
+
|
|
248
|
+
┌──────────────────────────┬───────┬──────────┐
|
|
249
|
+
│ Kategori │ Puan │ Seviye │
|
|
250
|
+
├──────────────────────────┼───────┼──────────┤
|
|
251
|
+
│ Dependency Güvenliği │ XX/100│ 🟢/🟡/🔴│
|
|
252
|
+
│ XSS Koruması │ XX/100│ 🟢/🟡/🔴│
|
|
253
|
+
│ CSRF & Request Security │ XX/100│ 🟢/🟡/🔴│
|
|
254
|
+
│ Auth & Authorization │ XX/100│ 🟢/🟡/🔴│
|
|
255
|
+
│ Environment & Secrets │ XX/100│ 🟢/🟡/🔴│
|
|
256
|
+
│ Input Validation │ XX/100│ 🟢/🟡/🔴│
|
|
257
|
+
│ Frontend Security │ XX/100│ 🟢/🟡/🔴│
|
|
258
|
+
├──────────────────────────┼───────┼──────────┤
|
|
259
|
+
│ GENEL │ XX/100│ 🟢/🟡/🔴│
|
|
260
|
+
└──────────────────────────┴───────┴──────────┘
|
|
261
|
+
|
|
262
|
+
🟢 90-100: Güvenli
|
|
263
|
+
🟡 60-89: İyileştirme gerekli
|
|
264
|
+
🔴 0-59: Kritik — acil müdahale
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Düzeltme Önerileri
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
🎯 Düzeltme Planı (Öncelik Sırasına Göre):
|
|
273
|
+
|
|
274
|
+
🔴 Kritik (Hemen Düzelt):
|
|
275
|
+
1. [Sorun] → [Çözüm] — [Etkilenen dosya]
|
|
276
|
+
2. [Sorun] → [Çözüm]
|
|
277
|
+
|
|
278
|
+
🟡 Yüksek (Bu Sprint İçinde):
|
|
279
|
+
3. [Sorun] → [Çözüm]
|
|
280
|
+
4. [Sorun] → [Çözüm]
|
|
281
|
+
|
|
282
|
+
🟢 Orta (Plana Al):
|
|
283
|
+
5. [Sorun] → [Çözüm]
|
|
284
|
+
6. [Sorun] → [Çözüm]
|
|
285
|
+
|
|
286
|
+
ℹ️ Bilgi (Best Practice Önerisi):
|
|
287
|
+
7. [Öneri]
|
|
288
|
+
8. [Öneri]
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## OWASP Top 10 Kontrol Listesi
|
|
294
|
+
|
|
295
|
+
Her taramada OWASP Top 10'u (2021) referans al:
|
|
296
|
+
|
|
297
|
+
| # | Risk | Kontrol | Durum |
|
|
298
|
+
|---|------|---------|-------|
|
|
299
|
+
| A01 | Broken Access Control | Yetkilendirme kontrolleri | ✅/❌ |
|
|
300
|
+
| A02 | Cryptographic Failures | Şifreleme/hashing uygulamaları | ✅/❌ |
|
|
301
|
+
| A03 | Injection | SQL/NoSQL/OS/LDAP injection koruması | ✅/❌ |
|
|
302
|
+
| A04 | Insecure Design | Güvenli tasarım pattern'ları | ✅/❌ |
|
|
303
|
+
| A05 | Security Misconfiguration | Header'lar, CORS, default ayarlar | ✅/❌ |
|
|
304
|
+
| A06 | Vulnerable Components | Dependency güvenliği | ✅/❌ |
|
|
305
|
+
| A07 | Auth Failures | Kimlik doğrulama zafiyetleri | ✅/❌ |
|
|
306
|
+
| A08 | Data Integrity Failures | Veri bütünlüğü kontrolleri | ✅/❌ |
|
|
307
|
+
| A09 | Logging Failures | Güvenlik log'lama | ✅/❌ |
|
|
308
|
+
| A10 | SSRF | Server-Side Request Forgery koruması | ✅/❌ |
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Çıktı Formatı
|
|
313
|
+
|
|
314
|
+
1. **Genel skor** — Hızlı bakış güvenlik puanlama tablosu
|
|
315
|
+
2. **Kritik bulgular** — Acil düzeltme gerektiren sorunlar (varsa ilk başta vurgula)
|
|
316
|
+
3. **Kategori detayları** — Her kategori için kontrol listesi sonuçları
|
|
317
|
+
4. **OWASP Top 10 uyum** — Hangi riskler karşılanmış, hangisi eksik
|
|
318
|
+
5. **Düzeltme planı** — Öncelik sırasına göre aksiyon listesi
|
|
319
|
+
6. **Otomatik düzeltilebilecekler** — Hemen uygulanabilecek fix'ler (kullanıcı onayı ile)
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# /write-tests — Test Yazma Skill'i
|
|
2
|
+
|
|
3
|
+
> **Tetikleyici:** `/write-tests [dosya/modül]`
|
|
4
|
+
> **Amaç:** Belirtilen component, hook veya utility için kapsamlı test dosyası üretmek.
|
|
5
|
+
> **Çıktı:** Proje convention'ına uygun test dosyası
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Temel İlkeler
|
|
10
|
+
|
|
11
|
+
- **Kullanıcı davranışını test et, implementation detail'leri değil.** Testing Library felsefesi.
|
|
12
|
+
- **Her test bağımsız (isolated) olmalı.** Testler arası paylaşılan state olmamalı.
|
|
13
|
+
- **Mock'ları minimum tut.** Gerçek modülleri mümkün olduğunca kullan.
|
|
14
|
+
- **Test piramidini uygula.** Çok unit test, az integration test, minimal E2E test.
|
|
15
|
+
- **Mevcut test convention'ına uy.** Proje kurallarındaki test yapısını takip et.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Kullanım Formatları
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
/write-tests src/components/Button.tsx
|
|
23
|
+
/write-tests src/hooks/useAuth.ts
|
|
24
|
+
/write-tests src/utils/formatDate.ts
|
|
25
|
+
/write-tests src/services/userService.ts
|
|
26
|
+
/write-tests src/features/auth/ ← Tüm klasör için test planı
|
|
27
|
+
/write-tests UserProfile ← Modül adı ile (AI dosyayı bulur)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Uygulama Adımları
|
|
33
|
+
|
|
34
|
+
### Adım 1: Hedef Analizi
|
|
35
|
+
|
|
36
|
+
Hedef dosyayı oku ve şunları tespit et:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
📋 Test Hedefi Analizi:
|
|
40
|
+
├── Hedef dosya: [dosya yolu]
|
|
41
|
+
├── Dosya tipi: [component / hook / util / service / page]
|
|
42
|
+
├── Export'lar: [fonksiyon/component listesi]
|
|
43
|
+
├── Bağımlılıklar: [import edilen modüller]
|
|
44
|
+
├── Props/Parametreler: [tip bilgileri]
|
|
45
|
+
├── Side effect'ler: [API çağrısı, localStorage, timer vs.]
|
|
46
|
+
├── Mevcut test: [var/yok, dosya yolu]
|
|
47
|
+
└── Test runner: [Vitest / Jest / projeden tespit et]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Adım 2: Proje Test Convention Tespiti
|
|
51
|
+
|
|
52
|
+
Projede mevcut testleri inceleyerek convention belirle:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
🔍 Test Convention:
|
|
56
|
+
├── Test runner: [Vitest / Jest]
|
|
57
|
+
├── Test dosya konumu: [yanında / __tests__/ klasöründe]
|
|
58
|
+
├── Dosya adlandırma: [*.test.ts / *.spec.ts / *.test.tsx]
|
|
59
|
+
├── Import stili: [import { render } from '@testing-library/react']
|
|
60
|
+
├── Describe/it yapısı: [nested describe / flat it]
|
|
61
|
+
├── Mock yaklaşımı: [vi.mock / jest.mock / MSW]
|
|
62
|
+
├── Setup dosyası: [setupTests.ts / vitest.setup.ts]
|
|
63
|
+
├── Custom render/wrapper: [var/yok — theme provider, router wrapper vs.]
|
|
64
|
+
└── Assertion stili: [expect().toBe / expect().toEqual / custom matchers]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Adım 3: Test Senaryoları Planlama
|
|
68
|
+
|
|
69
|
+
Her dosya tipi için farklı senaryo stratejisi:
|
|
70
|
+
|
|
71
|
+
#### Component Test Senaryoları
|
|
72
|
+
```
|
|
73
|
+
📝 Test Planı — [ComponentName]:
|
|
74
|
+
|
|
75
|
+
1. Rendering Testleri:
|
|
76
|
+
□ Default props ile doğru render edilme
|
|
77
|
+
□ Tüm prop varyasyonları (her prop kombinasyonu)
|
|
78
|
+
□ Conditional rendering (if/ternary ile gösterilen/gizlenen bölümler)
|
|
79
|
+
□ Children prop ile render
|
|
80
|
+
□ Empty/null state render
|
|
81
|
+
|
|
82
|
+
2. Kullanıcı Etkileşimi:
|
|
83
|
+
□ Click olayları (button, link, toggle)
|
|
84
|
+
□ Form input'ları (type, clear, submit)
|
|
85
|
+
□ Keyboard navigasyonu (Tab, Enter, Escape)
|
|
86
|
+
□ Hover/focus state'leri (tooltip, dropdown)
|
|
87
|
+
|
|
88
|
+
3. State Değişiklikleri:
|
|
89
|
+
□ Initial state doğruluğu
|
|
90
|
+
□ State geçişleri (loading → success, loading → error)
|
|
91
|
+
□ Controlled vs uncontrolled davranış
|
|
92
|
+
|
|
93
|
+
4. API/Async Operasyonlar:
|
|
94
|
+
□ Loading state gösterimi
|
|
95
|
+
□ Success state ve veri gösterimi
|
|
96
|
+
□ Error state ve hata mesajı
|
|
97
|
+
□ Retry mekanizması (varsa)
|
|
98
|
+
|
|
99
|
+
5. Edge Case'ler:
|
|
100
|
+
□ Boş veri / undefined prop
|
|
101
|
+
□ Çok uzun metin / overflow
|
|
102
|
+
□ Hızlı ardışık tıklama (debounce/throttle)
|
|
103
|
+
□ Unmount sırasında cleanup
|
|
104
|
+
|
|
105
|
+
6. Erişilebilirlik:
|
|
106
|
+
□ ARIA attribute'ları doğru mu?
|
|
107
|
+
□ Semantik HTML kullanılmış mı?
|
|
108
|
+
□ Keyboard ile erişilebilir mi?
|
|
109
|
+
□ Screen reader uyumlu mu?
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Hook Test Senaryoları
|
|
113
|
+
```
|
|
114
|
+
📝 Test Planı — [useHookName]:
|
|
115
|
+
|
|
116
|
+
1. Initial State:
|
|
117
|
+
□ Default değerler doğru mu?
|
|
118
|
+
□ Parametrelerle başlatma
|
|
119
|
+
|
|
120
|
+
2. State Güncellemeleri:
|
|
121
|
+
□ Her action/setter doğru çalışıyor mu?
|
|
122
|
+
□ State geçişleri beklenen sırada mı?
|
|
123
|
+
□ Birden fazla güncelleme batch ediliyor mu?
|
|
124
|
+
|
|
125
|
+
3. Side Effect'ler:
|
|
126
|
+
□ useEffect doğru zamanda çalışıyor mu?
|
|
127
|
+
□ Cleanup fonksiyonu çağrılıyor mu?
|
|
128
|
+
□ Dependency değişikliğinde yeniden çalışıyor mu?
|
|
129
|
+
|
|
130
|
+
4. API Entegrasyonu (varsa):
|
|
131
|
+
□ Fetch başarılı senaryo
|
|
132
|
+
□ Fetch hata senaryosu
|
|
133
|
+
□ Loading/stale/error state geçişleri
|
|
134
|
+
□ Cache invalidation (TanStack Query vs.)
|
|
135
|
+
|
|
136
|
+
5. Edge Case'ler:
|
|
137
|
+
□ Rapid state güncellemeleri
|
|
138
|
+
□ Unmount sırasında async işlem
|
|
139
|
+
□ Invalid parametre geçilmesi
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Utility/Service Test Senaryoları
|
|
143
|
+
```
|
|
144
|
+
📝 Test Planı — [functionName]:
|
|
145
|
+
|
|
146
|
+
1. Happy Path:
|
|
147
|
+
□ Normal input → beklenen output
|
|
148
|
+
□ Farklı geçerli input varyasyonları
|
|
149
|
+
|
|
150
|
+
2. Edge Case'ler:
|
|
151
|
+
□ Boş input (null, undefined, empty string, empty array)
|
|
152
|
+
□ Sınır değerler (0, -1, MAX_SAFE_INTEGER)
|
|
153
|
+
□ Özel karakterler / unicode
|
|
154
|
+
□ Çok büyük input
|
|
155
|
+
|
|
156
|
+
3. Error Handling:
|
|
157
|
+
□ Invalid input → doğru hata fırlatma
|
|
158
|
+
□ Hata mesajı açıklayıcı mı?
|
|
159
|
+
|
|
160
|
+
4. Type Safety (TypeScript):
|
|
161
|
+
□ Tip uyumsuzluğu compile-time'da yakalanıyor mu?
|
|
162
|
+
□ Return tipi doğru çıkarılıyor mu?
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Adım 4: Test Dosyası Oluşturma
|
|
166
|
+
|
|
167
|
+
Test planını onaylandıktan sonra, proje convention'ına uygun test dosyası yaz:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
📄 Test Dosyası Yapısı:
|
|
171
|
+
|
|
172
|
+
[dosya-adı].test.tsx (veya projede kullanılan uzantı)
|
|
173
|
+
├── Import'lar (test utility, component, mock'lar)
|
|
174
|
+
├── Mock tanımları (gerekirse)
|
|
175
|
+
├── describe('[ComponentName]', () => {
|
|
176
|
+
│ ├── describe('Rendering', () => { ... })
|
|
177
|
+
│ ├── describe('User Interaction', () => { ... })
|
|
178
|
+
│ ├── describe('State Changes', () => { ... })
|
|
179
|
+
│ ├── describe('API/Async', () => { ... })
|
|
180
|
+
│ ├── describe('Edge Cases', () => { ... })
|
|
181
|
+
│ └── describe('Accessibility', () => { ... })
|
|
182
|
+
│ })
|
|
183
|
+
└── Helper fonksiyonlar (renderWithProviders vs.)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Test Yazım Kuralları
|
|
189
|
+
|
|
190
|
+
### Element Seçim Önceliği (Testing Library)
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
1. getByRole → En çok tercih edilen (erişilebilirlik dostu)
|
|
194
|
+
2. getByLabelText → Form elemanları için
|
|
195
|
+
3. getByPlaceholderText → Input'lar için (label yoksa)
|
|
196
|
+
4. getByText → Görünür metin ile seçim
|
|
197
|
+
5. getByDisplayValue → Form değeri ile seçim
|
|
198
|
+
6. getByAltText → Görseller için
|
|
199
|
+
7. getByTitle → title attribute ile
|
|
200
|
+
8. getByTestId → SON ÇARE — sadece diğerleri işe yaramadığında
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Async Test Kuralları
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// ✅ Doğru — waitFor ile async bekleme
|
|
207
|
+
await waitFor(() => {
|
|
208
|
+
expect(screen.getByText('Loaded')).toBeInTheDocument();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// ✅ Doğru — findBy kullanımı (waitFor + getBy kısaltması)
|
|
212
|
+
const element = await screen.findByText('Loaded');
|
|
213
|
+
|
|
214
|
+
// ❌ Yanlış — setTimeout ile bekleme
|
|
215
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Mock Kuralları
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// ✅ Doğru — Minimal mock, sadece gerekli kısım
|
|
222
|
+
vi.mock('@/services/api', () => ({
|
|
223
|
+
fetchUser: vi.fn(),
|
|
224
|
+
}));
|
|
225
|
+
|
|
226
|
+
// ✅ Doğru — MSW ile API mock (integration test)
|
|
227
|
+
server.use(
|
|
228
|
+
http.get('/api/users/:id', () => {
|
|
229
|
+
return HttpResponse.json({ id: 1, name: 'Test User' });
|
|
230
|
+
})
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// ❌ Yanlış — Tüm modülü mock'lama (gereksiz)
|
|
234
|
+
vi.mock('@/services/api'); // Auto-mock tüm export'ları undefined yapar
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Assertion Best Practice'leri
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// ✅ Spesifik assertion
|
|
241
|
+
expect(screen.getByRole('button', { name: 'Submit' })).toBeEnabled();
|
|
242
|
+
expect(screen.getByRole('alert')).toHaveTextContent('Error occurred');
|
|
243
|
+
|
|
244
|
+
// ❌ Genel assertion (ne test ettiği belirsiz)
|
|
245
|
+
expect(container.innerHTML).toContain('Submit');
|
|
246
|
+
expect(document.querySelector('.error')).toBeTruthy();
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Dosya Tipi Bazlı Test Şablonları
|
|
252
|
+
|
|
253
|
+
### Component Test Şablonu
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import { render, screen, within } from '@testing-library/react';
|
|
257
|
+
import userEvent from '@testing-library/user-event';
|
|
258
|
+
import { describe, it, expect, vi } from 'vitest'; // veya jest'ten
|
|
259
|
+
import { ComponentName } from './ComponentName';
|
|
260
|
+
|
|
261
|
+
// Gerekirse wrapper
|
|
262
|
+
function renderComponent(props = {}) {
|
|
263
|
+
const defaultProps = {
|
|
264
|
+
// default prop değerleri
|
|
265
|
+
};
|
|
266
|
+
return render(<ComponentName {...defaultProps} {...props} />);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
describe('ComponentName', () => {
|
|
270
|
+
describe('Rendering', () => {
|
|
271
|
+
it('should render with default props', () => {
|
|
272
|
+
renderComponent();
|
|
273
|
+
expect(screen.getByRole('...')).toBeInTheDocument();
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe('User Interaction', () => {
|
|
278
|
+
it('should handle click event', async () => {
|
|
279
|
+
const user = userEvent.setup();
|
|
280
|
+
const handleClick = vi.fn();
|
|
281
|
+
renderComponent({ onClick: handleClick });
|
|
282
|
+
|
|
283
|
+
await user.click(screen.getByRole('button'));
|
|
284
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe('Edge Cases', () => {
|
|
289
|
+
it('should handle empty data gracefully', () => {
|
|
290
|
+
renderComponent({ data: [] });
|
|
291
|
+
expect(screen.getByText('No items')).toBeInTheDocument();
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Hook Test Şablonu
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { renderHook, act, waitFor } from '@testing-library/react';
|
|
301
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
302
|
+
import { useHookName } from './useHookName';
|
|
303
|
+
|
|
304
|
+
describe('useHookName', () => {
|
|
305
|
+
it('should return initial state', () => {
|
|
306
|
+
const { result } = renderHook(() => useHookName());
|
|
307
|
+
expect(result.current.value).toBe(initialValue);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('should update state', () => {
|
|
311
|
+
const { result } = renderHook(() => useHookName());
|
|
312
|
+
act(() => {
|
|
313
|
+
result.current.setValue('new value');
|
|
314
|
+
});
|
|
315
|
+
expect(result.current.value).toBe('new value');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should handle async operation', async () => {
|
|
319
|
+
const { result } = renderHook(() => useHookName());
|
|
320
|
+
act(() => {
|
|
321
|
+
result.current.fetchData();
|
|
322
|
+
});
|
|
323
|
+
await waitFor(() => {
|
|
324
|
+
expect(result.current.data).toBeDefined();
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Utility Test Şablonu
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { describe, it, expect } from 'vitest';
|
|
334
|
+
import { functionName } from './functionName';
|
|
335
|
+
|
|
336
|
+
describe('functionName', () => {
|
|
337
|
+
describe('happy path', () => {
|
|
338
|
+
it.each([
|
|
339
|
+
['input1', 'expected1'],
|
|
340
|
+
['input2', 'expected2'],
|
|
341
|
+
])('should return %s for input %s', (input, expected) => {
|
|
342
|
+
expect(functionName(input)).toBe(expected);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe('edge cases', () => {
|
|
347
|
+
it('should handle null input', () => {
|
|
348
|
+
expect(functionName(null)).toBe(defaultValue);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should handle empty string', () => {
|
|
352
|
+
expect(functionName('')).toBe(defaultValue);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
describe('error cases', () => {
|
|
357
|
+
it('should throw for invalid input', () => {
|
|
358
|
+
expect(() => functionName(invalidInput)).toThrow('Expected error message');
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Test Coverage Hedefleri
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
Önerilen Minimum Coverage:
|
|
370
|
+
├── Statements: %80+
|
|
371
|
+
├── Branches: %75+
|
|
372
|
+
├── Functions: %80+
|
|
373
|
+
└── Lines: %80+
|
|
374
|
+
|
|
375
|
+
Kritik Modüller İçin (auth, payment, data):
|
|
376
|
+
├── Statements: %95+
|
|
377
|
+
├── Branches: %90+
|
|
378
|
+
├── Functions: %95+
|
|
379
|
+
└── Lines: %95+
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## Çıktı Formatı
|
|
385
|
+
|
|
386
|
+
1. **Test planı özeti** — Hangi senaryolar test edilecek
|
|
387
|
+
2. **Kullanıcı onayı** — Plan uygun mu, ekleme/çıkarma var mı?
|
|
388
|
+
3. **Test dosyası** — Convention'a uygun, çalıştırılabilir test kodu
|
|
389
|
+
4. **Çalıştırma komutu** — `npx vitest run [dosya]` veya benzeri
|