#!/bin/python
# -*- coding: utf-8 -*-

"""
    Unit tests for gluon.html
"""

import unittest


from gluon.html import A, ASSIGNJS, B, BEAUTIFY, P, BODY, BR, BUTTON, CAT, CENTER, CODE, COL, COLGROUP, DIV, SPAN, URL, verifyURL
from gluon.html import truncate_string, EM, FIELDSET, FORM, H1, H2, H3, H4, H5, H6, HEAD, HR, HTML, I, IFRAME, IMG, INPUT, EMBED
from gluon.html import LABEL, LEGEND, LI, LINK, MARKMIN, MENU, META, OBJECT, OL, OPTGROUP, OPTION, PRE, SCRIPT, SELECT, STRONG
from gluon.html import STYLE, TABLE, TR, TD, TAG, TBODY, THEAD, TEXTAREA, TFOOT, TH, TITLE, TT, UL, XHTML, XML, web2pyHTMLParser
from gluon.storage import Storage
from gluon.html import XML_pickle, XML_unpickle
from gluon.html import TAG_pickler, TAG_unpickler
from gluon._compat import xrange, PY2, to_native
from gluon.decoder import decoder
import re

class TestBareHelpers(unittest.TestCase):

    # xmlescape() = covered by other tests

    # TODO: def test_call_as_list(self):

    def test_truncate_string(self):
        # Ascii text
        self.assertEqual(truncate_string('Lorem ipsum dolor sit amet, consectetur adipiscing elit, '
                                         'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
                                         length=30), 'Lorem ipsum dolor sit amet,...')
        self.assertEqual(truncate_string('Short text shorter than the length parameter.', length=100),
                         'Short text shorter than the length parameter.')
        # French text
        self.assertEqual(truncate_string('Un texte en français avec des accents et des caractères bizarre.', length=30),
                         'Un texte en français avec d...')

    def test_StaticURL(self):
        # test response.static_version coupled with response.static_version_urls
        self.assertEqual(URL('a', 'c', 'f'), '/a/c/f')
        self.assertEqual(URL('a', 'static', 'design.css'), '/a/static/design.css')
        response = Storage()
        response.static_version = '1.2.3'
        from gluon.globals import current
        current.response = response
        self.assertEqual(URL('a', 'static', 'design.css'), '/a/static/design.css')
        response.static_version_urls = True
        self.assertEqual(URL('a', 'static', 'design.css'), '/a/static/_1.2.3/design.css')

    def test_URL(self):
        self.assertEqual(URL('a', 'c', 'f', args='1'), '/a/c/f/1')
        self.assertEqual(URL('a', 'c', 'f', args=('1', '2')), '/a/c/f/1/2')
        self.assertEqual(URL('a', 'c', 'f', args=['1', '2']), '/a/c/f/1/2')
        self.assertEqual(URL('a', 'c', '/f'), '/a/c/f')
        self.assertEqual(URL('a', 'c', 'f.json'), '/a/c/f.json')
        from gluon.globals import current
        current.request = None
        self.assertRaises(SyntaxError, URL, *['a'])

        request = Storage()
        request.application = 'a'
        request.controller = 'c'
        request.function = 'f'
        request.env = {}

        from gluon.globals import current  # Can't be moved with other import
        current.request = request

        must_return = '/a/c/f'
        self.assertEqual(URL(), must_return)
        self.assertEqual(URL('f'), must_return)
        self.assertEqual(URL('c', 'f'), must_return)
        self.assertEqual(URL('a', 'c', 'f'), must_return)
        self.assertEqual(URL('a', 'c', 'f', extension='json'), '/a/c/f.json')

        def weird():
            pass
        self.assertEqual(URL('a', 'c', weird), '/a/c/weird')
        self.assertRaises(SyntaxError, URL, *['a', 'c', 1])
        # test signature
        rtn = URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
                  vars={'p': (1, 3), 'q': 2}, anchor='1', hmac_key='key')
        self.assertEqual(rtn, '/a/c/f/x/y/z?p=1&p=3&q=2&_signature=a32530f0d0caa80964bb92aad2bedf8a4486a31f#1')
        # test _signature exclusion
        rtn = URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
                  vars={'p': (1, 3), 'q': 2, '_signature': 'abc'},
                  anchor='1', hmac_key='key')
        self.assertEqual(rtn, '/a/c/f/x/y/z?p=1&p=3&q=2&_signature=a32530f0d0caa80964bb92aad2bedf8a4486a31f#1')
        # emulate user_signature
        current.session = Storage(auth=Storage(hmac_key='key'))
        self.assertEqual(URL(user_signature=True), '/a/c/f?_signature=c4aed53c08cff08f369dbf8b5ba51889430cf2c2')
        # hash_vars combination
        rtn = URL('a', 'c', 'f', args=['x', 'y', 'z'], vars={'p': (1, 3), 'q': 2}, hmac_key='key')
        self.assertEqual(rtn, '/a/c/f/x/y/z?p=1&p=3&q=2&_signature=a32530f0d0caa80964bb92aad2bedf8a4486a31f')
        rtn = URL('a', 'c', 'f', args=['x', 'y', 'z'], vars={'p': (1, 3), 'q': 2}, hmac_key='key', hash_vars=True)
        self.assertEqual(rtn, '/a/c/f/x/y/z?p=1&p=3&q=2&_signature=a32530f0d0caa80964bb92aad2bedf8a4486a31f')
        rtn = URL('a', 'c', 'f', args=['x', 'y', 'z'], vars={'p': (1, 3), 'q': 2}, hmac_key='key', hash_vars=False)
        self.assertEqual(rtn, '/a/c/f/x/y/z?p=1&p=3&q=2&_signature=0b5a0702039992aad23c82794b8496e5dcd59a5b')
        rtn = URL('a', 'c', 'f', args=['x', 'y', 'z'], vars={'p': (1, 3), 'q': 2}, hmac_key='key', hash_vars=['p'])
        self.assertEqual(rtn, '/a/c/f/x/y/z?p=1&p=3&q=2&_signature=5d01b982fd72b39674b012e0288071034e156d7a')
        rtn = URL('a', 'c', 'f', args=['x', 'y', 'z'], vars={'p': (1, 3), 'q': 2}, hmac_key='key', hash_vars='p')
        self.assertEqual(rtn, '/a/c/f/x/y/z?p=1&p=3&q=2&_signature=5d01b982fd72b39674b012e0288071034e156d7a')
        # test CRLF detection
        self.assertRaises(SyntaxError, URL, *['a\n', 'c', 'f'])
        self.assertRaises(SyntaxError, URL, *['a\r', 'c', 'f'])
        # test url_encode
        rtn = URL('a', 'c', 'f', args=['x', 'y', 'z'], vars={'maï': (1, 3), 'lié': 2}, url_encode=True)
        self.assertEqual(rtn, '/a/c/f/x/y/z?li%C3%A9=2&ma%C3%AF=1&ma%C3%AF=3')

    @unittest.skipIf(not PY2, "Skipping Python 3.x tests for test_URL_encode")
    def test_URL_encode(self):
        rtn = URL('a', 'c', 'f', args=['x', 'y', 'z'], vars={'maï': (1, 3), 'lié': 2}, url_encode=False)
        self.assertEqual(rtn, '/a/c/f/x/y/z?li\xc3\xa9=2&ma\xc3\xaf=1&ma\xc3\xaf=3')

    def test_verifyURL(self):
        r = Storage()
        r.application = 'a'
        r.controller = 'c'
        r.function = 'f'
        r.extension = 'html'
        r.env = {}
        r.get_vars = Storage()
        # missing signature as request.get_vars returns False
        rtn = verifyURL(r, 'key')
        self.assertEqual(rtn, False)
        # reverse tests from previous testcase with hash_vars combinations
        r.args = ['x', 'y', 'z']
        r.get_vars = Storage(p=(1, 3), q=2)
        # add signature
        r.get_vars['_signature'] = 'a32530f0d0caa80964bb92aad2bedf8a4486a31f'
        rtn = verifyURL(r, 'key')
        self.assertEqual(rtn, True)
        r.get_vars['_signature'] = 'a32530f0d0caa80964bb92aad2bedf8a4486a31f'
        rtn = verifyURL(r, 'key', hash_vars=True)
        self.assertEqual(rtn, True)
        r.get_vars['_signature'] = '0b5a0702039992aad23c82794b8496e5dcd59a5b'
        rtn = verifyURL(r, 'key', hash_vars=False)
        self.assertEqual(rtn, True)
        r.get_vars['_signature'] = '5d01b982fd72b39674b012e0288071034e156d7a'
        rtn = verifyURL(r, 'key', hash_vars=['p'])
        self.assertEqual(rtn, True)
        r.get_vars['_signature'] = '5d01b982fd72b39674b012e0288071034e156d7a'
        rtn = verifyURL(r, 'key', hash_vars='p')
        self.assertEqual(rtn, True)
        # without session, user_signature returns always False
        rtn = verifyURL(r, user_signature=True)
        self.assertEqual(rtn, False)
        # same goes if you don't use an hmac_key
        rtn = verifyURL(r)
        self.assertEqual(rtn, False)
        # emulate user signature
        from gluon.globals import current
        current.session = Storage(auth=Storage(hmac_key='key'))
        r.get_vars['_signature'] = 'a32530f0d0caa80964bb92aad2bedf8a4486a31f'
        rtn = verifyURL(r, user_signature=True)
        self.assertEqual(rtn, True)

    # TODO: def test_XmlComponent(self):

    def test_XML(self):
        # sanitization process
        self.assertEqual(XML('<h1>Hello<a data-hello="world">World</a></h1>').xml(),
                         b'<h1>Hello<a data-hello="world">World</a></h1>')
        # with sanitize, data-attributes are not permitted
        self.assertEqual(XML('<h1>Hello<a data-hello="world">World</a></h1>', sanitize=True).xml(),
                         b'<h1>HelloWorld</h1>')
        # stringify by default
        # FIXME PY3
        # seams that __repr__ is no longer enough
        ##self.assertEqual(XML('1.3'), '1.3')
        self.assertEqual(XML(u'<div>è</div>').xml(), b'<div>\xc3\xa8</div>')
        # make sure unicode works with sanitize
        self.assertEqual(XML(u'<div>è</div>', sanitize=True).xml(), b'<div>\xc3\xa8</div>')
        # you can calc len on the class, that equals the xml() and the str()
        ##self.assertEqual(len(XML('1.3')), len('1.3'))
        self.assertEqual(len(XML('1.3').xml()), len('1.3'))
        ##self.assertEqual(len(str(XML('1.3'))), len('1.3'))
        # you can concatenate them to strings (check for __add__ and __radd__ methods)
        ##self.assertEqual(XML('a') + 'b', 'ab')
        ##self.assertEqual(XML('a') + XML('b'), 'ab')
        ##self.assertEqual('a' + XML('b'), 'ab')
        # you can compare them
        ##self.assertEqual(XML('a') == XML('a'), True)
        # beware that the comparison is made on the XML repr

        self.assertEqual(XML('<h1>Hello<a data-hello="world">World</a></h1>', sanitize=True).__repr__(),
                         XML('<h1>HelloWorld</h1>').__repr__())
        # bug check for the sanitizer for closing no-close tags
        self.assertEqual(XML('<p>Test</p><br/><p>Test</p><br/>', sanitize=True).xml(),
                         XML('<p>Test</p><br /><p>Test</p><br />').xml())
        # basic flatten test
        self.assertEqual(XML('<p>Test</p>').flatten(), '<p>Test</p>')
        self.assertEqual(XML('<p>Test</p>').flatten(render=lambda text, tag, attr: text), '<p>Test</p>')

    def test_XML_pickle_unpickle(self):
        self.assertEqual(str(XML_unpickle(XML_pickle('data to be pickle')[1][0])), 'data to be pickle')

    def test_DIV(self):
        # Empty DIV()
        self.assertEqual(DIV().xml(), b'<div></div>')
        self.assertEqual(DIV('<>', _a='1', _b='2').xml(),
                         b'<div a="1" b="2">&lt;&gt;</div>')
        # attributes can be updated like in a dict
        div = DIV('<>', _a='1')
        div['_b'] = '2'
        self.assertEqual(div.xml(),
                         b'<div a="1" b="2">&lt;&gt;</div>')
        # also with a mapping
        div.update(_b=2, _c=3)
        self.assertEqual(div.xml(),
                         b'<div a="1" b="2" c="3">&lt;&gt;</div>')
        # length of the DIV is the number of components
        self.assertEqual(len(DIV('a', 'bc')), 2)
        # also if empty, DIV is True in a boolean evaluation
        self.assertTrue(True if DIV() else False)
        # parent and siblings
        a = DIV(SPAN('a'), DIV('b'))
        s = a.element('span')
        d = s.parent
        d['_class'] = 'abc'
        self.assertEqual(a.xml(), b'<div class="abc"><span>a</span><div>b</div></div>')
        self.assertEqual([el.xml() for el in s.siblings()], [b'<div>b</div>'])
        self.assertEqual(s.sibling().xml(), b'<div>b</div>')
        # siblings with wrong args
        self.assertEqual(s.siblings('a'), [])
        # siblings with good args
        self.assertEqual(s.siblings('div')[0].xml(), b'<div>b</div>')
        # Check for siblings with wrong kargs and value
        self.assertEqual(s.siblings(a='d'), [])
        # Check for siblings with good kargs and value
        # Can't figure this one out what is a right value here??
        # Commented for now...
        # self.assertEqual(s.siblings(div='<div>b</div>'), ???)
        # No other sibling should return None
        self.assertEqual(DIV(P('First element')).element('p').sibling(), None)
        # --------------------------------------------------------------------------------------------------------------
        # This use unicode to hit xmlescape() line :
        #     """
        #     elif isinstance(data, unicode):
        #         data = data.encode('utf8', 'xmlcharrefreplace')
        #     """
        self.assertEqual(DIV(u'Texte en français avec des caractères accentués...').xml(),
                         b'<div>Texte en fran\xc3\xa7ais avec des caract\xc3\xa8res accentu\xc3\xa9s...</div>')
        # --------------------------------------------------------------------------------------------------------------
        self.assertEqual(DIV('Test with an ID', _id='id-of-the-element').xml(),
                         b'<div id="id-of-the-element">Test with an ID</div>')
        self.assertEqual(DIV().element('p'), None)

        # Corner case for raise coverage of one line
        # I think such assert fail cause of python 2.6
        # Work under python 2.7
        # with self.assertRaises(SyntaxError) as cm:
        #     DIV(BR('<>')).xml()
        # self.assertEqual(cm.exception[0], '<br/> tags cannot have components')

        # test .get('attrib')
        self.assertEqual(DIV('<p>Test</p>', _class="class_test").get('_class'), 'class_test')
        self.assertEqual(DIV(b'a').xml(), b'<div>a</div>')

    def test_decoder(self):
        tag_html = '<div><span><a id="1-1" u:v="$">hello</a></span><p class="this is a test">world</p></div>'
        a = decoder(tag_html)
        self.assertEqual(a, tag_html)

    def test_CAT(self):
        # Empty CAT()
        self.assertEqual(CAT().xml(), b'')
        # CAT('')
        self.assertEqual(CAT('').xml(), b'')
        # CAT(' ')
        self.assertEqual(CAT(' ').xml(), b' ')

    def test_TAG_pickler_unpickler(self):
        # weird test
        self.assertEqual(TAG_unpickler(TAG_pickler(TAG.div('data to be pickle'))[1][0]).xml(),
                         b'<div>data to be pickle</div>')

    def test_TAG(self):
        self.assertEqual(TAG.first(TAG.second('test'), _key=3).xml(),
                         b'<first key="3"><second>test</second></first>')
        # ending in underscore "triggers" <input /> style
        self.assertEqual(TAG.first_(TAG.second('test'), _key=3).xml(),
                         b'<first key="3" />')
        # unicode test for TAG
        self.assertEqual(TAG.div(u'Texte en français avec des caractères accentués...').xml(),
                         b'<div>Texte en fran\xc3\xa7ais avec des caract\xc3\xa8res accentu\xc3\xa9s...</div>')

    def test_HTML(self):
        self.assertEqual(HTML('<>', _a='1', _b='2').xml(),
                         b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n<html a="1" b="2" lang="en">&lt;&gt;</html>')
        self.assertEqual(HTML('<>', _a='1', _b='2', doctype='strict').xml(),
                         b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n<html a="1" b="2" lang="en">&lt;&gt;</html>')
        self.assertEqual(HTML('<>', _a='1', _b='2', doctype='transitional').xml(),
                         b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n<html a="1" b="2" lang="en">&lt;&gt;</html>')
        self.assertEqual(HTML('<>', _a='1', _b='2', doctype='frameset').xml(),
                         b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">\n<html a="1" b="2" lang="en">&lt;&gt;</html>')
        self.assertEqual(HTML('<>', _a='1', _b='2', doctype='html5').xml(),
                         b'<!DOCTYPE HTML>\n<html a="1" b="2" lang="en">&lt;&gt;</html>')
        self.assertEqual(HTML('<>', _a='1', _b='2', doctype='').xml(),
                         b'<html a="1" b="2" lang="en">&lt;&gt;</html>')
        self.assertEqual(HTML('<>', _a='1', _b='2', doctype='CustomDocType').xml(),
                         b'CustomDocType\n<html a="1" b="2" lang="en">&lt;&gt;</html>')

    def test_XHTML(self):
        # Empty XHTML test
        self.assertEqual(XHTML().xml(),
                         b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"></html>')
        # Not Empty XHTML test
        self.assertEqual(XHTML('<>', _a='1', _b='2').xml(),
                         b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html a="1" b="2" lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">&lt;&gt;</html>')
        self.assertEqual(XHTML('<>', _a='1', _b='2', doctype='').xml(),
                         b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html a="1" b="2" lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">&lt;&gt;</html>')
        self.assertEqual(XHTML('<>', _a='1', _b='2', doctype='strict').xml(),
                         b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n<html a="1" b="2" lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">&lt;&gt;</html>')
        self.assertEqual(XHTML('<>', _a='1', _b='2', doctype='transitional').xml(),
                         b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html a="1" b="2" lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">&lt;&gt;</html>')
        self.assertEqual(XHTML('<>', _a='1', _b='2', doctype='frameset').xml(),
                         b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">\n<html a="1" b="2" lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">&lt;&gt;</html>')
        self.assertEqual(XHTML('<>', _a='1', _b='2', doctype='xmlns').xml(),
                         b'xmlns\n<html a="1" b="2" lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">&lt;&gt;</html>')
        self.assertEqual(XHTML('<>', _a='1', _b='2', _xmlns='xmlns').xml(),
                         b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html a="1" b="2" lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">&lt;&gt;</html>')

    def test_HEAD(self):
        self.assertEqual(HEAD('<>', _a='1', _b='2').xml(),
                         b'<head a="1" b="2">&lt;&gt;</head>')

    def test_TITLE(self):
        self.assertEqual(TITLE('<>', _a='1', _b='2').xml(),
                         b'<title a="1" b="2">&lt;&gt;</title>')

    def test_META(self):
        self.assertEqual(META(_a='1', _b='2').xml(),
                         b'<meta a="1" b="2" />')

    def test_LINK(self):
        self.assertEqual(LINK(_a='1', _b='2').xml(),
                         b'<link a="1" b="2" />')

    def test_SCRIPT(self):
        self.assertEqual(SCRIPT('<>', _a='1', _b='2').xml(),
                         b'''<script a="1" b="2"><!--
<>
//--></script>''')
        self.assertEqual(SCRIPT('<>').xml(),
                         b'''<script><!--
<>
//--></script>''')
        self.assertEqual(SCRIPT().xml(), b'<script></script>')
        self.assertEqual(SCRIPT(';').xml() + DIV().xml(), 
                         b'<script><!--\n;\n//--></script><div></div>')

    def test_STYLE(self):
        self.assertEqual(STYLE('<>', _a='1', _b='2').xml(),
                         b'<style a="1" b="2"><!--/*--><![CDATA[/*><!--*/\n<>\n/*]]>*/--></style>')
        # Try to hit : return DIV.xml(self)
        self.assertEqual(STYLE().xml(), b'<style></style>')

    def test_IMG(self):
        self.assertEqual(IMG(_a='1', _b='2').xml(),
                         b'<img a="1" b="2" />')

    def test_SPAN(self):
        self.assertEqual(SPAN('<>', _a='1', _b='2').xml(),
                         b'<span a="1" b="2">&lt;&gt;</span>')

    def test_BODY(self):
        self.assertEqual(BODY('<>', _a='1', _b='2').xml(),
                         b'<body a="1" b="2">&lt;&gt;</body>')

    def test_H1(self):
        self.assertEqual(H1('<>', _a='1', _b='2').xml(),
                         b'<h1 a="1" b="2">&lt;&gt;</h1>')

    def test_H2(self):
        self.assertEqual(H2('<>', _a='1', _b='2').xml(),
                         b'<h2 a="1" b="2">&lt;&gt;</h2>')

    def test_H3(self):
        self.assertEqual(H3('<>', _a='1', _b='2').xml(),
                         b'<h3 a="1" b="2">&lt;&gt;</h3>')

    def test_H4(self):
        self.assertEqual(H4('<>', _a='1', _b='2').xml(),
                         b'<h4 a="1" b="2">&lt;&gt;</h4>')

    def test_H5(self):
        self.assertEqual(H5('<>', _a='1', _b='2').xml(),
                         b'<h5 a="1" b="2">&lt;&gt;</h5>')

    def test_H6(self):
        self.assertEqual(H6('<>', _a='1', _b='2').xml(),
                         b'<h6 a="1" b="2">&lt;&gt;</h6>')

    def test_P(self):
        self.assertEqual(P('<>', _a='1', _b='2').xml(),
                         b'<p a="1" b="2">&lt;&gt;</p>')
        # test cr2br
        self.assertEqual(P('a\nb').xml(), b'<p>a\nb</p>')
        self.assertEqual(P('a\nb', cr2br=True).xml(), b'<p>a<br />b</p>')

    def test_STRONG(self):
        self.assertEqual(STRONG('<>', _a='1', _b='2').xml(),
                         b'<strong a="1" b="2">&lt;&gt;</strong>')

    def test_B(self):
        self.assertEqual(B('<>', _a='1', _b='2').xml(),
                         b'<b a="1" b="2">&lt;&gt;</b>')

    def test_BR(self):
        # empty BR()
        self.assertEqual(BR().xml(), b'<br />')
        self.assertEqual(BR(_a='1', _b='2').xml(), b'<br a="1" b="2" />')

    def test_HR(self):
        self.assertEqual(HR(_a='1', _b='2').xml(), b'<hr a="1" b="2" />')

    def test_A(self):
        self.assertEqual(A('<>', _a='1', _b='2').xml(),
                         b'<a a="1" b="2">&lt;&gt;</a>')
        self.assertEqual(A('a', cid='b').xml(),
                         b'<a data-w2p_disable_with="default" data-w2p_method="GET" data-w2p_target="b">a</a>')
        self.assertEqual(A('a', callback='b', _id='c').xml(),
                         b'<a data-w2p_disable_with="default" data-w2p_method="POST" href="b" id="c">a</a>')
        # Callback with no id trigger web2py_uuid() call
        from gluon.html import web2pyHTMLParser
        #a = A('a', callback='b').xml()

        #for tag in web2pyHTMLParser(a).tree.elements('a'):
        #    uuid_generated = tag.attributes['_id']
        #self.assertEqual(a,
        #                 b'<a data-w2p_disable_with="default" data-w2p_method="POST" href="b" id="{id}">a</a>'.format(id=uuid_generated))
        self.assertEqual(A('a', delete='tr').xml(),
                         b'<a data-w2p_disable_with="default" data-w2p_remove="tr">a</a>')
        self.assertEqual(A('a', _id='b', target='<self>').xml(),
                         b'<a data-w2p_disable_with="default" data-w2p_target="b" id="b">a</a>')
        self.assertEqual(A('a', component='b').xml(),
                         b'<a data-w2p_disable_with="default" data-w2p_method="GET" href="b">a</a>')
        self.assertEqual(A('a', _id='b', callback='c', noconfirm=True).xml(),
                         b'<a data-w2p_disable_with="default" data-w2p_method="POST" href="c" id="b">a</a>')
        self.assertEqual(A('a', cid='b').xml(),
                         b'<a data-w2p_disable_with="default" data-w2p_method="GET" data-w2p_target="b">a</a>')
        self.assertEqual(A('a', cid='b', _disable_with='processing...').xml(),
                         b'<a data-w2p_disable_with="processing..." data-w2p_method="GET" data-w2p_target="b">a</a>')
        self.assertEqual(A('a', callback='b', delete='tr', noconfirm=True, _id='c').xml(),
                         b'<a data-w2p_disable_with="default" data-w2p_method="POST" data-w2p_remove="tr" href="b" id="c">a</a>')
        self.assertEqual(A('a', callback='b', delete='tr', confirm='Are you sure?', _id='c').xml(),
                         b'<a data-w2p_confirm="Are you sure?" data-w2p_disable_with="default" data-w2p_method="POST" data-w2p_remove="tr" href="b" id="c">a</a>')

    def test_BUTTON(self):
        self.assertEqual(BUTTON('test', _type='button').xml(),
                         b'<button type="button">test</button>')

    def test_EM(self):
        self.assertEqual(EM('<>', _a='1', _b='2').xml(),
                         b'<em a="1" b="2">&lt;&gt;</em>')

    def test_EMBED(self):
        self.assertEqual(EMBED(_a='1', _b='2').xml(),
                         b'<embed a="1" b="2" />')

    def test_TT(self):
        self.assertEqual(TT('<>', _a='1', _b='2').xml(),
                         b'<tt a="1" b="2">&lt;&gt;</tt>')

    def test_PRE(self):
        self.assertEqual(PRE('<>', _a='1', _b='2').xml(),
                         b'<pre a="1" b="2">&lt;&gt;</pre>')

    def test_CENTER(self):
        self.assertEqual(CENTER('<>', _a='1', _b='2').xml(),
                         b'<center a="1" b="2">&lt;&gt;</center>')

    def test_CODE(self):
        self.assertEqual(CODE("print 'hello world'",
                              language='python',
                              link=None,
                              counter=1,
                              styles={},
                              highlight_line=None).xml(),
                         '<table><tr style="vertical-align:top;"><td style="min-width:40px; text-align: right;"><pre style="\nfont-size: 11px;\nfont-family: Bitstream Vera Sans Mono,monospace;\nbackground-color: transparent;\nmargin: 0;\npadding: 5px;\nborder: none;\ncolor: #A0A0A0;\n">1.</pre></td><td><pre style="\nfont-size: 11px;\nfont-family: Bitstream Vera Sans Mono,monospace;\nbackground-color: transparent;\nmargin: 0;\npadding: 5px;\nborder: none;\noverflow: auto;\nwhite-space: pre !important;\n"><span style="color:#185369; font-weight: bold">print </span><span style="color: #FF9966">\'hello world\'</span></pre></td></tr></table>')

    def test_LABEL(self):
        self.assertEqual(LABEL('<>', _a='1', _b='2').xml(),
                         b'<label a="1" b="2">&lt;&gt;</label>')

    def test_LI(self):
        self.assertEqual(LI('<>', _a='1', _b='2').xml(),
                         b'<li a="1" b="2">&lt;&gt;</li>')

    def test_UL(self):
        self.assertEqual(UL('<>', _a='1', _b='2').xml(),
                         b'<ul a="1" b="2"><li>&lt;&gt;</li></ul>')

    def test_OL(self):
        self.assertEqual(OL('<>', _a='1', _b='2').xml(),
                         b'<ol a="1" b="2"><li>&lt;&gt;</li></ol>')

    def test_TD(self):
        self.assertEqual(TD('<>', _a='1', _b='2').xml(),
                         b'<td a="1" b="2">&lt;&gt;</td>')

    def test_TH(self):
        self.assertEqual(TH('<>', _a='1', _b='2').xml(),
                         b'<th a="1" b="2">&lt;&gt;</th>')

    def test_TR(self):
        self.assertEqual(TR('<>', _a='1', _b='2').xml(),
                         b'<tr a="1" b="2"><td>&lt;&gt;</td></tr>')

    def test_THEAD(self):
        self.assertEqual(THEAD('<>', _a='1', _b='2').xml(),
                         b'<thead a="1" b="2"><tr><th>&lt;&gt;</th></tr></thead>')
        # self.assertEqual(THEAD(TRHEAD('<>'), _a='1', _b='2').xml(),
        #                  '<thead a="1" b="2"><tr><th>&lt;&gt;</th></tr></thead>')
        self.assertEqual(THEAD(TR('<>'), _a='1', _b='2').xml(),
                         b'<thead a="1" b="2"><tr><td>&lt;&gt;</td></tr></thead>')

    def test_TBODY(self):
        self.assertEqual(TBODY('<>', _a='1', _b='2').xml(),
                         b'<tbody a="1" b="2"><tr><td>&lt;&gt;</td></tr></tbody>')

    def test_TFOOT(self):
        self.assertEqual(TFOOT('<>', _a='1', _b='2').xml(),
                         b'<tfoot a="1" b="2"><tr><td>&lt;&gt;</td></tr></tfoot>')

    def test_COL(self):
        # Empty COL test
        self.assertEqual(COL().xml(), b'<col />')
        # Not Empty COL test
        self.assertEqual(COL(_span='2').xml(), b'<col span="2" />')
        # Commented for now not so sure how to make it pass properly was passing locally
        # I think this test is interesting and add value
        # This fail relate to python 2.6 limitation I think
        # Failing COL test
        # with self.assertRaises(SyntaxError) as cm:
        #     COL('<>').xml()
        # self.assertEqual(cm.exception[0], '<col/> tags cannot have components')
        # For now
        self.assertRaises(SyntaxError, COL, b'<>')

    def test_COLGROUP(self):
        # Empty COLGROUP test
        self.assertEqual(COLGROUP().xml(), b'<colgroup></colgroup>')
        # Not Empty COLGROUP test
        self.assertEqual(COLGROUP('<>', _a='1', _b='2').xml(), b'<colgroup a="1" b="2">&lt;&gt;</colgroup>')

    def test_TABLE(self):
        self.assertEqual(TABLE('<>', _a='1', _b='2').xml(),
                         b'<table a="1" b="2"><tr><td>&lt;&gt;</td></tr>' +
                         b'</table>')

    def test_I(self):
        self.assertEqual(I('<>', _a='1', _b='2').xml(),
                         b'<i a="1" b="2">&lt;&gt;</i>')

    def test_IFRAME(self):
        self.assertEqual(IFRAME('<>', _a='1', _b='2').xml(),
                         b'<iframe a="1" b="2">&lt;&gt;</iframe>')

    def test_INPUT(self):
        self.assertEqual(INPUT(_a='1', _b='2').xml(), b'<input a="1" b="2" type="text" />')
        # list value
        self.assertEqual(INPUT(_value=[1, 2, 3]).xml(), b'<input type="text" value="[1, 2, 3]" />')

    def test_TEXTAREA(self):
        self.assertEqual(TEXTAREA('<>', _a='1', _b='2').xml(),
                         b'<textarea a="1" b="2" cols="40" rows="10">&lt;&gt;' +
                         b'</textarea>')
        # override _rows and _cols
        self.assertEqual(TEXTAREA('<>', _a='1', _b='2', _rows=5, _cols=20).xml(),
                         b'<textarea a="1" b="2" cols="20" rows="5">&lt;&gt;' +
                         b'</textarea>')
        self.assertEqual(TEXTAREA('<>', value='bla bla bla...', _rows=10, _cols=40).xml(),
                         b'<textarea cols="40" rows="10">bla bla bla...</textarea>')

    def test_OPTION(self):
        self.assertEqual(OPTION('<>', _a='1', _b='2').xml(),
                         b'<option a="1" b="2" value="&lt;&gt;">&lt;&gt;' +
                         b'</option>')

    def test_OBJECT(self):
        self.assertEqual(OBJECT('<>', _a='1', _b='2').xml(),
                         b'<object a="1" b="2">&lt;&gt;</object>')

    def test_OPTGROUP(self):
        # Empty OPTGROUP test
        self.assertEqual(OPTGROUP().xml(),
                         b'<optgroup></optgroup>')
        # Not Empty OPTGROUP test
        self.assertEqual(OPTGROUP('<>', _a='1', _b='2').xml(),
                         b'<optgroup a="1" b="2"><option value="&lt;&gt;">&lt;&gt;</option></optgroup>')
        # With an OPTION
        self.assertEqual(OPTGROUP(OPTION('Option 1', _value='1'), _label='Group 1').xml(),
                         b'<optgroup label="Group 1"><option value="1">Option 1</option></optgroup>')

    def test_SELECT(self):
        self.assertEqual(SELECT('<>', _a='1', _b='2').xml(),
                         b'<select a="1" b="2">' +
                         b'<option value="&lt;&gt;">&lt;&gt;</option></select>')
        self.assertEqual(SELECT(OPTION('option 1', _value='1'),
                                OPTION('option 2', _value='2')).xml(),
                         b'<select><option value="1">option 1</option><option value="2">option 2</option></select>')
        self.assertEqual(SELECT(OPTION('option 1', _value='1', _selected='selected'),
                                OPTION('option 2', _value='2'),
                                _multiple='multiple').xml(),
                         b'<select multiple="multiple"><option selected="selected" value="1">option 1</option><option value="2">option 2</option></select>')
        # More then one select with mutilple
        self.assertEqual(SELECT(OPTION('option 1', _value='1', _selected='selected'),
                                OPTION('option 2', _value='2', _selected='selected'),
                                _multiple='multiple').xml(),
                         b'<select multiple="multiple"><option selected="selected" value="1">option 1</option><option selected="selected" value="2">option 2</option></select>'
                         )
        # OPTGROUP
        self.assertEqual(SELECT(OPTGROUP(OPTION('option 1', _value='1'),
                                         OPTION('option 2', _value='2'),
                                         _label='Group 1',)).xml(),
                         b'<select><optgroup label="Group 1"><option value="1">option 1</option><option value="2">option 2</option></optgroup></select>')
        # List
        self.assertEqual(SELECT([1, 2, 3, 4, 5]).xml(),
                         b'<select><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option></select>')
        # Tuple
        self.assertEqual(SELECT((1, 2, 3, 4, 5)).xml(),
                         b'<select><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option></select>')
        # String value
        self.assertEqual(SELECT('Option 1', 'Option 2').xml(),
                         b'<select><option value="Option 1">Option 1</option><option value="Option 2">Option 2</option></select>')
        # list as a value
        self.assertEqual(SELECT(OPTION('option 1', _value=[1, 2, 3]),
                                OPTION('option 2', _value=[4, 5, 6], _selected='selected'),
                                _multiple='multiple').xml(),
                         b'<select multiple="multiple"><option value="[1, 2, 3]">option 1</option><option selected="selected" value="[4, 5, 6]">option 2</option></select>')

    def test_FIELDSET(self):
        self.assertEqual(FIELDSET('<>', _a='1', _b='2').xml(),
                         b'<fieldset a="1" b="2">&lt;&gt;</fieldset>')

    def test_LEGEND(self):
        self.assertEqual(LEGEND('<>', _a='1', _b='2').xml(),
                         b'<legend a="1" b="2">&lt;&gt;</legend>')

    def test_FORM(self):
        self.assertEqual(FORM('<>', _a='1', _b='2').xml(),
                         b'<form a="1" action="#" b="2" enctype="multipart/form-data" method="post">&lt;&gt;</form>')
        # These 2 crash AppVeyor and Travis with: "ImportError: No YAML serializer available"
        # self.assertEqual(FORM('<>', _a='1', _b='2').as_yaml(),
        #                  "accepted: null\nattributes: {_a: '1', _action: '#', _b: '2', _enctype: multipart/form-data, _method: post}\ncomponents: [<>]\nerrors: {}\nlatest: {}\nparent: null\nvars: {}\n")
        # TODO check tags content
        self.assertEqual(len(FORM('<>', _a='1', _b='2').as_xml()), 334)

    def test_BEAUTIFY(self):
        #self.assertEqual(BEAUTIFY(['a', 'b', {'hello': 'world'}]).xml(),
        #                 '<div><table><tr><td><div>a</div></td></tr><tr><td><div>b</div></td></tr><tr><td><div><table><tr><td style="font-weight:bold;vertical-align:top;">hello</td><td style="vertical-align:top;">:</td><td><div>world</div></td></tr></table></div></td></tr></table></div>')
        # unicode
        self.assertEqual(BEAUTIFY([P(u'àéèûôç'), 'a', 'b', {'hello': 'world'}]).xml(),
                         b'<div><table><tr><td><div><p>\xc3\xa0\xc3\xa9\xc3\xa8\xc3\xbb\xc3\xb4\xc3\xa7</p></div></td></tr><tr><td><div>a</div></td></tr><tr><td><div>b</div></td></tr><tr><td><div><table><tr><td style="font-weight:bold;vertical-align:top;">hello</td><td style="vertical-align:top;">:</td><td><div>world</div></td></tr></table></div></td></tr></table></div>')

    def test_MENU(self):
        self.assertEqual(MENU([('Home', False, '/welcome/default/index', [])]).xml(),
                         b'<ul class="web2py-menu web2py-menu-vertical"><li class="web2py-menu-first"><a href="/welcome/default/index">Home</a></li></ul>')
        # Multiples entries menu
        self.assertEqual(MENU([('Home', False, '/welcome/default/index', []),
                               ('Item 1', False, '/welcome/default/func_one', []),
                               ('Item 2', False, '/welcome/default/func_two', []),
                               ('Item 3', False, '/welcome/default/func_three', []),
                               ('Item 4', False, '/welcome/default/func_four', [])]).xml(),
                         b'<ul class="web2py-menu web2py-menu-vertical"><li class="web2py-menu-first"><a href="/welcome/default/index">Home</a></li><li><a href="/welcome/default/func_one">Item 1</a></li><li><a href="/welcome/default/func_two">Item 2</a></li><li><a href="/welcome/default/func_three">Item 3</a></li><li class="web2py-menu-last"><a href="/welcome/default/func_four">Item 4</a></li></ul>'
                         )
        # mobile=True
        self.assertEqual(MENU([('Home', False, '/welcome/default/index', [])], mobile=True).xml(),
                         b'<select class="web2py-menu web2py-menu-vertical" onchange="window.location=this.value"><option value="/welcome/default/index">Home</option></select>')
        # Multiples entries menu for mobile
        self.assertEqual(MENU([('Home', False, '/welcome/default/index', []),
                               ('Item 1', False, '/welcome/default/func_one', []),
                               ('Item 2', False, '/welcome/default/func_two', []),
                               ('Item 3', False, '/welcome/default/func_three', []),
                               ('Item 4', False, '/welcome/default/func_four', [])], mobile=True).xml(),
                         b'<select class="web2py-menu web2py-menu-vertical" onchange="window.location=this.value"><option value="/welcome/default/index">Home</option><option value="/welcome/default/func_one">Item 1</option><option value="/welcome/default/func_two">Item 2</option><option value="/welcome/default/func_three">Item 3</option><option value="/welcome/default/func_four">Item 4</option></select>')

    # TODO: def test_embed64(self):

    def test_web2pyHTMLParser(self):
        #tag should not be a byte
        self.assertEqual(web2pyHTMLParser("<div></div>").tree.components[0].tag, 'div')
        a = str(web2pyHTMLParser('<div>a<span>b</div>c').tree)
        self.assertEqual(a, "<div>a<span>b</span></div>c")

        tree = web2pyHTMLParser('hello<div a="b">world</div>').tree
        tree.element(_a='b')['_c']=5
        self.assertEqual(str(tree), 'hello<div a="b" c="5">world</div>')

        a = str(web2pyHTMLParser('<div><img class="img"/></div>', closed=['img']).tree)
        self.assertEqual(a, '<div><img class="img" /></div>')

        #greater-than sign ( > )  --> decimal &#62; --> hexadecimal &#x3E;
        #Less-than sign    ( < )  --> decimal &#60; --> hexadecimal &#x3C;
        # test decimal
        a = str(web2pyHTMLParser('<div>&#60; &#62;</div>').tree)
        self.assertEqual(a, '<div>&lt; &gt;</div>')
        # test hexadecimal
        a = str(web2pyHTMLParser('<div>&#x3C; &#x3E;</div>').tree)
        self.assertEqual(a, '<div>&lt; &gt;</div>')

    def test_markdown(self):
        def markdown(text, tag=None, attributes={}):
            r = {None: re.sub('\s+',' ',text), \
                 'h1':'#'+text+'\\n\\n', \
                 'p':text+'\\n'}.get(tag,text)
            return r
        a=TAG('<h1>Header</h1><p>this is a     test</p>')
        ret = a.flatten(markdown)
        self.assertEqual(ret, '#Header\\n\\nthis is a test\\n')

    # TODO: def test_markdown_serializer(self):

    # TODO: def test_markmin_serializer(self):

    def test_MARKMIN(self):
        # This test pass with python 2.7 but expected to fail under 2.6
        # with self.assertRaises(TypeError) as cm:
        #     MARKMIN().xml()
        # self.assertEqual(cm.exception[0], '__init__() takes at least 2 arguments (1 given)')
        # For now
        self.assertRaises(TypeError, MARKMIN)
        self.assertEqual(MARKMIN('').xml(), b'')
        self.assertEqual(MARKMIN('<>').xml(),
                         b'<p>&lt;&gt;</p>')
        self.assertEqual(MARKMIN("``hello_world = 'Hello World!'``:python").xml(),
                         b'<code class="python">hello_world = \'Hello World!\'</code>')
        self.assertEqual(MARKMIN('<>').flatten(), b'<>')

    def test_ASSIGNJS(self):
        # empty assignation
        self.assertEqual(ASSIGNJS().xml(), b'')
        # text assignation
        self.assertEqual(ASSIGNJS(var1='1').xml(), b'var var1 = "1";\n')
        # int assignation
        self.assertEqual(ASSIGNJS(var2=2).xml(), b'var var2 = 2;\n')


class TestData(unittest.TestCase):

    def test_Adata(self):
        self.assertEqual(A('<>', data=dict(abc='<def?asd>', cde='standard'), _a='1', _b='2').xml(),
                         b'<a a="1" b="2" data-abc="&lt;def?asd&gt;" data-cde="standard">&lt;&gt;</a>')
