- Move the read-only OpenAPI compat surface from /api/v2/assets to /mjr/am/compat/assets so it never squats ComfyUI core's /api namespace; the legacy alias is opt-in via MJR_AM_LEGACY_API_V2_ASSETS=1. - Allow Python 3.14 (requires-python <3.15) and add 3.14 to CI and tox. - Stop wrapping api.queuePrompt: progress tracking now resolves the running prompt from the core Jobs API (getJobDetail), then /queue and /history, driven by websocket events. Remove wrapHostQueuePrompt. - Add opt-in Majoor.Integration.HideNativeAssetsTab setting that hides ComfyUI's native Assets sidebar tab through the official getSidebarTabs/unregisterSidebarTab API and restores it when disabled. - Fix all 913 type errors in tsconfig.test.json and type-check tests in CI; drop a duplicated `stubs` key in asset_card_inner test; make addToastHistory's type argument optional for the object form. - Update audit report, changelog, API reference and env var docs.
156 lines
5.2 KiB
TypeScript
156 lines
5.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const getRuntimeStatus = vi.fn();
|
|
const getSecuritySettings = vi.fn();
|
|
const hasRuntimeSecurityToken = vi.fn();
|
|
const t = vi.fn((key, fallback, vars) => {
|
|
if (!fallback) return key;
|
|
if (!vars || typeof vars !== "object") return fallback;
|
|
return String(fallback).replace(/\{(\w+)\}/g, (_m, name) => String(vars[name] ?? `{${name}}`));
|
|
});
|
|
|
|
vi.mock("../api/client.js", () => ({
|
|
getRuntimeStatus,
|
|
getSecuritySettings,
|
|
hasRuntimeSecurityToken,
|
|
}));
|
|
|
|
vi.mock("../app/i18n.js", () => ({
|
|
t,
|
|
}));
|
|
|
|
function createElement(tagName, registry) {
|
|
return {
|
|
tagName,
|
|
id: "",
|
|
style: {},
|
|
textContent: "",
|
|
title: "",
|
|
children: [],
|
|
parentElement: null,
|
|
appendChild(child) {
|
|
child.parentElement = this;
|
|
this.children.push(child);
|
|
if (child.id) registry.set(child.id, child);
|
|
return child;
|
|
},
|
|
remove() {
|
|
if (this.parentElement) {
|
|
this.parentElement.children = this.parentElement.children.filter(
|
|
(child) => child !== this,
|
|
);
|
|
}
|
|
if (this.id) registry.delete(this.id);
|
|
},
|
|
};
|
|
}
|
|
|
|
function createDocument() {
|
|
const registry = new Map();
|
|
const host = createElement("div", registry);
|
|
(host as any).className = "mjr-assets-manager mjr-am-container";
|
|
return {
|
|
host,
|
|
createElement: (tagName) => createElement(tagName, registry),
|
|
getElementById: (id) => registry.get(id) || null,
|
|
querySelector: (selector) =>
|
|
selector === ".mjr-assets-manager.mjr-am-container" ? host : null,
|
|
__registry: registry,
|
|
};
|
|
}
|
|
|
|
function createLocalStorage(initial = {}) {
|
|
const store = new Map(Object.entries(initial));
|
|
return {
|
|
getItem: vi.fn((key) => store.get(String(key)) ?? null),
|
|
setItem: vi.fn((key, value) => store.set(String(key), String(value))),
|
|
removeItem: vi.fn((key) => store.delete(String(key))),
|
|
key: vi.fn((index) => Array.from(store.keys())[index] ?? null),
|
|
get length() {
|
|
return store.size;
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("settingsRuntime", () => {
|
|
beforeEach(() => {
|
|
vi.resetModules();
|
|
vi.clearAllMocks();
|
|
const document = createDocument();
|
|
globalThis.document = document as any;
|
|
globalThis.window = {
|
|
__MJR_RUNTIME_STATUS_INTERVAL__: null,
|
|
__MJR_RUNTIME_STATUS_INFLIGHT__: null,
|
|
__MJR_RUNTIME_STATUS_MISS_COUNT__: null,
|
|
__MJR_RUNTIME_STATUS_HIDE_TIMEOUT__: null,
|
|
__MJR_RUNTIME_STATUS_HIDDEN__: null,
|
|
__MJR_RUNTIME_STATUS_SETTINGS_LISTENER__: null,
|
|
localStorage: createLocalStorage() as any,
|
|
addEventListener: vi.fn(),
|
|
} as any;
|
|
globalThis.sessionStorage = {
|
|
getItem: vi.fn(() => "token_1234567890abcd"),
|
|
} as any;
|
|
hasRuntimeSecurityToken.mockReturnValue(true);
|
|
globalThis.getComputedStyle = vi.fn(() => ({ position: "static" })) as any;
|
|
globalThis.setInterval = vi.fn(() => 1) as any;
|
|
globalThis.clearInterval = vi.fn();
|
|
});
|
|
|
|
it("renders runtime metrics and active write auth state in the dashboard", async () => {
|
|
getRuntimeStatus.mockResolvedValue({
|
|
ok: true,
|
|
data: {
|
|
db: { active_connections: 3 },
|
|
index: { enrichment_queue_length: 4 },
|
|
watcher: { pending_files: 5 },
|
|
},
|
|
});
|
|
getSecuritySettings.mockResolvedValue({
|
|
ok: true,
|
|
data: {
|
|
prefs: {
|
|
allow_write: true,
|
|
require_auth: true,
|
|
token_configured: true,
|
|
token_hint: "...abcd",
|
|
},
|
|
},
|
|
});
|
|
|
|
const mod = await import("../app/settings/settingsRuntime.js");
|
|
mod.startRuntimeStatusDashboard();
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
|
|
const dashboard = globalThis.document.getElementById("mjr-runtime-status-dashboard");
|
|
expect(dashboard).toBeTruthy();
|
|
expect((dashboard as any).__mjrMetricsLine.textContent).toBe(
|
|
"DB active: 3 | Enrich Q: 4 | Watcher pending: 5",
|
|
);
|
|
expect((dashboard as any).__mjrAuthLine.textContent).toBe("Write auth: active ...abcd");
|
|
expect((dashboard as any).__mjrAuthLine.style.color).toBe("#7ee0a0");
|
|
});
|
|
|
|
it("does not render the dashboard when the setting is hidden", async () => {
|
|
globalThis.window.localStorage = createLocalStorage({
|
|
mjrSettings: JSON.stringify({
|
|
version: 1,
|
|
data: {
|
|
observability: {
|
|
runtimeDashboardMode: "hidden",
|
|
},
|
|
},
|
|
}),
|
|
}) as any;
|
|
|
|
const mod = await import("../app/settings/settingsRuntime.js");
|
|
mod.startRuntimeStatusDashboard();
|
|
await Promise.resolve();
|
|
|
|
expect(globalThis.document.getElementById("mjr-runtime-status-dashboard")).toBeNull();
|
|
expect(getRuntimeStatus).not.toHaveBeenCalled();
|
|
expect(getSecuritySettings).not.toHaveBeenCalled();
|
|
});
|
|
});
|