feat(vcs): tint test files green in the changes tree (#940)

The changes tree in the Commit and Local Changes views now gives test files a light green background, so test changes stand out from production ones. A file counts as a test when it lies under a test source root (detected or marked by hand) or is named like a test (`*.test.ts`, `*Test.java`, …), the same check the project tree uses; folders under a test root get the tint too. Hover and selection still win.

The colour is a new `--bg-file-tests` token derived from `--success` and `--bg-island`, so every theme gets a matching tint, and it follows test roots as they are marked or unmarked.

Fixes #931.

Co-authored-by: jaw52 <[email protected]>
Цей коміт міститься в:
Jaw authored and GitHub committed 2026-11-03 14:40:18 +08:00
1 джерело 2161a2ca03
коміт 038c23a2c8
4 files changed
+76

No files matched your search

+4
Переглянути файл
@@ -218,6 +218,10 @@
gap: 4px;
border-radius: var(--radius-small);
}
/* Test files and the folders under a test root, as IDEA's green "Tests" file colour; hover and selection win. */
.testFile:not(:hover):not([aria-selected="true"]) {
background: var(--bg-file-tests);
}
.dropTarget {
box-shadow: inset 0 0 0 1px var(--accent);
background: color-mix(in srgb, var(--accent) 12%, transparent);
+5
Переглянути файл
@@ -8,6 +8,7 @@ import { tx } from "@/platform/i18n";
import clsx from "clsx";
import { executeAction } from "@/platform/actions";
import { dirname } from "@/platform/project";
import { isTestFile, useEffectiveRoots } from "@/platform/projectRoots";
import {
Button,
Checkbox,
@@ -398,6 +399,7 @@ export function ChangesTree({ mode }: { mode: "commit" | "local" | "dialog" }) {
const viewRef = useRef<HTMLButtonElement>(null);
const repos = useVcs((s) => s.roots);
const projectRoots = useEffectiveRoots();
const roots = useMemo(
() => buildChangeTree(status, root, groupByDir, lists, repos),
[status, root, groupByDir, lists, repos],
@@ -524,6 +526,9 @@ export function ChangesTree({ mode }: { mode: "commit" | "local" | "dialog" }) {
getLabel={nodeText}
getDirectory={(n) => (n.path && n.type !== "group" ? (n.type === "dir" ? n.path : dirname(n.path)) : null)}
getChildren={(n) => n.children}
rowClassName={(n) =>
n.type !== "group" && n.path && isTestFile(n.path, projectRoots) ? styles.testFile : undefined
}
isExpandable={(n) => n.type !== "file"}
expanded={expanded}
onExpandedChange={(_next, node, open) =>
+65
Переглянути файл
@@ -0,0 +1,65 @@
import { act, render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("@/platform/ipc", async (importOriginal) => {
const real = await importOriginal<typeof import("@/platform/ipc")>();
return { ...real, call: vi.fn() };
});
vi.mock("../DiffEditor", () => ({ DiffEditor: () => <div /> }));
import { ChangesTree } from "../CommitView";
import { useVcs } from "../store";
import { useProject } from "@/platform/project";
import { useProjectRoots } from "@/platform/projectRoots";
const change = (relPath: string) => ({
path: `/p/${relPath}`,
relPath,
staged: null,
unstaged: "modified" as const,
renamedFrom: null,
});
/** The tree row (`role=treeitem`) showing `name`. */
const row = (name: string) => screen.getByText(name).closest('[role="treeitem"]') as HTMLElement;
describe("test files in the changes tree (#931)", () => {
beforeEach(() => {
localStorage.clear();
useProject.setState({
project: { name: "p", root: "/p", trusted: true, isGit: true, settings: {} } as never,
settings: {} as never,
});
useProjectRoots.setState({
root: "/p",
detected: [{ path: "src/test/java", kind: "test", detectedFrom: "pom.xml" }],
});
useVcs.setState({
root: "/p",
repo: "/p",
roots: ["/p"],
status: [
change("src/main/java/App.java"),
change("src/test/java/AppTest.java"),
change("web/button.test.ts"),
change("web/button.ts"),
],
});
});
it("tints files under a test root or named like a test, as IDEA's Tests scope does", () => {
render(<ChangesTree mode="local" />);
expect(row("AppTest.java").className).toMatch(/testFile/);
expect(row("button.test.ts").className).toMatch(/testFile/);
expect(row("App.java").className).not.toMatch(/testFile/);
expect(row("button.ts").className).not.toMatch(/testFile/);
});
it("follows a folder being marked as test sources", () => {
render(<ChangesTree mode="local" />);
expect(row("App.java").className).not.toMatch(/testFile/);
act(() => useProjectRoots.setState({ detected: [{ path: "src/main/java", kind: "test", detectedFrom: null }] }));
expect(row("App.java").className).toMatch(/testFile/);
});
});
+2
Переглянути файл
@@ -314,6 +314,8 @@ html {
--bg-tree-selected: #2a4371;
--bg-tree-selected-inactive: #33353b;
--bg-tree-hover: #ffffff10;
/* IDEA's "Tests" file colour (FileColor.Green), derived so every theme gets a matching tint. */
--bg-file-tests: color-mix(in srgb, var(--success) 14%, var(--bg-island));
--bg-list-selected: var(--bg-tree-selected);
--bg-toolbar-hover: #ffffff17;
--bg-toolbar-pressed: #ffffff29;