package textfmt_test import ( "bytes" "code.squareroundforest.org/arpio/textfmt" "testing" ) func TestTeletype(t *testing.T) { t.Run("invalid", func(t *testing.T) { var b bytes.Buffer if err := textfmt.Teletype(&b, textfmt.Doc(textfmt.Entry{})); err == nil { t.Fatal("failed to fail") } }) t.Run("empty", func(t *testing.T) { var b bytes.Buffer if err := textfmt.Teletype(&b, textfmt.Doc()); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("example", func(t *testing.T) { doc := textfmt.Doc( textfmt.Title(0, "Example Text"), textfmt.Wrap( textfmt.Indent( textfmt.Paragraph(textfmt.Text("Below you can find some test text, with various text items.")), 8, 0, ), 30, ), textfmt.Title(1, "Document syntax:"), textfmt.Indent( textfmt.Syntax( textfmt.Symbol("textfmt.Doc"), textfmt.Symbol("("), textfmt.ZeroOrMore(textfmt.Symbol("Entry")), textfmt.Symbol(")"), ), 8, 0, ), textfmt.Title(1, "Entries:"), textfmt.Paragraph(textfmt.Text("textfmt supports the following entries:")), textfmt.List( textfmt.Item(textfmt.Text("CodeBlock")), textfmt.Item(textfmt.Text("DefinitionList")), textfmt.Item(textfmt.Text("List")), textfmt.Item(textfmt.Text("NumberedDefinitionList")), textfmt.Item(textfmt.Text("NumberedList")), textfmt.Item(textfmt.Text("Paragraph")), textfmt.Item(textfmt.Text("Syntax")), textfmt.Item(textfmt.Text("Table")), textfmt.Item(textfmt.Text("Title")), ), textfmt.Title(1, "Entry explanations:"), textfmt.Wrap( textfmt.DefinitionList( textfmt.Definition( textfmt.Text("CodeBlock"), textfmt.Text("a multiline block of code"), ), textfmt.Definition( textfmt.Text("DefinitionList"), textfmt.Text("a list of definitions like this one"), ), textfmt.Definition( textfmt.Text("List"), textfmt.Text("a list of items"), ), textfmt.Definition( textfmt.Text("NumberedDefinitionList"), textfmt.Text("numbered definitions"), ), textfmt.Definition( textfmt.Text("NumberedList"), textfmt.Text("numbered list"), ), textfmt.Definition( textfmt.Text("Paragraph"), textfmt.Text("paragraph of text"), ), textfmt.Definition( textfmt.Text("Syntax"), textfmt.Text("a syntax expression"), ), textfmt.Definition( textfmt.Text("Table"), textfmt.Text("a table"), ), textfmt.Definition( textfmt.Text("Title"), textfmt.Text("a title"), ), ), 48, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `Example Text Below you can find some test text, with various text items. Document syntax: textfmt.Doc ( [Entry]... ) Entries: textfmt supports the following entries: - CodeBlock - DefinitionList - List - NumberedDefinitionList - NumberedList - Paragraph - Syntax - Table - Title Entry explanations: - CodeBlock: a multiline block of code - DefinitionList: a list of definitions like this one - List: a list of items - NumberedDefinitionList: numbered definitions - NumberedList: numbered list - Paragraph: paragraph of text - Syntax: a syntax expression - Table: a table - Title: a title ` if b.String() != expect { t.Log(b.String()) t.Log(expect) logBytes(t, b.String()) logBytes(t, expect) t.Fatal() } }) t.Run("escape", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc(textfmt.Paragraph(textfmt.Text("Some sample text...\x00\nwith invalid chars."))) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = "Some sample text...ยท with invalid chars.\n" if b.String() != expect { logBytes(t, b.String()) logBytes(t, expect) t.Fatal(b.String()) } }) t.Run("styling ignored", func(t *testing.T) { doc := textfmt.Doc( textfmt.Paragraph( textfmt.Cat( textfmt.Italic(textfmt.Text("Some sample text... with")), textfmt.Bold(textfmt.Text("some")), textfmt.Italic(textfmt.Text("styling.")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "Some sample text... with some styling.\n" { t.Fatal(b.String()) } }) t.Run("single line by default", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc(textfmt.Paragraph(textfmt.Text("Some sample text...\n\non multiple lines."))) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "Some sample text... on multiple lines.\n" { t.Fatal(b.String()) } }) t.Run("wrap", func(t *testing.T) { t.Run("unindented", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc( textfmt.Wrap(textfmt.Paragraph(textfmt.Text("Some sample text...\n on multiple lines.")), 15), ) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "Some sample\ntext... on\nmultiple lines.\n" { t.Fatal(b.String()) } }) t.Run("indented", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap(textfmt.Paragraph(textfmt.Text("Some sample text...\n on multiple lines.")), 15), 4, 2, ), ) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != " Some sample\n text... on\n multiple\n lines.\n" { t.Fatal(b.String()) } }) }) t.Run("indent", func(t *testing.T) { t.Run("indent uniform", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap(textfmt.Paragraph(textfmt.Text("Some sample text...\non multiple lines.")), 15), 2, 2, ), ) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != " Some sample\n text... on\n multiple\n lines.\n" { t.Fatal(b.String()) } }) t.Run("indent first in", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap(textfmt.Paragraph(textfmt.Text("Some sample text...\non multiple lines.")), 15), 4, 2, ), ) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != " Some sample\n text... on\n multiple\n lines.\n" { t.Fatal(b.String()) } }) t.Run("indent first out", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap(textfmt.Paragraph(textfmt.Text("Some sample text...\non multiple lines.")), 15), 0, 2, ), ) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "Some sample\n text... on\n multiple\n lines.\n" { t.Fatal(b.String()) } }) }) t.Run("failing writer", func(t *testing.T) { w := &failingWriter{failAfter: 15} doc := textfmt.Doc( textfmt.Paragraph(textfmt.Text("Some sample text...\non multiple lines.")), ) if err := textfmt.Teletype(w, doc); err == nil { t.Fatal("failed to fail") } }) t.Run("concatenate", func(t *testing.T) { doc := textfmt.Doc( textfmt.Paragraph( textfmt.Cat( textfmt.Text("Text from"), textfmt.Text("multiple"), textfmt.Text("pieces."), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "Text from multiple pieces.\n" { t.Fatal(b.String()) } }) t.Run("link", func(t *testing.T) { t.Run("without label", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc(textfmt.Paragraph(textfmt.Link("", "https://sqrndfst.org"))) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "https://sqrndfst.org\n" { t.Fatal(b.String()) } }) t.Run("with label", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc(textfmt.Paragraph(textfmt.Link("a link", "https://sqrndfst.org"))) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "a link (https://sqrndfst.org)\n" { t.Fatal(b.String()) } }) }) t.Run("title", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc(textfmt.Title(0, "This is a title")) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "This is a title\n" { t.Fatal(b.String()) } }) t.Run("paragraph", func(t *testing.T) { t.Run("unwrapped", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc(textfmt.Paragraph(textfmt.Text("This is a paragraph."))) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "This is a paragraph.\n" { t.Fatal(b.String()) } }) t.Run("wrapped", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc( textfmt.Wrap(textfmt.Paragraph(textfmt.Text("This is a paragraph.")), 8), ) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "This is\na\nparagraph.\n" { t.Fatal(b.String()) } }) t.Run("indent without wrapping", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc( textfmt.Indent(textfmt.Paragraph(textfmt.Text("This is a paragraph.")), 4, 0), ) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != " This is a paragraph.\n" { t.Fatal(b.String()) } }) t.Run("indent in", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap(textfmt.Paragraph(textfmt.Text("This is a paragraph.")), 12), 4, 0, ), ) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != " This is\na paragraph.\n" { t.Fatal(b.String()) } }) t.Run("indent out", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap(textfmt.Paragraph(textfmt.Text("This is a paragraph.")), 12), 0, 4, ), ) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "This is a\n paragraph.\n" { t.Fatal(b.String()) } }) }) t.Run("list", func(t *testing.T) { t.Run("unwrapped", func(t *testing.T) { doc := textfmt.Doc( textfmt.List( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `- this is an item - this is another item - this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("wrapped", func(t *testing.T) { doc := textfmt.Doc( textfmt.Wrap( textfmt.List( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), 12, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `- this is an item - this is another item - this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent without wrapping", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.List( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), 4, 0, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` - this is an item - this is another item - this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent in", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap( textfmt.List( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), 18, ), 4, -2, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` - this is an item - this is another item - this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent out", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap( textfmt.List( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), 18, ), 0, 2, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `- this is an item - this is another item - this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) }) t.Run("numbered list", func(t *testing.T) { t.Run("unwrapped", func(t *testing.T) { doc := textfmt.Doc( textfmt.NumberedList( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1. this is an item 2. this is another item 3. this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("wrapped", func(t *testing.T) { doc := textfmt.Doc( textfmt.Wrap( textfmt.NumberedList( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), 15, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1. this is an item 2. this is another item 3. this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent without wrapping", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.NumberedList( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), 4, 0, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` 1. this is an item 2. this is another item 3. this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent in", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap( textfmt.NumberedList( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), 21, ), 4, -3, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` 1. this is an item 2. this is another item 3. this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent out", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap( textfmt.NumberedList( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is a third item")), ), 21, ), 0, 3, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1. this is an item 2. this is another item 3. this is a third item ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("long numbered list", func(t *testing.T) { doc := textfmt.Doc( textfmt.NumberedList( textfmt.Item(textfmt.Text("this is an item")), textfmt.Item(textfmt.Text("this is another item")), textfmt.Item(textfmt.Text("this is the third item")), textfmt.Item(textfmt.Text("this is the fourth item")), textfmt.Item(textfmt.Text("this is the fifth item")), textfmt.Item(textfmt.Text("this is the sixth item")), textfmt.Item(textfmt.Text("this is the seventh item")), textfmt.Item(textfmt.Text("this is the eighth item")), textfmt.Item(textfmt.Text("this is the nineth item")), textfmt.Item(textfmt.Text("this is the tenth item")), textfmt.Item(textfmt.Text("this is the eleventh item")), textfmt.Item(textfmt.Text("this is the twelveth item")), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1. this is an item 2. this is another item 3. this is the third item 4. this is the fourth item 5. this is the fifth item 6. this is the sixth item 7. this is the seventh item 8. this is the eighth item 9. this is the nineth item 10. this is the tenth item 11. this is the eleventh item 12. this is the twelveth item ` if b.String() != expect { t.Fatal(b.String()) } }) }) t.Run("definition list", func(t *testing.T) { t.Run("unwrapped", func(t *testing.T) { doc := textfmt.Doc( textfmt.DefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `- red: looks like strawberry - green: looks like grass - blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("wrapped", func(t *testing.T) { doc := textfmt.Doc( textfmt.Wrap( textfmt.DefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), 24, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `- red: looks like strawberry - green: looks like grass - blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent without wrapping", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.DefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), 4, 0, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` - red: looks like strawberry - green: looks like grass - blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent in", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap( textfmt.DefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), 21, ), 3, -9, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` - red: looks like strawberry - green: looks like grass - blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent out", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap( textfmt.DefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), 21, ), 0, 4, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `- red: looks like strawberry - green: looks like grass - blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) }) t.Run("numbered definition list", func(t *testing.T) { t.Run("unwrapped", func(t *testing.T) { doc := textfmt.Doc( textfmt.NumberedDefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1. red: looks like strawberry 2. green: looks like grass 3. blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("wrapped", func(t *testing.T) { doc := textfmt.Doc( textfmt.Wrap( textfmt.NumberedDefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), 24, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1. red: looks like strawberry 2. green: looks like grass 3. blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent without wrapping", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.NumberedDefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), 4, 0, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` 1. red: looks like strawberry 2. green: looks like grass 3. blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent in", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap( textfmt.NumberedDefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), 21, ), 3, -9, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` 1. red: looks like strawberry 2. green: looks like grass 3. blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent out", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap( textfmt.NumberedDefinitionList( textfmt.Definition(textfmt.Text("red"), textfmt.Text("looks like strawberry")), textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")), textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")), ), 21, ), 0, 4, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1. red: looks like strawberry 2. green: looks like grass 3. blue: looks like sky ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("long numbered definition list", func(t *testing.T) { doc := textfmt.Doc( textfmt.NumberedDefinitionList( textfmt.Definition(textfmt.Text("one"), textfmt.Text("this is an item")), textfmt.Definition(textfmt.Text("two"), textfmt.Text("this is another item")), textfmt.Definition(textfmt.Text("three"), textfmt.Text("this is the third item")), textfmt.Definition(textfmt.Text("four"), textfmt.Text("this is the fourth item")), textfmt.Definition(textfmt.Text("five"), textfmt.Text("this is the fifth item")), textfmt.Definition(textfmt.Text("six"), textfmt.Text("this is the sixth item")), textfmt.Definition(textfmt.Text("seven"), textfmt.Text("this is the seventh item")), textfmt.Definition(textfmt.Text("eight"), textfmt.Text("this is the eighth item")), textfmt.Definition(textfmt.Text("nine"), textfmt.Text("this is the nineth item")), textfmt.Definition(textfmt.Text("ten"), textfmt.Text("this is the tenth item")), textfmt.Definition(textfmt.Text("eleven"), textfmt.Text("this is the eleventh item")), textfmt.Definition(textfmt.Text("twelve"), textfmt.Text("this is the twelveth item")), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1. one: this is an item 2. two: this is another item 3. three: this is the third item 4. four: this is the fourth item 5. five: this is the fifth item 6. six: this is the sixth item 7. seven: this is the seventh item 8. eight: this is the eighth item 9. nine: this is the nineth item 10. ten: this is the tenth item 11. eleven: this is the eleventh item 12. twelve: this is the twelveth item ` if b.String() != expect { t.Fatal(b.String()) } }) }) t.Run("table", func(t *testing.T) { t.Run("now rows", func(t *testing.T) { var b bytes.Buffer doc := textfmt.Doc(textfmt.Table()) if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "\n" { t.Fatal(b.String()) } }) t.Run("no columns", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Row(), textfmt.Row(), textfmt.Row(), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "\n" { t.Fatal(b.String()) } }) t.Run("basic", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Row( textfmt.Cell(textfmt.Text("1")), textfmt.Cell(textfmt.Text("2")), textfmt.Cell(textfmt.Text("3")), ), textfmt.Row( textfmt.Cell(textfmt.Text("4")), textfmt.Cell(textfmt.Text("5")), textfmt.Cell(textfmt.Text("6")), ), textfmt.Row( textfmt.Cell(textfmt.Text("7")), textfmt.Cell(textfmt.Text("8")), textfmt.Cell(textfmt.Text("9")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1 | 2 | 3 --------- 4 | 5 | 6 --------- 7 | 8 | 9 ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("basic with header", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Header( textfmt.Cell(textfmt.Text("1")), textfmt.Cell(textfmt.Text("-1")), textfmt.Cell(textfmt.Text("0")), ), textfmt.Row( textfmt.Cell(textfmt.Text("1")), textfmt.Cell(textfmt.Text("2")), textfmt.Cell(textfmt.Text("3")), ), textfmt.Row( textfmt.Cell(textfmt.Text("4")), textfmt.Cell(textfmt.Text("5")), textfmt.Cell(textfmt.Text("6")), ), textfmt.Row( textfmt.Cell(textfmt.Text("7")), textfmt.Cell(textfmt.Text("8")), textfmt.Cell(textfmt.Text("9")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1 | -1 | 0 ========== 1 | 2 | 3 ---------- 4 | 5 | 6 ---------- 7 | 8 | 9 ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("header without rows", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Header( textfmt.Cell(textfmt.Text("foo")), textfmt.Cell(textfmt.Text("bar")), textfmt.Cell(textfmt.Text("baz")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "foo | bar | baz\n===============\n" { t.Fatal(b.String()) } }) t.Run("single row", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Row( textfmt.Cell(textfmt.Text("1")), textfmt.Cell(textfmt.Text("2")), textfmt.Cell(textfmt.Text("3")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "1 | 2 | 3\n" { t.Fatal(b.String()) } }) t.Run("single row with header", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Header( textfmt.Cell(textfmt.Text("foo")), textfmt.Cell(textfmt.Text("bar")), textfmt.Cell(textfmt.Text("baz")), ), textfmt.Row( textfmt.Cell(textfmt.Text("1")), textfmt.Cell(textfmt.Text("2")), textfmt.Cell(textfmt.Text("3")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "foo | bar | baz\n===============\n1 | 2 | 3 \n" { t.Fatal(b.String()) } }) t.Run("single column", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Row( textfmt.Cell(textfmt.Text("1")), ), textfmt.Row( textfmt.Cell(textfmt.Text("4")), ), textfmt.Row( textfmt.Cell(textfmt.Text("7")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "1\n-\n4\n-\n7\n" { t.Fatal(b.String()) } }) t.Run("single column with header", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Header( textfmt.Cell(textfmt.Text("foo")), ), textfmt.Row( textfmt.Cell(textfmt.Text("1")), ), textfmt.Row( textfmt.Cell(textfmt.Text("4")), ), textfmt.Row( textfmt.Cell(textfmt.Text("7")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "foo\n===\n1 \n---\n4 \n---\n7 \n" { t.Fatal(b.String()) } }) t.Run("single row and single column", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Row( textfmt.Cell(textfmt.Text("1")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "1\n" { t.Fatal(b.String()) } }) t.Run("single row and single column with header", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Header( textfmt.Cell(textfmt.Text("foo")), ), textfmt.Row( textfmt.Cell(textfmt.Text("1")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "foo\n===\n1 \n" { t.Fatal(b.String()) } }) t.Run("unequal number of row cells", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Row( textfmt.Cell(textfmt.Text("1")), textfmt.Cell(textfmt.Text("2")), ), textfmt.Row( textfmt.Cell(textfmt.Text("4")), ), textfmt.Row( textfmt.Cell(textfmt.Text("7")), textfmt.Cell(textfmt.Text("8")), textfmt.Cell(textfmt.Text("9")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1 | 2 | --------- 4 | | --------- 7 | 8 | 9 ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("unequal number of row cells with header", func(t *testing.T) { doc := textfmt.Doc( textfmt.Table( textfmt.Header( textfmt.Cell(textfmt.Text("1")), textfmt.Cell(textfmt.Text("-1")), ), textfmt.Row( textfmt.Cell(textfmt.Text("1")), textfmt.Cell(textfmt.Text("2")), textfmt.Cell(textfmt.Text("3")), ), textfmt.Row( textfmt.Cell(textfmt.Text("4")), ), textfmt.Row( textfmt.Cell(textfmt.Text("")), textfmt.Cell(textfmt.Text("8")), textfmt.Cell(textfmt.Text("9")), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `1 | -1 | ========== 1 | 2 | 3 ---------- 4 | | ---------- | 8 | 9 ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("all empty cells", func(t *testing.T) { doc := textfmt.Doc( textfmt.Wrap( textfmt.Table( textfmt.Header(textfmt.Cell(textfmt.Text("")), textfmt.Cell(textfmt.Text(""))), textfmt.Row(textfmt.Cell(textfmt.Text("")), textfmt.Cell(textfmt.Text(""))), textfmt.Row(textfmt.Cell(textfmt.Text("")), textfmt.Cell(textfmt.Text(""))), ), 72, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != " | \n===\n | \n---\n | \n" { t.Fatal(b.String()) } }) t.Run("no new lines by default", func(t *testing.T) { const txt = ` Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine (Pinus sylvestris). ` doc := textfmt.Doc( textfmt.Table( textfmt.Header(textfmt.Cell(textfmt.Text("Forest"))), textfmt.Row(textfmt.Cell(textfmt.Text(txt))), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal() } // << const expect = `Forest ===================================================================================================================================== Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine (Pinus sylvestris). ` if b.String() != expect { t.Fatal(b.String()) } }) t.Run("indent without wrap", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Table( textfmt.Header( textfmt.Cell(textfmt.Text("one")), textfmt.Cell(textfmt.Text("two")), textfmt.Cell(textfmt.Text("three")), ), textfmt.Row( textfmt.Cell(textfmt.Text("Walking through the mixed forests of Brandenburg in early autumn")), textfmt.Cell(textfmt.Text("one notices the dominant presence of Scots pine (Pinus sylvestris)")), textfmt.Cell(textfmt.Text("interspersed with sessile oak (Quercus petraea)")), ), textfmt.Row( textfmt.Cell(textfmt.Text("and silver birch (Betula pendula)")), textfmt.Cell(textfmt.Text("their canopies creating a mosaic of light")), textfmt.Cell(textfmt.Text("and shadow on the forest floor")), ), ), 0, 4, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` one | two | three ======================================================================================================================================================================================= Walking through the mixed forests of Brandenburg in early autumn | one notices the dominant presence of Scots pine (Pinus sylvestris) | interspersed with sessile oak (Quercus petraea) --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- and silver birch (Betula pendula) | their canopies creating a mosaic of light | and shadow on the forest floor ` if b.String() != expect { logBytes(t, expect) logBytes(t, b.String()) t.Log("\n" + expect) t.Fatal("\n" + b.String()) } }) t.Run("wrap without indent", func(t *testing.T) { doc := textfmt.Doc( textfmt.Wrap( textfmt.Table( textfmt.Header( textfmt.Cell(textfmt.Text("one")), textfmt.Cell(textfmt.Text("two")), textfmt.Cell(textfmt.Text("three")), ), textfmt.Row( textfmt.Cell(textfmt.Text("Walking through the mixed forests of Brandenburg in early autumn")), textfmt.Cell(textfmt.Text("one notices the dominant presence of Scots pine (Pinus sylvestris)")), textfmt.Cell(textfmt.Text("interspersed with sessile oak (Quercus petraea)")), ), textfmt.Row( textfmt.Cell(textfmt.Text("and silver birch (Betula pendula)")), textfmt.Cell(textfmt.Text("their canopies creating a mosaic of light")), textfmt.Cell(textfmt.Text("and shadow on the forest floor")), ), ), 72, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = `one | two | three =================================================================== Walking through the | one notices the dominant | interspersed with mixed forests of | presence of Scots pine | sessile oak Brandenburg in early | (Pinus sylvestris) | (Quercus petraea) autumn | | ------------------------------------------------------------------- and silver birch | their canopies creating | and shadow on the (Betula pendula) | a mosaic of light | forest floor ` if b.String() != expect { logBytes(t, expect) logBytes(t, b.String()) t.Log("\n" + expect) t.Fatal("\n" + b.String()) } }) t.Run("indent and wrap", func(t *testing.T) { doc := textfmt.Doc( textfmt.Indent( textfmt.Wrap( textfmt.Table( textfmt.Header( textfmt.Cell(textfmt.Text("one")), textfmt.Cell(textfmt.Text("two")), textfmt.Cell(textfmt.Text("three")), ), textfmt.Row( textfmt.Cell(textfmt.Text("Walking through the mixed forests of Brandenburg in early autumn")), textfmt.Cell(textfmt.Text("one notices the dominant presence of Scots pine (Pinus sylvestris)")), textfmt.Cell(textfmt.Text("interspersed with sessile oak (Quercus petraea)")), ), textfmt.Row( textfmt.Cell(textfmt.Text("and silver birch (Betula pendula)")), textfmt.Cell(textfmt.Text("their canopies creating a mosaic of light")), textfmt.Cell(textfmt.Text("and shadow on the forest floor")), ), ), 72, ), 0, 4, ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } const expect = ` one | two | three ================================================================== Walking through the | one notices the | interspersed with mixed forests of | dominant presence of | sessile oak Brandenburg in early | Scots pine (Pinus | (Quercus petraea) autumn | sylvestris) | ------------------------------------------------------------------ and silver birch | their canopies creating | and shadow on the (Betula pendula) | a mosaic of light | forest floor ` if b.String() != expect { logBytes(t, expect) logBytes(t, b.String()) t.Log("\n" + expect) t.Fatal("\n" + b.String()) } }) }) t.Run("code", func(t *testing.T) { const code = `func() textfmt.Document { return textfmt.Document( textfmt.Paragraph(textfmt.Text("Hello, world!")), ) }` var b bytes.Buffer if err := textfmt.Teletype(&b, textfmt.Doc(textfmt.CodeBlock(code))); err != nil { t.Fatal(err) } if b.String() != code+"\n" { t.Fatal(b.String()) } }) t.Run("syntax", func(t *testing.T) { t.Run("symbol", func(t *testing.T) { doc := textfmt.Doc(textfmt.Syntax(textfmt.Symbol("foo"))) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "foo\n" { t.Fatal(b.String()) } }) t.Run("zero or more symbols", func(t *testing.T) { doc := textfmt.Doc(textfmt.Syntax(textfmt.ZeroOrMore(textfmt.Symbol("foo")))) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "[foo]...\n" { t.Fatal(b.String()) } }) t.Run("one or more", func(t *testing.T) { doc := textfmt.Doc(textfmt.Syntax(textfmt.OneOrMore(textfmt.Symbol("foo")))) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "...\n" { t.Fatal(b.String()) } }) t.Run("required symbol", func(t *testing.T) { doc := textfmt.Doc(textfmt.Syntax(textfmt.Required(textfmt.Symbol("foo")))) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "\n" { t.Fatal(b.String()) } }) t.Run("optional symbol", func(t *testing.T) { doc := textfmt.Doc(textfmt.Syntax(textfmt.Optional(textfmt.Symbol("foo")))) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "[foo]\n" { t.Fatal(b.String()) } }) t.Run("sequence implicit", func(t *testing.T) { doc := textfmt.Doc( textfmt.Syntax( textfmt.Symbol("foo"), textfmt.Symbol("bar"), textfmt.Symbol("baz"), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "foo bar baz\n" { t.Fatal(b.String()) } }) t.Run("sequence", func(t *testing.T) { doc := textfmt.Doc( textfmt.Syntax( textfmt.Sequence( textfmt.Symbol("foo"), textfmt.Symbol("bar"), textfmt.Symbol("baz"), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "foo bar baz\n" { t.Fatal(b.String()) } }) t.Run("subsequence", func(t *testing.T) { doc := textfmt.Doc( textfmt.Syntax( textfmt.Symbol("corge"), textfmt.Sequence( textfmt.Symbol("foo"), textfmt.Symbol("bar"), textfmt.Symbol("baz"), ), textfmt.Symbol("garply"), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "corge (foo bar baz) garply\n" { t.Fatal(b.String()) } }) t.Run("top level choice", func(t *testing.T) { doc := textfmt.Doc( textfmt.Syntax( textfmt.Choice( textfmt.Symbol("foo"), textfmt.Symbol("bar"), textfmt.Symbol("baz"), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "foo\nbar\nbaz\n" { t.Fatal(b.String()) } }) t.Run("choice", func(t *testing.T) { doc := textfmt.Doc( textfmt.Syntax( textfmt.Symbol("corge"), textfmt.Choice( textfmt.Symbol("foo"), textfmt.Symbol("bar"), textfmt.Symbol("baz"), ), textfmt.Symbol("garply"), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "corge (foo|bar|baz) garply\n" { t.Fatal(b.String()) } }) t.Run("example", func(t *testing.T) { doc := textfmt.Doc( textfmt.Syntax( textfmt.Symbol("foo"), textfmt.ZeroOrMore(textfmt.Symbol("options")), textfmt.Required(textfmt.Symbol("filename")), textfmt.ZeroOrMore( textfmt.Choice( textfmt.Symbol("string"), textfmt.Symbol("number"), ), ), ), ) var b bytes.Buffer if err := textfmt.Teletype(&b, doc); err != nil { t.Fatal(err) } if b.String() != "foo [options]... [string|number]...\n" { t.Fatal(b.String()) } }) }) }