{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
-- Because of webdriver using dangerous constructors
{-# OPTIONS_GHC -fno-warn-incomplete-record-updates #-}
-- For the undefined trick
{-# OPTIONS_GHC -fno-warn-unused-pattern-binds #-}
-- | This is a helper module for 'Test.Syd.Webdriver' to let you use Yesod
-- routes to define webdriver tests.
module Test.Syd.Webdriver.Yesod
( -- * Defining webdriver tests with yesod
webdriverYesodSpec,
-- * Implementing webdriver tests with yesod
openRoute,
openRouteWithParams,
getCurrentRoute,
)
where
import Control.Arrow
import Control.Monad.Reader
import qualified Data.ByteString.Builder as BB
import qualified Data.ByteString.Lazy as LB
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import Network.HTTP.Client as HTTP
import qualified Network.HTTP.Types as HTTP
import Network.URI
import Test.Syd
import Test.Syd.Wai
import Test.Syd.Webdriver
import Test.Syd.Yesod
import Test.WebDriver as WD hiding (setWindowSize)
import qualified Yesod
-- | Run webdriver tests given a 'SetupFunc' for your app.
webdriverYesodSpec ::
Yesod.YesodDispatch app =>
(HTTP.Manager -> SetupFunc app) ->
WebdriverSpec app ->
Spec
webdriverYesodSpec appSetupFunc = webdriverSpec $ \man -> do
site <- appSetupFunc man
YesodClient {..} <- yesodClientSetupFunc man site
pure (yesodClientSiteURI, yesodClientSite)
-- | Open a given yesod 'Route'
openRoute ::
Yesod.RenderRoute app =>
Route app ->
WebdriverTestM app ()
openRoute route = openRouteWithParams route []
-- | Open a given yesod 'Route' with parameters
openRouteWithParams ::
Yesod.RenderRoute app =>
Route app ->
[(Text, Text)] ->
WebdriverTestM app ()
openRouteWithParams route extraParams = do
let (pathPieces, queryParams) = Yesod.renderRoute route
let q = queryTextToQuery $ map (second Just) (queryParams <> extraParams)
let pathBSBuilder = encodePath pathPieces q
let pathBS = LB.toStrict (BB.toLazyByteString pathBSBuilder)
case TE.decodeUtf8' pathBS of
Left err ->
liftIO $
expectationFailure $
unlines
[ unwords
[ "Failed to decode path from bytestring:",
show pathBS
],
show err
]
Right t -> openPath $ T.unpack t
-- | Get the current 'Route'
getCurrentRoute ::
Yesod.ParseRoute app =>
WebdriverTestM app (Route app)
getCurrentRoute = do
currentUrl <- getCurrentURL
case parseURI currentUrl of
Nothing -> liftIO $ expectationFailure $ "Should have been able to parse the current url into an URI: " <> currentUrl
Just URI {..} -> do
let (textPieces, query_) = HTTP.decodePath $ TE.encodeUtf8 $ T.pack $ concat [uriPath, uriQuery]
queryPieces = map unJust $ HTTP.queryToQueryText query_
case Yesod.parseRoute (textPieces, queryPieces) of
Nothing ->
liftIO $
expectationFailure $
unlines
[ "Should have been able to parse an App route from " <> currentUrl,
ppShow (textPieces, queryPieces)
]
Just route -> pure route
where
unJust (a, Just b) = (a, b)
unJust (a, Nothing) = (a, "")