| 1 | // Geometry tests. `node --test node-tui/layout.test.ts` |
| 2 | // |
| 3 | // The property that matters for the mouse is coverage: every cell of the body |
| 4 | // belongs to exactly one pane frame. A gap means a click that does nothing, and |
| 5 | // an overlap means a click that focuses whichever pane the handler happens to |
| 6 | // test first — both read as the mouse being broken. |
| 7 | import { test } from 'node:test'; |
| 8 | import assert from 'node:assert/strict'; |
| 9 | |
| 10 | import { computeLayout, contains } from './layout.ts'; |
| 11 | import type { Layout } from './layout.ts'; |
| 12 | |
| 13 | const layout = (over: Partial<Parameters<typeof computeLayout>[0]> = {}): Layout => |
| 14 | computeLayout({ |
| 15 | rows: 40, |
| 16 | columns: 140, |
| 17 | sessionsWidth: 22, |
| 18 | chatWidth: 34, |
| 19 | logRows: 0, |
| 20 | wideEnough: 100, |
| 21 | lineCount: 8, |
| 22 | zoom: 'none', |
| 23 | ...over, |
| 24 | }); |
| 25 | |
| 26 | test('every column of the body belongs to exactly one pane', () => { |
| 27 | const l = layout(); |
| 28 | const bottom = l.editorFrame.top + l.editorFrame.height; |
| 29 | for (let row = l.editorFrame.top; row < bottom; row++) { |
| 30 | for (let column = 0; column < 140; column++) { |
| 31 | const hits = [l.sessionsFrame, l.chatFrame, l.editorFrame] |
| 32 | .filter((rect) => contains(rect, row, column)).length; |
| 33 | assert.equal(hits, 1, `row ${row}, column ${column} matched ${hits} panes`); |
| 34 | } |
| 35 | } |
| 36 | }); |
| 37 | |
| 38 | test('a pane frame includes the border its text area excludes', () => { |
| 39 | const l = layout(); |
| 40 | // The chat's text area starts two columns inside its box (border, padding). |
| 41 | assert.ok(l.chat && l.chatFrame); |
| 42 | assert.equal(l.chat.left - l.chatFrame.left, 2); |
| 43 | assert.ok(contains(l.chatFrame, l.chat.top, l.chatFrame.left)); |
| 44 | assert.ok(!contains(l.chat, l.chat.top, l.chatFrame.left)); |
| 45 | }); |
| 46 | |
| 47 | test('narrow terminals have no side panes to click', () => { |
| 48 | const l = layout({ columns: 80 }); |
| 49 | assert.equal(l.sessionsFrame, null); |
| 50 | assert.equal(l.chatFrame, null); |
| 51 | assert.equal(l.editorFrame.left, 0); |
| 52 | assert.equal(l.editorFrame.width, 80); |
| 53 | }); |
| 54 | |
| 55 | test('an open log takes its rows from the panes, not from the status bar', () => { |
| 56 | const closed = layout(); |
| 57 | const open = layout({ logRows: 8 }); |
| 58 | assert.equal(closed.editor.height - open.editor.height, 8); |
| 59 | }); |