LoVisual/frontend/src/features/account/tests/cropMath.test.ts
loki5512344 e0531eda5e
fix(frontend): session cookie path, browser-language default, avatar crop, EN link previews, topbar logo
- nginx: rewrite the refresh cookie's Path=/auth to /api/auth on the
  production proxy — without this the browser never sent the cookie
  back on POST /api/auth/refresh and the session was lost on reload
- i18n: fallbackLng ru -> en, so only an actual ru browser locale
  defaults to Russian, everything else defaults to English
- index.html: explicit English og:*/twitter:* tags so link previews
  in Discord/etc. don't fall back to the (Russian) meta description
- Avatar upload: pick -> circular crop/reposition/zoom preview ->
  confirm -> upload, instead of uploading the raw file blind
- Topbar: logo pill background removed, pixel-heart mark added next
  to the LoVisual wordmark
- Fixed two tests left stale by the earlier tagline copy change

Claude-Session: https://claude.ai/code/session_01F1M1Jic1wTSn4igUENynmZ
2026-09-28 22:23:28 +02:00

54 lines
2.1 KiB
TypeScript

import { expect, test } from 'vitest'
import { clampPan, clampZoom, coverScale, sourceRect } from '../components/cropMath'
test('coverScale scales the shorter side to fill the viewport', () => {
expect(coverScale(1000, 2000, 200)).toBeCloseTo(0.2)
expect(coverScale(2000, 1000, 200)).toBeCloseTo(0.2)
})
test('clampZoom keeps zoom within [1, 4]', () => {
expect(clampZoom(0.5)).toBe(1)
expect(clampZoom(10)).toBe(4)
expect(clampZoom(2)).toBe(2)
})
test('clampPan keeps a square image centered at zoom 1 (no room to pan)', () => {
const clamped = clampPan({ x: 50, y: 50 }, 400, 400, 200, 1)
expect(clamped).toEqual({ x: 0, y: 0 })
})
test('clampPan allows panning up to the overflow at higher zoom', () => {
// 400x400 image, viewport 200, zoom 2 -> scale = 0.5*2 = 1, scaledSize 400, overflow (400-200)/2=100
const clamped = clampPan({ x: 1000, y: -1000 }, 400, 400, 200, 2)
expect(clamped).toEqual({ x: 100, y: -100 })
})
test('clampPan never lets the image show empty space (overflow stays >= 0)', () => {
const clamped = clampPan({ x: 5, y: 5 }, 300, 900, 240, 1)
// shorter side (300) covers the viewport exactly at zoom 1, so no pan room on x
expect(clamped.x).toBe(0)
// long side has plenty of room
expect(Math.abs(clamped.y)).toBeLessThanOrEqual(5)
})
test('sourceRect at zoom 1 with no offset centers the full covering square', () => {
const rect = sourceRect(400, 400, 200, 1, { x: 0, y: 0 })
expect(rect.sx).toBeCloseTo(0)
expect(rect.sy).toBeCloseTo(0)
expect(rect.sw).toBeCloseTo(400)
expect(rect.sh).toBeCloseTo(400)
})
test('sourceRect shrinks the source square as zoom increases', () => {
const rect = sourceRect(400, 400, 200, 2, { x: 0, y: 0 })
expect(rect.sw).toBeCloseTo(200)
expect(rect.sh).toBeCloseTo(200)
expect(rect.sx).toBeCloseTo(100)
expect(rect.sy).toBeCloseTo(100)
})
test('sourceRect shifts opposite the pan offset', () => {
const zoomed = sourceRect(400, 400, 200, 2, { x: 50, y: 0 })
// positive offset.x pans the image right on screen -> source window moves left
expect(zoomed.sx).toBeLessThan(100)
})