Files
ComfyUI_frontend/src/components/templates/thumbnails/BaseThumbnail.vue
Alexander Brown db1257fdb3 test: migrate 8 hard-case component tests from VTU to VTL (Phase 3) (#10493)
## Summary

Phase 3 of the VTL migration: migrate 8 hard-case component tests from
@vue/test-utils to @testing-library/vue (68 tests).

Stacked on #10490.

## Changes

- **What**: Migrate SignInForm, CurrentUserButton, NodeSearchBoxPopover,
BaseThumbnail, JobAssetsList, SelectionToolbox, QueueOverlayExpanded,
PackVersionSelectorPopover from VTU to VTL
- **`wrapper.vm` elimination**: 13 instances across 4 files (5 in
SignInForm, 3 in CurrentUserButton, 3 in PackVersionSelectorPopover, 2
in BaseThumbnail) replaced with user interactions or removed
- **`vm.$emit()` on stubs**: Interactive stubs with `setup(_, { emit })`
expose buttons or closure-based emit functions (QueueOverlayExpanded,
NodeSearchBoxPopover, JobAssetsList)
- **Removed**: 6 change-detector/redundant tests, 3 `@ts-expect-error`
annotations, `PackVersionSelectorVM` interface, `getVM` helper
- **BaseThumbnail**: Removed `useEventListener` mock — real event
handler attaches, `fireEvent.error(img)` triggers error state

## Review Focus

- Interactive stub patterns: `JobAssetsListStub` and `NodeSearchBoxStub`
use closure-based emit functions to trigger parent event handlers
without `vm.$emit`
- SignInForm form submission test fills PrimeVue Form fields via
`userEvent.type` and submits via button click (replaces `vm.onSubmit()`
direct call)
- CurrentUserButton Popover stub tracks open/close state reactively
- JobAssetsList: file-level `eslint-disable` for
`no-container`/`no-node-access`/`prefer-user-event` since stubs lack
ARIA roles and hover tests need `fireEvent`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10493-test-migrate-8-hard-case-component-tests-from-VTU-to-VTL-Phase-3-32e6d73d365081f88097df634606d7e3)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-03-27 12:37:09 -07:00

52 lines
1.3 KiB
Vue

<template>
<div
class="relative aspect-square w-full overflow-hidden rounded-t-lg select-none"
>
<div
v-if="!error"
ref="contentRef"
data-testid="thumbnail-content"
class="size-full transform-gpu transition-transform duration-1000 ease-out"
:style="
isHovered ? { transform: `scale(${1 + hoverZoom / 100})` } : undefined
"
>
<slot />
</div>
<div v-else class="flex size-full items-center justify-center">
<img
src="/assets/images/default-template.png"
draggable="false"
class="size-full transform-gpu object-cover transition-transform duration-300 ease-out"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { useEventListener } from '@vueuse/core'
import { onMounted, ref } from 'vue'
const error = ref(false)
const contentRef = ref<HTMLElement | null>(null)
const { hoverZoom = 4 } = defineProps<{
hoverZoom?: number
isHovered?: boolean
}>()
onMounted(() => {
const images = Array.from(contentRef.value?.getElementsByTagName('img') ?? [])
images.forEach((img) => {
useEventListener(img, 'error', () => {
error.value = true
})
})
})
</script>
<style scoped>
img {
transition: transform 1s cubic-bezier(0.2, 0, 0.4, 1);
}
</style>