mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 23:38:27 +00:00
test: dedupe channel and transport adapters
This commit is contained in:
@@ -77,8 +77,8 @@ Table 2:
|
||||
});
|
||||
|
||||
describe("extractCodeBlocks", () => {
|
||||
it("extracts a code block with language", () => {
|
||||
const text = `Here is some code:
|
||||
it("extracts code blocks across language/no-language/multiple variants", () => {
|
||||
const withLanguage = `Here is some code:
|
||||
|
||||
\`\`\`javascript
|
||||
const x = 1;
|
||||
@@ -86,31 +86,23 @@ console.log(x);
|
||||
\`\`\`
|
||||
|
||||
And more text.`;
|
||||
const withLanguageResult = extractCodeBlocks(withLanguage);
|
||||
expect(withLanguageResult.codeBlocks).toHaveLength(1);
|
||||
expect(withLanguageResult.codeBlocks[0].language).toBe("javascript");
|
||||
expect(withLanguageResult.codeBlocks[0].code).toBe("const x = 1;\nconsole.log(x);");
|
||||
expect(withLanguageResult.textWithoutCode).toContain("Here is some code:");
|
||||
expect(withLanguageResult.textWithoutCode).toContain("And more text.");
|
||||
expect(withLanguageResult.textWithoutCode).not.toContain("```");
|
||||
|
||||
const { codeBlocks, textWithoutCode } = extractCodeBlocks(text);
|
||||
|
||||
expect(codeBlocks).toHaveLength(1);
|
||||
expect(codeBlocks[0].language).toBe("javascript");
|
||||
expect(codeBlocks[0].code).toBe("const x = 1;\nconsole.log(x);");
|
||||
expect(textWithoutCode).toContain("Here is some code:");
|
||||
expect(textWithoutCode).toContain("And more text.");
|
||||
expect(textWithoutCode).not.toContain("```");
|
||||
});
|
||||
|
||||
it("extracts a code block without language", () => {
|
||||
const text = `\`\`\`
|
||||
const withoutLanguage = `\`\`\`
|
||||
plain code
|
||||
\`\`\``;
|
||||
const withoutLanguageResult = extractCodeBlocks(withoutLanguage);
|
||||
expect(withoutLanguageResult.codeBlocks).toHaveLength(1);
|
||||
expect(withoutLanguageResult.codeBlocks[0].language).toBeUndefined();
|
||||
expect(withoutLanguageResult.codeBlocks[0].code).toBe("plain code");
|
||||
|
||||
const { codeBlocks } = extractCodeBlocks(text);
|
||||
|
||||
expect(codeBlocks).toHaveLength(1);
|
||||
expect(codeBlocks[0].language).toBeUndefined();
|
||||
expect(codeBlocks[0].code).toBe("plain code");
|
||||
});
|
||||
|
||||
it("extracts multiple code blocks", () => {
|
||||
const text = `\`\`\`python
|
||||
const multiple = `\`\`\`python
|
||||
print("hello")
|
||||
\`\`\`
|
||||
|
||||
@@ -119,12 +111,10 @@ Some text
|
||||
\`\`\`bash
|
||||
echo "world"
|
||||
\`\`\``;
|
||||
|
||||
const { codeBlocks } = extractCodeBlocks(text);
|
||||
|
||||
expect(codeBlocks).toHaveLength(2);
|
||||
expect(codeBlocks[0].language).toBe("python");
|
||||
expect(codeBlocks[1].language).toBe("bash");
|
||||
const multipleResult = extractCodeBlocks(multiple);
|
||||
expect(multipleResult.codeBlocks).toHaveLength(2);
|
||||
expect(multipleResult.codeBlocks[0].language).toBe("python");
|
||||
expect(multipleResult.codeBlocks[1].language).toBe("bash");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -142,27 +132,20 @@ describe("extractLinks", () => {
|
||||
});
|
||||
|
||||
describe("stripMarkdown", () => {
|
||||
it("strips bold markers", () => {
|
||||
expect(stripMarkdown("This is **bold** text")).toBe("This is bold text");
|
||||
expect(stripMarkdown("This is __bold__ text")).toBe("This is bold text");
|
||||
});
|
||||
|
||||
it("strips italic markers", () => {
|
||||
expect(stripMarkdown("This is *italic* text")).toBe("This is italic text");
|
||||
expect(stripMarkdown("This is _italic_ text")).toBe("This is italic text");
|
||||
});
|
||||
|
||||
it("strips strikethrough markers", () => {
|
||||
expect(stripMarkdown("This is ~~deleted~~ text")).toBe("This is deleted text");
|
||||
});
|
||||
|
||||
it("removes horizontal rules", () => {
|
||||
expect(stripMarkdown("Above\n---\nBelow")).toBe("Above\n\nBelow");
|
||||
expect(stripMarkdown("Above\n***\nBelow")).toBe("Above\n\nBelow");
|
||||
});
|
||||
|
||||
it("strips inline code markers", () => {
|
||||
expect(stripMarkdown("Use `const` keyword")).toBe("Use const keyword");
|
||||
it("strips inline markdown marker variants", () => {
|
||||
const cases = [
|
||||
["strips bold **", "This is **bold** text", "This is bold text"],
|
||||
["strips bold __", "This is __bold__ text", "This is bold text"],
|
||||
["strips italic *", "This is *italic* text", "This is italic text"],
|
||||
["strips italic _", "This is _italic_ text", "This is italic text"],
|
||||
["strips strikethrough", "This is ~~deleted~~ text", "This is deleted text"],
|
||||
["removes hr ---", "Above\n---\nBelow", "Above\n\nBelow"],
|
||||
["removes hr ***", "Above\n***\nBelow", "Above\n\nBelow"],
|
||||
["strips inline code markers", "Use `const` keyword", "Use const keyword"],
|
||||
] as const;
|
||||
for (const [name, input, expected] of cases) {
|
||||
expect(stripMarkdown(input), name).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it("handles complex markdown", () => {
|
||||
|
||||
@@ -9,18 +9,19 @@ import {
|
||||
} from "./rich-menu.js";
|
||||
|
||||
describe("messageAction", () => {
|
||||
it("creates a message action", () => {
|
||||
const action = messageAction("Help", "/help");
|
||||
|
||||
expect(action.type).toBe("message");
|
||||
expect(action.label).toBe("Help");
|
||||
expect((action as { text: string }).text).toBe("/help");
|
||||
});
|
||||
|
||||
it("uses label as text when text not provided", () => {
|
||||
const action = messageAction("Click");
|
||||
|
||||
expect((action as { text: string }).text).toBe("Click");
|
||||
it("creates message actions with explicit or default text", () => {
|
||||
const cases = [
|
||||
{ name: "explicit text", label: "Help", text: "/help", expectedText: "/help" },
|
||||
{ name: "defaults to label", label: "Click", text: undefined, expectedText: "Click" },
|
||||
] as const;
|
||||
for (const testCase of cases) {
|
||||
const action = testCase.text
|
||||
? messageAction(testCase.label, testCase.text)
|
||||
: messageAction(testCase.label);
|
||||
expect(action.type, testCase.name).toBe("message");
|
||||
expect(action.label, testCase.name).toBe(testCase.label);
|
||||
expect((action as { text: string }).text, testCase.name).toBe(testCase.expectedText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,47 +62,32 @@ describe("postbackAction", () => {
|
||||
expect((action as { displayText: string }).displayText).toBe("Selected item 1");
|
||||
});
|
||||
|
||||
it("truncates data to 300 characters", () => {
|
||||
const longData = "x".repeat(400);
|
||||
const action = postbackAction("Test", longData);
|
||||
it("applies postback payload truncation and displayText behavior", () => {
|
||||
const truncatedData = postbackAction("Test", "x".repeat(400));
|
||||
expect((truncatedData as { data: string }).data.length).toBe(300);
|
||||
|
||||
expect((action as { data: string }).data.length).toBe(300);
|
||||
});
|
||||
const truncatedDisplay = postbackAction("Test", "data", "y".repeat(400));
|
||||
expect((truncatedDisplay as { displayText: string }).displayText?.length).toBe(300);
|
||||
|
||||
it("truncates displayText to 300 characters", () => {
|
||||
const longText = "y".repeat(400);
|
||||
const action = postbackAction("Test", "data", longText);
|
||||
|
||||
expect((action as { displayText: string }).displayText?.length).toBe(300);
|
||||
});
|
||||
|
||||
it("omits displayText when not provided", () => {
|
||||
const action = postbackAction("Test", "data");
|
||||
|
||||
expect((action as { displayText?: string }).displayText).toBeUndefined();
|
||||
const noDisplayText = postbackAction("Test", "data");
|
||||
expect((noDisplayText as { displayText?: string }).displayText).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("datetimePickerAction", () => {
|
||||
it("creates a date picker action", () => {
|
||||
const action = datetimePickerAction("Pick date", "date_picked", "date");
|
||||
|
||||
expect(action.type).toBe("datetimepicker");
|
||||
expect(action.label).toBe("Pick date");
|
||||
expect((action as { mode: string }).mode).toBe("date");
|
||||
expect((action as { data: string }).data).toBe("date_picked");
|
||||
});
|
||||
|
||||
it("creates a time picker action", () => {
|
||||
const action = datetimePickerAction("Pick time", "time_picked", "time");
|
||||
|
||||
expect((action as { mode: string }).mode).toBe("time");
|
||||
});
|
||||
|
||||
it("creates a datetime picker action", () => {
|
||||
const action = datetimePickerAction("Pick datetime", "datetime_picked", "datetime");
|
||||
|
||||
expect((action as { mode: string }).mode).toBe("datetime");
|
||||
it("creates picker actions for all supported modes", () => {
|
||||
const cases = [
|
||||
{ label: "Pick date", data: "date_picked", mode: "date" as const },
|
||||
{ label: "Pick time", data: "time_picked", mode: "time" as const },
|
||||
{ label: "Pick datetime", data: "datetime_picked", mode: "datetime" as const },
|
||||
];
|
||||
for (const testCase of cases) {
|
||||
const action = datetimePickerAction(testCase.label, testCase.data, testCase.mode);
|
||||
expect(action.type).toBe("datetimepicker");
|
||||
expect(action.label).toBe(testCase.label);
|
||||
expect((action as { mode: string }).mode).toBe(testCase.mode);
|
||||
expect((action as { data: string }).data).toBe(testCase.data);
|
||||
}
|
||||
});
|
||||
|
||||
it("includes initial/min/max when provided", () => {
|
||||
@@ -136,37 +122,22 @@ describe("createGridLayout", () => {
|
||||
];
|
||||
}
|
||||
|
||||
it("creates a 2x3 grid layout for tall menu", () => {
|
||||
it("computes expected 2x3 layout for supported menu heights", () => {
|
||||
const actions = createSixSimpleActions();
|
||||
|
||||
const areas = createGridLayout(1686, actions);
|
||||
|
||||
expect(areas.length).toBe(6);
|
||||
|
||||
// Check first row positions
|
||||
expect(areas[0].bounds.x).toBe(0);
|
||||
expect(areas[0].bounds.y).toBe(0);
|
||||
expect(areas[1].bounds.x).toBe(833);
|
||||
expect(areas[1].bounds.y).toBe(0);
|
||||
expect(areas[2].bounds.x).toBe(1666);
|
||||
expect(areas[2].bounds.y).toBe(0);
|
||||
|
||||
// Check second row positions
|
||||
expect(areas[3].bounds.y).toBe(843);
|
||||
expect(areas[4].bounds.y).toBe(843);
|
||||
expect(areas[5].bounds.y).toBe(843);
|
||||
});
|
||||
|
||||
it("creates a 2x3 grid layout for short menu", () => {
|
||||
const actions = createSixSimpleActions();
|
||||
|
||||
const areas = createGridLayout(843, actions);
|
||||
|
||||
expect(areas.length).toBe(6);
|
||||
|
||||
// Row height should be half of 843
|
||||
expect(areas[0].bounds.height).toBe(421);
|
||||
expect(areas[3].bounds.y).toBe(421);
|
||||
const cases = [
|
||||
{ height: 1686, firstRowY: 0, secondRowY: 843, rowHeight: 843 },
|
||||
{ height: 843, firstRowY: 0, secondRowY: 421, rowHeight: 421 },
|
||||
] as const;
|
||||
for (const testCase of cases) {
|
||||
const areas = createGridLayout(testCase.height, actions);
|
||||
expect(areas.length).toBe(6);
|
||||
expect(areas[0]?.bounds.y).toBe(testCase.firstRowY);
|
||||
expect(areas[0]?.bounds.height).toBe(testCase.rowHeight);
|
||||
expect(areas[3]?.bounds.y).toBe(testCase.secondRowY);
|
||||
expect(areas[0]?.bounds.x).toBe(0);
|
||||
expect(areas[1]?.bounds.x).toBe(833);
|
||||
expect(areas[2]?.bounds.x).toBe(1666);
|
||||
}
|
||||
});
|
||||
|
||||
it("assigns correct actions to areas", () => {
|
||||
@@ -222,17 +193,12 @@ describe("createDefaultMenuConfig", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("has message actions for all areas", () => {
|
||||
it("uses message actions with expected default commands", () => {
|
||||
const config = createDefaultMenuConfig();
|
||||
|
||||
for (const area of config.areas) {
|
||||
expect(area.action.type).toBe("message");
|
||||
}
|
||||
});
|
||||
|
||||
it("has expected default commands", () => {
|
||||
const config = createDefaultMenuConfig();
|
||||
|
||||
const commands = config.areas.map((a) => (a.action as { text: string }).text);
|
||||
expect(commands).toContain("/help");
|
||||
expect(commands).toContain("/status");
|
||||
|
||||
Reference in New Issue
Block a user