webdriver

a Haskell client for the Selenium WebDriver protocol

https://github.com/kallisti-dev/hs-webdriver

Version on this page:0.8.0.3
LTS Haskell 22.39:0.12.0.0
Stackage Nightly 2024-10-31:0.12.0.0
Latest on Hackage:0.12.0.0

See all snapshots webdriver appears in

BSD-3-Clause licensed by Adam Curtis
Maintained by [email protected]
This version can be pinned in stack with:webdriver-0.8.0.3@sha256:f3aacf2e7bd654cc0f8772c09ed535247a21d67e783bb805903c7f06dbf8e46f,3826

hs-webdriver is a Selenium WebDriver client for the Haskell programming language. You can use it to automate browser sessions for testing, system administration, etc.

For more information about Selenium itself, see http://seleniumhq.org/

Contents

Installation

hs-webdriver uses the Cabal build system to configure, build, install, and generate documentation on multiple platforms.

For more information on using Cabal and its various installation options, see the Cabal User’s Guide at http://www.haskell.org/cabal/users-guide/index.html

Installation from Hackage

hs-webdriver is hosted on Hackage under the name webdriver. Thus, the simplest way to download and install the most recent version of hs-webdriver is to run:

cabal install webdriver

There are also options to do system-wide installation, version selection, and other build options; see cabal-install documentation.

Installation from this repository

To build and install a git revision for a single user on your system, run these commands from within the repository directory

Using cabal-install

cabal install

Using Cabal

For systems without cabal-install available, you can also run the Setup.hs script, as such:

runhaskell Setup.hs configure --user
runhaskell Setup.hs build
runhaskell Setup.hs install

For more build options, please refer to the Cabal documentation.

#Getting Started WebDriver is a client-server protocol. Since hs-webdriver only implements a WebDriver client, you must have a WebDriver server to which you can connect in order to make use of this library.

##Using the Selenium Server While you can use any WebDriver server out there, probably the simplest server to use with hs-webdriver is Selenium Server. You’ll need an installation of the Java runtime to use this server. Once you’ve downloaded Selenium Server to your current working directory, you can start the server with this shell command:

java -jar selenium-server-standalone-*.jar

The server should now be listening at localhost on port 4444.

