Skip to content

Latest commit

 

History

History

NSelene

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Info

This directory contains a replica of NSelene - Selenide "from scratch port" to .NET of Яков Крамаренко, modified to build with C# 5.0 and Nuget 2.6.4. This allowing development in the SharpDevelop IDE which is far less resource-aggressive then Visual Studio.

The fork for upstreaam pulls and pull requests is located in standalone fork of the same repository.

Added Features

Support for XPath and Text Selector

The kind of selector can be defined through prefix in the argument

S("css = h1[name='hello']")
S("xpath = /h1[1]")
S("text = Hello")

or extra argument, or selector kind detection method (work in progress)

In the absence of the prefix the css Selector kind is used, for backward compatilility.

Validating CSS Selectors

The project cssSelector to XPath convertor (java) is already implemented lexer for cssSelector grammar.

The CssSelectorStringSplitter.java class defines ELEMENT_AND_ATTRIBUTE_FOLLOWED_BY_COMBINATOR_AND_REST_OF_LINE regexp grammar that can be fed into core java regexp Pattern class which will then capture head selector + optional attribute token and the rest (tail) of the valid cssSelector string:

ELEMENT_AND_ATTRIBUTE_FOLLOWED_BY_COMBINATOR_AND_REST_OF_LINE: ^([^ ~+>\[]*(\[[^\]]+\])*)($|(\s*([ ~+>])\s*([^ ~+>].*)$))

The CssElementAttributeParser.java class defines CSS_ATTRIBUTE PATTERN grammar that can be fed into core java regexp Pattern class which will then allow separating element an its attribute for one token.

CSS_ATTRIBUTE PATTERN: ^((-?[_a-zA-Z]+[_a-zA-Z0-9-]*)|([*]))?((:[a-z][a-z\-]*([(][^)]+[)])?)|(\[\s*(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)\s*((\=)|(\~=)|(\|=)|(\^=)|(\$=)|(\*=))?\s*(((["'])([-_.#a-zA-Z0-9:\/ ]+)(["']))|(([-_.#a-zA-Z0-9:\/]+)))?\s*\]))*$

For our purpose it is sufficient to confirm that the expression satisfies both grammars, probably simplify them further too: The following example is in Java, but comverison is simple, as it only uses

	@Test(enabled = true)
	public void cssSelectorComprehensiveTokenTest() {
    String cssSelectorString = "body > h1[name='hello'] h2:nth-of-type(1)";
		// String tokenTest = cssValidator.getTokenTest();
    // modified: the original implementation appears broken
		String  tokenTest = "^([^ ~+>\\[]*(?:\\[[^\\]]+\\])*)(?:\\s*[ ~+>]\\s*([^ ~+>\\[].*))*$";
		System.err.println(tokenTest);
		Pattern pattern = Pattern.compile(tokenTest);
		Matcher match = pattern.matcher(cssSelectorString);

		boolean found_token = true;
		boolean found_tail = true;
		List<String> tokenBuffer = new ArrayList<>();
		List<String> tailBuffer = new ArrayList<>();
		int cnt = 0;
		while (match.find() && found_token && found_tail && cnt < 100) {

			if (match.group(1) == null || match.group(1) == "") {
				found_token = false;
			}
			if (match.group(2) == null || match.group(2) == "") {
				found_tail = false;
			}
			if (found_token) {
				tokenBuffer.add(match.group(1));
				System.err
						.println(String.format("Token = \"%s\"", tokenBuffer.get(cnt)));
			}
			if (found_tail) {
				tailBuffer.add(match.group(2));
				System.err.println("Tail = " + tailBuffer.get(cnt));
				match = pattern.matcher(match.group(2));
			}
			cnt++;
		}
    // String conditionTest = cssValidator.getConditionTest();
   // unmodified. optimization is likely possible
   String conditionTest = "^((-?[_a-zA-Z]+[_a-zA-Z0-9-]*)|([*]))?((:[a-z][a-z\\-]*([(][^)]+[)])?)|(\\[\\s*(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)\\s*((\\=)|(\\~=)|(\\|=)|(\\^=)|(\\$=)|(\\*=))?\\s*(((["'])([-_.#a-zA-Z0-9:\\/ ]+)(["']))|(([-_.#a-zA-Z0-9:\\/]+)))?\\s*\\]))*$";
		for (String cssSelectorTokenString : tokenBuffer) {
			assertTrue(
					cssSelectorTokenString.matches(conditionTest));
		}
}

similar for XPath candidate expression (currently, sans ConditionTest):

		// TODO: convert into xpathValidator public method
		String xpathString = "a[@class='main']/b//c[contains(text(),'hello world')]";
		// final String tokenValidator = xpathValidator.getTokenValidator();
    final String tokenValidator = "^\\s*(/?/?\\s*[^ /\\[]+(?:\\[[^\\]]+\\])*)($|\\s*//?\\s*[^ /\\[]+.*$)";
		Pattern pattern = Pattern.compile(tokenValidator);
		Matcher match = pattern.matcher(xpathString);

		boolean found_token = true;
		boolean foundRemainder = true;
		List<String> tokenBuffer = new ArrayList<>();
		List<String> tailBuffer = new ArrayList<>();
		int cnt = 0; // paranoid
		while (match.find() && found_token && foundRemainder && cnt < 100) {

			if (match.group(1) == null || match.group(1) == "") {
				found_token = false;
			}
			if (match.group(2) == null || match.group(2) == "") {
				foundRemainder = false;
			}
			if (found_token) {
				tokenBuffer.add(match.group(1));
				if (debug) {
					System.err.println(
							String.format("Extracted token: \"%s\"", tokenBuffer.get(cnt)));
				}
			}
			if (foundRemainder) {
				String remainder = match.group(2);
				tailBuffer.add(remainder);
				if (debug) {
					System.err.println(
							String.format("Remaining of the xpath: \"%s\"", remainder));
				}
				match = pattern.matcher(remainder);
			} else {
				if (debug) {
					System.err.println("Tail fails to match. ");
				}
			}
			cnt++;
		}

Limitations

In this version of the project temporarily dropped support for using "static" feature of c# 6.0

See Also

* [selenide article](http://qa7.ru/blog/2016/08/15/selenide-post/)
* [selenide article (in Russian)](https://habr.com/company/jugru/blog/416757/)
* [selenide presentation](https://docs.google.com/presentation/d/1kuzqR8JGnVKIs2r0Bm83LdOfbZkSkoR93f1c8wd26ns/edit?usp=sharing)
* [cssSelector to XPath convertor](https://github.com/sam-rosenthal/java-cssSelector-to-xpath), java, grammar based - can be used for quick assertion of a valid cssSelectors
* [XPath parser, C#](https://github.com/quamotion/XPathParser) , does not seem to be grammar based, but can be simly add to package.config and used for quick assertion of a valid XPaths

Author

Serguei Kouzmine