From a5ad9b5ad90287273554164b24eccf0b2bc5f0ab Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Tue, 10 Jun 2025 15:26:58 -0700 Subject: [PATCH] [Test] [Performance] Apply perf monitor wrappers to test files (2/4) (#4125) --- .../tests/backgroundImageUpload.spec.ts | 88 +++-- browser_tests/tests/chatHistory.spec.ts | 146 +++++--- browser_tests/tests/dialog.spec.ts | 102 +++-- browser_tests/tests/groupNode.spec.ts | 354 ++++++++++++++---- browser_tests/tests/rerouteNode.spec.ts | 175 ++++++--- browser_tests/tests/selectionToolbox.spec.ts | 287 ++++++++++---- 6 files changed, 867 insertions(+), 285 deletions(-) diff --git a/browser_tests/tests/backgroundImageUpload.spec.ts b/browser_tests/tests/backgroundImageUpload.spec.ts index 24af9e8ac..bbaaed31c 100644 --- a/browser_tests/tests/backgroundImageUpload.spec.ts +++ b/browser_tests/tests/backgroundImageUpload.spec.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test' import { comfyPageFixture as test } from '../fixtures/ComfyPage' +import { PerformanceMonitor } from '../helpers/performanceMonitor' test.describe('Background Image Upload', () => { test.beforeEach(async ({ comfyPage }) => { @@ -44,9 +45,14 @@ test.describe('Background Image Upload', () => { await expect(clearButton).toBeDisabled() // Should be disabled when no image }) - test('should upload image file and set as background', async ({ + test('@perf should upload image file and set as background', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'upload-background-image-file' + + await perfMonitor.startMonitoring(testName) + // Open settings dialog await comfyPage.page.keyboard.press('Control+,') @@ -63,16 +69,18 @@ test.describe('Background Image Upload', () => { 'button:has(.pi-upload)' ) - // Set up file upload handler - const fileChooserPromise = comfyPage.page.waitForEvent('filechooser') - await uploadButton.click() - const fileChooser = await fileChooserPromise - - // Upload the test image - await fileChooser.setFiles(comfyPage.assetPath('image32x32.webp')) + // Set up file upload handler and upload + await perfMonitor.measureOperation('trigger-file-upload', async () => { + const fileChooserPromise = comfyPage.page.waitForEvent('filechooser') + await uploadButton.click() + const fileChooser = await fileChooserPromise + await fileChooser.setFiles(comfyPage.assetPath('image32x32.webp')) + }) // Wait for upload to complete and verify the setting was updated - await comfyPage.page.waitForTimeout(500) // Give time for file reading + await perfMonitor.measureOperation('process-uploaded-file', async () => { + await comfyPage.page.waitForTimeout(500) // Give time for file reading + }) // Verify the URL input now has an API URL const urlInput = backgroundImageSetting.locator('input[type="text"]') @@ -88,11 +96,18 @@ test.describe('Background Image Upload', () => { 'Comfy.Canvas.BackgroundImage' ) expect(settingValue).toMatch(/^\/api\/view\?.*subfolder=backgrounds/) + + await perfMonitor.finishMonitoring(testName) }) - test('should accept URL input for background image', async ({ + test('@perf should accept URL input for background image', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'input-background-image-url' + + await perfMonitor.startMonitoring(testName) + const testImageUrl = 'https://example.com/test-image.png' // Open settings dialog @@ -106,12 +121,13 @@ test.describe('Background Image Upload', () => { const backgroundImageSetting = comfyPage.page.locator( '#Comfy\\.Canvas\\.BackgroundImage' ) + // Enter URL in the input field const urlInput = backgroundImageSetting.locator('input[type="text"]') - await urlInput.fill(testImageUrl) - - // Trigger blur event to ensure the value is set - await urlInput.blur() + await perfMonitor.measureOperation('input-url-text', async () => { + await urlInput.fill(testImageUrl) + await urlInput.blur() + }) // Verify clear button is now enabled const clearButton = backgroundImageSetting.locator('button:has(.pi-trash)') @@ -122,15 +138,24 @@ test.describe('Background Image Upload', () => { 'Comfy.Canvas.BackgroundImage' ) expect(settingValue).toBe(testImageUrl) + + await perfMonitor.finishMonitoring(testName) }) - test('should clear background image when clear button is clicked', async ({ + test('@perf should clear background image when clear button is clicked', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'clear-background-image' + + await perfMonitor.startMonitoring(testName) + const testImageUrl = 'https://example.com/test-image.png' // First set a background image - await comfyPage.setSetting('Comfy.Canvas.BackgroundImage', testImageUrl) + await perfMonitor.measureOperation('set-initial-setting', async () => { + await comfyPage.setSetting('Comfy.Canvas.BackgroundImage', testImageUrl) + }) // Open settings dialog await comfyPage.page.keyboard.press('Control+,') @@ -152,7 +177,9 @@ test.describe('Background Image Upload', () => { await expect(clearButton).toBeEnabled() // Click the clear button - await clearButton.click() + await perfMonitor.measureOperation('click-clear-button', async () => { + await clearButton.click() + }) // Verify the input is now empty await expect(urlInput).toHaveValue('') @@ -165,6 +192,8 @@ test.describe('Background Image Upload', () => { 'Comfy.Canvas.BackgroundImage' ) expect(settingValue).toBe('') + + await perfMonitor.finishMonitoring(testName) }) test('should show tooltip on upload and clear buttons', async ({ @@ -211,9 +240,14 @@ test.describe('Background Image Upload', () => { await expect(clearTooltip).toBeVisible() }) - test('should maintain reactive updates between URL input and clear button state', async ({ + test('@perf should maintain reactive updates between URL input and clear button state', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'reactive-widget-updates' + + await perfMonitor.startMonitoring(testName) + // Open settings dialog await comfyPage.page.keyboard.press('Control+,') @@ -232,20 +266,30 @@ test.describe('Background Image Upload', () => { await expect(clearButton).toBeDisabled() // Type some text - clear button should become enabled - await urlInput.fill('test') + await perfMonitor.measureOperation('input-partial-text', async () => { + await urlInput.fill('test') + }) await expect(clearButton).toBeEnabled() // Clear the text manually - clear button should become disabled again - await urlInput.fill('') + await perfMonitor.measureOperation('clear-input-manually', async () => { + await urlInput.fill('') + }) await expect(clearButton).toBeDisabled() // Add text again - clear button should become enabled - await urlInput.fill('https://example.com/image.png') + await perfMonitor.measureOperation('input-full-url', async () => { + await urlInput.fill('https://example.com/image.png') + }) await expect(clearButton).toBeEnabled() // Use clear button - should clear input and disable itself - await clearButton.click() + await perfMonitor.measureOperation('clear-via-button', async () => { + await clearButton.click() + }) await expect(urlInput).toHaveValue('') await expect(clearButton).toBeDisabled() + + await perfMonitor.finishMonitoring(testName) }) }) diff --git a/browser_tests/tests/chatHistory.spec.ts b/browser_tests/tests/chatHistory.spec.ts index db3397514..27fd90d3b 100644 --- a/browser_tests/tests/chatHistory.spec.ts +++ b/browser_tests/tests/chatHistory.spec.ts @@ -1,6 +1,7 @@ import { Page, expect } from '@playwright/test' import { comfyPageFixture as test } from '../fixtures/ComfyPage' +import { PerformanceMonitor } from '../helpers/performanceMonitor' interface ChatHistoryEntry { prompt: string @@ -42,49 +43,65 @@ test.describe('Chat History Widget', () => { await comfyPage.page.waitForSelector('.pi-pencil') }) - test('displays chat history when receiving display_component message', async ({ + test('@perf displays chat history when receiving display_component message', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'display-chat-history-component' + + await perfMonitor.startMonitoring(testName) + // Verify the chat history is displayed correctly await expect(comfyPage.page.getByText('Hello')).toBeVisible() await expect(comfyPage.page.getByText('World')).toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) - test('handles message editing interaction', async ({ comfyPage }) => { + test('@perf handles message editing interaction', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'message-editing-interaction' + + await perfMonitor.startMonitoring(testName) + // Get first node's ID - nodeId = await comfyPage.page.evaluate(() => { - const node = window['app'].graph.nodes[0] + await perfMonitor.measureOperation('setup-node-widgets', async () => { + nodeId = await comfyPage.page.evaluate(() => { + const node = window['app'].graph.nodes[0] - // Make sure the node has a prompt widget (for editing functionality) - if (!node.widgets) { - node.widgets = [] - } + // Make sure the node has a prompt widget (for editing functionality) + if (!node.widgets) { + node.widgets = [] + } - // Add a prompt widget if it doesn't exist - if (!node.widgets.find((w) => w.name === 'prompt')) { - node.widgets.push({ - name: 'prompt', - type: 'text', - value: 'Original prompt' - }) - } + // Add a prompt widget if it doesn't exist + if (!node.widgets.find((w) => w.name === 'prompt')) { + node.widgets.push({ + name: 'prompt', + type: 'text', + value: 'Original prompt' + }) + } - return node.id + return node.id + }) }) - await renderChatHistory(comfyPage.page, [ - { - prompt: 'Message 1', - response: 'Response 1', - response_id: '123' - }, - { - prompt: 'Message 2', - response: 'Response 2', - response_id: '456' - } - ]) - await comfyPage.page.waitForSelector('.pi-pencil') + await perfMonitor.measureOperation('render-chat-history', async () => { + await renderChatHistory(comfyPage.page, [ + { + prompt: 'Message 1', + response: 'Response 1', + response_id: '123' + }, + { + prompt: 'Message 2', + response: 'Response 2', + response_id: '456' + } + ]) + await comfyPage.page.waitForSelector('.pi-pencil') + }) const originalTextAreaInput = await comfyPage.page .getByPlaceholder('text') @@ -92,48 +109,73 @@ test.describe('Chat History Widget', () => { .inputValue() // Click edit button on first message - await comfyPage.page.getByLabel('Edit').first().click() - await comfyPage.nextFrame() + await perfMonitor.measureOperation('click-edit-button', async () => { + await comfyPage.page.getByLabel('Edit').first().click() + await comfyPage.nextFrame() + }) // Verify cancel button appears await expect(comfyPage.page.getByLabel('Cancel')).toBeVisible() // Click cancel edit - await comfyPage.page.getByLabel('Cancel').click() + await perfMonitor.measureOperation('click-cancel-button', async () => { + await comfyPage.page.getByLabel('Cancel').click() + }) // Verify prompt input is restored await expect(comfyPage.page.getByPlaceholder('text').nth(1)).toHaveValue( originalTextAreaInput ) + + await perfMonitor.finishMonitoring(testName) }) - test('handles real-time updates to chat history', async ({ comfyPage }) => { + test('@perf handles real-time updates to chat history', async ({ + comfyPage + }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'real-time-chat-history-updates' + + await perfMonitor.startMonitoring(testName) + // Send initial history - await renderChatHistory(comfyPage.page, [ - { - prompt: 'Initial message', - response: 'Initial response', - response_id: '123' - } - ]) - await comfyPage.page.waitForSelector('.pi-pencil') + await perfMonitor.measureOperation('render-initial-history', async () => { + await renderChatHistory(comfyPage.page, [ + { + prompt: 'Initial message', + response: 'Initial response', + response_id: '123' + } + ]) + await comfyPage.page.waitForSelector('.pi-pencil') + }) + + await perfMonitor.markEvent('before-history-update') // Update history with additional messages - await renderChatHistory(comfyPage.page, [ - { - prompt: 'Follow-up', - response: 'New response', - response_id: '456' - } - ]) - await comfyPage.page.waitForSelector('.pi-pencil') + await perfMonitor.measureOperation('update-chat-history', async () => { + await renderChatHistory(comfyPage.page, [ + { + prompt: 'Follow-up', + response: 'New response', + response_id: '456' + } + ]) + await comfyPage.page.waitForSelector('.pi-pencil') + }) // Move mouse over the canvas to force update - await comfyPage.page.mouse.move(100, 100) - await comfyPage.nextFrame() + await perfMonitor.measureOperation('trigger-canvas-update', async () => { + await comfyPage.page.mouse.move(100, 100) + await comfyPage.nextFrame() + }) + + await perfMonitor.markEvent('after-canvas-update') // Verify new messages appear await expect(comfyPage.page.getByText('Follow-up')).toBeVisible() await expect(comfyPage.page.getByText('New response')).toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) }) diff --git a/browser_tests/tests/dialog.spec.ts b/browser_tests/tests/dialog.spec.ts index a5c46131a..574f04112 100644 --- a/browser_tests/tests/dialog.spec.ts +++ b/browser_tests/tests/dialog.spec.ts @@ -2,6 +2,7 @@ import { Locator, expect } from '@playwright/test' import type { Keybinding } from '../../src/schemas/keyBindingSchema' import { comfyPageFixture as test } from '../fixtures/ComfyPage' +import { PerformanceMonitor } from '../helpers/performanceMonitor' test.describe('Load workflow warning', () => { test('Should display a warning when loading a workflow with missing nodes', async ({ @@ -15,46 +16,89 @@ test.describe('Load workflow warning', () => { }) }) -test('Does not report warning on undo/redo', async ({ comfyPage }) => { +test('@perf Does not report warning on undo/redo', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'undo-redo-no-warning' + + await perfMonitor.startMonitoring(testName) + await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default') - await comfyPage.loadWorkflow('missing_nodes') + await perfMonitor.measureOperation('load-workflow', async () => { + await comfyPage.loadWorkflow('missing_nodes') + }) + await comfyPage.closeDialog() // Make a change to the graph - await comfyPage.doubleClickCanvas() - await comfyPage.searchBox.fillAndSelectFirstNode('KSampler') + await perfMonitor.measureOperation('add-node-sequence', async () => { + await comfyPage.doubleClickCanvas() + await comfyPage.searchBox.fillAndSelectFirstNode('KSampler') + }) // Undo and redo the change - await comfyPage.ctrlZ() + await perfMonitor.measureOperation('undo-operation', async () => { + await comfyPage.ctrlZ() + }) + await expect(comfyPage.page.locator('.comfy-missing-nodes')).not.toBeVisible() - await comfyPage.ctrlY() + + await perfMonitor.measureOperation('redo-operation', async () => { + await comfyPage.ctrlY() + }) + await expect(comfyPage.page.locator('.comfy-missing-nodes')).not.toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) test.describe('Execution error', () => { - test('Should display an error message when an execution error occurs', async ({ + test('@perf Should display an error message when an execution error occurs', async ({ comfyPage }) => { - await comfyPage.loadWorkflow('execution_error') - await comfyPage.queueButton.click() - await comfyPage.nextFrame() + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'execution-error-display' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('load-workflow', async () => { + await comfyPage.loadWorkflow('execution_error') + }) + + await perfMonitor.measureOperation('queue-execution', async () => { + await comfyPage.queueButton.click() + await comfyPage.nextFrame() + }) // Wait for the element with the .comfy-execution-error selector to be visible const executionError = comfyPage.page.locator('.comfy-error-report') await expect(executionError).toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) - test('Can display Issue Report form', async ({ comfyPage }) => { - await comfyPage.loadWorkflow('execution_error') - await comfyPage.queueButton.click() - await comfyPage.nextFrame() + test('@perf Can display Issue Report form', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'issue-report-form-display' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('load-workflow', async () => { + await comfyPage.loadWorkflow('execution_error') + }) + + await perfMonitor.measureOperation('queue-execution', async () => { + await comfyPage.queueButton.click() + await comfyPage.nextFrame() + }) await comfyPage.page.getByLabel('Help Fix This').click() const issueReportForm = comfyPage.page.getByText( 'Submit Error Report (Optional)' ) await expect(issueReportForm).toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) }) @@ -355,18 +399,28 @@ test.describe('Error dialog', () => { }) test.describe('Signin dialog', () => { - test('Paste content to signin dialog should not paste node on canvas', async ({ + test('@perf Paste content to signin dialog should not paste node on canvas', async ({ comfyPage }) => { - const nodeNum = (await comfyPage.getNodes()).length - await comfyPage.clickEmptyLatentNode() - await comfyPage.ctrlC() + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'signin-dialog-paste-isolation' - const textBox = comfyPage.widgetTextBox - await textBox.click() - await textBox.fill('test_password') - await textBox.press('Control+a') - await textBox.press('Control+c') + await perfMonitor.startMonitoring(testName) + + const nodeNum = (await comfyPage.getNodes()).length + + await perfMonitor.measureOperation('copy-node-sequence', async () => { + await comfyPage.clickEmptyLatentNode() + await comfyPage.ctrlC() + }) + + await perfMonitor.measureOperation('widget-text-operations', async () => { + const textBox = comfyPage.widgetTextBox + await textBox.click() + await textBox.fill('test_password') + await textBox.press('Control+a') + await textBox.press('Control+c') + }) await comfyPage.page.evaluate(() => { window['app'].extensionManager.dialog.showSignInDialog() @@ -378,5 +432,7 @@ test.describe('Signin dialog', () => { await expect(input).toHaveValue('test_password') expect(await comfyPage.getNodes()).toHaveLength(nodeNum) + + await perfMonitor.finishMonitoring(testName) }) }) diff --git a/browser_tests/tests/groupNode.spec.ts b/browser_tests/tests/groupNode.spec.ts index 0c0bb752a..86b1c37b6 100644 --- a/browser_tests/tests/groupNode.spec.ts +++ b/browser_tests/tests/groupNode.spec.ts @@ -2,6 +2,7 @@ import { expect } from '@playwright/test' import { ComfyPage, comfyPageFixture as test } from '../fixtures/ComfyPage' import type { NodeReference } from '../fixtures/utils/litegraphUtils' +import { PerformanceMonitor } from '../helpers/performanceMonitor' test.describe('Group Node', () => { test.describe('Node library sidebar', () => { @@ -21,25 +22,47 @@ test.describe('Group Node', () => { expect(await libraryTab.getFolder('group nodes').count()).toBe(1) }) - test('Can be added to canvas using node library sidebar', async ({ + test('@perf Can be added to canvas using node library sidebar', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'add-group-node-from-library' + + await perfMonitor.startMonitoring(testName) + const initialNodeCount = await comfyPage.getGraphNodesCount() // Add group node from node library sidebar - await libraryTab.getFolder(groupNodeCategory).click() - await libraryTab.getNode(groupNodeName).click() + await perfMonitor.measureOperation('expand-category-folder', async () => { + await libraryTab.getFolder(groupNodeCategory).click() + }) + + await perfMonitor.measureOperation('add-node-from-library', async () => { + await libraryTab.getNode(groupNodeName).click() + }) // Verify the node is added to the canvas expect(await comfyPage.getGraphNodesCount()).toBe(initialNodeCount + 1) + + await perfMonitor.finishMonitoring(testName) }) - test('Can be bookmarked and unbookmarked', async ({ comfyPage }) => { - await libraryTab.getFolder(groupNodeCategory).click() - await libraryTab - .getNode(groupNodeName) - .locator('.bookmark-button') - .click() + test('@perf Can be bookmarked and unbookmarked', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'bookmark-unbookmark-group-node' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('expand-category-folder', async () => { + await libraryTab.getFolder(groupNodeCategory).click() + }) + + await perfMonitor.measureOperation('bookmark-node', async () => { + await libraryTab + .getNode(groupNodeName) + .locator('.bookmark-button') + .click() + }) // Verify the node is added to the bookmarks tab expect( @@ -49,16 +72,20 @@ test.describe('Group Node', () => { expect(await libraryTab.getNode(groupNodeName).count()).not.toBe(0) // Unbookmark the node - await libraryTab - .getNode(groupNodeName) - .locator('.bookmark-button') - .first() - .click() + await perfMonitor.measureOperation('unbookmark-node', async () => { + await libraryTab + .getNode(groupNodeName) + .locator('.bookmark-button') + .first() + .click() + }) // Verify the node is removed from the bookmarks tab expect( await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2') ).toHaveLength(0) + + await perfMonitor.finishMonitoring(testName) }) test('Displays preview on bookmark hover', async ({ comfyPage }) => { @@ -95,18 +122,37 @@ test.describe('Group Node', () => { ) }) - test('Displays tooltip on title hover', async ({ comfyPage }) => { + test('@perf Displays tooltip on title hover', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'group-node-tooltip-display' + + await perfMonitor.startMonitoring(testName) + await comfyPage.setSetting('Comfy.EnableTooltips', true) - await comfyPage.convertAllNodesToGroupNode('Group Node') - await comfyPage.page.mouse.move(47, 173) - const tooltipTimeout = 500 - await comfyPage.page.waitForTimeout(tooltipTimeout + 16) + + await perfMonitor.measureOperation('convert-to-group-node', async () => { + await comfyPage.convertAllNodesToGroupNode('Group Node') + }) + + await perfMonitor.measureOperation('hover-for-tooltip', async () => { + await comfyPage.page.mouse.move(47, 173) + const tooltipTimeout = 500 + await comfyPage.page.waitForTimeout(tooltipTimeout + 16) + }) + await expect(comfyPage.page.locator('.node-tooltip')).toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) - test('Manage group opens with the correct group selected', async ({ + test('@perf Manage group opens with the correct group selected', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'manage-group-node-selection' + + await perfMonitor.startMonitoring(testName) + const makeGroup = async (name, type1, type2) => { const node1 = (await comfyPage.getNodeRefsByType(type1))[0] const node2 = (await comfyPage.getNodeRefsByType(type2))[0] @@ -117,21 +163,44 @@ test.describe('Group Node', () => { return await node2.convertToGroupNode(name) } - const group1 = await makeGroup( - 'g1', - 'CLIPTextEncode', - 'CheckpointLoaderSimple' - ) - const group2 = await makeGroup('g2', 'EmptyLatentImage', 'KSampler') + let group1 + await perfMonitor.measureOperation('create-first-group', async () => { + group1 = await makeGroup('g1', 'CLIPTextEncode', 'CheckpointLoaderSimple') + }) + + let group2 + await perfMonitor.measureOperation('create-second-group', async () => { + group2 = await makeGroup('g2', 'EmptyLatentImage', 'KSampler') + }) + + let manage1 + await perfMonitor.measureOperation('open-first-manage-dialog', async () => { + manage1 = await group1.manageGroupNode() + await comfyPage.nextFrame() + }) - const manage1 = await group1.manageGroupNode() - await comfyPage.nextFrame() expect(await manage1.getSelectedNodeType()).toBe('g1') - await manage1.close() + + await perfMonitor.measureOperation( + 'close-first-manage-dialog', + async () => { + await manage1.close() + } + ) + await expect(manage1.root).not.toBeVisible() - const manage2 = await group2.manageGroupNode() + let manage2 + await perfMonitor.measureOperation( + 'open-second-manage-dialog', + async () => { + manage2 = await group2.manageGroupNode() + } + ) + expect(await manage2.getSelectedNodeType()).toBe('g2') + + await perfMonitor.finishMonitoring(testName) }) test('Preserves hidden input configuration when containing duplicate node types', async ({ @@ -165,9 +234,14 @@ test.describe('Group Node', () => { expect(visibleInputCount).toBe(2) }) - test('Reconnects inputs after configuration changed via manage dialog save', async ({ + test('@perf Reconnects inputs after configuration changed via manage dialog save', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'reconnect-inputs-after-config-change' + + await perfMonitor.startMonitoring(testName) + const expectSingleNode = async (type: string) => { const nodes = await comfyPage.getNodeRefsByType(type) expect(nodes).toHaveLength(1) @@ -175,30 +249,65 @@ test.describe('Group Node', () => { } const latent = await expectSingleNode('EmptyLatentImage') const sampler = await expectSingleNode('KSampler') + // Remove existing link const samplerInput = await sampler.getInput(0) - await samplerInput.removeLinks() + await perfMonitor.measureOperation('remove-existing-links', async () => { + await samplerInput.removeLinks() + }) + // Group latent + sampler - await latent.click('title', { - modifiers: ['Shift'] + await perfMonitor.measureOperation('select-nodes-for-group', async () => { + await latent.click('title', { + modifiers: ['Shift'] + }) + await sampler.click('title', { + modifiers: ['Shift'] + }) }) - await sampler.click('title', { - modifiers: ['Shift'] + + let groupNode + await perfMonitor.measureOperation('convert-to-group-node', async () => { + groupNode = await sampler.convertToGroupNode() }) - const groupNode = await sampler.convertToGroupNode() + // Connect node to group const ckpt = await expectSingleNode('CheckpointLoaderSimple') - const input = await ckpt.connectOutput(0, groupNode, 0) + let input + await perfMonitor.measureOperation('connect-nodes', async () => { + input = await ckpt.connectOutput(0, groupNode, 0) + }) + expect(await input.getLinkCount()).toBe(1) + // Modify the group node via manage dialog - const manage = await groupNode.manageGroupNode() - await manage.selectNode('KSampler') - await manage.changeTab('Inputs') - await manage.setLabel('model', 'test') - await manage.save() - await manage.close() + await perfMonitor.markEvent('before-manage-dialog') + + let manage + await perfMonitor.measureOperation('open-manage-dialog', async () => { + manage = await groupNode.manageGroupNode() + }) + + await perfMonitor.measureOperation( + 'configure-in-manage-dialog', + async () => { + await manage.selectNode('KSampler') + await manage.changeTab('Inputs') + await manage.setLabel('model', 'test') + await manage.save() + } + ) + + await perfMonitor.measureOperation('close-manage-dialog', async () => { + await manage.close() + }) + + await perfMonitor.markEvent('after-manage-dialog') + // Ensure the link is still present expect(await input.getLinkCount()).toBe(1) + + await perfMonitor.finishMonitoring(testName) }) test('Loads from a workflow using the legacy path separator ("/")', async ({ @@ -254,57 +363,131 @@ test.describe('Group Node', () => { await groupNode.copy() }) - test('Copies and pastes group node within the same workflow', async ({ + test('@perf Copies and pastes group node within the same workflow', async ({ comfyPage }) => { - await comfyPage.ctrlV() + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'copy-paste-group-node-same-workflow' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('paste-group-node', async () => { + await comfyPage.ctrlV() + }) + await verifyNodeLoaded(comfyPage, 2) + + await perfMonitor.finishMonitoring(testName) }) - test('Copies and pastes group node after clearing workflow', async ({ + test('@perf Copies and pastes group node after clearing workflow', async ({ comfyPage }) => { - await comfyPage.menu.topbar.triggerTopbarCommand([ - 'Edit', - 'Clear Workflow' - ]) - await comfyPage.ctrlV() + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'copy-paste-group-node-after-clear' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('clear-workflow', async () => { + await comfyPage.menu.topbar.triggerTopbarCommand([ + 'Edit', + 'Clear Workflow' + ]) + }) + + await perfMonitor.measureOperation('paste-group-node', async () => { + await comfyPage.ctrlV() + }) + await verifyNodeLoaded(comfyPage, 1) + + await perfMonitor.finishMonitoring(testName) }) - test('Copies and pastes group node into a newly created blank workflow', async ({ + test('@perf Copies and pastes group node into a newly created blank workflow', async ({ comfyPage }) => { - await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New']) - await comfyPage.ctrlV() + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'copy-paste-group-node-new-workflow' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('create-new-workflow', async () => { + await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New']) + }) + + await perfMonitor.measureOperation('paste-group-node', async () => { + await comfyPage.ctrlV() + }) + await verifyNodeLoaded(comfyPage, 1) + + await perfMonitor.finishMonitoring(testName) }) - test('Copies and pastes group node across different workflows', async ({ + test('@perf Copies and pastes group node across different workflows', async ({ comfyPage }) => { - await comfyPage.loadWorkflow('default') - await comfyPage.ctrlV() - await verifyNodeLoaded(comfyPage, 1) - }) + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'copy-paste-group-node-different-workflow' - test('Serializes group node after copy and paste across workflows', async ({ - comfyPage - }) => { - await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New']) - await comfyPage.ctrlV() - const currentGraphState = await comfyPage.page.evaluate(() => - window['app'].graph.serialize() + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation( + 'load-different-workflow', + async () => { + await comfyPage.loadWorkflow('default') + } ) - await test.step('Load workflow containing a group node pasted from a different workflow', async () => { - await comfyPage.page.evaluate( - (workflow) => window['app'].loadGraphData(workflow), - currentGraphState + await perfMonitor.measureOperation('paste-group-node', async () => { + await comfyPage.ctrlV() + }) + + await verifyNodeLoaded(comfyPage, 1) + + await perfMonitor.finishMonitoring(testName) + }) + + test('@perf Serializes group node after copy and paste across workflows', async ({ + comfyPage + }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'serialize-group-node-cross-workflow' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('create-new-workflow', async () => { + await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New']) + }) + + await perfMonitor.measureOperation('paste-group-node', async () => { + await comfyPage.ctrlV() + }) + + let currentGraphState + await perfMonitor.measureOperation('serialize-graph', async () => { + currentGraphState = await comfyPage.page.evaluate(() => + window['app'].graph.serialize() ) - await comfyPage.nextFrame() + }) + + await test.step('Load workflow containing a group node pasted from a different workflow', async () => { + await perfMonitor.measureOperation( + 'load-serialized-workflow', + async () => { + await comfyPage.page.evaluate( + (workflow) => window['app'].loadGraphData(workflow), + currentGraphState + ) + await comfyPage.nextFrame() + } + ) + await verifyNodeLoaded(comfyPage, 1) }) + + await perfMonitor.finishMonitoring(testName) }) }) @@ -315,12 +498,31 @@ test.describe('Group Node', () => { await comfyPage.page.waitForTimeout(300) expect(await comfyPage.getVisibleToastCount()).toBe(1) }) - test('Convert to group node, selected 1 node', async ({ comfyPage }) => { + test('@perf Convert to group node, selected 1 node', async ({ + comfyPage + }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'convert-single-node-to-group-keybinding' + + await perfMonitor.startMonitoring(testName) + expect(await comfyPage.getVisibleToastCount()).toBe(0) - await comfyPage.clickTextEncodeNode1() - await comfyPage.page.keyboard.press('Alt+g') - await comfyPage.page.waitForTimeout(300) + + await perfMonitor.measureOperation('select-node', async () => { + await comfyPage.clickTextEncodeNode1() + }) + + await perfMonitor.measureOperation( + 'trigger-group-keybinding', + async () => { + await comfyPage.page.keyboard.press('Alt+g') + await comfyPage.page.waitForTimeout(300) + } + ) + expect(await comfyPage.getVisibleToastCount()).toBe(1) + + await perfMonitor.finishMonitoring(testName) }) }) }) diff --git a/browser_tests/tests/rerouteNode.spec.ts b/browser_tests/tests/rerouteNode.spec.ts index 5359580f2..4abdf2d94 100644 --- a/browser_tests/tests/rerouteNode.spec.ts +++ b/browser_tests/tests/rerouteNode.spec.ts @@ -2,6 +2,7 @@ import { expect } from '@playwright/test' import { comfyPageFixture as test } from '../fixtures/ComfyPage' import { getMiddlePoint } from '../fixtures/utils/litegraphUtils' +import { PerformanceMonitor } from '../helpers/performanceMonitor' test.describe('Reroute Node', () => { test.beforeEach(async ({ comfyPage }) => { @@ -12,29 +13,56 @@ test.describe('Reroute Node', () => { await comfyPage.setupWorkflowsDirectory({}) }) - test('loads from inserted workflow', async ({ comfyPage }) => { + test('@perf loads from inserted workflow', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'load-workflow-with-reroute' + + await perfMonitor.startMonitoring(testName) + const workflowName = 'single_connected_reroute_node.json' - await comfyPage.setupWorkflowsDirectory({ - [workflowName]: workflowName + await perfMonitor.measureOperation('setup-workflow-directory', async () => { + await comfyPage.setupWorkflowsDirectory({ + [workflowName]: workflowName + }) + }) + + await perfMonitor.measureOperation('setup-page', async () => { + await comfyPage.setup() + }) + + await perfMonitor.measureOperation('create-new-workflow', async () => { + await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New']) }) - await comfyPage.setup() - await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New']) // Insert the workflow const workflowsTab = comfyPage.menu.workflowsTab - await workflowsTab.open() - await workflowsTab.getPersistedItem(workflowName).click({ button: 'right' }) - const insertButton = comfyPage.page.locator('.p-contextmenu-item-link', { - hasText: 'Insert' + await perfMonitor.measureOperation('open-workflows-tab', async () => { + await workflowsTab.open() }) - await insertButton.click() - // Close the sidebar tab - await workflowsTab.tabButton.click() - await workflowsTab.root.waitFor({ state: 'hidden' }) - await comfyPage.setFocusMode(true) + await perfMonitor.measureOperation('insert-workflow', async () => { + await workflowsTab + .getPersistedItem(workflowName) + .click({ button: 'right' }) + const insertButton = comfyPage.page.locator('.p-contextmenu-item-link', { + hasText: 'Insert' + }) + await insertButton.click() + }) + + await perfMonitor.measureOperation('close-sidebar', async () => { + // Close the sidebar tab + await workflowsTab.tabButton.click() + await workflowsTab.root.waitFor({ state: 'hidden' }) + }) + + await perfMonitor.measureOperation('set-focus-mode', async () => { + await comfyPage.setFocusMode(true) + }) await expect(comfyPage.canvas).toHaveScreenshot('reroute_inserted.png') + + await perfMonitor.finishMonitoring(testName) }) }) @@ -43,53 +71,108 @@ test.describe('LiteGraph Native Reroute Node', () => { await comfyPage.setSetting('LiteGraph.Reroute.SplineOffset', 80) }) - test('loads from workflow', async ({ comfyPage }) => { - await comfyPage.loadWorkflow('reroute/native_reroute') + test('@perf loads from workflow', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'load-native-reroute-workflow' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('load-workflow', async () => { + await comfyPage.loadWorkflow('reroute/native_reroute') + }) + await expect(comfyPage.canvas).toHaveScreenshot('native_reroute.png') + + await perfMonitor.finishMonitoring(testName) }) - test('Can add reroute by alt clicking on link', async ({ comfyPage }) => { - const loadCheckpointNode = ( - await comfyPage.getNodeRefsByTitle('Load Checkpoint') - )[0] - const clipEncodeNode = ( - await comfyPage.getNodeRefsByTitle('CLIP Text Encode (Prompt)') - )[0] + test('@perf Can add reroute by alt clicking on link', async ({ + comfyPage + }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'add-reroute-alt-click' - const slot1 = await loadCheckpointNode.getOutput(1) - const slot2 = await clipEncodeNode.getInput(0) - const middlePoint = getMiddlePoint( - await slot1.getPosition(), - await slot2.getPosition() - ) + await perfMonitor.startMonitoring(testName) - await comfyPage.page.keyboard.down('Alt') - await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y) - await comfyPage.page.keyboard.up('Alt') + let loadCheckpointNode: any + let clipEncodeNode: any + + await perfMonitor.measureOperation('get-nodes', async () => { + loadCheckpointNode = ( + await comfyPage.getNodeRefsByTitle('Load Checkpoint') + )[0] + clipEncodeNode = ( + await comfyPage.getNodeRefsByTitle('CLIP Text Encode (Prompt)') + )[0] + }) + + let slot1: any + let slot2: any + let middlePoint: any + + await perfMonitor.measureOperation('calculate-link-position', async () => { + slot1 = await loadCheckpointNode.getOutput(1) + slot2 = await clipEncodeNode.getInput(0) + middlePoint = getMiddlePoint( + await slot1.getPosition(), + await slot2.getPosition() + ) + }) + + await perfMonitor.measureOperation('alt-click-link', async () => { + await comfyPage.page.keyboard.down('Alt') + await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y) + await comfyPage.page.keyboard.up('Alt') + }) await expect(comfyPage.canvas).toHaveScreenshot( 'native_reroute_alt_click.png' ) + + await perfMonitor.finishMonitoring(testName) }) - test('Can add reroute by clicking middle of link context menu', async ({ + test('@perf Can add reroute by clicking middle of link context menu', async ({ comfyPage }) => { - const loadCheckpointNode = ( - await comfyPage.getNodeRefsByTitle('Load Checkpoint') - )[0] - const clipEncodeNode = ( - await comfyPage.getNodeRefsByTitle('CLIP Text Encode (Prompt)') - )[0] + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'add-reroute-context-menu' - const slot1 = await loadCheckpointNode.getOutput(1) - const slot2 = await clipEncodeNode.getInput(0) - const middlePoint = getMiddlePoint( - await slot1.getPosition(), - await slot2.getPosition() + await perfMonitor.startMonitoring(testName) + + let loadCheckpointNode: any + let clipEncodeNode: any + + await perfMonitor.measureOperation('get-nodes', async () => { + loadCheckpointNode = ( + await comfyPage.getNodeRefsByTitle('Load Checkpoint') + )[0] + clipEncodeNode = ( + await comfyPage.getNodeRefsByTitle('CLIP Text Encode (Prompt)') + )[0] + }) + + let slot1: any + let slot2: any + let middlePoint: any + + await perfMonitor.measureOperation('calculate-link-position', async () => { + slot1 = await loadCheckpointNode.getOutput(1) + slot2 = await clipEncodeNode.getInput(0) + middlePoint = getMiddlePoint( + await slot1.getPosition(), + await slot2.getPosition() + ) + }) + + await perfMonitor.measureOperation( + 'click-link-for-context-menu', + async () => { + await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y) + } ) - await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y) + // Context menu interaction not monitored (floating menu - skip per guide) await comfyPage.page .locator('.litecontextmenu .litemenu-entry', { hasText: 'Add Reroute' }) .click() @@ -97,5 +180,7 @@ test.describe('LiteGraph Native Reroute Node', () => { await expect(comfyPage.canvas).toHaveScreenshot( 'native_reroute_context_menu.png' ) + + await perfMonitor.finishMonitoring(testName) }) }) diff --git a/browser_tests/tests/selectionToolbox.spec.ts b/browser_tests/tests/selectionToolbox.spec.ts index 203dd3506..a34386606 100644 --- a/browser_tests/tests/selectionToolbox.spec.ts +++ b/browser_tests/tests/selectionToolbox.spec.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test' import { comfyPageFixture } from '../fixtures/ComfyPage' +import { PerformanceMonitor } from '../helpers/performanceMonitor' const test = comfyPageFixture @@ -12,14 +13,21 @@ test.describe('Selection Toolbox', () => { await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true) }) - test('shows selection toolbox', async ({ comfyPage }) => { + test('@perf shows selection toolbox', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'show-selection-toolbox' + + await perfMonitor.startMonitoring(testName) + // By default, selection toolbox should be enabled expect( await comfyPage.page.locator('.selection-overlay-container').isVisible() ).toBe(false) // Select multiple nodes - await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) + await perfMonitor.measureOperation('select-multiple-nodes', async () => { + await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) + }) // Selection toolbox should be visible with multiple nodes selected await expect( @@ -28,16 +36,37 @@ test.describe('Selection Toolbox', () => { await expect( comfyPage.page.locator('.selection-overlay-container.show-border') ).toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) - test('shows at correct position when node is pasted', async ({ + test('@perf shows at correct position when node is pasted', async ({ comfyPage }) => { - await comfyPage.loadWorkflow('single_ksampler') - await comfyPage.selectNodes(['KSampler']) - await comfyPage.ctrlC() - await comfyPage.page.mouse.move(100, 100) - await comfyPage.ctrlV() + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'node-paste-position' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('load-workflow', async () => { + await comfyPage.loadWorkflow('single_ksampler') + }) + + await perfMonitor.measureOperation('select-node', async () => { + await comfyPage.selectNodes(['KSampler']) + }) + + await perfMonitor.measureOperation('copy-node', async () => { + await comfyPage.ctrlC() + }) + + await perfMonitor.measureOperation('position-mouse', async () => { + await comfyPage.page.mouse.move(100, 100) + }) + + await perfMonitor.measureOperation('paste-node', async () => { + await comfyPage.ctrlV() + }) const overlayContainer = comfyPage.page.locator( '.selection-overlay-container' @@ -51,28 +80,59 @@ test.describe('Selection Toolbox', () => { expect(Math.round(boundingBox!.x)).toBeCloseTo(90, -1) // Allow ~10px tolerance // 30px offset of node title height expect(Math.round(boundingBox!.y)).toBeCloseTo(60, -1) + + await perfMonitor.finishMonitoring(testName) }) - test('hide when select and drag happen at the same time', async ({ + test('@perf hide when select and drag happen at the same time', async ({ comfyPage }) => { - await comfyPage.loadWorkflow('single_ksampler') - const node = (await comfyPage.getNodeRefsByTitle('KSampler'))[0] - const nodePos = await node.getPosition() + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'hide-toolbox-during-drag' + + await perfMonitor.startMonitoring(testName) + + await perfMonitor.measureOperation('load-workflow', async () => { + await comfyPage.loadWorkflow('single_ksampler') + }) + + let node: any + let nodePos: any + await perfMonitor.measureOperation('get-node-position', async () => { + node = (await comfyPage.getNodeRefsByTitle('KSampler'))[0] + 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 perfMonitor.measureOperation('start-drag', async () => { + await comfyPage.page.mouse.move(nodePos.x + 100, nodePos.y - 15) + await comfyPage.page.mouse.down() + }) + + await perfMonitor.measureOperation('drag-to-position', async () => { + await comfyPage.page.mouse.move(nodePos.x + 200, nodePos.y + 200) + }) + await comfyPage.nextFrame() await expect( comfyPage.page.locator('.selection-overlay-container') ).not.toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) - test('shows border only with multiple selections', async ({ comfyPage }) => { + test('@perf shows border only with multiple selections', async ({ + comfyPage + }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'border-multiple-selections' + + await perfMonitor.startMonitoring(testName) + // Select single node - await comfyPage.selectNodes(['KSampler']) + await perfMonitor.measureOperation('select-single-node', async () => { + await comfyPage.selectNodes(['KSampler']) + }) // Selection overlay should be visible but without border await expect( @@ -83,7 +143,9 @@ test.describe('Selection Toolbox', () => { ).not.toBeVisible() // Select multiple nodes - await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) + await perfMonitor.measureOperation('select-multiple-nodes', async () => { + await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) + }) // Selection overlay should show border with multiple selections await expect( @@ -91,23 +153,37 @@ test.describe('Selection Toolbox', () => { ).toBeVisible() // Deselect to single node - await comfyPage.selectNodes(['CLIP Text Encode (Prompt)']) + await perfMonitor.measureOperation('deselect-to-single', async () => { + await comfyPage.selectNodes(['CLIP Text Encode (Prompt)']) + }) // Border should be hidden again await expect( comfyPage.page.locator('.selection-overlay-container.show-border') ).not.toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) - test('displays bypass button in toolbox when nodes are selected', async ({ + test('@perf displays bypass button in toolbox when nodes are selected', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'bypass-button-display' + + await perfMonitor.startMonitoring(testName) + // A group + a KSampler node - await comfyPage.loadWorkflow('single_group') + await perfMonitor.measureOperation('load-workflow', async () => { + await comfyPage.loadWorkflow('single_group') + }) // Select group + node should show bypass button - await comfyPage.page.focus('canvas') - await comfyPage.page.keyboard.press('Control+A') + await perfMonitor.measureOperation('select-all-nodes', async () => { + await comfyPage.page.focus('canvas') + await comfyPage.page.keyboard.press('Control+A') + }) + await expect( comfyPage.page.locator( '.selection-toolbox *[data-testid="bypass-button"]' @@ -115,20 +191,32 @@ test.describe('Selection Toolbox', () => { ).toBeVisible() // Deselect node (Only group is selected) should hide bypass button - await comfyPage.selectNodes(['KSampler']) + await perfMonitor.measureOperation('select-single-node', async () => { + await comfyPage.selectNodes(['KSampler']) + }) + await expect( comfyPage.page.locator( '.selection-toolbox *[data-testid="bypass-button"]' ) ).not.toBeVisible() + + await perfMonitor.finishMonitoring(testName) }) test.describe('Color Picker', () => { - test('displays color picker button and allows color selection', async ({ + test('@perf displays color picker button and allows color selection', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'color-picker-selection' + + await perfMonitor.startMonitoring(testName) + // Select a node - await comfyPage.selectNodes(['KSampler']) + await perfMonitor.measureOperation('select-node', async () => { + await comfyPage.selectNodes(['KSampler']) + }) // Color picker button should be visible const colorPickerButton = comfyPage.page.locator( @@ -137,7 +225,9 @@ test.describe('Selection Toolbox', () => { await expect(colorPickerButton).toBeVisible() // Click color picker button - await colorPickerButton.click() + await perfMonitor.measureOperation('open-color-picker', async () => { + await colorPickerButton.click() + }) // Color picker dropdown should be visible const colorPickerDropdown = comfyPage.page.locator( @@ -146,10 +236,12 @@ test.describe('Selection Toolbox', () => { await expect(colorPickerDropdown).toBeVisible() // Select a color (e.g., blue) - const blueColorOption = colorPickerDropdown.locator( - 'i[data-testid="blue"]' - ) - await blueColorOption.click() + await perfMonitor.measureOperation('select-color', async () => { + const blueColorOption = colorPickerDropdown.locator( + 'i[data-testid="blue"]' + ) + await blueColorOption.click() + }) // Dropdown should close after selection await expect(colorPickerDropdown).not.toBeVisible() @@ -158,13 +250,22 @@ test.describe('Selection Toolbox', () => { // Note: Exact verification method depends on how color is applied to nodes const selectedNode = (await comfyPage.getNodeRefsByTitle('KSampler'))[0] expect(selectedNode.getProperty('color')).not.toBeNull() + + await perfMonitor.finishMonitoring(testName) }) - test('color picker shows current color of selected nodes', async ({ + test('@perf color picker shows current color of selected nodes', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'color-picker-current-color' + + await perfMonitor.startMonitoring(testName) + // Select multiple nodes - await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) + await perfMonitor.measureOperation('select-multiple-nodes', async () => { + await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) + }) const colorPickerButton = comfyPage.page.locator( '.selection-toolbox .pi-circle-fill' @@ -174,84 +275,136 @@ test.describe('Selection Toolbox', () => { 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() + await perfMonitor.measureOperation('open-color-picker', async () => { + await colorPickerButton.click() + }) + + await perfMonitor.measureOperation('select-red-color', async () => { + 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) + + await perfMonitor.finishMonitoring(testName) }) - test('color picker shows mixed state for differently colored selections', async ({ + test('@perf color picker shows mixed state for differently colored selections', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'color-picker-mixed-state' + + await perfMonitor.startMonitoring(testName) + // 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']) + await perfMonitor.measureOperation('color-first-node', async () => { + 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() + await perfMonitor.measureOperation('color-second-node', async () => { + 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)']) + await perfMonitor.measureOperation('select-both-nodes', async () => { + 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') + + await perfMonitor.finishMonitoring(testName) }) - test('color picker shows correct color when selecting pre-colored node', async ({ + test('@perf color picker shows correct color when selecting pre-colored node', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'color-picker-pre-colored' + + await perfMonitor.startMonitoring(testName) + // 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() + await perfMonitor.measureOperation('color-node-blue', async () => { + 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']) + await perfMonitor.measureOperation('clear-selection', async () => { + await comfyPage.selectNodes(['KSampler']) + }) // Re-select the node - await comfyPage.selectNodes(['KSampler']) + await perfMonitor.measureOperation('reselect-node', async () => { + 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) + + await perfMonitor.finishMonitoring(testName) }) - test('colorization via color picker can be undone', async ({ + test('@perf colorization via color picker can be undone', async ({ comfyPage }) => { + const perfMonitor = new PerformanceMonitor(comfyPage.page) + const testName = 'color-picker-undo' + + await perfMonitor.startMonitoring(testName) + // 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() + await perfMonitor.measureOperation('color-node', async () => { + 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() + await perfMonitor.measureOperation('undo-operation', async () => { + 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() + + await perfMonitor.finishMonitoring(testName) }) }) })