{-# LANGUAGE OverloadedStrings #-} module Hasmin.Parser.InternalSpec where import Test.Hspec import Hasmin.Parser.Internal import Hasmin.Parser.Value import Test.Hspec.Attoparsec (shouldSucceedOn, shouldFailOn) import Data.Text (Text) import Hasmin.TestUtils ruleParserTest :: Spec ruleParserTest = describe "At-rule parser tests" $ mapM_ (matchSpecWithDesc atRule) atMediaParserTestInfo atMediaParserTestInfo :: [(String, Text, Text)] atMediaParserTestInfo = [("Doesn't choke on invalid @media expression", "@media screen and (min-resolution: 144 dpi){}", "@media screen and (min-resolution: 144 dpi){}") ] declarationParserTests :: Spec declarationParserTests = describe "declaration parser tests" $ mapM_ (matchSpecWithDesc declaration) declarationTestInfo declarationTestInfo :: [(String, Text, Text)] declarationTestInfo = [("Parses declaration with unknown property and value, ending in ';'", "a:b;", "a:b") ,("Parses declaration with unknown property and value, ending in '}'", "a:b}", "a:b") ,("Ignores comments before colon", "a/**/:b;", "a:b") ,("Ignores comments after colon", "a:/**/b;", "a:b") ,("Parses !important and prints it back without space", "a:b ! important;", "a:b!important") ,("Ignores comments before semicolon", "a:b/**/;", "a:b") ,("Parses declaration starting with '+' (IE <= 7)", "+property:a", "+property:a") ] declarationParsersTests :: Spec declarationParsersTests = describe "Declaration parsers tests" $ it "Parses valid font-style values, and fails with invalids" $ do fontStyle `shouldSucceedOn` ("normal" :: Text) fontStyle `shouldSucceedOn` ("italic" :: Text) fontStyle `shouldSucceedOn` ("oblique" :: Text) fontStyle `shouldFailOn` ("anything else" :: Text) styleRuleParserTests :: Spec styleRuleParserTests = describe "Style rule parser tests" $ mapM_ (matchSpecWithDesc styleRule) styleRuleTestInfo styleRuleTestInfo :: [(String, Text, Text)] styleRuleTestInfo = [("simple style rule", "a { margin : 0 ;}", "a{margin:0}") ,("Parses rule with an empty declaration (semicolon alone)", "h1 { ; }", "h1{}") ,("handles unset", "h1 {\n border: unset\n}", "h1{border:unset}") ,("handles initial", "camelCase {\r color: initial;}", "camelCase{color:initial}") ,("handles inherit", "h1 {\n\tpadding: inherit\f}", "h1{padding:inherit}") ] selectorParserTests :: Spec selectorParserTests = describe "Selector parser tests" $ mapM_ (matchSpecWithDesc selector) selectorTestInfo selectorParserFailures :: Spec selectorParserFailures = describe "Selector parser should fail on" . it "" $ selector `shouldFailOn` ("\\6543217" :: Text) selectorTestInfo :: [(String, Text, Text)] selectorTestInfo = [("Element", "h6", "h6") ,("Element with namespace", "ns|body", "ns|body") ,("Element with any namespace", "*|p", "*|p") ,("Element with '|' but no namespace", "|html", "html") -- bar shouldn't show ,("Class", ".class", ".class") ,("Class name with high BMP char", ".無", ".無") --,("Class name starting with number or dash", ".\\1\\#-\\.1 .\\--wie.-gehts", ".1#-.1.--wie.-gehts") ,("Class name with emoji", ".☺", ".☺") ,("Class name with multiple emoji", ".👊✊", ".👊✊") ,("Class name with escaped letter", ".\\t", ".\\t") ,("Class name with escaped unicode character", ".\\91", ".\\91") ,("Id", "#AnId", "#AnId") -- ,("Id starting with a number", "#\\5\\#-\\.5", "#5#-.5") ,("Id with latin-1 character", "#ñ", "#ñ") -- The following four have a pseudo class syntax, but are pseudo elements ,(":after pseudo element", ":after", ":after") ,(":before pseudo element", ":before", ":before") ,(":first-line pseudo element", ":first-line", ":first-line") ,(":first-letter pseudo element", ":first-letter", ":first-letter") -- Since both syntaxes are valid, we print the shorter one (with a single ':') ,("::after pseudo element", "::after", ":after") ,("::before pseudo element", "::before", ":before") ,("::first-line pseudo element", "::first-line", ":first-line") ,("::first-letter pseudo element", "::first-letter", ":first-letter") ,("Pseudo class", ":link", ":link") --,("Pseudo class with escapes", ":na\\me", ":name") --,("Pseudo class with content" ,("Universal selector", "*", "*") ,("Universal selector with namespace", "n|*", "n|*") ,("Universal selector with any namespace", "*|*", "*|*") ,("Universal selector with '|' but no namespace", "|*", "*") -- bar shouldn't show ,(":not() functional pseudo-class of a selector list", ":not(:nth-last-child(2n), elem, #id, .class)", ":not(:nth-last-child(2n),elem,#id,.class)") ,(":matches() functional pseudo-class of a selector list", ".class:matches(:nth-child(2) , element, #AnID, .aClass)", ".class:matches(:nth-child(2),element,#AnID,.aClass)") ] {- [("any element","*", "*") ,("an element of type E","E","E") ,("an E element with a \"foo\" attribute", "E[foo]", "E[foo]") ,("an E element whose \"foo\" attribute value is exactly equal to \"bar\"", "E[foo=\"bar\"]","E[foo=\"bar\"]" ) ,("an E element whose \"foo\" attribute value is a list of whitespace-separated values, one of which is exactly equal to \"bar\"", "E[foo~=\"bar\"]","E[foo~=\"bar\"]") ,("an E element whose \"foo\" attribute value begins exactly with the string \"bar\"", "E[foo^=\"bar\"]","E[foo^=\"bar\"]") ,("an E element whose \"foo\" attribute value ends exactly with the string \"bar\"", "E[foo$=\"bar\"]","E[foo$=\"bar\"]") ,("an E element whose \"foo\" attribute value contains the substring \"bar\"", "E[foo*=\"bar\"]","E[foo*=\"bar\"]") ,("an E element whose \"foo\" attribute has a hyphen-separated list of values beginning (from the left) with \"en\"", "E[foo|=\"en\"]","E[foo|=\"en\"]") ,("an E element, root of the document", "E:root","E:root") ,("an E element, the n-th child of its parent", "E:nth-child(n)","E:nth-child(n)") ,("an E element, the n-th child of its parent, counting from the last one", "E:nth-last-child(n)","E:nth-last-child(n)") ,("an E element, the n-th sibling of its type", "E:nth-of-type(n)","E:nth-of-type(n)") ,("an E element, the n-th sibling of its type, counting from the last one", "E:nth-last-of-type(n)","E:nth-last-of-type(n)") ,("an E element, first child of its parent", "E:first-child","E:first-child") ,("an E element, last child of its parent", "E:last-child","E:last-child") ,("an E element, first sibling of its type", "E:first-of-type","E:first-of-type") ,("an E element, last sibling of its type", "E:last-of-type","E:last-of-type") ,("an E element, only child of its parent", "E:only-child","E:only-child") ,("an E element, only sibling of its type", "E:only-of-type","E:only-of-type") ,("an E element that has no children (including text nodes)", "E:empty","E:empty") ,("An E element being the source anchor of a hyperlink of which the target is not yet visited", "E:link", "E:link") ,("An E element being the source anchor of a hyperlink of which the target is already visited", "E:visited", "E:visited") ,("an E element during active action", "E:active","E:active") ,("an E element during hover action", "E:hover","E:hover") ,("an E element during focus action", "E:focus","E:focus") ,("an E element being the target of the referring URI", "E:target","E:target") ,("an element of type E in language \"fr\" (the document language specifies how language is determined)", "E:lang(fr)","E:lang(fr)") ,("a user interface element E which is enabled", "E:enabled", "E:enabled") ,("a user interface element E which is disabled", "E:disabled","E:disabled") ,("a user interface element E which is checked (for instance a radio-button or checkbox)", "E:checked","E:checked") ,("the first formatted line of an E element", "E::first-line","E:first-line") ,("the first formatted letter of an E element", "E::first-letter","E:first-letter") ,("generated content before an E element", "E::before","E:before") ,("generated content after an E element", "E::after","E:after") ,("an E element whose class is \"warning\" (the document language specifies how class is determined).", "E.warning","E.warning") ,("an E element with ID equal to \"myid\".", "E#myid","E#myid") ,("an E element that does not match simple selector s", "E:not(s)","E:not(s)") ,("An F element descendant of an E element", "E F", "E F") ,("An F element child of an E element", "E > F", "E>F") ,("An F element immediately preceded by an E element", "E + F", "E+F") ,("An F element preceded by an E element", "E ~ F", "E~F") ] -} --stylesheetParserTests :: Spec --stylesheetParserTests = -- describe "stylesheet parser tests" $ -- mapM_ (matchSpecWithDesc stylesheet) stylesheetTestInfo --stylesheetTestInfo :: [(String, Text, Text)] --stylesheetTestInfo = spec :: Spec spec = do declarationParserTests declarationParsersTests selectorParserTests selectorParserFailures styleRuleParserTests ruleParserTest main :: IO () main = hspec spec