Subversion Repositories SE.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
12 7u83 1
-module(mochiweb_html_tests).
2
-include_lib("eunit/include/eunit.hrl").
3
 
4
to_html_test() ->
5
    ?assertEqual(
6
       <<"<html><head><title>hey!</title></head><body><p class=\"foo\">what's up<br /></p><div>sucka</div>RAW!<!-- comment! --></body></html>">>,
7
       iolist_to_binary(
8
         mochiweb_html:to_html({html, [],
9
                  [{<<"head">>, [],
10
                    [{title, <<"hey!">>}]},
11
                   {body, [],
12
                    [{p, [{class, foo}], [<<"what's">>, <<" up">>, {br}]},
13
                     {'div', <<"sucka">>},
14
                     {'=', <<"RAW!">>},
15
                     {comment, <<" comment! ">>}]}]}))),
16
    ?assertEqual(
17
       <<"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">">>,
18
       iolist_to_binary(
19
         mochiweb_html:to_html({doctype,
20
                  [<<"html">>, <<"PUBLIC">>,
21
                   <<"-//W3C//DTD XHTML 1.0 Transitional//EN">>,
22
                   <<"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>]}))),
23
    ?assertEqual(
24
       <<"<html><?xml:namespace prefix=\"o\" ns=\"urn:schemas-microsoft-com:office:office\"?></html>">>,
25
       iolist_to_binary(
26
         mochiweb_html:to_html({<<"html">>,[],
27
                  [{pi, <<"xml:namespace">>,
28
                    [{<<"prefix">>,<<"o">>},
29
                     {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}]}))),
30
    ok.
31
 
32
escape_test() ->
33
    ?assertEqual(
34
       <<"&amp;quot;\"word &gt;&lt;&lt;up!&amp;quot;">>,
35
       mochiweb_html:escape(<<"&quot;\"word ><<up!&quot;">>)),
36
    ?assertEqual(
37
       <<"&amp;quot;\"word &gt;&lt;&lt;up!&amp;quot;">>,
38
       mochiweb_html:escape("&quot;\"word ><<up!&quot;")),
39
    ?assertEqual(
40
       <<"&amp;quot;\"word &gt;&lt;&lt;up!&amp;quot;">>,
41
       mochiweb_html:escape('&quot;\"word ><<up!&quot;')),
42
    ok.
43
 
44
escape_attr_test() ->
45
    ?assertEqual(
46
       <<"&amp;quot;&quot;word &gt;&lt;&lt;up!&amp;quot;">>,
47
       mochiweb_html:escape_attr(<<"&quot;\"word ><<up!&quot;">>)),
48
    ?assertEqual(
49
       <<"&amp;quot;&quot;word &gt;&lt;&lt;up!&amp;quot;">>,
50
       mochiweb_html:escape_attr("&quot;\"word ><<up!&quot;")),
51
    ?assertEqual(
52
       <<"&amp;quot;&quot;word &gt;&lt;&lt;up!&amp;quot;">>,
53
       mochiweb_html:escape_attr('&quot;\"word ><<up!&quot;')),
54
    ?assertEqual(
55
       <<"12345">>,
56
       mochiweb_html:escape_attr(12345)),
57
    ?assertEqual(
58
       <<"1.5">>,
59
       mochiweb_html:escape_attr(1.5)),
60
    ok.
61
 
62
tokens_test() ->
63
    ?assertEqual(
64
       [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>},
65
                                {<<"wibble">>, <<"wibble">>},
66
                                {<<"alice">>, <<"bob">>}], true}],
67
       mochiweb_html:tokens(<<"<foo bar=baz wibble='wibble' alice=\"bob\"/>">>)),
68
    ?assertEqual(
69
       [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>},
70
                                {<<"wibble">>, <<"wibble">>},
71
                                {<<"alice">>, <<"bob">>}], true}],
72
       mochiweb_html:tokens(<<"<foo bar=baz wibble='wibble' alice=bob/>">>)),
73
    ?assertEqual(
74
       [{comment, <<"[if lt IE 7]>\n<style type=\"text/css\">\n.no_ie { display: none; }\n</style>\n<![endif]">>}],
75
       mochiweb_html:tokens(<<"<!--[if lt IE 7]>\n<style type=\"text/css\">\n.no_ie { display: none; }\n</style>\n<![endif]-->">>)),
76
    ?assertEqual(
77
       [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false},
78
        {data, <<" A= B <= C ">>, false},
79
        {end_tag, <<"script">>}],
80
       mochiweb_html:tokens(<<"<script type=\"text/javascript\"> A= B <= C </script>">>)),
