248 lines
9.7 KiB
JavaScript
248 lines
9.7 KiB
JavaScript
import assert from "node:assert/strict";
|
||
import { test } from "node:test";
|
||
import { setupDom } from "./helpers.mjs";
|
||
import { createJuzhen } from "../dist/juzhen.mjs";
|
||
|
||
test("pill:塊本身與其後代塊的內部不進入 standalone pass", () =>
|
||
{
|
||
for (const html of [
|
||
'<main><div class="pill"><p>甲ABC乙,丙。</p></div><p>正常ABC,文字。</p></main>',
|
||
'<main><p class="pill">甲ABC乙,丙。</p><p>正常ABC,文字。</p></main>',
|
||
])
|
||
{
|
||
const { dom, document } = setupDom(html);
|
||
const original = document.body.innerHTML;
|
||
const pill = document.querySelector(".pill");
|
||
const before = pill.innerHTML;
|
||
const jz = createJuzhen({ pillSelector: ".pill" });
|
||
try
|
||
{
|
||
jz.render(document.body);
|
||
assert.equal(pill.innerHTML, before, "pill 內部保持原樣");
|
||
assert.ok(document.querySelector("main > p:not(.pill) jz-hws"), "正常兄弟塊仍處理");
|
||
jz.revert(document.body);
|
||
assert.equal(pill.innerHTML, before);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
}
|
||
});
|
||
|
||
const exclusions = [
|
||
[ "avoid", "data-jz-skip" ],
|
||
[ "text", 'data-jz-level="text"' ],
|
||
[ "off", 'data-juzhen-off="orphan jinze"' ],
|
||
[ "predicate", 'class="skip-text"' ],
|
||
];
|
||
const skippedText = (node) => node.nodeType === 3
|
||
&& node.parentElement?.classList.contains("skip-text");
|
||
|
||
test("jinze:排除或關閉段落功能的兩側不建立禁斷綁定", () =>
|
||
{
|
||
for (const [ label, attr ] of exclusions)
|
||
{
|
||
for (const side of [ "before-close", "after-open" ])
|
||
{
|
||
const protectedHtml = '<span ' + attr + '>自管</span>';
|
||
const html = side === "before-close"
|
||
? '<p>' + protectedHtml + ',正常文字。</p>'
|
||
: '<p>正常文字(' + protectedHtml + '</p>';
|
||
const { dom, document } = setupDom(html);
|
||
const original = document.body.innerHTML;
|
||
const protectedEl = document.querySelector("span");
|
||
const jz = createJuzhen({ spacing: false, orphan: false, longWord: false,
|
||
isSkipped: skippedText });
|
||
try
|
||
{
|
||
jz.render(document.body);
|
||
assert.equal(protectedEl.closest("jz-jinze"), null, label + "/" + side);
|
||
assert.equal(protectedEl.innerHTML, "自管");
|
||
jz.revert(document.body);
|
||
assert.equal(document.body.innerHTML, original);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
}
|
||
}
|
||
});
|
||
|
||
test("jinze:透明元素內的排除內容不可被整元素退化綁定帶入", () =>
|
||
{
|
||
const html = '<p><a href="#">甲<span data-jz-skip>自管</span>乙</a>,正常。</p>';
|
||
const { dom, document } = setupDom(html);
|
||
const original = document.body.innerHTML;
|
||
const protectedEl = document.querySelector("[data-jz-skip]");
|
||
const jz = createJuzhen({ spacing: false, orphan: false, longWord: false });
|
||
try
|
||
{
|
||
jz.render(document.body);
|
||
assert.equal(protectedEl.closest("jz-jinze"), null);
|
||
jz.revert(document.body);
|
||
assert.equal(document.body.innerHTML, original);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
});
|
||
|
||
test("jinze:註解與空 Text 不阻斷禁則,回退保留註解身份和次序", () =>
|
||
{
|
||
const html = '<p>甲<!--anchor-->,乙。</p>';
|
||
const { dom, document } = setupDom(html);
|
||
const original = document.body.innerHTML;
|
||
const p = document.querySelector("p");
|
||
const anchor = p.childNodes[1];
|
||
const empty = document.createTextNode("");
|
||
p.insertBefore(empty, anchor);
|
||
const jz = createJuzhen({ spacing: false, orphan: false, longWord: false });
|
||
try
|
||
{
|
||
jz.render(p);
|
||
const group = p.querySelector("jz-jinze");
|
||
assert.equal(group.textContent, "甲,");
|
||
assert.ok(group.contains(anchor));
|
||
assert.ok(group.contains(empty));
|
||
jz.revert(p);
|
||
assert.equal(p.childNodes[1], anchor);
|
||
assert.equal(anchor.previousSibling.textContent, "甲");
|
||
assert.equal(anchor.nextSibling.textContent, ",乙。");
|
||
assert.equal(document.body.innerHTML, original);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
});
|
||
|
||
test("jinze:行內 render 根不把外部兄弟搬入生成群組", () =>
|
||
{
|
||
const html = '<p>甲<strong id="root">,乙。</strong>後文</p>';
|
||
const { dom, document } = setupDom(html);
|
||
const original = document.body.innerHTML;
|
||
const root = document.getElementById("root");
|
||
const parent = root.parentNode;
|
||
const before = root.previousSibling;
|
||
const after = root.nextSibling;
|
||
const jz = createJuzhen({ spacing: false, orphan: false, longWord: false });
|
||
try
|
||
{
|
||
jz.render(root);
|
||
assert.equal(root.parentNode, parent);
|
||
assert.equal(root.previousSibling, before);
|
||
assert.equal(root.nextSibling, after);
|
||
assert.equal(parent.querySelectorAll(":scope > jz-jinze").length, 0);
|
||
jz.revert(root);
|
||
assert.equal(document.body.innerHTML, original);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
});
|
||
|
||
test("orphan:排除/text/off 的尾部內容不計數,也不包裹", () =>
|
||
{
|
||
for (const [ label, attr ] of exclusions)
|
||
{
|
||
const html = '<p>正常文字<span ' + attr + '>自管文字</span></p>';
|
||
const { dom, document } = setupDom(html);
|
||
const original = document.body.innerHTML;
|
||
const jz = createJuzhen({ spacing: false, jiya: false, jinze: false, longWord: false,
|
||
isSkipped: skippedText });
|
||
try
|
||
{
|
||
jz.render(document.body);
|
||
assert.equal(document.querySelector("jz-orphan"), null, label + " 尾部是綁定邊界");
|
||
jz.revert(document.body);
|
||
assert.equal(document.body.innerHTML, original);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
}
|
||
});
|
||
|
||
test("orphan:只綁最後排除邊界之後的合資格末尾", () =>
|
||
{
|
||
for (const [ label, attr ] of exclusions)
|
||
{
|
||
for (const tail of [ "末", "最後" ])
|
||
{
|
||
const html = '<p>前文<span ' + attr + '>自管文字</span>' + tail + '</p>';
|
||
const { dom, document } = setupDom(html);
|
||
const original = document.body.innerHTML;
|
||
const jz = createJuzhen({ spacing: false, jiya: false, jinze: false, longWord: false,
|
||
isSkipped: skippedText });
|
||
try
|
||
{
|
||
jz.render(document.body);
|
||
const orphan = document.querySelector("jz-orphan");
|
||
assert.equal(orphan?.textContent ?? null, tail === "末" ? null : tail, label + "/" + tail);
|
||
assert.equal(document.querySelector("span").closest("jz-orphan"), null);
|
||
jz.revert(document.body);
|
||
assert.equal(document.body.innerHTML, original);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
}
|
||
}
|
||
});
|
||
|
||
test("orphan:不可切容器不以整綁繞過排除邊界,可切容器只切合資格末尾", () =>
|
||
{
|
||
for (const tag of [ 'span', 'a href="#"' ])
|
||
{
|
||
const closeTag = tag.split(" ")[0];
|
||
const html = '<p>前文<' + tag + '><span data-jz-skip>自管</span>最後</' + closeTag + '></p>';
|
||
const { dom, document } = setupDom(html);
|
||
const original = document.body.innerHTML;
|
||
const protectedEl = document.querySelector("[data-jz-skip]");
|
||
const jz = createJuzhen({ spacing: false, jiya: false, jinze: false, longWord: false });
|
||
try
|
||
{
|
||
jz.render(document.body);
|
||
assert.equal(protectedEl.closest("jz-orphan"), null);
|
||
assert.equal(document.querySelector("jz-orphan")?.textContent ?? null,
|
||
closeTag === "span" ? "最後" : null);
|
||
jz.revert(document.body);
|
||
assert.equal(document.body.innerHTML, original);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
}
|
||
});
|
||
|
||
test("jinze:完整跨節點 base/VS/surrogate 原子一起綁到閉標點", () =>
|
||
{
|
||
for (const parts of [ [ "𠀀", "\u{E0100}" ], [ "\uD840", "\uDC00", "\uDB40", "\uDD00" ] ])
|
||
{
|
||
const { dom, document } = setupDom("<p></p>");
|
||
const p = document.querySelector("p");
|
||
p.append("前文");
|
||
const authors = parts.map((part) =>
|
||
{
|
||
const span = document.createElement("span");
|
||
span.textContent = part;
|
||
p.appendChild(span);
|
||
return span;
|
||
});
|
||
p.append(",");
|
||
const original = p.innerHTML;
|
||
const jz = createJuzhen({ spacing: false, orphan: false, longWord: false });
|
||
try
|
||
{
|
||
jz.render(p);
|
||
assert.equal(p.querySelector("jz-jinze").textContent, "𠀀\u{E0100},");
|
||
const rendered = p.innerHTML;
|
||
jz.render(p);
|
||
assert.equal(p.innerHTML, rendered);
|
||
jz.revert(p);
|
||
assert.equal(p.innerHTML, original);
|
||
assert.deepEqual(Array.from(p.querySelectorAll("span")), authors);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
}
|
||
});
|
||
|
||
test("orphan:split surrogate 的補充平面字計為一個實義字", () =>
|
||
{
|
||
const { dom, document } = setupDom('<p>前文<strong>\uD840</strong><em>\uDC00</em>末</p>');
|
||
const p = document.querySelector("p");
|
||
const original = p.innerHTML;
|
||
const jz = createJuzhen({ spacing: false, jiya: false, jinze: false, longWord: false });
|
||
try
|
||
{
|
||
jz.render(p);
|
||
assert.equal(p.querySelector("jz-orphan").textContent, "𠀀末");
|
||
jz.revert(p);
|
||
assert.equal(p.innerHTML, original);
|
||
}
|
||
finally { jz.disconnect(); dom.window.close(); }
|
||
});
|