▍ humdrum codex / glint v1.0.2
license AGPL-3.0
3.1 KB raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
package preview

import (
	"reflect"
	"regexp"
	"strings"
	"testing"

	"github.com/charmbracelet/glamour/styles"
)

func TestLegibleTextContrasts(t *testing.T) {
	// Dark/mid accents → light text; very light bg → dark text.
	if got := legibleText("#4385BE"); got != "#FFFCF0" {
		t.Errorf("legibleText(blue) = %q, want light", got)
	}
	if got := legibleText("#FF5FAF"); got != "#FFFCF0" {
		t.Errorf("legibleText(pink) = %q, want light", got)
	}
	if got := legibleText("#F0E6BE"); got != "#100F0F" {
		t.Errorf("legibleText(pale yellow) = %q, want dark", got)
	}
}

func TestApplyThemeClearsBackgroundsAndH1(t *testing.T) {
	cfg := styles.DarkStyleConfig
	applyTheme(&cfg, Colors{Background: "#100F0F", Text: "#CECDC3", Heading: "#4385BE", Code: "#879A39", Link: "#3AA99F"})
	// No element keeps glamour's dark code/table panel: every BackgroundColor is the bg.
	var bad []string
	var walk func(v reflect.Value, path string)
	walk = func(v reflect.Value, path string) {
		switch v.Kind() {
		case reflect.Pointer:
			if !v.IsNil() {
				walk(v.Elem(), path)
			}
		case reflect.Struct:
			tp := v.Type()
			for i := 0; i < v.NumField(); i++ {
				f := v.Field(i)
				name := tp.Field(i).Name
				if name == "BackgroundColor" && f.Type() == reflect.TypeOf((*string)(nil)) && !f.IsNil() {
					if *f.Interface().(*string) != "#100F0F" && !strings.HasPrefix(path, ".H1") {
						bad = append(bad, path)
					}
					continue
				}
				walk(f, path+"."+name)
			}
		}
	}
	walk(reflect.ValueOf(cfg), "")
	if len(bad) > 0 {
		t.Errorf("backgrounds not cleared to theme bg at: %v", bad)
	}
	// H1 is the heading-color bar with legible text.
	if cfg.H1.BackgroundColor == nil || *cfg.H1.BackgroundColor != "#4385BE" {
		t.Errorf("H1 background not the heading color")
	}
	if cfg.H1.Color == nil || *cfg.H1.Color != "#FFFCF0" {
		t.Errorf("H1 text not legible color, got %v", cfg.H1.Color)
	}
}

// Headings below H1 should sit directly above their body — no blank line.
func TestRenderNoBlankLineAfterSubheadings(t *testing.T) {
	m := New("")
	m.SetColors(Colors{Background: "#100F0F", Text: "#CECDC3", Heading: "#4385BE", Code: "#879A39", Link: "#3AA99F"})
	m.SetSize(80, 40)
	if err := m.Render("# Title\n\nintro\n\n## Section\n\nbody\n\n### Sub\n\nmore\n"); err != nil {
		t.Fatal(err)
	}
	lines := stripANSI(m.vp.View())
	idx := func(want string) int {
		for i, ln := range lines {
			if strings.TrimSpace(ln) == want {
				return i
			}
		}
		t.Fatalf("line %q not found in:\n%s", want, strings.Join(lines, "\n"))
		return -1
	}
	// H2/H3 are immediately followed by their body — no intervening blank line.
	if got := strings.TrimSpace(lines[idx("## Section")+1]); got != "body" {
		t.Errorf("expected body directly after H2, got %q", got)
	}
	if got := strings.TrimSpace(lines[idx("### Sub")+1]); got != "more" {
		t.Errorf("expected body directly after H3, got %q", got)
	}
	// H1 keeps its trailing blank line.
	if got := strings.TrimSpace(lines[idx("Title")+1]); got != "" {
		t.Errorf("expected blank line after H1, got %q", got)
	}
}

var ansiRE = regexp.MustCompile("\x1b\\[[0-9;]*m")

func stripANSI(s string) []string {
	return strings.Split(ansiRE.ReplaceAllString(s, ""), "\n")
}