mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
*PR Created by the Glary-Bot Agent* --- ## Summary - Replace all `as unknown as Type` assertions in 59 unit test files with type-safe `@total-typescript/shoehorn` functions - Use `fromPartial<Type>()` for partial mock objects where deep-partial type-checks (21 files) - Use `fromAny<Type>()` for fundamentally incompatible types: null, undefined, primitives, variables, class expressions, and mocks with test-specific extra properties that `PartialDeepObject` rejects (remaining files) - All explicit type parameters preserved so TypeScript return types are correct - Browser test `.spec.ts` files excluded (shoehorn unavailable in `page.evaluate` browser context) ## Verification - `pnpm typecheck` ✅ - `pnpm lint` ✅ - `pnpm format` ✅ - Pre-commit hooks passed (format + oxlint + eslint + typecheck) - Migrated test files verified passing (ran representative subset) - No test behavior changes — only type assertion syntax changed - No UI changes — screenshots not applicable ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10761-test-migrate-as-unknown-as-to-total-typescript-shoehorn-3336d73d365081f6b8adc44db5dcc380) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com>
169 lines
5.2 KiB
TypeScript
169 lines
5.2 KiB
TypeScript
import { fromPartial } from '@total-typescript/shoehorn'
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
|
import { createMockLGraphNode } from '@/utils/__tests__/litegraphTestUtils'
|
|
import { useImageMenuOptions } from './useImageMenuOptions'
|
|
|
|
vi.mock('vue-i18n', async (importOriginal) => {
|
|
const actual = await importOriginal()
|
|
return {
|
|
...(actual as object),
|
|
useI18n: () => ({
|
|
t: (key: string) => key.split('.').pop() ?? key
|
|
})
|
|
}
|
|
})
|
|
|
|
vi.mock('@/stores/commandStore', () => ({
|
|
useCommandStore: () => ({ execute: vi.fn() })
|
|
}))
|
|
|
|
function mockClipboard(clipboard: Partial<Clipboard> | undefined) {
|
|
Object.defineProperty(navigator, 'clipboard', {
|
|
value: clipboard,
|
|
writable: true,
|
|
configurable: true
|
|
})
|
|
}
|
|
|
|
function createImageNode(
|
|
overrides: Partial<LGraphNode> | Record<string, unknown> = {}
|
|
): LGraphNode {
|
|
const img = new Image()
|
|
img.src = 'http://localhost/test.png'
|
|
Object.defineProperty(img, 'naturalWidth', { value: 100 })
|
|
Object.defineProperty(img, 'naturalHeight', { value: 100 })
|
|
|
|
return createMockLGraphNode({
|
|
imgs: [img],
|
|
imageIndex: 0,
|
|
pasteFile: vi.fn(),
|
|
pasteFiles: vi.fn(),
|
|
...overrides
|
|
})
|
|
}
|
|
|
|
describe('useImageMenuOptions', () => {
|
|
afterEach(() => {
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
describe('getImageMenuOptions', () => {
|
|
it('includes Paste Image option when node supports paste', () => {
|
|
const node = createImageNode()
|
|
const { getImageMenuOptions } = useImageMenuOptions()
|
|
const options = getImageMenuOptions(node)
|
|
const labels = options.map((o) => o.label)
|
|
|
|
expect(labels).toContain('Paste Image')
|
|
})
|
|
|
|
it('excludes Paste Image option when node does not support paste', () => {
|
|
const node = createImageNode({ pasteFiles: undefined })
|
|
const { getImageMenuOptions } = useImageMenuOptions()
|
|
const options = getImageMenuOptions(node)
|
|
const labels = options.map((o) => o.label)
|
|
|
|
expect(labels).not.toContain('Paste Image')
|
|
})
|
|
|
|
it('returns empty array when node has no images and no pasteFiles', () => {
|
|
const node = createMockLGraphNode({ imgs: [] })
|
|
const { getImageMenuOptions } = useImageMenuOptions()
|
|
|
|
expect(getImageMenuOptions(node)).toEqual([])
|
|
})
|
|
|
|
it('returns only Paste Image when node has no images but supports paste', () => {
|
|
const node = createMockLGraphNode({
|
|
imgs: [],
|
|
pasteFile: vi.fn(),
|
|
pasteFiles: vi.fn()
|
|
})
|
|
const { getImageMenuOptions } = useImageMenuOptions()
|
|
const options = getImageMenuOptions(node)
|
|
const labels = options.map((o) => o.label)
|
|
|
|
expect(labels).toEqual(['Paste Image'])
|
|
})
|
|
|
|
it('places Paste Image between Copy Image and Save Image', () => {
|
|
const node = createImageNode()
|
|
const { getImageMenuOptions } = useImageMenuOptions()
|
|
const options = getImageMenuOptions(node)
|
|
const labels = options.map((o) => o.label)
|
|
|
|
const copyIdx = labels.indexOf('Copy Image')
|
|
const pasteIdx = labels.indexOf('Paste Image')
|
|
const saveIdx = labels.indexOf('Save Image')
|
|
|
|
expect(copyIdx).toBeLessThan(pasteIdx)
|
|
expect(pasteIdx).toBeLessThan(saveIdx)
|
|
})
|
|
})
|
|
|
|
describe('pasteImage action', () => {
|
|
it('reads clipboard and calls pasteFiles on the node', async () => {
|
|
const node = createImageNode()
|
|
const mockBlob = new Blob(['fake'], { type: 'image/png' })
|
|
const mockClipboardItem = {
|
|
types: ['image/png'],
|
|
getType: vi.fn().mockResolvedValue(mockBlob)
|
|
}
|
|
|
|
mockClipboard(
|
|
fromPartial<Clipboard>({
|
|
read: vi.fn().mockResolvedValue([mockClipboardItem])
|
|
})
|
|
)
|
|
|
|
const { getImageMenuOptions } = useImageMenuOptions()
|
|
const options = getImageMenuOptions(node)
|
|
const pasteOption = options.find((o) => o.label === 'Paste Image')
|
|
|
|
await pasteOption!.action!()
|
|
|
|
expect(navigator.clipboard.read).toHaveBeenCalled()
|
|
expect(node.pasteFile).toHaveBeenCalledWith(expect.any(File))
|
|
expect(node.pasteFiles).toHaveBeenCalledWith(
|
|
expect.arrayContaining([expect.any(File)])
|
|
)
|
|
})
|
|
|
|
it('handles missing clipboard API gracefully', async () => {
|
|
const node = createImageNode()
|
|
mockClipboard(fromPartial<Clipboard>({ read: undefined }))
|
|
|
|
const { getImageMenuOptions } = useImageMenuOptions()
|
|
const options = getImageMenuOptions(node)
|
|
const pasteOption = options.find((o) => o.label === 'Paste Image')
|
|
|
|
await expect(pasteOption!.action!()).resolves.toBeUndefined()
|
|
expect(node.pasteFiles).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('handles clipboard with no image data gracefully', async () => {
|
|
const node = createImageNode()
|
|
const mockClipboardItem = {
|
|
types: ['text/plain'],
|
|
getType: vi.fn()
|
|
}
|
|
|
|
mockClipboard(
|
|
fromPartial<Clipboard>({
|
|
read: vi.fn().mockResolvedValue([mockClipboardItem])
|
|
})
|
|
)
|
|
|
|
const { getImageMenuOptions } = useImageMenuOptions()
|
|
const options = getImageMenuOptions(node)
|
|
const pasteOption = options.find((o) => o.label === 'Paste Image')
|
|
|
|
await pasteOption!.action!()
|
|
|
|
expect(node.pasteFiles).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
})
|