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