From e61b6d69f921b987611426e1e89fba41d0f7a12d Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Wed, 19 Feb 2025 10:19:26 -0500 Subject: [PATCH 1/3] Ensure codeblocks use accessible colors This commit leverages `cli` fork of `charmbracelet/glamour` with enhancement for configuring `chroma` formatter. It includes simple tests that enabling `gh` accessible features causes codeblocks to be downsampled to base 16 ANSI colors. --- go.mod | 16 +++++++------ go.sum | 36 ++++++++++++++--------------- pkg/markdown/markdown.go | 15 +++++++++++- pkg/markdown/markdown_test.go | 43 +++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 081f6f0..32bbc48 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/MakeNowJust/heredoc v1.0.0 github.com/charmbracelet/glamour v0.8.0 - github.com/charmbracelet/lipgloss v0.12.1 + github.com/charmbracelet/lipgloss v1.0.0 github.com/cli/browser v1.3.0 github.com/cli/safeexec v1.0.0 github.com/cli/shurcooL-graphql v0.0.4 @@ -18,8 +18,8 @@ require ( github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a github.com/stretchr/testify v1.7.0 github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e - golang.org/x/sys v0.28.0 - golang.org/x/term v0.27.0 + golang.org/x/sys v0.30.0 + golang.org/x/term v0.29.0 golang.org/x/text v0.21.0 gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/yaml.v3 v3.0.1 @@ -29,7 +29,7 @@ require ( github.com/alecthomas/chroma/v2 v2.14.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/charmbracelet/x/ansi v0.1.4 // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect github.com/gorilla/css v1.0.1 // indirect @@ -40,12 +40,14 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/yuin/goldmark v1.7.4 // indirect - github.com/yuin/goldmark-emoji v1.0.3 // indirect + github.com/yuin/goldmark v1.7.8 // indirect + github.com/yuin/goldmark-emoji v1.0.4 // indirect golang.org/x/net v0.33.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) + +replace github.com/charmbracelet/glamour => github.com/cli/glamour v0.0.0-20250219132354-dba8cf7ee336 diff --git a/go.sum b/go.sum index 0e7f62c..3f09124 100644 --- a/go.sum +++ b/go.sum @@ -16,16 +16,16 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWp github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= -github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= -github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs= -github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8= -github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= -github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= -github.com/charmbracelet/x/exp/golden v0.0.0-20240715153702-9ba8adf781c4 h1:6KzMkQeAF56rggw2NZu1L+TH7j9+DM1/2Kmh7KUxg1I= -github.com/charmbracelet/x/exp/golden v0.0.0-20240715153702-9ba8adf781c4/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= +github.com/cli/glamour v0.0.0-20250219132354-dba8cf7ee336 h1:8C3iXVZ6ZP8MblfrT1KKPMgW0q1ZO9nXSPvpKy79VEk= +github.com/cli/glamour v0.0.0-20250219132354-dba8cf7ee336/go.mod h1:OUgq8/2KmRHaylj8o+SGSoOiY+D0bNJfwaqN15oK4A0= github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/cli/shurcooL-graphql v0.0.4 h1:6MogPnQJLjKkaXPyGqPRXOI2qCsQdqNfUY1QSJu2GuY= @@ -70,8 +70,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -97,10 +97,10 @@ github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:Buzhfgf github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= -github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= -github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= +github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= +github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90= +github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -120,12 +120,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/pkg/markdown/markdown.go b/pkg/markdown/markdown.go index 6960b93..a1c90bb 100644 --- a/pkg/markdown/markdown.go +++ b/pkg/markdown/markdown.go @@ -39,7 +39,20 @@ func WithTheme(theme string) glamour.TermRendererOption { switch theme { case "light", "dark": if accessible { - return glamour.WithStyles(AccessibleStyleConfig(theme)) + // Applying multiple glamour.TermRendererOption here requires a wrapper that applies each + // within glamour.NewTermRenderer() in Render() below. + stylesOption := glamour.WithStyles(AccessibleStyleConfig(theme)) + chromaOption := glamour.WithChromaFormatter("terminal16") + + return func(tr *glamour.TermRenderer) error { + if err := stylesOption(tr); err != nil { + return err + } + if err := chromaOption(tr); err != nil { + return err + } + return nil + } } style = theme default: diff --git a/pkg/markdown/markdown_test.go b/pkg/markdown/markdown_test.go index b9467ea..5412588 100644 --- a/pkg/markdown/markdown_test.go +++ b/pkg/markdown/markdown_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/MakeNowJust/heredoc" "github.com/cli/go-gh/v2/pkg/accessibility" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -74,6 +75,48 @@ func Test_Render(t *testing.T) { accessibleEnvVar: "true", wantOut: fmt.Sprintf("%s1mh2", brightMagenta_4bitColorSeq), }, + { + name: "when the light theme is selected, the codeblock renders using 8-bit colors", + text: heredoc.Docf(` + %[1]s%[1]s%[1]sgo + fmt.Println("Hello, world!") + %[1]s%[1]s%[1]s + `, "`"), + theme: "light", + wantOut: "\x1b[0m\x1b[38;5;235mfmt\x1b[0m\x1b[38;5;210m.\x1b[0m\x1b[38;5;35mPrintln\x1b[0m\x1b[38;5;210m(\x1b[0m\x1b[38;5;95m\"Hello, world!\"\x1b[0m\x1b[38;5;210m)\x1b[0m", + }, + { + name: "when the dark theme is selected, the codeblock renders using 8-bit colors", + text: heredoc.Docf(` + %[1]s%[1]s%[1]sgo + fmt.Println("Hello, world!") + %[1]s%[1]s%[1]s + `, "`"), + theme: "dark", + wantOut: "\x1b[0m\x1b[38;5;235mfmt\x1b[0m\x1b[38;5;210m.\x1b[0m\x1b[38;5;35mPrintln\x1b[0m\x1b[38;5;210m(\x1b[0m\x1b[38;5;95m\"Hello, world!\"\x1b[0m\x1b[38;5;210m)\x1b[0m", + }, + { + name: "when the accessible env var is set and the light theme is selected, the codeblock renders using 4-bit colors", + text: heredoc.Docf(` + %[1]s%[1]s%[1]sgo + fmt.Println("Hello, world!") + %[1]s%[1]s%[1]s + `, "`"), + theme: "light", + accessibleEnvVar: "true", + wantOut: "\x1b[0m\x1b[30mfmt\x1b[0m\x1b[33m.\x1b[0m\x1b[36mPrintln\x1b[0m\x1b[33m(\x1b[0m\x1b[90m\"Hello, world!\"\x1b[0m\x1b[33m)\x1b[0m", + }, + { + name: "when the accessible env var is set and the dark theme is selected, the codeblock renders using 4-bit colors", + text: heredoc.Docf(` + %[1]s%[1]s%[1]sgo + fmt.Println("Hello, world!") + %[1]s%[1]s%[1]s + `, "`"), + theme: "dark", + accessibleEnvVar: "true", + wantOut: "\x1b[0m\x1b[30mfmt\x1b[0m\x1b[33m.\x1b[0m\x1b[36mPrintln\x1b[0m\x1b[33m(\x1b[0m\x1b[90m\"Hello, world!\"\x1b[0m\x1b[33m)\x1b[0m", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From c5336a563c1e02bf13679904649c56740e8718e9 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Mon, 24 Feb 2025 14:37:25 -0500 Subject: [PATCH 2/3] v2 iteration on testing codeblocks This commit is v2 approach to testing for accessible colors and codeblock rendering using an in-house approach to identifying ANSI escape sequences and analyzing them for color depth. After talking with @williammartin, this is likely going to be refactored a third time leveraging a module to help with parsing escape sequences from text. --- go.mod | 4 +- go.sum | 4 +- pkg/markdown/accessibility.go | 4 +- pkg/markdown/accessibility_test.go | 4 +- pkg/markdown/markdown.go | 2 +- pkg/markdown/markdown_test.go | 176 ++++++++++++++++++++++------- 6 files changed, 142 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index 32bbc48..a05420f 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.21 require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/MakeNowJust/heredoc v1.0.0 - github.com/charmbracelet/glamour v0.8.0 github.com/charmbracelet/lipgloss v1.0.0 github.com/cli/browser v1.3.0 + github.com/cli/glamour v0.0.0-20250220192152-8544502ccff9 github.com/cli/safeexec v1.0.0 github.com/cli/shurcooL-graphql v0.0.4 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 @@ -49,5 +49,3 @@ require ( golang.org/x/net v0.33.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) - -replace github.com/charmbracelet/glamour => github.com/cli/glamour v0.0.0-20250219132354-dba8cf7ee336 diff --git a/go.sum b/go.sum index 3f09124..ce72ee3 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99k github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= -github.com/cli/glamour v0.0.0-20250219132354-dba8cf7ee336 h1:8C3iXVZ6ZP8MblfrT1KKPMgW0q1ZO9nXSPvpKy79VEk= -github.com/cli/glamour v0.0.0-20250219132354-dba8cf7ee336/go.mod h1:OUgq8/2KmRHaylj8o+SGSoOiY+D0bNJfwaqN15oK4A0= +github.com/cli/glamour v0.0.0-20250220192152-8544502ccff9 h1:fPJNUzG+Au+pIfYx2c5QNngZ3KLj7xAzjutL6efy1x8= +github.com/cli/glamour v0.0.0-20250220192152-8544502ccff9/go.mod h1:bB4uNJ5F0+nzpGqwlhKEy7tD0PPL0SxNWEwZzG77vMg= github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/cli/shurcooL-graphql v0.0.4 h1:6MogPnQJLjKkaXPyGqPRXOI2qCsQdqNfUY1QSJu2GuY= diff --git a/pkg/markdown/accessibility.go b/pkg/markdown/accessibility.go index 0d1a41e..f618879 100644 --- a/pkg/markdown/accessibility.go +++ b/pkg/markdown/accessibility.go @@ -3,8 +3,8 @@ package markdown import ( "strconv" - "github.com/charmbracelet/glamour/ansi" - "github.com/charmbracelet/glamour/styles" + "github.com/cli/glamour/ansi" + "github.com/cli/glamour/styles" ) type ANSIColorCode int diff --git a/pkg/markdown/accessibility_test.go b/pkg/markdown/accessibility_test.go index 93ac9b1..4b33068 100644 --- a/pkg/markdown/accessibility_test.go +++ b/pkg/markdown/accessibility_test.go @@ -3,8 +3,8 @@ package markdown import ( "testing" - "github.com/charmbracelet/glamour/ansi" - "github.com/charmbracelet/glamour/styles" + "github.com/cli/glamour/ansi" + "github.com/cli/glamour/styles" "github.com/stretchr/testify/assert" ) diff --git a/pkg/markdown/markdown.go b/pkg/markdown/markdown.go index a1c90bb..88772b6 100644 --- a/pkg/markdown/markdown.go +++ b/pkg/markdown/markdown.go @@ -5,7 +5,7 @@ import ( "os" "strings" - "github.com/charmbracelet/glamour" + "github.com/cli/glamour" "github.com/cli/go-gh/v2/pkg/accessibility" ) diff --git a/pkg/markdown/markdown_test.go b/pkg/markdown/markdown_test.go index 5412588..ffc3909 100644 --- a/pkg/markdown/markdown_test.go +++ b/pkg/markdown/markdown_test.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "path/filepath" + "regexp" + "strconv" "strings" "testing" @@ -20,8 +22,140 @@ const ( customH2_8bitColorSeq = "\x1b[38;5;61;" magenta_4bitColorSeq = "\x1b[35;" brightMagenta_4bitColorSeq = "\x1b[95;" + + // TODO: Include a little more context on SGR including link to https://en.wikipedia.org/wiki/ANSI_escape_code#Select_Graphic_Rendition_parameters for more info + // sgrSequencePattern identifies ANSI escape sequences containing display attributes + // that affect the color, emphasis, and other aspects of displaying text. + sgrSequencePattern = `\x1b\[(.+?)m` + + // sgrAttributePattern analyzes separate display attributes within an ANSI escape sequence + // for detecting color depth (3-bit, 4-bit, 8-bit, 24-bit, etc) or other effects. + // + // This is a separate regex from sgrSequencePattern as `FindAllStringSubmatch()` does not + // handle repeating capture groups well. + sgrAttributePattern = `;?(?P\d+)` // TODO: change the `sequence` note; if we aren't actually using the name group, remove it ) +func Test_Render_Codeblocks(t *testing.T) { + t.Setenv("GLAMOUR_STYLE", "") + + sequencesRegex := regexp.MustCompile(sgrSequencePattern) + attributesRegex := regexp.MustCompile(sgrAttributePattern) + text := heredoc.Docf(` + %[1]s%[1]s%[1]sgo + package main + + import ( + "fmt" + ) + + func main() { + fmt.Println("Hello, world!") + } + %[1]s%[1]s%[1]s + `, "`") + + tests := []struct { + name string + text string + theme string + accessible bool + }{ + { + name: "when the light theme is selected, the codeblock renders using 8-bit colors", + text: text, + theme: "light", + }, + { + name: "when the dark theme is selected, the codeblock renders using 8-bit colors", + text: text, + theme: "dark", + }, + { + name: "when the accessible env var is set and the light theme is selected, the codeblock renders using 4-bit colors", + text: text, + theme: "light", + accessible: true, + }, + { + name: "when the accessible env var is set and the dark theme is selected, the codeblock renders using 4-bit colors", + text: text, + theme: "dark", + accessible: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.accessible { + t.Setenv(accessibility.ACCESSIBILITY_ENV, "true") + } + + out, err := Render(tt.text, WithTheme(tt.theme)) + require.NoError(t, err) + sequences := sequencesRegex.FindAllStringSubmatch(out, -1) + require.NotEmpty(t, sequences, "Failed to find expected SGR sequences in rendered output") + + // TODO: Review use of a module like https://github.com/leaanthony/go-ansi-parser/blob/main/ansi.go#L264 to do the sequence and attribute parsing such that we just have to iterate over the results + for _, sequence := range sequences { + attributes := attributesRegex.FindAllStringSubmatch(sequence[1], -1) + require.NotEmpty(t, attributes, "Failed to extract SGR attributes for testing") + + // Analysis loop handles index incrementing due to unique display attribute situations like color depth + for i := 0; i < len(attributes); { + // TODO: Use constants for 38,48 and maybe remove the int conversion + attribute, err := strconv.Atoi(attributes[i][1]) + require.NoError(t, err, "Failed to convert SGR attribute for testing") + + switch attribute { + case 38, 48: + // Display attributes for setting 8-bit and 24-bit foreground and background colors + colorDepth, err := strconv.Atoi(attributes[i+1][1]) + require.NoError(t, err, "Failed to convert SGR color depth attribute for testing") + + switch colorDepth { + case 2: + // 24-bit color display attribute form + // - ESC[38;2;⟨r⟩;⟨g⟩;⟨b⟩m for foreground colors + // - ESC[48;2;⟨r⟩;⟨g⟩;⟨b⟩m for background colors + require.False(t, tt.accessible, "24-bit color is not accessible, customizable") + + color24bitRed, err := strconv.Atoi(attributes[i+2][1]) + require.NoError(t, err, "Failed to convert 24-bit red color value for testing") + require.True(t, color24bitRed >= 0 && color24bitRed <= 255, "24-bit red color value out of 0-255 range") + + color24bitGreen, err := strconv.Atoi(attributes[i+3][1]) + require.NoError(t, err, "Failed to convert 24-bit green color value for testing") + require.True(t, color24bitGreen >= 0 && color24bitGreen <= 255, "24-bit green color value out of 0-255 range") + + color24bitBlue, err := strconv.Atoi(attributes[i+4][1]) + require.NoError(t, err, "Failed to convert 24-bit blue color value for testing") + require.True(t, color24bitBlue >= 0 && color24bitBlue <= 255, "24-bit blue color value out of 0-255 range") + + i += 5 + case 5: + // 8-bit color display attributes form: + // - ESC[38;5;⟨n⟩m for foreground colors + // - ESC[48;5;⟨n⟩m for background colors + require.False(t, tt.accessible, "8-bit color is not accessible, customizable") + + color8bit, err := strconv.Atoi(attributes[i+2][1]) + require.NoError(t, err, "Failed to convert 8-bit color value for testing") + require.True(t, color8bit >= 0 && color8bit <= 255, "8-bit color value out of 0-255 range") + + i += 3 + default: + require.Fail(t, "Unexpected color depth in attribute") + } + default: + // Increment index as this attribute does not affect accessibility currently + i += 1 + } + } + } + }) + } +} + // Test_Render verifies that the proper ANSI color codes are applied to the rendered // markdown by examining the ANSI escape sequences in the output for the correct color // match. For more information on ANSI color codes, see @@ -75,48 +209,6 @@ func Test_Render(t *testing.T) { accessibleEnvVar: "true", wantOut: fmt.Sprintf("%s1mh2", brightMagenta_4bitColorSeq), }, - { - name: "when the light theme is selected, the codeblock renders using 8-bit colors", - text: heredoc.Docf(` - %[1]s%[1]s%[1]sgo - fmt.Println("Hello, world!") - %[1]s%[1]s%[1]s - `, "`"), - theme: "light", - wantOut: "\x1b[0m\x1b[38;5;235mfmt\x1b[0m\x1b[38;5;210m.\x1b[0m\x1b[38;5;35mPrintln\x1b[0m\x1b[38;5;210m(\x1b[0m\x1b[38;5;95m\"Hello, world!\"\x1b[0m\x1b[38;5;210m)\x1b[0m", - }, - { - name: "when the dark theme is selected, the codeblock renders using 8-bit colors", - text: heredoc.Docf(` - %[1]s%[1]s%[1]sgo - fmt.Println("Hello, world!") - %[1]s%[1]s%[1]s - `, "`"), - theme: "dark", - wantOut: "\x1b[0m\x1b[38;5;235mfmt\x1b[0m\x1b[38;5;210m.\x1b[0m\x1b[38;5;35mPrintln\x1b[0m\x1b[38;5;210m(\x1b[0m\x1b[38;5;95m\"Hello, world!\"\x1b[0m\x1b[38;5;210m)\x1b[0m", - }, - { - name: "when the accessible env var is set and the light theme is selected, the codeblock renders using 4-bit colors", - text: heredoc.Docf(` - %[1]s%[1]s%[1]sgo - fmt.Println("Hello, world!") - %[1]s%[1]s%[1]s - `, "`"), - theme: "light", - accessibleEnvVar: "true", - wantOut: "\x1b[0m\x1b[30mfmt\x1b[0m\x1b[33m.\x1b[0m\x1b[36mPrintln\x1b[0m\x1b[33m(\x1b[0m\x1b[90m\"Hello, world!\"\x1b[0m\x1b[33m)\x1b[0m", - }, - { - name: "when the accessible env var is set and the dark theme is selected, the codeblock renders using 4-bit colors", - text: heredoc.Docf(` - %[1]s%[1]s%[1]sgo - fmt.Println("Hello, world!") - %[1]s%[1]s%[1]s - `, "`"), - theme: "dark", - accessibleEnvVar: "true", - wantOut: "\x1b[0m\x1b[30mfmt\x1b[0m\x1b[33m.\x1b[0m\x1b[36mPrintln\x1b[0m\x1b[33m(\x1b[0m\x1b[90m\"Hello, world!\"\x1b[0m\x1b[33m)\x1b[0m", - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 2b16a4407ce2defc144120dca84f1c40dbb6a8fc Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Tue, 25 Feb 2025 08:59:14 -0500 Subject: [PATCH 3/3] Simplify markdown codeblock tests, glamour helper This commit is focused on PR feedback around codeblock testing and simplifying related code: 1. Use of new `WithOptions(...TermRendererOption) TermRendererOption` to clean up `WithTheme()` The `glamour` TermRendererOption pattern has a limitation that users cannot compose multiple options without duplicating code or building one-off anonymous functions. However, this commit takes advantage of an enhancement in https://github.com/cli/glamour/pull/3 that allows users to leverage a helper to avoid building one-offs. 1. Use of new `leaanthony/go-ansi-parser` dependency for parsing ANSI escape sequences and display attributes In v1 of `markdown_test.go`, the codeblock tests were very simple, testing the result of output of markdown rendering against a string of ANSI escape sequences. The concern raised is that this was testing the result rather than behavior wanted. In v2 of `markdown_test.go`, the codeblock tests were refactored to use regex to extract and analyze ANSI escape sequences and display attributes. The concern raised was that this was a lot of logic to build and maintain and might benefit from a dependency that could do it. In v3 of `markdown_test.go`, a combination of v1 and v2 approaches for 1) testing that theme appropriate colors are used and 2) testing that ensures accessible display options are used when accessible experience is enabled --- go.mod | 5 +- go.sum | 6 ++ pkg/markdown/markdown.go | 18 ++--- pkg/markdown/markdown_test.go | 126 +++++++++++++--------------------- 4 files changed, 61 insertions(+), 94 deletions(-) diff --git a/go.mod b/go.mod index a05420f..126f6b7 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,16 @@ go 1.21 require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/MakeNowJust/heredoc v1.0.0 + github.com/alecthomas/chroma/v2 v2.14.0 github.com/charmbracelet/lipgloss v1.0.0 github.com/cli/browser v1.3.0 - github.com/cli/glamour v0.0.0-20250220192152-8544502ccff9 + github.com/cli/glamour v0.0.0-20250225134531-b1d96ed4a7e1 github.com/cli/safeexec v1.0.0 github.com/cli/shurcooL-graphql v0.0.4 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/henvic/httpretty v0.0.6 github.com/itchyny/gojq v0.12.15 + github.com/leaanthony/go-ansi-parser v1.6.1 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a @@ -26,7 +28,6 @@ require ( ) require ( - github.com/alecthomas/chroma/v2 v2.14.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/charmbracelet/x/ansi v0.8.0 // indirect diff --git a/go.sum b/go.sum index ce72ee3..76c8f96 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= github.com/cli/glamour v0.0.0-20250220192152-8544502ccff9 h1:fPJNUzG+Au+pIfYx2c5QNngZ3KLj7xAzjutL6efy1x8= github.com/cli/glamour v0.0.0-20250220192152-8544502ccff9/go.mod h1:bB4uNJ5F0+nzpGqwlhKEy7tD0PPL0SxNWEwZzG77vMg= +github.com/cli/glamour v0.0.0-20250225134531-b1d96ed4a7e1 h1:TrV+Bs2RKR/CeodJDRO6GXzIxT7PAOwbuxth8ACcucg= +github.com/cli/glamour v0.0.0-20250225134531-b1d96ed4a7e1/go.mod h1:bB4uNJ5F0+nzpGqwlhKEy7tD0PPL0SxNWEwZzG77vMg= github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/cli/shurcooL-graphql v0.0.4 h1:6MogPnQJLjKkaXPyGqPRXOI2qCsQdqNfUY1QSJu2GuY= @@ -60,8 +62,12 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A= +github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= diff --git a/pkg/markdown/markdown.go b/pkg/markdown/markdown.go index 88772b6..57ef6bf 100644 --- a/pkg/markdown/markdown.go +++ b/pkg/markdown/markdown.go @@ -39,20 +39,10 @@ func WithTheme(theme string) glamour.TermRendererOption { switch theme { case "light", "dark": if accessible { - // Applying multiple glamour.TermRendererOption here requires a wrapper that applies each - // within glamour.NewTermRenderer() in Render() below. - stylesOption := glamour.WithStyles(AccessibleStyleConfig(theme)) - chromaOption := glamour.WithChromaFormatter("terminal16") - - return func(tr *glamour.TermRenderer) error { - if err := stylesOption(tr); err != nil { - return err - } - if err := chromaOption(tr); err != nil { - return err - } - return nil - } + return glamour.WithOptions( + glamour.WithStyles(AccessibleStyleConfig(theme)), + glamour.WithChromaFormatter("terminal16"), + ) } style = theme default: diff --git a/pkg/markdown/markdown_test.go b/pkg/markdown/markdown_test.go index ffc3909..1b301b2 100644 --- a/pkg/markdown/markdown_test.go +++ b/pkg/markdown/markdown_test.go @@ -4,13 +4,13 @@ import ( "fmt" "os" "path/filepath" - "regexp" - "strconv" "strings" "testing" "github.com/MakeNowJust/heredoc" + "github.com/alecthomas/chroma/v2/styles" "github.com/cli/go-gh/v2/pkg/accessibility" + ansi "github.com/leaanthony/go-ansi-parser" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -22,25 +22,11 @@ const ( customH2_8bitColorSeq = "\x1b[38;5;61;" magenta_4bitColorSeq = "\x1b[35;" brightMagenta_4bitColorSeq = "\x1b[95;" - - // TODO: Include a little more context on SGR including link to https://en.wikipedia.org/wiki/ANSI_escape_code#Select_Graphic_Rendition_parameters for more info - // sgrSequencePattern identifies ANSI escape sequences containing display attributes - // that affect the color, emphasis, and other aspects of displaying text. - sgrSequencePattern = `\x1b\[(.+?)m` - - // sgrAttributePattern analyzes separate display attributes within an ANSI escape sequence - // for detecting color depth (3-bit, 4-bit, 8-bit, 24-bit, etc) or other effects. - // - // This is a separate regex from sgrSequencePattern as `FindAllStringSubmatch()` does not - // handle repeating capture groups well. - sgrAttributePattern = `;?(?P\d+)` // TODO: change the `sequence` note; if we aren't actually using the name group, remove it ) func Test_Render_Codeblocks(t *testing.T) { t.Setenv("GLAMOUR_STYLE", "") - sequencesRegex := regexp.MustCompile(sgrSequencePattern) - attributesRegex := regexp.MustCompile(sgrAttributePattern) text := heredoc.Docf(` %[1]s%[1]s%[1]sgo package main @@ -92,64 +78,14 @@ func Test_Render_Codeblocks(t *testing.T) { out, err := Render(tt.text, WithTheme(tt.theme)) require.NoError(t, err) - sequences := sequencesRegex.FindAllStringSubmatch(out, -1) - require.NotEmpty(t, sequences, "Failed to find expected SGR sequences in rendered output") - - // TODO: Review use of a module like https://github.com/leaanthony/go-ansi-parser/blob/main/ansi.go#L264 to do the sequence and attribute parsing such that we just have to iterate over the results - for _, sequence := range sequences { - attributes := attributesRegex.FindAllStringSubmatch(sequence[1], -1) - require.NotEmpty(t, attributes, "Failed to extract SGR attributes for testing") - - // Analysis loop handles index incrementing due to unique display attribute situations like color depth - for i := 0; i < len(attributes); { - // TODO: Use constants for 38,48 and maybe remove the int conversion - attribute, err := strconv.Atoi(attributes[i][1]) - require.NoError(t, err, "Failed to convert SGR attribute for testing") - - switch attribute { - case 38, 48: - // Display attributes for setting 8-bit and 24-bit foreground and background colors - colorDepth, err := strconv.Atoi(attributes[i+1][1]) - require.NoError(t, err, "Failed to convert SGR color depth attribute for testing") - - switch colorDepth { - case 2: - // 24-bit color display attribute form - // - ESC[38;2;⟨r⟩;⟨g⟩;⟨b⟩m for foreground colors - // - ESC[48;2;⟨r⟩;⟨g⟩;⟨b⟩m for background colors - require.False(t, tt.accessible, "24-bit color is not accessible, customizable") - - color24bitRed, err := strconv.Atoi(attributes[i+2][1]) - require.NoError(t, err, "Failed to convert 24-bit red color value for testing") - require.True(t, color24bitRed >= 0 && color24bitRed <= 255, "24-bit red color value out of 0-255 range") - - color24bitGreen, err := strconv.Atoi(attributes[i+3][1]) - require.NoError(t, err, "Failed to convert 24-bit green color value for testing") - require.True(t, color24bitGreen >= 0 && color24bitGreen <= 255, "24-bit green color value out of 0-255 range") - - color24bitBlue, err := strconv.Atoi(attributes[i+4][1]) - require.NoError(t, err, "Failed to convert 24-bit blue color value for testing") - require.True(t, color24bitBlue >= 0 && color24bitBlue <= 255, "24-bit blue color value out of 0-255 range") - - i += 5 - case 5: - // 8-bit color display attributes form: - // - ESC[38;5;⟨n⟩m for foreground colors - // - ESC[48;5;⟨n⟩m for background colors - require.False(t, tt.accessible, "8-bit color is not accessible, customizable") - - color8bit, err := strconv.Atoi(attributes[i+2][1]) - require.NoError(t, err, "Failed to convert 8-bit color value for testing") - require.True(t, color8bit >= 0 && color8bit <= 255, "8-bit color value out of 0-255 range") - - i += 3 - default: - require.Fail(t, "Unexpected color depth in attribute") - } - default: - // Increment index as this attribute does not affect accessibility currently - i += 1 - } + + styledText, err := ansi.Parse(out) + require.NoError(t, err) + + for _, st := range styledText { + if tt.accessible { + require.Equalf(t, st.ColourMode, ansi.Default, "Inaccessible color found in '%s' at %d", st, st.Offset) + require.Falsef(t, st.Faint(), "Inaccessible style found in '%s' at %d", st, st.Offset) } } }) @@ -161,7 +97,12 @@ func Test_Render_Codeblocks(t *testing.T) { // match. For more information on ANSI color codes, see // https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit func Test_Render(t *testing.T) { - t.Setenv("GLAMOUR_STYLE", "") + codeBlock := heredoc.Docf(` + %[1]s%[1]s%[1]sgo + fmt.Println("Hello, world!") + %[1]s%[1]s%[1]s + `, "`") + tests := []struct { name string text string @@ -209,14 +150,43 @@ func Test_Render(t *testing.T) { accessibleEnvVar: "true", wantOut: fmt.Sprintf("%s1mh2", brightMagenta_4bitColorSeq), }, + { + name: "when the light theme is selected, the codeblock renders using 8-bit colors", + text: codeBlock, + theme: "light", + wantOut: "\x1b[0m\x1b[38;5;235mfmt\x1b[0m\x1b[38;5;210m.\x1b[0m\x1b[38;5;35mPrintln\x1b[0m\x1b[38;5;210m(\x1b[0m\x1b[38;5;95m\"Hello, world!\"\x1b[0m\x1b[38;5;210m)\x1b[0m", + }, + { + name: "when the dark theme is selected, the codeblock renders using 8-bit colors", + text: codeBlock, + theme: "dark", + wantOut: "\x1b[0m\x1b[38;5;251mfmt\x1b[0m\x1b[38;5;187m.\x1b[0m\x1b[38;5;42mPrintln\x1b[0m\x1b[38;5;187m(\x1b[0m\x1b[38;5;173m\"Hello, world!\"\x1b[0m\x1b[38;5;187m)\x1b[0m", + }, + { + name: "when the accessible env var is set and the light theme is selected, the codeblock renders using 4-bit colors", + text: codeBlock, + theme: "light", + accessibleEnvVar: "true", + wantOut: "\x1b[0m\x1b[30mfmt\x1b[0m\x1b[33m.\x1b[0m\x1b[36mPrintln\x1b[0m\x1b[33m(\x1b[0m\x1b[90m\"Hello, world!\"\x1b[0m\x1b[33m)\x1b[0m", + }, + { + name: "when the accessible env var is set and the dark theme is selected, the codeblock renders using 4-bit colors", + text: codeBlock, + theme: "dark", + accessibleEnvVar: "true", + wantOut: "\x1b[0m\x1b[37mfmt\x1b[0m\x1b[37m.\x1b[0m\x1b[36mPrintln\x1b[0m\x1b[37m(\x1b[0m\x1b[33m\"Hello, world!\"\x1b[0m\x1b[37m)\x1b[0m", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + // Unregister cached chroma style causing codeblock tests to fail based on previous theme + delete(styles.Registry, "charm") t.Setenv(accessibility.ACCESSIBILITY_ENV, tt.accessibleEnvVar) - if tt.styleEnvVar != "" { - tmpDir := t.TempDir() - path := filepath.Join(tmpDir, fmt.Sprintf("%s.json", tt.styleEnvVar)) + if tt.styleEnvVar == "" { + t.Setenv("GLAMOUR_STYLE", "") + } else { + path := filepath.Join(t.TempDir(), fmt.Sprintf("%s.json", tt.styleEnvVar)) err := os.WriteFile(path, []byte(customGlamourStyle(t)), 0644) if err != nil { t.Fatal(err)