minifiers_test.go (6941B)
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 minifiers 15 16 import ( 17 "bytes" 18 "encoding/json" 19 "strings" 20 "testing" 21 22 qt "github.com/frankban/quicktest" 23 "github.com/gohugoio/hugo/config" 24 "github.com/gohugoio/hugo/media" 25 "github.com/gohugoio/hugo/output" 26 "github.com/tdewolff/minify/v2/html" 27 ) 28 29 func TestNew(t *testing.T) { 30 c := qt.New(t) 31 v := config.NewWithTestDefaults() 32 m, _ := New(media.DefaultTypes, output.DefaultFormats, v) 33 34 var rawJS string 35 var minJS string 36 rawJS = " var foo =1 ; foo ++ ; " 37 minJS = "var foo=1;foo++" 38 39 var rawJSON string 40 var minJSON string 41 rawJSON = " { \"a\" : 123 , \"b\":2, \"c\": 5 } " 42 minJSON = "{\"a\":123,\"b\":2,\"c\":5}" 43 44 for _, test := range []struct { 45 tp media.Type 46 rawString string 47 expectedMinString string 48 }{ 49 {media.CSSType, " body { color: blue; } ", "body{color:blue}"}, 50 {media.RSSType, " <hello> Hugo! </hello> ", "<hello>Hugo!</hello>"}, // RSS should be handled as XML 51 {media.JSONType, rawJSON, minJSON}, 52 {media.JavascriptType, rawJS, minJS}, 53 // JS Regex minifiers 54 {media.Type{MainType: "application", SubType: "ecmascript"}, rawJS, minJS}, 55 {media.Type{MainType: "application", SubType: "javascript"}, rawJS, minJS}, 56 {media.Type{MainType: "application", SubType: "x-javascript"}, rawJS, minJS}, 57 {media.Type{MainType: "application", SubType: "x-ecmascript"}, rawJS, minJS}, 58 {media.Type{MainType: "text", SubType: "ecmascript"}, rawJS, minJS}, 59 {media.Type{MainType: "text", SubType: "javascript"}, rawJS, minJS}, 60 {media.Type{MainType: "text", SubType: "x-javascript"}, rawJS, minJS}, 61 {media.Type{MainType: "text", SubType: "x-ecmascript"}, rawJS, minJS}, 62 // JSON Regex minifiers 63 {media.Type{MainType: "application", SubType: "json"}, rawJSON, minJSON}, 64 {media.Type{MainType: "application", SubType: "x-json"}, rawJSON, minJSON}, 65 {media.Type{MainType: "application", SubType: "ld+json"}, rawJSON, minJSON}, 66 {media.Type{MainType: "text", SubType: "json"}, rawJSON, minJSON}, 67 {media.Type{MainType: "text", SubType: "x-json"}, rawJSON, minJSON}, 68 {media.Type{MainType: "text", SubType: "ld+json"}, rawJSON, minJSON}, 69 } { 70 var b bytes.Buffer 71 72 c.Assert(m.Minify(test.tp, &b, strings.NewReader(test.rawString)), qt.IsNil) 73 c.Assert(b.String(), qt.Equals, test.expectedMinString) 74 } 75 } 76 77 func TestConfigureMinify(t *testing.T) { 78 c := qt.New(t) 79 v := config.NewWithTestDefaults() 80 v.Set("minify", map[string]any{ 81 "disablexml": true, 82 "tdewolff": map[string]any{ 83 "html": map[string]any{ 84 "keepwhitespace": true, 85 }, 86 }, 87 }) 88 m, _ := New(media.DefaultTypes, output.DefaultFormats, v) 89 90 for _, test := range []struct { 91 tp media.Type 92 rawString string 93 expectedMinString string 94 errorExpected bool 95 }{ 96 {media.HTMLType, "<hello> Hugo! </hello>", "<hello> Hugo! </hello>", false}, // configured minifier 97 {media.CSSType, " body { color: blue; } ", "body{color:blue}", false}, // default minifier 98 {media.XMLType, " <hello> Hugo! </hello> ", " <hello> Hugo! </hello> ", false}, // disable Xml minification 99 } { 100 var b bytes.Buffer 101 if !test.errorExpected { 102 c.Assert(m.Minify(test.tp, &b, strings.NewReader(test.rawString)), qt.IsNil) 103 c.Assert(b.String(), qt.Equals, test.expectedMinString) 104 } else { 105 err := m.Minify(test.tp, &b, strings.NewReader(test.rawString)) 106 c.Assert(err, qt.ErrorMatches, "minifier does not exist for mimetype") 107 } 108 } 109 } 110 111 func TestJSONRoundTrip(t *testing.T) { 112 c := qt.New(t) 113 v := config.NewWithTestDefaults() 114 m, _ := New(media.DefaultTypes, output.DefaultFormats, v) 115 116 for _, test := range []string{`{ 117 "glossary": { 118 "title": "example glossary", 119 "GlossDiv": { 120 "title": "S", 121 "GlossList": { 122 "GlossEntry": { 123 "ID": "SGML", 124 "SortAs": "SGML", 125 "GlossTerm": "Standard Generalized Markup Language", 126 "Acronym": "SGML", 127 "Abbrev": "ISO 8879:1986", 128 "GlossDef": { 129 "para": "A meta-markup language, used to create markup languages such as DocBook.", 130 "GlossSeeAlso": ["GML", "XML"] 131 }, 132 "GlossSee": "markup" 133 } 134 } 135 } 136 } 137 }`} { 138 139 var b bytes.Buffer 140 m1 := make(map[string]any) 141 m2 := make(map[string]any) 142 c.Assert(json.Unmarshal([]byte(test), &m1), qt.IsNil) 143 c.Assert(m.Minify(media.JSONType, &b, strings.NewReader(test)), qt.IsNil) 144 c.Assert(json.Unmarshal(b.Bytes(), &m2), qt.IsNil) 145 c.Assert(m1, qt.DeepEquals, m2) 146 } 147 } 148 149 func TestBugs(t *testing.T) { 150 c := qt.New(t) 151 v := config.NewWithTestDefaults() 152 m, _ := New(media.DefaultTypes, output.DefaultFormats, v) 153 154 for _, test := range []struct { 155 tp media.Type 156 rawString string 157 expectedMinString string 158 }{ 159 // https://github.com/gohugoio/hugo/issues/5506 160 {media.CSSType, " body { color: rgba(000, 000, 000, 0.7); }", "body{color:rgba(0,0,0,.7)}"}, 161 // https://github.com/gohugoio/hugo/issues/8332 162 {media.HTMLType, "<i class='fas fa-tags fa-fw'></i> Tags", `<i class='fas fa-tags fa-fw'></i> Tags`}, 163 } { 164 var b bytes.Buffer 165 166 c.Assert(m.Minify(test.tp, &b, strings.NewReader(test.rawString)), qt.IsNil) 167 c.Assert(b.String(), qt.Equals, test.expectedMinString) 168 } 169 } 170 171 // Renamed to Precision in v2.7.0. Check that we support both. 172 func TestDecodeConfigDecimalIsNowPrecision(t *testing.T) { 173 c := qt.New(t) 174 v := config.NewWithTestDefaults() 175 v.Set("minify", map[string]any{ 176 "disablexml": true, 177 "tdewolff": map[string]any{ 178 "css": map[string]any{ 179 "decimal": 3, 180 }, 181 "svg": map[string]any{ 182 "decimal": 3, 183 }, 184 }, 185 }) 186 187 conf, err := decodeConfig(v) 188 189 c.Assert(err, qt.IsNil) 190 c.Assert(conf.Tdewolff.CSS.Precision, qt.Equals, 3) 191 192 } 193 194 // Issue 9456 195 func TestDecodeConfigKeepWhitespace(t *testing.T) { 196 c := qt.New(t) 197 v := config.NewWithTestDefaults() 198 v.Set("minify", map[string]any{ 199 "tdewolff": map[string]any{ 200 "html": map[string]any{ 201 "keepEndTags": false, 202 }, 203 }, 204 }) 205 206 conf, err := decodeConfig(v) 207 208 c.Assert(err, qt.IsNil) 209 c.Assert(conf.Tdewolff.HTML, qt.DeepEquals, 210 html.Minifier{ 211 KeepComments: false, 212 KeepConditionalComments: true, 213 KeepDefaultAttrVals: true, 214 KeepDocumentTags: true, 215 KeepEndTags: false, 216 KeepQuotes: false, 217 KeepWhitespace: false}, 218 ) 219 220 }