93 lines
3.5 KiB
JavaScript
93 lines
3.5 KiB
JavaScript
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(); }
|
|
});
|