pi-lens 3.1.2 → 3.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.
Files changed (154) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +16 -12
  3. package/clients/ast-grep-client.js +8 -1
  4. package/clients/ast-grep-client.ts +9 -1
  5. package/clients/biome-client.js +51 -38
  6. package/clients/biome-client.ts +60 -58
  7. package/clients/dependency-checker.js +30 -1
  8. package/clients/dependency-checker.ts +35 -1
  9. package/clients/dispatch/__tests__/runner-registration.test.ts +286 -282
  10. package/clients/dispatch/bus-dispatcher.js +15 -14
  11. package/clients/dispatch/bus-dispatcher.ts +32 -25
  12. package/clients/dispatch/dispatcher.js +18 -25
  13. package/clients/dispatch/dispatcher.test.ts +2 -1
  14. package/clients/dispatch/dispatcher.ts +17 -28
  15. package/clients/dispatch/plan.js +77 -32
  16. package/clients/dispatch/plan.ts +78 -32
  17. package/clients/dispatch/runners/ast-grep-napi.js +36 -376
  18. package/clients/dispatch/runners/ast-grep-napi.ts +60 -433
  19. package/clients/dispatch/runners/index.js +8 -4
  20. package/clients/dispatch/runners/index.ts +8 -4
  21. package/clients/dispatch/runners/lsp.js +65 -0
  22. package/clients/dispatch/runners/lsp.ts +125 -0
  23. package/clients/dispatch/runners/oxlint.js +2 -2
  24. package/clients/dispatch/runners/oxlint.ts +2 -2
  25. package/clients/dispatch/runners/pyright.js +24 -8
  26. package/clients/dispatch/runners/pyright.ts +28 -14
  27. package/clients/dispatch/runners/rust-clippy.js +2 -2
  28. package/clients/dispatch/runners/rust-clippy.ts +2 -4
  29. package/clients/dispatch/runners/tree-sitter.js +14 -2
  30. package/clients/dispatch/runners/tree-sitter.ts +15 -2
  31. package/clients/dispatch/runners/ts-lsp.js +3 -3
  32. package/clients/dispatch/runners/ts-lsp.ts +8 -5
  33. package/clients/dispatch/runners/yaml-rule-parser.js +292 -0
  34. package/clients/dispatch/runners/yaml-rule-parser.ts +338 -0
  35. package/clients/dispatch/types.js +3 -0
  36. package/clients/dispatch/types.ts +3 -0
  37. package/clients/formatters.js +67 -14
  38. package/clients/formatters.ts +68 -15
  39. package/clients/installer/index.js +78 -10
  40. package/clients/installer/index.ts +519 -426
  41. package/clients/jscpd-client.js +28 -0
  42. package/clients/jscpd-client.ts +41 -3
  43. package/clients/knip-client.js +30 -1
  44. package/clients/knip-client.ts +34 -2
  45. package/clients/lsp/__tests__/client.test.ts +64 -41
  46. package/clients/lsp/__tests__/config.test.ts +25 -17
  47. package/clients/lsp/__tests__/launch.test.ts +108 -43
  48. package/clients/lsp/__tests__/service.test.ts +76 -48
  49. package/clients/lsp/client.js +87 -2
  50. package/clients/lsp/client.ts +150 -6
  51. package/clients/lsp/config.js +8 -11
  52. package/clients/lsp/config.ts +24 -21
  53. package/clients/lsp/index.js +69 -0
  54. package/clients/lsp/index.ts +82 -0
  55. package/clients/lsp/interactive-install.js +19 -8
  56. package/clients/lsp/interactive-install.ts +52 -27
  57. package/clients/lsp/launch.js +182 -32
  58. package/clients/lsp/launch.ts +241 -38
  59. package/clients/lsp/path-utils.js +3 -46
  60. package/clients/lsp/path-utils.ts +11 -51
  61. package/clients/lsp/server.js +93 -71
  62. package/clients/lsp/server.ts +173 -131
  63. package/clients/path-utils.js +142 -0
  64. package/clients/path-utils.ts +153 -0
  65. package/clients/ruff-client.js +33 -4
  66. package/clients/ruff-client.ts +44 -13
  67. package/clients/safe-spawn.js +3 -1
  68. package/clients/safe-spawn.ts +3 -1
  69. package/clients/services/effect-integration.js +11 -7
  70. package/clients/services/effect-integration.ts +34 -26
  71. package/clients/sg-runner.js +51 -9
  72. package/clients/sg-runner.ts +58 -15
  73. package/clients/tree-sitter-client.js +12 -0
  74. package/clients/tree-sitter-client.ts +12 -0
  75. package/clients/typescript-client.js +6 -2
  76. package/clients/typescript-client.ts +9 -2
  77. package/commands/booboo.js +2 -4
  78. package/commands/booboo.ts +2 -4
  79. package/index.ts +377 -93
  80. package/package.json +2 -1
  81. package/rules/tree-sitter-queries/tsx/no-nested-links.yml +45 -0
  82. package/rules/tree-sitter-queries/typescript/constructor-super.yml +55 -0
  83. package/rules/tree-sitter-queries/typescript/debugger.yml +1 -1
  84. package/rules/tree-sitter-queries/typescript/no-dupe-class-members.yml +47 -0
  85. package/tsconfig.json +1 -1
  86. package/clients/__tests__/file-time.test.js +0 -216
  87. package/clients/__tests__/format-service.test.js +0 -245
  88. package/clients/__tests__/formatters.test.js +0 -271
  89. package/clients/agent-behavior-client.test.js +0 -94
  90. package/clients/ast-grep-client.test.js +0 -129
  91. package/clients/ast-grep-client.test.ts +0 -155
  92. package/clients/biome-client.test.js +0 -144
  93. package/clients/cache-manager.test.js +0 -197
  94. package/clients/complexity-client.test.js +0 -234
  95. package/clients/dependency-checker.test.js +0 -60
  96. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  97. package/clients/dispatch/__tests__/runner-registration.test.js +0 -236
  98. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  99. package/clients/dispatch/dispatcher.format.test.js +0 -46
  100. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  101. package/clients/dispatch/dispatcher.test.js +0 -115
  102. package/clients/dispatch/runners/architect.test.js +0 -138
  103. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -106
  104. package/clients/dispatch/runners/oxlint.test.js +0 -230
  105. package/clients/dispatch/runners/pyright.test.js +0 -98
  106. package/clients/dispatch/runners/python-slop.test.js +0 -203
  107. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  108. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  109. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  110. package/clients/dispatch/runners/ts-slop.test.js +0 -180
  111. package/clients/dispatch/runners/ts-slop.test.ts +0 -230
  112. package/clients/dogfood.test.js +0 -201
  113. package/clients/file-kinds.test.js +0 -169
  114. package/clients/go-client.test.js +0 -127
  115. package/clients/jscpd-client.test.js +0 -127
  116. package/clients/knip-client.test.js +0 -112
  117. package/clients/lsp/__tests__/client.test.js +0 -325
  118. package/clients/lsp/__tests__/config.test.js +0 -166
  119. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  120. package/clients/lsp/__tests__/integration.test.js +0 -127
  121. package/clients/lsp/__tests__/launch.test.js +0 -260
  122. package/clients/lsp/__tests__/server.test.js +0 -259
  123. package/clients/lsp/__tests__/service.test.js +0 -417
  124. package/clients/metrics-client.test.js +0 -141
  125. package/clients/ruff-client.test.js +0 -132
  126. package/clients/rust-client.test.js +0 -108
  127. package/clients/sanitize.test.js +0 -177
  128. package/clients/secrets-scanner.test.js +0 -100
  129. package/clients/services/__tests__/effect-integration.test.js +0 -86
  130. package/clients/test-runner-client.test.js +0 -192
  131. package/clients/todo-scanner.test.js +0 -301
  132. package/clients/type-coverage-client.test.js +0 -105
  133. package/clients/typescript-client.codefix.test.js +0 -157
  134. package/clients/typescript-client.test.js +0 -105
  135. package/commands/clients/ast-grep-client.js +0 -250
  136. package/commands/clients/ast-grep-parser.js +0 -86
  137. package/commands/clients/ast-grep-rule-manager.js +0 -91
  138. package/commands/clients/ast-grep-types.js +0 -9
  139. package/commands/clients/biome-client.js +0 -380
  140. package/commands/clients/complexity-client.js +0 -667
  141. package/commands/clients/file-kinds.js +0 -177
  142. package/commands/clients/file-utils.js +0 -40
  143. package/commands/clients/jscpd-client.js +0 -169
  144. package/commands/clients/knip-client.js +0 -211
  145. package/commands/clients/ruff-client.js +0 -297
  146. package/commands/clients/safe-spawn.js +0 -88
  147. package/commands/clients/scan-utils.js +0 -83
  148. package/commands/clients/sg-runner.js +0 -190
  149. package/commands/clients/types.js +0 -11
  150. package/commands/clients/typescript-client.js +0 -505
  151. package/commands/rate.test.js +0 -119
  152. package/rules/ast-grep-rules/rules/no-dangerously-set-inner-html.yml +0 -13
  153. package/rules/ast-grep-rules/rules/no-debugger.yml +0 -12
  154. package/rules/ast-grep-rules/rules/no-eval.yml +0 -13
