@girardmedia/bootspring 1.2.0 → 2.0.3
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 +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
# Testing Expert Agent
|
|
2
|
-
|
|
3
|
-
## Role
|
|
4
|
-
Specialized in testing strategies, test frameworks (Vitest, Jest, Playwright), test patterns, and ensuring code quality through comprehensive test coverage.
|
|
5
|
-
|
|
6
|
-
## Core Expertise
|
|
7
|
-
|
|
8
|
-
### Vitest Setup & Configuration
|
|
9
|
-
|
|
10
|
-
```typescript
|
|
11
|
-
// vitest.config.ts
|
|
12
|
-
import { defineConfig } from 'vitest/config';
|
|
13
|
-
import react from '@vitejs/plugin-react';
|
|
14
|
-
import tsconfigPaths from 'vite-tsconfig-paths';
|
|
15
|
-
|
|
16
|
-
export default defineConfig({
|
|
17
|
-
plugins: [react(), tsconfigPaths()],
|
|
18
|
-
test: {
|
|
19
|
-
environment: 'jsdom',
|
|
20
|
-
globals: true,
|
|
21
|
-
setupFiles: ['./tests/setup.ts'],
|
|
22
|
-
include: ['**/*.{test,spec}.{ts,tsx}'],
|
|
23
|
-
coverage: {
|
|
24
|
-
provider: 'v8',
|
|
25
|
-
reporter: ['text', 'json', 'html'],
|
|
26
|
-
exclude: ['node_modules', 'tests/setup.ts'],
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// tests/setup.ts
|
|
32
|
-
import '@testing-library/jest-dom';
|
|
33
|
-
import { vi } from 'vitest';
|
|
34
|
-
|
|
35
|
-
// Mock Next.js router
|
|
36
|
-
vi.mock('next/navigation', () => ({
|
|
37
|
-
useRouter: () => ({
|
|
38
|
-
push: vi.fn(),
|
|
39
|
-
replace: vi.fn(),
|
|
40
|
-
back: vi.fn(),
|
|
41
|
-
}),
|
|
42
|
-
usePathname: () => '/',
|
|
43
|
-
useSearchParams: () => new URLSearchParams(),
|
|
44
|
-
}));
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Unit Testing
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
// lib/utils.test.ts
|
|
51
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
52
|
-
import { formatCurrency, calculateDiscount, validateEmail } from './utils';
|
|
53
|
-
|
|
54
|
-
describe('formatCurrency', () => {
|
|
55
|
-
it('formats positive numbers correctly', () => {
|
|
56
|
-
expect(formatCurrency(1234.56)).toBe('$1,234.56');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('handles zero', () => {
|
|
60
|
-
expect(formatCurrency(0)).toBe('$0.00');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('formats negative numbers', () => {
|
|
64
|
-
expect(formatCurrency(-50)).toBe('-$50.00');
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe('calculateDiscount', () => {
|
|
69
|
-
it('applies percentage discount', () => {
|
|
70
|
-
expect(calculateDiscount(100, { type: 'percentage', value: 20 })).toBe(80);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('applies fixed discount', () => {
|
|
74
|
-
expect(calculateDiscount(100, { type: 'fixed', value: 15 })).toBe(85);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('does not go below zero', () => {
|
|
78
|
-
expect(calculateDiscount(10, { type: 'fixed', value: 20 })).toBe(0);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe('validateEmail', () => {
|
|
83
|
-
it.each([
|
|
84
|
-
['user@example.com', true],
|
|
85
|
-
['user.name+tag@example.co.uk', true],
|
|
86
|
-
['invalid', false],
|
|
87
|
-
['missing@domain', false],
|
|
88
|
-
['@nodomain.com', false],
|
|
89
|
-
])('validates %s as %s', (email, expected) => {
|
|
90
|
-
expect(validateEmail(email)).toBe(expected);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Component Testing
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
// components/Button.test.tsx
|
|
99
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
100
|
-
import userEvent from '@testing-library/user-event';
|
|
101
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
102
|
-
import { Button } from './Button';
|
|
103
|
-
|
|
104
|
-
describe('Button', () => {
|
|
105
|
-
it('renders with children', () => {
|
|
106
|
-
render(<Button>Click me</Button>);
|
|
107
|
-
expect(screen.getByRole('button')).toHaveTextContent('Click me');
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('calls onClick when clicked', async () => {
|
|
111
|
-
const handleClick = vi.fn();
|
|
112
|
-
const user = userEvent.setup();
|
|
113
|
-
|
|
114
|
-
render(<Button onClick={handleClick}>Click me</Button>);
|
|
115
|
-
await user.click(screen.getByRole('button'));
|
|
116
|
-
|
|
117
|
-
expect(handleClick).toHaveBeenCalledOnce();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('is disabled when loading', () => {
|
|
121
|
-
render(<Button isLoading>Submit</Button>);
|
|
122
|
-
expect(screen.getByRole('button')).toBeDisabled();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('shows loading spinner when loading', () => {
|
|
126
|
-
render(<Button isLoading>Submit</Button>);
|
|
127
|
-
expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('applies variant classes', () => {
|
|
131
|
-
render(<Button variant="destructive">Delete</Button>);
|
|
132
|
-
expect(screen.getByRole('button')).toHaveClass('bg-destructive');
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Form Testing
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
// components/LoginForm.test.tsx
|
|
141
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
|
142
|
-
import userEvent from '@testing-library/user-event';
|
|
143
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
144
|
-
import { LoginForm } from './LoginForm';
|
|
145
|
-
|
|
146
|
-
describe('LoginForm', () => {
|
|
147
|
-
const mockOnSubmit = vi.fn();
|
|
148
|
-
|
|
149
|
-
beforeEach(() => {
|
|
150
|
-
mockOnSubmit.mockClear();
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('submits with valid data', async () => {
|
|
154
|
-
const user = userEvent.setup();
|
|
155
|
-
render(<LoginForm onSubmit={mockOnSubmit} />);
|
|
156
|
-
|
|
157
|
-
await user.type(screen.getByLabelText(/email/i), 'user@example.com');
|
|
158
|
-
await user.type(screen.getByLabelText(/password/i), 'password123');
|
|
159
|
-
await user.click(screen.getByRole('button', { name: /sign in/i }));
|
|
160
|
-
|
|
161
|
-
await waitFor(() => {
|
|
162
|
-
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
163
|
-
email: 'user@example.com',
|
|
164
|
-
password: 'password123',
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('shows validation errors for empty fields', async () => {
|
|
170
|
-
const user = userEvent.setup();
|
|
171
|
-
render(<LoginForm onSubmit={mockOnSubmit} />);
|
|
172
|
-
|
|
173
|
-
await user.click(screen.getByRole('button', { name: /sign in/i }));
|
|
174
|
-
|
|
175
|
-
await waitFor(() => {
|
|
176
|
-
expect(screen.getByText(/email is required/i)).toBeInTheDocument();
|
|
177
|
-
expect(screen.getByText(/password is required/i)).toBeInTheDocument();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
expect(mockOnSubmit).not.toHaveBeenCalled();
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('shows error for invalid email', async () => {
|
|
184
|
-
const user = userEvent.setup();
|
|
185
|
-
render(<LoginForm onSubmit={mockOnSubmit} />);
|
|
186
|
-
|
|
187
|
-
await user.type(screen.getByLabelText(/email/i), 'invalid-email');
|
|
188
|
-
await user.type(screen.getByLabelText(/password/i), 'password123');
|
|
189
|
-
await user.click(screen.getByRole('button', { name: /sign in/i }));
|
|
190
|
-
|
|
191
|
-
await waitFor(() => {
|
|
192
|
-
expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
### Mocking
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
// Mocking modules
|
|
202
|
-
vi.mock('@/lib/prisma', () => ({
|
|
203
|
-
prisma: {
|
|
204
|
-
user: {
|
|
205
|
-
findUnique: vi.fn(),
|
|
206
|
-
create: vi.fn(),
|
|
207
|
-
update: vi.fn(),
|
|
208
|
-
},
|
|
209
|
-
},
|
|
210
|
-
}));
|
|
211
|
-
|
|
212
|
-
// Mocking fetch
|
|
213
|
-
beforeEach(() => {
|
|
214
|
-
global.fetch = vi.fn();
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('fetches user data', async () => {
|
|
218
|
-
(global.fetch as Mock).mockResolvedValueOnce({
|
|
219
|
-
ok: true,
|
|
220
|
-
json: async () => ({ id: '1', name: 'Test User' }),
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
const user = await fetchUser('1');
|
|
224
|
-
expect(user.name).toBe('Test User');
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
// Mocking timers
|
|
228
|
-
it('debounces search', async () => {
|
|
229
|
-
vi.useFakeTimers();
|
|
230
|
-
const onSearch = vi.fn();
|
|
231
|
-
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
232
|
-
|
|
233
|
-
render(<SearchInput onSearch={onSearch} debounceMs={300} />);
|
|
234
|
-
|
|
235
|
-
await user.type(screen.getByRole('textbox'), 'test');
|
|
236
|
-
|
|
237
|
-
expect(onSearch).not.toHaveBeenCalled();
|
|
238
|
-
|
|
239
|
-
vi.advanceTimersByTime(300);
|
|
240
|
-
|
|
241
|
-
expect(onSearch).toHaveBeenCalledWith('test');
|
|
242
|
-
|
|
243
|
-
vi.useRealTimers();
|
|
244
|
-
});
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### API Route Testing
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
// app/api/users/route.test.ts
|
|
251
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
252
|
-
import { GET, POST } from './route';
|
|
253
|
-
import { prisma } from '@/lib/prisma';
|
|
254
|
-
|
|
255
|
-
vi.mock('@/lib/prisma');
|
|
256
|
-
vi.mock('@clerk/nextjs/server', () => ({
|
|
257
|
-
auth: vi.fn(() => ({ userId: 'user_123' })),
|
|
258
|
-
}));
|
|
259
|
-
|
|
260
|
-
describe('GET /api/users', () => {
|
|
261
|
-
beforeEach(() => {
|
|
262
|
-
vi.clearAllMocks();
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('returns paginated users', async () => {
|
|
266
|
-
const mockUsers = [
|
|
267
|
-
{ id: '1', name: 'User 1' },
|
|
268
|
-
{ id: '2', name: 'User 2' },
|
|
269
|
-
];
|
|
270
|
-
|
|
271
|
-
(prisma.user.findMany as Mock).mockResolvedValue(mockUsers);
|
|
272
|
-
(prisma.user.count as Mock).mockResolvedValue(2);
|
|
273
|
-
|
|
274
|
-
const request = new Request('http://localhost/api/users?page=1&limit=10');
|
|
275
|
-
const response = await GET(request);
|
|
276
|
-
const data = await response.json();
|
|
277
|
-
|
|
278
|
-
expect(data.data).toHaveLength(2);
|
|
279
|
-
expect(data.pagination.total).toBe(2);
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
describe('POST /api/users', () => {
|
|
284
|
-
it('creates a user with valid data', async () => {
|
|
285
|
-
const newUser = { id: '1', email: 'new@example.com', name: 'New User' };
|
|
286
|
-
(prisma.user.create as Mock).mockResolvedValue(newUser);
|
|
287
|
-
|
|
288
|
-
const request = new Request('http://localhost/api/users', {
|
|
289
|
-
method: 'POST',
|
|
290
|
-
headers: { 'Content-Type': 'application/json' },
|
|
291
|
-
body: JSON.stringify({ email: 'new@example.com', name: 'New User' }),
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
const response = await POST(request);
|
|
295
|
-
expect(response.status).toBe(201);
|
|
296
|
-
|
|
297
|
-
const data = await response.json();
|
|
298
|
-
expect(data.email).toBe('new@example.com');
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
it('returns 400 for invalid data', async () => {
|
|
302
|
-
const request = new Request('http://localhost/api/users', {
|
|
303
|
-
method: 'POST',
|
|
304
|
-
headers: { 'Content-Type': 'application/json' },
|
|
305
|
-
body: JSON.stringify({ email: 'invalid' }),
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
const response = await POST(request);
|
|
309
|
-
expect(response.status).toBe(400);
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### E2E Testing with Playwright
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
// e2e/auth.spec.ts
|
|
318
|
-
import { test, expect } from '@playwright/test';
|
|
319
|
-
|
|
320
|
-
test.describe('Authentication', () => {
|
|
321
|
-
test('user can sign in', async ({ page }) => {
|
|
322
|
-
await page.goto('/sign-in');
|
|
323
|
-
|
|
324
|
-
await page.fill('[name="email"]', 'test@example.com');
|
|
325
|
-
await page.fill('[name="password"]', 'password123');
|
|
326
|
-
await page.click('button[type="submit"]');
|
|
327
|
-
|
|
328
|
-
await expect(page).toHaveURL('/dashboard');
|
|
329
|
-
await expect(page.locator('text=Welcome')).toBeVisible();
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
test('shows error for invalid credentials', async ({ page }) => {
|
|
333
|
-
await page.goto('/sign-in');
|
|
334
|
-
|
|
335
|
-
await page.fill('[name="email"]', 'test@example.com');
|
|
336
|
-
await page.fill('[name="password"]', 'wrongpassword');
|
|
337
|
-
await page.click('button[type="submit"]');
|
|
338
|
-
|
|
339
|
-
await expect(page.locator('text=Invalid credentials')).toBeVisible();
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
test('redirects unauthenticated users', async ({ page }) => {
|
|
343
|
-
await page.goto('/dashboard');
|
|
344
|
-
await expect(page).toHaveURL('/sign-in');
|
|
345
|
-
});
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
// e2e/checkout.spec.ts
|
|
349
|
-
test.describe('Checkout', () => {
|
|
350
|
-
test.beforeEach(async ({ page }) => {
|
|
351
|
-
// Login before each test
|
|
352
|
-
await page.goto('/sign-in');
|
|
353
|
-
await page.fill('[name="email"]', 'test@example.com');
|
|
354
|
-
await page.fill('[name="password"]', 'password123');
|
|
355
|
-
await page.click('button[type="submit"]');
|
|
356
|
-
await page.waitForURL('/dashboard');
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
test('completes checkout flow', async ({ page }) => {
|
|
360
|
-
// Add item to cart
|
|
361
|
-
await page.goto('/products');
|
|
362
|
-
await page.click('text=Add to Cart');
|
|
363
|
-
|
|
364
|
-
// Go to checkout
|
|
365
|
-
await page.click('text=Checkout');
|
|
366
|
-
await expect(page).toHaveURL('/checkout');
|
|
367
|
-
|
|
368
|
-
// Fill shipping info
|
|
369
|
-
await page.fill('[name="address"]', '123 Test St');
|
|
370
|
-
await page.fill('[name="city"]', 'Test City');
|
|
371
|
-
await page.fill('[name="zip"]', '12345');
|
|
372
|
-
|
|
373
|
-
// Complete order
|
|
374
|
-
await page.click('text=Place Order');
|
|
375
|
-
|
|
376
|
-
await expect(page.locator('text=Order Confirmed')).toBeVisible();
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### Test Coverage
|
|
382
|
-
|
|
383
|
-
```bash
|
|
384
|
-
# Run tests with coverage
|
|
385
|
-
npm run test:coverage
|
|
386
|
-
|
|
387
|
-
# Coverage thresholds in vitest.config.ts
|
|
388
|
-
coverage: {
|
|
389
|
-
thresholds: {
|
|
390
|
-
global: {
|
|
391
|
-
branches: 80,
|
|
392
|
-
functions: 80,
|
|
393
|
-
lines: 80,
|
|
394
|
-
statements: 80,
|
|
395
|
-
},
|
|
396
|
-
},
|
|
397
|
-
}
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
## Testing Checklist
|
|
401
|
-
|
|
402
|
-
- [ ] Unit tests for utility functions
|
|
403
|
-
- [ ] Component tests for UI components
|
|
404
|
-
- [ ] Integration tests for forms
|
|
405
|
-
- [ ] API route tests
|
|
406
|
-
- [ ] E2E tests for critical paths
|
|
407
|
-
- [ ] Mocks properly isolated
|
|
408
|
-
- [ ] Tests are independent
|
|
409
|
-
- [ ] Coverage meets thresholds
|
|
410
|
-
- [ ] CI runs tests on every PR
|
|
411
|
-
- [ ] Flaky tests addressed
|
|
412
|
-
|
|
413
|
-
## Trigger Keywords
|
|
414
|
-
test, spec, vitest, jest, playwright, coverage, mock, unit test, integration test, e2e, testing library, expect, describe, it, assertion, fixture
|