81
    ?assertEqual(
82
       [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false},
83
        {data, <<" A= B <= C ">>, false},
84
        {end_tag, <<"script">>}],
85
       mochiweb_html:tokens(<<"<script type =\"text/javascript\"> A= B <= C </script>">>)),
86
    ?assertEqual(
87
       [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false},
88
        {data, <<" A= B <= C ">>, false},
89
        {end_tag, <<"script">>}],
90
       mochiweb_html:tokens(<<"<script type = \"text/javascript\"> A= B <= C </script>">>)),
91
    ?assertEqual(
92
       [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false},
93
        {data, <<" A= B <= C ">>, false},
94
        {end_tag, <<"script">>}],
95
       mochiweb_html:tokens(<<"<script type= \"text/javascript\"> A= B <= C </script>">>)),
96
    ?assertEqual(
97
       [{start_tag, <<"textarea">>, [], false},
98
        {data, <<"<html></body>">>, false},
99
        {end_tag, <<"textarea">>}],
100
       mochiweb_html:tokens(<<"<textarea><html></body></textarea>">>)),
101
    ?assertEqual(
102
       [{start_tag, <<"textarea">>, [], false},
103
        {data, <<"<html></body></textareaz>">>, false}],
104
       mochiweb_html:tokens(<<"<textarea ><html></body></textareaz>">>)),
