Files
cjk-autospace/test/punctuation-atoms.test.mjs
T

227 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { test } from "node:test";
import { setupDom } from "./helpers.mjs";
import { createJuzhen } from "../dist/juzhen.mjs";
const ONLY_JIYA = { spacing: false, jinze: false, orphan: false, longWord: false };
for (const selector of [ "\uFE00", "\u{E0100}" ])
{
for (const full of [ false, true ])
{
test("jiya atom:跨透明節點 VS " + selector.codePointAt(0).toString(16) + " full=" + full, () =>
{
const { dom, document } = setupDom('<p lang="zh-Hant">甲<strong id="base">,</strong><em id="vs">' + selector + '</em>「乙丙」</p>');
const root = document.querySelector("p");
const original = root.innerHTML;
const text = root.textContent;
const base = document.getElementById("base");
const vs = document.getElementById("vs");
let clicks = 0;
vs.addEventListener("click", () => { clicks += 1; });
const jz = createJuzhen(full ? {} : ONLY_JIYA);
try
{
jz.render(root);
const comma = root.querySelector("jz-char.bd-pause");
assert.equal(comma.textContent, "," + selector);
assert.equal(comma.querySelector("jz-inner").textContent, "," + selector);
assert.equal(comma.classList.contains("jz-half"), true);
assert.equal(root.querySelector("jz-char.bd-open").classList.contains("jz-half"), true);
assert.equal(document.getElementById("base"), base);
assert.equal(document.getElementById("vs"), vs);
vs.dispatchEvent(new dom.window.Event("click"));
assert.equal(clicks, 1);
assert.equal(root.textContent, text);
const rendered = root.innerHTML;
jz.render(root);
assert.equal(root.innerHTML, rendered, "重複 render 不增殖");
jz.revert(root);
assert.equal(root.innerHTML, original);
}
finally { jz.revert(root); jz.disconnect(); dom.window.close(); }
});
}
}
test("jiya atom:astral base 與 VS 的四個 surrogate 半字合成一個原子", () =>
{
const { dom, document } = setupDom('<p lang="zh-Hant"><strong></strong><em></em><i></i><b></b>「甲</p>');
const root = document.querySelector("p");
[ "\uD83D", "\uDE00", "\uDB40", "\uDD00" ].forEach((text, i) => { root.children[i].textContent = text; });
const original = root.innerHTML;
const jz = createJuzhen({ ...ONLY_JIYA, charClass: { biaodian: { pause: "😀" } } });
try
{
jz.render(root);
const chars = root.querySelectorAll("jz-char");
assert.equal(chars.length, 2);
assert.equal(chars[0].textContent, "😀\u{E0100}");
assert.equal(chars[0].classList.contains("jz-half"), true);
jz.revert(root);
assert.equal(root.innerHTML, original);
}
finally { jz.revert(root); jz.disconnect(); dom.window.close(); }
});
test("jiya 分類:astral 字集不包含原文中孤立的 surrogate 半字", () =>
{
const { dom, document } = setupDom('<p></p>');
const root = document.querySelector("p");
root.textContent = "\uD83DA\uDE00";
const original = root.textContent;
const jz = createJuzhen({ ...ONLY_JIYA, charClass: { biaodian: { pause: "😀" } } });
try
{
jz.render(root);
assert.equal(root.querySelector("jz-char"), null);
assert.equal(root.textContent, original);
}
finally { jz.revert(root); jz.disconnect(); dom.window.close(); }
});
for (const excluded of [ false, true ])
{
test("jiya 邊界:空 Text excluded=" + excluded, () =>
{
const { dom, document } = setupDom('<p lang="zh-Hant">,<strong></strong>「</p>');
const root = document.querySelector("p");
const empty = document.createTextNode("");
root.querySelector("strong").appendChild(empty);
const jz = createJuzhen({ ...ONLY_JIYA, isSkipped: (node) => excluded && node === empty });
try
{
jz.render(root);
assert.equal(root.querySelectorAll("jz-char.jz-half").length, excluded ? 0 : 2);
}
finally { jz.revert(root); jz.disconnect(); dom.window.close(); }
});
}
for (const side of [ "base", "vs" ])
{
test("jiya 跨原子局部回退:" + side + " 端保持原文及另一端位置", () =>
{
const { dom, document } = setupDom('<p lang="zh-Hant"><strong id="base">,</strong><em id="vs">\uFE00</em>「</p>');
const root = document.querySelector("p");
const jz = createJuzhen(ONLY_JIYA);
try
{
jz.render(root);
jz.revert(document.getElementById(side));
assert.equal(document.getElementById("base").textContent, ",");
assert.equal(document.getElementById("vs").textContent, "\uFE00");
assert.equal(root.textContent, ",\uFE00「");
jz.revert(root);
assert.equal(root.innerHTML, '<strong id="base">,</strong><em id="vs">\uFE00</em>「');
}
finally { jz.revert(root); jz.disconnect(); dom.window.close(); }
});
}
for (const mutation of [ "replace-source", "move-carrier", "move-author-text", "foreign-revert" ])
{
test("jiya 原子回退:" + mutation + " 保留作者外部修改/實例所有權", () =>
{
const { dom, document } = setupDom('<main><p lang="zh-Hant"><strong id="base">,</strong><em id="vs">\uFE00</em>「</p><aside></aside></main>');
const root = document.querySelector("p");
const jz = createJuzhen(ONLY_JIYA);
const other = createJuzhen(ONLY_JIYA);
const aside = document.querySelector("aside");
try
{
jz.render(root);
const comma = root.querySelector("jz-char.bd-pause");
const selector = comma.querySelector("jz-inner").lastChild;
if (mutation === "replace-source") { document.getElementById("vs").textContent = "作者新文"; }
else if (mutation === "move-carrier") { aside.appendChild(comma); }
else if (mutation === "move-author-text") { aside.appendChild(selector); }
else
{
const html = root.innerHTML;
other.revert(root);
assert.equal(root.innerHTML, html);
}
jz.revert(root);
assert.equal(document.getElementById("base").textContent, ",");
if (mutation === "replace-source")
{
assert.equal(document.getElementById("vs").textContent, "作者新文");
assert.equal(root.textContent, ",作者新文「");
}
else if (mutation === "move-author-text") { assert.equal(aside.textContent, "\uFE00"); }
else
{
assert.equal(document.getElementById("vs").textContent, "\uFE00");
assert.equal(root.querySelector("[data-jz]"), null);
assert.equal(aside.querySelector("[data-jz]"), null);
}
}
finally { jz.revert(document.body); jz.disconnect(); other.disconnect(); dom.window.close(); }
});
}
for (const mutation of [ "clone-outer", "clone-inner", "clone-base", "remove-outer" ])
{
test("jiya 原子失效:" + mutation + " 不重複或復活已刪內容", () =>
{
const { dom, document } = setupDom('<p lang="zh-Hant"><strong id="base">,</strong><em id="vs">\uFE00</em>「</p>');
const root = document.querySelector("p");
const jz = createJuzhen(ONLY_JIYA);
try
{
jz.render(root);
const outer = root.querySelector("jz-char.bd-pause");
const inner = outer.querySelector("jz-inner");
const base = document.getElementById("base");
if (mutation === "clone-outer") { outer.replaceWith(outer.cloneNode(true)); }
else if (mutation === "clone-inner") { inner.replaceWith(inner.cloneNode(true)); }
else if (mutation === "clone-base") { base.replaceWith(base.cloneNode(true)); }
else { outer.remove(); }
const before = root.textContent;
jz.revert(root);
assert.equal(root.textContent, before, "回退不能從失效的私有來源補回舊內容");
if (mutation !== "clone-inner")
{
const holder = document.createElement("aside");
holder.appendChild(outer);
jz.revert(holder);
assert.equal(holder.firstChild, outer, "失效舊產物撤銷所有權,重插入也不重新取得");
}
}
finally { jz.revert(root); jz.disconnect(); dom.window.close(); }
});
}
test("jiya 原子失效:來源父被替換不把 VS 放回已脫離的作者舊父", () =>
{
const { dom, document } = setupDom('<p lang="zh-Hant"><strong>,</strong><em id="vs">\uFE00</em>「</p>');
const root = document.querySelector("p");
const source = document.getElementById("vs");
const jz = createJuzhen(ONLY_JIYA);
try
{
jz.render(root);
source.replaceWith(source.cloneNode(true));
jz.revert(root);
assert.equal(source.textContent, "");
assert.equal(root.textContent, ",「");
}
finally { jz.revert(root); jz.disconnect(); dom.window.close(); }
});
test("jiya 原子生命週期:完整 render 根脫離文檔仍精確回退", () =>
{
const { dom, document } = setupDom('<p lang="zh-Hant"><strong>,</strong><em>\uFE00</em>「</p>');
const root = document.querySelector("p");
const original = root.innerHTML;
const jz = createJuzhen(ONLY_JIYA);
try
{
jz.render(root);
root.remove();
jz.revert(root);
assert.equal(root.innerHTML, original);
}
finally { jz.disconnect(); dom.window.close(); }
});