webdriver 0.9.0.1 → 0.10.0.0
raw patch · 17 files changed
+302/−263 lines, 17 filesdep ~aesondep ~basedep ~networknew-uploader
Dependency ranges changed: aeson, base, network, network-uri
Files
- .ghci +0/−2
- CHANGELOG.md +31/−21
- LICENSE +1/−0
- README.md +3/−0
- TODO.md +0/−18
- src/Test/WebDriver.hs +21/−20
- src/Test/WebDriver/Capabilities.hs +13/−9
- src/Test/WebDriver/Class.hs +13/−16
- src/Test/WebDriver/Commands.hs +5/−50
- src/Test/WebDriver/Commands/Internal.hs +6/−2
- src/Test/WebDriver/Cookies.hs +54/−0
- src/Test/WebDriver/Exceptions/Internal.hs +2/−2
- src/Test/WebDriver/IO.hs +16/−0
- src/Test/WebDriver/JSON.hs +20/−7
- src/Test/WebDriver/Monad.hs +6/−6
- src/Test/WebDriver/Session.hs +18/−19
- webdriver.cabal +93/−91
− .ghci
@@ -1,2 +0,0 @@-:set -XOverloadedStrings -XScopedTypeVariables-:m Test.WebDriver Data.Default.Class
CHANGELOG.md view
@@ -1,6 +1,16 @@ # Change Log -##0.9.0.1+## 0.10.0.0+* Add Aeson 2 compatibility to support GHC 9.0 and 9.2+* Derive MonadMask instance for the WD monad+* Fix parameter name in the `focusWindow` function+* Fix Cookie ToJSON instance+* Convert a couple noReturn calls to ignoreReturn to avoid a crash+* Switch to hpack+* Switch to GitHub Actions CI+* Remove cabal flags and shorten extra-source-files++## 0.9.0.1 * Fixed build errors when building against aeson-1.4.3.0 ## 0.9@@ -44,7 +54,7 @@ ## 0.8.1 * Previously internal convenience functions `noReturn` and `ignoreReturn` are now exported in Test.WebDriver.JSON * `elemInfo` is now deprecated due to it being phased out in the Marionette (Firefox) driver. It will likely be removed once Selenium 4 is released.-* Fixed an issue causing PAC settings to not work. +* Fixed an issue causing PAC settings to not work. ## 0.8.0.4 * Quick fix to parse "unsupported command" errors when using Marionette driver (Selenium + Marionette has nonstandard behavior when reporting that error type)@@ -57,7 +67,7 @@ * 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. +* findElems and and findElemsFrom were accidentally changed to return a single Element in the last release. This has been fixed. ## 0.8 @@ -109,18 +119,18 @@ * 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.- + ```hs -- |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- + ```hs -- |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@@ -132,30 +142,30 @@ ### 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.- + ```hs 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@@ -181,7 +191,7 @@ ## 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 +* 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@@ -192,7 +202,7 @@ * Support for monad-control 1.0 ## 0.6.0.3-* Relaxed upper bounds on text and http-client versions +* Relaxed upper bounds on text and http-client versions ## 0.6.0.2 * Added support for aeson > 0.8 and network > 2.6@@ -263,7 +273,7 @@ * 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. +* 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@@ -289,7 +299,7 @@ * 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 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@@ -307,7 +317,7 @@ ### 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 +### 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@@ -330,7 +340,7 @@ ### new features * new Common.Profile functions: hasExtension, hasFile -## hs-webdriver 0.3.1 +## 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.@@ -344,7 +354,7 @@ * 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. +* 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@@ -354,7 +364,7 @@ * major bux fixes in the Firefox profile code. Note that loadProfile is unlikely to work as expected, but prepareTempProfile should. -## hs-webdriver 0.3 +## 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.@@ -384,7 +394,7 @@ ## 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. +* 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
LICENSE view
@@ -1,4 +1,5 @@ Copyright (c) 2012-2015, Adam Curtis+Copyright (c) 2022, Tom McLaughlin All rights reserved. Redistribution and use in source and binary forms, with or without
README.md view
@@ -1,5 +1,8 @@ [](https://travis-ci.org/kallisti-dev/hs-webdriver) +# Welcome to `webdriver`+[](https://hackage.haskell.org/package/webdriver) + 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/
− TODO.md
@@ -1,18 +0,0 @@-#known issues-- fix loadProfile so that it doesn't cause an overlap with user addExtension calls--#features-- support new w3c webdriver spec: http://www.w3.org/TR/webdriver/- - try to support backwards compatibility with old wire protocol, otherwise provide legacy API in submodule (?) - - mock-up example of actions API: http://lpaste.net/3484676197546196992-- allow WDConfig to automatically load drivers. add modules with driver loading functions-- overload URL inputs/outputs to implicitly support structured URL types-- add support for Opera profiles-- POST "/session/{sessionId}/phantom/execute"--#documentation-- document errors.---#considerations-- consider adding withSession to SessionState so that it can be overloaded easily, or rewriting it so that it's overloaded but not a method itself.
src/Test/WebDriver.hs view
@@ -2,40 +2,41 @@ This module serves as the top-level interface to the Haskell WebDriver bindings, providing most of the functionality you're likely to want. -}-module Test.WebDriver- ( -- * WebDriver monad- WD(..) - -- * Running WebDriver commands++module Test.WebDriver (+ -- * WebDriver monad+ WD(..)+ -- * Running WebDriver commands , runSession, withSession, runWD- -- * WebDriver configuration+ -- * WebDriver configuration , WDConfig(..), defaultConfig- -- ** Configuration helper functions- -- | Instead of working with the 'Capabilities' record directly, you can use - -- these config modifier functions to specify common options.- , useBrowser, useProxy, useVersion, usePlatform - -- ** Session history configuration+ -- ** Configuration helper functions+ -- | Instead of working with the 'Capabilities' record directly, you can use+ -- these config modifier functions to specify common options.+ , useBrowser, useProxy, useVersion, usePlatform+ -- ** Session history configuration , SessionHistoryConfig, noHistory, unlimitedHistory, onlyMostRecentHistory- -- ** HTTP request header utilities+ -- ** HTTP request header utilities , withRequestHeaders, withAuthHeaders- -- * WebDriver commands+ -- * WebDriver commands , module Test.WebDriver.Commands- -- * Capabilities (advanced configuration)+ -- * Capabilities (advanced configuration) , Capabilities(..), defaultCaps, allCaps, modifyCaps , Platform(..), ProxyType(..)- -- ** Browser-specific capabilities+ -- ** Browser-specific capabilities , Browser(..), LogLevel(..)- -- *** Browser defaults+ -- *** Browser defaults , firefox, chrome, ie, opera, iPhone, iPad, android- -- * Exception handling+ -- * Exception handling , finallyClose, closeOnException , module Test.WebDriver.Exceptions- -- * Accessing session history+ -- * Accessing session history , SessionHistory(..), getSessionHistory, dumpSessionHistory ) where -import Test.WebDriver.Types import Test.WebDriver.Commands-import Test.WebDriver.Monad-import Test.WebDriver.Exceptions import Test.WebDriver.Config+import Test.WebDriver.Exceptions+import Test.WebDriver.Monad import Test.WebDriver.Session+import Test.WebDriver.Types
src/Test/WebDriver/Capabilities.hs view
@@ -1,14 +1,12 @@-{-# LANGUAGE OverloadedStrings, RecordWildCards, ConstraintKinds- #-}+{-# LANGUAGE OverloadedStrings, RecordWildCards, ConstraintKinds, CPP #-} module Test.WebDriver.Capabilities where -import Test.WebDriver.Firefox.Profile import Test.WebDriver.Chrome.Extension+import Test.WebDriver.Firefox.Profile import Test.WebDriver.JSON import Data.Aeson import Data.Aeson.Types (Parser, typeMismatch, Pair)-import qualified Data.HashMap.Strict as HM (delete, toList, empty) import Data.Text (Text, toLower, toUpper) import Data.Default.Class (Default(..))@@ -21,6 +19,13 @@ import Prelude -- hides some "unused import" warnings +#if MIN_VERSION_aeson(2,0,0)+import qualified Data.Aeson.KeyMap as HM (delete, toList, empty)+#else+import qualified Data.HashMap.Strict as HM (delete, toList, empty)+#endif++ -- |A typeclass for readable 'Capabilities' class GetCapabilities t where getCaps :: t -> Capabilities@@ -47,7 +52,7 @@ useVersion :: HasCapabilities t => String -> t -> t useVersion v = modifyCaps $ \c -> c { version = Just v } --- |A helper function for setting the 'platform' capability of a 'HasCapabilities' instance +-- |A helper function for setting the 'platform' capability of a 'HasCapabilities' instance usePlatform :: HasCapabilities t => Platform -> t -> t usePlatform p = modifyCaps $ \c -> c { platform = p } @@ -242,7 +247,7 @@ Phantomjs {..} -> catMaybes [ opt "phantomjs.binary.path" phantomjsBinary- ] ++ + ] ++ [ "phantomjs.cli.args" .= phantomjsOptions ] @@ -276,7 +281,7 @@ where --some helpful JSON accessor shorthands req :: FromJSON a => Text -> Parser a- req = (o .:) -- required field+ req = (o .:) . fromText -- required field opt :: FromJSON a => Text -> a -> Parser a opt k d = o .:?? k .!= d -- optional field b :: Text -> Parser (Maybe Bool)@@ -644,7 +649,7 @@ _ -> fail $ "Invalid ProxyType " ++ show pTyp where f :: FromJSON a => Text -> Parser a- f = (obj .:)+ f = (obj .:) . fromText parseJSON v = typeMismatch "ProxyType" v instance ToJSON ProxyType where@@ -768,4 +773,3 @@ 0 -> return AlignTop 1 -> return AlignBottom _ -> fail $ "Invalid integer for IEElementScrollBehavior: " ++ show n-
src/Test/WebDriver/Class.hs view
@@ -1,22 +1,16 @@-{-# LANGUAGE OverloadedStrings, DeriveDataTypeable, FlexibleContexts, CPP,- GeneralizedNewtypeDeriving, RecordWildCards, ConstraintKinds #-}-#ifndef CABAL_BUILD_DEVELOPER-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}-#endif-module Test.WebDriver.Class- ( -- * WebDriver class- WebDriver(..), Method, methodDelete, methodGet, methodPost,- ) where+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ConstraintKinds #-}++module Test.WebDriver.Class (+ -- * WebDriver class+ WebDriver(..), Method, methodDelete, methodGet, methodPost,+ ) where import Test.WebDriver.Session import Data.Aeson import Data.Text (Text)-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid (Monoid) -- for some reason "import Prelude" trick doesn't work with "import Data.Monoid"-#endif- import Network.HTTP.Types.Method (methodDelete, methodGet, methodPost, Method)- import Control.Monad.Trans.Class import Control.Monad.Trans.Error import Control.Monad.Trans.Except@@ -32,6 +26,11 @@ import Control.Monad.Trans.Writer.Strict as SW import Data.CallStack +#if !MIN_VERSION_base(4,8,0)+import Data.Monoid (Monoid) -- for some reason "import Prelude" trick doesn't work with "import Data.Monoid"+#endif++ -- |A class for monads that can handle wire protocol requests. This is the -- operation underlying all of the high-level commands exported in -- "Test.WebDriver.Commands". For more information on the wire protocol see@@ -52,7 +51,6 @@ instance WebDriver wd => WebDriver (LS.StateT s wd) where doCommand rm t a = lift (doCommand rm t a) - instance WebDriver wd => WebDriver (MaybeT wd) where doCommand rm t a = lift (doCommand rm t a) @@ -76,7 +74,6 @@ instance WebDriver wd => WebDriver (ExceptT e wd) where doCommand rm t a = lift (doCommand rm t a)- instance (Monoid w, WebDriver wd) => WebDriver (SRWS.RWST r w s wd) where doCommand rm t a = lift (doCommand rm t a)
src/Test/WebDriver/Commands.hs view
@@ -81,12 +81,10 @@ import Control.Monad import Control.Monad.Base import Data.Aeson-import Data.Aeson.TH import Data.Aeson.Types import Data.ByteString.Base64.Lazy as B64 import Data.ByteString.Lazy as LBS (ByteString, writeFile) import Data.CallStack-import qualified Data.Char as C import qualified Data.Foldable as F import Data.Maybe import Data.String (fromString)@@ -98,6 +96,7 @@ import Test.WebDriver.Capabilities import Test.WebDriver.Class import Test.WebDriver.Commands.Internal+import Test.WebDriver.Cookies import Test.WebDriver.Exceptions.Internal import Test.WebDriver.JSON import Test.WebDriver.Session@@ -125,7 +124,7 @@ -- |Close the current session and the browser associated with it. closeSession :: (HasCallStack, WebDriver wd) => wd ()-closeSession = do s@WDSession {..} <- getSession+closeSession = do s@WDSession {} <- getSession noReturn $ doSessCommand methodDelete "" Null putSession s { wdSessId = Nothing } @@ -306,7 +305,7 @@ windows = doSessCommand methodGet "/window_handles" Null focusWindow :: (HasCallStack, WebDriver wd) => WindowHandle -> wd ()-focusWindow w = noReturn $ doSessCommand methodPost "/window" . single "name" $ w+focusWindow w = noReturn $ doSessCommand methodPost "/window" . single "handle" $ w -- |Closes the given window closeWindow :: (HasCallStack, WebDriver wd) => WindowHandle -> wd ()@@ -327,7 +326,7 @@ -- |Set the dimensions of the current window. setWindowSize :: (HasCallStack, WebDriver wd) => (Word, Word) -> wd ()-setWindowSize = noReturn . doWinCommand methodPost currentWindow "/size"+setWindowSize = ignoreReturn . doWinCommand methodPost currentWindow "/size" . pair ("width", "height") -- |Get the coordinates of the current window.@@ -337,47 +336,7 @@ -- |Set the coordinates of the current window. setWindowPos :: (HasCallStack, WebDriver wd) => (Int, Int) -> wd ()-setWindowPos = noReturn . doWinCommand methodPost currentWindow "/position" . pair ("x","y")---- |Cookies are delicious delicacies. When sending cookies to the server, a value--- of Nothing indicates that the server should use a default value. When receiving--- cookies from the server, a value of Nothing indicates that the server is unable--- to specify the value.-data Cookie = Cookie { cookName :: Text- , cookValue :: Text -- ^- , cookPath :: Maybe Text -- ^path of this cookie.- -- if Nothing, defaults to /- , cookDomain :: Maybe Text -- ^domain of this cookie.- -- if Nothing, the current pages- -- domain is used- , cookSecure :: Maybe Bool -- ^Is this cookie secure?- , cookExpiry :: Maybe Double -- ^Expiry date expressed as- -- seconds since the Unix epoch- -- Nothing indicates that the- -- cookie never expires- } deriving (Eq, Show)---- |Creates a Cookie with only a name and value specified. All other--- fields are set to Nothing, which tells the server to use default values.-mkCookie :: Text -> Text -> Cookie-mkCookie name value = Cookie { cookName = name, cookValue = value,- cookPath = Nothing, cookDomain = Nothing,- cookSecure = Nothing, cookExpiry = Nothing- }--instance FromJSON Cookie where- parseJSON (Object o) = Cookie <$> req "name"- <*> req "value"- <*> opt "path" Nothing- <*> opt "domain" Nothing- <*> opt "secure" Nothing- <*> opt "expiry" Nothing- where- req :: FromJSON a => Text -> Parser a- req = (o .:)- opt :: FromJSON a => Text -> a -> Parser a- opt k d = o .:?? k .!= d- parseJSON v = typeMismatch "Cookie" v+setWindowPos = ignoreReturn . doWinCommand methodPost currentWindow "/position" . pair ("x","y") -- |Retrieve all cookies visible to the current page. cookies :: (HasCallStack, WebDriver wd) => wd [Cookie]@@ -816,7 +775,3 @@ getApplicationCacheStatus :: (WebDriver wd) => wd ApplicationCacheStatus getApplicationCacheStatus = doSessCommand methodGet "/application_cache/status" Null---- Moving this closer to the definition of Cookie seems to cause strange compile--- errors, so I'm leaving it here for now.-$( deriveToJSON (defaultOptions{fieldLabelModifier = map C.toLower . drop 4}) ''Cookie )
src/Test/WebDriver/Commands/Internal.hs view
@@ -1,6 +1,10 @@-{-# LANGUAGE OverloadedStrings, DeriveDataTypeable, GeneralizedNewtypeDeriving, ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables #-}+ {-# OPTIONS_HADDOCK not-home #-}--- |Internal functions used to implement the functions exported by+-- | Internal functions used to implement the functions exported by -- "Test.WebDriver.Commands". These may be useful for implementing non-standard -- webdriver commands. module Test.WebDriver.Commands.Internal
+ src/Test/WebDriver/Cookies.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}++module Test.WebDriver.Cookies where+++import Data.Aeson+import Data.Aeson.TH+import Data.Aeson.Types+import qualified Data.Char as C+import Data.Text (Text)+import Test.WebDriver.JSON++-- | Cookies are delicious delicacies. When sending cookies to the server, a value+-- of Nothing indicates that the server should use a default value. When receiving+-- cookies from the server, a value of Nothing indicates that the server is unable+-- to specify the value.+data Cookie = Cookie { cookName :: Text+ , cookValue :: Text -- ^+ , cookPath :: Maybe Text -- ^path of this cookie.+ -- if Nothing, defaults to /+ , cookDomain :: Maybe Text -- ^domain of this cookie.+ -- if Nothing, the current pages+ -- domain is used+ , cookSecure :: Maybe Bool -- ^Is this cookie secure?+ , cookExpiry :: Maybe Double -- ^Expiry date expressed as+ -- seconds since the Unix epoch+ -- Nothing indicates that the+ -- cookie never expires+ } deriving (Eq, Show)++-- |Creates a Cookie with only a name and value specified. All other+-- fields are set to Nothing, which tells the server to use default values.+mkCookie :: Text -> Text -> Cookie+mkCookie name value = Cookie { cookName = name, cookValue = value,+ cookPath = Nothing, cookDomain = Nothing,+ cookSecure = Nothing, cookExpiry = Nothing+ }++instance FromJSON Cookie where+ parseJSON (Object o) = Cookie <$> req "name"+ <*> req "value"+ <*> opt "path" Nothing+ <*> opt "domain" Nothing+ <*> opt "secure" Nothing+ <*> opt "expiry" Nothing+ where+ req :: FromJSON a => Text -> Parser a+ req = (o .:) . fromText+ opt :: FromJSON a => Text -> a -> Parser a+ opt k d = o .:?? k .!= d+ parseJSON v = typeMismatch "Cookie" v++$( deriveToJSON (defaultOptions{omitNothingFields = True, fieldLabelModifier = map C.toLower . drop 4}) ''Cookie )
src/Test/WebDriver/Exceptions/Internal.hs view
@@ -175,7 +175,7 @@ <*> opt "class" Nothing <*> (catMaybes <$> opt "stackTrace" []) where req :: FromJSON a => Text -> Parser a- req = (o .:) --required key+ req = (o .:) . fromText --required key opt :: FromJSON a => Text -> a -> Parser a opt k d = o .:?? k .!= d --optional key parseJSON v = typeMismatch "FailedCommandInfo" v@@ -186,7 +186,7 @@ <*> reqStr "methodName" <*> req "lineNumber" where req :: FromJSON a => Text -> Parser a- req = (o .:) -- all keys are required+ req = (o .:) . fromText -- all keys are required reqStr :: Text -> Parser String reqStr k = req k >>= maybe (return "") return parseJSON v = typeMismatch "StackFrame" v
+ src/Test/WebDriver/IO.hs view
@@ -0,0 +1,16 @@+module Test.WebDriver.IO where++import Control.Concurrent (threadDelay)+import Control.Monad.IO.Class (liftIO, MonadIO)+import Test.WebDriver+import Test.WebDriver.Class (WebDriver)+++sleepIO :: Double -> IO ()+sleepIO seconds = Control.Concurrent.threadDelay (round (seconds * 1e6))++sleepWD :: (MonadIO wd, WebDriver wd) => Double -> wd ()+sleepWD = liftIO . sleepIO++printWD :: Show s => s -> WD ()+printWD = liftIO . print
src/Test/WebDriver/JSON.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, FlexibleContexts, DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings, FlexibleContexts, DeriveDataTypeable, CPP #-} -- |A collection of convenience functions for using and parsing JSON values -- within 'WD'. All monadic parse errors are converted to asynchronous -- 'BadJSON' exceptions.@@ -28,6 +28,8 @@ , BadJSON(..) -- * parsing commands with no return value , NoReturn(..), noReturn, ignoreReturn++ , fromText ) where import Test.WebDriver.Class (WebDriver) @@ -37,7 +39,6 @@ import Data.ByteString.Lazy.Char8 (ByteString) import Data.Attoparsec.ByteString.Lazy (Result(..)) import qualified Data.Attoparsec.ByteString.Lazy as AP-import qualified Data.HashMap.Strict as HM import Control.Monad (join, void) import Control.Applicative@@ -48,6 +49,18 @@ import Prelude -- hides some "unused import" warnings +#if MIN_VERSION_aeson(2,0,0)+import qualified Data.Aeson.Key as A+import qualified Data.Aeson.KeyMap as HM+fromText :: Text -> A.Key+fromText = A.fromText+#else+import qualified Data.HashMap.Strict as HM+fromText :: Text -> Text+fromText = id+#endif++ instance Exception BadJSON -- |An error occured when parsing a JSON value. newtype BadJSON = BadJSON String@@ -76,18 +89,18 @@ -- |Construct a singleton JSON 'object' from a key and value. single :: ToJSON a => Text -> a -> Value-single a x = object [a .= x]+single a x = object [(fromText a) .= x] -- |Construct a 2-element JSON 'object' from a pair of keys and a pair of -- values. pair :: (ToJSON a, ToJSON b) => (Text,Text) -> (a,b) -> Value-pair (a,b) (x,y) = object [a .= x, b .= y]+pair (a,b) (x,y) = object [fromText a .= x, fromText b .= y] -- |Construct a 3-element JSON 'object' from a triple of keys and a triple of -- values. triple :: (ToJSON a, ToJSON b, ToJSON c) => (Text,Text,Text) -> (a,b,c) -> Value-triple (a,b,c) (x,y,z) = object [a .= x, b.= y, c .= z]+triple (a,b,c) (x,y,z) = object [fromText a .= x, fromText b.= y, fromText c .= z] -- |Parse a lazy 'ByteString' as a top-level JSON 'Value', then convert it to an@@ -101,12 +114,12 @@ -- |This operator is a wrapper over Aeson's '.:' operator. (!:) :: (MonadBaseControl IO wd, FromJSON a) => Object -> Text -> wd a-o !: k = aesonResultToWD $ parse (.: k) o+o !: k = aesonResultToWD $ parse (.: fromText k) o -- |Due to a breaking change in the '.:?' operator of aeson 0.10 (see <https://github.com/bos/aeson/issues/287>) that was subsequently reverted, this operator -- was added to provide consistent behavior compatible with all aeson versions. If the field is either missing or `Null`, this operator should return a `Nothing` result. (.:??) :: FromJSON a => Object -> Text -> Parser (Maybe a)-o .:?? k = fmap join (o .:? k)+o .:?? k = fmap join (o .:? fromText k) -- |Parse a JSON 'Object' as a pair. The first two string arguments specify the
src/Test/WebDriver/Monad.hs view
@@ -5,19 +5,19 @@ ) where import Test.WebDriver.Class-import Test.WebDriver.Session-import Test.WebDriver.Config import Test.WebDriver.Commands+import Test.WebDriver.Config import Test.WebDriver.Internal+import Test.WebDriver.Session import Control.Monad.Base (MonadBase, liftBase)-import Control.Monad.IO.Class import Control.Monad.Fix+import Control.Monad.IO.Class import Control.Monad.Trans.Control (MonadBaseControl(..), StM) import Control.Monad.Trans.State.Strict (StateT, evalStateT, get, put) --import Control.Monad.IO.Class (MonadIO) import Control.Exception.Lifted-import Control.Monad.Catch (MonadThrow, MonadCatch)+import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow) import Control.Applicative import Prelude -- hides some "unused import" warnings@@ -26,7 +26,7 @@ {- |A state monad for WebDriver commands. -} newtype WD a = WD (StateT WDSession IO a)- deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadCatch, MonadFix)+ deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadCatch, MonadFix, MonadMask) instance MonadBase IO WD where liftBase = WD . liftBase@@ -92,7 +92,7 @@ -- |Gets the command history for the current session. getSessionHistory :: WDSessionState wd => wd [SessionHistory]-getSessionHistory = fmap wdSessHist getSession +getSessionHistory = fmap wdSessHist getSession -- |Prints a history of API requests to stdout after computing the given action. dumpSessionHistory :: WDSessionStateControl wd => wd a -> wd a
src/Test/WebDriver/Session.hs view
@@ -1,9 +1,8 @@-{-# LANGUAGE OverloadedStrings, DeriveDataTypeable, FlexibleContexts, ScopedTypeVariables,- GeneralizedNewtypeDeriving, RecordWildCards, ConstraintKinds, CPP #-}--#ifndef CABAL_BUILD_DEVELOPER-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}-#endif+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE CPP #-} module Test.WebDriver.Session ( -- * WDSessionState class@@ -19,7 +18,7 @@ import Test.WebDriver.Session.History import Data.Aeson-import Data.ByteString as BS(ByteString) +import Data.ByteString as BS(ByteString) import Data.Text (Text) import Data.Maybe (listToMaybe) import Data.Monoid@@ -81,7 +80,7 @@ , wdSessHTTPManager :: Manager -- |Number of times to retry a HTTP request if it times out , wdSessHTTPRetryCount :: Int- -- |Custom request headers to add to every HTTP request. + -- |Custom request headers to add to every HTTP request. , wdSessRequestHeaders :: RequestHeaders -- |Custom request headers to add *only* to session creation requests. This is usually done -- when a WebDriver server requires HTTP auth.@@ -108,10 +107,10 @@ -- MonadBaseControl superclass is used for exception handling through -- the lifted-base package. class (Monad m, Applicative m) => WDSessionState m where- + -- |Retrieves the current session state of the monad getSession :: m WDSession- + -- |Sets a new session state for the monad putSession :: WDSession -> m () @@ -120,7 +119,7 @@ -- |Constraint synonym for another common pairing of 'WDSessionState' and 'MonadBaseControl' 'IO'. This -- is commonly used in library types to indicate use of lifted exception handling.-type WDSessionStateControl s = (WDSessionState s, MonadBaseControl IO s) +type WDSessionStateControl s = (WDSessionState s, MonadBaseControl IO s) modifySession :: WDSessionState s => (WDSession -> WDSession) -> s () modifySession f = getSession >>= putSession . f@@ -139,7 +138,7 @@ -- |The most recent SessionHistory entry recorded by this session, if any. mostRecentHistory :: WDSession -> Maybe SessionHistory mostRecentHistory = listToMaybe . wdSessHist- + -- |The most recent HTTP request issued by this session, if any. mostRecentHTTPRequest :: WDSession -> Maybe Request mostRecentHTTPRequest = fmap histRequest . mostRecentHistory@@ -161,15 +160,15 @@ withAuthHeaders wd = do authHeaders <- fmap wdSessAuthHeaders getSession withRequestHeaders authHeaders wd- + instance WDSessionState m => WDSessionState (LS.StateT s m) where getSession = lift getSession putSession = lift . putSession- + instance WDSessionState m => WDSessionState (SS.StateT s m) where getSession = lift getSession putSession = lift . putSession- + instance WDSessionState m => WDSessionState (MaybeT m) where getSession = lift getSession putSession = lift . putSession@@ -181,7 +180,7 @@ instance WDSessionState m => WDSessionState (ListT m) where getSession = lift getSession putSession = lift . putSession- + instance (Monoid w, WDSessionState m) => WDSessionState (LW.WriterT w m) where getSession = lift getSession putSession = lift . putSession@@ -189,11 +188,11 @@ instance (Monoid w, WDSessionState m) => WDSessionState (SW.WriterT w m) where getSession = lift getSession putSession = lift . putSession- + instance WDSessionState m => WDSessionState (ReaderT r m) where getSession = lift getSession putSession = lift . putSession- + instance (Error e, WDSessionState m) => WDSessionState (ErrorT e m) where getSession = lift getSession putSession = lift . putSession@@ -205,7 +204,7 @@ instance (Monoid w, WDSessionState m) => WDSessionState (SRWS.RWST r w s m) where getSession = lift getSession putSession = lift . putSession- + instance (Monoid w, WDSessionState wd) => WDSessionState (LRWS.RWST r w s wd) where getSession = lift getSession putSession = lift . putSession
webdriver.cabal view
@@ -1,95 +1,97 @@-Name: webdriver-Version: 0.9.0.1-Cabal-Version: >= 1.10-License: BSD3-License-File: LICENSE-Author: Adam Curtis-Maintainer: kallisti.dev@gmail.com-Homepage: https://github.com/kallisti-dev/hs-webdriver-Bug-Reports: https://github.com/kallisti-dev/hs-webdriver/issues-Category: Web, Browser, Testing, WebDriver, Selenium-Synopsis: a Haskell client for the Selenium WebDriver protocol-Build-Type: Simple-Extra-Source-Files: README.md, TODO.md, CHANGELOG.md, .ghci-Tested-With: GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1-Description:- A Selenium WebDriver client for Haskell.- You can use it to automate browser sessions- for testing, system administration, etc.- .- For more information about Selenium itself, see- <http://seleniumhq.org/>- .- To find out what's been changed in this version and others,- see the change log at- <https://github.com/kallisti-dev/hs-webdriver/blob/master/CHANGELOG.md>--Source-Repository head- type: git- location: git://github.com/kallisti-dev/hs-webdriver.git- -Flag network-uri- description: Get Network.URI from the network-uri package- default: True+cabal-version: 1.12 -Flag developer- description: Package development mode- default: False- manual: True+-- This file has been generated from package.yaml by hpack version 0.35.0.+--+-- see: https://github.com/sol/hpack -Library- hs-source-dirs: src- default-language: Haskell2010- ghc-options: -Wall- if flag(developer)- cpp-options: -DCABAL_BUILD_DEVELOPER- build-depends: base == 4.*- , aeson >= 0.6.2.0- , http-client >= 0.3- , http-types >= 0.8- , text >= 0.11.3- , bytestring >= 0.9- , attoparsec >= 0.10- , base64-bytestring >= 1.0- , transformers >= 0.4- , monad-control >= 0.3- , transformers-base >= 0.1- , lifted-base >= 0.1- , zip-archive >= 0.1.1.8- , directory > 1.0- , filepath > 1.0- , directory-tree >= 0.11- , temporary >= 1.0- , time > 1.0- , unordered-containers >= 0.1.3- , vector >= 0.3- , exceptions >= 0.4- , scientific >= 0.2- , data-default-class- , call-stack+name: webdriver+version: 0.10.0.0+synopsis: a Haskell client for the Selenium WebDriver protocol+description: A Selenium WebDriver client for Haskell.+ You can use it to automate browser sessions+ for testing, system administration, etc.+ .+ For more information about Selenium itself, see+ <http://seleniumhq.org/>+ .+ To find out what's been changed in this version and others,+ see the change log at+ <https://github.com/kallisti-dev/hs-webdriver/blob/master/CHANGELOG.md>+category: Web, Browser, Testing, WebDriver, Selenium+homepage: https://github.com/codedownio/hs-webdriver#readme+bug-reports: https://github.com/codedownio/hs-webdriver/issues+author: Adam Curtis <kallisti.dev@gmail.com>+maintainer: Tom McLaughlin <tom@codedown.io>+license: BSD3+license-file: LICENSE+build-type: Simple+tested-with:+ GHC == 8.6.5+ , GHC == 8.8.4+ , GHC == 8.10.7+ , GHC == 9.0.2+ , GHC == 9.2.5+extra-source-files:+ README.md+ CHANGELOG.md - if flag(network-uri)- build-depends: network-uri >= 2.6, network >= 2.6- else- build-depends: network-uri < 2.6, network >= 2.4 && < 2.6+source-repository head+ type: git+ location: https://github.com/codedownio/hs-webdriver - exposed-modules: Test.WebDriver- Test.WebDriver.Class- Test.WebDriver.Monad- Test.WebDriver.Session- Test.WebDriver.Session.History- Test.WebDriver.Config- Test.WebDriver.Exceptions- Test.WebDriver.Commands- Test.WebDriver.Commands.Wait- Test.WebDriver.Commands.Internal- Test.WebDriver.Common.Profile- Test.WebDriver.Common.Keys- Test.WebDriver.Firefox.Profile- Test.WebDriver.Chrome.Extension- Test.WebDriver.Capabilities- Test.WebDriver.Types- Test.WebDriver.JSON- Test.WebDriver.Utils- Test.WebDriver.Internal- Test.WebDriver.Exceptions.Internal+library+ exposed-modules:+ Test.WebDriver+ Test.WebDriver.Capabilities+ Test.WebDriver.Chrome.Extension+ Test.WebDriver.Class+ Test.WebDriver.Commands+ Test.WebDriver.Commands.Internal+ Test.WebDriver.Commands.Wait+ Test.WebDriver.Common.Keys+ Test.WebDriver.Common.Profile+ Test.WebDriver.Config+ Test.WebDriver.Cookies+ Test.WebDriver.Exceptions+ Test.WebDriver.Exceptions.Internal+ Test.WebDriver.Firefox.Profile+ Test.WebDriver.Internal+ Test.WebDriver.JSON+ Test.WebDriver.Monad+ Test.WebDriver.Session+ Test.WebDriver.Session.History+ Test.WebDriver.Types+ Test.WebDriver.Utils+ other-modules:+ Test.WebDriver.IO+ hs-source-dirs:+ src+ ghc-options: -Wall+ build-depends:+ aeson >=0.6.2.0+ , attoparsec >=0.10+ , base ==4.*+ , base64-bytestring >=1.0+ , bytestring >=0.9+ , call-stack+ , data-default-class+ , directory >1.0+ , directory-tree >=0.11+ , exceptions >=0.4+ , filepath >1.0+ , http-client >=0.3+ , http-types >=0.8+ , lifted-base >=0.1+ , monad-control >=0.3+ , network >=2.6+ , network-uri >=2.6+ , scientific >=0.2+ , temporary >=1.0+ , text >=0.11.3+ , time >1.0+ , transformers >=0.4+ , transformers-base >=0.1+ , unordered-containers >=0.1.3+ , vector >=0.3+ , zip-archive >=0.1.1.8+ default-language: Haskell2010