105
    ?assertEqual(
106
       [{pi, <<"xml:namespace">>,
107
         [{<<"prefix">>,<<"o">>},
108
          {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}],
109
       mochiweb_html:tokens(<<"<?xml:namespace prefix=\"o\" ns=\"urn:schemas-microsoft-com:office:office\"?>">>)),
110
    ?assertEqual(
111
       [{pi, <<"xml:namespace">>,
112
         [{<<"prefix">>,<<"o">>},
113
          {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}],
114
       mochiweb_html:tokens(<<"<?xml:namespace prefix=o ns=urn:schemas-microsoft-com:office:office \n?>">>)),
115
    ?assertEqual(
116
       [{pi, <<"xml:namespace">>,
117
         [{<<"prefix">>,<<"o">>},
118
          {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}],
119
       mochiweb_html:tokens(<<"<?xml:namespace prefix=o ns=urn:schemas-microsoft-com:office:office">>)),
120
    ?assertEqual(
121
       [{data, <<"<">>, false}],
122
       mochiweb_html:tokens(<<"&lt;">>)),
123
    ?assertEqual(
124
       [{data, <<"not html ">>, false},
125
        {data, <<"< at all">>, false}],
126
       mochiweb_html:tokens(<<"not html < at all">>)),
127
    ok.
128
 
129
surrogate_test() ->
130
    %% https://github.com/mochi/mochiweb/issues/164
131
    ?assertEqual(
132
       [{data,<<240,159,152,138>>,false}],
133
       mochiweb_html:tokens(<<"&#55357;&#56842;">>)).
134
 
135
parse_test() ->
136
    D0 = <<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">
137
<html>
138
 <head>
139
   <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">
140
   <title>Foo</title>
141
   <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/rel/dojo/resources/dojo.css\" media=\"screen\">
142
   <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/foo.css\" media=\"screen\">
143
   <!--[if lt IE 7]>
144
   <style type=\"text/css\">
145
     .no_ie { display: none; }
146
   </style>
147
   <![endif]-->
148
   <link rel=\"icon\" href=\"/static/images/favicon.ico\" type=\"image/x-icon\">
149
   <link rel=\"shortcut icon\" href=\"/static/images/favicon.ico\" type=\"image/x-icon\">
150
 </head>
151
 <body id=\"home\" class=\"tundra\"><![CDATA[&lt;<this<!-- is -->CDATA>&gt;]]></body>
152
</html>">>,
153
    ?assertEqual(
154
       {<<"html">>, [],
155
        [{<<"head">>, [],
156
          [{<<"meta">>,
157
            [{<<"http-equiv">>,<<"Content-Type">>},
158
             {<<"content">>,<<"text/html; charset=UTF-8">>}],
159
            []},
160
           {<<"title">>,[],[<<"Foo">>]},
161
           {<<"link">>,
162
            [{<<"rel">>,<<"stylesheet">>},
163
             {<<"type">>,<<"text/css">>},
164
             {<<"href">>,<<"/static/rel/dojo/resources/dojo.css">>},
165
             {<<"media">>,<<"screen">>}],
166
            []},
167
           {<<"link">>,
168
            [{<<"rel">>,<<"stylesheet">>},
169
             {<<"type">>,<<"text/css">>},
170
             {<<"href">>,<<"/static/foo.css">>},
171
             {<<"media">>,<<"screen">>}],
172
            []},
173
           {comment,<<"[if lt IE 7]>\n   <style type=\"text/css\">\n     .no_ie { display: none; }\n   </style>\n   <![endif]">>},
174
           {<<"link">>,
175
            [{<<"rel">>,<<"icon">>},
176
             {<<"href">>,<<"/static/images/favicon.ico">>},
177
             {<<"type">>,<<"image/x-icon">>}],
178
            []},
179
           {<<"link">>,
180
            [{<<"rel">>,<<"shortcut icon">>},
181
             {<<"href">>,<<"/static/images/favicon.ico">>},
182
             {<<"type">>,<<"image/x-icon">>}],
183
            []}]},
184
         {<<"body">>,
185
          [{<<"id">>,<<"home">>},
186
           {<<"class">>,<<"tundra">>}],
187
          [<<"&lt;<this<!-- is -->CDATA>&gt;">>]}]},
188
       mochiweb_html:parse(D0)),
189
    ?assertEqual(
190
       {<<"html">>,[],
191
        [{pi, <<"xml:namespace">>,
192
          [{<<"prefix">>,<<"o">>},
193
           {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}]},
194
       mochiweb_html:parse(
195
         <<"<html><?xml:namespace prefix=\"o\" ns=\"urn:schemas-microsoft-com:office:office\"?></html>">>)),
196
    ?assertEqual(
197
       {<<"html">>, [],
198
        [{<<"dd">>, [], [<<"foo">>]},
199
         {<<"dt">>, [], [<<"bar">>]}]},
200
       mochiweb_html:parse(<<"<html><dd>foo<dt>bar</html>">>)),
201
    %% Singleton sadness
202
    ?assertEqual(
203
       {<<"html">>, [],
204
        [{<<"link">>, [], []},
205
         <<"foo">>,
206
         {<<"br">>, [], []},
207
         <<"bar">>]},
208
       mochiweb_html:parse(<<"<html><link>foo<br>bar</html>">>)),
209
    ?assertEqual(
210
       {<<"html">>, [],
211
        [{<<"link">>, [], [<<"foo">>,
212
                           {<<"br">>, [], []},
213
                           <<"bar">>]}]},
214
       mochiweb_html:parse(<<"<html><link>foo<br>bar</link></html>">>)),
215
    %% Case insensitive tags
216
    ?assertEqual(
217
       {<<"html">>, [],
218
        [{<<"head">>, [], [<<"foo">>,
219
                           {<<"br">>, [], []},
220
                           <<"BAR">>]},
221
         {<<"body">>, [{<<"class">>, <<"">>}, {<<"bgcolor">>, <<"#Aa01fF">>}], []}
222
        ]},
223
       mochiweb_html:parse(<<"<html><Head>foo<bR>BAR</head><body Class=\"\" bgcolor=\"#Aa01fF\"></BODY></html>">>)),
224
    ok.
225
 
226
exhaustive_is_singleton_test() ->
227
    T = mochiweb_cover:clause_lookup_table(mochiweb_html, is_singleton),
228
    [?assertEqual(V, mochiweb_html:is_singleton(K)) || {K, V} <- T].
229
 
230
tokenize_attributes_test() ->
231
    ?assertEqual(
232
       {<<"foo">>,
233
        [{<<"bar">>, <<"b\"az">>},
234
         {<<"wibble">>, <<"wibble">>},
235
         {<<"taco", 16#c2, 16#a9>>, <<"bell">>},
236
         {<<"quux">>, <<"quux">>}],
237
        []},
238
       mochiweb_html:parse(<<"<foo bar=\"b&quot;az\" wibble taco&copy;=bell quux">>)),
239
    ok.
240
 
241
tokens2_test() ->
242
    D0 = <<"<channel><title>from __future__ import *</title><link>http://bob.pythonmac.org</link><description>Bob's Rants</description></channel>">>,
243
    ?assertEqual(
244
       [{start_tag,<<"channel">>,[],false},
245
        {start_tag,<<"title">>,[],false},
246
        {data,<<"from __future__ import *">>,false},
247
        {end_tag,<<"title">>},
248
        {start_tag,<<"link">>,[],true},
249
        {data,<<"http://bob.pythonmac.org">>,false},
250
        {end_tag,<<"link">>},
251
        {start_tag,<<"description">>,[],false},
252
        {data,<<"Bob's Rants">>,false},
253
        {end_tag,<<"description">>},
254
        {end_tag,<<"channel">>}],
255
       mochiweb_html:tokens(D0)),
256
    ok.
257
 
258
to_tokens_test() ->
259
    ?assertEqual(
260
       [{start_tag, <<"p">>, [{class, 1}], false},
261
        {end_tag, <<"p">>}],
262
       mochiweb_html:to_tokens({p, [{class, 1}], []})),
263
    ?assertEqual(
264
       [{start_tag, <<"p">>, [], false},
265
        {end_tag, <<"p">>}],
266
       mochiweb_html:to_tokens({p})),
267
    ?assertEqual(
268
       [{'=', <<"data">>}],
269
       mochiweb_html:to_tokens({'=', <<"data">>})),
270
    ?assertEqual(
271
       [{comment, <<"comment">>}],
272
       mochiweb_html:to_tokens({comment, <<"comment">>})),
273
    %% This is only allowed in sub-tags:
274
    %% {p, [{"class", "foo"}]} as {p, [{"class", "foo"}], []}
275
    %% On the outside it's always treated as follows:
276
    %% {p, [], [{"class", "foo"}]} as {p, [], [{"class", "foo"}]}
277
    ?assertEqual(
278
       [{start_tag, <<"html">>, [], false},
279
        {start_tag, <<"p">>, [{class, 1}], false},
280
        {end_tag, <<"p">>},
281
        {end_tag, <<"html">>}],
282
       mochiweb_html:to_tokens({html, [{p, [{class, 1}]}]})),
283
    ok.
284
 
285
parse2_test() ->
286
    D0 = <<"<channel><title>from __future__ import *</title><link>http://bob.pythonmac.org<br>foo</link><description>Bob's Rants</description></channel>">>,
287
    ?assertEqual(
288
       {<<"channel">>,[],
289
        [{<<"title">>,[],[<<"from __future__ import *">>]},
290
         {<<"link">>,[],[
291
                         <<"http://bob.pythonmac.org">>,
292
                         {<<"br">>,[],[]},
293
                         <<"foo">>]},
294
         {<<"description">>,[],[<<"Bob's Rants">>]}]},
295
       mochiweb_html:parse(D0)),
296
    ok.
297
 
298
parse_tokens_test() ->
299
    D0 = [{doctype,[<<"HTML">>,<<"PUBLIC">>,<<"-//W3C//DTD HTML 4.01 Transitional//EN">>]},
300
          {data,<<"\n">>,true},
301
          {start_tag,<<"html">>,[],false}],
302
    ?assertEqual(
303
       {<<"html">>, [], []},
304
       mochiweb_html:parse_tokens(D0)),
305
    D1 = D0 ++ [{end_tag, <<"html">>}],
306
    ?assertEqual(
307
       {<<"html">>, [], []},
308
       mochiweb_html:parse_tokens(D1)),
309
    D2 = D0 ++ [{start_tag, <<"body">>, [], false}],
310
    ?assertEqual(
311
       {<<"html">>, [], [{<<"body">>, [], []}]},
312
       mochiweb_html:parse_tokens(D2)),
313
    D3 = D0 ++ [{start_tag, <<"head">>, [], false},
314
                {end_tag, <<"head">>},
315
                {start_tag, <<"body">>, [], false}],
316
    ?assertEqual(
317
       {<<"html">>, [], [{<<"head">>, [], []}, {<<"body">>, [], []}]},
318
       mochiweb_html:parse_tokens(D3)),
319
    D4 = D3 ++ [{data,<<"\n">>,true},
320
                {start_tag,<<"div">>,[{<<"class">>,<<"a">>}],false},
321
                {start_tag,<<"a">>,[{<<"name">>,<<"#anchor">>}],false},
322
                {end_tag,<<"a">>},
323
                {end_tag,<<"div">>},
324
                {start_tag,<<"div">>,[{<<"class">>,<<"b">>}],false},
325
                {start_tag,<<"div">>,[{<<"class">>,<<"c">>}],false},
326
                {end_tag,<<"div">>},
327
                {end_tag,<<"div">>}],
328
    ?assertEqual(
329
       {<<"html">>, [],
330
        [{<<"head">>, [], []},
331
         {<<"body">>, [],
332
          [{<<"div">>, [{<<"class">>, <<"a">>}], [{<<"a">>, [{<<"name">>, <<"#anchor">>}], []}]},
333
           {<<"div">>, [{<<"class">>, <<"b">>}], [{<<"div">>, [{<<"class">>, <<"c">>}], []}]}
334
          ]}]},
335
       mochiweb_html:parse_tokens(D4)),
336
    D5 = [{start_tag,<<"html">>,[],false},
337
          {data,<<"\n">>,true},
338
          {data,<<"boo">>,false},
339
          {data,<<"hoo">>,false},
340
          {data,<<"\n">>,true},
341
          {end_tag,<<"html">>}],
342
    ?assertEqual(
343
       {<<"html">>, [], [<<"\nboohoo\n">>]},
344
       mochiweb_html:parse_tokens(D5)),
345
    D6 = [{start_tag,<<"html">>,[],false},
346
          {data,<<"\n">>,true},
347
          {data,<<"\n">>,true},
348
          {end_tag,<<"html">>}],
349
    ?assertEqual(
350
       {<<"html">>, [], []},
351
       mochiweb_html:parse_tokens(D6)),
352
    D7 = [{start_tag,<<"html">>,[],false},
353
          {start_tag,<<"ul">>,[],false},
354
          {start_tag,<<"li">>,[],false},
355
          {data,<<"word">>,false},
356
          {start_tag,<<"li">>,[],false},
357
          {data,<<"up">>,false},
358
          {end_tag,<<"li">>},
359
          {start_tag,<<"li">>,[],false},
360
          {data,<<"fdsa">>,false},
361
          {start_tag,<<"br">>,[],true},
362
          {data,<<"asdf">>,false},
363
          {end_tag,<<"ul">>},
364
          {end_tag,<<"html">>}],
365
    ?assertEqual(
366
       {<<"html">>, [],
367
        [{<<"ul">>, [],
368
          [{<<"li">>, [], [<<"word">>]},
369
           {<<"li">>, [], [<<"up">>]},
370
           {<<"li">>, [], [<<"fdsa">>,{<<"br">>, [], []}, <<"asdf">>]}]}]},
371
       mochiweb_html:parse_tokens(D7)),
372
    ok.
373
 
374
destack_test() ->
375
    ?assertEqual(
376
       {<<"a">>, [], []},
377
       mochiweb_html:destack([{<<"a">>, [], []}])),
378
    ?assertEqual(
379
       {<<"a">>, [], [{<<"b">>, [], []}]},
380
       mochiweb_html:destack([{<<"b">>, [], []}, {<<"a">>, [], []}])),
381
    ?assertEqual(
382
       {<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]},
383
       mochiweb_html:destack(
384
         [{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}])),
385
    ?assertEqual(
386
       [{<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]}],
387
       mochiweb_html:destack(
388
         <<"b">>,
389
         [{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}])),
390
    ?assertEqual(
391
       [{<<"b">>, [], [{<<"c">>, [], []}]}, {<<"a">>, [], []}],
392
       mochiweb_html:destack(
393
         <<"c">>,
394
         [{<<"c">>, [], []}, {<<"b">>, [], []},{<<"a">>, [], []}])),
395
    ok.
396
 
397
doctype_test() ->
398
    ?assertEqual(
399
       {<<"html">>,[],[{<<"head">>,[],[]}]},
400
       mochiweb_html:parse("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
401
                           "<html><head></head></body></html>")),
402
    %% http://code.google.com/p/mochiweb/issues/detail?id=52
403
    ?assertEqual(
404
       {<<"html">>,[],[{<<"head">>,[],[]}]},
405
       mochiweb_html:parse("<html>"
406
                           "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
407
                           "<head></head></body></html>")),
408
    %% http://github.com/mochi/mochiweb/pull/13
409
    ?assertEqual(
410
       {<<"html">>,[],[{<<"head">>,[],[]}]},
411
       mochiweb_html:parse("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"/>"
412
                           "<html>"
413
                           "<head></head></body></html>")),
414
    ok.
415
 
416
dumb_br_test() ->
417
    %% http://code.google.com/p/mochiweb/issues/detail?id=71
418
    ?assertEqual(
419
       {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]},
420
       mochiweb_html:parse("<div><br/><br/>z</br/></br/></div>")),
421
    ?assertEqual(
422
       {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]},
423
       mochiweb_html:parse("<div><br><br>z</br/></br/></div>")),
424
    ?assertEqual(
425
       {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>, {<<"br">>, [], []}, {<<"br">>, [], []}]},
426
       mochiweb_html:parse("<div><br><br>z<br/><br/></div>")),
427
    ?assertEqual(
428
       {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]},
429
       mochiweb_html:parse("<div><br><br>z</br></br></div>")).
430
 
431
empty_elements_test() ->
432
    ?assertEqual(
433
       {<<"div">>,[],[<<"a">>,{<<"area">>,[],[]},<<"z">>]},
434
       mochiweb_html:parse("<div>a<area>z</div>")),
435
    ?assertEqual(
436
       {<<"div">>,[],[<<"a">>,{<<"base">>,[],[]},<<"z">>]},
437
       mochiweb_html:parse("<div>a<base>z</div>")),
438
    ?assertEqual(
439
       {<<"div">>,[],[<<"a">>,{<<"br">>,[],[]},<<"z">>]},
440
       mochiweb_html:parse("<div>a<br>z</div>")),
441
    ?assertEqual(
442
       {<<"div">>,[],[<<"a">>,{<<"col">>,[],[]},<<"z">>]},
443
       mochiweb_html:parse("<div>a<col>z</div>")),
444
    ?assertEqual(
445
       {<<"div">>,[],[<<"a">>,{<<"embed">>,[],[]},<<"z">>]},
446
       mochiweb_html:parse("<div>a<embed>z</div>")),
447
    ?assertEqual(
448
       {<<"div">>,[],[<<"a">>,{<<"hr">>,[],[]},<<"z">>]},
449
       mochiweb_html:parse("<div>a<hr>z</div>")),
450
    ?assertEqual(
451
       {<<"div">>,[],[<<"a">>,{<<"img">>,[],[]},<<"z">>]},
452
       mochiweb_html:parse("<div>a<img>z</div>")),
453
    ?assertEqual(
454
       {<<"div">>,[],[<<"a">>,{<<"input">>,[],[]},<<"z">>]},
455
       mochiweb_html:parse("<div>a<input>z</div>")),
456
    ?assertEqual(
457
       {<<"div">>,[],[<<"a">>,{<<"keygen">>,[],[]},<<"z">>]},
458
       mochiweb_html:parse("<div>a<keygen>z</div>")),
459
    ?assertEqual(
460
       {<<"div">>,[],[<<"a">>,{<<"link">>,[],[]},<<"z">>]},
461
       mochiweb_html:parse("<div>a<link>z</div>")),
462
    ?assertEqual(
463
       {<<"div">>,[],[<<"a">>,{<<"meta">>,[],[]},<<"z">>]},
464
       mochiweb_html:parse("<div>a<meta>z</div>")),
465
    ?assertEqual(
466
       {<<"div">>,[],[<<"a">>,{<<"param">>,[],[]},<<"z">>]},
467
       mochiweb_html:parse("<div>a<param>z</div>")),
468
    ?assertEqual(
469
       {<<"div">>,[],[<<"a">>,{<<"source">>,[],[]},<<"z">>]},
470
       mochiweb_html:parse("<div>a<source>z</div>")),
471
    ?assertEqual(
472
       {<<"div">>,[],[<<"a">>,{<<"track">>,[],[]},<<"z">>]},
473
       mochiweb_html:parse("<div>a<track>z</div>")),
474
    ?assertEqual(
475
       {<<"div">>,[],[<<"a">>,{<<"wbr">>,[],[]},<<"z">>]},
476
       mochiweb_html:parse("<div>a<wbr>z</div>")).
477
 
478
php_test() ->
479
    %% http://code.google.com/p/mochiweb/issues/detail?id=71
480
    ?assertEqual(
481
       [{pi, <<"php\n">>}],
482
       mochiweb_html:tokens(
483
         "<?php\n?>")),
484
    ?assertEqual(
485
       {<<"div">>, [], [{pi, <<"php\n">>}]},
486
       mochiweb_html:parse(
487
         "<div><?php\n?></div>")),
488
    ok.
489
 
490
parse_unquoted_attr_test() ->
491
    D0 = <<"<html><img src=/images/icon.png/></html>">>,
492
    ?assertEqual(
493
        {<<"html">>,[],[
494
            { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] }
495
        ]},
496
        mochiweb_html:parse(D0)),
497
 
498
    D1 = <<"<html><img src=/images/icon.png></img></html>">>,
499
        ?assertEqual(
500
            {<<"html">>,[],[
501
                { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] }
502
            ]},
503
            mochiweb_html:parse(D1)),
504
 
505
    D2 = <<"<html><img src=/images/icon&gt;.png width=100></img></html>">>,
506
        ?assertEqual(
507
            {<<"html">>,[],[
508
                { <<"img">>, [ { <<"src">>, <<"/images/icon>.png">> }, { <<"width">>, <<"100">> } ], [] }
509
            ]},
510
            mochiweb_html:parse(D2)),
511
    ok.
512
 
513
parse_quoted_attr_test() ->
514
    D0 = <<"<html><img src='/images/icon.png'></html>">>,
515
    ?assertEqual(
516
        {<<"html">>,[],[
517
            { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] }
518
        ]},
519
        mochiweb_html:parse(D0)),
