webdriver 0.3.3 → 0.4
raw patch · 12 files changed
+168/−109 lines, 12 filesdep ~base64-bytestringdep ~data-defaultdep ~filepath
Dependency ranges changed: base64-bytestring, data-default, filepath, temporary, transformers, transformers-base, unordered-containers, vector, zip-archive
Files
- CHANGELOG.md +28/−12
- TODO +4/−1
- src/Test/WebDriver.hs +2/−2
- src/Test/WebDriver/Classes.hs +0/−31
- src/Test/WebDriver/Commands.hs +21/−47
- src/Test/WebDriver/Commands/Internal.hs +93/−0
- src/Test/WebDriver/Commands/Wait.hs +1/−1
- src/Test/WebDriver/Exceptions.hs +1/−1
- src/Test/WebDriver/Firefox/Profile.hs +1/−1
- src/Test/WebDriver/JSON.hs +4/−1
- src/Test/WebDriver/Monad.hs +2/−2
- webdriver.cabal +11/−10
CHANGELOG.md view
@@ -1,27 +1,43 @@-# Change Log+#Change Log -## hs-webdriver 0.3.3+##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-*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.+* 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+* 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+* 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 @@ -43,7 +59,7 @@ * 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.+* 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.
TODO view
@@ -6,7 +6,8 @@ -- add support for Opera profiles --- improve documentation. Document errors.+-- improve documentation+ -- document errors. -- provide examples -- basic runSession usage -- intermediate usage@@ -33,3 +34,5 @@ misc. unsorted --Use lazy ByteString where possible. patch base64-bytestring to work with lazy bytestrings++--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
@@ -7,14 +7,14 @@ WD(..), WDSession(..), defaultSession, SessionId(..) -- * Running WebDriver tests , runWD, runSession, withSession, finallyClose, closeOnException+ -- * WebDriver commands+ , module Test.WebDriver.Commands -- * Capabilities and configuration , Capabilities(..), defaultCaps, allCaps , Platform(..), ProxyType(..) -- ** Browser-specific configuration , Browser(..), LogPref(..) , firefox, chrome, ie, opera, iPhone, iPad, android- -- * WebDriver commands- , module Test.WebDriver.Commands -- * Exceptions , module Test.WebDriver.Exceptions ) where
src/Test/WebDriver/Classes.hs view
@@ -7,23 +7,15 @@ SessionState(..), modifySession -- ** WebDriver sessions , WDSession(..), SessionId(..), defaultSession- -- **Convenience function for :sessionId URLs- , doSessCommand- -- * No Session Exception- , NoSessionId(..) ) where --import Test.WebDriver.Internal import Data.Aeson import Network.HTTP (RequestMethod(..)) -import qualified Data.Text as T import Data.Text (Text) import Control.Monad.Trans.Control-import Control.Exception.Lifted (Exception, throwIO)-import Data.Typeable- import Control.Monad.Trans.Maybe import Control.Monad.Trans.Identity import Control.Monad.List@@ -102,29 +94,6 @@ newtype SessionId = SessionId Text deriving (Eq, Ord, Show, Read, FromJSON, ToJSON)---instance Exception NoSessionId--- |A command requiring a session ID was attempted when no session ID was --- available.-newtype NoSessionId = NoSessionId String - deriving (Eq, Show, Typeable)----- |This a convenient wrapper around 'doCommand' that automatically prepends--- the session URL parameter to the wire command URL. For example, passing--- a URL of "/refresh" will expand to "/session/:sessionId/refresh".-doSessCommand :: (WebDriver wd, ToJSON a, FromJSON b) => - RequestMethod -> Text -> a -> wd b-doSessCommand method path args = do- WDSession { wdSessId = mSessId } <- getSession- case mSessId of - Nothing -> throwIO . NoSessionId $ msg- where - msg = "doSessCommand: No session ID found for relative URL "- ++ show path- Just (SessionId sId) -> doCommand method - (T.concat ["/session/", sId, path]) args instance SessionState m => SessionState (LS.StateT s m) where
src/Test/WebDriver/Commands.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE OverloadedStrings, ScopedTypeVariables, ExistentialQuantification,- GeneralizedNewtypeDeriving, TemplateHaskell #-}+ TemplateHaskell #-} -- |This module exports basic WD actions that can be used to interact with a -- browser session. module Test.WebDriver.Commands @@ -18,6 +18,7 @@ , findElem, findElems, findElemFrom, findElemsFrom -- ** Interacting with elements , click, submit, getText+ -- *** Sending key inputs to elements , sendKeys, sendRawKeys, clearInput -- ** Element information , attr, cssProp, elemPos, elemSize@@ -70,6 +71,7 @@ , serverStatus ) where +import Test.WebDriver.Commands.Internal import Test.WebDriver.Classes import Test.WebDriver.JSON import Test.WebDriver.Capabilities@@ -93,7 +95,6 @@ import Control.Exception.Lifted (throwIO, catch, handle) import Data.Word import Data.String (fromString)-import Data.Default import qualified Data.Char as C import Prelude hiding (catch)@@ -112,7 +113,7 @@ sessUrl <- doCommand POST "/session" . single "desiredCapabilities" $ caps let sessId = SessionId . last . filter (not . T.null) . splitOn "/" $ sessUrl modifySession $ \sess -> sess {wdSessId = Just sessId}- return =<< getSession+ getSession -- |Retrieve a list of active sessions and their 'Capabilities'. sessions :: WebDriver wd => wd [(SessionId, Capabilities)]@@ -263,18 +264,6 @@ focusFrame :: WebDriver wd => FrameSelector -> wd () focusFrame s = doSessCommand POST "/frame" . single "id" $ s -{- |An opaque identifier for a browser window -}-newtype WindowHandle = WindowHandle Text- deriving (Eq, Ord, Show, Read, - FromJSON, ToJSON)-instance Default WindowHandle where- def = currentWindow---- |A special 'WindowHandle' that always refers to the currently focused window.--- This is also used by the 'Default' instance.-currentWindow :: WindowHandle-currentWindow = WindowHandle "current"- -- |Returns a handle to the currently focused window getCurrentWindow :: WebDriver wd => wd WindowHandle getCurrentWindow = doSessCommand GET "/window_handle" ()@@ -313,11 +302,6 @@ setWindowPos :: WebDriver wd => (Int, Int) -> wd () setWindowPos = doWinCommand POST currentWindow "/position" . pair ("x","y") -doWinCommand :: (WebDriver wd, ToJSON a, FromJSON b) => - RequestMethod -> WindowHandle -> Text -> a -> wd b-doWinCommand m (WindowHandle w) path a = - doSessCommand m (T.concat ["/window/", w, path]) a- -- |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@@ -389,17 +373,6 @@ getTitle :: WebDriver wd => wd Text getTitle = doSessCommand GET "/title" () -{- |An opaque identifier for a web page element. -}-newtype Element = Element Text- deriving (Eq, Ord, Show, Read)--instance FromJSON Element where- parseJSON (Object o) = Element <$> o .: "ELEMENT"- parseJSON v = typeMismatch "Element" v--instance ToJSON Element where- toJSON (Element e) = object ["ELEMENT" .= e]- -- |Specifies element(s) within a DOM tree using various selection methods. data Selector = ById Text | ByName Text@@ -522,11 +495,6 @@ (</=>) :: WebDriver wd => Element -> Element -> wd Bool e1 </=> e2 = not <$> (e1 <==> e2) -doElemCommand :: (WebDriver wd, ToJSON a, FromJSON b) => - RequestMethod -> Element -> Text -> a -> wd b-doElemCommand m (Element e) path a =- doSessCommand m (T.concat ["/element/", e, path]) a- -- |A screen orientation data Orientation = Landscape | Portrait deriving (Eq, Show, Ord, Bounded, Enum)@@ -585,14 +553,16 @@ deriving (Eq, Show, Ord, Bounded, Enum) instance ToJSON MouseButton where- toJSON = String . toUpper . fromString . show+ toJSON = toJSON . fromEnum instance FromJSON MouseButton where- parseJSON (String jStr) = case toLower jStr of- "left" -> return LeftButton- "middle" -> return MiddleButton- "right" -> return RightButton- err -> fail $ "Invalid MouseButton string " ++ show err+ parseJSON v = do+ n <- parseJSON v+ case n :: Integer of+ 0 -> return LeftButton+ 1 -> return MiddleButton+ 2 -> return RightButton+ err -> fail $ "Invalid JSON for MouseButton: " ++ show err parseJSON v = typeMismatch "MouseButton" v -- |Click at the current mouse position with the given mouse button.@@ -710,10 +680,6 @@ . fromArchive . (`addEntryToArchive` emptyArchive) --- |An HTML 5 storage type-data WebStorageType = LocalStorage | SessionStorage - deriving (Eq, Show, Ord, Bounded, Enum)- -- |Get the current number of keys in a web storage area. storageSize :: WebDriver wd => WebStorageType -> wd Integer storageSize s = doStorageCommand GET s "/size" ()@@ -726,6 +692,10 @@ deleteAllKeys :: WebDriver wd => WebStorageType -> wd () deleteAllKeys s = doStorageCommand DELETE s "" () +-- |An HTML 5 storage type+data WebStorageType = LocalStorage | SessionStorage + deriving (Eq, Show, Ord, Bounded, Enum)+ -- |Get the value associated with a key in the given web storage area. -- Unset keys result in empty strings, since the Web Storage spec -- makes no distinction between the empty string and an undefined value.@@ -740,11 +710,15 @@ deleteKey :: WebDriver wd => WebStorageType -> Text -> wd () deleteKey s k = doStorageCommand POST s ("/key/" `T.append` k) () +-- |A wrapper around 'doStorageCommand' to create web storage URLs. doStorageCommand :: (WebDriver wd, ToJSON a, FromJSON b) => RequestMethod -> WebStorageType -> Text -> a -> wd b doStorageCommand m s path a = doSessCommand m (T.concat ["/", s', path]) a where s' = case s of LocalStorage -> "local_storage" SessionStorage -> "session_storage"- +++-- Moving this closer to the definition of Cookie seems to cause strange compile +-- errors, so I'm leaving it here for now. $( deriveToJSON (map C.toLower . drop 4) ''Cookie )
+ src/Test/WebDriver/Commands/Internal.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE OverloadedStrings, DeriveDataTypeable, GeneralizedNewtypeDeriving #-}+{-# OPTIONS_HADDOCK not-home #-}+-- |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 + (-- * Low-level webdriver functions+ doCommand+ -- ** Commands with :sessionId URL parameter+ , doSessCommand, SessionId(..)+ -- ** Commands with element :id URL parameters+ , doElemCommand, Element(..)+ -- ** Commands with :windowHandle URL parameters+ , doWinCommand, WindowHandle(..), currentWindow+ -- * Exceptions+ , NoSessionId(..)+ ) where++import Test.WebDriver.Classes++import Data.Aeson+import Data.Aeson.Types+import Data.Text (Text)+import qualified Data.Text as T+import Control.Exception.Lifted+import Data.Typeable+import Data.Default+import Control.Applicative++{- |An opaque identifier for a web page element. -}+newtype Element = Element Text+ deriving (Eq, Ord, Show, Read)++instance FromJSON Element where+ parseJSON (Object o) = Element <$> o .: "ELEMENT"+ parseJSON v = typeMismatch "Element" v++instance ToJSON Element where+ toJSON (Element e) = object ["ELEMENT" .= e]+++{- |An opaque identifier for a browser window -}+newtype WindowHandle = WindowHandle Text+ deriving (Eq, Ord, Show, Read, + FromJSON, ToJSON)+instance Default WindowHandle where+ def = currentWindow++-- |A special 'WindowHandle' that always refers to the currently focused window.+-- This is also used by the 'Default' instance.+currentWindow :: WindowHandle+currentWindow = WindowHandle "current"++instance Exception NoSessionId+-- |A command requiring a session ID was attempted when no session ID was +-- available.+newtype NoSessionId = NoSessionId String + deriving (Eq, Show, Typeable)++-- |This a convenient wrapper around 'doCommand' that automatically prepends+-- the session URL parameter to the wire command URL. For example, passing+-- a URL of \"/refresh\" will expand to \"/session/:sessionId/refresh\", where+-- :sessionId is a URL parameter as described in +-- <http://code.google.com/p/selenium/wiki/JsonWireProtocol>+doSessCommand :: (WebDriver wd, ToJSON a, FromJSON b) => + RequestMethod -> Text -> a -> wd b+doSessCommand method path args = do+ WDSession { wdSessId = mSessId } <- getSession+ case mSessId of + Nothing -> throwIO . NoSessionId $ msg+ where + msg = "doSessCommand: No session ID found for relative URL "+ ++ show path+ Just (SessionId sId) -> doCommand method + (T.concat ["/session/", sId, path]) args++-- |A wrapper around 'doSessCommand' to create element URLs.+-- For example, passing a URL of "/active" will expand to +-- \"/session/:sessionId/element/:id/active\", where :sessionId and :id are URL+-- parameters as described in the wire protocol.+doElemCommand :: (WebDriver wd, ToJSON a, FromJSON b) => + RequestMethod -> Element -> Text -> a -> wd b+doElemCommand m (Element e) path a =+ doSessCommand m (T.concat ["/element/", e, path]) a++-- |A wrapper around 'doSessCommand' to create window handle URLS.+-- For example, passing a URL of \"/size\" will expand to+-- \"/session/:sessionId/window/:windowHandle/\", where :sessionId and +-- :windowHandle are URL parameters as described in the wire protocol+doWinCommand :: (WebDriver wd, ToJSON a, FromJSON b) => + RequestMethod -> WindowHandle -> Text -> a -> wd b+doWinCommand m (WindowHandle w) path a = + doSessCommand m (T.concat ["/window/", w, path]) a
src/Test/WebDriver/Commands/Wait.hs view
@@ -49,7 +49,7 @@ expectAll p xs = expect . and =<< mapM p xs -- |Wait until either the given action succeeds or the timeout is reached.--- The action will be retried every .25 seconds until no 'ExpectFailed' or+-- The action will be retried every .5 seconds until no 'ExpectFailed' or -- 'FailedCommand' 'NoSuchElement' exceptions occur. If the timeout is reached, -- then a 'Timeout' exception will be raised. The timeout value -- is expressed in seconds.
src/Test/WebDriver/Exceptions.hs view
@@ -7,5 +7,5 @@ , mkFailedCommandInfo, failedCommand )where import Test.WebDriver.Internal-import Test.WebDriver.Classes import Test.WebDriver.JSON+import Test.WebDriver.Commands.Internal
src/Test/WebDriver/Firefox/Profile.hs view
@@ -14,7 +14,7 @@ -- * Extensions , addExtension, deleteExtension, hasExtension -- * Other files and directories- , addFile, removeFile, hasFile+ , addFile, deleteFile, hasFile -- * Miscellaneous profile operations , unionProfiles, onProfileFiles, onProfilePrefs -- * Loading and preparing profiles
src/Test/WebDriver/JSON.hs view
@@ -1,7 +1,10 @@ {-# LANGUAGE OverloadedStrings, FlexibleContexts, DeriveDataTypeable #-} -- |A collection of convenience functions for using and parsing JSON values--- within 'WD'. All monadic parse errors are converted to asynchronous +-- within 'WD'. All monadic parse errors are converted to asynchronous -- 'BadJSON' exceptions.+-- +-- These functions are used internally to implement webdriver commands, and may+-- be useful for implementing non-standard commands. module Test.WebDriver.JSON ( -- * Access a JSON object key (!:)
src/Test/WebDriver/Monad.hs view
@@ -65,11 +65,11 @@ -- |A finalizer ensuring that the session is always closed at the end of -- the given 'WD' action, regardless of any exceptions.-finallyClose:: WD a -> WD a +finallyClose:: WebDriver wd => wd a -> wd a finallyClose wd = closeOnException wd <* closeSession -- |A variant of 'finallyClose' that only closes the session when an -- asynchronous exception is thrown, but otherwise leaves the session open -- if the action was successful.-closeOnException :: WD a -> WD a+closeOnException :: WebDriver wd => wd a -> wd a closeOnException wd = wd `onException` closeSession
webdriver.cabal view
@@ -1,5 +1,5 @@ Name: webdriver-Version: 0.3.3+Version: 0.4 Cabal-Version: >= 1.6 License: BSD3 License-File: LICENSE@@ -37,21 +37,21 @@ , bytestring == 0.9.* , text >= 0.7 && < 0.12 , time == 1.*- , transformers >= 0.2- , zip-archive >= 0.1.1.7+ , transformers >= 0.2 && < 0.5+ , zip-archive >= 0.1.1.8 && < 0.3 , directory == 1.*- , filepath >=1.1- , unordered-containers >= 0.1.3+ , filepath == 1.*+ , unordered-containers >= 0.1.3 && < 0.4 , attoparsec == 0.10.* , monad-control == 0.3.*- , transformers-base < 1.0- , vector >= 0.3+ , transformers-base >= 0.1 && < 1.0+ , vector >= 0.3 && < 0.11 , lifted-base == 0.1.* , MonadCatchIO-transformers >= 0.3 && < 0.4 , filesystem-trees >= 0.1.0.2 && < 0.2- , data-default- , temporary- , base64-bytestring+ , data-default >= 0.2 && < 1.0+ , temporary >= 1.0 && < 2.0+ , base64-bytestring == 0.1.* , cond >= 0.3 && < 0.5 exposed-modules: Test.WebDriver@@ -60,6 +60,7 @@ Test.WebDriver.Exceptions Test.WebDriver.Commands Test.WebDriver.Commands.Wait+ Test.WebDriver.Commands.Internal Test.WebDriver.Common.Profile Test.WebDriver.Firefox.Profile Test.WebDriver.Chrome.Extension