import { expect } from '@playwright/test' import { comfyPageFixture } from '../fixtures/ComfyPage' const test = comfyPageFixture test.beforeEach(async ({ comfyPage }) => { await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled') }) const BLUE_COLOR = 'rgb(51, 51, 85)' const RED_COLOR = 'rgb(85, 51, 51)' test.describe('Selection Toolbox', () => { test.beforeEach(async ({ comfyPage }) => { await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true) }) test('shows selection toolbox', async ({ comfyPage }) => { // By default, selection toolbox should be enabled await expect(comfyPage.selectionToolbox).not.toBeVisible() // Select multiple nodes await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) // Selection toolbox should be visible with multiple nodes selected await expect(comfyPage.selectionToolbox).toBeVisible() // Border is now drawn on canvas, check via screenshot await expect(comfyPage.canvas).toHaveScreenshot( 'selection-toolbox-multiple-nodes-border.png' ) }) test('shows at correct position when node is pasted', async ({ comfyPage }) => { await comfyPage.loadWorkflow('nodes/single_ksampler') await comfyPage.selectNodes(['KSampler']) await comfyPage.ctrlC() await comfyPage.page.mouse.move(100, 100) await comfyPage.ctrlV() const toolboxContainer = comfyPage.selectionToolbox await expect(toolboxContainer).toBeVisible() // Verify toolbox is positioned (canvas-based positioning has different coordinates) const boundingBox = await toolboxContainer.boundingBox() expect(boundingBox).not.toBeNull() // Canvas-based positioning can vary, just verify toolbox appears in reasonable bounds expect(boundingBox!.x).toBeGreaterThan(-200) // Not too far off-screen left expect(boundingBox!.x).toBeLessThan(1000) // Not too far off-screen right expect(boundingBox!.y).toBeGreaterThan(-100) // Not too far off-screen top }) test('hide when select and drag happen at the same time', async ({ comfyPage }) => { await comfyPage.loadWorkflow('nodes/single_ksampler') const node = (await comfyPage.getNodeRefsByTitle('KSampler'))[0] const nodePos = await node.getPosition() // Drag on the title of the node await comfyPage.page.mouse.move(nodePos.x + 100, nodePos.y - 15) await comfyPage.page.mouse.down() await comfyPage.page.mouse.move(nodePos.x + 200, nodePos.y + 200) await comfyPage.nextFrame() await expect(comfyPage.selectionToolbox).not.toBeVisible() }) test('shows border only with multiple selections', async ({ comfyPage }) => { // Select single node await comfyPage.selectNodes(['KSampler']) // Selection toolbox should be visible but without border await expect(comfyPage.selectionToolbox).toBeVisible() // Border is now drawn on canvas, check via screenshot await expect(comfyPage.canvas).toHaveScreenshot( 'selection-toolbox-single-node-no-border.png' ) // Select multiple nodes await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) // Selection border should show with multiple selections (canvas-based) await expect(comfyPage.canvas).toHaveScreenshot( 'selection-toolbox-multiple-selections-border.png' ) // Deselect to single node await comfyPage.selectNodes(['CLIP Text Encode (Prompt)']) // Border should be hidden again (canvas-based) await expect(comfyPage.canvas).toHaveScreenshot( 'selection-toolbox-single-selection-no-border.png' ) }) test('displays bypass button in toolbox when nodes are selected', async ({ comfyPage }) => { // A group + a KSampler node await comfyPage.loadWorkflow('groups/single_group') // Select group + node should show bypass button await comfyPage.page.focus('canvas') await comfyPage.page.keyboard.press('Control+A') await expect( comfyPage.page.locator( '.selection-toolbox *[data-testid="bypass-button"]' ) ).toBeVisible() // Deselect node (Only group is selected) should hide bypass button await comfyPage.selectNodes(['KSampler']) await expect( comfyPage.page.locator( '.selection-toolbox *[data-testid="bypass-button"]' ) ).not.toBeVisible() }) test.describe('Color Picker', () => { test('displays color picker button and allows color selection', async ({ comfyPage }) => { // Select a node await comfyPage.selectNodes(['KSampler']) // Color picker button should be visible const colorPickerButton = comfyPage.page.locator( '.selection-toolbox .pi-circle-fill' ) await expect(colorPickerButton).toBeVisible() // Click color picker button await colorPickerButton.click() // Color picker dropdown should be visible const colorPickerDropdown = comfyPage.page.locator( '.color-picker-container' ) await expect(colorPickerDropdown).toBeVisible() // Select a color (e.g., blue) const blueColorOption = colorPickerDropdown.locator( 'i[data-testid="blue"]' ) await blueColorOption.click() // Dropdown should close after selection await expect(colorPickerDropdown).not.toBeVisible() // Node should have the selected color class/style // Note: Exact verification method depends on how color is applied to nodes const selectedNode = (await comfyPage.getNodeRefsByTitle('KSampler'))[0] expect(await selectedNode.getProperty('color')).not.toBeNull() }) test('color picker shows current color of selected nodes', async ({ comfyPage }) => { // Select multiple nodes await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) const colorPickerButton = comfyPage.page.locator( '.selection-toolbox .pi-circle-fill' ) // Initially should show default color await expect(colorPickerButton).not.toHaveAttribute('color') // Click color picker and select a color await colorPickerButton.click() const redColorOption = comfyPage.page.locator( '.color-picker-container i[data-testid="red"]' ) await redColorOption.click() // Button should now show the selected color await expect(colorPickerButton).toHaveCSS('color', RED_COLOR) }) test('color picker shows mixed state for differently colored selections', async ({ comfyPage }) => { // Select first node and color it await comfyPage.selectNodes(['KSampler']) await comfyPage.page.locator('.selection-toolbox .pi-circle-fill').click() await comfyPage.page .locator('.color-picker-container i[data-testid="blue"]') .click() await comfyPage.selectNodes(['KSampler']) // Select second node and color it differently await comfyPage.selectNodes(['CLIP Text Encode (Prompt)']) await comfyPage.page.locator('.selection-toolbox .pi-circle-fill').click() await comfyPage.page .locator('.color-picker-container i[data-testid="red"]') .click() // Select both nodes await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) // Color picker should show null/mixed state const colorPickerButton = comfyPage.page.locator( '.selection-toolbox .pi-circle-fill' ) await expect(colorPickerButton).not.toHaveAttribute('color') }) test('color picker shows correct color when selecting pre-colored node', async ({ comfyPage }) => { // First color a node await comfyPage.selectNodes(['KSampler']) await comfyPage.page.locator('.selection-toolbox .pi-circle-fill').click() await comfyPage.page .locator('.color-picker-container i[data-testid="blue"]') .click() // Clear selection await comfyPage.selectNodes(['KSampler']) // Re-select the node await comfyPage.selectNodes(['KSampler']) // Color picker button should show the correct color const colorPickerButton = comfyPage.page.locator( '.selection-toolbox .pi-circle-fill' ) await expect(colorPickerButton).toHaveCSS('color', BLUE_COLOR) }) test('colorization via color picker can be undone', async ({ comfyPage }) => { // Select a node and color it await comfyPage.selectNodes(['KSampler']) await comfyPage.page.locator('.selection-toolbox .pi-circle-fill').click() await comfyPage.page .locator('.color-picker-container i[data-testid="blue"]') .click() // Undo the colorization await comfyPage.page.keyboard.press('Control+Z') await comfyPage.nextFrame() // Node should be uncolored again const selectedNode = (await comfyPage.getNodeRefsByTitle('KSampler'))[0] expect(await selectedNode.getProperty('color')).toBeUndefined() }) }) })