Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Introduction: Taxi

semperos edited this page Feb 19, 2012 · 5 revisions

For API Documentation, see Taxi API Documentation

Available as of version 0.6.0-alpha1, clj-webdriver now sports a concise, high-level API for interacting with browsers called the Taxi API. Leveraging the work done with implementing Selenium-WebDriver in terms of records and protocols, the Taxi API provides some stateful defaults and a number of utility functions that provide a more unified way of querying web pages and require fewer keystrokes to accomplish the same tasks.

As an example, here is how to log into Github using the Taxi API:

(use 'clj-webdriver.taxi)

(set-driver! {:browser :firefox})

(to "https://github.com")
(click "a[href*='login']")

(input-text "#login_field" "your_username")
(-> "#password"
  (input-text "your_password")
  submit)
(quit)

;; Or using a with-driver form that quits the browser at the end:
(with-driver {:browser :firefox}
  (to "https://github.com")
  (click "a[href*='login']")
  (input-text "#login_field" "your_username")
  (-> "#password"
    (input-text "your_password")
    submit))

Whereas the goal of the core API is flexibility in how one queries the page, the goal of the Taxi API is to provide a set of pluggable defaults that makes things more concise for your test cases.

Configuration & Details

There are only two pieces of state handled "in the background": a Driver instance and a finder function.

The Driver instance is specified just as in the core namespace; you pass in a :browser, as well as an optional :cache-spec if you want to use Caching Support. You can always access the driver instance in the var clj-webdriver.taxi/*driver*, though you should use set-driver! if you just need to start the browser.

The finder function is the new concept. A finder function takes a single query argument and returns all the elements that match that query. Alternatively, if an Element record itself is passed in as the query argument, it is returned unchanged. The clj-webdriver.taxi namespace provides two finder functions by default, css-finder and xpath-finder, with CSS querying as the default (per recommendations from the Selenium-WebDriver community). You can always access the finder function directly in the var clj-webdriver.taxi/*finder-fn*, though you should use set-finder! if you just need to change the default value.

Here are the implementations of the built-in css-finder and xpath-finder, to make things clearer:

(defn css-finder
  "Given a CSS query `q`, return a lazy seq of the elements found by calling `find-elements` with `by-css`. If `q` is an `Element`, it is returned unchanged."
  [q]
  (if (element? q)
    q
    (core/find-elements *driver* {:css q})))

(defn xpath-finder
  "Given a XPath query `q`, return a lazy seq of the elements found by calling `find-elements` with `by-xpath`. If `q` is an `Element`, it is returned unchanged."
  [q]
  (if (element? q)
    q
    (core/find-elements *driver* {:xpath q})))

Note that these functions leverage the internal *driver* instance, and pass that to core/find-elements using a default finding method (in these cases, :css and :xpath options). In conjunction with this, all functions that logically only take one Element (e.g., click, input-text, etc.) simply take the first element returned by these finder functions. This means that you can model your own finder functions on the examples above, if you need custom functionality.

These facts combined allow us to write (click "a#my_button") instead of the much longer (click driver (find-element {:css "a#my_button"}). More important than the length of the second function, however, is that it allows for several different ways of accomplishing the same query. Here are just a few:

(find-element {:css "a#my_button"})
(find-element {:xpath "//a[@id='my_button']"})
(find-element {:tag :a, :id "my_button"})

While this flexibility is important to the overall power of the clj-webdriver API, often you just need a single, unified way to query the page and author your tests. The Taxi API encourages just such a standardization, and provides added concision as a result.

At this point, all user-facing functionality provided at lower levels are exposed in the Taxi API. Unless you need more direct access to Driver or Element records (or you just have an aversion to pseudo-non-functional paradigms), you should use the Taxi API for your own development.

Clone this wiki locally