// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
package jodd.csselly;
import jodd.csselly.selector.AttributeSelector;
import jodd.csselly.selector.PseudoClass;
import jodd.csselly.selector.PseudoClassSelector;
import jodd.csselly.selector.PseudoFunctionExpression;
import jodd.csselly.selector.PseudoFunctionSelector;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.*;
public class CSSellyTest {
@Test
public void testSingleSelectors() {
CSSelly lexer = new CSSelly(" div ");
assertEquals("div", CSSelly.toString(lexer.parse()));
lexer = new CSSelly("div#id");
assertEquals("div#id", CSSelly.toString(lexer.parse()));
lexer = new CSSelly("div.klas");
assertEquals("div.klas", CSSelly.toString(lexer.parse()));
lexer = new CSSelly("div.k1.k2");
List<CssSelector> selectors = lexer.parse();
assertEquals("div.k1.k2", CSSelly.toString(selectors));
assertEquals(1, selectors.size());
assertEquals(2, selectors.get(0).selectorsCount());
lexer = new CSSelly("*");
assertEquals("*", CSSelly.toString(lexer.parse()));
lexer = new CSSelly("*.kl");
assertEquals("*.kl", CSSelly.toString(lexer.parse()));
lexer = new CSSelly(".kl");
assertEquals("*.kl", CSSelly.toString(lexer.parse()));
lexer = new CSSelly("#idi");
selectors = lexer.parse();
assertEquals("*#idi", CSSelly.toString(selectors));
assertEquals(1, selectors.size());
assertEquals(1, selectors.get(0).selectorsCount());
}
@Test
public void testMultipleSelectors() {
CSSelly lexer = new CSSelly(" div b#xo foo.solid #jodd * #bib.box.red ");
List<CssSelector> selectors = lexer.parse();
assertEquals("div b#xo foo.solid *#jodd * *#bib.box.red", CSSelly.toString(selectors));
assertEquals(6, selectors.size());
assertEquals(0, selectors.get(4).selectorsCount());
assertEquals(3, selectors.get(5).selectorsCount());
CssSelector first = selectors.get(0);
CssSelector second = selectors.get(1);
CssSelector third = selectors.get(2);
CssSelector fourth = selectors.get(3);
CssSelector fifth = selectors.get(4);
CssSelector sixt = selectors.get(5);
assertNull(first.getPrevCssSelector());
assertEquals(second, first.getNextCssSelector());
assertEquals(first, second.getPrevCssSelector());
assertEquals(third, second.getNextCssSelector());
assertEquals(second, third.getPrevCssSelector());
assertEquals(fourth, third.getNextCssSelector());
assertEquals(third, fourth.getPrevCssSelector());
assertEquals(fifth, fourth.getNextCssSelector());
assertEquals(fourth, fifth.getPrevCssSelector());
assertEquals(sixt, fifth.getNextCssSelector());
assertEquals(fifth, sixt.getPrevCssSelector());
assertNull(sixt.getNextCssSelector());
}
@Test
public void testAttributes() {
CSSelly lexer = new CSSelly("div[a1='123']");
List<CssSelector> selectors = lexer.parse();
assertEquals(1, selectors.size());
assertEquals("div[a1='123']", CSSelly.toString(selectors));
CssSelector cssSelector = selectors.get(0);
assertEquals("div", cssSelector.getElement());
assertEquals(1, cssSelector.selectorsCount());
Selector selector = cssSelector.getSelector(0);
assertEquals(Selector.Type.ATTRIBUTE, selector.getType());
AttributeSelector attributeSelector = (AttributeSelector) selector;
assertEquals("a1", attributeSelector.getName());
assertEquals("=", attributeSelector.getMatch().getSign());
assertEquals("123", attributeSelector.getValue());
lexer = new CSSelly("div[ a1 = \"123\" ]");
selectors = lexer.parse();
assertEquals("div[a1=\"123\"]", CSSelly.toString(selectors));
}
@Test
public void testCombinators() {
CSSelly lexer = new CSSelly("div b");
List<CssSelector> selectors = lexer.parse();
assertEquals(2, selectors.size());
CssSelector cssSelector = selectors.get(0);
assertEquals(Combinator.DESCENDANT, cssSelector.getCombinator());
cssSelector = selectors.get(1);
assertNull(cssSelector.getCombinator());
lexer = new CSSelly("div > b");
selectors = lexer.parse();
assertEquals(2, selectors.size());
cssSelector = selectors.get(0);
assertEquals(Combinator.CHILD, cssSelector.getCombinator());
cssSelector = selectors.get(1);
assertNull(cssSelector.getCombinator());
lexer = new CSSelly("div>b");
selectors = lexer.parse();
assertEquals(2, selectors.size());
cssSelector = selectors.get(0);
assertEquals(Combinator.CHILD, cssSelector.getCombinator());
cssSelector = selectors.get(1);
assertNull(cssSelector.getCombinator());
lexer = new CSSelly("div>b + span ~ i");
selectors = lexer.parse();
assertEquals(4, selectors.size());
cssSelector = selectors.get(0);
assertEquals(Combinator.CHILD, cssSelector.getCombinator());
cssSelector = selectors.get(1);
assertEquals(Combinator.ADJACENT_SIBLING, cssSelector.getCombinator());
cssSelector = selectors.get(2);
assertEquals(Combinator.GENERAL_SIBLING, cssSelector.getCombinator());
cssSelector = selectors.get(3);
assertNull(cssSelector.getCombinator());
}
@Test
public void testPseudoClasses() {
CSSelly lexer = new CSSelly("div:first-child");
List<CssSelector> selectors = lexer.parse();
assertEquals(1, selectors.size());
assertEquals("div:first-child", CSSelly.toString(selectors));
CssSelector cssSelector = selectors.get(0);
assertEquals(1, cssSelector.selectorsCount());
PseudoClassSelector psc = (PseudoClassSelector) cssSelector.getSelector(0);
assertEquals("first-child", psc.getPseudoClass().getPseudoClassName());
}
@Test
public void testPseudoFunctions() {
CSSelly lexer = new CSSelly("div:nth-child(2n+1)");
List<CssSelector> selectors = lexer.parse();
assertEquals(1, selectors.size());
assertEquals("div:nth-child(2n+1)", CSSelly.toString(selectors));
CssSelector cssSelector = selectors.get(0);
assertEquals(1, cssSelector.selectorsCount());
PseudoFunctionSelector pfns = (PseudoFunctionSelector) cssSelector.getSelector(0);
assertEquals("nth-child", pfns.getPseudoFunction().getPseudoFunctionName());
PseudoFunctionExpression pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(2, pfe.getValueA());
assertEquals(1, pfe.getValueB());
lexer = new CSSelly("div:nth-child(odd)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
assertEquals("nth-child", pfns.getPseudoFunction().getPseudoFunctionName());
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(2, pfe.getValueA());
assertEquals(1, pfe.getValueB());
lexer = new CSSelly("div:nth-child(even)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
assertEquals("nth-child", pfns.getPseudoFunction().getPseudoFunctionName());
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(2, pfe.getValueA());
assertEquals(0, pfe.getValueB());
lexer = new CSSelly("div:nth-child(10n-1)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(10, pfe.getValueA());
assertEquals(-1, pfe.getValueB());
lexer = new CSSelly("div:nth-child(10n+9)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(10, pfe.getValueA());
assertEquals(9, pfe.getValueB());
lexer = new CSSelly("div:nth-child(0n+5)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(0, pfe.getValueA());
assertEquals(5, pfe.getValueB());
lexer = new CSSelly("div:nth-child(5)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(0, pfe.getValueA());
assertEquals(5, pfe.getValueB());
lexer = new CSSelly("div:nth-child(1n + 0)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(1, pfe.getValueA());
assertEquals(0, pfe.getValueB());
lexer = new CSSelly("div:nth-child(n + 0)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(1, pfe.getValueA());
assertEquals(0, pfe.getValueB());
lexer = new CSSelly("div:nth-child(n)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(1, pfe.getValueA());
assertEquals(0, pfe.getValueB());
lexer = new CSSelly("div:nth-child(2n+0)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(2, pfe.getValueA());
assertEquals(0, pfe.getValueB());
lexer = new CSSelly("div:nth-child(2n)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(2, pfe.getValueA());
assertEquals(0, pfe.getValueB());
lexer = new CSSelly("div:nth-child( 3n + 1 )");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(3, pfe.getValueA());
assertEquals(1, pfe.getValueB());
lexer = new CSSelly("div:nth-child( +3n - 2 )");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(3, pfe.getValueA());
assertEquals(-2, pfe.getValueB());
lexer = new CSSelly("div:nth-child( -n+ 6)");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(-1, pfe.getValueA());
assertEquals(6, pfe.getValueB());
lexer = new CSSelly("div:nth-child( +6 )");
pfns = (PseudoFunctionSelector) lexer.parse().get(0).getSelector(0);
pfe = (PseudoFunctionExpression) pfns.getParsedExpression();
assertEquals(0, pfe.getValueA());
assertEquals(6, pfe.getValueB());
}
@Test
public void testErrors() {
try {
CSSelly lexer = new CSSelly("div ^ b");
lexer.parse();
fail();
} catch (CSSellyException ex) {
}
try {
CSSelly lexer = new CSSelly("div:wrong-pseudo-class-name");
lexer.parse();
fail();
} catch (CSSellyException ex) {
}
try {
CSSelly lexer = new CSSelly("div:nth-child(xxx)");
lexer.parse();
} catch (CSSellyException ex) {
}
}
@Test
public void testUppercaseClassNames() {
CSSelly lexer = new CSSelly("div.fooBar");
List<CssSelector> selectorList = lexer.parse();
assertEquals(1, selectorList.size());
}
@Test
public void testEscape() {
// element
CSSelly lexer = new CSSelly("itunes\\:image");
List<CssSelector> selectors = lexer.parse();
assertEquals(1, selectors.size());
CssSelector cssSelector = selectors.get(0);
assertEquals("itunes:image", cssSelector.getElement());
// attribute
lexer = new CSSelly("itunes\\:image#foo\\:bar");
selectors = lexer.parse();
assertEquals(1, selectors.size());
cssSelector = selectors.get(0);
assertEquals("itunes:image", cssSelector.getElement());
Selector selector = cssSelector.getSelector(0);
AttributeSelector attributeSelector = (AttributeSelector)selector;
assertEquals("foo:bar", attributeSelector.getValue());
}
@Test
public void testDoubleColon() {
CSSelly lexer = new CSSelly("foo::image");
List<CssSelector> selectors = lexer.parse();
assertEquals(1, selectors.size());
CssSelector cssSelector = selectors.get(0);
PseudoClassSelector pseudoClassSelector = (PseudoClassSelector) cssSelector.getSelector(0);
assertEquals("image", pseudoClassSelector.getPseudoClass().getPseudoClassName());
lexer = new CSSelly("foo::contains(xxx)");
selectors = lexer.parse();
assertEquals(1, selectors.size());
cssSelector = selectors.get(0);
PseudoFunctionSelector pseudoFunctionSelector = (PseudoFunctionSelector) cssSelector.getSelector(0);
assertEquals("contains", pseudoFunctionSelector.getPseudoFunction().getPseudoFunctionName());
}
@Test
public void test301() {
CSSelly lexerA = new CSSelly("input:not(':checked')");
CSSelly lexerB = new CSSelly("input:not(:checked)");
List<CssSelector> selectorsA = lexerA.parse();
List<CssSelector> selectorsB = lexerB.parse();
assertEquals(1, selectorsA.size());
assertEquals(1, selectorsB.size());
CssSelector cssSelectorA = selectorsA.get(0);
CssSelector cssSelectorB = selectorsB.get(0);
PseudoFunctionSelector pseudoFunctionSelectorA = (PseudoFunctionSelector) cssSelectorA.getSelector(0);
assertEquals("':checked'", pseudoFunctionSelectorA.getExpression());
PseudoFunctionSelector pseudoFunctionSelectorB = (PseudoFunctionSelector) cssSelectorB.getSelector(0);
assertEquals(":checked", pseudoFunctionSelectorB.getExpression());
List peA = (List) pseudoFunctionSelectorA.getParsedExpression();
assertEquals(1, peA.size());
assertEquals(1, ((List)peA.get(0)).size());
List peB = (List) pseudoFunctionSelectorB.getParsedExpression();
assertEquals(1, peB.size());
assertEquals(1, ((List)peB.get(0)).size());
CssSelector lastSelectorA = (CssSelector) ((List)peA.get(0)).get(0);
PseudoClassSelector pcsA = (PseudoClassSelector) lastSelectorA.selectors.get(0);
CssSelector lastSelectorB = (CssSelector) ((List)peB.get(0)).get(0);
PseudoClassSelector pcsB = (PseudoClassSelector) lastSelectorB.selectors.get(0);
assertEquals(PseudoClass.CHECKED.class.getSimpleName().toLowerCase(), pcsA.getPseudoClass().getPseudoClassName());
assertEquals(PseudoClass.CHECKED.class.getSimpleName().toLowerCase(), pcsB.getPseudoClass().getPseudoClassName());
}
@Test
public void test407() {
CSSelly lexer = new CSSelly("div:nth-child(2) > div:nth-child(1)");
List<CssSelector> selectors = lexer.parse();
assertEquals(2, selectors.size());
CssSelector s1 = selectors.get(0);
assertEquals(1, s1.selectorsCount());
assertEquals(Selector.Type.PSEUDO_FUNCTION, s1.getSelector(0).getType());
assertEquals("nth-child", ((PseudoFunctionSelector)s1.getSelector(0)).getPseudoFunction().getPseudoFunctionName());
CssSelector s2 = selectors.get(0);
assertEquals(1, s2.selectorsCount());
assertEquals(Selector.Type.PSEUDO_FUNCTION, s2.getSelector(0).getType());
assertEquals("nth-child", ((PseudoFunctionSelector)s2.getSelector(0)).getPseudoFunction().getPseudoFunctionName());
}
}