refactor(dsh-plugin): simplify browser commands

This commit is contained in:
drakezhang
2026-08-29 14:38:48 +08:00
parent 7163f2f9b0
commit 5df93143c5
25 changed files with 934 additions and 590 deletions
@@ -1,17 +1,17 @@
// @vitest-environment happy-dom
// browser_screenshot keyed toolview: view-model derivation from frozen blocks,
// browser_inspect keyed toolview: view-model derivation from frozen blocks,
// image/path-only/running/error rendering, the registration key, and the
// session-bound attachment loader.
import type { RunningToolCall, ToolResultNode } from "@deepseek-ai/dsh-client-runtime/client";
import { cleanup, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { apply } from "../../src/client/index";
import {
ScreenshotToolView,
type ScreenshotToolViewProps,
BrowserInspectToolView,
type BrowserInspectToolViewProps,
viewModelOf,
} from "../../src/client/ScreenshotToolView";
} from "../../src/client/BrowserInspectToolView";
import { apply } from "../../src/client/index";
const ATTACHMENT = {
attachmentId: "sha256:abc123",
@@ -25,7 +25,7 @@ const ATTACHMENT = {
function runningBlock(argsRaw: string): RunningToolCall {
return {
callId: "c1",
name: "browser_screenshot",
name: "browser_inspect",
argsRaw,
turn: 1,
step: 1,
@@ -41,7 +41,7 @@ function settledBlock(argsRaw: string, content: unknown[], isError = false): Too
seq: 1,
time: 0,
callId: "c1",
call: { name: "browser_screenshot", argsRaw },
call: { name: "browser_inspect", argsRaw },
callTime: 0,
content,
isError,
@@ -58,14 +58,14 @@ afterEach(cleanup);
describe("viewModelOf", () => {
it("derives the running model from the call frame", () => {
const model = viewModelOf(runningBlock('{"session":"s1"}'));
const model = viewModelOf(runningBlock('{"action":"screenshot","session":"s1"}'));
expect(model.state).toBe("running");
expect(model.command).toBe("bsk screenshot --session s1");
expect(model.image).toBeNull();
});
it("marks settled results carrying an image block", () => {
const block = settledBlock("{}", [
const block = settledBlock('{"action":"screenshot"}', [
{ type: "text", text: IMAGE_TEXT },
{ type: "image", attachment: ATTACHMENT },
]);
@@ -77,30 +77,56 @@ describe("viewModelOf", () => {
});
it("keeps the path-only form image-free", () => {
const model = viewModelOf(settledBlock("{}", [{ type: "text", text: PATH_TEXT }]));
const model = viewModelOf(
settledBlock('{"action":"screenshot"}', [{ type: "text", text: PATH_TEXT }]),
);
expect(model.state).toBe("ok");
expect(model.image).toBeNull();
expect(model.summary).toBe(PATH_TEXT);
});
it("marks error results", () => {
const model = viewModelOf(settledBlock("{}", [{ type: "text", text: "Error: boom" }], true));
const model = viewModelOf(
settledBlock('{"action":"screenshot"}', [{ type: "text", text: "Error: boom" }], true),
);
expect(model.state).toBe("error");
});
it("renders non-screenshot inspect actions as ordinary terminal calls", () => {
const model = viewModelOf(
settledBlock('{"action":"observe","session":"s1"}', [
{ type: "text", text: "page observation" },
]),
);
expect(model.title).toBe("Observe");
expect(model.command).toBe("bsk observe --session s1");
expect(model.image).toBeNull();
});
it("preserves action-specific arguments in diagnostic command cards", () => {
const model = viewModelOf(
runningBlock(
'{"action":"console","session":"s1","tabId":7,"since":4,"limit":20,"maxTextChars":500,"includeStack":true}',
),
);
expect(model.command).toBe(
"bsk console --session s1 --tab-id 7 --since 4 --limit 20 --max-text-chars 500 --include-stack",
);
});
});
describe("ScreenshotToolView", () => {
const baseProps = { callId: "c1", toolName: "browser_screenshot", openFile: () => {} };
describe("BrowserInspectToolView", () => {
const baseProps = { callId: "c1", toolName: "browser_inspect", openFile: () => {} };
const renderView = (
block: ScreenshotToolViewProps["block"],
loadImage: ScreenshotToolViewProps["loadImage"],
block: BrowserInspectToolViewProps["block"],
loadImage: BrowserInspectToolViewProps["loadImage"],
) => {
const props = { ...baseProps, block, loadImage } as unknown as ScreenshotToolViewProps;
return render(<ScreenshotToolView {...props} />);
const props = { ...baseProps, block, loadImage } as unknown as BrowserInspectToolViewProps;
return render(<BrowserInspectToolView {...props} />);
};
it("renders the screenshot image through the loader", async () => {
const block = settledBlock("{}", [
const block = settledBlock('{"action":"screenshot"}', [
{ type: "text", text: IMAGE_TEXT },
{ type: "image", attachment: ATTACHMENT },
]);
@@ -115,7 +141,7 @@ describe("ScreenshotToolView", () => {
});
it("renders the path-only form without touching the loader", async () => {
const block = settledBlock("{}", [{ type: "text", text: PATH_TEXT }]);
const block = settledBlock('{"action":"screenshot"}', [{ type: "text", text: PATH_TEXT }]);
const loadImage = vi.fn(async () => "blob:unused");
renderView(block, loadImage);
screen.getByRole("button", { name: /screenshot/i }).click();
@@ -125,14 +151,14 @@ describe("ScreenshotToolView", () => {
it("renders the running form without output", () => {
const loadImage = vi.fn();
renderView(runningBlock("{}"), loadImage);
renderView(runningBlock('{"action":"screenshot"}'), loadImage);
expect(screen.getByText("bsk screenshot --session (current)")).toBeTruthy();
expect(loadImage).not.toHaveBeenCalled();
});
});
describe("client plugin registration", () => {
it("registers the browser_screenshot toolview and the shell observation overlay", () => {
it("registers the browser_inspect toolview and the shell observation overlay", () => {
const registrations: { name: string; key?: string; id?: string }[] = [];
const sessions = { binding: () => undefined };
const ctx = {
@@ -150,7 +176,7 @@ describe("client plugin registration", () => {
};
apply(ctx as never);
expect(registrations).toEqual([
{ name: "tool.call.toolview", key: "browser_screenshot" },
{ name: "tool.call.toolview", key: "browser_inspect" },
{ name: "shell.overlay", id: "bsk-observation" },
]);
});