Files
rhinelab-ui/reference/navigation-transition-check.html

229 lines
7.2 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<meta charset="utf-8" />
<title>档案导航文字与刻度过渡检查</title>
<style>
body {
margin: 0;
background: #eae5e1;
font: 13px monospace;
}
iframe {
width: 1100px;
height: 620px;
border: 0;
}
pre {
padding: 16px;
white-space: pre-wrap;
}
</style>
<iframe src="/?scene=archive" title="真实交互场景"></iframe>
<pre id="result">正在检查…</pre>
<script type="module">
import { records, columnFiles, archiveColumns } from "/src/data.ts";
const frame = document.querySelector("iframe"),
result = document.querySelector("#result"),
checks = [];
const pause = (ms) => new Promise((r) => setTimeout(r, ms));
function check(ok, label) {
if (!ok) throw new Error(label);
checks.push(label);
}
async function until(fn) {
const start = performance.now();
while (!fn()) {
if (performance.now() - start > 12000) throw new Error("等待状态超时");
await pause(25);
}
}
try {
await until(
() =>
frame.contentWindow.rhine?.stats().loaded &&
!frame.contentDocument.querySelector("#loading"),
);
const win = frame.contentWindow,
doc = frame.contentDocument,
app = win.rhine;
const q = (s) => doc.querySelector(s),
value = (s) => q(s).querySelector(".rn-value")?.textContent;
const moving = (s) =>
q(s)
.getAnimations({ subtree: true })
.some((a) => a.playState === "running");
const height = (b) =>
parseFloat(win.getComputedStyle(b, "::before").height);
const button = (a) => q(`[data-action="${a}"]`);
app.archive();
await pause(800);
const ticks = [...q("#file-ticks").children],
a = columnFiles(0),
b = columnFiles(1);
app.select(a[1]);
await pause(600);
app.select(b[5]);
await pause(600);
button("column-prev").click();
await pause(90);
check(
moving("#column-name") && moving("#archive-category"),
"切列时底部列名与顶部分类同时滚动",
);
check(
value("#column-name") === archiveColumns[0] &&
value("#archive-category") === archiveColumns[0],
"分类语义文字立即对应当前列",
);
check(
ticks.every((node, i) => q("#file-ticks").children[i] === node),
"切列复用原有八个刻度节点",
);
check(
height(ticks[1]) > 12 &&
height(ticks[1]) < 33 &&
height(ticks[5]) > 12 &&
height(ticks[5]) < 33,
"跨列选中刻度在新旧位置间连续伸缩",
);
check(
app.stats().selected === records[a[1]].id,
"切列保留各自档案位置记忆",
);
button("column-next").click();
await pause(55);
button("column-prev").click();
await pause(650);
check(
ticks[1].getAttribute("aria-pressed") === "true" &&
Math.abs(height(ticks[1]) - 33) < 0.1,
"快速反向切列最终刻度正确",
);
ticks[3].click();
check(
app.stats().selected === records[a[3]].id,
"复用刻度按钮正确映射当前列档案",
);
button("column-next").click();
await pause(650);
check(
app.stats().selected === records[b[5]].id &&
ticks[5].getAttribute("aria-pressed") === "true",
"另一列的选择记忆与刻度一致",
);
check(
parseFloat(
win.getComputedStyle(q("#column-name .rn-face")).fontSize,
) === parseFloat(win.getComputedStyle(q("#column-name")).fontSize),
"列名内部文字保持原字号",
);
check(
parseFloat(
win.getComputedStyle(q("#archive-category .rn-face")).marginLeft,
) === 0,
"分类内部字形没有继承分隔符外边距",
);
for (let i = 0; i < 7; i++) {
button("column-next").click();
await pause(35);
}
await pause(700);
check(
value("#column-name") === archiveColumns[3] &&
value("#archive-category") === archiveColumns[3],
"跨首尾连续切列收敛到最终分类",
);
check(!moving("#selected-clearance"), "内容不变的权限标签不重复滚动");
app.select(a[0]);
await pause(2200);
const canvas = q("#three-scene canvas"),
label = q("#hover-label"),
rect = canvas.getBoundingClientRect(),
hits = [];
function hover(p) {
canvas.dispatchEvent(
new win.PointerEvent("pointermove", {
clientX: rect.left + p.x,
clientY: rect.top + p.y,
bubbles: true,
}),
);
}
for (
let y = rect.height * 0.2;
y < rect.height * 0.8 && hits.length < 3;
y += 22
) {
for (
let x = rect.width * 0.35;
x < rect.width * 0.93 && hits.length < 3;
x += 22
) {
hover({ x, y });
const text = value("#hover-title");
if (!label.hidden && !hits.some((p) => p.text === text))
hits.push({ x, y, text });
}
}
check(hits.length >= 2, "实际场景射线可命中至少两份不同档案");
canvas.dispatchEvent(new win.PointerEvent("pointerleave"));
hover(hits[0]);
check(
!label.hidden &&
!moving("#hover-title") &&
value("#hover-title") === hits[0].text,
"首次悬停直接显示当前标题",
);
await pause(100);
hover(hits[1]);
await pause(70);
check(
moving("#hover-title") && value("#hover-title") === hits[1].text,
"悬停从第一份切到第二份即开始文字滚动",
);
hover(hits[0]);
hover(hits[1]);
await pause(650);
check(
value("#hover-title") === hits[1].text && !moving("#hover-title"),
"快速悬停切换显示最终标题",
);
canvas.dispatchEvent(new win.PointerEvent("pointerleave"));
check(
label.hidden && !moving("#hover-title"),
"离开阵列结束隐藏标签的动画",
);
button("column-next").click();
await pause(60);
app.detail();
check(
!moving("#column-name") && !moving("#archive-category") && label.hidden,
"进入详情结束导航文字动画并隐藏悬停",
);
app.archive();
await pause(500);
button("settings").click();
await pause(350);
const reduced = q('[data-pref="reduced"]'),
before = reduced.checked;
if (!before) reduced.click();
app.select(b[2]);
await pause(40);
const reducedSettled =
!moving("#column-name") &&
!moving("#archive-category") &&
!moving("#selected-title");
if (!before) reduced.click();
check(reducedSettled, "减少动态效果立即更新全部档案文字");
result.textContent = JSON.stringify({ passed: true, checks }, null, 2);
} catch (error) {
result.textContent = JSON.stringify(
{ passed: false, checks, error: error.message },
null,
2,
);
console.error(error);
}
</script>
</html>