252 lines
8.3 KiB
HTML
252 lines
8.3 KiB
HTML
<!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>
|