Compare commits

...

11 Commits

Author SHA1 Message Date
dante01yoon
1407490f3b test: remove flaky SearchFilterChip E2E test
The filter chip add/remove behavior is already covered by
nodeSearchBox.spec.ts which tests removeFilter in the same
v1 (legacy) search box context.
2026-03-28 12:15:39 +09:00
dante01yoon
4e900813ea fix: replace screenshot tests with behavioral assertions in badge E2E
Screenshot tests require Linux golden images that don't exist yet.
Replace with functional assertions: filter chip add/remove and
node library tree badge visibility.
2026-03-28 12:15:39 +09:00
dante01yoon
5e4a82fa29 fix: wait for search input before adding filter in E2E tests 2026-03-28 12:15:39 +09:00
dante01yoon
444b6a4472 fix: migrate missed StatusBadge reference in TopMenuSection 2026-03-28 12:15:39 +09:00
dante01yoon
937e9b3190 test: add functional E2E test for SearchFilterChip remove button 2026-03-28 12:15:39 +09:00
dante01yoon
d512338a2b fix: use comfyPage.toast.closeToasts() instead of custom helper 2026-03-28 12:15:38 +09:00
dante01yoon
8d6a1cb251 refactor: migrate consumers from PrimeVue Badge and StatusBadge to Badge
Replace PrimeVue Badge in TreeExplorerTreeNode, UsageLogsTable, and
SearchFilterChip. Replace StatusBadge with Badge in all consumer
components. Replace PrimeVue Chip with native HTML in SearchFilterChip.
Update customerEventsService severity values to match Badge variants.
Add E2E visual regression tests for badge rendering.
2026-03-28 12:15:38 +09:00
dante01yoon
a4773708fe test: remove flaky NodePreviewCard E2E test
The text-xxxs to text-3xs rename is already validated by Badge
unit tests (twMerge regression), build success, and all other
passing E2E tests. This test was flaky because the first node
in the sampling folder may not always have inputs.
2026-03-28 12:15:38 +09:00
dante01yoon
7be65784f5 fix: match i18n text 'Inputs'/'Outputs' instead of CSS-uppercased 'INPUTS' 2026-03-28 12:15:38 +09:00
dante01yoon
06f3484694 fix: replace screenshot with behavioral assertions in text size rename test 2026-03-28 12:15:38 +09:00
dante01yoon
ffd2503dbe test: add visual regression test for text-2xs rename in NodePreviewCard 2026-03-28 12:15:38 +09:00
19 changed files with 66 additions and 110 deletions

View File

