Catálogo JavaScript / TypeScript¶
Os códigos implementados por falsegreen-js: uma varredura estática via a
API do compilador TypeScript, agnóstica de runner entre Jest, Vitest, Mocha+Chai, Jasmine, AVA,
node:test, Cypress, Playwright e Testing Library. Cobre .js, .jsx, .ts, .tsx,
.mjs, .cjs, .mts, .cts.
Os códigos compartilham um id com Python onde o conceito coincide; códigos JS* são
específicos do ecossistema. Confiança: ALTO bloqueia, BAIXO avisa. Julgamentos são
J1-J6.
Cada código emitido tem sua própria entrada abaixo. O índice leva direto a cada uma.
Índice¶
| Código | Conf | J | Resumo |
|---|---|---|---|
| C2 | ALTO | J1 | corpo de teste vazio |
| C2b | BAIXO | J1 | chama a unidade mas nunca afirma |
| C5 | ALTO | J2 | sempre verdadeira (expect(true).toBe(true)) |
| C6 | BAIXO | J4 | verificação fraca (toBeTruthy, .length > 0) |
| C7 | ALTO | J2 | autocomparação (expect(x).toBe(x)) |
| C8 | BAIXO | J4 | igualdade exata em um float |
| C8b | BAIXO | J4 | toBeCloseTo sem argumento de precisão |
| C9 | BAIXO | J4 | toThrow() sem tipo de erro ou mensagem |
| C11a | BAIXO | J2 | literal autoconfirmante ligado da chamada sob teste |
| C16 | BAIXO | J1 | depende de tempo, aleatoriedade ou um timer fixo |
| C18 | BAIXO | J2 | igualdade de string (String(x)/JSON.stringify) |
| C20 | ALTO | J1 | asserção morta depois de return/throw |
| C21 | BAIXO | J1 | nenhuma asserção roda incondicionalmente |
| C23 | BAIXO | J6 | lê um arquivo real num caminho literal / URL fixa |
| C37 | BAIXO | J4 | caso duplicado de it.each/test.each |
| C44 | ALTO | J2 | tautologia numérica sobre um comprimento (.length >= 0) |
| C48 | BAIXO | J1 | dark patch: vira um flag de modo de teste e afirma |
| CC | BAIXO | J1 | asserção comentada |
| JS1 | ALTO | J1 | teste focado pula o resto da suíte |
| JS2 | ALTO | J1 | expect sem matcher |
| JS3 | BAIXO | J2 | o snapshot é a única asserção |
| JS4 | BAIXO | J1 | teste pulado (it.skip/xit/it.todo) |
| JS5 | BAIXO | J1 | query/evento async sem await |
| JS6 | ALTO | J1 | describe/suíte vazia |
| JS7 | BAIXO | J1 | asserção em setTimeout/then sem await |
| JS8 | BAIXO | J3 | mocka a unidade sob teste e a afirma direto |
| JS9 | ALTO | J1 | asserção em um ramo literal morto |
| JS11 | BAIXO | J1 | try/catch engole a asserção |
| JS13 | BAIXO | J4 | query usada como instrução solta, nunca afirmada |
| JS15 | BAIXO | J4 | comparação embrulhada em um booleano |
| JS17 | BAIXO | J1 | bloco de teste comentado |
| JS18 | BAIXO | J1 | callback done em vez de async/await |
| JS21 | ALTO | J1 | matcher referenciado mas nunca chamado |
| JS22 | ALTO | J1 | tabela it.each/test.each vazia |
| JS23 | ALTO | J1 | expect.assertions(N) que o corpo não satisfaz |
| JS24 | BAIXO | J4 | query do Cypress sem asserção |
| JS25 | ALTO | J1 | a única asserção está dentro de um callback de iterador |
| JS26 | BAIXO | J1 | fake timers instalados mas nunca avançados |
| JS27 | BAIXO | J3 | toHaveBeenCalled* é o único oráculo num dublê local |
| JS29 | BAIXO | J6 | cadeia resolves/rejects é uma instrução solta |
| JS30 | ALTO | J2 | asserção literal contra literal |
| JS31 | BAIXO | J1 | try/catch engole um throw do SUT, sem asserção no erro |
| D1 | OFF | J4 | roleta de asserções |
| D3 | OFF | J4 | assert duplicado |
| D4 | OFF | J4 | casos de it.each sem título |
| D6 | OFF | J4 | console.* no corpo do teste |
| D7 | OFF | J4 | teste anônimo |
| D8 | OFF | J4 | número mágico em uma asserção |
| M2 | OFF | J5 | corpo de teste longo demais |
| PL7 | BAIXO | J5 | sem gate de cobertura |
| PL8 | BAIXO | J5 | bail interrompe a execução cedo |
| PL10 | BAIXO | J1 | passWithNoTests deixa uma suíte vazia passar |
Um código mantém seu id entre linguagens onde o smell é o mesmo; o sinal abaixo é a forma JavaScript. Dois ids compartilhados diferem em sentido por linguagem, veja a página Python: C31 não existe aqui; C44 é a tautologia numérica de comprimento aqui e no Python, ampliada no Robot para qualquer asserção de biblioteca vazia.
Códigos compartilhados (mesmo conceito do Python)¶
Estes espelham as entradas do catálogo Python; o sinal é a forma JavaScript.
C2 - corpo de teste vazio¶
J1 · ALTO · F1
Um teste sem matcher, sem expect, sem nenhuma asserção. Sempre verde.
C2b - chama a unidade mas nunca afirma¶
J1 · BAIXO · F1
O teste chama código de produção mas nenhum matcher segue. A verificação simplesmente falta.
C5 - asserção sempre verdadeira¶
J2 · ALTO · F3
expect(true).toBe(true), assert(1): estruturalmente garantida a passar, não protege nada.
C6 - verificação fraca¶
J4 · BAIXO · F4
toBeTruthy() / toBeDefined() ou um .length > 0: só que algo voltou, não o valor.
C7 - autocomparação¶
J2 · ALTO · F3
expect(x).toBe(x): os dois lados são a mesma expressão, sempre iguais.
C8 - igualdade exata em um float¶
J4 · BAIXO · F4
expect(compute()).toBe(3.14159): a aritmética de ponto flutuante torna a igualdade exata pouco
confiável. Use toBeCloseTo.
C8b - toBeCloseTo sem argumento de precisão¶
J4 · BAIXO · F4
toBeCloseTo(x) usa por padrão tolerância de 2 dígitos, que pode ser frouxa demais para o valor sob
teste. Passe uma precisão explícita.
C9 - toThrow() sem tipo de erro ou mensagem¶
J4 · BAIXO · F4
expect(fn).toThrow() sem argumento aceita qualquer erro, inclusive um de um typo dentro do teste.
C11a - literal autoconfirmante¶
J2 · BAIXO · F3
O valor esperado é ligado da mesma chamada sob teste: const e = foo(); expect(foo()).toBe(e). O
oráculo confirma a si mesmo. Ligue o valor esperado de forma independente.
C16 - depende de tempo, aleatoriedade ou um timer fixo¶
J1 · BAIXO · F6
Date.now / new Date() / Math.random / crypto.randomUUID / getRandomValues, ou um timer
fixo. Congele o tempo e semeie a aleatoriedade.
C18 - igualdade de string¶
J2 · BAIXO · F4
Compara String(x) / JSON.stringify(x) / um template literal com um literal de string. O formato
é detalhe de implementação; afirme o valor.
C20 - asserção morta depois de um terminador¶
J1 · ALTO · F2
Uma asserção depois de return / throw / process.exit / um switch exaustivo. A alcançabilidade
estruturada no nível de bloco (cfg.ts) prova que nunca roda.
C21 - nenhuma asserção roda incondicionalmente¶
J1 · BAIXO · F2
Toda asserção está dentro de um ramo; nenhuma fica na espinha garantida do teste. A mesma alcançabilidade do cfg.ts decide isso, e uma asserção morta não mascara o caso.
C23 - lê um arquivo real num caminho literal ou URL fixa¶
J6 · BAIXO · F6
readFileSync("/etc/...") ou uma URL fixa no código: mystery guest. Use uma fixture ou arquivo
temporário.
C37 - caso duplicado de it.each / test.each¶
J4 · BAIXO · F8
A mesma linha de argumentos aparece duas vezes na tabela; a duplicata roda o mesmo cenário e não adiciona cobertura.
C44 - tautologia numérica sobre um comprimento¶
J2 · ALTO · F3
expect(x.length).toBeGreaterThanOrEqual(0): um comprimento nunca é negativo, então a comparação é
sempre verdadeira.
C48 - dark patch: vira um flag de modo de teste e afirma¶
J1 · BAIXO · F2
O teste vira um flag de modo de teste (process.env.NODE_ENV = "test", process.env.TESTING = "1",
settings.TESTING = true) e depois afirma, então exercita o ramo só-de-teste do produto em vez do
comportamento real. Paridade com o C48 do Python.
CC - asserção comentada¶
J1 · BAIXO · F2
Uma linha no corpo é um expect(...) comentado: a verificação foi desligada e deixada.
Códigos específicos de JavaScript¶
JS1 - teste focado pula o resto da suíte¶
J1 · ALTO · F5
it.only / fit / describe.only exclui em silêncio todos os outros testes do arquivo da execução.
JS2 - expect sem matcher¶
J1 · ALTO · F1
expect(value) sem uma chamada de matcher. Nada é afirmado; a linha é um no-op.
JS3 - o snapshot é a única asserção¶
J4 · BAIXO · F3
Um teste cuja única verificação é toMatchSnapshot() / toMatchInlineSnapshot(). A baseline é
gerada a partir da saída, então detecta mudança, não correção.
JS4 - teste pulado¶
J1 · BAIXO · F5
it.skip / xit / it.todo deixado no lugar. Excluído em silêncio da execução.
JS5 - query ou evento async não aguardado¶
J1 · BAIXO · F2
Uma chamada do tipo promise ou value-only (findBy*, waitFor, userEvent, um expect().resolves
solto) é descartada como instrução nua, então a asserção seguinte lê um momento defasado.
A detecção passa pelo registro de oráculos.
JS6 - describe / suite vazio¶
J1 · ALTO · F1
Um bloco describe/suite sem teste dentro. Não contribui com nada e pode ser lido como cobertura.
JS7 - asserção em um setTimeout / callback then não aguardado¶
J1 · BAIXO · F2
A asserção vive em um callback de setTimeout/setInterval (braço de timer) ou em um
.then/.catch/.finally solto (braço de promise), então pode rodar depois de o teste reportar verde.
JS8 - faz mock da unidade sob teste e a afirma diretamente¶
J3 · BAIXO · F4
O teste faz mock da própria função que afirma testar, depois afirma o valor do mock. Ele testa a
configuração do mock, não o código. (A forma semântica, mais profunda, é o caso 10 / S12.)
JS9 - asserção em um ramo literal morto¶
J1 · ALTO · F2
if (false) { expect(...) } ou o else de if (true). A asserção é inalcançável.
JS11 - try/catch engole a asserção¶
J1 · BAIXO · F2
A asserção fica em um try cujo catch absorve o erro lançado (loga ou ignora), então uma
falha nunca se propaga.
JS13 - query usada como instrução solta, nunca afirmada¶
J4 · BAIXO · F1
getBy* / queryBy* chamado como instrução nua. A query roda mas nada é verificado nela.
JS15 - comparação envolvida em um booleano¶
J4 · BAIXO · F4
expect(a === b).toBe(true). O booleano colapsa o diff: na falha a mensagem é
false !== true, sem valores. Afirme os valores diretamente.
JS17 - bloco de teste comentado¶
J1 · BAIXO · F2
// it('...', ...) deixado no arquivo. O teste não roda.
JS18 - callback done em vez de async/await¶
J1 · BAIXO · F2
Um teste com callback done onde done() precede ou fica no mesmo nível da asserção, então o
runner conclui antes de a asserção lançar.
JS21 - matcher referenciado mas nunca chamado¶
J1 · ALTO · F2
expect(x).toBe sem (). O matcher é um acesso a propriedade; a verificação nunca roda. Irmão de
JS2.
JS22 - tabela it.each / test.each vazia¶
J1 · ALTO · F5
it.each([])(...). Zero casos são gerados, então o teste nunca roda. Paralelo de C45.
JS23 - expect.assertions(N) que o corpo não consegue satisfazer¶
J1 · ALTO · F5
expect.assertions(N) com um N numérico maior que as chamadas expect() incondicionais,
alcançáveis e não-aninhadas que podem rodar. A garantia que o autor escreveu para se proteger de
uma asserção async perdida em silêncio já nasce morta. Dispara só num déficit provável de N
literal: um expect em loop, em ramo, num .then/callback, ou num helper torna a contagem
indeterminada e suprime o achado. expect.hasAssertions() não tem contagem e é ignorado.
JS24 - query do Cypress sem asserção¶
J4 · BAIXO · F4
Uma cadeia de query do Cypress (cy.get/cy.find/cy.contains) usada como statement sem
.should/.and no fim e sem expect dentro de um callback .then. A query produz um subject que
nunca é asserido, o análogo cy.* da JS13. Comandos de ação (click/type/visit/...) fazem
trabalho em vez de consultar, então uma cadeia terminada num deles fica limpa, assim como uma
terminada em .should/.and.
JS25 - a única asserção fica dentro de um callback de iterador de array¶
J1 · ALTO · F2
A única expect fica dentro de um callback de forEach / map / filter / some / every /
flatMap. Numa coleção vazia o callback nunca roda, então o teste passa sem ter afirmado nada.
Afirme o comprimento antes, ou tire ao menos uma verificação para fora do iterador.
JS26 - fake timers instalados mas nunca avançados¶
J1 · BAIXO · F2
jest.useFakeTimers() / vi.useFakeTimers() (ou fake timers do sinon) é configurado, mas o
relógio nunca é avançado (runAllTimers, advanceTimersByTime, tick). O callback agendado nunca
dispara, então a asserção lê estado não-modificado e passa pelo motivo errado.
JS27 - toHaveBeenCalled* é o único oráculo sobre um dublê criado localmente¶
J3 · BAIXO · F4
A única asserção é toHaveBeenCalled / toHaveBeenCalledWith / toHaveBeenCalledTimes sobre um
mock criado no teste (jest.fn() / vi.fn()). Ela verifica a fiação do próprio teste, que o
dublê foi chamado, não que a unidade produziu o resultado certo.
JS29 - cadeia resolves / rejects como instrução solta¶
J1 · BAIXO · F2
expect(p).resolves.toBe(...) / .rejects... escrito como uma instrução que não é nem awaitada
nem returnada. O matcher devolve uma promise; o teste termina verde antes de ela resolver, então
uma rejeição posterior se perde.
JS30 - asserção literal contra literal¶
J2 · ALTO · F3
Ambos os operandos são fixos em tempo de parse: expect(2).toBe(3), chai expect(1).to.equal(1).
A asserção não toca a unidade sob teste, então é sempre-verdadeira ou sempre-falsa por construção,
nunca uma verificação do comportamento real.
JS31 - try/catch engole um possível throw sem asserção sobre a exceção¶
J1 · BAIXO · F2
Um try chama a unidade e o catch não relança nem afirma nada sobre o erro. Uma unidade que
para de lançar ainda passa verde. Irmã da JS11, onde o que é engolido é um expect; aqui não há
asserção nenhuma no caminho capturado.
Armadilhas de alto valor com evidência¶
O scanner também pega um conjunto de false-greens específicos de idioma documentados nos estudos empíricos de JS/TS:
- Armadilha de caixa em header HTTP (
J4): o supertest deixa em minúsculas os headers de resposta, entãores.headers['Content-Type']é sempreundefined. Use a chave em minúsculas. - Coerção por NOT bit a bit (
J4):~~res.header['content-length']transforma um header ausente em0, passando em silêncio quando o valor esperado é0. - Oráculo de campo autorreferente (
J2):expect(result).toEqual([{ createdAt: result[0].createdAt }])compara um campo consigo mesmo. Use um valor conhecido fixo. - Tautologia assina-depois-verifica (
J2): assinar um JWT e verificá-lo com a mesma chave passa mesmo queverify()pule a checagem, a menos que pareado com um teste negativo de chave errada.
Camada de projeto - auditoria de config (PL)¶
Emitidos por --config-audit, não pela varredura por arquivo. A suíte fica verde por configuração
do runner, não por um smell dentro de algum arquivo de teste.
PL7 - sem gate de cobertura¶
J5 · BAIXO · F8
Nenhum coverageThreshold / coverage.thresholds está configurado, então a cobertura pode cair a
zero e a suíte ainda passa. Adicione um limiar de cobertura.
PL8 - bail interrompe a execução cedo¶
J5 · BAIXO · F5
bail está ligado, então a execução para nas primeiras falhas e a contagem de testes reportada fica
incompleta.
PL10 - passWithNoTests deixa uma suíte vazia passar¶
J1 · BAIXO · F5
passWithNoTests deixa uma suíte vazia ou totalmente filtrada reportar verde, então um glob mal
configurado ou um arquivo apagado passa despercebido.
Códigos de diagnóstico (opcionais, OFF por padrão)¶
Família F8: não é false-green (o teste ainda protege), mostrado só numa passagem de diagnóstico.
| Código | O que sinaliza |
|---|---|
| D1 | roleta de asserções: muitas asserções num teste, nenhuma identificando qual falhou |
| D3 | assert duplicado: a mesma asserção aparece mais de uma vez |
| D4 | casos de it.each / test.each sem título: um caso que falha é nomeado só pelo índice |
| D6 | console.* no corpo do teste: artefato de debug que contorna o oráculo |
| D7 | teste anônimo: descrição vazia ou ausente |
| D8 | número mágico em uma asserção: um literal numérico nu em vez de uma constante nomeada |
| M2 | corpo de teste longo demais: passa do limiar de linhas |
Parecidos: NÃO sinalizar¶
expect(fn).not.toThrow()depois de uma chamada de setup: a ausência de exceção é a asserção significativa.expect(emitter).toHaveProperty('on'): checagem de duck-typing; a presença da interface é o contrato.expectTypeOf(v).toEqualTypeOf<T>(): uma asserção de tipo em tempo de compilação; falha notsc, não C5.expect(result).toMatchObject({ kind: 'error' })sobre uma união discriminada: o discriminante determina o ramo; não C6.vi.mocked(fn)/jest.mocked(fn): wrappers de mock tipados, o equivalente TS deautospec; não C13b.done()depois de uma série de asserções reais: o smell édone()antes da asserção, não depois.