Strict mode provides more granular API coverage tracking by expanding union types and checking each variant individually.
In normal mode, the tool only checks if a prop is tested at all. In strict mode, it checks if each possible value of a union type prop is tested.
// Component definition
const buttonProps = {
variant: {
type: String as PropType<'primary' | 'secondary' | 'tertiary'>,
default: 'primary'
},
disabled: { type: Boolean, default: false }
}Normal Mode:
- Checks if
variantis tested (any value) - Checks if
disabledis tested (any value)
Strict Mode:
- Checks if
variant='primary'is tested - Checks if
variant='secondary'is tested - Checks if
variant='tertiary'is tested - Checks if
disabled=trueis tested (false is filtered out)
Enable strict mode in your vitest.config.ts:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
reporters: ['default', ['vc-api-coverage', {
strict: true, // Enable strict mode
outputDir: 'coverage-api',
}]],
},
});String or number literal unions are expanded to individual variants:
// Type: 'sm' | 'md' | 'lg'
size: { type: String as PropType<'sm' | 'md' | 'lg'> }
// Strict mode checks:
// - size[sm]
// - size[md]
// - size[lg]Boolean props are expanded to only the true value (false is filtered):
// Type: boolean
disabled: { type: Boolean, default: false }
// Strict mode checks:
// - disabled[true]
// Note: disabled[false] is NOT checkedRationale: Testing the false case is often not meaningful as it represents the default/inactive state. Most tests focus on the active (true) behavior.
Mixed primitive types are expanded by type category:
// Type: string | number
value: { type: [String, Number] as PropType<string | number> }
// Strict mode checks:
// - value[string] - Any string value satisfies this
// - value[number] - Any number value satisfies thisType Matching Rules:
Literal values can satisfy primitive type requirements:
// Component definition
content: { type: String }
// Test with literal string
mount(Component, { props: { content: 'Hello' } })
// ✅ Literal string 'Hello' satisfies String type requirementThis applies to all primitive types:
- String literal
'text'→ satisfiesStringtype - Number literal
42→ satisfiesNumbertype - Boolean literal
true→ satisfiesBooleantype
// Type: 'fixed' | number | () => number
width: { type: [String, Number, Function] as PropType<'fixed' | number | (() => number)> }
// Strict mode checks:
// - width[fixed] - Must test with literal 'fixed'
// - width[number] - Any number satisfies this
// - width[object] - Any function satisfies this (see note below)Note on Function Types:
Due to TypeScript's type system, Vue's Function prop type is recognized as object in the coverage reports, not function. However, the tool correctly matches function values in tests:
// Component definition
content: { type: [String, Function] }
// Tests
mount(Component, { props: { content: 'text' } }) // ✅ Covers String type
mount(Component, { props: { content: () => 'text' } }) // ✅ Covers Function type (shown as 'object')
// Coverage report will show:
// - content[string] ✅
// - content[object] ✅ (represents Function type)The following types are automatically filtered and NOT checked:
undefined- Optional props don't require undefined testingnull- Null variants are filteredfalse- Boolean false is filtered (see Boolean section)
║ button/Button.tsx │ 2/3 │ Uncovered APIs: variant, disabled
║ button/Button.tsx │ 3/5 │ Uncovered APIs: variant[tertiary], disabled[true]
The strict mode report shows exactly which variants are not tested.
When strict mode is enabled, ensure your tests cover all meaningful variants:
describe('Button', () => {
it('renders with variant primary', () => {
mount(Button, { props: { variant: 'primary' } });
});
it('renders with variant secondary', () => {
mount(Button, { props: { variant: 'secondary' } });
});
it('renders with variant tertiary', () => {
mount(Button, { props: { variant: 'tertiary' } });
});
it('handles disabled state', () => {
mount(Button, { props: { disabled: true } });
});
});Strict mode intelligently detects and extracts values from loop-based tests:
1. for...of Loop
it('renders with different sizes', () => {
const sizes: ButtonSize[] = ['sm', 'md']; // Missing 'lg'
for (const size of sizes) {
mount(Button, { props: { size } });
// assertions...
}
});
// ✅ Detects: size[sm], size[md] are covered
// ❌ Reports: size[lg] is NOT covered2. for...in Loop
it('renders with different sizes', () => {
const sizes: ButtonSize[] = ['sm', 'md'];
for (const i in sizes) {
mount(Button, { props: { size: sizes[i] } });
// assertions...
}
});
// ✅ Detects array element access pattern3. Array.forEach()
it('renders with different sizes', () => {
const sizes: ButtonSize[] = ['sm', 'md'];
sizes.forEach(size => {
mount(Button, { props: { size } });
// assertions...
});
});
// ✅ Detects callback parameter pattern4. Array.map()
it('renders with different sizes', () => {
const sizes: ButtonSize[] = ['sm', 'md'];
const wrappers = sizes.map(size => mount(Button, { props: { size } }));
wrappers.forEach(wrapper => expect(wrapper.exists()).toBe(true));
});
// ✅ Detects map callback pattern5. JSX in Loops
it('renders with different sizes', () => {
const sizes: ButtonSize[] = ['sm', 'md'];
for (const size of sizes) {
render(() => <Button size={size} />, {});
// assertions...
}
});
// ✅ Works with JSX syntax tooThe tool uses AST (Abstract Syntax Tree) backtracking to:
- Detect when a prop value comes from a loop variable
- Find the source array being iterated
- Extract the array's literal values
- Compare against the full type definition to find missing values
This means you can write concise loop-based tests, and the tool will still accurately detect which enum values are covered and which are missing.
- More Thorough Testing: Ensures all code paths for different prop values are tested
- Better Type Coverage: Catches cases where only some enum values are tested
- Clearer Insights: Reports show exactly which variants need testing
- Prevents Regressions: Ensures new enum values trigger test additions
- More Granular Tracking: Increases the number of APIs to track
- Higher Test Requirements: May require more test cases to achieve 100% coverage
- More Detailed Reports: Coverage percentages may be lower initially
- Use for Critical Props: Enable strict mode for props that have distinct behavioral paths
- Document Variants: Comment why certain variants exist if not obvious
- Group Related Tests: Organize tests by prop variant for clarity
- Skip Meaningless Variants: Remember false is already filtered for booleans
| Type | Expanded | Coverage Matching | Example |
|---|---|---|---|
| Literal Union | ✅ Yes | Exact value match | 'a' | 'b' | 'c' → [a], [b], [c] |
| Boolean | ✅ Partial | Literal match | boolean → [true] only |
| String | ❌ No | Type or literal | string → [string], accepts any string literal |
| Number | ❌ No | Type or literal | number → [number], accepts any number literal |
| Function | ❌ No | Type match* | () => void → [object]* (see note below) |
| Object | ❌ No | Type match | object → [object] |
| Mixed Union | ✅ Yes | Type/literal | 'a' | number → [a], [number] |
| Optional | Filtered | — | T | undefined → T only |
*Function Type Note: Vue's Function prop type appears as [object] in reports due to TypeScript's type system, but correctly matches function values in tests.