Files
ComfyUI_frontend/src/components/sidebar/tabs/AssetsSidebarListView.test.ts
Benjamin Lu 25880aa024 fix: update asset video previews for card and list (#8908)
## Summary
Render generated video previews in list items using a real video element (instead of an image element (this caused errors before)) and include a custom play button with dimming per [the designs](https://www.figma.com/design/LVilZgHGk5RwWOkVN6yCEK/Queue-Progress-Modal?node-id=3928-39270&m=dev).

## Changes
- **What**:
  - List item preview path now renders a `video` element when `isVideoPreview` is true.
  - Video list preview uses `preload="metadata"`, `muted`, `playsinline`, and `pointer-events-none` so row click behavior stays unchanged.
  - Kept the custom overlay/play affordance and increased overlay dimming from `bg-black/10` to `bg-black/15`.
  - Updated tests for `AssetsListItem`, `MediaVideoTop`, and `AssetsSidebarListView`.

## Review Focus
- Confirm list item click behavior still opens/selects asset (no inline playback interaction).
- Confirm video list previews now show actual video frame path instead of broken image fallback.

## Limitation
Backend does not currently provide a dedicated poster/thumbnail image for video outputs in the job preview payload. In the frontend today, we can either show a video icon placeholder, or load/render the full video itself to obtain a preview frame.

## Screenshots (if applicable)
<img width="427" height="499" alt="image" src="https://github.com/user-attachments/assets/3f974817-9d73-4fee-9fa5-2f1f68942c06" />
<img width="230" height="92" alt="image" src="https://github.com/user-attachments/assets/1fbfdd6a-72dd-47e2-96bf-8f7eb41c36f2" />
2026-02-21 00:59:36 -08:00

93 lines
2.4 KiB
TypeScript

import { mount } from '@vue/test-utils'
import { defineComponent } from 'vue'
import { describe, expect, it, vi } from 'vitest'
import type { OutputStackListItem } from '@/platform/assets/composables/useOutputStacks'
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
import AssetsSidebarListView from './AssetsSidebarListView.vue'
vi.mock('vue-i18n', () => ({
useI18n: () => ({
t: (key: string) => key
})
}))
vi.mock('@/stores/assetsStore', () => ({
useAssetsStore: () => ({
isAssetDeleting: () => false
})
}))
const VirtualGridStub = defineComponent({
name: 'VirtualGrid',
props: {
items: {
type: Array,
default: () => []
}
},
template:
'<div><slot v-for="item in items" :key="item.key" name="item" :item="item" /></div>'
})
const buildAsset = (id: string, name: string): AssetItem =>
({
id,
name,
tags: []
}) satisfies AssetItem
const buildOutputItem = (asset: AssetItem): OutputStackListItem => ({
key: `asset-${asset.id}`,
asset
})
const mountListView = (assetItems: OutputStackListItem[] = []) =>
mount(AssetsSidebarListView, {
props: {
assetItems,
selectableAssets: [],
isSelected: () => false,
isStackExpanded: () => false,
toggleStack: async () => {},
assetType: 'output'
},
global: {
stubs: {
VirtualGrid: VirtualGridStub
}
}
})
describe('AssetsSidebarListView', () => {
it('shows generated assets header when there are assets', () => {
const wrapper = mountListView([buildOutputItem(buildAsset('a1', 'x.png'))])
expect(wrapper.text()).toContain('sideToolbar.generatedAssetsHeader')
})
it('does not show assets header when there are no assets', () => {
const wrapper = mountListView([])
expect(wrapper.text()).not.toContain('sideToolbar.generatedAssetsHeader')
})
it('marks mp4 assets as video previews', () => {
const videoAsset = {
...buildAsset('video-asset', 'clip.mp4'),
preview_url: '/api/view/clip.mp4',
user_metadata: {}
} satisfies AssetItem
const wrapper = mountListView([buildOutputItem(videoAsset)])
const listItems = wrapper.findAllComponents({ name: 'AssetsListItem' })
const assetListItem = listItems.at(-1)
expect(assetListItem).toBeDefined()
expect(assetListItem?.props('previewUrl')).toBe('/api/view/clip.mp4')
expect(assetListItem?.props('isVideoPreview')).toBe(true)
})
})