falsegreen-js 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -1
- package/README.md +2 -0
- package/dist/cases.js +2 -0
- package/dist/cli.js +1 -1
- package/dist/rules.js +26 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@ All notable changes to this project are documented here. The format is based on
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.0] - 2026-06-22
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- D8 (opt-in diagnostic): magic number in an assertion - a bare numeric literal as
|
|
13
|
+
the expected value. The most frequent smell in LLM-generated tests (2410.10628).
|
|
14
|
+
- JS15: inappropriate assertion - the comparison is wrapped in a boolean
|
|
15
|
+
(`expect(a === b).toBe(true)`), so the failure message is blind and the oracle
|
|
16
|
+
weak. Sourced from the xNose "Inappropriate Assertions" smell (Paul et al., 2024).
|
|
17
|
+
|
|
9
18
|
## [0.1.0] - 2026-06-22
|
|
10
19
|
|
|
11
20
|
### Added
|
|
@@ -36,5 +45,6 @@ All notable changes to this project are documented here. The format is based on
|
|
|
36
45
|
- pre-commit hook (`.pre-commit-hooks.yaml`), CI matrix (Node 18/20/22), and an npm
|
|
37
46
|
trusted-publishing release workflow.
|
|
38
47
|
|
|
39
|
-
[Unreleased]: https://github.com/vinicq/falsegreen-js/compare/v0.
|
|
48
|
+
[Unreleased]: https://github.com/vinicq/falsegreen-js/compare/v0.2.0...HEAD
|
|
49
|
+
[0.2.0]: https://github.com/vinicq/falsegreen-js/compare/v0.1.0...v0.2.0
|
|
40
50
|
[0.1.0]: https://github.com/vinicq/falsegreen-js/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -82,6 +82,7 @@ line up in the research. `JS*` codes are ecosystem-specific.
|
|
|
82
82
|
| JS9 | high | assertion in a dead branch (`if(false)` / `if(true){}else`) — never runs |
|
|
83
83
|
| JS11 | low | `try/catch` swallows the assertion — a failing `expect` is caught, test stays green |
|
|
84
84
|
| JS13 | low | query (`getBy*`/`queryBy*`) as a loose statement — its result is never asserted |
|
|
85
|
+
| JS15 | low | inappropriate assertion — comparison wrapped in a boolean (`expect(a===b).toBe(true)`), blind failure message |
|
|
85
86
|
|
|
86
87
|
Each code carries a judgment tag (J1-J6) shared with the
|
|
87
88
|
[falsegreen-skill](https://github.com/vinicq/falsegreen-skill) semantic framework.
|
|
@@ -99,6 +100,7 @@ default. Enable them with `--diagnostics`, or per code via config `severity`. Th
|
|
|
99
100
|
| D4 | diagnostic | `it.each`/`test.each` without titled cases (index-only) |
|
|
100
101
|
| D6 | diagnostic | `console.*` in a test body |
|
|
101
102
|
| D7 | diagnostic | anonymous test — empty or missing description |
|
|
103
|
+
| D8 | diagnostic | magic number — a bare numeric literal as the expected value |
|
|
102
104
|
| M2 | coupling | test body exceeds the line-count threshold |
|
|
103
105
|
|
|
104
106
|
```bash
|
package/dist/cases.js
CHANGED
|
@@ -38,6 +38,7 @@ export const CASES = {
|
|
|
38
38
|
JS9: { title: "assertion in a dead branch (if(false) / if(true){}else) — it never runs", confidence: "high", judgment: "J1" },
|
|
39
39
|
JS11: { title: "try/catch swallows the assertion — a failing expect is caught and the test stays green", confidence: "low", judgment: "J1" },
|
|
40
40
|
JS13: { title: "query (getBy*/queryBy*/wrapper.find) as a loose statement — its result is never asserted", confidence: "low", judgment: "J4" },
|
|
41
|
+
JS15: { title: "inappropriate assertion — the comparison is wrapped in a boolean (expect(a===b).toBe(true)), so the failure message is blind", confidence: "low", judgment: "J4" },
|
|
41
42
|
// --- diagnostic group (maintainability; default off, opt-in via --diagnostics
|
|
42
43
|
// or config severity). These are NOT false-green: the test still protects. They
|
|
43
44
|
// are a "plus" for test-code health, mirroring falsegreen's D/M group. -------
|
|
@@ -46,6 +47,7 @@ export const CASES = {
|
|
|
46
47
|
D4: { title: "it.each/test.each without titled cases — a failing case is identified only by its index", confidence: "off", judgment: "J4" },
|
|
47
48
|
D6: { title: "console.* in a test body — a debug artifact that bypasses the oracle", confidence: "off", judgment: "J4" },
|
|
48
49
|
D7: { title: "anonymous test — empty or missing description", confidence: "off", judgment: "J4" },
|
|
50
|
+
D8: { title: "magic number in an assertion — a bare numeric literal instead of a named constant", confidence: "off", judgment: "J4" },
|
|
49
51
|
M2: { title: "test body exceeds the line-count threshold — hard to read and maintain", confidence: "off", judgment: "J5" },
|
|
50
52
|
};
|
|
51
53
|
/** Default thresholds for the diagnostic group (overridable later via config). */
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { JUDGMENTS, groupOf } from "./cases.js";
|
|
3
3
|
import { scanPaths, scanFile, stagedFiles, loadConfig, } from "./scan.js";
|
|
4
|
-
const VERSION = "0.
|
|
4
|
+
const VERSION = "0.2.0";
|
|
5
5
|
const TOOL_URI = "https://github.com/vinicq/falsegreen-js";
|
|
6
6
|
const HELP = `falsegreen-js ${VERSION} - find false-positive JS/TS tests (static AST scan)
|
|
7
7
|
|
package/dist/rules.js
CHANGED
|
@@ -377,6 +377,32 @@ export function analyze(sf) {
|
|
|
377
377
|
if ((chain.matcher === "toThrow" || chain.matcher === "toThrowError") && chain.args.length === 0) {
|
|
378
378
|
push(lineOf(sf, node), "C9", "toThrow() with no error type or message accepts any error");
|
|
379
379
|
}
|
|
380
|
+
// JS15 inappropriate assertion: the comparison is wrapped in a boolean, so
|
|
381
|
+
// the matcher only sees true/false (expect(a === b).toBe(true)). The failure
|
|
382
|
+
// message is blind ("expected false to be true") and the oracle is weak.
|
|
383
|
+
const COMPARISON_OPS = new Set([
|
|
384
|
+
ts.SyntaxKind.EqualsEqualsEqualsToken, ts.SyntaxKind.ExclamationEqualsEqualsToken,
|
|
385
|
+
ts.SyntaxKind.EqualsEqualsToken, ts.SyntaxKind.ExclamationEqualsToken,
|
|
386
|
+
ts.SyntaxKind.LessThanToken, ts.SyntaxKind.GreaterThanToken,
|
|
387
|
+
ts.SyntaxKind.LessThanEqualsToken, ts.SyntaxKind.GreaterThanEqualsToken,
|
|
388
|
+
]);
|
|
389
|
+
const subjIsComparison = subj !== undefined && ts.isBinaryExpression(subj) &&
|
|
390
|
+
COMPARISON_OPS.has(subj.operatorToken.kind);
|
|
391
|
+
const boolMatcher = ((chain.matcher === "toBe" || chain.matcher === "toEqual" || chain.matcher === "toStrictEqual") &&
|
|
392
|
+
arg !== undefined && (arg.kind === ts.SyntaxKind.TrueKeyword || arg.kind === ts.SyntaxKind.FalseKeyword)) ||
|
|
393
|
+
chain.matcher === "toBeTruthy" || chain.matcher === "toBeFalsy";
|
|
394
|
+
if (subjIsComparison && boolMatcher) {
|
|
395
|
+
push(lineOf(sf, node), "JS15", "comparison wrapped in a boolean; assert the values directly");
|
|
396
|
+
}
|
|
397
|
+
// D8 (diagnostic, opt-in): a magic integer literal as the expected value.
|
|
398
|
+
// Floats are C8's concern; D8 covers bare integers abs > 1.
|
|
399
|
+
if ((chain.matcher === "toBe" || chain.matcher === "toEqual" || chain.matcher === "toStrictEqual") &&
|
|
400
|
+
arg && ts.isNumericLiteral(arg)) {
|
|
401
|
+
const v = Number(arg.text);
|
|
402
|
+
if (Number.isInteger(v) && Math.abs(v) > 1) {
|
|
403
|
+
push(lineOf(sf, node), "D8", `magic number ${arg.text} in the assertion`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
380
406
|
// C5 always-true
|
|
381
407
|
if (chain.matcher === "toBeTruthy" && literalTruthiness(subj) === true) {
|
|
382
408
|
push(lineOf(sf, node), "C5", "toBeTruthy on a constant truthy literal");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "falsegreen-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Find JavaScript/TypeScript unit tests that give false positives: green tests that protect nothing, and tests that pass while asserting the wrong thing. Deterministic AST scanner, sibling of falsegreen (Python).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jest", "vitest", "mocha", "testing", "test-smells", "false-positive",
|