content_test.go (8036B)
1 // Copyright 2019 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 helpers 15 16 import ( 17 "bytes" 18 "html/template" 19 "strings" 20 "testing" 21 22 "github.com/spf13/afero" 23 24 "github.com/gohugoio/hugo/common/loggers" 25 "github.com/gohugoio/hugo/config" 26 27 qt "github.com/frankban/quicktest" 28 ) 29 30 const tstHTMLContent = "<!DOCTYPE html><html><head><script src=\"http://two/foobar.js\"></script></head><body><nav><ul><li hugo-nav=\"section_0\"></li><li hugo-nav=\"section_1\"></li></ul></nav><article>content <a href=\"http://two/foobar\">foobar</a>. Follow up</article><p>This is some text.<br>And some more.</p></body></html>" 31 32 func TestTrimShortHTML(t *testing.T) { 33 tests := []struct { 34 input, output []byte 35 }{ 36 {[]byte(""), []byte("")}, 37 {[]byte("Plain text"), []byte("Plain text")}, 38 {[]byte(" \t\n Whitespace text\n\n"), []byte("Whitespace text")}, 39 {[]byte("<p>Simple paragraph</p>"), []byte("Simple paragraph")}, 40 {[]byte("\n \n \t <p> \t Whitespace\nHTML \n\t </p>\n\t"), []byte("Whitespace\nHTML")}, 41 {[]byte("<p>Multiple</p><p>paragraphs</p>"), []byte("<p>Multiple</p><p>paragraphs</p>")}, 42 {[]byte("<p>Nested<p>paragraphs</p></p>"), []byte("<p>Nested<p>paragraphs</p></p>")}, 43 {[]byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>"), []byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>")}, 44 } 45 46 c := newTestContentSpec() 47 for i, test := range tests { 48 output := c.TrimShortHTML(test.input) 49 if !bytes.Equal(test.output, output) { 50 t.Errorf("Test %d failed. Expected %q got %q", i, test.output, output) 51 } 52 } 53 } 54 55 func TestStripEmptyNav(t *testing.T) { 56 c := qt.New(t) 57 cleaned := stripEmptyNav([]byte("do<nav>\n</nav>\n\nbedobedo")) 58 c.Assert(cleaned, qt.DeepEquals, []byte("dobedobedo")) 59 } 60 61 func TestBytesToHTML(t *testing.T) { 62 c := qt.New(t) 63 c.Assert(BytesToHTML([]byte("dobedobedo")), qt.Equals, template.HTML("dobedobedo")) 64 } 65 66 func TestNewContentSpec(t *testing.T) { 67 cfg := config.NewWithTestDefaults() 68 c := qt.New(t) 69 70 cfg.Set("summaryLength", 32) 71 cfg.Set("buildFuture", true) 72 cfg.Set("buildExpired", true) 73 cfg.Set("buildDrafts", true) 74 75 spec, err := NewContentSpec(cfg, loggers.NewErrorLogger(), afero.NewMemMapFs(), nil) 76 77 c.Assert(err, qt.IsNil) 78 c.Assert(spec.summaryLength, qt.Equals, 32) 79 c.Assert(spec.BuildFuture, qt.Equals, true) 80 c.Assert(spec.BuildExpired, qt.Equals, true) 81 c.Assert(spec.BuildDrafts, qt.Equals, true) 82 } 83 84 var benchmarkTruncateString = strings.Repeat("This is a sentence about nothing.", 20) 85 86 func BenchmarkTestTruncateWordsToWholeSentence(b *testing.B) { 87 c := newTestContentSpec() 88 b.ResetTimer() 89 for i := 0; i < b.N; i++ { 90 c.TruncateWordsToWholeSentence(benchmarkTruncateString) 91 } 92 } 93 94 func BenchmarkTestTruncateWordsToWholeSentenceOld(b *testing.B) { 95 c := newTestContentSpec() 96 b.ResetTimer() 97 for i := 0; i < b.N; i++ { 98 c.truncateWordsToWholeSentenceOld(benchmarkTruncateString) 99 } 100 } 101 102 func TestTruncateWordsToWholeSentence(t *testing.T) { 103 c := newTestContentSpec() 104 type test struct { 105 input, expected string 106 max int 107 truncated bool 108 } 109 data := []test{ 110 {"a b c", "a b c", 12, false}, 111 {"a b c", "a b c", 3, false}, 112 {"a", "a", 1, false}, 113 {"This is a sentence.", "This is a sentence.", 5, false}, 114 {"This is also a sentence!", "This is also a sentence!", 1, false}, 115 {"To be. Or not to be. That's the question.", "To be.", 1, true}, 116 {" \nThis is not a sentence\nAnd this is another", "This is not a sentence", 4, true}, 117 {"", "", 10, false}, 118 {"This... is a more difficult test?", "This... is a more difficult test?", 1, false}, 119 } 120 for i, d := range data { 121 c.summaryLength = d.max 122 output, truncated := c.TruncateWordsToWholeSentence(d.input) 123 if d.expected != output { 124 t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output) 125 } 126 127 if d.truncated != truncated { 128 t.Errorf("Test %d failed. Expected truncated=%t got %t", i, d.truncated, truncated) 129 } 130 } 131 } 132 133 func TestTruncateWordsByRune(t *testing.T) { 134 c := newTestContentSpec() 135 type test struct { 136 input, expected string 137 max int 138 truncated bool 139 } 140 data := []test{ 141 {"", "", 1, false}, 142 {"a b c", "a b c", 12, false}, 143 {"a b c", "a b c", 3, false}, 144 {"a", "a", 1, false}, 145 {"Hello 中国", "", 0, true}, 146 {"这是中文,全中文。", "这是中文,", 5, true}, 147 {"Hello 中国", "Hello 中", 2, true}, 148 {"Hello 中国", "Hello 中国", 3, false}, 149 {"Hello中国 Good 好的", "Hello中国 Good 好", 9, true}, 150 {"This is a sentence.", "This is", 2, true}, 151 {"This is also a sentence!", "This", 1, true}, 152 {"To be. Or not to be. That's the question.", "To be. Or not", 4, true}, 153 {" \nThis is not a sentence\n ", "This is not", 3, true}, 154 } 155 for i, d := range data { 156 c.summaryLength = d.max 157 output, truncated := c.TruncateWordsByRune(strings.Fields(d.input)) 158 if d.expected != output { 159 t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output) 160 } 161 162 if d.truncated != truncated { 163 t.Errorf("Test %d failed. Expected truncated=%t got %t", i, d.truncated, truncated) 164 } 165 } 166 } 167 168 func TestExtractTOCNormalContent(t *testing.T) { 169 content := []byte("<nav>\n<ul>\nTOC<li><a href=\"#") 170 171 actualTocLessContent, actualToc := ExtractTOC(content) 172 expectedTocLess := []byte("TOC<li><a href=\"#") 173 expectedToc := []byte("<nav id=\"TableOfContents\">\n<ul>\n") 174 175 if !bytes.Equal(actualTocLessContent, expectedTocLess) { 176 t.Errorf("Actual tocless (%s) did not equal expected (%s) tocless content", actualTocLessContent, expectedTocLess) 177 } 178 179 if !bytes.Equal(actualToc, expectedToc) { 180 t.Errorf("Actual toc (%s) did not equal expected (%s) toc content", actualToc, expectedToc) 181 } 182 } 183 184 func TestExtractTOCGreaterThanSeventy(t *testing.T) { 185 content := []byte("<nav>\n<ul>\nTOC This is a very long content which will definitely be greater than seventy, I promise you that.<li><a href=\"#") 186 187 actualTocLessContent, actualToc := ExtractTOC(content) 188 // Because the start of Toc is greater than 70+startpoint of <li> content and empty TOC will be returned 189 expectedToc := []byte("") 190 191 if !bytes.Equal(actualTocLessContent, content) { 192 t.Errorf("Actual tocless (%s) did not equal expected (%s) tocless content", actualTocLessContent, content) 193 } 194 195 if !bytes.Equal(actualToc, expectedToc) { 196 t.Errorf("Actual toc (%s) did not equal expected (%s) toc content", actualToc, expectedToc) 197 } 198 } 199 200 func TestExtractNoTOC(t *testing.T) { 201 content := []byte("TOC") 202 203 actualTocLessContent, actualToc := ExtractTOC(content) 204 expectedToc := []byte("") 205 206 if !bytes.Equal(actualTocLessContent, content) { 207 t.Errorf("Actual tocless (%s) did not equal expected (%s) tocless content", actualTocLessContent, content) 208 } 209 210 if !bytes.Equal(actualToc, expectedToc) { 211 t.Errorf("Actual toc (%s) did not equal expected (%s) toc content", actualToc, expectedToc) 212 } 213 } 214 215 var totalWordsBenchmarkString = strings.Repeat("Hugo Rocks ", 200) 216 217 func TestTotalWords(t *testing.T) { 218 for i, this := range []struct { 219 s string 220 words int 221 }{ 222 {"Two, Words!", 2}, 223 {"Word", 1}, 224 {"", 0}, 225 {"One, Two, Three", 3}, 226 {totalWordsBenchmarkString, 400}, 227 } { 228 actualWordCount := TotalWords(this.s) 229 230 if actualWordCount != this.words { 231 t.Errorf("[%d] Actual word count (%d) for test string (%s) did not match %d", i, actualWordCount, this.s, this.words) 232 } 233 } 234 } 235 236 func BenchmarkTotalWords(b *testing.B) { 237 b.ResetTimer() 238 for i := 0; i < b.N; i++ { 239 wordCount := TotalWords(totalWordsBenchmarkString) 240 if wordCount != 400 { 241 b.Fatal("Wordcount error") 242 } 243 } 244 }