test: stabilize flaky Playwright tests (#10817)

Stabilize flaky Playwright tests by improving test reliability.

This PR aims to identify and fix flaky e2e tests.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10817-test-stabilize-flaky-Playwright-tests-3366d73d365081ada40de73ce11af625)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Alexander Brown
2026-04-07 19:47:27 -07:00
committed by GitHub
parent d73c4406ed
commit 4cb83353cb
29 changed files with 760 additions and 536 deletions

View File

@@ -97,9 +97,9 @@ test.describe('Vue Node Context Menu', () => {
})
await comfyPage.nextFrame()
expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(
initialCount + 1
)
await expect
.poll(() => comfyPage.nodeOps.getGraphNodesCount())
.toBe(initialCount + 1)
})
test('should duplicate node via context menu', async ({ comfyPage }) => {
@@ -108,9 +108,9 @@ test.describe('Vue Node Context Menu', () => {
await openContextMenu(comfyPage, 'Load Checkpoint')
await clickExactMenuItem(comfyPage, 'Duplicate')
expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(
initialCount + 1
)
await expect
.poll(() => comfyPage.nodeOps.getGraphNodesCount())
.toBe(initialCount + 1)
})
test('should pin and unpin node via context menu', async ({
@@ -125,7 +125,7 @@ test.describe('Vue Node Context Menu', () => {
const fixture = await comfyPage.vueNodes.getFixtureByTitle(nodeTitle)
await expect(fixture.pinIndicator).toBeVisible()
expect(await nodeRef.isPinned()).toBe(true)
await expect.poll(() => nodeRef.isPinned()).toBe(true)
// Verify drag blocked
const header = fixture.header
@@ -143,7 +143,7 @@ test.describe('Vue Node Context Menu', () => {
await clickExactMenuItem(comfyPage, 'Unpin')
await expect(fixture.pinIndicator).not.toBeVisible()
expect(await nodeRef.isPinned()).toBe(false)
await expect.poll(() => nodeRef.isPinned()).toBe(false)
})
test('should bypass node and remove bypass via context menu', async ({
@@ -155,7 +155,7 @@ test.describe('Vue Node Context Menu', () => {
await openContextMenu(comfyPage, nodeTitle)
await clickExactMenuItem(comfyPage, 'Bypass')
expect(await nodeRef.isBypassed()).toBe(true)
await expect.poll(() => nodeRef.isBypassed()).toBe(true)
await expect(getNodeWrapper(comfyPage, nodeTitle)).toHaveClass(
BYPASS_CLASS
)
@@ -163,7 +163,7 @@ test.describe('Vue Node Context Menu', () => {
await openContextMenu(comfyPage, nodeTitle)
await clickExactMenuItem(comfyPage, 'Remove Bypass')
expect(await nodeRef.isBypassed()).toBe(false)
await expect.poll(() => nodeRef.isBypassed()).toBe(false)
await expect(getNodeWrapper(comfyPage, nodeTitle)).not.toHaveClass(
BYPASS_CLASS
)
@@ -206,6 +206,26 @@ test.describe('Vue Node Context Menu', () => {
.grantPermissions(['clipboard-read', 'clipboard-write'])
await comfyPage.workflow.loadWorkflow('widgets/load_image_widget')
await comfyPage.vueNodes.waitForNodes(1)
await comfyPage.page
.locator('[data-node-id] img')
.first()
.waitFor({ state: 'visible' })
const [loadImageNode] =
await comfyPage.nodeOps.getNodeRefsByTitle('Load Image')
if (!loadImageNode) throw new Error('Load Image node not found')
await expect
.poll(
() =>
comfyPage.page.evaluate(
(nodeId) =>
window.app!.graph.getNodeById(nodeId)?.imgs?.length ?? 0,
loadImageNode.id
),
{ timeout: 5_000 }
)
.toBeGreaterThan(0)
})
test('should copy image to clipboard via context menu', async ({
@@ -215,13 +235,16 @@ test.describe('Vue Node Context Menu', () => {
await clickExactMenuItem(comfyPage, 'Copy Image')
// Verify the clipboard contains an image
const hasImage = await comfyPage.page.evaluate(async () => {
const items = await navigator.clipboard.read()
return items.some((item) =>
item.types.some((t) => t.startsWith('image/'))
)
})
expect(hasImage).toBe(true)
await expect
.poll(async () => {
return comfyPage.page.evaluate(async () => {
const items = await navigator.clipboard.read()
return items.some((item) =>
item.types.some((t) => t.startsWith('image/'))
)
})
})
.toBe(true)
})
test('should paste image to LoadImage node via context menu', async ({
@@ -374,9 +397,9 @@ test.describe('Vue Node Context Menu', () => {
})
await comfyPage.nextFrame()
expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(
initialCount + nodeTitles.length
)
await expect
.poll(() => comfyPage.nodeOps.getGraphNodesCount())
.toBe(initialCount + nodeTitles.length)
})
test('should duplicate selected nodes via context menu', async ({
@@ -387,9 +410,9 @@ test.describe('Vue Node Context Menu', () => {
await openMultiNodeContextMenu(comfyPage, nodeTitles)
await clickExactMenuItem(comfyPage, 'Duplicate')
expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(
initialCount + nodeTitles.length
)
await expect
.poll(() => comfyPage.nodeOps.getGraphNodesCount())
.toBe(initialCount + nodeTitles.length)
})
test('should pin and unpin selected nodes via context menu', async ({
@@ -420,7 +443,7 @@ test.describe('Vue Node Context Menu', () => {
for (const title of nodeTitles) {
const nodeRef = await getNodeRef(comfyPage, title)
expect(await nodeRef.isBypassed()).toBe(true)
await expect.poll(() => nodeRef.isBypassed()).toBe(true)
await expect(getNodeWrapper(comfyPage, title)).toHaveClass(BYPASS_CLASS)
}
@@ -429,7 +452,7 @@ test.describe('Vue Node Context Menu', () => {
for (const title of nodeTitles) {
const nodeRef = await getNodeRef(comfyPage, title)
expect(await nodeRef.isBypassed()).toBe(false)
await expect.poll(() => nodeRef.isBypassed()).toBe(false)
await expect(getNodeWrapper(comfyPage, title)).not.toHaveClass(
BYPASS_CLASS
)
@@ -501,9 +524,9 @@ test.describe('Vue Node Context Menu', () => {
const subgraphNode = comfyPage.vueNodes.getNodeByTitle('New Subgraph')
await expect(subgraphNode).toBeVisible()
expect(await comfyPage.nodeOps.getGraphNodesCount()).toBe(
initialCount - nodeTitles.length + 1
)
await expect
.poll(() => comfyPage.nodeOps.getGraphNodesCount())
.toBe(initialCount - nodeTitles.length + 1)
})
})
})