Files
ComfyUI_frontend/src/renderer/core/canvas/useAutoPan.test.ts
Alexander Brown 661e3d7949 test: migrate as unknown as to @total-typescript/shoehorn (#10761)
*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>
2026-03-30 19:20:18 -07:00

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