diff --git a/ui/src/pages/CompanySkills.test.tsx b/ui/src/pages/CompanySkills.test.tsx
new file mode 100644
index 00000000000..df199276c41
--- /dev/null
+++ b/ui/src/pages/CompanySkills.test.tsx
@@ -0,0 +1,276 @@
+// @vitest-environment jsdom
+
+import type { ComponentProps, ReactNode } from "react";
+import { flushSync } from "react-dom";
+import { createRoot, type Root } from "react-dom/client";
+import type { CompanySkillDetail, CompanySkillVersion } from "@paperclipai/shared";
+import { afterEach, describe, expect, it, vi } from "vitest";
+import { SkillDetailPage, getSkillVersionDiffSelection } from "./CompanySkills";
+
+vi.mock("@/lib/router", () => ({
+ Link: ({ children, to, ...props }: { children: ReactNode; to: string }) => (
+ {children}
+ ),
+ useNavigate: () => vi.fn(),
+ useParams: () => ({}),
+ useSearchParams: () => [new URLSearchParams(), vi.fn()],
+}));
+
+vi.mock("@/components/ui/button", () => ({
+ Button: ({
+ children,
+ type = "button",
+ variant: _variant,
+ size: _size,
+ asChild: _asChild,
+ ...props
+ }: ComponentProps<"button"> & { asChild?: boolean; variant?: string; size?: string }) => (
+
+ ),
+}));
+
+vi.mock("@/components/ui/dialog", () => ({
+ Dialog: ({ open, children }: { open?: boolean; children: ReactNode }) => (open ?
{children}
: null),
+ DialogContent: ({ children }: { children: ReactNode }) => {children}
,
+ DialogDescription: ({ children }: { children: ReactNode }) => {children}
,
+ DialogFooter: ({ children }: { children: ReactNode }) => {children}
,
+ DialogHeader: ({ children }: { children: ReactNode }) => {children}
,
+ DialogTitle: ({ children }: { children: ReactNode }) => {children}
,
+}));
+
+vi.mock("@/components/ui/dropdown-menu", () => ({
+ DropdownMenu: ({ children }: { children: ReactNode }) => <>{children}>,
+ DropdownMenuContent: () => null,
+ DropdownMenuItem: ({ children, onSelect }: { children: ReactNode; onSelect?: () => void }) => (
+
+ ),
+ DropdownMenuLabel: ({ children }: { children: ReactNode }) => {children}
,
+ DropdownMenuRadioGroup: ({ children }: { children: ReactNode }) => {children}
,
+ DropdownMenuRadioItem: ({ children, onSelect }: { children: ReactNode; onSelect?: () => void }) => (
+
+ ),
+ DropdownMenuSeparator: () =>
,
+ DropdownMenuTrigger: ({ children }: { children: ReactNode }) => <>{children}>,
+}));
+
+vi.mock("@/components/ui/popover", () => ({
+ Popover: ({ children }: { children: ReactNode }) => <>{children}>,
+ PopoverContent: () => null,
+ PopoverTrigger: ({ children }: { children: ReactNode }) => <>{children}>,
+}));
+
+vi.mock("@/components/ui/tooltip", () => ({
+ Tooltip: ({ children }: { children: ReactNode }) => <>{children}>,
+ TooltipContent: ({ children }: { children: ReactNode }) => {children},
+ TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children}>,
+}));
+
+vi.mock("@/components/ui/tabs", () => ({
+ Tabs: ({ children }: { children: ReactNode }) => {children}
,
+ TabsList: ({ children }: { children: ReactNode }) => {children}
,
+ TabsTrigger: ({ children }: { children: ReactNode }) => ,
+}));
+
+vi.mock("@/components/ui/checkbox", () => ({
+ Checkbox: (props: ComponentProps<"input">) => ,
+}));
+
+vi.mock("../components/MarkdownBody", () => ({
+ MarkdownBody: ({ children }: { children: ReactNode }) => {children}
,
+}));
+
+vi.mock("../components/MarkdownEditor", () => ({
+ MarkdownEditor: ({ value }: { value: string }) => ,
+}));
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
+
+let root: Root | null = null;
+let container: HTMLDivElement | null = null;
+
+async function act(callback: () => void | Promise) {
+ let result: void | Promise = undefined;
+ flushSync(() => {
+ result = callback();
+ });
+ await result;
+}
+
+afterEach(() => {
+ root?.unmount();
+ root = null;
+ container?.remove();
+ container = null;
+});
+
+function makeVersion(revisionNumber: number, content: string): CompanySkillVersion {
+ return {
+ id: `version-${revisionNumber}`,
+ companyId: "company-1",
+ companySkillId: "skill-1",
+ revisionNumber,
+ label: null,
+ fileInventory: [
+ {
+ path: "SKILL.md",
+ kind: "skill",
+ content,
+ },
+ ],
+ authorAgentId: null,
+ authorUserId: null,
+ createdAt: new Date(`2026-01-0${revisionNumber}T00:00:00Z`),
+ };
+}
+
+function makeDetail(currentVersion: CompanySkillVersion): CompanySkillDetail {
+ return {
+ id: "skill-1",
+ companyId: "company-1",
+ key: "demo-skill",
+ slug: "demo-skill",
+ name: "Demo Skill",
+ description: "A demo skill.",
+ markdown: "# Demo Skill",
+ sourceType: "local_path",
+ sourceLocator: null,
+ sourceRef: null,
+ trustLevel: "markdown_only",
+ compatibility: "compatible",
+ fileInventory: [{ path: "SKILL.md", kind: "skill" }],
+ iconUrl: null,
+ color: null,
+ tagline: null,
+ authorName: null,
+ homepageUrl: null,
+ categories: [],
+ sharingScope: "private",
+ publicShareToken: null,
+ forkedFromSkillId: null,
+ forkedFromCompanyId: null,
+ starCount: 0,
+ installCount: 0,
+ forkCount: 0,
+ currentVersionId: currentVersion.id,
+ metadata: null,
+ createdAt: new Date("2026-01-01T00:00:00Z"),
+ updatedAt: new Date("2026-01-02T00:00:00Z"),
+ attachedAgentCount: 0,
+ usedByAgents: [],
+ editable: true,
+ editableReason: null,
+ sourceLabel: "Local",
+ sourceBadge: "local",
+ sourcePath: null,
+ currentVersion,
+ starredByCurrentActor: false,
+ };
+}
+
+async function renderSkillDetail(versions: CompanySkillVersion[]) {
+ container = document.createElement("div");
+ document.body.appendChild(container);
+ root = createRoot(container);
+
+ await act(async () => {
+ root?.render(
+ ,
+ );
+ });
+
+ return container;
+}
+
+function buttonsNamed(node: ParentNode, name: string) {
+ return Array.from(node.querySelectorAll("button")).filter((button) => button.textContent?.trim() === name);
+}
+
+async function click(button: HTMLButtonElement) {
+ await act(async () => {
+ button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ });
+}
+
+describe("getSkillVersionDiffSelection", () => {
+ it("selects the previous saved revision or the initial baseline for row diffs", () => {
+ const v1 = makeVersion(1, "first");
+ const v2 = makeVersion(2, "second");
+
+ expect(getSkillVersionDiffSelection([v2, v1], v2.id)).toEqual({
+ leftVersionId: v1.id,
+ rightVersionId: v2.id,
+ });
+ expect(getSkillVersionDiffSelection([v2, v1], v1.id)).toEqual({
+ leftVersionId: null,
+ rightVersionId: v1.id,
+ });
+ });
+});
+
+describe("SkillDetailPage versions tab", () => {
+ it("opens per-row version diffs for newest and oldest revisions", async () => {
+ const v1 = makeVersion(1, "# Demo Skill\n\nFirst line");
+ const v2 = makeVersion(2, "# Demo Skill\n\nSecond line");
+ const node = await renderSkillDetail([v2, v1]);
+ const viewDiffButtons = buttonsNamed(node, "View diff") as HTMLButtonElement[];
+
+ expect(viewDiffButtons).toHaveLength(2);
+
+ await click(viewDiffButtons[0]!);
+ let dialog = node.querySelector('[role="dialog"]') as HTMLElement;
+ let selects = Array.from(dialog.querySelectorAll("select"));
+
+ expect(dialog.textContent).toContain("Diff");
+ expect(selects[0]?.value).toBe(v1.id);
+ expect(selects[1]?.value).toBe(v2.id);
+ expect(dialog.textContent).toContain("Second line");
+
+ await click(viewDiffButtons[1]!);
+ dialog = node.querySelector('[role="dialog"]') as HTMLElement;
+ selects = Array.from(dialog.querySelectorAll("select"));
+
+ expect(selects[0]?.value).toBe("");
+ expect(selects[1]?.value).toBe(v1.id);
+ expect(dialog.textContent).toContain("Initial");
+ expect(dialog.textContent).toContain("First line");
+ expect(dialog.textContent).not.toContain("Both sides are the same version");
+ });
+});