pi-lens 1.3.13 → 2.0.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 +27 -1
- package/README.md +19 -13
- package/clients/ast-grep-client.test.ts +8 -21
- package/clients/ast-grep-client.ts +2 -4
- package/clients/biome-client.test.ts +10 -17
- package/clients/complexity-client.test.ts +41 -44
- package/clients/complexity-client.ts +106 -0
- package/clients/dependency-checker.test.ts +10 -23
- package/clients/go-client.test.ts +8 -5
- package/clients/jscpd-client.test.ts +10 -19
- package/clients/jscpd-client.ts +1 -1
- package/clients/knip-client.test.ts +8 -7
- package/clients/metrics-client.test.ts +16 -19
- package/clients/ruff-client.test.ts +12 -17
- package/clients/rust-client.test.ts +8 -5
- package/clients/subprocess-client.ts +158 -0
- package/clients/test-runner-client.test.ts +39 -47
- package/clients/test-utils.ts +31 -0
- package/clients/todo-scanner.test.ts +47 -60
- package/clients/todo-scanner.ts +2 -0
- package/clients/type-coverage-client.test.ts +8 -7
- package/clients/typescript-client.test.ts +12 -21
- package/index.ts +501 -59
- package/package.json +2 -3
|
@@ -1,38 +1,30 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
2
|
import { TestRunnerClient } from "./test-runner-client.js";
|
|
3
|
-
import
|
|
3
|
+
import { createTempFile, setupTestEnvironment } from "./test-utils.js";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
import * as os from "node:os";
|
|
6
5
|
|
|
7
6
|
describe("TestRunnerClient", () => {
|
|
8
7
|
let client: TestRunnerClient;
|
|
9
8
|
let tmpDir: string;
|
|
10
|
-
|
|
11
|
-
function createTempFile(name: string, content: string): string {
|
|
12
|
-
const filePath = path.join(tmpDir, name);
|
|
13
|
-
const dir = path.dirname(filePath);
|
|
14
|
-
if (!fs.existsSync(dir)) {
|
|
15
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
-
}
|
|
17
|
-
fs.writeFileSync(filePath, content);
|
|
18
|
-
return filePath;
|
|
19
|
-
}
|
|
9
|
+
let cleanup: () => void;
|
|
20
10
|
|
|
21
11
|
beforeEach(() => {
|
|
22
12
|
client = new TestRunnerClient();
|
|
23
|
-
tmpDir =
|
|
13
|
+
({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-test-runner-"));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
cleanup();
|
|
24
18
|
});
|
|
25
19
|
|
|
26
20
|
afterEach(() => {
|
|
27
|
-
|
|
28
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
29
|
-
}
|
|
21
|
+
cleanup();
|
|
30
22
|
});
|
|
31
23
|
|
|
32
24
|
describe("detectRunner", () => {
|
|
33
25
|
it("should detect vitest from config file", () => {
|
|
34
|
-
createTempFile("vitest.config.ts", "export default {}");
|
|
35
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
26
|
+
createTempFile(tmpDir, "vitest.config.ts", "export default {}");
|
|
27
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
36
28
|
|
|
37
29
|
const result = client.detectRunner(tmpDir);
|
|
38
30
|
expect(result).not.toBeNull();
|
|
@@ -40,8 +32,8 @@ describe("TestRunnerClient", () => {
|
|
|
40
32
|
});
|
|
41
33
|
|
|
42
34
|
it("should detect jest from config file", () => {
|
|
43
|
-
createTempFile("jest.config.js", "module.exports = {}");
|
|
44
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
35
|
+
createTempFile(tmpDir, "jest.config.js", "module.exports = {}");
|
|
36
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
45
37
|
|
|
46
38
|
const result = client.detectRunner(tmpDir);
|
|
47
39
|
expect(result).not.toBeNull();
|
|
@@ -49,8 +41,8 @@ describe("TestRunnerClient", () => {
|
|
|
49
41
|
});
|
|
50
42
|
|
|
51
43
|
it("should detect pytest from config file", () => {
|
|
52
|
-
createTempFile("pytest.ini", "[tool:pytest]");
|
|
53
|
-
createTempFile("src/app.py", "x = 1");
|
|
44
|
+
createTempFile(tmpDir, "pytest.ini", "[tool:pytest]");
|
|
45
|
+
createTempFile(tmpDir, "src/app.py", "x = 1");
|
|
54
46
|
|
|
55
47
|
const result = client.detectRunner(tmpDir);
|
|
56
48
|
expect(result).not.toBeNull();
|
|
@@ -59,8 +51,8 @@ describe("TestRunnerClient", () => {
|
|
|
59
51
|
|
|
60
52
|
it("should detect runner from node_modules", () => {
|
|
61
53
|
// Create a node_modules/vitest to simulate installed package
|
|
62
|
-
createTempFile("node_modules/vitest/package.json", "{}");
|
|
63
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
54
|
+
createTempFile(tmpDir, "node_modules/vitest/package.json", "{}");
|
|
55
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
64
56
|
|
|
65
57
|
const result = client.detectRunner(tmpDir);
|
|
66
58
|
// Should detect vitest from node_modules
|
|
@@ -68,9 +60,9 @@ describe("TestRunnerClient", () => {
|
|
|
68
60
|
});
|
|
69
61
|
|
|
70
62
|
it("should prefer vitest over jest when both exist", () => {
|
|
71
|
-
createTempFile("vitest.config.ts", "export default {}");
|
|
72
|
-
createTempFile("jest.config.js", "module.exports = {}");
|
|
73
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
63
|
+
createTempFile(tmpDir, "vitest.config.ts", "export default {}");
|
|
64
|
+
createTempFile(tmpDir, "jest.config.js", "module.exports = {}");
|
|
65
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
74
66
|
|
|
75
67
|
const result = client.detectRunner(tmpDir);
|
|
76
68
|
expect(result!.runner).toBe("vitest");
|
|
@@ -79,9 +71,9 @@ describe("TestRunnerClient", () => {
|
|
|
79
71
|
|
|
80
72
|
describe("findTestFile", () => {
|
|
81
73
|
it("should find test file with .test.ts suffix", () => {
|
|
82
|
-
createTempFile("vitest.config.ts", "export default {}");
|
|
83
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
84
|
-
createTempFile("src/app.test.ts", "describe('app', () => {});");
|
|
74
|
+
createTempFile(tmpDir, "vitest.config.ts", "export default {}");
|
|
75
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
76
|
+
createTempFile(tmpDir, "src/app.test.ts", "describe('app', () => {});");
|
|
85
77
|
|
|
86
78
|
const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
|
|
87
79
|
expect(result).not.toBeNull();
|
|
@@ -90,9 +82,9 @@ describe("TestRunnerClient", () => {
|
|
|
90
82
|
});
|
|
91
83
|
|
|
92
84
|
it("should find test file with .spec.ts suffix", () => {
|
|
93
|
-
createTempFile("vitest.config.ts", "export default {}");
|
|
94
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
95
|
-
createTempFile("src/app.spec.ts", "describe('app', () => {});");
|
|
85
|
+
createTempFile(tmpDir, "vitest.config.ts", "export default {}");
|
|
86
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
87
|
+
createTempFile(tmpDir, "src/app.spec.ts", "describe('app', () => {});");
|
|
96
88
|
|
|
97
89
|
const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
|
|
98
90
|
expect(result).not.toBeNull();
|
|
@@ -100,9 +92,9 @@ describe("TestRunnerClient", () => {
|
|
|
100
92
|
});
|
|
101
93
|
|
|
102
94
|
it("should find test file in __tests__ directory", () => {
|
|
103
|
-
createTempFile("vitest.config.ts", "export default {}");
|
|
104
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
105
|
-
createTempFile("src/__tests__/app.test.ts", "describe('app', () => {});");
|
|
95
|
+
createTempFile(tmpDir, "vitest.config.ts", "export default {}");
|
|
96
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
97
|
+
createTempFile(tmpDir, "src/__tests__/app.test.ts", "describe('app', () => {});");
|
|
106
98
|
|
|
107
99
|
const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
|
|
108
100
|
expect(result).not.toBeNull();
|
|
@@ -110,9 +102,9 @@ describe("TestRunnerClient", () => {
|
|
|
110
102
|
});
|
|
111
103
|
|
|
112
104
|
it("should find test file in top-level tests/ directory", () => {
|
|
113
|
-
createTempFile("vitest.config.ts", "export default {}");
|
|
114
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
115
|
-
createTempFile("tests/app.test.ts", "describe('app', () => {});");
|
|
105
|
+
createTempFile(tmpDir, "vitest.config.ts", "export default {}");
|
|
106
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
107
|
+
createTempFile(tmpDir, "tests/app.test.ts", "describe('app', () => {});");
|
|
116
108
|
|
|
117
109
|
const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
|
|
118
110
|
expect(result).not.toBeNull();
|
|
@@ -120,9 +112,9 @@ describe("TestRunnerClient", () => {
|
|
|
120
112
|
});
|
|
121
113
|
|
|
122
114
|
it("should find pytest test file with test_ prefix", () => {
|
|
123
|
-
createTempFile("pytest.ini", "[tool:pytest]");
|
|
124
|
-
createTempFile("src/app.py", "x = 1");
|
|
125
|
-
createTempFile("tests/test_app.py", "def test_app(): pass");
|
|
115
|
+
createTempFile(tmpDir, "pytest.ini", "[tool:pytest]");
|
|
116
|
+
createTempFile(tmpDir, "src/app.py", "x = 1");
|
|
117
|
+
createTempFile(tmpDir, "tests/test_app.py", "def test_app(): pass");
|
|
126
118
|
|
|
127
119
|
const result = client.findTestFile(path.join(tmpDir, "src/app.py"), tmpDir);
|
|
128
120
|
expect(result).not.toBeNull();
|
|
@@ -131,8 +123,8 @@ describe("TestRunnerClient", () => {
|
|
|
131
123
|
});
|
|
132
124
|
|
|
133
125
|
it("should return null when no test file found", () => {
|
|
134
|
-
createTempFile("vitest.config.ts", "export default {}");
|
|
135
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
126
|
+
createTempFile(tmpDir, "vitest.config.ts", "export default {}");
|
|
127
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
136
128
|
|
|
137
129
|
const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
|
|
138
130
|
expect(result).toBeNull();
|
|
@@ -140,9 +132,9 @@ describe("TestRunnerClient", () => {
|
|
|
140
132
|
|
|
141
133
|
it("should find test file even without config (if runner installed)", () => {
|
|
142
134
|
// Simulate vitest installed in node_modules
|
|
143
|
-
createTempFile("node_modules/vitest/package.json", "{}");
|
|
144
|
-
createTempFile("src/app.ts", "export const app = {};");
|
|
145
|
-
createTempFile("src/app.test.ts", "describe('app', () => {});");
|
|
135
|
+
createTempFile(tmpDir, "node_modules/vitest/package.json", "{}");
|
|
136
|
+
createTempFile(tmpDir, "src/app.ts", "export const app = {};");
|
|
137
|
+
createTempFile(tmpDir, "src/app.test.ts", "describe('app', () => {});");
|
|
146
138
|
|
|
147
139
|
const result = client.findTestFile(path.join(tmpDir, "src/app.ts"), tmpDir);
|
|
148
140
|
// Should find the test file since vitest is "installed"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a temporary file within the given temp directory.
|
|
7
|
+
* Automatically creates parent directories if they don't exist.
|
|
8
|
+
*/
|
|
9
|
+
export function createTempFile(tmpDir: string, name: string, content: string): string {
|
|
10
|
+
const filePath = path.join(tmpDir, name);
|
|
11
|
+
const dir = path.dirname(filePath);
|
|
12
|
+
if (!fs.existsSync(dir)) {
|
|
13
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
fs.writeFileSync(filePath, content);
|
|
16
|
+
return filePath;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a temporary directory for testing.
|
|
21
|
+
* Returns the path and a cleanup function.
|
|
22
|
+
*/
|
|
23
|
+
export function setupTestEnvironment(prefix: string): { tmpDir: string; cleanup: () => void } {
|
|
24
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
25
|
+
const cleanup = () => {
|
|
26
|
+
if (tmpDir && fs.existsSync(tmpDir)) {
|
|
27
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
return { tmpDir, cleanup };
|
|
31
|
+
}
|
|
@@ -1,37 +1,24 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
2
|
import { TodoScanner } from "./todo-scanner.js";
|
|
3
|
-
import
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
import * as os from "node:os";
|
|
3
|
+
import { createTempFile, setupTestEnvironment } from "./test-utils.js";
|
|
6
4
|
|
|
7
5
|
describe("TodoScanner", () => {
|
|
8
|
-
let
|
|
6
|
+
let client: TodoScanner;
|
|
9
7
|
let tmpDir: string;
|
|
10
|
-
|
|
11
|
-
function createTempFile(name: string, content: string): string {
|
|
12
|
-
const filePath = path.join(tmpDir, name);
|
|
13
|
-
const dir = path.dirname(filePath);
|
|
14
|
-
if (!fs.existsSync(dir)) {
|
|
15
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
-
}
|
|
17
|
-
fs.writeFileSync(filePath, content);
|
|
18
|
-
return filePath;
|
|
19
|
-
}
|
|
8
|
+
let cleanup: () => void;
|
|
20
9
|
|
|
21
10
|
beforeEach(() => {
|
|
22
|
-
|
|
23
|
-
tmpDir =
|
|
11
|
+
client = new TodoScanner();
|
|
12
|
+
({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-todo-test-"));
|
|
24
13
|
});
|
|
25
14
|
|
|
26
15
|
afterEach(() => {
|
|
27
|
-
|
|
28
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
29
|
-
}
|
|
16
|
+
cleanup();
|
|
30
17
|
});
|
|
31
18
|
|
|
32
19
|
describe("scanFile", () => {
|
|
33
20
|
it("should return empty array for non-existent files", () => {
|
|
34
|
-
const result =
|
|
21
|
+
const result = client.scanFile("/nonexistent/file.ts");
|
|
35
22
|
expect(result).toEqual([]);
|
|
36
23
|
});
|
|
37
24
|
|
|
@@ -40,8 +27,8 @@ describe("TodoScanner", () => {
|
|
|
40
27
|
// TODO: implement this function
|
|
41
28
|
function foo() {}
|
|
42
29
|
`;
|
|
43
|
-
const filePath = createTempFile("test.ts", content);
|
|
44
|
-
const result =
|
|
30
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
31
|
+
const result = client.scanFile(filePath);
|
|
45
32
|
|
|
46
33
|
expect(result.length).toBe(1);
|
|
47
34
|
expect(result[0].type).toBe("TODO");
|
|
@@ -53,8 +40,8 @@ function foo() {}
|
|
|
53
40
|
// FIXME: this is broken
|
|
54
41
|
function foo() {}
|
|
55
42
|
`;
|
|
56
|
-
const filePath = createTempFile("test.ts", content);
|
|
57
|
-
const result =
|
|
43
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
44
|
+
const result = client.scanFile(filePath);
|
|
58
45
|
|
|
59
46
|
expect(result.length).toBe(1);
|
|
60
47
|
expect(result[0].type).toBe("FIXME");
|
|
@@ -65,8 +52,8 @@ function foo() {}
|
|
|
65
52
|
// HACK: temporary workaround
|
|
66
53
|
const x = 1;
|
|
67
54
|
`;
|
|
68
|
-
const filePath = createTempFile("test.ts", content);
|
|
69
|
-
const result =
|
|
55
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
56
|
+
const result = client.scanFile(filePath);
|
|
70
57
|
|
|
71
58
|
expect(result.length).toBe(1);
|
|
72
59
|
expect(result[0].type).toBe("HACK");
|
|
@@ -77,8 +64,8 @@ const x = 1;
|
|
|
77
64
|
// BUG: this causes a crash
|
|
78
65
|
const x = 1;
|
|
79
66
|
`;
|
|
80
|
-
const filePath = createTempFile("test.ts", content);
|
|
81
|
-
const result =
|
|
67
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
68
|
+
const result = client.scanFile(filePath);
|
|
82
69
|
|
|
83
70
|
expect(result.length).toBe(1);
|
|
84
71
|
expect(result[0].type).toBe("BUG");
|
|
@@ -89,8 +76,8 @@ const x = 1;
|
|
|
89
76
|
// NOTE: important design decision
|
|
90
77
|
const x = 1;
|
|
91
78
|
`;
|
|
92
|
-
const filePath = createTempFile("test.ts", content);
|
|
93
|
-
const result =
|
|
79
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
80
|
+
const result = client.scanFile(filePath);
|
|
94
81
|
|
|
95
82
|
expect(result.length).toBe(1);
|
|
96
83
|
expect(result[0].type).toBe("NOTE");
|
|
@@ -103,8 +90,8 @@ const x = 1;
|
|
|
103
90
|
*/
|
|
104
91
|
const x = 1;
|
|
105
92
|
`;
|
|
106
|
-
const filePath = createTempFile("test.ts", content);
|
|
107
|
-
const result =
|
|
93
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
94
|
+
const result = client.scanFile(filePath);
|
|
108
95
|
|
|
109
96
|
expect(result.length).toBe(1);
|
|
110
97
|
expect(result[0].type).toBe("TODO");
|
|
@@ -117,8 +104,8 @@ const x = 1;
|
|
|
117
104
|
*/
|
|
118
105
|
function foo() {}
|
|
119
106
|
`;
|
|
120
|
-
const filePath = createTempFile("test.ts", content);
|
|
121
|
-
const result =
|
|
107
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
108
|
+
const result = client.scanFile(filePath);
|
|
122
109
|
|
|
123
110
|
expect(result.length).toBe(1);
|
|
124
111
|
expect(result[0].type).toBe("TODO");
|
|
@@ -130,8 +117,8 @@ function foo() {}
|
|
|
130
117
|
def foo():
|
|
131
118
|
pass
|
|
132
119
|
`;
|
|
133
|
-
const filePath = createTempFile("test.py", content);
|
|
134
|
-
const result =
|
|
120
|
+
const filePath = createTempFile(tmpDir, "test.py", content);
|
|
121
|
+
const result = client.scanFile(filePath);
|
|
135
122
|
|
|
136
123
|
expect(result.length).toBe(1);
|
|
137
124
|
expect(result[0].type).toBe("TODO");
|
|
@@ -141,8 +128,8 @@ def foo():
|
|
|
141
128
|
const content = `
|
|
142
129
|
const message = "TODO: buy milk";
|
|
143
130
|
`;
|
|
144
|
-
const filePath = createTempFile("test.ts", content);
|
|
145
|
-
const result =
|
|
131
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
132
|
+
const result = client.scanFile(filePath);
|
|
146
133
|
|
|
147
134
|
expect(result.length).toBe(0);
|
|
148
135
|
});
|
|
@@ -154,8 +141,8 @@ const y = 2;
|
|
|
154
141
|
// TODO: fix this
|
|
155
142
|
const z = 3;
|
|
156
143
|
`;
|
|
157
|
-
const filePath = createTempFile("test.ts", content);
|
|
158
|
-
const result =
|
|
144
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
145
|
+
const result = client.scanFile(filePath);
|
|
159
146
|
|
|
160
147
|
expect(result[0].line).toBe(4);
|
|
161
148
|
});
|
|
@@ -166,8 +153,8 @@ const z = 3;
|
|
|
166
153
|
// FIXME: second
|
|
167
154
|
// HACK: third
|
|
168
155
|
`;
|
|
169
|
-
const filePath = createTempFile("test.ts", content);
|
|
170
|
-
const result =
|
|
156
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
157
|
+
const result = client.scanFile(filePath);
|
|
171
158
|
|
|
172
159
|
expect(result.length).toBe(3);
|
|
173
160
|
});
|
|
@@ -175,41 +162,41 @@ const z = 3;
|
|
|
175
162
|
|
|
176
163
|
describe("scanDirectory", () => {
|
|
177
164
|
it("should scan all files in directory", () => {
|
|
178
|
-
createTempFile("file1.ts", "// TODO: task 1");
|
|
179
|
-
createTempFile("file2.ts", "// FIXME: bug 1");
|
|
180
|
-
createTempFile("file3.py", "# HACK: workaround");
|
|
165
|
+
createTempFile(tmpDir, "file1.ts", "// TODO: task 1");
|
|
166
|
+
createTempFile(tmpDir, "file2.ts", "// FIXME: bug 1");
|
|
167
|
+
createTempFile(tmpDir, "file3.py", "# HACK: workaround");
|
|
181
168
|
|
|
182
|
-
const result =
|
|
169
|
+
const result = client.scanDirectory(tmpDir);
|
|
183
170
|
|
|
184
171
|
expect(result.items.length).toBe(3);
|
|
185
172
|
});
|
|
186
173
|
|
|
187
174
|
it("should skip node_modules", () => {
|
|
188
|
-
createTempFile("src/file.ts", "// TODO: task 1");
|
|
189
|
-
createTempFile("node_modules/lib/file.ts", "// TODO: should be skipped");
|
|
175
|
+
createTempFile(tmpDir, "src/file.ts", "// TODO: task 1");
|
|
176
|
+
createTempFile(tmpDir, "node_modules/lib/file.ts", "// TODO: should be skipped");
|
|
190
177
|
|
|
191
|
-
const result =
|
|
178
|
+
const result = client.scanDirectory(tmpDir);
|
|
192
179
|
|
|
193
180
|
expect(result.items.length).toBe(1);
|
|
194
181
|
expect(result.items[0].file).not.toContain("node_modules");
|
|
195
182
|
});
|
|
196
183
|
|
|
197
184
|
it("should group items by type", () => {
|
|
198
|
-
createTempFile("file1.ts", "// TODO: task 1");
|
|
199
|
-
createTempFile("file2.ts", "// TODO: task 2");
|
|
200
|
-
createTempFile("file3.ts", "// FIXME: bug 1");
|
|
185
|
+
createTempFile(tmpDir, "file1.ts", "// TODO: task 1");
|
|
186
|
+
createTempFile(tmpDir, "file2.ts", "// TODO: task 2");
|
|
187
|
+
createTempFile(tmpDir, "file3.ts", "// FIXME: bug 1");
|
|
201
188
|
|
|
202
|
-
const result =
|
|
189
|
+
const result = client.scanDirectory(tmpDir);
|
|
203
190
|
|
|
204
191
|
expect(result.byType.get("TODO")?.length).toBe(2);
|
|
205
192
|
expect(result.byType.get("FIXME")?.length).toBe(1);
|
|
206
193
|
});
|
|
207
194
|
|
|
208
195
|
it("should group items by file", () => {
|
|
209
|
-
createTempFile("file1.ts", "// TODO: task 1\n// FIXME: bug 1");
|
|
210
|
-
createTempFile("file2.ts", "// TODO: task 2");
|
|
196
|
+
createTempFile(tmpDir, "file1.ts", "// TODO: task 1\n// FIXME: bug 1");
|
|
197
|
+
createTempFile(tmpDir, "file2.ts", "// TODO: task 2");
|
|
211
198
|
|
|
212
|
-
const result =
|
|
199
|
+
const result = client.scanDirectory(tmpDir);
|
|
213
200
|
|
|
214
201
|
const file1Items = [...result.byFile.entries()].find(([k]) => k.includes("file1.ts"));
|
|
215
202
|
expect(file1Items?.[1].length).toBe(2);
|
|
@@ -219,7 +206,7 @@ const z = 3;
|
|
|
219
206
|
describe("formatResult", () => {
|
|
220
207
|
it("should return empty string for no results", () => {
|
|
221
208
|
const result = { items: [], byType: new Map(), byFile: new Map() };
|
|
222
|
-
expect(
|
|
209
|
+
expect(client.formatResult(result)).toBe("");
|
|
223
210
|
});
|
|
224
211
|
|
|
225
212
|
it("should format results with counts", () => {
|
|
@@ -232,7 +219,7 @@ const z = 3;
|
|
|
232
219
|
byFile: new Map([["test.ts", [{ type: "TODO" as const, message: "task 1", file: "test.ts", line: 1, column: 0 }]]]),
|
|
233
220
|
};
|
|
234
221
|
|
|
235
|
-
const formatted =
|
|
222
|
+
const formatted = client.formatResult(result);
|
|
236
223
|
expect(formatted).toContain("2 annotation(s)");
|
|
237
224
|
expect(formatted).toContain("TODO");
|
|
238
225
|
expect(formatted).toContain("FIXME");
|
|
@@ -248,7 +235,7 @@ const z = 3;
|
|
|
248
235
|
byFile: new Map(),
|
|
249
236
|
};
|
|
250
237
|
|
|
251
|
-
const formatted =
|
|
238
|
+
const formatted = client.formatResult(result);
|
|
252
239
|
// Check that FIXME line comes before TODO line in the sorted output
|
|
253
240
|
const fixmeLineIndex = formatted.indexOf("🔴");
|
|
254
241
|
const todoLineIndex = formatted.indexOf("📝");
|
|
@@ -268,7 +255,7 @@ const z = 3;
|
|
|
268
255
|
byFile: new Map(),
|
|
269
256
|
};
|
|
270
257
|
|
|
271
|
-
const formatted =
|
|
258
|
+
const formatted = client.formatResult(result);
|
|
272
259
|
expect(formatted).toContain("🔴"); // FIXME
|
|
273
260
|
expect(formatted).toContain("🟠"); // HACK
|
|
274
261
|
expect(formatted).toContain("🐛"); // BUG
|
package/clients/todo-scanner.ts
CHANGED
|
@@ -130,6 +130,8 @@ export class TodoScanner {
|
|
|
130
130
|
} else if (extensions.some(ext => entry.name.endsWith(ext))) {
|
|
131
131
|
// Skip this scanner file — its own type literals and regex cause false positives
|
|
132
132
|
if (entry.name === "todo-scanner.ts" || entry.name === "todo-scanner.js") continue;
|
|
133
|
+
// Skip test files — intentional annotations are test fixtures, not work items
|
|
134
|
+
if (/\.(test|spec)\.[jt]sx?$/.test(entry.name)) continue;
|
|
133
135
|
items.push(...this.scanFile(fullPath));
|
|
134
136
|
}
|
|
135
137
|
}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
2
|
import { TypeCoverageClient } from "./type-coverage-client.js";
|
|
3
|
-
import
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
import * as os from "node:os";
|
|
3
|
+
import { setupTestEnvironment } from "./test-utils.js";
|
|
6
4
|
|
|
7
5
|
describe("TypeCoverageClient", () => {
|
|
8
6
|
let client: TypeCoverageClient;
|
|
9
7
|
let tmpDir: string;
|
|
8
|
+
let cleanup: () => void;
|
|
10
9
|
|
|
11
10
|
beforeEach(() => {
|
|
12
11
|
client = new TypeCoverageClient();
|
|
13
|
-
tmpDir =
|
|
12
|
+
({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-typecoverage-test-"));
|
|
14
13
|
});
|
|
15
14
|
|
|
16
15
|
afterEach(() => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
cleanup();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
cleanup();
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
describe("isAvailable", () => {
|
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
2
|
import { TypeScriptClient } from "./typescript-client.js";
|
|
3
|
-
import
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
import * as os from "node:os";
|
|
3
|
+
import { createTempFile, setupTestEnvironment } from "./test-utils.js";
|
|
6
4
|
|
|
7
5
|
describe("TypeScriptClient", () => {
|
|
8
6
|
let client: TypeScriptClient;
|
|
9
7
|
let tmpDir: string;
|
|
10
|
-
|
|
11
|
-
function createTempFile(name: string, content: string): string {
|
|
12
|
-
const filePath = path.join(tmpDir, name);
|
|
13
|
-
const dir = path.dirname(filePath);
|
|
14
|
-
if (!fs.existsSync(dir)) {
|
|
15
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
-
}
|
|
17
|
-
fs.writeFileSync(filePath, content);
|
|
18
|
-
return filePath;
|
|
19
|
-
}
|
|
8
|
+
let cleanup: () => void;
|
|
20
9
|
|
|
21
10
|
beforeEach(() => {
|
|
22
11
|
client = new TypeScriptClient();
|
|
23
|
-
tmpDir =
|
|
12
|
+
({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-ts-test-"));
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
cleanup();
|
|
24
17
|
});
|
|
25
18
|
|
|
26
19
|
afterEach(() => {
|
|
27
|
-
|
|
28
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
29
|
-
}
|
|
20
|
+
cleanup();
|
|
30
21
|
});
|
|
31
22
|
|
|
32
23
|
describe("isTypeScriptFile", () => {
|
|
@@ -51,7 +42,7 @@ describe("TypeScriptClient", () => {
|
|
|
51
42
|
const content = `
|
|
52
43
|
const x: number = "string"; // Type error
|
|
53
44
|
`;
|
|
54
|
-
const filePath = createTempFile("test.ts", content);
|
|
45
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
55
46
|
|
|
56
47
|
client.updateFile(filePath, content);
|
|
57
48
|
const diags = client.getDiagnostics(filePath);
|
|
@@ -65,7 +56,7 @@ const x: number = "string"; // Type error
|
|
|
65
56
|
const x: number = 42;
|
|
66
57
|
const y: string = "hello";
|
|
67
58
|
`;
|
|
68
|
-
const filePath = createTempFile("test.ts", content);
|
|
59
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
69
60
|
|
|
70
61
|
client.updateFile(filePath, content);
|
|
71
62
|
const diags = client.getDiagnostics(filePath);
|
|
@@ -81,7 +72,7 @@ function test() {
|
|
|
81
72
|
return undefinedVariable;
|
|
82
73
|
}
|
|
83
74
|
`;
|
|
84
|
-
const filePath = createTempFile("test.ts", content);
|
|
75
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
85
76
|
|
|
86
77
|
client.updateFile(filePath, content);
|
|
87
78
|
const diags = client.getDiagnostics(filePath);
|
|
@@ -96,7 +87,7 @@ function add(a: number, b: number): number {
|
|
|
96
87
|
}
|
|
97
88
|
const result = add(1);
|
|
98
89
|
`;
|
|
99
|
-
const filePath = createTempFile("test.ts", content);
|
|
90
|
+
const filePath = createTempFile(tmpDir, "test.ts", content);
|
|
100
91
|
|
|
101
92
|
client.updateFile(filePath, content);
|
|
102
93
|
const diags = client.getDiagnostics(filePath);
|