This is an Important paragraph
" + @"" + @""; + + HTMLDocument *document = [HTMLDocument documentWithString:htmlString]; + return document; +} + +#pragma mark - Test Iterator + +- (void)testNodeIteratorInit +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.root, tree); + XCTAssertEqualObjects(iterator.referenceNode, tree); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); + XCTAssertEqualObjects(iterator.filter, nil); + XCTAssertEqual(iterator.whatToShow, HTMLNodeFilterShowAll); +} + +- (void)testNewIteratorNextNodeShouldBeRoot +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.nextNode, tree); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + +- (void)testNewIteratorPreviousNodeShouldBeNil +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.previousNode, nil); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); +} + +- (void)testNewIteratorPreviousNodeShouldBeNextNode +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + HTMLNode *node = iterator.nextNode; + + XCTAssertEqualObjects(iterator.previousNode, node); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); +} + +- (void)testNextPreviousIteration +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.previousNode, nil); + XCTAssertEqualObjects(iterator.nextNode.name, @"div"); + XCTAssertEqualObjects(iterator.nextNode.name, @"a"); + XCTAssertEqualObjects(iterator.previousNode.name, @"a"); + XCTAssertEqualObjects(iterator.previousNode.name, @"div"); + XCTAssertEqualObjects(iterator.nextNode.name, @"div"); + XCTAssertEqualObjects(iterator.nextNode.name, @"a"); + XCTAssertEqualObjects(iterator.nextNode.name, @"b"); + XCTAssertEqualObjects(iterator.nextNode.name, @"c"); + XCTAssertEqualObjects(iterator.previousNode.name, @"c"); + XCTAssertEqualObjects(iterator.previousNode.name, @"b"); + XCTAssertEqualObjects(iterator.previousNode.name, @"a"); + XCTAssertEqualObjects(iterator.previousNode.name, @"div"); +} + +- (void)testSingleNodeIteration +{ + HTMLElement *div = self.div; + NSArray *result = div.nodeIterator.allObjects; + NSArray *expected = @[@"div"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testSimpleTreeIteration +{ + HTMLElement *tree = self.simpleTree; + NSArray *result = tree.nodeIterator.allObjects; + NSArray *expected = @[@"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testNestedSimpleTreeIteration +{ + HTMLElement *tree = self.nestedSimpleTree; + NSArray *result = tree.nodeIterator.allObjects; + NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testComplexTreeIteration +{ + HTMLElement *tree = self.complexTree; + NSArray *result = tree.nodeIterator.allObjects; + NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +#pragma mark - Test Iterator ShowOptions (WhatToShow) + +- (void)testShowDocument +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowDocument filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#document"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqual([result.firstObject class], [HTMLDocument class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowDocumentType +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowDocumentType filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"html"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqual([result.firstObject class], [HTMLDocumentType class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowComment +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowComment filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#comment", @"#comment"]; + XCTAssertEqual(result.count, 2); + XCTAssertEqual([result[0] class], [HTMLComment class]); + XCTAssertEqual([result[1] class], [HTMLComment class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowText +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowText filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#text"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqual([result.firstObject class], [HTMLText class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowElement +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"html", @"div"]; + XCTAssertEqual(result.count, 2); + XCTAssertEqual([result.firstObject class], [HTMLElement class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowBitmask +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowElement | HTMLNodeFilterShowText + filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"html", @"#text", @"div"]; + XCTAssertEqual(result.count, 3); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +#pragma mark - Test Iterator Filter + +- (void)testNodeFilter +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowAll + filterBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if (node.nodeType == HTMLNodeComment) { + if ([[(HTMLComment *)node data] rangeOfString:@"second"].location != NSNotFound) { + return HTMLNodeFilterAccept; + } + } + return HTMLNodeFilterSkip; + }]; + + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#comment"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqualObjects([result[0] data], @"This is a second Comment"); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +#pragma mark - Test Removing Steps + +/* + Test cases for the Removing Steps + https://dom.spec.whatwg.org/#interface-nodeiterator + + Following DOM is used: + +| +| +|
+| "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:
+ 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:
+ 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 = @" "];
+
+ 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