Files
halo-theme-WarmIsland/scripts/smoke-clickfx.mjs
T
nxxy335 125d0592c9 fix: 修复升级后整站无样式与点击特效失效
- 旧哈希静态资源随版本升级被删除,被缓存的 HTML 仍请求旧文件名导致
  FileNotFoundException 500,补 compat/assets 历史资源并在构建时合并
- postbuild 增加静态资源引用校验,缺失即构建失败
- ClickEffect 改为常驻配置节点 + data 属性驱动,避免配置未落库时静默失效;
  强制回流修正 transition 不触发;特效层挂到 body 下避免被导航栏遮挡
- 新增 scripts: postbuild/snapshot-assets/package/smoke-clickfx
- 版本 1.2.1
2026-09-27 16:27:51 +08:00

155 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 点击特效脚本冒烟测试(Node 内置模块,无第三方依赖)。
*
* 直接从构建产物 templates/index.html 中取出特效脚本,放进桩 DOM 里执行,
* 校验:启用/禁用判定、拖尾与点击分支、节点生成与定位。
*
* 这类 bug 的共同点是"不报错但没效果",靠读代码很难发现,所以固化成测试。
* 用法:npm run build && npm run smoke
*/
import fs from "node:fs";
import path from "node:path";
import vm from "node:vm";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const template = path.join(root, "templates", "index.html");
if (!fs.existsSync(template)) {
console.error("[smoke] 未找到 templates/index.html,请先执行 npm run build");
process.exit(1);
}
const html = fs.readFileSync(template, "utf8");
const anchor = html.indexOf('id="wi-click-effect"');
if (anchor === -1) {
console.error("[smoke] 模板中未找到 #wi-click-effect 节点");
process.exit(1);
}
const scriptOpen = html.indexOf("<script", anchor);
const scriptStart = html.indexOf(">", scriptOpen) + 1;
const scriptEnd = html.indexOf("</script>", scriptStart);
const code = html.slice(scriptStart, scriptEnd);
const failures = [];
function makeEl(tag) {
return {
tagName: tag,
className: "",
innerHTML: "",
textContent: "",
children: [],
parentNode: null,
style: {},
_attrs: {},
get offsetWidth() {
return 1;
},
getAttribute(name) {
return Object.prototype.hasOwnProperty.call(this._attrs, name) ? this._attrs[name] : null;
},
appendChild(child) {
if (child.parentNode) return child;
child.parentNode = this;
this.children.push(child);
return child;
},
removeChild(child) {
const i = this.children.indexOf(child);
if (i >= 0) {
this.children.splice(i, 1);
child.parentNode = null;
}
return child;
},
};
}
function run(attrs, expectEnabled, label) {
const cfg = makeEl("div");
Object.assign(cfg._attrs, attrs);
const body = makeEl("body");
const listeners = {};
const sandbox = {
document: {
getElementById: (id) => (id === "wi-click-effect" ? cfg : null),
createElement: makeEl,
body,
addEventListener(type, fn) {
(listeners[type] = listeners[type] || []).push(fn);
},
},
window: {},
requestAnimationFrame: (fn) => fn(),
setTimeout: () => 0,
};
vm.createContext(sandbox);
vm.runInContext(code, sandbox);
const hasListener = Object.keys(listeners).length > 0;
if (hasListener !== expectEnabled) {
failures.push(`${label}:期望 ${expectEnabled ? "注册监听" : "不注册监听"},实际相反`);
return;
}
if (!expectEnabled) {
console.log(` ✔ ${label} -> 已跳过`);
return;
}
const layer = body.children.find((c) => c.className === "wi-click-fx-layer");
if (!layer) {
failures.push(`${label}:未创建 .wi-click-fx-layer`);
return;
}
if (layer.children.length !== 0) {
failures.push(`${label}:初始化时特效层不应有子节点`);
return;
}
const type = attrs["data-effect"] || "(缺失,走兜底 values)";
if (listeners.click) {
listeners.click[0]({ clientX: 120, clientY: 240 });
} else if (listeners.mousemove) {
listeners.mousemove[0]({ clientX: 120, clientY: 240 });
} else {
failures.push(`${label}:未注册 click 或 mousemove 监听`);
return;
}
const spawned = layer.children;
if (spawned.length === 0) {
failures.push(`${label}:effect=${type} 触发后未生成任何特效节点`);
return;
}
const first = spawned[0];
if (!String(first.className).startsWith("wi-click-fx ")) {
failures.push(`${label}:特效节点缺少 wi-click-fx 基类(${first.className})`);
}
if (first.style.left !== "120px" || first.style.top !== "240px") {
failures.push(`${label}:定位不正确(${first.style.left}, ${first.style.top})`);
}
console.log(` ✔ ${label} effect=${type} -> ${spawned.length} 个节点,className=${first.className}`);
}
console.log("禁用场景:");
run({ "data-enabled": "false", "data-effect": "values" }, false, "显式关闭");
run({ "data-effect": "values" }, false, "配置缺失");
run({ "data-enabled": "true", "data-effect": "off" }, false, "类型选了「关闭」");
console.log("点击场景:");
for (const effect of ["values", "hearts", "fireworks", "ripples", "particles", "stars"]) {
run({ "data-enabled": "true", "data-effect": effect }, true, effect);
}
console.log("兜底与拖尾:");
run({ "data-enabled": "true" }, true, "type 缺省");
run({ "data-enabled": "true", "data-effect": "star-trail" }, true, "star-trail");
run({ "data-enabled": "true", "data-effect": "particle-trail" }, true, "particle-trail");
if (failures.length > 0) {
console.error("\n[smoke] 失败:");
for (const f of failures) console.error(" - " + f);
process.exit(1);
}
console.log("\n[smoke] 点击特效全部通过");