// 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()); } }