520
 
521
    D1 = <<"<html><img src=\"/images/icon.png'></html>">>,
522
    ?assertEqual(
523
        {<<"html">>,[],[
524
            { <<"img">>, [ { <<"src">>, <<"/images/icon.png'></html>">> } ], [] }
525
        ]},
526
        mochiweb_html:parse(D1)),
527
 
528
    D2 = <<"<html><img src=\"/images/icon&gt;.png\"></html>">>,
529
    ?assertEqual(
530
        {<<"html">>,[],[
531
            { <<"img">>, [ { <<"src">>, <<"/images/icon>.png">> } ], [] }
532
        ]},
533
        mochiweb_html:parse(D2)),
534
 
535
    %% Quoted attributes can contain whitespace and newlines
536
    D3 = <<"<html><a href=\"#\" onclick=\"javascript: test(1,\ntrue);\"></html>">>,
537
    ?assertEqual(
538
        {<<"html">>,[],[
539
            { <<"a">>, [ { <<"href">>, <<"#">> }, {<<"onclick">>, <<"javascript: test(1,\ntrue);">>} ], [] }
540
        ]},
541
        mochiweb_html:parse(D3)),
542
    ok.
543
 
544
parse_missing_attr_name_test() ->
545
    D0 = <<"<html =black></html>">>,
