185 lines
6.1 KiB
HTML
185 lines
6.1 KiB
HTML
<!doctype html>
|
|
<html lang="zh-CN">
|
|
<meta charset="utf-8" />
|
|
<title>标题与查看器过渡检查</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
background: #eae5e1;
|
|
color: #252620;
|
|
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 } from "/src/data.ts";
|
|
import { createRollingText } from "@kitlangton/rolling-number";
|
|
import "@kitlangton/rolling-number/styles.css";
|
|
const iframe = document.querySelector("iframe"),
|
|
result = document.querySelector("#result");
|
|
const 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 > 10000) throw new Error("等待状态超时");
|
|
await pause(25);
|
|
}
|
|
}
|
|
try {
|
|
await until(
|
|
() =>
|
|
iframe.contentWindow.rhine?.stats().loaded &&
|
|
!iframe.contentDocument.querySelector("#loading") &&
|
|
iframe.contentWindow.rhine.stats().mode === "archive",
|
|
);
|
|
const win = iframe.contentWindow,
|
|
doc = iframe.contentDocument,
|
|
app = win.rhine;
|
|
app.archive();
|
|
await pause(300);
|
|
const title = doc.querySelector("#selected-title");
|
|
const value = () => title.querySelector(".rn-value")?.textContent;
|
|
const moving = () =>
|
|
title
|
|
.getAnimations({ subtree: true })
|
|
.some((a) => a.playState === "running");
|
|
app.select(1);
|
|
await until(moving);
|
|
check(
|
|
value() === records[1].title,
|
|
"普通切换播放文字滚动并立即更新可访问标题",
|
|
);
|
|
app.select(2);
|
|
app.select(3);
|
|
check(value() === records[3].title, "快速切换以最后选择为目标");
|
|
check(
|
|
win.getComputedStyle(title, "::after").content === "none",
|
|
"快切期间不再显示黑色横条",
|
|
);
|
|
app.select(4);
|
|
app.select(5);
|
|
await pause(60);
|
|
await until(() => !moving());
|
|
check(value() === records[5].title, "停止后滚动落到最终标题");
|
|
const finalGlyphs = [...title.querySelectorAll(".rn-visual .rn-face")]
|
|
.map((face) => face.textContent)
|
|
.join("");
|
|
check(finalGlyphs === records[5].title, "滚动完成后没有残留的旧文字");
|
|
app.select(6);
|
|
app.select(7);
|
|
app.detail();
|
|
check(
|
|
!moving() && value() === records[7].title,
|
|
"切进详情结束标题滚动并保留最终文字",
|
|
);
|
|
await until(() => app.stats().canInspect);
|
|
const opener = doc.querySelector('[data-action="model-viewer"]');
|
|
opener.focus();
|
|
opener.click();
|
|
const viewer = doc.querySelector(".model-viewer");
|
|
check(
|
|
viewer.dataset.transition === "opening" && !viewer.hidden,
|
|
"查看器保留进入动画阶段",
|
|
);
|
|
const before = win.getComputedStyle(viewer).opacity;
|
|
viewer
|
|
.querySelector("canvas")
|
|
.dispatchEvent(
|
|
new win.KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
|
|
);
|
|
check(
|
|
viewer.dataset.transition === "closing" && !viewer.hidden,
|
|
"进入途中 Esc 转入退出动画",
|
|
);
|
|
check(
|
|
doc.querySelector("#detail-ui").inert,
|
|
"退出动画结束前详情仍不接收输入",
|
|
);
|
|
await until(() => viewer.hidden);
|
|
check(
|
|
!doc.querySelector("#detail-ui").inert && doc.activeElement === opener,
|
|
"退出结束恢复详情与原按钮焦点",
|
|
);
|
|
opener.click();
|
|
await until(
|
|
() =>
|
|
viewer.dataset.transition === "open" &&
|
|
JSON.parse(viewer.dataset.stats || "{}").ready,
|
|
);
|
|
check(!viewer.hidden, "中途关闭后可重新打开并加载模型");
|
|
viewer.querySelector('[data-viewer="explode"]').click();
|
|
await pause(100);
|
|
viewer.querySelector('[data-viewer="close"]').click();
|
|
viewer.querySelector('[data-viewer="close"]').click();
|
|
await until(() => viewer.hidden);
|
|
check(
|
|
!doc.querySelector("#detail-ui").inert,
|
|
"拆解中重复关闭也能完整退出",
|
|
);
|
|
const host = document.createElement("span");
|
|
host.style.cssText =
|
|
"display:inline-block;font:24px system-ui;margin:16px";
|
|
document.body.append(host);
|
|
const text = createRollingText(host, {
|
|
text: "初始",
|
|
transition: "direct",
|
|
stagger: "none",
|
|
duration: 460,
|
|
});
|
|
await pause(100);
|
|
text.update({ text: "长期实验研究记录" });
|
|
await pause(90);
|
|
text.update({ text: "光" });
|
|
await pause(60);
|
|
text.update({ text: "光学研究 🧬 / A-01" });
|
|
await pause(100);
|
|
await until(
|
|
() =>
|
|
!host
|
|
.getAnimations({ subtree: true })
|
|
.some((a) => a.playState === "running"),
|
|
);
|
|
check(
|
|
host.querySelector(".rn-value").textContent === "光学研究 🧬 / A-01",
|
|
"长短标题与中文、符号混排能正确收敛",
|
|
);
|
|
check(
|
|
host.querySelectorAll(".rn-slot").length <=
|
|
[...new Intl.Segmenter().segment("光学研究 🧬 / A-01")].length,
|
|
"连续替换后回收旧字槽",
|
|
);
|
|
text.update({ text: "最终标题", animated: false });
|
|
check(
|
|
host.querySelector(".rn-value").textContent === "最终标题" &&
|
|
host.getAnimations({ subtree: true }).length === 0,
|
|
"减少动态效果立即显示最终标题",
|
|
);
|
|
text.destroy();
|
|
host.remove();
|
|
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>
|