Skip to content

Commit fc4f6dd

Browse files
committed
test(line): cover grapheme-safe template fields
1 parent d32aaea commit fc4f6dd

2 files changed

Lines changed: 48 additions & 2 deletions

File tree

extensions/line/src/message-cards.test.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,26 @@ import {
2020
messageAction,
2121
} from "./template-messages.js";
2222

23+
const loneHighSurrogate = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])/;
24+
2325
describe("createConfirmTemplate", () => {
2426
it("truncates text to 240 characters", () => {
2527
const longText = "x".repeat(300);
2628
const template = createConfirmTemplate(longText, messageAction("Yes"), messageAction("No"));
2729

2830
expect((template.template as { text: string }).text.length).toBe(240);
2931
});
32+
33+
it("drops a surrogate-pair emoji from fallback altText instead of splitting it", () => {
34+
const template = createConfirmTemplate(
35+
`${"x".repeat(399)}😀`,
36+
messageAction("Yes"),
37+
messageAction("No"),
38+
);
39+
40+
expect(template.altText).toBe("x".repeat(399));
41+
expect(loneHighSurrogate.test(template.altText)).toBe(false);
42+
});
3043
});
3144

3245
describe("createButtonTemplate", () => {
@@ -51,7 +64,16 @@ describe("createButtonTemplate", () => {
5164
const title = (template.template as { title: string }).title;
5265

5366
expect(title).toBe("x".repeat(39));
54-
expect(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(title)).toBe(false);
67+
expect(loneHighSurrogate.test(title)).toBe(false);
68+
});
69+
70+
it("drops a surrogate-pair emoji from explicit altText instead of splitting it", () => {
71+
const template = createButtonTemplate("Title", "Text", [messageAction("OK")], {
72+
altText: `${"x".repeat(399)}😀`,
73+
});
74+
75+
expect(template.altText).toBe("x".repeat(399));
76+
expect(loneHighSurrogate.test(template.altText)).toBe(false);
5577
});
5678

5779
it("truncates text to 60 chars when no thumbnail is provided", () => {
@@ -105,6 +127,17 @@ describe("createCarouselColumn", () => {
105127
expect(column.text.length).toBe(60);
106128
});
107129

130+
it("drops a surrogate-pair emoji from the title instead of splitting it", () => {
131+
const column = createCarouselColumn({
132+
title: `${"x".repeat(39)}😀`,
133+
text: "Text",
134+
actions: [messageAction("OK")],
135+
});
136+
137+
expect(column.title).toBe("x".repeat(39));
138+
expect(loneHighSurrogate.test(column.title ?? "")).toBe(false);
139+
});
140+
108141
it("does not split an emoji grapheme at the 60-code-unit boundary", () => {
109142
const text = `${"x".repeat(59)}👨‍👩‍👧‍👦after`;
110143
const column = createCarouselColumn({
@@ -172,6 +205,16 @@ describe("carousel column limits", () => {
172205
const template = createTemplate();
173206
expect((template.template as { columns: unknown[] }).columns.length).toBe(10);
174207
});
208+
209+
it("drops a surrogate-pair emoji from image-carousel altText instead of splitting it", () => {
210+
const template = createImageCarousel(
211+
[createImageCarouselColumn("https://example.com/0.jpg", messageAction("View"))],
212+
`${"x".repeat(399)}😀`,
213+
);
214+
215+
expect(template.altText).toBe("x".repeat(399));
216+
expect(loneHighSurrogate.test(template.altText)).toBe(false);
217+
});
175218
});
176219

177220
describe("createProductCarousel", () => {

extensions/line/src/template-messages.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ function truncateTemplateText(text: string, limit: number): string {
6565
return result;
6666
}
6767

68-
function truncateOptionalTemplateText(value: string | undefined, limit: number): string | undefined {
68+
function truncateOptionalTemplateText(
69+
value: string | undefined,
70+
limit: number,
71+
): string | undefined {
6972
return value === undefined ? undefined : truncateTemplateText(value, limit);
7073
}
7174

0 commit comments

Comments
 (0)