##Hello, World! With the Selenium server running locally, you’re ready to write browser automation scripts in Haskell. Let’s start with a simple example.

    {-# LANGUAGE OverloadedStrings #-}
    import Test.WebDriver
    
    myConfig :: WDConfig
    myConfig = defaultConfig
    
    main :: IO ()
    main = runSession myConfig $ do
      openPage "http://google.com"
      searchInput <- findElem (ByCSS "input[type='text']")
      sendKeys "Hello, World!" searchInput

hs-webdriver uses a very simple EDSL implemented within a state monad. Interacting with the remote browser is done via a sequence of commands within this monad. The state monad maintains implicit information about the WebDriver session between commands, so that individual commands only need to specify parameters relevant to the action they perform. If you’re new to monads, there are plenty of resources available for learning on the web, but for now you can think of the WD monad as implementing a very simple imperative language operating on an implicit browser session.

Let’s take a closer look at each piece of this example.

###Demonic invocations: a bit of boilerplate

    {-# LANGUAGE OverloadedStrings #-}

hs-webdriver uses the Text type to represent Unicode character sequences, which is significantly more efficient than the standard Haskell structure for strings. This directive tells GHC to overload string literals so that they can be used to represent Text values (or, more generally, any other instance of IsString).

    import Test.WebDriver

This line is fairly straightforward; we need to import the library so that we can use it! Most of the basic API is available through the Test.WebDriver module, so this is the only import you should need for most tests. There are other modules that may be of interest for advanced usage; in particular, Test.WebDriver.Commands.Wait provides so-called “implicit waits” as defined by other WebDriver libraries. There are also modules that support custom Firefox and Chrome extensions, but these features are not fully stable.

###Configuring a WebDriver session

    myConfig :: WDConfig
    myConfig = defaultConfig

To configure a new WebDriver session, we use the WDConfig type; this is a record type with various configuration fields. To connect to the Selenium server that we spawned earlier, the defaultConfig is sufficient. By default, the browser is set to Firefox, but that can be changed; the following configuration will use Google Chrome instead of Firefox for our test:

    myConfig :: WDConfig
    myConfig = defaultConfig { wdCapabilities = defaultCaps { browser = chrome } }

Note: To use Google Chrome, you need to install Google’s proprietary ChromeDriver in a directory where it can be recognized by Selenium Server (see: https://code.google.com/p/selenium/wiki/ChromeDriver).

###Initializing tests

    main :: IO ()
    main = runSession myConfig $ do

main is the standard entry point for a Haskell program, defined as a value of type IO a. In order to transform our WD action into an IO action, we use the runSession function, which has the type:

    runSession :: WDConfig -> WD a -> IO a

So we pass to runSession our configuration record along with a WebDriver “script” to perform, and it transforms the script into a side-effectful IO action. The WDConfig record is used to automatically initialize our session with the remote server.

Note: runSession does not automatically close the session it creates. This is intentional, as you may want to manually inspect the browser state after your code executes. If you want to have the session automatically close, you can use the finallyClose function to provide this behavior.

    main = runSession myConfig . finallyClose $ do

###Actually writing tests!

      openPage "http://google.com"
      searchInput <- findElem (ByCSS "input[type='text']")
      sendKeys "Hello, World!" searchInput

Interaction with the browser is accomplished via WebDriver “commands”, which are just function calls within the WD monad. Most of these commands are defined in the Test.WebDriver.Commands modules, and are fairly self-explanatory. In this example, openPage opens a new URL, and findElem searches for a DOM element on the current page which matches the given selector (possible selectors include ById, ByName, ByClass, ByTag, ByLinkText, ByCSS, and ByXPath). The DOM Element found by the result of the search is bound to the local variable searchInput, and sendKeys sends a sequence of emulated keystrokes to the given element.

This example contains all of the basic elements of a simple WebDriver test. For complete documentation on each command, check out the documentation for Test.WebDriver.Commands

#Integration with Haskell Testing Frameworks

This package does not provide utilities to integrate with popular Haskell testing frameworks. However, other packages exist for this purpose:

#Documentation

Documentation for hs-webdriver is available on Hackage at http://hackage.haskell.org/package/webdriver. You can also generate local HTML documentation from this source revision with the following shell command:

runhaskell Setup.hs haddock

Haddock will generate documentation and save it in dist/doc/html/webdriver

Changes

#Change Log

##0.8.0.3

  • Fixed build errors for GHC < 7.10. webdriver now builds with GHC stable releases 7.4.2, 7.6.3, and 7.8.4

##0.8.0.2

  • Fixed issue introduced in 0.8 that caused build failure when using aeson < 0.10

##0.8.0.1

  • findElems and and findElemsFrom were accidentally changed to return a single Element in the last release. This has been fixed.

##0.8

###Command changes

  • All commands that previously accepted a list parameter now accepts any instance of Foldable instead.

###Overloadable configuration It is now possible to define custom configuration types that can be used to initialize webdriver sessions.

runSession now has the following type:

  runSession :: WebDriverConfig conf => conf -> WD a -> IO a

And the typeclass to create new config types looks like this:

  -- |Class of types that can configure a WebDriver session.
  class WebDriverConfig c where
    -- |Produces a 'Capabilities' from the given configuration.
    mkCaps :: MonadBase IO m => c -> m Capabilities

    -- |Produces a 'WDSession' from the given configuration.
    mkSession :: MonadBase IO m => c -> m WDSession

Of course you can still use WDConfig, as it is now an instance of WebDriverConfig.

###Reworked custom HTTP headers interface

  • Support for custom request headers was added rather hastily, resulting in several functions having explicit RequestHeaders parameters. The interface has been reworked now so that custom request headers are stored inside WDSession and explicit RequestHeaders parameters have been removed.
  • There’s also now a distinction in configuration between wdAuthHeaders which are used only during initial session creation, and wdRequestHeaders, which are used with all other HTTP requests
  • Two new utility functions were added to make working with custom HTTP headers easier: withRequestHeaders and withAuthHeaders

###Clean-up and dependency changes

  • Removed a whole mess of unusued import and deprecation warnings when building with GHC 7.10
  • We now enforce an attoparsec lower bound of 0.10 (there was no lower bound before)
  • The unnecessary dependency on mtl is now removed.
  • Added some monad transformer instances for WebDriver and WDSessionState that were mysteriously missing: strict WriterT, ReaderT, ListT
  • data-default dependency was changed to data-default-class

##0.7

Because this is a fairly major update, changes have been described in detail and organized into categories. Most of the potentially breaking changes are to the “intermediate” API that might affect library code or advanced applications; changes that are not entirely “user-facing” but also not quite “internal”.

Basic web test code only has to contend with a few additional symbol exports, overloading of type signatures on some existing functions, and the reworked session history API.

###Top-level API (exposed byTest.WebDriver module)

  • withSession is now overloaded over the new constraint alias WDSessionStateControl (see below). This means that it can be used with monad transformer stacks over WD without any lifting required.
  • Added several new WDConfig convenience functions: modifyCaps, useBrowser, useVersion, usePlatform, useProxy; these functions are overloaded over the new HasCapabilities constraint alias (see below)
  • Reworked and improved session history API
    • Added a SessionHistory record type to replace the old (Request, Response ByteString) type. The new type has the same data as the previous tuple, but additionally records the number of attempted HTTP retries, and instead of Response ByteString uses Either SomeException (Response ByteString) so that HTTP request errors can be logged.

    • Removed wdKeepSessHist field from WDConfig and replaced it with wdHistoryConfig, which uses a new SessionHistoryConfig type.

        -- |A function used to append new requests/responses to session history.
        type SessionHistoryConfig = SessionHistory -> [SessionHistory] -> [SessionHistory]
      
    • The new field can be configured using several new constants: noHistory, onlyMostRecentHistory, and unlimitedHistory. Note: unlimitedHistory is now the default configuration for history. For the old behavior, use onlyMostRecentHistory.

    • New top-level functions for accessing session history

        -- |Gets the command history for the current session.
        getSessionHistory :: WDSessionState wd => wd [SessionHistory]
        
        -- |Prints a history of API requests to stdout after computing the given action
        --  or after an exception is thrown
        dumpSessionHistory :: WDSessionStateControl wd => wd a -> wd a
      

###Implicit waits API (Test.WebDriver.Commands.Wait)

  • Added functions for checking some expected conditions in explicit waits: expectNotStale, expectAlertOpen, catchFailedCommand

###Typeclass API

  • WDSessionState is now a superclass of Monad and Applicative instead of MonadBaseControl IO. This makes the class significantly more flexible in how it can be used, as it no longer requires IO as a base monad.

    • For convenience the following constraint aliases were added (requires ConstraintKinds extension to use). Several existing API functions have been updated to use these new constraints where appropriate.

        type WDSessionStateIO s = (WDSessionState s, MonadBase IO s)
        type WDSessionStateControl s = (WDSessionState s, MonadBaseControl IO s)
      
    • The WDSessionStateControl constraint is equivalent to the previous WDSessionState constraint.

    • The WebDriver class is unaffected (it is now a superclass of WDSessionStateControl), so code using the basic Test.WebDriver API will not be affected.

  • New typeclasses added to Test.WebDriver.Capabilities: GetCapabilities and SetCapabilities; for convenience a constraint alias HasCapabilities has been added to work with both of these classes (requires ConstraintKinds extension to use)

    ```hs
      -- |A typeclass for readable 'Capabilities'
      class GetCapabilities t where
        getCaps :: t -> Capabilities
      
      -- |A typeclass for writable 'Capabilities'
      class SetCapabilities t where
        setCaps :: Capabilities -> t -> t
      
      -- |Read/write 'Capabilities'
      type HasCapabilities t = (GetCapabilities t, SetCapabilities t)
    ```
    

###Minor API changes (not exposed to Test.WebDriver module)

  • Test.WebDriver.Session changes
    • new function mostRecentHistory added
    • lastHTTPRequest renamed to mostRecentHTTPRequest (for consistency)
    • mkSession moved from Test.WebDriver.Config to Test.WebDriver.Session
  • Test.WebDriver.Internal changes
    • sendHTTPRequest now returns (Either SomeException (Response ByteString)) and catches any exceptions that occur during the request. When using default configuration, the exceptions are also saved in ‘wdSessHist’.
    • Test.WebDriver.Internal no longer defines and exports exception types. All exception defined here previously have been moved to an unexposed Test.WebDriver.Exceptions.Internal module. These types are still exported in the usual places.

###Dependencies

  • Now supports http-types up to 0.9

##0.6.3.1

  • Fixed an issue with aeson 0.10 support

##0.6.3

  • Support aeson 0.10
  • Added support for multiple HTTP attempts per command request, using the new WDConfig field wdHTTPRetryCount

##0.6.2.1

  • Supports vector 0.11, aeson 0.9, attoparsec 0.13

##0.6.2

  • Supports GHC 7.10
  • Supports reworked Chrome capabilities used by newer versions of WebDriver
  • Servers that return empty JSON strings for commands with no return value will no longer cause parse errors

##0.6.1

  • Added the ability to pass HTTP request headers at session creation
  • Fixed an issue involving an obsolete JSON representation of Chrome capabilities
  • Relax upper bound on exceptions dependency

##0.6.0.4

  • Support for monad-control 1.0

##0.6.0.3

  • Relaxed upper bounds on text and http-client versions

##0.6.0.2

  • Added support for aeson > 0.8 and network > 2.6
  • Added support for the “X-Response-Body-Start” HTTP header used for error responses in newer http-client versions

##0.6.0.1

  • Fixed Haddock parse errors. No code changes introduced in this version.

##0.6

  • Rather than WDSession serving dual roles as configuration and state, its functionality has been split into 2 respective types: WDConfig and WDSession.
  • runSession now takes a WDConfig instead of WDSession and Capabilities parameters.
  • runSession no longer closes its session on successful completion; use finallyClose or closeOnException for this behavior
  • The old Test.WebDriver.Classes module has been split into Test.WebDriver.Session and Test.WebDriver.Class
  • SessionState typeclass renamed to WDSessionState
  • We now use the http-client package instead of HTTP. This is reflected in the addition of Manager fields in both WDConfig and WDSession

##0.5.5

  • Added optional HTTP history tracking for debugging purposes.

##0.5.4

  • MonadCatchIO is deprecated in favour of exceptions.
  • Relaxed dependencies on mtl, network and scientific.

##0.5.3.3

  • Relaxed text dependency up to 1.2

##0.5.3.2 ###bug fixes

  • fixed remaining compilation problems with aeson. now supports aeson >= 0.6.2.0 && < 0.8
  • now depends on directory-tree instead of filesystem-trees. this fixes several problems with firefox/chrome profile support.

##0.5.3.1 ###bug fixes

  • fixed compilation error with aeson 0.7 and greater

##0.5.3

###new features

  • SessionNotCreated constructor added to FailedCommandType
  • new command deleteCookieByName added

###bug fixes

  • asyncJS now properly distinguishes between a null return and a script timeout
  • fixed a change in waitWhile causing the opposite of expected behavior

##0.5.2 ###API changes

  • added many new Internet Explorer capabilities
  • added additionalCaps to Capabilities, which allows support for non-standard capabilities
  • Browser type now supports non-standard browsers via the new Browser constructor
  • added support for the new unexpectedAlertBehaviour capability

###new features

  • new command getApplicationCacheStatus supported
  • error reporting for unknown commands now slightly improved

###bug fixes

  • internal request URIs are now properly percent-encoded
  • improved handling of browser-specific capabilities
  • fixed incompatability with new session creation protocol changes introduced in selenium 2.35
  • updated to work with Aeson 0.6.2 and onward

##hs-webdriver 0.5.1 ###API changes

  • Test.WebDriver.Internal.FailedCommandInfo now stores screenshots as lazy bytestrings
  • Test.WebDriver.Common.Profile now stores PreparedProfile as a lazy bytestring
  • Test.WebDriver.Chrome.Extension now stores ChromeExtension as a lazy bytestring
  • The LogPref type has been renamed to LogLevel to reflect its use within the new log interface

###new features

  • a new log interface as specified by the webdriver standard. This includes the functions getLogs and getLogTypes, and the types LogType and LogEntry.
  • waitWhile and waitUntil now show more detailed information about why an explicit wait timed out.

##hs-webdriver 0.5.0.1 ###bug fixes

  • hs-webdriver now correctly handles a wider variety of server-specific responses when a webdriver command expects no return value.
  • An issue with the redirect status codes used during session creation has been fixed.
  • As a result of the above fixes, hs-webdriver should now work with chromedriver. Note that, prior to this version, you can still use chromedriver if you use the selenium standalone server jar as a proxy.

##hs-webdriver 0.5 ###API changes

  • Test.WebDriver.Commands.Wait.unexpected now accepts a String argument, which is used as an error message
  • screenshot and uploadZipEntry from Test.WebDriver.Commands now use lazy bytestrings
  • wdBasePath field added to WDSession. This allows you to specify a custom base path for all WebDriver requests. The default, as specified in the WebDriver standard, is “/wd/hub”

###new features

  • added Test.WebDriver.Commands.screenshotBase64

##hs-webdriver 0.4

###API changes

  • finallyClose and closeOnException are now overloaded on the WebDriver class.
  • NoSessionId and doSessCommand were moved from Test.WebDriver.Classes to Test.WebDriver.Commands.Internal

###bug fixes

  • fixed a typo in the export list of Firefox.Profile; deleteFile is now correctly exported instead of removeFile from System.Directory
  • fixed an error in the JSON representation of MouseButton

###new features

  • A new module, Test.WebDriver.Commands.Internal, which exports some low-level functions used to implement the high-level interface. This makes it possible for library users to extend hs-webdriver with nonstandard or unimplemented features.

hs-webdriver 0.3.3

###API changes

  • The representation of profile files has been changed to use a HashMap instead of an association list. This ensures that destination paths are always unique.

###bug fixes

  • The default preferences used by Selenium are now merged into the preferences of Firefox profiles loaded from disk.
  • addExtension will now correctly add extension directories to a profile.

###known issues

  • Because of the way loadProfile currently adds directories to the profileFiles HashMap, it’s possible for extensions added via addExtension to be overriden by the extensions originally listed in the on-disk extensions directory.

###new features

  • It’s now possible to add entire directories to a profile in pure code using addFile and addExtension.
  • new functions in Common.Profile: unionProfiles, onProfileFiles, onProfilePrefs
  • new function in Commands.Wait: onTimeout
  • the WD monad now has a MonadCatchIO instance, as an alternative to lifted-base for exception handling

hs-webdriver 0.3.2.1

###bug fixes

  • Removed a bug in waitWhile’ that resulted in an infinite loop
  • Fixed the incorrect representation of JSON profiles
  • Fixed relative path issues when zipping profile directories from disk

hs-webdriver 0.3.2

###bug fixes

  • Changed the constraint on filesystrem-trees to avoid a broken version
  • Added the missing exports for addFile and deleteFile in Common.Profile and Firefox.Profile

###new features

  • new Common.Profile functions: hasExtension, hasFile

hs-webdriver 0.3.1

###API changes

  • The representation of Profiles has changed, allowing it to store arbitrary files as well as extensions. The functional API for working with preferences and extensions ismostly unchanged, except for the behavior of calling addExtension consecutively with the same filepath argument.
  • The old <&&> and <||> operators in Test.WebDriver.Commands.Wait have been removed and replaced with the ones exported from Control.Conditional from the cond package.

###bug fixes

  • Fixed memory leak resulting from an infinite recursion in the FromJSON instance of PreparedProfile.
  • loadProfile now properly loads an entire Firefox profile from disk, rather than just the extensions and preferences.

###known issues

  • An issue involving lazy bytestring IO in the zip-archive package means that unusually large profiles might exceed the OSes open file limit.

###new features

  • several new functions for working with Firefox/Opera profiles have been added. This includes functions for loading large profiles from disk, functions for working with zipped profiles, and functions for adding arbitrary files to a profile in pure code.
  • new helper functions were added to Test.WebDriver.Commands.Wait, exported from the cond package.

hs-webdriver 0.3.0.1

###bug fixes

  • due to a nonconformance in the spec from the Grid server, wire responses were being received that contained no sessionId key, which subsequently resulted in a parse error from our JSON parser. This has been fixed, so that an omitted sessionId defaults to Nothing.
  • major bux fixes in the Firefox profile code. Note that loadProfile is unlikely to work as expected, but prepareTempProfile should.

hs-webdriver 0.3

API changes

  • 2 typeclasses were introduced. All WebDriver commands are now overloaded on WebDriver class, so that monad transformers with a WD base can be used conveniently.
  • The MonadState instance of WD has been removed and replaced by SessionState.
  • The Firefox profile code has been generalized to work with either Opera or Firefox profiles. A phantom type parameter is used to create a distinction between the two. See documentation on Common.Profile and Firefox.Profile to learn about the specific changes that were made.
  • FFLogPref is now removed and replaced by the LogPref type, because both Firefox and Opera config use the same logging preference values.
  • Several new modules have been created, including: Capabilities, Monad, Classes, Exceptions. Many of the definitions have been moved around, but the export lists of the pre-existing modules are the same.

bug fixes

  • Various issues with the serialization of capabilities meant that Chrome, IE, and Opera weren’t able to startup correctly with default capabilities. This is now fixed.

new features

  • General documentation improvements.
  • Opera configuration is now implemented.

hs-webdriver 0.2

API changes

  • FailedCommandInfo changed so that it stores a WDSession rather than just a Maybe SessionId, thus providing server host and port information as well as the session ID.
  • As a result, mkFailedCommandInfo is now String -> WD FailedCommandInfo, since it requires access to the WDSession state.
  • HTML5StorageType changed to the more accurate WebStorageType

new features

  • general documentation improvements
  • the uploadFile, uploadRawFile, and uploadZipEntry functions, which support uploading file contents to the remote server

hs-webdriver 0.1

API changes

  • getWindowSize, setWindowSize, getWindowPos, and setWindowPos have all been deprived of their WindowHandle argument. This is due to the fact that using unfocused windows with those commands causes undefined behavior.

new features

  • the mkCookie function for convenient cookie construction
  • the focusFrame function for focusing to individual frames
  • the setPageLoadTimeout function
  • the maximize function for maximizing windows
  • support for HTML 5 web storage