import unittest
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
import re
_SIMPLE_XML = r"""
-
Name
Description
"""
_SIMPLE_XHTML = r"""
Hello!
"""
_EMPTYTAGS_HTML = """
"""
_BOOLEANATTRS_XHTML= """
"""
_ENTITIES_XHTML= r"""
"""
_COMPLEX_XHTML = r"""
This will be escaped in html output: &
"""
_NVU_HTML = """
test doc
Oh yeah...
Yup
More
Stuff
Oh Yeah
1
2
3
4
And an image...
"""
_FILLMELDFORM_HTML = """\
Emergency Contacts
Return to list
"""
class MeldAPITests(unittest.TestCase):
def _makeElement(self, string):
from meld3 import parse_xmlstring
return parse_xmlstring(string)
def _makeElementFromHTML(self, string):
from meld3 import parse_htmlstring
return parse_htmlstring(string)
def test_findmeld(self):
root = self._makeElement(_SIMPLE_XML)
item = root.findmeld('item')
self.assertEqual(item.tag, 'item')
name = root.findmeld('name')
self.assertEqual(name.text, 'Name')
def test_findmeld_default(self):
root = self._makeElement(_SIMPLE_XML)
item = root.findmeld('item')
self.assertEqual(item.tag, 'item')
unknown = root.findmeld('unknown', 'foo')
self.assertEqual(unknown, 'foo')
self.assertEqual(root.findmeld('unknown'), None)
def test_repeat_nochild(self):
root = self._makeElement(_SIMPLE_XML)
item = root.findmeld('item')
self.assertEqual(item.tag, 'item')
data = [{'name':'Jeff Buckley', 'description':'ethereal'},
{'name':'Slipknot', 'description':'heavy'}]
for element, d in item.repeat(data):
element.findmeld('name').text = d['name']
element.findmeld('description').text = d['description']
self.assertEqual(item[0].text, 'Jeff Buckley')
self.assertEqual(item[1].text, 'ethereal')
def test_repeat_child(self):
root = self._makeElement(_SIMPLE_XML)
list = root.findmeld('list')
self.assertEqual(list.tag, 'list')
data = [{'name':'Jeff Buckley', 'description':'ethereal'},
{'name':'Slipknot', 'description':'heavy'}]
for element, d in list.repeat(data, 'item'):
element.findmeld('name').text = d['name']
element.findmeld('description').text = d['description']
self.assertEqual(list[0][0].text, 'Jeff Buckley')
self.assertEqual(list[0][1].text, 'ethereal')
self.assertEqual(list[1][0].text, 'Slipknot')
self.assertEqual(list[1][1].text, 'heavy')
def test_mod(self):
root = self._makeElement(_SIMPLE_XML)
root % {'description':'foo', 'name':'bar'}
name = root.findmeld('name')
self.assertEqual(name.text, 'bar')
desc = root.findmeld('description')
self.assertEqual(desc.text, 'foo')
def test_fillmelds(self):
root = self._makeElement(_SIMPLE_XML)
unfilled = root.fillmelds(**{'description':'foo', 'jammyjam':'a'})
desc = root.findmeld('description')
self.assertEqual(desc.text, 'foo')
self.assertEqual(unfilled, ['jammyjam'])
def test_fillmeldhtmlform(self):
data = [
{'honorific':'Mr.', 'firstname':'Chris', 'middlename':'Phillips',
'lastname':'McDonough', 'address1':'802 Caroline St.',
'address2':'Apt. 2B', 'city':'Fredericksburg', 'state': 'VA',
'zip':'22401', 'homephone':'555-1212', 'cellphone':'555-1313',
'email':'chrism@plope.com', 'suffix':'Sr.', 'over18':True,
'mailok:inputgroup':'true', 'favorite_color:inputgroup':'Green'},
{'honorific':'Mr.', 'firstname':'Fred', 'middlename':'',
'lastname':'Rogers', 'address1':'1 Imaginary Lane',
'address2':'Apt. 3A', 'city':'Never Never Land', 'state': 'LA',
'zip':'00001', 'homephone':'555-1111', 'cellphone':'555-4444',
'email':'fred@neighborhood.com', 'suffix':'Jr.', 'over18':False,
'mailok:inputgroup':'false','favorite_color:inputgroup':'Yellow',},
{'firstname':'Fred', 'middlename':'',
'lastname':'Rogers', 'address1':'1 Imaginary Lane',
'address2':'Apt. 3A', 'city':'Never Never Land', 'state': 'LA',
'zip':'00001', 'homephone':'555-1111', 'cellphone':'555-4444',
'email':'fred@neighborhood.com', 'suffix':'IV', 'over18':False,
'mailok:inputgroup':'false', 'favorite_color:inputgroup':'Blue',
'notthere':1,},
]
root = self._makeElementFromHTML(_FILLMELDFORM_HTML)
clone = root.clone()
unfilled = clone.fillmeldhtmlform(**data[0])
self.assertEqual(unfilled, [])
self.assertEqual(clone.findmeld('honorific').attrib['value'], 'Mr.')
self.assertEqual(clone.findmeld('firstname').attrib['value'], 'Chris')
middlename = clone.findmeld('middlename')
self.assertEqual(middlename.attrib['value'], 'Phillips')
suffix = clone.findmeld('suffix')
self.assertEqual(suffix[1].attrib['selected'], 'selected')
self.assertEqual(clone.findmeld('over18').attrib['checked'], 'checked')
mailok = clone.findmeld('mailok:inputgroup')
self.assertEqual(mailok[1].attrib['checked'], 'checked')
favoritecolor = clone.findmeld('favorite_color:inputgroup')
self.assertEqual(favoritecolor[1].attrib['checked'], 'checked')
clone = root.clone()
unfilled = clone.fillmeldhtmlform(**data[1])
self.assertEqual(unfilled, ['favorite_color:inputgroup'])
self.assertEqual(clone.findmeld('over18').attrib.get('checked'), None)
mailok = clone.findmeld('mailok:inputgroup')
self.assertEqual(mailok[2].attrib['checked'], 'checked')
self.assertEqual(mailok[1].attrib.get('checked'), None)
clone = root.clone()
unfilled = clone.fillmeldhtmlform(**data[2])
self.assertEqual(unfilled, ['notthere', 'suffix'])
self.assertEqual(clone.findmeld('honorific').text, None)
favoritecolor = clone.findmeld('favorite_color:inputgroup')
self.assertEqual(favoritecolor[2].attrib['checked'], 'checked')
self.assertEqual(favoritecolor[1].attrib.get('checked'), None)
def test_replace_removes_all_elements(self):
from meld3 import Replace
root = self._makeElement(_SIMPLE_XML)
L = root.findmeld('list')
L.replace('this is a textual replacement')
R = root[0]
self.assertEqual(R.tag, Replace)
self.assertEqual(len(root.getchildren()), 1)
def test_replace_replaces_the_right_element(self):
from meld3 import Replace
root = self._makeElement(_SIMPLE_XML)
D = root.findmeld('description')
D.replace('this is a textual replacement')
self.assertEqual(len(root.getchildren()), 1)
L = root[0]
self.assertEqual(L.tag, 'list')
self.assertEqual(len(L.getchildren()), 1)
I = L[0]
self.assertEqual(I.tag, 'item')
self.assertEqual(len(I.getchildren()), 2)
N = I[0]
self.assertEqual(N.tag, 'name')
self.assertEqual(len(N.getchildren()), 0)
D = I[1]
self.assertEqual(D.tag, Replace)
self.assertEqual(D.text, 'this is a textual replacement')
self.assertEqual(len(D.getchildren()), 0)
self.assertEqual(D.structure, False)
def test_content(self):
from meld3 import Replace
root = self._makeElement(_SIMPLE_XML)
D = root.findmeld('description')
D.content('this is a textual replacement')
self.assertEqual(len(root.getchildren()), 1)
L = root[0]
self.assertEqual(L.tag, 'list')
self.assertEqual(len(L.getchildren()), 1)
I = L[0]
self.assertEqual(I.tag, 'item')
self.assertEqual(len(I.getchildren()), 2)
N = I[0]
self.assertEqual(N.tag, 'name')
self.assertEqual(len(N.getchildren()), 0)
D = I[1]
self.assertEqual(D.tag, 'description')
self.assertEqual(D.text, None)
self.assertEqual(len(D.getchildren()), 1)
T = D[0]
self.assertEqual(T.tag, Replace)
self.assertEqual(T.text, 'this is a textual replacement')
self.assertEqual(T.structure, False)
def test_attributes(self):
from meld3 import _MELD_ID
root = self._makeElement(_COMPLEX_XHTML)
D = root.findmeld('form1')
D.attributes(foo='bar', baz='1', g='2', action='#')
self.assertEqual(D.attrib, {
'foo':'bar', 'baz':'1', 'g':'2',
'method':'POST', 'action':'#',
_MELD_ID: 'form1'})
def test_attributes_nonstringtype_raises(self):
root = self._makeElement(' ')
self.assertRaises(ValueError, root.attributes, foo=1)
class MeldElementInterfaceTests(unittest.TestCase):
def _getTargetClass(self):
from meld3 import _MeldElementInterface
return _MeldElementInterface
def _makeOne(self, *arg, **kw):
klass = self._getTargetClass()
return klass(*arg, **kw)
def test_repeat(self):
root = self._makeOne('root', {})
from meld3 import _MELD_ID
item = self._makeOne('item', {_MELD_ID:'item'})
record = self._makeOne('record', {_MELD_ID:'record'})
name = self._makeOne('name', {_MELD_ID:'name'})
description = self._makeOne('description', {_MELD_ID:'description'})
record.append(name)
record.append(description)
item.append(record)
root.append(item)
data = [{'name':'Jeff Buckley', 'description':'ethereal'},
{'name':'Slipknot', 'description':'heavy'}]
for element, d in item.repeat(data):
element.findmeld('name').text = d['name']
element.findmeld('description').text = d['description']
self.assertEqual(len(root), 2)
item1 = root[0]
self.assertEqual(len(item1), 1)
record1 = item1[0]
self.assertEqual(len(record1), 2)
name1 = record1[0]
desc1 = record1[1]
self.assertEqual(name1.text, 'Jeff Buckley')
self.assertEqual(desc1.text, 'ethereal')
item2 = root[1]
self.assertEqual(len(item2), 1)
record2 = item2[0]
self.assertEqual(len(record2), 2)
name2 = record2[0]
desc2 = record2[1]
self.assertEqual(name2.text, 'Slipknot')
self.assertEqual(desc2.text, 'heavy')
def test_content_simple_nostructure(self):
el = self._makeOne('div', {'id':'thediv'})
el.content('hello')
self.assertEqual(len(el._children), 1)
replacenode = el._children[0]
self.assertEqual(replacenode.parent, el)
self.assertEqual(replacenode.text, 'hello')
self.assertEqual(replacenode.structure, False)
from meld3 import Replace
self.assertEqual(replacenode.tag, Replace)
def test_content_simple_structure(self):
el = self._makeOne('div', {'id':'thediv'})
el.content('hello', structure=True)
self.assertEqual(len(el._children), 1)
replacenode = el._children[0]
self.assertEqual(replacenode.parent, el)
self.assertEqual(replacenode.text, 'hello')
self.assertEqual(replacenode.structure, True)
from meld3 import Replace
self.assertEqual(replacenode.tag, Replace)
def test_findmeld_simple(self):
from meld3 import _MELD_ID
el = self._makeOne('div', {_MELD_ID:'thediv'})
self.assertEqual(el.findmeld('thediv'), el)
def test_findmeld_simple_oneleveldown(self):
from meld3 import _MELD_ID
el = self._makeOne('div', {_MELD_ID:'thediv'})
span = self._makeOne('span', {_MELD_ID:'thespan'})
el.append(span)
self.assertEqual(el.findmeld('thespan'), span)
def test_findmeld_simple_twolevelsdown(self):
from meld3 import _MELD_ID
el = self._makeOne('div', {_MELD_ID:'thediv'})
span = self._makeOne('span', {_MELD_ID:'thespan'})
a = self._makeOne('a', {_MELD_ID:'thea'})
span.append(a)
el.append(span)
self.assertEqual(el.findmeld('thea'), a)
def test_ctor(self):
iface = self._makeOne('div', {'id':'thediv'})
self.assertEqual(iface.parent, None)
self.assertEqual(iface.tag, 'div')
self.assertEqual(iface.attrib, {'id':'thediv'})
def test_getiterator_simple(self):
div = self._makeOne('div', {'id':'thediv'})
iterator = div.getiterator()
self.assertEqual(len(iterator), 1)
self.assertEqual(iterator[0], div)
def test_getiterator(self):
div = self._makeOne('div', {'id':'thediv'})
span = self._makeOne('span', {})
span2 = self._makeOne('span', {'id':'2'})
span3 = self._makeOne('span3', {'id':'3'})
span3.text = 'abc'
span3.tail = ' '
div.append(span)
span.append(span2)
span2.append(span3)
it = div.getiterator()
self.assertEqual(len(it), 4)
self.assertEqual(it[0], div)
self.assertEqual(it[1], span)
self.assertEqual(it[2], span2)
self.assertEqual(it[3], span3)
def test_getiterator_tag_ignored(self):
div = self._makeOne('div', {'id':'thediv'})
span = self._makeOne('span', {})
span2 = self._makeOne('span', {'id':'2'})
span3 = self._makeOne('span3', {'id':'3'})
span3.text = 'abc'
span3.tail = ' '
div.append(span)
span.append(span2)
span2.append(span3)
it = div.getiterator(tag='div')
self.assertEqual(len(it), 4)
self.assertEqual(it[0], div)
self.assertEqual(it[1], span)
self.assertEqual(it[2], span2)
self.assertEqual(it[3], span3)
def test_append(self):
div = self._makeOne('div', {'id':'thediv'})
span = self._makeOne('span', {})
div.append(span)
self.assertEqual(div[0].tag, 'span')
self.assertEqual(span.parent, div)
def test__setitem__(self):
div = self._makeOne('div', {'id':'thediv'})
span = self._makeOne('span', {})
span2 = self._makeOne('span', {'id':'2'})
div.append(span)
div[0] = span2
self.assertEqual(div[0].tag, 'span')
self.assertEqual(div[0].attrib, {'id':'2'})
self.assertEqual(div[0].parent, div)
def test_insert(self):
div = self._makeOne('div', {'id':'thediv'})
span = self._makeOne('span', {})
span2 = self._makeOne('span', {'id':'2'})
div.append(span)
div.insert(0, span2)
self.assertEqual(div[0].tag, 'span')
self.assertEqual(div[0].attrib, {'id':'2'})
self.assertEqual(div[0].parent, div)
self.assertEqual(div[1].tag, 'span')
self.assertEqual(div[1].attrib, {})
self.assertEqual(div[1].parent, div)
def test_clone_simple(self):
div = self._makeOne('div', {'id':'thediv'})
div.text = 'abc'
div.tail = ' '
span = self._makeOne('span', {})
div.append(span)
div2 = div.clone()
def test_clone(self):
div = self._makeOne('div', {'id':'thediv'})
span = self._makeOne('span', {})
span2 = self._makeOne('span', {'id':'2'})
span3 = self._makeOne('span3', {'id':'3'})
span3.text = 'abc'
span3.tail = ' '
div.append(span)
span.append(span2)
span2.append(span3)
div2 = div.clone()
self.assertEqual(div.tag, div2.tag)
self.assertEqual(div.attrib, div2.attrib)
self.assertEqual(div[0].tag, div2[0].tag)
self.assertEqual(div[0].attrib, div2[0].attrib)
self.assertEqual(div[0][0].tag, div2[0][0].tag)
self.assertEqual(div[0][0].attrib, div2[0][0].attrib)
self.assertEqual(div[0][0][0].tag, div2[0][0][0].tag)
self.assertEqual(div[0][0][0].attrib, div2[0][0][0].attrib)
self.assertEqual(div[0][0][0].text, div2[0][0][0].text)
self.assertEqual(div[0][0][0].tail, div2[0][0][0].tail)
self.assertNotEqual(id(div), id(div2))
self.assertNotEqual(id(div[0]), id(div2[0]))
self.assertNotEqual(id(div[0][0]), id(div2[0][0]))
self.assertNotEqual(id(div[0][0][0]), id(div2[0][0][0]))
def test_deparent_noparent(self):
div = self._makeOne('div', {})
self.assertEqual(div.parent, None)
div.deparent()
self.assertEqual(div.parent, None)
def test_deparent_withparent(self):
parent = self._makeOne('parent', {})
self.assertEqual(parent.parent, None)
child = self._makeOne('child', {})
parent.append(child)
self.assertEqual(parent.parent, None)
self.assertEqual(child.parent, parent)
self.assertEqual(parent[0], child)
child.deparent()
self.assertEqual(child.parent, None)
self.assertRaises(IndexError, parent.__getitem__, 0)
def test_setslice(self):
parent = self._makeOne('parent', {})
child1 = self._makeOne('child1', {})
child2 = self._makeOne('child2', {})
child3 = self._makeOne('child3', {})
children = (child1, child2, child3)
parent[0:2] = children
self.assertEqual(child1.parent, parent)
self.assertEqual(child2.parent, parent)
self.assertEqual(child3.parent, parent)
self.assertEqual(parent._children, list(children))
def test_delslice(self):
parent = self._makeOne('parent', {})
child1 = self._makeOne('child1', {})
child2 = self._makeOne('child2', {})
child3 = self._makeOne('child3', {})
children = (child1, child2, child3)
parent[0:2] = children
del parent[0:2]
self.assertEqual(child1.parent, None)
self.assertEqual(child2.parent, None)
self.assertEqual(child3.parent, parent)
self.assertEqual(len(parent._children), 1)
def test_remove(self):
parent = self._makeOne('parent', {})
child1 = self._makeOne('child1', {})
parent.append(child1)
parent.remove(child1)
self.assertEqual(child1.parent, None)
self.assertEqual(len(parent._children), 0)
def test_lineage(self):
from meld3 import _MELD_ID
div1 = self._makeOne('div', {_MELD_ID:'div1'})
span1 = self._makeOne('span', {_MELD_ID:'span1'})
span2 = self._makeOne('span', {_MELD_ID:'span2'})
span3 = self._makeOne('span', {_MELD_ID:'span3'})
span4 = self._makeOne('span', {_MELD_ID:'span4'})
span5 = self._makeOne('span', {_MELD_ID:'span5'})
span6 = self._makeOne('span', {_MELD_ID:'span6'})
unknown = self._makeOne('span', {})
div2 = self._makeOne('div2', {_MELD_ID:'div2'})
div1.append(span1)
span1.append(span2)
span2.append(span3)
span3.append(unknown)
unknown.append(span4)
span4.append(span5)
span5.append(span6)
div1.append(div2)
def ids(L):
return [ x.meldid() for x in L ]
self.assertEqual(ids(div1.lineage()), ['div1'])
self.assertEqual(ids(span1.lineage()), ['span1', 'div1'])
self.assertEqual(ids(span2.lineage()), ['span2', 'span1', 'div1'])
self.assertEqual(ids(span3.lineage()), ['span3', 'span2', 'span1',
'div1'])
self.assertEqual(ids(unknown.lineage()), [None, 'span3', 'span2',
'span1',
'div1'])
self.assertEqual(ids(span4.lineage()), ['span4', None, 'span3',
'span2',
'span1','div1'])
self.assertEqual(ids(span5.lineage()), ['span5', 'span4', None,
'span3', 'span2',
'span1','div1'])
self.assertEqual(ids(span6.lineage()), ['span6', 'span5', 'span4',
None,'span3', 'span2',
'span1','div1'])
self.assertEqual(ids(div2.lineage()), ['div2', 'div1'])
def test_shortrepr(self):
div = self._makeOne('div', {'id':'div1'})
span = self._makeOne('span', {})
span2 = self._makeOne('span', {'id':'2'})
span3 = self._makeOne('span3', {'id':'3'})
span4 = self._makeOne('span4', {'id':'4'})
span5 = self._makeOne('span5', {'id':'5'})
span6 = self._makeOne('span6', {'id':'6'})
div2 = self._makeOne('div2', {'id':'div2'})
div.append(span)
span.append(span2)
div.append(div2)
r = div.shortrepr()
self.assertEqual(r, '')
def test_shortrepr2(self):
from meld3 import parse_xmlstring
root = parse_xmlstring(_COMPLEX_XHTML)
r = root.shortrepr()
self.assertEqual(r, '\n \n \n [...]\n\n \n [...]\n')
def test_diffmeld1(self):
from meld3 import parse_xmlstring
from meld3 import _MELD_ID
root = parse_xmlstring(_COMPLEX_XHTML)
clone = root.clone()
div = self._makeOne('div', {_MELD_ID:'newdiv'})
clone.append(div)
tr = clone.findmeld('tr')
tr.deparent()
title = clone.findmeld('title')
title.deparent()
clone.append(title)
# unreduced
diff = root.diffmeld(clone)
changes = diff['unreduced']
addedtags = [ x.attrib[_MELD_ID] for x in changes['added'] ]
removedtags = [x.attrib[_MELD_ID] for x in changes['removed'] ]
movedtags = [ x.attrib[_MELD_ID] for x in changes['moved'] ]
addedtags.sort()
removedtags.sort()
movedtags.sort()
self.assertEqual(addedtags,['newdiv'])
self.assertEqual(removedtags,['td1', 'td2', 'tr'])
self.assertEqual(movedtags, ['title'])
# reduced
changes = diff['reduced']
addedtags = [ x.attrib[_MELD_ID] for x in changes['added'] ]
removedtags = [x.attrib[_MELD_ID] for x in changes['removed'] ]
movedtags = [ x.attrib[_MELD_ID] for x in changes['moved'] ]
addedtags.sort()
removedtags.sort()
movedtags.sort()
self.assertEqual(addedtags,['newdiv'])
self.assertEqual(removedtags,['tr'])
self.assertEqual(movedtags, ['title'])
def test_diffmeld2(self):
source = """
"""
target = """
"""
from meld3 import parse_htmlstring
source_root = parse_htmlstring(source)
target_root = parse_htmlstring(target)
changes = source_root.diffmeld(target_root)
# unreduced
actual = [x.meldid() for x in changes['unreduced']['moved']]
expected = ['b']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['unreduced']['added']]
expected = []
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['unreduced']['removed']]
expected = []
self.assertEqual(expected, actual)
# reduced
actual = [x.meldid() for x in changes['reduced']['moved']]
expected = ['b']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['reduced']['added']]
expected = []
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['reduced']['removed']]
expected = []
self.assertEqual(expected, actual)
def test_diffmeld3(self):
source = """
"""
target = """
"""
from meld3 import parse_htmlstring
source_root = parse_htmlstring(source)
target_root = parse_htmlstring(target)
changes = source_root.diffmeld(target_root)
# unreduced
actual = [x.meldid() for x in changes['unreduced']['moved']]
expected = ['b', 'c']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['unreduced']['added']]
expected = ['d', 'e']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['unreduced']['removed']]
expected = ['z', 'y']
self.assertEqual(expected, actual)
# reduced
actual = [x.meldid() for x in changes['reduced']['moved']]
expected = ['b']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['reduced']['added']]
expected = ['d']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['reduced']['removed']]
expected = ['z']
self.assertEqual(expected, actual)
def test_diffmeld4(self):
source = """
"""
target = """
"""
from meld3 import parse_htmlstring
source_root = parse_htmlstring(source)
target_root = parse_htmlstring(target)
changes = source_root.diffmeld(target_root)
# unreduced
actual = [x.meldid() for x in changes['unreduced']['moved']]
expected = ['a', 'b']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['unreduced']['added']]
expected = ['m', 'n']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['unreduced']['removed']]
expected = ['c', 'd', 'z', 'y']
self.assertEqual(expected, actual)
# reduced
actual = [x.meldid() for x in changes['reduced']['moved']]
expected = ['a']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['reduced']['added']]
expected = ['m']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['reduced']['removed']]
expected = ['c', 'z']
self.assertEqual(expected, actual)
def test_diffmeld5(self):
source = """
"""
target = """
"""
from meld3 import parse_htmlstring
source_root = parse_htmlstring(source)
target_root = parse_htmlstring(target)
changes = source_root.diffmeld(target_root)
# unreduced
actual = [x.meldid() for x in changes['unreduced']['moved']]
expected = ['a', 'b', 'c', 'd']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['unreduced']['added']]
expected = []
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['unreduced']['removed']]
expected = []
self.assertEqual(expected, actual)
# reduced
actual = [x.meldid() for x in changes['reduced']['moved']]
expected = ['a', 'c']
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['reduced']['added']]
expected = []
self.assertEqual(expected, actual)
actual = [x.meldid() for x in changes['reduced']['removed']]
expected = []
self.assertEqual(expected, actual)
class ParserTests(unittest.TestCase):
def _parse(self, *args):
from meld3 import parse_xmlstring
root = parse_xmlstring(*args)
return root
def _parse_html(self, *args):
from meld3 import parse_htmlstring
root = parse_htmlstring(*args)
return root
def test_parse_simple_xml(self):
from meld3 import _MELD_ID
root = self._parse(_SIMPLE_XML)
self.assertEqual(root.tag, 'root')
self.assertEqual(root.parent, None)
l1st = root[0]
self.assertEqual(l1st.tag, 'list')
self.assertEqual(l1st.parent, root)
self.assertEqual(l1st.attrib[_MELD_ID], 'list')
item = l1st[0]
self.assertEqual(item.tag, 'item')
self.assertEqual(item.parent, l1st)
self.assertEqual(item.attrib[_MELD_ID], 'item')
name = item[0]
description = item[1]
self.assertEqual(name.tag, 'name')
self.assertEqual(name.parent, item)
self.assertEqual(name.attrib[_MELD_ID], 'name')
self.assertEqual(description.tag, 'description')
self.assertEqual(description.parent, item)
self.assertEqual(description.attrib[_MELD_ID], 'description')
def test_parse_simple_xhtml(self):
xhtml_ns = '{http://www.w3.org/1999/xhtml}%s'
from meld3 import _MELD_ID
root = self._parse(_SIMPLE_XHTML)
self.assertEqual(root.tag, xhtml_ns % 'html')
self.assertEqual(root.attrib, {})
self.assertEqual(root.parent, None)
body = root[0]
self.assertEqual(body.tag, xhtml_ns % 'body')
self.assertEqual(body.attrib[_MELD_ID], 'body')
self.assertEqual(body.parent, root)
def test_parse_complex_xhtml(self):
xhtml_ns = '{http://www.w3.org/1999/xhtml}%s'
from meld3 import _MELD_ID
root = self._parse(_COMPLEX_XHTML)
self.assertEqual(root.tag, xhtml_ns % 'html')
self.assertEqual(root.attrib, {})
self.assertEqual(root.parent, None)
head = root[0]
self.assertEqual(head.tag, xhtml_ns % 'head')
self.assertEqual(head.attrib, {})
self.assertEqual(head.parent, root)
meta = head[0]
self.assertEqual(meta.tag, xhtml_ns % 'meta')
self.assertEqual(meta.attrib['content'],
'text/html; charset=ISO-8859-1')
self.assertEqual(meta.parent, head)
title = head[1]
self.assertEqual(title.tag, xhtml_ns % 'title')
self.assertEqual(title.attrib[_MELD_ID], 'title')
self.assertEqual(title.parent, head)
comment = root[1]
body = root[2]
self.assertEqual(body.tag, xhtml_ns % 'body')
self.assertEqual(body.attrib, {})
self.assertEqual(body.parent, root)
div1 = body[0]
self.assertEqual(div1.tag, xhtml_ns % 'div')
self.assertEqual(div1.attrib, {'{http://foo/bar}baz': 'slab'})
self.assertEqual(div1.parent, body)
div2 = body[1]
self.assertEqual(div2.tag, xhtml_ns % 'div')
self.assertEqual(div2.attrib[_MELD_ID], 'content_well')
self.assertEqual(div2.parent, body)
form = div2[0]
self.assertEqual(form.tag, xhtml_ns % 'form')
self.assertEqual(form.attrib[_MELD_ID], 'form1')
self.assertEqual(form.attrib['action'], '.')
self.assertEqual(form.attrib['method'], 'POST')
self.assertEqual(form.parent, div2)
img = form[0]
self.assertEqual(img.tag, xhtml_ns % 'img')
self.assertEqual(img.parent, form)
table = form[1]
self.assertEqual(table.tag, xhtml_ns % 'table')
self.assertEqual(table.attrib[_MELD_ID], 'table1')
self.assertEqual(table.attrib['border'], '0')
self.assertEqual(table.parent, form)
tbody = table[0]
self.assertEqual(tbody.tag, xhtml_ns % 'tbody')
self.assertEqual(tbody.attrib[_MELD_ID], 'tbody')
self.assertEqual(tbody.parent, table)
tr = tbody[0]
self.assertEqual(tr.tag, xhtml_ns % 'tr')
self.assertEqual(tr.attrib[_MELD_ID], 'tr')
self.assertEqual(tr.attrib['class'], 'foo')
self.assertEqual(tr.parent, tbody)
td1 = tr[0]
self.assertEqual(td1.tag, xhtml_ns % 'td')
self.assertEqual(td1.attrib[_MELD_ID], 'td1')
self.assertEqual(td1.parent, tr)
td2 = tr[1]
self.assertEqual(td2.tag, xhtml_ns % 'td')
self.assertEqual(td2.attrib[_MELD_ID], 'td2')
self.assertEqual(td2.parent, tr)
def test_nvu_html(self):
from meld3 import _MELD_ID
from meld3 import Comment
root = self._parse_html(_NVU_HTML)
self.assertEqual(root.tag, 'html')
self.assertEqual(root.attrib, {})
self.assertEqual(root.parent, None)
head = root[0]
self.assertEqual(head.tag, 'head')
self.assertEqual(head.attrib, {})
self.assertEqual(head.parent, root)
meta = head[0]
self.assertEqual(meta.tag, 'meta')
self.assertEqual(meta.attrib['content'],
'text/html; charset=ISO-8859-1')
title = head[1]
self.assertEqual(title.tag, 'title')
self.assertEqual(title.attrib[_MELD_ID], 'title')
self.assertEqual(title.parent, head)
body = root[1]
self.assertEqual(body.tag, 'body')
self.assertEqual(body.attrib, {})
self.assertEqual(body.parent, root)
comment = body[0]
self.assertEqual(comment.tag, Comment)
br1 = body[1]
br2 = body[2]
table = body[3]
self.assertEqual(table.tag, 'table')
self.assertEqual(table.attrib, {'style':
'text-align: left; width: 100px;',
'border':'1',
'cellpadding':'2',
'cellspacing':'2'})
self.assertEqual(table.parent, body)
br3 = body[4]
href = body[5]
self.assertEqual(href.tag, 'a')
br4 = body[6]
br5 = body[7]
img = body[8]
self.assertEqual(img.tag, 'img')
def test_dupe_meldids_fails_parse_xml(self):
meld_ns = "http://www.plope.com/software/meld3"
repeated = (''
'Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xhtml_as_html(self):
root = self._parse(_SIMPLE_XHTML)
actual = self._write_html(root)
expected = """
Hello!
"""
self.assertNormalizedHTMLEqual(actual, expected)
def test_write_complex_xhtml_as_html(self):
root = self._parse(_COMPLEX_XHTML)
actual = self._write_html(root)
expected = """
This will be escaped in html output: &
"""
self.assertNormalizedHTMLEqual(actual, expected)
def test_write_complex_xhtml_as_xhtml(self):
# I'm not entirely sure if the cdata "script" quoting in this
# test is entirely correct for XHTML. Ryan Tomayko suggests
# that escaped entities are handled properly in script tags by
# XML-aware browsers at
# http://sourceforge.net/mailarchive/message.php?msg_id=10835582
# but I haven't tested it at all. ZPT does not seem to do
# this; it outputs unescaped data.
root = self._parse(_COMPLEX_XHTML)
actual = self._write_xhtml(root)
expected = """
This will be escaped in html output: &
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_emptytags_html(self):
root = self._parse(_EMPTYTAGS_HTML)
actual = self._write_html(root)
expected = """
"""
self.assertEqual(actual, expected)
def test_write_booleanattrs_xhtml_as_html(self):
root = self._parse(_BOOLEANATTRS_XHTML)
actual = self._write_html(root)
expected = """
"""
self.assertNormalizedHTMLEqual(actual, expected)
def test_write_simple_xhtml_pipeline(self):
root = self._parse(_SIMPLE_XHTML)
actual = self._write_xhtml(root, pipeline=True)
expected = """Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xml_pipeline(self):
root = self._parse(_SIMPLE_XML)
actual = self._write_xml(root, pipeline=True)
expected = """
-
Name
Description
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xml_override_encoding(self):
root = self._parse(_SIMPLE_XML)
from meld3 import doctype
actual = self._write_xml(root, encoding="latin-1")
expected = """
-
Name
Description
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xml_as_fragment(self):
root = self._parse(_SIMPLE_XML)
from meld3 import doctype
actual = self._write_xml(root, fragment=True)
expected = """
-
Name
Description
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xml_with_doctype(self):
root = self._parse(_SIMPLE_XML)
from meld3 import doctype
actual = self._write_xml(root, doctype=doctype.xhtml)
expected = """
-
Name
Description
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xml_doctype_nodeclaration(self):
root = self._parse(_SIMPLE_XML)
from meld3 import doctype
actual = self._write_xml(root, declaration=False,
doctype=doctype.xhtml)
expected = """
-
Name
Description
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xml_fragment_kills_doctype_and_declaration(self):
root = self._parse(_SIMPLE_XML)
from meld3 import doctype
actual = self._write_xml(root, declaration=True,
doctype=doctype.xhtml, fragment=True)
expected = """
-
Name
Description
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xhtml_override_encoding(self):
root = self._parse(_SIMPLE_XHTML)
from meld3 import doctype
actual = self._write_xhtml(root, encoding="latin-1", declaration=True)
expected = """Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xhtml_as_fragment(self):
root = self._parse(_SIMPLE_XHTML)
from meld3 import doctype
actual = self._write_xhtml(root, fragment=True)
expected = """Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xhtml_with_doctype(self):
root = self._parse(_SIMPLE_XHTML)
from meld3 import doctype
actual = self._write_xhtml(root, doctype=doctype.xhtml)
expected = """Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xhtml_doctype_nodeclaration(self):
root = self._parse(_SIMPLE_XHTML)
from meld3 import doctype
actual = self._write_xhtml(root, declaration=False,
doctype=doctype.xhtml)
expected = """Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xhtml_fragment_kills_doctype_and_declaration(self):
root = self._parse(_SIMPLE_XHTML)
from meld3 import doctype
actual = self._write_xhtml(root, declaration=True,
doctype=doctype.xhtml, fragment=True)
expected = """Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xhtml_as_html_fragment(self):
root = self._parse(_SIMPLE_XHTML)
from meld3 import doctype
actual = self._write_html(root, fragment=True)
expected = """Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xhtml_with_doctype_as_html(self):
root = self._parse(_SIMPLE_XHTML)
actual = self._write_html(root)
expected = """
Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_simple_xhtml_as_html_new_doctype(self):
root = self._parse(_SIMPLE_XHTML)
from meld3 import doctype
actual = self._write_html(root, doctype=doctype.html_strict)
expected = """
Hello!"""
self.assertNormalizedXMLEqual(actual, expected)
def test_write_entities_xhtml_no_doctype(self):
root = self._parse_html(_ENTITIES_XHTML)
# this will be considered an XHTML document by default; we needn't
# declare a doctype
actual = self._write_xhtml(root)
expected =r"""
"""
def test_write_entities_xhtml_with_doctype(self):
dt = ''
root = self._parse_html(dt + _ENTITIES_XHTML)
actual = self._write_xhtml(root)
expected =r"""
"""
def test_unknown_entity(self):
# exception thrown may vary by python or expat version
from xml.parsers import expat
self.assertRaises((expat.error, SyntaxError), self._parse,
'&fleeb;')
def test_content_nostructure(self):
root = self._parse(_SIMPLE_XML)
D = root.findmeld('description')
D.content('description &&', structure=False)
actual = self._write_xml(root)
expected = """
-
Name
description &<foo>&<bar>
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_content_structure(self):
root = self._parse(_SIMPLE_XML)
D = root.findmeld('description')
D.content('description & ', structure=True)
actual = self._write_xml(root)
expected = """
-
Name
description &
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_replace_nostructure(self):
root = self._parse(_SIMPLE_XML)
D = root.findmeld('description')
D.replace('description &&', structure=False)
actual = self._write_xml(root)
expected = """
-
Name
description &<foo>&<bar>
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_replace_structure(self):
root = self._parse(_SIMPLE_XML)
D = root.findmeld('description')
D.replace('description & ', structure=True)
actual = self._write_xml(root)
expected = """
-
Name
description &
"""
self.assertNormalizedXMLEqual(actual, expected)
def test_escape_cdata(self):
a = '< > <& &' && &foo "" http://www.plope.com?foo=bar&bang=baz {'
from meld3 import _escape_cdata
self.assertEqual('< > <& &' && &foo "" http://www.plope.com?foo=bar&bang=baz {', _escape_cdata(a))
def test_escape_attrib(self):
a = '< > <& &' && &foo "" http://www.plope.com?foo=bar&bang=baz {'
from meld3 import _escape_attrib
self.assertEqual('< > <& &' && &foo "" http://www.plope.com?foo=bar&bang=baz {', _escape_attrib(a))
def normalize_html(s):
s = re.sub(r"[ \t]+", " ", s)
s = re.sub(r"/>", ">", s)
return s
def normalize_xml(s):
s = re.sub(r"\s+", " ", s)
s = re.sub(r"(?s)\s+<", "<", s)
s = re.sub(r"(?s)>\s+", ">", s)
return s
def test_suite():
suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( MeldAPITests ) )
suite.addTest( unittest.makeSuite( MeldElementInterfaceTests ) )
suite.addTest( unittest.makeSuite( ParserTests ) )
suite.addTest( unittest.makeSuite( WriterTests) )
suite.addTest( unittest.makeSuite( UtilTests) )
return suite
def main():
unittest.main(defaultTest='test_suite')
if __name__ == '__main__':
main()