@mmerterden/multi-agent-pipeline 20.35.0 → 20.37.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/CHANGELOG.md +26 -0
- package/README.md +6 -5
- package/README.tr.md +6 -5
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/docs/facts.json +4 -4
- package/install/catalog-history.json +1 -1
- package/manifest.json +210 -136
- package/package.json +5 -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/office-pair/SKILL.md +66 -0
- package/pipeline/commands/multi-agent/office-status/SKILL.md +1 -1
- 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 +38 -167
- 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/lib/qr-code.mjs +26 -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/cross-cli-contract.md +4 -4
- 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/garbage-collect/phases.md +60 -0
- package/pipeline/multi-agent-refs/help/en.md +1 -0
- package/pipeline/multi-agent-refs/help/tr.md +1 -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/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/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/schemas/runs-index.schema.json +2 -1
- package/pipeline/scripts/contract-server.mjs +1 -1
- package/pipeline/scripts/office-pair.mjs +40 -9
- package/pipeline/scripts/runs-index.mjs +34 -7
- package/pipeline/scripts/status-overview.mjs +401 -0
- package/pipeline/skills/.skill-manifest.json +69 -65
- package/pipeline/skills/.skills-index.json +15 -4
- package/pipeline/skills/shared/README.md +4 -3
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +8 -71
- 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 +11 -73
- 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-office-pair/SKILL.md +19 -0
- package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +1 -1
- 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 +75 -219
- 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/skills-index.md +2 -1
- /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.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Auto-generated by `pipeline/scripts/build-skills-index.mjs` - do not hand-edit.
|
|
4
4
|
> Regenerate with `node pipeline/scripts/build-skills-index.mjs`.
|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**238 skills** across 2 groups.
|
|
7
7
|
|
|
8
8
|
| Group | Name | Platform | Description |
|
|
9
9
|
|-------|------|----------|-------------|
|
|
@@ -132,6 +132,7 @@
|
|
|
132
132
|
| core | `multi-agent-model` | - | Turn the top model rung on or off and keep the cost ledger's pricing in step with it. Use when asked to enable or disable Fable, or which mo |
|
|
133
133
|
| core | `multi-agent-office-off` | - | Close the office panel and stop its local server. Use when done with the panel. |
|
|
134
134
|
| core | `multi-agent-office-on` | - | Open the office: a local browser page that shows runs and agents live and drives the ecosystem (new runs, jobs, PRs, keys, memory, system, c |
|
|
135
|
+
| core | `multi-agent-office-pair` | - | Pair a phone with the office: QR, wait, compare the code with the user, enrol. Use when the office should be reachable from a phone. |
|
|
135
136
|
| core | `multi-agent-office-status` | - | Whether the office panel is running, its URL (never the token) and its last actions. Use when unsure if the panel is open. |
|
|
136
137
|
| core | `multi-agent-postmortem` | - | Post-mortem of a run from its session transcripts: loops, stalls, root-cause hypotheses on file:line, fixes. Read-only, redacted. Use when a |
|
|
137
138
|
| core | `multi-agent-prune-logs` | - | Delete per-task project logs under ~/.claude/logs/multi-agent (filter by age/project/task). Audit trail + metrics are preserved. Dry-run fir |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|