diff --git a/yesod-test/README.md b/yesod-test/README.md
index 1567d028..f17be004 100644
--- a/yesod-test/README.md
+++ b/yesod-test/README.md
@@ -10,7 +10,7 @@ using css selectors.
You can also easily build requests using forms present in the current page.
This is very useful for testing web applications built in yesod for example,
-were your forms may have field names generated by the framework or a randomly
+where your forms may have field names generated by the framework or a randomly
generated "\_token" field.
Your database is also directly available so you can use runDB to set up
diff --git a/yesod-test/Yesod/Test.hs b/yesod-test/Yesod/Test.hs
index 6a7e1b46..74dff8d9 100644
--- a/yesod-test/Yesod/Test.hs
+++ b/yesod-test/Yesod/Test.hs
@@ -8,19 +8,19 @@
Yesod.Test is a pragmatic framework for testing web applications built
using wai and persistent.
-By pragmatic I may also mean 'dirty'. It's main goal is to encourage integration
+By pragmatic I may also mean 'dirty'. Its main goal is to encourage integration
and system testing of web applications by making everything /easy to test/.
Your tests are like browser sessions that keep track of cookies and the last
visited page. You can perform assertions on the content of HTML responses,
-using css selectors to explore the document more easily.
+using CSS selectors to explore the document more easily.
You can also easily build requests using forms present in the current page.
-This is very useful for testing web applications built in yesod for example,
-were your forms may have field names generated by the framework or a randomly
-generated '_nonce' field.
+This is very useful for testing web applications built in yesod, for example,
+where your forms may have field names generated by the framework or a randomly
+generated nonce value.
-Your database is also directly available so you can use runDBRunner to set up
+Your database is also directly available so you can use 'runDB' to set up
backend pre-conditions, or to assert that your session is having the desired effect.
-}
@@ -38,12 +38,12 @@ module Yesod.Test
, yit
-- * Making requests
- -- | To make a request you need to point to an url and pass in some parameters.
- --
- -- To build your parameters you will use the RequestBuilder monad that lets you
- -- add values, add files, lookup fields by label and find the current
- -- nonce value and add it to your request too.
+ -- | You can construct requests with the 'RequestBuilder' monad, which lets you
+ -- set the URL and add parameters, headers, and files. Helper functions are provided to
+ -- lookup fields by label and to add the current nonce value from your forms.
+ -- Once built, the request can be executed with the 'request' method.
--
+ -- Convenience functions like 'get' and 'post' build and execute common requests.
, get
, post
, postBody
@@ -57,16 +57,22 @@ module Yesod.Test
, RequestBuilder
, setUrl
- -- | Yesod can auto generate field ids, so you are never sure what
- -- the argument name should be for each one of your args when constructing
+ -- *** Adding fields by label
+ -- | Yesod can auto generate field names, so you are never sure what
+ -- the argument name should be for each one of your inputs when constructing
-- your requests. What you do know is the /label/ of the field.
-- These functions let you add parameters to your request based
-- on currently displayed label names.
, byLabel
, fileByLabel
- -- | Does the current form have a _nonce? Use any of these to add it to your
- -- request parameters.
+ -- *** Nonces
+ -- | In order to prevent CSRF exploits, yesod-form adds a hidden input
+ -- to your forms with the name "_token". This token is a randomly generated,
+ -- per-session value called a /nonce/.
+ --
+ -- In order to prevent your forms from being rejected in tests, use one of
+ -- these functions to add the nonce to your request.
, addNonce
, addNonce_
@@ -188,7 +194,7 @@ data RequestPart
= ReqKvPart T.Text T.Text
| ReqFilePart T.Text FilePath BSL8.ByteString T.Text
--- | The RequestBuilder state monad constructs an url encoded string of arguments
+-- | The 'RequestBuilder' state monad constructs a URL encoded string of arguments
-- to send with your requests. Some of the functions that run on it use the current
-- response to analyze the forms that the server is expecting to receive.
type RequestBuilder site = ST.StateT (RequestBuilderData site) IO
@@ -274,12 +280,12 @@ withResponse' getter f = maybe err f . getter =<< ST.get
withResponse :: (SResponse -> YesodExample site a) -> YesodExample site a
withResponse = withResponse' yedResponse
--- | Use HXT to parse a value from an html tag.
+-- | Use HXT to parse a value from an HTML tag.
-- Check for usage examples in this module's source.
parseHTML :: HtmlLBS -> Cursor
parseHTML html = fromDocument $ HD.parseLBS html
--- | Query the last response using css selectors, returns a list of matched fragments
+-- | Query the last response using CSS selectors, returns a list of matched fragments
htmlQuery' :: MonadIO m
=> (state -> Maybe SResponse)
-> Query
@@ -289,7 +295,7 @@ htmlQuery' getter query = withResponse' getter $ \ res ->
Left err -> failure $ query <> " did not parse: " <> T.pack (show err)
Right matches -> return $ map (encodeUtf8 . TL.pack) matches
--- | Query the last response using css selectors, returns a list of matched fragments
+-- | Query the last response using CSS selectors, returns a list of matched fragments
htmlQuery :: Query -> YesodExample site [HtmlLBS]
htmlQuery = htmlQuery' yedResponse
@@ -354,7 +360,7 @@ bodyContains text = withResponse $ \ res ->
contains :: BSL8.ByteString -> String -> Bool
contains a b = DL.isInfixOf b (TL.unpack $ decodeUtf8 a)
--- | Queries the html using a css selector, and all matched elements must contain
+-- | Queries the HTML using a CSS selector, and all matched elements must contain
-- the given string.
htmlAllContain :: Query -> String -> YesodExample site ()
htmlAllContain query search = do
@@ -364,7 +370,7 @@ htmlAllContain query search = do
_ -> liftIO $ HUnit.assertBool ("Not all "++T.unpack query++" contain "++search) $
DL.all (DL.isInfixOf search) (map (TL.unpack . decodeUtf8) matches)
--- | Queries the html using a css selector, and passes if any matched
+-- | Queries the HTML using a CSS selector, and passes if any matched
-- element contains the given string.
--
-- Since 0.3.5
@@ -376,7 +382,7 @@ htmlAnyContain query search = do
_ -> liftIO $ HUnit.assertBool ("None of "++T.unpack query++" contain "++search) $
DL.any (DL.isInfixOf search) (map (TL.unpack . decodeUtf8) matches)
--- | Queries the html using a css selector, and fails if any matched
+-- | Queries the HTML using a CSS selector, and fails if any matched
-- element contains the given string (in other words, it is the logical
-- inverse of htmlAnyContains).
--
@@ -389,7 +395,7 @@ htmlNoneContain query search = do
found -> failure $ "Found " <> T.pack (show $ length found) <>
" instances of " <> T.pack search <> " in " <> query <> " elements"
--- | Performs a css query on the last response and asserts the matched elements
+-- | Performs a CSS query on the last response and asserts the matched elements
-- are as many as expected.
htmlCount :: Query -> Int -> YesodExample site ()
htmlCount query count = do
@@ -408,7 +414,7 @@ printMatches query = do
matches <- htmlQuery query
liftIO $ hPutStrLn stderr $ show matches
--- | Add a parameter with the given name and value.
+-- | Add a parameter with the given name and value to the request body.
addPostParam :: T.Text -> T.Text -> RequestBuilder site ()
addPostParam name value =
ST.modify $ \rbd -> rbd { rbdPostData = (addPostData (rbdPostData rbd)) }
@@ -416,16 +422,25 @@ addPostParam name value =
addPostData (MultipleItemsPostData posts) =
MultipleItemsPostData $ ReqKvPart name value : posts
+-- | Add a parameter with the given name and value to the query string.
addGetParam :: T.Text -> T.Text -> RequestBuilder site ()
addGetParam name value = ST.modify $ \rbd -> rbd
{ rbdGets = (TE.encodeUtf8 name, Just $ TE.encodeUtf8 value)
: rbdGets rbd
}
--- | Add a file to be posted with the current request
+-- | Add a file to be posted with the current request.
--
--- Adding a file will automatically change your request content-type to be multipart/form-data
-addFile :: T.Text -> FilePath -> T.Text -> RequestBuilder site ()
+-- Adding a file will automatically change your request content-type to be multipart/form-data.
+--
+-- ==== __Examples__
+--
+-- > request $ do
+-- > addFile "profile_picture" "static/img/picture.png" "img/png"
+addFile :: T.Text -- ^ The parameter name for the file.
+ -> FilePath -- ^ The path to the file.
+ -> T.Text -- ^ The MIME type of the file, e.g. "image/png".
+ -> RequestBuilder site ()
addFile name path mimetype = do
contents <- liftIO $ BSL8.readFile path
ST.modify $ \rbd -> rbd { rbdPostData = (addPostData (rbdPostData rbd) contents) }
@@ -476,18 +491,75 @@ nameFromLabel label = do
(<>) :: T.Text -> T.Text -> T.Text
(<>) = T.append
-byLabel :: T.Text -> T.Text -> RequestBuilder site ()
+-- How does this work for the alternate syntax?
+
+-- | Finds the @\