diff --git a/go.mod b/go.mod index 54365343..5781f1af 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/charmbracelet/lipgloss go 1.15 require ( + github.com/cockroachdb/datadriven v1.0.2 github.com/knz/lipgloss-convert v0.1.1 github.com/mattn/go-runewidth v0.0.13 github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 diff --git a/go.sum b/go.sum index ca30f0c2..b1ee2fd5 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= +github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= +github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/knz/lipgloss-convert v0.1.1 h1:11Wfcdif9jyyI5H3ohANyqkmIpVPjiBoGZdCzPWz9fA= github.com/knz/lipgloss-convert v0.1.1/go.mod h1:S14GmtoiW/VAHqB7xEzuZOt0/G6GQ2dfjJN0fHpm30Q= diff --git a/style_test.go b/style_test.go index e3c6599d..e321d3b3 100644 --- a/style_test.go +++ b/style_test.go @@ -6,7 +6,9 @@ import ( "testing" "github.com/charmbracelet/lipgloss" + "github.com/cockroachdb/datadriven" lipglossc "github.com/knz/lipgloss-convert" + "github.com/muesli/termenv" ) func BenchmarkStyleRender(b *testing.B) { @@ -115,8 +117,8 @@ func Example_frame() { type S = lipgloss.Style -// TestStyle validates most of the Get, Set and Unset methods. -func TestStyle(t *testing.T) { +// TestStyleMethods validates most of the Get, Set and Unset methods. +func TestStyleMethod(t *testing.T) { g := lipgloss.Color("#0f0") r := lipgloss.Color("#f00") b := lipgloss.Color("#00f") @@ -255,3 +257,63 @@ func TestStyle(t *testing.T) { } } } + +func TestRender(t *testing.T) { + curProfile := lipgloss.ColorProfile() + defer lipgloss.SetColorProfile(curProfile) + + lipgloss.SetColorProfile(termenv.TrueColor) + + datadriven.Walk(t, "testdata", func(t *testing.T, path string) { + d := driver{ + s: lipgloss.NewStyle(), + text: "hello!", + } + datadriven.RunTest(t, path, func(t *testing.T, td *datadriven.TestData) string { + return d.renderTest(t, td) + }) + }) +} + +type driver struct { + s lipgloss.Style + text string + spaces bool +} + +func (d *driver) renderTest(t *testing.T, td *datadriven.TestData) string { + switch td.Cmd { + case "text": + d.text = td.Input + return "ok" + + case "spvis": + d.spaces = !d.spaces + return "ok" + + case "show": + return lipglossc.Export(d.s) + + case "set": + newStyle, err := lipglossc.Import(d.s, td.Input) + if err != nil { + t.Fatalf("%s: invalid style: %v", td.Pos, err) + } + d.s = newStyle + + o := d.s.Render(d.text) + o = strings.ReplaceAll(o, "\n", "␤\n") + if !d.spaces { + o = strings.ReplaceAll(o, " ", "·") + } + // Add a "no newline at end" marker if there was no newline at the end. + if len(o) == 0 || o[len(o)-1] != '\n' { + o += "🛇" + } + return o + + default: + t.Fatalf("%s: unknown command: %q", td.Pos, td.Cmd) + return "" // unreachable + } +} diff --git a/testdata/align b/testdata/align new file mode 100644 index 00000000..748f76b2 --- /dev/null +++ b/testdata/align @@ -0,0 +1,55 @@ +set +width: 20; +---- +hello!··············🛇 + +subtest single_line + +set +align: right +---- +··············hello!🛇 + +set +align: center +---- +·······hello!·······🛇 + +set +align: left +---- +hello!··············🛇 + +subtest end + +subtest multi_line + +text +small + +long line +---- +ok + +set +align: right +---- +···············small␤ +····················␤ +···········long·line🛇 + +set +align: center +---- +·······small········␤ +····················␤ +·····long·line······🛇 + +set +align: left +---- +small···············␤ +····················␤ +long·line···········🛇 + +subtest end diff --git a/testdata/borders b/testdata/borders new file mode 100644 index 00000000..8a7e32b4 --- /dev/null +++ b/testdata/borders @@ -0,0 +1,254 @@ +# This text has the following properties: +# 2nd line is longer than the first, to demonstrate padding on 1st line. +# 3rd line is shorter than the first two, to demonstrate padding on the right. +text +hello +universe +!! +---- +ok + +# We also make the style 1 higher, to demonstrate border padded around empty lines. +set +height: 4 +---- +hello···␤ +universe␤ +!!······␤ +········🛇 + +subtest border_styles + +set +border: normal +---- +┌────────┐␤ +│hello···│␤ +│universe│␤ +│!!······│␤ +│········│␤ +└────────┘🛇 + +set +border: double +---- +╔════════╗␤ +║hello···║␤ +║universe║␤ +║!!······║␤ +║········║␤ +╚════════╝🛇 + +set +border: thick +---- +┏━━━━━━━━┓␤ +┃hello···┃␤ +┃universe┃␤ +┃!!······┃␤ +┃········┃␤ +┗━━━━━━━━┛🛇 + +set +border: rounded +---- +╭────────╮␤ +│hello···│␤ +│universe│␤ +│!!······│␤ +│········│␤ +╰────────╯🛇 + +set +border: hidden +---- +··········␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +··········🛇 + +subtest end + +subtest border_parts + +set +border: border("","","","","","","","") +---- +hello···␤ +universe␤ +!!······␤ +········🛇 + +set +border: border("a","","","","","","","") true +---- +·aaaaaaaa·␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +··········🛇 + +set +border: border("","a","","","","","","") true +---- +··········␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +·aaaaaaaa·🛇 + +set +border: border("","","a","","","","","") +---- +··········␤ +ahello····␤ +auniverse·␤ +a!!·······␤ +a·········␤ +··········🛇 + +set +border: border("","","","a","","","","") +---- +··········␤ +·hello···a␤ +·universea␤ +·!!······a␤ +·········a␤ +··········🛇 + +set +border: border("","","","","a","","","") +---- +a·········␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +··········🛇 + +set +border: border("","","","","","a","","") +---- +·········a␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +··········🛇 + +set +border: border("","","","","","","a","") +---- +··········␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +·········a🛇 + +set +border: border("","","","","","","","a") +---- +··········␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +a·········🛇 + +subtest end + +subtest border_wide_parts + +# The following tests use a wide rune as border. + +set +border: border("⏩","","","","","","","") true +---- +·⏩⏩⏩⏩·␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +··········🛇 + +set +border: border("","⏩","","","","","","") true +---- +··········␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +·⏩⏩⏩⏩·🛇 + +set +border: border("","","⏩","","","","","") +---- +···········␤ +⏩hello····␤ +⏩universe·␤ +⏩!!·······␤ +⏩·········␤ +···········🛇 + +set +border: border("","","","⏩","","","","") +---- +··········␤ +·hello···⏩␤ +·universe⏩␤ +·!!······⏩␤ +·········⏩␤ +··········🛇 + +set +border: border("","","","","⏩","","","") +---- +⏩········␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +··········🛇 + +# Rendering bug? +# see: https://github.com/charmbracelet/lipgloss/issues/114 +set +border: border("","","","","","⏩","","") +---- +·········⏩␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +··········🛇 + +# Rendering bug? +# see: https://github.com/charmbracelet/lipgloss/issues/114 +set +border: border("","","","","","","⏩","") +---- +··········␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +·········⏩🛇 + +set +border: border("","","","","","","","⏩") +---- +··········␤ +·hello····␤ +·universe·␤ +·!!·······␤ +··········␤ +⏩········🛇 + +subtest end diff --git a/testdata/color b/testdata/color new file mode 100644 index 00000000..7b73d678 --- /dev/null +++ b/testdata/color @@ -0,0 +1,126 @@ +set +width: 20; +---- +hello!··············🛇 + +# The remainder of this test is best viewed +# with a text editor that renders ANSI sequences. + +set +foreground: #f00 +---- +hello!··············🛇 + +set +background: #0f0 +---- +hello!··············🛇 + +subtest fill_colors + +set +width: 20; +height: 3; +background: unset; +foreground: #f00; +---- +hello!··············␤ +····················␤ +····················🛇 + +set +foreground: unset; +background: #f00; +---- +hello!··············␤ +····················␤ +····················🛇 + +subtest end + +subtest border_colors + +set +clear; +width: 20; +border-style: normal; +border-foreground: #f00 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + +set +border-foreground: unset; +border-top-foreground: #f00 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + +set +border-foreground: unset; +border-left-foreground: #f00 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + +set +border-foreground: unset; +border-right-foreground: #f00 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + +set +border-foreground: unset; +border-bottom-foreground: #f00 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + +set +border-foreground: unset; +border-background: #0f0 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + +set +border-background: unset; +border-top-background: #0f0 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + +set +border-background: unset; +border-left-background: #0f0 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + +set +border-background: unset; +border-right-background: #0f0 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + +set +border-background: unset; +border-bottom-background: #0f0 +---- +┌────────────────────┐␤ +│hello!··············│␤ +└────────────────────┘🛇 + + +subtest end diff --git a/testdata/formatting b/testdata/formatting new file mode 100644 index 00000000..575ef7b2 --- /dev/null +++ b/testdata/formatting @@ -0,0 +1,101 @@ +# Use a text with a space, to show space styling. +text +hello world! +---- +ok + +# Toggle display of spaces. +spvis +---- +ok + +set +clear; width: 20; +underline: true +---- +hello    world! 🛇 + +# Seemingly no difference! +# See issue: https://github.com/charmbracelet/lipgloss/issues/113 +set +clear; width: 20; +underline: true; underline-spaces: true +---- +hello    world! 🛇 + +# Weird output. +# See issue: https://github.com/charmbracelet/lipgloss/issues/113 +set +clear; width: 20; +underline: true; underline-spaces: false +---- +hello world! 🛇 + +# If underline is not set, underline-spaces does nothing. +set +clear; width: 20; +underline-spaces: true +---- +hello world! 🛇 + +set +clear; width: 20; +italic: true +---- +hello world! 🛇 + + +set +clear; width: 20; +bold: true +---- +hello world! 🛇 + + +set +clear; width: 20; +strikethrough: true +---- +hello    world! 🛇 + +# Seemingly no difference! +# See issue: https://github.com/charmbracelet/lipgloss/issues/113 +set +clear; width: 20; +strikethrough: true; strikethrough-spaces: true +---- +hello    world! 🛇 + +# Weird output. +# See issue: https://github.com/charmbracelet/lipgloss/issues/113 +set +clear; width: 20; +strikethrough: true; strikethrough-spaces: false +---- +hello world! 🛇 + +# If strikethrough is not set, strikethrough-spaces does nothing. +set +clear; width: 20; +strikethrough-spaces: true +---- +hello world! 🛇 + +set +clear; width: 20; +reverse: true +---- +hello world! 🛇 + + +set +clear; width: 20; +blink: true +---- +hello world! 🛇 + +set +clear; width: 20; +faint: true +---- +hello world! 🛇 diff --git a/testdata/spacing b/testdata/spacing new file mode 100644 index 00000000..9777c6a8 --- /dev/null +++ b/testdata/spacing @@ -0,0 +1,96 @@ +text +hello +world +---- +ok + +# Show height fill. +set +width: 10; +height: 4 +---- +hello·····␤ +world·····␤ +··········␤ +··········🛇 + +# Data wider than style: the style does wrapping. +set +width: 3; +---- +hel␤ +lo·␤ +wor␤ +ld·🛇 + +# Data taller than style. +# All lines in the input are rendered. +set +width: 10; +height: 1 +---- +hello·····␤ +world·····🛇 + +# Data both wider and taller than style. +# All lines in the input are rendered with wrapping. +set +width: 3; +height: 1 +---- +hel␤ +lo·␤ +wor␤ +ld·🛇 + +# Data wider than max width: data gets truncated. +# Render not wider than max width. +set +clear; +width: 10; +height: 4; +max-width: 3; +---- +hel␤ +wor␤ +···␤ +···🛇 + +# Data taller than max height: data gets truncated. +# Render not taller than max height. +set +clear; +width: 10; +height: 4; +max-height: 3; +---- +hello·····␤ +world·····␤ +··········🛇 + +set +max-height: 1 +---- +hello·····🛇 + +# If max-weight is larger than height, +# and the input is smaller than height, +# we don't get padding. +set +clear; +height: 2; +max-height: 4 +---- +hello␤ +world🛇 + +# If we get wrapping due to limited width +# max-height applies after the wrapping. +set +height: unset; +width: 3; +max-height: 3; +---- +hel␤ +lo·␤ +wor🛇