"];
+
+ return document;
+}
+
+- (void)testThatTreeWalkerParentHasNoEffectCurrentNodeWhenParentIsNotUnderRoot
+{
+ HTMLDocument *document = self.currentNodeDOM;
+ HTMLNode *first = document.body.firstChiledNode;
+
+ 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.firstChiledNode;
+
+ 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.firstChiledNode);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChiledNode);
+
+ walker.currentNode = document.documentElement;
+ XCTAssertNil(walker.previousNode);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement);
+
+ walker.currentNode = document.documentElement;
+ XCTAssertEqualObjects(walker.firstChild, document.documentElement.firstChiledNode);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChiledNode);
+
+ walker.currentNode = document.documentElement;
+ XCTAssertEqualObjects(walker.lastChild, document.documentElement.lastChildNode);
+ XCTAssertEqualObjects(walker.currentNode, document.documentElement.lastChildNode);
+
+ 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