* feat: add LINE plugin (#1630) (thanks @plum-dawg) * feat: complete LINE plugin (#1630) (thanks @plum-dawg) * chore: drop line plugin node_modules (#1630) (thanks @plum-dawg) * test: mock /context report in commands test (#1630) (thanks @plum-dawg) * test: limit macOS CI workers to avoid OOM (#1630) (thanks @plum-dawg) * test: reduce macOS CI vitest workers (#1630) (thanks @plum-dawg) --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
248 lines
7.7 KiB
TypeScript
248 lines
7.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
createGridLayout,
|
|
messageAction,
|
|
uriAction,
|
|
postbackAction,
|
|
datetimePickerAction,
|
|
createDefaultMenuConfig,
|
|
} 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("truncates label to 20 characters", () => {
|
|
const action = messageAction("This is a very long label text");
|
|
|
|
expect(action.label.length).toBe(20);
|
|
expect(action.label).toBe("This is a very long ");
|
|
});
|
|
});
|
|
|
|
describe("uriAction", () => {
|
|
it("creates a URI action", () => {
|
|
const action = uriAction("Open", "https://example.com");
|
|
|
|
expect(action.type).toBe("uri");
|
|
expect(action.label).toBe("Open");
|
|
expect((action as { uri: string }).uri).toBe("https://example.com");
|
|
});
|
|
|
|
it("truncates label to 20 characters", () => {
|
|
const action = uriAction("Click here to visit our website", "https://example.com");
|
|
|
|
expect(action.label.length).toBe(20);
|
|
});
|
|
});
|
|
|
|
describe("postbackAction", () => {
|
|
it("creates a postback action", () => {
|
|
const action = postbackAction("Select", "action=select&item=1", "Selected item 1");
|
|
|
|
expect(action.type).toBe("postback");
|
|
expect(action.label).toBe("Select");
|
|
expect((action as { data: string }).data).toBe("action=select&item=1");
|
|
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);
|
|
|
|
expect((action as { data: string }).data.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();
|
|
});
|
|
});
|
|
|
|
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("includes initial/min/max when provided", () => {
|
|
const action = datetimePickerAction("Pick", "data", "date", {
|
|
initial: "2024-06-15",
|
|
min: "2024-01-01",
|
|
max: "2024-12-31",
|
|
});
|
|
|
|
expect((action as { initial: string }).initial).toBe("2024-06-15");
|
|
expect((action as { min: string }).min).toBe("2024-01-01");
|
|
expect((action as { max: string }).max).toBe("2024-12-31");
|
|
});
|
|
});
|
|
|
|
describe("createGridLayout", () => {
|
|
it("creates a 2x3 grid layout for tall menu", () => {
|
|
const actions = [
|
|
messageAction("A1"),
|
|
messageAction("A2"),
|
|
messageAction("A3"),
|
|
messageAction("A4"),
|
|
messageAction("A5"),
|
|
messageAction("A6"),
|
|
] as [
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
];
|
|
|
|
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 = [
|
|
messageAction("A1"),
|
|
messageAction("A2"),
|
|
messageAction("A3"),
|
|
messageAction("A4"),
|
|
messageAction("A5"),
|
|
messageAction("A6"),
|
|
] as [
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
];
|
|
|
|
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);
|
|
});
|
|
|
|
it("assigns correct actions to areas", () => {
|
|
const actions = [
|
|
messageAction("Help", "/help"),
|
|
messageAction("Status", "/status"),
|
|
messageAction("Settings", "/settings"),
|
|
messageAction("About", "/about"),
|
|
messageAction("Feedback", "/feedback"),
|
|
messageAction("Contact", "/contact"),
|
|
] as [
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
ReturnType<typeof messageAction>,
|
|
];
|
|
|
|
const areas = createGridLayout(843, actions);
|
|
|
|
expect((areas[0].action as { text: string }).text).toBe("/help");
|
|
expect((areas[1].action as { text: string }).text).toBe("/status");
|
|
expect((areas[2].action as { text: string }).text).toBe("/settings");
|
|
expect((areas[3].action as { text: string }).text).toBe("/about");
|
|
expect((areas[4].action as { text: string }).text).toBe("/feedback");
|
|
expect((areas[5].action as { text: string }).text).toBe("/contact");
|
|
});
|
|
});
|
|
|
|
describe("createDefaultMenuConfig", () => {
|
|
it("creates a valid default menu configuration", () => {
|
|
const config = createDefaultMenuConfig();
|
|
|
|
expect(config.size.width).toBe(2500);
|
|
expect(config.size.height).toBe(843);
|
|
expect(config.selected).toBe(false);
|
|
expect(config.name).toBe("Default Menu");
|
|
expect(config.chatBarText).toBe("Menu");
|
|
expect(config.areas.length).toBe(6);
|
|
});
|
|
|
|
it("has valid area bounds", () => {
|
|
const config = createDefaultMenuConfig();
|
|
|
|
for (const area of config.areas) {
|
|
expect(area.bounds.x).toBeGreaterThanOrEqual(0);
|
|
expect(area.bounds.y).toBeGreaterThanOrEqual(0);
|
|
expect(area.bounds.width).toBeGreaterThan(0);
|
|
expect(area.bounds.height).toBeGreaterThan(0);
|
|
expect(area.bounds.x + area.bounds.width).toBeLessThanOrEqual(2500);
|
|
expect(area.bounds.y + area.bounds.height).toBeLessThanOrEqual(843);
|
|
}
|
|
});
|
|
|
|
it("has message actions for all areas", () => {
|
|
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");
|
|
expect(commands).toContain("/settings");
|
|
});
|
|
});
|