You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

345 lines
11 KiB

import { describe, it, expect, beforeEach, vi } from "vitest";
import {
createDefaultNodeData,
defaultNodeDimensions,
GROUP_COLORS,
GROUP_COLOR_ORDER,
} from "../nodeDefaults";
// Mock localStorage for loadGenerateImageDefaults
const localStorageMock = (() => {
let store: Record<string, string> = {};
return {
getItem: vi.fn((key: string) => store[key] ?? null),
setItem: vi.fn((key: string, value: string) => {
store[key] = value;
}),
clear: vi.fn(() => {
store = {};
}),
};
})();
Object.defineProperty(globalThis, "localStorage", {
value: localStorageMock,
writable: true,
});
describe("nodeDefaults utilities", () => {
beforeEach(() => {
localStorageMock.clear();
vi.clearAllMocks();
});
describe("defaultNodeDimensions", () => {
it("has all expected node types", () => {
const expectedTypes = [
"imageInput",
"annotation",
"prompt",
"array",
"nanoBanana",
"generateVideo",
"llmGenerate",
"glbViewer",
"output",
];
expectedTypes.forEach((type) => {
expect(defaultNodeDimensions[type as keyof typeof defaultNodeDimensions]).toBeDefined();
});
});
it("has width and height for each node type", () => {
Object.values(defaultNodeDimensions).forEach((dims) => {
expect(dims).toHaveProperty("width");
expect(dims).toHaveProperty("height");
expect(typeof dims.width).toBe("number");
expect(typeof dims.height).toBe("number");
expect(dims.width).toBeGreaterThan(0);
expect(dims.height).toBeGreaterThan(0);
});
});
});
describe("GROUP_COLORS", () => {
it("has all expected color keys", () => {
const expectedColors = ["neutral", "blue", "green", "purple", "orange", "red"];
expectedColors.forEach((color) => {
expect(GROUP_COLORS[color as keyof typeof GROUP_COLORS]).toBeDefined();
});
});
it("has valid hex color values", () => {
Object.values(GROUP_COLORS).forEach((color) => {
expect(color).toMatch(/^#[0-9a-fA-F]{6}$/);
});
});
});
describe("GROUP_COLOR_ORDER", () => {
it("has all colors from GROUP_COLORS", () => {
GROUP_COLOR_ORDER.forEach((color) => {
expect(GROUP_COLORS[color]).toBeDefined();
});
});
it("has same length as GROUP_COLORS", () => {
expect(GROUP_COLOR_ORDER.length).toBe(Object.keys(GROUP_COLORS).length);
});
it("starts with neutral", () => {
expect(GROUP_COLOR_ORDER[0]).toBe("neutral");
});
});
describe("createDefaultNodeData", () => {
it("creates correct structure for imageInput", () => {
const data = createDefaultNodeData("imageInput");
expect(data).toHaveProperty("image", null);
expect(data).toHaveProperty("filename", null);
expect(data).toHaveProperty("dimensions", null);
});
it("creates correct structure for annotation", () => {
const data = createDefaultNodeData("annotation");
expect(data).toHaveProperty("sourceImage", null);
expect(data).toHaveProperty("annotations");
expect(Array.isArray((data as any).annotations)).toBe(true);
expect(data).toHaveProperty("outputImage", null);
});
it("creates correct structure for prompt", () => {
const data = createDefaultNodeData("prompt");
expect(data).toHaveProperty("prompt", "");
});
it("creates correct structure for array", () => {
const data = createDefaultNodeData("array");
expect(data).toHaveProperty("inputText", null);
expect(data).toHaveProperty("splitMode", "delimiter");
expect(data).toHaveProperty("delimiter", "*");
expect(data).toHaveProperty("regexPattern", "");
expect(data).toHaveProperty("trimItems", true);
expect(data).toHaveProperty("removeEmpty", true);
expect(data).toHaveProperty("selectedOutputIndex", null);
expect(data).toHaveProperty("outputItems");
expect(Array.isArray((data as any).outputItems)).toBe(true);
expect(data).toHaveProperty("outputText", "[]");
expect(data).toHaveProperty("error", null);
});
it("creates correct structure for nanoBanana", () => {
const data = createDefaultNodeData("nanoBanana");
expect(data).toHaveProperty("inputImages");
expect(data).toHaveProperty("inputPrompt", null);
expect(data).toHaveProperty("outputImage", null);
expect(data).toHaveProperty("aspectRatio");
expect(data).toHaveProperty("resolution");
expect(data).toHaveProperty("model");
expect(data).toHaveProperty("selectedModel");
expect(data).toHaveProperty("batchSize", 1);
expect(data).toHaveProperty("useGoogleSearch");
expect(data).toHaveProperty("status", "idle");
expect(data).toHaveProperty("error", null);
expect(data).toHaveProperty("imageHistory");
expect(data).toHaveProperty("selectedHistoryIndex", 0);
});
it("creates correct structure for generateVideo", () => {
const data = createDefaultNodeData("generateVideo");
expect(data).toHaveProperty("inputImages");
expect(data).toHaveProperty("inputPrompt", null);
expect(data).toHaveProperty("outputVideo", null);
expect(data).toHaveProperty("selectedModel");
expect(data).toHaveProperty("durationSeconds", 5);
expect(data).toHaveProperty("resolution", "720p");
expect(data).toHaveProperty("status", "idle");
expect(data).toHaveProperty("error", null);
expect(data).toHaveProperty("videoHistory");
expect(data).toHaveProperty("selectedVideoHistoryIndex", 0);
});
it("creates correct structure for llmGenerate", () => {
const data = createDefaultNodeData("llmGenerate");
expect(data).toHaveProperty("inputPrompt", null);
expect(data).toHaveProperty("inputImages");
expect(data).toHaveProperty("outputText", null);
expect(data).toHaveProperty("provider", "newapiwg");
expect(data).toHaveProperty("model");
expect(data).toHaveProperty("temperature");
expect(data).toHaveProperty("maxTokens");
expect(data).toHaveProperty("status", "idle");
expect(data).toHaveProperty("error", null);
});
it("creates correct structure for glbViewer", () => {
const data = createDefaultNodeData("glbViewer");
expect(data).toHaveProperty("glbUrl", null);
expect(data).toHaveProperty("filename", null);
expect(data).toHaveProperty("capturedImage", null);
});
it("creates correct structure for output", () => {
const data = createDefaultNodeData("output");
expect(data).toHaveProperty("image", null);
});
it("uses stored defaults for nanoBanana when available", () => {
const customSettings = {
aspectRatio: "16:9",
resolution: "2K",
model: "nano-banana",
useGoogleSearch: true,
};
localStorageMock.setItem(
"node-banana-nanoBanana-defaults",
JSON.stringify(customSettings)
);
const data = createDefaultNodeData("nanoBanana");
expect((data as any).aspectRatio).toBe("16:9");
expect((data as any).resolution).toBe("2K");
expect((data as any).model).toBe("nano-banana");
expect((data as any).useGoogleSearch).toBe(true);
});
it("uses node defaults selectedModel for nanoBanana when set", () => {
const nodeDefaultsConfig = {
generateImage: {
selectedModel: { provider: "fal", modelId: "flux-pro", displayName: "Flux Pro" },
aspectRatio: "4:3",
useGoogleSearch: true,
},
};
localStorageMock.setItem(
"node-banana-node-defaults",
JSON.stringify(nodeDefaultsConfig)
);
const data = createDefaultNodeData("nanoBanana");
expect((data as any).selectedModel).toEqual({
provider: "fal",
modelId: "flux-pro",
displayName: "Flux Pro",
});
expect((data as any).aspectRatio).toBe("4:3");
expect((data as any).useGoogleSearch).toBe(true);
});
it("uses gpt-image-2-all for nanoBanana when no node default is set", () => {
const data = createDefaultNodeData("nanoBanana");
expect((data as any).selectedModel).toEqual({
provider: "newapiwg",
modelId: "gpt-image-2-all",
displayName: "gpt-image-2-all",
capabilities: ["text-to-image", "image-to-image"],
});
});
it("uses node defaults selectedModel for generateVideo when set", () => {
const nodeDefaultsConfig = {
generateVideo: {
selectedModel: { provider: "replicate", modelId: "kling-video", displayName: "Kling Video" },
durationSeconds: 8,
resolution: "1080p",
soundEnabled: false,
},
};
localStorageMock.setItem(
"node-banana-node-defaults",
JSON.stringify(nodeDefaultsConfig)
);
const data = createDefaultNodeData("generateVideo");
expect((data as any).selectedModel).toEqual({
provider: "replicate",
modelId: "kling-video",
displayName: "Kling Video",
});
expect((data as any).durationSeconds).toBe(8);
expect((data as any).resolution).toBe("1080p");
expect((data as any).soundEnabled).toBe(false);
});
it("uses NewApiWG selectedModel for generateVideo when not set", () => {
const data = createDefaultNodeData("generateVideo");
expect((data as any).selectedModel).toEqual({
provider: "newapiwg",
modelId: "viduq3-turbo",
displayName: "viduq3-turbo",
capabilities: ["text-to-video", "image-to-video"],
});
expect((data as any).durationSeconds).toBe(5);
expect((data as any).resolution).toBe("720p");
expect((data as any).soundEnabled).toBeUndefined();
});
it("uses node defaults for llmGenerate when set", () => {
const nodeDefaultsConfig = {
llm: {
provider: "openai",
model: "gpt-4.1-mini",
temperature: 0.3,
maxTokens: 4096,
},
};
localStorageMock.setItem(
"node-banana-node-defaults",
JSON.stringify(nodeDefaultsConfig)
);
const data = createDefaultNodeData("llmGenerate");
expect((data as any).provider).toBe("openai");
expect((data as any).model).toBe("gpt-4.1-mini");
expect((data as any).temperature).toBe(0.3);
expect((data as any).maxTokens).toBe(4096);
});
it("falls back to NewApiWG llmGenerate defaults when not set", () => {
const data = createDefaultNodeData("llmGenerate");
expect((data as any).provider).toBe("newapiwg");
expect((data as any).model).toBe("doubao-seed-2-0-lite-260428");
expect((data as any).temperature).toBe(0.7);
expect((data as any).maxTokens).toBe(8192);
});
it("partially overrides llmGenerate defaults with node defaults", () => {
const nodeDefaultsConfig = {
llm: {
temperature: 0.9,
},
};
localStorageMock.setItem(
"node-banana-node-defaults",
JSON.stringify(nodeDefaultsConfig)
);
const data = createDefaultNodeData("llmGenerate");
// Should use node default for temperature
expect((data as any).temperature).toBe(0.9);
// Should fall back to NewApiWG defaults for others
expect((data as any).provider).toBe("newapiwg");
expect((data as any).model).toBe("doubao-seed-2-0-lite-260428");
expect((data as any).maxTokens).toBe(8192);
});
});
});