-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Write end2end web application tests using webdriver and hspec -- -- For end to end testing of web applications from Haskell, the -- webdriver package is a great tool but just contains the code to -- communicate with the browser. This package integrates webdriver with -- hspec. @package hspec-webdriver @version 1.2.2 -- | Write hspec tests that are webdriver tests, automatically managing the -- webdriver sessions. -- -- This module re-exports functions from Test.Hspec and -- Test.WebDriver.Commands and it is intended that you just import -- Test.Hspec.WebDriver. If you need to import -- Test.Hspec or Test.WebDriver, you should do so using -- a qualified import. -- --
-- {-# LANGUAGE OverloadedStrings #-}
-- module XKCD where
--
-- import Test.Hspec.WebDriver
--
-- allBrowsers :: [Capabilities]
-- allBrowsers = [firefoxCaps, chromeCaps, ieCaps]
--
-- browsersExceptIE :: [Capabilities]
-- browsersExceptIE = [firefoxCaps, chromeCaps]
--
-- main :: IO ()
-- main = hspec $
-- describe "XKCD Tests" $ do
--
-- session "for 327" $ using allBrowsers $ do
-- it "opens the page" $ runWD $
-- openPage "http://www.xkcd.com/327/"
-- it "checks hover text" $ runWD $ do
-- e <- findElem $ ByCSS "div#comic > img"
-- e `shouldBeTag` "img"
-- e `shouldHaveAttr` ("title", "Her daughter is named Help I'm trapped in a driver's license factory.")
--
-- parallel $ session "for 303" $ using browsersExceptIE $ do
-- it "opens the page" $ runWD $
-- openPage "http://www.xkcd.com/303/"
-- it "checks the title" $ runWD $ do
-- e <- findElem $ ById "ctitle"
-- e `shouldBeTag` "div"
-- e `shouldHaveText` "Compiling"
--
--
-- The above code assumes selenium-server-standalone is running on
-- 127.0.0.1:4444 at path /wd/hub (this is the
-- default).
module Test.Hspec.WebDriver
-- | A webdriver example.
--
-- The webdriver action of type WD () should interact
-- with the webpage using commands from Test.WebDriver.Commands
-- (which is re-exported from this module) and then use the
-- expectations in this module. It is possible to split up the
-- spec of a single page into multiple examples where later examples
-- start with the web browser state from the end of the previous example.
-- This is helpful to keep each individual example small and allows the
-- entire spec to be described at the beginning with pending examples.
--
-- The way this works is that you combine examples into a session using
-- session or sessionWith. A webdriver session is then
-- threaded through all examples in a session so that a later example in
-- the session can rely on the webbrowser state as set up by the previous
-- example. The type system enforces that every webdriver example must be
-- located within a call to session or sessionWith. Indeed,
-- a WdExample produces a SpecWith
-- (WdTestSession multi) which can only be converted to
-- Spec using session or sessionWith. The reason for
-- the WdPending constructor is so that a pending example can be
-- specified with type SpecWith (WdTestSession
-- multi) so it can compose with the other webdriver examples.
--
-- The type multi is used when testing multiple sessions at once
-- (e.g. to test multiple interacting users), otherwise it is
-- (). Values of this type are used to determine which browser
-- session the example should be executed against. A new session is
-- created every time a new value of type multi is seen. Note
-- that the type system enforces that every example within the session
-- has the same type multi.
data WdExample multi
WdExample :: multi -> WdOptions -> WD () -> WdExample multi
WdPending :: Maybe String -> WdExample multi
-- | Optional options that can be passed to runWDOptions.
data WdOptions
WdOptions :: Bool -> WdOptions
-- | As soon as an example fails, skip all remaining tests in the session.
-- Defaults to True.
[skipRemainingTestsAfterFailure] :: WdOptions -> Bool
-- | A shorthand for constructing a WdExample from a webdriver
-- action when you are only testing a single browser session at once. See
-- the XKCD example at the top of the page.
runWD :: WD () -> WdExample ()
-- | A version of runWD that accepts some custom options
runWDOptions :: WdOptions -> WD () -> WdExample ()
-- | Create a webdriver example, specifying which of the multiple sessions
-- the example should be executed against. I suggest you create an
-- enumeration for multi, for example:
--
-- -- data TestUser = Gandolf | Bilbo | Legolas -- deriving (Show, Eq, Enum, Bounded) -- -- runUser :: TestUser -> WD () -> WDExample TestUser -- runUser = runWDWith -- -- spec :: Spec -- spec = session "tests some page" $ using [firefoxCaps] $ do -- it "does something with Gandolf" $ runUser Gandolf $ do -- openPage ... -- it "does something with Bilbo" $ runUser Bilbo $ do -- openPage ... -- it "goes back to the Gandolf session" $ runUser Gandolf $ do -- e <- findElem .... -- ... ---- -- In the above code, two sessions are created and the examples will go -- back and forth between the two sessions. Note that a session for -- Legolas will only be created the first time he shows up in a call to -- runUser, which might be never. To share information between -- the sessions (e.g. some data that Gandolf creates that Bilbo should -- expect), the best way I have found is to use IORefs created with -- runIO (wrapped in a utility module). runWDWith :: multi -> WD () -> WdExample multi -- | A version of runWDWith that accepts some custom options runWDWithOptions :: multi -> WdOptions -> WD () -> WdExample multi -- | A pending example. pending :: WdExample multi -- | A pending example with a message. pendingWith :: String -> WdExample multi -- | A version of example which lifts an IO () to a -- webdriver example (so it can be composed with other webdriver -- examples). In the case of multiple sessions, it doesn't really matter -- which session the expectation is executed against, so a default value -- is used. In the case of single sessions, the type is WdExample -- (). example :: Default multi => Expectation -> WdExample multi -- | Combine the examples nested inside this call into a webdriver session -- or multiple sessions. For each of the capabilities in the list, the -- examples are executed one at a time in depth-first order and so later -- examples can rely on the browser state created by earlier examples. -- These passes through the examples are independent for different -- capabilities. Note that when using parallel, the examples -- within a single pass still execute serially. Different passes through -- the examples will be executed in parallel. The sessions are managed as -- follows: -- --
-- allBrowsers :: [Capabilities] -- allBrowsers = [firefoxCaps, chromeCaps, ieCaps] -- -- browsersExceptIE :: [Capabilities] -- browsersExceptIE = [firefoxCaps, chromeCaps] -- -- mobileBrowsers :: [Capabilities] -- mobileBrowsers = [iphoneCaps, ipadCaps, androidCaps] -- -- myspec :: Spec -- myspec = do -- session "for the home page" $ using allBrowsers $ do -- it "loads the page" $ runWD $ do -- ... -- it "scrolls the carosel" $ runWD $ do -- ... -- session "for the users page" $ using browsersExceptIE $ do -- ... --using :: [caps] -> SpecWith (WdTestSession multi) -> ([caps], SpecWith (WdTestSession multi)) -- | Internal state for webdriver test sessions. data WdTestSession multi -- | Default capabilities which can be used in the list passed to -- using. I suggest creating a top-level definition such as -- allBrowsers and browsersWithoutIE such as in the -- XKCD example at the top of the page, so that you do not specify the -- browsers in the individual spec. firefoxCaps :: Capabilities -- | Default capabilities which can be used in the list passed to -- using. I suggest creating a top-level definition such as -- allBrowsers and browsersWithoutIE such as in the -- XKCD example at the top of the page, so that you do not specify the -- browsers in the individual spec. chromeCaps :: Capabilities -- | Default capabilities which can be used in the list passed to -- using. I suggest creating a top-level definition such as -- allBrowsers and browsersWithoutIE such as in the -- XKCD example at the top of the page, so that you do not specify the -- browsers in the individual spec. ieCaps :: Capabilities -- | Default capabilities which can be used in the list passed to -- using. I suggest creating a top-level definition such as -- allBrowsers and browsersWithoutIE such as in the -- XKCD example at the top of the page, so that you do not specify the -- browsers in the individual spec. operaCaps :: Capabilities -- | Default capabilities which can be used in the list passed to -- using. I suggest creating a top-level definition such as -- allBrowsers and browsersWithoutIE such as in the -- XKCD example at the top of the page, so that you do not specify the -- browsers in the individual spec. iphoneCaps :: Capabilities -- | Default capabilities which can be used in the list passed to -- using. I suggest creating a top-level definition such as -- allBrowsers and browsersWithoutIE such as in the -- XKCD example at the top of the page, so that you do not specify the -- browsers in the individual spec. ipadCaps :: Capabilities -- | Default capabilities which can be used in the list passed to -- using. I suggest creating a top-level definition such as -- allBrowsers and browsersWithoutIE such as in the -- XKCD example at the top of the page, so that you do not specify the -- browsers in the individual spec. androidCaps :: Capabilities -- | shouldBe lifted into the WD monad. shouldBe :: (Show a, Eq a) => a -> a -> WD () -- | Asserts that the given element matches the given tag. shouldBeTag :: Element -> Text -> WD () -- | Asserts that the given element has the given text. shouldHaveText :: Element -> Text -> WD () -- | Asserts that the given elemnt has the attribute given by (attr -- name, value). shouldHaveAttr :: Element -> (Text, Text) -> WD () -- | Asserts that the action returns the expected result. shouldReturn :: (Show a, Eq a) => WD a -> a -> WD () -- | Asserts that the action throws an exception. shouldThrow :: (Show e, Eq e, Exception e) => WD a -> e -> WD () -- | Run a given spec and write a report to stdout. Exit with -- exitFailure if at least one spec item fails. -- -- Note: hspec handles command-line options and reads -- config files. This is not always desirable. Use evalSpec and -- runSpecForest if you need more control over these aspects. hspec :: Spec -> IO () type Spec = SpecWith () type SpecWith a = SpecM a () -- | The describe function combines a list of specs into a larger -- spec. describe :: HasCallStack => String -> SpecWith a -> SpecWith a -- | context is an alias for describe. context :: HasCallStack => String -> SpecWith a -> SpecWith a -- | The it function creates a spec item. -- -- A spec item consists of: -- --
-- describe "absolute" $ do -- it "returns a positive number when given a negative number" $ -- absolute (-1) == 1 --it :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) -- | specify is an alias for it. specify :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) -- | parallel marks all spec items of the given spec to be safe for -- parallel evaluation. parallel :: SpecWith a -> SpecWith a -- | Run an IO action while constructing the spec tree. -- -- SpecM is a monad to construct a spec tree, without executing -- any spec items. runIO allows you to run IO actions during -- this construction phase. The IO action is always run when the spec -- tree is constructed (e.g. even when --dry-run is specified). -- If you do not need the result of the IO action to construct the spec -- tree, beforeAll may be more suitable for your use case. runIO :: IO r -> SpecM a r -- | A state monad for WebDriver commands. data () => WD a -- | A structure describing the capabilities of a session. This record -- serves dual roles. -- --