mdk-skills 2.2.0 → 2.2.2

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.
Files changed (191) hide show
  1. package/.claude/.install.log +31 -0
  2. package/.claude/backups/20260510.145547/.install.log +18 -0
  3. package/.claude/backups/20260510.145547/skills/test1/.meta.json +6 -0
  4. package/.claude/backups/20260510.145547/skills/test2/.meta.json +6 -0
  5. package/.claude/backups/20260510.145651/.install.log +19 -0
  6. package/.claude/backups/20260510.145651/profiles.json +67 -0
  7. package/.claude/backups/20260510.145651/settings.json +29 -0
  8. package/.claude/backups/20260510.145651/skills/frontend-code-review/.meta.json +6 -0
  9. package/.claude/backups/20260510.145651/skills/frontend-code-review/SKILL.md +167 -0
  10. package/.claude/backups/20260510.145651/skills/frontend-code-review/references/checklist.md +298 -0
  11. package/.claude/backups/20260510.145651/skills/frontend-design/.meta.json +6 -0
  12. package/.claude/backups/20260510.145651/skills/frontend-design/LICENSE.txt +177 -0
  13. package/.claude/backups/20260510.145651/skills/frontend-design/SKILL.md +42 -0
  14. package/.claude/backups/20260510.145651/skills/skill-creator/.meta.json +6 -0
  15. package/.claude/backups/20260510.145651/skills/skill-creator/SKILL.md +356 -0
  16. package/.claude/backups/20260510.145651/skills/skill-creator/references/output-patterns.md +82 -0
  17. package/.claude/backups/20260510.145651/skills/skill-creator/references/workflows.md +28 -0
  18. package/.claude/backups/20260510.145651/skills/skill-creator/scripts/init_skill.py +303 -0
  19. package/.claude/backups/20260510.145651/skills/skill-creator/scripts/package_skill.py +110 -0
  20. package/.claude/backups/20260510.145651/skills/skill-creator/scripts/quick_validate.py +95 -0
  21. package/.claude/backups/20260510.145651/skills/test1/.meta.json +6 -0
  22. package/.claude/backups/20260510.145651/skills/test2/.meta.json +6 -0
  23. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/.meta.json +6 -0
  24. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/SKILL.md +228 -0
  25. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/charts.csv +26 -0
  26. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/colors.csv +97 -0
  27. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/landing.csv +31 -0
  28. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/products.csv +97 -0
  29. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/prompts.csv +24 -0
  30. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  31. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  32. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  33. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  34. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  35. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  36. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  37. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  38. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  39. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  40. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/styles.csv +59 -0
  41. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/typography.csv +58 -0
  42. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  43. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/scripts/core.py +238 -0
  44. package/.claude/backups/20260510.145651/skills/ui-ux-pro-max/scripts/search.py +61 -0
  45. package/.claude/backups/20260510.145651/skills/v3-fe-biz-patterns/.meta.json +6 -0
  46. package/.claude/backups/20260510.145651/skills/v3-fe-biz-patterns/SKILL.md +26 -0
  47. package/.claude/backups/20260510.145651/skills/v3-fe-biz-patterns/references/infinite-scroll.md +292 -0
  48. package/.claude/backups/20260510.145651/skills/v3-fe-biz-patterns/references/pinia-store.md +174 -0
  49. package/.claude/backups/20260510.145651/skills/v3-fe-biz-patterns/references/service-layer.md +198 -0
  50. package/.claude/backups/20260510.145651/skills/v3-fe-biz-patterns/references/tab-anchor.md +1125 -0
  51. package/.claude/backups/20260510.145651/skills/v3-fe-biz-patterns/references/use-loading.md +114 -0
  52. package/.claude/backups/20260510.145651/skills/vue/.meta.json +6 -0
  53. package/.claude/backups/20260510.145651/skills/vue/SKILL.md +103 -0
  54. package/.claude/backups/20260510.145651/skills/vue/references/components.md +323 -0
  55. package/.claude/backups/20260510.145651/skills/vue/references/composables.md +358 -0
  56. package/.claude/backups/20260510.145651/skills/vue/references/directives.md +225 -0
  57. package/.claude/backups/20260510.145651/skills/vue/references/gotchas.md +438 -0
  58. package/.claude/backups/20260510.145651/skills/vue/references/provide-inject.md +174 -0
  59. package/.claude/backups/20260510.145651/skills/vue/references/reactivity.md +289 -0
  60. package/.claude/backups/20260510.145651/skills/vue/references/router.md +181 -0
  61. package/.claude/backups/20260510.145651/skills/vue/references/testing.md +294 -0
  62. package/.claude/backups/20260510.145651/skills/vue/references/typescript.md +172 -0
  63. package/.claude/backups/20260510.145651/skills/vue/references/utils-client.md +156 -0
  64. package/.claude/backups/20260510.150310/.install.log +30 -0
  65. package/.claude/backups/20260510.150310/profiles.json +67 -0
  66. package/.claude/backups/20260510.150310/settings.json +29 -0
  67. package/.claude/backups/20260510.150310/skills/frontend-code-review/.meta.json +6 -0
  68. package/.claude/backups/20260510.150310/skills/frontend-code-review/SKILL.md +167 -0
  69. package/.claude/backups/20260510.150310/skills/frontend-code-review/references/checklist.md +298 -0
  70. package/.claude/backups/20260510.150310/skills/frontend-design/.meta.json +6 -0
  71. package/.claude/backups/20260510.150310/skills/frontend-design/LICENSE.txt +177 -0
  72. package/.claude/backups/20260510.150310/skills/frontend-design/SKILL.md +42 -0
  73. package/.claude/backups/20260510.150310/skills/skill-creator/.meta.json +6 -0
  74. package/.claude/backups/20260510.150310/skills/skill-creator/SKILL.md +356 -0
  75. package/.claude/backups/20260510.150310/skills/skill-creator/references/output-patterns.md +82 -0
  76. package/.claude/backups/20260510.150310/skills/skill-creator/references/workflows.md +28 -0
  77. package/.claude/backups/20260510.150310/skills/skill-creator/scripts/init_skill.py +303 -0
  78. package/.claude/backups/20260510.150310/skills/skill-creator/scripts/package_skill.py +110 -0
  79. package/.claude/backups/20260510.150310/skills/skill-creator/scripts/quick_validate.py +95 -0
  80. package/.claude/backups/20260510.150310/skills/test1/.meta.json +6 -0
  81. package/.claude/backups/20260510.150310/skills/test2/.meta.json +6 -0
  82. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/.meta.json +6 -0
  83. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/SKILL.md +228 -0
  84. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/charts.csv +26 -0
  85. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/colors.csv +97 -0
  86. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/landing.csv +31 -0
  87. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/products.csv +97 -0
  88. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/prompts.csv +24 -0
  89. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  90. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  91. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  92. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  93. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  94. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  95. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  96. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  97. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  98. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  99. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/styles.csv +59 -0
  100. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/typography.csv +58 -0
  101. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  102. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/scripts/core.py +238 -0
  103. package/.claude/backups/20260510.150310/skills/ui-ux-pro-max/scripts/search.py +61 -0
  104. package/.claude/backups/20260510.150310/skills/v3-fe-biz-patterns/.meta.json +6 -0
  105. package/.claude/backups/20260510.150310/skills/v3-fe-biz-patterns/SKILL.md +26 -0
  106. package/.claude/backups/20260510.150310/skills/v3-fe-biz-patterns/references/infinite-scroll.md +292 -0
  107. package/.claude/backups/20260510.150310/skills/v3-fe-biz-patterns/references/pinia-store.md +174 -0
  108. package/.claude/backups/20260510.150310/skills/v3-fe-biz-patterns/references/service-layer.md +198 -0
  109. package/.claude/backups/20260510.150310/skills/v3-fe-biz-patterns/references/tab-anchor.md +1125 -0
  110. package/.claude/backups/20260510.150310/skills/v3-fe-biz-patterns/references/use-loading.md +114 -0
  111. package/.claude/backups/20260510.150310/skills/vue/.meta.json +6 -0
  112. package/.claude/backups/20260510.150310/skills/vue/SKILL.md +103 -0
  113. package/.claude/backups/20260510.150310/skills/vue/references/components.md +323 -0
  114. package/.claude/backups/20260510.150310/skills/vue/references/composables.md +358 -0
  115. package/.claude/backups/20260510.150310/skills/vue/references/directives.md +225 -0
  116. package/.claude/backups/20260510.150310/skills/vue/references/gotchas.md +438 -0
  117. package/.claude/backups/20260510.150310/skills/vue/references/provide-inject.md +174 -0
  118. package/.claude/backups/20260510.150310/skills/vue/references/reactivity.md +289 -0
  119. package/.claude/backups/20260510.150310/skills/vue/references/router.md +181 -0
  120. package/.claude/backups/20260510.150310/skills/vue/references/testing.md +294 -0
  121. package/.claude/backups/20260510.150310/skills/vue/references/typescript.md +172 -0
  122. package/.claude/backups/20260510.150310/skills/vue/references/utils-client.md +156 -0
  123. package/.claude/backups/CLAUDE.md.20260510.145155 +131 -0
  124. package/.claude/backups/CLAUDE.md.20260510.145651 +131 -0
  125. package/.claude/backups/CLAUDE.md.20260510.150310 +131 -0
  126. package/.claude/settings.json +1 -7
  127. package/.claude/skills/test1/.meta.json +6 -0
  128. package/.claude/skills/test2/.meta.json +6 -0
  129. package/package.json +1 -1
  130. package/scripts/cli.js +1 -1
  131. package/scripts/web-ui/server.js +2 -1
  132. package/scripts/web-ui/src/App.vue +1 -2
  133. package/scripts/web-ui/src/api/skills.js +5 -1
  134. package/scripts/web-ui/src/views/Dashboard.vue +12 -1
  135. package/.claude/backups/20260510.144501/.install.log +0 -1
  136. /package/.claude/backups/{20260510.144501 → 20260510.145547}/profiles.json +0 -0
  137. /package/.claude/backups/{20260510.144501 → 20260510.145547}/settings.json +0 -0
  138. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/frontend-code-review/.meta.json +0 -0
  139. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/frontend-code-review/SKILL.md +0 -0
  140. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/frontend-code-review/references/checklist.md +0 -0
  141. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/frontend-design/.meta.json +0 -0
  142. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/frontend-design/LICENSE.txt +0 -0
  143. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/frontend-design/SKILL.md +0 -0
  144. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/skill-creator/.meta.json +0 -0
  145. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/skill-creator/SKILL.md +0 -0
  146. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/skill-creator/references/output-patterns.md +0 -0
  147. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/skill-creator/references/workflows.md +0 -0
  148. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/skill-creator/scripts/init_skill.py +0 -0
  149. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/skill-creator/scripts/package_skill.py +0 -0
  150. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/skill-creator/scripts/quick_validate.py +0 -0
  151. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/.meta.json +0 -0
  152. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/SKILL.md +0 -0
  153. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/charts.csv +0 -0
  154. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/colors.csv +0 -0
  155. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/landing.csv +0 -0
  156. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/products.csv +0 -0
  157. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/prompts.csv +0 -0
  158. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -0
  159. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -0
  160. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -0
  161. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -0
  162. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -0
  163. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -0
  164. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/react.csv +0 -0
  165. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -0
  166. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -0
  167. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -0
  168. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/styles.csv +0 -0
  169. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/typography.csv +0 -0
  170. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -0
  171. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/scripts/core.py +0 -0
  172. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/ui-ux-pro-max/scripts/search.py +0 -0
  173. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/v3-fe-biz-patterns/.meta.json +0 -0
  174. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/v3-fe-biz-patterns/SKILL.md +0 -0
  175. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/v3-fe-biz-patterns/references/infinite-scroll.md +0 -0
  176. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/v3-fe-biz-patterns/references/pinia-store.md +0 -0
  177. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/v3-fe-biz-patterns/references/service-layer.md +0 -0
  178. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/v3-fe-biz-patterns/references/tab-anchor.md +0 -0
  179. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/v3-fe-biz-patterns/references/use-loading.md +0 -0
  180. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/.meta.json +0 -0
  181. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/SKILL.md +0 -0
  182. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/components.md +0 -0
  183. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/composables.md +0 -0
  184. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/directives.md +0 -0
  185. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/gotchas.md +0 -0
  186. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/provide-inject.md +0 -0
  187. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/reactivity.md +0 -0
  188. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/router.md +0 -0
  189. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/testing.md +0 -0
  190. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/typescript.md +0 -0
  191. /package/.claude/backups/{20260510.144501 → 20260510.145547}/skills/vue/references/utils-client.md +0 -0
