devskill 2.0.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.
- package/README.md +123 -0
- package/README.vn.md +104 -0
- package/SKILL_GUIDE.vn.md +252 -0
- package/bin/devskill.js +11 -0
- package/meta.ts +123 -0
- package/package.json +21 -0
- package/publish.sh +50 -0
- package/scripts/cli.ts +556 -0
- package/skills/builderx_api-contexts/SKILL.md +63 -0
- package/skills/builderx_api-contexts/references/core-multi-outbox.md +57 -0
- package/skills/builderx_api-controllers/SKILL.md +49 -0
- package/skills/builderx_api-controllers/references/core-fallback.md +62 -0
- package/skills/builderx_api-schemas/SKILL.md +76 -0
- package/skills/builderx_api-schemas/references/core-schema.md +78 -0
- package/skills/builderx_spa-api/SKILL.md +67 -0
- package/skills/builderx_spa-api/references/base-api.md +97 -0
- package/skills/builderx_spa-api/references/fetch.md +70 -0
- package/skills/builderx_spa-design/SKILL.md +82 -0
- package/skills/builderx_spa-design/references/core-text-design.md +64 -0
- package/skills/builderx_spa-design/references/features-feedback-actions.md +76 -0
- package/skills/builderx_spa-design/references/features-forms.md +91 -0
- package/skills/builderx_spa-design/references/features-layout-navigation.md +121 -0
- package/skills/builderx_spa-permission/SKILL.md +38 -0
- package/skills/builderx_spa-permission/references/has-permission.md +34 -0
- package/skills/composition-patterns/AGENTS.md +946 -0
- package/skills/composition-patterns/README.md +60 -0
- package/skills/composition-patterns/SKILL.md +89 -0
- package/skills/composition-patterns/SYNC.md +5 -0
- package/skills/composition-patterns/metadata.json +11 -0
- package/skills/composition-patterns/rules/_sections.md +29 -0
- package/skills/composition-patterns/rules/_template.md +24 -0
- package/skills/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skills/composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skills/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skills/composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skills/composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skills/composition-patterns/rules/state-context-interface.md +191 -0
- package/skills/composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skills/composition-patterns/rules/state-lift-state.md +125 -0
- package/skills/deploy-to-vercel/Archive.zip +0 -0
- package/skills/deploy-to-vercel/SKILL.md +296 -0
- package/skills/deploy-to-vercel/SYNC.md +5 -0
- package/skills/deploy-to-vercel/resources/deploy-codex.sh +301 -0
- package/skills/deploy-to-vercel/resources/deploy.sh +301 -0
- package/skills/pinia/SKILL.md +105 -0
- package/skills/pinia/references/api-in-composables.md +139 -0
- package/skills/pinia/references/setup-stores.md +105 -0
- package/skills/pinia/references/using-in-components.md +91 -0
- package/skills/pinia-options/SKILL.md +72 -0
- package/skills/pinia-options/references/core-option-stores.md +149 -0
- package/skills/pinia-options/references/using-in-options-api.md +105 -0
- package/skills/react-best-practices/AGENTS.md +3373 -0
- package/skills/react-best-practices/README.md +123 -0
- package/skills/react-best-practices/SKILL.md +143 -0
- package/skills/react-best-practices/SYNC.md +5 -0
- package/skills/react-best-practices/metadata.json +15 -0
- package/skills/react-best-practices/rules/_sections.md +46 -0
- package/skills/react-best-practices/rules/_template.md +28 -0
- package/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/skills/react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/skills/react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/react-best-practices/rules/server-hoist-static-io.md +142 -0
- package/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/skills/react-native-skills/AGENTS.md +2897 -0
- package/skills/react-native-skills/README.md +165 -0
- package/skills/react-native-skills/SKILL.md +121 -0
- package/skills/react-native-skills/SYNC.md +5 -0
- package/skills/react-native-skills/metadata.json +16 -0
- package/skills/react-native-skills/rules/_sections.md +86 -0
- package/skills/react-native-skills/rules/_template.md +28 -0
- package/skills/react-native-skills/rules/animation-derived-value.md +53 -0
- package/skills/react-native-skills/rules/animation-gesture-detector-press.md +95 -0
- package/skills/react-native-skills/rules/animation-gpu-properties.md +65 -0
- package/skills/react-native-skills/rules/design-system-compound-components.md +66 -0
- package/skills/react-native-skills/rules/fonts-config-plugin.md +71 -0
- package/skills/react-native-skills/rules/imports-design-system-folder.md +68 -0
- package/skills/react-native-skills/rules/js-hoist-intl.md +61 -0
- package/skills/react-native-skills/rules/list-performance-callbacks.md +44 -0
- package/skills/react-native-skills/rules/list-performance-function-references.md +132 -0
- package/skills/react-native-skills/rules/list-performance-images.md +53 -0
- package/skills/react-native-skills/rules/list-performance-inline-objects.md +97 -0
- package/skills/react-native-skills/rules/list-performance-item-expensive.md +94 -0
- package/skills/react-native-skills/rules/list-performance-item-memo.md +82 -0
- package/skills/react-native-skills/rules/list-performance-item-types.md +104 -0
- package/skills/react-native-skills/rules/list-performance-virtualize.md +67 -0
- package/skills/react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
- package/skills/react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
- package/skills/react-native-skills/rules/navigation-native-navigators.md +188 -0
- package/skills/react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
- package/skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
- package/skills/react-native-skills/rules/react-state-dispatcher.md +91 -0
- package/skills/react-native-skills/rules/react-state-fallback.md +56 -0
- package/skills/react-native-skills/rules/react-state-minimize.md +65 -0
- package/skills/react-native-skills/rules/rendering-no-falsy-and.md +74 -0
- package/skills/react-native-skills/rules/rendering-text-in-text-component.md +36 -0
- package/skills/react-native-skills/rules/scroll-position-no-state.md +82 -0
- package/skills/react-native-skills/rules/state-ground-truth.md +80 -0
- package/skills/react-native-skills/rules/ui-expo-image.md +66 -0
- package/skills/react-native-skills/rules/ui-image-gallery.md +104 -0
- package/skills/react-native-skills/rules/ui-measure-views.md +78 -0
- package/skills/react-native-skills/rules/ui-menus.md +174 -0
- package/skills/react-native-skills/rules/ui-native-modals.md +77 -0
- package/skills/react-native-skills/rules/ui-pressable.md +61 -0
- package/skills/react-native-skills/rules/ui-safe-area-scroll.md +65 -0
- package/skills/react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
- package/skills/react-native-skills/rules/ui-styling.md +87 -0
- package/skills/slidev/LICENSE.md +21 -0
- package/skills/slidev/README.md +61 -0
- package/skills/slidev/SKILL.md +189 -0
- package/skills/slidev/SYNC.md +5 -0
- package/skills/slidev/references/animation-click-marker.md +37 -0
- package/skills/slidev/references/animation-drawing.md +68 -0
- package/skills/slidev/references/animation-rough-marker.md +53 -0
- package/skills/slidev/references/api-slide-hooks.md +37 -0
- package/skills/slidev/references/build-og-image.md +36 -0
- package/skills/slidev/references/build-pdf.md +40 -0
- package/skills/slidev/references/build-remote-assets.md +34 -0
- package/skills/slidev/references/build-seo-meta.md +43 -0
- package/skills/slidev/references/code-groups.md +64 -0
- package/skills/slidev/references/code-import-snippet.md +55 -0
- package/skills/slidev/references/code-line-highlighting.md +50 -0
- package/skills/slidev/references/code-line-numbers.md +46 -0
- package/skills/slidev/references/code-magic-move.md +57 -0
- package/skills/slidev/references/code-max-height.md +37 -0
- package/skills/slidev/references/code-twoslash.md +42 -0
- package/skills/slidev/references/core-animations.md +196 -0
- package/skills/slidev/references/core-cli.md +140 -0
- package/skills/slidev/references/core-components.md +197 -0
- package/skills/slidev/references/core-exporting.md +148 -0
- package/skills/slidev/references/core-frontmatter.md +195 -0
- package/skills/slidev/references/core-global-context.md +155 -0
- package/skills/slidev/references/core-headmatter.md +188 -0
- package/skills/slidev/references/core-hosting.md +152 -0
- package/skills/slidev/references/core-layouts.md +286 -0
- package/skills/slidev/references/core-syntax.md +155 -0
- package/skills/slidev/references/diagram-latex.md +55 -0
- package/skills/slidev/references/diagram-mermaid.md +44 -0
- package/skills/slidev/references/diagram-plantuml.md +45 -0
- package/skills/slidev/references/editor-monaco-run.md +44 -0
- package/skills/slidev/references/editor-monaco-write.md +24 -0
- package/skills/slidev/references/editor-monaco.md +50 -0
- package/skills/slidev/references/editor-prettier.md +40 -0
- package/skills/slidev/references/editor-side.md +23 -0
- package/skills/slidev/references/editor-vscode.md +55 -0
- package/skills/slidev/references/layout-canvas-size.md +25 -0
- package/skills/slidev/references/layout-draggable.md +57 -0
- package/skills/slidev/references/layout-global-layers.md +50 -0
- package/skills/slidev/references/layout-slots.md +75 -0
- package/skills/slidev/references/layout-transform.md +33 -0
- package/skills/slidev/references/layout-zoom.md +39 -0
- package/skills/slidev/references/presenter-notes-ruby.md +35 -0
- package/skills/slidev/references/presenter-recording.md +30 -0
- package/skills/slidev/references/presenter-remote.md +40 -0
- package/skills/slidev/references/presenter-timer.md +34 -0
- package/skills/slidev/references/style-direction.md +34 -0
- package/skills/slidev/references/style-icons.md +46 -0
- package/skills/slidev/references/style-scoped.md +50 -0
- package/skills/slidev/references/syntax-block-frontmatter.md +39 -0
- package/skills/slidev/references/syntax-frontmatter-merging.md +49 -0
- package/skills/slidev/references/syntax-importing-slides.md +60 -0
- package/skills/slidev/references/syntax-mdc.md +51 -0
- package/skills/slidev/references/tool-eject-theme.md +27 -0
- package/skills/tsdown/LICENSE.md +22 -0
- package/skills/tsdown/README.md +77 -0
- package/skills/tsdown/SKILL.md +407 -0
- package/skills/tsdown/SYNC.md +5 -0
- package/skills/tsdown/references/README.md +139 -0
- package/skills/tsdown/references/advanced-benchmark.md +8 -0
- package/skills/tsdown/references/advanced-ci.md +89 -0
- package/skills/tsdown/references/advanced-hooks.md +363 -0
- package/skills/tsdown/references/advanced-plugins.md +381 -0
- package/skills/tsdown/references/advanced-programmatic.md +378 -0
- package/skills/tsdown/references/advanced-rolldown-options.md +117 -0
- package/skills/tsdown/references/guide-getting-started.md +178 -0
- package/skills/tsdown/references/guide-introduction.md +42 -0
- package/skills/tsdown/references/guide-migrate-from-tsup.md +189 -0
- package/skills/tsdown/references/option-cjs-default.md +98 -0
- package/skills/tsdown/references/option-cleaning.md +275 -0
- package/skills/tsdown/references/option-config-file.md +281 -0
- package/skills/tsdown/references/option-css.md +301 -0
- package/skills/tsdown/references/option-dependencies.md +385 -0
- package/skills/tsdown/references/option-dts.md +251 -0
- package/skills/tsdown/references/option-entry.md +211 -0
- package/skills/tsdown/references/option-exe.md +120 -0
- package/skills/tsdown/references/option-lint.md +127 -0
- package/skills/tsdown/references/option-log-level.md +91 -0
- package/skills/tsdown/references/option-minification.md +177 -0
- package/skills/tsdown/references/option-output-directory.md +270 -0
- package/skills/tsdown/references/option-output-format.md +181 -0
- package/skills/tsdown/references/option-package-exports.md +320 -0
- package/skills/tsdown/references/option-platform.md +256 -0
- package/skills/tsdown/references/option-root.md +88 -0
- package/skills/tsdown/references/option-shims.md +299 -0
- package/skills/tsdown/references/option-sourcemap.md +301 -0
- package/skills/tsdown/references/option-target.md +222 -0
- package/skills/tsdown/references/option-tree-shaking.md +335 -0
- package/skills/tsdown/references/option-unbundle.md +310 -0
- package/skills/tsdown/references/option-watch-mode.md +261 -0
- package/skills/tsdown/references/recipe-react.md +338 -0
- package/skills/tsdown/references/recipe-solid.md +46 -0
- package/skills/tsdown/references/recipe-svelte.md +54 -0
- package/skills/tsdown/references/recipe-vue.md +387 -0
- package/skills/tsdown/references/recipe-wasm.md +125 -0
- package/skills/tsdown/references/reference-cli.md +472 -0
- package/skills/turborepo/LICENSE.md +7 -0
- package/skills/turborepo/SKILL.md +951 -0
- package/skills/turborepo/SYNC.md +5 -0
- package/skills/turborepo/command/turborepo.md +70 -0
- package/skills/turborepo/references/best-practices/RULE.md +241 -0
- package/skills/turborepo/references/best-practices/dependencies.md +246 -0
- package/skills/turborepo/references/best-practices/packages.md +335 -0
- package/skills/turborepo/references/best-practices/structure.md +297 -0
- package/skills/turborepo/references/boundaries/RULE.md +126 -0
- package/skills/turborepo/references/caching/RULE.md +153 -0
- package/skills/turborepo/references/caching/gotchas.md +190 -0
- package/skills/turborepo/references/caching/remote-cache.md +127 -0
- package/skills/turborepo/references/ci/RULE.md +79 -0
- package/skills/turborepo/references/ci/github-actions.md +162 -0
- package/skills/turborepo/references/ci/patterns.md +145 -0
- package/skills/turborepo/references/ci/vercel.md +103 -0
- package/skills/turborepo/references/cli/RULE.md +100 -0
- package/skills/turborepo/references/cli/commands.md +297 -0
- package/skills/turborepo/references/configuration/RULE.md +235 -0
- package/skills/turborepo/references/configuration/global-options.md +239 -0
- package/skills/turborepo/references/configuration/gotchas.md +368 -0
- package/skills/turborepo/references/configuration/tasks.md +325 -0
- package/skills/turborepo/references/environment/RULE.md +123 -0
- package/skills/turborepo/references/environment/gotchas.md +175 -0
- package/skills/turborepo/references/environment/modes.md +101 -0
- package/skills/turborepo/references/filtering/RULE.md +148 -0
- package/skills/turborepo/references/filtering/patterns.md +152 -0
- package/skills/turborepo/references/watch/RULE.md +99 -0
- package/skills/vercel-cli-with-tokens/SKILL.md +328 -0
- package/skills/vercel-cli-with-tokens/SYNC.md +5 -0
- package/skills/vue/SKILL.md +90 -0
- package/skills/vue/references/composables.md +54 -0
- package/skills/vue/references/lifecycle.md +31 -0
- package/skills/vue/references/props-emits.md +62 -0
- package/skills/vue/references/provide-inject.md +47 -0
- package/skills/vue/references/state.md +65 -0
- package/skills/vue-antdv-tailwind/SKILL.md +33 -0
- package/skills/vue-antdv-tailwind/references/styling-rules.md +61 -0
- package/skills/vue-best-practices/LICENSE.md +21 -0
- package/skills/vue-best-practices/SKILL.md +154 -0
- package/skills/vue-best-practices/SYNC.md +5 -0
- package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
- package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
- package/skills/vue-best-practices/references/component-async.md +97 -0
- package/skills/vue-best-practices/references/component-data-flow.md +307 -0
- package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
- package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
- package/skills/vue-best-practices/references/component-slots.md +216 -0
- package/skills/vue-best-practices/references/component-suspense.md +228 -0
- package/skills/vue-best-practices/references/component-teleport.md +108 -0
- package/skills/vue-best-practices/references/component-transition-group.md +128 -0
- package/skills/vue-best-practices/references/component-transition.md +125 -0
- package/skills/vue-best-practices/references/composables.md +290 -0
- package/skills/vue-best-practices/references/directives.md +162 -0
- package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
- package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
- package/skills/vue-best-practices/references/plugins.md +166 -0
- package/skills/vue-best-practices/references/reactivity.md +344 -0
- package/skills/vue-best-practices/references/render-functions.md +201 -0
- package/skills/vue-best-practices/references/sfc.md +310 -0
- package/skills/vue-best-practices/references/state-management.md +135 -0
- package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
- package/skills/vue-jsx-best-practices/LICENSE.md +21 -0
- package/skills/vue-jsx-best-practices/SKILL.md +12 -0
- package/skills/vue-jsx-best-practices/SYNC.md +5 -0
- package/skills/vue-jsx-best-practices/reference/render-function-jsx-vue-vs-react.md +141 -0
- package/skills/vue-options/SKILL.md +98 -0
- package/skills/vue-options/references/lifecycle.md +107 -0
- package/skills/vue-options/references/mixins.md +96 -0
- package/skills/vue-options/references/props-emits.md +106 -0
- package/skills/vue-options/references/provide-inject.md +92 -0
- package/skills/vue-options/references/state.md +115 -0
- package/skills/vue-options-api-best-practices/LICENSE.md +21 -0
- package/skills/vue-options-api-best-practices/SKILL.md +23 -0
- package/skills/vue-options-api-best-practices/SYNC.md +5 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-lifecycle-hooks.md +95 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-methods.md +68 -0
- package/skills/vue-options-api-best-practices/reference/stateful-methods-lifecycle.md +61 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-arrow-functions-validators.md +141 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-computed-return-types.md +192 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-proptype-complex-types.md +212 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-provide-inject-limitations.md +135 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-type-event-handlers.md +202 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-use-definecomponent.md +172 -0
- package/skills/vue-options-api-best-practices/reference/ts-strict-mode-options-api.md +197 -0
- package/skills/vue-pinia-best-practices/LICENSE.md +21 -0
- package/skills/vue-pinia-best-practices/SKILL.md +21 -0
- package/skills/vue-pinia-best-practices/SYNC.md +5 -0
- package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
- package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
- package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
- package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
- package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
- package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
- package/skills/vue-router-best-practices/LICENSE.md +21 -0
- package/skills/vue-router-best-practices/SKILL.md +23 -0
- package/skills/vue-router-best-practices/SYNC.md +5 -0
- package/skills/vue-router-best-practices/reference/router-beforeenter-no-param-trigger.md +167 -0
- package/skills/vue-router-best-practices/reference/router-beforerouteenter-no-this.md +176 -0
- package/skills/vue-router-best-practices/reference/router-guard-async-await-pattern.md +227 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-infinite-loop.md +187 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-next-deprecated.md +150 -0
- package/skills/vue-router-best-practices/reference/router-param-change-no-lifecycle.md +181 -0
- package/skills/vue-router-best-practices/reference/router-simple-routing-cleanup.md +209 -0
- package/skills/vue-router-best-practices/reference/router-use-vue-router-for-production.md +183 -0
- package/skills/vue-testing-best-practices/LICENSE.md +21 -0
- package/skills/vue-testing-best-practices/SKILL.md +29 -0
- package/skills/vue-testing-best-practices/SYNC.md +5 -0
- package/skills/vue-testing-best-practices/reference/async-component-testing.md +163 -0
- package/skills/vue-testing-best-practices/reference/teleport-testing-complexity.md +158 -0
- package/skills/vue-testing-best-practices/reference/testing-async-await-flushpromises.md +175 -0
- package/skills/vue-testing-best-practices/reference/testing-browser-vs-node-runners.md +208 -0
- package/skills/vue-testing-best-practices/reference/testing-component-blackbox-approach.md +144 -0
- package/skills/vue-testing-best-practices/reference/testing-composables-helper-wrapper.md +238 -0
- package/skills/vue-testing-best-practices/reference/testing-e2e-playwright-recommended.md +242 -0
- package/skills/vue-testing-best-practices/reference/testing-no-snapshot-only.md +197 -0
- package/skills/vue-testing-best-practices/reference/testing-pinia-store-setup.md +228 -0
- package/skills/vue-testing-best-practices/reference/testing-suspense-async-components.md +229 -0
- package/skills/vue-testing-best-practices/reference/testing-vitest-recommended-for-vue.md +204 -0
- package/skills/vueuse-functions/LICENSE.md +21 -0
- package/skills/vueuse-functions/SKILL.md +419 -0
- package/skills/vueuse-functions/SYNC.md +5 -0
- package/skills/vueuse-functions/references/computedAsync.md +195 -0
- package/skills/vueuse-functions/references/computedEager.md +62 -0
- package/skills/vueuse-functions/references/computedInject.md +137 -0
- package/skills/vueuse-functions/references/computedWithControl.md +98 -0
- package/skills/vueuse-functions/references/createEventHook.md +86 -0
- package/skills/vueuse-functions/references/createGenericProjection.md +25 -0
- package/skills/vueuse-functions/references/createGlobalState.md +95 -0
- package/skills/vueuse-functions/references/createInjectionState.md +215 -0
- package/skills/vueuse-functions/references/createProjection.md +31 -0
- package/skills/vueuse-functions/references/createRef.md +54 -0
- package/skills/vueuse-functions/references/createReusableTemplate.md +357 -0
- package/skills/vueuse-functions/references/createSharedComposable.md +42 -0
- package/skills/vueuse-functions/references/createTemplatePromise.md +306 -0
- package/skills/vueuse-functions/references/createUnrefFn.md +51 -0
- package/skills/vueuse-functions/references/extendRef.md +76 -0
- package/skills/vueuse-functions/references/from.md +80 -0
- package/skills/vueuse-functions/references/get.md +30 -0
- package/skills/vueuse-functions/references/injectLocal.md +35 -0
- package/skills/vueuse-functions/references/isDefined.md +31 -0
- package/skills/vueuse-functions/references/logicAnd.md +40 -0
- package/skills/vueuse-functions/references/logicNot.md +36 -0
- package/skills/vueuse-functions/references/logicOr.md +40 -0
- package/skills/vueuse-functions/references/makeDestructurable.md +41 -0
- package/skills/vueuse-functions/references/onClickOutside.md +221 -0
- package/skills/vueuse-functions/references/onElementRemoval.md +88 -0
- package/skills/vueuse-functions/references/onKeyStroke.md +211 -0
- package/skills/vueuse-functions/references/onLongPress.md +227 -0
- package/skills/vueuse-functions/references/onStartTyping.md +53 -0
- package/skills/vueuse-functions/references/provideLocal.md +37 -0
- package/skills/vueuse-functions/references/reactify.md +144 -0
- package/skills/vueuse-functions/references/reactifyObject.md +62 -0
- package/skills/vueuse-functions/references/reactiveComputed.md +34 -0
- package/skills/vueuse-functions/references/reactiveOmit.md +86 -0
- package/skills/vueuse-functions/references/reactivePick.md +106 -0
- package/skills/vueuse-functions/references/refAutoReset.md +46 -0
- package/skills/vueuse-functions/references/refDebounced.md +81 -0
- package/skills/vueuse-functions/references/refDefault.md +36 -0
- package/skills/vueuse-functions/references/refManualReset.md +44 -0
- package/skills/vueuse-functions/references/refThrottled.md +99 -0
- package/skills/vueuse-functions/references/refWithControl.md +146 -0
- package/skills/vueuse-functions/references/set.md +30 -0
- package/skills/vueuse-functions/references/syncRef.md +195 -0
- package/skills/vueuse-functions/references/syncRefs.md +128 -0
- package/skills/vueuse-functions/references/templateRef.md +86 -0
- package/skills/vueuse-functions/references/toObserver.md +38 -0
- package/skills/vueuse-functions/references/toReactive.md +41 -0
- package/skills/vueuse-functions/references/toRef.md +75 -0
- package/skills/vueuse-functions/references/toRefs.md +81 -0
- package/skills/vueuse-functions/references/tryOnBeforeMount.md +34 -0
- package/skills/vueuse-functions/references/tryOnBeforeUnmount.md +32 -0
- package/skills/vueuse-functions/references/tryOnMounted.md +34 -0
- package/skills/vueuse-functions/references/tryOnScopeDispose.md +31 -0
- package/skills/vueuse-functions/references/tryOnUnmounted.md +32 -0
- package/skills/vueuse-functions/references/unrefElement.md +54 -0
- package/skills/vueuse-functions/references/until.md +161 -0
- package/skills/vueuse-functions/references/useAbs.md +31 -0
- package/skills/vueuse-functions/references/useActiveElement.md +85 -0
- package/skills/vueuse-functions/references/useAnimate.md +181 -0
- package/skills/vueuse-functions/references/useArrayDifference.md +84 -0
- package/skills/vueuse-functions/references/useArrayEvery.md +59 -0
- package/skills/vueuse-functions/references/useArrayFilter.md +63 -0
- package/skills/vueuse-functions/references/useArrayFind.md +50 -0
- package/skills/vueuse-functions/references/useArrayFindIndex.md +59 -0
- package/skills/vueuse-functions/references/useArrayFindLast.md +52 -0
- package/skills/vueuse-functions/references/useArrayIncludes.md +63 -0
- package/skills/vueuse-functions/references/useArrayJoin.md +74 -0
- package/skills/vueuse-functions/references/useArrayMap.md +59 -0
- package/skills/vueuse-functions/references/useArrayReduce.md +81 -0
- package/skills/vueuse-functions/references/useArraySome.md +59 -0
- package/skills/vueuse-functions/references/useArrayUnique.md +76 -0
- package/skills/vueuse-functions/references/useAsyncQueue.md +136 -0
- package/skills/vueuse-functions/references/useAsyncState.md +185 -0
- package/skills/vueuse-functions/references/useAsyncValidator.md +70 -0
- package/skills/vueuse-functions/references/useAuth.md +123 -0
- package/skills/vueuse-functions/references/useAverage.md +36 -0
- package/skills/vueuse-functions/references/useAxios.md +325 -0
- package/skills/vueuse-functions/references/useBase64.md +136 -0
- package/skills/vueuse-functions/references/useBattery.md +78 -0
- package/skills/vueuse-functions/references/useBluetooth.md +175 -0
- package/skills/vueuse-functions/references/useBreakpoints.md +172 -0
- package/skills/vueuse-functions/references/useBroadcastChannel.md +74 -0
- package/skills/vueuse-functions/references/useBrowserLocation.md +83 -0
- package/skills/vueuse-functions/references/useCached.md +52 -0
- package/skills/vueuse-functions/references/useCeil.md +31 -0
- package/skills/vueuse-functions/references/useChangeCase.md +79 -0
- package/skills/vueuse-functions/references/useClamp.md +85 -0
- package/skills/vueuse-functions/references/useClipboard.md +123 -0
- package/skills/vueuse-functions/references/useClipboardItems.md +94 -0
- package/skills/vueuse-functions/references/useCloned.md +91 -0
- package/skills/vueuse-functions/references/useColorMode.md +172 -0
- package/skills/vueuse-functions/references/useConfirmDialog.md +159 -0
- package/skills/vueuse-functions/references/useCookies.md +162 -0
- package/skills/vueuse-functions/references/useCountdown.md +105 -0
- package/skills/vueuse-functions/references/useCounter.md +86 -0
- package/skills/vueuse-functions/references/useCssSupports.md +35 -0
- package/skills/vueuse-functions/references/useCssVar.md +50 -0
- package/skills/vueuse-functions/references/useCurrentElement.md +61 -0
- package/skills/vueuse-functions/references/useCycleList.md +75 -0
- package/skills/vueuse-functions/references/useDark.md +144 -0
- package/skills/vueuse-functions/references/useDateFormat.md +145 -0
- package/skills/vueuse-functions/references/useDebounceFn.md +100 -0
- package/skills/vueuse-functions/references/useDebouncedRefHistory.md +40 -0
- package/skills/vueuse-functions/references/useDeviceMotion.md +86 -0
- package/skills/vueuse-functions/references/useDeviceOrientation.md +62 -0
- package/skills/vueuse-functions/references/useDevicePixelRatio.md +44 -0
- package/skills/vueuse-functions/references/useDevicesList.md +90 -0
- package/skills/vueuse-functions/references/useDisplayMedia.md +71 -0
- package/skills/vueuse-functions/references/useDocumentVisibility.md +45 -0
- package/skills/vueuse-functions/references/useDraggable.md +317 -0
- package/skills/vueuse-functions/references/useDrauu.md +65 -0
- package/skills/vueuse-functions/references/useDropZone.md +83 -0
- package/skills/vueuse-functions/references/useElementBounding.md +131 -0
- package/skills/vueuse-functions/references/useElementByPoint.md +48 -0
- package/skills/vueuse-functions/references/useElementHover.md +79 -0
- package/skills/vueuse-functions/references/useElementSize.md +78 -0
- package/skills/vueuse-functions/references/useElementVisibility.md +123 -0
- package/skills/vueuse-functions/references/useEventBus.md +101 -0
- package/skills/vueuse-functions/references/useEventListener.md +226 -0
- package/skills/vueuse-functions/references/useEventSource.md +204 -0
- package/skills/vueuse-functions/references/useExtractedObservable.md +198 -0
- package/skills/vueuse-functions/references/useEyeDropper.md +71 -0
- package/skills/vueuse-functions/references/useFavicon.md +67 -0
- package/skills/vueuse-functions/references/useFetch.md +546 -0
- package/skills/vueuse-functions/references/useFileDialog.md +91 -0
- package/skills/vueuse-functions/references/useFileSystemAccess.md +162 -0
- package/skills/vueuse-functions/references/useFirestore.md +129 -0
- package/skills/vueuse-functions/references/useFloor.md +31 -0
- package/skills/vueuse-functions/references/useFocus.md +99 -0
- package/skills/vueuse-functions/references/useFocusTrap.md +245 -0
- package/skills/vueuse-functions/references/useFocusWithin.md +57 -0
- package/skills/vueuse-functions/references/useFps.md +28 -0
- package/skills/vueuse-functions/references/useFullscreen.md +75 -0
- package/skills/vueuse-functions/references/useFuse.md +107 -0
- package/skills/vueuse-functions/references/useGamepad.md +222 -0
- package/skills/vueuse-functions/references/useGeolocation.md +85 -0
- package/skills/vueuse-functions/references/useIDBKeyval.md +93 -0
- package/skills/vueuse-functions/references/useIdle.md +88 -0
- package/skills/vueuse-functions/references/useImage.md +86 -0
- package/skills/vueuse-functions/references/useInfiniteScroll.md +157 -0
- package/skills/vueuse-functions/references/useIntersectionObserver.md +118 -0
- package/skills/vueuse-functions/references/useInterval.md +112 -0
- package/skills/vueuse-functions/references/useIntervalFn.md +50 -0
- package/skills/vueuse-functions/references/useIpcRenderer.md +144 -0
- package/skills/vueuse-functions/references/useIpcRendererInvoke.md +58 -0
- package/skills/vueuse-functions/references/useIpcRendererOn.md +52 -0
- package/skills/vueuse-functions/references/useJwt.md +57 -0
- package/skills/vueuse-functions/references/useKeyModifier.md +87 -0
- package/skills/vueuse-functions/references/useLastChanged.md +63 -0
- package/skills/vueuse-functions/references/useLocalStorage.md +41 -0
- package/skills/vueuse-functions/references/useMagicKeys.md +245 -0
- package/skills/vueuse-functions/references/useManualRefHistory.md +204 -0
- package/skills/vueuse-functions/references/useMath.md +47 -0
- package/skills/vueuse-functions/references/useMax.md +36 -0
- package/skills/vueuse-functions/references/useMediaControls.md +571 -0
- package/skills/vueuse-functions/references/useMediaQuery.md +53 -0
- package/skills/vueuse-functions/references/useMemoize.md +175 -0
- package/skills/vueuse-functions/references/useMemory.md +71 -0
- package/skills/vueuse-functions/references/useMin.md +36 -0
- package/skills/vueuse-functions/references/useMounted.md +38 -0
- package/skills/vueuse-functions/references/useMouse.md +113 -0
- package/skills/vueuse-functions/references/useMouseInElement.md +123 -0
- package/skills/vueuse-functions/references/useMousePressed.md +112 -0
- package/skills/vueuse-functions/references/useMutationObserver.md +61 -0
- package/skills/vueuse-functions/references/useNProgress.md +78 -0
- package/skills/vueuse-functions/references/useNavigatorLanguage.md +57 -0
- package/skills/vueuse-functions/references/useNetwork.md +106 -0
- package/skills/vueuse-functions/references/useNow.md +79 -0
- package/skills/vueuse-functions/references/useObjectUrl.md +55 -0
- package/skills/vueuse-functions/references/useObservable.md +91 -0
- package/skills/vueuse-functions/references/useOffsetPagination.md +199 -0
- package/skills/vueuse-functions/references/useOnline.md +41 -0
- package/skills/vueuse-functions/references/usePageLeave.md +42 -0
- package/skills/vueuse-functions/references/useParallax.md +58 -0
- package/skills/vueuse-functions/references/useParentElement.md +54 -0
- package/skills/vueuse-functions/references/usePerformanceObserver.md +48 -0
- package/skills/vueuse-functions/references/usePermission.md +79 -0
- package/skills/vueuse-functions/references/usePointer.md +89 -0
- package/skills/vueuse-functions/references/usePointerLock.md +60 -0
- package/skills/vueuse-functions/references/usePointerSwipe.md +80 -0
- package/skills/vueuse-functions/references/usePrecision.md +49 -0
- package/skills/vueuse-functions/references/usePreferredColorScheme.md +42 -0
- package/skills/vueuse-functions/references/usePreferredContrast.md +42 -0
- package/skills/vueuse-functions/references/usePreferredDark.md +41 -0
- package/skills/vueuse-functions/references/usePreferredLanguages.md +41 -0
- package/skills/vueuse-functions/references/usePreferredReducedMotion.md +42 -0
- package/skills/vueuse-functions/references/usePreferredReducedTransparency.md +42 -0
- package/skills/vueuse-functions/references/usePrevious.md +40 -0
- package/skills/vueuse-functions/references/useProjection.md +38 -0
- package/skills/vueuse-functions/references/useQRCode.md +53 -0
- package/skills/vueuse-functions/references/useRTDB.md +83 -0
- package/skills/vueuse-functions/references/useRafFn.md +68 -0
- package/skills/vueuse-functions/references/useRefHistory.md +285 -0
- package/skills/vueuse-functions/references/useResizeObserver.md +109 -0
- package/skills/vueuse-functions/references/useRound.md +31 -0
- package/skills/vueuse-functions/references/useRouteHash.md +27 -0
- package/skills/vueuse-functions/references/useRouteParams.md +38 -0
- package/skills/vueuse-functions/references/useRouteQuery.md +79 -0
- package/skills/vueuse-functions/references/useSSRWidth.md +47 -0
- package/skills/vueuse-functions/references/useScreenOrientation.md +96 -0
- package/skills/vueuse-functions/references/useScreenSafeArea.md +60 -0
- package/skills/vueuse-functions/references/useScriptTag.md +116 -0
- package/skills/vueuse-functions/references/useScroll.md +238 -0
- package/skills/vueuse-functions/references/useScrollLock.md +66 -0
- package/skills/vueuse-functions/references/useSessionStorage.md +41 -0
- package/skills/vueuse-functions/references/useShare.md +68 -0
- package/skills/vueuse-functions/references/useSortable.md +235 -0
- package/skills/vueuse-functions/references/useSorted.md +90 -0
- package/skills/vueuse-functions/references/useSpeechRecognition.md +94 -0
- package/skills/vueuse-functions/references/useSpeechSynthesis.md +105 -0
- package/skills/vueuse-functions/references/useStepper.md +137 -0
- package/skills/vueuse-functions/references/useStorage.md +278 -0
- package/skills/vueuse-functions/references/useStorageAsync.md +136 -0
- package/skills/vueuse-functions/references/useStyleTag.md +131 -0
- package/skills/vueuse-functions/references/useSubject.md +77 -0
- package/skills/vueuse-functions/references/useSubscription.md +33 -0
- package/skills/vueuse-functions/references/useSum.md +36 -0
- package/skills/vueuse-functions/references/useSupported.md +29 -0
- package/skills/vueuse-functions/references/useSwipe.md +75 -0
- package/skills/vueuse-functions/references/useTemplateRefsList.md +37 -0
- package/skills/vueuse-functions/references/useTextDirection.md +75 -0
- package/skills/vueuse-functions/references/useTextSelection.md +40 -0
- package/skills/vueuse-functions/references/useTextareaAutosize.md +97 -0
- package/skills/vueuse-functions/references/useThrottleFn.md +57 -0
- package/skills/vueuse-functions/references/useThrottledRefHistory.md +47 -0
- package/skills/vueuse-functions/references/useTimeAgo.md +154 -0
- package/skills/vueuse-functions/references/useTimeAgoIntl.md +117 -0
- package/skills/vueuse-functions/references/useTimeout.md +113 -0
- package/skills/vueuse-functions/references/useTimeoutFn.md +51 -0
- package/skills/vueuse-functions/references/useTimeoutPoll.md +47 -0
- package/skills/vueuse-functions/references/useTimestamp.md +89 -0
- package/skills/vueuse-functions/references/useTitle.md +113 -0
- package/skills/vueuse-functions/references/useToNumber.md +54 -0
- package/skills/vueuse-functions/references/useToString.md +34 -0
- package/skills/vueuse-functions/references/useToggle.md +103 -0
- package/skills/vueuse-functions/references/useTransition.md +265 -0
- package/skills/vueuse-functions/references/useTrunc.md +33 -0
- package/skills/vueuse-functions/references/useUrlSearchParams.md +121 -0
- package/skills/vueuse-functions/references/useUserMedia.md +1138 -0
- package/skills/vueuse-functions/references/useVModel.md +182 -0
- package/skills/vueuse-functions/references/useVModels.md +67 -0
- package/skills/vueuse-functions/references/useVibrate.md +87 -0
- package/skills/vueuse-functions/references/useVirtualList.md +182 -0
- package/skills/vueuse-functions/references/useWakeLock.md +50 -0
- package/skills/vueuse-functions/references/useWebNotification.md +176 -0
- package/skills/vueuse-functions/references/useWebSocket.md +299 -0
- package/skills/vueuse-functions/references/useWebWorker.md +60 -0
- package/skills/vueuse-functions/references/useWebWorkerFn.md +102 -0
- package/skills/vueuse-functions/references/useWindowFocus.md +46 -0
- package/skills/vueuse-functions/references/useWindowScroll.md +61 -0
- package/skills/vueuse-functions/references/useWindowSize.md +76 -0
- package/skills/vueuse-functions/references/useZoomFactor.md +53 -0
- package/skills/vueuse-functions/references/useZoomLevel.md +53 -0
- package/skills/vueuse-functions/references/watchArray.md +53 -0
- package/skills/vueuse-functions/references/watchAtMost.md +55 -0
- package/skills/vueuse-functions/references/watchDebounced.md +101 -0
- package/skills/vueuse-functions/references/watchDeep.md +54 -0
- package/skills/vueuse-functions/references/watchExtractedObservable.md +192 -0
- package/skills/vueuse-functions/references/watchIgnorable.md +120 -0
- package/skills/vueuse-functions/references/watchImmediate.md +44 -0
- package/skills/vueuse-functions/references/watchOnce.md +41 -0
- package/skills/vueuse-functions/references/watchPausable.md +86 -0
- package/skills/vueuse-functions/references/watchThrottled.md +108 -0
- package/skills/vueuse-functions/references/watchTriggerable.md +98 -0
- package/skills/vueuse-functions/references/watchWithFilter.md +54 -0
- package/skills/vueuse-functions/references/whenever.md +100 -0
- package/skills/web-design-guidelines/SKILL.md +39 -0
- package/skills/web-design-guidelines/SYNC.md +5 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Test Complex Composables with Host Component Wrapper
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Composables using lifecycle hooks or provide/inject fail when tested directly without a component context
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, testing, composables, vitest, lifecycle-hooks, provide-inject]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Test Complex Composables with Host Component Wrapper
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Composables that use Vue lifecycle hooks (`onMounted`, `onUnmounted`) or dependency injection (`inject`) require a component context to function. Testing them directly will cause errors or incorrect behavior.
|
|
12
|
+
|
|
13
|
+
Simple composables using only reactivity APIs can be tested directly. Complex composables need a helper function that creates a host component context.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Identify if composable uses lifecycle hooks or inject
|
|
18
|
+
- [ ] For simple composables (refs, computed only): test directly
|
|
19
|
+
- [ ] For complex composables: use `withSetup` helper pattern
|
|
20
|
+
- [ ] Clean up by unmounting the test app after each test
|
|
21
|
+
- [ ] Use `app.provide()` to mock injected dependencies
|
|
22
|
+
|
|
23
|
+
**Simple Composable - Test Directly:**
|
|
24
|
+
```javascript
|
|
25
|
+
// composables/useCounter.js
|
|
26
|
+
import { ref, computed } from 'vue'
|
|
27
|
+
|
|
28
|
+
export function useCounter(initialValue = 0) {
|
|
29
|
+
const count = ref(initialValue)
|
|
30
|
+
const doubled = computed(() => count.value * 2)
|
|
31
|
+
const increment = () => count.value++
|
|
32
|
+
|
|
33
|
+
return { count, doubled, increment }
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
// useCounter.test.js
|
|
39
|
+
import { describe, it, expect } from 'vitest'
|
|
40
|
+
import { useCounter } from './useCounter'
|
|
41
|
+
|
|
42
|
+
// CORRECT: Simple composable can be tested directly
|
|
43
|
+
describe('useCounter', () => {
|
|
44
|
+
it('initializes with default value', () => {
|
|
45
|
+
const { count } = useCounter()
|
|
46
|
+
expect(count.value).toBe(0)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('increments count', () => {
|
|
50
|
+
const { count, increment } = useCounter()
|
|
51
|
+
increment()
|
|
52
|
+
expect(count.value).toBe(1)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('computes doubled value', () => {
|
|
56
|
+
const { count, doubled, increment } = useCounter(5)
|
|
57
|
+
expect(doubled.value).toBe(10)
|
|
58
|
+
increment()
|
|
59
|
+
expect(doubled.value).toBe(12)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Complex Composable - Use Host Wrapper:**
|
|
65
|
+
```javascript
|
|
66
|
+
// composables/useFetch.js
|
|
67
|
+
import { ref, onMounted, onUnmounted, inject } from 'vue'
|
|
68
|
+
|
|
69
|
+
export function useFetch(url) {
|
|
70
|
+
const data = ref(null)
|
|
71
|
+
const error = ref(null)
|
|
72
|
+
const loading = ref(true)
|
|
73
|
+
let controller = null
|
|
74
|
+
|
|
75
|
+
// Uses inject - needs component context
|
|
76
|
+
const apiClient = inject('apiClient')
|
|
77
|
+
|
|
78
|
+
// Uses lifecycle hooks - needs component context
|
|
79
|
+
onMounted(async () => {
|
|
80
|
+
controller = new AbortController()
|
|
81
|
+
try {
|
|
82
|
+
const response = await apiClient.get(url, { signal: controller.signal })
|
|
83
|
+
data.value = response.data
|
|
84
|
+
} catch (e) {
|
|
85
|
+
if (e.name !== 'AbortError') error.value = e
|
|
86
|
+
} finally {
|
|
87
|
+
loading.value = false
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
onUnmounted(() => {
|
|
92
|
+
controller?.abort()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
return { data, error, loading }
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
// test-utils.js
|
|
101
|
+
import { createApp } from 'vue'
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Helper to test composables that need component context
|
|
105
|
+
*/
|
|
106
|
+
export function withSetup(composable) {
|
|
107
|
+
let result
|
|
108
|
+
|
|
109
|
+
const app = createApp({
|
|
110
|
+
setup() {
|
|
111
|
+
result = composable()
|
|
112
|
+
// Return a render function to suppress warnings
|
|
113
|
+
return () => {}
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
app.mount(document.createElement('div'))
|
|
118
|
+
|
|
119
|
+
return [result, app]
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
// useFetch.test.js
|
|
125
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
126
|
+
import { flushPromises } from '@vue/test-utils'
|
|
127
|
+
import { withSetup } from './test-utils'
|
|
128
|
+
import { useFetch } from './useFetch'
|
|
129
|
+
|
|
130
|
+
describe('useFetch', () => {
|
|
131
|
+
let app
|
|
132
|
+
const mockApiClient = {
|
|
133
|
+
get: vi.fn()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
afterEach(() => {
|
|
137
|
+
// IMPORTANT: Clean up to trigger onUnmounted
|
|
138
|
+
app?.unmount()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('fetches data on mount', async () => {
|
|
142
|
+
mockApiClient.get.mockResolvedValue({ data: { id: 1, name: 'Test' } })
|
|
143
|
+
|
|
144
|
+
const [result, testApp] = withSetup(() => useFetch('/api/test'))
|
|
145
|
+
app = testApp
|
|
146
|
+
|
|
147
|
+
// Provide mocked dependency
|
|
148
|
+
app.provide('apiClient', mockApiClient)
|
|
149
|
+
|
|
150
|
+
// Wait for async operations
|
|
151
|
+
await flushPromises()
|
|
152
|
+
|
|
153
|
+
expect(result.data.value).toEqual({ id: 1, name: 'Test' })
|
|
154
|
+
expect(result.loading.value).toBe(false)
|
|
155
|
+
expect(result.error.value).toBeNull()
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('handles errors', async () => {
|
|
159
|
+
const testError = new Error('Network error')
|
|
160
|
+
mockApiClient.get.mockRejectedValue(testError)
|
|
161
|
+
|
|
162
|
+
const [result, testApp] = withSetup(() => useFetch('/api/test'))
|
|
163
|
+
app = testApp
|
|
164
|
+
app.provide('apiClient', mockApiClient)
|
|
165
|
+
|
|
166
|
+
await flushPromises()
|
|
167
|
+
|
|
168
|
+
expect(result.error.value).toBe(testError)
|
|
169
|
+
expect(result.data.value).toBeNull()
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Enhanced withSetup Helper with Provide Support
|
|
175
|
+
```javascript
|
|
176
|
+
// test-utils.js
|
|
177
|
+
export function withSetup(composable, options = {}) {
|
|
178
|
+
let result
|
|
179
|
+
|
|
180
|
+
const app = createApp({
|
|
181
|
+
setup() {
|
|
182
|
+
result = composable()
|
|
183
|
+
return () => {}
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// Apply global provides before mounting
|
|
188
|
+
if (options.provide) {
|
|
189
|
+
Object.entries(options.provide).forEach(([key, value]) => {
|
|
190
|
+
app.provide(key, value)
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
app.mount(document.createElement('div'))
|
|
195
|
+
|
|
196
|
+
return [result, app]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Usage
|
|
200
|
+
const [result, app] = withSetup(() => useMyComposable(), {
|
|
201
|
+
provide: {
|
|
202
|
+
apiClient: mockApiClient,
|
|
203
|
+
currentUser: { id: 1, name: 'Test User' }
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Testing with @vue/test-utils mount
|
|
209
|
+
```javascript
|
|
210
|
+
import { mount } from '@vue/test-utils'
|
|
211
|
+
import { defineComponent } from 'vue'
|
|
212
|
+
import { useFetch } from './useFetch'
|
|
213
|
+
|
|
214
|
+
test('useFetch in component context', async () => {
|
|
215
|
+
const TestComponent = defineComponent({
|
|
216
|
+
setup() {
|
|
217
|
+
const { data, loading } = useFetch('/api/users')
|
|
218
|
+
return { data, loading }
|
|
219
|
+
},
|
|
220
|
+
template: '<div>{{ loading ? "Loading..." : data }}</div>'
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
const wrapper = mount(TestComponent, {
|
|
224
|
+
global: {
|
|
225
|
+
provide: {
|
|
226
|
+
apiClient: mockApiClient
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
await flushPromises()
|
|
232
|
+
expect(wrapper.text()).toContain('Test data')
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Reference
|
|
237
|
+
- [Vue.js Testing Guide - Testing Composables](https://vuejs.org/guide/scaling-up/testing#testing-composables)
|
|
238
|
+
- [Vue Test Utils - Mounting Components](https://test-utils.vuejs.org/guide/)
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Playwright for E2E Testing - Cross-Browser Support and Better DX
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Cypress has browser limitations and some features require paid subscriptions
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, testing, e2e, playwright, cypress, end-to-end]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Use Playwright for E2E Testing - Cross-Browser Support and Better DX
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Playwright offers superior cross-browser testing (Chromium, WebKit, Firefox), excellent debugging tools, and is fully open source. Cypress has limitations with WebKit support and requires paid subscriptions for some features.
|
|
12
|
+
|
|
13
|
+
Use Playwright for new E2E testing setups. Consider Cypress if team already has expertise or for its visual debugging UI.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Install Playwright with browsers for your target platforms
|
|
18
|
+
- [ ] Configure for Vue dev server integration
|
|
19
|
+
- [ ] Set up projects for different browsers
|
|
20
|
+
- [ ] Use locator strategies that match component test patterns
|
|
21
|
+
- [ ] Configure CI for parallel test execution
|
|
22
|
+
- [ ] Use trace and screenshot features for debugging
|
|
23
|
+
|
|
24
|
+
## Quick Setup
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Install Playwright
|
|
28
|
+
npm init playwright@latest
|
|
29
|
+
|
|
30
|
+
# This will create:
|
|
31
|
+
# - playwright.config.ts
|
|
32
|
+
# - tests/ directory
|
|
33
|
+
# - tests-examples/ directory
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**playwright.config.ts:**
|
|
37
|
+
```typescript
|
|
38
|
+
import { defineConfig, devices } from '@playwright/test'
|
|
39
|
+
|
|
40
|
+
export default defineConfig({
|
|
41
|
+
testDir: './e2e',
|
|
42
|
+
fullyParallel: true,
|
|
43
|
+
forbidOnly: !!process.env.CI,
|
|
44
|
+
retries: process.env.CI ? 2 : 0,
|
|
45
|
+
workers: process.env.CI ? 1 : undefined,
|
|
46
|
+
reporter: 'html',
|
|
47
|
+
|
|
48
|
+
use: {
|
|
49
|
+
// Base URL for navigation
|
|
50
|
+
baseURL: 'http://localhost:5173',
|
|
51
|
+
// Capture trace on first retry
|
|
52
|
+
trace: 'on-first-retry',
|
|
53
|
+
// Screenshot on failure
|
|
54
|
+
screenshot: 'only-on-failure',
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
projects: [
|
|
58
|
+
{
|
|
59
|
+
name: 'chromium',
|
|
60
|
+
use: { ...devices['Desktop Chrome'] },
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'firefox',
|
|
64
|
+
use: { ...devices['Desktop Firefox'] },
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'webkit',
|
|
68
|
+
use: { ...devices['Desktop Safari'] },
|
|
69
|
+
},
|
|
70
|
+
// Mobile viewports
|
|
71
|
+
{
|
|
72
|
+
name: 'Mobile Chrome',
|
|
73
|
+
use: { ...devices['Pixel 5'] },
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
|
|
77
|
+
// Run local dev server before tests
|
|
78
|
+
webServer: {
|
|
79
|
+
command: 'npm run dev',
|
|
80
|
+
url: 'http://localhost:5173',
|
|
81
|
+
reuseExistingServer: !process.env.CI,
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## E2E Test Example
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// e2e/user-flow.spec.ts
|
|
90
|
+
import { test, expect } from '@playwright/test'
|
|
91
|
+
|
|
92
|
+
test.describe('User Authentication', () => {
|
|
93
|
+
test('user can log in and see dashboard', async ({ page }) => {
|
|
94
|
+
// Navigate to login
|
|
95
|
+
await page.goto('/login')
|
|
96
|
+
|
|
97
|
+
// Fill login form
|
|
98
|
+
await page.getByLabel('Email').fill('user@example.com')
|
|
99
|
+
await page.getByLabel('Password').fill('password123')
|
|
100
|
+
await page.getByRole('button', { name: 'Sign In' }).click()
|
|
101
|
+
|
|
102
|
+
// Verify redirect to dashboard
|
|
103
|
+
await expect(page).toHaveURL('/dashboard')
|
|
104
|
+
await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible()
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('shows error for invalid credentials', async ({ page }) => {
|
|
108
|
+
await page.goto('/login')
|
|
109
|
+
|
|
110
|
+
await page.getByLabel('Email').fill('wrong@example.com')
|
|
111
|
+
await page.getByLabel('Password').fill('wrongpassword')
|
|
112
|
+
await page.getByRole('button', { name: 'Sign In' }).click()
|
|
113
|
+
|
|
114
|
+
await expect(page.getByRole('alert')).toContainText('Invalid credentials')
|
|
115
|
+
await expect(page).toHaveURL('/login')
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Playwright vs Cypress Comparison
|
|
121
|
+
|
|
122
|
+
| Feature | Playwright | Cypress |
|
|
123
|
+
|---------|------------|---------|
|
|
124
|
+
| Browsers | Chromium, Firefox, WebKit | Chromium, Firefox, Electron (WebKit experimental) |
|
|
125
|
+
| Cross-browser | Full support | Limited |
|
|
126
|
+
| Parallelization | Built-in | Requires Cypress Cloud |
|
|
127
|
+
| Open source | Fully | Core only |
|
|
128
|
+
| Mobile testing | Device emulation | Limited |
|
|
129
|
+
| Debugging | Inspector, trace viewer | Time-travel UI |
|
|
130
|
+
| API testing | Built-in | Plugin required |
|
|
131
|
+
| Iframes | Full support | Limited |
|
|
132
|
+
|
|
133
|
+
## Testing Vue Components with Data-Testid
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// e2e/product-list.spec.ts
|
|
137
|
+
import { test, expect } from '@playwright/test'
|
|
138
|
+
|
|
139
|
+
test('user can add product to cart', async ({ page }) => {
|
|
140
|
+
await page.goto('/products')
|
|
141
|
+
|
|
142
|
+
// Use data-testid for reliable selectors
|
|
143
|
+
await page.getByTestId('product-card').first().click()
|
|
144
|
+
|
|
145
|
+
// Verify product detail page
|
|
146
|
+
await expect(page.getByTestId('product-title')).toBeVisible()
|
|
147
|
+
|
|
148
|
+
// Add to cart
|
|
149
|
+
await page.getByTestId('add-to-cart-button').click()
|
|
150
|
+
|
|
151
|
+
// Verify cart updated
|
|
152
|
+
await expect(page.getByTestId('cart-count')).toHaveText('1')
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Page Object Pattern for Vue Apps
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// e2e/pages/LoginPage.ts
|
|
160
|
+
import { Page, Locator } from '@playwright/test'
|
|
161
|
+
|
|
162
|
+
export class LoginPage {
|
|
163
|
+
readonly page: Page
|
|
164
|
+
readonly emailInput: Locator
|
|
165
|
+
readonly passwordInput: Locator
|
|
166
|
+
readonly submitButton: Locator
|
|
167
|
+
readonly errorMessage: Locator
|
|
168
|
+
|
|
169
|
+
constructor(page: Page) {
|
|
170
|
+
this.page = page
|
|
171
|
+
this.emailInput = page.getByLabel('Email')
|
|
172
|
+
this.passwordInput = page.getByLabel('Password')
|
|
173
|
+
this.submitButton = page.getByRole('button', { name: 'Sign In' })
|
|
174
|
+
this.errorMessage = page.getByRole('alert')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async goto() {
|
|
178
|
+
await this.page.goto('/login')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async login(email: string, password: string) {
|
|
182
|
+
await this.emailInput.fill(email)
|
|
183
|
+
await this.passwordInput.fill(password)
|
|
184
|
+
await this.submitButton.click()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// e2e/auth.spec.ts
|
|
191
|
+
import { test, expect } from '@playwright/test'
|
|
192
|
+
import { LoginPage } from './pages/LoginPage'
|
|
193
|
+
|
|
194
|
+
test('successful login', async ({ page }) => {
|
|
195
|
+
const loginPage = new LoginPage(page)
|
|
196
|
+
await loginPage.goto()
|
|
197
|
+
await loginPage.login('user@example.com', 'password123')
|
|
198
|
+
|
|
199
|
+
await expect(page).toHaveURL('/dashboard')
|
|
200
|
+
})
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Visual Regression Testing
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
test('homepage visual regression', async ({ page }) => {
|
|
207
|
+
await page.goto('/')
|
|
208
|
+
|
|
209
|
+
// Full page screenshot comparison
|
|
210
|
+
await expect(page).toHaveScreenshot('homepage.png')
|
|
211
|
+
|
|
212
|
+
// Element-specific screenshot
|
|
213
|
+
await expect(page.getByTestId('hero-section')).toHaveScreenshot('hero.png')
|
|
214
|
+
})
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Running Tests
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# Run all tests
|
|
221
|
+
npx playwright test
|
|
222
|
+
|
|
223
|
+
# Run in headed mode (see browser)
|
|
224
|
+
npx playwright test --headed
|
|
225
|
+
|
|
226
|
+
# Run specific file
|
|
227
|
+
npx playwright test e2e/auth.spec.ts
|
|
228
|
+
|
|
229
|
+
# Run in specific browser
|
|
230
|
+
npx playwright test --project=chromium
|
|
231
|
+
|
|
232
|
+
# Debug mode
|
|
233
|
+
npx playwright test --debug
|
|
234
|
+
|
|
235
|
+
# Generate test from actions
|
|
236
|
+
npx playwright codegen localhost:5173
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Reference
|
|
240
|
+
- [Playwright Documentation](https://playwright.dev/)
|
|
241
|
+
- [Vue.js E2E Testing Recommendations](https://vuejs.org/guide/scaling-up/testing#e2e-testing)
|
|
242
|
+
- [Playwright Best Practices](https://playwright.dev/docs/best-practices)
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Avoid Snapshot-Only Tests - They Don't Prove Correctness
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Snapshot tests verify structure but not functionality, leading to false confidence and brittle tests
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, testing, snapshot, vitest, vue-test-utils, anti-pattern]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Avoid Snapshot-Only Tests - They Don't Prove Correctness
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Snapshot tests only verify that HTML structure hasn't changed - they don't verify that the component works correctly. Relying exclusively on snapshots leads to false confidence and tests that break on any refactoring, even when functionality is preserved.
|
|
12
|
+
|
|
13
|
+
Use snapshots sparingly for regression detection. Prefer behavioral assertions that test what the component does.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Don't use snapshots as the only assertion for component behavior
|
|
18
|
+
- [ ] Use snapshots for regression detection on stable UI components
|
|
19
|
+
- [ ] Always pair snapshots with behavioral assertions
|
|
20
|
+
- [ ] Keep snapshots small and focused (avoid full component snapshots)
|
|
21
|
+
- [ ] Review snapshot diffs carefully - don't blindly update
|
|
22
|
+
- [ ] Consider inline snapshots for small, critical structures
|
|
23
|
+
|
|
24
|
+
**Incorrect:**
|
|
25
|
+
```javascript
|
|
26
|
+
import { mount } from '@vue/test-utils'
|
|
27
|
+
import UserCard from './UserCard.vue'
|
|
28
|
+
|
|
29
|
+
// BAD: Snapshot-only test proves nothing about functionality
|
|
30
|
+
test('UserCard renders correctly', () => {
|
|
31
|
+
const wrapper = mount(UserCard, {
|
|
32
|
+
props: { user: { name: 'John', email: 'john@example.com' } }
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
expect(wrapper.html()).toMatchSnapshot()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// This test passes even if:
|
|
39
|
+
// - The email isn't clickable
|
|
40
|
+
// - The avatar doesn't load
|
|
41
|
+
// - User actions are completely broken
|
|
42
|
+
// - Accessibility is broken
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Correct:**
|
|
46
|
+
```javascript
|
|
47
|
+
import { mount } from '@vue/test-utils'
|
|
48
|
+
import UserCard from './UserCard.vue'
|
|
49
|
+
|
|
50
|
+
// CORRECT: Test actual behavior
|
|
51
|
+
test('UserCard displays user information', () => {
|
|
52
|
+
const wrapper = mount(UserCard, {
|
|
53
|
+
props: { user: { name: 'John', email: 'john@example.com' } }
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
expect(wrapper.find('[data-testid="user-name"]').text()).toBe('John')
|
|
57
|
+
expect(wrapper.find('[data-testid="user-email"]').text()).toBe('john@example.com')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('UserCard email link is clickable', async () => {
|
|
61
|
+
const wrapper = mount(UserCard, {
|
|
62
|
+
props: { user: { name: 'John', email: 'john@example.com' } }
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const emailLink = wrapper.find('a[href^="mailto:"]')
|
|
66
|
+
expect(emailLink.exists()).toBe(true)
|
|
67
|
+
expect(emailLink.attributes('href')).toBe('mailto:john@example.com')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('UserCard emits select event when clicked', async () => {
|
|
71
|
+
const wrapper = mount(UserCard, {
|
|
72
|
+
props: { user: { id: 1, name: 'John' } }
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
await wrapper.trigger('click')
|
|
76
|
+
|
|
77
|
+
expect(wrapper.emitted('select')).toBeTruthy()
|
|
78
|
+
expect(wrapper.emitted('select')[0]).toEqual([{ id: 1, name: 'John' }])
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## When Snapshots ARE Useful
|
|
83
|
+
|
|
84
|
+
### Regression Detection for Stable Components
|
|
85
|
+
```javascript
|
|
86
|
+
// ACCEPTABLE: Snapshot as additional check, not the only check
|
|
87
|
+
test('ErrorBoundary renders error message', () => {
|
|
88
|
+
const wrapper = mount(ErrorBoundary, {
|
|
89
|
+
props: { error: new Error('Something went wrong') }
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// Primary assertions - verify behavior
|
|
93
|
+
expect(wrapper.find('.error-title').text()).toBe('Error')
|
|
94
|
+
expect(wrapper.find('.error-message').text()).toContain('Something went wrong')
|
|
95
|
+
|
|
96
|
+
// Secondary snapshot - catches unexpected structural changes
|
|
97
|
+
expect(wrapper.find('.error-container').html()).toMatchSnapshot()
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Inline Snapshots for Small Structures
|
|
102
|
+
```javascript
|
|
103
|
+
// ACCEPTABLE: Inline snapshot for small, critical structure
|
|
104
|
+
test('generates correct list markup', () => {
|
|
105
|
+
const wrapper = mount(ListItem, { props: { item: 'Test' } })
|
|
106
|
+
|
|
107
|
+
expect(wrapper.html()).toMatchInlineSnapshot(`
|
|
108
|
+
"<li class="list-item">Test</li>"
|
|
109
|
+
`)
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Complex SVG or Icon Output
|
|
114
|
+
```javascript
|
|
115
|
+
// ACCEPTABLE: Snapshot for complex generated content
|
|
116
|
+
test('renders correct chart SVG', () => {
|
|
117
|
+
const wrapper = mount(PieChart, {
|
|
118
|
+
props: { data: [30, 40, 30] }
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Verify key behavior
|
|
122
|
+
expect(wrapper.findAll('path').length).toBe(3)
|
|
123
|
+
|
|
124
|
+
// Snapshot for full SVG structure
|
|
125
|
+
expect(wrapper.find('svg').html()).toMatchSnapshot()
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Better Alternatives to Snapshots
|
|
130
|
+
|
|
131
|
+
### Test Specific Elements
|
|
132
|
+
```javascript
|
|
133
|
+
// Instead of snapshotting entire component
|
|
134
|
+
test('renders product with all required fields', () => {
|
|
135
|
+
const wrapper = mount(ProductCard, {
|
|
136
|
+
props: { product: { name: 'Widget', price: 9.99, inStock: true } }
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
expect(wrapper.find('.product-name').text()).toBe('Widget')
|
|
140
|
+
expect(wrapper.find('.product-price').text()).toContain('9.99')
|
|
141
|
+
expect(wrapper.find('.in-stock-badge').exists()).toBe(true)
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Test CSS Classes for Styling
|
|
146
|
+
```javascript
|
|
147
|
+
test('applies danger styling for errors', () => {
|
|
148
|
+
const wrapper = mount(Alert, {
|
|
149
|
+
props: { type: 'error', message: 'Failed!' }
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
expect(wrapper.classes()).toContain('alert-danger')
|
|
153
|
+
expect(wrapper.find('.alert-icon').classes()).toContain('icon-error')
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Use Testing Library Queries
|
|
158
|
+
```javascript
|
|
159
|
+
import { render, screen } from '@testing-library/vue'
|
|
160
|
+
|
|
161
|
+
test('form has accessible labels', () => {
|
|
162
|
+
render(LoginForm)
|
|
163
|
+
|
|
164
|
+
// Testing Library queries verify accessibility
|
|
165
|
+
expect(screen.getByLabelText('Email')).toBeInTheDocument()
|
|
166
|
+
expect(screen.getByLabelText('Password')).toBeInTheDocument()
|
|
167
|
+
expect(screen.getByRole('button', { name: 'Sign In' })).toBeInTheDocument()
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Snapshot Anti-Patterns
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// ANTI-PATTERN: Giant component snapshot
|
|
175
|
+
test('page renders', () => {
|
|
176
|
+
const wrapper = mount(EntirePageComponent)
|
|
177
|
+
expect(wrapper.html()).toMatchSnapshot() // 500+ lines of HTML
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
// ANTI-PATTERN: Snapshot with dynamic content
|
|
181
|
+
test('shows current date', () => {
|
|
182
|
+
const wrapper = mount(DateDisplay)
|
|
183
|
+
expect(wrapper.html()).toMatchSnapshot() // Fails every day!
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
// ANTI-PATTERN: Snapshot after every test
|
|
187
|
+
test('button works', async () => {
|
|
188
|
+
const wrapper = mount(Counter)
|
|
189
|
+
await wrapper.find('button').trigger('click')
|
|
190
|
+
expect(wrapper.html()).toMatchSnapshot() // Redundant
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Reference
|
|
195
|
+
- [Vue.js Testing Guide - What Not to Test](https://vuejs.org/guide/scaling-up/testing)
|
|
196
|
+
- [Effective Snapshot Testing](https://kentcdodds.com/blog/effective-snapshot-testing)
|
|
197
|
+
- [Vitest Snapshot Testing](https://vitest.dev/guide/snapshot.html)
|