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 |
<<"&quot;\"word ><<up!&quot;">>,
|
|
|
35 |
mochiweb_html:escape(<<""\"word ><<up!"">>)),
|
|
|
36 |
?assertEqual(
|
|
|
37 |
<<"&quot;\"word ><<up!&quot;">>,
|
|
|
38 |
mochiweb_html:escape(""\"word ><<up!"")),
|
|
|
39 |
?assertEqual(
|
|
|
40 |
<<"&quot;\"word ><<up!&quot;">>,
|
|
|
41 |
mochiweb_html:escape('"\"word ><<up!"')),
|
|
|
42 |
ok.
|
|
|
43 |
|
|
|
44 |
escape_attr_test() ->
|
|
|
45 |
?assertEqual(
|
|
|
46 |
<<"&quot;"word ><<up!&quot;">>,
|
|
|
47 |
mochiweb_html:escape_attr(<<""\"word ><<up!"">>)),
|
|
|
48 |
?assertEqual(
|
|
|
49 |
<<"&quot;"word ><<up!&quot;">>,
|
|
|
50 |
mochiweb_html:escape_attr(""\"word ><<up!"")),
|
|
|
51 |
?assertEqual(
|
|
|
52 |
<<"&quot;"word ><<up!&quot;">>,
|
|
|
53 |
mochiweb_html:escape_attr('"\"word ><<up!"')),
|
|
|
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(<<"<">>)),
|
|
|
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(<<"��">>)).
|
|
|
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[<<this<!-- is -->CDATA>>]]></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 |
[<<"<<this<!-- is -->CDATA>>">>]}]},
|
|
|
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"az\" wibble taco©=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>.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>.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 |
).
|