minify_html_common/tests/
mod.rs

1use std::collections::HashMap;
2
3pub fn create_common_test_data() -> HashMap<&'static [u8], &'static [u8]> {
4  let mut t = HashMap::<&'static [u8], &'static [u8]>::new();
5
6  // collapse whitespace
7  t.insert(b"<a>   \n&#32;   </a>", b"<a> </a>");
8  // Tag names should be case insensitive.
9  t.insert(b"<A>   \n&#32;   </a>", b"<a> </a>");
10  t.insert(b"<a>   \n&#32;   </A>", b"<a> </a>");
11
12  // collapse and trim whitespace
13  t.insert(b"<label>   \n&#32;   </label>", b"<label></label>");
14  t.insert(b"<label>   \n&#32;a   </label>", b"<label>a</label>");
15  t.insert(b"<label>   \n&#32;a   b   </label>", b"<label>a b</label>");
16  // Tag names should be case insensitive.
17  t.insert(b"<lAbEL>   \n&#32;a   b   </LABel>", b"<label>a b</label>");
18
19  // collapse destroy whole and trim whitespace
20  t.insert(b"<ul>   \n&#32;   </ul>", b"<ul></ul>");
21  t.insert(b"<ul>   \n&#32;a   </ul>", b"<ul>a</ul>");
22  t.insert(b"<ul>   \n&#32;a   b   </ul>", b"<ul>a b</ul>");
23  t.insert(
24    b"<ul>   \n&#32;a<pre></pre>   <pre></pre>b   </ul>",
25    b"<ul>a<pre></pre><pre></pre>b</ul>",
26  );
27  t.insert(
28    b"<svg> <path> </path> <path> </path> </svg>",
29    b"<svg><path></path><path></path></svg>",
30  );
31  // Tag names should be case insensitive.
32  t.insert(b"<uL>   \n&#32;a   b   </UL>", b"<ul>a b</ul>");
33
34  // no whitespace minification
35  t.insert(b"<pre>   \n&#32; \t   </pre>", b"<pre>   \n  \t   </pre>");
36  t.insert(
37    b"<textarea>   \n&#32; \t   </textarea>",
38    b"<textarea>   \n  \t   </textarea>",
39  );
40  // Tag names should be case insensitive.
41  t.insert(b"<pRe>   \n&#32; \t   </PRE>", b"<pre>   \n  \t   </pre>");
42  t.insert(
43    b"<pre>  <span>  1    2   </span>  </pre>",
44    b"<pre>  <span>  1    2   </span>  </pre>",
45  );
46  t.insert(
47    b"<pre>  <span>  1 <pre>\n</pre>    2   </span>  </pre>",
48    b"<pre>  <span>  1 <pre>\n</pre>    2   </span>  </pre>",
49  );
50  t.insert(
51    b"<div>  <pre>  <span>  1 <pre>\n</pre>    2   </span>  </pre>  </div>",
52    b"<div><pre>  <span>  1 <pre>\n</pre>    2   </span>  </pre></div>",
53  );
54  t.insert(
55    br#"<pre><code>fn main() {
56  println!("Hello, world!");
57  <span>loop {
58    println!("Hello, world!");
59  }</span>
60</code></pre>"#,
61    br#"<pre><code>fn main() {
62  println!("Hello, world!");
63  <span>loop {
64    println!("Hello, world!");
65  }</span>
66</code></pre>"#,
67  );
68
69  // parsing omitted closing tag
70  t.insert(b"<html>", b"<html>");
71  t.insert(b" <html>\n", b"<html>");
72  t.insert(
73    b"<html><div> <p>Foo</div></html>",
74    b"<html><div><p>Foo</div>",
75  );
76
77  // self closing svg tag whitespace removal
78  t.insert(b"<svg><path d=a /></svg>", b"<svg><path d=a /></svg>");
79  t.insert(b"<svg><path d=a/ /></svg>", b"<svg><path d=a/ /></svg>");
80  t.insert(b"<svg><path d=\"a/\" /></svg>", b"<svg><path d=a/ /></svg>");
81  t.insert(b"<svg><path d=\"a/\"/></svg>", b"<svg><path d=a/ /></svg>");
82  t.insert(b"<svg><path d='a/' /></svg>", b"<svg><path d=a/ /></svg>");
83  t.insert(b"<svg><path d='a/'/></svg>", b"<svg><path d=a/ /></svg>");
84
85  // parsing with omitted tags
86  t.insert(b"<ul><li>1<li>2<li>3</ul>", b"<ul><li>1<li>2<li>3</ul>");
87  t.insert(b"<rt>", b"<rt>");
88  t.insert(b"<rt><rp>1</rp><div></div>", b"<rt><rp>1</rp><div></div>");
89  t.insert(b"<div><rt></div>", b"<div><rt></div>");
90  t.insert(b"<html><head><body>", b"<html><head><body>");
91  t.insert(b"<html><head><body>", b"<html><head><body>");
92  // Tag names should be case insensitive.
93  t.insert(b"<rt>", b"<rt>");
94
95  // removal of optional tags
96  t.insert(
97    b"<ul><li>1</li><li>2</li><li>3</li></ul>",
98    b"<ul><li>1<li>2<li>3</ul>",
99  );
100  t.insert(b"<rt></rt>", b"<rt>");
101  t.insert(
102    b"<rt></rt><rp>1</rp><div></div>",
103    b"<rt><rp>1</rp><div></div>",
104  );
105  t.insert(b"<div><rt></rt></div>", b"<div><rt></div>");
106  t.insert(
107    br#"
108        <html>
109            <head>
110            </head>
111
112            <body>
113            </body>
114        </html>
115    "#,
116    b"<html><head><body>",
117  );
118  // Tag names should be case insensitive.
119  t.insert(b"<RT></rt>", b"<rt>");
120
121  // removal of optional closing p tag
122  t.insert(b"<p></p><address></address>", b"<p><address></address>");
123  t.insert(b"<p></p>", b"<p>");
124  t.insert(b"<map><p></p></map>", b"<map><p></p></map>");
125  t.insert(
126    b"<map><p></p><address></address></map>",
127    b"<map><p><address></address></map>",
128  );
129
130  // attr double quoted value minification
131  t.insert(b"<a b=\" hello \"></a>", b"<a b=\" hello \"></a>");
132  t.insert(b"<a b=' hello '></a>", b"<a b=\" hello \"></a>");
133  t.insert(br#"<a b="/>aaaa"></a>"#, br#"<a b="/>aaaa"></a>"#);
134  t.insert(br#"<a b="</a>a"></a>"#, br#"<a b="</a>a"></a>"#);
135  t.insert(b"<a b=&#x20;hello&#x20;></a>", b"<a b=\" hello \"></a>");
136  t.insert(b"<a b=&#x20hello&#x20></a>", b"<a b=\" hello \"></a>");
137
138  // attr single quoted value minification
139  t.insert(b"<a b=\"&quot;hello\"></a>", b"<a b='\"hello'></a>");
140  t.insert(b"<a b='\"hello'></a>", b"<a b='\"hello'></a>");
141  t.insert(b"<a b='/>a'></a>", b"<a b=\"/>a\"></a>");
142  t.insert(
143    b"<a b=&#x20;he&quot;llo&#x20;></a>",
144    b"<a b=' he\"llo '></a>",
145  );
146
147  // attr unquoted value minification
148  t.insert(b"<a b=\"hello\"></a>", b"<a b=hello></a>");
149  t.insert(b"<a b='hello'></a>", b"<a b=hello></a>");
150  t.insert(b"<a b=/&gt></a>", br#"<a b="/>"></a>"#);
151  t.insert(b"<a b=/&gt&lt;a></a>", br#"<a b="/><a"></a>"#);
152  t.insert(b"<a b=hello></a>", b"<a b=hello></a>");
153
154  // class attr value minification
155  t.insert(b"<a class=&#x20;c></a>", b"<a class=c></a>");
156  t.insert(
157    b"<a class=&#x20;c&#x20&#x20;d&#x20></a>",
158    b"<a class=\"c d\"></a>",
159  );
160  t.insert(b"<a class=&#x20&#x20&#x20;&#x20></a>", b"<a></a>");
161  t.insert(b"<a class=\"  c\n \n  \"></a>", b"<a class=c></a>");
162  t.insert(b"<a class=\"  c\n \nd  \"></a>", b"<a class=\"c d\"></a>");
163  t.insert(b"<a class=\"  \n \n  \"></a>", b"<a></a>");
164  t.insert(b"<a class='  c\n \n  '></a>", b"<a class=c></a>");
165  t.insert(b"<a class='  c\n \nd  '></a>", b"<a class=\"c d\"></a>");
166  t.insert(b"<a class='  \n \n  '></a>", b"<a></a>");
167  // Attribute names should be case insensitive.
168  t.insert(b"<a CLasS='  \n \n  '></a>", b"<a></a>");
169
170  // d attr value minification
171  t.insert(b"<svg><path d=&#x20;c /></svg>", b"<svg><path d=c /></svg>");
172  t.insert(
173    b"<svg><path d=&#x20;c&#x20&#x20;d&#x20 /></svg>",
174    b"<svg><path d=\"c d\"/></svg>",
175  );
176  t.insert(
177    b"<svg><path d=&#x20;&#x20&#x20&#x20 /></svg>",
178    b"<svg><path/></svg>",
179  );
180  t.insert(
181    b"<svg><path d=\"  c\n \n  \" /></svg>",
182    b"<svg><path d=c /></svg>",
183  );
184  t.insert(
185    b"<svg><path d=\"  c\n \nd  \" /></svg>",
186    b"<svg><path d=\"c d\"/></svg>",
187  );
188  t.insert(
189    b"<svg><path d=\"  \n \n  \" /></svg>",
190    b"<svg><path/></svg>",
191  );
192  t.insert(
193    b"<svg><path d='  c\n \n  ' /></svg>",
194    b"<svg><path d=c /></svg>",
195  );
196  t.insert(
197    b"<svg><path d='  c\n \nd  ' /></svg>",
198    b"<svg><path d=\"c d\"/></svg>",
199  );
200  t.insert(b"<svg><path d='  \n \n  ' /></svg>", b"<svg><path/></svg>");
201  // Attribute names should be case insensitive.
202  t.insert(b"<svg><path D='  \n \n  ' /></svg>", b"<svg><path/></svg>");
203
204  // boolean attr value removal
205  t.insert(b"<div hidden=\"true\"></div>", b"<div hidden></div>");
206  t.insert(b"<div hidden=\"false\"></div>", b"<div hidden></div>");
207  t.insert(b"<div hidden=\"1\"></div>", b"<div hidden></div>");
208  t.insert(b"<div hidden=\"0\"></div>", b"<div hidden></div>");
209  t.insert(b"<div hidden=\"abc\"></div>", b"<div hidden></div>");
210  t.insert(b"<div hidden=\"\"></div>", b"<div hidden></div>");
211  t.insert(b"<div hidden></div>", b"<div hidden></div>");
212  // Attribute names should be case insensitive.
213  t.insert(b"<div HIDden=\"true\"></div>", b"<div hidden></div>");
214
215  // empty attr removal
216  t.insert(b"<div lang=\"  \"></div>", b"<div lang=\"  \"></div>");
217  t.insert(b"<div lang=\"\"></div>", b"<div></div>");
218  t.insert(b"<div lang=''></div>", b"<div></div>");
219  t.insert(b"<div lang=></div>", b"<div></div>");
220  t.insert(b"<div lang></div>", b"<div></div>");
221
222  // default attr value removal
223  t.insert(b"<a target=\"_self\"></a>", b"<a></a>");
224  t.insert(b"<a target='_self'></a>", b"<a></a>");
225  t.insert(b"<a target=_self></a>", b"<a></a>");
226  // Attribute names should be case insensitive.
227  t.insert(b"<a taRGET='_self'></a>", b"<a></a>");
228
229  // script type attr value removal
230  t.insert(
231    b"<script type=\"application/ecmascript\"></script>",
232    b"<script></script>",
233  );
234  t.insert(
235    b"<script type=\"application/javascript\"></script>",
236    b"<script></script>",
237  );
238  t.insert(
239    b"<script type=\"text/jscript\"></script>",
240    b"<script></script>",
241  );
242  t.insert(
243    b"<script type=\"text/plain\"></script>",
244    b"<script type=text/plain></script>",
245  );
246  // Tag and attribute names should be case insensitive.
247  t.insert(
248    b"<SCRipt TYPE=\"application/ecmascript\"></SCrIPT>",
249    b"<script></script>",
250  );
251
252  // empty attr value removal
253  t.insert(b"<div a=\"  \"></div>", b"<div a=\"  \"></div>");
254  t.insert(b"<div a=\"\"></div>", b"<div a></div>");
255  t.insert(b"<div a=''></div>", b"<div a></div>");
256  t.insert(b"<div a=></div>", b"<div a></div>");
257  t.insert(b"<div a></div>", b"<div a></div>");
258
259  // hexadecimal entity decoding
260  t.insert(b"&#x2E", b".");
261  t.insert(b"&#x2F", b"/");
262  t.insert(b"&#x2f", b"/");
263  t.insert(b"&#x00", b"\0");
264  t.insert(b"&#x30", b"0");
265  t.insert(b"&#x0030", b"0");
266  t.insert(b"&#x000000000000000000000000000000000000000000030", b"0");
267  t.insert(b"&#x30;", b"0");
268  t.insert(b"&#x0030;", b"0");
269  t.insert(b"&#x000000000000000000000000000000000000000000030;", b"0");
270  t.insert(b"&#x1151;", b"\xe1\x85\x91");
271  t.insert(b"&#x11FFFF;", b"\xef\xbf\xbd");
272  t.insert(
273    b"&#xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;",
274    b"\xef\xbf\xbd",
275  );
276
277  // decimal entity decoding
278  t.insert(b"&#48", b"0");
279  t.insert(b"&#0048", b"0");
280  t.insert(b"&#000000000000000000000000000000000000000000048", b"0");
281  t.insert(b"&#48;", b"0");
282  t.insert(b"&#0048;", b"0");
283  t.insert(b"&#000000000000000000000000000000000000000000048;", b"0");
284  t.insert(b"&#4433;", b"\xe1\x85\x91");
285  t.insert(b"&#1114112;", b"\xef\xbf\xbd");
286  t.insert(
287    b"&#999999999999999999999999999999999999999999999;",
288    b"\xef\xbf\xbd",
289  );
290
291  // named entity decoding
292  t.insert(b"&gt", b">");
293  t.insert(b"&gt;", b">");
294  t.insert(b"&amp", b"&");
295  t.insert(b"&amp;", b"&");
296  t.insert(b"&xxxyyyzzz", b"&xxxyyyzzz");
297  t.insert(b"&ampere", b"&ere");
298  t.insert(b"They & Co.", b"They & Co.");
299  t.insert(b"if (this && that)", b"if (this && that)");
300  // These entities decode to longer UTF-8 sequences, so we keep them encoded.
301  t.insert(b"&nLt;", b"&nLt;");
302  t.insert(b"&nLt;abc", b"&nLt;abc");
303  t.insert(b"&nGt;", b"&nGt;");
304
305  // Named entities not ending with ';' in attr values are not decoded if immediately
306  // followed by an alphanumeric or `=` character. (See parser for more details.)
307  t.insert(
308    br#"<a href="exam ple?&gta=5"></a>"#,
309    br#"<a href="exam ple?&gta=5"></a>"#,
310  );
311  t.insert(
312    br#"<a href="exam ple?&gt=5"></a>"#,
313    br#"<a href="exam ple?&gt=5"></a>"#,
314  );
315  t.insert(
316    br#"<a href="exam ple?&gt~5"></a>"#,
317    br#"<a href="exam ple?>~5"></a>"#,
318  );
319
320  // unintentional entity prevention
321  t.insert(b"&ampamp", b"&ampamp");
322  t.insert(b"&ampamp;", b"&ampamp;");
323  t.insert(b"&amp;amp", b"&ampamp");
324  t.insert(b"&amp;amp;", b"&ampamp;");
325  t.insert(b"&&#97&#109;&#112;;", b"&ampamp;");
326  t.insert(b"&&#97&#109;p;", b"&ampamp;");
327  t.insert(b"&am&#112", b"&ampamp");
328  t.insert(b"&am&#112;", b"&ampamp");
329  t.insert(b"&am&#112&#59", b"&ampamp;");
330  t.insert(b"&am&#112;;", b"&ampamp;");
331  t.insert(b"&am&#112;&#59", b"&ampamp;");
332  t.insert(b"&am&#112;&#59;", b"&ampamp;");
333
334  t.insert(b"&l&#116", b"&amplt");
335  t.insert(b"&&#108t", b"&amplt");
336  t.insert(b"&&#108t;", b"&amplt;");
337  t.insert(b"&&#108t&#59", b"&amplt;");
338  t.insert(b"&amplt", b"&amplt");
339  t.insert(b"&amplt;", b"&amplt;");
340
341  t.insert(b"&am&am&#112", b"&am&ampamp");
342  t.insert(b"&am&am&#112&#59", b"&am&ampamp;");
343
344  t.insert(b"&amp&nLt;", b"&&nLt;");
345  t.insert(b"&am&nLt;", b"&am&nLt;");
346  t.insert(b"&am&nLt;a", b"&am&nLt;a");
347  t.insert(b"&am&nLt", b"&am&nLt");
348
349  // left chevron in content
350  t.insert(b"<pre><</pre>", b"<pre><</pre>");
351  t.insert(b"<pre>< </pre>", b"<pre>< </pre>");
352  t.insert(b"<pre> < </pre>", b"<pre> < </pre>");
353
354  t.insert(b"<pre> &lta </pre>", b"<pre> &LTa </pre>");
355  t.insert(b"<pre> &lt;a </pre>", b"<pre> &LTa </pre>");
356  t.insert(b"<pre> &LTa </pre>", b"<pre> &LTa </pre>");
357  t.insert(b"<pre> &LT;a </pre>", b"<pre> &LTa </pre>");
358
359  t.insert(b"<pre> &lt? </pre>", b"<pre> &LT? </pre>");
360  t.insert(b"<pre> &lt;? </pre>", b"<pre> &LT? </pre>");
361  t.insert(b"<pre> &LT? </pre>", b"<pre> &LT? </pre>");
362  t.insert(b"<pre> &LT;? </pre>", b"<pre> &LT? </pre>");
363
364  t.insert(b"<pre> &lt;/ </pre>", b"<pre> &LT/ </pre>");
365  t.insert(b"<pre> &lt;! </pre>", b"<pre> &LT! </pre>");
366
367  t.insert(b"&LT", b"<");
368  t.insert(b"&LT;", b"<");
369  t.insert(b"&LT;;", b"<;");
370  t.insert(b"&LT;&#59", b"<;");
371  t.insert(b"&LT;&#59;", b"<;");
372  t.insert(b"&lt", b"<");
373  t.insert(b"&lt;", b"<");
374  t.insert(b"&lt;;", b"<;");
375  t.insert(b"&lt;&#59", b"<;");
376  t.insert(b"&lt;&#59;", b"<;");
377
378  t.insert(b"&LTa", b"&LTa");
379  t.insert(b"&LT;a", b"&LTa");
380  t.insert(b"&LT;a;", b"&LTa;");
381  t.insert(b"&LT;a&#59", b"&LTa;");
382  t.insert(b"&LT;a&#59;", b"&LTa;");
383  t.insert(b"&LT;a;&#59;", b"&LTa;;");
384
385  t.insert(b"&lt;&#33", b"&LT!");
386  t.insert(b"&lt;&#38", b"<&");
387  t.insert(b"&lt;&#47", b"&LT/");
388  t.insert(b"&lt;&#63", b"&LT?");
389  t.insert(b"&lt;&#64", b"<@");
390
391  // comments removal
392  t.insert(
393    b"<pre>a <!-- akd--sj\n <!-- \t\0f--ajk--df->lafj -->  b</pre>",
394    b"<pre>a   b</pre>",
395  );
396  t.insert(b"&a<!-- akd--sj\n <!-- \t\0f--ajk--df->lafj -->mp", b"&amp");
397  t.insert(
398    b"<script><!-- akd--sj\n <!-- \t\0f--ajk--df->lafj --></script>",
399    b"<script><!-- akd--sj\n <!-- \t\0f--ajk--df->lafj --></script>",
400  );
401
402  // processing instructions
403  t.insert(b"<?php hello??? >>  ?>", b"<?php hello??? >>  ?>");
404  t.insert(b"av<?xml 1.0 ?>g", b"av<?xml 1.0 ?>g");
405
406  // self closing svg
407  t.insert(
408    b"<a><svg viewBox=\"0 0 700 100\" /></a><footer></footer>",
409    b"<a><svg viewbox=\"0 0 700 100\"/></a><footer></footer>",
410  );
411  t.insert(
412    b"<a><svg viewBox=\"0 0 700 100\"></svg></a><footer></footer>",
413    b"<a><svg viewbox=\"0 0 700 100\"></svg></a><footer></footer>",
414  );
415
416  t
417}
418
419pub fn create_common_css_test_data() -> HashMap<&'static [u8], &'static [u8]> {
420  let mut t = HashMap::<&'static [u8], &'static [u8]>::new();
421
422  // style element minification
423  t.insert(
424    b"<style>div { color: yellow }</style>",
425    b"<style>div{color:#ff0}</style>",
426  );
427
428  t
429}
430
431pub fn create_common_js_test_data() -> HashMap<&'static [u8], &'static [u8]> {
432  let mut t = HashMap::<&'static [u8], &'static [u8]>::new();
433
434  // js minification
435  t.insert(b"<script></script>", b"<script></script>");
436  t.insert(b"<script> </script>", b"<script></script>");
437  t.insert(b"<script>let a = 1;</script>", b"<script>let a=1;</script>");
438  t.insert(
439    b"<script type=text/javascript>let a = 1;</script>",
440    b"<script>let a=1;</script>",
441  );
442  t.insert(
443    br#"
444        <script>let a = 1;</script>
445        <script>let b = 2;</script>
446    "#,
447    b"<script>let a=1;</script><script>let b=2;</script>",
448  );
449  t.insert(
450    b"<scRIPt type=text/plain>   alert(1.00000);   </scripT>",
451    b"<script type=text/plain>   alert(1.00000);   </script>",
452  );
453  t.insert(
454    br#"
455        <script>
456            // This is a comment.
457            let a = 1;
458        </script>
459    "#,
460    b"<script>// This is a comment.\nlet a=1;</script>",
461  );
462
463  // js minification unintentional closing tag
464  /* TODO Reenable once unintentional script closing tag escaping is implemented in minify-js.
465  t.insert(
466      br#"<script>let a = "</" + "script>";</script>"#,
467      br#"<script>let a="<\/script>";</script>"#,
468  );
469  t.insert(
470      br#"<script>let a = "</S" + "cRiPT>";</script>"#,
471      br#"<script>let a="<\/ScRiPT>";</script>"#,
472  );
473  t.insert(
474      br#"<script>let a = "\u003c/script>";</script>"#,
475      br#"<script>let a="<\/script>";</script>"#,
476  );
477  t.insert(
478      br#"<script>let a = "\u003c/scrIPt>";</script>"#,
479      br#"<script>let a="<\/scrIPt>";</script>"#,
480  );
481  */
482
483  t
484}
485
486pub fn create_common_noncompliant_test_data() -> HashMap<&'static [u8], &'static [u8]> {
487  let mut t = HashMap::<&'static [u8], &'static [u8]>::new();
488
489  // attr unquoted value minification
490  t.insert(b"<a b==></a>", b"<a b==></a>");
491  t.insert(b"<a b=`'\"<<==/`/></a>", b"<a b=`'\"<<==/`/></a>");
492
493  t
494}