@mmerterden/multi-agent-pipeline 20.36.0 → 20.37.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/CHANGELOG.md +37 -0
- package/docs/facts.json +2 -2
- package/docs/features.md +1 -1
- package/docs/recovery-guide.md +1 -1
- package/install/catalog-history.json +1 -1
- package/manifest.json +225 -148
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/SKILL.md +33 -154
- package/pipeline/commands/multi-agent/analysis/SKILL.md +14 -68
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +5 -8
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +38 -109
- package/pipeline/commands/multi-agent/channels/SKILL.md +38 -167
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +37 -144
- package/pipeline/commands/multi-agent/design-check/SKILL.md +27 -131
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +22 -48
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +37 -180
- package/pipeline/commands/multi-agent/jira/SKILL.md +16 -46
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +12 -7
- package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -184
- package/pipeline/commands/multi-agent/resume/SKILL.md +30 -136
- package/pipeline/commands/multi-agent/review/SKILL.md +24 -199
- package/pipeline/commands/multi-agent/setup/SKILL.md +25 -181
- package/pipeline/commands/multi-agent/stack/SKILL.md +9 -53
- package/pipeline/commands/multi-agent/status/SKILL.md +13 -2
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +27 -226
- package/pipeline/commands/multi-agent/sync/SKILL.md +35 -164
- package/pipeline/commands/multi-agent/test-gen/SKILL.md +2 -4
- package/pipeline/commands/multi-agent/update/SKILL.md +18 -60
- package/pipeline/contract/CHANGELOG.md +9 -0
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/fixtures/runs-awaiting-question.json +1 -1
- package/pipeline/contract/fixtures/runs-empty.json +1 -1
- package/pipeline/contract/fixtures/runs-failed.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
- package/pipeline/contract/fixtures/runs-running.json +1 -1
- package/pipeline/contract/manifest.json +5 -5
- package/pipeline/contract/types/index.d.ts +3 -2
- package/pipeline/multi-agent-refs/audit-guide.md +20 -0
- package/pipeline/multi-agent-refs/autopilot-on/details.md +148 -0
- package/pipeline/multi-agent-refs/channels/generator.md +14 -0
- package/pipeline/multi-agent-refs/channels/preview.md +13 -0
- package/pipeline/multi-agent-refs/channels/reference.md +46 -0
- package/pipeline/multi-agent-refs/complaint-analysis/phases.md +159 -0
- package/pipeline/multi-agent-refs/design-check/export.md +1 -0
- package/pipeline/multi-agent-refs/design-check/init-inventory.md +1 -1
- package/pipeline/multi-agent-refs/design-check/scope-and-ledger.md +64 -0
- package/pipeline/multi-agent-refs/features/analysis-command-notes.md +53 -0
- package/pipeline/multi-agent-refs/features/commit-multi-repo.md +63 -0
- package/pipeline/multi-agent-refs/features/dev-multi-repo.md +51 -0
- package/pipeline/multi-agent-refs/features/test-gap.md +40 -0
- package/pipeline/multi-agent-refs/features/worktree-finalize.md +1 -1
- package/pipeline/multi-agent-refs/garbage-collect/phases.md +60 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/plan.md +71 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/resolve-and-scan.md +135 -0
- package/pipeline/multi-agent-refs/jira/picker-detail.md +71 -0
- package/pipeline/multi-agent-refs/orchestrator/copilot-pipeline.md +88 -0
- package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +96 -1
- package/pipeline/multi-agent-refs/orchestrator/phase-3-user-test.md +12 -9
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -4
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +8 -47
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +21 -65
- package/pipeline/multi-agent-refs/phases/phase-4-commit.md +9 -59
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +0 -31
- package/pipeline/multi-agent-refs/refactor/sweep.md +190 -0
- package/pipeline/multi-agent-refs/resume/details.md +115 -2
- package/pipeline/multi-agent-refs/review/inputs.md +33 -1
- package/pipeline/multi-agent-refs/review/module-guides.md +9 -1
- package/pipeline/multi-agent-refs/review/panel.md +75 -0
- package/pipeline/multi-agent-refs/review/valven.md +5 -1
- package/pipeline/multi-agent-refs/rules.md +1 -1
- package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +4 -0
- package/pipeline/multi-agent-refs/setup/token-save-flow.md +13 -0
- package/pipeline/multi-agent-refs/stack/background.md +72 -0
- package/pipeline/multi-agent-refs/store-ready/gates.md +139 -0
- package/pipeline/multi-agent-refs/store-ready/report.md +40 -2
- package/pipeline/multi-agent-refs/sync/office.md +10 -0
- package/pipeline/multi-agent-refs/sync/overview.md +61 -0
- package/pipeline/multi-agent-refs/sync/repo.md +91 -0
- package/pipeline/multi-agent-refs/update/details.md +83 -0
- package/pipeline/office/index.html +6 -3
- package/pipeline/office/toolkit.json +1 -1
- package/pipeline/rules/figma-pipeline.md +6 -3
- package/pipeline/schemas/prefs.schema.json +1 -1
- package/pipeline/schemas/runs-index.schema.json +2 -1
- package/pipeline/scripts/_memory-policy.mjs +27 -5
- package/pipeline/scripts/checkpoint-fold.sh +59 -0
- package/pipeline/scripts/contract-server.mjs +1 -1
- package/pipeline/scripts/gate-ledger.mjs +1 -1
- package/pipeline/scripts/office-control.mjs +41 -15
- package/pipeline/scripts/offload-ref.sh +1 -1
- package/pipeline/scripts/rework-rulings.mjs +5 -3
- package/pipeline/scripts/runs-index.mjs +34 -7
- package/pipeline/scripts/scope-check-gate.mjs +15 -2
- package/pipeline/scripts/status-overview.mjs +401 -0
- package/pipeline/scripts/user-test-handoff.sh +203 -0
- package/pipeline/scripts/validate-planning.mjs +5 -3
- package/pipeline/scripts/validate-triage.mjs +2 -1
- package/pipeline/scripts/write-state.mjs +13 -4
- package/pipeline/skills/.skill-manifest.json +64 -64
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +7 -70
- package/pipeline/skills/shared/core/apple-archive-compliance/references/examples.md +67 -0
- package/pipeline/skills/shared/core/apple-archive-compliance/references/version-history.md +8 -0
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +10 -72
- package/pipeline/skills/shared/core/google-play-compliance/references/examples.md +69 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/notes.md +7 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/version-history.md +5 -0
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +39 -262
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +35 -73
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +37 -337
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +27 -122
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +8 -340
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +37 -180
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +12 -7
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -184
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +14 -82
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +25 -277
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +13 -2
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +73 -217
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +11 -162
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-code.md +257 -0
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +7 -58
- package/pipeline/skills/shared/external/android-architecture/references/code-examples.md +68 -0
- package/pipeline/skills/shared/external/app-intents/SKILL.md +3 -45
- package/pipeline/skills/shared/external/app-intents/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +5 -81
- package/pipeline/skills/shared/external/app-store-optimization/references/engagement-and-localization.md +96 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +6 -112
- package/pipeline/skills/shared/external/app-store-review/references/monetization-and-submission.md +138 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +10 -117
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models-code.md +186 -0
- package/pipeline/skills/shared/external/authentication/SKILL.md +3 -50
- package/pipeline/skills/shared/external/authentication/references/code-examples.md +59 -0
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +3 -84
- package/pipeline/skills/shared/external/callkit-voip/references/code-examples.md +93 -0
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +7 -263
- package/pipeline/skills/shared/external/ci-cd-pipelines/references/pipeline-examples.md +281 -0
- package/pipeline/skills/shared/external/clean-code/SKILL.md +8 -41
- package/pipeline/skills/shared/external/clean-code/references/code-examples.md +56 -0
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +8 -146
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-code.md +214 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +6 -80
- package/pipeline/skills/shared/external/compose-components/references/code-examples.md +89 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +5 -64
- package/pipeline/skills/shared/external/compose-navigation/references/code-examples.md +70 -0
- package/pipeline/skills/shared/external/core-data/SKILL.md +9 -157
- package/pipeline/skills/shared/external/core-data/references/core-data-code.md +221 -0
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +3 -89
- package/pipeline/skills/shared/external/core-nfc/references/code-examples.md +98 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +17 -133
- package/pipeline/skills/shared/external/coreml/references/coreml-code.md +208 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +8 -131
- package/pipeline/skills/shared/external/css-modern/references/code-examples.md +142 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +6 -103
- package/pipeline/skills/shared/external/database-patterns/references/orm-examples.md +112 -0
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +7 -48
- package/pipeline/skills/shared/external/html-semantic/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +6 -19
- package/pipeline/skills/shared/external/humanizer/references/voice.md +24 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +13 -170
- package/pipeline/skills/shared/external/ios-accessibility/references/assistive-tech-review.md +224 -0
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +2 -24
- package/pipeline/skills/shared/external/ios-localization/references/code-examples.md +30 -0
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +3 -58
- package/pipeline/skills/shared/external/ios-networking/references/code-examples.md +67 -0
- package/pipeline/skills/shared/external/ios-security/SKILL.md +4 -64
- package/pipeline/skills/shared/external/ios-security/references/code-examples.md +74 -0
- package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -90
- package/pipeline/skills/shared/external/live-activities/references/code-examples.md +108 -0
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -23
- package/pipeline/skills/shared/external/localization-reuse-map/references/project-settings.md +22 -0
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +5 -95
- package/pipeline/skills/shared/external/mapkit-location/references/code-examples.md +117 -0
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +7 -106
- package/pipeline/skills/shared/external/nextjs-app-router/references/app-router-examples.md +124 -0
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -31
- package/pipeline/skills/shared/external/permissionkit/references/code-examples.md +37 -0
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +6 -74
- package/pipeline/skills/shared/external/play-store-review/references/signing-and-policies.md +83 -0
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -125
- package/pipeline/skills/shared/external/push-notifications/references/notification-code.md +176 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +7 -158
- package/pipeline/skills/shared/external/python-patterns/references/python-examples.md +176 -0
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +7 -157
- package/pipeline/skills/shared/external/rest-api-design/references/api-examples.md +175 -0
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +11 -230
- package/pipeline/skills/shared/external/swift-architecture/references/pattern-examples.md +295 -0
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +17 -149
- package/pipeline/skills/shared/external/swift-codable/references/codable-recipes.md +207 -0
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +12 -99
- package/pipeline/skills/shared/external/swift-concurrency/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-additions.md +18 -0
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +9 -78
- package/pipeline/skills/shared/external/swift-formatstyle/references/formatstyle-examples.md +113 -0
- package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -56
- package/pipeline/skills/shared/external/swift-language/references/code-examples.md +65 -0
- package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +1 -0
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +6 -122
- package/pipeline/skills/shared/external/swiftdata/references/code-examples.md +140 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +12 -139
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/interop-code.md +198 -0
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +9 -81
- package/pipeline/skills/shared/external/tailwind-css/references/code-examples.md +91 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +7 -207
- package/pipeline/skills/shared/external/testing-backend/references/testing-examples.md +225 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +8 -89
- package/pipeline/skills/shared/external/typescript-patterns/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -20
- package/pipeline/skills/shared/external/vision-framework/references/code-examples.md +26 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +7 -169
- package/pipeline/skills/shared/external/vue-composition/references/vue-examples.md +187 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +7 -174
- package/pipeline/skills/shared/external/web-accessibility/references/a11y-examples.md +192 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +8 -66
- package/pipeline/skills/shared/external/web-performance/references/code-examples.md +77 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +11 -186
- package/pipeline/skills/shared/external/web-testing/references/testing-examples.md +213 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/format-and-output.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/publish-and-snapshot.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/sources-and-recipes.md +0 -0
|
@@ -209,150 +209,11 @@ describe('useCounter', () => {
|
|
|
209
209
|
|
|
210
210
|
## MSW (Mock Service Worker)
|
|
211
211
|
|
|
212
|
-
|
|
213
|
-
// src/mocks/handlers.ts
|
|
214
|
-
import { http, HttpResponse } from 'msw';
|
|
215
|
-
|
|
216
|
-
export const handlers = [
|
|
217
|
-
http.get('/api/users', () => {
|
|
218
|
-
return HttpResponse.json([
|
|
219
|
-
{ id: '1', name: 'Alice', email: 'alice@example.com' },
|
|
220
|
-
{ id: '2', name: 'Bob', email: 'bob@example.com' },
|
|
221
|
-
]);
|
|
222
|
-
}),
|
|
223
|
-
|
|
224
|
-
http.get('/api/users/:id', ({ params }) => {
|
|
225
|
-
const { id } = params;
|
|
226
|
-
if (id === '999') {
|
|
227
|
-
return new HttpResponse(null, { status: 404 });
|
|
228
|
-
}
|
|
229
|
-
return HttpResponse.json({ id, name: 'Alice', email: 'alice@example.com' });
|
|
230
|
-
}),
|
|
231
|
-
|
|
232
|
-
http.post('/api/users', async ({ request }) => {
|
|
233
|
-
const body = await request.json();
|
|
234
|
-
return HttpResponse.json({ id: '3', ...body }, { status: 201 });
|
|
235
|
-
}),
|
|
236
|
-
|
|
237
|
-
http.delete('/api/users/:id', () => {
|
|
238
|
-
return new HttpResponse(null, { status: 204 });
|
|
239
|
-
}),
|
|
240
|
-
];
|
|
241
|
-
|
|
242
|
-
// src/mocks/server.ts
|
|
243
|
-
import { setupServer } from 'msw/node';
|
|
244
|
-
import { handlers } from './handlers';
|
|
245
|
-
|
|
246
|
-
export const server = setupServer(...handlers);
|
|
247
|
-
|
|
248
|
-
// src/test/setup.ts
|
|
249
|
-
import { server } from '../mocks/server';
|
|
250
|
-
|
|
251
|
-
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
|
|
252
|
-
afterEach(() => server.resetHandlers());
|
|
253
|
-
afterAll(() => server.close());
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
```tsx
|
|
257
|
-
// Using MSW in tests with per-test overrides
|
|
258
|
-
import { http, HttpResponse } from 'msw';
|
|
259
|
-
import { server } from '../mocks/server';
|
|
260
|
-
|
|
261
|
-
describe('UserList', () => {
|
|
262
|
-
it('shows error state on API failure', async () => {
|
|
263
|
-
// Override handler for this test only
|
|
264
|
-
server.use(
|
|
265
|
-
http.get('/api/users', () => {
|
|
266
|
-
return new HttpResponse(null, { status: 500 });
|
|
267
|
-
})
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
render(<UserList />);
|
|
271
|
-
expect(await screen.findByText(/failed to load/i)).toBeInTheDocument();
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
```
|
|
212
|
+
Code for this section: [Web testing examples](references/testing-examples.md).
|
|
275
213
|
|
|
276
214
|
## Playwright (E2E Testing)
|
|
277
215
|
|
|
278
|
-
|
|
279
|
-
// playwright.config.ts
|
|
280
|
-
import { defineConfig, devices } from '@playwright/test';
|
|
281
|
-
|
|
282
|
-
export default defineConfig({
|
|
283
|
-
testDir: './e2e',
|
|
284
|
-
fullyParallel: true,
|
|
285
|
-
forbidOnly: !!process.env.CI,
|
|
286
|
-
retries: process.env.CI ? 2 : 0,
|
|
287
|
-
workers: process.env.CI ? 1 : undefined,
|
|
288
|
-
reporter: 'html',
|
|
289
|
-
use: {
|
|
290
|
-
baseURL: 'http://localhost:3000',
|
|
291
|
-
trace: 'on-first-retry',
|
|
292
|
-
screenshot: 'only-on-failure',
|
|
293
|
-
},
|
|
294
|
-
projects: [
|
|
295
|
-
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
296
|
-
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
297
|
-
{ name: 'mobile', use: { ...devices['iPhone 14'] } },
|
|
298
|
-
],
|
|
299
|
-
webServer: {
|
|
300
|
-
command: 'npm run dev',
|
|
301
|
-
url: 'http://localhost:3000',
|
|
302
|
-
reuseExistingServer: !process.env.CI,
|
|
303
|
-
},
|
|
304
|
-
});
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
```typescript
|
|
308
|
-
// e2e/auth.spec.ts
|
|
309
|
-
import { test, expect } from '@playwright/test';
|
|
310
|
-
|
|
311
|
-
test.describe('Authentication', () => {
|
|
312
|
-
test('successful login redirects to dashboard', async ({ page }) => {
|
|
313
|
-
await page.goto('/login');
|
|
314
|
-
|
|
315
|
-
await page.getByLabel('Email').fill('user@example.com');
|
|
316
|
-
await page.getByLabel('Password').fill('password123');
|
|
317
|
-
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
318
|
-
|
|
319
|
-
await expect(page).toHaveURL('/dashboard');
|
|
320
|
-
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
test('shows error for invalid credentials', async ({ page }) => {
|
|
324
|
-
await page.goto('/login');
|
|
325
|
-
|
|
326
|
-
await page.getByLabel('Email').fill('wrong@example.com');
|
|
327
|
-
await page.getByLabel('Password').fill('wrongpass');
|
|
328
|
-
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
329
|
-
|
|
330
|
-
await expect(page.getByText('Invalid credentials')).toBeVisible();
|
|
331
|
-
await expect(page).toHaveURL('/login');
|
|
332
|
-
});
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
// e2e/checkout.spec.ts
|
|
336
|
-
test('completes checkout flow', async ({ page }) => {
|
|
337
|
-
await page.goto('/products');
|
|
338
|
-
|
|
339
|
-
// Add item to cart
|
|
340
|
-
await page.getByRole('button', { name: 'Add to cart' }).first().click();
|
|
341
|
-
await expect(page.getByTestId('cart-count')).toHaveText('1');
|
|
342
|
-
|
|
343
|
-
// Navigate to checkout
|
|
344
|
-
await page.getByRole('link', { name: 'Cart' }).click();
|
|
345
|
-
await page.getByRole('button', { name: 'Checkout' }).click();
|
|
346
|
-
|
|
347
|
-
// Fill shipping info
|
|
348
|
-
await page.getByLabel('Address').fill('123 Main St');
|
|
349
|
-
await page.getByLabel('City').fill('New York');
|
|
350
|
-
await page.getByRole('button', { name: 'Place order' }).click();
|
|
351
|
-
|
|
352
|
-
// Verify confirmation
|
|
353
|
-
await expect(page.getByText('Order confirmed')).toBeVisible();
|
|
354
|
-
});
|
|
355
|
-
```
|
|
216
|
+
Code for this section: [Web testing examples](references/testing-examples.md).
|
|
356
217
|
|
|
357
218
|
### Structure, error states and time
|
|
358
219
|
|
|
@@ -360,56 +221,16 @@ test('completes checkout flow', async ({ page }) => {
|
|
|
360
221
|
is a subset by default: list what must be there, in order, and leave out the rest.
|
|
361
222
|
Names take a regex, so localized copy does not break it.
|
|
362
223
|
|
|
363
|
-
|
|
364
|
-
test('orders page structure', async ({ page }) => {
|
|
365
|
-
await page.goto('/orders');
|
|
366
|
-
await expect(page.getByRole('main')).toMatchAriaSnapshot(`
|
|
367
|
-
- heading "Orders" [level=1]
|
|
368
|
-
- list:
|
|
369
|
-
- listitem: /Order #\\d+/
|
|
370
|
-
- button /Save|Kaydet/
|
|
371
|
-
`);
|
|
372
|
-
});
|
|
373
|
-
```
|
|
224
|
+
Error and empty states come from the network layer, not from a seeded backend.
|
|
374
225
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
test('shows the empty state', async ({ page }) => {
|
|
379
|
-
await page.route('**/api/orders*', (route) => route.fulfill({ json: [] }));
|
|
380
|
-
await page.goto('/orders');
|
|
381
|
-
await expect(page.getByText('No orders yet')).toBeVisible();
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
test('shows the retry banner on a server error', async ({ page }) => {
|
|
385
|
-
await page.route('**/api/orders*', (route) => route.fulfill({ status: 500, body: 'boom' }));
|
|
386
|
-
await page.goto('/orders');
|
|
387
|
-
await expect(page.getByRole('button', { name: 'Retry' })).toBeVisible();
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
test('goes offline gracefully', async ({ page, context }) => {
|
|
391
|
-
await page.goto('/orders');
|
|
392
|
-
await context.setOffline(true);
|
|
393
|
-
await page.getByRole('button', { name: 'Refresh' }).click();
|
|
394
|
-
await expect(page.getByText('You are offline')).toBeVisible();
|
|
395
|
-
});
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
Session expiry, countdowns and dates use the clock instead of waiting:
|
|
399
|
-
|
|
400
|
-
```typescript
|
|
401
|
-
test('logs out after 30 idle minutes', async ({ page }) => {
|
|
402
|
-
await page.clock.install({ time: new Date('2026-01-01T09:00:00') });
|
|
403
|
-
await page.goto('/dashboard');
|
|
404
|
-
await page.clock.fastForward('30:00');
|
|
405
|
-
await expect(page).toHaveURL('/login');
|
|
406
|
-
});
|
|
407
|
-
```
|
|
226
|
+
Session expiry, countdowns and dates use the clock instead of waiting.
|
|
408
227
|
|
|
409
228
|
The multi-agent-toolkit MCP server exposes the same capabilities to an agent
|
|
410
229
|
driving a live browser (`web_route`, `web_network_state`, `web_clock`,
|
|
411
230
|
`web_assert_aria`); `web_generate_locator` turns a snapshot ref into the
|
|
412
|
-
`getByRole` / `getByTestId` locator to paste into a spec like the ones
|
|
231
|
+
`getByRole` / `getByTestId` locator to paste into a spec like the ones in the reference.
|
|
232
|
+
|
|
233
|
+
Code for this section: [Web testing examples](references/testing-examples.md).
|
|
413
234
|
|
|
414
235
|
## Do's
|
|
415
236
|
|
|
@@ -443,3 +264,7 @@ driving a live browser (`web_route`, `web_network_state`, `web_clock`,
|
|
|
443
264
|
| Playwright timeout | Slow page load or wrong selector | Increase timeout, use more specific locators |
|
|
444
265
|
| Snapshot test breaks on every change | Testing too much visual detail | Narrow snapshot scope or use targeted assertions |
|
|
445
266
|
| "act() warning" | State update outside act | Wrap state updates in `act()` or use `findBy` queries |
|
|
267
|
+
|
|
268
|
+
## References
|
|
269
|
+
|
|
270
|
+
- [Web testing examples](references/testing-examples.md): open when setting up MSW handlers or writing Playwright tests.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Web testing examples
|
|
2
|
+
|
|
3
|
+
<!-- toc -->
|
|
4
|
+
- [MSW (Mock Service Worker)](#msw-mock-service-worker)
|
|
5
|
+
- [Playwright (E2E Testing)](#playwright-e2e-testing)
|
|
6
|
+
- [Structure, error states and time](#structure-error-states-and-time)
|
|
7
|
+
<!-- /toc -->
|
|
8
|
+
|
|
9
|
+
Full code for MSW handlers and Playwright end-to-end tests, including page objects, error states and time control. SKILL.md keeps the pyramid, the rules and troubleshooting.
|
|
10
|
+
|
|
11
|
+
## MSW (Mock Service Worker)
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// src/mocks/handlers.ts
|
|
15
|
+
import { http, HttpResponse } from 'msw';
|
|
16
|
+
|
|
17
|
+
export const handlers = [
|
|
18
|
+
http.get('/api/users', () => {
|
|
19
|
+
return HttpResponse.json([
|
|
20
|
+
{ id: '1', name: 'Alice', email: 'alice@example.com' },
|
|
21
|
+
{ id: '2', name: 'Bob', email: 'bob@example.com' },
|
|
22
|
+
]);
|
|
23
|
+
}),
|
|
24
|
+
|
|
25
|
+
http.get('/api/users/:id', ({ params }) => {
|
|
26
|
+
const { id } = params;
|
|
27
|
+
if (id === '999') {
|
|
28
|
+
return new HttpResponse(null, { status: 404 });
|
|
29
|
+
}
|
|
30
|
+
return HttpResponse.json({ id, name: 'Alice', email: 'alice@example.com' });
|
|
31
|
+
}),
|
|
32
|
+
|
|
33
|
+
http.post('/api/users', async ({ request }) => {
|
|
34
|
+
const body = await request.json();
|
|
35
|
+
return HttpResponse.json({ id: '3', ...body }, { status: 201 });
|
|
36
|
+
}),
|
|
37
|
+
|
|
38
|
+
http.delete('/api/users/:id', () => {
|
|
39
|
+
return new HttpResponse(null, { status: 204 });
|
|
40
|
+
}),
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// src/mocks/server.ts
|
|
44
|
+
import { setupServer } from 'msw/node';
|
|
45
|
+
import { handlers } from './handlers';
|
|
46
|
+
|
|
47
|
+
export const server = setupServer(...handlers);
|
|
48
|
+
|
|
49
|
+
// src/test/setup.ts
|
|
50
|
+
import { server } from '../mocks/server';
|
|
51
|
+
|
|
52
|
+
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
|
|
53
|
+
afterEach(() => server.resetHandlers());
|
|
54
|
+
afterAll(() => server.close());
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
// Using MSW in tests with per-test overrides
|
|
59
|
+
import { http, HttpResponse } from 'msw';
|
|
60
|
+
import { server } from '../mocks/server';
|
|
61
|
+
|
|
62
|
+
describe('UserList', () => {
|
|
63
|
+
it('shows error state on API failure', async () => {
|
|
64
|
+
// Override handler for this test only
|
|
65
|
+
server.use(
|
|
66
|
+
http.get('/api/users', () => {
|
|
67
|
+
return new HttpResponse(null, { status: 500 });
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
render(<UserList />);
|
|
72
|
+
expect(await screen.findByText(/failed to load/i)).toBeInTheDocument();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Playwright (E2E Testing)
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// playwright.config.ts
|
|
81
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
82
|
+
|
|
83
|
+
export default defineConfig({
|
|
84
|
+
testDir: './e2e',
|
|
85
|
+
fullyParallel: true,
|
|
86
|
+
forbidOnly: !!process.env.CI,
|
|
87
|
+
retries: process.env.CI ? 2 : 0,
|
|
88
|
+
workers: process.env.CI ? 1 : undefined,
|
|
89
|
+
reporter: 'html',
|
|
90
|
+
use: {
|
|
91
|
+
baseURL: 'http://localhost:3000',
|
|
92
|
+
trace: 'on-first-retry',
|
|
93
|
+
screenshot: 'only-on-failure',
|
|
94
|
+
},
|
|
95
|
+
projects: [
|
|
96
|
+
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
97
|
+
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
98
|
+
{ name: 'mobile', use: { ...devices['iPhone 14'] } },
|
|
99
|
+
],
|
|
100
|
+
webServer: {
|
|
101
|
+
command: 'npm run dev',
|
|
102
|
+
url: 'http://localhost:3000',
|
|
103
|
+
reuseExistingServer: !process.env.CI,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// e2e/auth.spec.ts
|
|
110
|
+
import { test, expect } from '@playwright/test';
|
|
111
|
+
|
|
112
|
+
test.describe('Authentication', () => {
|
|
113
|
+
test('successful login redirects to dashboard', async ({ page }) => {
|
|
114
|
+
await page.goto('/login');
|
|
115
|
+
|
|
116
|
+
await page.getByLabel('Email').fill('user@example.com');
|
|
117
|
+
await page.getByLabel('Password').fill('password123');
|
|
118
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
119
|
+
|
|
120
|
+
await expect(page).toHaveURL('/dashboard');
|
|
121
|
+
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('shows error for invalid credentials', async ({ page }) => {
|
|
125
|
+
await page.goto('/login');
|
|
126
|
+
|
|
127
|
+
await page.getByLabel('Email').fill('wrong@example.com');
|
|
128
|
+
await page.getByLabel('Password').fill('wrongpass');
|
|
129
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
130
|
+
|
|
131
|
+
await expect(page.getByText('Invalid credentials')).toBeVisible();
|
|
132
|
+
await expect(page).toHaveURL('/login');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// e2e/checkout.spec.ts
|
|
137
|
+
test('completes checkout flow', async ({ page }) => {
|
|
138
|
+
await page.goto('/products');
|
|
139
|
+
|
|
140
|
+
// Add item to cart
|
|
141
|
+
await page.getByRole('button', { name: 'Add to cart' }).first().click();
|
|
142
|
+
await expect(page.getByTestId('cart-count')).toHaveText('1');
|
|
143
|
+
|
|
144
|
+
// Navigate to checkout
|
|
145
|
+
await page.getByRole('link', { name: 'Cart' }).click();
|
|
146
|
+
await page.getByRole('button', { name: 'Checkout' }).click();
|
|
147
|
+
|
|
148
|
+
// Fill shipping info
|
|
149
|
+
await page.getByLabel('Address').fill('123 Main St');
|
|
150
|
+
await page.getByLabel('City').fill('New York');
|
|
151
|
+
await page.getByRole('button', { name: 'Place order' }).click();
|
|
152
|
+
|
|
153
|
+
// Verify confirmation
|
|
154
|
+
await expect(page.getByText('Order confirmed')).toBeVisible();
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Structure, error states and time
|
|
159
|
+
|
|
160
|
+
`toMatchAriaSnapshot` checks a region's structure in one assertion. The template
|
|
161
|
+
is a subset by default: list what must be there, in order, and leave out the rest.
|
|
162
|
+
Names take a regex, so localized copy does not break it.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
test('orders page structure', async ({ page }) => {
|
|
166
|
+
await page.goto('/orders');
|
|
167
|
+
await expect(page.getByRole('main')).toMatchAriaSnapshot(`
|
|
168
|
+
- heading "Orders" [level=1]
|
|
169
|
+
- list:
|
|
170
|
+
- listitem: /Order #\\d+/
|
|
171
|
+
- button /Save|Kaydet/
|
|
172
|
+
`);
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Error and empty states come from the network layer, not from a seeded backend:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
test('shows the empty state', async ({ page }) => {
|
|
180
|
+
await page.route('**/api/orders*', (route) => route.fulfill({ json: [] }));
|
|
181
|
+
await page.goto('/orders');
|
|
182
|
+
await expect(page.getByText('No orders yet')).toBeVisible();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('shows the retry banner on a server error', async ({ page }) => {
|
|
186
|
+
await page.route('**/api/orders*', (route) => route.fulfill({ status: 500, body: 'boom' }));
|
|
187
|
+
await page.goto('/orders');
|
|
188
|
+
await expect(page.getByRole('button', { name: 'Retry' })).toBeVisible();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('goes offline gracefully', async ({ page, context }) => {
|
|
192
|
+
await page.goto('/orders');
|
|
193
|
+
await context.setOffline(true);
|
|
194
|
+
await page.getByRole('button', { name: 'Refresh' }).click();
|
|
195
|
+
await expect(page.getByText('You are offline')).toBeVisible();
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Session expiry, countdowns and dates use the clock instead of waiting:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
test('logs out after 30 idle minutes', async ({ page }) => {
|
|
203
|
+
await page.clock.install({ time: new Date('2026-01-01T09:00:00') });
|
|
204
|
+
await page.goto('/dashboard');
|
|
205
|
+
await page.clock.fastForward('30:00');
|
|
206
|
+
await expect(page).toHaveURL('/login');
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The multi-agent-toolkit MCP server exposes the same capabilities to an agent
|
|
211
|
+
driving a live browser (`web_route`, `web_network_state`, `web_clock`,
|
|
212
|
+
`web_assert_aria`); `web_generate_locator` turns a snapshot ref into the
|
|
213
|
+
`getByRole` / `getByTestId` locator to paste into a spec like the ones above.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|