diff --git a/apps/code/src/renderer/features/code-review/components/ReviewShell.test.tsx b/apps/code/src/renderer/features/code-review/components/ReviewShell.test.tsx new file mode 100644 index 000000000..9e820f68c --- /dev/null +++ b/apps/code/src/renderer/features/code-review/components/ReviewShell.test.tsx @@ -0,0 +1,109 @@ +import { render } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; + +vi.mock("@renderer/features/code-review/stores/reviewNavigationStore", () => ({ + useReviewNavigationStore: vi.fn(), +})); +vi.mock("@features/code-editor/stores/diffViewerStore", () => ({ + useDiffViewerStore: vi.fn(), +})); +vi.mock("@features/task-detail/components/ChangesPanel", () => ({ + ChangesPanel: () => null, +})); +vi.mock("@features/git-interaction/utils/diffStats", () => ({ + computeDiffStats: () => ({ linesAdded: 0, linesRemoved: 0 }), +})); +vi.mock("@stores/themeStore", () => ({ + useThemeStore: vi.fn(() => ({ isDarkMode: false })), +})); +vi.mock("@pierre/diffs/react", () => ({ + WorkerPoolContextProvider: ({ children }: { children: React.ReactNode }) => + children, +})); +vi.mock("@pierre/diffs/worker/worker.js?worker&url", () => ({ default: "" })); +vi.mock("@components/ui/FileIcon", () => ({ + FileIcon: () => , +})); + +import { DeferredDiffPlaceholder, DiffFileHeader } from "./ReviewShell"; + +type FileDiffMetadata = import("@pierre/diffs/react").FileDiffMetadata; + +function makeFileDiff(name: string): FileDiffMetadata { + return { + name, + prevName: null, + hunks: [{ additionLines: 3, deletionLines: 1 }], + } as unknown as FileDiffMetadata; +} + +function findSpan( + container: HTMLElement, + match: (s: HTMLSpanElement) => boolean, +): HTMLSpanElement { + const spans = Array.from(container.querySelectorAll("span")); + const found = spans.find(match); + if (!found) throw new Error("span not found"); + return found; +} + +function renderHeader(path: string) { + const diff = render( + {}} + />, + ); + const deferred = render( + {}} + />, + ); + return { diff, deferred }; +} + +describe.each([ + ["DiffFileHeader", "diff" as const], + ["DeferredDiffPlaceholder", "deferred" as const], +])("%s", (_name, which) => { + it("renders the directory path and filename", () => { + const rendered = renderHeader( + "src/renderer/features/code-review/components/ReviewShell.tsx", + )[which]; + + const text = rendered.container.querySelector("button")?.textContent ?? ""; + expect(text).toContain("src/renderer/features/code-review/components/"); + expect(text).toContain("ReviewShell.tsx"); + }); + + it("truncates the directory path and keeps the filename intact", () => { + const rendered = renderHeader( + "src/a/very/deeply/nested/structure/ReviewShell.tsx", + )[which]; + + const dirSpan = findSpan( + rendered.container, + (s) => s.style.color === "var(--gray-9)" && !s.style.fontWeight, + ); + const fileSpan = findSpan( + rendered.container, + (s) => s.style.fontWeight === "600", + ); + + expect(dirSpan.style.overflow).toBe("hidden"); + expect(dirSpan.style.textOverflow).toBe("ellipsis"); + expect(dirSpan.style.whiteSpace).toBe("nowrap"); + + expect(fileSpan.style.whiteSpace).toBe("nowrap"); + expect(fileSpan.style.flexShrink).toBe("0"); + + expect(dirSpan.parentElement).toBe(fileSpan.parentElement); + expect(dirSpan.parentElement?.style.display).toBe("flex"); + }); +}); diff --git a/apps/code/src/renderer/features/code-review/components/ReviewShell.tsx b/apps/code/src/renderer/features/code-review/components/ReviewShell.tsx index 1a705f68b..10e43b65d 100644 --- a/apps/code/src/renderer/features/code-review/components/ReviewShell.tsx +++ b/apps/code/src/renderer/features/code-review/components/ReviewShell.tsx @@ -509,9 +509,24 @@ function FileHeaderRow({ }} /> - {dirPath} - - {fileName} + + + {fileName} + + + {dirPath} + {additions > 0 && (