move tests into separate module
Project: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/commit/f540b156 Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/tree/f540b156 Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/diff/f540b156 Branch: refs/heads/1843-feature-bigcouch Commit: f540b156a75e375eb6331f4aa15d24bb179bb4dc Parents: bfc0c38 Author: Bob Ippolito <[email protected]> Authored: Mon May 6 14:43:57 2013 -0700 Committer: Bob Ippolito <[email protected]> Committed: Mon May 6 14:43:57 2013 -0700 ---------------------------------------------------------------------- src/mochiweb_html.erl | 574 +------------------------------------- test/mochiweb_html_tests.erl | 572 +++++++++++++++++++++++++++++++++++++ 2 files changed, 575 insertions(+), 571 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/f540b156/src/mochiweb_html.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_html.erl b/src/mochiweb_html.erl index 1d90f3b..c38f138 100644 --- a/src/mochiweb_html.erl +++ b/src/mochiweb_html.erl @@ -5,6 +5,9 @@ -module(mochiweb_html). -export([tokens/1, parse/1, parse_tokens/1, to_tokens/1, escape/1, escape_attr/1, to_html/1]). +-ifdef(TEST). +-export([destack/1, destack/2, is_singleton/1]). +-endif. %% This is a macro to placate syntax highlighters.. -define(QUOTE, $\"). @@ -759,574 +762,3 @@ tokenize_textarea(Bin, S=#decoder{offset=O}, Start) -> <<_:Start/binary, Raw/binary>> -> {{data, Raw, false}, S} end. - - -%% -%% Tests -%% --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -to_html_test() -> - ?assertEqual( - <<"<html><head><title>hey!</title></head><body><p class=\"foo\">what's up<br /></p><div>sucka</div>RAW!<!-- comment! --></body></html>">>, - iolist_to_binary( - to_html({html, [], - [{<<"head">>, [], - [{title, <<"hey!">>}]}, - {body, [], - [{p, [{class, foo}], [<<"what's">>, <<" up">>, {br}]}, - {'div', <<"sucka">>}, - {'=', <<"RAW!">>}, - {comment, <<" comment! ">>}]}]}))), - ?assertEqual( - <<"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">">>, - iolist_to_binary( - to_html({doctype, - [<<"html">>, <<"PUBLIC">>, - <<"-//W3C//DTD XHTML 1.0 Transitional//EN">>, - <<"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>]}))), - ?assertEqual( - <<"<html><?xml:namespace prefix=\"o\" ns=\"urn:schemas-microsoft-com:office:office\"?></html>">>, - iolist_to_binary( - to_html({<<"html">>,[], - [{pi, <<"xml:namespace">>, - [{<<"prefix">>,<<"o">>}, - {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}]}))), - ok. - -escape_test() -> - ?assertEqual( - <<"&quot;\"word ><<up!&quot;">>, - escape(<<""\"word ><<up!"">>)), - ?assertEqual( - <<"&quot;\"word ><<up!&quot;">>, - escape(""\"word ><<up!"")), - ?assertEqual( - <<"&quot;\"word ><<up!&quot;">>, - escape('"\"word ><<up!"')), - ok. - -escape_attr_test() -> - ?assertEqual( - <<"&quot;"word ><<up!&quot;">>, - escape_attr(<<""\"word ><<up!"">>)), - ?assertEqual( - <<"&quot;"word ><<up!&quot;">>, - escape_attr(""\"word ><<up!"")), - ?assertEqual( - <<"&quot;"word ><<up!&quot;">>, - escape_attr('"\"word ><<up!"')), - ?assertEqual( - <<"12345">>, - escape_attr(12345)), - ?assertEqual( - <<"1.5">>, - escape_attr(1.5)), - ok. - -tokens_test() -> - ?assertEqual( - [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>}, - {<<"wibble">>, <<"wibble">>}, - {<<"alice">>, <<"bob">>}], true}], - tokens(<<"<foo bar=baz wibble='wibble' alice=\"bob\"/>">>)), - ?assertEqual( - [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>}, - {<<"wibble">>, <<"wibble">>}, - {<<"alice">>, <<"bob">>}], true}], - tokens(<<"<foo bar=baz wibble='wibble' alice=bob/>">>)), - ?assertEqual( - [{comment, <<"[if lt IE 7]>\n<style type=\"text/css\">\n.no_ie { display: none; }\n</style>\n<![endif]">>}], - tokens(<<"<!--[if lt IE 7]>\n<style type=\"text/css\">\n.no_ie { display: none; }\n</style>\n<![endif]-->">>)), - ?assertEqual( - [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, - {data, <<" A= B <= C ">>, false}, - {end_tag, <<"script">>}], - tokens(<<"<script type=\"text/javascript\"> A= B <= C </script>">>)), - ?assertEqual( - [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, - {data, <<" A= B <= C ">>, false}, - {end_tag, <<"script">>}], - tokens(<<"<script type =\"text/javascript\"> A= B <= C </script>">>)), - ?assertEqual( - [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, - {data, <<" A= B <= C ">>, false}, - {end_tag, <<"script">>}], - tokens(<<"<script type = \"text/javascript\"> A= B <= C </script>">>)), - ?assertEqual( - [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, - {data, <<" A= B <= C ">>, false}, - {end_tag, <<"script">>}], - tokens(<<"<script type= \"text/javascript\"> A= B <= C </script>">>)), - ?assertEqual( - [{start_tag, <<"textarea">>, [], false}, - {data, <<"<html></body>">>, false}, - {end_tag, <<"textarea">>}], - tokens(<<"<textarea><html></body></textarea>">>)), - ?assertEqual( - [{start_tag, <<"textarea">>, [], false}, - {data, <<"<html></body></textareaz>">>, false}], - tokens(<<"<textarea ><html></body></textareaz>">>)), - ?assertEqual( - [{pi, <<"xml:namespace">>, - [{<<"prefix">>,<<"o">>}, - {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}], - tokens(<<"<?xml:namespace prefix=\"o\" ns=\"urn:schemas-microsoft-com:office:office\"?>">>)), - ?assertEqual( - [{pi, <<"xml:namespace">>, - [{<<"prefix">>,<<"o">>}, - {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}], - tokens(<<"<?xml:namespace prefix=o ns=urn:schemas-microsoft-com:office:office \n?>">>)), - ?assertEqual( - [{pi, <<"xml:namespace">>, - [{<<"prefix">>,<<"o">>}, - {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}], - tokens(<<"<?xml:namespace prefix=o ns=urn:schemas-microsoft-com:office:office">>)), - ?assertEqual( - [{data, <<"<">>, false}], - tokens(<<"<">>)), - ?assertEqual( - [{data, <<"not html ">>, false}, - {data, <<"< at all">>, false}], - tokens(<<"not html < at all">>)), - ok. - -parse_test() -> - D0 = <<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\"> -<html> - <head> - <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"> - <title>Foo</title> - <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/rel/dojo/resources/dojo.css\" media=\"screen\"> - <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/foo.css\" media=\"screen\"> - <!--[if lt IE 7]> - <style type=\"text/css\"> - .no_ie { display: none; } - </style> - <![endif]--> - <link rel=\"icon\" href=\"/static/images/favicon.ico\" type=\"image/x-icon\"> - <link rel=\"shortcut icon\" href=\"/static/images/favicon.ico\" type=\"image/x-icon\"> - </head> - <body id=\"home\" class=\"tundra\"><![CDATA[<<this<!-- is -->CDATA>>]]></body> -</html>">>, - ?assertEqual( - {<<"html">>, [], - [{<<"head">>, [], - [{<<"meta">>, - [{<<"http-equiv">>,<<"Content-Type">>}, - {<<"content">>,<<"text/html; charset=UTF-8">>}], - []}, - {<<"title">>,[],[<<"Foo">>]}, - {<<"link">>, - [{<<"rel">>,<<"stylesheet">>}, - {<<"type">>,<<"text/css">>}, - {<<"href">>,<<"/static/rel/dojo/resources/dojo.css">>}, - {<<"media">>,<<"screen">>}], - []}, - {<<"link">>, - [{<<"rel">>,<<"stylesheet">>}, - {<<"type">>,<<"text/css">>}, - {<<"href">>,<<"/static/foo.css">>}, - {<<"media">>,<<"screen">>}], - []}, - {comment,<<"[if lt IE 7]>\n <style type=\"text/css\">\n .no_ie { display: none; }\n </style>\n <![endif]">>}, - {<<"link">>, - [{<<"rel">>,<<"icon">>}, - {<<"href">>,<<"/static/images/favicon.ico">>}, - {<<"type">>,<<"image/x-icon">>}], - []}, - {<<"link">>, - [{<<"rel">>,<<"shortcut icon">>}, - {<<"href">>,<<"/static/images/favicon.ico">>}, - {<<"type">>,<<"image/x-icon">>}], - []}]}, - {<<"body">>, - [{<<"id">>,<<"home">>}, - {<<"class">>,<<"tundra">>}], - [<<"<<this<!-- is -->CDATA>>">>]}]}, - parse(D0)), - ?assertEqual( - {<<"html">>,[], - [{pi, <<"xml:namespace">>, - [{<<"prefix">>,<<"o">>}, - {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}]}, - parse( - <<"<html><?xml:namespace prefix=\"o\" ns=\"urn:schemas-microsoft-com:office:office\"?></html>">>)), - ?assertEqual( - {<<"html">>, [], - [{<<"dd">>, [], [<<"foo">>]}, - {<<"dt">>, [], [<<"bar">>]}]}, - parse(<<"<html><dd>foo<dt>bar</html>">>)), - %% Singleton sadness - ?assertEqual( - {<<"html">>, [], - [{<<"link">>, [], []}, - <<"foo">>, - {<<"br">>, [], []}, - <<"bar">>]}, - parse(<<"<html><link>foo<br>bar</html>">>)), - ?assertEqual( - {<<"html">>, [], - [{<<"link">>, [], [<<"foo">>, - {<<"br">>, [], []}, - <<"bar">>]}]}, - parse(<<"<html><link>foo<br>bar</link></html>">>)), - %% Case insensitive tags - ?assertEqual( - {<<"html">>, [], - [{<<"head">>, [], [<<"foo">>, - {<<"br">>, [], []}, - <<"BAR">>]}, - {<<"body">>, [{<<"class">>, <<"">>}, {<<"bgcolor">>, <<"#Aa01fF">>}], []} - ]}, - parse(<<"<html><Head>foo<bR>BAR</head><body Class=\"\" bgcolor=\"#Aa01fF\"></BODY></html>">>)), - ok. - -exhaustive_is_singleton_test() -> - T = mochiweb_cover:clause_lookup_table(?MODULE, is_singleton), - [?assertEqual(V, is_singleton(K)) || {K, V} <- T]. - -tokenize_attributes_test() -> - ?assertEqual( - {<<"foo">>, - [{<<"bar">>, <<"b\"az">>}, - {<<"wibble">>, <<"wibble">>}, - {<<"taco", 16#c2, 16#a9>>, <<"bell">>}, - {<<"quux">>, <<"quux">>}], - []}, - parse(<<"<foo bar=\"b"az\" wibble taco©=bell quux">>)), - ok. - -tokens2_test() -> - D0 = <<"<channel><title>from __future__ import *</title><link>http://bob.pythonmac.org</link><description>Bob's Rants</description></channel>">>, - ?assertEqual( - [{start_tag,<<"channel">>,[],false}, - {start_tag,<<"title">>,[],false}, - {data,<<"from __future__ import *">>,false}, - {end_tag,<<"title">>}, - {start_tag,<<"link">>,[],true}, - {data,<<"http://bob.pythonmac.org">>,false}, - {end_tag,<<"link">>}, - {start_tag,<<"description">>,[],false}, - {data,<<"Bob's Rants">>,false}, - {end_tag,<<"description">>}, - {end_tag,<<"channel">>}], - tokens(D0)), - ok. - -to_tokens_test() -> - ?assertEqual( - [{start_tag, <<"p">>, [{class, 1}], false}, - {end_tag, <<"p">>}], - to_tokens({p, [{class, 1}], []})), - ?assertEqual( - [{start_tag, <<"p">>, [], false}, - {end_tag, <<"p">>}], - to_tokens({p})), - ?assertEqual( - [{'=', <<"data">>}], - to_tokens({'=', <<"data">>})), - ?assertEqual( - [{comment, <<"comment">>}], - to_tokens({comment, <<"comment">>})), - %% This is only allowed in sub-tags: - %% {p, [{"class", "foo"}]} as {p, [{"class", "foo"}], []} - %% On the outside it's always treated as follows: - %% {p, [], [{"class", "foo"}]} as {p, [], [{"class", "foo"}]} - ?assertEqual( - [{start_tag, <<"html">>, [], false}, - {start_tag, <<"p">>, [{class, 1}], false}, - {end_tag, <<"p">>}, - {end_tag, <<"html">>}], - to_tokens({html, [{p, [{class, 1}]}]})), - ok. - -parse2_test() -> - D0 = <<"<channel><title>from __future__ import *</title><link>http://bob.pythonmac.org<br>foo</link><description>Bob's Rants</description></channel>">>, - ?assertEqual( - {<<"channel">>,[], - [{<<"title">>,[],[<<"from __future__ import *">>]}, - {<<"link">>,[],[ - <<"http://bob.pythonmac.org">>, - {<<"br">>,[],[]}, - <<"foo">>]}, - {<<"description">>,[],[<<"Bob's Rants">>]}]}, - parse(D0)), - ok. - -parse_tokens_test() -> - D0 = [{doctype,[<<"HTML">>,<<"PUBLIC">>,<<"-//W3C//DTD HTML 4.01 Transitional//EN">>]}, - {data,<<"\n">>,true}, - {start_tag,<<"html">>,[],false}], - ?assertEqual( - {<<"html">>, [], []}, - parse_tokens(D0)), - D1 = D0 ++ [{end_tag, <<"html">>}], - ?assertEqual( - {<<"html">>, [], []}, - parse_tokens(D1)), - D2 = D0 ++ [{start_tag, <<"body">>, [], false}], - ?assertEqual( - {<<"html">>, [], [{<<"body">>, [], []}]}, - parse_tokens(D2)), - D3 = D0 ++ [{start_tag, <<"head">>, [], false}, - {end_tag, <<"head">>}, - {start_tag, <<"body">>, [], false}], - ?assertEqual( - {<<"html">>, [], [{<<"head">>, [], []}, {<<"body">>, [], []}]}, - parse_tokens(D3)), - D4 = D3 ++ [{data,<<"\n">>,true}, - {start_tag,<<"div">>,[{<<"class">>,<<"a">>}],false}, - {start_tag,<<"a">>,[{<<"name">>,<<"#anchor">>}],false}, - {end_tag,<<"a">>}, - {end_tag,<<"div">>}, - {start_tag,<<"div">>,[{<<"class">>,<<"b">>}],false}, - {start_tag,<<"div">>,[{<<"class">>,<<"c">>}],false}, - {end_tag,<<"div">>}, - {end_tag,<<"div">>}], - ?assertEqual( - {<<"html">>, [], - [{<<"head">>, [], []}, - {<<"body">>, [], - [{<<"div">>, [{<<"class">>, <<"a">>}], [{<<"a">>, [{<<"name">>, <<"#anchor">>}], []}]}, - {<<"div">>, [{<<"class">>, <<"b">>}], [{<<"div">>, [{<<"class">>, <<"c">>}], []}]} - ]}]}, - parse_tokens(D4)), - D5 = [{start_tag,<<"html">>,[],false}, - {data,<<"\n">>,true}, - {data,<<"boo">>,false}, - {data,<<"hoo">>,false}, - {data,<<"\n">>,true}, - {end_tag,<<"html">>}], - ?assertEqual( - {<<"html">>, [], [<<"\nboohoo\n">>]}, - parse_tokens(D5)), - D6 = [{start_tag,<<"html">>,[],false}, - {data,<<"\n">>,true}, - {data,<<"\n">>,true}, - {end_tag,<<"html">>}], - ?assertEqual( - {<<"html">>, [], []}, - parse_tokens(D6)), - D7 = [{start_tag,<<"html">>,[],false}, - {start_tag,<<"ul">>,[],false}, - {start_tag,<<"li">>,[],false}, - {data,<<"word">>,false}, - {start_tag,<<"li">>,[],false}, - {data,<<"up">>,false}, - {end_tag,<<"li">>}, - {start_tag,<<"li">>,[],false}, - {data,<<"fdsa">>,false}, - {start_tag,<<"br">>,[],true}, - {data,<<"asdf">>,false}, - {end_tag,<<"ul">>}, - {end_tag,<<"html">>}], - ?assertEqual( - {<<"html">>, [], - [{<<"ul">>, [], - [{<<"li">>, [], [<<"word">>]}, - {<<"li">>, [], [<<"up">>]}, - {<<"li">>, [], [<<"fdsa">>,{<<"br">>, [], []}, <<"asdf">>]}]}]}, - parse_tokens(D7)), - ok. - -destack_test() -> - {<<"a">>, [], []} = - destack([{<<"a">>, [], []}]), - {<<"a">>, [], [{<<"b">>, [], []}]} = - destack([{<<"b">>, [], []}, {<<"a">>, [], []}]), - {<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]} = - destack([{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}]), - [{<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]}] = - destack(<<"b">>, - [{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}]), - [{<<"b">>, [], [{<<"c">>, [], []}]}, {<<"a">>, [], []}] = - destack(<<"c">>, - [{<<"c">>, [], []}, {<<"b">>, [], []},{<<"a">>, [], []}]), - ok. - -doctype_test() -> - ?assertEqual( - {<<"html">>,[],[{<<"head">>,[],[]}]}, - mochiweb_html:parse("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" - "<html><head></head></body></html>")), - %% http://code.google.com/p/mochiweb/issues/detail?id=52 - ?assertEqual( - {<<"html">>,[],[{<<"head">>,[],[]}]}, - mochiweb_html:parse("<html>" - "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" - "<head></head></body></html>")), - %% http://github.com/mochi/mochiweb/pull/13 - ?assertEqual( - {<<"html">>,[],[{<<"head">>,[],[]}]}, - mochiweb_html:parse("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"/>" - "<html>" - "<head></head></body></html>")), - ok. - -dumb_br_test() -> - %% http://code.google.com/p/mochiweb/issues/detail?id=71 - ?assertEqual( - {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]}, - mochiweb_html:parse("<div><br/><br/>z</br/></br/></div>")), - ?assertEqual( - {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]}, - mochiweb_html:parse("<div><br><br>z</br/></br/></div>")), - ?assertEqual( - {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>, {<<"br">>, [], []}, {<<"br">>, [], []}]}, - mochiweb_html:parse("<div><br><br>z<br/><br/></div>")), - ?assertEqual( - {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]}, - mochiweb_html:parse("<div><br><br>z</br></br></div>")). - - -php_test() -> - %% http://code.google.com/p/mochiweb/issues/detail?id=71 - ?assertEqual( - [{pi, <<"php\n">>}], - mochiweb_html:tokens( - "<?php\n?>")), - ?assertEqual( - {<<"div">>, [], [{pi, <<"php\n">>}]}, - mochiweb_html:parse( - "<div><?php\n?></div>")), - ok. - -parse_unquoted_attr_test() -> - D0 = <<"<html><img src=/images/icon.png/></html>">>, - ?assertEqual( - {<<"html">>,[],[ - { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] } - ]}, - mochiweb_html:parse(D0)), - - D1 = <<"<html><img src=/images/icon.png></img></html>">>, - ?assertEqual( - {<<"html">>,[],[ - { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] } - ]}, - mochiweb_html:parse(D1)), - - D2 = <<"<html><img src=/images/icon>.png width=100></img></html>">>, - ?assertEqual( - {<<"html">>,[],[ - { <<"img">>, [ { <<"src">>, <<"/images/icon>.png">> }, { <<"width">>, <<"100">> } ], [] } - ]}, - mochiweb_html:parse(D2)), - ok. - -parse_quoted_attr_test() -> - D0 = <<"<html><img src='/images/icon.png'></html>">>, - ?assertEqual( - {<<"html">>,[],[ - { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] } - ]}, - mochiweb_html:parse(D0)), - - D1 = <<"<html><img src=\"/images/icon.png'></html>">>, - ?assertEqual( - {<<"html">>,[],[ - { <<"img">>, [ { <<"src">>, <<"/images/icon.png'></html>">> } ], [] } - ]}, - mochiweb_html:parse(D1)), - - D2 = <<"<html><img src=\"/images/icon>.png\"></html>">>, - ?assertEqual( - {<<"html">>,[],[ - { <<"img">>, [ { <<"src">>, <<"/images/icon>.png">> } ], [] } - ]}, - mochiweb_html:parse(D2)), - - %% Quoted attributes can contain whitespace and newlines - D3 = <<"<html><a href=\"#\" onclick=\"javascript: test(1,\ntrue);\"></html>">>, - ?assertEqual( - {<<"html">>,[],[ - { <<"a">>, [ { <<"href">>, <<"#">> }, {<<"onclick">>, <<"javascript: test(1,\ntrue);">>} ], [] } - ]}, - mochiweb_html:parse(D3)), - ok. - -parse_missing_attr_name_test() -> - D0 = <<"<html =black></html>">>, - ?assertEqual( - {<<"html">>, [ { <<"=">>, <<"=">> }, { <<"black">>, <<"black">> } ], [] }, - mochiweb_html:parse(D0)), - ok. - -parse_broken_pi_test() -> - D0 = <<"<html><?xml:namespace prefix = o ns = \"urn:schemas-microsoft-com:office:office\" /></html>">>, - ?assertEqual( - {<<"html">>, [], [ - { pi, <<"xml:namespace">>, [ { <<"prefix">>, <<"o">> }, - { <<"ns">>, <<"urn:schemas-microsoft-com:office:office">> } ] } - ] }, - mochiweb_html:parse(D0)), - ok. - -parse_funny_singletons_test() -> - D0 = <<"<html><input><input>x</input></input></html>">>, - ?assertEqual( - {<<"html">>, [], [ - { <<"input">>, [], [] }, - { <<"input">>, [], [ <<"x">> ] } - ] }, - mochiweb_html:parse(D0)), - ok. - -to_html_singleton_test() -> - D0 = <<"<link />">>, - T0 = {<<"link">>,[],[]}, - ?assertEqual(D0, iolist_to_binary(to_html(T0))), - - D1 = <<"<head><link /></head>">>, - T1 = {<<"head">>,[],[{<<"link">>,[],[]}]}, - ?assertEqual(D1, iolist_to_binary(to_html(T1))), - - D2 = <<"<head><link /><link /></head>">>, - T2 = {<<"head">>,[],[{<<"link">>,[],[]}, {<<"link">>,[],[]}]}, - ?assertEqual(D2, iolist_to_binary(to_html(T2))), - - %% Make sure singletons are converted to singletons. - D3 = <<"<head><link /></head>">>, - T3 = {<<"head">>,[],[{<<"link">>,[],[<<"funny">>]}]}, - ?assertEqual(D3, iolist_to_binary(to_html(T3))), - - D4 = <<"<link />">>, - T4 = {<<"link">>,[],[<<"funny">>]}, - ?assertEqual(D4, iolist_to_binary(to_html(T4))), - - ok. - -parse_amp_test_() -> - [?_assertEqual( - {<<"html">>,[], - [{<<"body">>,[{<<"onload">>,<<"javascript:A('1&2')">>}],[]}]}, - mochiweb_html:parse("<html><body onload=\"javascript:A('1&2')\"></body></html>")), - ?_assertEqual( - {<<"html">>,[], - [{<<"body">>,[{<<"onload">>,<<"javascript:A('1& 2')">>}],[]}]}, - mochiweb_html:parse("<html><body onload=\"javascript:A('1& 2')\"></body></html>")), - ?_assertEqual( - {<<"html">>,[], - [{<<"body">>,[],[<<"& ">>]}]}, - mochiweb_html:parse("<html><body>& </body></html>")), - ?_assertEqual( - {<<"html">>,[], - [{<<"body">>,[],[<<"&">>]}]}, - mochiweb_html:parse("<html><body>&</body></html>"))]. - -parse_unescaped_lt_test() -> - D1 = <<"<div> < < <a href=\"/\">Back</a></div>">>, - ?assertEqual( - {<<"div">>, [], [<<" < < ">>, {<<"a">>, [{<<"href">>, <<"/">>}], - [<<"Back">>]}]}, - mochiweb_html:parse(D1)), - - D2 = <<"<div> << <a href=\"/\">Back</a></div>">>, - ?assertEqual( - {<<"div">>, [], [<<" << ">>, {<<"a">>, [{<<"href">>, <<"/">>}], - [<<"Back">>]}]}, - mochiweb_html:parse(D2)). - --endif. http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/f540b156/test/mochiweb_html_tests.erl ---------------------------------------------------------------------- diff --git a/test/mochiweb_html_tests.erl b/test/mochiweb_html_tests.erl new file mode 100644 index 0000000..cf97fc4 --- /dev/null +++ b/test/mochiweb_html_tests.erl @@ -0,0 +1,572 @@ +-module(mochiweb_html_tests). +-include_lib("eunit/include/eunit.hrl"). + +to_html_test() -> + ?assertEqual( + <<"<html><head><title>hey!</title></head><body><p class=\"foo\">what's up<br /></p><div>sucka</div>RAW!<!-- comment! --></body></html>">>, + iolist_to_binary( + mochiweb_html:to_html({html, [], + [{<<"head">>, [], + [{title, <<"hey!">>}]}, + {body, [], + [{p, [{class, foo}], [<<"what's">>, <<" up">>, {br}]}, + {'div', <<"sucka">>}, + {'=', <<"RAW!">>}, + {comment, <<" comment! ">>}]}]}))), + ?assertEqual( + <<"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">">>, + iolist_to_binary( + mochiweb_html:to_html({doctype, + [<<"html">>, <<"PUBLIC">>, + <<"-//W3C//DTD XHTML 1.0 Transitional//EN">>, + <<"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>]}))), + ?assertEqual( + <<"<html><?xml:namespace prefix=\"o\" ns=\"urn:schemas-microsoft-com:office:office\"?></html>">>, + iolist_to_binary( + mochiweb_html:to_html({<<"html">>,[], + [{pi, <<"xml:namespace">>, + [{<<"prefix">>,<<"o">>}, + {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}]}))), + ok. + +escape_test() -> + ?assertEqual( + <<"&quot;\"word ><<up!&quot;">>, + mochiweb_html:escape(<<""\"word ><<up!"">>)), + ?assertEqual( + <<"&quot;\"word ><<up!&quot;">>, + mochiweb_html:escape(""\"word ><<up!"")), + ?assertEqual( + <<"&quot;\"word ><<up!&quot;">>, + mochiweb_html:escape('"\"word ><<up!"')), + ok. + +escape_attr_test() -> + ?assertEqual( + <<"&quot;"word ><<up!&quot;">>, + mochiweb_html:escape_attr(<<""\"word ><<up!"">>)), + ?assertEqual( + <<"&quot;"word ><<up!&quot;">>, + mochiweb_html:escape_attr(""\"word ><<up!"")), + ?assertEqual( + <<"&quot;"word ><<up!&quot;">>, + mochiweb_html:escape_attr('"\"word ><<up!"')), + ?assertEqual( + <<"12345">>, + mochiweb_html:escape_attr(12345)), + ?assertEqual( + <<"1.5">>, + mochiweb_html:escape_attr(1.5)), + ok. + +tokens_test() -> + ?assertEqual( + [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>}, + {<<"wibble">>, <<"wibble">>}, + {<<"alice">>, <<"bob">>}], true}], + mochiweb_html:tokens(<<"<foo bar=baz wibble='wibble' alice=\"bob\"/>">>)), + ?assertEqual( + [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>}, + {<<"wibble">>, <<"wibble">>}, + {<<"alice">>, <<"bob">>}], true}], + mochiweb_html:tokens(<<"<foo bar=baz wibble='wibble' alice=bob/>">>)), + ?assertEqual( + [{comment, <<"[if lt IE 7]>\n<style type=\"text/css\">\n.no_ie { display: none; }\n</style>\n<![endif]">>}], + mochiweb_html:tokens(<<"<!--[if lt IE 7]>\n<style type=\"text/css\">\n.no_ie { display: none; }\n</style>\n<![endif]-->">>)), + ?assertEqual( + [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, + {data, <<" A= B <= C ">>, false}, + {end_tag, <<"script">>}], + mochiweb_html:tokens(<<"<script type=\"text/javascript\"> A= B <= C </script>">>)), + ?assertEqual( + [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, + {data, <<" A= B <= C ">>, false}, + {end_tag, <<"script">>}], + mochiweb_html:tokens(<<"<script type =\"text/javascript\"> A= B <= C </script>">>)), + ?assertEqual( + [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, + {data, <<" A= B <= C ">>, false}, + {end_tag, <<"script">>}], + mochiweb_html:tokens(<<"<script type = \"text/javascript\"> A= B <= C </script>">>)), + ?assertEqual( + [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, + {data, <<" A= B <= C ">>, false}, + {end_tag, <<"script">>}], + mochiweb_html:tokens(<<"<script type= \"text/javascript\"> A= B <= C </script>">>)), + ?assertEqual( + [{start_tag, <<"textarea">>, [], false}, + {data, <<"<html></body>">>, false}, + {end_tag, <<"textarea">>}], + mochiweb_html:tokens(<<"<textarea><html></body></textarea>">>)), + ?assertEqual( + [{start_tag, <<"textarea">>, [], false}, + {data, <<"<html></body></textareaz>">>, false}], + mochiweb_html:tokens(<<"<textarea ><html></body></textareaz>">>)), + ?assertEqual( + [{pi, <<"xml:namespace">>, + [{<<"prefix">>,<<"o">>}, + {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}], + mochiweb_html:tokens(<<"<?xml:namespace prefix=\"o\" ns=\"urn:schemas-microsoft-com:office:office\"?>">>)), + ?assertEqual( + [{pi, <<"xml:namespace">>, + [{<<"prefix">>,<<"o">>}, + {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}], + mochiweb_html:tokens(<<"<?xml:namespace prefix=o ns=urn:schemas-microsoft-com:office:office \n?>">>)), + ?assertEqual( + [{pi, <<"xml:namespace">>, + [{<<"prefix">>,<<"o">>}, + {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}], + mochiweb_html:tokens(<<"<?xml:namespace prefix=o ns=urn:schemas-microsoft-com:office:office">>)), + ?assertEqual( + [{data, <<"<">>, false}], + mochiweb_html:tokens(<<"<">>)), + ?assertEqual( + [{data, <<"not html ">>, false}, + {data, <<"< at all">>, false}], + mochiweb_html:tokens(<<"not html < at all">>)), + ok. + +parse_test() -> + D0 = <<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\"> +<html> + <head> + <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"> + <title>Foo</title> + <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/rel/dojo/resources/dojo.css\" media=\"screen\"> + <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/foo.css\" media=\"screen\"> + <!--[if lt IE 7]> + <style type=\"text/css\"> + .no_ie { display: none; } + </style> + <![endif]--> + <link rel=\"icon\" href=\"/static/images/favicon.ico\" type=\"image/x-icon\"> + <link rel=\"shortcut icon\" href=\"/static/images/favicon.ico\" type=\"image/x-icon\"> + </head> + <body id=\"home\" class=\"tundra\"><![CDATA[<<this<!-- is -->CDATA>>]]></body> +</html>">>, + ?assertEqual( + {<<"html">>, [], + [{<<"head">>, [], + [{<<"meta">>, + [{<<"http-equiv">>,<<"Content-Type">>}, + {<<"content">>,<<"text/html; charset=UTF-8">>}], + []}, + {<<"title">>,[],[<<"Foo">>]}, + {<<"link">>, + [{<<"rel">>,<<"stylesheet">>}, + {<<"type">>,<<"text/css">>}, + {<<"href">>,<<"/static/rel/dojo/resources/dojo.css">>}, + {<<"media">>,<<"screen">>}], + []}, + {<<"link">>, + [{<<"rel">>,<<"stylesheet">>}, + {<<"type">>,<<"text/css">>}, + {<<"href">>,<<"/static/foo.css">>}, + {<<"media">>,<<"screen">>}], + []}, + {comment,<<"[if lt IE 7]>\n <style type=\"text/css\">\n .no_ie { display: none; }\n </style>\n <![endif]">>}, + {<<"link">>, + [{<<"rel">>,<<"icon">>}, + {<<"href">>,<<"/static/images/favicon.ico">>}, + {<<"type">>,<<"image/x-icon">>}], + []}, + {<<"link">>, + [{<<"rel">>,<<"shortcut icon">>}, + {<<"href">>,<<"/static/images/favicon.ico">>}, + {<<"type">>,<<"image/x-icon">>}], + []}]}, + {<<"body">>, + [{<<"id">>,<<"home">>}, + {<<"class">>,<<"tundra">>}], + [<<"<<this<!-- is -->CDATA>>">>]}]}, + mochiweb_html:parse(D0)), + ?assertEqual( + {<<"html">>,[], + [{pi, <<"xml:namespace">>, + [{<<"prefix">>,<<"o">>}, + {<<"ns">>,<<"urn:schemas-microsoft-com:office:office">>}]}]}, + mochiweb_html:parse( + <<"<html><?xml:namespace prefix=\"o\" ns=\"urn:schemas-microsoft-com:office:office\"?></html>">>)), + ?assertEqual( + {<<"html">>, [], + [{<<"dd">>, [], [<<"foo">>]}, + {<<"dt">>, [], [<<"bar">>]}]}, + mochiweb_html:parse(<<"<html><dd>foo<dt>bar</html>">>)), + %% Singleton sadness + ?assertEqual( + {<<"html">>, [], + [{<<"link">>, [], []}, + <<"foo">>, + {<<"br">>, [], []}, + <<"bar">>]}, + mochiweb_html:parse(<<"<html><link>foo<br>bar</html>">>)), + ?assertEqual( + {<<"html">>, [], + [{<<"link">>, [], [<<"foo">>, + {<<"br">>, [], []}, + <<"bar">>]}]}, + mochiweb_html:parse(<<"<html><link>foo<br>bar</link></html>">>)), + %% Case insensitive tags + ?assertEqual( + {<<"html">>, [], + [{<<"head">>, [], [<<"foo">>, + {<<"br">>, [], []}, + <<"BAR">>]}, + {<<"body">>, [{<<"class">>, <<"">>}, {<<"bgcolor">>, <<"#Aa01fF">>}], []} + ]}, + mochiweb_html:parse(<<"<html><Head>foo<bR>BAR</head><body Class=\"\" bgcolor=\"#Aa01fF\"></BODY></html>">>)), + ok. + +exhaustive_is_singleton_test() -> + T = mochiweb_cover:clause_lookup_table(mochiweb_html, is_singleton), + [?assertEqual(V, mochiweb_html:is_singleton(K)) || {K, V} <- T]. + +tokenize_attributes_test() -> + ?assertEqual( + {<<"foo">>, + [{<<"bar">>, <<"b\"az">>}, + {<<"wibble">>, <<"wibble">>}, + {<<"taco", 16#c2, 16#a9>>, <<"bell">>}, + {<<"quux">>, <<"quux">>}], + []}, + mochiweb_html:parse(<<"<foo bar=\"b"az\" wibble taco©=bell quux">>)), + ok. + +tokens2_test() -> + D0 = <<"<channel><title>from __future__ import *</title><link>http://bob.pythonmac.org</link><description>Bob's Rants</description></channel>">>, + ?assertEqual( + [{start_tag,<<"channel">>,[],false}, + {start_tag,<<"title">>,[],false}, + {data,<<"from __future__ import *">>,false}, + {end_tag,<<"title">>}, + {start_tag,<<"link">>,[],true}, + {data,<<"http://bob.pythonmac.org">>,false}, + {end_tag,<<"link">>}, + {start_tag,<<"description">>,[],false}, + {data,<<"Bob's Rants">>,false}, + {end_tag,<<"description">>}, + {end_tag,<<"channel">>}], + mochiweb_html:tokens(D0)), + ok. + +to_tokens_test() -> + ?assertEqual( + [{start_tag, <<"p">>, [{class, 1}], false}, + {end_tag, <<"p">>}], + mochiweb_html:to_tokens({p, [{class, 1}], []})), + ?assertEqual( + [{start_tag, <<"p">>, [], false}, + {end_tag, <<"p">>}], + mochiweb_html:to_tokens({p})), + ?assertEqual( + [{'=', <<"data">>}], + mochiweb_html:to_tokens({'=', <<"data">>})), + ?assertEqual( + [{comment, <<"comment">>}], + mochiweb_html:to_tokens({comment, <<"comment">>})), + %% This is only allowed in sub-tags: + %% {p, [{"class", "foo"}]} as {p, [{"class", "foo"}], []} + %% On the outside it's always treated as follows: + %% {p, [], [{"class", "foo"}]} as {p, [], [{"class", "foo"}]} + ?assertEqual( + [{start_tag, <<"html">>, [], false}, + {start_tag, <<"p">>, [{class, 1}], false}, + {end_tag, <<"p">>}, + {end_tag, <<"html">>}], + mochiweb_html:to_tokens({html, [{p, [{class, 1}]}]})), + ok. + +parse2_test() -> + D0 = <<"<channel><title>from __future__ import *</title><link>http://bob.pythonmac.org<br>foo</link><description>Bob's Rants</description></channel>">>, + ?assertEqual( + {<<"channel">>,[], + [{<<"title">>,[],[<<"from __future__ import *">>]}, + {<<"link">>,[],[ + <<"http://bob.pythonmac.org">>, + {<<"br">>,[],[]}, + <<"foo">>]}, + {<<"description">>,[],[<<"Bob's Rants">>]}]}, + mochiweb_html:parse(D0)), + ok. + +parse_tokens_test() -> + D0 = [{doctype,[<<"HTML">>,<<"PUBLIC">>,<<"-//W3C//DTD HTML 4.01 Transitional//EN">>]}, + {data,<<"\n">>,true}, + {start_tag,<<"html">>,[],false}], + ?assertEqual( + {<<"html">>, [], []}, + mochiweb_html:parse_tokens(D0)), + D1 = D0 ++ [{end_tag, <<"html">>}], + ?assertEqual( + {<<"html">>, [], []}, + mochiweb_html:parse_tokens(D1)), + D2 = D0 ++ [{start_tag, <<"body">>, [], false}], + ?assertEqual( + {<<"html">>, [], [{<<"body">>, [], []}]}, + mochiweb_html:parse_tokens(D2)), + D3 = D0 ++ [{start_tag, <<"head">>, [], false}, + {end_tag, <<"head">>}, + {start_tag, <<"body">>, [], false}], + ?assertEqual( + {<<"html">>, [], [{<<"head">>, [], []}, {<<"body">>, [], []}]}, + mochiweb_html:parse_tokens(D3)), + D4 = D3 ++ [{data,<<"\n">>,true}, + {start_tag,<<"div">>,[{<<"class">>,<<"a">>}],false}, + {start_tag,<<"a">>,[{<<"name">>,<<"#anchor">>}],false}, + {end_tag,<<"a">>}, + {end_tag,<<"div">>}, + {start_tag,<<"div">>,[{<<"class">>,<<"b">>}],false}, + {start_tag,<<"div">>,[{<<"class">>,<<"c">>}],false}, + {end_tag,<<"div">>}, + {end_tag,<<"div">>}], + ?assertEqual( + {<<"html">>, [], + [{<<"head">>, [], []}, + {<<"body">>, [], + [{<<"div">>, [{<<"class">>, <<"a">>}], [{<<"a">>, [{<<"name">>, <<"#anchor">>}], []}]}, + {<<"div">>, [{<<"class">>, <<"b">>}], [{<<"div">>, [{<<"class">>, <<"c">>}], []}]} + ]}]}, + mochiweb_html:parse_tokens(D4)), + D5 = [{start_tag,<<"html">>,[],false}, + {data,<<"\n">>,true}, + {data,<<"boo">>,false}, + {data,<<"hoo">>,false}, + {data,<<"\n">>,true}, + {end_tag,<<"html">>}], + ?assertEqual( + {<<"html">>, [], [<<"\nboohoo\n">>]}, + mochiweb_html:parse_tokens(D5)), + D6 = [{start_tag,<<"html">>,[],false}, + {data,<<"\n">>,true}, + {data,<<"\n">>,true}, + {end_tag,<<"html">>}], + ?assertEqual( + {<<"html">>, [], []}, + mochiweb_html:parse_tokens(D6)), + D7 = [{start_tag,<<"html">>,[],false}, + {start_tag,<<"ul">>,[],false}, + {start_tag,<<"li">>,[],false}, + {data,<<"word">>,false}, + {start_tag,<<"li">>,[],false}, + {data,<<"up">>,false}, + {end_tag,<<"li">>}, + {start_tag,<<"li">>,[],false}, + {data,<<"fdsa">>,false}, + {start_tag,<<"br">>,[],true}, + {data,<<"asdf">>,false}, + {end_tag,<<"ul">>}, + {end_tag,<<"html">>}], + ?assertEqual( + {<<"html">>, [], + [{<<"ul">>, [], + [{<<"li">>, [], [<<"word">>]}, + {<<"li">>, [], [<<"up">>]}, + {<<"li">>, [], [<<"fdsa">>,{<<"br">>, [], []}, <<"asdf">>]}]}]}, + mochiweb_html:parse_tokens(D7)), + ok. + +destack_test() -> + ?assertEqual( + {<<"a">>, [], []}, + mochiweb_html:destack([{<<"a">>, [], []}])), + ?assertEqual( + {<<"a">>, [], [{<<"b">>, [], []}]}, + mochiweb_html:destack([{<<"b">>, [], []}, {<<"a">>, [], []}])), + ?assertEqual( + {<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]}, + mochiweb_html:destack( + [{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}])), + ?assertEqual( + [{<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]}], + mochiweb_html:destack( + <<"b">>, + [{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}])), + ?assertEqual( + [{<<"b">>, [], [{<<"c">>, [], []}]}, {<<"a">>, [], []}], + mochiweb_html:destack( + <<"c">>, + [{<<"c">>, [], []}, {<<"b">>, [], []},{<<"a">>, [], []}])), + ok. + +doctype_test() -> + ?assertEqual( + {<<"html">>,[],[{<<"head">>,[],[]}]}, + mochiweb_html:parse("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" + "<html><head></head></body></html>")), + %% http://code.google.com/p/mochiweb/issues/detail?id=52 + ?assertEqual( + {<<"html">>,[],[{<<"head">>,[],[]}]}, + mochiweb_html:parse("<html>" + "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" + "<head></head></body></html>")), + %% http://github.com/mochi/mochiweb/pull/13 + ?assertEqual( + {<<"html">>,[],[{<<"head">>,[],[]}]}, + mochiweb_html:parse("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"/>" + "<html>" + "<head></head></body></html>")), + ok. + +dumb_br_test() -> + %% http://code.google.com/p/mochiweb/issues/detail?id=71 + ?assertEqual( + {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]}, + mochiweb_html:parse("<div><br/><br/>z</br/></br/></div>")), + ?assertEqual( + {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]}, + mochiweb_html:parse("<div><br><br>z</br/></br/></div>")), + ?assertEqual( + {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>, {<<"br">>, [], []}, {<<"br">>, [], []}]}, + mochiweb_html:parse("<div><br><br>z<br/><br/></div>")), + ?assertEqual( + {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]}, + mochiweb_html:parse("<div><br><br>z</br></br></div>")). + + +php_test() -> + %% http://code.google.com/p/mochiweb/issues/detail?id=71 + ?assertEqual( + [{pi, <<"php\n">>}], + mochiweb_html:tokens( + "<?php\n?>")), + ?assertEqual( + {<<"div">>, [], [{pi, <<"php\n">>}]}, + mochiweb_html:parse( + "<div><?php\n?></div>")), + ok. + +parse_unquoted_attr_test() -> + D0 = <<"<html><img src=/images/icon.png/></html>">>, + ?assertEqual( + {<<"html">>,[],[ + { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] } + ]}, + mochiweb_html:parse(D0)), + + D1 = <<"<html><img src=/images/icon.png></img></html>">>, + ?assertEqual( + {<<"html">>,[],[ + { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] } + ]}, + mochiweb_html:parse(D1)), + + D2 = <<"<html><img src=/images/icon>.png width=100></img></html>">>, + ?assertEqual( + {<<"html">>,[],[ + { <<"img">>, [ { <<"src">>, <<"/images/icon>.png">> }, { <<"width">>, <<"100">> } ], [] } + ]}, + mochiweb_html:parse(D2)), + ok. + +parse_quoted_attr_test() -> + D0 = <<"<html><img src='/images/icon.png'></html>">>, + ?assertEqual( + {<<"html">>,[],[ + { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] } + ]}, + mochiweb_html:parse(D0)), + + D1 = <<"<html><img src=\"/images/icon.png'></html>">>, + ?assertEqual( + {<<"html">>,[],[ + { <<"img">>, [ { <<"src">>, <<"/images/icon.png'></html>">> } ], [] } + ]}, + mochiweb_html:parse(D1)), + + D2 = <<"<html><img src=\"/images/icon>.png\"></html>">>, + ?assertEqual( + {<<"html">>,[],[ + { <<"img">>, [ { <<"src">>, <<"/images/icon>.png">> } ], [] } + ]}, + mochiweb_html:parse(D2)), + + %% Quoted attributes can contain whitespace and newlines + D3 = <<"<html><a href=\"#\" onclick=\"javascript: test(1,\ntrue);\"></html>">>, + ?assertEqual( + {<<"html">>,[],[ + { <<"a">>, [ { <<"href">>, <<"#">> }, {<<"onclick">>, <<"javascript: test(1,\ntrue);">>} ], [] } + ]}, + mochiweb_html:parse(D3)), + ok. + +parse_missing_attr_name_test() -> + D0 = <<"<html =black></html>">>, + ?assertEqual( + {<<"html">>, [ { <<"=">>, <<"=">> }, { <<"black">>, <<"black">> } ], [] }, + mochiweb_html:parse(D0)), + ok. + +parse_broken_pi_test() -> + D0 = <<"<html><?xml:namespace prefix = o ns = \"urn:schemas-microsoft-com:office:office\" /></html>">>, + ?assertEqual( + {<<"html">>, [], [ + { pi, <<"xml:namespace">>, [ { <<"prefix">>, <<"o">> }, + { <<"ns">>, <<"urn:schemas-microsoft-com:office:office">> } ] } + ] }, + mochiweb_html:parse(D0)), + ok. + +parse_funny_singletons_test() -> + D0 = <<"<html><input><input>x</input></input></html>">>, + ?assertEqual( + {<<"html">>, [], [ + { <<"input">>, [], [] }, + { <<"input">>, [], [ <<"x">> ] } + ] }, + mochiweb_html:parse(D0)), + ok. + +to_html_singleton_test() -> + D0 = <<"<link />">>, + T0 = {<<"link">>,[],[]}, + ?assertEqual(D0, iolist_to_binary(mochiweb_html:to_html(T0))), + + D1 = <<"<head><link /></head>">>, + T1 = {<<"head">>,[],[{<<"link">>,[],[]}]}, + ?assertEqual(D1, iolist_to_binary(mochiweb_html:to_html(T1))), + + D2 = <<"<head><link /><link /></head>">>, + T2 = {<<"head">>,[],[{<<"link">>,[],[]}, {<<"link">>,[],[]}]}, + ?assertEqual(D2, iolist_to_binary(mochiweb_html:to_html(T2))), + + %% Make sure singletons are converted to singletons. + D3 = <<"<head><link /></head>">>, + T3 = {<<"head">>,[],[{<<"link">>,[],[<<"funny">>]}]}, + ?assertEqual(D3, iolist_to_binary(mochiweb_html:to_html(T3))), + + D4 = <<"<link />">>, + T4 = {<<"link">>,[],[<<"funny">>]}, + ?assertEqual(D4, iolist_to_binary(mochiweb_html:to_html(T4))), + + ok. + +parse_amp_test_() -> + [?_assertEqual( + {<<"html">>,[], + [{<<"body">>,[{<<"onload">>,<<"javascript:A('1&2')">>}],[]}]}, + mochiweb_html:parse("<html><body onload=\"javascript:A('1&2')\"></body></html>")), + ?_assertEqual( + {<<"html">>,[], + [{<<"body">>,[{<<"onload">>,<<"javascript:A('1& 2')">>}],[]}]}, + mochiweb_html:parse("<html><body onload=\"javascript:A('1& 2')\"></body></html>")), + ?_assertEqual( + {<<"html">>,[], + [{<<"body">>,[],[<<"& ">>]}]}, + mochiweb_html:parse("<html><body>& </body></html>")), + ?_assertEqual( + {<<"html">>,[], + [{<<"body">>,[],[<<"&">>]}]}, + mochiweb_html:parse("<html><body>&</body></html>"))]. + +parse_unescaped_lt_test() -> + D1 = <<"<div> < < <a href=\"/\">Back</a></div>">>, + ?assertEqual( + {<<"div">>, [], [<<" < < ">>, {<<"a">>, [{<<"href">>, <<"/">>}], + [<<"Back">>]}]}, + mochiweb_html:parse(D1)), + + D2 = <<"<div> << <a href=\"/\">Back</a></div>">>, + ?assertEqual( + {<<"div">>, [], [<<" << ">>, {<<"a">>, [{<<"href">>, <<"/">>}], + [<<"Back">>]}]}, + mochiweb_html:parse(D2)).
