Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add versions of 'htmlAnyContain', 'htmlAllContain', 'htmlNoneContain' which match strings verbatim #1786

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions yesod-test/ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# ChangeLog for yesod-test

## 1.6.16

* Add `htmlAnyContainPreEscaped`, `htmlAllContainPreEscaped`, `htmlNoneContainPreEscaped`. [#1786](https://github.com/yesodweb/yesod/pull/1786)

## 1.6.15

Expand Down
58 changes: 58 additions & 0 deletions yesod-test/Yesod/Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ module Yesod.Test
, htmlAllContain
, htmlAnyContain
, htmlNoneContain
, htmlAllContainPreEscaped
, htmlAnyContainPreEscaped
, htmlNoneContainPreEscaped
Copy link
Contributor Author

@eahlberg eahlberg Oct 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

html*PreEscaped preferable to e.g. html*ContainRaw? PreEscaped was used since that's what it's called in the Blaze docs.

, htmlCount
, requireJSONResponse

Expand Down Expand Up @@ -754,6 +757,61 @@ htmlNoneContain query search = do
found -> failure $ "Found " <> T.pack (show $ length found) <>
" instances of " <> T.pack search <> " in " <> query <> " elements"

-- | Queries the HTML using a CSS selector, and all matched elements must contain
-- the given string verbatim.
--
-- ==== __Examples__
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- > get HomeR
-- > htmlAllContainPreEscaped "meta" " "content=\"site description\"" -- Every <meta> tag contains the string "content=site description" verbatim
--
-- Since 16.6.6
htmlAllContainPreEscaped :: HasCallStack => Query -> String -> YesodExample site ()
htmlAllContainPreEscaped query search = do
matches <- htmlQuery query
case matches of
[] -> failure $ "Nothing matched css query: " <> query
_ -> liftIO $ HUnit.assertBool ("Not all "++T.unpack query++" contain "++search ++ " matches: " ++ show matches) $
DL.all (DL.isInfixOf search) (map (TL.unpack . decodeUtf8) matches)
Copy link
Contributor Author

@eahlberg eahlberg Oct 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for the escaping, this is the exact same code as htmlAllContain, should we refactor this?


-- | Queries the HTML using a CSS selector, and passes if any matched
-- element contains the given string verbatim.
--
-- ==== __Examples__
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- > get HomeR
-- > htmlAnyContainPreEscaped "meta" "content=\"site description\"" -- At least one <meta> tag contains the string "content=site description" verbatim
--
-- Since 16.6.6
htmlAnyContainPreEscaped :: HasCallStack => Query -> String -> YesodExample site ()
htmlAnyContainPreEscaped query search = do
matches <- htmlQuery query
case matches of
[] -> failure $ "Nothing matched css query: " <> query
_ -> liftIO $ HUnit.assertBool ("None of "++T.unpack query++" contain "++search ++ " matches: " ++ show matches) $
DL.any (DL.isInfixOf search) (map (TL.unpack . decodeUtf8) matches)

-- | Queries the HTML using a CSS selector, and fails if any matched
-- element contains the given string verbatim (in other words, it is
-- the logical inverse of htmlAnyContainPreEscaped).
--
-- ==== __Examples__
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- > get HomeR
-- > htmlNoneContainPreEscaped "meta" "content=\"site description\"" -- No <meta> tag contains the string "content=site description" verbatim
--
-- Since 16.6.6
htmlNoneContainPreEscaped :: HasCallStack => Query -> String -> YesodExample site ()
htmlNoneContainPreEscaped query search = do
matches <- htmlQuery query
case DL.filter (DL.isInfixOf search) (map (TL.unpack . decodeUtf8) matches) of
[] -> return ()
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
-- are as many as expected.
--
Expand Down
7 changes: 5 additions & 2 deletions yesod-test/test/main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ main = hspec $ do
yit "finding html" $ do
get ("/html" :: Text)
statusIs 200
htmlAnyContainPreEscaped "meta" "content=\"site description\""
htmlAllContainPreEscaped "meta" "content=\"site description"
Copy link
Contributor Author

@eahlberg eahlberg Oct 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice if there are more concise examples than this but I couldn't come up with any

htmlNoneContainPreEscaped "meta" "foobar"
Copy link
Contributor Author

@eahlberg eahlberg Oct 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This checks absence in the same was as the test for htmlNoneContain, I think it'd be better with a test case which illustrates the difference between the two functions.

htmlCount "p" 2
htmlAllContain "p" "Hello"
htmlAllContain "span" "O'Kon"
Expand Down Expand Up @@ -230,7 +233,7 @@ main = hspec $ do
get ("/htmlWithLink" :: Text)
clickOn "a#thelink"
statusIs 200
bodyEquals "<html><head><title>Hello</title></head><body><p>Hello World</p><p>Hello Moon and <span>O'Kon</span></p></body></html>"
bodyEquals "<html><head><meta name=description content=\"site description\"/><meta name=og:description content=\"site description og\"/><title>Hello</title></head><body><p>Hello World</p><p>Hello Moon and <span>O'Kon</span></p></body></html>"

get ("/htmlWithLink" :: Text)
bad <- tryAny (clickOn "a#nonexistentlink")
Expand Down Expand Up @@ -579,7 +582,7 @@ app = liteApp $ do
FormSuccess (foo, _) -> return $ toHtml foo
_ -> defaultLayout widget
onStatic "html" $ dispatchTo $
return ("<html><head><title>Hello</title></head><body><p>Hello World</p><p>Hello Moon and <span>O'Kon</span></p></body></html>" :: Text)
return ("<html><head><meta name=description content=\"site description\"/><meta name=og:description content=\"site description og\"/><title>Hello</title></head><body><p>Hello World</p><p>Hello Moon and <span>O'Kon</span></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)
Expand Down
2 changes: 1 addition & 1 deletion yesod-test/yesod-test.cabal
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: yesod-test
version: 1.6.15
version: 1.6.16
license: MIT
license-file: LICENSE
author: Nubis <[email protected]>
Expand Down