mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-16 22:25:12 +00:00
Add unit-test-vue-pinia skill (#1005)
This commit is contained in:
@@ -234,6 +234,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
|
||||
| [typespec-api-operations](../skills/typespec-api-operations/SKILL.md) | Add GET, POST, PATCH, and DELETE operations to a TypeSpec API plugin with proper routing, parameters, and adaptive cards | None |
|
||||
| [typespec-create-agent](../skills/typespec-create-agent/SKILL.md) | Generate a complete TypeSpec declarative agent with instructions, capabilities, and conversation starters for Microsoft 365 Copilot | None |
|
||||
| [typespec-create-api-plugin](../skills/typespec-create-api-plugin/SKILL.md) | Generate a TypeSpec API plugin with REST operations, authentication, and Adaptive Cards for Microsoft 365 Copilot | None |
|
||||
| [unit-test-vue-pinia](../skills/unit-test-vue-pinia/SKILL.md) | Write and review unit tests for Vue 3 + TypeScript + Vitest + Pinia codebases. Use when creating or updating tests for components, composables, and stores; mocking Pinia with createTestingPinia; applying Vue Test Utils patterns; and enforcing black-box assertions over implementation details. | `references/pinia-patterns.md` |
|
||||
| [update-avm-modules-in-bicep](../skills/update-avm-modules-in-bicep/SKILL.md) | Update Azure Verified Modules (AVM) to latest versions in Bicep files. | None |
|
||||
| [update-implementation-plan](../skills/update-implementation-plan/SKILL.md) | Update an existing implementation plan file with new or update requirements to provide new features, refactoring existing code or upgrading packages, design, architecture or infrastructure. | None |
|
||||
| [update-llms](../skills/update-llms/SKILL.md) | Update the llms.txt file in the root folder to reflect changes in documentation or specifications following the llms.txt specification at https://llmstxt.org/ | None |
|
||||
|
||||
198
skills/unit-test-vue-pinia/SKILL.md
Normal file
198
skills/unit-test-vue-pinia/SKILL.md
Normal file
@@ -0,0 +1,198 @@
|
||||
---
|
||||
name: unit-test-vue-pinia
|
||||
category: testing
|
||||
description: 'Write and review unit tests for Vue 3 + TypeScript + Vitest + Pinia codebases. Use when creating or updating tests for components, composables, and stores; mocking Pinia with createTestingPinia; applying Vue Test Utils patterns; and enforcing black-box assertions over implementation details.'
|
||||
---
|
||||
|
||||
# unit-test-vue-pinia
|
||||
|
||||
Use this skill to create or review unit tests for Vue components, composables, and Pinia stores. Keep tests small, deterministic, and behavior-first.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Identify the behavior boundary first: component UI behavior, composable behavior, or store behavior.
|
||||
2. Choose the narrowest test style that can prove that behavior.
|
||||
3. Set up Pinia with the least powerful option that still covers the scenario.
|
||||
4. Drive the test through public inputs such as props, form updates, button clicks, emitted child events, and store APIs.
|
||||
5. Assert observable outputs and side effects before considering any instance-level assertion.
|
||||
6. Return or review tests with clear behavior-oriented names and note any remaining coverage gaps.
|
||||
|
||||
## Core Rules
|
||||
|
||||
- Test one behavior per test.
|
||||
- Assert observable input/output behavior first (rendered text, emitted events, callback calls, store state changes).
|
||||
- Avoid implementation-coupled assertions.
|
||||
- Access `wrapper.vm` only in exceptional cases when there is no reasonable DOM, prop, emit, or store-level assertion.
|
||||
- Prefer explicit setup in `beforeEach()` and reset mocks every test.
|
||||
- Use checked-in reference material in `references/pinia-patterns.md` as the local source of truth for standard Pinia test setups.
|
||||
|
||||
## Pinia Testing Approach
|
||||
|
||||
Use `references/pinia-patterns.md` first, then fall back to Pinia's testing cookbook when the checked-in examples do not cover the case.
|
||||
|
||||
### Default pattern for component tests
|
||||
|
||||
Use `createTestingPinia` as a global plugin while mounting.
|
||||
Prefer `createSpy: vi.fn` as the default for consistency and easier action-spy assertions.
|
||||
|
||||
```ts
|
||||
const wrapper = mount(ComponentUnderTest, {
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
By default, actions are stubbed and spied.
|
||||
Use `stubActions: true` (default) when the test only needs to verify whether an action was called (or not called).
|
||||
|
||||
### Accepted minimal Pinia setups
|
||||
|
||||
The following are also valid and should not be flagged as incorrect:
|
||||
|
||||
- `createTestingPinia({})` when the test does not assert Pinia action spy behavior.
|
||||
- `createTestingPinia({ initialState: ... })` or `createTestingPinia({ stubActions: ... })` without `createSpy`, when the test only needs state seeding or action stubbing behavior and does not inspect generated spies.
|
||||
- `setActivePinia(createTestingPinia(...))` in store/composable-focused tests (without mounting a component) when mocking/seeding dependent stores is needed.
|
||||
|
||||
Use `createSpy: vi.fn` when action spy assertions are part of the test intent.
|
||||
|
||||
### Execute real actions only when needed
|
||||
|
||||
Use `stubActions: false` only when the test must validate the action's real behavior and side effects. Do not switch it on by default for simple "was called" assertions.
|
||||
|
||||
```ts
|
||||
const wrapper = mount(ComponentUnderTest, {
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
stubActions: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Seed store state with `initialState`
|
||||
|
||||
```ts
|
||||
const wrapper = mount(ComponentUnderTest, {
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
initialState: {
|
||||
counter: { n: 20 },
|
||||
user: { name: "Leia Organa" },
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Add Pinia plugins through `createTestingPinia`
|
||||
|
||||
```ts
|
||||
const wrapper = mount(ComponentUnderTest, {
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
plugins: [myPiniaPlugin],
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Getter override pattern for edge cases
|
||||
|
||||
```ts
|
||||
const pinia = createTestingPinia({ createSpy: vi.fn });
|
||||
const store = useCounterStore(pinia);
|
||||
|
||||
store.double = 999;
|
||||
// @ts-expect-error test-only reset of overridden getter
|
||||
store.double = undefined;
|
||||
```
|
||||
|
||||
### Pure store unit tests
|
||||
|
||||
Prefer pure store tests with `createPinia()` when the goal is to validate store state transitions and action behavior without component rendering. Use `createTestingPinia()` only when you need stubbed dependent stores, seeded test doubles, or action spies.
|
||||
|
||||
```ts
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia());
|
||||
});
|
||||
|
||||
it("increments", () => {
|
||||
const counter = useCounterStore();
|
||||
counter.increment();
|
||||
expect(counter.n).toBe(1);
|
||||
});
|
||||
```
|
||||
|
||||
## Vue Test Utils Approach
|
||||
|
||||
Follow Vue Test Utils guidance: <https://test-utils.vuejs.org/guide/>
|
||||
|
||||
- Mount shallow by default for focused unit tests.
|
||||
- Mount full component trees only when integration behavior is the subject.
|
||||
- Drive behavior through props, user-like interactions, and emitted events.
|
||||
- Prefer `findComponent(...).vm.$emit(...)` for child stub events instead of touching parent internals.
|
||||
- Use `nextTick` only when updates are async.
|
||||
- Assert emitted events and payloads with `wrapper.emitted(...)`.
|
||||
- Access `wrapper.vm` only when no DOM assertion, emitted event assertion, prop assertion, or store-level assertion can express the behavior. Treat it as an exception and keep the assertion narrowly scoped.
|
||||
|
||||
## Key Testing Snippets
|
||||
|
||||
Emit and assert payload:
|
||||
|
||||
```ts
|
||||
await wrapper.find("button").trigger("click");
|
||||
expect(wrapper.emitted("submit")?.[0]?.[0]).toBe("Mango Mission");
|
||||
```
|
||||
|
||||
Update input and assert output:
|
||||
|
||||
```ts
|
||||
await wrapper.find("input").setValue("Agent Violet");
|
||||
await wrapper.find("form").trigger("submit");
|
||||
expect(wrapper.emitted("save")?.[0]?.[0]).toBe("Agent Violet");
|
||||
```
|
||||
|
||||
## Test Writing Workflow
|
||||
|
||||
1. Identify the behavior boundary to test.
|
||||
2. Build minimal fixture data (only fields needed by that behavior).
|
||||
3. Configure Pinia and required test doubles.
|
||||
4. Trigger behavior through public inputs.
|
||||
5. Assert public outputs and side effects.
|
||||
6. Refactor test names to describe behavior, not implementation.
|
||||
|
||||
## Constraints and Safety
|
||||
|
||||
- Do not test private/internal implementation details.
|
||||
- Do not overuse snapshots for dynamic UI behavior.
|
||||
- Do not assert every field in large objects if only one behavior matters.
|
||||
- Keep fake data deterministic; avoid random values.
|
||||
- Do not claim a Pinia setup is wrong when it is one of the accepted minimal setups above.
|
||||
- Do not rewrite working tests toward deeper mounting or real actions unless the behavior under test requires that extra surface area.
|
||||
- Flag missing test coverage, brittle selectors, and implementation-coupled assertions explicitly during review.
|
||||
|
||||
## Output Contract
|
||||
|
||||
- For `create` or `update`, return the finished test code plus a short note describing the selected Pinia strategy.
|
||||
- For `review`, return concrete findings first, then missing coverage or brittleness risks.
|
||||
- When the safest choice is ambiguous, state the assumption that drove the chosen test setup.
|
||||
|
||||
## References
|
||||
|
||||
- `references/pinia-patterns.md`
|
||||
- Pinia testing cookbook: <https://pinia.vuejs.org/cookbook/testing.html>
|
||||
- Vue Test Utils guide: <https://test-utils.vuejs.org/guide/>
|
||||
95
skills/unit-test-vue-pinia/references/pinia-patterns.md
Normal file
95
skills/unit-test-vue-pinia/references/pinia-patterns.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Pinia Testing Snippets (Cookbook-Aligned)
|
||||
|
||||
Use these patterns directly when writing tests with `@pinia/testing`.
|
||||
|
||||
## Component mount with `createTestingPinia`
|
||||
|
||||
```ts
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { createTestingPinia } from "@pinia/testing";
|
||||
import { vi } from "vitest";
|
||||
|
||||
const wrapper = mount(ComponentUnderTest, {
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Execute real actions
|
||||
|
||||
Use this only when behavior inside the action must run.
|
||||
If the test only checks call/no-call expectations, keep default stubbing (`stubActions: true`).
|
||||
|
||||
```ts
|
||||
const wrapper = mount(ComponentUnderTest, {
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
stubActions: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Seed starting state
|
||||
|
||||
```ts
|
||||
const wrapper = mount(ComponentUnderTest, {
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
initialState: {
|
||||
counter: { n: 10 },
|
||||
profile: { name: "Sherlock Holmes" },
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Use store in test and assert action call
|
||||
|
||||
```ts
|
||||
const pinia = createTestingPinia({ createSpy: vi.fn });
|
||||
const store = useCounterStore(pinia);
|
||||
|
||||
store.increment();
|
||||
expect(store.increment).toHaveBeenCalledTimes(1);
|
||||
```
|
||||
|
||||
## Add plugin under test
|
||||
|
||||
```ts
|
||||
const wrapper = mount(ComponentUnderTest, {
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
plugins: [myPiniaPlugin],
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Override and reset getters for edge tests
|
||||
|
||||
```ts
|
||||
const pinia = createTestingPinia({ createSpy: vi.fn });
|
||||
const store = useCounterStore(pinia);
|
||||
|
||||
store.double = 42;
|
||||
expect(store.double).toBe(42);
|
||||
|
||||
// @ts-expect-error test-only reset
|
||||
store.double = undefined;
|
||||
```
|
||||
Reference in New Issue
Block a user