test: cover browser snapshot labels and efficient mode
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
- Security: add detect-secrets CI scan and baseline guidance. (#227) — thanks @Hyaxia.
|
- Security: add detect-secrets CI scan and baseline guidance. (#227) — thanks @Hyaxia.
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
- Browser: add tests for snapshot labels/efficient query params and labeled image responses.
|
||||||
- Doctor: avoid re-adding WhatsApp config when only legacy ack reactions are set. (#927, fixes #900) — thanks @grp06.
|
- Doctor: avoid re-adding WhatsApp config when only legacy ack reactions are set. (#927, fixes #900) — thanks @grp06.
|
||||||
- Agents: scrub tuple `items` schemas for Gemini tool calls. (#926, fixes #746) — thanks @grp06.
|
- Agents: scrub tuple `items` schemas for Gemini tool calls. (#926, fixes #746) — thanks @grp06.
|
||||||
- Embedded runner: suppress raw API error payloads from replies. (#924) — thanks @grp06.
|
- Embedded runner: suppress raw API error payloads from replies. (#924) — thanks @grp06.
|
||||||
|
|||||||
@@ -80,14 +80,9 @@ function resolveProviderToolPolicy(params: {
|
|||||||
const normalizedProvider = normalizeProviderKey(provider);
|
const normalizedProvider = normalizeProviderKey(provider);
|
||||||
const rawModelId = params.modelId?.trim().toLowerCase();
|
const rawModelId = params.modelId?.trim().toLowerCase();
|
||||||
const fullModelId =
|
const fullModelId =
|
||||||
rawModelId && !rawModelId.includes("/")
|
rawModelId && !rawModelId.includes("/") ? `${normalizedProvider}/${rawModelId}` : rawModelId;
|
||||||
? `${normalizedProvider}/${rawModelId}`
|
|
||||||
: rawModelId;
|
|
||||||
|
|
||||||
const candidates = [
|
const candidates = [...(fullModelId ? [fullModelId] : []), normalizedProvider];
|
||||||
...(fullModelId ? [fullModelId] : []),
|
|
||||||
normalizedProvider,
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const key of candidates) {
|
for (const key of candidates) {
|
||||||
const match = lookup.get(key);
|
const match = lookup.get(key);
|
||||||
|
|||||||
@@ -125,8 +125,7 @@ export function createClawdbotCodingTools(options?: {
|
|||||||
});
|
});
|
||||||
const profilePolicy = resolveToolProfilePolicy(profile);
|
const profilePolicy = resolveToolProfilePolicy(profile);
|
||||||
const providerProfilePolicy = resolveToolProfilePolicy(providerProfile);
|
const providerProfilePolicy = resolveToolProfilePolicy(providerProfile);
|
||||||
const scopeKey =
|
const scopeKey = options?.exec?.scopeKey ?? (agentId ? `agent:${agentId}` : undefined);
|
||||||
options?.exec?.scopeKey ?? (agentId ? `agent:${agentId}` : undefined);
|
|
||||||
const subagentPolicy =
|
const subagentPolicy =
|
||||||
isSubagentSessionKey(options?.sessionKey) && options?.sessionKey
|
isSubagentSessionKey(options?.sessionKey) && options?.sessionKey
|
||||||
? resolveSubagentToolPolicy(options.config)
|
? resolveSubagentToolPolicy(options.config)
|
||||||
@@ -240,9 +239,7 @@ export function createClawdbotCodingTools(options?: {
|
|||||||
hasRepliedRef: options?.hasRepliedRef,
|
hasRepliedRef: options?.hasRepliedRef,
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
const toolsFiltered = profilePolicy
|
const toolsFiltered = profilePolicy ? filterToolsByPolicy(tools, profilePolicy) : tools;
|
||||||
? filterToolsByPolicy(tools, profilePolicy)
|
|
||||||
: tools;
|
|
||||||
const providerProfileFiltered = providerProfilePolicy
|
const providerProfileFiltered = providerProfilePolicy
|
||||||
? filterToolsByPolicy(toolsFiltered, providerProfilePolicy)
|
? filterToolsByPolicy(toolsFiltered, providerProfilePolicy)
|
||||||
: toolsFiltered;
|
: toolsFiltered;
|
||||||
|
|||||||
@@ -52,6 +52,17 @@ vi.mock("../../config/config.js", () => ({
|
|||||||
loadConfig: vi.fn(() => ({ browser: {} })),
|
loadConfig: vi.fn(() => ({ browser: {} })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const toolCommonMocks = vi.hoisted(() => ({
|
||||||
|
imageResultFromFile: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.mock("./common.js", async () => {
|
||||||
|
const actual = await vi.importActual<typeof import("./common.js")>("./common.js");
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
imageResultFromFile: toolCommonMocks.imageResultFromFile,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
import { DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "../../browser/constants.js";
|
import { DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "../../browser/constants.js";
|
||||||
import { createBrowserTool } from "./browser-tool.js";
|
import { createBrowserTool } from "./browser-tool.js";
|
||||||
|
|
||||||
@@ -103,3 +114,47 @@ describe("browser tool snapshot maxChars", () => {
|
|||||||
expect(Object.hasOwn(opts ?? {}, "maxChars")).toBe(false);
|
expect(Object.hasOwn(opts ?? {}, "maxChars")).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("browser tool snapshot labels", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns image + text when labels are requested", async () => {
|
||||||
|
const tool = createBrowserTool();
|
||||||
|
const imageResult = {
|
||||||
|
content: [
|
||||||
|
{ type: "text", text: "label text" },
|
||||||
|
{ type: "image", data: "base64", mimeType: "image/png" },
|
||||||
|
],
|
||||||
|
details: { path: "/tmp/snap.png" },
|
||||||
|
};
|
||||||
|
|
||||||
|
toolCommonMocks.imageResultFromFile.mockResolvedValueOnce(imageResult);
|
||||||
|
browserClientMocks.browserSnapshot.mockResolvedValueOnce({
|
||||||
|
ok: true,
|
||||||
|
format: "ai",
|
||||||
|
targetId: "t1",
|
||||||
|
url: "https://example.com",
|
||||||
|
snapshot: "label text",
|
||||||
|
imagePath: "/tmp/snap.png",
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await tool.execute?.(null, {
|
||||||
|
action: "snapshot",
|
||||||
|
format: "ai",
|
||||||
|
labels: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(toolCommonMocks.imageResultFromFile).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
path: "/tmp/snap.png",
|
||||||
|
extraText: "label text",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(result).toEqual(imageResult);
|
||||||
|
expect(result?.content).toHaveLength(2);
|
||||||
|
expect(result?.content?.[0]).toMatchObject({ type: "text", text: "label text" });
|
||||||
|
expect(result?.content?.[1]).toMatchObject({ type: "image" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -49,6 +49,40 @@ describe("browser client", () => {
|
|||||||
).rejects.toThrow(/409: conflict/i);
|
).rejects.toThrow(/409: conflict/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("adds labels + efficient mode query params to snapshots", async () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
vi.stubGlobal(
|
||||||
|
"fetch",
|
||||||
|
vi.fn(async (url: string) => {
|
||||||
|
calls.push(url);
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({
|
||||||
|
ok: true,
|
||||||
|
format: "ai",
|
||||||
|
targetId: "t1",
|
||||||
|
url: "https://x",
|
||||||
|
snapshot: "ok",
|
||||||
|
}),
|
||||||
|
} as unknown as Response;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
browserSnapshot("http://127.0.0.1:18791", {
|
||||||
|
format: "ai",
|
||||||
|
labels: true,
|
||||||
|
mode: "efficient",
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({ ok: true, format: "ai" });
|
||||||
|
|
||||||
|
const snapshotCall = calls.find((url) => url.includes("/snapshot?"));
|
||||||
|
expect(snapshotCall).toBeTruthy();
|
||||||
|
const parsed = new URL(snapshotCall as string);
|
||||||
|
expect(parsed.searchParams.get("labels")).toBe("1");
|
||||||
|
expect(parsed.searchParams.get("mode")).toBe("efficient");
|
||||||
|
});
|
||||||
|
|
||||||
it("uses the expected endpoints + methods for common calls", async () => {
|
it("uses the expected endpoints + methods for common calls", async () => {
|
||||||
const calls: Array<{ url: string; init?: RequestInit }> = [];
|
const calls: Array<{ url: string; init?: RequestInit }> = [];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user