fix(web): 修復排版邊界與增量生命週期
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
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('<p lang="zh-Hant">「甲乙</p>');
|
||||
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('<p lang="zh-Hant">「甲乙</p>');
|
||||
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(); }
|
||||
});
|
||||
Reference in New Issue
Block a user