Skip to content

Engine/Testes: extrair métricas puras de Race.tsx para src/lib/metrics.ts + testes de honestidade (WPM/precisão) #32

Description

@caioross

Contexto

A honestidade das métricas (WPM, precisão, erros) é a verdade sagrada do domínio — foi a origem do feedback do Iniciante que virou #20 ("Precisão 100%" coexistindo com "Erros 1"). Mas hoje toda a lógica de cálculo mora inline dentro do componente src/components/Race.tsx, o que a torna impossível de testar por unidade:

  • correctChars — loop char-a-char (Race.tsx:49-53)
  • wpm(correctChars / 5) / elapsedMin arredondado (Race.tsx:55)
  • accuracy(1 - errors/totalKeystrokes) * 100 com clamp e arredondamento (Race.tsx:56-57)
  • progresstyped.length / code.length (Race.tsx:58)
  • contagem de erros/keystrokes em handleInput, incluindo a regra "backspace não conta keystroke" (Race.tsx:81-92)

Existe o script .claude/skills/cr-typing-engine/scripts/validate-metrics.mjs, mas ele valida uma cópia da lógica, não o código que roda em produção — divergência silenciosa é possível. Esta issue extrai as funções puras para src/lib/ e as cobre com testes reais, tornando a engine testável sem alterar comportamento.

Depende do runner instalado pela issue de infra de testes (Vitest) — se ela ainda não tiver mergeado, esta pode incluir a config mínima ou aguardar.

Escopo

  1. Criar src/lib/metrics.ts com funções puras e determinísticas, extraídas 1:1 de Race.tsx (sem mudar fórmula nem arredondamento):
    • countCorrectChars(typed: string, code: string): number
    • computeWpm(correctChars: number, elapsedMin: number): number
    • computeAccuracy(errors: number, totalKeystrokes: number): number
    • computeProgress(typedLen: number, codeLen: number): number
  2. Race.tsx passa a importar e usar essas funções (nenhuma mudança de comportamento observável — mesmos números na tela).
  3. Suite src/lib/metrics.test.ts cobrindo as invariantes de honestidade.

Acceptance criteria (verificáveis)

  • Race.tsx não contém mais o cálculo inline — usa metrics.ts; a UI mostra exatamente os mesmos valores de antes (sem regressão visual/numérica).
  • computeWpm: elapsedMin <= 0.0010 (evita divisão por ~0); conta só correctChars (chars errados não contam WPM).
  • computeAccuracy: totalKeystrokes === 0100; nunca retorna 100 quando errors > 0 (a honestidade de Engine/Honestidade: "Precisão 100%" coexiste com "Erros 1" — arredondamento mascara erro e rótulo confunde #20 — o arredondamento de 0.995 não pode mascarar erro); clampada em [0,100].
  • countCorrectChars: só conta posições onde typed[i] === code[i]; typed mais longo que code não estoura.
  • computeProgress: codeLen === 00; clampada em [0,1].
  • node scripts/validate-metrics.mjs (via skill) continua verde; pnpm test verde.

Dica de abordagem

  • Extração pura, zero mudança de fórmula — copie exatamente Race.tsx:49-58. O ganho é testabilidade e uma única fonte de verdade (a duplicata em validate-metrics.mjs pode passar a importar de metrics.ts numa fatia futura).
  • Área sagrada (HANDBOOK §2): Race.tsx está no caminho de input da corrida (60fps). A extração deve ser puro refactor — as funções continuam chamadas dentro de useMemo/render como hoje, sem trabalho extra por keystroke. Não introduza alocação/loop novo no hot path.
  • Gate: pnpm typecheck && pnpm build && pnpm test && node scripts/validate-metrics.mjs. PR normal (§7.3 — refactor + testes de engine com validate-metrics verde).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Prioridade normalarea:engineMotor de digitação, WPM, precisão, anti-cheat

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions