# SKILL: Testing — patrones

Tier 2. Vitest básico → docs vitest.

## Setup

```typescript
// @vitest-environment happy-dom        ← tope del archivo para DOM (3x más rápido que jsdom)
import { describe, test, expect, beforeEach, vi } from 'vitest'
```

## Stores — `_clearRegistry` entre tests

```typescript
import { defineStore, _clearRegistry } from 'arckode-ui'   // path directo, no barrel
beforeEach(() => { _clearRegistry() })
test('singleton', () => {
  const u = defineStore('t', { state: { x: signal(0) }, actions: {} })
  expect(u()).toBe(u())
})
```
Consumidores con `id` distinto por test no necesitan `_clearRegistry`.

## Services — mockear fetch

```typescript
function mockOk(body: unknown, status = 200) {
  vi.spyOn(global, 'fetch').mockResolvedValueOnce(
    new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' } })
  )
}
beforeEach(() => { vi.restoreAllMocks() })
```

## Componentes — NO importar `.ark`, construir manual

`.ark` requiere Vite plugin. Para tests:
```typescript
const TestComp = defineComponent({ name: 'T', setup: () => ({ state:{}, computed:{}, actions:{} }) })
;(TestComp as any).__render = () => h('div', null, 'hello')
const c = document.createElement('div')
const unmount = mountComponent(TestComp, {}, c, null)
expect(c.textContent).toBe('hello'); unmount()
```

## `watch` es async — esperar microtask

```typescript
const a = signal(0); let captured = null
watch(a, (nv) => { captured = nv })
a.value = 5
await new Promise(r => queueMicrotask(r as () => void))
expect(captured).toBe(5)
```
`effect()` síncrono. `watch()` dispara en `queueMicrotask`.

## Analyzer

```typescript
import { analyze } from '../analyzer'                      // path directo
function findViolation(vs, code) { return vs.find(v => v.code === code) }
test('detecta X', () => {
  const v = findViolation(analyze(bad, 'X.ark'), 'CODE')
  expect(v?.severity).toBe('error'); expect(v?.fix).toContain('esperado')
})
```

## Templates CLI pasan analyzer

```typescript
test('scaffold válido', () => {
  const errors = analyze(componentTemplate('Foo'), 'Foo.ark').filter(v => v.severity === 'error')
  expect(errors).toHaveLength(0)
})
```
Hay test que valida TODOS los templates con 0 errors (`src/cli/__tests__/templates.test.ts`). Tipo nuevo → sumar test ahí.

## Teleport — target ANTES del mount

```typescript
const root = document.createElement('div'); root.id = 'modals'; document.body.appendChild(root)
// recién acá mount(App, '#app')
```

## onError — silenciar console.error

```typescript
const spy = vi.spyOn(console, 'error').mockImplementation(() => {})
mountComponent(Parent, {}, container, null); await Promise.resolve()
expect(captured).toHaveLength(1); spy.mockRestore()
```

## batch() — contar invocaciones del effect

```typescript
const a = signal(0), b = signal(0); const spy = vi.fn()
effect(() => { spy(a.value, b.value) })
batch(() => { a.value = 1; b.value = 2 })
expect(spy).toHaveBeenCalledTimes(2)              // 1 inicial + 1 batch (no 3)
```

## Anti-patterns

- NO importar `.ark` en tests del runtime (Vite plugin requerido)
- NO compartir signals a nivel módulo entre tests (contaminación)
- `type-check` excluye `__tests__/` del strict (`noUncheckedIndexedAccess` + `exactOptionalPropertyTypes`)
