okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
4db295e
main
1// Screenshots: a test that wants to look at a screen calls shot with what 2// the view drew. Without ALBEDO_SHOT_DIR it does nothing, so the gate never 3// writes files; test/manual/tui_shot.py turns the .ans files into PNGs. 4package tui 5 6import ( 7"maps" 8"os" 9"path/filepath" 10"slices" 11"testing" 12) 13 14// shotTerminal makes the styles what a catppuccin terminal that reported its 15// colors gets, as a user's terminal does, instead of the palette-slot 16// fallback tests otherwise see (reverse video selection, no tints). It 17// restores them after the test, and does nothing without ALBEDO_SHOT_DIR. 18func shotTerminal (t * testing.T ) { 19t .Helper () 20if os .Getenv ("ALBEDO_SHOT_DIR" )== "" { 21return 22 } 23styles ,ramp := DefaultStyles ,transcriptInk 24t .Cleanup (func () {DefaultStyles ,transcriptInk = styles ,ramp }) 25palette := map [int ]rgb {} 26for slot ,hex := range map [int ]string {ansiRed :"#f38ba8" ,ansiGreen :"#a6e3a1" ,ansiBlue :"#89b4fa" ,ansiMagenta :"#cba6f7" ,ansiCyan :"#94e2d5" } { 27palette [slot ]= * parseHex (hex ) 28 } 29colors := termColors {fg :parseHex ("#cdd6f4" ),bg :parseHex ("#1e1e2e" ),palette :palette } 30mixed ,ok := mixInk (colors ) 31if ! ok { 32t .Fatal ("the mock terminal colors were not trusted" ) 33 } 34useInk (mixed ) 35} 36 37func shot (t * testing.T ,name ,view string ) { 38t .Helper () 39dir := os .Getenv ("ALBEDO_SHOT_DIR" ) 40if dir == "" { 41return 42 } 43if err := os .MkdirAll (dir ,0o755 );err != nil { 44t .Fatal (err ) 45 } 46if err := os .WriteFile (filepath .Join (dir ,name + ".ans" ), []byte (view ),0o644 );err != nil { 47t .Fatal (err ) 48 } 49} 50 51// A shot size is a terminal to draw every state in. 52type shotSize struct { 53name string 54width ,height int 55} 56 57var shotSizes = []shotSize {{"wide" ,120 ,30 }, {"narrow" ,70 ,20 }} 58 59// A shot state is one situation of a screen: how it draws in a terminal of 60// the given size. 61type shotState struct { 62name string 63view func (width ,height int )string 64} 65 66var shotScreens = map [string ][]shotState {} 67 68// registerShots lists the states a screen can be in, from an init in the 69// screen's shot_<screen>_test.go, so the gallery draws all of them in every 70// size. Cover what a person would want to look at: the list, a filter typed, 71// each confirmation, loading, empty, failure, a notice, a form open. 72func registerShots (screen string ,states ... shotState ) { 73shotScreens [screen ]= append (shotScreens [screen ],states ... ) 74} 75 76// TestShotGallery draws every registered state at every size to 77// $ALBEDO_SHOT_DIR/<screen>--<state>--<size>.ans. Without the variable it 78// still draws them, which keeps each state buildable. Run one screen with 79// -run TestShotGallery/<screen>. 80func TestShotGallery (t * testing.T ) { 81shotTerminal (t ) 82for _ ,screen := range slices .Sorted (maps .Keys (shotScreens )) { 83t .Run (screen ,func (t * testing.T ) { 84for _ ,state := range shotScreens [screen ] { 85for _ ,size := range shotSizes { 86view := state .view (size .width ,size .height ) 87if view == "" { 88t .Errorf ("%s/%s drew nothing at %s" ,screen ,state .name ,size .name ) 89 } 90shot (t ,screen + "--" + state .name + "--" + size .name ,view ) 91 } 92 } 93 }) 94 } 95}