Files
oh-my-pi/packages/coding-agent/test/read-tool-group.test.ts
T
can1357 3c1a31062c ux(coding-agent): refined tool renderers with padded status headers
- Updated read-group rendering to use the enabled success mark and omitted duplicate success marks on child files in grouped reads.
- Adjusted find and search renderers to apply tool-title coloring, search icons, and padded layout via createCachedComponent.
- Added tests covering read-group success marking and padded find/search renderer output.
2026-06-07 09:03:09 +02:00

170 lines
5.8 KiB
TypeScript

import { afterEach, beforeAll, describe, expect, it, vi } from "bun:test";
import { getDefault } from "../src/config/settings-schema";
import { ReadToolGroupComponent, readArgsTargetInternalUrl } from "../src/modes/components/read-tool-group";
import * as themeModule from "../src/modes/theme/theme";
describe("ReadToolGroupComponent", () => {
beforeAll(async () => {
await themeModule.initTheme(false, undefined, undefined, "dark", "light");
});
afterEach(() => {
vi.restoreAllMocks();
});
it("keeps inline read previews disabled by default", () => {
expect(getDefault("read.toolResultPreview")).toBe(false);
const component = new ReadToolGroupComponent();
component.updateArgs({ path: "/tmp/example.ts" }, "read-0");
component.updateResult(
{
content: [{ type: "text", text: "line 1\nline 2\nline 3\nline 4" }],
},
false,
"read-0",
);
const rendered = Bun.stripANSI(component.render(120).join("\n"));
expect(rendered).toContain("Read /tmp/example.ts");
expect(rendered).not.toContain("line 1");
expect(rendered.toLowerCase()).not.toContain("ctrl+o");
});
it("uses the read-specific success mark for completed reads", () => {
const component = new ReadToolGroupComponent();
component.updateArgs({ path: "/tmp/example.ts" }, "read-success");
component.updateResult(
{
content: [{ type: "text", text: "line 1" }],
},
false,
"read-success",
);
const rendered = component.render(120).join("\n");
const plain = Bun.stripANSI(rendered);
expect(plain).toContain(themeModule.theme.status.enabled);
expect(plain).not.toContain(themeModule.theme.status.success);
expect(rendered).toContain(themeModule.theme.fg("text", themeModule.theme.status.enabled));
expect(rendered).not.toContain(themeModule.theme.fg("success", themeModule.theme.status.enabled));
});
it("omits duplicate success marks from multi-read child rows", () => {
const component = new ReadToolGroupComponent();
component.updateArgs({ path: "/tmp/one.ts" }, "read-one");
component.updateArgs({ path: "/tmp/two.ts" }, "read-two");
component.updateResult({ content: [{ type: "text", text: "one" }] }, false, "read-one");
component.updateResult({ content: [{ type: "text", text: "two" }] }, false, "read-two");
const plain = Bun.stripANSI(component.render(120).join("\n"));
expect(plain).toContain("Read (2)");
expect(plain).toContain(`${themeModule.theme.tree.branch} /tmp/one.ts`);
expect(plain).toContain(`${themeModule.theme.tree.last} /tmp/two.ts`);
expect(plain).not.toContain(`${themeModule.theme.tree.branch} ${themeModule.theme.status.enabled}`);
expect(plain).not.toContain(`${themeModule.theme.tree.last} ${themeModule.theme.status.enabled}`);
});
it("renders warning previews with warning styling instead of success styling", () => {
const component = new ReadToolGroupComponent({ showContentPreview: true });
component.updateArgs({ path: "/tmp/example.ts" }, "read-1");
component.updateResult(
{
content: [{ type: "text", text: "const a = 1;\nconst b = 2;\nconst c = 3;" }],
details: { suffixResolution: { from: "/tmp/exampl.ts", to: "/tmp/example.ts" } },
},
false,
"read-1",
);
const rendered = Bun.stripANSI(component.render(120).join("\n"));
expect(rendered).toContain(themeModule.theme.status.warning);
expect(rendered).not.toContain(themeModule.theme.status.success);
expect(rendered).toContain("corrected from");
});
it("highlights only the collapsed preview lines", () => {
const highlightSpy = vi.spyOn(themeModule, "highlightCode");
const component = new ReadToolGroupComponent({ showContentPreview: true });
component.updateArgs({ path: "/tmp/example.ts" }, "read-2");
component.updateResult(
{
content: [
{
type: "text",
text: "line 1\nline 2\nline 3\nline 4\nline 5",
},
],
},
false,
"read-2",
);
const rendered = Bun.stripANSI(component.render(120).join("\n"));
const highlightedInput = highlightSpy.mock.calls[0]?.[0];
expect(highlightedInput).toBe("line 1\nline 2\nline 3");
expect(rendered).toContain("line 1");
expect(rendered).not.toContain("line 4");
expect(rendered.toLowerCase()).toContain("ctrl+o");
});
it("does not render a duplicate summary row when inline previews are enabled", () => {
const component = new ReadToolGroupComponent({ showContentPreview: true });
component.updateArgs({ path: "/tmp/example.ts:L10-L20" }, "read-3");
component.updateResult(
{
content: [{ type: "text", text: "line 1\nline 2\nline 3\nline 4" }],
},
false,
"read-3",
);
const rendered = Bun.stripANSI(component.render(120).join("\n"));
const matches = rendered.match(/Read \/tmp\/example\.ts:L10-L20/g) ?? [];
expect(matches).toHaveLength(1);
});
});
describe("readArgsTargetInternalUrl", () => {
it.each([
["skill://my-skill"],
["skill://my-skill/file.md"],
["omp://docs/tools/read.md"],
["issue://123"],
["pr://can1357/oh-my-pi/456"],
["agent://abc"],
["artifact://abc"],
["memory://root"],
["rule://name"],
["mcp://server/resource"],
["local://PLAN.md"],
])("treats %s as an internal URL read", target => {
expect(readArgsTargetInternalUrl({ path: target })).toBe(true);
expect(readArgsTargetInternalUrl({ file_path: target })).toBe(true);
});
it.each([
["/tmp/example.ts"],
["./relative/path.md"],
["https://example.com/file"],
[""],
])("treats %s as a filesystem/external target", target => {
expect(readArgsTargetInternalUrl({ path: target })).toBe(false);
});
it("returns false for non-record / missing arguments", () => {
expect(readArgsTargetInternalUrl(undefined)).toBe(false);
expect(readArgsTargetInternalUrl(null)).toBe(false);
expect(readArgsTargetInternalUrl("skill://x")).toBe(false);
expect(readArgsTargetInternalUrl(["skill://x"])).toBe(false);
expect(readArgsTargetInternalUrl({})).toBe(false);
expect(readArgsTargetInternalUrl({ path: 42 })).toBe(false);
});
});