用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tomevault-io/skills-registry --skill vue-test命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | vue-test |
| description | > Use when this capability is needed. |
Run, author, and validate tests for Vue.js applications — components, Pinia stores, composables, router integration, and Nuxt server routes.
Constraint: Never modify production source files. Only create or edit files under the test directory. Use generic placeholders (<your-component>, <your-store>) — never assume specific filenames exist.
Request: $ARGUMENTS
Identify the project's test runner and configuration.
# Check for Vitest (preferred)
cat vitest.config.ts 2>/dev/null || cat vitest.config.js 2>/dev/null || cat vite.config.ts 2>/dev/null
# Check for Jest fallback
cat jest.config.ts 2>/dev/null || cat jest.config.js 2>/dev/null
Decision table:
| Found | Framework | Config File |
|---|---|---|
vitest.config.* or vitest in vite.config.* | Vitest | vitest.config.ts |
jest.config.* or "jest" in package.json | Jest | jest.config.ts |
| Neither | Vitest (default) | Create vitest.config.ts |
Verify test utilities are installed:
# Check package.json for required deps
grep -E "vitest|jest|@vue/test-utils|@pinia/testing" package.json
If @vue/test-utils is missing, inform the user:
# Install with project's package manager
npm install --save-dev @vue/test-utils # or yarn add -D / pnpm add -D
Record the detected framework for use in subsequent steps.
Use mount for full rendering (when child component behavior matters) or shallowMount for isolated unit tests (stubs child components).
import { mount } from '@vue/test-utils'
import YourComponent from '@/components/<your-component>.vue'
describe('<your-component>', () => {
it('renders the expected content', () => {
const wrapper = mount(YourComponent, {
props: {
title: 'Test Title',
items: [{ id: 1, label: 'Item 1' }],
},
})
expect(wrapper.text()).toContain('Test Title')
expect(wrapper.findAll('[data-testid="item"]')).toHaveLength(1)
})
it('emits an event when the button is clicked', async () => {
const wrapper = mount(YourComponent)
await wrapper.find('[data-testid="submit-btn"]').trigger('click')
expect(wrapper.emitted('submit')).toHaveLength(1)
(wrapper.()![]).([{ : }])
})
})
import { shallowMount } from '@vue/test-utils'
import ParentComponent from '@/components/<your-parent>.vue'
it('renders without mounting children', () => {
const wrapper = shallowMount(ParentComponent)
// Child components are stubbed — only ParentComponent logic is tested
expect(wrapper.exists()).toBe(true)
})
const wrapper = mount(YourComponent, {
global: {
plugins: [router, pinia],
stubs: {
'RouterLink': true,
'Teleport': true,
},
mocks: {
$t: (key: string) => key, // i18n mock
},
},
})
Use @pinia/testing to create a testing-specific Pinia instance with optional stubbed actions.
# Ensure @pinia/testing is installed
grep "@pinia/testing" package.json || echo "Missing: npm install --save-dev @pinia/testing"
import { setActivePinia, createPinia } from 'pinia'
import { useYourStore } from '@/stores/<your-store>'
describe('<your-store>', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
it('initializes with default state', () => {
const store = useYourStore()
expect(store.items).toEqual([])
expect(store.isLoading).toBe(false)
})
it('updates state via action', async () => {
const store = useYourStore()
await store.fetchItems()
expect(store.items.length).toBeGreaterThan(0)
})
})
import { mount } from '@vue/test-utils'
import { createTestingPinia } from '@pinia/testing'
import YourComponent from '@/components/<your-component>.vue'
it('uses mocked store actions', async () => {
const wrapper = mount(YourComponent, {
global: {
plugins: [
createTestingPinia({
initialState: {
'<your-store>': { items: [{ id: 1, name: 'Mock Item' }] },
},
stubActions: false, // set true to auto-stub all actions
}),
],
},
})
expect(wrapper.text()).toContain('Mock Item')
})
To mock individual actions, pass createSpy: vi.fn (Vitest) or createSpy: jest.fn (Jest) to createTestingPinia, then override the specific action on the store instance with a mock function.
Composables that use Vue's reactivity (ref, computed, watch) need a component instance context. Use the renderHook pattern or a minimal wrapper component.
import { mount } from '@vue/test-utils'
import { defineComponent } from 'vue'
import { useYourComposable } from '@/composables/<your-composable>'
function withSetup<T>(composable: () => T) {
let result: T
const Wrapper = defineComponent({
setup() {
result = composable()
return () => null // render nothing
},
})
const wrapper = mount(Wrapper)
return { result: result!, wrapper }
}
describe('<your-composable>', () => {
it('returns reactive state', () => {
const { result } = withSetup(() => useYourComposable())
expect(result.count.value).toBe(0)
result.increment()
expect(result.count.value).toBe(1)
})
})
For composables with lifecycle hooks (onMounted, onUnmounted), use the same withSetup helper. Call await flushPromises() after mounting to resolve async onMounted effects, and wrapper.unmount() to trigger cleanup hooks.
Stub useRoute and useRouter to test components with routing dependencies without a real router instance.
import { mount } from '@vue/test-utils'
import { vi } from 'vitest'
import YourComponent from '@/components/<your-component>.vue'
const mockPush = vi.fn()
const mockRoute = {
params: { id: '42' },
query: { tab: 'details' },
name: 'item-detail',
path: '/items/42',
}
vi.mock('vue-router', () => ({
useRoute: () => mockRoute,
useRouter: () => ({
push: mockPush,
replace: vi.fn(),
back: vi.fn(),
}),
}))
describe('<your-component> with router', () => {
it('reads route params', () => {
const wrapper = mount(YourComponent)
expect(wrapper.text()).toContain('42')
})
it('navigates on action', async () => {
wrapper = ()
wrapper.().()
(mockPush).({ : })
})
})
For integration tests requiring a real router, create one with createRouter({ history: createMemoryHistory(), routes: [...] }), call router.push('/target') and await router.isReady(), then pass the router as a global plugin to mount.
Check for Nuxt by looking for nuxt.config.ts or nuxt.config.js.
ls nuxt.config.ts nuxt.config.js 2>/dev/null
If Nuxt is detected, test server routes using $fetch from @nuxt/test-utils:
import { describe, it, expect } from 'vitest'
import { setup, $fetch } from '@nuxt/test-utils'
describe('server routes', () => {
// Start a Nuxt test server
setup({ server: true })
it('GET /api/<your-endpoint> returns expected data', async () => {
const data = await $fetch('/api/<your-endpoint>')
expect(data).toHaveProperty('items')
expect(Array.isArray(data.items)).toBe(true)
})
it('POST /api/<your-endpoint> creates a resource', async () => {
const data = await $fetch('/api/<your-endpoint>', {
method: 'POST',
body: { name: 'Test Item' },
})
expect(data).toHaveProperty('id')
expect(data.name).toBe('Test Item')
})
})
Skip this step if Nuxt is not detected.
import { mount, flushPromises } from '@vue/test-utils'
import { Suspense } from 'vue'
import AsyncComponent from '@/components/<your-async-component>.vue'
it('renders async component after resolution', async () => {
const wrapper = mount({
template: `
<Suspense>
<AsyncComponent />
<template #fallback>Loading...</template>
</Suspense>
`,
components: { AsyncComponent },
})
expect(wrapper.text()).toContain('Loading...')
await flushPromises()
expect(wrapper.text()).not.toContain('Loading...')
expect(wrapper.findComponent(AsyncComponent).exists()).toBe(true)
})
import { mount, flushPromises } from '@vue/test-utils'
import { defineAsyncComponent } from 'vue'
const LazyComponent = defineAsyncComponent(
() => import('@/components/<your-lazy-component>.vue')
)
it('loads and renders the lazy component', async () => {
const wrapper = mount({
template: '<Suspense><LazyComponent /></Suspense>',
components: { LazyComponent },
})
await flushPromises()
expect(wrapper.text()).toBeTruthy()
})
Execute tests based on the detected framework from STEP 1.
# Vitest
npx vitest run --reporter=json --outputFile=test-results/vue-raw.json $TEST_SCOPE
# Jest fallback
npx jest --json --outputFile=test-results/vue-raw.json $TEST_SCOPE
# Run specific test categories
npx vitest run --reporter=json --outputFile=test-results/vue-raw.json "**/*.spec.ts" # all specs
npx vitest run --reporter=json --outputFile=test-results/vue-raw.json "components/" # component tests
npx vitest run --reporter=json --outputFile=test-results/vue-raw.json "stores/" # store tests
Replace $TEST_SCOPE with the scope from $ARGUMENTS (file path, directory, or test name pattern).
If tests fail, capture the output and analyze failures before proceeding.
Parse the raw test output and write a structured result file.
{
"skill": "vue-test",
"timestamp": "<ISO-8601>",
"result": "PASSED",
"summary": {
"total": 0,
"passed": 0,
"failed": 0,
"skipped": 0,
"flaky": 0
},
"quality_gate": "PASSED",
"framework": "vitest",
"scope": "<test-scope from arguments>",
"failures": [],
"warnings": [],
Write to test-results/vue-test.json.
Result field values:
| result | Condition |
|---|---|
PASSED | All tests passed |
FAILED | One or more tests failed |
FLAKY | Tests passed on retry but failed initially |
Failure entry format:
{
"test": "<test-name>",
"category": "ASSERTION_FAILURE",
"file": "<file-path>:<line>",
"message": "<failure message>",
"confidence": "HIGH"
}
data-testid attributes for element selection — never rely on CSS classes or DOM structureawait flushPromises() after any async operation before assertingwrapper.unmount() or afterEach(() => wrapper.unmount()) to prevent memory leaksbeforeEach with a fresh createPinia() or createTestingPinia()test-results/vue-test.json on every runvi.fn() (Vitest) or jest.fn() (Jest) consistently — match the detected framework@vue/test-utils and enzyme in the same project — they are incompatiblesetTimeout or sleep to wait for async operations — use flushPromises() or await nextTick()$patch to keep tests realistic@/, ~/, #imports)it.todo() and a comment linking the issueSource: abhayla/claude-best-practices — distributed by TomeVault.