cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Drive the mouse through libghostty in e2e tests

Dami committed 6b928ed
The harness encodes clicks, drags, motion and wheel events with
libghostty's mouse encoder from the terminal's current tracking mode.
New tests cover selection by drag and Shift+click, clicks while typing,
column resizing and the wheel, plus golden screens for the resize handle
and a column being resized.
commit
6b928eda0eceef6d5d4b7da899ab99b10be065ad
parent
1aedf0d9bb52392e650b1e1527c6ffd2aea43d8a
author
Dami <dami.etoile@gmail.com>
5 files changed, +251 -1
Ae2e/mouse_test.go147++++++++++++++++++++++++++++++
Me2e/screens_test.go15+++-
Ae2e/testdata/screens/hover-resize-handle.html30++++++
Ae2e/testdata/screens/resizing-column-light.html30++++++
Ae2e/testdata/screens/resizing-column.html30++++++
A e2e/mouse_test.go +147 -0
@@ -0,0 +1,147 @@
1+package e2e2+3+import (4+ "strings"5+ "testing"6+ "time"7+8+ ghostty "go.mitchellh.com/libghostty"9+)10+11+const cellW, cellH = 8, 1612+13+// mouse sends one mouse event at a cell, encoded by libghostty from the14+// terminal's current tracking mode, as Ghostty would send it. A zero15+// button means no button (plain motion).16+func (s *session) mouse(action ghostty.MouseAction, button ghostty.MouseButton, col, row int, mods ghostty.Mods) {17+ s.t.Helper()18+ s.mu.Lock()19+ enc, err := ghostty.NewMouseEncoder()20+ if err != nil {21+ s.mu.Unlock()22+ s.t.Fatal(err)23+ }24+ defer enc.Close()25+ ev, err := ghostty.NewMouseEvent()26+ if err != nil {27+ s.mu.Unlock()28+ s.t.Fatal(err)29+ }30+ defer ev.Close()31+ enc.SetOptSize(ghostty.MouseEncoderSize{32+ ScreenWidth: uint32(s.cols) * cellW, ScreenHeight: uint32(s.rows) * cellH,33+ CellWidth: cellW, CellHeight: cellH,34+ })35+ enc.SetOptFromTerminal(s.vt)36+ enc.SetOptAnyButtonPressed(button != 0 && action == ghostty.MouseActionMotion)37+ ev.SetAction(action)38+ if button != 0 {39+ ev.SetButton(button)40+ } else {41+ ev.ClearButton()42+ }43+ ev.SetMods(mods)44+ ev.SetPosition(ghostty.MousePosition{X: float32(col*cellW + cellW/2), Y: float32(row*cellH + cellH/2)})45+ data, err := enc.Encode(ev)46+ s.mu.Unlock()47+ if err != nil {48+ s.t.Fatal(err)49+ }50+ if len(data) == 0 {51+ return52+ }53+ if _, err := s.pty.Write(data); err != nil {54+ s.t.Fatal(err)55+ }56+ time.Sleep(5 * time.Millisecond)57+}58+59+// click presses and releases the left button at a screen cell.60+func (s *session) click(col, row int, mods ghostty.Mods) {61+ s.mouse(ghostty.MouseActionPress, ghostty.MouseButtonLeft, col, row, mods)62+ s.mouse(ghostty.MouseActionRelease, ghostty.MouseButtonLeft, col, row, mods)63+}64+65+// drag presses at one cell, moves through the path and releases at the end.66+func (s *session) drag(path ...[2]int) {67+ first, last := path[0], path[len(path)-1]68+ s.mouse(ghostty.MouseActionPress, ghostty.MouseButtonLeft, first[0], first[1], 0)69+ for _, p := range path[1:] {70+ s.mouse(ghostty.MouseActionMotion, ghostty.MouseButtonLeft, p[0], p[1], 0)71+ }72+ s.mouse(ghostty.MouseActionRelease, ghostty.MouseButtonLeft, last[0], last[1], 0)73+}74+75+// wheel scrolls at a cell; up is button 4, down is button 5.76+func (s *session) wheel(col, row, clicks int) {77+ button := ghostty.MouseButtonFive78+ if clicks < 0 {79+ button, clicks = ghostty.MouseButtonFour, -clicks80+ }81+ for range clicks {82+ s.mouse(ghostty.MouseActionPress, button, col, row, 0)83+ }84+}85+86+// Screen x of a cell in column c with default widths and no scrolling.87+func colX(c int) int { return 6 + c*10 + 2 }88+89+func TestMouseClickDragAndShiftClick(t *testing.T) {90+ s := start(t, "")91+ s.click(colX(1), gridRow1+2, 0)92+ s.waitFor(" B3 ")93+ s.drag([2]int{colX(0), gridRow1}, [2]int{colX(1), gridRow1 + 1}, [2]int{colX(2), gridRow1 + 2})94+ s.eventually("drag selection", func() bool { return strings.Contains(s.line(29), "A1:C3") })95+ s.click(colX(3), gridRow1+4, ghostty.ModShift)96+ s.eventually("shift+click extends", func() bool { return strings.Contains(s.line(29), "A1:D5") })97+98+ // The app asked the terminal to pass Shift+click through.99+ s.mu.Lock()100+ raw := string(s.raw)101+ s.mu.Unlock()102+ if !strings.Contains(raw, "\x1b[>1s") {103+ t.Error("XTSHIFTESCAPE not requested")104+ }105+}106+107+func TestMouseClickWhileTypingAndFormulaReference(t *testing.T) {108+ s := start(t, "")109+ s.keys("40", "<enter>", "2", "<enter>", "=")110+ s.click(colX(0), gridRow1, 0)111+ s.waitForLine(1, "=A1")112+ s.keys("+")113+ s.click(colX(0), gridRow1+1, 0)114+ s.waitForLine(1, "=A1+A2")115+ s.keys("<enter>")116+ s.waitForLine(gridRow1+2, numRow(3, "42"))117+118+ // Clicking another cell while typing plain text accepts it there.119+ s.keys("note")120+ s.click(colX(2), gridRow1, 0)121+ s.waitFor(" C1 ")122+ s.waitForLine(gridRow1+3, " 4 note")123+}124+125+func TestMouseResizeAndWheel(t *testing.T) {126+ s := start(t, "")127+ border := 6 + 10 - 1128+ s.drag([2]int{border, 3}, [2]int{border + 4, 3}, [2]int{border + 8, 3})129+ s.eventually("column A wider", func() bool {130+ return strings.HasPrefix(s.line(3), strings.Repeat(" ", 6)+strings.Repeat(" ", 8)+"A")131+ })132+ s.wheel(colX(2), gridRow1+5, 4)133+ s.waitForLine(gridRow1, " 13")134+ // The active cell didn't move; arrowing to A2 scrolls just enough to135+ // show it again.136+ s.keys("<down>")137+ s.waitForLine(gridRow1, " 2")138+}139+140+func TestMouseDoubleClickEdits(t *testing.T) {141+ s := start(t, "")142+ s.keys("hello", "<enter>")143+ s.click(colX(0), gridRow1, 0)144+ s.click(colX(0), gridRow1, 0)145+ s.waitFor("EDIT")146+ s.waitForLine(1, "hello")147+}
M e2e/screens_test.go +14 -1
A e2e/testdata/screens/hover-resize-handle.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> B7 </span> =SUM(B3:B5) <span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold"> READY</span><span style="background:var(--vt-palette-6)"> </span>2+3+4+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-6);background:var(--vt-palette-8);font-weight:bold"> A ▐</span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold"> B </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> C D E F G H I </span>5+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 1 </span> Household budget 20266+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 2 </span>7+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 3 </span> Rent 14508+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 4 </span> Groceries 612.49+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 5 </span> Transit 9610+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 6 </span> Savings r<span style="color:var(--vt-palette-1)"> #DIV/0! </span>11+<span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold"> 7 </span> Total <span style="color:var(--vt-palette-0);background:var(--vt-palette-6)"> 2158.4 </span>12+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 8 </span>13+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 9 </span>14+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 10 </span>15+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 11 </span>16+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 12 </span>17+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 13 </span>18+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 14 </span>19+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 15 </span>20+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 16 </span>21+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 17 </span>22+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 18 </span>23+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 19 </span>24+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 20 </span>25+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 21 </span>26+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 22 </span>27+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 23 </span>28+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 24 </span>29+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 25 </span>30+untitled<span style="color:var(--vt-palette-8)"> modified</span> <span style="font-weight:bold">F1</span> <span style="color:var(--vt-palette-8)">help </span><span style="font-weight:bold">F10</span> <span style="color:var(--vt-palette-8)">menu </span><span style="font-weight:bold">Ctrl+Q</span> <span style="color:var(--vt-palette-8)">quit</span></pre>\ No newline at end of file
A e2e/testdata/screens/resizing-column-light.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> B7 </span> =SUM(B3:B5) <span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold"> READY</span><span style="background:var(--vt-palette-6)"> </span>2+3+<span style="font-weight:bold">Column A</span><span style="color:var(--vt-palette-0)"> width </span>16<span style="color:var(--vt-palette-0)"> double-click the border to fit</span>4+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> A </span><span style="color:var(--vt-palette-6);background:var(--vt-palette-7);font-weight:bold">▐</span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold"> B </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> C D E F G H </span>5+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 1 </span> Household budget 20266+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 2 </span>7+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 3 </span> Rent 14508+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 4 </span> Groceries 612.49+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 5 </span> Transit 9610+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 6 </span> Savings rate <span style="color:var(--vt-palette-1)"> #DIV/0! </span>11+<span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold"> 7 </span> Total <span style="color:var(--vt-palette-0);background:var(--vt-palette-6)"> 2158.4 </span>12+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 8 </span>13+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 9 </span>14+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 10 </span>15+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 11 </span>16+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 12 </span>17+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 13 </span>18+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 14 </span>19+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 15 </span>20+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 16 </span>21+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 17 </span>22+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 18 </span>23+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 19 </span>24+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 20 </span>25+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 21 </span>26+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 22 </span>27+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 23 </span>28+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 24 </span>29+<span style="color:var(--vt-palette-0);background:var(--vt-palette-7)"> 25 </span>30+untitled<span style="color:var(--vt-palette-0)"> modified</span> <span style="font-weight:bold">F1</span> <span style="color:var(--vt-palette-0)">help </span><span style="font-weight:bold">F10</span> <span style="color:var(--vt-palette-0)">menu </span><span style="font-weight:bold">Ctrl+Q</span> <span style="color:var(--vt-palette-0)">quit</span></pre>\ No newline at end of file
A e2e/testdata/screens/resizing-column.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> B7 </span> =SUM(B3:B5) <span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold"> READY</span><span style="background:var(--vt-palette-6)"> </span>2+3+<span style="font-weight:bold">Column A</span><span style="color:var(--vt-palette-8)"> width </span>16<span style="color:var(--vt-palette-8)"> double-click the border to fit</span>4+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> A </span><span style="color:var(--vt-palette-6);background:var(--vt-palette-8);font-weight:bold">▐</span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold"> B </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> C D E F G H </span>5+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 1 </span> Household budget 20266+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 2 </span>7+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 3 </span> Rent 14508+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 4 </span> Groceries 612.49+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 5 </span> Transit 9610+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 6 </span> Savings rate <span style="color:var(--vt-palette-1)"> #DIV/0! </span>11+<span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold"> 7 </span> Total <span style="color:var(--vt-palette-0);background:var(--vt-palette-6)"> 2158.4 </span>12+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 8 </span>13+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 9 </span>14+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 10 </span>15+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 11 </span>16+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 12 </span>17+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 13 </span>18+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 14 </span>19+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 15 </span>20+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 16 </span>21+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 17 </span>22+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 18 </span>23+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 19 </span>24+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 20 </span>25+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 21 </span>26+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 22 </span>27+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 23 </span>28+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 24 </span>29+<span style="color:var(--vt-palette-15);background:var(--vt-palette-8)"> 25 </span>30+untitled<span style="color:var(--vt-palette-8)"> modified</span> <span style="font-weight:bold">F1</span> <span style="color:var(--vt-palette-8)">help </span><span style="font-weight:bold">F10</span> <span style="color:var(--vt-palette-8)">menu </span><span style="font-weight:bold">Ctrl+Q</span> <span style="color:var(--vt-palette-8)">quit</span></pre>\ No newline at end of file