import assert from "node:assert/strict"; import { test } from "node:test"; import { setupDom } from "./helpers.mjs"; import { createJuzhen } from "../dist/juzhen.mjs"; function layoutHarness(document) { const originalResizeObserver = globalThis.ResizeObserver; const originalRects = window.Element.prototype.getClientRects; const observers = []; let measurements = 0; let resolveFonts; const ready = new Promise((resolve) => { resolveFonts = resolve; }); Object.defineProperty(document, "fonts", { configurable: true, value: { ready } }); globalThis.ResizeObserver = class { constructor(callback) { this.callback = callback; this.disconnected = false; observers.push(this); } observe() {} disconnect() { this.disconnected = true; } fire() { this.callback([], this); } }; window.Element.prototype.getClientRects = function () { measurements += 1; return [ { top: 0, bottom: 16, left: 0, right: 16, width: 16, height: 16 } ]; }; return { observers, resolveFonts, count: () => measurements, restore() { window.Element.prototype.getClientRects = originalRects; if (originalResizeObserver === undefined) { delete globalThis.ResizeObserver; } else { globalThis.ResizeObserver = originalResizeObserver; } }, }; } for (const signal of [ "resize", "fonts" ]) { test("layout lifecycle:重排後舊 " + signal + " 回調不得量測新產物", async () => { const { dom, document } = setupDom('

「甲乙

'); const root = document.querySelector("p"); const layout = layoutHarness(document); const jz = createJuzhen({ spacing: false, orphan: false, jinze: false, longWord: false }); try { jz.render(root); jz.rerender(root); assert.equal(layout.observers.length, 2); assert.equal(layout.observers[0].disconnected, true); const before = layout.count(); if (signal === "resize") { layout.observers[0].fire(); assert.equal(layout.count(), before, "已卸載回調不碰目前產物"); layout.observers[1].fire(); assert.equal(layout.count(), before + 1, "目前回調正常執行"); } else { layout.resolveFonts(); await Promise.resolve(); assert.equal(layout.count(), before + 1, "只執行目前安裝的字型回調"); } } finally { jz.revert(root); jz.disconnect(); layout.restore(); dom.window.close(); } }); } test("layout lifecycle:回退後字型 Promise 不再查詢根子樹", async () => { const { dom, document } = setupDom('

「甲乙

'); const root = document.querySelector("p"); const layout = layoutHarness(document); const jz = createJuzhen({ spacing: false, orphan: false, jinze: false, longWord: false }); const nativeQuery = root.querySelectorAll; let queries = 0; try { jz.render(root); jz.revert(root); root.querySelectorAll = function (...args) { queries += 1; return nativeQuery.apply(this, args); }; layout.resolveFonts(); await Promise.resolve(); assert.equal(queries, 0, "卸載後不再進入 layout 工作"); assert.equal(root.innerHTML, "「甲乙"); } finally { root.querySelectorAll = nativeQuery; jz.disconnect(); layout.restore(); dom.window.close(); } });