Initial commit: RHINE LAB · ANALYSIS OS:三维界面与动效实验(本仓库不含个人归档索引数据)
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<meta charset="utf-8" />
|
||||
<title>详情与弹窗过渡检查</title>
|
||||
<style>
|
||||
body {
|
||||
font: 14px system-ui;
|
||||
background: #e8e5e1;
|
||||
color: #252820;
|
||||
padding: 20px;
|
||||
}
|
||||
iframe {
|
||||
width: 1100px;
|
||||
height: 620px;
|
||||
border: 0;
|
||||
}
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
<h1>详情与弹窗过渡检查</h1>
|
||||
<pre id="result">正在检查…</pre>
|
||||
<iframe src="/?scene=archive" title="真实应用"></iframe>
|
||||
<script type="module">
|
||||
const frame = document.querySelector("iframe"),
|
||||
result = document.querySelector("#result"),
|
||||
checks = [];
|
||||
const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
const check = (condition, name) => {
|
||||
if (!condition) throw Error(name);
|
||||
checks.push(name);
|
||||
};
|
||||
async function until(fn) {
|
||||
const start = performance.now();
|
||||
while (!fn()) {
|
||||
if (performance.now() - start > 30000) throw Error("等待超时");
|
||||
await pause(25);
|
||||
}
|
||||
}
|
||||
let previousPrefs, previousSaved;
|
||||
try {
|
||||
await until(
|
||||
() =>
|
||||
frame.contentWindow.rhine?.stats().loaded &&
|
||||
!frame.contentDocument.querySelector("#loading"),
|
||||
);
|
||||
const win = frame.contentWindow,
|
||||
doc = frame.contentDocument,
|
||||
app = win.rhine;
|
||||
const q = (selector) => doc.querySelector(selector);
|
||||
previousPrefs = win.localStorage.getItem("rhine-settings");
|
||||
previousSaved = win.localStorage.getItem("rhine-saved");
|
||||
const key = (element, value) =>
|
||||
element.dispatchEvent(
|
||||
new win.KeyboardEvent("keydown", { key: value, bubbles: true }),
|
||||
);
|
||||
const finish = (element) =>
|
||||
element
|
||||
.getAnimations({ subtree: true })
|
||||
.forEach((animation) => animation.finish());
|
||||
const openSettings = async () => {
|
||||
q('[data-action="settings"]').click();
|
||||
await until(() => q('[data-pref="reduced"]'));
|
||||
};
|
||||
await openSettings();
|
||||
let reduced = q('[data-pref="reduced"]');
|
||||
if (reduced.checked) reduced.click();
|
||||
q('[data-action="close-modal"]').click();
|
||||
await until(() => !q(".terminal-modal"));
|
||||
|
||||
const opener = q('[data-action="search"]');
|
||||
opener.focus();
|
||||
opener.click();
|
||||
let backdrop = q(".modal-backdrop");
|
||||
check(
|
||||
backdrop.dataset.transition === "opening",
|
||||
"检索弹窗以入场状态打开",
|
||||
);
|
||||
finish(backdrop);
|
||||
await pause(0);
|
||||
q('[data-action="close-modal"]').click();
|
||||
check(
|
||||
backdrop.isConnected && backdrop.dataset.transition === "closing",
|
||||
"关闭时保留弹窗节点进行淡出",
|
||||
);
|
||||
check(
|
||||
q("#archive-ui").inert && q(".system-nav").inert,
|
||||
"退出完成前保持背景输入隔离",
|
||||
);
|
||||
key(q("#archive-search"), "Escape");
|
||||
key(q("#archive-search"), "Escape");
|
||||
finish(backdrop);
|
||||
await until(() => !q(".terminal-modal"));
|
||||
check(
|
||||
doc.activeElement === opener && !q("#archive-ui").inert,
|
||||
"重复 Esc 后正确释放弹窗并恢复原按钮焦点",
|
||||
);
|
||||
|
||||
opener.click();
|
||||
backdrop = q(".modal-backdrop");
|
||||
const intro = backdrop.getAnimations()[0];
|
||||
intro.pause();
|
||||
intro.currentTime = 120;
|
||||
const before = Number(win.getComputedStyle(backdrop).opacity);
|
||||
q('[data-action="close-modal"]').click();
|
||||
check(
|
||||
Math.abs(Number(win.getComputedStyle(backdrop).opacity) - before) <
|
||||
0.02,
|
||||
"打开途中关闭从当前透明度接续",
|
||||
);
|
||||
finish(backdrop);
|
||||
await until(() => !q(".terminal-modal"));
|
||||
|
||||
opener.click();
|
||||
finish(q(".modal-backdrop"));
|
||||
await pause(0);
|
||||
const wanted = q('[data-result="5"]');
|
||||
wanted.click();
|
||||
check(
|
||||
q(".modal-backdrop").dataset.transition === "closing" &&
|
||||
app.stats().mode === "archive",
|
||||
"检索选档先完成弹窗退出",
|
||||
);
|
||||
finish(q(".modal-backdrop"));
|
||||
await until(
|
||||
() => app.stats().mode === "detail" && app.stats().canInspect,
|
||||
);
|
||||
check(
|
||||
app.stats().selected === "X-006" && !q("#detail-ui").inert,
|
||||
"弹窗退出后进入所选档案",
|
||||
);
|
||||
await until(() => doc.activeElement === q("#detail-content"));
|
||||
check(true, "详情可读后焦点进入正文");
|
||||
q('[data-tab="notes"]').click();
|
||||
const panel = q("#tab-panel"),
|
||||
content = panel.firstChild;
|
||||
panel.scrollTop = 80;
|
||||
const scroll = panel.scrollTop,
|
||||
bookmark = q('[data-action="bookmark"]');
|
||||
bookmark.focus();
|
||||
bookmark.click();
|
||||
check(
|
||||
q("#tab-panel") === panel &&
|
||||
panel.firstChild === content &&
|
||||
panel.scrollTop === scroll,
|
||||
"收藏保留正文节点和滚动位置",
|
||||
);
|
||||
check(
|
||||
doc.activeElement === bookmark &&
|
||||
bookmark.getAttribute("aria-pressed") ===
|
||||
String(app.stats().saved.includes("X-006")),
|
||||
"收藏保留按钮焦点并同步按下状态",
|
||||
);
|
||||
bookmark.click();
|
||||
q('[data-tab="history"]').click();
|
||||
q('[data-tab="overview"]').click();
|
||||
q('[data-tab="notes"]').click();
|
||||
check(
|
||||
panel.getAttribute("aria-labelledby") === "tab-notes" &&
|
||||
q('[data-tab="notes"]').getAttribute("aria-selected") === "true",
|
||||
"页签快速反向切换显示最后选择",
|
||||
);
|
||||
check(
|
||||
panel.getAnimations().length <= 1,
|
||||
"连续页签切换只保留当前内容动画",
|
||||
);
|
||||
|
||||
const detail = q("#detail-ui");
|
||||
finish(detail);
|
||||
await pause(0);
|
||||
q('[data-action="back"]').click();
|
||||
check(
|
||||
!detail.hidden &&
|
||||
detail.inert &&
|
||||
detail.dataset.transition === "closing",
|
||||
"详情返回时保留可见退出并禁用旧内容",
|
||||
);
|
||||
const shade = Number(
|
||||
q("#stage").style.getPropertyValue("--detail-shade"),
|
||||
);
|
||||
check(shade > 0.9, "返回首帧保留镜头对应的背景遮罩");
|
||||
const exit = detail.getAnimations()[0];
|
||||
exit.pause();
|
||||
exit.currentTime = 90;
|
||||
const detailBefore = Number(win.getComputedStyle(detail).opacity);
|
||||
app.detail();
|
||||
check(
|
||||
!detail.hidden &&
|
||||
detail.dataset.transition === "opening" &&
|
||||
Math.abs(
|
||||
Number(win.getComputedStyle(detail).opacity) - detailBefore,
|
||||
) < 0.02,
|
||||
"详情返回中重新进入从当前透明度衔接",
|
||||
);
|
||||
await until(() => app.stats().canInspect);
|
||||
app.archive();
|
||||
finish(detail);
|
||||
await until(() => detail.hidden);
|
||||
check(
|
||||
app.stats().mode === "archive" && doc.activeElement === q(".read-file"),
|
||||
"完整返回后隐藏详情并恢复读取按钮焦点",
|
||||
);
|
||||
await until(
|
||||
() =>
|
||||
Number(q("#stage").style.getPropertyValue("--detail-shade")) < 0.01,
|
||||
);
|
||||
check(true, "背景遮罩随镜头归位连续退回阵列");
|
||||
|
||||
await openSettings();
|
||||
reduced = q('[data-pref="reduced"]');
|
||||
reduced.click();
|
||||
q('[data-action="close-modal"]').click();
|
||||
check(!q(".terminal-modal"), "减少动态效果时弹窗立即退出");
|
||||
app.detail();
|
||||
await until(() => app.stats().canInspect);
|
||||
q('[data-tab="history"]').click();
|
||||
check(
|
||||
q("#tab-panel").getAnimations().length === 0,
|
||||
"减少动态效果时页签不播放动画",
|
||||
);
|
||||
app.archive();
|
||||
check(detail.hidden, "减少动态效果时详情立即隐藏");
|
||||
await openSettings();
|
||||
q('[data-pref="reduced"]').click();
|
||||
q('[data-action="close-modal"]').click();
|
||||
await until(() => !q(".terminal-modal"));
|
||||
result.textContent = JSON.stringify(
|
||||
{ passed: true, count: checks.length, checks },
|
||||
null,
|
||||
2,
|
||||
);
|
||||
} catch (error) {
|
||||
result.textContent = JSON.stringify(
|
||||
{ passed: false, checks, error: error.message },
|
||||
null,
|
||||
2,
|
||||
);
|
||||
console.error(error);
|
||||
} finally {
|
||||
const storage = frame.contentWindow.localStorage;
|
||||
if (previousPrefs !== undefined)
|
||||
previousPrefs === null
|
||||
? storage.removeItem("rhine-settings")
|
||||
: storage.setItem("rhine-settings", previousPrefs);
|
||||
if (previousSaved !== undefined)
|
||||
previousSaved === null
|
||||
? storage.removeItem("rhine-saved")
|
||||
: storage.setItem("rhine-saved", previousSaved);
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
Reference in New Issue
Block a user