@smicolon/ai-kit 0.3.2 → 0.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.
- package/README.md +73 -40
- package/dist/index.js +260 -126
- package/package.json +5 -5
- package/.claude-plugin/marketplace.json +0 -369
- package/packs/architect/CHANGELOG.md +0 -17
- package/packs/architect/README.md +0 -58
- package/packs/architect/agents/system-architect.md +0 -768
- package/packs/architect/commands/diagram-create.md +0 -300
- package/packs/better-auth/.mcp.json +0 -14
- package/packs/better-auth/CHANGELOG.md +0 -26
- package/packs/better-auth/README.md +0 -125
- package/packs/better-auth/agents/auth-architect.md +0 -278
- package/packs/better-auth/commands/auth-provider-add.md +0 -265
- package/packs/better-auth/commands/auth-setup.md +0 -298
- package/packs/better-auth/skills/auth-security/SKILL.md +0 -425
- package/packs/better-auth/skills/better-auth-patterns/SKILL.md +0 -455
- package/packs/dev-loop/CHANGELOG.md +0 -69
- package/packs/dev-loop/README.md +0 -155
- package/packs/dev-loop/commands/cancel-dev.md +0 -21
- package/packs/dev-loop/commands/dev-loop.md +0 -72
- package/packs/dev-loop/commands/dev-plan.md +0 -351
- package/packs/dev-loop/hooks/hooks.json +0 -15
- package/packs/dev-loop/hooks/stop-hook.sh +0 -178
- package/packs/dev-loop/scripts/setup-dev-loop.sh +0 -194
- package/packs/dev-loop/skills/tdd-planner/SKILL.md +0 -249
- package/packs/dev-loop/skills/tdd-planner/references/framework-patterns.md +0 -874
- package/packs/dev-loop/skills/tdd-planner/references/good-example.md +0 -260
- package/packs/dev-loop/skills/tdd-planner/references/plan-template.md +0 -275
- package/packs/django/CHANGELOG.md +0 -39
- package/packs/django/README.md +0 -92
- package/packs/django/agents/django-architect.md +0 -182
- package/packs/django/agents/django-builder.md +0 -250
- package/packs/django/agents/django-feature-based.md +0 -420
- package/packs/django/agents/django-reviewer.md +0 -253
- package/packs/django/agents/django-tester.md +0 -230
- package/packs/django/commands/api-endpoint.md +0 -285
- package/packs/django/commands/model-create.md +0 -178
- package/packs/django/commands/test-generate.md +0 -325
- package/packs/django/rules/migrations.md +0 -138
- package/packs/django/rules/models.md +0 -167
- package/packs/django/rules/serializers.md +0 -126
- package/packs/django/rules/services.md +0 -131
- package/packs/django/rules/tests.md +0 -140
- package/packs/django/rules/views.md +0 -102
- package/packs/django/skills/import-convention-enforcer/SKILL.md +0 -226
- package/packs/django/skills/import-convention-enforcer/patterns/django-imports.md +0 -343
- package/packs/django/skills/migration-safety-checker/SKILL.md +0 -375
- package/packs/django/skills/model-entity-validator/SKILL.md +0 -298
- package/packs/django/skills/performance-optimizer/SKILL.md +0 -447
- package/packs/django/skills/red-phase-verifier/SKILL.md +0 -180
- package/packs/django/skills/security-first-validator/SKILL.md +0 -435
- package/packs/django/skills/test-coverage-advisor/SKILL.md +0 -394
- package/packs/django/skills/test-validity-checker/SKILL.md +0 -194
- package/packs/failure-log/CHANGELOG.md +0 -20
- package/packs/failure-log/README.md +0 -168
- package/packs/failure-log/commands/failure-add.md +0 -106
- package/packs/failure-log/commands/failure-list.md +0 -89
- package/packs/failure-log/hooks/hooks.json +0 -16
- package/packs/failure-log/hooks/scripts/inject-failures.sh +0 -64
- package/packs/failure-log/skills/failure-log-manager/SKILL.md +0 -164
- package/packs/flutter/CHANGELOG.md +0 -19
- package/packs/flutter/README.md +0 -170
- package/packs/flutter/agents/flutter-architect.md +0 -166
- package/packs/flutter/agents/flutter-builder.md +0 -303
- package/packs/flutter/agents/release-manager.md +0 -355
- package/packs/flutter/commands/fastlane-setup.md +0 -188
- package/packs/flutter/commands/flutter-build.md +0 -90
- package/packs/flutter/commands/flutter-deploy.md +0 -133
- package/packs/flutter/commands/flutter-test.md +0 -117
- package/packs/flutter/commands/signing-setup.md +0 -209
- package/packs/flutter/hooks/hooks.json +0 -17
- package/packs/flutter/skills/fastlane-knowledge/SKILL.md +0 -193
- package/packs/flutter/skills/flutter-architecture/SKILL.md +0 -127
- package/packs/flutter/skills/store-publishing/SKILL.md +0 -163
- package/packs/hono/CHANGELOG.md +0 -19
- package/packs/hono/README.md +0 -143
- package/packs/hono/agents/hono-architect.md +0 -240
- package/packs/hono/agents/hono-builder.md +0 -285
- package/packs/hono/agents/hono-reviewer.md +0 -279
- package/packs/hono/agents/hono-tester.md +0 -346
- package/packs/hono/commands/middleware-create.md +0 -223
- package/packs/hono/commands/project-init.md +0 -306
- package/packs/hono/commands/route-create.md +0 -153
- package/packs/hono/commands/rpc-client.md +0 -263
- package/packs/hono/skills/cloudflare-bindings/SKILL.md +0 -408
- package/packs/hono/skills/hono-patterns/SKILL.md +0 -309
- package/packs/hono/skills/rpc-typesafe/SKILL.md +0 -388
- package/packs/hono/skills/zod-validation/SKILL.md +0 -332
- package/packs/nestjs/CHANGELOG.md +0 -29
- package/packs/nestjs/README.md +0 -75
- package/packs/nestjs/agents/nestjs-architect.md +0 -402
- package/packs/nestjs/agents/nestjs-builder.md +0 -301
- package/packs/nestjs/agents/nestjs-tester.md +0 -437
- package/packs/nestjs/commands/module-create.md +0 -369
- package/packs/nestjs/rules/controllers.md +0 -92
- package/packs/nestjs/rules/dto.md +0 -124
- package/packs/nestjs/rules/entities.md +0 -102
- package/packs/nestjs/rules/services.md +0 -106
- package/packs/nestjs/skills/barrel-export-manager/SKILL.md +0 -389
- package/packs/nestjs/skills/import-convention-enforcer/SKILL.md +0 -365
- package/packs/nextjs/CHANGELOG.md +0 -36
- package/packs/nextjs/README.md +0 -76
- package/packs/nextjs/agents/frontend-tester.md +0 -680
- package/packs/nextjs/agents/frontend-visual.md +0 -820
- package/packs/nextjs/agents/nextjs-architect.md +0 -331
- package/packs/nextjs/agents/nextjs-modular.md +0 -433
- package/packs/nextjs/commands/component-create.md +0 -398
- package/packs/nextjs/rules/api-routes.md +0 -129
- package/packs/nextjs/rules/components.md +0 -106
- package/packs/nextjs/rules/hooks.md +0 -132
- package/packs/nextjs/skills/accessibility-validator/SKILL.md +0 -445
- package/packs/nextjs/skills/import-convention-enforcer/SKILL.md +0 -399
- package/packs/nextjs/skills/react-form-validator/SKILL.md +0 -569
- package/packs/nuxtjs/CHANGELOG.md +0 -30
- package/packs/nuxtjs/README.md +0 -56
- package/packs/nuxtjs/agents/frontend-tester.md +0 -680
- package/packs/nuxtjs/agents/frontend-visual.md +0 -820
- package/packs/nuxtjs/agents/nuxtjs-architect.md +0 -537
- package/packs/nuxtjs/commands/component-create.md +0 -223
- package/packs/nuxtjs/rules/components.md +0 -101
- package/packs/nuxtjs/rules/composables.md +0 -118
- package/packs/nuxtjs/rules/server-routes.md +0 -127
- package/packs/nuxtjs/skills/accessibility-validator/SKILL.md +0 -183
- package/packs/nuxtjs/skills/import-convention-enforcer/SKILL.md +0 -196
- package/packs/nuxtjs/skills/veevalidate-form-validator/SKILL.md +0 -190
- package/packs/onboard/CHANGELOG.md +0 -22
- package/packs/onboard/README.md +0 -103
- package/packs/onboard/agents/onboard-guide.md +0 -118
- package/packs/onboard/commands/onboard.md +0 -313
- package/packs/onboard/skills/onboard-context-provider/SKILL.md +0 -98
- package/packs/tanstack-router/CHANGELOG.md +0 -30
- package/packs/tanstack-router/README.md +0 -113
- package/packs/tanstack-router/agents/tanstack-architect.md +0 -173
- package/packs/tanstack-router/agents/tanstack-builder.md +0 -360
- package/packs/tanstack-router/agents/tanstack-tester.md +0 -454
- package/packs/tanstack-router/commands/form-create.md +0 -313
- package/packs/tanstack-router/commands/query-create.md +0 -263
- package/packs/tanstack-router/commands/route-create.md +0 -190
- package/packs/tanstack-router/commands/table-create.md +0 -413
- package/packs/tanstack-router/skills/ai-patterns/SKILL.md +0 -370
- package/packs/tanstack-router/skills/db-patterns/SKILL.md +0 -346
- package/packs/tanstack-router/skills/devtools-patterns/SKILL.md +0 -415
- package/packs/tanstack-router/skills/form-patterns/SKILL.md +0 -425
- package/packs/tanstack-router/skills/pacer-patterns/SKILL.md +0 -341
- package/packs/tanstack-router/skills/query-patterns/SKILL.md +0 -359
- package/packs/tanstack-router/skills/router-patterns/SKILL.md +0 -285
- package/packs/tanstack-router/skills/store-patterns/SKILL.md +0 -351
- package/packs/tanstack-router/skills/table-patterns/SKILL.md +0 -531
- package/packs/tanstack-router/skills/tanstack-conventions/SKILL.md +0 -428
- package/packs/tanstack-router/skills/virtual-patterns/SKILL.md +0 -490
- package/packs/worktree/CHANGELOG.md +0 -45
- package/packs/worktree/README.md +0 -219
- package/packs/worktree/commands/wt.md +0 -93
- package/packs/worktree/scripts/wt.sh +0 -957
- package/packs/worktree/skills/worktree-manager/SKILL.md +0 -113
|
@@ -1,874 +0,0 @@
|
|
|
1
|
-
# Framework Detection and Patterns
|
|
2
|
-
|
|
3
|
-
Reference for detecting frameworks and applying appropriate TDD patterns.
|
|
4
|
-
|
|
5
|
-
**Supports any framework** - Use `--test-cmd` and `--lint-cmd` for unlisted frameworks.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Framework Detection
|
|
10
|
-
|
|
11
|
-
### Detection Order
|
|
12
|
-
|
|
13
|
-
Check in this order (first match wins):
|
|
14
|
-
|
|
15
|
-
**Mobile:**
|
|
16
|
-
1. **Flutter** - `pubspec.yaml` with `flutter:`
|
|
17
|
-
2. **React Native** - `react-native` in package.json
|
|
18
|
-
|
|
19
|
-
**Python:**
|
|
20
|
-
3. **Django** - `manage.py` exists
|
|
21
|
-
4. **FastAPI** - `fastapi` in pyproject.toml
|
|
22
|
-
5. **Flask** - `flask` in pyproject.toml
|
|
23
|
-
|
|
24
|
-
**Node.js:**
|
|
25
|
-
6. **NestJS** - `@nestjs/core` in package.json
|
|
26
|
-
7. **Next.js** - `next` in package.json
|
|
27
|
-
8. **Nuxt.js** - `nuxt` in package.json
|
|
28
|
-
9. **Hono** - `hono` in package.json
|
|
29
|
-
10. **Express** - `express` in package.json
|
|
30
|
-
11. **TanStack** - `@tanstack/react-router` in package.json
|
|
31
|
-
|
|
32
|
-
**Systems:**
|
|
33
|
-
12. **Go** - `go.mod` exists
|
|
34
|
-
13. **Rust** - `Cargo.toml` exists
|
|
35
|
-
|
|
36
|
-
**Web Frameworks:**
|
|
37
|
-
14. **Rails** - `rails` in Gemfile
|
|
38
|
-
15. **Laravel** - `laravel` in composer.json
|
|
39
|
-
|
|
40
|
-
**Generic Fallbacks:**
|
|
41
|
-
16. **Python** - `pyproject.toml` or `requirements.txt` exists
|
|
42
|
-
17. **Node** - `package.json` exists
|
|
43
|
-
|
|
44
|
-
### Detection Commands
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
# Mobile
|
|
48
|
-
[ -f "pubspec.yaml" ] && grep -q "flutter:" pubspec.yaml && echo "flutter"
|
|
49
|
-
[ -f "package.json" ] && grep -q "react-native" package.json && echo "react-native"
|
|
50
|
-
|
|
51
|
-
# Python
|
|
52
|
-
[ -f "manage.py" ] && echo "django"
|
|
53
|
-
[ -f "pyproject.toml" ] && grep -q "fastapi" pyproject.toml && echo "fastapi"
|
|
54
|
-
[ -f "pyproject.toml" ] && grep -q "flask" pyproject.toml && echo "flask"
|
|
55
|
-
|
|
56
|
-
# Node.js
|
|
57
|
-
grep -q "@nestjs/core" package.json 2>/dev/null && echo "nestjs"
|
|
58
|
-
grep -q '"next"' package.json 2>/dev/null && echo "nextjs"
|
|
59
|
-
grep -q '"nuxt"' package.json 2>/dev/null && echo "nuxtjs"
|
|
60
|
-
grep -q '"hono"' package.json 2>/dev/null && echo "hono"
|
|
61
|
-
grep -q '"express"' package.json 2>/dev/null && echo "express"
|
|
62
|
-
grep -q '"@tanstack/react-router"' package.json 2>/dev/null && echo "tanstack"
|
|
63
|
-
|
|
64
|
-
# Go
|
|
65
|
-
[ -f "go.mod" ] && echo "go"
|
|
66
|
-
|
|
67
|
-
# Rust
|
|
68
|
-
[ -f "Cargo.toml" ] && echo "rust"
|
|
69
|
-
|
|
70
|
-
# Ruby
|
|
71
|
-
[ -f "Gemfile" ] && grep -q "rails" Gemfile && echo "rails"
|
|
72
|
-
|
|
73
|
-
# PHP
|
|
74
|
-
[ -f "composer.json" ] && grep -q "laravel" composer.json && echo "laravel"
|
|
75
|
-
|
|
76
|
-
# Generic fallbacks
|
|
77
|
-
[ -f "pyproject.toml" ] || [ -f "requirements.txt" ] && echo "python"
|
|
78
|
-
[ -f "package.json" ] && echo "node"
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Package Manager Detection
|
|
82
|
-
|
|
83
|
-
For Node.js projects, detect package manager from lockfile:
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
# Detect package manager (first match wins, defaults to bun)
|
|
87
|
-
[ -f "bun.lockb" ] && PM="bun"
|
|
88
|
-
[ -f "pnpm-lock.yaml" ] && PM="pnpm"
|
|
89
|
-
[ -f "yarn.lock" ] && PM="yarn"
|
|
90
|
-
[ -f "package-lock.json" ] && PM="npm"
|
|
91
|
-
[ -z "$PM" ] && PM="bun" # Default to bun
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Use `${PM} test` and `${PM} run lint` for Node.js commands.
|
|
95
|
-
|
|
96
|
-
### Custom Frameworks
|
|
97
|
-
|
|
98
|
-
For unlisted frameworks, use CLI flags:
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
/dev-plan "Build auth" --framework elixir --test-cmd "mix test" --lint-cmd "mix credo"
|
|
102
|
-
/dev-plan "Add API" --framework kotlin --test-cmd "gradle test" --lint-cmd "ktlint"
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
## Django Patterns
|
|
108
|
-
|
|
109
|
-
### Commands
|
|
110
|
-
|
|
111
|
-
| Purpose | Command |
|
|
112
|
-
|---------|---------|
|
|
113
|
-
| Test | `pytest --tb=short` |
|
|
114
|
-
| Test Verbose | `pytest -v` |
|
|
115
|
-
| Test Single | `pytest tests/test_file.py::test_name` |
|
|
116
|
-
| Coverage | `pytest --cov=app_name --cov-report=term-missing` |
|
|
117
|
-
| Lint | `ruff check .` |
|
|
118
|
-
| Format | `ruff format .` |
|
|
119
|
-
| Migrations | `python manage.py makemigrations && python manage.py migrate` |
|
|
120
|
-
|
|
121
|
-
### TDD Phase Template
|
|
122
|
-
|
|
123
|
-
```markdown
|
|
124
|
-
### Phase N: Red - {{Component}} Tests
|
|
125
|
-
|
|
126
|
-
**Tasks:**
|
|
127
|
-
- Create tests/test_{{component}}.py
|
|
128
|
-
- Import necessary fixtures
|
|
129
|
-
- Write test cases using pytest
|
|
130
|
-
|
|
131
|
-
**Verification:**
|
|
132
|
-
```bash
|
|
133
|
-
pytest tests/test_{{component}}.py -v
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
**Conventions:**
|
|
137
|
-
- Use `import {{app}}.models as _{{app}}_models`
|
|
138
|
-
- Use factory_boy for test data
|
|
139
|
-
- Use pytest fixtures, not setUp/tearDown
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Common Test Patterns
|
|
143
|
-
|
|
144
|
-
```python
|
|
145
|
-
# Model tests
|
|
146
|
-
import pytest
|
|
147
|
-
import users.models as _users_models
|
|
148
|
-
|
|
149
|
-
@pytest.mark.django_db
|
|
150
|
-
def test_user_creation():
|
|
151
|
-
user = _users_models.User.objects.create_user(
|
|
152
|
-
email="test@example.com",
|
|
153
|
-
password="testpass123"
|
|
154
|
-
)
|
|
155
|
-
assert user.email == "test@example.com"
|
|
156
|
-
assert user.check_password("testpass123")
|
|
157
|
-
|
|
158
|
-
# API tests
|
|
159
|
-
from rest_framework.test import APIClient
|
|
160
|
-
|
|
161
|
-
@pytest.fixture
|
|
162
|
-
def api_client():
|
|
163
|
-
return APIClient()
|
|
164
|
-
|
|
165
|
-
@pytest.mark.django_db
|
|
166
|
-
def test_login(api_client):
|
|
167
|
-
response = api_client.post("/api/auth/login/", {
|
|
168
|
-
"email": "test@example.com",
|
|
169
|
-
"password": "testpass123"
|
|
170
|
-
})
|
|
171
|
-
assert response.status_code == 200
|
|
172
|
-
assert "token" in response.data
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
## NestJS Patterns
|
|
178
|
-
|
|
179
|
-
### Commands
|
|
180
|
-
|
|
181
|
-
Use detected package manager (`${PM}` = bun/pnpm/yarn/npm):
|
|
182
|
-
|
|
183
|
-
| Purpose | Command |
|
|
184
|
-
|---------|---------|
|
|
185
|
-
| Test | `${PM} test` |
|
|
186
|
-
| Test Watch | `${PM} run test:watch` |
|
|
187
|
-
| Test Coverage | `${PM} run test:cov` |
|
|
188
|
-
| Test E2E | `${PM} run test:e2e` |
|
|
189
|
-
| Lint | `${PM} run lint` |
|
|
190
|
-
| Format | `${PM} run format` |
|
|
191
|
-
|
|
192
|
-
### TDD Phase Template
|
|
193
|
-
|
|
194
|
-
```markdown
|
|
195
|
-
### Phase N: Red - {{Component}} Tests
|
|
196
|
-
|
|
197
|
-
**Tasks:**
|
|
198
|
-
- Create {{component}}.spec.ts
|
|
199
|
-
- Set up test module with mocks
|
|
200
|
-
- Write test cases
|
|
201
|
-
|
|
202
|
-
**Verification:**
|
|
203
|
-
```bash
|
|
204
|
-
${PM} test -- --testPathPattern={{component}}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
**Conventions:**
|
|
208
|
-
- Use barrel imports: `import { Entity } from 'src/module/entities'`
|
|
209
|
-
- Mock dependencies with Jest
|
|
210
|
-
- Use describe/it blocks
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Common Test Patterns
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
// Service tests
|
|
217
|
-
import { Test, TestingModule } from '@nestjs/testing';
|
|
218
|
-
import { UsersService } from './users.service';
|
|
219
|
-
import { getRepositoryToken } from '@nestjs/typeorm';
|
|
220
|
-
import { User } from './entities';
|
|
221
|
-
|
|
222
|
-
describe('UsersService', () => {
|
|
223
|
-
let service: UsersService;
|
|
224
|
-
let mockRepository: jest.Mocked<any>;
|
|
225
|
-
|
|
226
|
-
beforeEach(async () => {
|
|
227
|
-
mockRepository = {
|
|
228
|
-
find: jest.fn(),
|
|
229
|
-
findOne: jest.fn(),
|
|
230
|
-
save: jest.fn(),
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const module: TestingModule = await Test.createTestingModule({
|
|
234
|
-
providers: [
|
|
235
|
-
UsersService,
|
|
236
|
-
{ provide: getRepositoryToken(User), useValue: mockRepository },
|
|
237
|
-
],
|
|
238
|
-
}).compile();
|
|
239
|
-
|
|
240
|
-
service = module.get<UsersService>(UsersService);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('should find all users', async () => {
|
|
244
|
-
mockRepository.find.mockResolvedValue([{ id: '1', email: 'test@test.com' }]);
|
|
245
|
-
const result = await service.findAll();
|
|
246
|
-
expect(result).toHaveLength(1);
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
---
|
|
252
|
-
|
|
253
|
-
## Next.js Patterns
|
|
254
|
-
|
|
255
|
-
### Commands
|
|
256
|
-
|
|
257
|
-
Use detected package manager (`${PM}` = bun/pnpm/yarn/npm):
|
|
258
|
-
|
|
259
|
-
| Purpose | Command |
|
|
260
|
-
|---------|---------|
|
|
261
|
-
| Test | `${PM} test` |
|
|
262
|
-
| Test Watch | `${PM} test -- --watch` |
|
|
263
|
-
| Test Coverage | `${PM} test -- --coverage` |
|
|
264
|
-
| Lint | `${PM} run lint` |
|
|
265
|
-
| Type Check | `${PM} run tsc --noEmit` |
|
|
266
|
-
|
|
267
|
-
### TDD Phase Template
|
|
268
|
-
|
|
269
|
-
```markdown
|
|
270
|
-
### Phase N: Red - {{Component}} Tests
|
|
271
|
-
|
|
272
|
-
**Tasks:**
|
|
273
|
-
- Create __tests__/{{component}}.test.tsx
|
|
274
|
-
- Set up React Testing Library
|
|
275
|
-
- Write component/hook tests
|
|
276
|
-
|
|
277
|
-
**Verification:**
|
|
278
|
-
```bash
|
|
279
|
-
${PM} test -- --testPathPattern={{component}}
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
**Conventions:**
|
|
283
|
-
- Use @/ path aliases
|
|
284
|
-
- Test user interactions, not implementation
|
|
285
|
-
- Use React Testing Library
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### Common Test Patterns
|
|
289
|
-
|
|
290
|
-
```tsx
|
|
291
|
-
// Component tests
|
|
292
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
293
|
-
import { LoginForm } from '@/components/LoginForm';
|
|
294
|
-
|
|
295
|
-
describe('LoginForm', () => {
|
|
296
|
-
it('should submit form with email and password', async () => {
|
|
297
|
-
const onSubmit = jest.fn();
|
|
298
|
-
render(<LoginForm onSubmit={onSubmit} />);
|
|
299
|
-
|
|
300
|
-
fireEvent.change(screen.getByLabelText(/email/i), {
|
|
301
|
-
target: { value: 'test@example.com' },
|
|
302
|
-
});
|
|
303
|
-
fireEvent.change(screen.getByLabelText(/password/i), {
|
|
304
|
-
target: { value: 'password123' },
|
|
305
|
-
});
|
|
306
|
-
fireEvent.click(screen.getByRole('button', { name: /submit/i }));
|
|
307
|
-
|
|
308
|
-
expect(onSubmit).toHaveBeenCalledWith({
|
|
309
|
-
email: 'test@example.com',
|
|
310
|
-
password: 'password123',
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
// Hook tests
|
|
316
|
-
import { renderHook, act } from '@testing-library/react';
|
|
317
|
-
import { useAuth } from '@/hooks/useAuth';
|
|
318
|
-
|
|
319
|
-
describe('useAuth', () => {
|
|
320
|
-
it('should login user', async () => {
|
|
321
|
-
const { result } = renderHook(() => useAuth());
|
|
322
|
-
|
|
323
|
-
await act(async () => {
|
|
324
|
-
await result.current.login('test@example.com', 'password');
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
expect(result.current.isAuthenticated).toBe(true);
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
---
|
|
333
|
-
|
|
334
|
-
## Nuxt.js Patterns
|
|
335
|
-
|
|
336
|
-
### Commands
|
|
337
|
-
|
|
338
|
-
Use detected package manager (`${PM}` = bun/pnpm/yarn/npm):
|
|
339
|
-
|
|
340
|
-
| Purpose | Command |
|
|
341
|
-
|---------|---------|
|
|
342
|
-
| Test | `${PM} test` (vitest) |
|
|
343
|
-
| Test Watch | `${PM} test -- --watch` |
|
|
344
|
-
| Test Coverage | `${PM} test -- --coverage` |
|
|
345
|
-
| Lint | `${PM} run lint` |
|
|
346
|
-
| Type Check | `${PM} run nuxi typecheck` |
|
|
347
|
-
|
|
348
|
-
### TDD Phase Template
|
|
349
|
-
|
|
350
|
-
```markdown
|
|
351
|
-
### Phase N: Red - {{Component}} Tests
|
|
352
|
-
|
|
353
|
-
**Tasks:**
|
|
354
|
-
- Create tests/{{component}}.test.ts
|
|
355
|
-
- Set up Vue Test Utils
|
|
356
|
-
- Write component/composable tests
|
|
357
|
-
|
|
358
|
-
**Verification:**
|
|
359
|
-
```bash
|
|
360
|
-
${PM} test -- {{component}}
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
**Conventions:**
|
|
364
|
-
- Use ~/ path aliases
|
|
365
|
-
- Use Vue 3 Composition API
|
|
366
|
-
- Test with @vue/test-utils
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Common Test Patterns
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
// Component tests
|
|
373
|
-
import { mount } from '@vue/test-utils';
|
|
374
|
-
import { describe, it, expect } from 'vitest';
|
|
375
|
-
import LoginForm from '~/components/LoginForm.vue';
|
|
376
|
-
|
|
377
|
-
describe('LoginForm', () => {
|
|
378
|
-
it('emits submit with credentials', async () => {
|
|
379
|
-
const wrapper = mount(LoginForm);
|
|
380
|
-
|
|
381
|
-
await wrapper.find('input[name="email"]').setValue('test@example.com');
|
|
382
|
-
await wrapper.find('input[name="password"]').setValue('password123');
|
|
383
|
-
await wrapper.find('form').trigger('submit');
|
|
384
|
-
|
|
385
|
-
expect(wrapper.emitted('submit')).toBeTruthy();
|
|
386
|
-
expect(wrapper.emitted('submit')[0]).toEqual([{
|
|
387
|
-
email: 'test@example.com',
|
|
388
|
-
password: 'password123',
|
|
389
|
-
}]);
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
// Composable tests
|
|
394
|
-
import { describe, it, expect } from 'vitest';
|
|
395
|
-
import { useAuth } from '~/composables/useAuth';
|
|
396
|
-
|
|
397
|
-
describe('useAuth', () => {
|
|
398
|
-
it('should login user', async () => {
|
|
399
|
-
const { login, isAuthenticated } = useAuth();
|
|
400
|
-
|
|
401
|
-
await login('test@example.com', 'password');
|
|
402
|
-
|
|
403
|
-
expect(isAuthenticated.value).toBe(true);
|
|
404
|
-
});
|
|
405
|
-
});
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
---
|
|
409
|
-
|
|
410
|
-
## Generic Fallback
|
|
411
|
-
|
|
412
|
-
When no specific framework is detected:
|
|
413
|
-
|
|
414
|
-
### Commands
|
|
415
|
-
|
|
416
|
-
| Purpose | Command |
|
|
417
|
-
|---------|---------|
|
|
418
|
-
| Test (Python) | `pytest` |
|
|
419
|
-
| Test (Node) | `${PM} test` (defaults to bun) |
|
|
420
|
-
| Lint (Python) | `ruff check .` |
|
|
421
|
-
| Lint (Node) | `${PM} run lint` |
|
|
422
|
-
|
|
423
|
-
### TDD Phase Template
|
|
424
|
-
|
|
425
|
-
```markdown
|
|
426
|
-
### Phase N: Red - {{Component}} Tests
|
|
427
|
-
|
|
428
|
-
**Tasks:**
|
|
429
|
-
- Create test file for component
|
|
430
|
-
- Write test cases
|
|
431
|
-
|
|
432
|
-
**Verification:**
|
|
433
|
-
Run test command for the detected test framework
|
|
434
|
-
|
|
435
|
-
**Self-correction:**
|
|
436
|
-
- If no test framework found, suggest installing one
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
---
|
|
440
|
-
|
|
441
|
-
## Framework-Specific Considerations
|
|
442
|
-
|
|
443
|
-
### Django + Smicolon Conventions
|
|
444
|
-
|
|
445
|
-
- Use absolute modular imports: `import app.models as _app_models`
|
|
446
|
-
- UUID primary keys on all models
|
|
447
|
-
- Timestamps: `created_at`, `updated_at`
|
|
448
|
-
- Soft deletes: `is_deleted` field
|
|
449
|
-
- Service layer for business logic
|
|
450
|
-
|
|
451
|
-
### NestJS + Smicolon Conventions
|
|
452
|
-
|
|
453
|
-
- Barrel exports in all directories
|
|
454
|
-
- Absolute imports: `import { X } from 'src/module/entities'`
|
|
455
|
-
- UUID primary keys
|
|
456
|
-
- TypeORM soft deletes with `@DeleteDateColumn()`
|
|
457
|
-
|
|
458
|
-
### Next.js + Smicolon Conventions
|
|
459
|
-
|
|
460
|
-
- Path aliases: `@/components`, `@/hooks`
|
|
461
|
-
- React Hook Form + Zod for forms
|
|
462
|
-
- TanStack Query for data fetching
|
|
463
|
-
- WCAG 2.1 AA accessibility
|
|
464
|
-
|
|
465
|
-
### Nuxt.js + Smicolon Conventions
|
|
466
|
-
|
|
467
|
-
- Path aliases: `~/components`, `~/composables`
|
|
468
|
-
- VeeValidate + Zod for forms
|
|
469
|
-
- Built-in composables: `useFetch`, `useAsyncData`
|
|
470
|
-
- WCAG 2.1 AA accessibility
|
|
471
|
-
|
|
472
|
-
---
|
|
473
|
-
|
|
474
|
-
## Go Patterns
|
|
475
|
-
|
|
476
|
-
### Commands
|
|
477
|
-
|
|
478
|
-
| Purpose | Command |
|
|
479
|
-
|---------|---------|
|
|
480
|
-
| Test | `go test ./...` |
|
|
481
|
-
| Test Verbose | `go test -v ./...` |
|
|
482
|
-
| Test Single | `go test -run TestName ./pkg` |
|
|
483
|
-
| Coverage | `go test -cover ./...` |
|
|
484
|
-
| Lint | `golangci-lint run` |
|
|
485
|
-
| Format | `gofmt -w .` |
|
|
486
|
-
|
|
487
|
-
### Common Test Patterns
|
|
488
|
-
|
|
489
|
-
```go
|
|
490
|
-
// user_test.go
|
|
491
|
-
package user
|
|
492
|
-
|
|
493
|
-
import (
|
|
494
|
-
"testing"
|
|
495
|
-
)
|
|
496
|
-
|
|
497
|
-
func TestCreateUser(t *testing.T) {
|
|
498
|
-
// Arrange
|
|
499
|
-
service := NewUserService(mockDB)
|
|
500
|
-
|
|
501
|
-
// Act
|
|
502
|
-
user, err := service.Create("test@example.com", "password123")
|
|
503
|
-
|
|
504
|
-
// Assert
|
|
505
|
-
if err != nil {
|
|
506
|
-
t.Fatalf("expected no error, got %v", err)
|
|
507
|
-
}
|
|
508
|
-
if user.Email != "test@example.com" {
|
|
509
|
-
t.Errorf("expected email test@example.com, got %s", user.Email)
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
func TestCreateUser_InvalidEmail(t *testing.T) {
|
|
514
|
-
service := NewUserService(mockDB)
|
|
515
|
-
|
|
516
|
-
_, err := service.Create("invalid", "password123")
|
|
517
|
-
|
|
518
|
-
if err == nil {
|
|
519
|
-
t.Fatal("expected error for invalid email")
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
### Table-Driven Tests
|
|
525
|
-
|
|
526
|
-
```go
|
|
527
|
-
func TestValidateEmail(t *testing.T) {
|
|
528
|
-
tests := []struct {
|
|
529
|
-
name string
|
|
530
|
-
email string
|
|
531
|
-
wantErr bool
|
|
532
|
-
}{
|
|
533
|
-
{"valid email", "test@example.com", false},
|
|
534
|
-
{"missing @", "testexample.com", true},
|
|
535
|
-
{"empty", "", true},
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
for _, tt := range tests {
|
|
539
|
-
t.Run(tt.name, func(t *testing.T) {
|
|
540
|
-
err := ValidateEmail(tt.email)
|
|
541
|
-
if (err != nil) != tt.wantErr {
|
|
542
|
-
t.Errorf("ValidateEmail() error = %v, wantErr %v", err, tt.wantErr)
|
|
543
|
-
}
|
|
544
|
-
})
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
---
|
|
550
|
-
|
|
551
|
-
## Rust Patterns
|
|
552
|
-
|
|
553
|
-
### Commands
|
|
554
|
-
|
|
555
|
-
| Purpose | Command |
|
|
556
|
-
|---------|---------|
|
|
557
|
-
| Test | `cargo test` |
|
|
558
|
-
| Test Single | `cargo test test_name` |
|
|
559
|
-
| Test Verbose | `cargo test -- --nocapture` |
|
|
560
|
-
| Lint | `cargo clippy` |
|
|
561
|
-
| Format | `cargo fmt` |
|
|
562
|
-
|
|
563
|
-
### Common Test Patterns
|
|
564
|
-
|
|
565
|
-
```rust
|
|
566
|
-
// src/user.rs
|
|
567
|
-
#[cfg(test)]
|
|
568
|
-
mod tests {
|
|
569
|
-
use super::*;
|
|
570
|
-
|
|
571
|
-
#[test]
|
|
572
|
-
fn test_create_user() {
|
|
573
|
-
let user = User::new("test@example.com", "password123");
|
|
574
|
-
assert_eq!(user.email, "test@example.com");
|
|
575
|
-
assert!(user.verify_password("password123"));
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
#[test]
|
|
579
|
-
fn test_invalid_email() {
|
|
580
|
-
let result = User::new("invalid", "password123");
|
|
581
|
-
assert!(result.is_err());
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
#[test]
|
|
585
|
-
#[should_panic(expected = "Email required")]
|
|
586
|
-
fn test_empty_email_panics() {
|
|
587
|
-
User::new("", "password123").unwrap();
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
---
|
|
593
|
-
|
|
594
|
-
## Rails Patterns
|
|
595
|
-
|
|
596
|
-
### Commands
|
|
597
|
-
|
|
598
|
-
| Purpose | Command |
|
|
599
|
-
|---------|---------|
|
|
600
|
-
| Test | `bundle exec rspec` |
|
|
601
|
-
| Test Single | `bundle exec rspec spec/models/user_spec.rb` |
|
|
602
|
-
| Test Line | `bundle exec rspec spec/models/user_spec.rb:15` |
|
|
603
|
-
| Lint | `bundle exec rubocop` |
|
|
604
|
-
| Format | `bundle exec rubocop -a` |
|
|
605
|
-
|
|
606
|
-
### Common Test Patterns (RSpec)
|
|
607
|
-
|
|
608
|
-
```ruby
|
|
609
|
-
# spec/models/user_spec.rb
|
|
610
|
-
require 'rails_helper'
|
|
611
|
-
|
|
612
|
-
RSpec.describe User, type: :model do
|
|
613
|
-
describe 'validations' do
|
|
614
|
-
it { should validate_presence_of(:email) }
|
|
615
|
-
it { should validate_uniqueness_of(:email) }
|
|
616
|
-
end
|
|
617
|
-
|
|
618
|
-
describe '#authenticate' do
|
|
619
|
-
let(:user) { create(:user, password: 'password123') }
|
|
620
|
-
|
|
621
|
-
context 'with valid password' do
|
|
622
|
-
it 'returns true' do
|
|
623
|
-
expect(user.authenticate('password123')).to be true
|
|
624
|
-
end
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
context 'with invalid password' do
|
|
628
|
-
it 'returns false' do
|
|
629
|
-
expect(user.authenticate('wrong')).to be false
|
|
630
|
-
end
|
|
631
|
-
end
|
|
632
|
-
end
|
|
633
|
-
end
|
|
634
|
-
|
|
635
|
-
# spec/requests/auth_spec.rb
|
|
636
|
-
RSpec.describe 'Authentication', type: :request do
|
|
637
|
-
describe 'POST /login' do
|
|
638
|
-
let(:user) { create(:user) }
|
|
639
|
-
|
|
640
|
-
it 'returns JWT token' do
|
|
641
|
-
post '/login', params: { email: user.email, password: 'password123' }
|
|
642
|
-
expect(response).to have_http_status(:ok)
|
|
643
|
-
expect(json_response['token']).to be_present
|
|
644
|
-
end
|
|
645
|
-
end
|
|
646
|
-
end
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
---
|
|
650
|
-
|
|
651
|
-
## Laravel Patterns
|
|
652
|
-
|
|
653
|
-
### Commands
|
|
654
|
-
|
|
655
|
-
| Purpose | Command |
|
|
656
|
-
|---------|---------|
|
|
657
|
-
| Test | `php artisan test` |
|
|
658
|
-
| Test Filter | `php artisan test --filter=UserTest` |
|
|
659
|
-
| Test Parallel | `php artisan test --parallel` |
|
|
660
|
-
| Lint | `./vendor/bin/pint` |
|
|
661
|
-
| Static Analysis | `./vendor/bin/phpstan analyse` |
|
|
662
|
-
|
|
663
|
-
### Common Test Patterns
|
|
664
|
-
|
|
665
|
-
```php
|
|
666
|
-
// tests/Feature/AuthTest.php
|
|
667
|
-
<?php
|
|
668
|
-
|
|
669
|
-
namespace Tests\Feature;
|
|
670
|
-
|
|
671
|
-
use App\Models\User;
|
|
672
|
-
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
673
|
-
use Tests\TestCase;
|
|
674
|
-
|
|
675
|
-
class AuthTest extends TestCase
|
|
676
|
-
{
|
|
677
|
-
use RefreshDatabase;
|
|
678
|
-
|
|
679
|
-
public function test_user_can_login_with_valid_credentials(): void
|
|
680
|
-
{
|
|
681
|
-
$user = User::factory()->create([
|
|
682
|
-
'email' => 'test@example.com',
|
|
683
|
-
'password' => bcrypt('password123'),
|
|
684
|
-
]);
|
|
685
|
-
|
|
686
|
-
$response = $this->postJson('/api/login', [
|
|
687
|
-
'email' => 'test@example.com',
|
|
688
|
-
'password' => 'password123',
|
|
689
|
-
]);
|
|
690
|
-
|
|
691
|
-
$response->assertOk()
|
|
692
|
-
->assertJsonStructure(['token', 'user']);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
public function test_login_fails_with_invalid_credentials(): void
|
|
696
|
-
{
|
|
697
|
-
$user = User::factory()->create();
|
|
698
|
-
|
|
699
|
-
$response = $this->postJson('/api/login', [
|
|
700
|
-
'email' => $user->email,
|
|
701
|
-
'password' => 'wrong-password',
|
|
702
|
-
]);
|
|
703
|
-
|
|
704
|
-
$response->assertUnauthorized();
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
```
|
|
708
|
-
|
|
709
|
-
---
|
|
710
|
-
|
|
711
|
-
## FastAPI Patterns
|
|
712
|
-
|
|
713
|
-
### Commands
|
|
714
|
-
|
|
715
|
-
| Purpose | Command |
|
|
716
|
-
|---------|---------|
|
|
717
|
-
| Test | `pytest --tb=short` |
|
|
718
|
-
| Test Verbose | `pytest -v` |
|
|
719
|
-
| Coverage | `pytest --cov=app` |
|
|
720
|
-
| Lint | `ruff check .` |
|
|
721
|
-
| Format | `ruff format .` |
|
|
722
|
-
|
|
723
|
-
### Common Test Patterns
|
|
724
|
-
|
|
725
|
-
```python
|
|
726
|
-
# tests/test_auth.py
|
|
727
|
-
import pytest
|
|
728
|
-
from httpx import AsyncClient
|
|
729
|
-
from app.main import app
|
|
730
|
-
|
|
731
|
-
@pytest.fixture
|
|
732
|
-
async def client():
|
|
733
|
-
async with AsyncClient(app=app, base_url="http://test") as ac:
|
|
734
|
-
yield ac
|
|
735
|
-
|
|
736
|
-
@pytest.mark.asyncio
|
|
737
|
-
async def test_login(client):
|
|
738
|
-
response = await client.post("/auth/login", json={
|
|
739
|
-
"email": "test@example.com",
|
|
740
|
-
"password": "password123"
|
|
741
|
-
})
|
|
742
|
-
assert response.status_code == 200
|
|
743
|
-
assert "access_token" in response.json()
|
|
744
|
-
|
|
745
|
-
@pytest.mark.asyncio
|
|
746
|
-
async def test_login_invalid_credentials(client):
|
|
747
|
-
response = await client.post("/auth/login", json={
|
|
748
|
-
"email": "test@example.com",
|
|
749
|
-
"password": "wrong"
|
|
750
|
-
})
|
|
751
|
-
assert response.status_code == 401
|
|
752
|
-
```
|
|
753
|
-
|
|
754
|
-
---
|
|
755
|
-
|
|
756
|
-
## Hono Patterns
|
|
757
|
-
|
|
758
|
-
### Commands
|
|
759
|
-
|
|
760
|
-
| Purpose | Command |
|
|
761
|
-
|---------|---------|
|
|
762
|
-
| Test | `bun test` |
|
|
763
|
-
| Test Watch | `bun test --watch` |
|
|
764
|
-
| Lint | `bun run lint` |
|
|
765
|
-
| Format | `bun run format` |
|
|
766
|
-
|
|
767
|
-
### Common Test Patterns
|
|
768
|
-
|
|
769
|
-
```typescript
|
|
770
|
-
// src/routes/auth.test.ts
|
|
771
|
-
import { describe, it, expect } from 'bun:test';
|
|
772
|
-
import { app } from './app';
|
|
773
|
-
|
|
774
|
-
describe('Auth Routes', () => {
|
|
775
|
-
it('POST /login returns token', async () => {
|
|
776
|
-
const res = await app.request('/login', {
|
|
777
|
-
method: 'POST',
|
|
778
|
-
body: JSON.stringify({
|
|
779
|
-
email: 'test@example.com',
|
|
780
|
-
password: 'password123',
|
|
781
|
-
}),
|
|
782
|
-
headers: { 'Content-Type': 'application/json' },
|
|
783
|
-
});
|
|
784
|
-
|
|
785
|
-
expect(res.status).toBe(200);
|
|
786
|
-
const json = await res.json();
|
|
787
|
-
expect(json.token).toBeDefined();
|
|
788
|
-
});
|
|
789
|
-
|
|
790
|
-
it('POST /login rejects invalid credentials', async () => {
|
|
791
|
-
const res = await app.request('/login', {
|
|
792
|
-
method: 'POST',
|
|
793
|
-
body: JSON.stringify({
|
|
794
|
-
email: 'test@example.com',
|
|
795
|
-
password: 'wrong',
|
|
796
|
-
}),
|
|
797
|
-
headers: { 'Content-Type': 'application/json' },
|
|
798
|
-
});
|
|
799
|
-
|
|
800
|
-
expect(res.status).toBe(401);
|
|
801
|
-
});
|
|
802
|
-
});
|
|
803
|
-
```
|
|
804
|
-
|
|
805
|
-
---
|
|
806
|
-
|
|
807
|
-
## Flutter Patterns
|
|
808
|
-
|
|
809
|
-
### Commands
|
|
810
|
-
|
|
811
|
-
| Purpose | Command |
|
|
812
|
-
|---------|---------|
|
|
813
|
-
| Test | `flutter test` |
|
|
814
|
-
| Test Single | `flutter test test/widget_test.dart` |
|
|
815
|
-
| Coverage | `flutter test --coverage` |
|
|
816
|
-
| Lint | `flutter analyze` |
|
|
817
|
-
| Format | `dart format .` |
|
|
818
|
-
|
|
819
|
-
### Common Test Patterns
|
|
820
|
-
|
|
821
|
-
```dart
|
|
822
|
-
// test/providers/auth_provider_test.dart
|
|
823
|
-
import 'package:flutter_test/flutter_test.dart';
|
|
824
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
825
|
-
import 'package:myapp/providers/auth_provider.dart';
|
|
826
|
-
|
|
827
|
-
void main() {
|
|
828
|
-
group('AuthProvider', () {
|
|
829
|
-
test('initial state is unauthenticated', () {
|
|
830
|
-
final container = ProviderContainer();
|
|
831
|
-
final state = container.read(authProvider);
|
|
832
|
-
|
|
833
|
-
expect(state.isAuthenticated, false);
|
|
834
|
-
expect(state.user, isNull);
|
|
835
|
-
});
|
|
836
|
-
|
|
837
|
-
test('login updates state', () async {
|
|
838
|
-
final container = ProviderContainer();
|
|
839
|
-
|
|
840
|
-
await container.read(authProvider.notifier).login(
|
|
841
|
-
'test@example.com',
|
|
842
|
-
'password123',
|
|
843
|
-
);
|
|
844
|
-
|
|
845
|
-
final state = container.read(authProvider);
|
|
846
|
-
expect(state.isAuthenticated, true);
|
|
847
|
-
expect(state.user?.email, 'test@example.com');
|
|
848
|
-
});
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
// test/widgets/login_screen_test.dart
|
|
853
|
-
import 'package:flutter/material.dart';
|
|
854
|
-
import 'package:flutter_test/flutter_test.dart';
|
|
855
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
856
|
-
import 'package:myapp/screens/login_screen.dart';
|
|
857
|
-
|
|
858
|
-
void main() {
|
|
859
|
-
testWidgets('login form submits correctly', (tester) async {
|
|
860
|
-
await tester.pumpWidget(
|
|
861
|
-
ProviderScope(
|
|
862
|
-
child: MaterialApp(home: LoginScreen()),
|
|
863
|
-
),
|
|
864
|
-
);
|
|
865
|
-
|
|
866
|
-
await tester.enterText(find.byKey(Key('email')), 'test@example.com');
|
|
867
|
-
await tester.enterText(find.byKey(Key('password')), 'password123');
|
|
868
|
-
await tester.tap(find.byType(ElevatedButton));
|
|
869
|
-
await tester.pump();
|
|
870
|
-
|
|
871
|
-
// Verify navigation or state change
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
```
|