@@ -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()
}
/**

View File

@@ -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
})

View File

@@ -82,7 +82,7 @@
>
<i class="icon-[lucide--panel-right] size-4" />
</Button>
<StatusBadge
<Badge
v-if="showErrorIndicatorOnPanelButton"
variant="dot"
severity="danger"
@@ -141,7 +141,7 @@ import ErrorOverlay from '@/components/error/ErrorOverlay.vue'
import ActionBarButtons from '@/components/topbar/ActionBarButtons.vue'
import CurrentUserButton from '@/components/topbar/CurrentUserButton.vue'
import LoginButton from '@/components/topbar/LoginButton.vue'
import StatusBadge from '@/components/common/StatusBadge.vue'
import Badge from '@/components/common/Badge.vue'
import Button from '@/components/ui/button/Button.vue'
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
import { useQueueFeatureFlags } from '@/composables/queue/useQueueFeatureFlags'

View File

@@ -44,7 +44,7 @@ const renderActionbar = (showRunProgressBar: boolean) => {
name: 'Panel',
template: '<div><slot /></div>'
},
StatusBadge: true,
Badge: true,
ComfyRunButton: {
name: 'ComfyRunButton',
template: '<button type="button">Run</button>'

View File

@@ -59,7 +59,7 @@
<span class="text-sm font-normal tabular-nums">
{{ activeJobsLabel }}
</span>
<StatusBadge
<Badge
v-if="activeJobsCount > 0"
data-testid="active-jobs-indicator"
variant="dot"
@@ -104,7 +104,7 @@ import { computed, nextTick, ref, watch } from 'vue'
import type { ComponentPublicInstance } from 'vue'
import { useI18n } from 'vue-i18n'
import StatusBadge from '@/components/common/StatusBadge.vue'
import Badge from '@/components/common/Badge.vue'
import QueueInlineProgress from '@/components/queue/QueueInlineProgress.vue'
import Button from '@/components/ui/button/Button.vue'
import { useQueueFeatureFlags } from '@/composables/queue/useQueueFeatureFlags'

View File

@@ -1,17 +1,28 @@
<template>
<Chip removable @remove="emit('remove', $event)">
<Badge size="small" :class="semanticBadgeClass">
{{ badge }}
</Badge>
<span
class="inline-flex items-center gap-1 rounded-2xl bg-surface-700 py-0.5 pr-1 pl-2 text-xs"
>
<Badge :label="badge" :class="semanticBadgeClass" />
{{ text }}
</Chip>
<button
type="button"
:aria-label="$t('g.remove')"
class="inline-flex cursor-pointer items-center justify-center rounded-full p-0.5 hover:bg-surface-600"
@click="emit('remove', $event)"
>
<i
class="icon-[lucide--x] size-3 text-muted-foreground"
aria-hidden="true"
/>
</button>
</span>
</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'
export interface SearchFilter {
text: string
badge: string

View File

@@ -1,36 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue'
import { cn } from '@/utils/tailwindUtil'
import { statusBadgeVariants } from './statusBadge.variants'
import type { StatusBadgeVariants } from './statusBadge.variants'
const {
label,
severity = 'default',
variant,
class: className
} = defineProps<{
label?: string | number
severity?: StatusBadgeVariants['severity']
variant?: StatusBadgeVariants['variant']
class?: string
}>()
const badgeClass = computed(() =>
cn(
statusBadgeVariants({
severity,
variant: variant ?? (label == null ? 'dot' : 'label')
}),
className
)
)
</script>
<template>
<span :class="badgeClass">
{{ label }}
</span>
</template>

View File

@@ -1,6 +1,6 @@
import { createTestingPinia } from '@pinia/testing'
import { fireEvent, render, screen } from '@testing-library/vue'
import Badge from 'primevue/badge'
import Badge from '@/components/common/Badge.vue'
import PrimeVue from 'primevue/config'
import InputText from 'primevue/inputtext'
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'

View File

@@ -23,9 +23,10 @@
</span>
<Badge
v-if="showNodeBadgeText"
:value="nodeBadgeText"
:label="nodeBadgeText"
severity="secondary"
class="leaf-count-badge"
:variant="nodeBadgeText.length > 1 ? 'label' : 'circle'"
class="ml-2"
/>
</div>
<div
@@ -38,7 +39,7 @@
<script setup lang="ts" generic="T">
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview'
import Badge from 'primevue/badge'
import Badge from '@/components/common/Badge.vue'
import { computed, inject, ref } from 'vue'
import EditableText from '@/components/common/EditableText.vue'
@@ -146,9 +147,6 @@ if (props.node.droppable) {
align-items: center;
justify-content: space-between;
}
.leaf-count-badge {
margin-left: 0.5rem;
}
.node-content {
display: flex;
align-items: center;

View File

@@ -1,26 +0,0 @@
import type { VariantProps } from 'cva'
import { cva } from 'cva'
export const statusBadgeVariants = cva({
base: 'inline-flex items-center justify-center rounded-full',
variants: {
severity: {
default: 'bg-primary-background text-base-foreground',
secondary: 'bg-secondary-background text-base-foreground',
warn: 'bg-warning-background text-base-background',
danger: 'bg-destructive-background text-white',
contrast: 'bg-base-foreground text-base-background'
},
variant: {
label: 'h-3.5 px-1 text-3xs font-semibold uppercase',
dot: 'size-2',
circle: 'size-3.5 text-3xs font-semibold'
}
},
defaultVariants: {
severity: 'default',
variant: 'label'
}
})
export type StatusBadgeVariants = VariantProps<typeof statusBadgeVariants>

View File

@@ -1,6 +1,6 @@
import { createTestingPinia } from '@pinia/testing'
import { mount } from '@vue/test-utils'
import Badge from 'primevue/badge'
import Badge from '@/components/common/Badge.vue'
import Button from '@/components/ui/button/Button.vue'
import Column from 'primevue/column'
import PrimeVue from 'primevue/config'
@@ -125,13 +125,13 @@ describe('UsageLogsTable', () => {
mockCustomerEventsService.getEventSeverity.mockImplementation((type) => {
switch (type) {
case EventType.CREDIT_ADDED:
return 'success'
return 'default'
case EventType.ACCOUNT_CREATED:
return 'info'
return 'secondary'
case EventType.API_USAGE_COMPLETED:
return 'warning'
return 'warn'
default:
return 'info'
return 'secondary'
}
})
mockCustomerEventsService.formatAmount.mockImplementation((amount) => {

View File

@@ -20,7 +20,7 @@
<Column field="event_type" :header="$t('credits.eventType')">
<template #body="{ data }">
<Badge
:value="customerEventService.formatEventType(data.event_type)"
:label="customerEventService.formatEventType(data.event_type)"
:severity="customerEventService.getEventSeverity(data.event_type)"
/>
</template>
@@ -91,7 +91,7 @@
</template>
<script setup lang="ts">
import Badge from 'primevue/badge'
import Badge from '@/components/common/Badge.vue'
import Column from 'primevue/column'
import DataTable from 'primevue/datatable'
import Message from 'primevue/message'

View File

@@ -3,7 +3,7 @@ import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import Loader from '@/components/loader/Loader.vue'
import StatusBadge from '@/components/common/StatusBadge.vue'
import Badge from '@/components/common/Badge.vue'
import type { AssetDownload } from '@/stores/assetDownloadStore'
import { cn } from '@/utils/tailwindUtil'
@@ -40,11 +40,11 @@ const isPending = computed(() => job.status === 'created')
<i
class="icon-[lucide--circle-alert] size-4 text-destructive-background"
/>
<StatusBadge :label="t('progressToast.failed')" severity="danger" />
<Badge :label="t('progressToast.failed')" severity="danger" />
</template>
<template v-else-if="isCompleted">
<StatusBadge :label="t('progressToast.finished')" severity="contrast" />
<Badge :label="t('progressToast.finished')" severity="contrast" />
</template>
<template v-else-if="isRunning">

View File

@@ -20,7 +20,7 @@
<span ref="textRef" class="min-w-0 truncate">
<slot />
</span>
<StatusBadge
<Badge
v-if="badge !== undefined"
:label="String(badge)"
severity="contrast"
@@ -33,7 +33,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import StatusBadge from '@/components/common/StatusBadge.vue'
import Badge from '@/components/common/Badge.vue'
import type { NavItemData } from '@/types/navTypes'
import NavIcon from './NavIcon.vue'

View File

@@ -119,9 +119,10 @@
@click.stop="handleSelect"
>
{{ $t('g.use') }}
<StatusBadge
<Badge
v-if="isNewlyImported"
severity="contrast"
variant="dot"
class="absolute -top-0.5 -right-0.5"
/>
</Button>
@@ -137,7 +138,7 @@ import { useI18n } from 'vue-i18n'
import IconGroup from '@/components/button/IconGroup.vue'
import MoreButton from '@/components/button/MoreButton.vue'
import StatusBadge from '@/components/common/StatusBadge.vue'
import Badge from '@/components/common/Badge.vue'
import { showConfirmDialog } from '@/components/dialog/confirm/confirmDialog'
import Button from '@/components/ui/button/Button.vue'
import AssetBadgeGroup from '@/platform/assets/components/AssetBadgeGroup.vue'

View File

@@ -28,7 +28,7 @@
"
@click="$emit('stepClick', step.name)"
>
<StatusBadge
<Badge
:label="step.number"
variant="circle"
severity="contrast"
@@ -67,7 +67,7 @@ import { computed } from 'vue'
import { vAutoAnimate } from '@formkit/auto-animate/vue'
import StatusBadge from '@/components/common/StatusBadge.vue'
import Badge from '@/components/common/Badge.vue'
import Button from '@/components/ui/button/Button.vue'
import type { ComfyHubPublishStep } from '@/platform/workflow/sharing/composables/useComfyHubPublishWizard'
import { cn } from '@/utils/tailwindUtil'

View File

@@ -80,7 +80,7 @@
<span class="text-sm font-bold text-text-primary">
{{ subscriptionTierName }}
</span>
<StatusBadge
<Badge
v-if="isCancelled"
:label="$t('subscription.canceled')"
severity="warn"
@@ -365,7 +365,7 @@ import { useI18n } from 'vue-i18n'
import { useToast } from 'primevue/usetoast'
import StatusBadge from '@/components/common/StatusBadge.vue'
import Badge from '@/components/common/Badge.vue'
import Button from '@/components/ui/button/Button.vue'
import { useBillingContext } from '@/composables/billing/useBillingContext'
import { useBillingOperationStore } from '@/platform/workspace/stores/billingOperationStore'

View File

@@ -216,15 +216,17 @@ describe('useCustomerEventsService', () => {
describe('getEventSeverity', () => {
it('should return correct severity for known event types', () => {
expect(service.getEventSeverity(EventType.CREDIT_ADDED)).toBe('success')
expect(service.getEventSeverity(EventType.ACCOUNT_CREATED)).toBe('info')
expect(service.getEventSeverity(EventType.CREDIT_ADDED)).toBe('default')
expect(service.getEventSeverity(EventType.ACCOUNT_CREATED)).toBe(
'secondary'
)
expect(service.getEventSeverity(EventType.API_USAGE_COMPLETED)).toBe(
'warning'
'warn'
)
})
it('should return default severity for unknown event types', () => {
expect(service.getEventSeverity('unknown_event')).toBe('info')
expect(service.getEventSeverity('unknown_event')).toBe('secondary')
})
})

View File

@@ -2,6 +2,7 @@ import type { AxiosError, AxiosResponse } from 'axios'
import axios from 'axios'
import { ref, watch } from 'vue'
import type { BadgeVariants } from '@/components/common/badge.variants'
import { getComfyApiBaseUrl } from '@/config/comfyApi'
import { d } from '@/i18n'
import { useAuthStore } from '@/stores/authStore'
@@ -134,16 +135,18 @@ export const useCustomerEventsService = () => {
return value
}
function getEventSeverity(eventType: string) {
function getEventSeverity(
eventType: string
): NonNullable<BadgeVariants['severity']> {
switch (eventType) {
case 'credit_added':
return 'success'
return 'default'
case 'account_created':
return 'info'
return 'secondary'
case 'api_usage_completed':
return 'warning'
return 'warn'
default:
return 'info'
return 'secondary'
}
}