create-ai-project 1.11.2 → 1.12.1
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/.claude/agents-en/acceptance-test-generator.md +13 -13
- package/.claude/agents-en/code-reviewer.md +8 -10
- package/.claude/agents-en/design-sync.md +6 -5
- package/.claude/agents-en/document-reviewer.md +8 -7
- package/.claude/agents-en/integration-test-reviewer.md +5 -4
- package/.claude/agents-en/prd-creator.md +7 -6
- package/.claude/agents-en/quality-fixer-frontend.md +3 -14
- package/.claude/agents-en/quality-fixer.md +9 -20
- package/.claude/agents-en/requirement-analyzer.md +8 -7
- package/.claude/agents-en/rule-advisor.md +57 -128
- package/.claude/agents-en/task-decomposer.md +4 -10
- package/.claude/agents-en/task-executor-frontend.md +4 -16
- package/.claude/agents-en/task-executor.md +5 -16
- package/.claude/agents-en/technical-designer-frontend.md +17 -15
- package/.claude/agents-en/technical-designer.md +13 -15
- package/.claude/agents-en/work-planner.md +9 -14
- package/.claude/agents-ja/acceptance-test-generator.md +9 -15
- package/.claude/agents-ja/code-reviewer.md +3 -11
- package/.claude/agents-ja/design-sync.md +2 -6
- package/.claude/agents-ja/document-reviewer.md +4 -9
- package/.claude/agents-ja/integration-test-reviewer.md +2 -5
- package/.claude/agents-ja/prd-creator.md +3 -7
- package/.claude/agents-ja/quality-fixer-frontend.md +2 -13
- package/.claude/agents-ja/quality-fixer.md +7 -18
- package/.claude/agents-ja/requirement-analyzer.md +5 -8
- package/.claude/agents-ja/rule-advisor.md +57 -128
- package/.claude/agents-ja/task-decomposer.md +4 -10
- package/.claude/agents-ja/task-executor-frontend.md +3 -15
- package/.claude/agents-ja/task-executor.md +3 -17
- package/.claude/agents-ja/technical-designer-frontend.md +17 -15
- package/.claude/agents-ja/technical-designer.md +13 -15
- package/.claude/agents-ja/work-planner.md +9 -14
- package/.claude/commands-en/build.md +2 -2
- package/.claude/commands-en/design.md +1 -1
- package/.claude/commands-en/implement.md +8 -8
- package/.claude/commands-en/plan.md +3 -3
- package/.claude/commands-en/project-inject.md +4 -4
- package/.claude/commands-en/{refine-rule.md → refine-skill.md} +47 -48
- package/.claude/commands-en/{sync-rules.md → sync-skills.md} +29 -29
- package/.claude/commands-ja/build.md +2 -2
- package/.claude/commands-ja/design.md +1 -1
- package/.claude/commands-ja/implement.md +8 -8
- package/.claude/commands-ja/plan.md +3 -3
- package/.claude/commands-ja/project-inject.md +4 -4
- package/.claude/{commands/refine-rule.md → commands-ja/refine-skill.md} +25 -25
- package/.claude/{commands/sync-rules.md → commands-ja/sync-skills.md} +28 -28
- package/{docs/rules-en/coding-standards.md → .claude/skills-en/coding-standards/SKILL.md} +21 -108
- package/{docs/rules-en/documentation-criteria.md → .claude/skills-en/documentation-criteria/SKILL.md} +40 -42
- package/{docs/adr/template-en.md → .claude/skills-en/documentation-criteria/references/adr-template.md} +1 -1
- package/{docs/design/template-en.md → .claude/skills-en/documentation-criteria/references/design-template.md} +11 -31
- package/{docs/plans/template-en.md → .claude/skills-en/documentation-criteria/references/plan-template.md} +4 -4
- package/{docs/prd/template-en.md → .claude/skills-en/documentation-criteria/references/prd-template.md} +1 -1
- package/{docs/rules-en/frontend/technical-spec.md → .claude/skills-en/frontend/technical-spec/SKILL.md} +17 -13
- package/{docs/rules-en/frontend/typescript.md → .claude/skills-en/frontend/typescript-rules/SKILL.md} +17 -12
- package/{docs/rules-en/frontend/typescript-testing.md → .claude/skills-en/frontend/typescript-testing/SKILL.md} +11 -6
- package/{docs/rules-en/architecture/implementation-approach.md → .claude/skills-en/implementation-approach/SKILL.md} +7 -2
- package/{docs/rules-en/integration-e2e-testing.md → .claude/skills-en/integration-e2e-testing/SKILL.md} +15 -18
- package/{docs/rules-en/project-context.md → .claude/skills-en/project-context/SKILL.md} +7 -3
- package/.claude/skills-en/subagents-orchestration-guide/SKILL.md +224 -0
- package/.claude/skills-en/task-analyzer/SKILL.md +131 -0
- package/{docs/rules-en/rules-index.yaml → .claude/skills-en/task-analyzer/references/skills-index.yaml} +34 -20
- package/{docs/rules-en/technical-spec.md → .claude/skills-en/technical-spec/SKILL.md} +6 -6
- package/{docs/rules-en/typescript.md → .claude/skills-en/typescript-rules/SKILL.md} +15 -10
- package/{docs/rules-en/typescript-testing.md → .claude/skills-en/typescript-testing/SKILL.md} +10 -4
- package/{docs/rules-ja/coding-standards.md → .claude/skills-ja/coding-standards/SKILL.md} +12 -99
- package/{docs/rules-ja/documentation-criteria.md → .claude/skills-ja/documentation-criteria/SKILL.md} +18 -5
- package/.claude/skills-ja/documentation-criteria/references/adr-template.md +64 -0
- package/.claude/skills-ja/documentation-criteria/references/design-template.md +261 -0
- package/{docs/plans/template-ja.md → .claude/skills-ja/documentation-criteria/references/plan-template.md} +38 -38
- package/{docs/prd/template-ja.md → .claude/skills-ja/documentation-criteria/references/prd-template.md} +33 -33
- package/{docs/rules-ja/frontend/technical-spec.md → .claude/skills-ja/frontend/technical-spec/SKILL.md} +13 -9
- package/.claude/skills-ja/frontend/typescript-rules/SKILL.md +315 -0
- package/{docs/rules-ja/frontend/typescript-testing.md → .claude/skills-ja/frontend/typescript-testing/SKILL.md} +93 -5
- package/{docs/rules/architecture/implementation-approach.md → .claude/skills-ja/implementation-approach/SKILL.md} +10 -5
- package/{docs/rules-ja/integration-e2e-testing.md → .claude/skills-ja/integration-e2e-testing/SKILL.md} +5 -8
- package/{docs/rules-ja/project-context.md → .claude/skills-ja/project-context/SKILL.md} +7 -3
- package/.claude/skills-ja/subagents-orchestration-guide/SKILL.md +212 -0
- package/.claude/skills-ja/task-analyzer/SKILL.md +131 -0
- package/{docs/rules-ja/rules-index.yaml → .claude/skills-ja/task-analyzer/references/skills-index.yaml} +34 -19
- package/{docs/rules-ja/technical-spec.md → .claude/skills-ja/technical-spec/SKILL.md} +6 -6
- package/{docs/rules-ja/typescript.md → .claude/skills-ja/typescript-rules/SKILL.md} +16 -11
- package/{docs/rules-ja/typescript-testing.md → .claude/skills-ja/typescript-testing/SKILL.md} +11 -5
- package/CLAUDE.en.md +6 -6
- package/CLAUDE.ja.md +6 -6
- package/CLAUDE.md +19 -28
- package/README.ja.md +39 -10
- package/README.md +39 -10
- package/package.json +1 -1
- package/scripts/set-language.js +35 -53
- package/scripts/setup-project.js +4 -1
- package/.claude/agents/acceptance-test-generator.md +0 -316
- package/.claude/agents/code-reviewer.md +0 -193
- package/.claude/agents/document-reviewer.md +0 -182
- package/.claude/agents/prd-creator.md +0 -186
- package/.claude/agents/quality-fixer.md +0 -295
- package/.claude/agents/requirement-analyzer.md +0 -161
- package/.claude/agents/rule-advisor.md +0 -194
- package/.claude/agents/task-decomposer.md +0 -291
- package/.claude/agents/task-executor.md +0 -270
- package/.claude/agents/technical-designer.md +0 -343
- package/.claude/agents/work-planner.md +0 -181
- package/.claude/commands/build.md +0 -78
- package/.claude/commands/design.md +0 -27
- package/.claude/commands/implement.md +0 -79
- package/.claude/commands/plan.md +0 -43
- package/.claude/commands/project-inject.md +0 -76
- package/.claude/commands/review.md +0 -78
- package/.claude/commands/task.md +0 -13
- package/.claude/commands-ja/refine-rule.md +0 -206
- package/.claude/commands-ja/sync-rules.md +0 -116
- package/.claude/settings.local.json +0 -74
- package/docs/adr/template-ja.md +0 -64
- package/docs/design/template-ja.md +0 -285
- package/docs/guides/en/sub-agents.md +0 -343
- package/docs/guides/ja/sub-agents.md +0 -343
- package/docs/guides/sub-agents.md +0 -306
- package/docs/plans/20250123-integration-test-improvement.md +0 -993
- package/docs/rules/ai-development-guide.md +0 -260
- package/docs/rules/documentation-criteria.md +0 -180
- package/docs/rules/project-context.md +0 -38
- package/docs/rules/rules-index.yaml +0 -137
- package/docs/rules/technical-spec.md +0 -47
- package/docs/rules/typescript-testing.md +0 -188
- package/docs/rules/typescript.md +0 -166
- package/docs/rules-ja/architecture/implementation-approach.md +0 -136
- package/docs/rules-ja/frontend/typescript.md +0 -131
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend/typescript-rules
|
|
3
|
+
description: 型安全性、コンポーネント設計、状態管理、エラーハンドリングを含むReact/TypeScriptフロントエンド開発ルール。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeScript 開発ルール(フロントエンド)
|
|
7
|
+
|
|
8
|
+
## 型システム
|
|
9
|
+
|
|
10
|
+
### 型安全性の原則
|
|
11
|
+
- **strictモード必須**: tsconfig.jsonでstrict: trueを設定
|
|
12
|
+
- **any型使用禁止**: コードベースでany型を使用しない
|
|
13
|
+
- **as使用最小化**: 型キャストはやむを得ない場合のみ(理由をコメント)
|
|
14
|
+
- **unknown優先**: any型が必要な場合はunknown + 型ガード
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// 良い: 型ガード付きのunknown
|
|
18
|
+
function processData(data: unknown): User {
|
|
19
|
+
if (!isUser(data)) throw new Error('Invalid user data')
|
|
20
|
+
return data
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 悪い: any型の使用
|
|
24
|
+
function processData(data: any): User {
|
|
25
|
+
return data as User
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 型定義のベストプラクティス
|
|
30
|
+
|
|
31
|
+
#### オブジェクト型
|
|
32
|
+
- **interface優先**: 拡張可能なオブジェクト型にはinterfaceを使用
|
|
33
|
+
- **typeはunion/intersection用**: 複合型やユーティリティ型に使用
|
|
34
|
+
- **readonlyの活用**: 不変なプロパティにはreadonlyを明示
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// 良い: 明確な型定義
|
|
38
|
+
interface User {
|
|
39
|
+
readonly id: string
|
|
40
|
+
name: string
|
|
41
|
+
email: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type UserWithRole = User & { role: 'admin' | 'user' }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### 関数型
|
|
48
|
+
- **戻り値型を明示**: 複雑なロジックを持つ関数
|
|
49
|
+
- **ジェネリクスの活用**: 再利用可能な型安全な関数
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// 良い: 戻り値型を明示
|
|
53
|
+
function calculateTotal(items: CartItem[]): number {
|
|
54
|
+
return items.reduce((sum, item) => sum + item.price, 0)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 良い: ジェネリクスの活用
|
|
58
|
+
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
|
|
59
|
+
// implementation
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Reactコンポーネント設計
|
|
64
|
+
|
|
65
|
+
### Function Components必須
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// 良い: Function Component
|
|
69
|
+
const UserCard: React.FC<UserCardProps> = ({ user, onSelect }) => {
|
|
70
|
+
return (
|
|
71
|
+
<div onClick={() => onSelect(user.id)}>
|
|
72
|
+
{user.name}
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 悪い: Class Component(非推奨)
|
|
78
|
+
class UserCard extends React.Component<UserCardProps> { }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Props型定義
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
interface ButtonProps {
|
|
85
|
+
label: string
|
|
86
|
+
onClick: () => void
|
|
87
|
+
variant?: 'primary' | 'secondary'
|
|
88
|
+
disabled?: boolean
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const Button: React.FC<ButtonProps> = ({
|
|
92
|
+
label,
|
|
93
|
+
onClick,
|
|
94
|
+
variant = 'primary',
|
|
95
|
+
disabled = false
|
|
96
|
+
}) => {
|
|
97
|
+
// implementation
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Children Props
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
interface LayoutProps {
|
|
105
|
+
children: React.ReactNode
|
|
106
|
+
sidebar?: React.ReactNode
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const Layout: React.FC<LayoutProps> = ({ children, sidebar }) => (
|
|
110
|
+
<div>
|
|
111
|
+
<main>{children}</main>
|
|
112
|
+
{sidebar && <aside>{sidebar}</aside>}
|
|
113
|
+
</div>
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 状態管理
|
|
118
|
+
|
|
119
|
+
### useState型定義
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// 良い: 明示的な型
|
|
123
|
+
const [user, setUser] = useState<User | null>(null)
|
|
124
|
+
const [items, setItems] = useState<Item[]>([])
|
|
125
|
+
|
|
126
|
+
// 良い: 初期値から推論可能な場合
|
|
127
|
+
const [count, setCount] = useState(0)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### useReducerの型安全性
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
type Action =
|
|
134
|
+
| { type: 'SET_USER'; payload: User }
|
|
135
|
+
| { type: 'CLEAR_USER' }
|
|
136
|
+
| { type: 'SET_ERROR'; payload: string }
|
|
137
|
+
|
|
138
|
+
interface State {
|
|
139
|
+
user: User | null
|
|
140
|
+
error: string | null
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const reducer = (state: State, action: Action): State => {
|
|
144
|
+
switch (action.type) {
|
|
145
|
+
case 'SET_USER':
|
|
146
|
+
return { ...state, user: action.payload, error: null }
|
|
147
|
+
case 'CLEAR_USER':
|
|
148
|
+
return { ...state, user: null }
|
|
149
|
+
case 'SET_ERROR':
|
|
150
|
+
return { ...state, error: action.payload }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Custom Hooks
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
interface UseUserReturn {
|
|
159
|
+
user: User | null
|
|
160
|
+
loading: boolean
|
|
161
|
+
error: Error | null
|
|
162
|
+
refetch: () => Promise<void>
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function useUser(userId: string): UseUserReturn {
|
|
166
|
+
const [user, setUser] = useState<User | null>(null)
|
|
167
|
+
const [loading, setLoading] = useState(true)
|
|
168
|
+
const [error, setError] = useState<Error | null>(null)
|
|
169
|
+
|
|
170
|
+
const refetch = useCallback(async () => {
|
|
171
|
+
setLoading(true)
|
|
172
|
+
try {
|
|
173
|
+
const data = await fetchUser(userId)
|
|
174
|
+
setUser(data)
|
|
175
|
+
} catch (e) {
|
|
176
|
+
setError(e instanceof Error ? e : new Error('Unknown error'))
|
|
177
|
+
} finally {
|
|
178
|
+
setLoading(false)
|
|
179
|
+
}
|
|
180
|
+
}, [userId])
|
|
181
|
+
|
|
182
|
+
useEffect(() => { refetch() }, [refetch])
|
|
183
|
+
|
|
184
|
+
return { user, loading, error, refetch }
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## エラーハンドリング
|
|
189
|
+
|
|
190
|
+
### Error Boundary
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
interface ErrorBoundaryProps {
|
|
194
|
+
children: React.ReactNode
|
|
195
|
+
fallback: React.ReactNode
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface ErrorBoundaryState {
|
|
199
|
+
hasError: boolean
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
203
|
+
state = { hasError: false }
|
|
204
|
+
|
|
205
|
+
static getDerivedStateFromError(): ErrorBoundaryState {
|
|
206
|
+
return { hasError: true }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
render() {
|
|
210
|
+
if (this.state.hasError) return this.props.fallback
|
|
211
|
+
return this.props.children
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### APIエラーハンドリング
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
interface ApiError {
|
|
220
|
+
code: string
|
|
221
|
+
message: string
|
|
222
|
+
details?: Record<string, string>
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function isApiError(error: unknown): error is ApiError {
|
|
226
|
+
return (
|
|
227
|
+
typeof error === 'object' &&
|
|
228
|
+
error !== null &&
|
|
229
|
+
'code' in error &&
|
|
230
|
+
'message' in error
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function fetchWithErrorHandling<T>(url: string): Promise<T> {
|
|
235
|
+
const response = await fetch(url)
|
|
236
|
+
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
const error: unknown = await response.json()
|
|
239
|
+
if (isApiError(error)) {
|
|
240
|
+
throw new ApiError(error.code, error.message)
|
|
241
|
+
}
|
|
242
|
+
throw new Error('Unknown API error')
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return response.json() as Promise<T>
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## イベントハンドリング
|
|
250
|
+
|
|
251
|
+
### イベント型
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
255
|
+
e.preventDefault()
|
|
256
|
+
// handle click
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
260
|
+
const value = e.target.value
|
|
261
|
+
// handle change
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
265
|
+
e.preventDefault()
|
|
266
|
+
// handle submit
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## コーディング規約
|
|
271
|
+
|
|
272
|
+
### 命名規則
|
|
273
|
+
- **コンポーネント**: PascalCase(例: `UserProfile`)
|
|
274
|
+
- **フック**: camelCase + use接頭辞(例: `useUserData`)
|
|
275
|
+
- **型/インターフェース**: PascalCase(例: `UserProps`)
|
|
276
|
+
- **定数**: SCREAMING_SNAKE_CASE(例: `MAX_RETRY_COUNT`)
|
|
277
|
+
- **ファイル名**: コンポーネントはPascalCase、その他はcamelCase
|
|
278
|
+
|
|
279
|
+
### インポート順序
|
|
280
|
+
1. React関連
|
|
281
|
+
2. 外部ライブラリ
|
|
282
|
+
3. 内部モジュール(絶対パス)
|
|
283
|
+
4. 内部モジュール(相対パス)
|
|
284
|
+
5. 型のみのインポート
|
|
285
|
+
6. スタイル/アセット
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { useState, useEffect } from 'react'
|
|
289
|
+
import { useQuery } from '@tanstack/react-query'
|
|
290
|
+
import { api } from '@/lib/api'
|
|
291
|
+
import { formatDate } from '../utils'
|
|
292
|
+
import type { User } from '@/types'
|
|
293
|
+
import styles from './Component.module.css'
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## アンチパターン
|
|
297
|
+
|
|
298
|
+
### 避けるべきパターン
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
// 悪い: Propsのスプレッド展開
|
|
302
|
+
const Button = (props: ButtonProps) => <button {...props} />
|
|
303
|
+
|
|
304
|
+
// 良い: 明示的なPropsの受け渡し
|
|
305
|
+
const Button = ({ label, onClick, disabled }: ButtonProps) => (
|
|
306
|
+
<button onClick={onClick} disabled={disabled}>{label}</button>
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
// 悪い: インラインでの複雑なロジック
|
|
310
|
+
{items.filter(i => i.active).map(i => <Item key={i.id} {...i} />)}
|
|
311
|
+
|
|
312
|
+
// 良い: 事前に変数として抽出
|
|
313
|
+
const activeItems = items.filter(item => item.active)
|
|
314
|
+
{activeItems.map(item => <Item key={item.id} item={item} />)}
|
|
315
|
+
```
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend/typescript-testing
|
|
3
|
+
description: Vitest、React Testing Library、MSWを使用したフロントエンドテストルール。カバレッジ要件、テスト設計原則、品質基準を含む。
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# TypeScript テストルール(フロントエンド)
|
|
2
7
|
|
|
3
8
|
## テストフレームワーク
|
|
@@ -16,7 +21,7 @@
|
|
|
16
21
|
- **再現性**: テストは環境に依存せず、常に同じ結果を返す
|
|
17
22
|
- **可読性**: テストコードも製品コードと同様の品質を維持
|
|
18
23
|
|
|
19
|
-
###
|
|
24
|
+
### カバレッジ要件
|
|
20
25
|
**必須**: 単体テストのカバレッジは60%以上
|
|
21
26
|
**コンポーネント別目標**:
|
|
22
27
|
- Atoms(Button、Text等): 70%以上
|
|
@@ -72,11 +77,11 @@ src/
|
|
|
72
77
|
|
|
73
78
|
### テストコードの品質ルール
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
**推奨: すべてのテストを常に有効に保つ**
|
|
76
81
|
- メリット: テストスイートの完全性を保証
|
|
77
82
|
- 実践: 問題があるテストは修正して有効化
|
|
78
83
|
|
|
79
|
-
|
|
84
|
+
**避けるべき: test.skip()やコメントアウト**
|
|
80
85
|
- 理由: テストの穴が生まれ、品質チェックが不完全になる
|
|
81
86
|
- 対処: 不要なテストは完全に削除する
|
|
82
87
|
|
|
@@ -84,7 +89,7 @@ src/
|
|
|
84
89
|
|
|
85
90
|
### MSW(Mock Service Worker)セットアップ
|
|
86
91
|
```typescript
|
|
87
|
-
//
|
|
92
|
+
// 型安全なMSWハンドラー
|
|
88
93
|
import { rest } from 'msw'
|
|
89
94
|
|
|
90
95
|
const handlers = [
|
|
@@ -96,7 +101,7 @@ const handlers = [
|
|
|
96
101
|
|
|
97
102
|
### コンポーネントモックの型安全性
|
|
98
103
|
```typescript
|
|
99
|
-
//
|
|
104
|
+
// 必要な部分のみ
|
|
100
105
|
type TestProps = Pick<ButtonProps, 'label' | 'onClick'>
|
|
101
106
|
const mockProps: TestProps = { label: 'Click', onClick: vi.fn() }
|
|
102
107
|
|
|
@@ -122,3 +127,86 @@ describe('Button', () => {
|
|
|
122
127
|
})
|
|
123
128
|
})
|
|
124
129
|
```
|
|
130
|
+
|
|
131
|
+
## テスト品質基準
|
|
132
|
+
|
|
133
|
+
### 境界値・異常系の網羅
|
|
134
|
+
正常系に加え、境界値と異常系を含める。
|
|
135
|
+
```typescript
|
|
136
|
+
it('renders empty state for empty array', () => {
|
|
137
|
+
render(<UserList users={[]} />)
|
|
138
|
+
expect(screen.getByText('ユーザーがいません')).toBeInTheDocument()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('displays error message on API failure', async () => {
|
|
142
|
+
server.use(rest.get('/api/users', (req, res, ctx) => res(ctx.status(500))))
|
|
143
|
+
render(<UserList />)
|
|
144
|
+
expect(await screen.findByText('エラーが発生しました')).toBeInTheDocument()
|
|
145
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### ユーザー中心のクエリ
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// 良い: アクセシブルなクエリ
|
|
152
|
+
screen.getByRole('button', { name: 'Submit' })
|
|
153
|
+
screen.getByLabelText('Email')
|
|
154
|
+
screen.getByText('Welcome')
|
|
155
|
+
|
|
156
|
+
// 悪い: 実装詳細への依存
|
|
157
|
+
screen.getByTestId('submit-btn')
|
|
158
|
+
container.querySelector('.btn-primary')
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 非同期処理のテスト
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
it('loads and displays user data', async () => {
|
|
165
|
+
render(<UserProfile userId="1" />)
|
|
166
|
+
|
|
167
|
+
// ローディング状態を確認
|
|
168
|
+
expect(screen.getByText('Loading...')).toBeInTheDocument()
|
|
169
|
+
|
|
170
|
+
// データ表示を待機
|
|
171
|
+
expect(await screen.findByText('John Doe')).toBeInTheDocument()
|
|
172
|
+
|
|
173
|
+
// ローディングが消えていることを確認
|
|
174
|
+
expect(screen.queryByText('Loading...')).not.toBeInTheDocument()
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### フォームテスト
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
it('submits form with valid data', async () => {
|
|
182
|
+
const onSubmit = vi.fn()
|
|
183
|
+
render(<LoginForm onSubmit={onSubmit} />)
|
|
184
|
+
|
|
185
|
+
await userEvent.type(screen.getByLabelText('Email'), 'test@example.com')
|
|
186
|
+
await userEvent.type(screen.getByLabelText('Password'), 'password123')
|
|
187
|
+
await userEvent.click(screen.getByRole('button', { name: 'Login' }))
|
|
188
|
+
|
|
189
|
+
expect(onSubmit).toHaveBeenCalledWith({
|
|
190
|
+
email: 'test@example.com',
|
|
191
|
+
password: 'password123'
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## アンチパターン
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// 悪い: 実装詳細のテスト
|
|
200
|
+
it('calls setState', () => {
|
|
201
|
+
const setState = vi.spyOn(React, 'useState')
|
|
202
|
+
render(<Counter />)
|
|
203
|
+
// ...
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// 良い: ユーザーが見る結果をテスト
|
|
207
|
+
it('increments count when clicked', () => {
|
|
208
|
+
render(<Counter />)
|
|
209
|
+
fireEvent.click(screen.getByRole('button', { name: '+' }))
|
|
210
|
+
expect(screen.getByText('Count: 1')).toBeInTheDocument()
|
|
211
|
+
})
|
|
212
|
+
```
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: implementation-approach
|
|
3
|
+
description: メタ認知的アプローチによる実装戦略選択フレームワーク。確認レベル、統合ポイント定義を含む。
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# 実装戦略選択フレームワーク(メタ認知的アプローチ)
|
|
2
7
|
|
|
3
8
|
## メタ認知的戦略選択プロセス
|
|
@@ -86,7 +91,7 @@
|
|
|
86
91
|
**適用条件**: 機能間の依存が少ない、ユーザーが利用可能な形で出力、アーキテクチャ全層への変更が必要
|
|
87
92
|
**確認方法**: 各機能完成時のエンドユーザー価値提供
|
|
88
93
|
|
|
89
|
-
#### 水平スライス(基盤駆動)
|
|
94
|
+
#### 水平スライス(基盤駆動)
|
|
90
95
|
**特徴**: アーキテクチャ層別の段階的構築
|
|
91
96
|
**適用条件**: 基盤システムの安定性が重要、複数機能が共通基盤に依存、層別の段階的確認が有効
|
|
92
97
|
**確認方法**: 全基盤層完成時の統合動作確認
|
|
@@ -105,7 +110,7 @@
|
|
|
105
110
|
各タスクの完了確認における優先順位:
|
|
106
111
|
|
|
107
112
|
- **L1: 機能動作確認** - エンドユーザー機能として動作(例:検索実行可能)
|
|
108
|
-
- **L2: テスト動作確認** - 新規テストが追加されパス(例:型定義テスト)
|
|
113
|
+
- **L2: テスト動作確認** - 新規テストが追加されパス(例:型定義テスト)
|
|
109
114
|
- **L3: ビルド成功確認** - コンパイルエラーなし(例:インターフェース定義)
|
|
110
115
|
|
|
111
116
|
**優先順位**: L1 > L2 > L3 の順で確認可能性を重視
|
|
@@ -121,7 +126,7 @@
|
|
|
121
126
|
## アンチパターン
|
|
122
127
|
|
|
123
128
|
- **パターン固執**: リスト内の戦略のみで選択し、独自の組み合わせを検討しない
|
|
124
|
-
- **分析不足**: Phase 1の分析フレームワークを飛ばして戦略選択
|
|
129
|
+
- **分析不足**: Phase 1の分析フレームワークを飛ばして戦略選択
|
|
125
130
|
- **リスク軽視**: Phase 3のリスク分析マトリクスを省略して実装着手
|
|
126
131
|
- **制約無視**: Phase 4の制約チェックリストを確認せず戦略決定
|
|
127
132
|
- **根拠省略**: Phase 6の文書化テンプレートを使用せず戦略選択
|
|
@@ -129,8 +134,8 @@
|
|
|
129
134
|
## メタ認知的実行のための指針
|
|
130
135
|
|
|
131
136
|
1. **既知パターンの活用**: 出発点として参考し、創造的組み合わせを探索
|
|
132
|
-
2. **WebSearch積極活用**: 同種技術スタックの実装事例を調査
|
|
137
|
+
2. **WebSearch積極活用**: 同種技術スタックの実装事例を調査
|
|
133
138
|
3. **5 Whys適用**: 根本理由を追求し本質を把握
|
|
134
139
|
4. **複数観点評価**: Phase 1-4の各観点から網羅的に評価
|
|
135
140
|
5. **創造的思考**: 複数戦略の順序適用やプロジェクト固有の制約を活かした設計を検討
|
|
136
|
-
6. **判断根拠明示**: 設計ドキュメントでの戦略選択根拠を明示化
|
|
141
|
+
6. **判断根拠明示**: 設計ドキュメントでの戦略選択根拠を明示化
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: integration-e2e-testing
|
|
3
|
+
description: 統合・E2Eテスト設計原則、ROI計算、テストスケルトン仕様、レビュー基準。
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# 統合テスト・E2Eテスト設計・実装ルール
|
|
2
7
|
|
|
3
8
|
## テスト種別と上限
|
|
@@ -101,14 +106,6 @@ it('AC2-property: モデル名は常にgemini-3-pro-image-preview', () => {
|
|
|
101
106
|
| `// 検証項目:` がない | 「振る舞い」記述の「観測可能な結果」から導出 |
|
|
102
107
|
| 両方ある | 検証項目を優先、振る舞いは補足として使用 |
|
|
103
108
|
|
|
104
|
-
**振る舞いからの導出例**:
|
|
105
|
-
```
|
|
106
|
-
// 振る舞い: ユーザーが決済完了 → DBに注文作成 → 注文確認画面表示
|
|
107
|
-
↓ 導出
|
|
108
|
-
- 注文がDBに作成される(または作成関数が呼び出される)
|
|
109
|
-
- 注文確認画面のデータが返される
|
|
110
|
-
```
|
|
111
|
-
|
|
112
109
|
### 統合テストのモック境界
|
|
113
110
|
|
|
114
111
|
| 判断基準 | モック | 実物 |
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: project-context
|
|
3
|
+
description: プロジェクトの性質、技術スタック、実装原則を含むプロジェクト固有コンテキスト。各プロジェクトでカスタマイズ可能。
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# プロジェクトコンテキスト
|
|
2
7
|
|
|
3
8
|
## 基本設定
|
|
@@ -30,9 +35,8 @@
|
|
|
30
35
|
- 技術的制約事項
|
|
31
36
|
|
|
32
37
|
2. **アーキテクチャの選択**
|
|
33
|
-
-
|
|
34
|
-
- `docs/rules/architecture/` にプロジェクト固有の設計を配置
|
|
38
|
+
- アーキテクチャスキルから適切なパターンを選択
|
|
35
39
|
|
|
36
40
|
3. **環境設定**
|
|
37
41
|
- プロジェクトに適した環境変数管理方法の実装
|
|
38
|
-
- プロジェクト固有の設定ファイル追加
|
|
42
|
+
- プロジェクト固有の設定ファイル追加
|