@@ -1,86 +0,0 @@
1
- /**
2
- * Effect Integration Tests
3
- *
4
- * Tests for Effect-TS concurrent runner execution.
5
- */
6
- import { beforeEach, describe, expect, it, vi } from "vitest";
7
- import { clearRunnerRegistry, listRunners, registerRunner, } from "../../dispatch/dispatcher.js";
8
- import { dispatchLintWithEffect, dispatchWithEffect, } from "../effect-integration.js";
9
- describe("Effect Integration", () => {
10
- beforeEach(async () => {
11
- clearRunnerRegistry();
12
- // Register a simple test runner
13
- const testRunner = {
14
- id: "test-runner",
15
- appliesTo: ["jsts"],
16
- priority: 10,
17
- enabledByDefault: true,
18
- async run(ctx) {
19
- return {
20
- status: "succeeded",
21
- diagnostics: [{
22
- id: "test:1",
23
- message: "Test diagnostic",
24
- filePath: ctx.filePath,
25
- severity: "info",
26
- semantic: "silent",
27
- tool: "test-runner",
28
- }],
29
- semantic: "none",
30
- };
31
- },
32
- };
33
- registerRunner(testRunner);
34
- });
35
- it("should have runners registered", () => {
36
- const runners = listRunners();
37
- expect(runners.length).toBeGreaterThan(0);
38
- expect(runners.some(r => r.id === "test-runner")).toBe(true);
39
- });
40
- it("should run effect integration with real runners", async () => {
41
- const ctx = {
42
- filePath: "test.ts",
43
- cwd: "/test",
44
- kind: "jsts",
45
- pi: {
46
- getFlag: vi.fn(() => false),
47
- },
48
- autofix: true,
49
- deltaMode: false,
50
- baselines: new Map(),
51
- hasTool: vi.fn(() => Promise.resolve(false)),
52
- log: vi.fn(),
53
- };
54
- // Get actual runners for jsts
55
- const { getRunnersForKind } = await import("../../dispatch/dispatcher.js");
56
- const runners = getRunnersForKind("jsts");
57
- const group = {
58
- runnerIds: runners.slice(0, 2).map(r => r.id), // Use first 2 runners
59
- mode: "all",
60
- };
61
- const result = await dispatchWithEffect(ctx, [group]);
62
- // Just verify it doesn't crash and returns valid result
63
- expect(result).toBeDefined();
64
- expect(typeof result.durationMs).toBe("number");
65
- });
66
- it("should handle --lens-effect flag path", async () => {
67
- const mockPi = {
68
- getFlag: vi.fn((flag) => flag === "lens-effect"),
69
- readFile: vi.fn(),
70
- writeFile: vi.fn(),
71
- editFile: vi.fn(),
72
- bash: vi.fn(),
73
- ui: {
74
- notify: vi.fn(),
75
- progress: vi.fn(),
76
- prompt: vi.fn(),
77
- },
78
- llm: {
79
- stream: vi.fn(),
80
- createMessage: vi.fn(),
81
- },
82
- };
83
- const output = await dispatchLintWithEffect("test.ts", "/test", mockPi);
84
- expect(typeof output).toBe("string");
85
- });
86
- });
@@ -1,192 +0,0 @@
1
- import * as path from "node:path";
2
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
3
- import { TestRunnerClient } from "./test-runner-client.js";
4
- import { createTempFile, setupTestEnvironment } from "./test-utils.js";
5
- describe("TestRunnerClient", () => {
6
- let client;
7
- let tmpDir;
8
- let cleanup;
9
- beforeEach(() => {
10
- client = new TestRunnerClient();
11
- ({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-test-runner-"));
12
- });
13
- afterEach(() => {
14
- cleanup();
15
- });
16
- afterEach(() => {
17
- cleanup();
18
- });
19
- describe("detectRunner", () => {
20
- it("should detect vitest from config file", () => {
21
- createTempFile(tmpDir, "vitest.config.ts", "export default {}");
22
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
23
- const result = client.detectRunner(tmpDir);
24
- expect(result).not.toBeNull();
25
- expect(result?.runner).toBe("vitest");
26
- });
27
- it("should detect jest from config file", () => {
28
- createTempFile(tmpDir, "jest.config.js", "module.exports = {}");
29
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
30
- const result = client.detectRunner(tmpDir);
31
- expect(result).not.toBeNull();
32
- expect(result?.runner).toBe("jest");
33
- });
34
- it("should detect pytest from config file", () => {
35
- createTempFile(tmpDir, "pytest.ini", "[tool:pytest]");
36
- createTempFile(tmpDir, "src/app.py", "x = 1");
37
- const result = client.detectRunner(tmpDir);
38
- expect(result).not.toBeNull();
39
- expect(result?.runner).toBe("pytest");
40
- });
41
- it("should detect runner from node_modules", () => {
42
- // Create a node_modules/vitest to simulate installed package
43
- createTempFile(tmpDir, "node_modules/vitest/package.json", "{}");
44
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
45
- const result = client.detectRunner(tmpDir);
46
- // Should detect vitest from node_modules
47
- expect(result).not.toBeNull();
48
- });
49
- it("should prefer vitest over jest when both exist", () => {
50
- createTempFile(tmpDir, "vitest.config.ts", "export default {}");
51
- createTempFile(tmpDir, "jest.config.js", "module.exports = {}");
52
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
53
- const result = client.detectRunner(tmpDir);
54
- expect(result?.runner).toBe("vitest");
55
- });
56
- });
57
- describe("findTestFile", () => {
58
- it("should find test file with .test.ts suffix", () => {
59
- createTempFile(tmpDir, "vitest.config.ts", "export default {}");
60
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
61
- createTempFile(tmpDir, "src/app.test.ts", "describe('app', () => {});");
62
- const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
63
- expect(result).not.toBeNull();
64
- expect(result?.testFile).toContain("app.test.ts");
65
- expect(result?.runner).toBe("vitest");
66
- });
67
- it("should find test file with .spec.ts suffix", () => {
68
- createTempFile(tmpDir, "vitest.config.ts", "export default {}");
69
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
70
- createTempFile(tmpDir, "src/app.spec.ts", "describe('app', () => {});");
71
- const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
72
- expect(result).not.toBeNull();
73
- expect(result?.testFile).toContain("app.spec.ts");
74
- });
75
- it("should find test file in __tests__ directory", () => {
76
- createTempFile(tmpDir, "vitest.config.ts", "export default {}");
77
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
78
- createTempFile(tmpDir, "src/__tests__/app.test.ts", "describe('app', () => {});");
79
- const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
80
- expect(result).not.toBeNull();
81
- expect(result?.testFile).toContain("__tests__");
82
- });
83
- it("should find test file in top-level tests/ directory", () => {
84
- createTempFile(tmpDir, "vitest.config.ts", "export default {}");
85
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
86
- createTempFile(tmpDir, "tests/app.test.ts", "describe('app', () => {});");
87
- const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
88
- expect(result).not.toBeNull();
89
- expect(result?.testFile).toContain(path.join("tests", "app.test.ts"));
90
- });
91
- it("should find pytest test file with test_ prefix", () => {
92
- createTempFile(tmpDir, "pytest.ini", "[tool:pytest]");
93
- createTempFile(tmpDir, "src/app.py", "x = 1");
94
- createTempFile(tmpDir, "tests/test_app.py", "def test_app(): pass");
95
- const result = client.findTestFile(path.join(tmpDir, "src/app.py"), tmpDir);
96
- expect(result).not.toBeNull();
97
- expect(result?.testFile).toContain("test_app.py");
98
- expect(result?.runner).toBe("pytest");
99
- });
100
- it("should return null when no test file found", () => {
101
- createTempFile(tmpDir, "vitest.config.ts", "export default {}");
102
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
103
- const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
104
- expect(result).toBeNull();
105
- });
106
- it("should find test file even without config (if runner installed)", () => {
107
- // Simulate vitest installed in node_modules
108
- createTempFile(tmpDir, "node_modules/vitest/package.json", "{}");
109
- createTempFile(tmpDir, "src/app.ts", "export const app = {};");
110
- createTempFile(tmpDir, "src/app.test.ts", "describe('app', () => {});");
111
- const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
112
- // Should find the test file since vitest is "installed"
113
- expect(result).not.toBeNull();
114
- });
115
- });
116
- describe("formatResult", () => {
117
- it("should format passing tests", () => {
118
- const result = {
119
- file: "/test/app.test.ts",
120
- sourceFile: "/test/app.ts",
121
- runner: "vitest",
122
- passed: 5,
123
- failed: 0,
124
- skipped: 0,
125
- failures: [],
126
- duration: 420,
127
- };
128
- const formatted = client.formatResult(result);
129
- expect(formatted).toContain("✓");
130
- expect(formatted).toContain("5/5 passed");
131
- expect(formatted).toContain("0.42s");
132
- });
133
- it("should format failing tests", () => {
134
- const result = {
135
- file: "/test/app.test.ts",
136
- sourceFile: "/test/app.ts",
137
- runner: "vitest",
138
- passed: 3,
139
- failed: 2,
140
- skipped: 0,
141
- failures: [
142
- {
143
- name: "should add",
144
- message: "expected 4, got 3",
145
- location: "app.test.ts:10",
146
- },
147
- {
148
- name: "should subtract",
149
- message: "expected 1, got 2",
150
- location: "app.test.ts:20",
151
- },
152
- ],
153
- duration: 420,
154
- };
155
- const formatted = client.formatResult(result);
156
- expect(formatted).toContain("✗");
157
- expect(formatted).toContain("2/5 failed");
158
- expect(formatted).toContain("should add");
159
- expect(formatted).toContain("should subtract");
160
- });
161
- it("should format runner errors", () => {
162
- const result = {
163
- file: "/test/app.test.ts",
164
- sourceFile: "/test/app.ts",
165
- runner: "vitest",
166
- passed: 0,
167
- failed: 0,
168
- skipped: 0,
169
- failures: [],
170
- duration: 0,
171
- error: "Test file not found",
172
- };
173
- const formatted = client.formatResult(result);
174
- expect(formatted).toContain("⚠");
175
- expect(formatted).toContain("Could not run tests");
176
- });
177
- it("should return empty string for no tests", () => {
178
- const result = {
179
- file: "/test/app.test.ts",
180
- sourceFile: "/test/app.ts",
181
- runner: "vitest",
182
- passed: 0,
183
- failed: 0,
184
- skipped: 0,
185
- failures: [],
186
- duration: 0,
187
- };
188
- const formatted = client.formatResult(result);
189
- expect(formatted).toBe("");
190
- });
191
- });
192
- });
@@ -1,301 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
- import { createTempFile, setupTestEnvironment } from "./test-utils.js";
3
- import { TodoScanner } from "./todo-scanner.js";
4
- describe("TodoScanner", () => {
5
- let client;
6
- let tmpDir;
7
- let cleanup;
8
- beforeEach(() => {
9
- client = new TodoScanner();
10
- ({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-todo-test-"));
11
- });
12
- afterEach(() => {
13
- cleanup();
14
- });
15
- describe("scanFile", () => {
16
- it("should return empty array for non-existent files", () => {
17
- const result = client.scanFile("/nonexistent/file.ts");
18
- expect(result).toEqual([]);
19
- });
20
- it("should find TODO comments", () => {
21
- const content = `
22
- // TODO: implement this function
23
- function foo() {}
24
- `;
25
- const filePath = createTempFile(tmpDir, "test.ts", content);
26
- const result = client.scanFile(filePath);
27
- expect(result.length).toBe(1);
28
- expect(result[0].type).toBe("TODO");
29
- expect(result[0].message).toContain("implement this function");
30
- });
31
- it("should find FIXME comments", () => {
32
- const content = `
33
- // FIXME: this is broken
34
- function foo() {}
35
- `;
36
- const filePath = createTempFile(tmpDir, "test.ts", content);
37
- const result = client.scanFile(filePath);
38
- expect(result.length).toBe(1);
39
- expect(result[0].type).toBe("FIXME");
40
- });
41
- it("should find HACK comments", () => {
42
- const content = `
43
- // HACK: temporary workaround
44
- const x = 1;
45
- `;
46
- const filePath = createTempFile(tmpDir, "test.ts", content);
47
- const result = client.scanFile(filePath);
48
- expect(result.length).toBe(1);
49
- expect(result[0].type).toBe("HACK");
50
- });
51
- it("should find BUG comments", () => {
52
- const content = `
53
- // BUG: this causes a crash
54
- const x = 1;
55
- `;
56
- const filePath = createTempFile(tmpDir, "test.ts", content);
57
- const result = client.scanFile(filePath);
58
- expect(result.length).toBe(1);
59
- expect(result[0].type).toBe("BUG");
60
- });
61
- it("should find NOTE comments", () => {
62
- const content = `
63
- // NOTE: important design decision
64
- const x = 1;
65
- `;
66
- const filePath = createTempFile(tmpDir, "test.ts", content);
67
- const result = client.scanFile(filePath);
68
- expect(result.length).toBe(1);
69
- expect(result[0].type).toBe("NOTE");
70
- });
71
- it("should find TODO in block comments", () => {
72
- const content = `
73
- /*
74
- * TODO: refactor this later
75
- */
76
- const x = 1;
77
- `;
78
- const filePath = createTempFile(tmpDir, "test.ts", content);
79
- const result = client.scanFile(filePath);
80
- expect(result.length).toBe(1);
81
- expect(result[0].type).toBe("TODO");
82
- });
83
- it("should find TODO in JSDoc comments", () => {
84
- const content = `
85
- /**
86
- * TODO: add proper documentation
87
- */
88
- function foo() {}
89
- `;
90
- const filePath = createTempFile(tmpDir, "test.ts", content);
91
- const result = client.scanFile(filePath);
92
- expect(result.length).toBe(1);
93
- expect(result[0].type).toBe("TODO");
94
- });
95
- it("should find TODO in Python comments", () => {
96
- const content = `
97
- # TODO: implement this
98
- def foo():
99
- pass
100
- `;
101
- const filePath = createTempFile(tmpDir, "test.py", content);
102
- const result = client.scanFile(filePath);
103
- expect(result.length).toBe(1);
104
- expect(result[0].type).toBe("TODO");
105
- });
106
- it("should skip TODO in strings", () => {
107
- const content = `
108
- const message = "TODO: buy milk";
109
- `;
110
- const filePath = createTempFile(tmpDir, "test.ts", content);
111
- const result = client.scanFile(filePath);
112
- expect(result.length).toBe(0);
113
- });
114
- it("should report correct line numbers", () => {
115
- const content = `
116
- const x = 1;
117
- const y = 2;
118
- // TODO: fix this
119
- const z = 3;
120
- `;
121
- const filePath = createTempFile(tmpDir, "test.ts", content);
122
- const result = client.scanFile(filePath);
123
- expect(result[0].line).toBe(4);
124
- });
125
- it("should find multiple annotations in one file", () => {
126
- const content = `
127
- // TODO: first
128
- // FIXME: second
129
- // HACK: third
130
- `;
131
- const filePath = createTempFile(tmpDir, "test.ts", content);
132
- const result = client.scanFile(filePath);
133
- expect(result.length).toBe(3);
134
- });
135
- });
136
- describe("scanDirectory", () => {
137
- it("should scan all files in directory", () => {
138
- createTempFile(tmpDir, "file1.ts", "// TODO: task 1");
139
- createTempFile(tmpDir, "file2.ts", "// FIXME: bug 1");
140
- createTempFile(tmpDir, "file3.py", "# HACK: workaround");
141
- const result = client.scanDirectory(tmpDir);
142
- expect(result.items.length).toBe(3);
143
- });
144
- it("should skip node_modules", () => {
145
- createTempFile(tmpDir, "src/file.ts", "// TODO: task 1");
146
- createTempFile(tmpDir, "node_modules/lib/file.ts", "// TODO: should be skipped");
147
- const result = client.scanDirectory(tmpDir);
148
- expect(result.items.length).toBe(1);
149
- expect(result.items[0].file).not.toContain("node_modules");
150
- });
151
- it("should group items by type", () => {
152
- createTempFile(tmpDir, "file1.ts", "// TODO: task 1");
153
- createTempFile(tmpDir, "file2.ts", "// TODO: task 2");
154
- createTempFile(tmpDir, "file3.ts", "// FIXME: bug 1");
155
- const result = client.scanDirectory(tmpDir);
156
- expect(result.byType.get("TODO")?.length).toBe(2);
157
- expect(result.byType.get("FIXME")?.length).toBe(1);
158
- });
159
- it("should group items by file", () => {
160
- createTempFile(tmpDir, "file1.ts", "// TODO: task 1\n// FIXME: bug 1");
161
- createTempFile(tmpDir, "file2.ts", "// TODO: task 2");
162
- const result = client.scanDirectory(tmpDir);
163
- const file1Items = [...result.byFile.entries()].find(([k]) => k.includes("file1.ts"));
164
- expect(file1Items?.[1].length).toBe(2);
165
- });
166
- });
167
- describe("formatResult", () => {
168
- it("should return empty string for no results", () => {
169
- const result = { items: [], byType: new Map(), byFile: new Map() };
170
- expect(client.formatResult(result)).toBe("");
171
- });
172
- it("should format results with counts", () => {
173
- const result = {
174
- items: [
175
- {
176
- type: "TODO",
177
- message: "task 1",
178
- file: "test.ts",
179
- line: 1,
180
- column: 0,
181
- },
182
- {
183
- type: "FIXME",
184
- message: "bug 1",
185
- file: "test.ts",
186
- line: 2,
187
- column: 0,
188
- },
189
- ],
190
- byType: new Map([
191
- [
192
- "TODO",
193
- [
194
- {
195
- type: "TODO",
196
- message: "task 1",
197
- file: "test.ts",
198
- line: 1,
199
- column: 0,
200
- },
201
- ],
202
- ],
203
- ]),
204
- byFile: new Map([
205
- [
206
- "test.ts",
207
- [
208
- {
209
- type: "TODO",
210
- message: "task 1",
211
- file: "test.ts",
212
- line: 1,
213
- column: 0,
214
- },
215
- ],
216
- ],
217
- ]),
218
- };
219
- const formatted = client.formatResult(result);
220
- expect(formatted).toContain("2 annotation(s)");
221
- expect(formatted).toContain("TODO");
222
- expect(formatted).toContain("FIXME");
223
- });
224
- it("should prioritize FIXME/HACK before TODO", () => {
225
- const result = {
226
- items: [
227
- {
228
- type: "FIXME",
229
- message: "bug",
230
- file: "test.ts",
231
- line: 2,
232
- column: 0,
233
- },
234
- {
235
- type: "TODO",
236
- message: "task",
237
- file: "test.ts",
238
- line: 1,
239
- column: 0,
240
- },
241
- ],
242
- byType: new Map(),
243
- byFile: new Map(),
244
- };
245
- const formatted = client.formatResult(result);
246
- // Check that FIXME line comes before TODO line in the sorted output
247
- const fixmeLineIndex = formatted.indexOf("🔴");
248
- const todoLineIndex = formatted.indexOf("📝");
249
- expect(fixmeLineIndex).toBeLessThan(todoLineIndex);
250
- });
251
- it("should show correct icons", () => {
252
- const result = {
253
- items: [
254
- {
255
- type: "FIXME",
256
- message: "bug",
257
- file: "test.ts",
258
- line: 1,
259
- column: 0,
260
- },
261
- {
262
- type: "HACK",
263
- message: "hack",
264
- file: "test.ts",
265
- line: 2,
266
- column: 0,
267
- },
268
- {
269
- type: "BUG",
270
- message: "bug",
271
- file: "test.ts",
272
- line: 3,
273
- column: 0,
274
- },
275
- {
276
- type: "TODO",
277
- message: "todo",
278
- file: "test.ts",
279
- line: 4,
280
- column: 0,
281
- },
282
- {
283
- type: "NOTE",
284
- message: "note",
285
- file: "test.ts",
286
- line: 5,
287
- column: 0,
288
- },
289
- ],
290
- byType: new Map(),
291
- byFile: new Map(),
292
- };
293
- const formatted = client.formatResult(result);
294
- expect(formatted).toContain("🔴"); // FIXME
295
- expect(formatted).toContain("🟠"); // HACK
296
- expect(formatted).toContain("🐛"); // BUG
297
- expect(formatted).toContain("📝"); // TODO
298
- expect(formatted).toContain("â„šī¸"); // NOTE
299
- });
300
- });
301
- });
@@ -1,105 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
- import { setupTestEnvironment } from "./test-utils.js";
3
- import { TypeCoverageClient } from "./type-coverage-client.js";
4
- describe("TypeCoverageClient", () => {
5
- let client;
6
- let _tmpDir;
7
- let cleanup;
8
- beforeEach(() => {
9
- client = new TypeCoverageClient();
10
- ({ tmpDir: _tmpDir, cleanup } = setupTestEnvironment("pi-lens-typecoverage-test-"));
11
- });
12
- afterEach(() => {
13
- cleanup();
14
- });
15
- afterEach(() => {
16
- cleanup();
17
- });
18
- describe("isAvailable", () => {
19
- it("should check type-coverage availability", () => {
20
- const available = client.isAvailable();
21
- expect(typeof available).toBe("boolean");
22
- });
23
- });
24
- describe("formatResult", () => {
25
- it("should return empty string when not successful", () => {
26
- const result = {
27
- success: false,
28
- percentage: 0,
29
- typed: 0,
30
- total: 0,
31
- untypedLocations: [],
32
- };
33
- expect(client.formatResult(result)).toBe("");
34
- });
35
- it("should show coverage percentage", () => {
36
- const result = {
37
- success: true,
38
- percentage: 95,
39
- typed: 95,
40
- total: 100,
41
- untypedLocations: [],
42
- };
43
- const formatted = client.formatResult(result);
44
- expect(formatted).toContain("95.0%");
45
- expect(formatted).toContain("95/100");
46
- });
47
- it("should show warning for low coverage", () => {
48
- const result = {
49
- success: true,
50
- percentage: 80,
51
- typed: 80,
52
- total: 100,
53
- untypedLocations: [],
54
- };
55
- const formatted = client.formatResult(result);
56
- expect(formatted).toContain("⚠");
57
- });
58
- it("should show checkmark for high coverage", () => {
59
- const result = {
60
- success: true,
61
- percentage: 100,
62
- typed: 100,
63
- total: 100,
64
- untypedLocations: [],
65
- };
66
- const formatted = client.formatResult(result);
67
- expect(formatted).toContain("✓");
68
- });
69
- it("should show untyped locations", () => {
70
- const result = {
71
- success: true,
72
- percentage: 90,
73
- typed: 90,
74
- total: 100,
75
- untypedLocations: [
76
- { file: "test.ts", line: 10, column: 5, name: "x" },
77
- { file: "test.ts", line: 20, column: 8, name: "y" },
78
- ],
79
- };
80
- const formatted = client.formatResult(result);
81
- expect(formatted).toContain("test.ts:10");
82
- expect(formatted).toContain("test.ts:20");
83
- expect(formatted).toContain("x");
84
- expect(formatted).toContain("y");
85
- });
86
- it("should truncate long untyped location lists", () => {
87
- const locations = Array.from({ length: 20 }, (_, i) => ({
88
- file: `file${i}.ts`,
89
- line: i + 1,
90
- column: 0,
91
- name: `var${i}`,
92
- }));
93
- const result = {
94
- success: true,
95
- percentage: 80,
96
- typed: 80,
97
- total: 100,
98
- untypedLocations: locations,
99
- };
100
- const formatted = client.formatResult(result, 10);
101
- expect(formatted).toContain("...");
102
- expect(formatted).toContain("10 more");
103
- });
104
- });
105
- });