Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
234 lines
3.9 KiB
Markdown
234 lines
3.9 KiB
Markdown
---
|
|
name: test-api
|
|
description: test/it function for defining tests with modifiers
|
|
---
|
|
|
|
# Test API
|
|
|
|
## Basic Test
|
|
|
|
```ts
|
|
import { expect, test } from 'vitest'
|
|
|
|
test('adds numbers', () => {
|
|
expect(1 + 1).toBe(2)
|
|
})
|
|
|
|
// Alias: it
|
|
import { it } from 'vitest'
|
|
|
|
it('works the same', () => {
|
|
expect(true).toBe(true)
|
|
})
|
|
```
|
|
|
|
## Async Tests
|
|
|
|
```ts
|
|
test('async test', async () => {
|
|
const result = await fetchData()
|
|
expect(result).toBeDefined()
|
|
})
|
|
|
|
// Promises are automatically awaited
|
|
test('returns promise', () => {
|
|
return fetchData().then(result => {
|
|
expect(result).toBeDefined()
|
|
})
|
|
})
|
|
```
|
|
|
|
## Test Options
|
|
|
|
```ts
|
|
// Timeout (default: 5000ms)
|
|
test('slow test', async () => {
|
|
// ...
|
|
}, 10_000)
|
|
|
|
// Or with options object
|
|
test('with options', { timeout: 10_000, retry: 2 }, async () => {
|
|
// ...
|
|
})
|
|
```
|
|
|
|
## Test Modifiers
|
|
|
|
### Skip Tests
|
|
|
|
```ts
|
|
test.skip('skipped test', () => {
|
|
// Won't run
|
|
})
|
|
|
|
// Conditional skip
|
|
test.skipIf(process.env.CI)('not in CI', () => {})
|
|
test.runIf(process.env.CI)('only in CI', () => {})
|
|
|
|
// Dynamic skip via context
|
|
test('dynamic skip', ({ skip }) => {
|
|
skip(someCondition, 'reason')
|
|
// ...
|
|
})
|
|
```
|
|
|
|
### Focus Tests
|
|
|
|
```ts
|
|
test.only('only this runs', () => {
|
|
// Other tests in file are skipped
|
|
})
|
|
```
|
|
|
|
### Todo Tests
|
|
|
|
```ts
|
|
test.todo('implement later')
|
|
|
|
test.todo('with body', () => {
|
|
// Not run, shows in report
|
|
})
|
|
```
|
|
|
|
### Failing Tests
|
|
|
|
```ts
|
|
test.fails('expected to fail', () => {
|
|
expect(1).toBe(2) // Test passes because assertion fails
|
|
})
|
|
```
|
|
|
|
### Concurrent Tests
|
|
|
|
```ts
|
|
// Run tests in parallel
|
|
test.concurrent('test 1', async ({ expect }) => {
|
|
// Use context.expect for concurrent tests
|
|
expect(await fetch1()).toBe('result')
|
|
})
|
|
|
|
test.concurrent('test 2', async ({ expect }) => {
|
|
expect(await fetch2()).toBe('result')
|
|
})
|
|
```
|
|
|
|
### Sequential Tests
|
|
|
|
```ts
|
|
// Force sequential in concurrent context
|
|
test.sequential('must run alone', async () => {})
|
|
```
|
|
|
|
## Parameterized Tests
|
|
|
|
### test.each
|
|
|
|
```ts
|
|
test.each([
|
|
[1, 1, 2],
|
|
[1, 2, 3],
|
|
[2, 1, 3],
|
|
])('add(%i, %i) = %i', (a, b, expected) => {
|
|
expect(a + b).toBe(expected)
|
|
})
|
|
|
|
// With objects
|
|
test.each([
|
|
{ a: 1, b: 1, expected: 2 },
|
|
{ a: 1, b: 2, expected: 3 },
|
|
])('add($a, $b) = $expected', ({ a, b, expected }) => {
|
|
expect(a + b).toBe(expected)
|
|
})
|
|
|
|
// Template literal
|
|
test.each`
|
|
a | b | expected
|
|
${1} | ${1} | ${2}
|
|
${1} | ${2} | ${3}
|
|
`('add($a, $b) = $expected', ({ a, b, expected }) => {
|
|
expect(a + b).toBe(expected)
|
|
})
|
|
```
|
|
|
|
### test.for
|
|
|
|
Preferred over `.each` - doesn't spread arrays:
|
|
|
|
```ts
|
|
test.for([
|
|
[1, 1, 2],
|
|
[1, 2, 3],
|
|
])('add(%i, %i) = %i', ([a, b, expected], { expect }) => {
|
|
// Second arg is TestContext
|
|
expect(a + b).toBe(expected)
|
|
})
|
|
```
|
|
|
|
## Test Context
|
|
|
|
First argument provides context utilities:
|
|
|
|
```ts
|
|
test('with context', ({ expect, skip, task }) => {
|
|
console.log(task.name) // Test name
|
|
skip(someCondition) // Skip dynamically
|
|
expect(1).toBe(1) // Context-bound expect
|
|
})
|
|
```
|
|
|
|
## Custom Test with Fixtures
|
|
|
|
```ts
|
|
import { test as base } from 'vitest'
|
|
|
|
const test = base.extend({
|
|
db: async ({}, use) => {
|
|
const db = await createDb()
|
|
await use(db)
|
|
await db.close()
|
|
},
|
|
})
|
|
|
|
test('query', async ({ db }) => {
|
|
const users = await db.query('SELECT * FROM users')
|
|
expect(users).toBeDefined()
|
|
})
|
|
```
|
|
|
|
## Retry Configuration
|
|
|
|
```ts
|
|
test('flaky test', { retry: 3 }, async () => {
|
|
// Retries up to 3 times on failure
|
|
})
|
|
|
|
// Advanced retry options
|
|
test('with delay', {
|
|
retry: {
|
|
count: 3,
|
|
delay: 1000,
|
|
condition: /timeout/i, // Only retry on timeout errors
|
|
},
|
|
}, async () => {})
|
|
```
|
|
|
|
## Tags
|
|
|
|
```ts
|
|
test('database test', { tags: ['db', 'slow'] }, async () => {})
|
|
|
|
// Run with: vitest --tags db
|
|
```
|
|
|
|
## Key Points
|
|
|
|
- Tests with no body are marked as `todo`
|
|
- `test.only` throws in CI unless `allowOnly: true`
|
|
- Use context's `expect` for concurrent tests and snapshots
|
|
- Function name is used as test name if passed as first arg
|
|
|
|
<!--
|
|
Source references:
|
|
- https://vitest.dev/api/test.html
|
|
-->
|