
"""
Copyright 2011 Software Freedom Conservancy.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from __future__ import unicode_literals

__docformat__ = "restructuredtext en"

try:
    import http.client as http_client
except ImportError:
    import httplib as http_client

try:
    import urllib.parse as urllib_parse
except ImportError:
    import urllib as urllib_parse

class selenium(object):
    """
    Defines an object that runs Selenium commands.

    **Element Locators**

    Element Locators tell Selenium which HTML element a command refers to.
    The format of a locator is:

    \ *locatorType*\ **=**\ \ *argument*


    We support the following strategies for locating elements:


    *   \ **identifier**\ =\ *id*:
        Select the element with the specified @id attribute. If no match is
        found, select the first element whose @name attribute is \ *id*.
        (This is normally the default; see below.)
    *   \ **id**\ =\ *id*:
        Select the element with the specified @id attribute.
    *   \ **name**\ =\ *name*:
        Select the first element with the specified @name attribute.

        *   username
        *   name=username


        The name may optionally be followed by one or more \ *element-filters*, separated from the name by whitespace.  If the \ *filterType* is not specified, \ **value**\  is assumed.

        *   name=flavour value=chocolate


    *   \ **dom**\ =\ *javascriptExpression*:

        Find an element by evaluating the specified string.  This allows you to traverse the HTML Document Object
        Model using JavaScript.  Note that you must not return a value in this string; simply make it the last expression in the block.

        *   dom=document.forms['myForm'].myDropdown
        *   dom=document.images[56]
        *   dom=function foo() { return document.links[1]; }; foo();


    *   \ **xpath**\ =\ *xpathExpression*:
        Locate an element using an XPath expression.

        *   xpath=//img[@alt='The image alt text']
        *   xpath=//table[@id='table1']//tr[4]/td[2]
        *   xpath=//a[contains(@href,'#id1')]
        *   xpath=//a[contains(@href,'#id1')]/@class
        *   xpath=(//table[@class='stylee'])//th[text()='theHeaderText']/../td
        *   xpath=//input[@name='name2' and @value='yes']
        *   xpath=//\*[text()="right"]


    *   \ **link**\ =\ *textPattern*:
        Select the link (anchor) element which contains text matching the
        specified \ *pattern*.

        *   link=The link text


    *   \ **css**\ =\ *cssSelectorSyntax*:
        Select the element using css selectors. Please refer to CSS2 selectors, CSS3 selectors for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.

        *   css=a[href="#id3"]
        *   css=span#firstChild + span


        Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after).

    *   \ **ui**\ =\ *uiSpecifierString*:
        Locate an element by resolving the UI specifier string to another locator, and evaluating it. See the Selenium UI-Element Reference for more details.

        *   ui=loginPages::loginButton()
        *   ui=settingsPages::toggle(label=Hide Email)
        *   ui=forumPages::postBody(index=2)//a[2]





    Without an explicit locator prefix, Selenium uses the following default
    strategies:


    *   \ **dom**\ , for locators starting with "document."
    *   \ **xpath**\ , for locators starting with "//"
    *   \ **identifier**\ , otherwise

    **Element Filters**

    Element filters can be used with a locator to refine a list of candidate elements.  They are currently used only in the 'name' element-locator.

    Filters look much like locators, ie.

    \ *filterType*\ **=**\ \ *argument*

    Supported element-filters are:

    \ **value=**\ \ *valuePattern*


    Matches elements based on their values.  This is particularly useful for refining a list of similarly-named toggle-buttons.

    \ **index=**\ \ *index*


    Selects a single element based on its position in the list (offset from zero).

    **String-match Patterns**

    Various Pattern syntaxes are available for matching string values:


    *   \ **glob:**\ \ *pattern*:
        Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
        kind of limited regular-expression syntax typically used in command-line
        shells. In a glob pattern, "\*" represents any sequence of characters, and "?"
        represents any single character. Glob patterns match against the entire
        string.
    *   \ **regexp:**\ \ *regexp*:
        Match a string using a regular-expression. The full power of JavaScript
        regular-expressions is available.
    *   \ **regexpi:**\ \ *regexpi*:
        Match a string using a case-insensitive regular-expression.
    *   \ **exact:**\ \ *string*:

        Match a string exactly, verbatim, without any of that fancy wildcard
        stuff.



    If no pattern prefix is specified, Selenium assumes that it's a "glob"
    pattern.



    For commands that return multiple values (such as verifySelectOptions),
    the string being matched is a comma-separated list of the return values,
    where both commas and backslashes in the values are backslash-escaped.
    When providing a pattern, the optional matching syntax (i.e. glob,
    regexp, etc.) is specified once, as usual, at the beginning of the
    pattern.


    """

    ### This part is hard-coded in the XSL
    def __init__(self, host, port, browserStartCommand, browserURL, http_timeout=90):
        self.host = host
        self.port = port
        self.browserStartCommand = browserStartCommand
        self.browserURL = browserURL
        self.sessionId = None
        self.extensionJs = ""
        self.http_timeout = http_timeout

    def setExtensionJs(self, extensionJs):
        self.extensionJs = extensionJs

    def start(self, browserConfigurationOptions=None, driver=None):
        start_args = [self.browserStartCommand, self.browserURL, self.extensionJs]
        if browserConfigurationOptions:
            start_args.append(browserConfigurationOptions)
        if driver:
            start_args.append('webdriver.remote.sessionid=%s' % driver.session_id)

        # Sessions can take a while to start, specially in grid environments
        self.http_timeout *= 5
        result = self.get_string("ge
tNewBrowserSession", start_args)
        self.http_timeout /= 5
        try:
            self.sessionId = result
        except ValueError:
            raise Exception(result)

   
 def stop(self):
        self.do_command("testComplete", [])
        self.sessionId = None

    def do_command(self, verb, args):
        conn = http_client.HTTPConnection(self.host, self.port, timeout=self.http_timeout)
        try:
            body = 'cmd=' + urllib_parse.quote_plus(unicode(verb).encode('utf-8'))
            for i in range(len(args)):
                body += '&' + unicode(i+1) + '=' + \
                        urllib_parse.quote_plus(unicode(args[i]).encode('utf-8'))
            if (None != self.sessionId):
                body += "&sessionId=" + unicode(self.sessionId)
            headers = {
                "Content-Type":
                "application/x-www-form-urlencoded; charset=utf-8"
            }
            conn.request("POST", "/selenium-server/d