fix(web): 修復排版邊界與增量生命週期

This commit is contained in:
2026-10-02 19:08:02 +08:00
parent 1c2fb0ed59
commit b1a16c4969
39 changed files with 4763 additions and 673 deletions
+88
View File
@@ -730,3 +730,91 @@ test("observer provenance: transition inference and queue consumption scale line
}
context.diagnostic("linear probes complete " + JSON.stringify(reports));
});
test("observer:手動局部操作先結算其他兄弟塊的待交付變更", async () =>
{
for (const operation of [ "render", "rerender", "revert" ])
{
const { dom, document } = setupDom('<main><p id="a">原文,甲。</p><p id="b">原文,乙。</p></main>');
const root = document.querySelector("main");
const a = document.getElementById("a");
const b = document.getElementById("b");
const jz = createJuzhen({ jinze: false, orphan: false, longWord: false });
try
{
jz.render(root);
jz.observe(root);
a.textContent = "改後,新文。";
jz[operation](b);
await tick();
assert.equal(a.querySelectorAll("jz-char").length, 2, operation + " 不丟棄兄弟變更");
if (operation === "revert") { assert.equal(b.querySelector("[data-jz]"), null); }
a.textContent = "再次,更新。";
await tick();
assert.equal(a.querySelectorAll("jz-char").length, 2, "結算後繼續觀察");
}
finally { jz.disconnect(); dom.window.close(); }
}
});
test("observer:祖先分類待更新時手動 revert 子塊仍最後生效", async () =>
{
const { dom, document } = setupDom('<main><p id="a">甲文,內容。</p><p id="b">乙文,內容。</p></main>');
const root = document.querySelector("main");
const a = document.getElementById("a");
const b = document.getElementById("b");
const jz = createJuzhen();
try
{
jz.render(root);
jz.observe(root);
root.setAttribute("data-jz-level", "text");
jz.revert(b);
await tick();
assert.equal(a.getAttribute("data-jz-block-level"), "text");
assert.equal(a.querySelector("jz-orphan"), null, "兄弟塊採新祖先級別");
assert.equal(b.querySelector("[data-jz]"), null, "舊祖先記錄不重新 render 已回退子塊");
assert.equal(b.hasAttribute("data-jz-block-level"), false);
}
finally { jz.disconnect(); dom.window.close(); }
});
test("observer:暫停前結算失敗仍非同步報錯,不阻擋手動操作及後續觀察", async () =>
{
const { dom, document } = setupDom('<main><p id="a">甲文,內容。</p><p id="b">乙文,內容。</p></main>');
const root = document.querySelector("main");
const a = document.getElementById("a");
const b = document.getElementById("b");
const expected = new Error("pending mutation failure");
let shouldThrow = false;
const errors = [];
dom.window.addEventListener("error", (event) =>
{
errors.push(event.error);
event.preventDefault();
});
const jz = createJuzhen({
jinze: false, orphan: false, longWord: false,
isSkipped(node)
{
if (shouldThrow && node === a) { shouldThrow = false; throw expected; }
return false;
},
});
try
{
jz.render(root);
jz.observe(root);
a.textContent = "改後,新文。";
shouldThrow = true;
assert.doesNotThrow(() => jz.revert(b), "舊 observer 錯誤不變成同步 API 例外");
assert.equal(errors.length, 0);
await tick();
assert.deepEqual(errors, [ expected ]);
assert.equal(b.querySelector("[data-jz]"), null);
a.textContent = "再次,更新。";
await tick();
assert.equal(a.querySelectorAll("jz-char").length, 2);
}
finally { jz.disconnect(); dom.window.close(); }
});