|
1 | 1 | import { describe, expect, test } from 'vitest' |
2 | 2 | import { createGestureLock, resetLock, tryLock } from '../src/gestures/lock' |
3 | 3 | import { clampFontSize, touchDistance } from '../src/gestures/pinch' |
4 | | -import { averageY, pageSeq, scrollSeq } from '../src/gestures/scroll' |
| 4 | +import { averageY, pageSeq, scrollSeq, terminalGrid, touchToCell } from '../src/gestures/scroll' |
5 | 5 | import { isValidSwipe } from '../src/gestures/swipe' |
| 6 | +import { mockTerminal } from './fixtures' |
6 | 7 |
|
7 | 8 | describe('isValidSwipe', () => { |
8 | 9 | const config = { |
@@ -185,3 +186,100 @@ describe('pageSeq', () => { |
185 | 186 | expect(source).toContain('\\x3c${code};${x};${y}M') |
186 | 187 | }) |
187 | 188 | }) |
| 189 | + |
| 190 | +describe('terminalGrid', () => { |
| 191 | + test('returns cols/rows from terminal when available', () => { |
| 192 | + const term = { ...mockTerminal(), cols: 120, rows: 40 } |
| 193 | + const rect = { width: 800, height: 600 } as DOMRect |
| 194 | + expect(terminalGrid(rect, term)).toEqual({ cols: 120, rows: 40 }) |
| 195 | + }) |
| 196 | + |
| 197 | + test('rounds non-integer cols/rows', () => { |
| 198 | + const term = { ...mockTerminal(), cols: 79.6, rows: 23.4 } |
| 199 | + const rect = { width: 800, height: 600 } as DOMRect |
| 200 | + expect(terminalGrid(rect, term)).toEqual({ cols: 80, rows: 23 }) |
| 201 | + }) |
| 202 | + |
| 203 | + test('falls back to 80x24 when term has no cols/rows and no measure element', () => { |
| 204 | + const term = mockTerminal() |
| 205 | + const rect = { width: 800, height: 600 } as DOMRect |
| 206 | + expect(terminalGrid(rect, term)).toEqual({ cols: 80, rows: 24 }) |
| 207 | + }) |
| 208 | + |
| 209 | + test('falls back to 80x24 when cols/rows are zero', () => { |
| 210 | + const term = { ...mockTerminal(), cols: 0, rows: 0 } |
| 211 | + const rect = { width: 800, height: 600 } as DOMRect |
| 212 | + expect(terminalGrid(rect, term)).toEqual({ cols: 80, rows: 24 }) |
| 213 | + }) |
| 214 | + |
| 215 | + test('uses char measure element when term cols/rows unavailable', () => { |
| 216 | + const term = mockTerminal() |
| 217 | + const rect = { width: 800, height: 480 } as DOMRect |
| 218 | + const measure = document.createElement('span') |
| 219 | + measure.className = 'xterm-char-measure-element' |
| 220 | + measure.style.width = '10px' |
| 221 | + measure.style.height = '20px' |
| 222 | + document.body.appendChild(measure) |
| 223 | + |
| 224 | + // happy-dom may not compute real layout, so this tests the fallback path |
| 225 | + const result = terminalGrid(rect, term) |
| 226 | + expect(result.cols).toBeGreaterThan(0) |
| 227 | + expect(result.rows).toBeGreaterThan(0) |
| 228 | + |
| 229 | + document.body.removeChild(measure) |
| 230 | + }) |
| 231 | +}) |
| 232 | + |
| 233 | +describe('touchToCell', () => { |
| 234 | + function makeScreen(width: number, height: number): HTMLElement { |
| 235 | + const el = document.createElement('div') |
| 236 | + Object.defineProperty(el, 'getBoundingClientRect', { |
| 237 | + value: () => ({ |
| 238 | + left: 0, |
| 239 | + top: 0, |
| 240 | + width, |
| 241 | + height, |
| 242 | + right: width, |
| 243 | + bottom: height, |
| 244 | + x: 0, |
| 245 | + y: 0, |
| 246 | + toJSON() {}, |
| 247 | + }), |
| 248 | + }) |
| 249 | + return el |
| 250 | + } |
| 251 | + |
| 252 | + function makeTouch(clientX: number, clientY: number): Touch { |
| 253 | + return { clientX, clientY } as Touch |
| 254 | + } |
| 255 | + |
| 256 | + test('touch at top-left returns cell (1, 1)', () => { |
| 257 | + const screen = makeScreen(800, 480) |
| 258 | + const term = { ...mockTerminal(), cols: 80, rows: 24 } |
| 259 | + expect(touchToCell(makeTouch(0, 0), screen, term)).toEqual({ x: 1, y: 1 }) |
| 260 | + }) |
| 261 | + |
| 262 | + test('touch at bottom-right returns cell (cols, rows)', () => { |
| 263 | + const screen = makeScreen(800, 480) |
| 264 | + const term = { ...mockTerminal(), cols: 80, rows: 24 } |
| 265 | + // Touch at the far edge — should map to last cell |
| 266 | + expect(touchToCell(makeTouch(799, 479), screen, term)).toEqual({ x: 80, y: 24 }) |
| 267 | + }) |
| 268 | + |
| 269 | + test('touch at centre returns middle cell', () => { |
| 270 | + const screen = makeScreen(800, 480) |
| 271 | + const term = { ...mockTerminal(), cols: 80, rows: 24 } |
| 272 | + const cell = touchToCell(makeTouch(400, 240), screen, term) |
| 273 | + expect(cell.x).toBe(41) |
| 274 | + expect(cell.y).toBe(13) |
| 275 | + }) |
| 276 | + |
| 277 | + test('clamps touch outside screen bounds', () => { |
| 278 | + const screen = makeScreen(800, 480) |
| 279 | + const term = { ...mockTerminal(), cols: 80, rows: 24 } |
| 280 | + // Touch far below/right of screen |
| 281 | + expect(touchToCell(makeTouch(9999, 9999), screen, term)).toEqual({ x: 80, y: 24 }) |
| 282 | + // Touch above/left of screen |
| 283 | + expect(touchToCell(makeTouch(-100, -100), screen, term)).toEqual({ x: 1, y: 1 }) |
| 284 | + }) |
| 285 | +}) |
0 commit comments