> For the complete documentation index, see [llms.txt](https://docs.ipcheck.ing/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipcheck.ing/developer/pt-br/development/testing.md).

# Testes

A configuração do executor de testes do Node.js e o que é (e o que não é) coberto pelos testes.

O MyIP usa o **executor de testes integrado do Node.js**. Não há Jest, nem Vitest, nem qualquer dependência de framework de teste.

```bash
pnpm test     # node --test tests/*.test.js
```

Toda spec fica em `tests/`, plana e nomeada `<subject>.test.js`. As specs composables têm prefixo: `tests/composable-status-tone.test.js`, `tests/composable-refresh-orchestrator.test.js`.

Para executar um arquivo durante a iteração:

```bash
node --test tests/guards.test.js
```

## Formato das specs

`node:test` para estrutura, `node:assert/strict` para asserções. Sem helpers personalizados além do que um arquivo precisa:

{% code title="tests/composable-status-tone.test.js" %}

```js
// Testes para ipFieldTone — o mapeamento unificado "string de status → tom" que
// WebRtcTest / DnsLeaksTest / RuleTest / ConnectivityTest usam.

import assert from 'node:assert/strict';
import { describe, it } from 'node:test';

import { ipFieldTone } from '../frontend/composables/use-status-tone.js';

describe('ipFieldTone()', () => {
  it('retorna "wait" quando o valor é igual ao rótulo de espera', () => {
    assert.equal(ipFieldTone('Waiting', { waitLabels: 'Waiting', errorLabels: 'Error' }), 'wait');
  });
});
```

{% endcode %}

Como qualquer outro arquivo do projeto, uma spec começa com um comentário de cabeçalho dizendo o que ela cobre.

## O que recebe uma spec

{% hint style="success" %}
**Qualquer lógica não visual que possa ser exercitada sem uma chamada de rede vem com uma spec — na mesma alteração.**
{% endhint %}

Na prática:

* **Funções puras** — validadores, formatadores, parsers. `tests/valid-ip.test.js`, `tests/bgp-prefix.test.js`, `tests/mtr-parse.test.js`, e as duas specs da Calculadora IP dirigidas por tabela, `tests/ip-math.test.js` e `tests/ip-calc.test.js`.
* **Transformações** — qualquer coisa que reformata dados de upstream. `tests/transform-ip-data.test.js`, `tests/service-status-transform.test.js`.
* **Composables com entradas simuláveis** — lógica que você pode conduzir passando valores. `tests/composable-achievement-engine.test.js`, `tests/composable-info-mask.test.js`.
* **Middleware** — `tests/guards.test.js` cobre cada guard em `common/guards.js` com `(req, res, next)` stubs.
* **Arquivos de dados estáticos** — formato e integridade. `tests/changelog.test.js`, `tests/achievements.test.js`, `tests/sections.test.js`, `tests/ip-databases.test.js`.
* **Handlers de API** — apenas cobertura de smoke, veja abaixo.

Quando o comportamento mudar, atualize as specs afetadas **na mesma alteração**. Não adie.

### Specs de ponte

Quando um helper vive em `common/` e é reexportado por meio de `frontend/utils/`, a spec importa **ambos os caminhos** e afirma que eles são iguais. `tests/valid-ip.test.js` faz isso, o que impede que uma ponte volte a ganhar silenciosamente uma implementação duplicada.

Para uma ponte que reexporta um módulo inteiro (`export * from '../../common/ip-math.js'`), `tests/ip-math.test.js` vai um passo além: importa ambos como namespaces e percorre cada exportação de `common`, afirmando que a ponte devolve a mesma função — assim uma nova exportação nunca pode faltar silenciosamente no lado do frontend.

## O que não recebe uma spec

| Fora de escopo         | Motivo                                                        |
| ---------------------- | ------------------------------------------------------------- |
| Renderização do Vue    | O executor do Node não monta nada — nem DOM, nem componentes. |
| Chamadas reais de rede | Nenhuma spec pode atingir um upstream ao vivo.                |
| APIs do navegador      | WebRTC, `navigator`, fingerprinting de canvas e afins.        |

{% hint style="warning" %}
**Mudanças visuais não podem ser testadas automaticamente.** Se a sua mudança for visual, diga isso explicitamente ao repassá-la e deixe uma pessoa olhar para ela em `pnpm dev`. Uma execução bem-sucedida de `pnpm check` não prova nada sobre a aparência da UI.
{% endhint %}

## Testes de smoke para handlers de API

Os handlers em `api/` recebem sua cobertura de smoke em `tests/api-handlers.test.js`. A maioria já está coberta hoje; um handler que você adicionar ou tocar inclui seu bloco na mesma alteração. A filosofia é estreita e rígida:

{% hint style="danger" %}
**Nunca acesse um upstream real.** Afirme apenas em branches que retornam **antes de** a primeira `fetchUpstream` chamada.
{% endhint %}

Isso deixa três tipos de asserção:

* **Bloqueio por método** — `POST` para um handler que aceita apenas GET retorna `405`.
* **Branches de parâmetros** — entrada ausente ou malformada retorna `400`.
* **"API key missing" retorna cedo** — o handler desiste antes de fazer a chamada externa.

O arquivo fornece dois stubs que todo teste de handler reutiliza:

{% code title="tests/api-handlers.test.js" %}

```js
function createRequest(options = {}) {
    const method = options.method || 'GET';
    const query = options.query || {};
    const referer = Object.hasOwn(options, 'referer') ? options.referer : 'http://localhost/';
    const headers = {};
    if (referer !== undefined) headers.referer = referer;
    return { method, headers, query, body: options.body };
}

function createResponse() {
    return {
        statusCode: 200,
        body: undefined,
        status(code) { this.statusCode = code; return this; },
        json(payload) { this.body = payload; return this; },
        send(payload) { this.body = payload; return this; },
    };
}
```

{% endcode %}

Um bloco completo de handler é assim:

{% code title="tests/api-handlers.test.js" %}

```js
describe('mac-checker handler', () => {
    it('rejects missing ?mac', async () => {
        const res = createResponse();
        await macCheckerHandler(createRequest(), res);
        assert.equal(res.statusCode, 400);
        assert.deepEqual(res.body, { error: 'No MAC address provided' });
    });

    it('rejects invalid MAC format', async () => {
        const res = createResponse();
        await macCheckerHandler(createRequest({ query: { mac: 'not-a-mac' } }), res);
        assert.equal(res.statusCode, 400);
        assert.deepEqual(res.body, { error: 'Invalid MAC address' });
    });
});
```

{% endcode %}

### Variáveis de ambiente

Testes que alteram uma variável de ambiente registram o nome dela no `ENV_KEYS` array no topo do arquivo. `beforeEach` faz backup dessas chaves e `afterEach` as restaura, para que nenhuma spec vaze estado para a próxima.

### Não duplique o middleware

As verificações de Referer e a validação de parâmetros são impostas pelo middleware, não pelos handlers. Elas são cobertas uma vez, em `tests/guards.test.js`. Uma spec de handler não deve reafirmar "rejeita um domínio inválido" — o handler nunca vê um.

A convenção é anotar isso em um comentário acima do `describe` bloco, como o bloco do handler OONI faz:

```js
// Domain presence/shape is enforced by requireValidDomain middleware
// (tests/guards.test.js); the handler's only pre-fetch branch is the
// defensive method gate.
```

### Os bloqueios defensivos por método permanecem

Alguns handlers mantêm um `req.method !== 'GET'` verificação mesmo que a rota já restrinja o método. Esses bloqueios existem porque os testes de smoke afirmam diretamente sobre eles. Mantenha-os.

## Specs relacionadas que vale conhecer

| Spec                                                            | Cobre                                                                                                                                                                                                                                                                                                                                                                                                                              |
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tests/guards.test.js`                                          | Todo middleware em `common/guards.js`                                                                                                                                                                                                                                                                                                                                                                                              |
| `tests/fetch-with-timeout.test.js`                              | Comportamento de timeout e cancelamento do upstream                                                                                                                                                                                                                                                                                                                                                                                |
| `tests/locale-packs.test.js`                                    | A validação de tradução: o registro e os arquivos de pacote concordam, cada pacote contém exatamente `en`as chaves do inglês, os placeholders são um subconjunto do inglês e um `locale completo` não deixa nada sem traduzir — veja [i18n](/developer/pt-br/development/i18n.md)                                                                                                                                                  |
| `tests/locale-registry.test.js` / `tests/locale-pack.test.js`   | O formato das entradas do registro e os mapeamentos derivados; a `""`convenção de "- significa não traduzido" e sua remoção em tempo de build                                                                                                                                                                                                                                                                                      |
| `tests/index-html-i18n.test.js`                                 | `index.html`o texto inline mantido manualmente — piadas da tela de boot, JSON-LD, seletor de idioma — corresponde ao registro                                                                                                                                                                                                                                                                                                      |
| `tests/i18n-scaffold.test.js`                                   | Os `pnpm i18n-new` / `pnpm i18n-sync` scripts de scaffolding                                                                                                                                                                                                                                                                                                                                                                       |
| `tests/changelog.test.js`                                       | Formato do Changelog, e uma tradução para cada `locale completo` locale — veja [i18n](/developer/pt-br/development/i18n.md)                                                                                                                                                                                                                                                                                                        |
| `tests/report-schema.test.js` / `tests/report-builders.test.js` | O pipeline de relatório de diagnóstico compartilhável                                                                                                                                                                                                                                                                                                                                                                              |
| `tests/app-commands.test.js`                                    | O barramento de comandos: registro, despacho, timeouts e o contrato de código de rejeição                                                                                                                                                                                                                                                                                                                                          |
| `tests/ip-math.test.js`                                         | `common/ip-math.js`: parsing rígido de IPv4 / IPv6, formatação RFC 5952, máscaras e contagens (incluindo o `/31` caso RFC 3021), contenção, split / aggregate / range-to-CIDR — e a verificação de identidade da ponte acima                                                                                                                                                                                                       |
| `tests/ip-calc.test.js`                                         | `frontend/utils/ip-calc.js`: a ordem das regras do classificador e `motivo` códigos, as tabelas de finalidade especial da IANA (cada linha é analisada, está alinhada, cita um RFC e tem um id exclusivo), os decodificadores IPv6 (IPv4 embutido, Teredo, EUI-64, multicast, ULA), nomes PTR, formas ofuscadas e embutidas, formatação de contagem, e que `calculate()` nunca lança exceção. A entrada MAC é verificada *ausente* |
| `tests/connectivity-lists.test.js`                              | O modelo de múltiplas listas da seção Connectivity (`frontend/utils/connectivity-lists.js`): limpeza na inicialização e migração das chaves legadas de destino plano, guards de CRUD de listas e planejamento de importação curada                                                                                                                                                                                                 |
| `tests/connectivity-import-lists.test.js`                       | Integridade dos dados das listas de importação curadas de Connectivity: formato das entradas, URLs apenas HTTPS, cobertura de locale dos nomes das listas e um PNG de favicon versionado para cada membro. Ícones ausentes são buscados automaticamente localmente por meio de `scripts/fetch-favicons.js`; o CI permanece offline e apenas informa para executar `pnpm fetch-favicons` e fazer commit dos PNGs                    |
| `tests/banners.test.js`                                         | Os helpers de banner de seção, mais a validação de contrato de quaisquer arquivos de dados de tempo de deploy presentes em `frontend/data/banners/` — verde de forma vacuamente verdadeira quando o diretório ignorado pelo git está vazio. Veja [Banners de seção](/developer/pt-br/configuration/section-banners.md)                                                                                                             |
| `tests/persona-i18n.test.js`                                    | Cada chave de id, reason e detail do In-depth Persona Check declarada em `frontend/utils/persona/check-ids.js` tem texto em cada `locale completo` pacote                                                                                                                                                                                                                                                                          |

## `pnpm check` precisa estar verde

```bash
pnpm check     # pnpm test && pnpm build
```

Testes mais uma build real de produção. Execute-o antes de cada entrega — um PR, um commit, uma solicitação de revisão.

O CI executa as mesmas duas etapas (`pnpm test`, depois `pnpm run build`) em cada push e pull request em relação a `main` e `dev`, no Node 24, instalando com `pnpm install --frozen-lockfile`. Uma execução local bem-sucedida do `check` normalmente significa uma execução de CI bem-sucedida.

## Próximo

* [Convenções de Codificação](/developer/pt-br/development/coding-conventions.md) — as regras contra as quais as specs estão validando.
* [Adicionando uma Nova Ferramenta](/developer/pt-br/development/adding-a-new-tool.md) — onde os testes se encaixam em uma funcionalidade completa.
* [Backend](/developer/pt-br/architecture/backend.md) — design de handlers e middleware.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ipcheck.ing/developer/pt-br/development/testing.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
