"use strict"; const assert = require("node:assert/strict"); const fs = require("node:fs"); const path = require("node:path"); const vm = require("node:vm"); /** 用最小 DOM 替身检查快速切换英雄时旧请求不会覆盖新预览。 */ async function checkLatestChampionWins() { const elements = new Map(); const pending = new Map(); const element = (id) => { if (!elements.has(id)) elements.set(id, { id, dataset: {}, style: { setProperty() {} }, scrollTop: 0, classList: { toggle() {}, contains() { return false; } }, setAttribute() {}, contains() { return false; }, appendChild() {}, querySelector() { return null; }, querySelectorAll() { return []; }, }); return elements.get(id); }; const document = { getElementById: element, documentElement: element("root"), activeElement: null, createElement() { return { ...element("button"), dataset: {} }; }, querySelectorAll() { return []; }, }; const reply = (data) => ({ ok: true, json: async () => data }); const context = vm.createContext({ document, localStorage: { getItem() { return null; }, setItem() {} }, setTimeout, clearTimeout, setInterval() {}, fetch(url) { if (url.startsWith("/api/skins")) { const id = Number(new URL(url, "http://localhost").searchParams.get("id")); return new Promise((resolve) => pending.set(id, resolve)); } if (url === "/api/champions") return Promise.resolve(reply([])); if (url === "/api/current") return Promise.resolve(reply({ championId: 0 })); return Promise.resolve(reply({ lcu: "disconnected", profile: { server: "global", injectionMode: "cslol" }, skins: 0 })); }, }); vm.runInContext(fs.readFileSync(path.join(__dirname, "renderer.js"), "utf8"), context); const first = vm.runInContext("openChampion(1)", context); const second = vm.runInContext("openChampion(2)", context); pending.get(2)(reply({ id: 2, name: "新英雄", skins: [] })); await second; pending.get(1)(reply({ id: 1, name: "旧英雄", skins: [] })); await first; assert.equal(vm.runInContext("detail.id", context), 2); } checkLatestChampionWins().catch((error) => { console.error(error); process.exitCode = 1; });