+| "HTML "
+|
+| " Kit"
+|
+| "This is an "
+|
+| "Important"
+| " paragraph"
+*/
+
+static void (^ RemoveThenInsertNode)(HTMLNode *) = ^ (HTMLNode *node) {
+ HTMLNode *parent = node.parentNode;
+ HTMLNode *nextSibling = node.nextSibling;
+ [parent removeChildNode:node];
+ [parent insertNode:node beforeChildNode:nextSibling];
+};
+
+static void (^ IterateUpToNode)(HTMLNodeIterator *, HTMLNode *) = ^ (HTMLNodeIterator *iterator, HTMLNode *target) {
+ for(HTMLNode *node = iterator.referenceNode; node && (node != target); node = iterator.nextNode);
+};
+
+static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) {
+ while (node.lastChild) {
+ node = node.lastChild;
+ }
+ return node;
+};
+
+- (void)testThatRemovingRootNodeShouldNotAffectIterator
+{
+ HTMLDocument *document = self.document;
+ HTMLNode *node = document.body.firstChild; //
+
+ HTMLNodeIterator *iterator = node.nodeIterator;
+
+ [document.body removeChildNode:node];
+
+ XCTAssertEqualObjects(iterator.root, node);
+ XCTAssertEqualObjects(iterator.referenceNode, node);
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
+ XCTAssertEqualObjects(iterator.referenceNode.parentNode, nil);
+}
+
+- (void)testThatRemovingANonInclusiveAnscestorOfReferenceShouldNotAffectIterator
+{
+ HTMLDocument *document = self.document;
+ HTMLNode *body = document.body;
+
+ HTMLNodeIterator *iterator = body.nodeIterator;
+
+ [iterator nextNode]; // Reference node:
+ [iterator nextNode]; // Reference node:
+
+ RemoveThenInsertNode(iterator.root.childNodes[1]); // Remove
+
+ XCTAssertEqualObjects(iterator.root, body);
+ XCTAssertEqualObjects(iterator.referenceNode, body.firstChild);
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
+}
+
+- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NilOldPreviousSibling
+{
+ HTMLDocument *document = self.document;
+
+ HTMLNodeIterator *iterator = document.body.nodeIterator;
+
+ [iterator nextNode]; // Reference node:
+
+ HTMLNode *node = iterator.nextNode; // Reference node:
+ RemoveThenInsertNode(node); // Remove with old previos sibling being nil
+
+ XCTAssertEqualObjects(iterator.referenceNode, iterator.root);
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
+
+ HTMLNode *next = iterator.nextNode; // "Hello"
+ XCTAssertEqualObjects(next, iterator.root.firstChild);
+}
+
+- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NonNilOldPreviousSibling_NotBeforeReference
+{
+ HTMLDocument *document = self.document;
+ HTMLNode *body = document.body;
+
+ HTMLNodeIterator *iterator = body.nodeIterator;
+
+ HTMLNode *node = iterator.root.childNodes[1]; //
+ IterateUpToNode(iterator, node); // Reference node:
, pointer-before-reference: NO
+ RemoveThenInsertNode(node); // Remove
with old previos sibling being
+
+ XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChild));
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
+}
+
+- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NonNilOldPreviousSibling_BeforeReference
+{
+ HTMLDocument *document = self.document;
+ HTMLNode *body = document.body;
+
+ HTMLNodeIterator *iterator = body.nodeIterator;
+
+ HTMLNode *node = iterator.root.childNodes[1]; //
+ IterateUpToNode(iterator, node); // Reference node:
, pointer-before-reference: NO
+ [iterator previousNode]; // pointer-before-reference: YES
+ RemoveThenInsertNode(node); // Remove
with old previos sibling being
+
+ XCTAssertEqualObjects(iterator.referenceNode, body.firstChild);
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
+}
+
+- (void)testThatRemovingThenReinsertingReferenceNodeAfterNextShouldReturnItAgain
+{
+ HTMLDocument *document = self.document;
+ HTMLNodeIterator *iterator = document.body.nodeIterator;
+
+ [iterator nextNode]; // Reference node:
+
+ HTMLNode *node = iterator.nextNode; //
+ RemoveThenInsertNode(node);
+
+ XCTAssertEqualObjects(iterator.referenceNode, iterator.root);
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
+
+ HTMLNode *next = iterator.nextNode;
+ XCTAssertEqualObjects(next, node);
+}
+
+- (void)testThatRemovingThenReinsertingReferenceNodeAfterPreviousShouldReturnItAgain
+{
+ HTMLDocument *document = self.document;
+ HTMLNodeIterator *iterator = document.body.nodeIterator;
+
+ [iterator nextNode]; // Reference node:
+ [iterator nextNode]; // Reference node:
+
+ HTMLNode *node = iterator.previousNode; // Reference node: , pointer-before-reference: YES
+ HTMLNode *next = node.nextSibling; //
+ RemoveThenInsertNode(node);
+
+ XCTAssertEqualObjects(iterator.referenceNode, next);
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
+
+ HTMLNode *previous = iterator.previousNode;
+ XCTAssertEqualObjects(previous, LastDescendant(node));
+}
+
+- (void)testThatRemovingParentOfReferenceNodeShouldUpdateIterator_NotBeforeReference
+{
+ HTMLDocument *document = self.document;
+ HTMLNode *body = document.body;
+
+ HTMLNodeIterator *iterator = body.nodeIterator;
+ HTMLNode *parent = body.childNodes[1];
+
+ IterateUpToNode(iterator, parent); // Reference node:
, pointer-before-reference: NO
+ [iterator nextNode]; // Reference node: "This is an "
+ RemoveThenInsertNode(parent);
+
+ XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChild));
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
+}
+
+- (void)testThatRemovingParentOfReferenceNodeShouldUpdateIterator_BeforeReference
+{
+ HTMLDocument *document = self.document;
+ HTMLNode *body = document.body;
+
+ HTMLNodeIterator *iterator = body.nodeIterator;
+ HTMLNode *parent = body.childNodes[1];
+
+ IterateUpToNode(iterator, parent); // Reference node:
, pointer-before-reference: NO
+ [iterator nextNode]; // Reference node: "This is an "
+ [iterator previousNode]; // pointer-before-reference: YES
+ RemoveThenInsertNode(parent);
+
+ XCTAssertEqualObjects(iterator.referenceNode, body.firstChild);
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
+}
+
+- (void)testRemoveReferenceNode_NilPreviousSibling_NonNilParentFirstChild
+{
+ HTMLDocument *document = self.document;
+ HTMLNode *body = document.body;
+
+ HTMLNodeIterator *iterator = body.nodeIterator;
+
+ [iterator nextNode]; // Reference node:
+ [iterator nextNode]; // Reference node:
+
+ HTMLNode *node = iterator.previousNode; // Reference node: , pointer-before-reference: YES
+ XCTAssertNil(node.previousSibling);
+ XCTAssertNotNil(node.nextSibling);
+
+ HTMLNode *nextSibling = node.nextSibling; //
+ RemoveThenInsertNode(node);
+
+ XCTAssertEqualObjects(iterator.referenceNode, nextSibling);
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
+
+ HTMLNode *next = iterator.nextNode; //
+ XCTAssertNotEqualObjects(next, node);
+ XCTAssertEqualObjects(next, nextSibling);
+}
+
+- (void)testRemoveReferenceNode_NodeAfterOldParentIsOutsideRoot_BeforeReference
+{
+ HTMLDocument *document = self.document;
+ HTMLNode *body = document.body;
+
+ body.innerHTML = @"
";
+
+ HTMLNodeIterator *iterator = body.firstChild.nodeIterator;
+
+ IterateUpToNode(iterator, LastDescendant(body.firstChild)); // Referecne node:
+ HTMLNode *node = [iterator previousNode]; // pointer-before-reference: YES
+ RemoveThenInsertNode(node);
+
+ XCTAssertEqualObjects(iterator.referenceNode, iterator.root.firstChild);
+ XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
+}
+
+@end
diff --git a/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m b/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m
deleted file mode 100644
index 654fbcf..0000000
--- a/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m
+++ /dev/null
@@ -1,148 +0,0 @@
-//
-// HTMLKitNodeTreeEnumratorTests.m
-// HTMLKit
-//
-// Created by Iska on 28/03/15.
-// Copyright (c) 2015 BrainCookie. All rights reserved.
-//
-
-#import
-
-#import "HTMLNodes.h"
-
-@interface HTMLKitNodeTreeEnumratorTests : XCTestCase
-
-@end
-
-@implementation HTMLKitNodeTreeEnumratorTests
-
-#pragma mark - Elements
-
-- (HTMLElement *)div
-{
- return [[HTMLElement alloc] initWithTagName:@"div"];
-}
-
-- (HTMLElement *)simpleTree
-{
- /*
- | div
- | a
- | b
- | c
- */
- HTMLElement *div = self.div;
- [div appendNode:[[HTMLElement alloc] initWithTagName:@"a"]];
- [div appendNode:[[HTMLElement alloc] initWithTagName:@"b"]];
- [div appendNode:[[HTMLElement alloc] initWithTagName:@"c"]];
- return div;
-}
-
-- (HTMLElement *)nestedSimpleTree
-{
- /*
- | div
- | div
- | a
- | b
- | c
- | div
- | a
- | b
- | c
- */
- HTMLElement *div = self.div;
- [div appendNode:self.simpleTree];
- [div appendNode:self.simpleTree];
- return div;
-}
-
-- (HTMLElement *)complexTree
-{
- /*
- | div
- | div
- | div
- | a
- | b
- | c
- | e
- | f
- | div
- | a
- | b
- | c
- */
- HTMLElement *root = self.div;
-
- HTMLElement *div = self.div;
- [div appendNode:self.simpleTree];
- [root appendNode:div];
-
- HTMLElement *e = [[HTMLElement alloc] initWithTagName:@"e"];
- [e appendNode:[[HTMLElement alloc] initWithTagName:@"f"]];
- [root appendNode:e];
- [root appendNode:self.simpleTree];
-
- return root;
-}
-
-#pragma mark - Tests
-
-- (void)testSingle
-{
- HTMLElement *div = self.div;
- NSArray *result = div.treeEnumerator.allObjects;
- NSArray *expected = @[@"div"];
- XCTAssertEqualObjects([result valueForKey:@"name"], expected);
-}
-
-- (void)testSimpleTree
-{
- HTMLElement *tree = self.simpleTree;
- NSArray *result = tree.treeEnumerator.allObjects;
- NSArray *expected = @[@"div", @"a", @"b", @"c"];
- XCTAssertEqualObjects([result valueForKey:@"name"], expected);
-}
-
-- (void)testSimpleTreeReversed
-{
- HTMLElement *tree = self.simpleTree;
- NSArray *result = tree.reverseTreeEnumerator.allObjects;
- NSArray *expected = @[@"div", @"c", @"b", @"a"];
- XCTAssertEqualObjects([result valueForKey:@"name"], expected);
-}
-
-- (void)testNestedSimpleTree
-{
- HTMLElement *tree = self.nestedSimpleTree;
- NSArray *result = tree.treeEnumerator.allObjects;
- NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"];
- XCTAssertEqualObjects([result valueForKey:@"name"], expected);
-}
-
-- (void)testNestedSimpleTreeReversed
-{
- HTMLElement *tree = self.nestedSimpleTree;
- NSArray *result = tree.reverseTreeEnumerator.allObjects;
- NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"div", @"c", @"b", @"a"];
- XCTAssertEqualObjects([result valueForKey:@"name"], expected);
-}
-
-- (void)testComplexSimpleTree
-{
- HTMLElement *tree = self.complexTree;
- NSArray *result = tree.treeEnumerator.allObjects;
- NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"];
- XCTAssertEqualObjects([result valueForKey:@"name"], expected);
-}
-
-- (void)testComplexSimpleTreeReversed
-{
- HTMLElement *tree = self.complexTree;
- NSArray *result = tree.reverseTreeEnumerator.allObjects;
- NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"e", @"f", @"div", @"div", @"c", @"b", @"a"];
- XCTAssertEqualObjects([result valueForKey:@"name"], expected);
-}
-
-@end
diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m
new file mode 100644
index 0000000..87f37f9
--- /dev/null
+++ b/HTMLKitTests/HTMLKitNodesTests.m
@@ -0,0 +1,485 @@
+//
+// HTMLKitNodesTests.m
+// HTMLKit
+//
+// Created by Iska on 20/04/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "HTMLDOM.h"
+
+@interface HTMLKitNodesTests : XCTestCase
+
+@end
+
+@implementation HTMLKitNodesTests
+
+- (void)setUp
+{
+ [super setUp];
+}
+
+- (void)tearDown
+{
+ [super tearDown];
+}
+
+- (void)testInitNode
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+ XCTAssertNotNil(node);
+ XCTAssertEqualObjects(node.name, @"name");
+ XCTAssertEqual(node.nodeType, HTMLNodeElement);
+ XCTAssertNotNil(node.childNodes);
+ XCTAssertEqual(node.childNodes.count, 0);
+
+ XCTAssertNil(node.ownerDocument);
+ XCTAssertNil(node.parentNode);
+ XCTAssertNil(node.parentElement);
+ XCTAssertNil(node.firstChild);
+ XCTAssertNil(node.lastChild);
+ XCTAssertNil(node.previousSibling);
+ XCTAssertNil(node.lastChild);
+}
+
+- (void)testAppendNode
+{
+ HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
+ HTMLComment *comment = [HTMLComment new];
+ [element appendNode:comment];
+
+ XCTAssertEqual(element.childNodesCount, 1);
+ XCTAssertEqual(element.firstChild, comment);
+
+ HTMLElement *firstElement = [HTMLElement new];
+ HTMLElement *secondElement = [HTMLElement new];
+ NSArray *nodes = @[firstElement, secondElement];
+
+ [element appendNodes:nodes];
+
+ XCTAssertEqual(element.childNodesCount, 3);
+ XCTAssertEqual(element.firstChild, comment);
+ XCTAssertEqual(element.lastChild, secondElement);
+}
+
+- (void)testPrependNode
+{
+ HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
+ HTMLText *text = [HTMLText new];
+ [element appendNode:text];
+
+ HTMLComment *comment = [HTMLComment new];
+ [element prependNode:comment];
+
+ XCTAssertEqual(element.childNodesCount, 2);
+ XCTAssertEqual(element.firstChild, comment);
+
+ HTMLElement *firstElement = [HTMLElement new];
+ HTMLElement *secondElement = [HTMLElement new];
+ NSArray *nodes = @[firstElement, secondElement];
+
+ [element prependNodes:nodes];
+
+ XCTAssertEqual(element.childNodesCount, 4);
+ XCTAssertEqual(element.firstChild, firstElement);
+ XCTAssertEqual(element.lastChild, text);
+}
+
+- (void)testAppendDocumentFragment
+{
+ HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
+ HTMLComment *comment = [HTMLComment new];
+ [element appendNode:comment];
+
+ HTMLDocumentFragment *fragment = [HTMLDocumentFragment new];
+ HTMLElement *firstChild = [HTMLElement new];
+ HTMLElement *secondChild = [HTMLElement new];
+ [fragment appendNode:firstChild];
+ [fragment appendNode:secondChild];
+
+ [element appendNode:fragment];
+ XCTAssertEqual(element.childNodesCount, 3);
+ XCTAssertEqual(fragment.childNodesCount, 0);
+
+ XCTAssertEqualObjects(firstChild.parentNode, element);
+ XCTAssertEqualObjects(secondChild.parentNode, element);
+ XCTAssertEqualObjects(element.firstChild, comment);
+ XCTAssertEqualObjects(element.firstChild.nextSibling, firstChild);
+ XCTAssertEqualObjects(element.lastChild.previousSibling, firstChild);
+ XCTAssertEqualObjects(element.lastChild, secondChild);
+}
+
+- (void)testParentNode
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ XCTAssertNil(firstChild.parentNode);
+
+ [node appendNode:firstChild];
+ XCTAssertTrue(node.hasChildNodes);
+ XCTAssertEqual(node.childNodesCount, 1);
+
+ XCTAssertNotNil(firstChild.parentNode);
+ XCTAssertEqualObjects(firstChild.parentNode, node);
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+
+ [node appendNodes:@[secondChild, thirdChild]];
+ XCTAssertTrue(node.hasChildNodes);
+ XCTAssertEqual(node.childNodesCount, 3);
+
+ XCTAssertNotNil(secondChild.parentNode);
+ XCTAssertEqualObjects(secondChild.parentNode, node);
+
+ XCTAssertNotNil(thirdChild.parentNode);
+ XCTAssertEqualObjects(thirdChild.parentNode, node);
+}
+
+- (void)testChildNodesCount
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+ XCTAssertFalse(node.hasChildNodes);
+ XCTAssertEqual(node.childNodesCount, 0);
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ XCTAssertTrue(node.hasChildNodes);
+ XCTAssertEqual(node.childNodesCount, 1);
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ XCTAssertTrue(node.hasChildNodes);
+ XCTAssertEqual(node.childNodesCount, 2);
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node appendNode:thirdChild];
+
+ XCTAssertTrue(node.hasChildNodes);
+ XCTAssertEqual(node.childNodesCount, 3);
+}
+
+- (void)testFirstAndLastChildNodes
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ XCTAssertEqualObjects(node.firstChild, firstChild);
+ XCTAssertEqualObjects(node.lastChild, firstChild);
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ XCTAssertEqualObjects(node.firstChild, firstChild);
+ XCTAssertEqualObjects(node.lastChild, secondChild);
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node appendNode:thirdChild];
+
+ XCTAssertEqualObjects(node.firstChild, firstChild);
+ XCTAssertEqualObjects(node.lastChild, thirdChild);
+}
+
+- (void)testNextAndPreviousSiblingNodes
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node appendNode:thirdChild];
+
+ XCTAssertNil(firstChild.previousSibling);
+ XCTAssertEqualObjects(firstChild.nextSibling, secondChild);
+
+ XCTAssertEqualObjects(secondChild.previousSibling, firstChild);
+ XCTAssertEqualObjects(secondChild.nextSibling, thirdChild);
+
+ XCTAssertEqualObjects(thirdChild.previousSibling, secondChild);
+ XCTAssertNil(thirdChild.nextSibling);
+}
+
+- (void)testHasChildNodeOfType
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ XCTAssertFalse([node hasChildNodeOfType:HTMLNodeElement]);
+ XCTAssertFalse([node hasChildNodeOfType:HTMLNodeText]);
+ XCTAssertFalse([node hasChildNodeOfType:HTMLNodeComment]);
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ XCTAssertTrue([node hasChildNodeOfType:HTMLNodeElement]);
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeText];
+ [node appendNode:secondChild];
+
+ XCTAssertTrue([node hasChildNodeOfType:HTMLNodeText]);
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeComment];
+ [node appendNode:thirdChild];
+
+ XCTAssertTrue([node hasChildNodeOfType:HTMLNodeComment]);
+}
+
+- (void)testChildNodeAtIndex
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node appendNode:thirdChild];
+
+ XCTAssertEqualObjects([node childNodeAtIndex:0], firstChild);
+ XCTAssertEqualObjects([node childNodeAtIndex:1], secondChild);
+ XCTAssertEqualObjects([node childNodeAtIndex:2], thirdChild);
+}
+
+- (void)testIndexOfChildNode
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node appendNode:thirdChild];
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], 0);
+ XCTAssertEqual([node indexOfChildNode:secondChild], 1);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], 2);
+}
+
+- (void)testIndertNodeBeforeChild
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node insertNode:secondChild beforeChildNode:firstChild];
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], 1);
+ XCTAssertEqual([node indexOfChildNode:secondChild], 0);
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node insertNode:thirdChild beforeChildNode:firstChild];
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], 2);
+ XCTAssertEqual([node indexOfChildNode:secondChild], 0);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], 1);
+}
+
+- (void)testReplaceChildNode
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node replaceChildNode:firstChild withNode:thirdChild];
+
+ XCTAssertNil(firstChild.parentNode);
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:secondChild], 1);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], 0);
+}
+
+- (void)testReplaceAllChildNodes
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node replaceAllChildNodesWithNode:thirdChild];
+
+ XCTAssertNil(firstChild.parentNode);
+ XCTAssertNil(secondChild.parentNode);
+ XCTAssertEqualObjects(thirdChild.parentNode, node);
+
+ XCTAssertEqual(node.childNodesCount, 1);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], 0);
+}
+
+- (void)testRemoveFromParentNode
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ [firstChild removeFromParentNode];
+
+ XCTAssertNil(firstChild.parentNode);
+ XCTAssertEqual(node.childNodesCount, 0);
+ XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
+}
+
+- (void)testRemoveChildNode
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node appendNode:thirdChild];
+
+ [node removeChildNode:secondChild];
+ XCTAssertEqual(node.childNodesCount, 2);
+
+ XCTAssertEqualObjects(firstChild.parentNode, node);
+ XCTAssertNil(secondChild.parentNode);
+ XCTAssertEqualObjects(thirdChild.parentNode, node);
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], 0);
+ XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], 1);
+
+ [node removeChildNode:firstChild];
+ XCTAssertEqual(node.childNodesCount, 1);
+
+ XCTAssertNil(firstChild.parentNode);
+ XCTAssertNil(secondChild.parentNode);
+ XCTAssertEqualObjects(thirdChild.parentNode, node);
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], 0);
+}
+
+- (void)testRemoveChildNodeAtIndex
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node appendNode:thirdChild];
+
+ [node removeChildNodeAtIndex:1];
+ XCTAssertEqual(node.childNodesCount, 2);
+
+ XCTAssertEqualObjects(firstChild.parentNode, node);
+ XCTAssertNil(secondChild.parentNode);
+ XCTAssertEqualObjects(thirdChild.parentNode, node);
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], 0);
+ XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], 1);
+
+ [node removeChildNodeAtIndex:0];
+ XCTAssertEqual(node.childNodesCount, 1);
+
+ XCTAssertNil(firstChild.parentNode);
+ XCTAssertNil(secondChild.parentNode);
+ XCTAssertEqualObjects(thirdChild.parentNode, node);
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], 0);
+}
+
+- (void)testRemoveAllChildNodes
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node appendNode:thirdChild];
+
+ [node removeAllChildNodes];
+ XCTAssertEqual(node.childNodesCount, 0);
+
+ XCTAssertNil(firstChild.parentNode);
+ XCTAssertNil(secondChild.parentNode);
+ XCTAssertNil(thirdChild.parentNode);
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], NSNotFound);
+}
+
+- (void)testReparentChildNodes
+{
+ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
+
+ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
+ [node appendNode:firstChild];
+
+ HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
+ [node appendNode:secondChild];
+
+ HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
+ [node appendNode:thirdChild];
+
+ HTMLNode *newParent = [[HTMLNode alloc] initWithName:@"new-parent" type:HTMLNodeElement];
+
+ [node reparentChildNodesIntoNode:newParent];
+ XCTAssertEqual(node.childNodesCount, 0);
+
+ XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
+ XCTAssertEqual([node indexOfChildNode:thirdChild], NSNotFound);
+
+ XCTAssertEqualObjects(firstChild.parentNode, newParent);
+ XCTAssertEqualObjects(secondChild.parentNode, newParent);
+ XCTAssertEqualObjects(thirdChild.parentNode, newParent);
+}
+
+- (void)testElementSetInnerHTML
+{
+ HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
+ [element appendNode:[[HTMLElement alloc] initWithTagName:@"img"]];
+
+ [element setInnerHTML:@""];
+
+ XCTAssertEqual(element.childNodesCount, 2);
+
+ XCTAssertEqualObjects(element.firstChild.asElement.tagName, @"p");
+ XCTAssertEqualObjects([element childNodeAtIndex:1].asElement.tagName, @"p");
+}
+
+@end
diff --git a/HTMLKitTests/HTMLKitParserPerformance.m b/HTMLKitTests/HTMLKitParserPerformance.m
index f4839d0..005282f 100644
--- a/HTMLKitTests/HTMLKitParserPerformance.m
+++ b/HTMLKitTests/HTMLKitParserPerformance.m
@@ -15,6 +15,8 @@
@implementation HTMLKitParserPerformance
+#define HTMLKIT_NO_DOM_CHECKS
+
- (void)testParserPerformance
{
NSString *path = [[NSBundle bundleForClass:self.class] resourcePath];
@@ -28,4 +30,6 @@
}];
}
+#undef HTMLKIT_NO_DOM_CHECKS
+
@end
diff --git a/HTMLKitTests/HTMLKitTreeConstructionTests.m b/HTMLKitTests/HTMLKitTreeConstructionTests.m
index cfb7430..ca1f223 100644
--- a/HTMLKitTests/HTMLKitTreeConstructionTests.m
+++ b/HTMLKitTests/HTMLKitTreeConstructionTests.m
@@ -9,7 +9,7 @@
#import
#import "HTML5LibTreeConstructionTest.h"
-#import "HTMLNodes.h"
+#import "HTMLDOM.h"
#import "HTMLParser.h"
@@ -121,14 +121,14 @@
{
XCTAssertEqualObjects(actual.name, expected.name, @"Node name mismatch [%@ should be %@]:\n%@",
actual.name, expected.name, message);
- XCTAssert(actual.type == expected.type, @"Node type mismatch [%hd should be %hd]:\n%@",
- actual.type, expected.type, message);
+ XCTAssert(actual.nodeType == expected.nodeType, @"Node type mismatch [%hd should be %hd]:\n%@",
+ actual.nodeType, expected.nodeType, message);
- if (actual.type != expected.type) {
+ if (actual.nodeType != expected.nodeType) {
return;
}
- switch (actual.type) {
+ switch (actual.nodeType) {
case HTMLNodeDocumentType:
XCTAssertEqualObjects([(HTMLDocumentType *)actual publicIdentifier], [(HTMLDocumentType *)expected publicIdentifier], @"%@", message);
XCTAssertEqualObjects([(HTMLDocumentType *)actual systemIdentifier], [(HTMLDocumentType *)expected systemIdentifier], @"%@", message);
diff --git a/HTMLKitTests/HTMLTreeWalkerTests.m b/HTMLKitTests/HTMLTreeWalkerTests.m
new file mode 100644
index 0000000..6aeeaf2
--- /dev/null
+++ b/HTMLKitTests/HTMLTreeWalkerTests.m
@@ -0,0 +1,503 @@
+ //
+ // HTMLTreeWalkerTests.m
+ // HTMLKit
+ //
+ // Created by Iska on 05/06/15.
+ // Copyright (c) 2015 BrainCookie. All rights reserved.
+ //
+
+#import
+#import "HTMLTreeWalker.h"
+#import "HTMLDOM.h"
+
+@interface HTMLTreeWalkerTests : XCTestCase
+
+@end
+
+@implementation HTMLTreeWalkerTests
+
+#pragma mark - Asserts
+
+#define AssertElementWithId(input, id) \
+ do { \
+ HTMLNode *node = input;\
+ XCTAssertEqual(node.nodeType, HTMLNodeElement);\
+ XCTAssertEqualObjects(node.asElement[@"id"], id);\
+ } while(0)
+
+#define AssertTextWithValue(input, value) \
+ do { \
+ HTMLNode *node = input;\
+ XCTAssertEqual(node.nodeType, HTMLNodeText);\
+ XCTAssertEqualObjects(node.textContent, value);\
+ } while(0)
+
+#define AssertCommentWithValue(input, value) \
+ do { \
+ HTMLNode *node = input;\
+ XCTAssertEqual(node.nodeType, HTMLNodeComment);\
+ XCTAssertEqualObjects(node.textContent, value);\
+ } while(0)
+
+#pragma mark - Tests
+
+- (void)testTreeWalkerInit
+{
+ HTMLNode *root = self.basicWalkingDOM;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root];
+
+ XCTAssertNotNil(walker);
+ XCTAssertNotNil(walker.root);
+ XCTAssertNotNil(walker.currentNode);
+ XCTAssertNil(walker.filter);
+ XCTAssertEqual(walker.whatToShow, HTMLNodeFilterShowAll);
+
+ XCTAssertEqualObjects(walker.root, root);
+ XCTAssertEqualObjects(walker.root, walker.currentNode);
+}
+
+#pragma mark - Basic Walking
+
+- (HTMLNode *)basicWalkingDOM
+{
+ // Tree structure:
+ // #a
+ // |
+ // +----+----+
+ // | |
+ // "b" #c
+ // |
+ // +----+----+
+ // | |
+ // #d
+ // |
+ // +----+----+
+ // | | |
+ // "e" #f "i"
+ // |
+ // +--+--+
+ // | |
+ // "g"
+
+ HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"a"}];
+
+ [div appendNode:[[HTMLText alloc] initWithData:@"b"]];
+
+ HTMLElement *c = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"c"}];
+ [div appendNode:c];
+
+ HTMLElement *d = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"d"}];
+ [c appendNode:d];
+ [c appendNode:[[HTMLComment alloc] initWithData:@"j"]];
+
+ [d appendNode:[[HTMLText alloc] initWithData:@"e"]];
+
+ HTMLElement *f = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"f"}];
+ [d appendNode:f];
+ [d appendNode:[[HTMLText alloc] initWithData:@"i"]];
+
+ [f appendNode:[[HTMLText alloc] initWithData:@"g"]];
+ [f appendNode:[[HTMLComment alloc] initWithData:@"h"]];
+
+ return div;
+}
+
+- (void)testBasicWalking
+{
+ HTMLNode *root = self.basicWalkingDOM;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root];
+
+ HTMLNode *f = root.lastChild.firstChild.childNodes[1];
+
+ AssertElementWithId(walker.currentNode, @"a");
+ XCTAssertNil(walker.parentNode);
+ AssertElementWithId(walker.currentNode, @"a");
+
+ AssertTextWithValue(walker.firstChild, @"b");
+ AssertTextWithValue(walker.currentNode, @"b");
+
+ AssertElementWithId(walker.nextSibling, @"c");
+ AssertElementWithId(walker.currentNode, @"c");
+
+ AssertCommentWithValue(walker.lastChild, @"j");
+ AssertCommentWithValue(walker.currentNode, @"j");
+
+ AssertElementWithId(walker.previousSibling, @"d");
+ AssertElementWithId(walker.currentNode, @"d");
+
+ AssertTextWithValue(walker.nextNode, @"e");
+ AssertTextWithValue(walker.currentNode, @"e");
+
+ AssertElementWithId(walker.parentNode, @"d");
+ AssertElementWithId(walker.currentNode, @"d");
+
+ AssertElementWithId(walker.previousNode, @"c");
+ AssertElementWithId(walker.currentNode, @"c");
+
+ XCTAssertNil(walker.nextSibling);
+ AssertElementWithId(walker.currentNode, @"c");
+
+ walker.currentNode = f;
+ XCTAssertEqualObjects(walker.currentNode, f);
+}
+
+#pragma mark - Current Node
+
+- (HTMLDocument *)currentNodeDOM
+{
+ HTMLDocument *document = [HTMLDocument documentWithString:
+ @""
+ @"
"];
+
+ return document;
+}
+
+- (void)testThatTreeWalkerParentHasNoEffectCurrentNodeWhenParentIsNotUnderRoot
+{
+ HTMLDocument *document = self.currentNodeDOM;
+ HTMLNode *first = document.body.firstChild;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first
+ showOptions:HTMLNodeFilterShowElement
+ filter:nil];
+
+ AssertElementWithId(walker.currentNode, @"first");
+ XCTAssertNil(walker.parentNode);
+ AssertElementWithId(walker.currentNode, @"first");
+}
+
+- (void)testThatSettingCurrentNodeToNodesNotUnderRootIsHandledCorrectly
+{
+ HTMLDocument *document = self.currentNodeDOM;
+ HTMLNode *first = document.body.firstChild;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first
+ showOptions:HTMLNodeFilterShowElement|HTMLNodeFilterShowComment
+ filter:nil];
+ walker.currentNode = document.documentElement;
+ XCTAssertNil(walker.parentNode);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement);
+
+ walker.currentNode = document.documentElement;
+ XCTAssertEqualObjects(walker.nextNode, document.documentElement.firstChild);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChild);
+
+ walker.currentNode = document.documentElement;
+ XCTAssertNil(walker.previousNode);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement);
+
+ walker.currentNode = document.documentElement;
+ XCTAssertEqualObjects(walker.firstChild, document.documentElement.firstChild);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChild);
+
+ walker.currentNode = document.documentElement;
+ XCTAssertEqualObjects(walker.lastChild, document.documentElement.lastChild);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement.lastChild);
+
+ walker.currentNode = document.documentElement;
+ XCTAssertNil(walker.nextSibling);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement);
+
+ walker.currentNode = document.documentElement;
+ XCTAssertNil(walker.previousSibling);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement);
+}
+
+#pragma mark - Filter
+
+- (HTMLElement *)filterBasicDOM
+{
+ HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"root"}];
+
+ HTMLElement *a1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"A1"}];
+ HTMLElement *b1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B1"}];
+ HTMLElement *b2 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B2"}];
+
+ [root appendNode:a1];
+ [a1 appendNode:b1];
+ [a1 appendNode:b2];
+
+ return root;
+}
+
+- (void)testTreeWalkerNilFilter
+{
+ HTMLElement *root = self.filterBasicDOM;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:nil];
+
+ AssertElementWithId(walker.currentNode, @"root");
+ AssertElementWithId(walker.firstChild, @"A1");
+ AssertElementWithId(walker.currentNode, @"A1");
+ AssertElementWithId(walker.nextNode, @"B1");
+ AssertElementWithId(walker.currentNode, @"B1");
+}
+
+- (void)testTreeWalkerWithFilter
+{
+ HTMLElement *root = self.filterBasicDOM;
+
+ id
filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
+ if ([node.asElement[@"id"] isEqualToString:@"B1"]) {
+ return HTMLNodeFilterSkip;
+ }
+ return HTMLNodeFilterAccept;
+ }];
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ AssertElementWithId(walker.currentNode, @"root");
+ AssertElementWithId(walker.firstChild, @"A1");
+ AssertElementWithId(walker.currentNode, @"A1");
+ AssertElementWithId(walker.nextNode, @"B2");
+ AssertElementWithId(walker.currentNode, @"B2");
+}
+
+#pragma mark - Filter Skip
+
+- (HTMLElement *)filterDOM
+{
+ HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"root"}];
+
+ HTMLElement *a1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"A1"}];
+ HTMLElement *b1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B1"}];
+ HTMLElement *b2 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B2"}];
+ HTMLElement *b3 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B3"}];
+ HTMLElement *c1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"C1"}];
+
+ [root appendNode:a1];
+ [a1 appendNode:b1];
+ [a1 appendNode:b2];
+ [a1 appendNode:b3];
+ [b1 appendNode:c1];
+
+ return root;
+}
+
+- (id)skipB1Filter
+{
+ id filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
+ if ([node.asElement[@"id"] isEqualToString:@"B1"]) {
+ return HTMLNodeFilterSkip;
+ }
+ return HTMLNodeFilterAccept;
+ }];
+ return filter;
+}
+
+- (id)skipB2Filter
+{
+ id filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
+ if ([node.asElement[@"id"] isEqualToString:@"B2"]) {
+ return HTMLNodeFilterSkip;
+ }
+ return HTMLNodeFilterAccept;
+ }];
+ return filter;
+}
+
+- (id)rejectB1Filter
+{
+ id filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
+ if ([node.asElement[@"id"] isEqualToString:@"B1"]) {
+ return HTMLNodeFilterReject;
+ }
+ return HTMLNodeFilterAccept;
+ }];
+ return filter;
+}
+
+- (id)rejectB2Filter
+{
+ id filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
+ if ([node.asElement[@"id"] isEqualToString:@"B2"]) {
+ return HTMLNodeFilterReject;
+ }
+ return HTMLNodeFilterAccept;
+ }];
+ return filter;
+}
+
+static HTMLElement * (^ FindElementById)(HTMLNode *, NSString *) = ^ HTMLElement * (HTMLNode *root, NSString *id) {
+ for (HTMLNode *node in [root nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) {
+ if ([node.asElement[@"id"] isEqualToString:id]) {
+ return node.asElement;
+ }
+ }
+ return nil;
+};
+
+- (void)testThatFilterSkipsNextNode
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.skipB1Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ AssertElementWithId(walker.nextNode, @"A1");
+ AssertElementWithId(walker.nextNode, @"C1");
+ AssertElementWithId(walker.nextNode, @"B2");
+ AssertElementWithId(walker.nextNode, @"B3");
+}
+
+- (void)testThatFilterSkipsFirstChild
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.skipB1Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ AssertElementWithId(walker.firstChild, @"A1");
+ AssertElementWithId(walker.firstChild, @"C1");
+}
+
+- (void)testThatFilterSkipsNextSibling
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.skipB2Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ AssertElementWithId(walker.firstChild, @"A1");
+ AssertElementWithId(walker.firstChild, @"B1");
+ AssertElementWithId(walker.nextSibling, @"B3");
+}
+
+- (void)testThatFilterSkipsParentNode
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.skipB1Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ walker.currentNode = FindElementById(root, @"C1");
+ AssertElementWithId(walker.parentNode, @"A1");
+}
+
+- (void)testThatFilterSkipsPreviousSibling
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.skipB2Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ walker.currentNode = FindElementById(root, @"B3");
+ AssertElementWithId(walker.previousSibling, @"B1");
+}
+
+- (void)testThatFilterSkipsPreviousNode
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.skipB1Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ walker.currentNode = FindElementById(root, @"B3");
+ AssertElementWithId(walker.previousNode, @"B2");
+ AssertElementWithId(walker.previousNode, @"C1");
+ AssertElementWithId(walker.previousNode, @"A1");
+}
+
+#pragma mark - Filter Reject
+
+- (void)testThatFilterRejectsNextNode
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.rejectB1Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ AssertElementWithId(walker.nextNode, @"A1");
+ AssertElementWithId(walker.nextNode, @"B2");
+ AssertElementWithId(walker.nextNode, @"B3");
+}
+
+- (void)testThatFilterRejectsFirstChild
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.rejectB1Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ AssertElementWithId(walker.firstChild, @"A1");
+ AssertElementWithId(walker.firstChild, @"B2");
+}
+
+- (void)testThatFilterRejectsNextSibling
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.rejectB2Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ AssertElementWithId(walker.firstChild, @"A1");
+ AssertElementWithId(walker.firstChild, @"B1");
+ AssertElementWithId(walker.nextSibling, @"B3");
+}
+
+- (void)testThatFilterRejectsParentNode
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.rejectB1Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ walker.currentNode = FindElementById(root, @"C1");
+ AssertElementWithId(walker.parentNode, @"A1");
+}
+
+- (void)testThatFilterRejectsPreviousSibling
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.rejectB2Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ walker.currentNode = FindElementById(root, @"B3");
+ AssertElementWithId(walker.previousSibling, @"B1");
+}
+
+- (void)testThatFilterRejectsPreviousNode
+{
+ HTMLElement *root = self.filterDOM;
+ id filter = self.rejectB1Filter;
+
+ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
+ showOptions:HTMLNodeFilterShowElement
+ filter:filter];
+
+ walker.currentNode = FindElementById(root, @"B3");
+ AssertElementWithId(walker.previousNode, @"B2");
+ AssertElementWithId(walker.previousNode, @"A1");
+}
+
+
+@end