configure-test-stack
Skill7 starsChanged 15 days agoMITView on GitHub
Use when setting up tests for a frontend project - Vitest (Node unit and integration, real-browser UI via Playwright), Storybook stories as tests, Playwright e2e and MSW, under tests/ or colocated per the stack profile.
- Reads credentials
- Installs packages
1 other project uses a version of this.
Adopted by
Most recently taken up by
- VelimirMueller/ai-development-skillsword for word, first seen Oct 25, 2026
Other versions1 project uses a version of this.
Word for word · 1
- VelimirMueller/ai-development-skills★ 7100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository156 other files it gives its agents.
- skills/swift/swiftui-design-principles/AGENTS.mdAGENTS.md
- skills/swift/ios-code-audit/CLAUDE.mdCLAUDE.md
- skills/swift/swift-focusengine-pro/llms.txtllms.txt
- build-llm-seamSkill
- build-mcp-serverSkill
- build-rag-pipelineSkill
- secure-llm-featuresSkill
- set-up-llm-evalsSkill
- build-nextjs-backendSkill
- build-supabase-edge-functionSkill
- configure-backend-testsSkill
- design-http-apiSkill
- harden-backendSkill
- scaffold-fastapi-serviceSkill
- scaffold-go-serviceSkill
- scaffold-hono-serviceSkill
- secure-supabase-rlsSkill
- set-up-backend-authSkill
- set-up-background-jobsSkill
- set-up-databaseSkill
- set-up-nextjs-supabase-authSkill
- set-up-observabilitySkill
- set-up-supabaseSkill
- build-cliSkill
- release-cliSkill
- set-up-dev-toolchainSkill
- audit-securitySkill
- audit-toolchainSkill
- extend-skillsetSkill
- promote-releaseSkill
- set-up-stack-profileSkill
- write-commit-messagesSkill
- write-pull-requestsSkill
- analyze-complexitySkill
- apply-algorithmsSkill
- apply-design-patternsSkill
- apply-design-principlesSkill
- apply-solidSkill
- choose-data-structureSkill
- debug-systematicallySkill
- enforce-dependency-ruleSkill
- refactor-safelySkill
- slice-and-estimateSkill
- write-clean-testsSkill
- write-readable-codeSkill
- clean-frontend-scaffoldingSkill
- configure-accessibilitySkill
- configure-analyticsSkill
- configure-ciSkill
- configure-error-trackingSkill
- configure-lintingSkill
- configure-typescriptSkill
- create-moduleSkill
- optimize-performanceSkill
- scaffold-frontend-projectSkill
- set-up-authSkill
- set-up-design-systemSkill
- set-up-document-headSkill
- set-up-error-boundariesSkill
- set-up-feature-flagsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 16
Read .claude/stack-profile.md if present: tests.layout is tests-dir (default) or colocated.
- Very specific: line 214
If the profile says tests-dir and src//*.test.* exist, move them to tests/ui/ (component tests) or tests/unit/ (pure logic) and switch …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Configure Test Stack
1. Audit current state
grep -E '"(vitest|@playwright/test|msw|@storybook/addon-vitest)"' package.json 2>/dev/null
ls vitest.config.* playwright.config.* tests/ .env.test .claude/stack-profile.md 2>/dev/null
find src -name "*.test.*" -o -name "*.spec.*" 2>/dev/null # co-located testsRead .claude/stack-profile.md if present: tests.layout is tests-dir (default) or colocated; frontend.framework picks the render adapter; package_manager replaces pnpm. With colocated, or an established colocated suite, use the globs in test-layout.md ("Colocated variant") in step 6, create only tests/{e2e,mocks,setup} in step 5, and skip step 9 (no migration).
Prerequisites: the @/ alias (configure-typescript) and validate-env (.env.test, step 5). MSW mocks the fetcher seam from set-up-state-management.
2. Decide what to do
- Nothing → full setup (steps 3–10).
- Partial → add only the missing layer (Vitest present but no browser
uiproject, or no e2e). - Co-located tests found and the profile says
tests-dir→ migrate (step 9).
3. Detect framework
React → vitest-browser-react. Vue → vitest-browser-vue. Vitest, Playwright and MSW are framework-agnostic.
4. Install
pnpm add -D vitest @vitest/browser-playwright @vitest/coverage-v8 \
vitest-browser-react @playwright/test msw # Vue: vitest-browser-vue instead
pnpm exec playwright install chromiumNo @testing-library/jest-dom: Vitest's expect.element(...) already has toBeVisible, toBeEnabled, … (checked: UI tests pass in both frameworks without it). With Storybook installed: pnpm exec storybook add @storybook/addon-vitest (not dlx: it must run the installed Storybook version).
5. Create the tree, the env file and the test tsconfig
tests/
├── unit/ # pure logic — Node
├── integration/ # modules + data layer (a hook/query against MSW) — Node
├── ui/ # components — real browser via Playwright
├── e2e/ # full flows — Playwright
├── mocks/ # handlers.ts · node.ts · browser.ts (no setup code here)
├── public/ # mockServiceWorker.js (generated, served to the ui project only)
├── setup/ # msw-node.ts · ui-msw.ts
└── tsconfig.json.env.test (Vitest runs in Vite's test mode and loads it; env.ts throws without these):
VITE_API_URL=http://api.testtests/tsconfig.json: test files are outside src/, so no tsconfig type-checks them until this exists. It inherits the app config, including paths. Add it to the root references and put the config files in tsconfig.node.json's include:
{
"extends": "../tsconfig.app.json",
"compilerOptions": { "tsBuildInfoFile": "../node_modules/.tmp/tsconfig.tests.tsbuildinfo" },
"include": ["./**/*.ts", "./**/*.tsx"]
}// tsconfig.json → "references": [ …, { "path": "./tests" } ]
// tsconfig.node.json → "include": ["vite.config.ts", "vitest.config.ts", "playwright.config.ts"]Vue: same, vue-tsc -b checks it through pnpm typecheck.
6. Configure Vitest (one config, several projects)
// vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
import viteConfig from './vite.config.ts';
export default mergeConfig(
viteConfig,
defineConfig({
test: {
coverage: { provider: 'v8', include: ['src/**'] },
projects: [
{
test: {
name: 'unit',
environment: 'node',
include: ['tests/unit/**/*.{test,spec}.ts'],
setupFiles: ['tests/setup/msw-node.ts'],
},
},
{
test: {
name: 'integration',
environment: 'node',
include: ['tests/integration/**/*.{test,spec}.ts'],
setupFiles: ['tests/setup/msw-node.ts'],
},
},
{
publicDir: 'tests/public',
test: {
name: 'ui',
include: ['tests/ui/**/*.{test,spec}.{ts,tsx}'],
setupFiles: ['tests/setup/ui-msw.ts'],
browser: {
enabled: true,
provider: playwright(),
headless: true,
instances: [{ browser: 'chromium' }],
},
},
},
],
},
}),
);- Vitest 5: inline
projectsinherit the root config (alias, plugins) becauseextends: trueis the default; on Vitest 3/4 addextends: trueto each project. The import ends in.ts: Vite 8 warns about extension-less config imports. uiruns in real Chromium, not jsdom (test-stack.md). Itsincludecovers.tsand.tsx: Vue tests are.ts.publicDir: 'tests/public'serves MSW's worker to theuiproject only, so it never lands in the productionpublic/folder.coverage.include: ['src/**']keeps test helpers out of the report and shows untested source files at 0%.- Cold cache: browser mode may print "Vite unexpectedly reloaded a test" and fail the first run after a new dependency is discovered; rerun (it passed on the second run here). Persistent flake: add the named dependency to the project's
optimizeDeps.include.
7. Playwright (e2e)
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
use: { baseURL: 'http://localhost:5173', trace: 'on-first-retry' },
webServer: {
command: 'pnpm dev --port 5173 --strictPort',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
});webServer starts the dev server and reuses a running one locally; --strictPort fails loudly instead of moving to 5174 and missing baseURL. Playwright reads paths from the tsconfig itself. One smoke spec keeps the setup honest:
// tests/e2e/smoke.spec.ts
import { expect, test } from '@playwright/test';
test('the app boots without console errors', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', (e) => errors.push(e.message));
await page.goto('/');
await expect(page.locator('#root, #app')).not.toBeEmpty(); // React mounts to #root, Vue to #app
expect(errors).toEqual([]);
});8. MSW: mock the network, not modules
// tests/mocks/handlers.ts
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('*/todos', () => HttpResponse.json([{ id: '1', text: 'Demo', done: false }])),
];*/todos matches whatever base VITE_API_URL has; a relative /todos handler never matches in Node (fetch rejects relative URLs) and http.get('/todos') ignores http://api.test/todos (both reproduced).
// tests/mocks/node.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);// tests/setup/msw-node.ts
import { afterAll, afterEach, beforeAll } from 'vitest';
import { server } from '../mocks/node';
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());Browser (ui project), same handlers:
// tests/mocks/browser.ts
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);// tests/setup/ui-msw.ts
import { afterAll, afterEach, beforeAll } from 'vitest';
import { worker } from '../mocks/browser';
beforeAll(() => worker.start({ onUnhandledRequest: 'error' }));
afterEach(() => worker.resetHandlers());
afterAll(() => worker.stop());pnpm exec msw init tests/public --no-save # writes tests/public/mockServiceWorker.js; without it the ui project fails to start the workerTests import the worker from tests/mocks/browser.ts, never from the setup file: importing ui-msw.ts creates a second worker whose handlers the real, started worker never sees (a ws test found no handler until this was fixed). Biome lints the generated worker: in biome.json use "files": { "includes": ["**", "!**/mockServiceWorker.js"] }.
9. Storybook stories as tests; migrate co-located tests
With Storybook installed, the addon (step 4) registers its own Vitest project; do not copy it into vitest.config.ts. Stories stay beside their components.
If the profile says tests-dir and src/**/*.test.* exist, move them to tests/ui/ (component tests) or tests/unit/ (pure logic) and switch imports to @/.
10. Verify
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test:ui": "vitest run --project ui",
"test:coverage": "vitest run --coverage",
"test:e2e": "playwright test"
}
}One passing example per project proves the wiring (all three pass):
// tests/ui/Button.test.tsx (React; Vue: vitest-browser-vue, same shape)
import { render } from 'vitest-browser-react';
import { expect, test, vi } from 'vitest';
import { Button } from '@/components/atoms/Button';
test('calls onClick when pressed', async () => {
const onClick = vi.fn();
const screen = await render(<Button onClick={onClick}>Save</Button>);
await screen.getByRole('button', { name: 'Save' }).click();
expect(onClick).toHaveBeenCalledOnce();
});// tests/integration/fetcher.test.ts
import { expect, test } from 'vitest';
import { fetcher } from '@/libs/fetcher';
import type { Todo } from '@/libs/queryKeys';
test('fetcher hits msw', async () => {
const todos = await fetcher<Todo[]>('/todos?status=all');
expect(todos[0]?.text).toBe('Demo');
});pnpm typecheck && pnpm test && pnpm test:e2erender of both adapters is async: without await, screen.getByRole is not a function (reproduced). CI runs pnpm test and pnpm test:e2e as separate steps; e2e is slower and starts a server.
References
- ./test-layout.md — folders, what each type holds, the colocated variant, the trophy.
- ./test-stack.md — real browser over jsdom, stories as tests, MSW at the network, projects, e2e, coverage; deviations.
- ../_shared/stack-versions.md — Vitest, Playwright, MSW, Storybook lines.
- ../_shared/conventions.md —
@/alias, thetypecheckrule.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

