Compare commits

...

7 Commits

Author SHA1 Message Date
dante01yoon
9986949a10 fix(test): use polling assertions for async changeTracker state
isCurrentWorkflowModified, undoQueueSize, and redoQueueSize update
asynchronously after undo/redo actions. Non-retrying assertions
fail when shard timing shifts.
2026-04-03 07:19:21 +09:00
dante01yoon
f0a5e6d22c fix(test): use DuplicateWorkflow + middle-click pattern, rm duplicate file
- loadWorkflow replaces current tab content (no new tab created),
  causing closeWorkflowTab to timeout on missing tab locator
- closeWorkflowTab relies on hover-dependent X button; middle-click
  is the proven close mechanism used by all sibling tests
- git rm the duplicate workflowTabSave.spec.ts (lint-staged restored it)
2026-04-02 22:52:24 +09:00
dante01yoon
0be22ec64a fix(test): deduplicate tab save tests, add unmodified-close scenario
Remove workflowTabSave.spec.ts (tests 2/3 duplicate workflowPersistence).
Add unique unmodified-inactive-tab-close test to workflowPersistence
using the proven saveWorkflow/loadWorkflow pattern that passes CI.
2026-04-02 22:35:03 +09:00
dante01yoon
4ad5506a8c fix(test): simplify tab save regression to single stable test
Remove 2 complex multi-dialog tests that fail in CI due to strict
mode violations and timeout issues. Keep only the core regression
scenario: closing an inactive tab preserves the active workflow.
2026-04-02 22:08:09 +09:00
GitHub Action
4d141f2424 [automated] Apply ESLint and Oxfmt fixes 2026-04-02 08:29:10 +00:00
dante01yoon
0111168002 test: use setupWorkflowsDirectory to speed up workflow tab save tests
Replace slow UI-based workflow saving in beforeEach/test setup with
setupWorkflowsDirectory() API calls. Opens pre-populated workflows
from the sidebar instead of going through topbar menu interactions,
fixing the 15s timeout in CI.
2026-04-02 17:26:08 +09:00
dante01yoon
1e77a3aa1b test: add E2E regression tests for workflow tab save bug (#10745)
Verify closing an inactive workflow tab does not overwrite its content
with the active tab's graph. Covers saved, modified, and unsaved tabs.
2026-04-02 10:31:57 +09:00
2 changed files with 90 additions and 15 deletions

View File

@@ -33,35 +33,45 @@ test.describe('Change Tracker', { tag: '@workflow' }, () => {
// Save, confirm no errors & workflow modified flag removed
await comfyPage.menu.topbar.saveWorkflow('undo-redo-test')
expect(await comfyPage.toast.getToastErrorCount()).toBe(0)
expect(await comfyPage.workflow.isCurrentWorkflowModified()).toBe(false)
expect(await comfyPage.workflow.getUndoQueueSize()).toBe(0)
expect(await comfyPage.workflow.getRedoQueueSize()).toBe(0)
await expect
.poll(() => comfyPage.workflow.isCurrentWorkflowModified())
.toBe(false)
await expect.poll(() => comfyPage.workflow.getUndoQueueSize()).toBe(0)
await expect.poll(() => comfyPage.workflow.getRedoQueueSize()).toBe(0)
const node = (await comfyPage.nodeOps.getFirstNodeRef())!
await node.click('title')
await node.click('collapse')
await expect(node).toBeCollapsed()
expect(await comfyPage.workflow.isCurrentWorkflowModified()).toBe(true)
expect(await comfyPage.workflow.getUndoQueueSize()).toBe(1)
expect(await comfyPage.workflow.getRedoQueueSize()).toBe(0)
await expect
.poll(() => comfyPage.workflow.isCurrentWorkflowModified())
.toBe(true)
await expect.poll(() => comfyPage.workflow.getUndoQueueSize()).toBe(1)
await expect.poll(() => comfyPage.workflow.getRedoQueueSize()).toBe(0)
await comfyPage.keyboard.bypass()
await expect(node).toBeBypassed()
expect(await comfyPage.workflow.isCurrentWorkflowModified()).toBe(true)
expect(await comfyPage.workflow.getUndoQueueSize()).toBe(2)
expect(await comfyPage.workflow.getRedoQueueSize()).toBe(0)
await expect
.poll(() => comfyPage.workflow.isCurrentWorkflowModified())
.toBe(true)
await expect.poll(() => comfyPage.workflow.getUndoQueueSize()).toBe(2)
await expect.poll(() => comfyPage.workflow.getRedoQueueSize()).toBe(0)
await comfyPage.keyboard.undo()
await expect(node).not.toBeBypassed()
expect(await comfyPage.workflow.isCurrentWorkflowModified()).toBe(true)
expect(await comfyPage.workflow.getUndoQueueSize()).toBe(1)
expect(await comfyPage.workflow.getRedoQueueSize()).toBe(1)
await expect
.poll(() => comfyPage.workflow.isCurrentWorkflowModified())
.toBe(true)
await expect.poll(() => comfyPage.workflow.getUndoQueueSize()).toBe(1)
await expect.poll(() => comfyPage.workflow.getRedoQueueSize()).toBe(1)
await comfyPage.keyboard.undo()
await expect(node).not.toBeCollapsed()
expect(await comfyPage.workflow.isCurrentWorkflowModified()).toBe(false)
expect(await comfyPage.workflow.getUndoQueueSize()).toBe(0)
expect(await comfyPage.workflow.getRedoQueueSize()).toBe(2)
await expect
.poll(() => comfyPage.workflow.isCurrentWorkflowModified())
.toBe(false)
await expect.poll(() => comfyPage.workflow.getUndoQueueSize()).toBe(0)
await expect.poll(() => comfyPage.workflow.getRedoQueueSize()).toBe(2)
})
})

