-
Notifications
You must be signed in to change notification settings - Fork 93
Introduction: Taxi
For API Documentation with examples, see Taxi API Documentation
Clj-webdriver provides several layers of abstraction over Selenium-WebDriver. The Taxi API represents the highest-level, most concise way for interacting with the browser and elements on the page. Its primary features include:
- Simple mechanisms for opening/closing browsers
- Default CSS query capability for finding elements on the page (configurable)
- Functions for managing HTML elements, multiple open windows, cookies, test timeouts, and more
- Seamless interop with lower levels of clj-webdriver's API
As a quick 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)
Whereas the goal of clj-webdriver's Core API is flexibility in how one queries the page and an explicit, functional style, the goal of the Taxi API is to provide a set of pluggable defaults that makes things more concise while writing tests.
There are only two pieces of state handled "in the background": a Driver
instance and a finder function. These are controlled with set-driver!
and set-finder!
respectively.
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
record in the var clj-webdriver.taxi/*driver*
, though you should use set-driver!
when possible.
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 (this allows for nice function composition). 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!
when possible.
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.
If you want to use multiple browsers with the Taxi API, instead of registering a default one with the set-driver!
function, you may simply create a new Driver
instance manually and use it with the Taxi API. For example, the following calls to to
are equivalent:
(def driver (new-driver {:browser :chrome}))
(set-driver! driver)
(to "http://example.com")
(to driver "http://example.com")
This is true throughout the Taxi API. Any function that implicitly uses the driver registered with set-driver!
can also optionally take a driver explicitly. In this case, you could start as many browsers as you want, never call set-driver!
, and just pass them in when needed. For example:
(def driver-a (new-driver {:browser :chrome}))
(def driver-b (new-driver {:browser :firefox}))
(to driver-a "http://clojure.org")
(to driver-b "http://factorcode.org")
;; etc.
The clj-webdriver Wiki by Daniel Gregoire and community is licensed under a Creative Commons Attribution 4.0 International License. Based on a work at https://github.com/semperos/clj-webdriver/wiki.