@spaceflow/review 5.0.0 → 5.0.2
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 +18 -0
- package/README.md +5 -11
- package/dist/index.js +13 -44
- package/package.json +3 -3
- package/src/README.md +4 -4
- package/src/deletion-impact.service.spec.ts +8 -12
- package/src/deletion-impact.service.ts +5 -5
- package/src/issue-verify.service.spec.ts +0 -4
- package/src/locales/en/review.json +2 -2
- package/src/locales/zh-cn/review.json +2 -2
- package/src/mcp/index.ts +1 -5
- package/src/parse-title-options.spec.ts +4 -4
- package/src/parse-title-options.ts +3 -3
- package/src/review-context.ts +1 -1
- package/src/review-llm.spec.ts +1 -18
- package/src/review-result-model.ts +6 -6
- package/src/review.config.ts +2 -2
- package/src/review.service.spec.ts +4 -59
- package/src/review.service.ts +0 -27
- package/src/utils/review-pr-comment.spec.ts +8 -11
- package/src/utils/review-pr-comment.ts +2 -2
- package/src/coverage/base.css +0 -224
- package/src/coverage/block-navigation.js +0 -87
- package/src/coverage/clover.xml +0 -1942
- package/src/coverage/coverage-final.json +0 -7
- package/src/coverage/favicon.png +0 -0
- package/src/coverage/index.html +0 -131
- package/src/coverage/prettify.css +0 -1
- package/src/coverage/prettify.js +0 -2
- package/src/coverage/sort-arrow-sprite.png +0 -0
- package/src/coverage/sorter.js +0 -210
- package/src/coverage/src/deletion-impact.service.ts.html +0 -2716
- package/src/coverage/src/index.html +0 -161
- package/src/coverage/src/issue-verify.service.ts.html +0 -1006
- package/src/coverage/src/parse-title-options.ts.html +0 -640
- package/src/coverage/src/review-spec/index.html +0 -131
- package/src/coverage/src/review-spec/review-spec.service.ts.html +0 -2782
- package/src/coverage/src/review-spec/types.ts.html +0 -535
- package/src/coverage/src/review.service.ts.html +0 -8911
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
|
@@ -29,9 +29,6 @@ function mockSummary(overrides: Partial<FileSummary> = {}): FileSummary {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
vi.mock("c12");
|
|
32
|
-
vi.mock("@anthropic-ai/claude-agent-sdk", () => ({
|
|
33
|
-
query: vi.fn(),
|
|
34
|
-
}));
|
|
35
32
|
vi.mock("fs/promises");
|
|
36
33
|
vi.mock("child_process");
|
|
37
34
|
vi.mock("@opencode-ai/sdk", () => ({
|
|
@@ -98,9 +95,6 @@ class TestReviewService extends ReviewService {
|
|
|
98
95
|
executeDeletionOnly(context: Partial<ReviewContext>) {
|
|
99
96
|
return super.executeDeletionOnly(context as ReviewContext);
|
|
100
97
|
}
|
|
101
|
-
ensureClaudeCli(ci?: boolean) {
|
|
102
|
-
return super.ensureClaudeCli(ci);
|
|
103
|
-
}
|
|
104
98
|
resolveSourceData(context: Partial<ReviewContext>) {
|
|
105
99
|
return super.resolveSourceData(context as ReviewContext);
|
|
106
100
|
}
|
|
@@ -208,7 +202,7 @@ describe("ReviewService", () => {
|
|
|
208
202
|
chat: vi.fn(),
|
|
209
203
|
chatStream: vi.fn(),
|
|
210
204
|
createSession: vi.fn(),
|
|
211
|
-
getAvailableAdapters: vi.fn().mockReturnValue(["
|
|
205
|
+
getAvailableAdapters: vi.fn().mockReturnValue(["openai", "open-code"]),
|
|
212
206
|
};
|
|
213
207
|
|
|
214
208
|
service = new TestReviewService(
|
|
@@ -292,7 +286,7 @@ describe("ReviewService", () => {
|
|
|
292
286
|
specSources: ["/spec/dir"],
|
|
293
287
|
dryRun: true,
|
|
294
288
|
ci: false,
|
|
295
|
-
llmMode: "
|
|
289
|
+
llmMode: "openai" as const,
|
|
296
290
|
};
|
|
297
291
|
|
|
298
292
|
const mockPR = {
|
|
@@ -358,7 +352,7 @@ describe("ReviewService", () => {
|
|
|
358
352
|
specSources: ["/spec/dir"],
|
|
359
353
|
dryRun: false,
|
|
360
354
|
ci: true,
|
|
361
|
-
llmMode: "
|
|
355
|
+
llmMode: "openai" as const,
|
|
362
356
|
} as ReviewContext;
|
|
363
357
|
|
|
364
358
|
const mockPR = {
|
|
@@ -408,7 +402,7 @@ describe("ReviewService", () => {
|
|
|
408
402
|
specSources: ["/spec/dir"],
|
|
409
403
|
dryRun: false,
|
|
410
404
|
ci: false,
|
|
411
|
-
llmMode: "
|
|
405
|
+
llmMode: "openai",
|
|
412
406
|
};
|
|
413
407
|
|
|
414
408
|
gitProvider.getPullRequest.mockRejectedValue(new Error("Gitea API Error"));
|
|
@@ -922,13 +916,6 @@ describe("ReviewService", () => {
|
|
|
922
916
|
});
|
|
923
917
|
});
|
|
924
918
|
|
|
925
|
-
describe("ReviewService.ensureClaudeCli", () => {
|
|
926
|
-
it("should not throw when claude is installed", async () => {
|
|
927
|
-
vi.spyOn(require("child_process"), "execSync").mockImplementation(() => Buffer.from("1.0.0"));
|
|
928
|
-
await expect(service.ensureClaudeCli()).resolves.toBeUndefined();
|
|
929
|
-
});
|
|
930
|
-
});
|
|
931
|
-
|
|
932
919
|
describe("ReviewService.resolveSourceData - direct file mode", () => {
|
|
933
920
|
it("should bypass local uncommitted scanning when files are specified", async () => {
|
|
934
921
|
const context: ReviewContext = {
|
|
@@ -1273,46 +1260,4 @@ describe("ReviewService", () => {
|
|
|
1273
1260
|
expect(gitProvider.updateIssueComment).toHaveBeenCalled();
|
|
1274
1261
|
});
|
|
1275
1262
|
});
|
|
1276
|
-
|
|
1277
|
-
describe("ReviewService.ensureClaudeCli", () => {
|
|
1278
|
-
it("should do nothing when claude is already installed", async () => {
|
|
1279
|
-
const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
1280
|
-
// execSync is already mocked globally
|
|
1281
|
-
|
|
1282
|
-
await service.ensureClaudeCli();
|
|
1283
|
-
expect(consoleSpy).not.toHaveBeenCalledWith("🔧 Claude CLI 未安装,正在安装...");
|
|
1284
|
-
consoleSpy.mockRestore();
|
|
1285
|
-
});
|
|
1286
|
-
|
|
1287
|
-
it("should install claude when not found", async () => {
|
|
1288
|
-
const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
1289
|
-
// Mock execSync to throw then succeed
|
|
1290
|
-
const execSyncMock = vi.mocked(await import("child_process"));
|
|
1291
|
-
execSyncMock.execSync
|
|
1292
|
-
.mockImplementationOnce(() => {
|
|
1293
|
-
throw new Error("command not found");
|
|
1294
|
-
})
|
|
1295
|
-
.mockImplementationOnce(() => Buffer.from(""));
|
|
1296
|
-
|
|
1297
|
-
await service.ensureClaudeCli();
|
|
1298
|
-
expect(consoleSpy).toHaveBeenCalledWith("🔧 Claude CLI 未安装,正在安装...");
|
|
1299
|
-
expect(consoleSpy).toHaveBeenCalledWith("✅ Claude CLI 安装完成");
|
|
1300
|
-
consoleSpy.mockRestore();
|
|
1301
|
-
});
|
|
1302
|
-
|
|
1303
|
-
it("should throw error when installation fails", async () => {
|
|
1304
|
-
const execSyncMock = vi.mocked(await import("child_process"));
|
|
1305
|
-
execSyncMock.execSync
|
|
1306
|
-
.mockImplementationOnce(() => {
|
|
1307
|
-
throw new Error("command not found");
|
|
1308
|
-
})
|
|
1309
|
-
.mockImplementationOnce(() => {
|
|
1310
|
-
throw new Error("install failed");
|
|
1311
|
-
});
|
|
1312
|
-
|
|
1313
|
-
await expect(service.ensureClaudeCli()).rejects.toThrow(
|
|
1314
|
-
"Claude CLI 安装失败: install failed",
|
|
1315
|
-
);
|
|
1316
|
-
});
|
|
1317
|
-
});
|
|
1318
1263
|
});
|
package/src/review.service.ts
CHANGED
|
@@ -20,7 +20,6 @@ import { MarkdownFormatter, ReviewReportService } from "./review-report";
|
|
|
20
20
|
import { ReviewOptions } from "./review.config";
|
|
21
21
|
import { IssueVerifyService } from "./issue-verify.service";
|
|
22
22
|
import { DeletionImpactService } from "./deletion-impact.service";
|
|
23
|
-
import { execSync } from "child_process";
|
|
24
23
|
import { ReviewContextBuilder, type ReviewContext } from "./review-context";
|
|
25
24
|
import { ReviewIssueFilter } from "./review-issue-filter";
|
|
26
25
|
import { filterFilesByIncludes } from "./review-includes-filter";
|
|
@@ -730,30 +729,4 @@ export class ReviewService {
|
|
|
730
729
|
|
|
731
730
|
return result;
|
|
732
731
|
}
|
|
733
|
-
|
|
734
|
-
/**
|
|
735
|
-
* 确保 Claude CLI 已安装
|
|
736
|
-
*/
|
|
737
|
-
protected async ensureClaudeCli(ci?: boolean): Promise<void> {
|
|
738
|
-
try {
|
|
739
|
-
execSync("claude --version", { stdio: "ignore" });
|
|
740
|
-
} catch {
|
|
741
|
-
if (ci) {
|
|
742
|
-
throw new Error(
|
|
743
|
-
"Claude CLI 未安装。CI 环境请在 workflow 中预装: npm install -g @anthropic-ai/claude-code",
|
|
744
|
-
);
|
|
745
|
-
}
|
|
746
|
-
console.log("🔧 Claude CLI 未安装,正在安装...");
|
|
747
|
-
try {
|
|
748
|
-
execSync("npm install -g @anthropic-ai/claude-code", {
|
|
749
|
-
stdio: "inherit",
|
|
750
|
-
});
|
|
751
|
-
console.log("✅ Claude CLI 安装完成");
|
|
752
|
-
} catch (installError) {
|
|
753
|
-
throw new Error(
|
|
754
|
-
`Claude CLI 安装失败: ${installError instanceof Error ? installError.message : String(installError)}`,
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
732
|
}
|
|
@@ -27,9 +27,7 @@ describe("utils/review-pr-comment", () => {
|
|
|
27
27
|
|
|
28
28
|
describe("extractIssueKeyFromBody", () => {
|
|
29
29
|
it("提取标准格式的 issue key", () => {
|
|
30
|
-
expect(extractIssueKeyFromBody("<!-- issue-key: src/a.ts:10:R1 -->")).toBe(
|
|
31
|
-
"src/a.ts:10:R1",
|
|
32
|
-
);
|
|
30
|
+
expect(extractIssueKeyFromBody("<!-- issue-key: src/a.ts:10:R1 -->")).toBe("src/a.ts:10:R1");
|
|
33
31
|
});
|
|
34
32
|
|
|
35
33
|
it("body 不含 issue-key 时返回 null", () => {
|
|
@@ -69,9 +67,9 @@ describe("utils/review-pr-comment", () => {
|
|
|
69
67
|
|
|
70
68
|
describe("generateIssueKey", () => {
|
|
71
69
|
it("拼接 file:line:ruleId", () => {
|
|
72
|
-
expect(
|
|
73
|
-
|
|
74
|
-
)
|
|
70
|
+
expect(generateIssueKey({ file: "src/a.ts", line: "10", ruleId: "R1" } as any)).toBe(
|
|
71
|
+
"src/a.ts:10:R1",
|
|
72
|
+
);
|
|
75
73
|
});
|
|
76
74
|
});
|
|
77
75
|
|
|
@@ -129,21 +127,20 @@ describe("utils/review-pr-comment", () => {
|
|
|
129
127
|
});
|
|
130
128
|
|
|
131
129
|
it("resolved(非 fixed)计入 resolved,不计入 pending", () => {
|
|
132
|
-
const issues = [
|
|
133
|
-
{ file: "a.ts", line: "1", ruleId: "R1", resolved: "2024-01-01" },
|
|
134
|
-
] as any[];
|
|
130
|
+
const issues = [{ file: "a.ts", line: "1", ruleId: "R1", resolved: "2024-01-01" }] as any[];
|
|
135
131
|
const stats = calculateIssueStats(issues);
|
|
136
132
|
expect(stats.resolved).toBe(1);
|
|
137
133
|
expect(stats.pending).toBe(0);
|
|
138
134
|
});
|
|
139
135
|
|
|
140
|
-
it("fixed
|
|
136
|
+
it("fixed 和 resolved 同时存在时各自独立计数,pending 不重复减", () => {
|
|
141
137
|
const issues = [
|
|
142
138
|
{ file: "a.ts", line: "1", ruleId: "R1", fixed: "2024-01-01", resolved: "2024-01-01" },
|
|
143
139
|
] as any[];
|
|
144
140
|
const stats = calculateIssueStats(issues);
|
|
145
141
|
expect(stats.fixed).toBe(1);
|
|
146
|
-
expect(stats.resolved).toBe(
|
|
142
|
+
expect(stats.resolved).toBe(1);
|
|
143
|
+
expect(stats.pending).toBe(0);
|
|
147
144
|
});
|
|
148
145
|
|
|
149
146
|
it("fixRate 计算正确(2/4 = 50%)", () => {
|
|
@@ -177,9 +177,9 @@ export function calculateIssueStats(issues: ReviewIssue[]): ReviewStats {
|
|
|
177
177
|
const validIssue = issues.filter((i) => i.valid !== "false");
|
|
178
178
|
const validTotal = validIssue.length;
|
|
179
179
|
const fixed = validIssue.filter((i) => i.fixed).length;
|
|
180
|
-
const resolved = validIssue.filter((i) => i.resolved
|
|
180
|
+
const resolved = validIssue.filter((i) => i.resolved).length;
|
|
181
181
|
const invalid = total - validTotal;
|
|
182
|
-
const pending = validTotal - fixed
|
|
182
|
+
const pending = validTotal - validIssue.filter((i) => i.fixed || i.resolved).length;
|
|
183
183
|
const fixRate = validTotal > 0 ? Math.round((fixed / validTotal) * 100 * 10) / 10 : 0;
|
|
184
184
|
const resolveRate = validTotal > 0 ? Math.round((resolved / validTotal) * 100 * 10) / 10 : 0;
|
|
185
185
|
return { total, validTotal, fixed, resolved, invalid, pending, fixRate, resolveRate };
|
package/src/coverage/base.css
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
body, html {
|
|
2
|
-
margin:0; padding: 0;
|
|
3
|
-
height: 100%;
|
|
4
|
-
}
|
|
5
|
-
body {
|
|
6
|
-
font-family: Helvetica Neue, Helvetica, Arial;
|
|
7
|
-
font-size: 14px;
|
|
8
|
-
color:#333;
|
|
9
|
-
}
|
|
10
|
-
.small { font-size: 12px; }
|
|
11
|
-
*, *:after, *:before {
|
|
12
|
-
-webkit-box-sizing:border-box;
|
|
13
|
-
-moz-box-sizing:border-box;
|
|
14
|
-
box-sizing:border-box;
|
|
15
|
-
}
|
|
16
|
-
h1 { font-size: 20px; margin: 0;}
|
|
17
|
-
h2 { font-size: 14px; }
|
|
18
|
-
pre {
|
|
19
|
-
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
|
20
|
-
margin: 0;
|
|
21
|
-
padding: 0;
|
|
22
|
-
-moz-tab-size: 2;
|
|
23
|
-
-o-tab-size: 2;
|
|
24
|
-
tab-size: 2;
|
|
25
|
-
}
|
|
26
|
-
a { color:#0074D9; text-decoration:none; }
|
|
27
|
-
a:hover { text-decoration:underline; }
|
|
28
|
-
.strong { font-weight: bold; }
|
|
29
|
-
.space-top1 { padding: 10px 0 0 0; }
|
|
30
|
-
.pad2y { padding: 20px 0; }
|
|
31
|
-
.pad1y { padding: 10px 0; }
|
|
32
|
-
.pad2x { padding: 0 20px; }
|
|
33
|
-
.pad2 { padding: 20px; }
|
|
34
|
-
.pad1 { padding: 10px; }
|
|
35
|
-
.space-left2 { padding-left:55px; }
|
|
36
|
-
.space-right2 { padding-right:20px; }
|
|
37
|
-
.center { text-align:center; }
|
|
38
|
-
.clearfix { display:block; }
|
|
39
|
-
.clearfix:after {
|
|
40
|
-
content:'';
|
|
41
|
-
display:block;
|
|
42
|
-
height:0;
|
|
43
|
-
clear:both;
|
|
44
|
-
visibility:hidden;
|
|
45
|
-
}
|
|
46
|
-
.fl { float: left; }
|
|
47
|
-
@media only screen and (max-width:640px) {
|
|
48
|
-
.col3 { width:100%; max-width:100%; }
|
|
49
|
-
.hide-mobile { display:none!important; }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.quiet {
|
|
53
|
-
color: #7f7f7f;
|
|
54
|
-
color: rgba(0,0,0,0.5);
|
|
55
|
-
}
|
|
56
|
-
.quiet a { opacity: 0.7; }
|
|
57
|
-
|
|
58
|
-
.fraction {
|
|
59
|
-
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
|
60
|
-
font-size: 10px;
|
|
61
|
-
color: #555;
|
|
62
|
-
background: #E8E8E8;
|
|
63
|
-
padding: 4px 5px;
|
|
64
|
-
border-radius: 3px;
|
|
65
|
-
vertical-align: middle;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
div.path a:link, div.path a:visited { color: #333; }
|
|
69
|
-
table.coverage {
|
|
70
|
-
border-collapse: collapse;
|
|
71
|
-
margin: 10px 0 0 0;
|
|
72
|
-
padding: 0;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
table.coverage td {
|
|
76
|
-
margin: 0;
|
|
77
|
-
padding: 0;
|
|
78
|
-
vertical-align: top;
|
|
79
|
-
}
|
|
80
|
-
table.coverage td.line-count {
|
|
81
|
-
text-align: right;
|
|
82
|
-
padding: 0 5px 0 20px;
|
|
83
|
-
}
|
|
84
|
-
table.coverage td.line-coverage {
|
|
85
|
-
text-align: right;
|
|
86
|
-
padding-right: 10px;
|
|
87
|
-
min-width:20px;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
table.coverage td span.cline-any {
|
|
91
|
-
display: inline-block;
|
|
92
|
-
padding: 0 5px;
|
|
93
|
-
width: 100%;
|
|
94
|
-
}
|
|
95
|
-
.missing-if-branch {
|
|
96
|
-
display: inline-block;
|
|
97
|
-
margin-right: 5px;
|
|
98
|
-
border-radius: 3px;
|
|
99
|
-
position: relative;
|
|
100
|
-
padding: 0 4px;
|
|
101
|
-
background: #333;
|
|
102
|
-
color: yellow;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.skip-if-branch {
|
|
106
|
-
display: none;
|
|
107
|
-
margin-right: 10px;
|
|
108
|
-
position: relative;
|
|
109
|
-
padding: 0 4px;
|
|
110
|
-
background: #ccc;
|
|
111
|
-
color: white;
|
|
112
|
-
}
|
|
113
|
-
.missing-if-branch .typ, .skip-if-branch .typ {
|
|
114
|
-
color: inherit !important;
|
|
115
|
-
}
|
|
116
|
-
.coverage-summary {
|
|
117
|
-
border-collapse: collapse;
|
|
118
|
-
width: 100%;
|
|
119
|
-
}
|
|
120
|
-
.coverage-summary tr { border-bottom: 1px solid #bbb; }
|
|
121
|
-
.keyline-all { border: 1px solid #ddd; }
|
|
122
|
-
.coverage-summary td, .coverage-summary th { padding: 10px; }
|
|
123
|
-
.coverage-summary tbody { border: 1px solid #bbb; }
|
|
124
|
-
.coverage-summary td { border-right: 1px solid #bbb; }
|
|
125
|
-
.coverage-summary td:last-child { border-right: none; }
|
|
126
|
-
.coverage-summary th {
|
|
127
|
-
text-align: left;
|
|
128
|
-
font-weight: normal;
|
|
129
|
-
white-space: nowrap;
|
|
130
|
-
}
|
|
131
|
-
.coverage-summary th.file { border-right: none !important; }
|
|
132
|
-
.coverage-summary th.pct { }
|
|
133
|
-
.coverage-summary th.pic,
|
|
134
|
-
.coverage-summary th.abs,
|
|
135
|
-
.coverage-summary td.pct,
|
|
136
|
-
.coverage-summary td.abs { text-align: right; }
|
|
137
|
-
.coverage-summary td.file { white-space: nowrap; }
|
|
138
|
-
.coverage-summary td.pic { min-width: 120px !important; }
|
|
139
|
-
.coverage-summary tfoot td { }
|
|
140
|
-
|
|
141
|
-
.coverage-summary .sorter {
|
|
142
|
-
height: 10px;
|
|
143
|
-
width: 7px;
|
|
144
|
-
display: inline-block;
|
|
145
|
-
margin-left: 0.5em;
|
|
146
|
-
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
|
|
147
|
-
}
|
|
148
|
-
.coverage-summary .sorted .sorter {
|
|
149
|
-
background-position: 0 -20px;
|
|
150
|
-
}
|
|
151
|
-
.coverage-summary .sorted-desc .sorter {
|
|
152
|
-
background-position: 0 -10px;
|
|
153
|
-
}
|
|
154
|
-
.status-line { height: 10px; }
|
|
155
|
-
/* yellow */
|
|
156
|
-
.cbranch-no { background: yellow !important; color: #111; }
|
|
157
|
-
/* dark red */
|
|
158
|
-
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
|
|
159
|
-
.low .chart { border:1px solid #C21F39 }
|
|
160
|
-
.highlighted,
|
|
161
|
-
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
|
|
162
|
-
background: #C21F39 !important;
|
|
163
|
-
}
|
|
164
|
-
/* medium red */
|
|
165
|
-
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
|
|
166
|
-
/* light red */
|
|
167
|
-
.low, .cline-no { background:#FCE1E5 }
|
|
168
|
-
/* light green */
|
|
169
|
-
.high, .cline-yes { background:rgb(230,245,208) }
|
|
170
|
-
/* medium green */
|
|
171
|
-
.cstat-yes { background:rgb(161,215,106) }
|
|
172
|
-
/* dark green */
|
|
173
|
-
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
|
|
174
|
-
.high .chart { border:1px solid rgb(77,146,33) }
|
|
175
|
-
/* dark yellow (gold) */
|
|
176
|
-
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
|
|
177
|
-
.medium .chart { border:1px solid #f9cd0b; }
|
|
178
|
-
/* light yellow */
|
|
179
|
-
.medium { background: #fff4c2; }
|
|
180
|
-
|
|
181
|
-
.cstat-skip { background: #ddd; color: #111; }
|
|
182
|
-
.fstat-skip { background: #ddd; color: #111 !important; }
|
|
183
|
-
.cbranch-skip { background: #ddd !important; color: #111; }
|
|
184
|
-
|
|
185
|
-
span.cline-neutral { background: #eaeaea; }
|
|
186
|
-
|
|
187
|
-
.coverage-summary td.empty {
|
|
188
|
-
opacity: .5;
|
|
189
|
-
padding-top: 4px;
|
|
190
|
-
padding-bottom: 4px;
|
|
191
|
-
line-height: 1;
|
|
192
|
-
color: #888;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.cover-fill, .cover-empty {
|
|
196
|
-
display:inline-block;
|
|
197
|
-
height: 12px;
|
|
198
|
-
}
|
|
199
|
-
.chart {
|
|
200
|
-
line-height: 0;
|
|
201
|
-
}
|
|
202
|
-
.cover-empty {
|
|
203
|
-
background: white;
|
|
204
|
-
}
|
|
205
|
-
.cover-full {
|
|
206
|
-
border-right: none !important;
|
|
207
|
-
}
|
|
208
|
-
pre.prettyprint {
|
|
209
|
-
border: none !important;
|
|
210
|
-
padding: 0 !important;
|
|
211
|
-
margin: 0 !important;
|
|
212
|
-
}
|
|
213
|
-
.com { color: #999 !important; }
|
|
214
|
-
.ignore-none { color: #999; font-weight: normal; }
|
|
215
|
-
|
|
216
|
-
.wrapper {
|
|
217
|
-
min-height: 100%;
|
|
218
|
-
height: auto !important;
|
|
219
|
-
height: 100%;
|
|
220
|
-
margin: 0 auto -48px;
|
|
221
|
-
}
|
|
222
|
-
.footer, .push {
|
|
223
|
-
height: 48px;
|
|
224
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
var jumpToCode = (function init() {
|
|
3
|
-
// Classes of code we would like to highlight in the file view
|
|
4
|
-
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
|
|
5
|
-
|
|
6
|
-
// Elements to highlight in the file listing view
|
|
7
|
-
var fileListingElements = ['td.pct.low'];
|
|
8
|
-
|
|
9
|
-
// We don't want to select elements that are direct descendants of another match
|
|
10
|
-
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
|
|
11
|
-
|
|
12
|
-
// Selector that finds elements on the page to which we can jump
|
|
13
|
-
var selector =
|
|
14
|
-
fileListingElements.join(', ') +
|
|
15
|
-
', ' +
|
|
16
|
-
notSelector +
|
|
17
|
-
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
|
|
18
|
-
|
|
19
|
-
// The NodeList of matching elements
|
|
20
|
-
var missingCoverageElements = document.querySelectorAll(selector);
|
|
21
|
-
|
|
22
|
-
var currentIndex;
|
|
23
|
-
|
|
24
|
-
function toggleClass(index) {
|
|
25
|
-
missingCoverageElements
|
|
26
|
-
.item(currentIndex)
|
|
27
|
-
.classList.remove('highlighted');
|
|
28
|
-
missingCoverageElements.item(index).classList.add('highlighted');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function makeCurrent(index) {
|
|
32
|
-
toggleClass(index);
|
|
33
|
-
currentIndex = index;
|
|
34
|
-
missingCoverageElements.item(index).scrollIntoView({
|
|
35
|
-
behavior: 'smooth',
|
|
36
|
-
block: 'center',
|
|
37
|
-
inline: 'center'
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function goToPrevious() {
|
|
42
|
-
var nextIndex = 0;
|
|
43
|
-
if (typeof currentIndex !== 'number' || currentIndex === 0) {
|
|
44
|
-
nextIndex = missingCoverageElements.length - 1;
|
|
45
|
-
} else if (missingCoverageElements.length > 1) {
|
|
46
|
-
nextIndex = currentIndex - 1;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
makeCurrent(nextIndex);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function goToNext() {
|
|
53
|
-
var nextIndex = 0;
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
typeof currentIndex === 'number' &&
|
|
57
|
-
currentIndex < missingCoverageElements.length - 1
|
|
58
|
-
) {
|
|
59
|
-
nextIndex = currentIndex + 1;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
makeCurrent(nextIndex);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return function jump(event) {
|
|
66
|
-
if (
|
|
67
|
-
document.getElementById('fileSearch') === document.activeElement &&
|
|
68
|
-
document.activeElement != null
|
|
69
|
-
) {
|
|
70
|
-
// if we're currently focused on the search input, we don't want to navigate
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
switch (event.which) {
|
|
75
|
-
case 78: // n
|
|
76
|
-
case 74: // j
|
|
77
|
-
goToNext();
|
|
78
|
-
break;
|
|
79
|
-
case 66: // b
|
|
80
|
-
case 75: // k
|
|
81
|
-
case 80: // p
|
|
82
|
-
goToPrevious();
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
})();
|
|
87
|
-
window.addEventListener('keydown', jumpToCode);
|