mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
test: cover assets sidebar browser journeys
This commit is contained in:
@@ -4,6 +4,10 @@ import { expect } from '@playwright/test'
|
||||
import type { WorkspaceStore } from '../../types/globals'
|
||||
import { TestIds } from '../selectors'
|
||||
|
||||
function escapeRegExp(value: string): string {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
|
||||
class SidebarTab {
|
||||
constructor(
|
||||
public readonly page: Page,
|
||||
@@ -372,7 +376,11 @@ export class AssetsSidebarTab extends SidebarTab {
|
||||
}
|
||||
|
||||
getAssetCardByName(name: string) {
|
||||
return this.assetCards.filter({ hasText: name }).first()
|
||||
return this.root
|
||||
.getByRole('button', {
|
||||
name: new RegExp(`^${escapeRegExp(name)}\\s+-\\s+`)
|
||||
})
|
||||
.first()
|
||||
}
|
||||
|
||||
get selectedCards() {
|
||||
@@ -469,7 +477,11 @@ export class AssetsSidebarTab extends SidebarTab {
|
||||
}
|
||||
|
||||
async openContextMenuForAsset(name: string) {
|
||||
await this.asset(name).click({ button: 'right' })
|
||||
await this.asset(name).dispatchEvent('contextmenu', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
button: 2
|
||||
})
|
||||
await this.page
|
||||
.locator('.p-contextmenu')
|
||||
.waitFor({ state: 'visible', timeout: 3000 })
|
||||
@@ -570,7 +582,11 @@ export class AssetsSidebarTab extends SidebarTab {
|
||||
|
||||
async rightClickAsset(name: string) {
|
||||
const card = this.getAssetCardByName(name)
|
||||
await card.click({ button: 'right' })
|
||||
await card.dispatchEvent('contextmenu', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
button: 2
|
||||
})
|
||||
await this.page
|
||||
.locator('.p-contextmenu')
|
||||
.waitFor({ state: 'visible', timeout: 3000 })
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { expect, mergeTests } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { JobEntry } from '@comfyorg/ingest-types'
|
||||
|
||||
import { comfyPageFixture } from '@e2e/fixtures/ComfyPage'
|
||||
@@ -61,6 +62,41 @@ const SAMPLE_IMPORTED_FILES = [
|
||||
'audio_clip.wav'
|
||||
]
|
||||
|
||||
async function installClipboardRecorder(page: Page): Promise<void> {
|
||||
await page.addInitScript(() => {
|
||||
const scopedWindow = window as Window & {
|
||||
__assetClipboardWrites?: string[]
|
||||
}
|
||||
const writes: string[] = []
|
||||
|
||||
scopedWindow.__assetClipboardWrites = writes
|
||||
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
configurable: true,
|
||||
value: {
|
||||
writeText: async (text: string) => {
|
||||
writes.push(text)
|
||||
}
|
||||
} satisfies Pick<Clipboard, 'writeText'>
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function readClipboardWrites(page: Page): Promise<string[]> {
|
||||
return await page.evaluate(() => {
|
||||
const scopedWindow = window as Window & {
|
||||
__assetClipboardWrites?: string[]
|
||||
}
|
||||
|
||||
return scopedWindow.__assetClipboardWrites ?? []
|
||||
})
|
||||
}
|
||||
|
||||
function assetCardLabel(name: string): RegExp {
|
||||
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
return new RegExp(`^${escapedName}\\s+-\\s+`)
|
||||
}
|
||||
|
||||
test.describe('Assets sidebar - empty states', () => {
|
||||
test.beforeEach(async ({ comfyPage, assetScenario }) => {
|
||||
await assetScenario.seedEmptyState()
|
||||
@@ -533,6 +569,132 @@ test.describe('Assets sidebar - bulk actions', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Assets sidebar - preview and folder view', () => {
|
||||
test.beforeEach(async ({ comfyPage, assetScenario }) => {
|
||||
await installClipboardRecorder(comfyPage.page)
|
||||
await assetScenario.seedGeneratedHistory(SAMPLE_JOBS)
|
||||
await assetScenario.seedImportedFiles([])
|
||||
await comfyPage.setup()
|
||||
})
|
||||
|
||||
test('opens a generated asset in the gallery dialog', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
|
||||
await tab.openAssetPreview('landscape.png')
|
||||
|
||||
await expect(tab.previewDialog).toBeVisible()
|
||||
await expect(tab.previewImage('landscape.png')).toBeVisible()
|
||||
})
|
||||
|
||||
test('opens a multi-output job folder and returns to all assets', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
|
||||
await tab.openOutputFolder('abstract_art.png')
|
||||
|
||||
await expect(tab.backToAssetsButton).toBeVisible()
|
||||
await expect(tab.copyJobIdButton).toBeVisible()
|
||||
await expect(tab.assetCards).toHaveCount(2)
|
||||
await expect(tab.asset('abstract_art-2.png')).toBeVisible()
|
||||
await expect(tab.asset('abstract_art.png')).toBeVisible()
|
||||
|
||||
await tab.backToAssetsButton.click()
|
||||
|
||||
await expect(tab.backToAssetsButton).toBeHidden()
|
||||
await expect(tab.assetCards).toHaveCount(SAMPLE_JOBS.length)
|
||||
await expect(
|
||||
tab.root.getByRole('button', {
|
||||
name: assetCardLabel('abstract_art-2.png')
|
||||
})
|
||||
).toHaveCount(0)
|
||||
})
|
||||
|
||||
test('copies the folder job ID to the clipboard', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
|
||||
await tab.openOutputFolder('abstract_art.png')
|
||||
await tab.copyJobIdButton.click()
|
||||
|
||||
await expect(
|
||||
comfyPage.page.locator('.p-toast-message-success')
|
||||
).toBeVisible()
|
||||
await expect(
|
||||
comfyPage.page.getByText('Job ID copied to clipboard')
|
||||
).toBeVisible()
|
||||
expect(await readClipboardWrites(comfyPage.page)).toEqual(['job-gamma'])
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Assets sidebar - delete confirmation', () => {
|
||||
test.beforeEach(async ({ comfyPage, assetScenario }) => {
|
||||
await assetScenario.seedGeneratedHistory(SAMPLE_JOBS)
|
||||
await assetScenario.seedImportedFiles([])
|
||||
await comfyPage.setup()
|
||||
})
|
||||
|
||||
test('confirming delete removes the asset and shows success feedback', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
|
||||
const initialCount = await tab.assetCards.count()
|
||||
|
||||
await tab.runContextMenuAction('portrait.png', 'Delete')
|
||||
|
||||
await expect(comfyPage.confirmDialog.root).toBeVisible()
|
||||
await expect(
|
||||
comfyPage.confirmDialog.root.getByText('Delete this asset?')
|
||||
).toBeVisible()
|
||||
await expect(
|
||||
comfyPage.confirmDialog.root.getByText(
|
||||
'This asset will be permanently removed.'
|
||||
)
|
||||
).toBeVisible()
|
||||
|
||||
await comfyPage.confirmDialog.click('delete')
|
||||
|
||||
await expect(tab.assetCards).toHaveCount(initialCount - 1, {
|
||||
timeout: 5000
|
||||
})
|
||||
await expect(
|
||||
tab.root.getByRole('button', { name: assetCardLabel('portrait.png') })
|
||||
).toHaveCount(0)
|
||||
await expect(
|
||||
comfyPage.page.locator('.p-toast-message-success')
|
||||
).toBeVisible()
|
||||
await expect(
|
||||
comfyPage.page.getByText('Asset deleted successfully')
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('cancelling delete preserves the asset', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
|
||||
const initialCount = await tab.assetCards.count()
|
||||
|
||||
await tab.runContextMenuAction('portrait.png', 'Delete')
|
||||
await expect(comfyPage.confirmDialog.root).toBeVisible()
|
||||
|
||||
await comfyPage.confirmDialog.click('reject')
|
||||
|
||||
await expect(tab.assetCards).toHaveCount(initialCount)
|
||||
await expect(tab.asset('portrait.png')).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Assets sidebar - pagination', () => {
|
||||
test('initial load fetches first batch with offset 0', async ({
|
||||
comfyPage,
|
||||
|
||||
Reference in New Issue
Block a user