@@ -0,0 +1,294 @@
1
+ # Vue Testing
2
+
3
+ Test patterns for Vue 3 components, composables, and utilities.
4
+
5
+ ## Quick Reference
6
+
7
+ | Test Type | Pattern |
8
+ | ---------------- | ------------------------------------ |
9
+ | Component | `mount(Component, { props, slots })` |
10
+ | User interaction | `await wrapper.trigger('click')` |
11
+ | Emitted events | `wrapper.emitted('update')` |
12
+ | Composable | Call directly, test return values |
13
+ | Utils | Pure function testing (easiest) |
14
+
15
+ ## Stack
16
+
17
+ - **Vitest** - test runner
18
+ - **@vue/test-utils** - component mounting, interaction
19
+ - **@testing-library/vue** - user-centric alternative
20
+ - **happy-dom / jsdom** - DOM environment
21
+
22
+ ## File Location
23
+
24
+ Colocate tests with code:
25
+
26
+ ```
27
+ Button.vue → Button.spec.ts
28
+ useAuth.ts → useAuth.spec.ts
29
+ formatters.ts → formatters.spec.ts
30
+ ```
31
+
32
+ ## Component Tests
33
+
34
+ ### Basic
35
+
36
+ ```ts
37
+ import { mount } from '@vue/test-utils'
38
+ import Button from './Button.vue'
39
+
40
+ it('renders slot', () => {
41
+ const wrapper = mount(Button, {
42
+ slots: { default: 'Click me' }
43
+ })
44
+ expect(wrapper.text()).toBe('Click me')
45
+ })
46
+
47
+ it('emits on click', async () => {
48
+ const wrapper = mount(Button)
49
+ await wrapper.trigger('click')
50
+ expect(wrapper.emitted('click')).toHaveLength(1)
51
+ })
52
+ ```
53
+
54
+ ### Props
55
+
56
+ ```ts
57
+ it('applies variant class', () => {
58
+ const wrapper = mount(Button, {
59
+ props: { variant: 'primary' }
60
+ })
61
+ expect(wrapper.classes()).toContain('btn-primary')
62
+ })
63
+ ```
64
+
65
+ ### Emits
66
+
67
+ ```ts
68
+ it('emits update with payload', async () => {
69
+ const wrapper = mount(Input)
70
+ await wrapper.find('input').setValue('new value')
71
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['new value'])
72
+ })
73
+ ```
74
+
75
+ ### Slots
76
+
77
+ ```ts
78
+ it('renders named slots', () => {
79
+ const wrapper = mount(Card, {
80
+ slots: {
81
+ header: '<h1>Title</h1>',
82
+ default: '<p>Content</p>'
83
+ }
84
+ })
85
+ expect(wrapper.html()).toContain('<h1>Title</h1>')
86
+ })
87
+ ```
88
+
89
+ ## Composable Tests
90
+
91
+ Call directly, no mounting needed:
92
+
93
+ ```ts
94
+ import { useCounter } from './useCounter'
95
+
96
+ it('increments count', () => {
97
+ const { count, increment } = useCounter(0)
98
+ expect(count.value).toBe(0)
99
+ increment()
100
+ expect(count.value).toBe(1)
101
+ })
102
+
103
+ it('resets to initial', () => {
104
+ const { count, increment, reset } = useCounter(5)
105
+ increment()
106
+ increment()
107
+ expect(count.value).toBe(7)
108
+ reset()
109
+ expect(count.value).toBe(5)
110
+ })
111
+ ```
112
+
113
+ ## Utils Tests
114
+
115
+ Easiest - pure functions:
116
+
117
+ ```ts
118
+ import { formatCurrency, slugify } from './formatters'
119
+
120
+ describe('formatCurrency', () => {
121
+ it('formats USD', () => {
122
+ expect(formatCurrency(10.5)).toBe('$10.50')
123
+ })
124
+ })
125
+
126
+ describe('slugify', () => {
127
+ it('converts to lowercase', () => {
128
+ expect(slugify('Hello World')).toBe('hello-world')
129
+ })
130
+
131
+ it('removes special chars', () => {
132
+ expect(slugify('Hello! World?')).toBe('hello-world')
133
+ })
134
+ })
135
+ ```
136
+
137
+ ## Mocking
138
+
139
+ **Composables:**
140
+
141
+ ```ts
142
+ import { vi } from 'vitest'
143
+
144
+ vi.mock('./useAuth', () => ({
145
+ useAuth: vi.fn(() => ({
146
+ user: { id: 1, name: 'Test' },
147
+ isAuthenticated: true
148
+ }))
149
+ }))
150
+ ```
151
+
152
+ **API calls:**
153
+
154
+ ```ts
155
+ global.fetch = vi.fn(() =>
156
+ Promise.resolve({
157
+ json: () => Promise.resolve({ data: [] })
158
+ })
159
+ )
160
+ ```
161
+
162
+ ## Router Mocking
163
+
164
+ Mock `useRoute` and `useRouter` for component tests:
165
+
166
+ ```ts
167
+ import { vi } from 'vitest'
168
+ import { mount } from '@vue/test-utils'
169
+
170
+ vi.mock('vue-router', () => ({
171
+ useRoute: vi.fn(() => ({
172
+ params: { id: '123' },
173
+ query: { filter: 'active' },
174
+ path: '/users/123',
175
+ })),
176
+ useRouter: vi.fn(() => ({
177
+ push: vi.fn(),
178
+ replace: vi.fn(),
179
+ })),
180
+ }))
181
+
182
+ it('uses route params', () => {
183
+ const wrapper = mount(UserPage)
184
+ expect(wrapper.text()).toContain('123')
185
+ })
186
+ ```
187
+
188
+ **Dynamic route mocking per test:**
189
+
190
+ ```ts
191
+ import { useRoute } from 'vue-router'
192
+
193
+ it('handles different routes', () => {
194
+ vi.mocked(useRoute).mockReturnValue({
195
+ params: { id: '456' },
196
+ } as any)
197
+
198
+ const wrapper = mount(UserPage)
199
+ expect(wrapper.text()).toContain('456')
200
+ })
201
+ ```
202
+
203
+ ## Suspense and Teleport
204
+
205
+ **Testing async components with Suspense:**
206
+
207
+ ```ts
208
+ import { flushPromises, mount } from '@vue/test-utils'
209
+
210
+ it('renders async content', async () => {
211
+ const wrapper = mount(AsyncComponent, {
212
+ global: {
213
+ stubs: { Suspense: false }, // Don't stub Suspense
214
+ },
215
+ })
216
+
217
+ // Wait for async setup to complete
218
+ await flushPromises()
219
+
220
+ expect(wrapper.text()).toContain('Loaded content')
221
+ })
222
+ ```
223
+
224
+ **Testing Teleport:**
225
+
226
+ ```ts
227
+ it('teleports modal content', () => {
228
+ const wrapper = mount(Modal, {
229
+ global: {
230
+ stubs: {
231
+ teleport: true, // Stub teleport to render inline
232
+ },
233
+ },
234
+ })
235
+
236
+ expect(wrapper.text()).toContain('Modal content')
237
+ })
238
+ ```
239
+
240
+ **Access teleported content:**
241
+
242
+ ```ts
243
+ it('finds teleported content', () => {
244
+ document.body.innerHTML = '<div id="modal-target"></div>'
245
+
246
+ mount(Modal, { props: { open: true } })
247
+
248
+ // Content teleports to #modal-target
249
+ expect(document.body.innerHTML).toContain('Modal content')
250
+ })
251
+ ```
252
+
253
+ ## Best Practices
254
+
255
+ **Do:**
256
+
257
+ - Test behavior (what user sees/does), not implementation
258
+ - Arrange-Act-Assert structure
259
+ - One assertion per test
260
+ - Descriptive test names
261
+ - Mock external dependencies
262
+
263
+ **Don't:**
264
+
265
+ - Test Vue internals (reactivity)
266
+ - Test third-party libraries
267
+ - Test trivial getters/setters
268
+ - Test implementation details
269
+
270
+ ## What to Test
271
+
272
+ **Test:**
273
+
274
+ - User interactions (clicks, inputs)
275
+ - Conditional rendering
276
+ - Props validation, emitted events
277
+ - Computed values, business logic
278
+
279
+ **Skip:**
280
+
281
+ - Vue internals, third-party libs
282
+ - Trivial getters/setters
283
+ - Implementation details
284
+
285
+ ## Running
286
+
287
+ ```bash
288
+ pnpm test # all
289
+ pnpm exec vitest Button.spec.ts # specific
290
+ pnpm exec vitest --watch # watch
291
+ pnpm test --coverage # coverage
292
+ ```
293
+
294
+ **Docs:** [vitest.dev](https://vitest.dev/) · [test-utils.vuejs.org](https://test-utils.vuejs.org/)
@@ -0,0 +1,172 @@
1
+ # Vue TypeScript Patterns
2
+
3
+ TypeScript-specific patterns for Vue 3 development.
4
+
5
+ ## Provide/Inject Types
6
+
7
+ Use `InjectionKey` for type-safe dependency injection:
8
+
9
+ ```ts
10
+ import type { InjectionKey } from 'vue'
11
+ import type { User } from './types'
12
+
13
+ // Define typed key
14
+ export const UserKey: InjectionKey<User> = Symbol('user')
15
+
16
+ // Provider component
17
+ const user = ref<User>({ id: 1, name: 'John' })
18
+ provide(UserKey, user)
19
+
20
+ // Consumer component
21
+ const user = inject(UserKey) // Ref<User> | undefined
22
+ const user = inject(UserKey)! // Ref<User> (assert non-null)
23
+ ```
24
+
25
+ **With default value:**
26
+
27
+ ```ts
28
+ const user = inject(UserKey, ref({ id: 0, name: 'Guest' }))
29
+ // Type: Ref<User> (no undefined)
30
+ ```
31
+
32
+ ## vue-tsc Strict Templates
33
+
34
+ Enable stricter template type checking:
35
+
36
+ ```bash
37
+ # Check templates with strict mode
38
+ vue-tsc --noEmit --strict-templates
39
+ ```
40
+
41
+ Catches template errors like:
42
+
43
+ - Accessing non-existent properties
44
+ - Wrong prop types
45
+ - Missing required props
46
+
47
+ ## tsconfig Settings
48
+
49
+ **Required for Vue 3:**
50
+
51
+ ```json
52
+ {
53
+ "compilerOptions": {
54
+ "moduleResolution": "bundler",
55
+ "verbatimModuleSyntax": true,
56
+ "strict": true,
57
+ "jsx": "preserve"
58
+ }
59
+ }
60
+ ```
61
+
62
+ **`moduleResolution: "bundler"`** - Matches Vite/webpack resolution. Avoids `.js` extension issues.
63
+
64
+ **`verbatimModuleSyntax: true`** - Enforces explicit `type` imports:
65
+
66
+ ```ts
67
+ // ❌ May cause issues with bundlers
68
+ import { User } from './types'
69
+
70
+ // ✅ Explicit type import
71
+ import type { User } from './types'
72
+ ```
73
+
74
+ ## Component Type Helpers
75
+
76
+ **Extract props type from component:**
77
+
78
+ ```ts
79
+ import type { ComponentProps, ComponentSlots, ComponentEmits } from 'vue-component-type-helpers'
80
+ import MyComponent from './MyComponent.vue'
81
+
82
+ type Props = ComponentProps<typeof MyComponent>
83
+ type Slots = ComponentSlots<typeof MyComponent>
84
+ type Emits = ComponentEmits<typeof MyComponent>
85
+ ```
86
+
87
+ **Extract exposed methods:**
88
+
89
+ ```ts
90
+ import type { ComponentExposed } from 'vue-component-type-helpers'
91
+
92
+ type Exposed = ComponentExposed<typeof MyComponent>
93
+ ```
94
+
95
+ ## Generic Components
96
+
97
+ Define generic components with typed slots:
98
+
99
+ ```vue
100
+ <script setup lang="ts" generic="T extends { id: string }">
101
+ defineProps<{
102
+ items: T[]
103
+ }>()
104
+
105
+ defineSlots<{
106
+ default: (props: { item: T }) => any
107
+ }>()
108
+ </script>
109
+
110
+ <template>
111
+ <div v-for="item in items" :key="item.id">
112
+ <slot :item="item" />
113
+ </div>
114
+ </template>
115
+ ```
116
+
117
+ ## Ref Type Narrowing
118
+
119
+ Handle ref type narrowing correctly:
120
+
121
+ ```ts
122
+ const maybeUser = ref<User | null>(null)
123
+
124
+ // ❌ TypeScript still sees User | null
125
+ if (maybeUser.value) {
126
+ maybeUser.value.name // Error: possibly null
127
+ }
128
+
129
+ // ✅ Use computed or extract value
130
+ const userName = computed(() => maybeUser.value?.name ?? 'Guest')
131
+
132
+ // ✅ Or guard in same expression
133
+ maybeUser.value && maybeUser.value.name
134
+ ```
135
+
136
+ ## Event Handler Types
137
+
138
+ Type event handlers correctly:
139
+
140
+ ```ts
141
+ // DOM events
142
+ const onClick = (e: MouseEvent) => { ... }
143
+ const onInput = (e: Event) => {
144
+ const target = e.target as HTMLInputElement
145
+ console.log(target.value)
146
+ }
147
+
148
+ // Component emits
149
+ const onUpdate = (value: string) => { ... }
150
+ ```
151
+
152
+ ## Common Mistakes
153
+
154
+ **Forgetting to import types explicitly:**
155
+
156
+ ```ts
157
+ // ❌ Runtime import of type-only
158
+ import { User } from './types'
159
+
160
+ // ✅ Type-only import
161
+ import type { User } from './types'
162
+ ```
163
+
164
+ **Not using `as const` for literal types:**
165
+
166
+ ```ts
167
+ // ❌ Type is string[]
168
+ const variants = ['primary', 'secondary']
169
+
170
+ // ✅ Type is readonly ['primary', 'secondary']
171
+ const variants = ['primary', 'secondary'] as const
172
+ ```
@@ -0,0 +1,156 @@
1
+ # Client Utilities
2
+
3
+ Pure functions for formatting, validation, transformation, and parsing.
4
+
5
+ ## Quick Reference
6
+
7
+ | Category | Examples |
8
+ | ------------ | --------------------------------------------- |
9
+ | Formatters | `formatCurrency`, `formatDate`, `formatBytes` |
10
+ | Validators | `isValidEmail`, `isValidUrl`, `isValidPhone` |
11
+ | Transformers | `slugify`, `truncate`, `capitalize` |
12
+ | Parsers | `parseQuery`, `parseJSON`, `parseDate` |
13
+
14
+ ## Rules
15
+
16
+ **Pure functions:**
17
+
18
+ - Same input → same output
19
+ - No side effects
20
+ - No external state mutation
21
+ - No API calls, no refs, no reactive
22
+
23
+ **When NOT to use utils:**
24
+
25
+ - Stateful logic → use composables
26
+ - Vue-specific → use composables
27
+ - Component logic → keep in component
28
+ - API calls → use queries
29
+
30
+ ## Structure
31
+
32
+ ```ts
33
+ // utils/formatters.ts
34
+ export function formatCurrency(amount: number, currency = 'USD'): string {
35
+ return new Intl.NumberFormat('en-US', {
36
+ style: 'currency',
37
+ currency,
38
+ }).format(amount)
39
+ }
40
+
41
+ export function formatRelativeTime(date: Date): string {
42
+ const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
43
+ const diff = date.getTime() - Date.now()
44
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24))
45
+ return rtf.format(days, 'day')
46
+ }
47
+ ```
48
+
49
+ **Naming:** Descriptive verbs (`formatCurrency`, `validateEmail`, `parseQuery`)
50
+ **Organization:** Group by category (`formatters.ts`, `validators.ts`)
51
+ **Exports:** Named exports only
52
+
53
+ ## Examples by Category
54
+
55
+ **Formatters:**
56
+
57
+ ```ts
58
+ // utils/formatters.ts
59
+ export function formatBytes(bytes: number): string { ... }
60
+ export function formatPhone(phone: string): string { ... }
61
+ ```
62
+
63
+ **Validators:**
64
+
65
+ ```ts
66
+ // utils/validators.ts
67
+ export function isValidEmail(email: string): boolean {
68
+ return /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/.test(email)
69
+ }
70
+
71
+ export function isValidUrl(url: string): boolean {
72
+ try { new URL(url); return true }
73
+ catch { return false }
74
+ }
75
+ ```
76
+
77
+ **Transformers:**
78
+
79
+ ```ts
80
+ // utils/transformers.ts
81
+ export function slugify(text: string): string {
82
+ return text.toLowerCase()
83
+ .replace(/[^\w\s-]/g, '')
84
+ .replace(/\s+/g, '-')
85
+ }
86
+
87
+ export function truncate(text: string, length: number): string {
88
+ return text.length > length ? `${text.slice(0, length)}...` : text
89
+ }
90
+ ```
91
+
92
+ **Parsers:**
93
+
94
+ ```ts
95
+ // utils/parsers.ts
96
+ export function parseQuery(search: string): Record<string, string> {
97
+ return Object.fromEntries(new URLSearchParams(search))
98
+ }
99
+
100
+ export function parseJSON<T>(json: string, fallback: T): T {
101
+ try { return JSON.parse(json) }
102
+ catch { return fallback }
103
+ }
104
+ ```
105
+
106
+ ## Common Mistakes
107
+
108
+ **Side effects (not pure):**
109
+
110
+ ```ts
111
+ // ❌ Wrong - mutates external state
112
+ let count = 0
113
+ export function increment() {
114
+ count++
115
+ return count
116
+ }
117
+
118
+ // ✅ Correct - pure
119
+ export function add(a: number, b: number): number {
120
+ return a + b
121
+ }
122
+ ```
123
+
124
+ **Using utils for stateful logic:**
125
+
126
+ ```ts
127
+ // ❌ Wrong - should be composable
128
+ export function useCounter() { ... }
129
+
130
+ // ✅ Correct - pure transformation
131
+ export function formatCount(count: number): string { ... }
132
+ ```
133
+
134
+ ## Organization
135
+
136
+ **Flat for small projects:**
137
+
138
+ ```
139
+ utils/
140
+ ├── formatters.ts
141
+ ├── validators.ts
142
+ └── transformers.ts
143
+ ```
144
+
145
+ **Nested for large projects:**
146
+
147
+ ```
148
+ utils/
149
+ ├── formatters/
150
+ │ ├── date.ts
151
+ │ ├── currency.ts
152
+ │ └── index.ts
153
+ └── validators/
154
+ ├── email.ts
155
+ └── index.ts
156
+ ```