Compare commits

...

28 Commits

Author SHA1 Message Date
Johnpaul
e9447d43a0 test: merge targeted screenshots, remove --user root from CI
Merge feat/targeted-screenshot-tests changes and remove --user root
from CI container options to run as container default user.
2026-04-14 19:31:01 +01:00
Johnpaul
b969cc5ce9 Merge remote-tracking branch 'origin/feat/targeted-screenshot-tests' into test/comfy-complete-ci-container 2026-04-14 19:27:05 +01:00
github-actions
68df807248 [automated] Update test expectations 2026-04-14 16:38:43 +00:00
Johnpaul
7a01408ecf test: hide legacy floating menu globally via CSS in setup()
Hide .comfy-menu.no-drag.comfy-menu-manual-pos via addStyleTag in
ComfyPage.setup(). This runs after every page load/reload, so the
menu is always hidden in screenshots without needing per-test
closeMenu() calls.

Remove all closeMenu() calls from 16 test files.
2026-04-14 17:30:49 +01:00
github-actions
485de05548 [automated] Update test expectations 2026-04-14 14:16:46 +00:00
Johnpaul Chiwetelu
464d04b709 Merge branch 'main' into feat/targeted-screenshot-tests 2026-04-14 15:10:27 +01:00
Johnpaul
3d4461f6ca fix: add per-test closeMenu for restore-workflow screenshot tests
File-level closeMenu was removed (interfered with Top menu tests),
so add it at the start of the two restore-workflow tests that take
screenshots with Disabled menu.
2026-04-14 15:04:19 +01:00
github-actions
b391383e78 [automated] Update test expectations 2026-04-14 00:02:08 +00:00
Johnpaul Chiwetelu
2d5d5c556d Merge branch 'main' into feat/targeted-screenshot-tests 2026-04-14 00:56:02 +01:00
Johnpaul
bdd977c231 fix: remove closeMenu from file-level beforeEach where tests switch to Top
closeMenu() in file-level beforeEach interferes with tests that
switch UseNewMenu to 'Top' in describe-level beforeEach. The click
consumes DOM events needed by group node conversion and prevents
error overlays from appearing.

