Files
ComfyUI_frontend/src/components/queue/QueueOverlayExpanded.test.ts
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

106 lines
2.9 KiB
TypeScript

import { render, screen } from '@testing-library/vue'
import userEvent from '@testing-library/user-event'
import { defineComponent } from 'vue'
import { describe, expect, it, vi } from 'vitest'
import type { JobListItem } from '@/composables/queue/useJobList'
vi.mock('@/composables/queue/useJobMenu', () => ({
useJobMenu: () => ({ jobMenuEntries: [] })
}))
vi.mock('@/composables/useErrorHandling', () => ({
useErrorHandling: () => ({
wrapWithErrorHandlingAsync: <T extends (...args: never[]) => unknown>(
fn: T
) => fn
})
}))
import QueueOverlayExpanded from '@/components/queue/QueueOverlayExpanded.vue'
const QueueOverlayHeaderStub = {
template: '<div />'
}
const JobFiltersBarStub = {
template: '<div />'
}
const testJob: JobListItem = {
id: 'job-1',
title: 'Job 1',
meta: 'meta',
state: 'pending'
}
const JobAssetsListStub = defineComponent({
name: 'JobAssetsList',
setup(_, { emit }) {
return {
triggerCancel: () => emit('cancel-item', testJob),
triggerDelete: () => emit('delete-item', testJob),
triggerView: () => emit('view-item', testJob)
}
},
template: `
<div class="job-assets-list-stub">
<button data-testid="stub-cancel" @click="triggerCancel()" />
<button data-testid="stub-delete" @click="triggerDelete()" />
<button data-testid="stub-view" @click="triggerView()" />
</div>
`
})
const JobContextMenuStub = {
template: '<div />'
}
const defaultProps = {
headerTitle: 'Jobs',
queuedCount: 1,
selectedJobTab: 'All' as const,
selectedWorkflowFilter: 'all' as const,
selectedSortMode: 'mostRecent' as const,
displayedJobGroups: [],
hasFailedJobs: false
}
const stubs = {
QueueOverlayHeader: QueueOverlayHeaderStub,
JobFiltersBar: JobFiltersBarStub,
JobAssetsList: JobAssetsListStub,
JobContextMenu: JobContextMenuStub
}
describe('QueueOverlayExpanded', () => {
it('renders JobAssetsList', () => {
const { container } = render(QueueOverlayExpanded, {
props: defaultProps,
global: { stubs }
})
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
expect(container.querySelector('.job-assets-list-stub')).toBeTruthy()
})
it('re-emits list item actions from JobAssetsList', async () => {
const user = userEvent.setup()
const onCancelItem = vi.fn<(item: JobListItem) => void>()
const onDeleteItem = vi.fn<(item: JobListItem) => void>()
const onViewItem = vi.fn<(item: JobListItem) => void>()
render(QueueOverlayExpanded, {
props: { ...defaultProps, onCancelItem, onDeleteItem, onViewItem },
global: { stubs }
})
await user.click(screen.getByTestId('stub-cancel'))
await user.click(screen.getByTestId('stub-delete'))
await user.click(screen.getByTestId('stub-view'))
expect(onCancelItem).toHaveBeenCalledWith(testJob)
expect(onDeleteItem).toHaveBeenCalledWith(testJob)
expect(onViewItem).toHaveBeenCalledWith(testJob)
})
})