webdriver-w3c-0.0.3: test/Web/Api/WebDriver/Monad/Test/Server/Page.hs
{-# LANGUAGE RecordWildCards #-}
module Web.Api.WebDriver.Monad.Test.Server.Page (
HtmlTag(..)
, Attr(..)
, Document(Text, tag, attrs, children, elementId)
, Page(url, contents)
, buildPage
, node
, requestPage
, CssSelector()
, getElementById
, cssMatchDocument
, parseCss
, tagIsClearable
, pageAboutBlank
) where
import Text.ParserCombinators.Parsec
import Data.Monoid
import Data.List
data HtmlTag
= Html
| Head
| Title
| Body
| Div
| P
| Ol
| Ul
| Li
| Form
| Input
| Button
deriving Eq
instance Show HtmlTag where
show t = case t of
Html -> "html"; Head -> "head"; Title -> "title"; Body -> "body"
Div -> "div"; P -> "p"; Ol -> "ol"; Ul -> "ul"; _ -> error "Show HtmlTag"
tagIsClearable :: HtmlTag -> Bool
tagIsClearable t = case t of
Input -> True
_ -> False
data Attr
= Id
| Class
| Name
deriving (Eq, Show)
data Document
= Text String
| Document
{ elementId :: String
, tag :: HtmlTag
, attrs :: [(Attr, Maybe String)]
, children :: [Document]
}
deriving (Eq, Show)
attrHasValue :: Attr -> String -> Document -> Bool
attrHasValue _ _ (Text _) = False
attrHasValue a v Document{..} =
case lookup a attrs of
Just (Just val) -> v == val
_ -> False
data Page = Page
{ contents :: Document
, url :: String
} deriving Show
node :: HtmlTag -> [(Attr, Maybe String)] -> [Document] -> Document
node tag attrs children =
let elementId = "" in
Document{..}
pageAboutBlank :: Page
pageAboutBlank = Page
{ contents = Text ""
, url = "about:blank"
}
assignIds :: String -> Document -> Document
assignIds _ h@(Text str) = Text str
assignIds base h@Document{..} = h
{ elementId = base
, children = zipWith prefix [1..] children
}
where
prefix i child = assignIds (base ++ "." ++ show i) child
buildPage :: String -> Document -> Page
buildPage url doc =
let contents = assignIds "" doc
in Page{..}
test1 :: Page
test1 = buildPage "example.com" $
node Html []
[
]
getElementById :: String -> Page -> Maybe Document
getElementById str Page{..} = getFirst $ get contents
where
get :: Document -> First Document
get (Text _) = First Nothing
get d@Document{..} = if elementId == str
then First (Just d)
else mconcat $ map get children
data CssSelector
= CssTag HtmlTag
| CssClass HtmlTag String
| CssHash HtmlTag String
| CssAttr HtmlTag Attr String
deriving Show
pHtmlTag :: Parser HtmlTag
pHtmlTag = choice $ map try
[ string "html" >> return Html
, string "head" >> return Head
, string "title" >> return Title
, string "body" >> return Body
, string "div" >> return Div
, string "p" >> return P
, string "ol" >> return Ol
, string "ul" >> return Ul
, string "li" >> return Li
, string "form" >> return Form
, string "input" >> return Input
, string "button" >> return Button
]
pAttr :: Parser Attr
pAttr = choice
[ string "class" >> return Class
, string "id" >> return Id
]
tokenchar :: Parser String
tokenchar = many1 $ oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ ['-','_']
pCssSelector :: Parser CssSelector
pCssSelector =
choice
[ try $ do
tag <- pHtmlTag
char '.'
classname <- tokenchar
return (CssClass tag classname)
, try $ do
tag <- pHtmlTag
char '#'
name <- tokenchar
return (CssHash tag name)
, try $ do
tag <- pHtmlTag
char '['
attr <- pAttr
char '='
char '\''
value <- tokenchar
char '\''
char ']'
return (CssAttr tag attr value)
] <|> do
tag <- pHtmlTag
return (CssTag tag)
parseCss :: String -> Either ParseError CssSelector
parseCss str = parse pCssSelector "" str
cssMatchDocument :: CssSelector -> Document -> [Document]
cssMatchDocument _ (Text _) = []
cssMatchDocument selector d@Document{..} =
let
match = case selector of
CssTag t -> t == tag
CssClass t c -> t == tag && attrHasValue Class c d
CssHash t h -> t == tag && attrHasValue Id h d
CssAttr t a v -> t == tag && attrHasValue a v d
in
(if match then (d:) else id) $
concatMap (cssMatchDocument selector) children
requestPage :: String -> [Page] -> Maybe Page
requestPage _ [] = Nothing
requestPage path (p@Page{..}:ps) =
if url == path
then Just p
else requestPage path ps