mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +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>
211 lines
5.5 KiB
TypeScript
211 lines
5.5 KiB
TypeScript
import { fromPartial } from '@total-typescript/shoehorn'
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import type { DragAndScale } from '@/lib/litegraph/src/DragAndScale'
|
|
import {
|
|
AutoPanController,
|
|
calculateEdgePanSpeed
|
|
} from '@/renderer/core/canvas/useAutoPan'
|
|
|
|
describe('calculateEdgePanSpeed', () => {
|
|
const MAX = 15
|
|
|
|
it('returns 0 when pointer is in the center', () => {
|
|
expect(calculateEdgePanSpeed(500, 0, 1000, 1, MAX)).toBe(0)
|
|
})
|
|
|
|
it('returns negative speed near the left/top edge', () => {
|
|
const speed = calculateEdgePanSpeed(10, 0, 1000, 1, MAX)
|
|
expect(speed).toBeLessThan(0)
|
|
})
|
|
|
|
it('returns positive speed near the right/bottom edge', () => {
|
|
const speed = calculateEdgePanSpeed(990, 0, 1000, 1, MAX)
|
|
expect(speed).toBeGreaterThan(0)
|
|
})
|
|
|
|
it('returns max speed at the exact edge', () => {
|
|
const speed = calculateEdgePanSpeed(0, 0, 1000, 1, MAX)
|
|
expect(speed).toBe(-15)
|
|
})
|
|
|
|
it('returns 0 at exactly the threshold boundary', () => {
|
|
const speed = calculateEdgePanSpeed(50, 0, 1000, 1, MAX)
|
|
expect(speed).toBe(0)
|
|
})
|
|
|
|
it('scales speed linearly with edge proximity', () => {
|
|
const halfwaySpeed = calculateEdgePanSpeed(25, 0, 1000, 1, MAX)
|
|
const quarterSpeed = calculateEdgePanSpeed(37.5, 0, 1000, 1, MAX)
|
|
|
|
expect(halfwaySpeed).toBeCloseTo(-15 * 0.5)
|
|
expect(quarterSpeed).toBeCloseTo(-15 * 0.25)
|
|
})
|
|
|
|
it('divides speed by scale (zoom level)', () => {
|
|
const speedAtScale1 = calculateEdgePanSpeed(0, 0, 1000, 1, MAX)
|
|
const speedAtScale2 = calculateEdgePanSpeed(0, 0, 1000, 2, MAX)
|
|
|
|
expect(speedAtScale2).toBe(speedAtScale1 / 2)
|
|
})
|
|
|
|
it('returns max speed when pointer is outside bounds', () => {
|
|
expect(calculateEdgePanSpeed(-10, 0, 1000, 1, MAX)).toBe(-15)
|
|
expect(calculateEdgePanSpeed(1010, 0, 1000, 1, MAX)).toBe(15)
|
|
})
|
|
|
|
it('returns 0 when maxPanSpeed is 0 (disabled)', () => {
|
|
expect(calculateEdgePanSpeed(0, 0, 1000, 1, 0)).toBe(0)
|
|
expect(calculateEdgePanSpeed(10, 0, 1000, 1, 0)).toBe(0)
|
|
})
|
|
|
|
it('uses custom maxPanSpeed', () => {
|
|
const speed = calculateEdgePanSpeed(0, 0, 1000, 1, 30)
|
|
expect(speed).toBe(-30)
|
|
})
|
|
})
|
|
|
|
describe('AutoPanController', () => {
|
|
let mockCanvas: HTMLCanvasElement
|
|
let mockDs: DragAndScale
|
|
let onPanMock: ReturnType<typeof vi.fn<(dx: number, dy: number) => void>>
|
|
let controller: AutoPanController
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers()
|
|
|
|
mockCanvas = fromPartial<HTMLCanvasElement>({
|
|
getBoundingClientRect: () => ({
|
|
left: 0,
|
|
top: 0,
|
|
right: 800,
|
|
bottom: 600,
|
|
width: 800,
|
|
height: 600,
|
|
x: 0,
|
|
y: 0,
|
|
toJSON: () => {}
|
|
})
|
|
})
|
|
|
|
mockDs = fromPartial<DragAndScale>({ offset: [0, 0], scale: 1 })
|
|
|
|
onPanMock = vi.fn<(dx: number, dy: number) => void>()
|
|
controller = new AutoPanController({
|
|
canvas: mockCanvas,
|
|
ds: mockDs,
|
|
maxPanSpeed: 15,
|
|
onPan: onPanMock
|
|
})
|
|
})
|
|
|
|
afterEach(() => {
|
|
controller.stop()
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
it('does not pan when pointer is in the center', () => {
|
|
controller.updatePointer(400, 300)
|
|
controller.start()
|
|
|
|
vi.advanceTimersByTime(16)
|
|
|
|
expect(onPanMock).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('pans when pointer is near the right edge', () => {
|
|
controller.updatePointer(790, 300)
|
|
controller.start()
|
|
|
|
vi.advanceTimersByTime(16)
|
|
|
|
expect(onPanMock).toHaveBeenCalled()
|
|
const [dx, dy] = onPanMock.mock.calls[0]
|
|
expect(dx).toBeGreaterThan(0)
|
|
expect(dy).toBe(0)
|
|
})
|
|
|
|
it('pans when pointer is near the bottom edge', () => {
|
|
controller.updatePointer(400, 590)
|
|
controller.start()
|
|
|
|
vi.advanceTimersByTime(16)
|
|
|
|
expect(onPanMock).toHaveBeenCalled()
|
|
const [dx, dy] = onPanMock.mock.calls[0]
|
|
expect(dx).toBe(0)
|
|
expect(dy).toBeGreaterThan(0)
|
|
})
|
|
|
|
it('pans diagonally when pointer is near a corner', () => {
|
|
controller.updatePointer(790, 590)
|
|
controller.start()
|
|
|
|
vi.advanceTimersByTime(16)
|
|
|
|
expect(onPanMock).toHaveBeenCalled()
|
|
const [dx, dy] = onPanMock.mock.calls[0]
|
|
expect(dx).toBeGreaterThan(0)
|
|
expect(dy).toBeGreaterThan(0)
|
|
})
|
|
|
|
it('updates ds.offset when panning', () => {
|
|
controller.updatePointer(0, 300)
|
|
controller.start()
|
|
|
|
vi.advanceTimersByTime(16)
|
|
|
|
expect(mockDs.offset[0]).toBeGreaterThan(0)
|
|
expect(mockDs.offset[1]).toBe(0)
|
|
})
|
|
|
|
it('accounts for zoom level in offset changes', () => {
|
|
controller.updatePointer(0, 300)
|
|
controller.start()
|
|
|
|
vi.advanceTimersByTime(16)
|
|
const offsetAtScale1 = mockDs.offset[0]
|
|
|
|
controller.stop()
|
|
mockDs.offset[0] = 0
|
|
mockDs.scale = 2
|
|
|
|
controller.start()
|
|
vi.advanceTimersByTime(16)
|
|
const offsetAtScale2 = mockDs.offset[0]
|
|
|
|
expect(offsetAtScale2).toBeCloseTo(offsetAtScale1 / 2)
|
|
})
|
|
|
|
it('stops panning when stop() is called', () => {
|
|
controller.updatePointer(790, 300)
|
|
controller.start()
|
|
|
|
vi.advanceTimersByTime(16)
|
|
const callCount = onPanMock.mock.calls.length
|
|
expect(callCount).toBeGreaterThan(0)
|
|
|
|
controller.stop()
|
|
|
|
vi.advanceTimersByTime(16)
|
|
expect(onPanMock).toHaveBeenCalledTimes(callCount)
|
|
})
|
|
|
|
it('does not pan when maxPanSpeed is 0', () => {
|
|
const disabledController = new AutoPanController({
|
|
canvas: mockCanvas,
|
|
ds: mockDs,
|
|
maxPanSpeed: 0,
|
|
onPan: onPanMock
|
|
})
|
|
|
|
disabledController.updatePointer(0, 0)
|
|
disabledController.start()
|
|
|
|
vi.advanceTimersByTime(16)
|
|
|
|
expect(onPanMock).not.toHaveBeenCalled()
|
|
disabledController.stop()
|
|
})
|
|
})
|