116 lines
5.3 KiB
TypeScript
116 lines
5.3 KiB
TypeScript
|
|
// @vitest-environment jsdom
|
||
|
|
// เทสต์ serialize/segmentsToHtml ของ editor กล่องข้อความ (utils/textbox-editor.ts)
|
||
|
|
// ที่ต้องมี DOM จริง — แยกจากชุด pure utils (environment node ตาม vitest.config.ts)
|
||
|
|
// ด้วย pragma ต่อไฟล์ เหมือน tests/hooks/useCustomSelection-dom.test.ts
|
||
|
|
// พฤติกรรม editor จริง (keydown/execCommand/gesture) ยืนยันด้วย Playwright แทน
|
||
|
|
import { describe, expect, it } from "vitest";
|
||
|
|
import { segmentsToHtml, serializeEditor } from "@/utils/textbox-editor";
|
||
|
|
|
||
|
|
/** div root พร้อม innerHTML ตามที่ contentEditable ค้างไว้ตอน blur */
|
||
|
|
function mount(html: string): HTMLElement {
|
||
|
|
const el = document.createElement("div");
|
||
|
|
el.innerHTML = html;
|
||
|
|
return el;
|
||
|
|
}
|
||
|
|
|
||
|
|
describe("serializeEditor — ขึ้นบรรทัดใหม่", () => {
|
||
|
|
it("โครงสร้าง Enter ของ Chrome text→div ต่อท้าย → ได้ newline คั่น (เดิมหายเป็น abcdef)", () => {
|
||
|
|
const r = serializeEditor(mount("abc<div>def</div>"));
|
||
|
|
expect(r.text).toBe("abc\ndef");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("หลายบรรทัดเป็น div เรียงกันทั้งก้อน → คั่นครบทุกบรรทัด", () => {
|
||
|
|
const r = serializeEditor(mount("<div>a</div><div>b</div><div>c</div>"));
|
||
|
|
expect(r.text).toBe("a\nb\nc");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("<br> (insertLineBreak ของ Enter ที่เราดักเอง) → newline เหมือนกัน", () => {
|
||
|
|
const r = serializeEditor(mount("abc<br>def"));
|
||
|
|
expect(r.text).toBe("abc\ndef");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("<br> ตามด้วย <div> ติดกัน → newline เดียว ไม่ซ้ำสอง", () => {
|
||
|
|
const r = serializeEditor(mount("a<br><div>b</div>"));
|
||
|
|
expect(r.text).toBe("a\nb");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("<p> ก็เป็น block คั่นบรรทัดเหมือน <div>", () => {
|
||
|
|
const r = serializeEditor(mount("abc<p>def</p>"));
|
||
|
|
expect(r.text).toBe("abc\ndef");
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("serializeEditor — ตัวหนาเป็น segments", () => {
|
||
|
|
it("<b> กลางข้อความ → 3 segments ปกติ/หนา/ปกติ + text ต่อกันไม่มีช่องว่าง", () => {
|
||
|
|
const r = serializeEditor(mount("a<b>bold</b>c"));
|
||
|
|
expect(r.text).toBe("aboldc");
|
||
|
|
expect(r.segments).toEqual([
|
||
|
|
{ text: "a" },
|
||
|
|
{ text: "bold", bold: true },
|
||
|
|
{ text: "c" },
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("segment ติดกันสไตล์เดียวกัน (รวมข้าม <b> สองอัน) → merge เป็น segment เดียว", () => {
|
||
|
|
const r = serializeEditor(mount("<b>a</b><b>b</b>"));
|
||
|
|
expect(r.segments).toEqual([{ text: "ab", bold: true }]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("newline ที่รอยต่อ block → segment ปกติของตัวเอง (newline ไม่มี glyph จึงไม่กระทบการแสดงผล)", () => {
|
||
|
|
const r = serializeEditor(mount("<b>a</b><div><b>b</b></div>"));
|
||
|
|
expect(r.text).toBe("a\nb");
|
||
|
|
expect(r.segments).toEqual([
|
||
|
|
{ text: "a", bold: true },
|
||
|
|
{ text: "\n" },
|
||
|
|
{ text: "b", bold: true },
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("serializeEditor — whitespace ท้าย", () => {
|
||
|
|
it("<br> ปิดท้ายที่ browser เติมให้ contentEditable → ไม่เหลือ newline ค้างใน DB", () => {
|
||
|
|
const r = serializeEditor(mount("hello<br>"));
|
||
|
|
expect(r.text).toBe("hello");
|
||
|
|
expect(r.segments).toEqual([{ text: "hello" }]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("newline ท้ายหลายตัว + space ตัดหมด และตัดจาก segments ให้ text กับ segments ตรงกัน", () => {
|
||
|
|
const r = serializeEditor(mount("a<b>b</b><br><br> "));
|
||
|
|
expect(r.text).toBe("ab");
|
||
|
|
expect(r.segments).toEqual([{ text: "a" }, { text: "b", bold: true }]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("ก้อนว่างทั้งหมด (แค่ <br>) → text ว่าง + segments ว่าง", () => {
|
||
|
|
const r = serializeEditor(mount("<br>"));
|
||
|
|
expect(r.text).toBe("");
|
||
|
|
expect(r.segments).toEqual([]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("space หน้าไม่ถูกตัด (เว้นวรรคตั้งใจยังเก็บ)", () => {
|
||
|
|
const r = serializeEditor(mount(" a"));
|
||
|
|
expect(r.text).toBe(" a");
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("segmentsToHtml", () => {
|
||
|
|
it("escape HTML ของข้อความ + แปลง \\n เป็น <br>", () => {
|
||
|
|
expect(segmentsToHtml("a<b>&x\ny")).toBe("a<b>&x<br>y");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("segments bold → <b> ตามส่วน", () => {
|
||
|
|
expect(
|
||
|
|
segmentsToHtml("ab", [{ text: "a" }, { text: "b", bold: true }]),
|
||
|
|
).toBe("a<b>b</b>");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("round-trip: segmentsToHtml → DOM → serializeEditor ได้ text/segments เดิม", () => {
|
||
|
|
const text = "หนา\nปกติ\nธรรมดา";
|
||
|
|
const segments = [
|
||
|
|
{ text: "หนา", bold: true },
|
||
|
|
{ text: "\nปกติ\nธรรมดา" },
|
||
|
|
];
|
||
|
|
const html = segmentsToHtml(text, segments);
|
||
|
|
expect(serializeEditor(mount(html))).toEqual({ text, segments });
|
||
|
|
});
|
||
|
|
});
|