Files
DiffLens/src/main/windowState.test.ts
T

100 lines
3.8 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { parseState, clampBounds, MIN_WIDTH, MIN_HEIGHT, type Rect } from './windowState'
/** 1920×1040 工作区(常见全高清屏去掉任务栏) */
const work: Rect = { x: 0, y: 0, width: 1920, height: 1040 }
describe('parseState', () => {
it('空输入回退空状态', () => {
expect(parseState(null)).toEqual({})
expect(parseState(undefined)).toEqual({})
expect(parseState('')).toEqual({})
})
it('非法 JSON 回退空状态', () => {
expect(parseState('{broken json!!')).toEqual({})
})
it('合法 bounds 与 lastDir 被保留', () => {
const s = parseState(
JSON.stringify({ bounds: { x: 10, y: 20, width: 1000, height: 700 }, lastDir: 'C:/docs' })
)
expect(s.bounds).toEqual({ x: 10, y: 20, width: 1000, height: 700 })
expect(s.lastDir).toBe('C:/docs')
})
it('bounds 字段类型异常或缺失时被忽略', () => {
expect(parseState(JSON.stringify({ bounds: { x: 'a', y: 2, width: 1000, height: 700 } })).bounds).toBeUndefined()
expect(parseState(JSON.stringify({ bounds: { x: 1, y: 2, width: 1000 } })).bounds).toBeUndefined()
expect(parseState(JSON.stringify({ bounds: null })).bounds).toBeUndefined()
})
it('bounds 含 NaN/Infinity 等非有限数值时被忽略', () => {
const s = parseState(
'{"bounds":{"x":NaN,"y":2,"width":1000,"height":700}}'
)
expect(s.bounds).toBeUndefined()
})
it('lastDir 非字符串或空串时被忽略', () => {
expect(parseState(JSON.stringify({ lastDir: 42 })).lastDir).toBeUndefined()
expect(parseState(JSON.stringify({ lastDir: '' })).lastDir).toBeUndefined()
})
})
describe('clampBounds', () => {
it('工作区内合法 bounds 原样保留', () => {
const b = { x: 100, y: 50, width: 1280, height: 820 }
expect(clampBounds(b, work)).toEqual(b)
})
it('小于最小尺寸时钳制到最小值', () => {
const c = clampBounds({ x: 100, y: 100, width: 400, height: 300 }, work)
expect(c.width).toBe(MIN_WIDTH)
expect(c.height).toBe(MIN_HEIGHT)
})
it('超出工作区尺寸时封顶到工作区大小', () => {
const c = clampBounds({ x: 0, y: 0, width: 4000, height: 3000 }, work)
expect(c.width).toBe(work.width)
expect(c.height).toBe(work.height)
})
it('窗口移出屏幕左侧时拉回(右边缘至少 60px 进入工作区)', () => {
const c = clampBounds({ x: -2000, y: 100, width: 1000, height: 700 }, work)
expect(c.x).toBe(work.x - 1000 + 60)
})
it('窗口移出屏幕右侧时拉回(左边缘至少 60px 进入工作区)', () => {
const c = clampBounds({ x: 5000, y: 100, width: 1000, height: 700 }, work)
expect(c.x).toBe(work.x + work.width - 60)
})
it('窗口移出屏幕下方时拉回', () => {
const c = clampBounds({ x: 100, y: 5000, width: 1000, height: 700 }, work)
expect(c.y).toBe(work.y + work.height - 60)
})
it('带偏移的工作区(多显示器副屏)同样钳制', () => {
const side: Rect = { x: 1920, y: 200, width: 1080, height: 900 }
const c = clampBounds({ x: 0, y: 0, width: 1000, height: 700 }, side)
// 左边缘至少 60px 进入副屏
expect(c.x).toBe(side.x - 1000 + 60)
})
it('非整数尺寸与坐标被取整', () => {
const c = clampBounds({ x: 10.6, y: 20.2, width: 1000.7, height: 700.1 }, work)
expect(Number.isInteger(c.x)).toBe(true)
expect(Number.isInteger(c.y)).toBe(true)
expect(Number.isInteger(c.width)).toBe(true)
expect(Number.isInteger(c.height)).toBe(true)
})
it('工作区小于最小尺寸时尺寸仍保持最小值', () => {
const tiny: Rect = { x: 0, y: 0, width: 800, height: 500 }
const c = clampBounds({ x: 0, y: 0, width: 1000, height: 700 }, tiny)
expect(c.width).toBe(MIN_WIDTH)
expect(c.height).toBe(MIN_HEIGHT)
})
})