83 lines
3.3 KiB
TypeScript
83 lines
3.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
applyRichTextStylePatch,
|
|
normalizeExtraRichText,
|
|
richTextContentSignature,
|
|
sanitizeRichTextHtml,
|
|
sanitizeRichTextUrl,
|
|
} from "@/lib/rich-text";
|
|
|
|
describe("rich text sanitization", () => {
|
|
it("keeps supported formatting, colors, and safe links", () => {
|
|
expect(sanitizeRichTextHtml(
|
|
'<p><strong>Good</strong> <span style="color: #ff3b30">news</span> <a href="https://example.com" onclick="bad()">link</a></p>',
|
|
)).toBe(
|
|
'<p><strong>Good</strong> <span style="color: #FF3B30">news</span> <a href="https://example.com" target="_blank" rel="noopener noreferrer">link</a></p>',
|
|
);
|
|
});
|
|
|
|
it("normalizes browser-generated RGB text colors to safe hex colors", () => {
|
|
expect(sanitizeRichTextHtml(
|
|
'<p><span style="color: rgb(0, 255, 0);">green</span> <span style="color: rgb(255, 204, 0)">yellow</span></p>',
|
|
)).toBe(
|
|
'<p><span style="color: #00FF00">green</span> <span style="color: #FFCC00">yellow</span></p>',
|
|
);
|
|
});
|
|
|
|
it("removes executable markup, unsafe links, and unsupported styles", () => {
|
|
expect(sanitizeRichTextHtml(
|
|
'<script>alert(1)</script><p onclick="bad()">Safe <span style="background:red;color:red">text</span><a href="javascript:bad()">link</a></p>',
|
|
)).toBe("<p>Safe <span>text</span><a>link</a></p>");
|
|
expect(sanitizeRichTextUrl("data:text/html,bad")).toBe("");
|
|
});
|
|
});
|
|
|
|
describe("legacy Extra rich text conversion", () => {
|
|
it("escapes plain text, preserves newlines, and converts phrase colors", () => {
|
|
expect(normalizeExtraRichText("<unsafe>\nMASSIVE damage", [
|
|
{ text: "MASSIVE", color: "#ff0000" },
|
|
])).toBe(
|
|
'<p><unsafe><br><span style="color: #FF0000">MASSIVE</span> damage</p>',
|
|
);
|
|
});
|
|
|
|
it("leaves sanitized rich text in rich-text form", () => {
|
|
expect(normalizeExtraRichText('<p><em>Ready</em></p>', [
|
|
{ text: "Ready", color: "#ff0000" },
|
|
])).toBe("<p><em>Ready</em></p>");
|
|
});
|
|
});
|
|
|
|
describe("bulk rich-text styles", () => {
|
|
it("applies a style to every text node without replacing structure or links", () => {
|
|
expect(applyRichTextStylePatch(
|
|
'<p>First <a href="https://example.com">link</a></p><ul><li>Second</li></ul>',
|
|
{ bold: true },
|
|
)).toBe(
|
|
'<p><strong>First </strong><a href="https://example.com" target="_blank" rel="noopener noreferrer"><strong>link</strong></a></p><ul><li><strong>Second</strong></li></ul>',
|
|
);
|
|
});
|
|
|
|
it("removes only requested styles and applies normalized colors", () => {
|
|
expect(applyRichTextStylePatch(
|
|
'<p><strong>Bold <em>and italic</em></strong></p>',
|
|
{ bold: false },
|
|
)).toBe("<p>Bold <em>and italic</em></p>");
|
|
expect(applyRichTextStylePatch("<p>Red</p>", { color: "#00ff00" }))
|
|
.toBe('<p><span style="color: #00FF00">Red</span></p>');
|
|
expect(applyRichTextStylePatch(
|
|
'<p><span style="color: #00FF00">Green</span></p>',
|
|
{ color: null },
|
|
)).toBe("<p>Green</p>");
|
|
});
|
|
|
|
it("compares content independently from visual formatting", () => {
|
|
expect(richTextContentSignature(
|
|
'<p><strong>Same</strong> <span style="color: #FF0000">text</span></p>',
|
|
)).toBe("<p>Same text</p>");
|
|
expect(richTextContentSignature("<p>Changed text</p>"))
|
|
.not.toBe(richTextContentSignature("<p>Original text</p>"));
|
|
});
|
|
});
|