From f039e3be9e4a11808508c8cd3043b340deea040f Mon Sep 17 00:00:00 2001 From: Cameron Moore Date: Mon, 26 Dec 2016 15:23:20 -0600 Subject: [PATCH] parser: Refactor frontmatter parser and add tests Lots of cleanups here: - Refactor InterfaceToConfig and InterfaceToFrontMatter to use io.Writer. - Simplify InterfaceToFrontMatter by wrapping InterfaceToConfig. - Export FrontmatterType since we return it in DetectFrontMatter. - Refactor removeTOMLIdentifier to avoid blindly replacing "+++". - Update HandleJSONMetaData to return an empty map on nil input. - Updates vendored goorgeous package and test for org-mode frontmatter. - Add tests and godoc comments. Coverage for parser package increased from 45.2% to 85.2%. --- commands/import_jekyll.go | 10 +- commands/new.go | 10 +- commands/undraft_test.go | 12 +- hugolib/page.go | 13 +- parser/frontmatter.go | 163 ++++++++++++--------- parser/frontmatter_test.go | 281 +++++++++++++++++++++++++++++++++++++ parser/page.go | 18 +++ parser/page_test.go | 130 +++++++++++++++++ vendor/vendor.json | 6 +- 9 files changed, 552 insertions(+), 91 deletions(-) create mode 100644 parser/page_test.go diff --git a/commands/import_jekyll.go b/commands/import_jekyll.go index 151fffa8a..c33b68f2e 100644 --- a/commands/import_jekyll.go +++ b/commands/import_jekyll.go @@ -251,17 +251,13 @@ func createConfigFromJekyll(fs afero.Fs, inpath string, kind string, jekyllConfi } kind = parser.FormatSanitize(kind) - by, err := parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind)) + var buf bytes.Buffer + err = parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind), &buf) if err != nil { return err } - err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), fs) - if err != nil { - return - } - - return nil + return helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), &buf, fs) } func copyFile(source string, dest string) error { diff --git a/commands/new.go b/commands/new.go index fbecb6854..7bab13e97 100644 --- a/commands/new.go +++ b/commands/new.go @@ -356,15 +356,11 @@ func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) { } kind = parser.FormatSanitize(kind) - by, err := parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind)) + var buf bytes.Buffer + err = parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind), &buf) if err != nil { return err } - err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), fs.Source) - if err != nil { - return - } - - return nil + return helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), &buf, fs.Source) } diff --git a/commands/undraft_test.go b/commands/undraft_test.go index 45b785bb0..6ed172205 100644 --- a/commands/undraft_test.go +++ b/commands/undraft_test.go @@ -46,17 +46,17 @@ func TestUndraftContent(t *testing.T) { {yamlDraftFM, ""}, } - for _, test := range tests { + for i, test := range tests { r := bytes.NewReader([]byte(test.fm)) p, _ := parser.ReadFrom(r) res, err := undraftContent(p) if test.expectedErr != "" { if err == nil { - t.Error("Expected error, got none") + t.Error("[%d] Expected error, got none", i) continue } if err.Error() != test.expectedErr { - t.Errorf("Expected %q, got %q", test.expectedErr, err) + t.Errorf("[%d] Expected %q, got %q", i, test.expectedErr, err) continue } } else { @@ -64,19 +64,19 @@ func TestUndraftContent(t *testing.T) { p, _ = parser.ReadFrom(r) meta, err := p.Metadata() if err != nil { - t.Errorf("unexpected error %q", err) + t.Errorf("[%d] unexpected error %q", i, err) continue } for k, v := range meta.(map[string]interface{}) { if k == "draft" { if v.(bool) { - t.Errorf("Expected %q to be \"false\", got \"true\"", k) + t.Errorf("[%d] Expected %q to be \"false\", got \"true\"", i, k) continue } } if k == "date" { if !strings.HasPrefix(v.(string), time.Now().Format("2006-01-02")) { - t.Errorf("Expected %v to start with %v", v.(string), time.Now().Format("2006-01-02")) + t.Errorf("[%d] Expected %v to start with %v", i, v.(string), time.Now().Format("2006-01-02")) } } } diff --git a/hugolib/page.go b/hugolib/page.go index 02f889d93..5ee31c2a8 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -1424,15 +1424,20 @@ func (p *Page) SetSourceMetaData(in interface{}, mark rune) (err error) { } }() - var by []byte + buf := bp.GetBuffer() + defer bp.PutBuffer(buf) - by, err = parser.InterfaceToFrontMatter(in, mark) + err = parser.InterfaceToFrontMatter(in, mark, buf) if err != nil { return } - by = append(by, '\n') - p.Source.Frontmatter = by + _, err = buf.WriteRune('\n') + if err != nil { + return + } + + p.Source.Frontmatter = buf.Bytes() return } diff --git a/parser/frontmatter.go b/parser/frontmatter.go index e57a593ab..797c6fcf0 100644 --- a/parser/frontmatter.go +++ b/parser/frontmatter.go @@ -17,6 +17,7 @@ import ( "bytes" "encoding/json" "errors" + "io" "strings" "github.com/chaseadamsio/goorgeous" @@ -25,113 +26,116 @@ import ( "gopkg.in/yaml.v2" ) -type frontmatterType struct { - markstart, markend []byte - Parse func([]byte) (interface{}, error) - includeMark bool +// FrontmatterType represents a type of frontmatter. +type FrontmatterType struct { + // Parse decodes content into a Go interface. + Parse func([]byte) (interface{}, error) + + markstart, markend []byte // starting and ending delimiters + includeMark bool // include start and end mark in output } -func InterfaceToConfig(in interface{}, mark rune) ([]byte, error) { +// InterfaceToConfig encodes a given input based upon the mark and writes to w. +func InterfaceToConfig(in interface{}, mark rune, w io.Writer) error { if in == nil { - return []byte{}, errors.New("input was nil") + return errors.New("input was nil") } - b := new(bytes.Buffer) - switch mark { case rune(YAMLLead[0]): - by, err := yaml.Marshal(in) + b, err := yaml.Marshal(in) if err != nil { - return nil, err + return err } - b.Write(by) - _, err = b.Write([]byte("...")) - if err != nil { - return nil, err - } - return b.Bytes(), nil + + _, err = w.Write(b) + return err + case rune(TOMLLead[0]): tree := toml.TreeFromMap(in.(map[string]interface{})) - return []byte(tree.String()), nil + b := []byte(tree.String()) + + _, err := w.Write(b) + return err + case rune(JSONLead[0]): - by, err := json.MarshalIndent(in, "", " ") + b, err := json.MarshalIndent(in, "", " ") if err != nil { - return nil, err + return err } - b.Write(by) - _, err = b.Write([]byte("\n")) + + _, err = w.Write(b) if err != nil { - return nil, err + return err } - return b.Bytes(), nil + + _, err = w.Write([]byte{'\n'}) + return err + default: - return nil, errors.New("Unsupported Format provided") + return errors.New("Unsupported Format provided") } } -func InterfaceToFrontMatter(in interface{}, mark rune) ([]byte, error) { +// InterfaceToFrontMatter encodes a given input into a frontmatter +// representation based upon the mark with the appropriate front matter delimiters +// surrounding the output, which is written to w. +func InterfaceToFrontMatter(in interface{}, mark rune, w io.Writer) error { if in == nil { - return []byte{}, errors.New("input was nil") + return errors.New("input was nil") } - b := new(bytes.Buffer) - switch mark { case rune(YAMLLead[0]): - _, err := b.Write([]byte(YAMLDelimUnix)) + _, err := w.Write([]byte(YAMLDelimUnix)) if err != nil { - return nil, err - } - by, err := yaml.Marshal(in) - if err != nil { - return nil, err - } - b.Write(by) - _, err = b.Write([]byte(YAMLDelimUnix)) - if err != nil { - return nil, err - } - return b.Bytes(), nil - case rune(TOMLLead[0]): - _, err := b.Write([]byte(TOMLDelimUnix)) - if err != nil { - return nil, err + return err } - tree := toml.TreeFromMap(in.(map[string]interface{})) - b.Write([]byte(tree.String())) - _, err = b.Write([]byte("\n" + TOMLDelimUnix)) + err = InterfaceToConfig(in, mark, w) if err != nil { - return nil, err + return err } - return b.Bytes(), nil - case rune(JSONLead[0]): - by, err := json.MarshalIndent(in, "", " ") + + _, err = w.Write([]byte(YAMLDelimUnix)) + return err + + case rune(TOMLLead[0]): + _, err := w.Write([]byte(TOMLDelimUnix)) if err != nil { - return nil, err + return err } - b.Write(by) - _, err = b.Write([]byte("\n")) + + err = InterfaceToConfig(in, mark, w) if err != nil { - return nil, err + return err } - return b.Bytes(), nil + + _, err = w.Write([]byte("\n" + TOMLDelimUnix)) + return err + default: - return nil, errors.New("Unsupported Format provided") + return InterfaceToConfig(in, mark, w) } } +// FormatToLeadRune takes a given format kind and return the leading front +// matter delimiter. func FormatToLeadRune(kind string) rune { switch FormatSanitize(kind) { case "yaml": return rune([]byte(YAMLLead)[0]) case "json": return rune([]byte(JSONLead)[0]) + case "org": + return '#' default: return rune([]byte(TOMLLead)[0]) } } +// FormatSanitize returns the canonical format name for a given kind. +// // TODO(bep) move to helpers func FormatSanitize(kind string) string { switch strings.ToLower(kind) { @@ -141,27 +145,31 @@ func FormatSanitize(kind string) string { return "toml" case "json", "js": return "json" + case "org": + return kind default: return "toml" } } // DetectFrontMatter detects the type of frontmatter analysing its first character. -func DetectFrontMatter(mark rune) (f *frontmatterType) { +func DetectFrontMatter(mark rune) (f *FrontmatterType) { switch mark { case '-': - return &frontmatterType{[]byte(YAMLDelim), []byte(YAMLDelim), HandleYAMLMetaData, false} + return &FrontmatterType{HandleYAMLMetaData, []byte(YAMLDelim), []byte(YAMLDelim), false} case '+': - return &frontmatterType{[]byte(TOMLDelim), []byte(TOMLDelim), HandleTOMLMetaData, false} + return &FrontmatterType{HandleTOMLMetaData, []byte(TOMLDelim), []byte(TOMLDelim), false} case '{': - return &frontmatterType{[]byte{'{'}, []byte{'}'}, HandleJSONMetaData, true} + return &FrontmatterType{HandleJSONMetaData, []byte{'{'}, []byte{'}'}, true} case '#': - return &frontmatterType{[]byte("#+"), []byte("\n"), HandleOrgMetaData, false} + return &FrontmatterType{HandleOrgMetaData, []byte("#+"), []byte("\n"), false} default: return nil } } +// HandleTOMLMetaData unmarshals TOML-encoded datum and returns a Go interface +// representing the encoded data structure. func HandleTOMLMetaData(datum []byte) (interface{}, error) { m := map[string]interface{}{} datum = removeTOMLIdentifier(datum) @@ -177,22 +185,49 @@ func HandleTOMLMetaData(datum []byte) (interface{}, error) { return m, nil } +// removeTOMLIdentifier removes, if necessary, beginning and ending TOML +// frontmatter delimiters from a byte slice. func removeTOMLIdentifier(datum []byte) []byte { - return bytes.Replace(datum, []byte(TOMLDelim), []byte(""), -1) + ld := len(datum) + if ld < 8 { + return datum + } + + b := bytes.TrimPrefix(datum, []byte(TOMLDelim)) + if ld-len(b) != 3 { + // No TOML prefix trimmed, so bail out + return datum + } + + b = bytes.Trim(b, "\r\n") + return bytes.TrimSuffix(b, []byte(TOMLDelim)) } +// HandleYAMLMetaData unmarshals YAML-encoded datum and returns a Go interface +// representing the encoded data structure. func HandleYAMLMetaData(datum []byte) (interface{}, error) { m := map[string]interface{}{} err := yaml.Unmarshal(datum, &m) return m, err } +// HandleJSONMetaData unmarshals JSON-encoded datum and returns a Go interface +// representing the encoded data structure. func HandleJSONMetaData(datum []byte) (interface{}, error) { + if datum == nil { + // Package json returns on error on nil input. + // Return an empty map to be consistent with our other supported + // formats. + return make(map[string]interface{}), nil + } + var f interface{} err := json.Unmarshal(datum, &f) return f, err } +// HandleOrgMetaData unmarshals org-mode encoded datum and returns a Go +// interface representing the encoded data structure. func HandleOrgMetaData(datum []byte) (interface{}, error) { return goorgeous.OrgHeaders(datum) } diff --git a/parser/frontmatter_test.go b/parser/frontmatter_test.go index 081910094..5aef3562f 100644 --- a/parser/frontmatter_test.go +++ b/parser/frontmatter_test.go @@ -14,9 +14,231 @@ package parser import ( + "bytes" + "reflect" "testing" ) +func TestInterfaceToConfig(t *testing.T) { + cases := []struct { + input interface{} + mark byte + want []byte + isErr bool + }{ + // TOML + {map[string]interface{}{}, TOMLLead[0], nil, false}, + { + map[string]interface{}{"title": "test 1"}, + TOMLLead[0], + []byte("title = \"test 1\"\n"), + false, + }, + + // YAML + {map[string]interface{}{}, YAMLLead[0], []byte("{}\n"), false}, + { + map[string]interface{}{"title": "test 1"}, + YAMLLead[0], + []byte("title: test 1\n"), + false, + }, + + // JSON + {map[string]interface{}{}, JSONLead[0], []byte("{}\n"), false}, + { + map[string]interface{}{"title": "test 1"}, + JSONLead[0], + []byte("{\n \"title\": \"test 1\"\n}\n"), + false, + }, + + // Errors + {nil, TOMLLead[0], nil, true}, + {map[string]interface{}{}, '$', nil, true}, + } + + for i, c := range cases { + var buf bytes.Buffer + + err := InterfaceToConfig(c.input, rune(c.mark), &buf) + if err != nil { + if c.isErr { + continue + } + t.Fatalf("[%d] unexpected error value: %v", i, err) + } + + if !reflect.DeepEqual(buf.Bytes(), c.want) { + t.Errorf("[%d] not equal:\nwant %q,\n got %q", i, c.want, buf.Bytes()) + } + } +} + +func TestInterfaceToFrontMatter(t *testing.T) { + cases := []struct { + input interface{} + mark rune + want []byte + isErr bool + }{ + // TOML + {map[string]interface{}{}, '+', []byte("+++\n\n+++\n"), false}, + { + map[string]interface{}{"title": "test 1"}, + '+', + []byte("+++\ntitle = \"test 1\"\n\n+++\n"), + false, + }, + + // YAML + {map[string]interface{}{}, '-', []byte("---\n{}\n---\n"), false}, // + { + map[string]interface{}{"title": "test 1"}, + '-', + []byte("---\ntitle: test 1\n---\n"), + false, + }, + + // JSON + {map[string]interface{}{}, '{', []byte("{}\n"), false}, + { + map[string]interface{}{"title": "test 1"}, + '{', + []byte("{\n \"title\": \"test 1\"\n}\n"), + false, + }, + + // Errors + {nil, '+', nil, true}, + {map[string]interface{}{}, '$', nil, true}, + } + + for i, c := range cases { + var buf bytes.Buffer + err := InterfaceToFrontMatter(c.input, c.mark, &buf) + if err != nil { + if c.isErr { + continue + } + t.Fatalf("[%d] unexpected error value: %v", i, err) + } + + if !reflect.DeepEqual(buf.Bytes(), c.want) { + t.Errorf("[%d] not equal:\nwant %q,\n got %q", i, c.want, buf.Bytes()) + } + } +} + +func TestHandleTOMLMetaData(t *testing.T) { + cases := []struct { + input []byte + want interface{} + isErr bool + }{ + {nil, map[string]interface{}{}, false}, + {[]byte("title = \"test 1\""), map[string]interface{}{"title": "test 1"}, false}, + {[]byte("a = [1, 2, 3]"), map[string]interface{}{"a": []interface{}{int64(1), int64(2), int64(3)}}, false}, + {[]byte("b = [\n[1, 2],\n[3, 4]\n]"), map[string]interface{}{"b": []interface{}{[]interface{}{int64(1), int64(2)}, []interface{}{int64(3), int64(4)}}}, false}, + // errors + {[]byte("z = [\n[1, 2]\n[3, 4]\n]"), nil, true}, + } + + for i, c := range cases { + res, err := HandleTOMLMetaData(c.input) + if err != nil { + if c.isErr { + continue + } + t.Fatalf("[%d] unexpected error value: %v", i, err) + } + + if !reflect.DeepEqual(res, c.want) { + t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res) + } + } +} + +func TestHandleYAMLMetaData(t *testing.T) { + cases := []struct { + input []byte + want interface{} + isErr bool + }{ + {nil, map[string]interface{}{}, false}, + {[]byte("title: test 1"), map[string]interface{}{"title": "test 1"}, false}, + {[]byte("a: Easy!\nb:\n c: 2\n d: [3, 4]"), map[string]interface{}{"a": "Easy!", "b": map[interface{}]interface{}{"c": 2, "d": []interface{}{3, 4}}}, false}, + // errors + {[]byte("z = not toml"), nil, true}, + } + + for i, c := range cases { + res, err := HandleYAMLMetaData(c.input) + if err != nil { + if c.isErr { + continue + } + t.Fatalf("[%d] unexpected error value: %v", i, err) + } + + if !reflect.DeepEqual(res, c.want) { + t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res) + } + } +} + +func TestHandleJSONMetaData(t *testing.T) { + cases := []struct { + input []byte + want interface{} + isErr bool + }{ + {nil, map[string]interface{}{}, false}, + {[]byte("{\"title\": \"test 1\"}"), map[string]interface{}{"title": "test 1"}, false}, + // errors + {[]byte("{noquotes}"), nil, true}, + } + + for i, c := range cases { + res, err := HandleJSONMetaData(c.input) + if err != nil { + if c.isErr { + continue + } + t.Fatalf("[%d] unexpected error value: %v", i, err) + } + + if !reflect.DeepEqual(res, c.want) { + t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res) + } + } +} + +func TestHandleOrgMetaData(t *testing.T) { + cases := []struct { + input []byte + want interface{} + isErr bool + }{ + {nil, map[string]interface{}{}, false}, + {[]byte("#+title: test 1\n"), map[string]interface{}{"title": "test 1"}, false}, + } + + for i, c := range cases { + res, err := HandleOrgMetaData(c.input) + if err != nil { + if c.isErr { + continue + } + t.Fatalf("[%d] unexpected error value: %v", i, err) + } + + if !reflect.DeepEqual(res, c.want) { + t.Errorf("[%d] not equal: given %q\nwant %#v,\n got %#v", i, c.input, c.want, res) + } + } +} + func TestFormatToLeadRune(t *testing.T) { for i, this := range []struct { kind string @@ -25,8 +247,10 @@ func TestFormatToLeadRune(t *testing.T) { {"yaml", '-'}, {"yml", '-'}, {"toml", '+'}, + {"tml", '+'}, {"json", '{'}, {"js", '{'}, + {"org", '#'}, {"unknown", '+'}, } { result := FormatToLeadRune(this.kind) @@ -36,3 +260,60 @@ func TestFormatToLeadRune(t *testing.T) { } } } + +func TestDetectFrontMatter(t *testing.T) { + cases := []struct { + mark rune + want *FrontmatterType + }{ + // funcs are uncomparable, so we ignore FrontmatterType.Parse in these tests + {'-', &FrontmatterType{nil, []byte(YAMLDelim), []byte(YAMLDelim), false}}, + {'+', &FrontmatterType{nil, []byte(TOMLDelim), []byte(TOMLDelim), false}}, + {'{', &FrontmatterType{nil, []byte("{"), []byte("}"), true}}, + {'#', &FrontmatterType{nil, []byte("#+"), []byte("\n"), false}}, + {'$', nil}, + } + + for _, c := range cases { + res := DetectFrontMatter(c.mark) + if res == nil { + if c.want == nil { + continue + } + + t.Fatalf("want %v, got %v", *c.want, res) + } + + if !reflect.DeepEqual(res.markstart, c.want.markstart) { + t.Errorf("markstart mismatch: want %v, got %v", c.want.markstart, res.markstart) + } + if !reflect.DeepEqual(res.markend, c.want.markend) { + t.Errorf("markend mismatch: want %v, got %v", c.want.markend, res.markend) + } + if !reflect.DeepEqual(res.includeMark, c.want.includeMark) { + t.Errorf("includeMark mismatch: want %v, got %v", c.want.includeMark, res.includeMark) + } + } +} + +func TestRemoveTOMLIdentifier(t *testing.T) { + cases := []struct { + input string + want string + }{ + {"a = 1", "a = 1"}, + {"a = 1\r\n", "a = 1\r\n"}, + {"+++\r\na = 1\r\n+++\r\n", "a = 1\r\n"}, + {"+++\na = 1\n+++\n", "a = 1\n"}, + {"+++\nb = \"+++ oops +++\"\n+++\n", "b = \"+++ oops +++\"\n"}, + {"+++\nc = \"\"\"+++\noops\n+++\n\"\"\"\"\n+++\n", "c = \"\"\"+++\noops\n+++\n\"\"\"\"\n"}, + {"+++\nd = 1\n+++", "d = 1\n"}, + } + + for i, c := range cases { + res := removeTOMLIdentifier([]byte(c.input)) + if string(res) != c.want { + t.Errorf("[%d] given %q\nwant: %q\n got: %q", i, c.input, c.want, res) + } + } +} diff --git a/parser/page.go b/parser/page.go index 3347380d7..a0679289c 100644 --- a/parser/page.go +++ b/parser/page.go @@ -64,30 +64,42 @@ var ( // Page represents a parsed content page. type Page interface { + // FrontMatter contains the raw frontmatter with relevant delimiters. FrontMatter() []byte + + // Content contains the raw page content. Content() []byte + + // IsRenderable denotes that the page should be rendered. IsRenderable() bool + + // Metadata returns the unmarshalled frontmatter data. Metadata() (interface{}, error) } +// page implements the Page interface. type page struct { render bool frontmatter []byte content []byte } +// Content returns the raw page content. func (p *page) Content() []byte { return p.content } +// FrontMatter contains the raw frontmatter with relevant delimiters. func (p *page) FrontMatter() []byte { return p.frontmatter } +// IsRenderable denotes that the page should be rendered. func (p *page) IsRenderable() bool { return p.render } +// Metadata returns the unmarshalled frontmatter data. func (p *page) Metadata() (meta interface{}, err error) { frontmatter := p.FrontMatter() @@ -151,6 +163,7 @@ func ReadFrom(r io.Reader) (p Page, err error) { return newp, nil } +// chompBOM scans any leading Unicode Byte Order Markers from r. func chompBOM(r io.RuneScanner) (err error) { for { c, _, err := r.ReadRune() @@ -164,6 +177,7 @@ func chompBOM(r io.RuneScanner) (err error) { } } +// chompWhitespace scans any leading Unicode whitespace from r. func chompWhitespace(r io.RuneScanner) (err error) { for { c, _, err := r.ReadRune() @@ -177,6 +191,9 @@ func chompWhitespace(r io.RuneScanner) (err error) { } } +// chompFrontmatterStartComment checks r for a leading HTML comment. If a +// comment is found, it is read from r and then whitespace is trimmed from the +// beginning of r. func chompFrontmatterStartComment(r *bufio.Reader) (err error) { candidate, err := r.Peek(32) if err != nil { @@ -206,6 +223,7 @@ func chompFrontmatterStartComment(r *bufio.Reader) (err error) { return nil } +// chompFrontmatterEndComment checks r for a trailing HTML comment. func chompFrontmatterEndComment(r *bufio.Reader) (err error) { candidate, err := r.Peek(32) if err != nil { diff --git a/parser/page_test.go b/parser/page_test.go new file mode 100644 index 000000000..07d7660d4 --- /dev/null +++ b/parser/page_test.go @@ -0,0 +1,130 @@ +package parser + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPage(t *testing.T) { + cases := []struct { + raw string + + content string + frontmatter string + renderable bool + metadata map[string]interface{} + }{ + { + testPageLeader + jsonPageFrontMatter + "\n" + testPageTrailer + jsonPageContent, + jsonPageContent, + jsonPageFrontMatter, + true, + map[string]interface{}{ + "title": "JSON Test 1", + "social": []interface{}{ + []interface{}{"a", "#"}, + []interface{}{"b", "#"}, + }, + }, + }, + { + testPageLeader + tomlPageFrontMatter + testPageTrailer + tomlPageContent, + tomlPageContent, + tomlPageFrontMatter, + true, + map[string]interface{}{ + "title": "TOML Test 1", + "social": []interface{}{ + []interface{}{"a", "#"}, + []interface{}{"b", "#"}, + }, + }, + }, + { + testPageLeader + yamlPageFrontMatter + testPageTrailer + yamlPageContent, + yamlPageContent, + yamlPageFrontMatter, + true, + map[string]interface{}{ + "title": "YAML Test 1", + "social": []interface{}{ + []interface{}{"a", "#"}, + []interface{}{"b", "#"}, + }, + }, + }, + { + testPageLeader + orgPageFrontMatter + orgPageContent, + orgPageContent, + orgPageFrontMatter, + true, + map[string]interface{}{ + "TITLE": "Org Test 1", + "categories": []string{"a", "b"}, + }, + }, + } + + for i, c := range cases { + p := pageMust(ReadFrom(strings.NewReader(c.raw))) + meta, err := p.Metadata() + + mesg := fmt.Sprintf("[%d]", i) + + require.Nil(t, err, mesg) + assert.Equal(t, c.content, string(p.Content()), mesg+" content") + assert.Equal(t, c.frontmatter, string(p.FrontMatter()), mesg+" frontmatter") + assert.Equal(t, c.renderable, p.IsRenderable(), mesg+" renderable") + assert.Equal(t, c.metadata, meta, mesg+" metadata") + } +} + +var ( + testWhitespace = "\t\t\n\n" + testPageLeader = "\ufeff" + testWhitespace + "\n" + + jsonPageContent = "# JSON Test\n" + jsonPageFrontMatter = `{ + "title": "JSON Test 1", + "social": [ + ["a", "#"], + ["b", "#"] + ] +}` + + tomlPageContent = "# TOML Test\n" + tomlPageFrontMatter = `+++ +title = "TOML Test 1" +social = [ + ["a", "#"], + ["b", "#"], +] ++++ +` + + yamlPageContent = "# YAML Test\n" + yamlPageFrontMatter = `--- +title: YAML Test 1 +social: + - - "a" + - "#" + - - "b" + - "#" +--- +` + + orgPageContent = "* Org Test\n" + orgPageFrontMatter = `#+TITLE: Org Test 1 +#+categories: a b +` + + pageHTMLComment = ` +` +) diff --git a/vendor/vendor.json b/vendor/vendor.json index 738eddfe6..7891d3ef4 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -33,10 +33,10 @@ "revisionTime": "2016-04-08T19:03:23Z" }, { - "checksumSHA1": "RxIwAgjIuBpwde5BCZRLLK7VRG8=", + "checksumSHA1": "tOtpDG/zYOvYRQeSHcg8IhZnRHQ=", "path": "github.com/chaseadamsio/goorgeous", - "revision": "72a06e1b07db57f3931f5a9c00f3f04e636ad0a8", - "revisionTime": "2017-02-17T13:03:04Z" + "revision": "054aba677f27bd60872cfe68f8145dc57bdf4746", + "revisionTime": "2017-02-22T05:25:03Z" }, { "checksumSHA1": "ntacCkWfMT63DaehXLG5FeXWyNM=",