{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Yesod.Test
(
yesodSpec
, YesodSpec
, yesodSpecWithSiteGenerator
, yesodSpecWithSiteGeneratorAndArgument
, yesodSpecApp
, YesodExample
, YesodExampleData(..)
, TestApp
, YSpec
, testApp
, YesodSpecTree (..)
, ydescribe
, yit
, testModifySite
, testSetCookie
, testDeleteCookie
, testModifyCookies
, testClearCookies
, get
, post
, postBody
, performMethod
, followRedirect
, getLocation
, request
, addRequestHeader
, addBasicAuthHeader
, setMethod
, addPostParam
, addGetParam
, addBareGetParam
, addFile
, setRequestBody
, RequestBuilder
, SIO
, setUrl
, clickOn
, byLabel
, byLabelExact
, byLabelContain
, byLabelPrefix
, byLabelSuffix
, bySelectorLabelContain
, fileByLabel
, fileByLabelExact
, fileByLabelContain
, fileByLabelPrefix
, fileByLabelSuffix
, chooseByLabel
, checkByLabel
, selectByLabel
, addToken
, addToken_
, addTokenFromCookie
, addTokenFromCookieNamedToHeaderNamed
, assertEqual
, assertNotEq
, assertEqualNoShow
, assertEq
, assertHeader
, assertNoHeader
, statusIs
, bodyEquals
, bodyContains
, bodyNotContains
, htmlAllContain
, htmlAnyContain
, htmlNoneContain
, htmlCount
, requireJSONResponse
, getTestYesod
, getResponse
, getRequestCookies
, printBody
, printMatches
, htmlQuery
, parseHTML
, withResponse
) where
import qualified Test.Hspec.Core.Spec as Hspec
import qualified Data.List as DL
import qualified Data.ByteString.Char8 as BS8
import Data.ByteString (ByteString)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Text.Encoding.Error as TErr
import qualified Data.ByteString.Lazy.Char8 as BSL8
import qualified Test.HUnit as HUnit
import qualified Network.HTTP.Types as H
#if MIN_VERSION_network(3, 0, 0)
import qualified Network.Socket as Sock
#else
import qualified Network.Socket.Internal as Sock
#endif
import Data.CaseInsensitive (CI)
import qualified Data.CaseInsensitive as CI
import qualified Text.Blaze.Renderer.String as Blaze
import qualified Text.Blaze as Blaze
import Network.Wai
import Network.Wai.Test hiding (assertHeader, assertNoHeader, request)
import Control.Monad.IO.Class
import System.IO
import Yesod.Core.Unsafe (runFakeHandler)
import Yesod.Test.TransversingCSS
import Yesod.Core
import qualified Data.Text.Lazy as TL
import Data.Text.Lazy.Encoding (encodeUtf8, decodeUtf8, decodeUtf8With)
import Text.XML.Cursor hiding (element)
import qualified Text.XML.Cursor as C
import qualified Text.HTML.DOM as HD
import Control.Monad.Trans.Writer
import qualified Data.Map as M
import qualified Web.Cookie as Cookie
import qualified Blaze.ByteString.Builder as Builder
import Data.Time.Clock (getCurrentTime)
import Control.Applicative ((<$>))
import Text.Show.Pretty (ppShow)
import Data.Monoid (mempty)
#if MIN_VERSION_base(4,9,0)
import GHC.Stack (HasCallStack)
#elif MIN_VERSION_base(4,8,1)
import GHC.Stack (CallStack)
type HasCallStack = (?callStack :: CallStack)
#else
import GHC.Exts (Constraint)
type HasCallStack = (() :: Constraint)
#endif
import Data.ByteArray.Encoding (convertToBase, Base(..))
import Network.HTTP.Types.Header (hContentType)
import Data.Aeson (eitherDecode')
import Control.Monad (unless)
import Yesod.Test.Internal (getBodyTextPreview, contentTypeHeaderIsUtf8)
import Yesod.Test.Internal.SIO
{-# DEPRECATED byLabel "This function seems to have multiple bugs (ref: https://github.com/yesodweb/yesod/pull/1459). Use byLabelExact, byLabelContain, byLabelPrefix or byLabelSuffix instead" #-}
{-# DEPRECATED fileByLabel "This function seems to have multiple bugs (ref: https://github.com/yesodweb/yesod/pull/1459). Use fileByLabelExact, fileByLabelContain, fileByLabelPrefix or fileByLabelSuffix instead" #-}
data YesodExampleData site = YesodExampleData
{ forall site. YesodExampleData site -> Application
yedApp :: !Application
, forall site. YesodExampleData site -> site
yedSite :: !site
, forall site. YesodExampleData site -> Cookies
yedCookies :: !Cookies
, forall site. YesodExampleData site -> Maybe SResponse
yedResponse :: !(Maybe SResponse)
}
type YesodExample site = SIO (YesodExampleData site)
type Cookies = M.Map ByteString Cookie.SetCookie
type YesodSpec site = Writer [YesodSpecTree site] ()
data YesodSpecTree site
= YesodSpecGroup String [YesodSpecTree site]
| YesodSpecItem String (YesodExample site ())
getTestYesod :: YesodExample site site
getTestYesod :: forall site. YesodExample site site
getTestYesod = (YesodExampleData site -> site)
-> SIO (YesodExampleData site) (YesodExampleData site)
-> SIO (YesodExampleData site) site
forall a b.
(a -> b)
-> SIO (YesodExampleData site) a -> SIO (YesodExampleData site) b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap YesodExampleData site -> site
forall site. YesodExampleData site -> site
yedSite SIO (YesodExampleData site) (YesodExampleData site)
forall s. SIO s s
getSIO
getResponse :: YesodExample site (Maybe SResponse)
getResponse :: forall site. YesodExample site (Maybe SResponse)
getResponse = (YesodExampleData site -> Maybe SResponse)
-> SIO (YesodExampleData site) (YesodExampleData site)
-> SIO (YesodExampleData site) (Maybe SResponse)
forall a b.
(a -> b)
-> SIO (YesodExampleData site) a -> SIO (YesodExampleData site) b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap YesodExampleData site -> Maybe SResponse
forall site. YesodExampleData site -> Maybe SResponse
yedResponse SIO (YesodExampleData site) (YesodExampleData site)
forall s. SIO s s
getSIO
data RequestBuilderData site = RequestBuilderData
{ forall site. RequestBuilderData site -> RBDPostData
rbdPostData :: RBDPostData
, forall site. RequestBuilderData site -> Maybe SResponse
rbdResponse :: (Maybe SResponse)
, forall site. RequestBuilderData site -> ByteString
rbdMethod :: H.Method
, forall site. RequestBuilderData site -> site
rbdSite :: site
, forall site. RequestBuilderData site -> [Text]
rbdPath :: [T.Text]
, forall site. RequestBuilderData site -> Query
rbdGets :: H.Query
, :: H.RequestHeaders
}
data RBDPostData = MultipleItemsPostData [RequestPart]
| BinaryPostData BSL8.ByteString
data RequestPart
= ReqKvPart T.Text T.Text
| ReqFilePart T.Text FilePath BSL8.ByteString T.Text
type RequestBuilder site = SIO (RequestBuilderData site)
ydescribe :: String -> YesodSpec site -> YesodSpec site
ydescribe :: forall site. String -> YesodSpec site -> YesodSpec site
ydescribe String
label YesodSpec site
yspecs = [YesodSpecTree site] -> YesodSpec site
forall (m :: * -> *) w. Monad m => w -> WriterT w m ()
tell [String -> [YesodSpecTree site] -> YesodSpecTree site
forall site. String -> [YesodSpecTree site] -> YesodSpecTree site
YesodSpecGroup String
label ([YesodSpecTree site] -> YesodSpecTree site)
-> [YesodSpecTree site] -> YesodSpecTree site
forall a b. (a -> b) -> a -> b
$ YesodSpec site -> [YesodSpecTree site]
forall w a. Writer w a -> w
execWriter YesodSpec site
yspecs]
yesodSpec :: YesodDispatch site
=> site
-> YesodSpec site
-> Hspec.Spec
yesodSpec :: forall site. YesodDispatch site => site -> YesodSpec site -> Spec
yesodSpec site
site YesodSpec site
yspecs =
[SpecTree ()] -> Spec
forall a. [SpecTree a] -> SpecWith a
Hspec.fromSpecList ([SpecTree ()] -> Spec) -> [SpecTree ()] -> Spec
forall a b. (a -> b) -> a -> b
$ (YesodSpecTree site -> SpecTree ())
-> [YesodSpecTree site] -> [SpecTree ()]
forall a b. (a -> b) -> [a] -> [b]
map YesodSpecTree site -> SpecTree ()
unYesod ([YesodSpecTree site] -> [SpecTree ()])
-> [YesodSpecTree site] -> [SpecTree ()]
forall a b. (a -> b) -> a -> b
$ YesodSpec site -> [YesodSpecTree site]
forall w a. Writer w a -> w
execWriter YesodSpec site
yspecs
where
unYesod :: YesodSpecTree site -> SpecTree ()
unYesod (YesodSpecGroup String
x [YesodSpecTree site]
y) = String -> [SpecTree ()] -> SpecTree ()
forall a. HasCallStack => String -> [SpecTree a] -> SpecTree a
Hspec.specGroup String
x ([SpecTree ()] -> SpecTree ()) -> [SpecTree ()] -> SpecTree ()
forall a b. (a -> b) -> a -> b
$ (YesodSpecTree site -> SpecTree ())
-> [YesodSpecTree site] -> [SpecTree ()]
forall a b. (a -> b) -> [a] -> [b]
map YesodSpecTree site -> SpecTree ()
unYesod [YesodSpecTree site]
y
unYesod (YesodSpecItem String
x YesodExample site ()
y) = String -> IO () -> SpecTree (Arg (IO ()))
forall e.
(HasCallStack, Example e) =>
String -> e -> SpecTree (Arg e)
Hspec.specItem String
x (IO () -> SpecTree (Arg (IO ())))
-> IO () -> SpecTree (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
app <- site -> IO Application
forall site. YesodDispatch site => site -> IO Application
toWaiAppPlain site
site
evalSIO y YesodExampleData
{ yedApp = app
, yedSite = site
, yedCookies = M.empty
, yedResponse = Nothing
}
yesodSpecWithSiteGenerator :: YesodDispatch site
=> IO site
-> YesodSpec site
-> Hspec.Spec
yesodSpecWithSiteGenerator :: forall site.
YesodDispatch site =>
IO site -> YesodSpec site -> Spec
yesodSpecWithSiteGenerator IO site
getSiteAction =
(() -> IO site) -> YesodSpec site -> Spec
forall site a.
YesodDispatch site =>
(a -> IO site) -> YesodSpec site -> SpecWith a
yesodSpecWithSiteGeneratorAndArgument (IO site -> () -> IO site
forall a b. a -> b -> a
const IO site
getSiteAction)
yesodSpecWithSiteGeneratorAndArgument :: YesodDispatch site
=> (a -> IO site)
-> YesodSpec site
-> Hspec.SpecWith a
yesodSpecWithSiteGeneratorAndArgument :: forall site a.
YesodDispatch site =>
(a -> IO site) -> YesodSpec site -> SpecWith a
yesodSpecWithSiteGeneratorAndArgument a -> IO site
getSiteAction YesodSpec site
yspecs =
[SpecTree a] -> SpecWith a
forall a. [SpecTree a] -> SpecWith a
Hspec.fromSpecList ([SpecTree a] -> SpecWith a) -> [SpecTree a] -> SpecWith a
forall a b. (a -> b) -> a -> b
$ (YesodSpecTree site -> SpecTree a)
-> [YesodSpecTree site] -> [SpecTree a]
forall a b. (a -> b) -> [a] -> [b]
map ((a -> IO site) -> YesodSpecTree site -> SpecTree a
forall {site} {t}.
YesodDispatch site =>
(t -> IO site) -> YesodSpecTree site -> SpecTree t
unYesod a -> IO site
getSiteAction) ([YesodSpecTree site] -> [SpecTree a])
-> [YesodSpecTree site] -> [SpecTree a]
forall a b. (a -> b) -> a -> b
$ YesodSpec site -> [YesodSpecTree site]
forall w a. Writer w a -> w
execWriter YesodSpec site
yspecs
where
unYesod :: (t -> IO site) -> YesodSpecTree site -> SpecTree (Arg (t -> IO ()))
unYesod t -> IO site
getSiteAction' (YesodSpecGroup String
x [YesodSpecTree site]
y) = String
-> [SpecTree (Arg (t -> IO ()))] -> SpecTree (Arg (t -> IO ()))
forall a. HasCallStack => String -> [SpecTree a] -> SpecTree a
Hspec.specGroup String
x ([SpecTree (Arg (t -> IO ()))] -> SpecTree (Arg (t -> IO ())))
-> [SpecTree (Arg (t -> IO ()))] -> SpecTree (Arg (t -> IO ()))
forall a b. (a -> b) -> a -> b
$ (YesodSpecTree site -> SpecTree (Arg (t -> IO ())))
-> [YesodSpecTree site] -> [SpecTree (Arg (t -> IO ()))]
forall a b. (a -> b) -> [a] -> [b]
map ((t -> IO site) -> YesodSpecTree site -> SpecTree (Arg (t -> IO ()))
unYesod t -> IO site
getSiteAction') [YesodSpecTree site]
y
unYesod t -> IO site
getSiteAction' (YesodSpecItem String
x YesodExample site ()
y) = String -> (t -> IO ()) -> SpecTree (Arg (t -> IO ()))
forall e.
(HasCallStack, Example e) =>
String -> e -> SpecTree (Arg e)
Hspec.specItem String
x ((t -> IO ()) -> SpecTree (Arg (t -> IO ())))
-> (t -> IO ()) -> SpecTree (Arg (t -> IO ()))
forall a b. (a -> b) -> a -> b
$ \t
a -> do
site <- t -> IO site
getSiteAction' t
a
app <- toWaiAppPlain site
evalSIO y YesodExampleData
{ yedApp = app
, yedSite = site
, yedCookies = M.empty
, yedResponse = Nothing
}
yesodSpecApp :: YesodDispatch site
=> site
-> IO Application
-> YesodSpec site
-> Hspec.Spec
yesodSpecApp :: forall site.
YesodDispatch site =>
site -> IO Application -> YesodSpec site -> Spec
yesodSpecApp site
site IO Application
getApp YesodSpec site
yspecs =
[SpecTree ()] -> Spec
forall a. [SpecTree a] -> SpecWith a
Hspec.fromSpecList ([SpecTree ()] -> Spec) -> [SpecTree ()] -> Spec
forall a b. (a -> b) -> a -> b
$ (YesodSpecTree site -> SpecTree ())
-> [YesodSpecTree site] -> [SpecTree ()]
forall a b. (a -> b) -> [a] -> [b]
map YesodSpecTree site -> SpecTree ()
unYesod ([YesodSpecTree site] -> [SpecTree ()])
-> [YesodSpecTree site] -> [SpecTree ()]
forall a b. (a -> b) -> a -> b
$ YesodSpec site -> [YesodSpecTree site]
forall w a. Writer w a -> w
execWriter YesodSpec site
yspecs
where
unYesod :: YesodSpecTree site -> SpecTree ()
unYesod (YesodSpecGroup String
x [YesodSpecTree site]
y) = String -> [SpecTree ()] -> SpecTree ()
forall a. HasCallStack => String -> [SpecTree a] -> SpecTree a
Hspec.specGroup String
x ([SpecTree ()] -> SpecTree ()) -> [SpecTree ()] -> SpecTree ()
forall a b. (a -> b) -> a -> b
$ (YesodSpecTree site -> SpecTree ())
-> [YesodSpecTree site] -> [SpecTree ()]
forall a b. (a -> b) -> [a] -> [b]
map YesodSpecTree site -> SpecTree ()
unYesod [YesodSpecTree site]
y
unYesod (YesodSpecItem String
x YesodExample site ()
y) = String -> IO () -> SpecTree (Arg (IO ()))
forall e.
(HasCallStack, Example e) =>
String -> e -> SpecTree (Arg e)
Hspec.specItem String
x (IO () -> SpecTree (Arg (IO ())))
-> IO () -> SpecTree (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
app <- IO Application
getApp
evalSIO y YesodExampleData
{ yedApp = app
, yedSite = site
, yedCookies = M.empty
, yedResponse = Nothing
}
yit :: String -> YesodExample site () -> YesodSpec site
yit :: forall site. String -> YesodExample site () -> YesodSpec site
yit String
label YesodExample site ()
example = [YesodSpecTree site] -> WriterT [YesodSpecTree site] Identity ()
forall (m :: * -> *) w. Monad m => w -> WriterT w m ()
tell [String -> YesodExample site () -> YesodSpecTree site
forall site. String -> YesodExample site () -> YesodSpecTree site
YesodSpecItem String
label YesodExample site ()
example]
testModifySite :: YesodDispatch site
=> (site -> IO (site, Middleware))
-> YesodExample site ()
testModifySite :: forall site.
YesodDispatch site =>
(site -> IO (site, Middleware)) -> YesodExample site ()
testModifySite site -> IO (site, Middleware)
newSiteFn = do
currentSite <- YesodExample site site
forall site. YesodExample site site
getTestYesod
(newSite, middleware) <- liftIO $ newSiteFn currentSite
app <- liftIO $ toWaiAppPlain newSite
modifySIO $ \YesodExampleData site
yed -> YesodExampleData site
yed { yedSite = newSite, yedApp = middleware app }
testSetCookie :: Cookie.SetCookie -> YesodExample site ()
testSetCookie :: forall site. SetCookie -> YesodExample site ()
testSetCookie SetCookie
cookie = do
let key :: ByteString
key = SetCookie -> ByteString
Cookie.setCookieName SetCookie
cookie
(YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ())
-> (YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ \YesodExampleData site
yed -> YesodExampleData site
yed { yedCookies = M.insert key cookie (yedCookies yed) }
testDeleteCookie :: ByteString -> YesodExample site ()
testDeleteCookie :: forall site. ByteString -> YesodExample site ()
testDeleteCookie ByteString
k = do
(YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ())
-> (YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ \YesodExampleData site
yed -> YesodExampleData site
yed { yedCookies = M.delete k (yedCookies yed) }
testModifyCookies :: (Cookies -> Cookies) -> YesodExample site ()
testModifyCookies :: forall site. (Cookies -> Cookies) -> YesodExample site ()
testModifyCookies Cookies -> Cookies
f = do
(YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ())
-> (YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ \YesodExampleData site
yed -> YesodExampleData site
yed { yedCookies = f (yedCookies yed) }
testClearCookies :: YesodExample site ()
testClearCookies :: forall site. YesodExample site ()
testClearCookies = do
(YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ())
-> (YesodExampleData site -> YesodExampleData site)
-> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ \YesodExampleData site
yed -> YesodExampleData site
yed { yedCookies = M.empty }
withResponse' :: HasCallStack
=> (state -> Maybe SResponse)
-> [T.Text]
-> (SResponse -> SIO state a)
-> SIO state a
withResponse' :: forall state a.
HasCallStack =>
(state -> Maybe SResponse)
-> [Text] -> (SResponse -> SIO state a) -> SIO state a
withResponse' state -> Maybe SResponse
getter [Text]
errTrace SResponse -> SIO state a
f = SIO state a
-> (SResponse -> SIO state a) -> Maybe SResponse -> SIO state a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe SIO state a
err SResponse -> SIO state a
f (Maybe SResponse -> SIO state a)
-> (state -> Maybe SResponse) -> state -> SIO state a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. state -> Maybe SResponse
getter (state -> SIO state a) -> SIO state state -> SIO state a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< SIO state state
forall s. SIO s s
getSIO
where err :: SIO state a
err = Text -> SIO state a
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure Text
msg
msg :: Text
msg = if [Text] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Text]
errTrace
then Text
"There was no response, you should make a request."
else
Text
"There was no response, you should make a request. A response was needed because: \n - "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> [Text] -> Text
T.intercalate Text
"\n - " [Text]
errTrace
withResponse :: HasCallStack => (SResponse -> YesodExample site a) -> YesodExample site a
withResponse :: forall site a.
HasCallStack =>
(SResponse -> YesodExample site a) -> YesodExample site a
withResponse = (YesodExampleData site -> Maybe SResponse)
-> [Text]
-> (SResponse -> SIO (YesodExampleData site) a)
-> SIO (YesodExampleData site) a
forall state a.
HasCallStack =>
(state -> Maybe SResponse)
-> [Text] -> (SResponse -> SIO state a) -> SIO state a
withResponse' YesodExampleData site -> Maybe SResponse
forall site. YesodExampleData site -> Maybe SResponse
yedResponse []
parseHTML :: HtmlLBS -> Cursor
parseHTML :: HtmlLBS -> Cursor
parseHTML HtmlLBS
html = Document -> Cursor
fromDocument (Document -> Cursor) -> Document -> Cursor
forall a b. (a -> b) -> a -> b
$ HtmlLBS -> Document
HD.parseLBS HtmlLBS
html
htmlQuery' :: HasCallStack
=> (state -> Maybe SResponse)
-> [T.Text]
-> Query
-> SIO state [HtmlLBS]
htmlQuery' :: forall state.
HasCallStack =>
(state -> Maybe SResponse) -> [Text] -> Text -> SIO state [HtmlLBS]
htmlQuery' state -> Maybe SResponse
getter [Text]
errTrace Text
query = (state -> Maybe SResponse)
-> [Text]
-> (SResponse -> SIO state [HtmlLBS])
-> SIO state [HtmlLBS]
forall state a.
HasCallStack =>
(state -> Maybe SResponse)
-> [Text] -> (SResponse -> SIO state a) -> SIO state a
withResponse' state -> Maybe SResponse
getter (Text
"Tried to invoke htmlQuery' in order to read HTML of a previous response." Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: [Text]
errTrace) ((SResponse -> SIO state [HtmlLBS]) -> SIO state [HtmlLBS])
-> (SResponse -> SIO state [HtmlLBS]) -> SIO state [HtmlLBS]
forall a b. (a -> b) -> a -> b
$ \ SResponse
res ->
case HtmlLBS -> Text -> Either String [String]
findBySelector (SResponse -> HtmlLBS
simpleBody SResponse
res) Text
query of
Left String
err -> Text -> SIO state [HtmlLBS]
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO state [HtmlLBS]) -> Text -> SIO state [HtmlLBS]
forall a b. (a -> b) -> a -> b
$ Text
query Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" did not parse: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack (String -> String
forall a. Show a => a -> String
show String
err)
Right [String]
matches -> [HtmlLBS] -> SIO state [HtmlLBS]
forall a. a -> SIO state a
forall (m :: * -> *) a. Monad m => a -> m a
return ([HtmlLBS] -> SIO state [HtmlLBS])
-> [HtmlLBS] -> SIO state [HtmlLBS]
forall a b. (a -> b) -> a -> b
$ (String -> HtmlLBS) -> [String] -> [HtmlLBS]
forall a b. (a -> b) -> [a] -> [b]
map (Text -> HtmlLBS
encodeUtf8 (Text -> HtmlLBS) -> (String -> Text) -> String -> HtmlLBS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
TL.pack) [String]
matches
htmlQuery :: HasCallStack => Query -> YesodExample site [HtmlLBS]
htmlQuery :: forall site. HasCallStack => Text -> YesodExample site [HtmlLBS]
htmlQuery = (YesodExampleData site -> Maybe SResponse)
-> [Text] -> Text -> SIO (YesodExampleData site) [HtmlLBS]
forall state.
HasCallStack =>
(state -> Maybe SResponse) -> [Text] -> Text -> SIO state [HtmlLBS]
htmlQuery' YesodExampleData site -> Maybe SResponse
forall site. YesodExampleData site -> Maybe SResponse
yedResponse []
assertEq :: (HasCallStack, Eq a, Show a)
=> String
-> a
-> a
-> YesodExample site ()
assertEq :: forall a site.
(HasCallStack, Eq a, Show a) =>
String -> a -> a -> YesodExample site ()
assertEq String
m a
a a
b =
IO () -> SIO (YesodExampleData site) ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> SIO (YesodExampleData site) ())
-> IO () -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ String -> a -> a -> IO ()
forall a. (HasCallStack, Eq a, Show a) => String -> a -> a -> IO ()
HUnit.assertEqual String
msg a
a a
b
where msg :: String
msg = String
"Assertion: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
m String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\n"
assertNotEq :: (HasCallStack, Eq a, Show a) => String -> a -> a -> YesodExample site ()
assertNotEq :: forall a site.
(HasCallStack, Eq a, Show a) =>
String -> a -> a -> YesodExample site ()
assertNotEq String
m a
a a
b =
IO () -> SIO (YesodExampleData site) ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> SIO (YesodExampleData site) ())
-> IO () -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> IO ()
String -> Bool -> IO ()
HUnit.assertBool String
msg (a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/= a
b)
where msg :: String
msg = String
"Assertion: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
m String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++
String
"Both arguments: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
ppShow a
a String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\n"
{-# DEPRECATED assertEqual "Use assertEq instead" #-}
assertEqual :: (HasCallStack, Eq a) => String -> a -> a -> YesodExample site ()
assertEqual :: forall a site.
(HasCallStack, Eq a) =>
String -> a -> a -> YesodExample site ()
assertEqual = String -> a -> a -> YesodExample site ()
forall a site.
(HasCallStack, Eq a) =>
String -> a -> a -> YesodExample site ()
assertEqualNoShow
assertEqualNoShow :: (HasCallStack, Eq a) => String -> a -> a -> YesodExample site ()
assertEqualNoShow :: forall a site.
(HasCallStack, Eq a) =>
String -> a -> a -> YesodExample site ()
assertEqualNoShow String
msg a
a a
b = IO () -> SIO (YesodExampleData site) ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> SIO (YesodExampleData site) ())
-> IO () -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> IO ()
String -> Bool -> IO ()
HUnit.assertBool String
msg (a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
b)
statusIs :: HasCallStack => Int -> YesodExample site ()
statusIs :: forall site. HasCallStack => Int -> YesodExample site ()
statusIs Int
number = do
(SResponse -> YesodExample site ()) -> YesodExample site ()
forall site a.
HasCallStack =>
(SResponse -> YesodExample site a) -> YesodExample site a
withResponse ((SResponse -> YesodExample site ()) -> YesodExample site ())
-> (SResponse -> YesodExample site ()) -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ \(SResponse Status
status RequestHeaders
headers HtmlLBS
body) -> do
let mContentType :: Maybe ByteString
mContentType = HeaderName -> RequestHeaders -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup HeaderName
hContentType RequestHeaders
headers
isUTF8ContentType :: Bool
isUTF8ContentType = Bool -> (ByteString -> Bool) -> Maybe ByteString -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False ByteString -> Bool
contentTypeHeaderIsUtf8 Maybe ByteString
mContentType
IO () -> YesodExample site ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> YesodExample site ()) -> IO () -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ (String -> Bool -> IO ()) -> Bool -> String -> IO ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip HasCallStack => String -> Bool -> IO ()
String -> Bool -> IO ()
HUnit.assertBool (Status -> Int
H.statusCode Status
status Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
number) (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
[ String
"Expected status was ", Int -> String
forall a. Show a => a -> String
show Int
number
, String
" but received status was ", Int -> String
forall a. Show a => a -> String
show (Int -> String) -> Int -> String
forall a b. (a -> b) -> a -> b
$ Status -> Int
H.statusCode Status
status
, if Bool
isUTF8ContentType
then String
". For debugging, the body was: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> (Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ HtmlLBS -> Text
getBodyTextPreview HtmlLBS
body)
else String
""
]
assertHeader :: HasCallStack => CI BS8.ByteString -> BS8.ByteString -> YesodExample site ()
HeaderName
header ByteString
value = (SResponse -> YesodExample site ()) -> YesodExample site ()
forall site a.
HasCallStack =>
(SResponse -> YesodExample site a) -> YesodExample site a
withResponse ((SResponse -> YesodExample site ()) -> YesodExample site ())
-> (SResponse -> YesodExample site ()) -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ \ SResponse { simpleHeaders :: SResponse -> RequestHeaders
simpleHeaders = RequestHeaders
h } ->
case HeaderName -> RequestHeaders -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup HeaderName
header RequestHeaders
h of
Maybe ByteString
Nothing -> Text -> YesodExample site ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> YesodExample site ()) -> Text -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
[ String
"Expected header "
, HeaderName -> String
forall a. Show a => a -> String
show HeaderName
header
, String
" to be "
, ByteString -> String
forall a. Show a => a -> String
show ByteString
value
, String
", but it was not present"
]
Just ByteString
value' -> IO () -> YesodExample site ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> YesodExample site ()) -> IO () -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ (String -> Bool -> IO ()) -> Bool -> String -> IO ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip HasCallStack => String -> Bool -> IO ()
String -> Bool -> IO ()
HUnit.assertBool (ByteString
value ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
value') (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
[ String
"Expected header "
, HeaderName -> String
forall a. Show a => a -> String
show HeaderName
header
, String
" to be "
, ByteString -> String
forall a. Show a => a -> String
show ByteString
value
, String
", but received "
, ByteString -> String
forall a. Show a => a -> String
show ByteString
value'
]
assertNoHeader :: HasCallStack => CI BS8.ByteString -> YesodExample site ()
HeaderName
header = (SResponse -> YesodExample site ()) -> YesodExample site ()
forall site a.
HasCallStack =>
(SResponse -> YesodExample site a) -> YesodExample site a
withResponse ((SResponse -> YesodExample site ()) -> YesodExample site ())
-> (SResponse -> YesodExample site ()) -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ \ SResponse { simpleHeaders :: SResponse -> RequestHeaders
simpleHeaders = RequestHeaders
h } ->
case HeaderName -> RequestHeaders -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup HeaderName
header RequestHeaders
h of
Maybe ByteString
Nothing -> () -> YesodExample site ()
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just ByteString
s -> Text -> YesodExample site ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> YesodExample site ()) -> Text -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
[ String
"Unexpected header "
, HeaderName -> String
forall a. Show a => a -> String
show HeaderName
header
, String
" containing "
, ByteString -> String
forall a. Show a => a -> String
show ByteString
s
]
bodyEquals :: HasCallStack => String -> YesodExample site ()
bodyEquals :: forall site. HasCallStack => String -> YesodExample site ()
bodyEquals String
text = (SResponse -> YesodExample site ()) -> YesodExample site ()
forall site a.
HasCallStack =>
(SResponse -> YesodExample site a) -> YesodExample site a
withResponse ((SResponse -> YesodExample site ()) -> YesodExample site ())
-> (SResponse -> YesodExample site ()) -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ \ SResponse
res -> do
let actual :: HtmlLBS
actual = SResponse -> HtmlLBS
simpleBody SResponse
res
msg :: String
msg = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ String
"Expected body to equal:\n\t"
, String
text String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\n"
, String
"Actual is:\n\t"
, Text -> String
TL.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ OnDecodeError -> HtmlLBS -> Text
decodeUtf8With OnDecodeError
TErr.lenientDecode HtmlLBS
actual
]
IO () -> YesodExample site ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> YesodExample site ()) -> IO () -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> IO ()
String -> Bool -> IO ()
HUnit.assertBool String
msg (Bool -> IO ()) -> Bool -> IO ()
forall a b. (a -> b) -> a -> b
$ HtmlLBS
actual HtmlLBS -> HtmlLBS -> Bool
forall a. Eq a => a -> a -> Bool
== Text -> HtmlLBS
encodeUtf8 (String -> Text
TL.pack String
text)
bodyContains :: HasCallStack => String -> YesodExample site ()
bodyContains :: forall site. HasCallStack => String -> YesodExample site ()
bodyContains String
text = (SResponse -> YesodExample site ()) -> YesodExample site ()
forall site a.
HasCallStack =>
(SResponse -> YesodExample site a) -> YesodExample site a
withResponse ((SResponse -> YesodExample site ()) -> YesodExample site ())
-> (SResponse -> YesodExample site ()) -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ \ SResponse
res ->
IO () -> YesodExample site ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> YesodExample site ()) -> IO () -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> IO ()
String -> Bool -> IO ()
HUnit.assertBool (String
"Expected body to contain " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
text) (Bool -> IO ()) -> Bool -> IO ()
forall a b. (a -> b) -> a -> b
$
(SResponse -> HtmlLBS
simpleBody SResponse
res) HtmlLBS -> String -> Bool
`contains` String
text
bodyNotContains :: HasCallStack => String -> YesodExample site ()
bodyNotContains :: forall site. HasCallStack => String -> YesodExample site ()
bodyNotContains String
text = (SResponse -> YesodExample site ()) -> YesodExample site ()
forall site a.
HasCallStack =>
(SResponse -> YesodExample site a) -> YesodExample site a
withResponse ((SResponse -> YesodExample site ()) -> YesodExample site ())
-> (SResponse -> YesodExample site ()) -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ \ SResponse
res ->
IO () -> YesodExample site ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> YesodExample site ()) -> IO () -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> IO ()
String -> Bool -> IO ()
HUnit.assertBool (String
"Expected body not to contain " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
text) (Bool -> IO ()) -> Bool -> IO ()
forall a b. (a -> b) -> a -> b
$
Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ HtmlLBS -> String -> Bool
contains (SResponse -> HtmlLBS
simpleBody SResponse
res) String
text
contains :: BSL8.ByteString -> String -> Bool
contains :: HtmlLBS -> String -> Bool
contains HtmlLBS
a String
b = String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
DL.isInfixOf String
b (Text -> String
TL.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ HtmlLBS -> Text
decodeUtf8 HtmlLBS
a)
htmlAllContain :: HasCallStack => Query -> String -> YesodExample site ()
htmlAllContain :: forall site. HasCallStack => Text -> String -> YesodExample site ()
htmlAllContain Text
query String
search = do
matches <- Text -> YesodExample site [HtmlLBS]
forall site. HasCallStack => Text -> YesodExample site [HtmlLBS]
htmlQuery Text
query
case matches of
[] -> Text -> SIO (YesodExampleData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (YesodExampleData site) ())
-> Text -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ Text
"Nothing matched css query: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
query
[HtmlLBS]
_ -> IO () -> SIO (YesodExampleData site) ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> SIO (YesodExampleData site) ())
-> IO () -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> IO ()
String -> Bool -> IO ()
HUnit.assertBool (String
"Not all "String -> String -> String
forall a. [a] -> [a] -> [a]
++Text -> String
T.unpack Text
queryString -> String -> String
forall a. [a] -> [a] -> [a]
++String
" contain "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
search String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" matches: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ [HtmlLBS] -> String
forall a. Show a => a -> String
show [HtmlLBS]
matches) (Bool -> IO ()) -> Bool -> IO ()
forall a b. (a -> b) -> a -> b
$
(String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
DL.all (String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
DL.isInfixOf (String -> String
escape String
search)) ((HtmlLBS -> String) -> [HtmlLBS] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Text -> String
TL.unpack (Text -> String) -> (HtmlLBS -> Text) -> HtmlLBS -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HtmlLBS -> Text
decodeUtf8) [HtmlLBS]
matches)
escape :: String -> String
escape :: String -> String
escape = Markup -> String
Blaze.renderMarkup (Markup -> String) -> (String -> Markup) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Markup
Blaze.string
htmlAnyContain :: HasCallStack => Query -> String -> YesodExample site ()
htmlAnyContain :: forall site. HasCallStack => Text -> String -> YesodExample site ()
htmlAnyContain Text
query String
search = do
matches <- Text -> YesodExample site [HtmlLBS]
forall site. HasCallStack => Text -> YesodExample site [HtmlLBS]
htmlQuery Text
query
case matches of
[] -> Text -> SIO (YesodExampleData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (YesodExampleData site) ())
-> Text -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ Text
"Nothing matched css query: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
query
[HtmlLBS]
_ -> IO () -> SIO (YesodExampleData site) ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> SIO (YesodExampleData site) ())
-> IO () -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> IO ()
String -> Bool -> IO ()
HUnit.assertBool (String
"None of "String -> String -> String
forall a. [a] -> [a] -> [a]
++Text -> String
T.unpack Text
queryString -> String -> String
forall a. [a] -> [a] -> [a]
++String
" contain "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
search String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" matches: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ [HtmlLBS] -> String
forall a. Show a => a -> String
show [HtmlLBS]
matches) (Bool -> IO ()) -> Bool -> IO ()
forall a b. (a -> b) -> a -> b
$
(String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
DL.any (String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
DL.isInfixOf (String -> String
escape String
search)) ((HtmlLBS -> String) -> [HtmlLBS] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Text -> String
TL.unpack (Text -> String) -> (HtmlLBS -> Text) -> HtmlLBS -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HtmlLBS -> Text
decodeUtf8) [HtmlLBS]
matches)
htmlNoneContain :: HasCallStack => Query -> String -> YesodExample site ()
htmlNoneContain :: forall site. HasCallStack => Text -> String -> YesodExample site ()
htmlNoneContain Text
query String
search = do
matches <- Text -> YesodExample site [HtmlLBS]
forall site. HasCallStack => Text -> YesodExample site [HtmlLBS]
htmlQuery Text
query
case DL.filter (DL.isInfixOf (escape search)) (map (TL.unpack . decodeUtf8) matches) of
[] -> () -> SIO (YesodExampleData site) ()
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
[String]
found -> Text -> SIO (YesodExampleData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (YesodExampleData site) ())
-> Text -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ Text
"Found " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack (Int -> String
forall a. Show a => a -> String
show (Int -> String) -> Int -> String
forall a b. (a -> b) -> a -> b
$ [String] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
found) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
Text
" instances of " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack String
search Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" in " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
query Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" elements"
htmlCount :: HasCallStack => Query -> Int -> YesodExample site ()
htmlCount :: forall site. HasCallStack => Text -> Int -> YesodExample site ()
htmlCount Text
query Int
count = do
matches <- ([HtmlLBS] -> Int)
-> SIO (YesodExampleData site) [HtmlLBS]
-> SIO (YesodExampleData site) Int
forall a b.
(a -> b)
-> SIO (YesodExampleData site) a -> SIO (YesodExampleData site) b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [HtmlLBS] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
DL.length (SIO (YesodExampleData site) [HtmlLBS]
-> SIO (YesodExampleData site) Int)
-> SIO (YesodExampleData site) [HtmlLBS]
-> SIO (YesodExampleData site) Int
forall a b. (a -> b) -> a -> b
$ Text -> SIO (YesodExampleData site) [HtmlLBS]
forall site. HasCallStack => Text -> YesodExample site [HtmlLBS]
htmlQuery Text
query
liftIO $ flip HUnit.assertBool (matches == count)
("Expected "++(show count)++" elements to match "++T.unpack query++", found "++(show matches))
requireJSONResponse :: (HasCallStack, FromJSON a) => YesodExample site a
requireJSONResponse :: forall a site. (HasCallStack, FromJSON a) => YesodExample site a
requireJSONResponse = do
(SResponse -> YesodExample site a) -> YesodExample site a
forall site a.
HasCallStack =>
(SResponse -> YesodExample site a) -> YesodExample site a
withResponse ((SResponse -> YesodExample site a) -> YesodExample site a)
-> (SResponse -> YesodExample site a) -> YesodExample site a
forall a b. (a -> b) -> a -> b
$ \(SResponse Status
_status RequestHeaders
headers HtmlLBS
body) -> do
let mContentType :: Maybe ByteString
mContentType = HeaderName -> RequestHeaders -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup HeaderName
hContentType RequestHeaders
headers
isJSONContentType :: Bool
isJSONContentType = Bool -> (ByteString -> Bool) -> Maybe ByteString -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False ByteString -> Bool
contentTypeHeaderIsJson Maybe ByteString
mContentType
Bool
-> SIO (YesodExampleData site) () -> SIO (YesodExampleData site) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless
Bool
isJSONContentType
(Text -> SIO (YesodExampleData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (YesodExampleData site) ())
-> Text -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Expected `Content-Type: application/json` in the headers, got: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ RequestHeaders -> String
forall a. Show a => a -> String
show RequestHeaders
headers)
case HtmlLBS -> Either String a
forall a. FromJSON a => HtmlLBS -> Either String a
eitherDecode' HtmlLBS
body of
Left String
err -> Text -> YesodExample site a
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> YesodExample site a) -> Text -> YesodExample site a
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat [Text
"Failed to parse JSON response; error: ", String -> Text
T.pack String
err, Text
"JSON: ", HtmlLBS -> Text
getBodyTextPreview HtmlLBS
body]
Right a
v -> a -> YesodExample site a
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return a
v
printBody :: YesodExample site ()
printBody :: forall site. YesodExample site ()
printBody = (SResponse -> YesodExample site ()) -> YesodExample site ()
forall site a.
HasCallStack =>
(SResponse -> YesodExample site a) -> YesodExample site a
withResponse ((SResponse -> YesodExample site ()) -> YesodExample site ())
-> (SResponse -> YesodExample site ()) -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ \ SResponse { simpleBody :: SResponse -> HtmlLBS
simpleBody = HtmlLBS
b } ->
IO () -> YesodExample site ()
forall a. IO a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> YesodExample site ()) -> IO () -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ Handle -> HtmlLBS -> IO ()
BSL8.hPutStrLn Handle
stderr HtmlLBS
b
printMatches :: HasCallStack => Query -> YesodExample site ()
printMatches :: forall site. HasCallStack => Text -> YesodExample site ()
printMatches Text
query = do
matches <- Text -> YesodExample site [HtmlLBS]
forall site. HasCallStack => Text -> YesodExample site [HtmlLBS]
htmlQuery Text
query
liftIO $ hPutStrLn stderr $ show matches
addPostParam :: T.Text -> T.Text -> RequestBuilder site ()
addPostParam :: forall site. Text -> Text -> RequestBuilder site ()
addPostParam Text
name Text
value =
(RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ())
-> (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ \RequestBuilderData site
rbd -> RequestBuilderData site
rbd { rbdPostData = (addPostData (rbdPostData rbd)) }
where addPostData :: RBDPostData -> RBDPostData
addPostData (BinaryPostData HtmlLBS
_) = String -> RBDPostData
forall a. HasCallStack => String -> a
error String
"Trying to add post param to binary content."
addPostData (MultipleItemsPostData [RequestPart]
posts) =
[RequestPart] -> RBDPostData
MultipleItemsPostData ([RequestPart] -> RBDPostData) -> [RequestPart] -> RBDPostData
forall a b. (a -> b) -> a -> b
$ Text -> Text -> RequestPart
ReqKvPart Text
name Text
value RequestPart -> [RequestPart] -> [RequestPart]
forall a. a -> [a] -> [a]
: [RequestPart]
posts
addGetParam :: T.Text -> T.Text -> RequestBuilder site ()
addGetParam :: forall site. Text -> Text -> RequestBuilder site ()
addGetParam Text
name Text
value = (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ())
-> (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ \RequestBuilderData site
rbd -> RequestBuilderData site
rbd
{ rbdGets = (TE.encodeUtf8 name, Just $ TE.encodeUtf8 value)
: rbdGets rbd
}
addBareGetParam :: T.Text -> RequestBuilder site ()
addBareGetParam :: forall site. Text -> RequestBuilder site ()
addBareGetParam Text
name = (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ())
-> (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ \RequestBuilderData site
rbd ->
RequestBuilderData site
rbd {rbdGets = (TE.encodeUtf8 name, Nothing) : rbdGets rbd}
addFile :: T.Text
-> FilePath
-> T.Text
-> RequestBuilder site ()
addFile :: forall site. Text -> String -> Text -> RequestBuilder site ()
addFile Text
name String
path Text
mimetype = do
contents <- IO HtmlLBS -> SIO (RequestBuilderData site) HtmlLBS
forall a. IO a -> SIO (RequestBuilderData site) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO HtmlLBS -> SIO (RequestBuilderData site) HtmlLBS)
-> IO HtmlLBS -> SIO (RequestBuilderData site) HtmlLBS
forall a b. (a -> b) -> a -> b
$ String -> IO HtmlLBS
BSL8.readFile String
path
modifySIO $ \RequestBuilderData site
rbd -> RequestBuilderData site
rbd { rbdPostData = (addPostData (rbdPostData rbd) contents) }
where addPostData :: RBDPostData -> HtmlLBS -> RBDPostData
addPostData (BinaryPostData HtmlLBS
_) HtmlLBS
_ = String -> RBDPostData
forall a. HasCallStack => String -> a
error String
"Trying to add file after setting binary content."
addPostData (MultipleItemsPostData [RequestPart]
posts) HtmlLBS
contents =
[RequestPart] -> RBDPostData
MultipleItemsPostData ([RequestPart] -> RBDPostData) -> [RequestPart] -> RBDPostData
forall a b. (a -> b) -> a -> b
$ Text -> String -> HtmlLBS -> Text -> RequestPart
ReqFilePart Text
name String
path HtmlLBS
contents Text
mimetype RequestPart -> [RequestPart] -> [RequestPart]
forall a. a -> [a] -> [a]
: [RequestPart]
posts
genericNameFromLabel :: HasCallStack => (T.Text -> T.Text -> Bool) -> T.Text -> RequestBuilder site T.Text
genericNameFromLabel :: forall site.
HasCallStack =>
(Text -> Text -> Bool) -> Text -> RequestBuilder site Text
genericNameFromLabel Text -> Text -> Bool
match Text
label = do
mres <- (RequestBuilderData site -> Maybe SResponse)
-> SIO (RequestBuilderData site) (RequestBuilderData site)
-> SIO (RequestBuilderData site) (Maybe SResponse)
forall a b.
(a -> b)
-> SIO (RequestBuilderData site) a
-> SIO (RequestBuilderData site) b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap RequestBuilderData site -> Maybe SResponse
forall site. RequestBuilderData site -> Maybe SResponse
rbdResponse SIO (RequestBuilderData site) (RequestBuilderData site)
forall s. SIO s s
getSIO
res <-
case mres of
Maybe SResponse
Nothing -> Text -> SIO (RequestBuilderData site) SResponse
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure Text
"genericNameFromLabel: No response available"
Just SResponse
res -> SResponse -> SIO (RequestBuilderData site) SResponse
forall a. a -> SIO (RequestBuilderData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return SResponse
res
let body = SResponse -> HtmlLBS
simpleBody SResponse
res
case genericNameFromHTML match label body of
Left Text
e -> Text -> SIO (RequestBuilderData site) Text
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure Text
e
Right Text
x -> Text -> SIO (RequestBuilderData site) Text
forall a. a -> SIO (RequestBuilderData site) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
x
genericNameFromSelectorLabel :: HasCallStack => (T.Text -> T.Text -> Bool) -> T.Text -> T.Text -> RequestBuilder site T.Text
genericNameFromSelectorLabel :: forall site.
HasCallStack =>
(Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site Text
genericNameFromSelectorLabel Text -> Text -> Bool
match Text
selector Text
label = do
body <- String -> RequestBuilder site HtmlLBS
forall site. String -> RequestBuilder site HtmlLBS
htmlBody String
"genericNameSelectorFromLabel"
html <-
case findBySelector body selector of
Left String
parseError -> Text -> RequestBuilder site HtmlLBS
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> RequestBuilder site HtmlLBS)
-> Text -> RequestBuilder site HtmlLBS
forall a b. (a -> b) -> a -> b
$ Text
"genericNameFromSelectorLabel: Parse error" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack String
parseError
Right [] -> Text -> RequestBuilder site HtmlLBS
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> RequestBuilder site HtmlLBS)
-> Text -> RequestBuilder site HtmlLBS
forall a b. (a -> b) -> a -> b
$ Text
"genericNameFromSelectorLabel: No fragments match selector " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
selector
Right [String
matchingFragment] -> HtmlLBS -> RequestBuilder site HtmlLBS
forall a. a -> SIO (RequestBuilderData site) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (HtmlLBS -> RequestBuilder site HtmlLBS)
-> HtmlLBS -> RequestBuilder site HtmlLBS
forall a b. (a -> b) -> a -> b
$ String -> HtmlLBS
BSL8.pack String
matchingFragment
Right [String]
_matchingFragments -> Text -> RequestBuilder site HtmlLBS
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> RequestBuilder site HtmlLBS)
-> Text -> RequestBuilder site HtmlLBS
forall a b. (a -> b) -> a -> b
$ Text
"genericNameFromSelectorLabel: Multiple fragments match selector " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
selector
case genericNameFromHTML match label html of
Left Text
e -> Text -> SIO (RequestBuilderData site) Text
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure Text
e
Right Text
x -> Text -> SIO (RequestBuilderData site) Text
forall a. a -> SIO (RequestBuilderData site) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
x
genericNameFromHTML :: (T.Text -> T.Text -> Bool) -> T.Text -> HtmlLBS -> Either T.Text T.Text
genericNameFromHTML :: (Text -> Text -> Bool) -> Text -> HtmlLBS -> Either Text Text
genericNameFromHTML Text -> Text -> Bool
match Text
label HtmlLBS
html =
let
parsedHTML :: Cursor
parsedHTML = HtmlLBS -> Cursor
parseHTML HtmlLBS
html
mlabel :: [Cursor]
mlabel = Cursor
parsedHTML
Cursor -> (Cursor -> [Cursor]) -> [Cursor]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Name -> Cursor -> [Cursor]
C.element Name
"label"
(Cursor -> [Cursor]) -> (Cursor -> [Cursor]) -> Cursor -> [Cursor]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Text -> Cursor -> [Cursor]
isContentMatch Text
label
mfor :: [Text]
mfor = [Cursor]
mlabel [Cursor] -> (Cursor -> [Text]) -> [Text]
forall a b. [a] -> (a -> [b]) -> [b]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Name -> Cursor -> [Text]
attribute Name
"for"
isContentMatch :: Text -> Cursor -> [Cursor]
isContentMatch Text
x Cursor
c
| Text
x Text -> Text -> Bool
`match` [Text] -> Text
T.concat (Cursor
c Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Cursor -> [Text]
content) = [Cursor
c]
| Bool
otherwise = []
in case [Text]
mfor of
Text
for:[] -> do
let mname :: [Text]
mname = Cursor
parsedHTML
Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Name -> Text -> Cursor -> [Cursor]
attributeIs Name
"id" Text
for
(Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Name -> Cursor -> [Text]
attribute Name
"name"
case [Text]
mname of
Text
"":[Text]
_ -> Text -> Either Text Text
forall a b. a -> Either a b
Left (Text -> Either Text Text) -> Text -> Either Text Text
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat
[ Text
"Label "
, Text
label
, Text
" resolved to id "
, Text
for
, Text
" which was not found. "
]
Text
name:[Text]
_ -> Text -> Either Text Text
forall a b. b -> Either a b
Right Text
name
[] -> Text -> Either Text Text
forall a b. a -> Either a b
Left (Text -> Either Text Text) -> Text -> Either Text Text
forall a b. (a -> b) -> a -> b
$ Text
"No input with id " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
for
[] ->
case (Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
filter (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"") ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ [Cursor]
mlabel [Cursor] -> (Cursor -> [Text]) -> [Text]
forall a b. [a] -> (a -> [b]) -> [b]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Cursor -> [Cursor]
forall node. Cursor node -> [Cursor node]
child (Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Name -> Cursor -> [Cursor]
C.element Name
"input" (Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Name -> Cursor -> [Text]
attribute Name
"name") of
[] -> Text -> Either Text Text
forall a b. a -> Either a b
Left (Text -> Either Text Text) -> Text -> Either Text Text
forall a b. (a -> b) -> a -> b
$ Text
"No label contained: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
label
Text
name:[Text]
_ -> Text -> Either Text Text
forall a b. b -> Either a b
Right Text
name
[Text]
_ -> Text -> Either Text Text
forall a b. a -> Either a b
Left (Text -> Either Text Text) -> Text -> Either Text Text
forall a b. (a -> b) -> a -> b
$ Text
"More than one label contained " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
label
byLabelWithMatch :: (T.Text -> T.Text -> Bool)
-> T.Text
-> T.Text
-> RequestBuilder site ()
byLabelWithMatch :: forall site.
(Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
byLabelWithMatch Text -> Text -> Bool
match Text
label Text
value = do
name <- (Text -> Text -> Bool) -> Text -> RequestBuilder site Text
forall site.
HasCallStack =>
(Text -> Text -> Bool) -> Text -> RequestBuilder site Text
genericNameFromLabel Text -> Text -> Bool
match Text
label
addPostParam name value
bySelectorLabelWithMatch :: (T.Text -> T.Text -> Bool)
-> T.Text
-> T.Text
-> T.Text
-> RequestBuilder site ()
bySelectorLabelWithMatch :: forall site.
(Text -> Text -> Bool)
-> Text -> Text -> Text -> RequestBuilder site ()
bySelectorLabelWithMatch Text -> Text -> Bool
match Text
selector Text
label Text
value = do
name <- (Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site Text
forall site.
HasCallStack =>
(Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site Text
genericNameFromSelectorLabel Text -> Text -> Bool
match Text
selector Text
label
addPostParam name value
byLabel :: T.Text
-> T.Text
-> RequestBuilder site ()
byLabel :: forall site. Text -> Text -> RequestBuilder site ()
byLabel = (Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
byLabelWithMatch Text -> Text -> Bool
T.isInfixOf
byLabelExact :: T.Text
-> T.Text
-> RequestBuilder site ()
byLabelExact :: forall site. Text -> Text -> RequestBuilder site ()
byLabelExact = (Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
byLabelWithMatch Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
(==)
byLabelContain :: T.Text
-> T.Text
-> RequestBuilder site ()
byLabelContain :: forall site. Text -> Text -> RequestBuilder site ()
byLabelContain = (Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
byLabelWithMatch Text -> Text -> Bool
T.isInfixOf
byLabelPrefix :: T.Text
-> T.Text
-> RequestBuilder site ()
byLabelPrefix :: forall site. Text -> Text -> RequestBuilder site ()
byLabelPrefix = (Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
byLabelWithMatch Text -> Text -> Bool
T.isPrefixOf
byLabelSuffix :: T.Text
-> T.Text
-> RequestBuilder site ()
byLabelSuffix :: forall site. Text -> Text -> RequestBuilder site ()
byLabelSuffix = (Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool) -> Text -> Text -> RequestBuilder site ()
byLabelWithMatch Text -> Text -> Bool
T.isSuffixOf
bySelectorLabelContain :: T.Text
-> T.Text
-> T.Text
-> RequestBuilder site ()
bySelectorLabelContain :: forall site. Text -> Text -> Text -> RequestBuilder site ()
bySelectorLabelContain = (Text -> Text -> Bool)
-> Text -> Text -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool)
-> Text -> Text -> Text -> RequestBuilder site ()
bySelectorLabelWithMatch Text -> Text -> Bool
T.isInfixOf
fileByLabelWithMatch :: (T.Text -> T.Text -> Bool)
-> T.Text
-> FilePath
-> T.Text
-> RequestBuilder site ()
fileByLabelWithMatch :: forall site.
(Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
fileByLabelWithMatch Text -> Text -> Bool
match Text
label String
path Text
mime = do
name <- (Text -> Text -> Bool) -> Text -> RequestBuilder site Text
forall site.
HasCallStack =>
(Text -> Text -> Bool) -> Text -> RequestBuilder site Text
genericNameFromLabel Text -> Text -> Bool
match Text
label
addFile name path mime
fileByLabel :: T.Text
-> FilePath
-> T.Text
-> RequestBuilder site ()
fileByLabel :: forall site. Text -> String -> Text -> RequestBuilder site ()
fileByLabel = (Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
fileByLabelWithMatch Text -> Text -> Bool
T.isInfixOf
fileByLabelExact :: T.Text
-> FilePath
-> T.Text
-> RequestBuilder site ()
fileByLabelExact :: forall site. Text -> String -> Text -> RequestBuilder site ()
fileByLabelExact = (Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
fileByLabelWithMatch Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
(==)
fileByLabelContain :: T.Text
-> FilePath
-> T.Text
-> RequestBuilder site ()
fileByLabelContain :: forall site. Text -> String -> Text -> RequestBuilder site ()
fileByLabelContain = (Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
fileByLabelWithMatch Text -> Text -> Bool
T.isInfixOf
fileByLabelPrefix :: T.Text
-> FilePath
-> T.Text
-> RequestBuilder site ()
fileByLabelPrefix :: forall site. Text -> String -> Text -> RequestBuilder site ()
fileByLabelPrefix = (Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
fileByLabelWithMatch Text -> Text -> Bool
T.isPrefixOf
fileByLabelSuffix :: T.Text
-> FilePath
-> T.Text
-> RequestBuilder site ()
fileByLabelSuffix :: forall site. Text -> String -> Text -> RequestBuilder site ()
fileByLabelSuffix = (Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
forall site.
(Text -> Text -> Bool)
-> Text -> String -> Text -> RequestBuilder site ()
fileByLabelWithMatch Text -> Text -> Bool
T.isSuffixOf
addToken_ :: HasCallStack => Query -> RequestBuilder site ()
addToken_ :: forall site. HasCallStack => Text -> RequestBuilder site ()
addToken_ Text
scope = do
matches <- (RequestBuilderData site -> Maybe SResponse)
-> [Text] -> Text -> SIO (RequestBuilderData site) [HtmlLBS]
forall state.
HasCallStack =>
(state -> Maybe SResponse) -> [Text] -> Text -> SIO state [HtmlLBS]
htmlQuery' RequestBuilderData site -> Maybe SResponse
forall site. RequestBuilderData site -> Maybe SResponse
rbdResponse [Text
"Tried to get CSRF token with addToken'"] (Text -> SIO (RequestBuilderData site) [HtmlLBS])
-> Text -> SIO (RequestBuilderData site) [HtmlLBS]
forall a b. (a -> b) -> a -> b
$ Text
scope Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" input[name=_token][type=hidden][value]"
case matches of
[] -> Text -> SIO (RequestBuilderData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (RequestBuilderData site) ())
-> Text -> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ Text
"No CSRF token found in the current page"
HtmlLBS
element:[] -> Text -> Text -> SIO (RequestBuilderData site) ()
forall site. Text -> Text -> RequestBuilder site ()
addPostParam Text
"_token" (Text -> SIO (RequestBuilderData site) ())
-> Text -> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. HasCallStack => [a] -> a
head ([Text] -> Text) -> [Text] -> Text
forall a b. (a -> b) -> a -> b
$ Name -> Cursor -> [Text]
attribute Name
"value" (Cursor -> [Text]) -> Cursor -> [Text]
forall a b. (a -> b) -> a -> b
$ HtmlLBS -> Cursor
parseHTML HtmlLBS
element
[HtmlLBS]
_ -> Text -> SIO (RequestBuilderData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (RequestBuilderData site) ())
-> Text -> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ Text
"More than one CSRF token found in the page"
addToken :: HasCallStack => RequestBuilder site ()
addToken :: forall site. HasCallStack => RequestBuilder site ()
addToken = Text -> RequestBuilder site ()
forall site. HasCallStack => Text -> RequestBuilder site ()
addToken_ Text
""
addTokenFromCookie :: HasCallStack => RequestBuilder site ()
addTokenFromCookie :: forall site. HasCallStack => RequestBuilder site ()
addTokenFromCookie = ByteString -> HeaderName -> RequestBuilder site ()
forall site.
HasCallStack =>
ByteString -> HeaderName -> RequestBuilder site ()
addTokenFromCookieNamedToHeaderNamed ByteString
defaultCsrfCookieName HeaderName
defaultCsrfHeaderName
addTokenFromCookieNamedToHeaderNamed :: HasCallStack
=> ByteString
-> CI ByteString
-> RequestBuilder site ()
ByteString
cookieName HeaderName
headerName = do
cookies <- RequestBuilder site Cookies
forall site. HasCallStack => RequestBuilder site Cookies
getRequestCookies
case M.lookup cookieName cookies of
Just SetCookie
csrfCookie -> (HeaderName, ByteString) -> SIO (RequestBuilderData site) ()
forall site. (HeaderName, ByteString) -> RequestBuilder site ()
addRequestHeader (HeaderName
headerName, SetCookie -> ByteString
Cookie.setCookieValue SetCookie
csrfCookie)
Maybe SetCookie
Nothing -> Text -> SIO (RequestBuilderData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (RequestBuilderData site) ())
-> Text -> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat
[ Text
"addTokenFromCookieNamedToHeaderNamed failed to lookup CSRF cookie with name: "
, String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> String
forall a. Show a => a -> String
show ByteString
cookieName
, Text
". Cookies were: "
, String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Cookies -> String
forall a. Show a => a -> String
show Cookies
cookies
]
getRequestCookies :: HasCallStack => RequestBuilder site Cookies
getRequestCookies :: forall site. HasCallStack => RequestBuilder site Cookies
getRequestCookies = do
requestBuilderData <- SIO (RequestBuilderData site) (RequestBuilderData site)
forall s. SIO s s
getSIO
headers <- case simpleHeaders Control.Applicative.<$> rbdResponse requestBuilderData of
Just RequestHeaders
h -> RequestHeaders -> SIO (RequestBuilderData site) RequestHeaders
forall a. a -> SIO (RequestBuilderData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return RequestHeaders
h
Maybe RequestHeaders
Nothing -> Text -> SIO (RequestBuilderData site) RequestHeaders
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure Text
"getRequestCookies: No request has been made yet; the cookies can't be looked up."
return $ M.fromList $ map (\SetCookie
c -> (SetCookie -> ByteString
Cookie.setCookieName SetCookie
c, SetCookie
c)) (parseSetCookies headers)
post :: (Yesod site, RedirectUrl site url)
=> url
-> YesodExample site ()
post :: forall site url.
(Yesod site, RedirectUrl site url) =>
url -> YesodExample site ()
post = ByteString -> url -> YesodExample site ()
forall site url.
(Yesod site, RedirectUrl site url) =>
ByteString -> url -> YesodExample site ()
performMethod ByteString
"POST"
postBody :: (Yesod site, RedirectUrl site url)
=> url
-> BSL8.ByteString
-> YesodExample site ()
postBody :: forall site url.
(Yesod site, RedirectUrl site url) =>
url -> HtmlLBS -> YesodExample site ()
postBody url
url HtmlLBS
body = RequestBuilder site () -> YesodExample site ()
forall site. RequestBuilder site () -> YesodExample site ()
request (RequestBuilder site () -> YesodExample site ())
-> RequestBuilder site () -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ do
ByteString -> RequestBuilder site ()
forall site. ByteString -> RequestBuilder site ()
setMethod ByteString
"POST"
url -> RequestBuilder site ()
forall site url.
(Yesod site, RedirectUrl site url) =>
url -> RequestBuilder site ()
setUrl url
url
HtmlLBS -> RequestBuilder site ()
forall site. HtmlLBS -> RequestBuilder site ()
setRequestBody HtmlLBS
body
get :: (Yesod site, RedirectUrl site url)
=> url
-> YesodExample site ()
get :: forall site url.
(Yesod site, RedirectUrl site url) =>
url -> YesodExample site ()
get = ByteString -> url -> YesodExample site ()
forall site url.
(Yesod site, RedirectUrl site url) =>
ByteString -> url -> YesodExample site ()
performMethod ByteString
"GET"
performMethod :: (Yesod site, RedirectUrl site url)
=> ByteString
-> url
-> YesodExample site ()
performMethod :: forall site url.
(Yesod site, RedirectUrl site url) =>
ByteString -> url -> YesodExample site ()
performMethod ByteString
method url
url = RequestBuilder site () -> YesodExample site ()
forall site. RequestBuilder site () -> YesodExample site ()
request (RequestBuilder site () -> YesodExample site ())
-> RequestBuilder site () -> YesodExample site ()
forall a b. (a -> b) -> a -> b
$ do
ByteString -> RequestBuilder site ()
forall site. ByteString -> RequestBuilder site ()
setMethod ByteString
method
url -> RequestBuilder site ()
forall site url.
(Yesod site, RedirectUrl site url) =>
url -> RequestBuilder site ()
setUrl url
url
followRedirect :: Yesod site
=> YesodExample site (Either T.Text T.Text)
followRedirect :: forall site. Yesod site => YesodExample site (Either Text Text)
followRedirect = do
mr <- YesodExample site (Maybe SResponse)
forall site. YesodExample site (Maybe SResponse)
getResponse
case mr of
Maybe SResponse
Nothing -> Either Text Text -> SIO (YesodExampleData site) (Either Text Text)
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Text Text
-> SIO (YesodExampleData site) (Either Text Text))
-> Either Text Text
-> SIO (YesodExampleData site) (Either Text Text)
forall a b. (a -> b) -> a -> b
$ Text -> Either Text Text
forall a b. a -> Either a b
Left Text
"followRedirect called, but there was no previous response, so no redirect to follow"
Just SResponse
r -> do
if Bool -> Bool
not ((Status -> Int
H.statusCode (Status -> Int) -> Status -> Int
forall a b. (a -> b) -> a -> b
$ SResponse -> Status
simpleStatus SResponse
r) Int -> [Int] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Int
301, Int
302, Int
303, Int
307, Int
308])
then Either Text Text -> SIO (YesodExampleData site) (Either Text Text)
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Text Text
-> SIO (YesodExampleData site) (Either Text Text))
-> Either Text Text
-> SIO (YesodExampleData site) (Either Text Text)
forall a b. (a -> b) -> a -> b
$ Text -> Either Text Text
forall a b. a -> Either a b
Left Text
"followRedirect called, but previous request was not a redirect"
else do
case HeaderName -> RequestHeaders -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup HeaderName
"Location" (SResponse -> RequestHeaders
simpleHeaders SResponse
r) of
Maybe ByteString
Nothing -> Either Text Text -> SIO (YesodExampleData site) (Either Text Text)
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Text Text
-> SIO (YesodExampleData site) (Either Text Text))
-> Either Text Text
-> SIO (YesodExampleData site) (Either Text Text)
forall a b. (a -> b) -> a -> b
$ Text -> Either Text Text
forall a b. a -> Either a b
Left Text
"followRedirect called, but no location header set"
Just ByteString
h -> let url :: Text
url = ByteString -> Text
TE.decodeUtf8 ByteString
h in
Text -> YesodExample site ()
forall site url.
(Yesod site, RedirectUrl site url) =>
url -> YesodExample site ()
get Text
url YesodExample site ()
-> SIO (YesodExampleData site) (Either Text Text)
-> SIO (YesodExampleData site) (Either Text Text)
forall a b.
SIO (YesodExampleData site) a
-> SIO (YesodExampleData site) b -> SIO (YesodExampleData site) b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Either Text Text -> SIO (YesodExampleData site) (Either Text Text)
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> Either Text Text
forall a b. b -> Either a b
Right Text
url)
getLocation :: ParseRoute site => YesodExample site (Either T.Text (Route site))
getLocation :: forall site.
ParseRoute site =>
YesodExample site (Either Text (Route site))
getLocation = do
mr <- YesodExample site (Maybe SResponse)
forall site. YesodExample site (Maybe SResponse)
getResponse
case mr of
Maybe SResponse
Nothing -> Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site))
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site)))
-> Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site))
forall a b. (a -> b) -> a -> b
$ Text -> Either Text (Route site)
forall a b. a -> Either a b
Left Text
"getLocation called, but there was no previous response, so no Location header"
Just SResponse
r -> case HeaderName -> RequestHeaders -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup HeaderName
"Location" (SResponse -> RequestHeaders
simpleHeaders SResponse
r) of
Maybe ByteString
Nothing -> Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site))
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site)))
-> Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site))
forall a b. (a -> b) -> a -> b
$ Text -> Either Text (Route site)
forall a b. a -> Either a b
Left Text
"getLocation called, but the previous response has no Location header"
Just ByteString
h -> case ([Text], [(Text, Text)]) -> Maybe (Route site)
forall a.
ParseRoute a =>
([Text], [(Text, Text)]) -> Maybe (Route a)
parseRoute (([Text], [(Text, Text)]) -> Maybe (Route site))
-> ([Text], [(Text, Text)]) -> Maybe (Route site)
forall a b. (a -> b) -> a -> b
$ ByteString -> ([Text], [(Text, Text)])
decodePath ByteString
h of
Maybe (Route site)
Nothing -> Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site))
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site)))
-> Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site))
forall a b. (a -> b) -> a -> b
$ Text -> Either Text (Route site)
forall a b. a -> Either a b
Left Text
"getLocation called, but couldn’t parse it into a route"
Just Route site
l -> Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site))
forall a. a -> SIO (YesodExampleData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site)))
-> Either Text (Route site)
-> SIO (YesodExampleData site) (Either Text (Route site))
forall a b. (a -> b) -> a -> b
$ Route site -> Either Text (Route site)
forall a b. b -> Either a b
Right Route site
l
where decodePath :: ByteString -> ([Text], [(Text, Text)])
decodePath ByteString
b = let (ByteString
x, ByteString
y) = (Char -> Bool) -> ByteString -> (ByteString, ByteString)
BS8.break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==Char
'?') ByteString
b
in (ByteString -> [Text]
H.decodePathSegments ByteString
x, (Text, Maybe Text) -> (Text, Text)
forall {b} {a}. Monoid b => (a, Maybe b) -> (a, b)
unJust ((Text, Maybe Text) -> (Text, Text))
-> [(Text, Maybe Text)] -> [(Text, Text)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ByteString -> [(Text, Maybe Text)]
H.parseQueryText ByteString
y)
unJust :: (a, Maybe b) -> (a, b)
unJust (a
a, Just b
b) = (a
a, b
b)
unJust (a
a, Maybe b
Nothing) = (a
a, b
forall a. Monoid a => a
Data.Monoid.mempty)
setMethod :: H.Method -> RequestBuilder site ()
setMethod :: forall site. ByteString -> RequestBuilder site ()
setMethod ByteString
m = (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ())
-> (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ \RequestBuilderData site
rbd -> RequestBuilderData site
rbd { rbdMethod = m }
setUrl :: (Yesod site, RedirectUrl site url)
=> url
-> RequestBuilder site ()
setUrl :: forall site url.
(Yesod site, RedirectUrl site url) =>
url -> RequestBuilder site ()
setUrl url
url' = do
site <- (RequestBuilderData site -> site)
-> SIO (RequestBuilderData site) (RequestBuilderData site)
-> SIO (RequestBuilderData site) site
forall a b.
(a -> b)
-> SIO (RequestBuilderData site) a
-> SIO (RequestBuilderData site) b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap RequestBuilderData site -> site
forall site. RequestBuilderData site -> site
rbdSite SIO (RequestBuilderData site) (RequestBuilderData site)
forall s. SIO s s
getSIO
eurl <- Yesod.Core.Unsafe.runFakeHandler
M.empty
(const $ error "Yesod.Test: No logger available")
site
(toTextUrl url')
url <- either (error . show) return eurl
let (urlPath, urlQuery) = T.break (== '?') url
modifySIO $ \RequestBuilderData site
rbd -> RequestBuilderData site
rbd
{ rbdPath =
case DL.filter (/="") $ H.decodePathSegments $ TE.encodeUtf8 urlPath of
(Text
"http:":Text
_:[Text]
rest) -> [Text]
rest
(Text
"https:":Text
_:[Text]
rest) -> [Text]
rest
[Text]
x -> [Text]
x
, rbdGets = rbdGets rbd ++ H.parseQuery (TE.encodeUtf8 urlQuery)
}
clickOn :: (HasCallStack, Yesod site) => Query -> YesodExample site ()
clickOn :: forall site.
(HasCallStack, Yesod site) =>
Text -> YesodExample site ()
clickOn Text
query = do
(YesodExampleData site -> Maybe SResponse)
-> [Text]
-> (SResponse -> SIO (YesodExampleData site) ())
-> SIO (YesodExampleData site) ()
forall state a.
HasCallStack =>
(state -> Maybe SResponse)
-> [Text] -> (SResponse -> SIO state a) -> SIO state a
withResponse' YesodExampleData site -> Maybe SResponse
forall site. YesodExampleData site -> Maybe SResponse
yedResponse [Text
"Tried to invoke clickOn in order to read HTML of a previous response."] ((SResponse -> SIO (YesodExampleData site) ())
-> SIO (YesodExampleData site) ())
-> (SResponse -> SIO (YesodExampleData site) ())
-> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ \ SResponse
res ->
case HtmlLBS -> Text -> Text -> Either String [[Text]]
findAttributeBySelector (SResponse -> HtmlLBS
simpleBody SResponse
res) Text
query Text
"href" of
Left String
err -> Text -> SIO (YesodExampleData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (YesodExampleData site) ())
-> Text -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ Text
query Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" did not parse: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack (String -> String
forall a. Show a => a -> String
show String
err)
Right [[Text
match]] -> Text -> SIO (YesodExampleData site) ()
forall site url.
(Yesod site, RedirectUrl site url) =>
url -> YesodExample site ()
get Text
match
Right [[Text]]
matches -> Text -> SIO (YesodExampleData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (YesodExampleData site) ())
-> Text -> SIO (YesodExampleData site) ()
forall a b. (a -> b) -> a -> b
$ Text
"Expected exactly one match for clickOn: got " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack ([[Text]] -> String
forall a. Show a => a -> String
show [[Text]]
matches)
setRequestBody :: BSL8.ByteString -> RequestBuilder site ()
setRequestBody :: forall site. HtmlLBS -> RequestBuilder site ()
setRequestBody HtmlLBS
body = (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ())
-> (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ \RequestBuilderData site
rbd -> RequestBuilderData site
rbd { rbdPostData = BinaryPostData body }
addRequestHeader :: H.Header -> RequestBuilder site ()
(HeaderName, ByteString)
header = (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall s. (s -> s) -> SIO s ()
modifySIO ((RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ())
-> (RequestBuilderData site -> RequestBuilderData site)
-> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ \RequestBuilderData site
rbd -> RequestBuilderData site
rbd
{ rbdHeaders = header : rbdHeaders rbd
}
addBasicAuthHeader :: CI ByteString
-> CI ByteString
-> RequestBuilder site ()
HeaderName
username HeaderName
password =
let credentials :: ByteString
credentials = Base -> ByteString -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
Base -> bin -> bout
convertToBase Base
Base64 (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ HeaderName -> ByteString
forall s. CI s -> s
CI.original (HeaderName -> ByteString) -> HeaderName -> ByteString
forall a b. (a -> b) -> a -> b
$ HeaderName
username HeaderName -> HeaderName -> HeaderName
forall a. Semigroup a => a -> a -> a
<> HeaderName
":" HeaderName -> HeaderName -> HeaderName
forall a. Semigroup a => a -> a -> a
<> HeaderName
password
in (HeaderName, ByteString) -> RequestBuilder site ()
forall site. (HeaderName, ByteString) -> RequestBuilder site ()
addRequestHeader (HeaderName
"Authorization", ByteString
"Basic " ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
credentials)
request :: RequestBuilder site ()
-> YesodExample site ()
request :: forall site. RequestBuilder site () -> YesodExample site ()
request RequestBuilder site ()
reqBuilder = do
YesodExampleData app site oldCookies mRes <- SIO (YesodExampleData site) (YesodExampleData site)
forall s. SIO s s
getSIO
RequestBuilderData {..} <- liftIO $ execSIO reqBuilder RequestBuilderData
{ rbdPostData = MultipleItemsPostData []
, rbdResponse = mRes
, rbdMethod = "GET"
, rbdSite = site
, rbdPath = []
, rbdGets = []
, rbdHeaders = []
}
let path
| [Text] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Text]
rbdPath = Text
"/"
| Bool
otherwise = ByteString -> Text
TE.decodeUtf8 (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ Builder -> ByteString
Builder.toByteString (Builder -> ByteString) -> Builder -> ByteString
forall a b. (a -> b) -> a -> b
$ [Text] -> Builder
H.encodePathSegments [Text]
rbdPath
currentUtc <- liftIO getCurrentTime
let cookies = (SetCookie -> Bool) -> Cookies -> Cookies
forall a k. (a -> Bool) -> Map k a -> Map k a
M.filter (UTCTime -> SetCookie -> Bool
checkCookieTime UTCTime
currentUtc) Cookies
oldCookies
cookiesForPath = (SetCookie -> Bool) -> Cookies -> Cookies
forall a k. (a -> Bool) -> Map k a -> Map k a
M.filter (Text -> SetCookie -> Bool
checkCookiePath Text
path) Cookies
cookies
let req = case RBDPostData
rbdPostData of
MultipleItemsPostData [RequestPart]
x ->
if (RequestPart -> Bool) -> [RequestPart] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
DL.any RequestPart -> Bool
isFile [RequestPart]
x
then ([RequestPart] -> SRequest
multipart [RequestPart]
x)
else SRequest
singlepart
BinaryPostData HtmlLBS
_ -> SRequest
singlepart
where singlepart :: SRequest
singlepart = Cookies
-> RBDPostData
-> ByteString
-> RequestHeaders
-> Text
-> Query
-> SRequest
forall a0.
Map a0 SetCookie
-> RBDPostData
-> ByteString
-> RequestHeaders
-> Text
-> Query
-> SRequest
makeSinglepart Cookies
cookiesForPath RBDPostData
rbdPostData ByteString
rbdMethod RequestHeaders
rbdHeaders Text
path Query
rbdGets
multipart :: [RequestPart] -> SRequest
multipart [RequestPart]
x = Cookies
-> [RequestPart]
-> ByteString
-> RequestHeaders
-> Text
-> Query
-> SRequest
forall a0.
Map a0 SetCookie
-> [RequestPart]
-> ByteString
-> RequestHeaders
-> Text
-> Query
-> SRequest
makeMultipart Cookies
cookiesForPath [RequestPart]
x ByteString
rbdMethod RequestHeaders
rbdHeaders Text
path Query
rbdGets
response <- liftIO $ runSession (srequest req
{ simpleRequest = (simpleRequest req)
{ httpVersion = H.http11
}
}) app
let newCookies = RequestHeaders -> [SetCookie]
parseSetCookies (RequestHeaders -> [SetCookie]) -> RequestHeaders -> [SetCookie]
forall a b. (a -> b) -> a -> b
$ SResponse -> RequestHeaders
simpleHeaders SResponse
response
cookies' = [(ByteString, SetCookie)] -> Cookies
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [(SetCookie -> ByteString
Cookie.setCookieName SetCookie
c, SetCookie
c) | SetCookie
c <- [SetCookie]
newCookies] Cookies -> Cookies -> Cookies
forall k a. Ord k => Map k a -> Map k a -> Map k a
`M.union` Cookies
cookies
putSIO $ YesodExampleData app site cookies' (Just response)
where
isFile :: RequestPart -> Bool
isFile (ReqFilePart Text
_ String
_ HtmlLBS
_ Text
_) = Bool
True
isFile RequestPart
_ = Bool
False
checkCookieTime :: UTCTime -> SetCookie -> Bool
checkCookieTime UTCTime
t SetCookie
c = case SetCookie -> Maybe UTCTime
Cookie.setCookieExpires SetCookie
c of
Maybe UTCTime
Nothing -> Bool
True
Just UTCTime
t' -> UTCTime
t UTCTime -> UTCTime -> Bool
forall a. Ord a => a -> a -> Bool
< UTCTime
t'
checkCookiePath :: Text -> SetCookie -> Bool
checkCookiePath Text
url SetCookie
c =
case SetCookie -> Maybe ByteString
Cookie.setCookiePath SetCookie
c of
Maybe ByteString
Nothing -> Bool
True
Just ByteString
x -> ByteString
x ByteString -> ByteString -> Bool
`BS8.isPrefixOf` Text -> ByteString
TE.encodeUtf8 Text
url
boundary :: String
boundary :: String
boundary = String
"*******noneedtomakethisrandom"
separator :: ByteString
separator = [ByteString] -> ByteString
BS8.concat [ByteString
"--", String -> ByteString
BS8.pack String
boundary, ByteString
"\r\n"]
makeMultipart :: M.Map a0 Cookie.SetCookie
-> [RequestPart]
-> H.Method
-> [H.Header]
-> T.Text
-> H.Query
-> SRequest
makeMultipart :: forall a0.
Map a0 SetCookie
-> [RequestPart]
-> ByteString
-> RequestHeaders
-> Text
-> Query
-> SRequest
makeMultipart Map a0 SetCookie
cookies [RequestPart]
parts ByteString
method RequestHeaders
extraHeaders Text
urlPath Query
urlQuery =
Request -> HtmlLBS -> SRequest
SRequest Request
simpleRequest' ([RequestPart] -> HtmlLBS
simpleRequestBody' [RequestPart]
parts)
where simpleRequestBody' :: [RequestPart] -> HtmlLBS
simpleRequestBody' [RequestPart]
x =
[ByteString] -> HtmlLBS
BSL8.fromChunks [[RequestPart] -> ByteString
multiPartBody [RequestPart]
x]
simpleRequest' :: Request
simpleRequest' = RequestHeaders
-> ByteString -> RequestHeaders -> Text -> Query -> Request
mkRequest
[ (HeaderName
"Cookie", ByteString
cookieValue)
, (HeaderName
"Content-Type", ByteString
contentTypeValue)]
ByteString
method RequestHeaders
extraHeaders Text
urlPath Query
urlQuery
cookieValue :: ByteString
cookieValue = Builder -> ByteString
Builder.toByteString (Builder -> ByteString) -> Builder -> ByteString
forall a b. (a -> b) -> a -> b
$ Cookies -> Builder
Cookie.renderCookies Cookies
cookiePairs
cookiePairs :: Cookies
cookiePairs = [ (SetCookie -> ByteString
Cookie.setCookieName SetCookie
c, SetCookie -> ByteString
Cookie.setCookieValue SetCookie
c)
| SetCookie
c <- ((a0, SetCookie) -> SetCookie) -> [(a0, SetCookie)] -> [SetCookie]
forall a b. (a -> b) -> [a] -> [b]
map (a0, SetCookie) -> SetCookie
forall a b. (a, b) -> b
snd ([(a0, SetCookie)] -> [SetCookie])
-> [(a0, SetCookie)] -> [SetCookie]
forall a b. (a -> b) -> a -> b
$ Map a0 SetCookie -> [(a0, SetCookie)]
forall k a. Map k a -> [(k, a)]
M.toList Map a0 SetCookie
cookies ]
contentTypeValue :: ByteString
contentTypeValue = String -> ByteString
BS8.pack (String -> ByteString) -> String -> ByteString
forall a b. (a -> b) -> a -> b
$ String
"multipart/form-data; boundary=" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
boundary
multiPartBody :: [RequestPart] -> ByteString
multiPartBody [RequestPart]
parts =
[ByteString] -> ByteString
BS8.concat ([ByteString] -> ByteString) -> [ByteString] -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
separator ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
: [[ByteString] -> ByteString
BS8.concat [RequestPart -> ByteString
multipartPart RequestPart
p, ByteString
separator] | RequestPart
p <- [RequestPart]
parts]
multipartPart :: RequestPart -> ByteString
multipartPart (ReqKvPart Text
k Text
v) = [ByteString] -> ByteString
BS8.concat
[ ByteString
"Content-Disposition: form-data; "
, ByteString
"name=\"", Text -> ByteString
TE.encodeUtf8 Text
k, ByteString
"\"\r\n\r\n"
, Text -> ByteString
TE.encodeUtf8 Text
v, ByteString
"\r\n"]
multipartPart (ReqFilePart Text
k String
v HtmlLBS
bytes Text
mime) = [ByteString] -> ByteString
BS8.concat
[ ByteString
"Content-Disposition: form-data; "
, ByteString
"name=\"", Text -> ByteString
TE.encodeUtf8 Text
k, ByteString
"\"; "
, ByteString
"filename=\"", String -> ByteString
BS8.pack String
v, ByteString
"\"\r\n"
, ByteString
"Content-Type: ", Text -> ByteString
TE.encodeUtf8 Text
mime, ByteString
"\r\n\r\n"
, [ByteString] -> ByteString
BS8.concat ([ByteString] -> ByteString) -> [ByteString] -> ByteString
forall a b. (a -> b) -> a -> b
$ HtmlLBS -> [ByteString]
BSL8.toChunks HtmlLBS
bytes, ByteString
"\r\n"]
makeSinglepart :: M.Map a0 Cookie.SetCookie
-> RBDPostData
-> H.Method
-> [H.Header]
-> T.Text
-> H.Query
-> SRequest
makeSinglepart :: forall a0.
Map a0 SetCookie
-> RBDPostData
-> ByteString
-> RequestHeaders
-> Text
-> Query
-> SRequest
makeSinglepart Map a0 SetCookie
cookies RBDPostData
rbdPostData ByteString
method RequestHeaders
extraHeaders Text
urlPath Query
urlQuery =
Request -> HtmlLBS -> SRequest
SRequest Request
simpleRequest' (RBDPostData -> HtmlLBS
simpleRequestBody' RBDPostData
rbdPostData)
where
simpleRequest' :: Request
simpleRequest' = (RequestHeaders
-> ByteString -> RequestHeaders -> Text -> Query -> Request
mkRequest
([ (HeaderName
"Cookie", ByteString
cookieValue) ] RequestHeaders -> RequestHeaders -> RequestHeaders
forall a. [a] -> [a] -> [a]
++ RBDPostData -> RequestHeaders
forall {a} {b}. (IsString a, IsString b) => RBDPostData -> [(a, b)]
headersForPostData RBDPostData
rbdPostData)
ByteString
method RequestHeaders
extraHeaders Text
urlPath Query
urlQuery)
simpleRequestBody' :: RBDPostData -> HtmlLBS
simpleRequestBody' (MultipleItemsPostData [RequestPart]
x) =
[ByteString] -> HtmlLBS
BSL8.fromChunks ([ByteString] -> HtmlLBS) -> [ByteString] -> HtmlLBS
forall a b. (a -> b) -> a -> b
$ ByteString -> [ByteString]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> [ByteString]) -> ByteString -> [ByteString]
forall a b. (a -> b) -> a -> b
$ Bool -> Cookies -> ByteString
H.renderSimpleQuery Bool
False
(Cookies -> ByteString) -> Cookies -> ByteString
forall a b. (a -> b) -> a -> b
$ (RequestPart -> Cookies) -> [RequestPart] -> Cookies
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap RequestPart -> Cookies
singlepartPart [RequestPart]
x
simpleRequestBody' (BinaryPostData HtmlLBS
x) = HtmlLBS
x
cookieValue :: ByteString
cookieValue = Builder -> ByteString
Builder.toByteString (Builder -> ByteString) -> Builder -> ByteString
forall a b. (a -> b) -> a -> b
$ Cookies -> Builder
Cookie.renderCookies Cookies
cookiePairs
cookiePairs :: Cookies
cookiePairs = [ (SetCookie -> ByteString
Cookie.setCookieName SetCookie
c, SetCookie -> ByteString
Cookie.setCookieValue SetCookie
c)
| SetCookie
c <- ((a0, SetCookie) -> SetCookie) -> [(a0, SetCookie)] -> [SetCookie]
forall a b. (a -> b) -> [a] -> [b]
map (a0, SetCookie) -> SetCookie
forall a b. (a, b) -> b
snd ([(a0, SetCookie)] -> [SetCookie])
-> [(a0, SetCookie)] -> [SetCookie]
forall a b. (a -> b) -> a -> b
$ Map a0 SetCookie -> [(a0, SetCookie)]
forall k a. Map k a -> [(k, a)]
M.toList Map a0 SetCookie
cookies ]
singlepartPart :: RequestPart -> Cookies
singlepartPart (ReqFilePart Text
_ String
_ HtmlLBS
_ Text
_) = []
singlepartPart (ReqKvPart Text
k Text
v) = [(Text -> ByteString
TE.encodeUtf8 Text
k, Text -> ByteString
TE.encodeUtf8 Text
v)]
headersForPostData :: RBDPostData -> [(a, b)]
headersForPostData (MultipleItemsPostData []) = []
headersForPostData (MultipleItemsPostData [RequestPart]
_ ) = [(a
"Content-Type", b
"application/x-www-form-urlencoded")]
headersForPostData (BinaryPostData HtmlLBS
_ ) = []
mkRequest :: RequestHeaders
-> ByteString -> RequestHeaders -> Text -> Query -> Request
mkRequest RequestHeaders
headers ByteString
method RequestHeaders
extraHeaders Text
urlPath Query
urlQuery = Request
defaultRequest
{ requestMethod = method
, remoteHost = Sock.SockAddrInet 1 2
, requestHeaders = headers ++ extraHeaders
, rawPathInfo = TE.encodeUtf8 urlPath
, pathInfo = H.decodePathSegments $ TE.encodeUtf8 urlPath
, rawQueryString = H.renderQuery False urlQuery
, queryString = urlQuery
}
parseSetCookies :: [H.Header] -> [Cookie.SetCookie]
parseSetCookies :: RequestHeaders -> [SetCookie]
parseSetCookies RequestHeaders
headers = ((HeaderName, ByteString) -> SetCookie)
-> RequestHeaders -> [SetCookie]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> SetCookie
Cookie.parseSetCookie (ByteString -> SetCookie)
-> ((HeaderName, ByteString) -> ByteString)
-> (HeaderName, ByteString)
-> SetCookie
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (HeaderName, ByteString) -> ByteString
forall a b. (a, b) -> b
snd) (RequestHeaders -> [SetCookie]) -> RequestHeaders -> [SetCookie]
forall a b. (a -> b) -> a -> b
$ ((HeaderName, ByteString) -> Bool)
-> RequestHeaders -> RequestHeaders
forall a. (a -> Bool) -> [a] -> [a]
DL.filter ((HeaderName
"Set-Cookie"HeaderName -> HeaderName -> Bool
forall a. Eq a => a -> a -> Bool
==) (HeaderName -> Bool)
-> ((HeaderName, ByteString) -> HeaderName)
-> (HeaderName, ByteString)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (HeaderName, ByteString) -> HeaderName
forall a b. (a, b) -> a
fst) (RequestHeaders -> RequestHeaders)
-> RequestHeaders -> RequestHeaders
forall a b. (a -> b) -> a -> b
$ RequestHeaders
headers
failure :: (HasCallStack, MonadIO a) => T.Text -> a b
failure :: forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure Text
reason = (IO (ZonkAny 0) -> a (ZonkAny 0)
forall a. IO a -> a a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (ZonkAny 0) -> a (ZonkAny 0))
-> IO (ZonkAny 0) -> a (ZonkAny 0)
forall a b. (a -> b) -> a -> b
$ String -> IO (ZonkAny 0)
forall a. HasCallStack => String -> IO a
HUnit.assertFailure (String -> IO (ZonkAny 0)) -> String -> IO (ZonkAny 0)
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
reason) a (ZonkAny 0) -> a b -> a b
forall a b. a a -> a b -> a b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> String -> a b
forall a. HasCallStack => String -> a
error String
""
type TestApp site = (site, Middleware)
testApp :: site -> Middleware -> TestApp site
testApp :: forall site. site -> Middleware -> TestApp site
testApp site
site Middleware
middleware = (site
site, Middleware
middleware)
type YSpec site = Hspec.SpecWith (TestApp site)
instance YesodDispatch site => Hspec.Example (SIO (YesodExampleData site) a) where
type Arg (SIO (YesodExampleData site) a) = TestApp site
evaluateExample :: SIO (YesodExampleData site) a
-> Params
-> (ActionWith (Arg (SIO (YesodExampleData site) a)) -> IO ())
-> ProgressCallback
-> IO Result
evaluateExample SIO (YesodExampleData site) a
example Params
params ActionWith (Arg (SIO (YesodExampleData site) a)) -> IO ()
action =
IO ()
-> Params
-> (ActionWith (Arg (IO ())) -> IO ())
-> ProgressCallback
-> IO Result
forall e.
Example e =>
e
-> Params
-> (ActionWith (Arg e) -> IO ())
-> ProgressCallback
-> IO Result
Hspec.evaluateExample
(ActionWith (Arg (SIO (YesodExampleData site) a)) -> IO ()
action (ActionWith (Arg (SIO (YesodExampleData site) a)) -> IO ())
-> ActionWith (Arg (SIO (YesodExampleData site) a)) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(site
site, Middleware
middleware) -> do
app <- site -> IO Application
forall site. YesodDispatch site => site -> IO Application
toWaiAppPlain site
site
_ <- evalSIO example YesodExampleData
{ yedApp = middleware app
, yedSite = site
, yedCookies = M.empty
, yedResponse = Nothing
}
return ())
Params
params
((() -> IO ()) -> () -> IO ()
forall a b. (a -> b) -> a -> b
$ ())
chooseByLabel :: T.Text -> RequestBuilder site ()
chooseByLabel :: forall site. Text -> RequestBuilder site ()
chooseByLabel Text
label = do
name <- (Text -> Text -> Bool) -> Text -> RequestBuilder site Text
forall site.
HasCallStack =>
(Text -> Text -> Bool) -> Text -> RequestBuilder site Text
genericNameFromLabel Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
(==) Text
label
value <- genericValueFromLabel (==) label
addPostParam name value
checkByLabel :: T.Text -> RequestBuilder site ()
checkByLabel :: forall site. Text -> RequestBuilder site ()
checkByLabel Text
label = do
name <- (Text -> Text -> Bool) -> Text -> RequestBuilder site Text
forall site.
HasCallStack =>
(Text -> Text -> Bool) -> Text -> RequestBuilder site Text
genericNameFromLabel Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
(==) Text
label
value <- genericValueFromLabel (==) label
addPostParam name value
selectByLabel :: T.Text -> T.Text -> RequestBuilder site ()
selectByLabel :: forall site. Text -> Text -> RequestBuilder site ()
selectByLabel Text
label Text
option = do
name <- (Text -> Text -> Bool) -> Text -> RequestBuilder site Text
forall site.
HasCallStack =>
(Text -> Text -> Bool) -> Text -> RequestBuilder site Text
genericNameFromLabel Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
(==) Text
label
parsedHtml <- parseHTML <$> htmlBody "selectByLabel"
let values = Cursor
parsedHtml Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Name -> Cursor -> [Cursor]
C.element Name
"select"
(Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Name -> Text -> Cursor -> [Cursor]
attributeIs Name
"name" Text
name
(Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall node a.
Axis node -> (Cursor node -> [a]) -> Cursor node -> [a]
&/ Name -> Cursor -> [Cursor]
C.element Name
"option"
(Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Text -> Cursor -> [Cursor]
isContentMatch Text
option
(Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Name -> Cursor -> [Text]
attribute Name
"value"
case values of
[] -> Text -> SIO (RequestBuilderData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (RequestBuilderData site) ())
-> Text -> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat [Text
"selectByLabel: option '" , Text
option, Text
"' not found in select '", Text
label, Text
"'"]
[Text
value] -> Text -> Text -> SIO (RequestBuilderData site) ()
forall site. Text -> Text -> RequestBuilder site ()
addPostParam Text
name Text
value
[Text]
_ -> Text -> SIO (RequestBuilderData site) ()
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (RequestBuilderData site) ())
-> Text -> SIO (RequestBuilderData site) ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat [Text
"selectByLabel: too many options '", Text
option, Text
"' found in select '", Text
label, Text
"'"]
where isContentMatch :: Text -> Cursor -> [Cursor]
isContentMatch Text
x Cursor
c
| Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== [Text] -> Text
T.concat (Cursor
c Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Cursor -> [Text]
content) = [Cursor
c]
| Bool
otherwise = []
genericValueFromLabel :: HasCallStack => (T.Text -> T.Text -> Bool) -> T.Text -> RequestBuilder site T.Text
genericValueFromLabel :: forall site.
HasCallStack =>
(Text -> Text -> Bool) -> Text -> RequestBuilder site Text
genericValueFromLabel Text -> Text -> Bool
match Text
label = do
body <- String -> RequestBuilder site HtmlLBS
forall site. String -> RequestBuilder site HtmlLBS
htmlBody String
"genericValueFromLabel"
case genericValueFromHTML match label body of
Left Text
e -> Text -> SIO (RequestBuilderData site) Text
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure Text
e
Right Text
x -> Text -> SIO (RequestBuilderData site) Text
forall a. a -> SIO (RequestBuilderData site) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
x
genericValueFromHTML :: (T.Text -> T.Text -> Bool) -> T.Text -> HtmlLBS -> Either T.Text T.Text
genericValueFromHTML :: (Text -> Text -> Bool) -> Text -> HtmlLBS -> Either Text Text
genericValueFromHTML Text -> Text -> Bool
match Text
label HtmlLBS
html =
let
parsedHTML :: Cursor
parsedHTML = HtmlLBS -> Cursor
parseHTML HtmlLBS
html
mlabel :: [Cursor]
mlabel = Cursor
parsedHTML
Cursor -> (Cursor -> [Cursor]) -> [Cursor]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Name -> Cursor -> [Cursor]
C.element Name
"label"
(Cursor -> [Cursor]) -> (Cursor -> [Cursor]) -> Cursor -> [Cursor]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Text -> Cursor -> [Cursor]
isContentMatch Text
label
mfor :: [Text]
mfor = [Cursor]
mlabel [Cursor] -> (Cursor -> [Text]) -> [Text]
forall a b. [a] -> (a -> [b]) -> [b]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Name -> Cursor -> [Text]
attribute Name
"for"
isContentMatch :: Text -> Cursor -> [Cursor]
isContentMatch Text
x Cursor
c
| Text
x Text -> Text -> Bool
`match` [Text] -> Text
T.concat (Cursor
c Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Cursor -> [Text]
content) = [Cursor
c]
| Bool
otherwise = []
in case [Text]
mfor of
Text
for:[] -> do
let mvalue :: [Text]
mvalue = Cursor
parsedHTML
Cursor -> (Cursor -> [Text]) -> [Text]
forall node a. Cursor node -> (Cursor node -> [a]) -> [a]
$// Name -> Text -> Cursor -> [Cursor]
attributeIs Name
"id" Text
for
(Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Name -> Cursor -> [Text]
attribute Name
"value"
case [Text]
mvalue of
Text
"":[Text]
_ -> Text -> Either Text Text
forall a b. a -> Either a b
Left (Text -> Either Text Text) -> Text -> Either Text Text
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat
[ Text
"Label "
, Text
label
, Text
" resolved to id "
, Text
for
, Text
" which was not found. "
]
Text
value:[Text]
_ -> Text -> Either Text Text
forall a b. b -> Either a b
Right Text
value
[] -> Text -> Either Text Text
forall a b. a -> Either a b
Left (Text -> Either Text Text) -> Text -> Either Text Text
forall a b. (a -> b) -> a -> b
$ Text
"No input with id " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
for
[] ->
case (Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
filter (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"") ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ [Cursor]
mlabel [Cursor] -> (Cursor -> [Text]) -> [Text]
forall a b. [a] -> (a -> [b]) -> [b]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Cursor -> [Cursor]
forall node. Cursor node -> [Cursor node]
child (Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Name -> Cursor -> [Cursor]
C.element Name
"input" (Cursor -> [Cursor]) -> (Cursor -> [Text]) -> Cursor -> [Text]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Name -> Cursor -> [Text]
attribute Name
"value") of
[] -> Text -> Either Text Text
forall a b. a -> Either a b
Left (Text -> Either Text Text) -> Text -> Either Text Text
forall a b. (a -> b) -> a -> b
$ Text
"No label contained: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
label
Text
value:[Text]
_ -> Text -> Either Text Text
forall a b. b -> Either a b
Right Text
value
[Text]
_ -> Text -> Either Text Text
forall a b. a -> Either a b
Left (Text -> Either Text Text) -> Text -> Either Text Text
forall a b. (a -> b) -> a -> b
$ Text
"More than one label contained " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
label
htmlBody :: String -> RequestBuilder site BSL8.ByteString
htmlBody :: forall site. String -> RequestBuilder site HtmlLBS
htmlBody String
funcName = do
mres <- (RequestBuilderData site -> Maybe SResponse)
-> SIO (RequestBuilderData site) (RequestBuilderData site)
-> SIO (RequestBuilderData site) (Maybe SResponse)
forall a b.
(a -> b)
-> SIO (RequestBuilderData site) a
-> SIO (RequestBuilderData site) b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap RequestBuilderData site -> Maybe SResponse
forall site. RequestBuilderData site -> Maybe SResponse
rbdResponse SIO (RequestBuilderData site) (RequestBuilderData site)
forall s. SIO s s
getSIO
res <-
case mres of
Maybe SResponse
Nothing -> Text -> SIO (RequestBuilderData site) SResponse
forall (a :: * -> *) b. (HasCallStack, MonadIO a) => Text -> a b
failure (Text -> SIO (RequestBuilderData site) SResponse)
-> Text -> SIO (RequestBuilderData site) SResponse
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
funcName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": No response available"
Just SResponse
res -> SResponse -> SIO (RequestBuilderData site) SResponse
forall a. a -> SIO (RequestBuilderData site) a
forall (m :: * -> *) a. Monad m => a -> m a
return SResponse
res
return $ simpleBody res