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
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