546
    ?assertEqual(
547
        {<<"html">>, [ { <<"=">>, <<"=">> }, { <<"black">>, <<"black">> } ], [] },
548
       mochiweb_html:parse(D0)),
549
    ok.
550
 
551
parse_broken_pi_test() ->
552
        D0 = <<"<html><?xml:namespace prefix = o ns = \"urn:schemas-microsoft-com:office:office\" /></html>">>,
553
        ?assertEqual(
554
                {<<"html">>, [], [
555
                        { pi, <<"xml:namespace">>, [ { <<"prefix">>, <<"o">> },
556
                                                     { <<"ns">>, <<"urn:schemas-microsoft-com:office:office">> } ] }
557
                ] },
558
                mochiweb_html:parse(D0)),
559
        ok.
560
 
561
parse_funny_singletons_test() ->
562
        D0 = <<"<html><input><input>x</input></input></html>">>,
563
        ?assertEqual(
564
                {<<"html">>, [], [
565
                        { <<"input">>, [], [] },
566
                        { <<"input">>, [], [ <<"x">> ] }
567
                ] },
568
                mochiweb_html:parse(D0)),
569
        ok.
570
 
571
to_html_singleton_test() ->
572
    D0 = <<"<link />">>,
573
    T0 = {<<"link">>,[],[]},
