diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py
index 1663b1f1143ddc..99c94ff14ca36d 100644
--- a/Lib/test/test_minidom.py
+++ b/Lib/test/test_minidom.py
@@ -1,16 +1,20 @@
-# test for xml.dom.minidom
+"""Tests for xml.dom.minidom."""
import copy
-import pickle
import io
+import pickle
from test import support
import unittest
+from xml.dom.minicompat import NodeList
import xml.dom.minidom
-
-from xml.dom.minidom import parse, Node, Document, parseString
+from xml.dom.minidom import Document
+from xml.dom.minidom import DocumentFragment
+from xml.dom.minidom import Element
from xml.dom.minidom import getDOMImplementation
-
+from xml.dom.minidom import Node
+from xml.dom.minidom import parse
+from xml.dom.minidom import parseString
tstfile = support.findfile("test.xml", subdir="xmltestdata")
sample = ("\n"
@@ -81,49 +85,6 @@ def testGetElementsByTagName(self):
dom.documentElement.getElementsByTagName("LI"))
dom.unlink()
- def testInsertBefore(self):
- dom = parseString("")
- root = dom.documentElement
- elem = root.childNodes[0]
- nelem = dom.createElement("element")
- root.insertBefore(nelem, elem)
- self.confirm(len(root.childNodes) == 2
- and root.childNodes.length == 2
- and root.childNodes[0] is nelem
- and root.childNodes.item(0) is nelem
- and root.childNodes[1] is elem
- and root.childNodes.item(1) is elem
- and root.firstChild is nelem
- and root.lastChild is elem
- and root.toxml() == ""
- , "testInsertBefore -- node properly placed in tree")
- nelem = dom.createElement("element")
- root.insertBefore(nelem, None)
- self.confirm(len(root.childNodes) == 3
- and root.childNodes.length == 3
- and root.childNodes[1] is elem
- and root.childNodes.item(1) is elem
- and root.childNodes[2] is nelem
- and root.childNodes.item(2) is nelem
- and root.lastChild is nelem
- and nelem.previousSibling is elem
- and root.toxml() == ""
- , "testInsertBefore -- node properly placed in tree")
- nelem2 = dom.createElement("bar")
- root.insertBefore(nelem2, nelem)
- self.confirm(len(root.childNodes) == 4
- and root.childNodes.length == 4
- and root.childNodes[2] is nelem2
- and root.childNodes.item(2) is nelem2
- and root.childNodes[3] is nelem
- and root.childNodes.item(3) is nelem
- and nelem2.nextSibling is nelem
- and nelem.previousSibling is nelem2
- and root.toxml() ==
- ""
- , "testInsertBefore -- node properly placed in tree")
- dom.unlink()
-
def _create_fragment_test_nodes(self):
dom = parseString("")
orig = dom.createTextNode("original")
@@ -137,48 +98,6 @@ def _create_fragment_test_nodes(self):
frag.appendChild(c3)
return dom, orig, c1, c2, c3, frag
- def testInsertBeforeFragment(self):
- dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
- dom.documentElement.insertBefore(frag, None)
- self.confirm(tuple(dom.documentElement.childNodes) ==
- (orig, c1, c2, c3),
- "insertBefore(, None)")
- frag.unlink()
- dom.unlink()
-
- dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
- dom.documentElement.insertBefore(frag, orig)
- self.confirm(tuple(dom.documentElement.childNodes) ==
- (c1, c2, c3, orig),
- "insertBefore(, orig)")
- frag.unlink()
- dom.unlink()
-
- def testAppendChild(self):
- dom = parse(tstfile)
- dom.documentElement.appendChild(dom.createComment("Hello"))
- self.confirm(dom.documentElement.childNodes[-1].nodeName == "#comment")
- self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
- dom.unlink()
-
- def testAppendChildFragment(self):
- dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
- dom.documentElement.appendChild(frag)
- self.confirm(tuple(dom.documentElement.childNodes) ==
- (orig, c1, c2, c3),
- "appendChild()")
- frag.unlink()
- dom.unlink()
-
- def testReplaceChildFragment(self):
- dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
- dom.documentElement.replaceChild(frag, orig)
- orig.unlink()
- self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3),
- "replaceChild()")
- frag.unlink()
- dom.unlink()
-
def testLegalChildren(self):
dom = Document()
elem = dom.createElement('element')
@@ -380,15 +299,6 @@ def testChangeAttr(self):
and el.getAttribute("spam2") == "bam2")
dom.unlink()
- def testGetAttrList(self):
- pass
-
- def testGetAttrValues(self):
- pass
-
- def testGetAttrLength(self):
- pass
-
def testGetAttribute(self):
dom = Document()
child = dom.appendChild(
@@ -409,8 +319,6 @@ def testGetAttributeNS(self):
self.assertEqual(child2.getAttributeNS("http://www.python.org", "missing"),
'')
- def testGetAttributeNode(self): pass
-
def testGetElementsByTagNameNS(self):
d="""
@@ -481,8 +389,6 @@ def testAttributeRepr(self):
self.confirm(str(node) == repr(node))
dom.unlink()
- def testTextNodeRepr(self): pass
-
def testWriteXML(self):
str = ''
dom = parseString(str)
@@ -546,14 +452,6 @@ def testProcessingInstruction(self):
and pi.localName is None
and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE)
- def testProcessingInstructionRepr(self): pass
-
- def testTextRepr(self): pass
-
- def testWriteText(self): pass
-
- def testDocumentElement(self): pass
-
def testTooManyDocumentElements(self):
doc = parseString("")
elem = doc.createElement("extra")
@@ -562,26 +460,6 @@ def testTooManyDocumentElements(self):
elem.unlink()
doc.unlink()
- def testCreateElementNS(self): pass
-
- def testCreateAttributeNS(self): pass
-
- def testParse(self): pass
-
- def testParseString(self): pass
-
- def testComment(self): pass
-
- def testAttrListItem(self): pass
-
- def testAttrListItems(self): pass
-
- def testAttrListItemNS(self): pass
-
- def testAttrListKeys(self): pass
-
- def testAttrListKeysNS(self): pass
-
def testRemoveNamedItem(self):
doc = parseString("")
e = doc.documentElement
@@ -601,88 +479,6 @@ def testRemoveNamedItemNS(self):
self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS,
"http://xml.python.org/", "b")
- def testAttrListValues(self): pass
-
- def testAttrListLength(self): pass
-
- def testAttrList__getitem__(self): pass
-
- def testAttrList__setitem__(self): pass
-
- def testSetAttrValueandNodeValue(self): pass
-
- def testParseElement(self): pass
-
- def testParseAttributes(self): pass
-
- def testParseElementNamespaces(self): pass
-
- def testParseAttributeNamespaces(self): pass
-
- def testParseProcessingInstructions(self): pass
-
- def testChildNodes(self): pass
-
- def testFirstChild(self): pass
-
- def testHasChildNodes(self):
- dom = parseString("")
- doc = dom.documentElement
- self.assertTrue(doc.hasChildNodes())
- dom2 = parseString("")
- doc2 = dom2.documentElement
- self.assertFalse(doc2.hasChildNodes())
-
- def _testCloneElementCopiesAttributes(self, e1, e2, test):
- attrs1 = e1.attributes
- attrs2 = e2.attributes
- keys1 = list(attrs1.keys())
- keys2 = list(attrs2.keys())
- keys1.sort()
- keys2.sort()
- self.confirm(keys1 == keys2, "clone of element has same attribute keys")
- for i in range(len(keys1)):
- a1 = attrs1.item(i)
- a2 = attrs2.item(i)
- self.confirm(a1 is not a2
- and a1.value == a2.value
- and a1.nodeValue == a2.nodeValue
- and a1.namespaceURI == a2.namespaceURI
- and a1.localName == a2.localName
- , "clone of attribute node has proper attribute values")
- self.confirm(a2.ownerElement is e2,
- "clone of attribute node correctly owned")
-
- def _setupCloneElement(self, deep):
- dom = parseString("")
- root = dom.documentElement
- clone = root.cloneNode(deep)
- self._testCloneElementCopiesAttributes(
- root, clone, "testCloneElement" + (deep and "Deep" or "Shallow"))
- # mutilate the original so shared data is detected
- root.tagName = root.nodeName = "MODIFIED"
- root.setAttribute("attr", "NEW VALUE")
- root.setAttribute("added", "VALUE")
- return dom, clone
-
- def testCloneElementShallow(self):
- dom, clone = self._setupCloneElement(0)
- self.confirm(len(clone.childNodes) == 0
- and clone.childNodes.length == 0
- and clone.parentNode is None
- and clone.toxml() == ''
- , "testCloneElementShallow")
- dom.unlink()
-
- def testCloneElementDeep(self):
- dom, clone = self._setupCloneElement(1)
- self.confirm(len(clone.childNodes) == 1
- and clone.childNodes.length == 1
- and clone.parentNode is None
- and clone.toxml() == ''
- , "testCloneElementDeep")
- dom.unlink()
-
def testCloneDocumentShallow(self):
doc = parseString("\n"
""
@@ -888,208 +684,6 @@ def testCloneNodeEntity(self):
self.check_clone_node_entity(False)
self.check_clone_node_entity(True)
- def testNormalize(self):
- doc = parseString("")
- root = doc.documentElement
- root.appendChild(doc.createTextNode("first"))
- root.appendChild(doc.createTextNode("second"))
- self.confirm(len(root.childNodes) == 2
- and root.childNodes.length == 2,
- "testNormalize -- preparation")
- doc.normalize()
- self.confirm(len(root.childNodes) == 1
- and root.childNodes.length == 1
- and root.firstChild is root.lastChild
- and root.firstChild.data == "firstsecond"
- , "testNormalize -- result")
- doc.unlink()
-
- doc = parseString("")
- root = doc.documentElement
- root.appendChild(doc.createTextNode(""))
- doc.normalize()
- self.confirm(len(root.childNodes) == 0
- and root.childNodes.length == 0,
- "testNormalize -- single empty node removed")
- doc.unlink()
-
- def testNormalizeCombineAndNextSibling(self):
- doc = parseString("")
- root = doc.documentElement
- root.appendChild(doc.createTextNode("first"))
- root.appendChild(doc.createTextNode("second"))
- root.appendChild(doc.createElement("i"))
- self.confirm(len(root.childNodes) == 3
- and root.childNodes.length == 3,
- "testNormalizeCombineAndNextSibling -- preparation")
- doc.normalize()
- self.confirm(len(root.childNodes) == 2
- and root.childNodes.length == 2
- and root.firstChild.data == "firstsecond"
- and root.firstChild is not root.lastChild
- and root.firstChild.nextSibling is root.lastChild
- and root.firstChild.previousSibling is None
- and root.lastChild.previousSibling is root.firstChild
- and root.lastChild.nextSibling is None
- , "testNormalizeCombinedAndNextSibling -- result")
- doc.unlink()
-
- def testNormalizeDeleteWithPrevSibling(self):
- doc = parseString("")
- root = doc.documentElement
- root.appendChild(doc.createTextNode("first"))
- root.appendChild(doc.createTextNode(""))
- self.confirm(len(root.childNodes) == 2
- and root.childNodes.length == 2,
- "testNormalizeDeleteWithPrevSibling -- preparation")
- doc.normalize()
- self.confirm(len(root.childNodes) == 1
- and root.childNodes.length == 1
- and root.firstChild.data == "first"
- and root.firstChild is root.lastChild
- and root.firstChild.nextSibling is None
- and root.firstChild.previousSibling is None
- , "testNormalizeDeleteWithPrevSibling -- result")
- doc.unlink()
-
- def testNormalizeDeleteWithNextSibling(self):
- doc = parseString("")
- root = doc.documentElement
- root.appendChild(doc.createTextNode(""))
- root.appendChild(doc.createTextNode("second"))
- self.confirm(len(root.childNodes) == 2
- and root.childNodes.length == 2,
- "testNormalizeDeleteWithNextSibling -- preparation")
- doc.normalize()
- self.confirm(len(root.childNodes) == 1
- and root.childNodes.length == 1
- and root.firstChild.data == "second"
- and root.firstChild is root.lastChild
- and root.firstChild.nextSibling is None
- and root.firstChild.previousSibling is None
- , "testNormalizeDeleteWithNextSibling -- result")
- doc.unlink()
-
- def testNormalizeDeleteWithTwoNonTextSiblings(self):
- doc = parseString("")
- root = doc.documentElement
- root.appendChild(doc.createElement("i"))
- root.appendChild(doc.createTextNode(""))
- root.appendChild(doc.createElement("i"))
- self.confirm(len(root.childNodes) == 3
- and root.childNodes.length == 3,
- "testNormalizeDeleteWithTwoSiblings -- preparation")
- doc.normalize()
- self.confirm(len(root.childNodes) == 2
- and root.childNodes.length == 2
- and root.firstChild is not root.lastChild
- and root.firstChild.nextSibling is root.lastChild
- and root.firstChild.previousSibling is None
- and root.lastChild.previousSibling is root.firstChild
- and root.lastChild.nextSibling is None
- , "testNormalizeDeleteWithTwoSiblings -- result")
- doc.unlink()
-
- def testNormalizeDeleteAndCombine(self):
- doc = parseString("")
- root = doc.documentElement
- root.appendChild(doc.createTextNode(""))
- root.appendChild(doc.createTextNode("second"))
- root.appendChild(doc.createTextNode(""))
- root.appendChild(doc.createTextNode("fourth"))
- root.appendChild(doc.createTextNode(""))
- self.confirm(len(root.childNodes) == 5
- and root.childNodes.length == 5,
- "testNormalizeDeleteAndCombine -- preparation")
- doc.normalize()
- self.confirm(len(root.childNodes) == 1
- and root.childNodes.length == 1
- and root.firstChild is root.lastChild
- and root.firstChild.data == "secondfourth"
- and root.firstChild.previousSibling is None
- and root.firstChild.nextSibling is None
- , "testNormalizeDeleteAndCombine -- result")
- doc.unlink()
-
- def testNormalizeRecursion(self):
- doc = parseString(""
- ""
- ""
- "t"
- #
- #x
- ""
- ""
- ""
- "t2"
- #x2
- ""
- "t3"
- #x3
- ""
- #
- "")
- root = doc.documentElement
- root.childNodes[0].appendChild(doc.createTextNode(""))
- root.childNodes[0].appendChild(doc.createTextNode("x"))
- root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2"))
- root.childNodes[1].appendChild(doc.createTextNode("x3"))
- root.appendChild(doc.createTextNode(""))
- self.confirm(len(root.childNodes) == 3
- and root.childNodes.length == 3
- and len(root.childNodes[0].childNodes) == 4
- and root.childNodes[0].childNodes.length == 4
- and len(root.childNodes[1].childNodes) == 3
- and root.childNodes[1].childNodes.length == 3
- and len(root.childNodes[1].childNodes[0].childNodes) == 2
- and root.childNodes[1].childNodes[0].childNodes.length == 2
- , "testNormalize2 -- preparation")
- doc.normalize()
- self.confirm(len(root.childNodes) == 2
- and root.childNodes.length == 2
- and len(root.childNodes[0].childNodes) == 2
- and root.childNodes[0].childNodes.length == 2
- and len(root.childNodes[1].childNodes) == 2
- and root.childNodes[1].childNodes.length == 2
- and len(root.childNodes[1].childNodes[0].childNodes) == 1
- and root.childNodes[1].childNodes[0].childNodes.length == 1
- , "testNormalize2 -- childNodes lengths")
- self.confirm(root.childNodes[0].childNodes[1].data == "tx"
- and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2"
- and root.childNodes[1].childNodes[1].data == "t3x3"
- , "testNormalize2 -- joined text fields")
- self.confirm(root.childNodes[0].childNodes[1].nextSibling is None
- and root.childNodes[0].childNodes[1].previousSibling
- is root.childNodes[0].childNodes[0]
- and root.childNodes[0].childNodes[0].previousSibling is None
- and root.childNodes[0].childNodes[0].nextSibling
- is root.childNodes[0].childNodes[1]
- and root.childNodes[1].childNodes[1].nextSibling is None
- and root.childNodes[1].childNodes[1].previousSibling
- is root.childNodes[1].childNodes[0]
- and root.childNodes[1].childNodes[0].previousSibling is None
- and root.childNodes[1].childNodes[0].nextSibling
- is root.childNodes[1].childNodes[1]
- , "testNormalize2 -- sibling pointers")
- doc.unlink()
-
-
- def testBug0777884(self):
- doc = parseString("text")
- text = doc.documentElement.childNodes[0]
- self.assertEqual(text.nodeType, Node.TEXT_NODE)
- # Should run quietly, doing nothing.
- text.normalize()
- doc.unlink()
-
- def testBug1433694(self):
- doc = parseString("t")
- node = doc.documentElement
- node.childNodes[1].nodeValue = ""
- node.normalize()
- self.confirm(node.childNodes[-1].nextSibling is None,
- "Final child's .nextSibling should be None")
-
def testSiblings(self):
doc = parseString("text?")
root = doc.documentElement
@@ -1373,15 +967,6 @@ def testWholeText(self):
self.checkWholeText(text, "cabd")
self.checkWholeText(text2, "cabd")
- def testPatch1094164(self):
- doc = parseString("")
- elem = doc.documentElement
- e = elem.firstChild
- self.confirm(e.parentNode is elem, "Before replaceChild()")
- # Check that replacing a child with itself leaves the tree unchanged
- elem.replaceChild(e, e)
- self.confirm(e.parentNode is elem, "After replaceChild()")
-
def testReplaceWholeText(self):
def setup():
doc = parseString("ad")
@@ -1612,15 +1197,6 @@ def testExceptionOnSpacesInXMLNSValue(self):
with self.assertRaisesRegex(ValueError, 'Unsupported syntax'):
parseString('')
- def testDocRemoveChild(self):
- doc = parse(tstfile)
- title_tag = doc.documentElement.getElementsByTagName("TITLE")[0]
- self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag)
- num_children_before = len(doc.childNodes)
- doc.removeChild(doc.childNodes[0])
- num_children_after = len(doc.childNodes)
- self.assertTrue(num_children_after == num_children_before - 1)
-
def testProcessingInstructionNameError(self):
# wrong variable in .nodeValue property will
# lead to "NameError: name 'data' is not defined"
@@ -1663,5 +1239,441 @@ def test_cdata_parsing(self):
dom2 = parseString(dom1.toprettyxml())
self.checkWholeText(dom2.getElementsByTagName('node')[0].firstChild, '')
+ def test_toxml(self):
+ # simple test
+ xml_str = 'text
'
+ root = parseString(xml_str)
+ self.assertEqual(root.toxml(), 'text
')
+ self.assertEqual(
+ root.toxml(standalone=True),
+ 'text
')
+ self.assertEqual(
+ root.toxml(standalone=False),
+ 'text
')
+ self.assertEqual(
+ root.toxml(encoding="utf-8"),
+ b'text
')
+
+ def test_hasChildNodes(self):
+ """Test if a node has children."""
+ dom = parseString("")
+ doc = dom.documentElement
+ self.assertTrue(doc.hasChildNodes())
+ dom2 = parseString("")
+ doc2 = dom2.documentElement
+ self.assertFalse(doc2.hasChildNodes())
+
+ def test_childNodes(self):
+ """Test the list of children Nodes of a DOM Element."""
+ dom = parseString('')
+ doc = dom.documentElement
+ self.assertEqual(len(doc.childNodes), 2)
+ self.assertIsInstance(doc.childNodes[0], Element)
+ self.assertIsInstance(doc.childNodes, NodeList)
+
+ def test_firstChild(self):
+ """Test access to the first child of a DOM Element."""
+ dom = parseString('')
+ doc = dom.documentElement
+ self.assertEqual(doc.firstChild.toxml(), '
')
+ self.assertIsInstance(doc.firstChild, Element)
+ dom = parseString('')
+ doc = dom.documentElement
+ self.assertIsNone(doc.firstChild)
+
+ def test_lastChild(self):
+ """Test access to the last child of a DOM Element."""
+ dom = parseString('')
+ doc = dom.documentElement
+ self.assertEqual(doc.lastChild.toxml(), '
')
+ self.assertIsInstance(doc.lastChild, Element)
+ dom = parseString('')
+ doc = dom.documentElement
+ self.assertIsNone(doc.lastChild)
+
+ def test_insertBefore_with_document_fragment_node(self):
+ """Test insertBefore for DOCUMENT_FRAGMENT_NODE."""
+ # Preparing the test
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ newNode = dom.createElement('new')
+ fragment = DocumentFragment()
+ fragment.appendChild(newNode)
+ # insertBefore with document fragment
+ parentNode.insertBefore(fragment, existingNode)
+ self.assertEqual(parentNode.toxml(),
+ '')
+ self.assertIs(parentNode.firstChild, newNode)
+ self.assertEqual(parentNode.childNodes.length, 2)
+ # after the insert the fragment is empty
+ self.assertEqual(fragment.childNodes.length, 0)
+
+ def test_insertBefore_with_invalid_node_type(self):
+ """Test insertBefore with invalid node type."""
+ # Preparing the test
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ doc = getDOMImplementation().createDocument(None, "doc", None)
+ # parentNode.insertBefore(doc, existingNode) will raise
+ self.assertRaises(xml.dom.HierarchyRequestErr,
+ parentNode.insertBefore, doc, existingNode)
+
+ def test_insertBefore_with_text_node(self):
+ """Test insertBefore for text node."""
+ # Preparing the test
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ newNode = dom.createTextNode('new text')
+ # insertBefore with text node
+ parentNode.insertBefore(newNode, existingNode)
+ self.assertEqual(parentNode.toxml(),
+ 'new text')
+
+ def test_insertBefore_with_no_reference_node(self):
+ """Test insertBefore with no reference node."""
+ # Preparing the test
+ dom = parseString('')
+ parentNode = dom.documentElement
+ newNode = dom.createTextNode('text trailing')
+ # insertBefore with no reference node aka None
+ parentNode.insertBefore(newNode, None)
+ self.assertEqual(parentNode.toxml(),
+ 'text trailing')
+
+ def test_insertBefore_with_no_children(self):
+ """Test insertBefore with no children node."""
+ # Preparing the test
+ dom = parseString('')
+ parentNode = dom.documentElement
+ newNode = dom.createElement('new')
+ existingNode = dom.createElement('existing')
+ # insertBefore with existing node not existing
+ self.assertRaises(xml.dom.NotFoundErr,
+ parentNode.insertBefore, newNode, existingNode)
+
+ def test_insertBefore_return_value(self):
+ """Test insertBefore returned value."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ newNode = dom.createElement('new')
+ existingNode = parentNode.firstChild
+ return_value = parentNode.insertBefore(newNode, existingNode)
+ self.assertEqual(return_value, newNode)
+
+ def test_appendChild(self):
+ """Test appendChild for a simple node."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ newNode = dom.createElement('new')
+ # append a new node child
+ parentNode.appendChild(newNode)
+ self.assertEqual(parentNode.toxml(),
+ '')
+
+ def test_appendChild_with_document_fragment_node(self):
+ """Test appendChild for DOCUMENT_FRAGMENT_NODE."""
+ # Preparing the test
+ dom = parseString('')
+ parentNode = dom.documentElement
+ newNode = dom.createElement('new')
+ fragment = DocumentFragment()
+ fragment.appendChild(newNode)
+ # appendChild with document fragment
+ parentNode.appendChild(fragment)
+ self.assertEqual(parentNode.toxml(),
+ '')
+ # after the appendChild, the fragment should be empty
+ self.assertEqual(fragment.childNodes.length, 0)
+
+ def test_appendChild_with_invalid_node_type(self):
+ """Test appendChild with invalid node type."""
+ # Preparing the test
+ dom = parseString('')
+ parentNode = dom.documentElement
+ doc = getDOMImplementation().createDocument(None, "doc", None)
+ # parentNode.appendChild(doc) will raise
+ self.assertRaises(xml.dom.HierarchyRequestErr,
+ parentNode.appendChild, doc)
+
+ def test_appendChild_return_value(self):
+ """Test appendChild returned value."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ newNode = dom.createElement('new')
+ # append a new node child
+ return_value = parentNode.appendChild(newNode)
+ self.assertEqual(return_value, newNode)
+
+ def test_replaceChild(self):
+ """Test replaceChild for a simple node."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ newNode = dom.createElement('new')
+ # replace existingNode by newNode
+ parentNode.replaceChild(newNode, existingNode)
+ self.assertEqual(parentNode.toxml(), '')
+
+ def test_replaceChild_with_document_fragment_node(self):
+ """Test replaceChild for DOCUMENT_FRAGMENT_NODE."""
+ # Preparing the test
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ newNode = dom.createElement('new')
+ fragment = DocumentFragment()
+ fragment.appendChild(newNode)
+ # replaceChild with document fragment
+ parentNode.replaceChild(fragment, existingNode)
+ self.assertEqual(parentNode.toxml(), '')
+ # after the replaceChild, the fragment should be empty
+ self.assertEqual(fragment.childNodes.length, 0)
+
+ def test_replaceChild_with_invalid_node_type(self):
+ """Test replaceChild with invalid node type."""
+ # Preparing the test
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ doc = getDOMImplementation().createDocument(None, "doc", None)
+ # parentNode.replaceChild(doc, existingNode) will raise
+ self.assertRaises(xml.dom.HierarchyRequestErr,
+ parentNode.replaceChild, doc, existingNode)
+
+ def test_replaceChild_with_same_node(self):
+ """Test replaceChild with same node."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ # replace existingNode by existingNode
+ parentNode.replaceChild(existingNode, existingNode)
+ self.assertEqual(parentNode.toxml(), '')
+
+ def test_replaceChild_with_new_child_parent_not_None(self):
+ """Test replaceChild with new child parent is not Nonce."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ newdom = parseString('')
+ newparentNode = newdom.documentElement
+ newNode = newparentNode.firstChild
+ # replace existingNode by newNode
+ parentNode.replaceChild(newNode, existingNode)
+ self.assertEqual(parentNode.toxml(), '')
+
+ def test_replaceChild_with_no_existing_node(self):
+ """Test replaceChild with missing existing node."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = dom.createElement('existing')
+ newdom = parseString('')
+ newparentNode = newdom.documentElement
+ newNode = newparentNode.firstChild
+ # replace existingNode by newNode
+ self.assertRaises(xml.dom.NotFoundErr,
+ parentNode.replaceChild, newNode, existingNode)
+
+ def test_replaceChild_return_value(self):
+ """Test replaceChild returned value."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ newNode = dom.createElement('new')
+ # replace a new node child
+ return_value = parentNode.replaceChild(newNode, existingNode)
+ self.assertEqual(return_value, existingNode)
+
+ def test_removeChild(self):
+ """Test removeChild for a simple node."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = parentNode.firstChild
+ # remove an existing node child
+ parentNode.removeChild(existingNode)
+ self.assertEqual(parentNode.toxml(), '')
+
+ def test_removeChild_with_no_existing_node(self):
+ """Test removeChild with missing existing node."""
+ dom = parseString('')
+ parentNode = dom.documentElement
+ existingNode = dom.createElement('existing')
+ # remove non existing Node
+ self.assertRaises(xml.dom.NotFoundErr,
+ parentNode.removeChild, existingNode)
+
+ def test_normalize_text_nodes(self):
+ """Test normalize two text nodes."""
+ doc = parseString("")
+ root = doc.documentElement
+ root.appendChild(doc.createTextNode("first"))
+ root.appendChild(doc.createTextNode("second"))
+ # Two text nodes have been created
+ # len(root.childNodes) == 2
+ # root.firstChild.data == 'first'
+ doc.normalize()
+ self.assertEqual(len(root.childNodes), 1)
+ self.assertEqual(root.firstChild.data, 'firstsecond')
+ self.assertEqual(root.toxml(), 'firstsecond')
+
+
+ def test_normalize_empty_text_node(self):
+ """Test normalize removing the childNode of an empty text node."""
+ doc = parseString("")
+ root = doc.documentElement
+ root.appendChild(doc.createTextNode(""))
+ # len(root.childNodes) == 1
+ # root.firstChild.data == ''
+ doc.normalize()
+ self.assertEqual(len(root.childNodes), 0)
+ self.assertIs(root.firstChild, None)
+ self.assertEqual(root.toxml(), '')
+
+
+ def test_normalize_text_node_and_element_node(self):
+ """Test normalize merges two text nodes but not element node."""
+ doc = parseString("")
+ root = doc.documentElement
+ root.appendChild(doc.createTextNode("first"))
+ root.appendChild(doc.createTextNode("second"))
+ root.appendChild(doc.createElement("element"))
+ # len(root.childNodes) == 3
+ # root.firstChild.data == 'first'
+ doc.normalize()
+ self.assertEqual(len(root.childNodes), 2)
+ self.assertEqual(root.firstChild.data, 'firstsecond')
+ self.assertEqual(root.toxml(), 'firstsecond')
+
+ def test_normalize_two_text_nodes_with_first_empty(self):
+ """Test that normalize removes the first empty text node only."""
+ doc = parseString("")
+ root = doc.documentElement
+ root.appendChild(doc.createTextNode(""))
+ root.appendChild(doc.createTextNode("second"))
+ # Two text nodes have been created
+ # len(root.childNodes) == 2
+ # root.firstChild.data == ''
+ # root.lastChild.data == 'second'
+ doc.normalize()
+ self.assertEqual(len(root.childNodes), 1)
+ self.assertEqual(root.firstChild.data, 'second')
+ self.assertEqual(root.toxml(), 'second')
+
+ def test_normalize_two_text_nodes_with_last_empty(self):
+ """Test that normalize removes the last empty text node only."""
+ doc = parseString("")
+ root = doc.documentElement
+ root.appendChild(doc.createTextNode("first"))
+ root.appendChild(doc.createTextNode(""))
+ # Two text nodes have been created
+ # len(root.childNodes) == 2
+ # root.firstChild.data == 'first'
+ # root.lastChild.data == ''
+ doc.normalize()
+ self.assertEqual(len(root.childNodes), 1)
+ self.assertEqual(root.firstChild.data, 'first')
+ self.assertEqual(root.toxml(), 'first')
+
+ def test_normalize_two_element_nodes_with_empty_text_node(self):
+ """Test that normalize removes the empty text node in between."""
+ doc = parseString("")
+ root = doc.documentElement
+ root.appendChild(doc.createElement('first'))
+ root.appendChild(doc.createTextNode(''))
+ root.appendChild(doc.createElement('second'))
+ # Two text nodes have been created
+ # len(root.childNodes) == 3
+ # root.toxml() == ''
+ doc.normalize()
+ self.assertEqual(len(root.childNodes), 2)
+ self.assertEqual(root.toxml(), '')
+
+ def text_normalize_nested_elements_with_text_nodes(self):
+ """Test that normalize is still working in a more complex tree."""
+ xml = 'texttext'
+ doc = parseString(xml)
+ root = doc.documentElement
+ root.childNodes[0].appendChild(doc.createTextNode('added'))
+ root.childNodes[1].appendChild(doc.createTextNode(''))
+ doc.normalize()
+ self.assertEqual(len(root.childNodes[0]), 1)
+ self.assertEqual(len(root.childNodes[1]), 1)
+ self.assertEqual(
+ root.toxml(),
+ 'textaddedtext')
+
+ def test_normalize_on_text_node(self):
+ """Test that normalize on a text Node doesn't fail.
+
+ See https://bugs.python.org/issue777884
+ """
+ doc = parseString("text")
+ text = doc.documentElement.childNodes[0]
+ self.assertEqual(text.nodeType, Node.TEXT_NODE)
+ # Should run quietly, doing nothing.
+ text.normalize()
+
+ def test_normalize_unlink_child_node(self):
+ """Test that normalize unlinks empty child nodes.
+
+ See https://bugs.python.org/issue1433694
+ """
+ doc = parseString("text")
+ root = doc.documentElement
+ root.childNodes[1].nodeValue = ''
+ doc.normalize()
+ # Final child's .nextSibling should be None
+ self.assertIsNone(root.childNodes[-1].nextSibling)
+
+ def test_cloneNode_with_simple_element(self):
+ """Test cloneNode for a simple Element Node."""
+ doc = parseString("")
+ root = doc.documentElement
+ new_clone = root.cloneNode(deep=True)
+ self.assertEqual(new_clone.toxml(), root.toxml())
+ self.assertEqual(new_clone.nodeType, root.nodeType)
+ new_clone = root.cloneNode(deep=False)
+ self.assertEqual(new_clone.toxml(), '')
+
+ def test_cloneNode_with_text_node(self):
+ """Test cloneNode with text node."""
+ doc = parseString("text")
+ root = doc.documentElement
+ new_clone = root.cloneNode(deep=True)
+ self.assertEqual(new_clone.toxml(), root.toxml())
+ self.assertEqual(new_clone.nodeType, root.nodeType)
+ new_clone = root.cloneNode(deep=False)
+ self.assertEqual(new_clone.toxml(), '')
+
+ def test_cloneNode_with_text_and_element_nodes(self):
+ """Test cloneNode with text and element nodes."""
+ doc = parseString("text")
+ root = doc.documentElement
+ new_clone = root.cloneNode(deep=True)
+ self.assertEqual(new_clone.toxml(), root.toxml())
+ self.assertEqual(new_clone.nodeType, root.nodeType)
+ new_clone = root.cloneNode(deep=False)
+ self.assertEqual(new_clone.toxml(), '')
+
+ def test_cloneNode_with_element_node_with_attribute(self):
+ """Test cloneNode with element nodes with attribute."""
+ doc = parseString("")
+ root = doc.documentElement
+ new_clone = root.cloneNode(deep=True)
+ self.assertEqual(new_clone.toxml(), root.toxml())
+ self.assertEqual(new_clone.nodeType, root.nodeType)
+ new_clone = root.cloneNode(deep=False)
+ self.assertEqual(new_clone.toxml(), '')
+
+ def test_cloneNode_parentNode_None(self):
+ """Test cloneNode that the parent node is None."""
+ doc = parseString("")
+ root = doc.documentElement
+ new_clone = root.firstChild.cloneNode(deep=True)
+ self.assertEqual(new_clone.toxml(), '')
+ self.assertIsNone(new_clone.parentNode)
+
if __name__ == "__main__":
unittest.main()
diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py
index d09ef5e7d0371a..916c0503b3d39b 100644
--- a/Lib/xml/dom/minidom.py
+++ b/Lib/xml/dom/minidom.py
@@ -32,6 +32,7 @@
class Node(xml.dom.Node):
+ """Define properties accessible on a DOM node."""
namespaceURI = None # this is non-null only for elements and attributes
parentNode = None
ownerDocument = None
@@ -44,6 +45,7 @@ def __bool__(self):
return True
def toxml(self, encoding=None, standalone=None):
+ """Generate a string representation of a DOM."""
return self.toprettyxml("", "", encoding, standalone)
def toprettyxml(self, indent="\t", newl="\n", encoding=None,
@@ -66,6 +68,7 @@ def toprettyxml(self, indent="\t", newl="\n", encoding=None,
return writer.detach().getvalue()
def hasChildNodes(self):
+ """Return True if the node has children, False else."""
return bool(self.childNodes)
def _get_childNodes(self):
@@ -80,10 +83,17 @@ def _get_lastChild(self):
return self.childNodes[-1]
def insertBefore(self, newChild, refChild):
+ """Insert a new DOM Node before an existing Node.
+
+ https://dom.spec.whatwg.org/#dom-node-insertbefore
+ The insertBefore(node, child) method, when invoked,
+ must return the result of pre-inserting node into
+ this before child.
+ See also https://dom.spec.whatwg.org/#concept-node-pre-insert
+ """
if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
for c in tuple(newChild.childNodes):
self.insertBefore(c, refChild)
- ### The DOM does not clearly specify what to return in this case
return newChild
if newChild.nodeType not in self._child_node_types:
raise xml.dom.HierarchyRequestErr(
@@ -112,10 +122,10 @@ def insertBefore(self, newChild, refChild):
return newChild
def appendChild(self, node):
+ """Append a child node to an existing node."""
if node.nodeType == self.DOCUMENT_FRAGMENT_NODE:
for c in tuple(node.childNodes):
self.appendChild(c)
- ### The DOM does not clearly specify what to return in this case
return node
if node.nodeType not in self._child_node_types:
raise xml.dom.HierarchyRequestErr(
@@ -129,6 +139,7 @@ def appendChild(self, node):
return node
def replaceChild(self, newChild, oldChild):
+ """Replace an existing node with a new node."""
if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
refChild = oldChild.nextSibling
self.removeChild(oldChild)
@@ -161,6 +172,7 @@ def replaceChild(self, newChild, oldChild):
return oldChild
def removeChild(self, oldChild):
+ """Remove an existing child."""
try:
self.childNodes.remove(oldChild)
except ValueError:
@@ -172,11 +184,16 @@ def removeChild(self, oldChild):
oldChild.nextSibling = oldChild.previousSibling = None
if oldChild.nodeType in _nodeTypes_with_children:
_clear_id_cache(self)
-
oldChild.parentNode = None
return oldChild
def normalize(self):
+ """Transform a node into its normalized form.
+
+ Removes empty exclusive Text nodes and concatenates the data of
+ remaining contiguous exclusive Text nodes into the first of
+ their nodes.
+ """
L = []
for child in self.childNodes:
if child.nodeType == Node.TEXT_NODE:
@@ -204,6 +221,7 @@ def normalize(self):
self.childNodes[:] = L
def cloneNode(self, deep):
+ """The Node.cloneNode() method returns a duplicate of the node."""
return _clone_node(self, deep, self.ownerDocument or self)
def isSupported(self, feature, version):
@@ -864,7 +882,7 @@ def getElementsByTagNameNS(self, namespaceURI, localName):
self, namespaceURI, localName, NodeList())
def __repr__(self):
- return "" % (self.tagName, id(self))
+ return f""
def writexml(self, writer, indent="", addindent="", newl=""):
"""Write an XML element to a file-like object
@@ -1323,6 +1341,7 @@ def _get_internalSubset(self):
return self.internalSubset
def cloneNode(self, deep):
+ """The Node.cloneNode() method returns a duplicate of the node."""
if self.ownerDocument is None:
# it's ok
clone = DocumentType(None)
@@ -1886,7 +1905,9 @@ def renameNode(self, n, namespaceURI, name):
def _clone_node(node, deep, newOwnerDocument):
"""
- Clone a node and give it the new owner document.
+ Returns a copy of node.
+
+ If deep is true, the copy also includes the node’s descendants.
Called by Node.cloneNode and Document.importNode
"""
if node.ownerDocument.isSameNode(newOwnerDocument):