github.com/SuCicada/su-hugo@v1.0.0/parser/pageparser/pageparser_intro_test.go (about)

     1  // Copyright 2018 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package pageparser
    15  
    16  import (
    17  	"fmt"
    18  	"strings"
    19  	"testing"
    20  
    21  	qt "github.com/frankban/quicktest"
    22  )
    23  
    24  type lexerTest struct {
    25  	name  string
    26  	input string
    27  	items []typeText
    28  }
    29  
    30  type typeText struct {
    31  	typ  ItemType
    32  	text string
    33  }
    34  
    35  func nti(tp ItemType, val string) typeText {
    36  	return typeText{typ: tp, text: val}
    37  }
    38  
    39  var (
    40  	tstJSON                = `{ "a": { "b": "\"Hugo\"}" } }`
    41  	tstFrontMatterTOML     = nti(TypeFrontMatterTOML, "foo = \"bar\"\n")
    42  	tstFrontMatterYAML     = nti(TypeFrontMatterYAML, "foo: \"bar\"\n")
    43  	tstFrontMatterYAMLCRLF = nti(TypeFrontMatterYAML, "foo: \"bar\"\r\n")
    44  	tstFrontMatterJSON     = nti(TypeFrontMatterJSON, tstJSON+"\r\n")
    45  	tstSomeText            = nti(tText, "\nSome text.\n")
    46  	tstSummaryDivider      = nti(TypeLeadSummaryDivider, "<!--more-->\n")
    47  	tstNewline             = nti(tText, "\n")
    48  
    49  	tstORG = `
    50  #+TITLE: T1
    51  #+AUTHOR: A1
    52  #+DESCRIPTION: D1
    53  `
    54  	tstFrontMatterORG = nti(TypeFrontMatterORG, tstORG)
    55  )
    56  
    57  var crLfReplacer = strings.NewReplacer("\r", "#", "\n", "$")
    58  
    59  // TODO(bep) a way to toggle ORG mode vs the rest.
    60  var frontMatterTests = []lexerTest{
    61  	{"empty", "", []typeText{tstEOF}},
    62  	{"Byte order mark", "\ufeff\nSome text.\n", []typeText{nti(TypeIgnore, "\ufeff"), tstSomeText, tstEOF}},
    63  	{"HTML Document", `  <html>  `, []typeText{nti(tError, "plain HTML documents not supported")}},
    64  	{"HTML Document with shortcode", `<html>{{< sc1 >}}</html>`, []typeText{nti(tError, "plain HTML documents not supported")}},
    65  	{"No front matter", "\nSome text.\n", []typeText{tstSomeText, tstEOF}},
    66  	{"YAML front matter", "---\nfoo: \"bar\"\n---\n\nSome text.\n", []typeText{tstFrontMatterYAML, tstSomeText, tstEOF}},
    67  	{"YAML empty front matter", "---\n---\n\nSome text.\n", []typeText{nti(TypeFrontMatterYAML, ""), tstSomeText, tstEOF}},
    68  	{"YAML commented out front matter", "<!--\n---\nfoo: \"bar\"\n---\n-->\nSome text.\n", []typeText{nti(TypeIgnore, "<!--\n"), tstFrontMatterYAML, nti(TypeIgnore, "-->"), tstSomeText, tstEOF}},
    69  	{"YAML commented out front matter, no end", "<!--\n---\nfoo: \"bar\"\n---\nSome text.\n", []typeText{nti(TypeIgnore, "<!--\n"), tstFrontMatterYAML, nti(tError, "starting HTML comment with no end")}},
    70  	// Note that we keep all bytes as they are, but we need to handle CRLF
    71  	{"YAML front matter CRLF", "---\r\nfoo: \"bar\"\r\n---\n\nSome text.\n", []typeText{tstFrontMatterYAMLCRLF, tstSomeText, tstEOF}},
    72  	{"TOML front matter", "+++\nfoo = \"bar\"\n+++\n\nSome text.\n", []typeText{tstFrontMatterTOML, tstSomeText, tstEOF}},
    73  	{"JSON front matter", tstJSON + "\r\n\nSome text.\n", []typeText{tstFrontMatterJSON, tstSomeText, tstEOF}},
    74  	{"ORG front matter", tstORG + "\nSome text.\n", []typeText{tstFrontMatterORG, tstSomeText, tstEOF}},
    75  	{"Summary divider ORG", tstORG + "\nSome text.\n# more\nSome text.\n", []typeText{tstFrontMatterORG, tstSomeText, nti(TypeLeadSummaryDivider, "# more\n"), nti(tText, "Some text.\n"), tstEOF}},
    76  	{"Summary divider", "+++\nfoo = \"bar\"\n+++\n\nSome text.\n<!--more-->\nSome text.\n", []typeText{tstFrontMatterTOML, tstSomeText, tstSummaryDivider, nti(tText, "Some text.\n"), tstEOF}},
    77  	{"Summary divider same line", "+++\nfoo = \"bar\"\n+++\n\nSome text.<!--more-->Some text.\n", []typeText{tstFrontMatterTOML, nti(tText, "\nSome text."), nti(TypeLeadSummaryDivider, "<!--more-->"), nti(tText, "Some text.\n"), tstEOF}},
    78  	// https://github.com/gohugoio/hugo/issues/5402
    79  	{"Summary and shortcode, no space", "+++\nfoo = \"bar\"\n+++\n\nSome text.\n<!--more-->{{< sc1 >}}\nSome text.\n", []typeText{tstFrontMatterTOML, tstSomeText, nti(TypeLeadSummaryDivider, "<!--more-->"), tstLeftNoMD, tstSC1, tstRightNoMD, tstSomeText, tstEOF}},
    80  	// https://github.com/gohugoio/hugo/issues/5464
    81  	{"Summary and shortcode only", "+++\nfoo = \"bar\"\n+++\n{{< sc1 >}}\n<!--more-->\n{{< sc2 >}}", []typeText{tstFrontMatterTOML, tstLeftNoMD, tstSC1, tstRightNoMD, tstNewline, tstSummaryDivider, tstLeftNoMD, tstSC2, tstRightNoMD, tstEOF}},
    82  }
    83  
    84  func TestFrontMatter(t *testing.T) {
    85  	t.Parallel()
    86  	c := qt.New(t)
    87  	for i, test := range frontMatterTests {
    88  		items := collect([]byte(test.input), false, lexIntroSection)
    89  		if !equal(test.input, items, test.items) {
    90  			got := itemsToString(items, []byte(test.input))
    91  			expected := testItemsToString(test.items)
    92  			c.Assert(got, qt.Equals, expected, qt.Commentf("Test %d: %s", i, test.name))
    93  		}
    94  	}
    95  }
    96  
    97  func itemsToString(items []Item, source []byte) string {
    98  	var sb strings.Builder
    99  	for i, item := range items {
   100  		var s string
   101  		if item.Err != nil {
   102  			s = item.Err.Error()
   103  		} else {
   104  			s = string(item.Val(source))
   105  		}
   106  		sb.WriteString(fmt.Sprintf("%s: %s\n", item.Type, s))
   107  
   108  		if i < len(items)-1 {
   109  			sb.WriteString("\n")
   110  		}
   111  	}
   112  	return crLfReplacer.Replace(sb.String())
   113  }
   114  
   115  func testItemsToString(items []typeText) string {
   116  	var sb strings.Builder
   117  	for i, item := range items {
   118  		sb.WriteString(fmt.Sprintf("%s: %s\n", item.typ, item.text))
   119  
   120  		if i < len(items)-1 {
   121  			sb.WriteString("\n")
   122  		}
   123  	}
   124  	return crLfReplacer.Replace(sb.String())
   125  }
   126  
   127  func collectWithConfig(input []byte, skipFrontMatter bool, stateStart stateFunc, cfg Config) (items []Item) {
   128  	l := newPageLexer(input, stateStart, cfg)
   129  	l.run()
   130  	iter := NewIterator(l.items)
   131  
   132  	for {
   133  		item := iter.Next()
   134  		items = append(items, item)
   135  		if item.Type == tEOF || item.Type == tError {
   136  			break
   137  		}
   138  	}
   139  	return
   140  }
   141  
   142  func collect(input []byte, skipFrontMatter bool, stateStart stateFunc) (items []Item) {
   143  	var cfg Config
   144  
   145  	return collectWithConfig(input, skipFrontMatter, stateStart, cfg)
   146  }
   147  
   148  func collectStringMain(input string) []Item {
   149  	return collect([]byte(input), true, lexMainSection)
   150  }
   151  
   152  // no positional checking, for now ...
   153  func equal(source string, got []Item, expect []typeText) bool {
   154  	if len(got) != len(expect) {
   155  		return false
   156  	}
   157  	sourceb := []byte(source)
   158  	for k := range got {
   159  		g := got[k]
   160  		e := expect[k]
   161  		if g.Type != e.typ {
   162  			return false
   163  		}
   164  
   165  		var s string
   166  		if g.Err != nil {
   167  			s = g.Err.Error()
   168  		} else {
   169  			s = string(g.Val(sourceb))
   170  		}
   171  
   172  		if s != e.text {
   173  			return false
   174  		}
   175  
   176  	}
   177  	return true
   178  }