feishu.test.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import { describe, expect, it } from "vitest";
  2. import { FeishuCardRenderer } from "@/lib/webhook/renderers/feishu";
  3. import type { StructuredMessage } from "@/lib/webhook/types";
  4. describe("FeishuCardRenderer", () => {
  5. const renderer = new FeishuCardRenderer();
  6. it("should render interactive card with correct structure", () => {
  7. const message: StructuredMessage = {
  8. header: { title: "测试标题", icon: "🔔", level: "info" },
  9. sections: [],
  10. timestamp: new Date("2025-01-02T12:00:00Z"),
  11. };
  12. const result = renderer.render(message);
  13. const body = JSON.parse(result.body);
  14. expect(body.msg_type).toBe("interactive");
  15. expect(body.card.schema).toBe("2.0");
  16. expect(body.card.header.title.content).toContain("🔔 测试标题");
  17. expect(body.card.header.template).toBe("blue");
  18. });
  19. it("should map level to correct template color", () => {
  20. const levels = [
  21. { level: "info" as const, template: "blue" },
  22. { level: "warning" as const, template: "orange" },
  23. { level: "error" as const, template: "red" },
  24. ];
  25. for (const { level, template } of levels) {
  26. const message: StructuredMessage = {
  27. header: { title: "标题", level },
  28. sections: [],
  29. timestamp: new Date(),
  30. };
  31. const result = renderer.render(message);
  32. const body = JSON.parse(result.body);
  33. expect(body.card.header.template).toBe(template);
  34. }
  35. });
  36. it("should render text section as markdown element", () => {
  37. const message: StructuredMessage = {
  38. header: { title: "标题", level: "info" },
  39. sections: [{ content: [{ type: "text", value: "普通文本内容" }] }],
  40. timestamp: new Date(),
  41. };
  42. const result = renderer.render(message);
  43. const body = JSON.parse(result.body);
  44. const elements = body.card.body.elements;
  45. expect(
  46. elements.some((e: any) => e.tag === "markdown" && e.content.includes("普通文本内容"))
  47. ).toBe(true);
  48. });
  49. it("should render quote as markdown with quote syntax", () => {
  50. const message: StructuredMessage = {
  51. header: { title: "标题", level: "warning" },
  52. sections: [{ content: [{ type: "quote", value: "引用内容" }] }],
  53. timestamp: new Date(),
  54. };
  55. const result = renderer.render(message);
  56. const body = JSON.parse(result.body);
  57. const elements = body.card.body.elements;
  58. expect(
  59. elements.some((e: any) => e.tag === "markdown" && e.content.includes("> 引用内容"))
  60. ).toBe(true);
  61. });
  62. it("should render fields as column set", () => {
  63. const message: StructuredMessage = {
  64. header: { title: "标题", level: "error" },
  65. sections: [
  66. {
  67. title: "详细信息",
  68. content: [
  69. {
  70. type: "fields",
  71. items: [
  72. { label: "名称", value: "测试" },
  73. { label: "状态", value: "正常" },
  74. ],
  75. },
  76. ],
  77. },
  78. ],
  79. timestamp: new Date(),
  80. };
  81. const result = renderer.render(message);
  82. const body = JSON.parse(result.body);
  83. const bodyStr = JSON.stringify(body.card.body);
  84. expect(bodyStr).toContain("名称");
  85. expect(bodyStr).toContain("测试");
  86. });
  87. it("should render list items", () => {
  88. const message: StructuredMessage = {
  89. header: { title: "排行榜", level: "info" },
  90. sections: [
  91. {
  92. content: [
  93. {
  94. type: "list",
  95. style: "ordered",
  96. items: [
  97. { icon: "🥇", primary: "用户A", secondary: "消费 $10" },
  98. { icon: "🥈", primary: "用户B", secondary: "消费 $5" },
  99. ],
  100. },
  101. ],
  102. },
  103. ],
  104. timestamp: new Date(),
  105. };
  106. const result = renderer.render(message);
  107. const body = JSON.parse(result.body);
  108. const bodyStr = JSON.stringify(body.card.body);
  109. expect(bodyStr).toContain("🥇");
  110. expect(bodyStr).toContain("用户A");
  111. });
  112. it("should render divider element", () => {
  113. const message: StructuredMessage = {
  114. header: { title: "标题", level: "info" },
  115. sections: [{ content: [{ type: "divider" }] }],
  116. timestamp: new Date(),
  117. };
  118. const result = renderer.render(message);
  119. const body = JSON.parse(result.body);
  120. const elements = body.card.body.elements;
  121. expect(elements.some((e: any) => e.tag === "hr")).toBe(true);
  122. });
  123. it("should include timestamp in footer", () => {
  124. const message: StructuredMessage = {
  125. header: { title: "标题", level: "info" },
  126. sections: [],
  127. timestamp: new Date("2025-01-02T12:00:00Z"),
  128. };
  129. const result = renderer.render(message);
  130. const body = JSON.parse(result.body);
  131. const bodyStr = JSON.stringify(body.card.body);
  132. expect(bodyStr).toContain("2025");
  133. });
  134. });