Merge pull request #1408 from mwotton/master

add clickOn function (closes #1405)
This commit is contained in:
Maximilian Tagher 2017-06-21 15:50:12 -07:00 committed by GitHub
commit 0223c0a586
5 changed files with 69 additions and 9 deletions

View File

@ -1,3 +1,8 @@
## 1.5.7
* Add clickOn.
[#1408](https://github.com/yesodweb/yesod/pull/1408)
## 1.5.6 ## 1.5.6
* Add assertNotEq. * Add assertNotEq.

View File

@ -62,6 +62,7 @@ module Yesod.Test
, setRequestBody , setRequestBody
, RequestBuilder , RequestBuilder
, setUrl , setUrl
, clickOn
-- *** Adding fields by label -- *** Adding fields by label
-- | Yesod can auto generate field names, so you are never sure what -- | Yesod can auto generate field names, so you are never sure what
@ -830,6 +831,25 @@ setUrl url' = do
, rbdGets = rbdGets rbd ++ H.parseQuery (TE.encodeUtf8 urlQuery) , rbdGets = rbdGets rbd ++ H.parseQuery (TE.encodeUtf8 urlQuery)
} }
-- | Click on a link defined by a CSS query
--
-- ==== __ Examples__
--
-- > get "/foobar"
-- > clickOn "a#idofthelink"
--
-- @since 1.5.7
clickOn :: Yesod site => Query -> YesodExample site ()
clickOn query = do
withResponse' yedResponse ["Tried to invoke clickOn in order to read HTML of a previous response."] $ \ res ->
case findAttributeBySelector (simpleBody res) query "href" of
Left err -> failure $ query <> " did not parse: " <> T.pack (show err)
Right [[match]] -> get match
Right matches -> failure $ "Expected exactly one match for clickOn: got " <> T.pack (show matches)
-- | Simple way to set HTTP request body -- | Simple way to set HTTP request body
-- --
-- ==== __ Examples__ -- ==== __ Examples__

View File

