/**
* Copyright (c) 2009-2015, Christer Sandberg
*/
package se.fishtank.css.selectors.matching;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import se.fishtank.css.selectors.Support;
import se.fishtank.css.selectors.dom.W3CNode;
import se.fishtank.css.selectors.dom.Traversal;
import se.fishtank.css.selectors.dom.Visitor;
import se.fishtank.css.selectors.parser.SelectorParser;
import se.fishtank.css.selectors.selector.PseudoFunctionSelector;
import se.fishtank.css.selectors.selector.Selector;
import se.fishtank.css.selectors.selector.SimpleSelector;
import static org.junit.Assert.assertEquals;
/**
* Tests the {@linkplain se.fishtank.css.selectors.matching.SelectorMatcher}
*
* @author Christer Sandberg
*/
public class SelectorMatcherTest {
private static final W3CNode ROOT = Support.getTestDocument();
@Test
public void testSelectorMatching() {
final SelectorMatcher<W3CNode> selectorMatcher = new SelectorMatcher<>();
for (Map.Entry<String, Integer> entry : createTestSelectorsMap().entrySet()) {
String selector = entry.getKey();
int expectedCount = entry.getValue();
List<Selector> selectors = SelectorParser.parse(selector);
CountingVisitor visitor = new CountingVisitor(selectorMatcher, selectors);
Traversal.traverseElements(ROOT, visitor);
assertEquals(selector, expectedCount, visitor.count);
}
}
@Test
public void testSelectorMatcherWithSimpleSelectorMatcher() {
SelectorMatcher<W3CNode> selectorMatcher = new SelectorMatcher<>(new ContainsMatcher());
CountingVisitor visitor1 = new CountingVisitor(selectorMatcher, SelectorParser.parse("h3:contains('palace')"));
Traversal.traverseElements(ROOT, visitor1);
assertEquals(1, visitor1.count);
CountingVisitor visitor2 = new CountingVisitor(selectorMatcher, SelectorParser.parse(":contains('Boom')"));
Traversal.traverseElements(ROOT, visitor2);
assertEquals(0, visitor2.count);
}
private static LinkedHashMap<String, Integer> createTestSelectorsMap() {
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put(":root", 1);
map.put(":empty", 2);
map.put("div:first-child", 51);
map.put("div:nth-child(even)", 106);
map.put("div:nth-child(2n)", 106);
map.put("div:nth-child(odd)", 137);
map.put("div:nth-child(2n+1)", 137);
map.put("div:nth-child(n)", 243);
map.put("script:first-of-type", 1);
map.put("div:last-child", 53);
map.put("script:last-of-type", 1);
map.put("script:nth-last-child(odd)", 1);
map.put("script:nth-last-child(even)", 1);
map.put("script:nth-last-child(5)", 0);
map.put("script:nth-of-type(2)", 1);
map.put("script:nth-last-of-type(n)", 2);
map.put("div:only-child", 22);
map.put("meta:only-of-type", 1);
map.put("div > div", 242);
map.put("div + div", 190);
map.put("div ~ div", 190);
map.put("body", 1);
map.put("body div", 243);
map.put("div", 243);
map.put("div div", 242);
map.put("div div div", 241);
map.put("div, div, div", 243);
map.put("div, a, span", 243);
map.put(".dialog", 51);
map.put("div.dialog", 51);
map.put("div .dialog", 51);
map.put("div.character, div.dialog", 99);
map.put("#speech5", 1);
map.put("div#speech5", 1);
map.put("div #speech5", 1);
map.put("div.scene div.dialog",49);
map.put("div#scene1 div.dialog div", 142);
map.put("#scene1 #speech1", 1);
map.put("div[class]", 103);
map.put("div[class=dialog]", 50);
map.put("div[class^=dia]", 51);
map.put("div[class$=log]", 50);
map.put("div[class*=sce]", 1);
map.put("div[class|=dialog]", 50);
map.put("div[class~=dialog]", 51);
map.put("head > :not(meta)", 2);
map.put("head > :not(:last-child)", 2);
return map;
}
static class CountingVisitor implements Visitor<W3CNode> {
final SelectorMatcher<W3CNode> selectorMatcher;
final List<Selector> selectors;
int count = 0;
CountingVisitor(SelectorMatcher<W3CNode> selectorMatcher, List<Selector> selectors) {
this.selectorMatcher = selectorMatcher;
this.selectors = selectors;
}
@Override
public void visit(W3CNode node) {
if (selectorMatcher.matchesSelectors(selectors, node)) {
++count;
}
}
}
static class ContainsMatcher implements SimpleSelectorMatcher<W3CNode> {
@Override
public boolean matches(SimpleSelector simpleSelector, W3CNode node) {
if (!(simpleSelector instanceof PseudoFunctionSelector)) {
return false;
}
PseudoFunctionSelector selector = (PseudoFunctionSelector) simpleSelector;
if (!selector.name.equals("contains")) {
return false;
}
String text = node.getUnderlying().getTextContent();
return text != null && text.contains(selector.arguments);
}
}
}