View File

@@ -323,6 +323,71 @@ test.describe('Workflow Persistence', () => {
expect(linkCountAfter).toBe(linkCountBefore)
})
test('Closing an unmodified inactive tab preserves both workflows', async ({
comfyPage
}) => {
test.info().annotations.push({
type: 'regression',
description:
'PR #10745 — closing inactive tab could corrupt the persisted file'
})
await comfyPage.settings.setSetting(
'Comfy.Workflow.WorkflowTabsPosition',
'Topbar'
)
const suffix = Date.now().toString(36)
const nameA = `test-A-${suffix}`
const nameB = `test-B-${suffix}`
// Save the default workflow as A
await comfyPage.menu.topbar.saveWorkflow(nameA)
const nodeCountA = await comfyPage.nodeOps.getNodeCount()
// Create B: duplicate, add a node, then save (unmodified after save)
await comfyPage.command.executeCommand('Comfy.DuplicateWorkflow')
await comfyPage.nextFrame()
await comfyPage.page.evaluate(() => {
window.app!.graph.add(window.LiteGraph!.createNode('Note', undefined, {}))
})
await comfyPage.nextFrame()
await comfyPage.menu.topbar.saveWorkflow(nameB)
const nodeCountB = await comfyPage.nodeOps.getNodeCount()
expect(nodeCountB).toBe(nodeCountA + 1)
// Switch to A (making B inactive and unmodified)
await comfyPage.menu.topbar.getWorkflowTab(nameA).click()
await comfyPage.workflow.waitForWorkflowIdle()
await expect
.poll(() => comfyPage.nodeOps.getNodeCount(), { timeout: 3000 })
.toBe(nodeCountA)
// Close inactive B via middle-click — no save dialog expected
await comfyPage.menu.topbar.getWorkflowTab(nameB).click({
button: 'middle'
})
await comfyPage.nextFrame()
// A should still have its own content
await expect
.poll(() => comfyPage.nodeOps.getNodeCount(), { timeout: 3000 })
.toBe(nodeCountA)
// Reopen B from saved list
const workflowsTab = comfyPage.menu.workflowsTab
await workflowsTab.open()
await workflowsTab.getPersistedItem(nameB).dblclick()
await comfyPage.workflow.waitForWorkflowIdle()
// B should have its original content, not A's
await expect
.poll(() => comfyPage.nodeOps.getNodeCount(), { timeout: 5000 })
.toBe(nodeCountB)
})
test('Closing an inactive tab with save preserves its own content', async ({
comfyPage
}) => {