574
    ?assertEqual(D0, iolist_to_binary(mochiweb_html:to_html(T0))),
575
 
576
    D1 = <<"<head><link /></head>">>,
577
    T1 = {<<"head">>,[],[{<<"link">>,[],[]}]},
578
    ?assertEqual(D1, iolist_to_binary(mochiweb_html:to_html(T1))),
579
 
580
    D2 = <<"<head><link /><link /></head>">>,
581
    T2 = {<<"head">>,[],[{<<"link">>,[],[]}, {<<"link">>,[],[]}]},
582
    ?assertEqual(D2, iolist_to_binary(mochiweb_html:to_html(T2))),
583
 
584
    %% Make sure singletons are converted to singletons.
585
    D3 = <<"<head><link /></head>">>,
586
    T3 = {<<"head">>,[],[{<<"link">>,[],[<<"funny">>]}]},
587
    ?assertEqual(D3, iolist_to_binary(mochiweb_html:to_html(T3))),
588
 
589
    D4 = <<"<link />">>,
590
    T4 = {<<"link">>,[],[<<"funny">>]},
591
    ?assertEqual(D4, iolist_to_binary(mochiweb_html:to_html(T4))),
592
 
593
    ok.
594
 
595
parse_amp_test_() ->
596
    [?_assertEqual(
597
       {<<"html">>,[],
598
        [{<<"body">>,[{<<"onload">>,<<"javascript:A('1&2')">>}],[]}]},
599
       mochiweb_html:parse("<html><body onload=\"javascript:A('1&2')\"></body></html>")),
600
     ?_assertEqual(
601
        {<<"html">>,[],
602
         [{<<"body">>,[{<<"onload">>,<<"javascript:A('1& 2')">>}],[]}]},
603
        mochiweb_html:parse("<html><body onload=\"javascript:A('1& 2')\"></body></html>")),
604
     ?_assertEqual(
605
        {<<"html">>,[],
606
         [{<<"body">>,[],[<<"& ">>]}]},
607
        mochiweb_html:parse("<html><body>& </body></html>")),
608
     ?_assertEqual(
609
        {<<"html">>,[],
610
         [{<<"body">>,[],[<<"&">>]}]},
611
        mochiweb_html:parse("<html><body>&</body></html>")),
612
     ?_assertEqual(
613
        {<<"html">>,[],
614
         [{<<"body">>,[],[<<"&;">>]}]},
615
        mochiweb_html:parse("<html><body>&;</body></html>")),
616
     ?_assertEqual(
617
        {<<"html">>,[],
618
         [{<<"body">>,[],[<<"&MISSING;">>]}]},
619
        mochiweb_html:parse("<html><body>&MISSING;</body></html>"))].
