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,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Teleported Content Requires Special Testing Approach
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Vue Test Utils cannot find teleported content using standard wrapper.find() methods
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, teleport, testing, vue-test-utils]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Teleported Content Requires Special Testing Approach
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Vue Test Utils scopes queries to the mounted component. Teleported content renders outside the component's DOM tree, so `wrapper.find()` cannot locate it. This leads to failing tests and confusion.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Stub Teleport in unit tests to keep content in component tree
|
|
16
|
+
- [ ] Use `document.body` queries for integration tests with real Teleport
|
|
17
|
+
- [ ] Consider using `getComponent()` instead of DOM queries for teleported components
|
|
18
|
+
|
|
19
|
+
**Problem - Standard Testing Fails:**
|
|
20
|
+
```vue
|
|
21
|
+
<!-- Modal.vue -->
|
|
22
|
+
<template>
|
|
23
|
+
<button @click="open = true">Open</button>
|
|
24
|
+
<Teleport to="body">
|
|
25
|
+
<div v-if="open" class="modal" data-testid="modal">
|
|
26
|
+
<input type="text" data-testid="modal-input" />
|
|
27
|
+
</div>
|
|
28
|
+
</Teleport>
|
|
29
|
+
</template>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// Modal.spec.ts - BROKEN
|
|
34
|
+
import { mount } from '@vue/test-utils'
|
|
35
|
+
import Modal from './Modal.vue'
|
|
36
|
+
|
|
37
|
+
test('modal input exists', async () => {
|
|
38
|
+
const wrapper = mount(Modal)
|
|
39
|
+
await wrapper.find('button').trigger('click')
|
|
40
|
+
|
|
41
|
+
// FAILS: Teleported content is not in wrapper's DOM tree
|
|
42
|
+
expect(wrapper.find('[data-testid="modal-input"]').exists()).toBe(true)
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Solution 1 - Stub Teleport:**
|
|
47
|
+
```ts
|
|
48
|
+
import { mount } from '@vue/test-utils'
|
|
49
|
+
import Modal from './Modal.vue'
|
|
50
|
+
|
|
51
|
+
test('modal input exists', async () => {
|
|
52
|
+
const wrapper = mount(Modal, {
|
|
53
|
+
global: {
|
|
54
|
+
stubs: {
|
|
55
|
+
// Stub teleport to render content inline
|
|
56
|
+
Teleport: true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
await wrapper.find('button').trigger('click')
|
|
62
|
+
|
|
63
|
+
// Works: Content renders inside wrapper
|
|
64
|
+
expect(wrapper.find('[data-testid="modal-input"]').exists()).toBe(true)
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Solution 2 - Query Document Body:**
|
|
69
|
+
```ts
|
|
70
|
+
import { mount } from '@vue/test-utils'
|
|
71
|
+
import Modal from './Modal.vue'
|
|
72
|
+
|
|
73
|
+
test('modal renders to body', async () => {
|
|
74
|
+
const wrapper = mount(Modal, {
|
|
75
|
+
attachTo: document.body // Required for Teleport to work
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
await wrapper.find('button').trigger('click')
|
|
79
|
+
|
|
80
|
+
// Query the actual DOM
|
|
81
|
+
const modal = document.querySelector('[data-testid="modal"]')
|
|
82
|
+
expect(modal).toBeTruthy()
|
|
83
|
+
|
|
84
|
+
const input = document.querySelector('[data-testid="modal-input"]')
|
|
85
|
+
expect(input).toBeTruthy()
|
|
86
|
+
|
|
87
|
+
// Cleanup
|
|
88
|
+
wrapper.unmount()
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Solution 3 - Custom Teleport Stub with Content Access:**
|
|
93
|
+
```ts
|
|
94
|
+
import { mount, config } from '@vue/test-utils'
|
|
95
|
+
import { h, Teleport } from 'vue'
|
|
96
|
+
import Modal from './Modal.vue'
|
|
97
|
+
|
|
98
|
+
// Custom stub that renders content in a testable way
|
|
99
|
+
const TeleportStub = {
|
|
100
|
+
setup(props, { slots }) {
|
|
101
|
+
return () => h('div', { class: 'teleport-stub' }, slots.default?.())
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
test('modal with custom stub', async () => {
|
|
106
|
+
const wrapper = mount(Modal, {
|
|
107
|
+
global: {
|
|
108
|
+
stubs: {
|
|
109
|
+
Teleport: TeleportStub
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
await wrapper.find('button').trigger('click')
|
|
115
|
+
|
|
116
|
+
// Content is inside .teleport-stub
|
|
117
|
+
expect(wrapper.find('.teleport-stub [data-testid="modal-input"]').exists()).toBe(true)
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Testing Vue Final Modal and UI Libraries
|
|
122
|
+
|
|
123
|
+
Libraries like Vue Final Modal use Teleport internally, causing test failures:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
// Problem: Vue Final Modal teleports to body
|
|
127
|
+
import { VueFinalModal } from 'vue-final-modal'
|
|
128
|
+
|
|
129
|
+
test('modal content', async () => {
|
|
130
|
+
const wrapper = mount(MyComponent, {
|
|
131
|
+
global: {
|
|
132
|
+
stubs: {
|
|
133
|
+
// Stub the modal component to avoid teleport issues
|
|
134
|
+
VueFinalModal: true
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## E2E Testing (Cypress, Playwright)
|
|
142
|
+
|
|
143
|
+
E2E tests query the real DOM, so Teleport works naturally:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
// Cypress
|
|
147
|
+
it('opens modal', () => {
|
|
148
|
+
cy.visit('/page-with-modal')
|
|
149
|
+
cy.get('button').click()
|
|
150
|
+
|
|
151
|
+
// Works: Cypress queries the real DOM
|
|
152
|
+
cy.get('[data-testid="modal"]').should('be.visible')
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Reference
|
|
157
|
+
- [Vue Test Utils - Teleport](https://test-utils.vuejs.org/guide/advanced/teleport)
|
|
158
|
+
- [Vue Test Utils - Stubs](https://test-utils.vuejs.org/guide/advanced/stubs-shallow-mount)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Properly Handle Async Updates with nextTick and flushPromises
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Race conditions and flaky tests occur when async DOM updates or API calls complete after assertions run
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, testing, async, flushPromises, nextTick, vitest, vue-test-utils, race-condition]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Properly Handle Async Updates with nextTick and flushPromises
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Vue updates the DOM asynchronously. Without properly awaiting these updates, tests may assert against stale DOM state, causing intermittent failures and false negatives.
|
|
12
|
+
|
|
13
|
+
Use `await` with triggers and `setValue`, use `nextTick` for reactive updates, and use `flushPromises` for external async operations like API calls.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Always await `trigger()` and `setValue()` calls
|
|
18
|
+
- [ ] Use `await nextTick()` after programmatic reactive state changes
|
|
19
|
+
- [ ] Use `await flushPromises()` for external async operations (API calls, timers)
|
|
20
|
+
- [ ] Don't chain multiple `nextTick` calls - use `flushPromises` instead
|
|
21
|
+
- [ ] Consider using `waitFor` from testing-library for polling assertions
|
|
22
|
+
|
|
23
|
+
**Incorrect:**
|
|
24
|
+
```javascript
|
|
25
|
+
import { mount } from '@vue/test-utils'
|
|
26
|
+
import SearchComponent from './SearchComponent.vue'
|
|
27
|
+
|
|
28
|
+
// BAD: Not awaiting trigger - assertion runs before DOM updates
|
|
29
|
+
test('search filters results', () => {
|
|
30
|
+
const wrapper = mount(SearchComponent)
|
|
31
|
+
|
|
32
|
+
wrapper.find('input').setValue('vue') // Missing await!
|
|
33
|
+
wrapper.find('button').trigger('click') // Missing await!
|
|
34
|
+
|
|
35
|
+
// This assertion likely fails - DOM hasn't updated yet
|
|
36
|
+
expect(wrapper.findAll('.result').length).toBe(3)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// BAD: Using nextTick for API calls
|
|
40
|
+
test('loads data from API', async () => {
|
|
41
|
+
const wrapper = mount(DataLoader)
|
|
42
|
+
|
|
43
|
+
await nextTick() // This won't wait for the API call!
|
|
44
|
+
|
|
45
|
+
// Assertion runs before fetch completes
|
|
46
|
+
expect(wrapper.find('.data').text()).toBe('Loaded data')
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Correct:**
|
|
51
|
+
```javascript
|
|
52
|
+
import { mount, flushPromises } from '@vue/test-utils'
|
|
53
|
+
import { nextTick } from 'vue'
|
|
54
|
+
import SearchComponent from './SearchComponent.vue'
|
|
55
|
+
import DataLoader from './DataLoader.vue'
|
|
56
|
+
|
|
57
|
+
// CORRECT: Await trigger and setValue
|
|
58
|
+
test('search filters results', async () => {
|
|
59
|
+
const wrapper = mount(SearchComponent)
|
|
60
|
+
|
|
61
|
+
await wrapper.find('input').setValue('vue')
|
|
62
|
+
await wrapper.find('button').trigger('click')
|
|
63
|
+
|
|
64
|
+
expect(wrapper.findAll('.result').length).toBe(3)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// CORRECT: Use flushPromises for API calls
|
|
68
|
+
test('loads data from API', async () => {
|
|
69
|
+
const wrapper = mount(DataLoader)
|
|
70
|
+
|
|
71
|
+
// Wait for all pending promises to resolve
|
|
72
|
+
await flushPromises()
|
|
73
|
+
|
|
74
|
+
expect(wrapper.find('.data').text()).toBe('Loaded data')
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## When to Use Each Method
|
|
79
|
+
|
|
80
|
+
### `await trigger()` / `await setValue()` - User Interactions
|
|
81
|
+
```javascript
|
|
82
|
+
// These methods return nextTick internally
|
|
83
|
+
await wrapper.find('button').trigger('click')
|
|
84
|
+
await wrapper.find('input').setValue('new value')
|
|
85
|
+
await wrapper.find('form').trigger('submit')
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `await nextTick()` - Programmatic Reactive Updates
|
|
89
|
+
```javascript
|
|
90
|
+
import { nextTick } from 'vue'
|
|
91
|
+
|
|
92
|
+
test('reflects programmatic state changes', async () => {
|
|
93
|
+
const wrapper = mount(Counter)
|
|
94
|
+
|
|
95
|
+
// Direct state modification (when testing with exposed internals)
|
|
96
|
+
wrapper.vm.count = 5
|
|
97
|
+
|
|
98
|
+
await nextTick() // Wait for Vue to update DOM
|
|
99
|
+
|
|
100
|
+
expect(wrapper.find('.count').text()).toBe('5')
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `await flushPromises()` - External Async Operations
|
|
105
|
+
```javascript
|
|
106
|
+
import { flushPromises } from '@vue/test-utils'
|
|
107
|
+
|
|
108
|
+
test('displays fetched data', async () => {
|
|
109
|
+
const wrapper = mount(UserProfile, {
|
|
110
|
+
props: { userId: 1 }
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// Wait for component's API call to complete
|
|
114
|
+
await flushPromises()
|
|
115
|
+
|
|
116
|
+
expect(wrapper.find('.username').text()).toBe('John')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Sometimes you need multiple flushPromises for chained async operations
|
|
120
|
+
test('processes data after fetch', async () => {
|
|
121
|
+
const wrapper = mount(DataProcessor)
|
|
122
|
+
|
|
123
|
+
await flushPromises() // Wait for fetch
|
|
124
|
+
await flushPromises() // Wait for processing triggered by fetch
|
|
125
|
+
|
|
126
|
+
expect(wrapper.find('.processed').exists()).toBe(true)
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Common Pattern: Combining Methods
|
|
131
|
+
```javascript
|
|
132
|
+
test('submits form and shows success', async () => {
|
|
133
|
+
const wrapper = mount(ContactForm)
|
|
134
|
+
|
|
135
|
+
// Fill form (awaiting each interaction)
|
|
136
|
+
await wrapper.find('#name').setValue('John')
|
|
137
|
+
await wrapper.find('#email').setValue('john@example.com')
|
|
138
|
+
|
|
139
|
+
// Submit form
|
|
140
|
+
await wrapper.find('form').trigger('submit')
|
|
141
|
+
|
|
142
|
+
// Wait for API submission to complete
|
|
143
|
+
await flushPromises()
|
|
144
|
+
|
|
145
|
+
// Assert success state
|
|
146
|
+
expect(wrapper.find('.success-message').exists()).toBe(true)
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Testing with MSW or Mock APIs
|
|
151
|
+
```javascript
|
|
152
|
+
import { flushPromises } from '@vue/test-utils'
|
|
153
|
+
import { rest } from 'msw'
|
|
154
|
+
import { setupServer } from 'msw/node'
|
|
155
|
+
|
|
156
|
+
const server = setupServer(
|
|
157
|
+
rest.get('/api/user', (req, res, ctx) => {
|
|
158
|
+
return res(ctx.json({ name: 'John' }))
|
|
159
|
+
})
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
test('displays user data', async () => {
|
|
163
|
+
const wrapper = mount(UserCard)
|
|
164
|
+
|
|
165
|
+
// MSW might require multiple flushPromises
|
|
166
|
+
await flushPromises()
|
|
167
|
+
await flushPromises()
|
|
168
|
+
|
|
169
|
+
expect(wrapper.find('.name').text()).toBe('John')
|
|
170
|
+
})
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Reference
|
|
174
|
+
- [Vue Test Utils - Asynchronous Behavior](https://test-utils.vuejs.org/guide/advanced/async-suspense)
|
|
175
|
+
- [Vue.js Testing Guide](https://vuejs.org/guide/scaling-up/testing)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Choose Browser-Based Runner for Style and DOM Event Testing
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Node-based runners cannot test real CSS behavior, native DOM events, cookies, or computed styles
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, testing, component-testing, vitest, browser, jsdom]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Choose Browser-Based Runner for Style and DOM Event Testing
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Node-based test runners (Vitest with jsdom/happy-dom) simulate the DOM but cannot test real CSS rendering, native browser events, cookies, computed styles, or cross-browser behavior. Use browser-based runners when these matter.
|
|
12
|
+
|
|
13
|
+
Use Vitest for most component tests (fast), but use Vitest Browser Mode when testing visual/DOM-dependent features.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Use Vitest (node) for logic-focused component tests
|
|
18
|
+
- [ ] Use Vitest Browser Mode for style-dependent tests
|
|
19
|
+
- [ ] Use Vitest Browser Mode for native events (focus, drag, resize)
|
|
20
|
+
- [ ] Use Vitest Browser Mode for cookies and computed CSS styles
|
|
21
|
+
- [ ] Accept slower speed tradeoff for browser accuracy
|
|
22
|
+
|
|
23
|
+
## When to Use Each Approach
|
|
24
|
+
|
|
25
|
+
### Node-Based Runner (Vitest + happy-dom/jsdom)
|
|
26
|
+
Best for:
|
|
27
|
+
- Pure logic testing
|
|
28
|
+
- State management
|
|
29
|
+
- Event emission
|
|
30
|
+
- Props/slots behavior
|
|
31
|
+
- Most component interactions
|
|
32
|
+
- Fast CI/CD pipelines
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
// vitest.config.js
|
|
36
|
+
export default defineConfig({
|
|
37
|
+
test: {
|
|
38
|
+
environment: 'happy-dom', // or 'jsdom'
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
// Fast but limited - fine for most tests
|
|
45
|
+
test('button emits click event', async () => {
|
|
46
|
+
const wrapper = mount(Button)
|
|
47
|
+
await wrapper.trigger('click')
|
|
48
|
+
expect(wrapper.emitted('click')).toBeTruthy()
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Vitest Browser Mode
|
|
53
|
+
Required for:
|
|
54
|
+
- CSS computed styles verification
|
|
55
|
+
- CSS transitions/animations
|
|
56
|
+
- Real focus/blur behavior
|
|
57
|
+
- Drag and drop
|
|
58
|
+
- Cookie operations
|
|
59
|
+
- Viewport-dependent behavior
|
|
60
|
+
- Cross-browser validation
|
|
61
|
+
|
|
62
|
+
## Vitest Browser Mode Setup
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install -D @vitest/browser playwright
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// vitest.config.js
|
|
70
|
+
import { defineConfig } from 'vitest/config'
|
|
71
|
+
|
|
72
|
+
export default defineConfig({
|
|
73
|
+
test: {
|
|
74
|
+
browser: {
|
|
75
|
+
enabled: true,
|
|
76
|
+
name: 'chromium',
|
|
77
|
+
provider: 'playwright',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// Button.browser.test.js
|
|
85
|
+
import { render } from 'vitest-browser-vue'
|
|
86
|
+
import Button from './Button.vue'
|
|
87
|
+
|
|
88
|
+
test('has correct hover styling', async () => {
|
|
89
|
+
const { getByRole } = render(Button, { props: { label: 'Click me' } })
|
|
90
|
+
|
|
91
|
+
const button = getByRole('button')
|
|
92
|
+
|
|
93
|
+
// Check initial style
|
|
94
|
+
await expect.element(button).toHaveStyle({
|
|
95
|
+
backgroundColor: 'rgb(59, 130, 246)' // blue
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('maintains focus after click', async () => {
|
|
100
|
+
const { getByRole } = render(Button)
|
|
101
|
+
|
|
102
|
+
const button = getByRole('button')
|
|
103
|
+
await button.click()
|
|
104
|
+
|
|
105
|
+
await expect.element(button).toHaveFocus()
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Examples: What Each Runner Can/Cannot Test
|
|
110
|
+
|
|
111
|
+
### Styles - Browser Required
|
|
112
|
+
```javascript
|
|
113
|
+
// Node runner: CANNOT verify actual CSS
|
|
114
|
+
test('danger button has red background', () => {
|
|
115
|
+
const wrapper = mount(Button, { props: { variant: 'danger' } })
|
|
116
|
+
// This only checks class exists, not actual color
|
|
117
|
+
expect(wrapper.classes()).toContain('bg-red-500')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
// Vitest Browser Mode: CAN verify computed styles
|
|
121
|
+
test('danger button renders red', async () => {
|
|
122
|
+
const { getByRole } = render(Button, { props: { variant: 'danger' } })
|
|
123
|
+
await expect.element(getByRole('button')).toHaveStyle({
|
|
124
|
+
backgroundColor: 'rgb(239, 68, 68)'
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Computed CSS Styles - Browser Required
|
|
130
|
+
```javascript
|
|
131
|
+
// Node runner: CANNOT get real computed styles
|
|
132
|
+
test('button has correct padding', () => {
|
|
133
|
+
const wrapper = mount(Button)
|
|
134
|
+
// getComputedStyle returns empty/default values in jsdom
|
|
135
|
+
const style = window.getComputedStyle(wrapper.element)
|
|
136
|
+
// style.padding will be empty string, not actual computed value
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
// Vitest Browser Mode: Real computed styles
|
|
140
|
+
test('button has correct padding', async () => {
|
|
141
|
+
const { getByRole } = render(Button)
|
|
142
|
+
const button = getByRole('button')
|
|
143
|
+
|
|
144
|
+
await expect.element(button).toHaveStyle({
|
|
145
|
+
padding: '12px 24px'
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Native Events - Browser Required
|
|
151
|
+
```javascript
|
|
152
|
+
// Node runner: Synthetic events only
|
|
153
|
+
test('handles drag and drop', async () => {
|
|
154
|
+
const wrapper = mount(DraggableList)
|
|
155
|
+
// trigger('dragstart') is synthetic - may not work as expected
|
|
156
|
+
await wrapper.find('.item').trigger('dragstart')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// Vitest Browser Mode: Real native events via userEvent
|
|
160
|
+
import { userEvent } from '@vitest/browser/context'
|
|
161
|
+
|
|
162
|
+
test('reorders items on drag', async () => {
|
|
163
|
+
const { getByTestId } = render(DraggableList)
|
|
164
|
+
|
|
165
|
+
const item = getByTestId('item-1')
|
|
166
|
+
const target = getByTestId('item-3')
|
|
167
|
+
|
|
168
|
+
await userEvent.dragAndDrop(item, target)
|
|
169
|
+
|
|
170
|
+
// Assert reordering
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Recommended Testing Strategy
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
// vitest.config.js - Separate test configurations
|
|
178
|
+
|
|
179
|
+
export default defineConfig({
|
|
180
|
+
test: {
|
|
181
|
+
// Default: Node environment for speed
|
|
182
|
+
environment: 'happy-dom',
|
|
183
|
+
|
|
184
|
+
// Browser tests in separate directory
|
|
185
|
+
include: ['src/**/*.test.{js,ts}'],
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
// Run browser tests separately
|
|
190
|
+
// npx vitest --browser.enabled
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Directory Structure
|
|
194
|
+
```
|
|
195
|
+
tests/
|
|
196
|
+
├── unit/ # Fast node-based tests
|
|
197
|
+
│ ├── Button.test.js
|
|
198
|
+
│ └── useCounter.test.js
|
|
199
|
+
├── component/ # Slower browser-based tests
|
|
200
|
+
│ ├── Button.browser.test.js
|
|
201
|
+
│ └── DragDrop.browser.test.js
|
|
202
|
+
└── e2e/ # Full E2E tests (Playwright)
|
|
203
|
+
└── user-flow.spec.ts
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Reference
|
|
207
|
+
- [Vue.js Testing - Component Testing](https://vuejs.org/guide/scaling-up/testing#component-testing)
|
|
208
|
+
- [Vitest Browser Mode](https://vitest.dev/guide/browser.html)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Test Components Using Blackbox Approach - Focus on Behavior Not Implementation
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Implementation-aware tests become brittle and break during refactoring, leading to high maintenance burden
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, testing, component-testing, vitest, vue-test-utils, blackbox]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Test Components Using Blackbox Approach - Focus on Behavior Not Implementation
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Tests that rely on implementation details (internal state, private methods, component structure) break during refactoring even when functionality remains correct. This leads to false negatives and high test maintenance burden.
|
|
12
|
+
|
|
13
|
+
Follow Kent C. Dodds' testing philosophy: "The more your tests resemble how your software is used, the more confidence they can give you."
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Test what the component does, not how it does it
|
|
18
|
+
- [ ] Query elements by user-visible attributes (text, role, testid)
|
|
19
|
+
- [ ] Simulate user interactions (click, type) rather than calling methods directly
|
|
20
|
+
- [ ] Assert on rendered output, emitted events, and visible state changes
|
|
21
|
+
- [ ] Avoid accessing component internal state or private methods
|
|
22
|
+
- [ ] Use data-testid attributes for elements without semantic meaning
|
|
23
|
+
|
|
24
|
+
**Incorrect:**
|
|
25
|
+
```javascript
|
|
26
|
+
import { mount } from '@vue/test-utils'
|
|
27
|
+
import Counter from './Counter.vue'
|
|
28
|
+
|
|
29
|
+
// BAD: Testing implementation details
|
|
30
|
+
test('counter increments', async () => {
|
|
31
|
+
const wrapper = mount(Counter)
|
|
32
|
+
|
|
33
|
+
// Accessing internal state directly
|
|
34
|
+
expect(wrapper.vm.count).toBe(0)
|
|
35
|
+
|
|
36
|
+
// Calling internal method instead of simulating user action
|
|
37
|
+
wrapper.vm.increment()
|
|
38
|
+
|
|
39
|
+
// Checking internal state instead of visible output
|
|
40
|
+
expect(wrapper.vm.count).toBe(1)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// BAD: Testing component structure
|
|
44
|
+
test('has increment button', () => {
|
|
45
|
+
const wrapper = mount(Counter)
|
|
46
|
+
|
|
47
|
+
// Testing implementation detail - what if button becomes an anchor?
|
|
48
|
+
expect(wrapper.find('button').exists()).toBe(true)
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Correct:**
|
|
53
|
+
```javascript
|
|
54
|
+
import { mount } from '@vue/test-utils'
|
|
55
|
+
import Counter from './Counter.vue'
|
|
56
|
+
|
|
57
|
+
// CORRECT: Testing behavior like a user would
|
|
58
|
+
test('counter displays updated value after clicking increment', async () => {
|
|
59
|
+
const wrapper = mount(Counter, {
|
|
60
|
+
props: { max: 10 }
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// Assert initial visible state
|
|
64
|
+
expect(wrapper.find('[data-testid="counter-value"]').text()).toContain('0')
|
|
65
|
+
|
|
66
|
+
// Simulate user action
|
|
67
|
+
await wrapper.find('[data-testid="increment-button"]').trigger('click')
|
|
68
|
+
|
|
69
|
+
// Assert visible result
|
|
70
|
+
expect(wrapper.find('[data-testid="counter-value"]').text()).toContain('1')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// CORRECT: Testing emitted events (public API)
|
|
74
|
+
test('emits change event with new value when incremented', async () => {
|
|
75
|
+
const wrapper = mount(Counter)
|
|
76
|
+
|
|
77
|
+
await wrapper.find('[data-testid="increment-button"]').trigger('click')
|
|
78
|
+
|
|
79
|
+
expect(wrapper.emitted('change')).toHaveLength(1)
|
|
80
|
+
expect(wrapper.emitted('change')[0]).toEqual([1])
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Using @testing-library/vue for Better Blackbox Tests
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
import { render, screen, fireEvent } from '@testing-library/vue'
|
|
88
|
+
import Counter from './Counter.vue'
|
|
89
|
+
|
|
90
|
+
// Testing Library encourages accessible, user-centric queries
|
|
91
|
+
test('increments counter on button click', async () => {
|
|
92
|
+
render(Counter)
|
|
93
|
+
|
|
94
|
+
// Query by role - how screen readers see it
|
|
95
|
+
const button = screen.getByRole('button', { name: /increment/i })
|
|
96
|
+
const display = screen.getByText('0')
|
|
97
|
+
|
|
98
|
+
await fireEvent.click(button)
|
|
99
|
+
|
|
100
|
+
expect(screen.getByText('1')).toBeInTheDocument()
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## What to Test vs What Not to Test
|
|
105
|
+
|
|
106
|
+
### DO Test (Public Interface)
|
|
107
|
+
```javascript
|
|
108
|
+
// Props affect rendered output
|
|
109
|
+
test('shows title from props', () => {
|
|
110
|
+
const wrapper = mount(Card, {
|
|
111
|
+
props: { title: 'Hello World' }
|
|
112
|
+
})
|
|
113
|
+
expect(wrapper.text()).toContain('Hello World')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// Slots render correctly
|
|
117
|
+
test('renders slot content', () => {
|
|
118
|
+
const wrapper = mount(Card, {
|
|
119
|
+
slots: { default: '<p>Slot content</p>' }
|
|
120
|
+
})
|
|
121
|
+
expect(wrapper.text()).toContain('Slot content')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// Emitted events
|
|
125
|
+
test('emits close event when X clicked', async () => {
|
|
126
|
+
const wrapper = mount(Modal)
|
|
127
|
+
await wrapper.find('[data-testid="close-button"]').trigger('click')
|
|
128
|
+
expect(wrapper.emitted('close')).toBeTruthy()
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### DON'T Test (Implementation Details)
|
|
133
|
+
```javascript
|
|
134
|
+
// Don't test internal computed properties
|
|
135
|
+
// Don't test internal methods
|
|
136
|
+
// Don't test component options/setup internals
|
|
137
|
+
// Don't test that specific child components are rendered (unless critical)
|
|
138
|
+
// Don't rely exclusively on snapshot tests for correctness
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Reference
|
|
142
|
+
- [Vue.js Testing Guide](https://vuejs.org/guide/scaling-up/testing)
|
|
143
|
+
- [Vue Test Utils - Testing Philosophy](https://test-utils.vuejs.org/guide/)
|
|
144
|
+
- [Testing Library Guiding Principles](https://testing-library.com/docs/guiding-principles)
|