Files
MajoorWaldi-ComfyUI-Majoor-…/ui/tests/grid_selection_manager.vitest.ts
T
Ewald ALOEBOETOE d0ab7d594e Resolve remaining ComfyUI integration conflicts
- 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.
2026-11-03 21:16:34 +00:00

114 lines
3.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
getSelectedIdSet,
reconcileSelectionIds,
safeEscapeId,
setSelectionIds,
syncSelectionClasses,
} from "../features/grid/GridSelectionManager.js";
function makeCard(id) {
const classSet = new Set();
const attrs = new Map();
return {
dataset: { mjrAssetId: String(id) },
classList: {
add(v) {
classSet.add(v);
},
remove(v) {
classSet.delete(v);
},
has(v) {
return classSet.has(v);
},
},
setAttribute(k, v) {
attrs.set(k, v);
},
getAttribute(k) {
return attrs.get(k);
},
};
}
describe("GridSelectionManager", () => {
it("supports single and multi selection dataset keys", () => {
const g1 = { dataset: { mjrSelectedAssetIds: JSON.stringify([1, "2"]) } };
expect(Array.from(getSelectedIdSet(g1)).sort()).toEqual(["1", "2"]);
const g2 = { dataset: { mjrSelectedAssetId: "9" } };
expect(Array.from(getSelectedIdSet(g2))).toEqual(["9"]);
});
it("updates cards and emits event when selection changes", () => {
const cards = [makeCard(1), makeCard(2), makeCard(3)];
const events = [];
globalThis.CustomEvent = class {
constructor(type, init) {
(this as any).type = type;
(this as any).detail = init?.detail;
}
} as any;
globalThis.window = {
dispatchEvent(evt) {
events.push(evt);
},
} as any;
const grid = {
dataset: {},
dispatchEvent(evt) {
events.push(evt);
},
};
syncSelectionClasses(grid, new Set(["2"]), () => cards);
expect(cards[1].classList.has("is-selected")).toBe(true);
expect(cards[0].getAttribute("aria-selected")).toBe("false");
const selected = setSelectionIds(grid, ["1", 3, "3"], { activeId: "3" }, () => cards);
expect(selected.sort()).toEqual(["1", "3"]);
expect((grid.dataset as any).mjrSelectedAssetId).toBe("3");
expect(events.length).toBeGreaterThanOrEqual(1);
});
it("falls back when CSS.escape is unavailable", () => {
globalThis.CSS = undefined;
expect(safeEscapeId('a"b\\c')).toBe('a\\"b\\\\c');
});
it("reconciles selection against visible assets and reports pruned items", () => {
const cards = [makeCard(1), makeCard(2), makeCard(3)];
const events = [];
globalThis.CustomEvent = class {
constructor(type, init) {
(this as any).type = type;
(this as any).detail = init?.detail;
}
} as any;
globalThis.window = {
dispatchEvent(evt) {
events.push(evt);
},
} as any;
const grid = {
dataset: {
mjrSelectedAssetIds: JSON.stringify(["1", "2", "9"]),
mjrSelectedAssetId: "2",
},
dispatchEvent(evt) {
events.push(evt);
},
};
const result = reconcileSelectionIds(grid, ["2", "3"], { activeId: "2" }, () => cards);
expect(result.pruned).toBe(2);
expect(result.selectedIds).toEqual(["2"]);
expect(grid.dataset.mjrSelectedAssetIds).toBe(JSON.stringify(["2"]));
expect(grid.dataset.mjrSelectedAssetId).toBe("2");
expect(events.some((evt) => evt.type === "mjr:selection-changed")).toBe(true);
});
});