620
 
621
parse_unescaped_lt_test() ->
622
    D1 = <<"<div> < < <a href=\"/\">Back</a></div>">>,
623
    ?assertEqual(
624
        {<<"div">>, [], [<<" < < ">>, {<<"a">>, [{<<"href">>, <<"/">>}],
625
                                       [<<"Back">>]}]},
626
        mochiweb_html:parse(D1)),
627
 
628
    D2 = <<"<div> << <a href=\"/\">Back</a></div>">>,
629
    ?assertEqual(
630
        {<<"div">>, [], [<<" << ">>, {<<"a">>, [{<<"href">>, <<"/">>}],
631
                                      [<<"Back">>]}]},
632
    mochiweb_html:parse(D2)).
633
 
634
html5_doctype_test() ->
635
    ?assertEqual(
636
       [{doctype,[<<"html">>]},
637
        {start_tag,<<"head">>,[],false},
638
        {end_tag,<<"head">>},
639
        {start_tag,<<"body">>,[],false},
640
        {end_tag,<<"body">>}],
641
       mochiweb_html:tokens("<!doctype html><head></head><body></body>")).
642
 
643
implicit_html_test() ->
644
    %% https://github.com/mochi/mochiweb/issues/110
645
    ?assertEqual(
646
       {<<"html">>, [],
647
        [{<<"head">>, [], []},
648
         {<<"body">>, [], []}]},
649
       mochiweb_html:parse("<!doctype html><head></head><body></body>")).
650
 
651
no_letter_no_tag_test() ->
652
    ?assertEqual(
653
       {<<"html">>,[],
654
         [{<<"body">>,[],[<<"<3><!><*><<>>">>,{<<"body">>,[],[]}]}]},
655
       mochiweb_html:parse(<<"<html><body><3><!><*><<>><body></html>">>)
656
      ).