2 changed files with 109 additions and 2 deletions
@ -0,0 +1,107 @@ |
|||||
|
import { describe, it, expect, vi, beforeEach } from "vitest"; |
||||
|
import { fireEvent, render, screen } from "@testing-library/react"; |
||||
|
|
||||
|
import { CanvasOutlineDrawer } from "@/components/CanvasOutlineDrawer"; |
||||
|
|
||||
|
const translations: Record<string, string> = { |
||||
|
"canvasOutline.title": "Canvas outline", |
||||
|
"canvasOutline.group": "Group", |
||||
|
"canvasOutline.nodeFallback": "Node", |
||||
|
"canvasOutline.searchPlaceholder": "Search nodes", |
||||
|
"canvasOutline.empty": "No nodes on the canvas", |
||||
|
"canvasOutline.noSearchResults": "No matching nodes", |
||||
|
"canvasOutline.totalNodes": "{count} nodes", |
||||
|
"canvasOutline.groupNodeCount": "{count} nodes", |
||||
|
"canvasOutline.expandGroup": "Expand group", |
||||
|
"canvasOutline.collapseGroup": "Collapse group", |
||||
|
"node.imageInput": "Image Input", |
||||
|
"node.prompt": "Prompt", |
||||
|
}; |
||||
|
|
||||
|
vi.mock("@/i18n", () => ({ |
||||
|
useI18n: () => ({ |
||||
|
t: (key: string, values?: Record<string, string | number>) => { |
||||
|
let text = translations[key] ?? key; |
||||
|
if (values) { |
||||
|
for (const [name, value] of Object.entries(values)) { |
||||
|
text = text.replace(`{${name}}`, String(value)); |
||||
|
} |
||||
|
} |
||||
|
return text; |
||||
|
}, |
||||
|
}), |
||||
|
})); |
||||
|
|
||||
|
const mockSelectSingleNode = vi.fn(); |
||||
|
const mockSetNavigationTarget = vi.fn(); |
||||
|
const mockUseWorkflowStore = vi.fn(); |
||||
|
|
||||
|
vi.mock("@/store/workflowStore", () => ({ |
||||
|
useWorkflowStore: (selector: (state: unknown) => unknown) => mockUseWorkflowStore(selector), |
||||
|
})); |
||||
|
|
||||
|
const createState = () => ({ |
||||
|
nodes: [ |
||||
|
{ |
||||
|
id: "image-1", |
||||
|
type: "imageInput", |
||||
|
position: { x: 0, y: 0 }, |
||||
|
data: { customTitle: "Hero image" }, |
||||
|
}, |
||||
|
{ |
||||
|
id: "group-node-group-1", |
||||
|
type: "group", |
||||
|
position: { x: 100, y: 100 }, |
||||
|
data: { groupId: "group-1" }, |
||||
|
}, |
||||
|
{ |
||||
|
id: "prompt-1", |
||||
|
type: "prompt", |
||||
|
parentId: "group-node-group-1", |
||||
|
groupId: "group-1", |
||||
|
position: { x: 20, y: 30 }, |
||||
|
data: {}, |
||||
|
}, |
||||
|
], |
||||
|
groups: { |
||||
|
"group-1": { |
||||
|
id: "group-1", |
||||
|
name: "Storyboard", |
||||
|
color: "neutral", |
||||
|
position: { x: 100, y: 100 }, |
||||
|
size: { width: 400, height: 300 }, |
||||
|
}, |
||||
|
}, |
||||
|
selectSingleNode: mockSelectSingleNode, |
||||
|
setNavigationTarget: mockSetNavigationTarget, |
||||
|
}); |
||||
|
|
||||
|
describe("CanvasOutlineDrawer", () => { |
||||
|
beforeEach(() => { |
||||
|
vi.clearAllMocks(); |
||||
|
mockUseWorkflowStore.mockImplementation((selector) => selector(createState())); |
||||
|
}); |
||||
|
|
||||
|
it("renders root nodes, grouped child nodes, and navigates to clicked nodes", () => { |
||||
|
render(<CanvasOutlineDrawer open onClose={vi.fn()} />); |
||||
|
|
||||
|
expect(screen.getByText("Hero image")).toBeInTheDocument(); |
||||
|
expect(screen.getByText("Storyboard")).toBeInTheDocument(); |
||||
|
expect(screen.getByText("Prompt")).toBeInTheDocument(); |
||||
|
expect(screen.getByText("2 nodes")).toBeInTheDocument(); |
||||
|
|
||||
|
fireEvent.click(screen.getByTitle("Hero image")); |
||||
|
|
||||
|
expect(mockSelectSingleNode).toHaveBeenCalledWith("image-1"); |
||||
|
expect(mockSetNavigationTarget).toHaveBeenCalledWith("image-1"); |
||||
|
}); |
||||
|
|
||||
|
it("navigates to the backing group node when a group row is clicked", () => { |
||||
|
render(<CanvasOutlineDrawer open onClose={vi.fn()} />); |
||||
|
|
||||
|
fireEvent.click(screen.getByTitle("Storyboard")); |
||||
|
|
||||
|
expect(mockSelectSingleNode).toHaveBeenCalledWith("group-node-group-1"); |
||||
|
expect(mockSetNavigationTarget).toHaveBeenCalledWith("group-node-group-1"); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue