mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 08:48:37 +00:00
refactor(tests): dedupe browser and telegram tool test fixtures
This commit is contained in:
@@ -108,16 +108,33 @@ function mockSingleBrowserProxyNode() {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("browser tool snapshot maxChars", () => {
|
function resetBrowserToolMocks() {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
||||||
|
nodesUtilsMocks.listNodes.mockResolvedValue([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerBrowserToolAfterEachReset() {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
resetBrowserToolMocks();
|
||||||
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
|
||||||
nodesUtilsMocks.listNodes.mockResolvedValue([]);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runSnapshotToolCall(params: {
|
||||||
|
snapshotFormat: "ai" | "aria";
|
||||||
|
refs?: "aria" | "dom";
|
||||||
|
maxChars?: number;
|
||||||
|
profile?: string;
|
||||||
|
}) {
|
||||||
|
const tool = createBrowserTool();
|
||||||
|
await tool.execute?.("call-1", { action: "snapshot", ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("browser tool snapshot maxChars", () => {
|
||||||
|
registerBrowserToolAfterEachReset();
|
||||||
|
|
||||||
it("applies the default ai snapshot limit", async () => {
|
it("applies the default ai snapshot limit", async () => {
|
||||||
const tool = createBrowserTool();
|
await runSnapshotToolCall({ snapshotFormat: "ai" });
|
||||||
await tool.execute?.("call-1", { action: "snapshot", snapshotFormat: "ai" });
|
|
||||||
|
|
||||||
expect(browserClientMocks.browserSnapshot).toHaveBeenCalledWith(
|
expect(browserClientMocks.browserSnapshot).toHaveBeenCalledWith(
|
||||||
undefined,
|
undefined,
|
||||||
@@ -184,8 +201,7 @@ describe("browser tool snapshot maxChars", () => {
|
|||||||
configMocks.loadConfig.mockReturnValue({
|
configMocks.loadConfig.mockReturnValue({
|
||||||
browser: { snapshotDefaults: { mode: "efficient" } },
|
browser: { snapshotDefaults: { mode: "efficient" } },
|
||||||
});
|
});
|
||||||
const tool = createBrowserTool();
|
await runSnapshotToolCall({ snapshotFormat: "ai" });
|
||||||
await tool.execute?.("call-1", { action: "snapshot", snapshotFormat: "ai" });
|
|
||||||
|
|
||||||
expect(browserClientMocks.browserSnapshot).toHaveBeenCalledWith(
|
expect(browserClientMocks.browserSnapshot).toHaveBeenCalledWith(
|
||||||
undefined,
|
undefined,
|
||||||
@@ -263,11 +279,7 @@ describe("browser tool snapshot maxChars", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("browser tool url alias support", () => {
|
describe("browser tool url alias support", () => {
|
||||||
afterEach(() => {
|
registerBrowserToolAfterEachReset();
|
||||||
vi.clearAllMocks();
|
|
||||||
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
|
||||||
nodesUtilsMocks.listNodes.mockResolvedValue([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("accepts url alias for open", async () => {
|
it("accepts url alias for open", async () => {
|
||||||
const tool = createBrowserTool();
|
const tool = createBrowserTool();
|
||||||
@@ -308,11 +320,7 @@ describe("browser tool url alias support", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("browser tool act compatibility", () => {
|
describe("browser tool act compatibility", () => {
|
||||||
afterEach(() => {
|
registerBrowserToolAfterEachReset();
|
||||||
vi.clearAllMocks();
|
|
||||||
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
|
||||||
nodesUtilsMocks.listNodes.mockResolvedValue([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("accepts flattened act params for backward compatibility", async () => {
|
it("accepts flattened act params for backward compatibility", async () => {
|
||||||
const tool = createBrowserTool();
|
const tool = createBrowserTool();
|
||||||
@@ -364,10 +372,7 @@ describe("browser tool act compatibility", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("browser tool snapshot labels", () => {
|
describe("browser tool snapshot labels", () => {
|
||||||
afterEach(() => {
|
registerBrowserToolAfterEachReset();
|
||||||
vi.clearAllMocks();
|
|
||||||
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns image + text when labels are requested", async () => {
|
it("returns image + text when labels are requested", async () => {
|
||||||
const tool = createBrowserTool();
|
const tool = createBrowserTool();
|
||||||
@@ -409,11 +414,7 @@ describe("browser tool snapshot labels", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("browser tool external content wrapping", () => {
|
describe("browser tool external content wrapping", () => {
|
||||||
afterEach(() => {
|
registerBrowserToolAfterEachReset();
|
||||||
vi.clearAllMocks();
|
|
||||||
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
|
||||||
nodesUtilsMocks.listNodes.mockResolvedValue([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("wraps aria snapshots as external content", async () => {
|
it("wraps aria snapshots as external content", async () => {
|
||||||
browserClientMocks.browserSnapshot.mockResolvedValueOnce({
|
browserClientMocks.browserSnapshot.mockResolvedValueOnce({
|
||||||
@@ -525,11 +526,7 @@ describe("browser tool external content wrapping", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("browser tool act stale target recovery", () => {
|
describe("browser tool act stale target recovery", () => {
|
||||||
afterEach(() => {
|
registerBrowserToolAfterEachReset();
|
||||||
vi.clearAllMocks();
|
|
||||||
configMocks.loadConfig.mockReturnValue({ browser: {} });
|
|
||||||
nodesUtilsMocks.listNodes.mockResolvedValue([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("retries chrome act once without targetId when tab id is stale", async () => {
|
it("retries chrome act once without targetId when tab id is stale", async () => {
|
||||||
browserActionsMocks.browserAct
|
browserActionsMocks.browserAct
|
||||||
|
|||||||
@@ -51,6 +51,22 @@ describe("handleTelegramAction", () => {
|
|||||||
} as OpenClawConfig;
|
} as OpenClawConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendInlineButtonsMessage(params: {
|
||||||
|
to: string;
|
||||||
|
buttons: Array<Array<{ text: string; callback_data: string; style?: string }>>;
|
||||||
|
inlineButtons: "dm" | "group" | "all";
|
||||||
|
}) {
|
||||||
|
await handleTelegramAction(
|
||||||
|
{
|
||||||
|
action: "sendMessage",
|
||||||
|
to: params.to,
|
||||||
|
content: "Choose",
|
||||||
|
buttons: params.buttons,
|
||||||
|
},
|
||||||
|
telegramConfig({ capabilities: { inlineButtons: params.inlineButtons } }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function expectReactionAdded(reactionLevel: "minimal" | "extensive") {
|
async function expectReactionAdded(reactionLevel: "minimal" | "extensive") {
|
||||||
await handleTelegramAction(defaultReactionAction, reactionConfig(reactionLevel));
|
await handleTelegramAction(defaultReactionAction, reactionConfig(reactionLevel));
|
||||||
expect(reactMessageTelegram).toHaveBeenCalledWith(
|
expect(reactMessageTelegram).toHaveBeenCalledWith(
|
||||||
@@ -103,9 +119,6 @@ describe("handleTelegramAction", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("accepts snake_case message_id for reactions", async () => {
|
it("accepts snake_case message_id for reactions", async () => {
|
||||||
const cfg = {
|
|
||||||
channels: { telegram: { botToken: "tok", reactionLevel: "minimal" } },
|
|
||||||
} as OpenClawConfig;
|
|
||||||
await handleTelegramAction(
|
await handleTelegramAction(
|
||||||
{
|
{
|
||||||
action: "react",
|
action: "react",
|
||||||
@@ -113,7 +126,7 @@ describe("handleTelegramAction", () => {
|
|||||||
message_id: "456",
|
message_id: "456",
|
||||||
emoji: "✅",
|
emoji: "✅",
|
||||||
},
|
},
|
||||||
cfg,
|
reactionConfig("minimal"),
|
||||||
);
|
);
|
||||||
expect(reactMessageTelegram).toHaveBeenCalledWith(
|
expect(reactMessageTelegram).toHaveBeenCalledWith(
|
||||||
"123",
|
"123",
|
||||||
@@ -143,9 +156,6 @@ describe("handleTelegramAction", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("removes reactions on empty emoji", async () => {
|
it("removes reactions on empty emoji", async () => {
|
||||||
const cfg = {
|
|
||||||
channels: { telegram: { botToken: "tok", reactionLevel: "minimal" } },
|
|
||||||
} as OpenClawConfig;
|
|
||||||
await handleTelegramAction(
|
await handleTelegramAction(
|
||||||
{
|
{
|
||||||
action: "react",
|
action: "react",
|
||||||
@@ -153,7 +163,7 @@ describe("handleTelegramAction", () => {
|
|||||||
messageId: "456",
|
messageId: "456",
|
||||||
emoji: "",
|
emoji: "",
|
||||||
},
|
},
|
||||||
cfg,
|
reactionConfig("minimal"),
|
||||||
);
|
);
|
||||||
expect(reactMessageTelegram).toHaveBeenCalledWith(
|
expect(reactMessageTelegram).toHaveBeenCalledWith(
|
||||||
"123",
|
"123",
|
||||||
@@ -476,44 +486,29 @@ describe("handleTelegramAction", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("allows inline buttons in DMs with tg: prefixed targets", async () => {
|
it("allows inline buttons in DMs with tg: prefixed targets", async () => {
|
||||||
const cfg = telegramConfig({ capabilities: { inlineButtons: "dm" } });
|
await sendInlineButtonsMessage({
|
||||||
await handleTelegramAction(
|
to: "tg:5232990709",
|
||||||
{
|
buttons: [[{ text: "Ok", callback_data: "cmd:ok" }]],
|
||||||
action: "sendMessage",
|
inlineButtons: "dm",
|
||||||
to: "tg:5232990709",
|
});
|
||||||
content: "Choose",
|
|
||||||
buttons: [[{ text: "Ok", callback_data: "cmd:ok" }]],
|
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
);
|
|
||||||
expect(sendMessageTelegram).toHaveBeenCalled();
|
expect(sendMessageTelegram).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows inline buttons in groups with topic targets", async () => {
|
it("allows inline buttons in groups with topic targets", async () => {
|
||||||
const cfg = telegramConfig({ capabilities: { inlineButtons: "group" } });
|
await sendInlineButtonsMessage({
|
||||||
await handleTelegramAction(
|
to: "telegram:group:-1001234567890:topic:456",
|
||||||
{
|
buttons: [[{ text: "Ok", callback_data: "cmd:ok" }]],
|
||||||
action: "sendMessage",
|
inlineButtons: "group",
|
||||||
to: "telegram:group:-1001234567890:topic:456",
|
});
|
||||||
content: "Choose",
|
|
||||||
buttons: [[{ text: "Ok", callback_data: "cmd:ok" }]],
|
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
);
|
|
||||||
expect(sendMessageTelegram).toHaveBeenCalled();
|
expect(sendMessageTelegram).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sends messages with inline keyboard buttons when enabled", async () => {
|
it("sends messages with inline keyboard buttons when enabled", async () => {
|
||||||
const cfg = telegramConfig({ capabilities: { inlineButtons: "all" } });
|
await sendInlineButtonsMessage({
|
||||||
await handleTelegramAction(
|
to: "@testchannel",
|
||||||
{
|
buttons: [[{ text: " Option A ", callback_data: " cmd:a " }]],
|
||||||
action: "sendMessage",
|
inlineButtons: "all",
|
||||||
to: "@testchannel",
|
});
|
||||||
content: "Choose",
|
|
||||||
buttons: [[{ text: " Option A ", callback_data: " cmd:a " }]],
|
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
);
|
|
||||||
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
||||||
"@testchannel",
|
"@testchannel",
|
||||||
"Choose",
|
"Choose",
|
||||||
@@ -524,24 +519,19 @@ describe("handleTelegramAction", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("forwards optional button style", async () => {
|
it("forwards optional button style", async () => {
|
||||||
const cfg = telegramConfig({ capabilities: { inlineButtons: "all" } });
|
await sendInlineButtonsMessage({
|
||||||
await handleTelegramAction(
|
to: "@testchannel",
|
||||||
{
|
inlineButtons: "all",
|
||||||
action: "sendMessage",
|
buttons: [
|
||||||
to: "@testchannel",
|
[
|
||||||
content: "Choose",
|
{
|
||||||
buttons: [
|
text: "Option A",
|
||||||
[
|
callback_data: "cmd:a",
|
||||||
{
|
style: "primary",
|
||||||
text: "Option A",
|
},
|
||||||
callback_data: "cmd:a",
|
|
||||||
style: "primary",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
},
|
],
|
||||||
cfg,
|
});
|
||||||
);
|
|
||||||
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
||||||
"@testchannel",
|
"@testchannel",
|
||||||
"Choose",
|
"Choose",
|
||||||
@@ -601,6 +591,25 @@ describe("readTelegramButtons", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("handleTelegramAction per-account gating", () => {
|
describe("handleTelegramAction per-account gating", () => {
|
||||||
|
function accountTelegramConfig(params: {
|
||||||
|
accounts: Record<
|
||||||
|
string,
|
||||||
|
{ botToken: string; actions?: { sticker?: boolean; reactions?: boolean } }
|
||||||
|
>;
|
||||||
|
topLevelBotToken?: string;
|
||||||
|
topLevelActions?: { reactions?: boolean };
|
||||||
|
}): OpenClawConfig {
|
||||||
|
return {
|
||||||
|
channels: {
|
||||||
|
telegram: {
|
||||||
|
...(params.topLevelBotToken ? { botToken: params.topLevelBotToken } : {}),
|
||||||
|
...(params.topLevelActions ? { actions: params.topLevelActions } : {}),
|
||||||
|
accounts: params.accounts,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
}
|
||||||
|
|
||||||
async function expectAccountStickerSend(cfg: OpenClawConfig, accountId = "media") {
|
async function expectAccountStickerSend(cfg: OpenClawConfig, accountId = "media") {
|
||||||
await handleTelegramAction(
|
await handleTelegramAction(
|
||||||
{ action: "sendSticker", to: "123", fileId: "sticker-id", accountId },
|
{ action: "sendSticker", to: "123", fileId: "sticker-id", accountId },
|
||||||
@@ -614,15 +623,11 @@ describe("handleTelegramAction per-account gating", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it("allows sticker when account config enables it", async () => {
|
it("allows sticker when account config enables it", async () => {
|
||||||
const cfg = {
|
const cfg = accountTelegramConfig({
|
||||||
channels: {
|
accounts: {
|
||||||
telegram: {
|
media: { botToken: "tok-media", actions: { sticker: true } },
|
||||||
accounts: {
|
|
||||||
media: { botToken: "tok-media", actions: { sticker: true } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
} as OpenClawConfig;
|
});
|
||||||
await expectAccountStickerSend(cfg);
|
await expectAccountStickerSend(cfg);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -647,30 +652,22 @@ describe("handleTelegramAction per-account gating", () => {
|
|||||||
|
|
||||||
it("uses account-merged config, not top-level config", async () => {
|
it("uses account-merged config, not top-level config", async () => {
|
||||||
// Top-level has no sticker enabled, but the account does
|
// Top-level has no sticker enabled, but the account does
|
||||||
const cfg = {
|
const cfg = accountTelegramConfig({
|
||||||
channels: {
|
topLevelBotToken: "tok-base",
|
||||||
telegram: {
|
accounts: {
|
||||||
botToken: "tok-base",
|
media: { botToken: "tok-media", actions: { sticker: true } },
|
||||||
accounts: {
|
|
||||||
media: { botToken: "tok-media", actions: { sticker: true } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
} as OpenClawConfig;
|
});
|
||||||
await expectAccountStickerSend(cfg);
|
await expectAccountStickerSend(cfg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("inherits top-level reaction gate when account overrides sticker only", async () => {
|
it("inherits top-level reaction gate when account overrides sticker only", async () => {
|
||||||
const cfg = {
|
const cfg = accountTelegramConfig({
|
||||||
channels: {
|
topLevelActions: { reactions: false },
|
||||||
telegram: {
|
accounts: {
|
||||||
actions: { reactions: false },
|
media: { botToken: "tok-media", actions: { sticker: true } },
|
||||||
accounts: {
|
|
||||||
media: { botToken: "tok-media", actions: { sticker: true } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
} as OpenClawConfig;
|
});
|
||||||
|
|
||||||
const result = await handleTelegramAction(
|
const result = await handleTelegramAction(
|
||||||
{
|
{
|
||||||
@@ -689,16 +686,12 @@ describe("handleTelegramAction per-account gating", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("allows account to explicitly re-enable top-level disabled reaction gate", async () => {
|
it("allows account to explicitly re-enable top-level disabled reaction gate", async () => {
|
||||||
const cfg = {
|
const cfg = accountTelegramConfig({
|
||||||
channels: {
|
topLevelActions: { reactions: false },
|
||||||
telegram: {
|
accounts: {
|
||||||
actions: { reactions: false },
|
media: { botToken: "tok-media", actions: { sticker: true, reactions: true } },
|
||||||
accounts: {
|
|
||||||
media: { botToken: "tok-media", actions: { sticker: true, reactions: true } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
} as OpenClawConfig;
|
});
|
||||||
|
|
||||||
await handleTelegramAction(
|
await handleTelegramAction(
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user