@ -10,16 +10,16 @@ and it returns a list of the HTML fragments that matched the given query.
Only a subset of the CSS spec is currently supported: Only a subset of the CSS spec is currently supported:
* By tag name: /table td a/ * By tag name: /table td a/
* By class names: /.container .content/ * By class names: /.container .content/
* By Id: /#oneId/ * By Id: /#oneId/
* By attribute: /[hasIt]/, /[exact=match]/, /[contains*=text]/, /[starts^=with]/, /[ends$=with]/ * By attribute: /[hasIt]/, /[exact=match]/, /[contains*=text]/, /[starts^=with]/, /[ends$=with]/
* Union: /a, span, p/ * Union: /a, span, p/
* Immediate children: /div > p/ * Immediate children: /div > p/
* Get jiggy with it: /div[data-attr=yeah] > .mon, .foo.bar div, #oneThing/ * Get jiggy with it: /div[data-attr=yeah] > .mon, .foo.bar div, #oneThing/
@ -27,6 +27,7 @@ Only a subset of the CSS spec is currently supported:
module Yesod.Test.TransversingCSS ( module Yesod.Test.TransversingCSS (
findBySelector, findBySelector,
findAttributeBySelector,
HtmlLBS, HtmlLBS,
Query, Query,
-- * For HXT hackers -- * For HXT hackers
@ -41,7 +42,7 @@ where
import Yesod.Test.CssQuery import Yesod.Test.CssQuery
import qualified Data.Text as T import qualified Data.Text as T
import Control.Applicative ((<$>), (<*>)) import qualified Control.Applicative
import Text.XML import Text.XML
import Text.XML.Cursor import Text.XML.Cursor
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
@ -58,9 +59,30 @@ type HtmlLBS = L.ByteString
-- --
-- * Right: List of matching Html fragments. -- * Right: List of matching Html fragments.
findBySelector :: HtmlLBS -> Query -> Either String [String] findBySelector :: HtmlLBS -> Query -> Either String [String]
findBySelector html query = (\x -> map (renderHtml . toHtml . node) . runQuery x) findBySelector html query =
Control.Applicative.<$> (Right $ fromDocument $ HD.parseLBS html) map (renderHtml . toHtml . node) Control.Applicative.<$> findCursorsBySelector html query
Control.Applicative.<*> parseQuery query
-- | Perform a css 'Query' on 'Html'. Returns Either
--
-- * Left: Query parse error.
--
-- * Right: List of matching Cursors
findCursorsBySelector :: HtmlLBS -> Query -> Either String [Cursor]
findCursorsBySelector html query =
runQuery (fromDocument $ HD.parseLBS html)
Control.Applicative.<$> parseQuery query
-- | Perform a css 'Query' on 'Html'. Returns Either
--
-- * Left: Query parse error.
--
-- * Right: List of matching Cursors
--
-- @since 1.5.7
findAttributeBySelector :: HtmlLBS -> Query -> T.Text -> Either String [[T.Text]]
findAttributeBySelector html query attr =
map (laxAttribute attr) Control.Applicative.<$> findCursorsBySelector html query
-- Run a compiled query on Html, returning a list of matching Html fragments. -- Run a compiled query on Html, returning a list of matching Html fragments.
runQuery :: Cursor -> [[SelectorGroup]] -> [Cursor] runQuery :: Cursor -> [[SelectorGroup]] -> [Cursor]

View File

@ -34,6 +34,7 @@ import Data.ByteString.Lazy.Char8 ()
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Text.HTML.DOM as HD import qualified Text.HTML.DOM as HD
import Network.HTTP.Types.Status (status301, status303, unsupportedMediaType415) import Network.HTTP.Types.Status (status301, status303, unsupportedMediaType415)
import Control.Exception.Lifted(SomeException, try)
parseQuery_ :: Text -> [[SelectorGroup]] parseQuery_ :: Text -> [[SelectorGroup]]
parseQuery_ = either error id . parseQuery parseQuery_ = either error id . parseQuery
@ -169,6 +170,16 @@ main = hspec $ do
addToken_ "body" addToken_ "body"
statusIs 200 statusIs 200
bodyEquals "12345" bodyEquals "12345"
yit "can follow a link via clickOn" $ do
get ("/htmlWithLink" :: Text)
clickOn "a#thelink"
statusIs 200
bodyEquals "<html><head><title>Hello</title></head><body><p>Hello World</p><p>Hello Moon</p></body></html>"
get ("/htmlWithLink" :: Text)
(bad :: Either SomeException ()) <- try (clickOn "a#nonexistentlink")
assertEq "bad link" (isLeft bad) True
ydescribe "utf8 paths" $ do ydescribe "utf8 paths" $ do
yit "from path" $ do yit "from path" $ do
@ -326,6 +337,8 @@ app = liteApp $ do
onStatic "html" $ dispatchTo $ onStatic "html" $ dispatchTo $
return ("<html><head><title>Hello</title></head><body><p>Hello World</p><p>Hello Moon</p></body></html>" :: Text) return ("<html><head><title>Hello</title></head><body><p>Hello World</p><p>Hello Moon</p></body></html>" :: Text)
onStatic "htmlWithLink" $ dispatchTo $
return ("<html><head><title>A link</title></head><body><a href=\"/html\" id=\"thelink\">Link!</a></body></html>" :: Text)
onStatic "labels" $ dispatchTo $ onStatic "labels" $ dispatchTo $
return ("<html><label><input type='checkbox' name='fooname' id='foobar'>Foo Bar</label></html>" :: Text) return ("<html><label><input type='checkbox' name='fooname' id='foobar'>Foo Bar</label></html>" :: Text)

View File

@ -1,5 +1,5 @@
name: yesod-test name: yesod-test
version: 1.5.6 version: 1.5.7
license: MIT license: MIT
license-file: LICENSE license-file: LICENSE
author: Nubis <nubis@woobiz.com.ar> author: Nubis <nubis@woobiz.com.ar>