agentic-team-templates 0.3.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 +280 -0
- package/bin/cli.js +5 -0
- package/package.json +47 -0
- package/src/index.js +521 -0
- package/templates/_shared/code-quality.md +162 -0
- package/templates/_shared/communication.md +114 -0
- package/templates/_shared/core-principles.md +62 -0
- package/templates/_shared/git-workflow.md +165 -0
- package/templates/_shared/security-fundamentals.md +173 -0
- package/templates/blockchain/.cursorrules/defi-patterns.md +520 -0
- package/templates/blockchain/.cursorrules/gas-optimization.md +339 -0
- package/templates/blockchain/.cursorrules/overview.md +130 -0
- package/templates/blockchain/.cursorrules/security.md +318 -0
- package/templates/blockchain/.cursorrules/smart-contracts.md +364 -0
- package/templates/blockchain/.cursorrules/testing.md +415 -0
- package/templates/blockchain/.cursorrules/web3-integration.md +538 -0
- package/templates/blockchain/CLAUDE.md +389 -0
- package/templates/cli-tools/.cursorrules/architecture.md +412 -0
- package/templates/cli-tools/.cursorrules/arguments.md +406 -0
- package/templates/cli-tools/.cursorrules/distribution.md +546 -0
- package/templates/cli-tools/.cursorrules/error-handling.md +455 -0
- package/templates/cli-tools/.cursorrules/overview.md +136 -0
- package/templates/cli-tools/.cursorrules/testing.md +537 -0
- package/templates/cli-tools/.cursorrules/user-experience.md +545 -0
- package/templates/cli-tools/CLAUDE.md +356 -0
- package/templates/data-engineering/.cursorrules/data-modeling.md +367 -0
- package/templates/data-engineering/.cursorrules/data-quality.md +455 -0
- package/templates/data-engineering/.cursorrules/overview.md +85 -0
- package/templates/data-engineering/.cursorrules/performance.md +339 -0
- package/templates/data-engineering/.cursorrules/pipeline-design.md +280 -0
- package/templates/data-engineering/.cursorrules/security.md +460 -0
- package/templates/data-engineering/.cursorrules/testing.md +452 -0
- package/templates/data-engineering/CLAUDE.md +974 -0
- package/templates/devops-sre/.cursorrules/capacity-planning.md +653 -0
- package/templates/devops-sre/.cursorrules/change-management.md +584 -0
- package/templates/devops-sre/.cursorrules/chaos-engineering.md +651 -0
- package/templates/devops-sre/.cursorrules/disaster-recovery.md +641 -0
- package/templates/devops-sre/.cursorrules/incident-management.md +565 -0
- package/templates/devops-sre/.cursorrules/observability.md +714 -0
- package/templates/devops-sre/.cursorrules/overview.md +230 -0
- package/templates/devops-sre/.cursorrules/postmortems.md +588 -0
- package/templates/devops-sre/.cursorrules/runbooks.md +760 -0
- package/templates/devops-sre/.cursorrules/slo-sli.md +617 -0
- package/templates/devops-sre/.cursorrules/toil-reduction.md +567 -0
- package/templates/devops-sre/CLAUDE.md +1007 -0
- package/templates/documentation/.cursorrules/adr.md +277 -0
- package/templates/documentation/.cursorrules/api-documentation.md +411 -0
- package/templates/documentation/.cursorrules/code-comments.md +253 -0
- package/templates/documentation/.cursorrules/maintenance.md +260 -0
- package/templates/documentation/.cursorrules/overview.md +82 -0
- package/templates/documentation/.cursorrules/readme-standards.md +306 -0
- package/templates/documentation/CLAUDE.md +120 -0
- package/templates/fullstack/.cursorrules/api-contracts.md +331 -0
- package/templates/fullstack/.cursorrules/architecture.md +298 -0
- package/templates/fullstack/.cursorrules/overview.md +109 -0
- package/templates/fullstack/.cursorrules/shared-types.md +348 -0
- package/templates/fullstack/.cursorrules/testing.md +386 -0
- package/templates/fullstack/CLAUDE.md +349 -0
- package/templates/ml-ai/.cursorrules/data-engineering.md +483 -0
- package/templates/ml-ai/.cursorrules/deployment.md +601 -0
- package/templates/ml-ai/.cursorrules/model-development.md +538 -0
- package/templates/ml-ai/.cursorrules/monitoring.md +658 -0
- package/templates/ml-ai/.cursorrules/overview.md +131 -0
- package/templates/ml-ai/.cursorrules/security.md +637 -0
- package/templates/ml-ai/.cursorrules/testing.md +678 -0
- package/templates/ml-ai/CLAUDE.md +1136 -0
- package/templates/mobile/.cursorrules/navigation.md +246 -0
- package/templates/mobile/.cursorrules/offline-first.md +302 -0
- package/templates/mobile/.cursorrules/overview.md +71 -0
- package/templates/mobile/.cursorrules/performance.md +345 -0
- package/templates/mobile/.cursorrules/testing.md +339 -0
- package/templates/mobile/CLAUDE.md +233 -0
- package/templates/platform-engineering/.cursorrules/ci-cd.md +778 -0
- package/templates/platform-engineering/.cursorrules/developer-experience.md +632 -0
- package/templates/platform-engineering/.cursorrules/infrastructure-as-code.md +600 -0
- package/templates/platform-engineering/.cursorrules/kubernetes.md +710 -0
- package/templates/platform-engineering/.cursorrules/observability.md +747 -0
- package/templates/platform-engineering/.cursorrules/overview.md +215 -0
- package/templates/platform-engineering/.cursorrules/security.md +855 -0
- package/templates/platform-engineering/.cursorrules/testing.md +878 -0
- package/templates/platform-engineering/CLAUDE.md +850 -0
- package/templates/utility-agent/.cursorrules/action-control.md +284 -0
- package/templates/utility-agent/.cursorrules/context-management.md +186 -0
- package/templates/utility-agent/.cursorrules/hallucination-prevention.md +253 -0
- package/templates/utility-agent/.cursorrules/overview.md +78 -0
- package/templates/utility-agent/.cursorrules/token-optimization.md +369 -0
- package/templates/utility-agent/CLAUDE.md +513 -0
- package/templates/web-backend/.cursorrules/api-design.md +255 -0
- package/templates/web-backend/.cursorrules/authentication.md +309 -0
- package/templates/web-backend/.cursorrules/database-patterns.md +298 -0
- package/templates/web-backend/.cursorrules/error-handling.md +366 -0
- package/templates/web-backend/.cursorrules/overview.md +69 -0
- package/templates/web-backend/.cursorrules/security.md +358 -0
- package/templates/web-backend/.cursorrules/testing.md +395 -0
- package/templates/web-backend/CLAUDE.md +366 -0
- package/templates/web-frontend/.cursorrules/accessibility.md +296 -0
- package/templates/web-frontend/.cursorrules/component-patterns.md +204 -0
- package/templates/web-frontend/.cursorrules/overview.md +72 -0
- package/templates/web-frontend/.cursorrules/performance.md +325 -0
- package/templates/web-frontend/.cursorrules/state-management.md +227 -0
- package/templates/web-frontend/.cursorrules/styling.md +271 -0
- package/templates/web-frontend/.cursorrules/testing.md +311 -0
- package/templates/web-frontend/CLAUDE.md +399 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# Fullstack Testing
|
|
2
|
+
|
|
3
|
+
Testing strategies for full-stack applications.
|
|
4
|
+
|
|
5
|
+
## Testing Layers
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────────────┐
|
|
9
|
+
│ E2E Tests │
|
|
10
|
+
│ (Full user journeys, critical paths) │
|
|
11
|
+
├─────────────────────────────────────────────────────┤
|
|
12
|
+
│ Integration Tests │
|
|
13
|
+
│ (API tests, component + API, DB tests) │
|
|
14
|
+
├─────────────────────────────────────────────────────┤
|
|
15
|
+
│ Unit Tests │
|
|
16
|
+
│ (Pure functions, isolated components, services) │
|
|
17
|
+
└─────────────────────────────────────────────────────┘
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## End-to-End Tests
|
|
21
|
+
|
|
22
|
+
Test complete user flows across the full stack.
|
|
23
|
+
|
|
24
|
+
### Critical Path Testing
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// e2e/auth.spec.ts
|
|
28
|
+
import { test, expect } from '@playwright/test';
|
|
29
|
+
|
|
30
|
+
test.describe('Authentication Flow', () => {
|
|
31
|
+
test('user can sign up and access dashboard', async ({ page }) => {
|
|
32
|
+
// Navigate to signup
|
|
33
|
+
await page.goto('/signup');
|
|
34
|
+
|
|
35
|
+
// Fill form
|
|
36
|
+
await page.fill('[name="email"]', 'test@example.com');
|
|
37
|
+
await page.fill('[name="password"]', 'SecurePass123!');
|
|
38
|
+
await page.fill('[name="name"]', 'Test User');
|
|
39
|
+
|
|
40
|
+
// Submit
|
|
41
|
+
await page.click('button[type="submit"]');
|
|
42
|
+
|
|
43
|
+
// Should redirect to dashboard
|
|
44
|
+
await expect(page).toHaveURL('/dashboard');
|
|
45
|
+
await expect(page.locator('text=Welcome, Test User')).toBeVisible();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('user can log in with existing account', async ({ page }) => {
|
|
49
|
+
await page.goto('/login');
|
|
50
|
+
await page.fill('[name="email"]', 'existing@example.com');
|
|
51
|
+
await page.fill('[name="password"]', 'password123');
|
|
52
|
+
await page.click('button[type="submit"]');
|
|
53
|
+
|
|
54
|
+
await expect(page).toHaveURL('/dashboard');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('shows error for invalid credentials', async ({ page }) => {
|
|
58
|
+
await page.goto('/login');
|
|
59
|
+
await page.fill('[name="email"]', 'wrong@example.com');
|
|
60
|
+
await page.fill('[name="password"]', 'wrongpassword');
|
|
61
|
+
await page.click('button[type="submit"]');
|
|
62
|
+
|
|
63
|
+
await expect(page.locator('text=Invalid credentials')).toBeVisible();
|
|
64
|
+
await expect(page).toHaveURL('/login');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Data Flow Testing
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
// e2e/crud.spec.ts
|
|
73
|
+
test.describe('Post Management', () => {
|
|
74
|
+
test.beforeEach(async ({ page }) => {
|
|
75
|
+
// Login before each test
|
|
76
|
+
await loginAs(page, 'testuser@example.com');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('user can create, edit, and delete a post', async ({ page }) => {
|
|
80
|
+
// Create
|
|
81
|
+
await page.goto('/posts/new');
|
|
82
|
+
await page.fill('[name="title"]', 'Test Post');
|
|
83
|
+
await page.fill('[name="content"]', 'Test content');
|
|
84
|
+
await page.click('button[type="submit"]');
|
|
85
|
+
|
|
86
|
+
// Verify creation
|
|
87
|
+
await expect(page).toHaveURL(/\/posts\/[\w-]+/);
|
|
88
|
+
await expect(page.locator('h1')).toHaveText('Test Post');
|
|
89
|
+
|
|
90
|
+
// Edit
|
|
91
|
+
await page.click('text=Edit');
|
|
92
|
+
await page.fill('[name="title"]', 'Updated Post');
|
|
93
|
+
await page.click('button[type="submit"]');
|
|
94
|
+
|
|
95
|
+
await expect(page.locator('h1')).toHaveText('Updated Post');
|
|
96
|
+
|
|
97
|
+
// Delete
|
|
98
|
+
await page.click('text=Delete');
|
|
99
|
+
await page.click('text=Confirm');
|
|
100
|
+
|
|
101
|
+
await expect(page).toHaveURL('/posts');
|
|
102
|
+
await expect(page.locator('text=Updated Post')).not.toBeVisible();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### API Integration in E2E
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// e2e/helpers.ts
|
|
111
|
+
import { test as base } from '@playwright/test';
|
|
112
|
+
|
|
113
|
+
// Extend test with API context
|
|
114
|
+
export const test = base.extend<{ api: APIRequestContext }>({
|
|
115
|
+
api: async ({ playwright }, use) => {
|
|
116
|
+
const api = await playwright.request.newContext({
|
|
117
|
+
baseURL: process.env.API_URL,
|
|
118
|
+
});
|
|
119
|
+
await use(api);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Use in tests
|
|
124
|
+
test('displays data from API', async ({ page, api }) => {
|
|
125
|
+
// Seed data via API
|
|
126
|
+
await api.post('/api/posts', {
|
|
127
|
+
data: { title: 'Seeded Post', content: 'Content' },
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Verify in UI
|
|
131
|
+
await page.goto('/posts');
|
|
132
|
+
await expect(page.locator('text=Seeded Post')).toBeVisible();
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Integration Tests
|
|
137
|
+
|
|
138
|
+
### Full Stack Integration
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// tests/integration/users.test.ts
|
|
142
|
+
import { createTestApp } from '../helpers/app';
|
|
143
|
+
import { createTestDb, cleanDb } from '../helpers/db';
|
|
144
|
+
|
|
145
|
+
describe('User Management Integration', () => {
|
|
146
|
+
let app: Express;
|
|
147
|
+
let db: TestDatabase;
|
|
148
|
+
|
|
149
|
+
beforeAll(async () => {
|
|
150
|
+
db = await createTestDb();
|
|
151
|
+
app = createTestApp(db);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
afterAll(async () => {
|
|
155
|
+
await db.close();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
beforeEach(async () => {
|
|
159
|
+
await cleanDb(db);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('creates user and returns in list', async () => {
|
|
163
|
+
// Create via API
|
|
164
|
+
const createRes = await request(app)
|
|
165
|
+
.post('/api/users')
|
|
166
|
+
.send({ email: 'new@example.com', name: 'New User' });
|
|
167
|
+
|
|
168
|
+
expect(createRes.status).toBe(201);
|
|
169
|
+
|
|
170
|
+
// Verify via API
|
|
171
|
+
const listRes = await request(app).get('/api/users');
|
|
172
|
+
expect(listRes.body.data).toContainEqual(
|
|
173
|
+
expect.objectContaining({ email: 'new@example.com' })
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// Verify in database
|
|
177
|
+
const dbUser = await db.user.findUnique({
|
|
178
|
+
where: { email: 'new@example.com' },
|
|
179
|
+
});
|
|
180
|
+
expect(dbUser).not.toBeNull();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Component + API Integration
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
// tests/integration/UserProfile.test.tsx
|
|
189
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
190
|
+
import { rest } from 'msw';
|
|
191
|
+
import { setupServer } from 'msw/node';
|
|
192
|
+
import { UserProfile } from '@/components/UserProfile';
|
|
193
|
+
|
|
194
|
+
const server = setupServer(
|
|
195
|
+
rest.get('/api/users/:id', (req, res, ctx) => {
|
|
196
|
+
return res(ctx.json({
|
|
197
|
+
data: { id: req.params.id, name: 'John Doe', email: 'john@example.com' },
|
|
198
|
+
}));
|
|
199
|
+
})
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
beforeAll(() => server.listen());
|
|
203
|
+
afterEach(() => server.resetHandlers());
|
|
204
|
+
afterAll(() => server.close());
|
|
205
|
+
|
|
206
|
+
describe('UserProfile', () => {
|
|
207
|
+
it('fetches and displays user data', async () => {
|
|
208
|
+
render(<UserProfile userId="123" />);
|
|
209
|
+
|
|
210
|
+
// Loading state
|
|
211
|
+
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
|
212
|
+
|
|
213
|
+
// Data loaded
|
|
214
|
+
await waitFor(() => {
|
|
215
|
+
expect(screen.getByText('John Doe')).toBeInTheDocument();
|
|
216
|
+
expect(screen.getByText('john@example.com')).toBeInTheDocument();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('handles API error', async () => {
|
|
221
|
+
server.use(
|
|
222
|
+
rest.get('/api/users/:id', (req, res, ctx) => {
|
|
223
|
+
return res(ctx.status(500));
|
|
224
|
+
})
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
render(<UserProfile userId="123" />);
|
|
228
|
+
|
|
229
|
+
await waitFor(() => {
|
|
230
|
+
expect(screen.getByText(/error/i)).toBeInTheDocument();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Contract Testing
|
|
237
|
+
|
|
238
|
+
Verify frontend and backend agree on API shape.
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
// tests/contract/user.contract.test.ts
|
|
242
|
+
import { CreateUserSchema, UserResponseSchema } from '@myapp/shared';
|
|
243
|
+
|
|
244
|
+
describe('User API Contract', () => {
|
|
245
|
+
it('POST /users accepts valid CreateUserInput', async () => {
|
|
246
|
+
const input = {
|
|
247
|
+
email: 'test@example.com',
|
|
248
|
+
name: 'Test User',
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Validate against shared schema
|
|
252
|
+
const parseResult = CreateUserSchema.safeParse(input);
|
|
253
|
+
expect(parseResult.success).toBe(true);
|
|
254
|
+
|
|
255
|
+
// Send to actual API
|
|
256
|
+
const response = await request(app)
|
|
257
|
+
.post('/api/users')
|
|
258
|
+
.send(input);
|
|
259
|
+
|
|
260
|
+
expect(response.status).toBe(201);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('GET /users returns valid UserResponse[]', async () => {
|
|
264
|
+
const response = await request(app).get('/api/users');
|
|
265
|
+
|
|
266
|
+
expect(response.status).toBe(200);
|
|
267
|
+
|
|
268
|
+
// Validate response against shared schema
|
|
269
|
+
for (const user of response.body.data) {
|
|
270
|
+
const parseResult = UserResponseSchema.safeParse(user);
|
|
271
|
+
expect(parseResult.success).toBe(true);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Test Data Management
|
|
278
|
+
|
|
279
|
+
### Factories
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
// tests/factories/user.ts
|
|
283
|
+
import { faker } from '@faker-js/faker';
|
|
284
|
+
import { CreateUserInput, User } from '@myapp/shared';
|
|
285
|
+
|
|
286
|
+
export const createUserInput = (overrides?: Partial<CreateUserInput>): CreateUserInput => ({
|
|
287
|
+
email: faker.internet.email(),
|
|
288
|
+
name: faker.person.fullName(),
|
|
289
|
+
...overrides,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
export const createUser = (overrides?: Partial<User>): User => ({
|
|
293
|
+
id: faker.string.uuid(),
|
|
294
|
+
email: faker.internet.email(),
|
|
295
|
+
name: faker.person.fullName(),
|
|
296
|
+
role: 'user',
|
|
297
|
+
createdAt: new Date(),
|
|
298
|
+
updatedAt: new Date(),
|
|
299
|
+
...overrides,
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Database Seeding
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
// tests/helpers/seed.ts
|
|
307
|
+
export const seedDatabase = async (db: Database) => {
|
|
308
|
+
const adminUser = await db.user.create({
|
|
309
|
+
data: createUserInput({ email: 'admin@test.com', role: 'admin' }),
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const regularUser = await db.user.create({
|
|
313
|
+
data: createUserInput({ email: 'user@test.com' }),
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
const posts = await Promise.all([
|
|
317
|
+
db.post.create({ data: { title: 'Post 1', authorId: regularUser.id } }),
|
|
318
|
+
db.post.create({ data: { title: 'Post 2', authorId: regularUser.id } }),
|
|
319
|
+
]);
|
|
320
|
+
|
|
321
|
+
return { adminUser, regularUser, posts };
|
|
322
|
+
};
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Test Organization
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
project/
|
|
329
|
+
├── packages/
|
|
330
|
+
│ ├── web/
|
|
331
|
+
│ │ └── __tests__/ # Frontend unit/integration tests
|
|
332
|
+
│ └── api/
|
|
333
|
+
│ └── __tests__/ # Backend unit/integration tests
|
|
334
|
+
├── tests/
|
|
335
|
+
│ ├── e2e/ # End-to-end tests
|
|
336
|
+
│ │ ├── auth.spec.ts
|
|
337
|
+
│ │ └── crud.spec.ts
|
|
338
|
+
│ ├── integration/ # Cross-package integration tests
|
|
339
|
+
│ │ └── users.test.ts
|
|
340
|
+
│ ├── contract/ # API contract tests
|
|
341
|
+
│ │ └── user.contract.test.ts
|
|
342
|
+
│ ├── factories/ # Test data factories
|
|
343
|
+
│ └── helpers/ # Test utilities
|
|
344
|
+
└── playwright.config.ts
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Best Practices
|
|
348
|
+
|
|
349
|
+
### 1. Test at the Right Level
|
|
350
|
+
|
|
351
|
+
- **Unit tests**: Pure functions, isolated logic
|
|
352
|
+
- **Integration tests**: Components with API, database operations
|
|
353
|
+
- **E2E tests**: Critical user journeys only
|
|
354
|
+
|
|
355
|
+
### 2. Keep E2E Tests Fast
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
// Use API to set up state instead of UI
|
|
359
|
+
test.beforeEach(async ({ api }) => {
|
|
360
|
+
await api.post('/api/test/seed', { scenario: 'userWithPosts' });
|
|
361
|
+
});
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 3. Test Error Scenarios
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
test('shows error when API fails', async ({ page }) => {
|
|
368
|
+
// Mock API failure
|
|
369
|
+
await page.route('/api/users', (route) =>
|
|
370
|
+
route.fulfill({ status: 500 })
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
await page.goto('/users');
|
|
374
|
+
await expect(page.locator('text=Failed to load')).toBeVisible();
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### 4. Use Realistic Data
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
// Use faker for realistic test data
|
|
382
|
+
const user = {
|
|
383
|
+
email: faker.internet.email(),
|
|
384
|
+
name: faker.person.fullName(),
|
|
385
|
+
};
|
|
386
|
+
```
|