github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/minifiers/minifiers_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 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  }