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>
| A | e2e/ | 147 | ++++++++++++++++++++++++++++++ |
| M | e2e/ | 15 | +++- |
| A | e2e/ | 30 | ++++++ |
| A | e2e/ | 30 | ++++++ |
| A | e2e/ | 30 | ++++++ |
@@ -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+}@@ -9,6 +9,8 @@ import (
99 "strings"1010 "testing"1111 "time"12+13+ ghostty "go.mitchellh.com/libghostty"1214 )1315 1416 var update = flag.Bool("update", false, "rewrite golden screens in testdata/screens")@@ -86,12 +88,23 @@ var screens = []screen{
8688 s.waitFor("HELP")8789 }},8890 {name: "narrow", opts: options{cols: 60, rows: 16}, setup: budget},91+ {name: "hover-resize-handle", setup: func(s *session) {92+ budget(s)93+ s.mouse(ghostty.MouseActionMotion, 0, 6+10-1, 3, 0)94+ s.waitFor("▐")95+ }},96+ {name: "resizing-column", setup: func(s *session) {97+ budget(s)98+ s.mouse(ghostty.MouseActionPress, ghostty.MouseButtonLeft, 6+10-1, 3, 0)99+ s.mouse(ghostty.MouseActionMotion, ghostty.MouseButtonLeft, 6+10+5, 3, 0)100+ s.waitFor("Column A width 16")101+ }},89102 }90103 91104 // Key screens are also recorded on a light terminal, where the app picks92105 // its light theme from the reported background color.93106 func init() {94- for _, name := range []string{"budget", "point-range", "selection-stats", "quit-confirm", "help"} {107+ for _, name := range []string{"budget", "point-range", "selection-stats", "quit-confirm", "help", "resizing-column"} {95108 for _, sc := range screens {96109 if sc.name == name {97110 sc.name += "-light"@@ -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@@ -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@@ -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