mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-23 15:59:47 +00:00
Compare commits
11 Commits
test/image
...
feature/mi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5a669ff0d | ||
|
|
2266827248 | ||
|
|
614f482c18 | ||
|
|
e4286aabf3 | ||
|
|
ed14722fe2 | ||
|
|
69e35c00f3 | ||
|
|
7721a78087 | ||
|
|
ec19b49cc3 | ||
|
|
0d3f272e6a | ||
|
|
5319441b24 | ||
|
|
921226f79f |
@@ -85,7 +85,10 @@ export class ComfyNodeSearchBox {
|
||||
}
|
||||
|
||||
async removeFilter(index: number) {
|
||||
await this.filterChips.nth(index).locator('.p-chip-remove-icon').click()
|
||||
await this.filterChips
|
||||
.nth(index)
|
||||
.getByRole('button', { name: 'Remove' })
|
||||
.click()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,7 +110,7 @@ test.describe('Node search box', { tag: '@node' }, () => {
|
||||
async ({ comfyPage }) => {
|
||||
await comfyPage.canvasOps.disconnectEdge()
|
||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||
await comfyPage.page.locator('.p-chip-remove-icon').click()
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler', {
|
||||
exact: true
|
||||
})
|
||||
|
||||
61
src/components/common/SearchFilterChip.test.ts
Normal file
61
src/components/common/SearchFilterChip.test.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { render, screen } from '@testing-library/vue'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import SearchFilterChip from './SearchFilterChip.vue'
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
messages: { en: { g: { remove: 'Remove' } } }
|
||||
})
|
||||
|
||||
function renderChip(
|
||||
props: { text: string; badge: string; badgeClass: string },
|
||||
onRemove?: (...args: unknown[]) => void
|
||||
) {
|
||||
return render(SearchFilterChip, {
|
||||
props: { ...props, ...(onRemove ? { onRemove } : {}) },
|
||||
global: { plugins: [i18n] }
|
||||
})
|
||||
}
|
||||
|
||||
describe('SearchFilterChip', () => {
|
||||
it('renders badge and text', () => {
|
||||
renderChip({ text: 'MODEL', badge: 'I', badgeClass: 'i-badge' })
|
||||
expect(screen.getByText('MODEL')).toBeInTheDocument()
|
||||
expect(screen.getByText('I')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('applies semantic badge class for input type', () => {
|
||||
renderChip({ text: 'CLIP', badge: 'I', badgeClass: 'i-badge' })
|
||||
const badge = screen.getByText('I')
|
||||
expect(badge.className).toContain('bg-green-500')
|
||||
})
|
||||
|
||||
it('applies semantic badge class for output type', () => {
|
||||
renderChip({ text: 'IMAGE', badge: 'O', badgeClass: 'o-badge' })
|
||||
const badge = screen.getByText('O')
|
||||
expect(badge.className).toContain('bg-red-500')
|
||||
})
|
||||
|
||||
it('shows remove button and emits remove on click', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onRemove = vi.fn()
|
||||
renderChip({ text: 'MODEL', badge: 'I', badgeClass: 'i-badge' }, onRemove)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Remove' }))
|
||||
expect(onRemove).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('falls back to raw badgeClass when no semantic mapping', () => {
|
||||
renderChip({
|
||||
text: 'CUSTOM',
|
||||
badge: 'X',
|
||||
badgeClass: 'custom-class'
|
||||
})
|
||||
const badge = screen.getByText('X')
|
||||
expect(badge.className).toContain('custom-class')
|
||||
})
|
||||
})
|
||||
@@ -1,17 +1,23 @@
|
||||
<template>
|
||||
<Chip removable @remove="emit('remove', $event)">
|
||||
<Badge size="small" :class="semanticBadgeClass">
|
||||
{{ badge }}
|
||||
</Badge>
|
||||
{{ text }}
|
||||
</Chip>
|
||||
<Tag
|
||||
:label="text"
|
||||
shape="rounded"
|
||||
removable
|
||||
class="bg-surface-700"
|
||||
@remove="emit('remove', $event)"
|
||||
>
|
||||
<template #icon>
|
||||
<Badge :label="badge" :class="semanticBadgeClass" />
|
||||
</template>
|
||||
</Tag>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Badge from 'primevue/badge'
|
||||
import Chip from 'primevue/chip'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import Tag from '@/components/chip/Tag.vue'
|
||||
|
||||
export interface SearchFilter {
|
||||
text: string
|
||||
badge: string
|
||||
|
||||
@@ -37,18 +37,17 @@
|
||||
:value="formatNumberWithSuffix(nodeFrequency, { roundToInt: true })"
|
||||
severity="secondary"
|
||||
/>
|
||||
<Chip
|
||||
<ChipTag
|
||||
v-if="nodeDef.nodeSource.type !== NodeSourceType.Unknown"
|
||||
:label="nodeDef.nodeSource.displayText"
|
||||
class="text-sm font-light"
|
||||
>
|
||||
{{ nodeDef.nodeSource.displayText }}
|
||||
</Chip>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Chip from 'primevue/chip'
|
||||
import ChipTag from '@/components/chip/Tag.vue'
|
||||
import Tag from 'primevue/tag'
|
||||
import { computed } from 'vue'
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { render, screen } from '@testing-library/vue'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import { DownloadStatus } from '@comfyorg/comfyui-electron-types'
|
||||
|
||||
import type { ElectronDownload } from '@/stores/electronDownloadStore'
|
||||
|
||||
import DownloadItem from './DownloadItem.vue'
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
messages: {
|
||||
en: {
|
||||
g: { remove: 'Remove' },
|
||||
electronFileDownload: {
|
||||
cancelled: 'Cancelled',
|
||||
pause: 'Pause',
|
||||
resume: 'Resume',
|
||||
cancel: 'Cancel'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function renderDownloadItem(download: ElectronDownload) {
|
||||
return render(DownloadItem, {
|
||||
props: { download },
|
||||
global: {
|
||||
plugins: [createTestingPinia(), i18n],
|
||||
stubs: {
|
||||
ProgressBar: true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
describe('DownloadItem', () => {
|
||||
it('shows cancelled tag with remove button for cancelled downloads', () => {
|
||||
renderDownloadItem({
|
||||
url: 'http://example.com/model.bin',
|
||||
filename: 'model.bin',
|
||||
savePath: '/models/checkpoints/model.bin',
|
||||
status: DownloadStatus.CANCELLED
|
||||
})
|
||||
|
||||
expect(screen.getByText('Cancelled')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Remove' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows cancelled tag for error downloads', () => {
|
||||
renderDownloadItem({
|
||||
url: 'http://example.com/model.bin',
|
||||
filename: 'model.bin',
|
||||
savePath: '/models/checkpoints/model.bin',
|
||||
status: DownloadStatus.ERROR
|
||||
})
|
||||
|
||||
expect(screen.getByText('Cancelled')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not show cancelled tag for in-progress downloads', () => {
|
||||
renderDownloadItem({
|
||||
url: 'http://example.com/model.bin',
|
||||
filename: 'model.bin',
|
||||
savePath: '/models/checkpoints/model.bin',
|
||||
status: DownloadStatus.IN_PROGRESS,
|
||||
progress: 0.5
|
||||
})
|
||||
|
||||
expect(screen.queryByText('Cancelled')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('displays file path label', () => {
|
||||
renderDownloadItem({
|
||||
url: 'http://example.com/model.bin',
|
||||
filename: 'model.bin',
|
||||
savePath: '/models/checkpoints/model.bin',
|
||||
status: DownloadStatus.CANCELLED
|
||||
})
|
||||
|
||||
expect(screen.getByText('checkpoints/model.bin')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -4,13 +4,12 @@
|
||||
{{ getDownloadLabel(download.savePath ?? '') }}
|
||||
</div>
|
||||
<div v-if="['cancelled', 'error'].includes(download.status ?? '')">
|
||||
<Chip
|
||||
class="mt-2 h-6 bg-red-700 text-sm font-light"
|
||||
<Tag
|
||||
:label="t('electronFileDownload.cancelled')"
|
||||
class="mt-2 bg-red-700 text-sm font-light"
|
||||
removable
|
||||
@remove="handleRemoveDownload"
|
||||
>
|
||||
{{ t('electronFileDownload.cancelled') }}
|
||||
</Chip>
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
@@ -67,8 +66,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Chip from 'primevue/chip'
|
||||
import ProgressBar from 'primevue/progressbar'
|
||||
|
||||
import Tag from '@/components/chip/Tag.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
|
||||
Reference in New Issue
Block a user