Ejemplo n.º 1
0
class Browser(object):
    """
    A Browser object, analogous to zombie.js' ``Browser``.
    """
    def __init__(self, server=None):
        """
        Start a new Browser instance.

        :param server: an (optional) instance of
                       :class:`zombie.proxy.server.ZombieProxyServer`.
        """
        #
        # If a proxy server isn't specified, spawn one automatically.
        #
        if server is None:
            server = ZombieProxyServer()
        self.server = server
        self.client = ZombieProxyClient(server.socket)

    #
    # Forms
    #
    def fill(self, field, value):
        """
        Fill a specified form field in the current document.

        :param field: an instance of :class:`zombie.dom.DOMNode`
        :param value: any string value
        :return: self to allow function chaining.
        """
        self.client.nowait('browser.fill', (field, value))
        return self

    def pressButton(self, selector):
        """
        Press a specific button.

        :param selector: CSS selector or innerText
        :return: self to allow function chaining.
        """
        self.client.wait('browser.pressButton', selector)
        return self

    def check(self, selector):
        self.client.nowait('browser.check', (selector, ))
        return self

    def uncheck(self, selector):
        self.client.nowait('browser.uncheck', (selector, ))
        return self

    def select(self, selector, value):
        self.client.nowait('browser.select', (selector, value))
        return self

    def selectOption(self, selector):
        self.client.nowait('browser.selectOption', (selector, ))
        return self

    def unselect(self, selector, value):
        self.client.nowait('browser.unselect', (selector, value))
        return self

    def attach(self, selector, filename):
        self.client.nowait('browser.attach', (selector, filename))
        return self

    def choose(self, selector):
        self.client.nowait('browser.choose', (selector, ))
        return self

    #
    # query
    #
    def field(self, selector, context=None):
        element = self.client.create_element('browser.field',
                                             (selector, context))
        return DOMNode(element, self)

    #
    # Document Content
    #
    def load(self, html):
        """
        Loads raw html
        """
        self.client.wait('browser.load', html)

    @property
    def body(self):
        """
        Returns a :class:`zombie.dom.DOMNode` representing the body element of
        the current document.
        """
        element = self.client.create_element('browser.body')
        return DOMNode(element, self)

    def html(self, selector=None, context=None):
        """
        Returns the HTML content (string) of the current document.

        :param selector: an optional string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        return self.client.json('browser.html', (selector, context))

    def query(self, selector, context=None):
        """
        Evaluate a CSS selector against the document (or an optional context
        :class:`zombie.dom.DOMNode`) and return a single
        :class:`zombie.dom.DOMNode` object.

        :param selector: a string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        element = self.client.create_element('browser.query',
                                             (selector, context))
        return DOMNode.factory(element, self)

    def queryAll(self, selector, context=None):
        """
        Evaluate a CSS selector against the document (or an optional context
        :class:`zombie.dom.DOMNode`) and return a list of
        :class:`zombie.dom.DOMNode` objects.

        :param selector: a string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        elements = self.client.create_elements('browser.queryAll',
                                               (selector, context))
        return [DOMNode(e, self) for e in elements]

    def css(self, selector, context=None):
        """
        An alias for :class:`zombie.browser.Browser.queryAll`.

        :param selector: a string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        return self.queryAll(selector, context)

    def text(self, selector, context=None):
        """
        Returns the text content of specific elements.

        :param selector: a string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        return self.client.json('browser.text', (selector, context))

    def unselectOption(self, selector):
        """
        Unselects the given option

        Special case. It seems there is a problem in Zombie: unselectOption
        doesn't accept a selector.

        Pull request pending in the zombie project
        """
        self.query(selector).unselectOption()
        return self

    #
    # Navigation
    #
    def clickLink(self, selector):
        """
        Clicks on a link. The first argument is the link text or CSS selector.

        :param selector: an optional string CSS selector
                        (http://zombie.labnotes.org/selectors) or inner text

        Returns the :class:`zombie.browser.Browser` to allow function chaining.
        """
        self.client.wait('browser.clickLink', selector)
        return self

    @property
    def location(self):
        """
        Returns the location of the current document (same as
        ``window.location.toString()``).
        """
        return self.client.json('browser.location.toString()')

    @location.setter
    def location(self, url):
        """
        Changes document location, loading a new document if necessary (same as
        setting ``window.location``). This will also work if you just need to
        change the hash (Zombie.js will fire a hashchange event).
        """
        self.visit(url)

    def visit(self, url):
        """
        A shortcut to load the document from the specified URL.

        Returns the :class:`zombie.browser.Browser` to allow function chaining.
        """
        self.client.wait('browser.visit', url)
        return self

    def back(self):
        """
        Navigate to the previous page in history.

        Returns the :class:`zombie.browser.Browser` to allow function chaining.
        """
        self.client.wait('browser.back')
        return self

    def link(self, selector):
        """
        Finds and returns a link ``<a>`` element (:class:`zombie.dom.DOMNode`).
        You can use a CSS selector or find a link by its text contents (case
        sensitive, but ignores leading/trailing spaces).

        :param selector: an optional string CSS selector
                        (http://zombie.labnotes.org/selectors) or inner text
        """
        element = self.client.create_element('browser.link', (selector, ))
        return DOMNode(element, self)

    def reload(self):
        """
        Reloads the current page.

        Returns the :class:`zombie.browser.Browser` to allow function chaining.
        """
        self.client.wait('browser.reload')
        return self

    @property
    def statusCode(self):
        """
        Returns the status code returned for this page request (200, 303, etc).
        """
        return self.client.json('browser.statusCode')

    @property
    def success(self):
        """
        Returns ``True`` if the status code is 2xx.
        """
        return self.client.json('browser.success')

    @property
    def redirected(self):
        """
        Returns ``True`` if the page request followed a redirect.
        """
        return self.client.json('browser.redirected')

    def fire(self, selector, event_name):
        self.client.wait('browser.fire', selector, event_name)
        return self

    #
    # Debugging
    #
    def dump(self):
        """
        Prints a debug string including zombie.js version, current URL,
        history, cookies, event loop, etc.  Useful for debugging and submitting
        error reports.
        """
        self.client.json('browser.dump()')

    def get_resource(self, url):
        """
        Gets a resource and returns a json with information
        """
        return self.client.wait_return('browser.resources.get', url)

    def post_resource(self, url, form_params):
        options = {
            'headers': {
                'content-type': 'application/x-www-form-urlencoded'
            },
            'params': form_params
        }
        return self.client.wait_return('browser.resources.post', url, options)

    def evaluate(self, code):
        return self.client.json('browser.evaluate', (code, ))

    def wait(self, wait_argument=None):
        arguments = [] if wait_argument is None else [wait_argument]
        self.client.wait('browser.wait', *arguments)

    @property
    def resources(self):
        """
        Returns a list of resources loaded by the browser, e.g.,
        ::
            [{
                'method': 'GET',
                'url': 'http://www.example.com/',
                'statusCode': '200',
                'statusText': 'OK',
                'time': '200ms'
            }]
        """
        js = """
            browser.resources.map(
                function(r){
                    var request = r.request;
                    var response = r.response;
                    return {
                        'method': request.method,
                        'url': request.url,
                        'statusCode': response.statusCode,
                        'statusText': response.statusText,
                        'time': (response.time - request.time) + 'ms',
                    }
                }
            )
        """
        return self.client.json(js)

    def viewInBrowser(self):
        """
        Views the current document in a real Web browser. Uses the default
        system browser on OS X, BSD and Linux. Probably errors on Windows.
        """
        return self.client.send('browser.viewInBrowser()')  # pragma: nocover
Ejemplo n.º 2
0
class Browser(object):
    """
    A Browser object, analogous to zombie.js' ``Browser``.
    """

    def __init__(self, server=None):
        """
        Start a new Browser instance.

        :param server: an (optional) instance of
                       :class:`zombie.proxy.server.ZombieProxyServer`.
        """
        #
        # If a proxy server isn't specified, spawn one automatically.
        #
        if server is None:
            server = ZombieProxyServer()
        self.server = server
        self.client = ZombieProxyClient(server.socket)

    #
    # Forms
    #
    def fill(self, field, value):
        """
        Fill a specified form field in the current document.

        :param field: an instance of :class:`zombie.dom.DOMNode`
        :param value: any string value
        :return: self to allow function chaining.
        """
        self.client.nowait('browser.fill', (field, value))
        return self

    def pressButton(self, selector):
        """
        Press a specific button.

        :param selector: CSS selector or innerText
        :return: self to allow function chaining.
        """
        self.client.wait('browser.pressButton', selector)
        return self

    def check(self, selector):
        self.client.nowait('browser.check', (selector,))
        return self

    def uncheck(self, selector):
        self.client.nowait('browser.uncheck', (selector,))
        return self

    def select(self, selector, value):
        self.client.nowait('browser.select', (selector, value))
        return self

    def selectOption(self, selector):
        self.client.nowait('browser.selectOption', (selector,))
        return self

    def unselect(self, selector, value):
        self.client.nowait('browser.unselect', (selector, value))
        return self

    def attach(self, selector, filename):
        self.client.nowait('browser.attach', (selector, filename))
        return self

    def choose(self, selector):
        self.client.nowait('browser.choose', (selector, ))
        return self

    #
    # query
    #
    def field(self, selector, context=None):
        element = self.client.create_element(
            'browser.field', (selector, context))
        return DOMNode(element, self)

    #
    # Document Content
    #
    def load(self, html):
        """
        Loads raw html
        """
        self.client.wait('browser.load', html)

    @property
    def body(self):
        """
        Returns a :class:`zombie.dom.DOMNode` representing the body element of
        the current document.
        """
        element = self.client.create_element('browser.body')
        return DOMNode(element, self)

    def html(self, selector=None, context=None):
        """
        Returns the HTML content (string) of the current document.

        :param selector: an optional string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        return self.client.json('browser.html', (selector, context))

    def query(self, selector, context=None):
        """
        Evaluate a CSS selector against the document (or an optional context
        :class:`zombie.dom.DOMNode`) and return a single
        :class:`zombie.dom.DOMNode` object.

        :param selector: a string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        element = self.client.create_element(
            'browser.query', (selector, context))
        return DOMNode.factory(element, self)

    def queryAll(self, selector, context=None):
        """
        Evaluate a CSS selector against the document (or an optional context
        :class:`zombie.dom.DOMNode`) and return a list of
        :class:`zombie.dom.DOMNode` objects.

        :param selector: a string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        elements = self.client.create_elements(
            'browser.queryAll', (selector, context))
        return [DOMNode(e, self) for e in elements]

    def css(self, selector, context=None):
        """
        An alias for :class:`zombie.browser.Browser.queryAll`.

        :param selector: a string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        return self.queryAll(selector, context)

    def text(self, selector, context=None):
        """
        Returns the text content of specific elements.

        :param selector: a string CSS selector
                        (http://zombie.labnotes.org/selectors)
        :param context: an (optional) instance of :class:`zombie.dom.DOMNode`
        """
        return self.client.json('browser.text', (selector, context))

    def unselectOption(self, selector):
        """
        Unselects the given option

        Special case. It seems there is a problem in Zombie: unselectOption
        doesn't accept a selector.

        Pull request pending in the zombie project
        """
        self.query(selector).unselectOption()
        return self

    #
    # Navigation
    #
    def clickLink(self, selector):
        """
        Clicks on a link. The first argument is the link text or CSS selector.

        :param selector: an optional string CSS selector
                        (http://zombie.labnotes.org/selectors) or inner text

        Returns the :class:`zombie.browser.Browser` to allow function chaining.
        """
        self.client.wait('browser.clickLink', selector)
        return self

    @property
    def location(self):
        """
        Returns the location of the current document (same as
        ``window.location.toString()``).
        """
        return self.client.json('browser.location.toString()')

    @location.setter
    def location(self, url):
        """
        Changes document location, loading a new document if necessary (same as
        setting ``window.location``). This will also work if you just need to
        change the hash (Zombie.js will fire a hashchange event).
        """
        self.visit(url)

    def visit(self, url):
        """
        A shortcut to load the document from the specified URL.

        Returns the :class:`zombie.browser.Browser` to allow function chaining.
        """
        self.client.wait('browser.visit', url)
        return self

    def back(self):
        """
        Navigate to the previous page in history.

        Returns the :class:`zombie.browser.Browser` to allow function chaining.
        """
        self.client.wait('browser.back')
        return self

    def link(self, selector):
        """
        Finds and returns a link ``<a>`` element (:class:`zombie.dom.DOMNode`).
        You can use a CSS selector or find a link by its text contents (case
        sensitive, but ignores leading/trailing spaces).

        :param selector: an optional string CSS selector
                        (http://zombie.labnotes.org/selectors) or inner text
        """
        element = self.client.create_element('browser.link', (selector,))
        return DOMNode(element, self)

    def reload(self):
        """
        Reloads the current page.

        Returns the :class:`zombie.browser.Browser` to allow function chaining.
        """
        self.client.wait('browser.reload')
        return self

    @property
    def statusCode(self):
        """
        Returns the status code returned for this page request (200, 303, etc).
        """
        return self.client.json('browser.statusCode')

    @property
    def success(self):
        """
        Returns ``True`` if the status code is 2xx.
        """
        return self.client.json('browser.success')

    @property
    def redirected(self):
        """
        Returns ``True`` if the page request followed a redirect.
        """
        return self.client.json('browser.redirected')

    def fire(self, selector, event_name):
        self.client.wait('browser.fire', selector, event_name)
        return self

    #
    # Debugging
    #
    def dump(self):
        """
        Prints a debug string including zombie.js version, current URL,
        history, cookies, event loop, etc.  Useful for debugging and submitting
        error reports.
        """
        self.client.json('browser.dump()')

    def get_resource(self, url):
        """
        Gets a resource and returns a json with information
        """
        return self.client.wait_return('browser.resources.get', url)

    def post_resource(self, url, form_params):
        options = {
            'headers': {
                'content-type': 'application/x-www-form-urlencoded'},
            'params': form_params}
        return self.client.wait_return('browser.resources.post', url, options)

    def evaluate(self, code):
        return self.client.json('browser.evaluate', (code, ))

    def wait(self, wait_argument=None):
        arguments = [] if wait_argument is None else [wait_argument]
        self.client.wait('browser.wait', *arguments)

    @property
    def resources(self):
        """
        Returns a list of resources loaded by the browser, e.g.,
        ::
            [{
                'method': 'GET',
                'url': 'http://www.example.com/',
                'statusCode': '200',
                'statusText': 'OK',
                'time': '200ms'
            }]
        """
        js = """
            browser.resources.map(
                function(r){
                    var request = r.request;
                    var response = r.response;
                    return {
                        'method': request.method,
                        'url': request.url,
                        'statusCode': response.statusCode,
                        'statusText': response.statusText,
                        'time': (response.time - request.time) + 'ms',
                    }
                }
            )
        """
        return self.client.json(js)

    def viewInBrowser(self):
        """
        Views the current document in a real Web browser. Uses the default
        system browser on OS X, BSD and Linux. Probably errors on Windows.
        """
        return self.client.send('browser.viewInBrowser()')  # pragma: nocover