import assert from "node:assert/strict"; import fs from "node:fs"; import vm from "node:vm"; const sourcePath = new URL("../web/js/o1keyUpdateButton.js", import.meta.url); const source = fs.readFileSync(sourcePath, "utf8").replace(/^import .*;\s*$/gm, ""); class Element { constructor(tagName) { this.tagName = tagName; this.children = []; this.listeners = new Map(); this.style = {}; this.disabled = false; this.textContent = ""; } append(...children) { this.children.push(...children); } replaceChildren(...children) { this.children = [...children]; } setAttribute(name, value) { this[name] = value; } addEventListener(name, listener) { this.listeners.set(name, listener); } } let extension; let tab; let requests = 0; let fetchFailure = false; let response = { ok: true, async json() { return { updated: true, version: "abc1234", requirements_changed: false }; } }; const app = { registerExtension(value) { extension = value; }, extensionManager: { getSidebarTabs() { return tab ? [tab] : []; }, registerSidebarTab(value) { tab = value; }, }, }; const api = { async fetchApi(path, options) { requests++; assert.equal(path, "/o1key/update"); assert.equal(options.method, "POST"); assert.equal(options.headers["X-O1Key-Update"], "1"); if (fetchFailure) throw new Error("offline"); return response; }, }; vm.runInNewContext(source, { app, api, document: { createElement: (tag) => new Element(tag) }, window: { confirm: () => true } }); extension.setup(); assert.equal(tab.id, "o1key-update"); assert.equal(tab.title, "更新"); const firstTab = tab; extension.setup(); assert.equal(tab, firstTab); const container = new Element("div"); tab.render(container); const [, description, sourceLabel, button, status, suggestion] = container.children[0].children; assert.match(description.textContent, /安全快进/); assert.match(sourceLabel.textContent, /git\.o1key\.com\/publisher\/comfyui_o1key/); await button.listeners.get("click")(); assert.equal(requests, 1); assert.match(status.textContent, /abc1234/); assert.match(suggestion.textContent, /重启 ComfyUI/); response = { ok: true, async json() { return { updated: true, version: "def5678", requirements_changed: true }; } }; await button.listeners.get("click")(); assert.equal(requests, 2); assert.match(suggestion.textContent, /安装 requirements.txt/); response = { ok: true, async json() { return { updated: false, version: "def5678" }; } }; await button.listeners.get("click")(); assert.match(status.textContent, /已是最新版本/); assert.match(suggestion.textContent, /无需重启/); response = { ok: false, status: 409, async json() { return { code: "local_changes", error: "插件目录有本地修改", suggestion: "请先保存修改" }; } }; await button.listeners.get("click")(); assert.match(status.textContent, /本地修改/); assert.match(suggestion.textContent, /请先保存修改/); response = { ok: false, status: 404 }; await button.listeners.get("click")(); assert.match(status.textContent, /尚未加载/); assert.match(suggestion.textContent, /重启 ComfyUI/); response = { ok: false, status: 500, async json() { throw new Error("invalid JSON"); } }; await button.listeners.get("click")(); assert.match(status.textContent, /无法读取/); fetchFailure = true; await button.listeners.get("click")(); assert.match(status.textContent, /无法连接本地 ComfyUI/); assert.equal(button.disabled, false);