@macpaw/cctk 1.0.0-beta.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/License +21 -0
- package/README.md +142 -0
- package/bin/index.js +5 -0
- package/dist/index.js +1 -0
- package/package.json +65 -0
- package/templates/claude/agents/brainstormer.md +113 -0
- package/templates/claude/agents/code-reviewer.md +157 -0
- package/templates/claude/agents/copywriter.md +110 -0
- package/templates/claude/agents/database-admin.md +92 -0
- package/templates/claude/agents/debugger.md +137 -0
- package/templates/claude/agents/docs-manager.md +208 -0
- package/templates/claude/agents/fullstack-developer.md +95 -0
- package/templates/claude/agents/git-manager.md +394 -0
- package/templates/claude/agents/journal-writer.md +113 -0
- package/templates/claude/agents/mcp-manager.md +89 -0
- package/templates/claude/agents/planner.md +108 -0
- package/templates/claude/agents/project-manager.md +125 -0
- package/templates/claude/agents/registry.json +304 -0
- package/templates/claude/agents/researcher.md +37 -0
- package/templates/claude/agents/scout-external.md +140 -0
- package/templates/claude/agents/scout.md +106 -0
- package/templates/claude/agents/tester.md +105 -0
- package/templates/claude/agents/ui-ux-designer.md +208 -0
- package/templates/claude/commands/ask.md +56 -0
- package/templates/claude/commands/bootstrap/auto/fast.md +106 -0
- package/templates/claude/commands/bootstrap/auto/parallel.md +64 -0
- package/templates/claude/commands/bootstrap/auto.md +110 -0
- package/templates/claude/commands/bootstrap.md +130 -0
- package/templates/claude/commands/brainstorm.md +75 -0
- package/templates/claude/commands/code/auto.md +198 -0
- package/templates/claude/commands/code/no-test.md +172 -0
- package/templates/claude/commands/code/parallel.md +100 -0
- package/templates/claude/commands/code.md +197 -0
- package/templates/claude/commands/coding-level.md +37 -0
- package/templates/claude/commands/content/cro.md +41 -0
- package/templates/claude/commands/content/enhance.md +12 -0
- package/templates/claude/commands/content/fast.md +11 -0
- package/templates/claude/commands/content/good.md +14 -0
- package/templates/claude/commands/cook/auto/fast.md +26 -0
- package/templates/claude/commands/cook/auto/parallel.md +49 -0
- package/templates/claude/commands/cook/auto.md +15 -0
- package/templates/claude/commands/cook.md +101 -0
- package/templates/claude/commands/debug.md +13 -0
- package/templates/claude/commands/docs/init.md +37 -0
- package/templates/claude/commands/docs/summarize.md +22 -0
- package/templates/claude/commands/docs/update.md +78 -0
- package/templates/claude/commands/fix/ci.md +17 -0
- package/templates/claude/commands/fix/fast.md +18 -0
- package/templates/claude/commands/fix/hard.md +35 -0
- package/templates/claude/commands/fix/logs.md +26 -0
- package/templates/claude/commands/fix/parallel.md +54 -0
- package/templates/claude/commands/fix/test.md +20 -0
- package/templates/claude/commands/fix/types.md +9 -0
- package/templates/claude/commands/fix/ui.md +33 -0
- package/templates/claude/commands/fix.md +43 -0
- package/templates/claude/commands/git/cm.md +5 -0
- package/templates/claude/commands/git/cp.md +4 -0
- package/templates/claude/commands/git/merge.md +40 -0
- package/templates/claude/commands/git/pr.md +50 -0
- package/templates/claude/commands/journal.md +7 -0
- package/templates/claude/commands/kanban.md +99 -0
- package/templates/claude/commands/plan/archive.md +57 -0
- package/templates/claude/commands/plan/ci.md +33 -0
- package/templates/claude/commands/plan/cro.md +67 -0
- package/templates/claude/commands/plan/fast.md +66 -0
- package/templates/claude/commands/plan/hard.md +92 -0
- package/templates/claude/commands/plan/parallel.md +129 -0
- package/templates/claude/commands/plan/two.md +45 -0
- package/templates/claude/commands/plan/validate.md +117 -0
- package/templates/claude/commands/plan.md +30 -0
- package/templates/claude/commands/preview.md +87 -0
- package/templates/claude/commands/registry.json +313 -0
- package/templates/claude/commands/review/codebase/parallel.md +122 -0
- package/templates/claude/commands/review/codebase.md +47 -0
- package/templates/claude/commands/scout/ext.md +35 -0
- package/templates/claude/commands/scout.md +28 -0
- package/templates/claude/commands/skill/add.md +36 -0
- package/templates/claude/commands/skill/create.md +29 -0
- package/templates/claude/commands/skill/fix-logs.md +22 -0
- package/templates/claude/commands/skill/optimize/auto.md +25 -0
- package/templates/claude/commands/skill/optimize.md +34 -0
- package/templates/claude/commands/skill/plan.md +45 -0
- package/templates/claude/commands/test/ui.md +91 -0
- package/templates/claude/commands/test.md +8 -0
- package/templates/claude/commands/use-mcp.md +34 -0
- package/templates/claude/commands/watzup.md +8 -0
- package/templates/claude/commands/worktree.md +126 -0
- package/templates/claude/hooks/dev-rules-reminder.cjs +258 -0
- package/templates/claude/hooks/docs/README.md +42 -0
- package/templates/claude/hooks/lib/cctk-config-utils.cjs +751 -0
- package/templates/claude/hooks/lib/cctk-paths.cjs +106 -0
- package/templates/claude/hooks/lib/context-tracker.cjs +346 -0
- package/templates/claude/hooks/privacy-block.cjs +289 -0
- package/templates/claude/hooks/registry.json +77 -0
- package/templates/claude/hooks/scout-block/broad-pattern-detector.cjs +293 -0
- package/templates/claude/hooks/scout-block/error-formatter.cjs +156 -0
- package/templates/claude/hooks/scout-block/path-extractor.cjs +359 -0
- package/templates/claude/hooks/scout-block/pattern-matcher.cjs +184 -0
- package/templates/claude/hooks/scout-block/vendor/ignore.js +626 -0
- package/templates/claude/hooks/scout-block.cjs +167 -0
- package/templates/claude/hooks/session-end.cjs +35 -0
- package/templates/claude/hooks/session-init.cjs +214 -0
- package/templates/claude/hooks/subagent-init.cjs +161 -0
- package/templates/claude/hooks/write-compact-marker.cjs +153 -0
- package/templates/claude/output-styles/coding-level-1.md +148 -0
- package/templates/claude/output-styles/coding-level-2.md +159 -0
- package/templates/claude/output-styles/coding-level-3.md +91 -0
- package/templates/claude/scripts/README.md +33 -0
- package/templates/claude/scripts/generate-catalogs.cjs +318 -0
- package/templates/claude/scripts/registry.json +48 -0
- package/templates/claude/scripts/set-active-plan.cjs +45 -0
- package/templates/claude/scripts/validate-docs.cjs +447 -0
- package/templates/claude/scripts/worktree.cjs +658 -0
- package/templates/claude/skills/README.md +112 -0
- package/templates/claude/skills/ai-artist/SKILL.md +75 -0
- package/templates/claude/skills/ai-artist/references/advanced-techniques.md +184 -0
- package/templates/claude/skills/ai-artist/references/domain-code.md +66 -0
- package/templates/claude/skills/ai-artist/references/domain-data.md +72 -0
- package/templates/claude/skills/ai-artist/references/domain-marketing.md +66 -0
- package/templates/claude/skills/ai-artist/references/domain-patterns.md +33 -0
- package/templates/claude/skills/ai-artist/references/domain-writing.md +68 -0
- package/templates/claude/skills/ai-artist/references/image-prompting.md +141 -0
- package/templates/claude/skills/ai-artist/references/llm-prompting.md +165 -0
- package/templates/claude/skills/ai-artist/references/nano-banana.md +59 -0
- package/templates/claude/skills/ai-artist/references/reasoning-techniques.md +201 -0
- package/templates/claude/skills/backend-development/SKILL.md +95 -0
- package/templates/claude/skills/backend-development/references/backend-api-design.md +495 -0
- package/templates/claude/skills/backend-development/references/backend-architecture.md +454 -0
- package/templates/claude/skills/backend-development/references/backend-authentication.md +338 -0
- package/templates/claude/skills/backend-development/references/backend-code-quality.md +659 -0
- package/templates/claude/skills/backend-development/references/backend-debugging.md +904 -0
- package/templates/claude/skills/backend-development/references/backend-devops.md +494 -0
- package/templates/claude/skills/backend-development/references/backend-mindset.md +387 -0
- package/templates/claude/skills/backend-development/references/backend-performance.md +397 -0
- package/templates/claude/skills/backend-development/references/backend-security.md +290 -0
- package/templates/claude/skills/backend-development/references/backend-technologies.md +256 -0
- package/templates/claude/skills/backend-development/references/backend-testing.md +429 -0
- package/templates/claude/skills/better-auth/SKILL.md +204 -0
- package/templates/claude/skills/better-auth/references/advanced-features.md +553 -0
- package/templates/claude/skills/better-auth/references/database-integration.md +577 -0
- package/templates/claude/skills/better-auth/references/email-password-auth.md +416 -0
- package/templates/claude/skills/better-auth/references/oauth-providers.md +430 -0
- package/templates/claude/skills/better-auth/scripts/better_auth_init.py +521 -0
- package/templates/claude/skills/chrome-devtools/SKILL.md +473 -0
- package/templates/claude/skills/chrome-devtools/references/cdp-domains.md +694 -0
- package/templates/claude/skills/chrome-devtools/references/performance-guide.md +940 -0
- package/templates/claude/skills/chrome-devtools/references/puppeteer-reference.md +953 -0
- package/templates/claude/skills/chrome-devtools/scripts/README.md +288 -0
- package/templates/claude/skills/chrome-devtools/scripts/aria-snapshot.js +368 -0
- package/templates/claude/skills/chrome-devtools/scripts/click.js +92 -0
- package/templates/claude/skills/chrome-devtools/scripts/console.js +85 -0
- package/templates/claude/skills/chrome-devtools/scripts/evaluate.js +59 -0
- package/templates/claude/skills/chrome-devtools/scripts/fill.js +84 -0
- package/templates/claude/skills/chrome-devtools/scripts/inject-auth.js +247 -0
- package/templates/claude/skills/chrome-devtools/scripts/install-deps.sh +11 -0
- package/templates/claude/skills/chrome-devtools/scripts/install.sh +36 -0
- package/templates/claude/skills/chrome-devtools/scripts/lib/browser.js +335 -0
- package/templates/claude/skills/chrome-devtools/scripts/lib/selector.js +183 -0
- package/templates/claude/skills/chrome-devtools/scripts/navigate.js +60 -0
- package/templates/claude/skills/chrome-devtools/scripts/network.js +113 -0
- package/templates/claude/skills/chrome-devtools/scripts/package.json +16 -0
- package/templates/claude/skills/chrome-devtools/scripts/performance.js +159 -0
- package/templates/claude/skills/chrome-devtools/scripts/screenshot.js +212 -0
- package/templates/claude/skills/chrome-devtools/scripts/select-ref.js +151 -0
- package/templates/claude/skills/chrome-devtools/scripts/snapshot.js +143 -0
- package/templates/claude/skills/chrome-devtools/scripts/ws-debug.js +47 -0
- package/templates/claude/skills/chrome-devtools/scripts/ws-full-debug.js +115 -0
- package/templates/claude/skills/claude-code/references/advanced-features.md +399 -0
- package/templates/claude/skills/claude-code/references/agent-skills.md +399 -0
- package/templates/claude/skills/claude-code/references/api-reference.md +498 -0
- package/templates/claude/skills/claude-code/references/best-practices.md +447 -0
- package/templates/claude/skills/claude-code/references/cicd-integration.md +428 -0
- package/templates/claude/skills/claude-code/references/common-workflows.md +107 -0
- package/templates/claude/skills/claude-code/references/configuration.md +480 -0
- package/templates/claude/skills/claude-code/references/enterprise-features.md +472 -0
- package/templates/claude/skills/claude-code/references/getting-started.md +244 -0
- package/templates/claude/skills/claude-code/references/hooks-and-plugins.md +444 -0
- package/templates/claude/skills/claude-code/references/hooks-comprehensive.md +622 -0
- package/templates/claude/skills/claude-code/references/ide-integration.md +316 -0
- package/templates/claude/skills/claude-code/references/mcp-integration.md +386 -0
- package/templates/claude/skills/claude-code/references/slash-commands.md +460 -0
- package/templates/claude/skills/claude-code/references/troubleshooting.md +455 -0
- package/templates/claude/skills/claude-code/skill.md +60 -0
- package/templates/claude/skills/code-review/SKILL.md +143 -0
- package/templates/claude/skills/code-review/references/code-review-reception.md +209 -0
- package/templates/claude/skills/code-review/references/requesting-code-review.md +105 -0
- package/templates/claude/skills/code-review/references/verification-before-completion.md +139 -0
- package/templates/claude/skills/context-engineering/SKILL.md +86 -0
- package/templates/claude/skills/context-engineering/references/context-compression.md +84 -0
- package/templates/claude/skills/context-engineering/references/context-degradation.md +93 -0
- package/templates/claude/skills/context-engineering/references/context-fundamentals.md +75 -0
- package/templates/claude/skills/context-engineering/references/context-optimization.md +82 -0
- package/templates/claude/skills/context-engineering/references/evaluation.md +89 -0
- package/templates/claude/skills/context-engineering/references/memory-systems.md +88 -0
- package/templates/claude/skills/context-engineering/references/multi-agent-patterns.md +90 -0
- package/templates/claude/skills/context-engineering/references/project-development.md +97 -0
- package/templates/claude/skills/context-engineering/references/tool-design.md +86 -0
- package/templates/claude/skills/context-engineering/scripts/compression_evaluator.py +329 -0
- package/templates/claude/skills/context-engineering/scripts/context_analyzer.py +294 -0
- package/templates/claude/skills/databases/SKILL.md +232 -0
- package/templates/claude/skills/databases/references/mongodb-aggregation.md +447 -0
- package/templates/claude/skills/databases/references/mongodb-atlas.md +465 -0
- package/templates/claude/skills/databases/references/mongodb-crud.md +408 -0
- package/templates/claude/skills/databases/references/mongodb-indexing.md +442 -0
- package/templates/claude/skills/databases/references/postgresql-administration.md +594 -0
- package/templates/claude/skills/databases/references/postgresql-performance.md +527 -0
- package/templates/claude/skills/databases/references/postgresql-psql-cli.md +467 -0
- package/templates/claude/skills/databases/references/postgresql-queries.md +475 -0
- package/templates/claude/skills/databases/scripts/db_backup.py +502 -0
- package/templates/claude/skills/databases/scripts/db_migrate.py +414 -0
- package/templates/claude/skills/databases/scripts/db_performance_check.py +445 -0
- package/templates/claude/skills/debugging/SKILL.md +84 -0
- package/templates/claude/skills/debugging/references/defense-in-depth.md +124 -0
- package/templates/claude/skills/debugging/references/root-cause-tracing.md +122 -0
- package/templates/claude/skills/debugging/references/systematic-debugging.md +102 -0
- package/templates/claude/skills/debugging/references/verification.md +123 -0
- package/templates/claude/skills/debugging/scripts/find-polluter.sh +63 -0
- package/templates/claude/skills/debugging/scripts/find-polluter.test.md +102 -0
- package/templates/claude/skills/devops/SKILL.md +293 -0
- package/templates/claude/skills/devops/references/browser-rendering.md +305 -0
- package/templates/claude/skills/devops/references/cloudflare-d1-kv.md +123 -0
- package/templates/claude/skills/devops/references/cloudflare-platform.md +271 -0
- package/templates/claude/skills/devops/references/cloudflare-r2-storage.md +280 -0
- package/templates/claude/skills/devops/references/cloudflare-workers-advanced.md +312 -0
- package/templates/claude/skills/devops/references/cloudflare-workers-apis.md +309 -0
- package/templates/claude/skills/devops/references/cloudflare-workers-basics.md +418 -0
- package/templates/claude/skills/devops/references/docker-basics.md +297 -0
- package/templates/claude/skills/devops/references/docker-compose.md +292 -0
- package/templates/claude/skills/devops/references/gcloud-platform.md +307 -0
- package/templates/claude/skills/devops/references/gcloud-services.md +304 -0
- package/templates/claude/skills/devops/scripts/cloudflare_deploy.py +269 -0
- package/templates/claude/skills/devops/scripts/docker_optimize.py +320 -0
- package/templates/claude/skills/docs-seeker/SKILL.md +95 -0
- package/templates/claude/skills/docs-seeker/package.json +24 -0
- package/templates/claude/skills/docs-seeker/references/advanced.md +78 -0
- package/templates/claude/skills/docs-seeker/references/context7-patterns.md +68 -0
- package/templates/claude/skills/docs-seeker/references/errors.md +68 -0
- package/templates/claude/skills/docs-seeker/scripts/analyze-llms-txt.js +245 -0
- package/templates/claude/skills/docs-seeker/scripts/detect-topic.js +172 -0
- package/templates/claude/skills/docs-seeker/scripts/fetch-docs.js +212 -0
- package/templates/claude/skills/docs-seeker/workflows/library-search.md +87 -0
- package/templates/claude/skills/docs-seeker/workflows/repo-analysis.md +91 -0
- package/templates/claude/skills/docs-seeker/workflows/topic-search.md +77 -0
- package/templates/claude/skills/frontend-design/SKILL.md +85 -0
- package/templates/claude/skills/frontend-design/references/analysis-best-practices.md +80 -0
- package/templates/claude/skills/frontend-design/references/analysis-prompts.md +141 -0
- package/templates/claude/skills/frontend-design/references/analysis-techniques.md +118 -0
- package/templates/claude/skills/frontend-design/references/animejs.md +396 -0
- package/templates/claude/skills/frontend-design/references/design-extraction-overview.md +71 -0
- package/templates/claude/skills/frontend-design/references/extraction-best-practices.md +141 -0
- package/templates/claude/skills/frontend-design/references/extraction-output-templates.md +162 -0
- package/templates/claude/skills/frontend-design/references/extraction-prompts.md +127 -0
- package/templates/claude/skills/frontend-design/references/technical-accessibility.md +119 -0
- package/templates/claude/skills/frontend-design/references/technical-best-practices.md +97 -0
- package/templates/claude/skills/frontend-design/references/technical-optimization.md +44 -0
- package/templates/claude/skills/frontend-design/references/technical-overview.md +90 -0
- package/templates/claude/skills/frontend-design/references/technical-workflows.md +150 -0
- package/templates/claude/skills/frontend-design/references/visual-analysis-overview.md +95 -0
- package/templates/claude/skills/frontend-development/SKILL.md +399 -0
- package/templates/claude/skills/frontend-development/resources/common-patterns.md +331 -0
- package/templates/claude/skills/frontend-development/resources/complete-examples.md +872 -0
- package/templates/claude/skills/frontend-development/resources/component-patterns.md +502 -0
- package/templates/claude/skills/frontend-development/resources/data-fetching.md +767 -0
- package/templates/claude/skills/frontend-development/resources/file-organization.md +502 -0
- package/templates/claude/skills/frontend-development/resources/loading-and-error-states.md +501 -0
- package/templates/claude/skills/frontend-development/resources/performance.md +406 -0
- package/templates/claude/skills/frontend-development/resources/routing-guide.md +364 -0
- package/templates/claude/skills/frontend-development/resources/styling-guide.md +428 -0
- package/templates/claude/skills/frontend-development/resources/typescript-standards.md +418 -0
- package/templates/claude/skills/markdown-novel-viewer/SKILL.md +272 -0
- package/templates/claude/skills/markdown-novel-viewer/assets/directory-browser.css +215 -0
- package/templates/claude/skills/markdown-novel-viewer/assets/favicon.png +0 -0
- package/templates/claude/skills/markdown-novel-viewer/assets/novel-theme.css +872 -0
- package/templates/claude/skills/markdown-novel-viewer/assets/reader.js +378 -0
- package/templates/claude/skills/markdown-novel-viewer/assets/template.html +85 -0
- package/templates/claude/skills/markdown-novel-viewer/package.json +15 -0
- package/templates/claude/skills/markdown-novel-viewer/scripts/lib/http-server.cjs +434 -0
- package/templates/claude/skills/markdown-novel-viewer/scripts/lib/markdown-renderer.cjs +335 -0
- package/templates/claude/skills/markdown-novel-viewer/scripts/lib/plan-navigator.cjs +509 -0
- package/templates/claude/skills/markdown-novel-viewer/scripts/lib/port-finder.cjs +49 -0
- package/templates/claude/skills/markdown-novel-viewer/scripts/lib/process-mgr.cjs +150 -0
- package/templates/claude/skills/markdown-novel-viewer/scripts/server.cjs +398 -0
- package/templates/claude/skills/mcp-builder/SKILL.md +328 -0
- package/templates/claude/skills/mcp-builder/reference/evaluation.md +602 -0
- package/templates/claude/skills/mcp-builder/reference/mcp_best_practices.md +915 -0
- package/templates/claude/skills/mcp-builder/reference/node_mcp_server.md +916 -0
- package/templates/claude/skills/mcp-builder/reference/python_mcp_server.md +752 -0
- package/templates/claude/skills/mcp-builder/scripts/connections.py +151 -0
- package/templates/claude/skills/mcp-builder/scripts/evaluation.py +381 -0
- package/templates/claude/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/templates/claude/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/templates/claude/skills/mcp-management/README.md +219 -0
- package/templates/claude/skills/mcp-management/SKILL.md +209 -0
- package/templates/claude/skills/mcp-management/assets/tools.json +3146 -0
- package/templates/claude/skills/mcp-management/references/configuration.md +114 -0
- package/templates/claude/skills/mcp-management/references/gemini-cli-integration.md +209 -0
- package/templates/claude/skills/mcp-management/references/mcp-protocol.md +116 -0
- package/templates/claude/skills/mcp-management/scripts/.env.example +10 -0
- package/templates/claude/skills/mcp-management/scripts/cli.ts +202 -0
- package/templates/claude/skills/mcp-management/scripts/mcp-client.ts +247 -0
- package/templates/claude/skills/mcp-management/scripts/package.json +20 -0
- package/templates/claude/skills/mcp-management/scripts/tsconfig.json +15 -0
- package/templates/claude/skills/media-processing/SKILL.md +91 -0
- package/templates/claude/skills/media-processing/references/common-workflows.md +132 -0
- package/templates/claude/skills/media-processing/references/ffmpeg-encoding.md +358 -0
- package/templates/claude/skills/media-processing/references/ffmpeg-filters.md +503 -0
- package/templates/claude/skills/media-processing/references/ffmpeg-streaming.md +395 -0
- package/templates/claude/skills/media-processing/references/format-compatibility.md +375 -0
- package/templates/claude/skills/media-processing/references/imagemagick-batch.md +612 -0
- package/templates/claude/skills/media-processing/references/imagemagick-editing.md +623 -0
- package/templates/claude/skills/media-processing/references/rmbg-background-removal.md +66 -0
- package/templates/claude/skills/media-processing/references/troubleshooting.md +109 -0
- package/templates/claude/skills/media-processing/scripts/README.md +102 -0
- package/templates/claude/skills/media-processing/scripts/batch-remove-background.sh +124 -0
- package/templates/claude/skills/media-processing/scripts/batch_resize.py +342 -0
- package/templates/claude/skills/media-processing/scripts/media_convert.py +311 -0
- package/templates/claude/skills/media-processing/scripts/remove-background.sh +96 -0
- package/templates/claude/skills/media-processing/scripts/remove-bg-node.js +181 -0
- package/templates/claude/skills/mermaidjs-v11/SKILL.md +115 -0
- package/templates/claude/skills/mermaidjs-v11/references/cli-usage.md +228 -0
- package/templates/claude/skills/mermaidjs-v11/references/configuration.md +232 -0
- package/templates/claude/skills/mermaidjs-v11/references/diagram-types.md +315 -0
- package/templates/claude/skills/mermaidjs-v11/references/examples.md +344 -0
- package/templates/claude/skills/mermaidjs-v11/references/integration.md +310 -0
- package/templates/claude/skills/planning/SKILL.md +115 -0
- package/templates/claude/skills/planning/references/codebase-understanding.md +62 -0
- package/templates/claude/skills/planning/references/output-standards.md +127 -0
- package/templates/claude/skills/planning/references/plan-organization.md +150 -0
- package/templates/claude/skills/planning/references/research-phase.md +49 -0
- package/templates/claude/skills/planning/references/solution-design.md +63 -0
- package/templates/claude/skills/plans-kanban/SKILL.md +157 -0
- package/templates/claude/skills/plans-kanban/assets/dashboard-template.html +119 -0
- package/templates/claude/skills/plans-kanban/assets/dashboard.css +1594 -0
- package/templates/claude/skills/plans-kanban/assets/dashboard.js +659 -0
- package/templates/claude/skills/plans-kanban/assets/favicon.png +0 -0
- package/templates/claude/skills/plans-kanban/package.json +13 -0
- package/templates/claude/skills/plans-kanban/scripts/lib/dashboard-renderer.cjs +941 -0
- package/templates/claude/skills/plans-kanban/scripts/lib/http-server.cjs +310 -0
- package/templates/claude/skills/plans-kanban/scripts/lib/plan-metadata-extractor.cjs +489 -0
- package/templates/claude/skills/plans-kanban/scripts/lib/plan-parser.cjs +194 -0
- package/templates/claude/skills/plans-kanban/scripts/lib/plan-scanner.cjs +277 -0
- package/templates/claude/skills/plans-kanban/scripts/lib/port-finder.cjs +49 -0
- package/templates/claude/skills/plans-kanban/scripts/lib/process-mgr.cjs +128 -0
- package/templates/claude/skills/plans-kanban/scripts/server.cjs +249 -0
- package/templates/claude/skills/problem-solving/SKILL.md +96 -0
- package/templates/claude/skills/problem-solving/references/attribution.md +69 -0
- package/templates/claude/skills/problem-solving/references/collision-zone-thinking.md +79 -0
- package/templates/claude/skills/problem-solving/references/inversion-exercise.md +91 -0
- package/templates/claude/skills/problem-solving/references/meta-pattern-recognition.md +87 -0
- package/templates/claude/skills/problem-solving/references/scale-game.md +95 -0
- package/templates/claude/skills/problem-solving/references/simplification-cascades.md +80 -0
- package/templates/claude/skills/problem-solving/references/when-stuck.md +72 -0
- package/templates/claude/skills/registry.json +258 -0
- package/templates/claude/skills/repomix/SKILL.md +247 -0
- package/templates/claude/skills/repomix/references/configuration.md +211 -0
- package/templates/claude/skills/repomix/references/usage-patterns.md +232 -0
- package/templates/claude/skills/repomix/scripts/README.md +179 -0
- package/templates/claude/skills/repomix/scripts/repomix_batch.py +455 -0
- package/templates/claude/skills/repomix/scripts/repos.example.json +15 -0
- package/templates/claude/skills/research/SKILL.md +168 -0
- package/templates/claude/skills/sequential-thinking/.env.example +8 -0
- package/templates/claude/skills/sequential-thinking/README.md +183 -0
- package/templates/claude/skills/sequential-thinking/SKILL.md +94 -0
- package/templates/claude/skills/sequential-thinking/package.json +31 -0
- package/templates/claude/skills/sequential-thinking/references/advanced-strategies.md +79 -0
- package/templates/claude/skills/sequential-thinking/references/advanced-techniques.md +76 -0
- package/templates/claude/skills/sequential-thinking/references/core-patterns.md +95 -0
- package/templates/claude/skills/sequential-thinking/references/examples-api.md +88 -0
- package/templates/claude/skills/sequential-thinking/references/examples-architecture.md +94 -0
- package/templates/claude/skills/sequential-thinking/references/examples-debug.md +90 -0
- package/templates/claude/skills/sequential-thinking/scripts/format-thought.js +182 -0
- package/templates/claude/skills/sequential-thinking/scripts/process-thought.js +252 -0
- package/templates/claude/skills/skill-creator/LICENSE.txt +202 -0
- package/templates/claude/skills/skill-creator/SKILL.md +266 -0
- package/templates/claude/skills/skill-creator/references/agent-skills-spec.md +51 -0
- package/templates/claude/skills/skill-creator/scripts/encoding_utils.py +21 -0
- package/templates/claude/skills/skill-creator/scripts/init_skill.py +304 -0
- package/templates/claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/templates/claude/skills/skill-creator/scripts/quick_validate.py +66 -0
- package/templates/claude/skills/template-skill/SKILL.md +6 -0
- package/templates/claude/skills/vitest/SKILL.md +595 -0
- package/templates/claude/skills/vitest/references/async-patterns.md +82 -0
- package/templates/claude/skills/vitest/references/mock-patterns.md +78 -0
- package/templates/claude/skills/vitest/references/monorepo-setup.md +185 -0
- package/templates/claude/skills/vitest/references/turborepo-setup.md +332 -0
- package/templates/claude/skills/web-frameworks/SKILL.md +324 -0
- package/templates/claude/skills/web-frameworks/references/nextjs-app-router.md +465 -0
- package/templates/claude/skills/web-frameworks/references/nextjs-data-fetching.md +459 -0
- package/templates/claude/skills/web-frameworks/references/nextjs-optimization.md +511 -0
- package/templates/claude/skills/web-frameworks/references/nextjs-server-components.md +495 -0
- package/templates/claude/skills/web-frameworks/references/remix-icon-integration.md +603 -0
- package/templates/claude/skills/web-frameworks/references/turborepo-caching.md +551 -0
- package/templates/claude/skills/web-frameworks/references/turborepo-pipelines.md +517 -0
- package/templates/claude/skills/web-frameworks/references/turborepo-setup.md +542 -0
- package/templates/claude/skills/web-frameworks/scripts/nextjs_init.py +547 -0
- package/templates/claude/skills/web-frameworks/scripts/turborepo_migrate.py +394 -0
- package/templates/claude/workflows/development-rules.md +40 -0
- package/templates/claude/workflows/documentation-management.md +121 -0
- package/templates/claude/workflows/orchestration-protocol.md +16 -0
- package/templates/claude/workflows/primary-workflow.md +45 -0
- package/templates/claude/workflows/registry.json +37 -0
- package/templates/common/.cct.json +41 -0
- package/templates/common/.cctkignore +22 -0
- package/templates/common/.env.example +39 -0
- package/templates/common/.mcp.json.example +16 -0
- package/templates/common/metadata.json +15 -0
- package/templates/common/settings.json +79 -0
- package/templates/common/statusline.cjs +271 -0
- package/templates/config/.repomixignore +22 -0
- package/templates/config/AGENTS.md +55 -0
- package/templates/config/CLAUDE.md +87 -0
- package/templates/plans/bug-fix-template.md +69 -0
- package/templates/plans/feature-implementation-template.md +84 -0
- package/templates/plans/refactor-template.md +82 -0
- package/templates/plans/template-usage-guide.md +58 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vitest
|
|
3
|
+
description: >
|
|
4
|
+
Write high-quality Vitest tests for Next.js applications and shared monorepo packages
|
|
5
|
+
(hooks, utilities, components). Use this skill whenever the user asks to write, fix,
|
|
6
|
+
generate, or improve tests using Vitest — including unit tests, component tests, hook
|
|
7
|
+
tests, mocking strategies, test setup, and coverage. Trigger for any request involving
|
|
8
|
+
"test", "spec", "vitest", "testing-library", or "mock" in a TypeScript/React/Next.js context.
|
|
9
|
+
Also trigger when the user shares a source file and asks "write tests for this" or
|
|
10
|
+
"how do I test this".
|
|
11
|
+
license: MIT
|
|
12
|
+
version: 1.0.0
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Vitest Testing Skill
|
|
16
|
+
|
|
17
|
+
A guide for writing production-quality Vitest tests in Next.js monorepos with shared
|
|
18
|
+
packages (components, hooks, utilities).
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Detect the Repo Context First
|
|
23
|
+
|
|
24
|
+
**Before giving any setup advice or config examples, check which environment you're in.** The right configuration differs significantly across setups.
|
|
25
|
+
|
|
26
|
+
### Step 1 — Identify the repo type
|
|
27
|
+
|
|
28
|
+
Look for these signals (check files the user shares, or ask them to run `ls` at the root):
|
|
29
|
+
|
|
30
|
+
| Signal | Conclusion |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `turbo.json` present at root | **Turborepo monorepo** → follow [Turborepo section](#turborepo-monorepo) |
|
|
33
|
+
| `pnpm-workspace.yaml` or `lerna.json` or `nx.json` but no `turbo.json` | **Other monorepo** → follow [Generic Monorepo section](#generic-monorepo) |
|
|
34
|
+
| Single `package.json` at root, no workspace config | **Standalone project** → follow [Standalone section](#standalone-project) |
|
|
35
|
+
|
|
36
|
+
If unsure, ask: *"Does your project have a `turbo.json` at the root? Or is it a single-package project?"*
|
|
37
|
+
|
|
38
|
+
### Step 2 — Jump to the right config section
|
|
39
|
+
|
|
40
|
+
- Turborepo → [Turborepo Monorepo](#turborepo-monorepo)
|
|
41
|
+
- Other monorepo → [Generic Monorepo](#generic-monorepo)
|
|
42
|
+
- Single project → [Standalone Project](#standalone-project)
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Stack Assumptions
|
|
47
|
+
|
|
48
|
+
Unless told otherwise, assume:
|
|
49
|
+
- **Vitest** as the test runner
|
|
50
|
+
- **@testing-library/react** + **@testing-library/user-event** for component/hook tests
|
|
51
|
+
- **@testing-library/jest-dom** for DOM matchers (`toBeInTheDocument`, etc.)
|
|
52
|
+
- **MSW v2** for API mocking (when network calls are involved)
|
|
53
|
+
- **TypeScript** throughout
|
|
54
|
+
|
|
55
|
+
If the user's stack differs from any of the above, adapt accordingly.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Quick Decision Tree
|
|
60
|
+
|
|
61
|
+
Before writing any test, identify what you're testing:
|
|
62
|
+
|
|
63
|
+
| Subject | Strategy | Reference |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| Pure function / utility | Unit test, no rendering | [→ Utilities](#utilities) |
|
|
66
|
+
| Custom React hook | `renderHook` from testing-library | [→ Hooks](#hooks) |
|
|
67
|
+
| React component (presentational) | `render` + assertions | [→ Components](#components) |
|
|
68
|
+
| React component (with data fetching) | MSW + `render` | [→ Async Components](#async-components) |
|
|
69
|
+
| Next.js page / route handler | Per-file mocking of `next/*` | [→ Next.js Specifics](#nextjs-specifics) |
|
|
70
|
+
| Context / Provider | Wrap with provider in `render` | [→ Components](#components) |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Standalone Project
|
|
75
|
+
|
|
76
|
+
A single `package.json`, no workspaces. Simple and self-contained.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// vitest.config.ts
|
|
80
|
+
import { defineConfig } from 'vitest/config'
|
|
81
|
+
import react from '@vitejs/plugin-react'
|
|
82
|
+
import tsconfigPaths from 'vite-tsconfig-paths'
|
|
83
|
+
|
|
84
|
+
export default defineConfig({
|
|
85
|
+
plugins: [react(), tsconfigPaths()],
|
|
86
|
+
test: {
|
|
87
|
+
environment: 'jsdom',
|
|
88
|
+
globals: true,
|
|
89
|
+
setupFiles: ['./src/test/setup.ts'],
|
|
90
|
+
coverage: {
|
|
91
|
+
provider: 'v8',
|
|
92
|
+
reporter: ['text', 'lcov'],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// src/test/setup.ts
|
|
100
|
+
import '@testing-library/jest-dom'
|
|
101
|
+
import { afterEach } from 'vitest'
|
|
102
|
+
import { cleanup } from '@testing-library/react'
|
|
103
|
+
|
|
104
|
+
afterEach(() => cleanup())
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Run: `vitest` / `vitest run --coverage`
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Generic Monorepo
|
|
112
|
+
|
|
113
|
+
A monorepo without Turborepo (Lerna, Nx, bare pnpm workspaces, etc.). Each package has its own `vitest.config.ts`; a root `vitest.workspace.ts` ties them together.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// vitest.workspace.ts (root)
|
|
117
|
+
import { defineWorkspace } from 'vitest/config'
|
|
118
|
+
|
|
119
|
+
export default defineWorkspace([
|
|
120
|
+
'packages/*/vitest.config.ts',
|
|
121
|
+
'apps/*/vitest.config.ts',
|
|
122
|
+
])
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Per-package config and shared `test-utils` setup: see [`references/monorepo-setup.md`](./references/monorepo-setup.md).
|
|
126
|
+
|
|
127
|
+
Run all: `vitest --workspace` | Single package: `vitest --project ui`
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Turborepo Monorepo
|
|
132
|
+
|
|
133
|
+
> **Use this section when `turbo.json` is present at the root.**
|
|
134
|
+
|
|
135
|
+
For the complete Turborepo setup guide, read [`references/turborepo-setup.md`](./references/turborepo-setup.md).
|
|
136
|
+
|
|
137
|
+
**Key differences from a generic monorepo:**
|
|
138
|
+
|
|
139
|
+
- Tests are run **per-package** via `turbo run test` — Turborepo caches results and only re-runs packages affected by changes
|
|
140
|
+
- Each package must declare a `"test"` script in its `package.json`
|
|
141
|
+
- A root `vitest.workspace.ts` is **optional** — Turborepo orchestrates runs itself, so you may not need it
|
|
142
|
+
- `turbo.json` must declare `test` as a task with correct `inputs` so caching works correctly
|
|
143
|
+
|
|
144
|
+
**Minimal `turbo.json` task definition:**
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"$schema": "https://turbo.build/schema.json",
|
|
149
|
+
"tasks": {
|
|
150
|
+
"test": {
|
|
151
|
+
"dependsOn": ["^build"],
|
|
152
|
+
"inputs": ["src/**/*.ts", "src/**/*.tsx", "**/*.test.ts", "**/*.test.tsx"],
|
|
153
|
+
"outputs": ["coverage/**"],
|
|
154
|
+
"cache": true
|
|
155
|
+
},
|
|
156
|
+
"test:watch": {
|
|
157
|
+
"cache": false,
|
|
158
|
+
"persistent": true
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Each package's `package.json`:**
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"scripts": {
|
|
169
|
+
"test": "vitest run",
|
|
170
|
+
"test:watch": "vitest",
|
|
171
|
+
"test:coverage": "vitest run --coverage"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Running tests:**
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# All packages (respects cache — skips unchanged packages)
|
|
180
|
+
turbo run test
|
|
181
|
+
|
|
182
|
+
# Force re-run everything, ignore cache
|
|
183
|
+
turbo run test --force
|
|
184
|
+
|
|
185
|
+
# Only packages affected by current git changes
|
|
186
|
+
turbo run test --filter=...[HEAD^1]
|
|
187
|
+
|
|
188
|
+
# Single package by name
|
|
189
|
+
turbo run test --filter=@acme/ui
|
|
190
|
+
|
|
191
|
+
# Single package + its dependents
|
|
192
|
+
turbo run test --filter=@acme/ui...
|
|
193
|
+
|
|
194
|
+
# Watch mode (not cached — use directly in the package)
|
|
195
|
+
cd packages/ui && pnpm test:watch
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
> ⚠️ Don't run `vitest --workspace` from the root in a Turborepo project — it bypasses Turborepo's cache and task graph. Always go through `turbo run test`.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Utilities
|
|
203
|
+
|
|
204
|
+
Pure functions are the easiest to test. Be exhaustive: happy path, edge cases, error cases.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
// packages/utils/src/format.test.ts
|
|
208
|
+
import { describe, it, expect } from 'vitest'
|
|
209
|
+
import { formatCurrency } from './format'
|
|
210
|
+
|
|
211
|
+
describe('formatCurrency', () => {
|
|
212
|
+
it('formats a positive number', () => {
|
|
213
|
+
expect(formatCurrency(1234.5)).toBe('$1,234.50')
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('handles zero', () => {
|
|
217
|
+
expect(formatCurrency(0)).toBe('$0.00')
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('handles negative numbers', () => {
|
|
221
|
+
expect(formatCurrency(-50)).toBe('-$50.00')
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('throws for non-numeric input', () => {
|
|
225
|
+
expect(() => formatCurrency(NaN)).toThrow()
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Checklist:**
|
|
231
|
+
- [ ] Happy path
|
|
232
|
+
- [ ] Boundary / edge values (0, empty string, null, undefined)
|
|
233
|
+
- [ ] Error / invalid input
|
|
234
|
+
- [ ] All significant branches of logic
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Hooks
|
|
239
|
+
|
|
240
|
+
Use `renderHook` + `act` for custom hooks. Never test implementation details — test observable behavior.
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
// packages/hooks/src/useCounter.test.ts
|
|
244
|
+
import { describe, it, expect } from 'vitest'
|
|
245
|
+
import { renderHook, act } from '@testing-library/react'
|
|
246
|
+
import { useCounter } from './useCounter'
|
|
247
|
+
|
|
248
|
+
describe('useCounter', () => {
|
|
249
|
+
it('initializes with the given value', () => {
|
|
250
|
+
const { result } = renderHook(() => useCounter(5))
|
|
251
|
+
expect(result.current.count).toBe(5)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it('increments the count', () => {
|
|
255
|
+
const { result } = renderHook(() => useCounter(0))
|
|
256
|
+
act(() => result.current.increment())
|
|
257
|
+
expect(result.current.count).toBe(1)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
it('does not go below 0 when decrementing', () => {
|
|
261
|
+
const { result } = renderHook(() => useCounter(0))
|
|
262
|
+
act(() => result.current.decrement())
|
|
263
|
+
expect(result.current.count).toBe(0)
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Hooks that use context
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import { renderHook } from '@testing-library/react'
|
|
272
|
+
import { ThemeProvider } from '../ThemeProvider'
|
|
273
|
+
|
|
274
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
275
|
+
<ThemeProvider defaultTheme="dark">{children}</ThemeProvider>
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
const { result } = renderHook(() => useTheme(), { wrapper })
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Hooks with async operations
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
import { waitFor } from '@testing-library/react'
|
|
285
|
+
|
|
286
|
+
it('fetches user data', async () => {
|
|
287
|
+
const { result } = renderHook(() => useUser('user-1'))
|
|
288
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false))
|
|
289
|
+
expect(result.current.data?.name).toBe('Alice')
|
|
290
|
+
})
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Components
|
|
296
|
+
|
|
297
|
+
### Basic rendering and interactions
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
// packages/ui/src/Button.test.tsx
|
|
301
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
302
|
+
import { render, screen } from '@testing-library/react'
|
|
303
|
+
import userEvent from '@testing-library/user-event'
|
|
304
|
+
import { Button } from './Button'
|
|
305
|
+
|
|
306
|
+
describe('Button', () => {
|
|
307
|
+
it('renders its label', () => {
|
|
308
|
+
render(<Button>Save</Button>)
|
|
309
|
+
expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument()
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('calls onClick when clicked', async () => {
|
|
313
|
+
const user = userEvent.setup()
|
|
314
|
+
const handleClick = vi.fn()
|
|
315
|
+
render(<Button onClick={handleClick}>Save</Button>)
|
|
316
|
+
await user.click(screen.getByRole('button', { name: 'Save' }))
|
|
317
|
+
expect(handleClick).toHaveBeenCalledOnce()
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it('is disabled when the disabled prop is set', () => {
|
|
321
|
+
render(<Button disabled>Save</Button>)
|
|
322
|
+
expect(screen.getByRole('button')).toBeDisabled()
|
|
323
|
+
})
|
|
324
|
+
})
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Query priority (in order of preference)
|
|
328
|
+
|
|
329
|
+
1. `getByRole` — mirrors how assistive tech sees the page
|
|
330
|
+
2. `getByLabelText` — for form elements
|
|
331
|
+
3. `getByPlaceholderText` — fallback for inputs
|
|
332
|
+
4. `getByText` — for non-interactive text
|
|
333
|
+
5. `getByTestId` — last resort only
|
|
334
|
+
|
|
335
|
+
Avoid `getByClassName` or DOM traversal — tests will break on refactors.
|
|
336
|
+
|
|
337
|
+
### Form interactions
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
it('submits with the entered email', async () => {
|
|
341
|
+
const user = userEvent.setup()
|
|
342
|
+
const onSubmit = vi.fn()
|
|
343
|
+
render(<LoginForm onSubmit={onSubmit} />)
|
|
344
|
+
|
|
345
|
+
await user.type(screen.getByLabelText('Email'), 'alice@example.com')
|
|
346
|
+
await user.type(screen.getByLabelText('Password'), 'secret123')
|
|
347
|
+
await user.click(screen.getByRole('button', { name: /sign in/i }))
|
|
348
|
+
|
|
349
|
+
expect(onSubmit).toHaveBeenCalledWith({
|
|
350
|
+
email: 'alice@example.com',
|
|
351
|
+
password: 'secret123',
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Testing error states and conditional rendering
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
it('shows validation error for an empty email', async () => {
|
|
360
|
+
const user = userEvent.setup()
|
|
361
|
+
render(<LoginForm onSubmit={vi.fn()} />)
|
|
362
|
+
await user.click(screen.getByRole('button', { name: /sign in/i }))
|
|
363
|
+
expect(screen.getByText('Email is required')).toBeInTheDocument()
|
|
364
|
+
})
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Async Components
|
|
370
|
+
|
|
371
|
+
Use **MSW v2** to intercept fetch/axios calls at the network level.
|
|
372
|
+
|
|
373
|
+
### MSW setup (`src/test/server.ts`)
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
import { setupServer } from 'msw/node'
|
|
377
|
+
import { http, HttpResponse } from 'msw'
|
|
378
|
+
|
|
379
|
+
export const handlers = [
|
|
380
|
+
http.get('/api/users/:id', ({ params }) => {
|
|
381
|
+
return HttpResponse.json({ id: params.id, name: 'Alice' })
|
|
382
|
+
}),
|
|
383
|
+
]
|
|
384
|
+
|
|
385
|
+
export const server = setupServer(...handlers)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Wire up MSW in `setup.ts`
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
import { server } from './server'
|
|
392
|
+
import { beforeAll, afterEach, afterAll } from 'vitest'
|
|
393
|
+
|
|
394
|
+
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
|
|
395
|
+
afterEach(() => server.resetHandlers())
|
|
396
|
+
afterAll(() => server.close())
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Override handlers per-test
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
import { http, HttpResponse } from 'msw'
|
|
403
|
+
import { server } from '@/test/server'
|
|
404
|
+
|
|
405
|
+
it('shows an error on API failure', async () => {
|
|
406
|
+
server.use(
|
|
407
|
+
http.get('/api/users/:id', () => HttpResponse.json({ message: 'Not found' }, { status: 404 }))
|
|
408
|
+
)
|
|
409
|
+
render(<UserProfile userId="user-1" />)
|
|
410
|
+
expect(await screen.findByText('User not found')).toBeInTheDocument()
|
|
411
|
+
})
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## Mocking
|
|
417
|
+
|
|
418
|
+
### Module mocks (`vi.mock`)
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
// Mock an entire module
|
|
422
|
+
vi.mock('@/lib/analytics', () => ({
|
|
423
|
+
track: vi.fn(),
|
|
424
|
+
identify: vi.fn(),
|
|
425
|
+
}))
|
|
426
|
+
|
|
427
|
+
// Access the mock in tests
|
|
428
|
+
import { track } from '@/lib/analytics'
|
|
429
|
+
expect(vi.mocked(track)).toHaveBeenCalledWith('button_clicked', { label: 'Save' })
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Partial mocks
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
vi.mock('@/lib/date', async (importOriginal) => {
|
|
436
|
+
const actual = await importOriginal<typeof import('@/lib/date')>()
|
|
437
|
+
return { ...actual, now: vi.fn(() => new Date('2024-01-01')) }
|
|
438
|
+
})
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Spying on methods
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
import * as storageModule from '@/lib/storage'
|
|
445
|
+
|
|
446
|
+
it('calls localStorage.setItem', () => {
|
|
447
|
+
const spy = vi.spyOn(storageModule, 'saveToStorage')
|
|
448
|
+
render(<SettingsForm />)
|
|
449
|
+
// ... interact
|
|
450
|
+
expect(spy).toHaveBeenCalled()
|
|
451
|
+
})
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Mocking `next/navigation`
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
vi.mock('next/navigation', () => ({
|
|
458
|
+
useRouter: () => ({ push: vi.fn(), replace: vi.fn(), back: vi.fn() }),
|
|
459
|
+
usePathname: () => '/dashboard',
|
|
460
|
+
useSearchParams: () => new URLSearchParams(),
|
|
461
|
+
}))
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Mocking `next/image` and `next/link`
|
|
465
|
+
|
|
466
|
+
```ts
|
|
467
|
+
vi.mock('next/image', () => ({
|
|
468
|
+
default: (props: React.ImgHTMLAttributes<HTMLImageElement>) => <img {...props} />,
|
|
469
|
+
}))
|
|
470
|
+
|
|
471
|
+
vi.mock('next/link', () => ({
|
|
472
|
+
default: ({ href, children }: { href: string; children: React.ReactNode }) => (
|
|
473
|
+
<a href={href}>{children}</a>
|
|
474
|
+
),
|
|
475
|
+
}))
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## Next.js Specifics
|
|
481
|
+
|
|
482
|
+
### Server Components
|
|
483
|
+
|
|
484
|
+
Server Components can't be rendered with testing-library directly. Two strategies:
|
|
485
|
+
|
|
486
|
+
1. **Extract logic** into a utility or hook and test that instead (preferred)
|
|
487
|
+
2. **Integration test** via Playwright/Cypress for full page behavior
|
|
488
|
+
|
|
489
|
+
### Route Handlers (`app/api/*/route.ts`)
|
|
490
|
+
|
|
491
|
+
```ts
|
|
492
|
+
// app/api/hello/route.test.ts
|
|
493
|
+
import { GET } from './route'
|
|
494
|
+
import { NextRequest } from 'next/server'
|
|
495
|
+
|
|
496
|
+
it('returns 200 with a greeting', async () => {
|
|
497
|
+
const req = new NextRequest('http://localhost/api/hello')
|
|
498
|
+
const res = await GET(req)
|
|
499
|
+
const body = await res.json()
|
|
500
|
+
|
|
501
|
+
expect(res.status).toBe(200)
|
|
502
|
+
expect(body.message).toBe('Hello')
|
|
503
|
+
})
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Middleware
|
|
507
|
+
|
|
508
|
+
Test middleware by importing the function directly and passing a mock `NextRequest`:
|
|
509
|
+
|
|
510
|
+
```ts
|
|
511
|
+
import { middleware } from './middleware'
|
|
512
|
+
import { NextRequest } from 'next/server'
|
|
513
|
+
|
|
514
|
+
it('redirects unauthenticated users', async () => {
|
|
515
|
+
const req = new NextRequest('http://localhost/dashboard')
|
|
516
|
+
const res = await middleware(req)
|
|
517
|
+
expect(res.status).toBe(307)
|
|
518
|
+
expect(res.headers.get('location')).toContain('/login')
|
|
519
|
+
})
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Shared Packages (Monorepo Patterns)
|
|
525
|
+
|
|
526
|
+
### Path aliases across packages
|
|
527
|
+
|
|
528
|
+
In each package's `vitest.config.ts`, use `vite-tsconfig-paths` so `@/` aliases resolve correctly:
|
|
529
|
+
|
|
530
|
+
```ts
|
|
531
|
+
import tsconfigPaths from 'vite-tsconfig-paths'
|
|
532
|
+
// add to plugins: [tsconfigPaths()]
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Shared test utilities
|
|
536
|
+
|
|
537
|
+
Create a `packages/test-utils` package (works for both Turborepo and generic monorepos):
|
|
538
|
+
|
|
539
|
+
```ts
|
|
540
|
+
// packages/test-utils/src/render.tsx
|
|
541
|
+
import { render as rtlRender, RenderOptions } from '@testing-library/react'
|
|
542
|
+
import { AllProviders } from './AllProviders' // QueryClient, Theme, etc.
|
|
543
|
+
|
|
544
|
+
export function render(ui: React.ReactElement, options?: RenderOptions) {
|
|
545
|
+
return rtlRender(ui, { wrapper: AllProviders, ...options })
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export * from '@testing-library/react'
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Import this custom `render` in all tests to avoid boilerplate provider wrapping.
|
|
552
|
+
|
|
553
|
+
**In Turborepo:** make sure `@acme/test-utils` is listed as a `devDependency` in each package that uses it, and that `turbo.json` includes `"^build"` in the `test` task's `dependsOn` so the test-utils package is built before tests run.
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## Best Practices
|
|
558
|
+
|
|
559
|
+
**DO:**
|
|
560
|
+
- Test behavior, not implementation (no asserting on internal state or private methods)
|
|
561
|
+
- Use `screen` queries over destructured render results
|
|
562
|
+
- Use `userEvent` over `fireEvent` (more realistic)
|
|
563
|
+
- Use `findBy*` for async elements (it automatically waits)
|
|
564
|
+
- Group related tests with `describe`
|
|
565
|
+
- Name tests as sentences: `it('shows an error when the email is invalid')`
|
|
566
|
+
- Clean up mocks in `afterEach` or use `vi.restoreAllMocks()`
|
|
567
|
+
|
|
568
|
+
**DON'T:**
|
|
569
|
+
- Assert on CSS classes or styles (fragile)
|
|
570
|
+
- Use `getByTestId` unless absolutely necessary
|
|
571
|
+
- Test third-party library behavior
|
|
572
|
+
- Snapshot test large component trees (hard to maintain)
|
|
573
|
+
- Use `waitFor` just to delay — prefer `findBy*` for element appearance
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## Coverage Targets (suggested)
|
|
578
|
+
|
|
579
|
+
| Package type | Line coverage target |
|
|
580
|
+
|---|---|
|
|
581
|
+
| Utilities / pure functions | 95%+ |
|
|
582
|
+
| Hooks | 85%+ |
|
|
583
|
+
| UI components | 75%+ |
|
|
584
|
+
| Next.js pages | 60%+ (supplement with e2e) |
|
|
585
|
+
|
|
586
|
+
Run coverage: `vitest run --coverage`
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
## Reference Files
|
|
591
|
+
|
|
592
|
+
- [→ `references/mock-patterns.md`](./references/mock-patterns.md) — Advanced mocking patterns (timers, globals, env vars)
|
|
593
|
+
- [→ `references/async-patterns.md`](./references/async-patterns.md) — Async testing, loading states, error boundaries
|
|
594
|
+
- [→ `references/monorepo-setup.md`](./references/monorepo-setup.md) — Generic monorepo config (shared test-utils, path aliases, workspace setup)
|
|
595
|
+
- [→ `references/turborepo-setup.md`](./references/turborepo-setup.md) — Full Turborepo setup (task config, caching, pipeline, CI)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Async Testing Patterns
|
|
2
|
+
|
|
3
|
+
## Loading states
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
it('shows a spinner while loading', () => {
|
|
7
|
+
render(<UserProfile userId="1" />)
|
|
8
|
+
expect(screen.getByRole('progressbar')).toBeInTheDocument()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('shows user data after loading', async () => {
|
|
12
|
+
render(<UserProfile userId="1" />)
|
|
13
|
+
expect(await screen.findByText('Alice')).toBeInTheDocument()
|
|
14
|
+
expect(screen.queryByRole('progressbar')).not.toBeInTheDocument()
|
|
15
|
+
})
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Error boundaries
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// Suppress the React error boundary console output
|
|
22
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
23
|
+
|
|
24
|
+
it('renders the fallback on error', () => {
|
|
25
|
+
const ThrowingComponent = () => { throw new Error('Boom') }
|
|
26
|
+
render(
|
|
27
|
+
<ErrorBoundary fallback={<p>Something went wrong</p>}>
|
|
28
|
+
<ThrowingComponent />
|
|
29
|
+
</ErrorBoundary>
|
|
30
|
+
)
|
|
31
|
+
expect(screen.getByText('Something went wrong')).toBeInTheDocument()
|
|
32
|
+
consoleSpy.mockRestore()
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Polling / intervals
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
beforeEach(() => vi.useFakeTimers())
|
|
40
|
+
afterEach(() => vi.useRealTimers())
|
|
41
|
+
|
|
42
|
+
it('re-fetches every 5 seconds', async () => {
|
|
43
|
+
render(<LivePriceWidget />)
|
|
44
|
+
expect(fetch).toHaveBeenCalledTimes(1)
|
|
45
|
+
vi.advanceTimersByTime(5000)
|
|
46
|
+
expect(fetch).toHaveBeenCalledTimes(2)
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Waiting for element removal
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
it('removes the toast after 3 seconds', async () => {
|
|
54
|
+
render(<App />)
|
|
55
|
+
userEvent.click(screen.getByRole('button', { name: /notify/i }))
|
|
56
|
+
expect(screen.getByRole('alert')).toBeInTheDocument()
|
|
57
|
+
await waitForElementToBeRemoved(() => screen.queryByRole('alert'), { timeout: 4000 })
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## React Query / SWR
|
|
62
|
+
|
|
63
|
+
Wrap with a fresh QueryClient per test to avoid cache bleed:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
67
|
+
|
|
68
|
+
function createWrapper() {
|
|
69
|
+
const queryClient = new QueryClient({
|
|
70
|
+
defaultOptions: { queries: { retry: false } },
|
|
71
|
+
})
|
|
72
|
+
return ({ children }: { children: React.ReactNode }) => (
|
|
73
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
it('fetches and displays the user', async () => {
|
|
78
|
+
const { result } = renderHook(() => useUser('1'), { wrapper: createWrapper() })
|
|
79
|
+
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
|
80
|
+
expect(result.current.data?.name).toBe('Alice')
|
|
81
|
+
})
|
|
82
|
+
```
|