You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by be...@apache.org on 2014/02/13 17:05:59 UTC
[04/50] mochiweb commit: updated refs/heads/import-upstream to 8eb1f22
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/import-upstream
Commit: f540b156a75e375eb6331f4aa15d24bb179bb4dc
Parents: bfc0c38
Author: Bob Ippolito <bo...@redivi.com>
Authored: Mon May 6 14:43:57 2013 -0700
Committer: Bob Ippolito <bo...@redivi.com>
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)).