diff --git a/w3/python/core/fundamental_interface/Node.py b/w3/python/core/fundamental_interface/Node.py index ae27bb3..a9b347a 100644 --- a/w3/python/core/fundamental_interface/Node.py +++ b/w3/python/core/fundamental_interface/Node.py @@ -64,11 +64,10 @@ class Node: def __init__(self, node_type: NodeType, node_name: DOMString, + owner_document: Optional[_Document], node_value: Optional[DOMString] = None, - parent_node: Optional[_AnyNode] = None, child_nodes: Optional[Iterable[_AnyNode]] = None, attributes: Optional[Iterable[_AnyNode]] = None, - owner_document: Optional[_Document] = None, read_only: bool = False) -> None: if node_value is None: node_value = '' @@ -76,7 +75,7 @@ def __init__(self, self._set_node_type(node_type) self._set_node_name(node_name) self._set_node_value(node_value) - self._set_parent_node(parent_node) + self._set_parent_node(None) self._init_child_nodes(child_nodes) self._init_attributes(attributes) self._set_owner_document(owner_document) @@ -85,22 +84,12 @@ def __init__(self, self._node_type: NodeType self._node_name: DOMString self._node_value: DOMString - self._parent_node: _AnyNode + self._parent_node: Optional[_AnyNode] self._child_nodes: NodeList self._attributes: _NamedNodeMap - self._owner_document: _Document + self._owner_document: Optional[_Document] self._read_only: bool - def _check_modifiable(self) -> None: - """Checks if this node is modifiable. - - Raises: - DOMException: - - `NO_MODIFICATION_ALLOWED_ERR`: Raised when the node is readonly. - """ - if self._read_only: - raise DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR) - @property def node_name(self) -> DOMString: """Read only; The name of this node, depending on its type.""" @@ -132,7 +121,8 @@ def _set_node_value(self, value: DOMString) -> None: DOMException: - `NO_MODIFICATION_ALLOWED_ERR`: Raised when the node is readonly. """ - self._check_modifiable() + if self._read_only: + raise DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR) self._node_value = DOMString(value) @property @@ -188,7 +178,7 @@ def first_child(self) -> Optional[_AnyNode]: If there is no such node, this returns `None`. """ - if not self.child_nodes: + if not self.has_child_nodes(): return None return self.child_nodes.item(0) @@ -198,7 +188,7 @@ def last_child(self) -> Optional[_AnyNode]: If there is no such node, this returns `None`. """ - if not self.child_nodes: + if not self.has_child_nodes(): return None return self.child_nodes.item(self.child_nodes.length-1) @@ -257,15 +247,236 @@ def owner_document(self) -> Optional[_Document]: This is also the `Document` object used to create new nodes. When this node is a `Document` this is `None`. """ - if self.node_type == NodeType.DOCUMENT_NODE: - return None return self._owner_document def _set_owner_document(self, owner_document: Optional[_Document] = None) -> None: """Indirect accessor to set the 'owner_document' property.""" + if owner_document is None: + if self.node_type != NodeType.DOCUMENT_NODE: + raise ValueError('`Node` should have a `Document` object ', + 'which associated with this node, ', + 'Unless this node is a `Document`.') self._owner_document = owner_document + def insert_before(self, + new_child: _AnyNode, + ref_child: Optional[_AnyNode] = None) -> _AnyNode: + """Inserts the node `new_child` before the existing child node `ref_child`. If `ref_child` is `None`, insert `new_child` at the end of the list of children. + + If `new_child` is a `DocumentFragment` object, all of its children are inserted, in the same order, before `ref_child`. If the `new_child` is already in the tree, it is first removed. + + Args: + new_child: The node to insert. + ref_child: The reference node, i.e., the node before which the new node must be inserted. + + Returns: + The node being inserted. + + Raises: + DOMException: + - `HIERARCHY_REQUEST_ERR`: Raised if this node is of a type that does not allow children of the type of the `new_child` node, or if the node to insert is one of this node's ancestors. + - `WRONG_DOCUMENT_ERR`: Raised if `new_child` was created from a different document than the one that created this node. + - `NO_MODIFICATION_ALLOWED_ERR`: Raised if this node is readonly. + - `NOT_FOUND_ERR`: Raised if `ref_child` is not a child of this node. + """ + # `HIERARCHY_REQUEST_ERR` should be checked on subclasses by overriding. + if self._read_only: + raise DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR) + if new_child.owner_document is not self.owner_document: + raise DOMException(DOMException.WRONG_DOCUMENT_ERR) + if ref_child is not None: + if ref_child not in self.child_nodes: + raise DOMException(DOMException.NOT_FOUND_ERR) + # If `new_child` and `old_child` share the same reference, + # this method does nothing. + if new_child is ref_child: + return new_child + # If `ref_child` is null, insert `new_child` at the end of the list of children. + # + # This operation is equivalent to the `append_child()` method. + if ref_child is None: + self.append_child(new_child) + return new_child + # If `new_child` is a `DocumentFragment` object, + # all of its children are inserted, in the same order. + # + # This is done by recursive calls. + if new_child.node_type == NodeType.DOCUMENT_FRAGMENT_NODE: + grand_child_node: _AnyNode + for grand_child_node in new_child.child_nodes: + self.insert_before(grand_child_node, ref_child) + return new_child + # If the `new_child` is already in the tree, + # it is first removed. + if new_child in self.child_nodes: + self.remove_child(new_child) + # Otherwise, simply insert `new_child` using the methods of `NodeList(list)`. + ref_index = self.child_nodes.index(ref_child) + self.child_nodes.insert(ref_index, new_child) + new_child._set_parent_node(self) + return new_child + + def replace_child(self, + new_child: _AnyNode, + old_child: _AnyNode) -> _AnyNode: + """Replaces the child node `old_child` with `new_child` in the list of children, and returns the `old_child` node. + + If the `new_child` is already in the tree, it is first removed. + + Args: + new_child: The new node to put in the child list. + old_child: The node being replaced in the list. + + Returns: + The node replaced. + + Raisees: + DOMException: + - `HIERARCHY_REQUEST_ERR`: Raised if this node is of a type that does not allow children of the type of the `new_child` node, or it the node to put in is one of this node's ancestors. + - `WRONG_DOCUMENT_ERR`: Raised if `new_child` was created from a different document than the one that created this node. + - `NO_MODIFICATION_ALLOWED_ERR`: Raised if this node is readonly. + - `NOT_FOUND_ERR`: Raised if `old_child` is not a child of this node. + """ + # `HIERARCHY_REQUEST_ERR` should be checked on subclasses by overriding. + if self._read_only: + raise DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR) + if new_child.owner_document is not self.owner_document: + raise DOMException(DOMException.WRONG_DOCUMENT_ERR) + if old_child not in self.child_nodes: + raise DOMException(DOMException.NOT_FOUND_ERR) + # If `new_child` and `old_child` share the same reference, + # this method does nothing. + if new_child is old_child: + return old_child + # If the `new_child` is already in the tree, + # it is first removed. + if new_child in self.child_nodes: + self.remove_child(new_child) + repl_index = self.child_nodes.index(old_child) + self.child_nodes[repl_index] = new_child + new_child._set_parent_node(self) + return old_child + + def remove_child(self, + old_child: _AnyNode) -> _AnyNode: + """Removes the child node indicated by `old_chlid` from the list of children, and returns it. + + Args: + old_chlid: The node being removed. + + Returns: + The node removed. + + Raises: + DOMException: + - `NO_MODIFICATION_ALLOWED_ERR`: Raised if this node is readonly. + - `NOT_FOUND_ERR`: Raised if `old_chlid` is not a child of this node. + """ + # `HIERARCHY_REQUEST_ERR` should be checked on subclasses by overriding. + if self._read_only: + raise DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR) + if old_child not in self.child_nodes: + raise DOMException(DOMException.NOT_FOUND_ERR) + # Removes node using the method of `NodeList(list)`. + self.child_nodes.remove(old_child) + old_child._set_parent_node(None) + return old_child + + def append_child(self, new_child: _AnyNode) -> _AnyNode: + """Adds the node `new_child` to the end of the list of children of this node. + + If the `new_child` is already in the tree, it is first removed. + + Args: + new_child: The node to add. If it is a `DocumentFragment` object, the entire contents of the document fragment are moved into the child list of this node + + Returns: + The node added. + + Raises: + DOMException: + - `HIERARCHY_REQUEST_ERR`: Raised if this node is of a type that does not allow children of the type of the `new_child` node, or if the node to append is one of this node's ancestors. + - `WRONG_DOCUMENT_ERR`: Raised if `new_child` was created from a different document than the one that created this node. + - `NO_MODIFICATION_ALLOWED_ERR`: Raised if this node is readonly. + """ + # `HIERARCHY_REQUEST_ERR` should be checked on subclasses by overriding. + if self._read_only: + raise DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR) + if new_child.owner_document is not self.owner_document: + raise DOMException(DOMException.WRONG_DOCUMENT_ERR) + # If `new_child` is a `DocumentFragment` object, + # all of its children are appended, in the same order. + # + # This is done by recursive calls. + if new_child.node_type == NodeType.DOCUMENT_FRAGMENT_NODE: + grand_child_node: _AnyNode + for grand_child_node in new_child.child_nodes: + self.append_child(grand_child_node) + return new_child + # If the `new_child` is already in the tree, + # it is first removed. + if new_child in self.child_nodes: + self.remove_child(new_child) + # Otherwise, simply append `new_child` using the methods of `NodeList(list)`. + self.child_nodes.append(new_child) + new_child._set_parent_node(self) + return new_child + + def has_child_nodes(self) -> bool: + """This is a convenience method to allow easy determination of whether a node has any children. + + Returns: + `True` if the node has any children, `False` if the node has no children. + """ + return bool(self.child_nodes) + + def clone_node(self, deep: bool = False) -> _AnyNode: + """Returns a duplicate of this node. + + i.e., serves as a generic copy constructor for nodes. The duplicate node has no parent (parentNode returns `None`.). + Cloning an `Element` copies all attributes and their values, including those generated by the XML processor to represent defaulted attributes, but this method does not copy any text it contains unless it is a deep clone, since the text is contained in a child `Text` node. + Cloning any other type of node simply returns a copy of this node. + + Args: + deep: If `True`, recursively clone the subtree under the specified node; if `False`, clone only the node itself (and its attributes, if it is an `Element`). + + Returns: + The duplicate node. + + This method raises no exceptions. + """ + if deep: + return self._deep_copy() + else: + return self._shallow_copy() + + def _shallow_copy(self) -> _AnyNode: + node = self.__class__( + owner_document=self.owner_document, + node_type=self.node_type, + node_name=self.node_name, + node_value=self.node_value, + read_only=self._read_only + ) + return node + + def _deep_copy(self) -> _AnyNode: + def copy_recursive(node_iterable: Iterable[_AnyNode]): + node: _AnyNode + for node in node_iterable: + yield node.clone_node(True) + node = self.__class__( + owner_document=self.owner_document, + node_type=self.node_type, + node_name=self.node_name, + node_value=self.node_value, + child_nodes=copy_recursive(self.child_nodes), + attributes=copy_recursive(self.attributes.values()), + read_only=self._read_only + ) + return node + _AnyNode = Node _NamedNodeMap = Dict[str, _AnyNode] # TODO: Implement NamedNodeMap (#19) diff --git a/w3/python/core/fundamental_interface/NodeList_test.py b/w3/python/core/fundamental_interface/NodeList_test.py index b66e4c8..402bf56 100644 --- a/w3/python/core/fundamental_interface/NodeList_test.py +++ b/w3/python/core/fundamental_interface/NodeList_test.py @@ -1,4 +1,4 @@ -from typing import Iterator +from typing import Iterator, Optional import unittest from w3.dom import Node @@ -6,7 +6,20 @@ from w3.dom import NodeList -def _make_nodes(n: int) -> Iterator[Node]: +def _create_document_node(): + return Node(node_type=NodeType.DOCUMENT_NODE, + node_name='#document', + owner_document=None) + + +def _create_text_node(document: Node): + assert document.node_type == NodeType.DOCUMENT_NODE + return Node(node_type=NodeType.TEXT_NODE, + node_name='#text', + owner_document=document) + + +def _make_nodes(n: int, document: Optional[Node] = None) -> Iterator[Node]: """Accessor to create `n` random nodes immediatly. Args: @@ -15,20 +28,26 @@ def _make_nodes(n: int) -> Iterator[Node]: Yields: A randomly generated node. """ + if document is None: + document = _create_document_node() for _ in range(n): - yield Node(NodeType.TEXT_NODE, '#text') + yield _create_text_node(document) -class Test_NodeList(unittest.TestCase): - def testInit(self): +class TestDunder_Init(unittest.TestCase): + def test_EmptyList(self): NodeList() - # Initiate with a node + + def test_SingleNode(self): node = next(_make_nodes(1)) NodeList([node]) - # Initiate with multiple nodes. + + def test_MultipleNodes(self): NodeList(_make_nodes(4)) - def testIter(self): + +class TestDunder_Iter(unittest.TestCase): + def test_ForLoop(self): nodes = [*_make_nodes(4)] node_list = NodeList(nodes) # Create an iterator for checking @@ -36,7 +55,9 @@ def testIter(self): for list_elem in node_list: self.assertEqual(list_elem, next(node_iter)) - def testBool(self): + +class TestDunder_Bool(unittest.TestCase): + def test_Raw(self): # Empty `NodeList` should equal False. node_list = NodeList() self.assertFalse(node_list) @@ -44,46 +65,69 @@ def testBool(self): node_list = NodeList(_make_nodes(2)) self.assertTrue(node_list) - def testGetItem(self): + +class TestDunder_GetItem(unittest.TestCase): + def test_IntegerIndex(self): nodes = [*_make_nodes(4)] node_list = NodeList(nodes) for i in range(4): self.assertEqual(node_list[i], nodes[i]) - def testSetItem(self): - node = Node(NodeType.TEXT_NODE, - '#text') - node_list = NodeList(_make_nodes(4)) - node_list[2] = node - self.assertEqual(node_list[2], node) - def testItem_inSize(self): +class TestDunder_SetItem(unittest.TestCase): + def test_IntegerIndex(self): + document = _create_document_node() + text_node = _create_text_node(document) + node_list = NodeList(_make_nodes(4, document)) + node_list[2] = text_node + self.assertEqual(node_list[2], text_node) + + +class TestDunder_Contains(unittest.TestCase): + def test_In(self): + nodes = [*_make_nodes(4)] + node_list = NodeList(nodes) + for n in node_list: + self.assertIn(n, node_list) + + def testContains_NotIn(self): + internal_nodes = [*_make_nodes(4)] + external_node = _make_nodes(1) + node_list = NodeList(internal_nodes) + self.assertNotIn(external_node, node_list) + + +class TestProperty_Length(unittest.TestCase): + def test_Empty(self): + node_list = NodeList() + self.assertEqual(node_list.length, 0) + + def test_OneItem(self): + node_list = NodeList(_make_nodes(1)) + self.assertEqual(node_list.length, 1) + + def test_SomeItems(self): + node_list = NodeList(_make_nodes(5)) + self.assertEqual(node_list.length, 5) + + +class TestMethod_Item(unittest.TestCase): + def test_InSize(self): nodes = [*_make_nodes(5)] node_list = NodeList(nodes) for i in range(5): self.assertEqual(node_list.item(i), nodes[i]) - def testItem_greaterOrEqualThanSize(self): + def test_GreaterOrEqualThanSize(self): node_list = NodeList(_make_nodes(5)) for i in range(5, 8): self.assertIsNone(node_list.item(i)) - def testItem_notValid(self): + def test_InvalidIndex(self): node_list = NodeList(_make_nodes(5)) for idx in [-1, -9999, 'hi', '1', None, Node, NodeList, b'0']: self.assertIsNone(node_list.item(idx)) - def testLength(self): - # Empty NodeList - node_list = NodeList() - self.assertEqual(node_list.length, 0) - # NodeList with a node. - node_list = NodeList(_make_nodes(1)) - self.assertEqual(node_list.length, 1) - # NodeList with some items. - node_list = NodeList(_make_nodes(5)) - self.assertEqual(node_list.length, 5) - if __name__ == '__main__': unittest.main() diff --git a/w3/python/core/fundamental_interface/Node_test.py b/w3/python/core/fundamental_interface/Node_test.py index 556039c..73c27ce 100644 --- a/w3/python/core/fundamental_interface/Node_test.py +++ b/w3/python/core/fundamental_interface/Node_test.py @@ -5,72 +5,549 @@ from w3.dom import NodeType -class Test_Node(unittest.TestCase): - def test_init(self): - node = Node(node_type=NodeType.DOCUMENT_NODE, - node_name='#document', - read_only=False) - self.assertEqual(node.node_type, NodeType.DOCUMENT_NODE) - self.assertEqual(node.node_name, '#document') - self.assertEqual(node.node_value, '') +def _create_document_node(): + return Node(node_type=NodeType.DOCUMENT_NODE, + node_name='#document', + owner_document=None) + + +def _create_element_node(document: Node): + assert document.node_type == NodeType.DOCUMENT_NODE + return Node(node_type=NodeType.ELEMENT_NODE, + node_name='tagName', + owner_document=document) + - def testNodeValue_get(self): - node = Node(node_type=NodeType.TEXT_NODE, +class TestDunder_Init(unittest.TestCase): + def test_Default(self): + # Creating document node. + document = Node(node_type=NodeType.DOCUMENT_NODE, + node_name='#document', + owner_document=None, + read_only=False) + self.assertEqual(document.node_type, NodeType.DOCUMENT_NODE) + self.assertEqual(document.node_name, '#document') + self.assertEqual(document.node_value, '') + + +class TestProperty_NodeValue(unittest.TestCase): + def setUp(self) -> None: + self.document = _create_document_node() + return super().setUp() + + def testGetter(self): + text = Node(node_type=NodeType.TEXT_NODE, node_name='#text', + owner_document=self.document, node_value='lorem ipsum') - self.assertEqual(node.node_value, 'lorem ipsum') + self.assertEqual(text.node_value, 'lorem ipsum') - def testNodeValue_set(self): - # set value from constructor - node = Node(node_type=NodeType.TEXT_NODE, + def testConstructor(self): + text = Node(node_type=NodeType.TEXT_NODE, node_name='#text', + owner_document=self.document, node_value='foo', read_only=False) - self.assertEqual(node.node_value, 'foo') - # set using setter - node.node_value = 'bar' - self.assertEqual(node.node_value, 'bar') + self.assertEqual(text.node_value, 'foo') + + def testSetter(self): + text = Node(node_type=NodeType.TEXT_NODE, + node_name='#text', + owner_document=self.document, + read_only=False) + text.node_value = 'bar' + self.assertEqual(text.node_value, 'bar') - def testNodeValue_setReadOnly(self): - node = Node(node_type=NodeType.TEXT_NODE, + def testSetter_ReadOnly(self): + text = Node(node_type=NodeType.TEXT_NODE, node_name='#text', + owner_document=self.document, node_value='foo', read_only=True) - try: - node.node_value = 'bar' - except DOMException as e: - code = e.args[0] - self.assertEqual(code, DOMException.NO_MODIFICATION_ALLOWED_ERR) - else: - self.fail() + # Testing + with self.assertRaises(DOMException) as context_manager: + text.node_value = 'bar' + self.assertEqual(context_manager.exception.args[0], + DOMException.NO_MODIFICATION_ALLOWED_ERR) # `node_value` should not be modified. - self.assertEqual(node.node_value, 'foo') + self.assertEqual(text.node_value, 'foo') - def testNodeType_get(self): - for node_type in NodeType: - node = Node(node_type=node_type, - node_name='') - self.assertEqual(node.node_type, node_type) - def testNodeType_setCorrectTypes(self): +class TestProperty_NodeType(unittest.TestCase): + def setUp(self) -> None: + self.document = _create_document_node() + return super().setUp() + + def testGetter(self): for node_type in NodeType: node = Node(node_type=node_type, - node_name='') + node_name='', + owner_document=self.document) self.assertEqual(node.node_type, node_type) - def testNodeType_setWrongTypes(self): - for node_type in [-1, None, 'hi', Node, True, NodeType, 'DOCUMENT_NODE']: - with self.assertRaises(TypeError): + def testSetter_CorrectTypes(self): + for node_type in NodeType: + with self.subTest(node_type=node_type): Node(node_type=node_type, - node_name='') - - def testParentNode_get(self): - parent_node = Node(node_type=NodeType.DOCUMENT_FRAGMENT_NODE, - node_name='#document-fragment') - node = Node(node_type=NodeType.ELEMENT_NODE, - node_name='tagName', - parent_node=parent_node) - self.assertEqual(node.parent_node, parent_node) + node_name='', + owner_document=self.document) + + def testSetter_WrongTypes(self): + for node_type in [-1, None, 'hi', True, Node, NodeType, 'DOCUMENT_NODE']: + with self.subTest(node_type=node_type): + with self.assertRaises((TypeError, ValueError)): + Node(node_type=node_type, + node_name='', + owner_document=self.document) + + +class TestProperty_ParentNode(unittest.TestCase): + def setUp(self) -> None: + self.document = _create_document_node() + return super().setUp() + + def testConstrutor(self): + docfrag = Node(node_type=NodeType.DOCUMENT_FRAGMENT_NODE, + node_name='#document-fragment', + owner_document=self.document) + self.assertIsNone(docfrag.parent_node) + + def testGetter(self): + parent_node = _create_element_node(self.document) + child_node = _create_element_node(self.document) + # Warning: this is assuming that `append_child()` method works properly. + parent_node.append_child(child_node) + self.assertEqual(child_node.parent_node, parent_node) + + def testSetter_Raises_Exception(self): + parent_node = _create_element_node(self.document) + child_node = _create_element_node(self.document) + # Testing + try: + child_node.parent_node = parent_node # type: ignore + except: + self.assertNotEqual(child_node.parent_node, parent_node) + else: + self.fail() + + +class TestMethod_InsertBefore(unittest.TestCase): + def setUp(self) -> None: + self.document = _create_document_node() + return super().setUp() + + def test_WithoutRefNode(self): + # ====================================== + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + new_child_node = _create_element_node(self.document) + # Testing + parent_node.insert_before(new_child_node) + self.assertEqual(new_child_node.parent_node, parent_node) + self.assertEqual(parent_node.child_nodes.item(0), new_child_node) + self.assertEqual(parent_node.child_nodes.length, 1) + + def test_WithRefNode(self): + # ====================================== + # + # + # + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + new_child_node = _create_element_node(self.document) + ref_child_node = _create_element_node(self.document) + parent_node.append_child(ref_child_node) + # Testing + parent_node.insert_before(new_child_node, ref_child_node) + self.assertEqual(new_child_node.parent_node, parent_node) + self.assertEqual(parent_node.child_nodes.item(0), new_child_node) + self.assertEqual(parent_node.child_nodes.item(1), ref_child_node) + self.assertEqual(parent_node.child_nodes.length, 2) + + def test_InsertExistingChild(self): + # ====================================== + # + # + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + ref_child_node = _create_element_node(self.document) + new_child_node = _create_element_node(self.document) + parent_node.append_child(ref_child_node) + parent_node.append_child(new_child_node) + # Testing + parent_node.insert_before(new_child_node, ref_child_node) + self.assertEqual(new_child_node.parent_node, parent_node) + self.assertEqual(parent_node.child_nodes.item(0), new_child_node) + self.assertEqual(parent_node.child_nodes.item(1), ref_child_node) + self.assertEqual(parent_node.child_nodes.length, 2) + + def test_InsertDocumentFragmentNode(self): + # ====================================== + # + # + # + # + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + docfrag_node = Node(node_type=NodeType.DOCUMENT_FRAGMENT_NODE, + node_name='#document-fragment', + owner_document=self.document) + first_child_node = _create_element_node(self.document) + second_child_node = _create_element_node(self.document) + docfrag_node.append_child(first_child_node) + docfrag_node.append_child(second_child_node) + # Testing + parent_node.insert_before(docfrag_node) + self.assertEqual(parent_node.child_nodes.item(0), first_child_node) + self.assertEqual(parent_node.child_nodes.item(1), second_child_node) + self.assertEqual(parent_node.child_nodes.length, 2) + + def test_Raises_HIERARCHY_REQUEST_ERR(self): + self.skipTest(NotImplemented) # TODO + + def test_Raises_WRONG_DOCUMENT_ERR(self): + # ====================================== + # + # + # + # + # + # + # + # ====================================== + document_1 = _create_document_node() + document_2 = _create_document_node() + elem_node_1 = _create_element_node(document_1) + elem_node_2 = _create_element_node(document_2) + # Testing + with self.assertRaises(DOMException) as context_manager: + elem_node_1.insert_before(elem_node_2) + self.assertEqual(context_manager.exception.args[0], + DOMException.WRONG_DOCUMENT_ERR) + + def test_Raises_NO_MODIFICATION_ALLOWED_ERR(self): + # ====================================== + # + # + # + # + # ====================================== + parent_node_readonly = Node(node_type=NodeType.ELEMENT_NODE, + node_name='tagName', + owner_document=self.document, + read_only=True) + new_child_node = _create_element_node(self.document) + # Testing + with self.assertRaises(DOMException) as context_manager: + parent_node_readonly.insert_before(new_child_node) + self.assertEqual(context_manager.exception.args[0], + DOMException.NO_MODIFICATION_ALLOWED_ERR) + + def test_Raises_NOT_FOUND_ERR(self): + # ====================================== + # + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + new_child_node = _create_element_node(self.document) + ref_child_node = _create_element_node(self.document) + # Testing + with self.assertRaises(DOMException) as context_manager: + parent_node.insert_before(new_child_node, ref_child_node) + self.assertEqual(context_manager.exception.args[0], + DOMException.NOT_FOUND_ERR) + + +class TestMethod_AppendChild(unittest.TestCase): + def setUp(self) -> None: + self.document = _create_document_node() + return super().setUp() + + def test_AppendNewChild(self): + # ====================================== + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + new_child_node = _create_element_node(self.document) + # Testing + parent_node.append_child(new_child_node) + self.assertEqual(new_child_node.parent_node, parent_node) + self.assertEqual(parent_node.child_nodes.item(0), new_child_node) + self.assertEqual(parent_node.child_nodes.length, 1) + + def test_AppendExistingChild(self): + # ====================================== + # + # + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + new_child_node = _create_element_node(self.document) + old_child_node = _create_element_node(self.document) + parent_node.append_child(new_child_node) + parent_node.append_child(old_child_node) + # Testing + parent_node.append_child(new_child_node) + self.assertEqual(old_child_node.parent_node, parent_node) + self.assertEqual(parent_node.child_nodes.item(0), old_child_node) + self.assertEqual(parent_node.child_nodes.item(1), new_child_node) + self.assertEqual(parent_node.child_nodes.length, 2) + + def test_AppendDocumentFragmentNode(self): + # ====================================== + # + # + # + # + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + docfrag_node = Node(node_type=NodeType.DOCUMENT_FRAGMENT_NODE, + node_name='#document-fragment', + owner_document=self.document) + first_child_node = _create_element_node(self.document) + second_child_node = _create_element_node(self.document) + docfrag_node.append_child(first_child_node) + docfrag_node.append_child(second_child_node) + # Testing + parent_node.append_child(docfrag_node) + self.assertEqual(parent_node.child_nodes.item(0), first_child_node) + self.assertEqual(parent_node.child_nodes.item(1), second_child_node) + self.assertEqual(parent_node.child_nodes.length, 2) + + def test_Raises_HIERARCHY_REQUEST_ERR(self): + self.skipTest(NotImplemented) # TODO + + def test_Raises_WRONG_DOCUMENT_ERR(self): + # ====================================== + # + # + # + # + # + # + # + # ====================================== + document_1 = _create_document_node() + document_2 = _create_document_node() + elem_node_1 = _create_element_node(document_1) + elem_node_2 = _create_element_node(document_2) + # Testing + with self.assertRaises(DOMException) as context_manager: + elem_node_1.append_child(elem_node_2) + self.assertEqual(context_manager.exception.args[0], + DOMException.WRONG_DOCUMENT_ERR) + + def test_Raises_NO_MODIFICATION_ALLOWED_ERR(self): + # ====================================== + # + # + # + # + # ====================================== + parent_node_readonly = Node(node_type=NodeType.ELEMENT_NODE, + node_name='tagName', + owner_document=self.document, + read_only=True) + new_child_node = _create_element_node(self.document) + # Testing + with self.assertRaises(DOMException) as context_manager: + parent_node_readonly.append_child(new_child_node) + self.assertEqual(context_manager.exception.args[0], + DOMException.NO_MODIFICATION_ALLOWED_ERR) + + +class TestMethod_ReplaceChild(unittest.TestCase): + def setUp(self) -> None: + self.document = _create_document_node() + return super().setUp() + + def test_ReplaceExistingChild(self): + # ====================================== + # + # + # + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + new_child_node = _create_element_node(self.document) + old_child_node = _create_element_node(self.document) + parent_node.append_child(old_child_node) + # Testing + parent_node.replace_child(new_child_node, old_child_node) + self.assertIn(new_child_node, parent_node.child_nodes) + self.assertNotIn(old_child_node, parent_node.child_nodes) + self.assertEqual(new_child_node.parent_node, parent_node) + self.assertEqual(parent_node.child_nodes.item(0), new_child_node) + self.assertEqual(parent_node.child_nodes.length, 1) + + def test_Raises_HIERARCHY_REQUEST_ERR(self): + self.skipTest(NotImplemented) # TODO + + def test_Raises_WRONG_DOCUMENT_ERR(self): + # ====================================== + # + # + # + # + # + # + # + # + # + # ====================================== + document_1 = _create_document_node() + document_2 = _create_document_node() + parent_node = _create_element_node(document_1) + old_child_node = _create_element_node(document_1) + new_child_node = _create_element_node(document_2) + parent_node.append_child(old_child_node) + # Testing + with self.assertRaises(DOMException) as context_manager: + parent_node.replace_child(new_child_node, old_child_node) + self.assertEqual(context_manager.exception.args[0], + DOMException.WRONG_DOCUMENT_ERR) + + def test_Raises_NO_MODIFICATION_ALLOWED_ERR(self): + # ====================================== + # + # + # + # + # ====================================== + parent_node_readonly = Node(node_type=NodeType.ELEMENT_NODE, + node_name='tagName', + owner_document=self.document, + read_only=True) + new_child_node = _create_element_node(self.document) + # Testing + with self.assertRaises(DOMException) as context_manager: + parent_node_readonly.append_child(new_child_node) + self.assertEqual(context_manager.exception.args[0], + DOMException.NO_MODIFICATION_ALLOWED_ERR) + + +class TestMethod_HasChild(unittest.TestCase): + def setUp(self) -> None: + self.document = _create_document_node() + return super().setUp() + + def test_True(self): + # ====================================== + # + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + child_node = _create_element_node(self.document) + parent_node.append_child(child_node) + # Testing + self.assertTrue(parent_node.has_child_nodes()) + + def test_False(self): + # ====================================== + # + # + # + # ====================================== + node = _create_element_node(self.document) + # Testing + self.assertFalse(node.has_child_nodes()) + + +class TestMethod_RemoveChild(unittest.TestCase): + def setUp(self) -> None: + self.document = _create_document_node() + return super().setUp() + + def test_RemoveExistingChlid(self): + # ====================================== + # + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + child_node = _create_element_node(self.document) + parent_node.append_child(child_node) + # Testing + parent_node.remove_child(child_node) + self.assertNotIn(child_node, parent_node.child_nodes) + self.assertIsNone(child_node.parent_node) + + def test_Raises_NOT_FOUND_ERR(self): + # ====================================== + # + # + # + # + # ====================================== + parent_node = _create_element_node(self.document) + child_node = _create_element_node(self.document) + # Testing + with self.assertRaises(DOMException) as context_manager: + parent_node.remove_child(child_node) + self.assertEqual(context_manager.exception.args[0], + DOMException.NOT_FOUND_ERR) + + def test_Raises_NO_MODIFICATION_ALLOWED_ERR(self): + # ====================================== + # + # + # + # + # + # ====================================== + child_node = _create_element_node(self.document) + parent_node = Node(node_type=NodeType.ELEMENT_NODE, + node_name='tagName', + owner_document=self.document, + child_nodes=[child_node], + read_only=True) + # Testing + with self.assertRaises(DOMException) as context_manager: + parent_node.remove_child(child_node) + self.assertEqual(context_manager.exception.args[0], + DOMException.NO_MODIFICATION_ALLOWED_ERR) + + +class TestMethod_CloneNode(unittest.TestCase): + # TODO + pass if __name__ == '__main__':