Remove from: groupNode, interaction, primitiveNode file-level.
Keep per-setup() closeMenu calls where specifically needed.
2026-04-14 00:54:52 +01:00
github-actions
7e6fdef0f4 [automated] Update test expectations 2026-04-13 23:34:57 +00:00
Johnpaul Chiwetelu
81ddb96218 Merge branch 'main' into feat/targeted-screenshot-tests 2026-04-14 00:26:56 +01:00
Johnpaul
dc851e2e21 fix: make closeMenu resilient to non-clickable button
Button can exist in DOM but not be actionable (e.g. on mobile where
it's off-screen). Use isVisible() check and catch click failures
with a short timeout to avoid blocking tests.
2026-04-14 00:19:51 +01:00
Johnpaul
d6a13a3162 test: target execution and saveImageAndWebp screenshots
- execution: clip to CLIP Text Encode nodes instead of full canvas
- saveImageAndWebp: screenshot each node individually instead of canvas
2026-04-13 23:55:23 +01:00
github-actions
01ceaad837 [automated] Update test expectations 2026-04-13 22:24:18 +00:00
Johnpaul
7c3a16b16c test: close menu after setup() calls and in rerouteNode beforeEach
- colorPalette: add closeMenu() after setup() that reloads page
- interaction: add closeMenu() after setup({ clearStorage: false })
- rerouteNode: add closeMenu() in Native Reroute describe beforeEach
- Remove orphaned animated-image-preview golden images
2026-04-13 23:15:35 +01:00
github-actions
fd25ea2969 [automated] Update test expectations 2026-04-13 21:58:34 +00:00
Johnpaul
9c92669988 test: make closeMenu safe, use canvas for saveImageAndWebp
- closeMenu() now checks if close button exists before clicking
- saveImageAndWebp screenshots canvas instead of full page
- Delete widget golden images for regeneration
2026-04-13 22:49:34 +01:00
Johnpaul
fc9e442e36 test: close floating menu in beforeEach for remaining canvas tests
Close the legacy floating menu before screenshots in 12 more test
files that use UseNewMenu='Disabled'. This removes the menu overlay
from canvas screenshots, reducing visual noise and screenshot size.

Delete 147 golden images that need regeneration without the menu.
2026-04-13 22:44:41 +01:00
github-actions
346a4fbb64 [automated] Update test expectations 2026-04-13 21:30:12 +00:00
Johnpaul
9484b153f9 merge: resolve conflict in colors.spec.ts with main 2026-04-13 22:22:32 +01:00
Johnpaul
9b7c35dae1 test: use targeted screenshots instead of full-canvas captures
Screenshot tests now clip to specific nodes/regions instead of
capturing the entire canvas. This reduces screenshot size and
makes tests more focused on what they actually verify.

- Add getNodeClipRegion() helper for computing page-coordinate
  clip regions from LiteGraph node positions
- Convert 28 full-canvas screenshots to targeted clips across
  widget, nodeDisplay, primitiveNode, noteNode, recordAudio specs
- Use Vue node locators for mute, bypass, and color state tests
- Close floating menu in beforeEach for tests using UseNewMenu=Disabled
- Delete old golden images (will be regenerated in CI)
2026-04-13 22:16:11 +01:00
github-actions
4c05fcda6d [automated] Update test expectations 2026-04-10 17:47:53 +00:00
Johnpaul Chiwetelu
b77b803d49 Merge branch 'main' into test/comfy-complete-ci-container 2026-04-10 15:25:04 +01:00
Johnpaul
7ad363a036 fix: run CI container as root for /ComfyUI/user write access 2026-04-08 15:59:32 +01:00
Johnpaul Chiwetelu
eaaaa15d7d Merge branch 'main' into test/comfy-complete-ci-container 2026-04-08 15:31:23 +01:00
Johnpaul Chiwetelu
7af8b854ad Merge branch 'main' into test/comfy-complete-ci-container 2026-04-07 23:54:42 +01:00
Johnpaul
31df47f79e test: swap CI container to comfy-complete-ci for testing
Drop-in replacement of comfyui-ci-container:0.0.16 with
comfy-complete-ci:latest to test compatibility with the
complete container in CI.
2026-04-03 00:18:59 +01:00
84 changed files with 234 additions and 59 deletions

View File

@@ -54,7 +54,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
container:
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.16
image: ghcr.io/comfy-org/comfy-complete-ci:latest
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
@@ -102,7 +102,7 @@ jobs:
needs: setup
runs-on: ubuntu-latest
container:
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.16
image: ghcr.io/comfy-org/comfy-complete-ci:latest
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -322,6 +322,9 @@ export class ComfyPage {
window.app && window.app.extensionManager
)
await this.page.locator('.p-blockui-mask').waitFor({ state: 'hidden' })
await this.page.addStyleTag({
content: '.comfy-menu.no-drag.comfy-menu-manual-pos { display: none; }'
})
await this.nextFrame()
}
@@ -371,8 +374,11 @@ export class ComfyPage {
}
async closeMenu() {
await this.page.locator('button.comfy-close-menu-btn').click()
await this.nextFrame()
const btn = this.page.locator('button.comfy-close-menu-btn')
if (await btn.isVisible()) {
await btn.click({ timeout: 2000 }).catch(() => {})
await this.nextFrame()
}
}
async clickDialogButton(prompt: string, buttonText: string = 'Yes') {

View File

@@ -0,0 +1,61 @@
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
/**
* Compute a clip region encompassing one or more nodes on the canvas.
* Returns page-level coordinates for use with
* `page.toHaveScreenshot({ clip })`.
*
* Accounts for zoom scale, pan offset, title bar height, and
* canvas element position on page.
*/
export async function getNodeClipRegion(
comfyPage: ComfyPage,
nodeIds: NodeId[],
padding = 40
): Promise<{ x: number; y: number; width: number; height: number }> {
const canvasBox = await comfyPage.canvas.boundingBox()
if (!canvasBox) throw new Error('Canvas element not visible')
const region = await comfyPage.page.evaluate(
([ids, pad]) => {
const canvas = window.app!.canvas
const ds = canvas.ds
let minX = Infinity
let minY = Infinity
let maxX = -Infinity
let maxY = -Infinity
for (const id of ids) {
const node = canvas.graph!.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found`)
const pos = ds.convertOffsetToCanvas([node.pos[0], node.pos[1]])
const scaledWidth = node.size[0] * ds.scale
const scaledHeight = node.size[1] * ds.scale
const titleHeight = window.LiteGraph!.NODE_TITLE_HEIGHT * ds.scale
minX = Math.min(minX, pos[0])
minY = Math.min(minY, pos[1] - titleHeight)
maxX = Math.max(maxX, pos[0] + scaledWidth)
maxY = Math.max(maxY, pos[1] + scaledHeight)
}
return {
x: Math.max(0, minX - pad),
y: Math.max(0, minY - pad),
width: maxX - minX + pad * 2,
height: maxY - minY + pad * 2
}
},
[nodeIds, padding] as const
)
return {
x: Math.max(0, canvasBox.x + region.x),
y: Math.max(0, canvasBox.y + region.y),
width: region.width,
height: region.height
}
}

View File

@@ -2,6 +2,7 @@ import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
import { TestIds } from '@e2e/fixtures/selectors'
import { getNodeClipRegion } from '@e2e/fixtures/utils/screenshotClip'
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
@@ -33,8 +34,16 @@ test.describe('Execution', { tag: ['@smoke', '@workflow'] }, () => {
.getByTestId(TestIds.dialogs.errorOverlayDismiss)
.click()
await errorOverlay.waitFor({ state: 'hidden' })
await expect(comfyPage.canvas).toHaveScreenshot(
'execution-error-unconnected-slot.png'
const nodes = await comfyPage.nodeOps.getNodeRefsByTitle(
'CLIP Text Encode (Prompt)'
)
const clip = await getNodeClipRegion(
comfyPage,
nodes.map((n) => n.id)
)
await expect(comfyPage.page).toHaveScreenshot(
'execution-error-unconnected-slot.png',
{ clip }
)
}
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -781,7 +781,6 @@ test.describe('Canvas Interaction', { tag: '@screenshot' }, () => {
})
test('@mobile Can pan with touch', async ({ comfyPage }) => {
await comfyPage.closeMenu()
await comfyPage.canvasOps.panWithTouch({ x: 200, y: 200 })
await expect(comfyPage.canvas).toHaveScreenshot('panned-touch.png')
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -2,6 +2,7 @@ import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
import { TestIds } from '@e2e/fixtures/selectors'
import { getNodeClipRegion } from '@e2e/fixtures/utils/screenshotClip'
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
@@ -12,27 +13,47 @@ test.beforeEach(async ({ comfyPage }) => {
test.describe('Optional input', { tag: ['@screenshot', '@node'] }, () => {
test('No shape specified', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('inputs/optional_input_no_shape')
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('optional_input.png', {
clip
})
})
test('Wrong shape specified', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('inputs/optional_input_wrong_shape')
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('optional_input.png', {
clip
})
})
test('Correct shape specified', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('inputs/optional_input_correct_shape')
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('optional_input.png', {
clip
})
})
test('Force input', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('inputs/force_input')
await expect(comfyPage.canvas).toHaveScreenshot('force_input.png')
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('force_input.png', {
clip
})
})
test('Default input', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('inputs/default_input')
await expect(comfyPage.canvas).toHaveScreenshot('default_input.png')
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('default_input.png', {
clip
})
})
test('Only optional inputs', async ({ comfyPage }) => {
@@ -74,22 +95,32 @@ test.describe('Optional input', { tag: ['@screenshot', '@node'] }, () => {
test('slider', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('inputs/simple_slider')
await expect(comfyPage.canvas).toHaveScreenshot('simple_slider.png')
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('simple_slider.png', {
clip
})
})
test('unknown converted widget', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow(
'missing/missing_nodes_converted_widget'
)
await expect(comfyPage.canvas).toHaveScreenshot(
'missing_nodes_converted_widget.png'
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot(
'missing_nodes_converted_widget.png',
{ clip }
)
})
test('dynamically added input', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('inputs/dynamically_added_input')
await expect(comfyPage.canvas).toHaveScreenshot(
'dynamically_added_input.png'
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot(
'dynamically_added_input.png',
{ clip }
)
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -150,7 +150,6 @@ test.describe('Node search box', { tag: '@node' }, () => {
})
test('@mobile Can trigger on empty canvas tap', async ({ comfyPage }) => {
await comfyPage.closeMenu()
await comfyPage.workflow.loadWorkflow('nodes/single_ksampler')
const screenCenter = {
x: 200,

View File

@@ -1,6 +1,8 @@
import { expect } from '@playwright/test'
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
import { getNodeClipRegion } from '@e2e/fixtures/utils/screenshotClip'
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
@@ -9,6 +11,7 @@ test.beforeEach(async ({ comfyPage }) => {
test.describe('Note Node', { tag: '@node' }, () => {
test('Can load node nodes', { tag: '@screenshot' }, async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('nodes/note_nodes')
await expect(comfyPage.canvas).toHaveScreenshot('note_nodes.png')
const clip = await getNodeClipRegion(comfyPage, [1, 2] as NodeId[])
await expect(comfyPage.page).toHaveScreenshot('note_nodes.png', { clip })
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@@ -2,6 +2,7 @@ import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
import { TestIds } from '@e2e/fixtures/selectors'
import { getNodeClipRegion } from '@e2e/fixtures/utils/screenshotClip'
import type { NodeReference } from '@e2e/fixtures/utils/litegraphUtils'
test.beforeEach(async ({ comfyPage }) => {
@@ -11,7 +12,11 @@ test.beforeEach(async ({ comfyPage }) => {
test.describe('Primitive Node', { tag: ['@screenshot', '@node'] }, () => {
test('Can load with correct size', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('primitive/primitive_node')
await expect(comfyPage.canvas).toHaveScreenshot('primitive_node.png')
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('primitive_node.png', {
clip
})
})
// When link is dropped on widget, it should automatically convert the widget
@@ -26,8 +31,13 @@ test.describe('Primitive Node', { tag: ['@screenshot', '@node'] }, () => {
await comfyPage.nodeOps.getNodeRefById(2)
// Connect the output of the primitive node to the input of first widget of the ksampler node
await primitiveNode.connectWidget(0, ksamplerNode, 0)
await expect(comfyPage.canvas).toHaveScreenshot(
'primitive_node_connected.png'
const clip = await getNodeClipRegion(comfyPage, [
primitiveNode.id,
ksamplerNode.id
])
await expect(comfyPage.page).toHaveScreenshot(
'primitive_node_connected.png',
{ clip }
)
})
@@ -40,8 +50,13 @@ test.describe('Primitive Node', { tag: ['@screenshot', '@node'] }, () => {
const clipEncoderNode: NodeReference =
await comfyPage.nodeOps.getNodeRefById(2)
await primitiveNode.connectWidget(0, clipEncoderNode, 0)
await expect(comfyPage.canvas).toHaveScreenshot(
'primitive_node_connected_dom_widget.png'
const clip = await getNodeClipRegion(comfyPage, [
primitiveNode.id,
clipEncoderNode.id
])
await expect(comfyPage.page).toHaveScreenshot(
'primitive_node_connected_dom_widget.png',
{ clip }
)
})
@@ -54,8 +69,13 @@ test.describe('Primitive Node', { tag: ['@screenshot', '@node'] }, () => {
const ksamplerNode: NodeReference =
await comfyPage.nodeOps.getNodeRefById(2)
await primitiveNode.connectWidget(0, ksamplerNode, 0)
await expect(comfyPage.canvas).toHaveScreenshot(
'static_primitive_connected.png'
const clip = await getNodeClipRegion(comfyPage, [
primitiveNode.id,
ksamplerNode.id
])
await expect(comfyPage.page).toHaveScreenshot(
'static_primitive_connected.png',
{ clip }
)
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -1,6 +1,7 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
import { getNodeClipRegion } from '@e2e/fixtures/utils/screenshotClip'
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
@@ -30,6 +31,10 @@ test.describe('Record Audio Node', { tag: '@screenshot' }, () => {
.toBe(1)
// Take a screenshot of the canvas with the RecordAudio node
await expect(comfyPage.canvas).toHaveScreenshot('record_audio_node.png')
const nodes = await comfyPage.nodeOps.getNodeRefsByType('RecordAudio')
const clip = await getNodeClipRegion(comfyPage, [nodes[0].id])
await expect(comfyPage.page).toHaveScreenshot('record_audio_node.png', {
clip
})
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -29,9 +29,8 @@ test.describe(
timeout: 30000
})
await expect(comfyPage.page).toHaveScreenshot(
'save-image-and-webm-preview.png'
)
await expect(saveImageNode).toHaveScreenshot('save-image-preview.png')
await expect(saveWebmNode).toHaveScreenshot('save-webm-preview.png')
})
}
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -25,9 +25,9 @@ test.describe('Vue Node Bypass', { tag: '@vue-nodes' }, () => {
.getByTestId('node-inner-wrapper')
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
'vue-node-bypassed-state.png'
)
await expect(
comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
).toHaveScreenshot('vue-node-bypassed-state.png')
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
await expect(checkpointNode).not.toHaveClass(BYPASS_CLASS)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -34,7 +34,7 @@ test.describe(
.getByTestId(TestIds.selectionToolbox.colorBlue)
.click()
await expect(comfyPage.canvas).toHaveScreenshot(
await expect(loadCheckpointNode).toHaveScreenshot(
'vue-node-custom-color-blue.png'
)
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -17,9 +17,7 @@ test.describe('Vue Node Mute', { tag: '@vue-nodes' }, () => {
const checkpointNode =
comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
await expect(checkpointNode).toHaveCSS('opacity', MUTE_OPACITY)
await expect(comfyPage.canvas).toHaveScreenshot(
'vue-node-muted-state.png'
)
await expect(checkpointNode).toHaveScreenshot('vue-node-muted-state.png')
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
await expect(checkpointNode).not.toHaveCSS('opacity', MUTE_OPACITY)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -2,6 +2,7 @@ import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
import { DefaultGraphPositions } from '@e2e/fixtures/constants/defaultGraphPositions'
import { getNodeClipRegion } from '@e2e/fixtures/utils/screenshotClip'
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
@@ -15,18 +16,29 @@ test.describe('Combo text widget', { tag: ['@screenshot', '@widget'] }, () => {
0.2,
1
)
await expect(comfyPage.canvas).toHaveScreenshot(
'load-checkpoint-resized-min-width.png'
const loadCheckpointNode = (
await comfyPage.nodeOps.getNodeRefsByTitle('Load Checkpoint')
)[0]
const loadCheckpointClip = await getNodeClipRegion(comfyPage, [
loadCheckpointNode.id
])
await expect(comfyPage.page).toHaveScreenshot(
'load-checkpoint-resized-min-width.png',
{ clip: loadCheckpointClip }
)
await comfyPage.closeMenu()
await comfyPage.nodeOps.resizeNode(
DefaultGraphPositions.ksampler.pos,
DefaultGraphPositions.ksampler.size,
0.2,
1
)
await expect(comfyPage.canvas).toHaveScreenshot(
`ksampler-resized-min-width.png`
const ksamplerNode = (
await comfyPage.nodeOps.getNodeRefsByTitle('KSampler')
)[0]
const ksamplerClip = await getNodeClipRegion(comfyPage, [ksamplerNode.id])
await expect(comfyPage.page).toHaveScreenshot(
`ksampler-resized-min-width.png`,
{ clip: ksamplerClip }
)
})
@@ -37,8 +49,13 @@ test.describe('Combo text widget', { tag: ['@screenshot', '@widget'] }, () => {
0.8,
0.8
)
await expect(comfyPage.canvas).toHaveScreenshot(
'empty-latent-resized-80-percent.png'
const emptyLatentNode = (
await comfyPage.nodeOps.getNodeRefsByTitle('Empty Latent Image')
)[0]
const clip = await getNodeClipRegion(comfyPage, [emptyLatentNode.id])
await expect(comfyPage.page).toHaveScreenshot(
'empty-latent-resized-80-percent.png',
{ clip }
)
})
@@ -50,7 +67,13 @@ test.describe('Combo text widget', { tag: ['@screenshot', '@widget'] }, () => {
1,
true
)
await expect(comfyPage.canvas).toHaveScreenshot('resized-to-original.png')
const loadCheckpointNode = (
await comfyPage.nodeOps.getNodeRefsByTitle('Load Checkpoint')
)[0]
const clip = await getNodeClipRegion(comfyPage, [loadCheckpointNode.id])
await expect(comfyPage.page).toHaveScreenshot('resized-to-original.png', {
clip
})
})
test('should refresh combo values of optional inputs', async ({
@@ -105,12 +128,16 @@ test.describe('Combo text widget', { tag: ['@screenshot', '@widget'] }, () => {
test.describe('Boolean widget', { tag: ['@screenshot', '@widget'] }, () => {
test('Can toggle', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('widgets/boolean_widget')
await expect(comfyPage.canvas).toHaveScreenshot('boolean_widget.png')
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('boolean_widget.png', {
clip
})
const widget = await node.getWidget(0)
await widget.click()
await expect(comfyPage.canvas).toHaveScreenshot(
'boolean_widget_toggled.png'
await expect(comfyPage.page).toHaveScreenshot(
'boolean_widget_toggled.png',
{ clip }
)
})
})
@@ -129,7 +156,10 @@ test.describe('Slider widget', { tag: ['@screenshot', '@widget'] }, () => {
}
})
await widget.dragHorizontal(50)
await expect(comfyPage.canvas).toHaveScreenshot('slider_widget_dragged.png')
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('slider_widget_dragged.png', {
clip
})
await expect
.poll(() => comfyPage.page.evaluate(() => window.widgetValue))
@@ -151,7 +181,10 @@ test.describe('Number widget', { tag: ['@screenshot', '@widget'] }, () => {
}
})
await widget.dragHorizontal(50)
await expect(comfyPage.canvas).toHaveScreenshot('seed_widget_dragged.png')
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('seed_widget_dragged.png', {
clip
})
await expect
.poll(() => comfyPage.page.evaluate(() => window.widgetValue))
@@ -179,8 +212,10 @@ test.describe(
.poll(async () => (await node.getSize()).height)
.toBeGreaterThan(initialSize.height)
await expect(comfyPage.canvas).toHaveScreenshot(
'ksampler_widget_added.png'
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot(
'ksampler_widget_added.png',
{ clip }
)
})
}
@@ -189,8 +224,11 @@ test.describe(
test.describe('Image widget', { tag: ['@screenshot', '@widget'] }, () => {
test('Can load image', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('widgets/load_image_widget')
await expect(comfyPage.canvas).toHaveScreenshot('load_image_widget.png', {
maxDiffPixels: 50
const nodes = await comfyPage.nodeOps.getNodeRefsByType('LoadImage')
const clip = await getNodeClipRegion(comfyPage, [nodes[0].id])
await expect(comfyPage.page).toHaveScreenshot('load_image_widget.png', {
maxDiffPixels: 50,
clip
})
})
@@ -208,8 +246,10 @@ test.describe('Image widget', { tag: ['@screenshot', '@widget'] }, () => {
})
// Expect the image preview to change automatically
await expect(comfyPage.canvas).toHaveScreenshot(
'image_preview_drag_and_dropped.png'
const clip = await getNodeClipRegion(comfyPage, [loadImageNode.id])
await expect(comfyPage.page).toHaveScreenshot(
'image_preview_drag_and_dropped.png',
{ clip }
)
// Expect the filename combo value to be updated
@@ -264,9 +304,10 @@ test.describe('Image widget', { tag: ['@screenshot', '@widget'] }, () => {
await comfyPage.nextFrame()
// Expect the image preview to change automatically
await expect(comfyPage.canvas).toHaveScreenshot(
const clip = await getNodeClipRegion(comfyPage, [loadImageNode.id])
await expect(comfyPage.page).toHaveScreenshot(
'image_preview_changed_by_combo_value.png',
{ maxDiffPixels: 50 }
{ maxDiffPixels: 50, clip }
)
// Expect the filename combo value to be updated
@@ -403,7 +444,11 @@ test.describe('Load audio widget', { tag: ['@screenshot', '@widget'] }, () => {
test('Can load audio', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('widgets/load_audio_widget')
await expect(comfyPage.page.locator('.comfy-audio')).toBeVisible()
await expect(comfyPage.canvas).toHaveScreenshot('load_audio_widget.png')
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
const clip = await getNodeClipRegion(comfyPage, [node.id])
await expect(comfyPage.page).toHaveScreenshot('load_audio_widget.png', {
clip
})
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB