hasmin 0.3.1.3 → 0.3.2
raw patch · 27 files changed
+538/−263 lines, 27 filesdep ~gitrevdep ~hopfliPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: gitrev, hopfli
API changes (from Hackage documentation)
+ Hasmin.Types.BgSize: Auto :: Auto
+ Hasmin.Types.BgSize: BgSize :: (Either PercentageLength Auto) -> (Maybe (Either PercentageLength Auto)) -> BgSize
+ Hasmin.Types.BgSize: Contain :: BgSize
+ Hasmin.Types.BgSize: Cover :: BgSize
+ Hasmin.Types.BgSize: data Auto
+ Hasmin.Types.BgSize: data BgSize
+ Hasmin.Types.BgSize: instance GHC.Classes.Eq Hasmin.Types.BgSize.Auto
+ Hasmin.Types.BgSize: instance GHC.Classes.Eq Hasmin.Types.BgSize.BgSize
+ Hasmin.Types.BgSize: instance GHC.Show.Show Hasmin.Types.BgSize.Auto
+ Hasmin.Types.BgSize: instance GHC.Show.Show Hasmin.Types.BgSize.BgSize
+ Hasmin.Types.BgSize: instance Hasmin.Types.Class.Minifiable Hasmin.Types.BgSize.BgSize
+ Hasmin.Types.BgSize: instance Hasmin.Types.Class.ToText Hasmin.Types.BgSize.Auto
+ Hasmin.Types.BgSize: instance Hasmin.Types.Class.ToText Hasmin.Types.BgSize.BgSize
+ Hasmin.Types.FilterFunction: Blur :: Distance -> FilterFunction
+ Hasmin.Types.FilterFunction: Brightness :: (Either Number Percentage) -> FilterFunction
+ Hasmin.Types.FilterFunction: Contrast :: (Either Number Percentage) -> FilterFunction
+ Hasmin.Types.FilterFunction: DropShadow :: Distance -> Distance -> (Maybe Distance) -> (Maybe Color) -> FilterFunction
+ Hasmin.Types.FilterFunction: Grayscale :: (Either Number Percentage) -> FilterFunction
+ Hasmin.Types.FilterFunction: HueRotate :: Angle -> FilterFunction
+ Hasmin.Types.FilterFunction: Invert :: (Either Number Percentage) -> FilterFunction
+ Hasmin.Types.FilterFunction: Opacity :: (Either Number Percentage) -> FilterFunction
+ Hasmin.Types.FilterFunction: Saturate :: (Either Number Percentage) -> FilterFunction
+ Hasmin.Types.FilterFunction: Sepia :: (Either Number Percentage) -> FilterFunction
+ Hasmin.Types.FilterFunction: data FilterFunction
+ Hasmin.Types.FilterFunction: instance GHC.Classes.Eq Hasmin.Types.FilterFunction.FilterFunction
+ Hasmin.Types.FilterFunction: instance GHC.Show.Show Hasmin.Types.FilterFunction.FilterFunction
+ Hasmin.Types.FilterFunction: instance Hasmin.Types.Class.Minifiable Hasmin.Types.FilterFunction.FilterFunction
+ Hasmin.Types.FilterFunction: instance Hasmin.Types.Class.ToText Hasmin.Types.FilterFunction.FilterFunction
+ Hasmin.Types.FilterFunction: minifyPseudoShadow :: (Minifiable b, Minifiable t1, Minifiable t2, Traversable t) => (t2 -> t1 -> Maybe Distance -> t b -> b1) -> t2 -> t1 -> Maybe Distance -> t b -> ReaderT * Config Identity b1
+ Hasmin.Types.Position: PosBottom :: PosKeyword
+ Hasmin.Types.Position: PosCenter :: PosKeyword
+ Hasmin.Types.Position: PosLeft :: PosKeyword
+ Hasmin.Types.Position: PosRight :: PosKeyword
+ Hasmin.Types.Position: PosTop :: PosKeyword
+ Hasmin.Types.Position: Position :: Maybe PosKeyword -> Maybe PercentageLength -> Maybe PosKeyword -> Maybe PercentageLength -> Position
+ Hasmin.Types.Position: [offset1] :: Position -> Maybe PercentageLength
+ Hasmin.Types.Position: [offset2] :: Position -> Maybe PercentageLength
+ Hasmin.Types.Position: [origin1] :: Position -> Maybe PosKeyword
+ Hasmin.Types.Position: [origin2] :: Position -> Maybe PosKeyword
+ Hasmin.Types.Position: data PosKeyword
+ Hasmin.Types.Position: data Position
+ Hasmin.Types.Position: instance GHC.Classes.Eq Hasmin.Types.Position.PosKeyword
+ Hasmin.Types.Position: instance GHC.Classes.Eq Hasmin.Types.Position.Position
+ Hasmin.Types.Position: instance GHC.Show.Show Hasmin.Types.Position.PosKeyword
+ Hasmin.Types.Position: instance GHC.Show.Show Hasmin.Types.Position.Position
+ Hasmin.Types.Position: instance Hasmin.Types.Class.Minifiable Hasmin.Types.Position.Position
+ Hasmin.Types.Position: instance Hasmin.Types.Class.ToText Hasmin.Types.Position.PosKeyword
+ Hasmin.Types.Position: instance Hasmin.Types.Class.ToText Hasmin.Types.Position.Position
+ Hasmin.Types.Position: l0 :: Maybe PercentageLength
+ Hasmin.Types.Position: minifyPosition :: Position -> Position
+ Hasmin.Types.Position: p50 :: Maybe PercentageLength
+ Hasmin.Types.RepeatStyle: RSPair :: RSKeyword -> (Maybe RSKeyword) -> RepeatStyle
+ Hasmin.Types.RepeatStyle: RepeatX :: RepeatStyle
+ Hasmin.Types.RepeatStyle: RepeatY :: RepeatStyle
+ Hasmin.Types.RepeatStyle: RsNoRepeat :: RSKeyword
+ Hasmin.Types.RepeatStyle: RsRepeat :: RSKeyword
+ Hasmin.Types.RepeatStyle: RsRound :: RSKeyword
+ Hasmin.Types.RepeatStyle: RsSpace :: RSKeyword
+ Hasmin.Types.RepeatStyle: data RSKeyword
+ Hasmin.Types.RepeatStyle: data RepeatStyle
+ Hasmin.Types.RepeatStyle: instance GHC.Classes.Eq Hasmin.Types.RepeatStyle.RSKeyword
+ Hasmin.Types.RepeatStyle: instance GHC.Classes.Eq Hasmin.Types.RepeatStyle.RepeatStyle
+ Hasmin.Types.RepeatStyle: instance GHC.Show.Show Hasmin.Types.RepeatStyle.RSKeyword
+ Hasmin.Types.RepeatStyle: instance GHC.Show.Show Hasmin.Types.RepeatStyle.RepeatStyle
+ Hasmin.Types.RepeatStyle: instance Hasmin.Types.Class.Minifiable Hasmin.Types.RepeatStyle.RepeatStyle
+ Hasmin.Types.RepeatStyle: instance Hasmin.Types.Class.ToText Hasmin.Types.RepeatStyle.RSKeyword
+ Hasmin.Types.RepeatStyle: instance Hasmin.Types.Class.ToText Hasmin.Types.RepeatStyle.RepeatStyle
+ Hasmin.Types.TimingFunction: CubicBezier :: Number -> Number -> Number -> Number -> TimingFunction
+ Hasmin.Types.TimingFunction: Ease :: TimingFunction
+ Hasmin.Types.TimingFunction: EaseIn :: TimingFunction
+ Hasmin.Types.TimingFunction: EaseInOut :: TimingFunction
+ Hasmin.Types.TimingFunction: EaseOut :: TimingFunction
+ Hasmin.Types.TimingFunction: End :: StepsSecondParam
+ Hasmin.Types.TimingFunction: Linear :: TimingFunction
+ Hasmin.Types.TimingFunction: Start :: StepsSecondParam
+ Hasmin.Types.TimingFunction: StepEnd :: TimingFunction
+ Hasmin.Types.TimingFunction: StepStart :: TimingFunction
+ Hasmin.Types.TimingFunction: Steps :: Int -> (Maybe StepsSecondParam) -> TimingFunction
+ Hasmin.Types.TimingFunction: data StepsSecondParam
+ Hasmin.Types.TimingFunction: data TimingFunction
+ Hasmin.Types.TimingFunction: instance GHC.Classes.Eq Hasmin.Types.TimingFunction.StepsSecondParam
+ Hasmin.Types.TimingFunction: instance GHC.Classes.Eq Hasmin.Types.TimingFunction.TimingFunction
+ Hasmin.Types.TimingFunction: instance GHC.Show.Show Hasmin.Types.TimingFunction.StepsSecondParam
+ Hasmin.Types.TimingFunction: instance GHC.Show.Show Hasmin.Types.TimingFunction.TimingFunction
+ Hasmin.Types.TimingFunction: instance Hasmin.Types.Class.Minifiable Hasmin.Types.TimingFunction.TimingFunction
+ Hasmin.Types.TimingFunction: instance Hasmin.Types.Class.ToText Hasmin.Types.TimingFunction.StepsSecondParam
+ Hasmin.Types.TimingFunction: instance Hasmin.Types.Class.ToText Hasmin.Types.TimingFunction.TimingFunction
Files
- hasmin.cabal +15/−12
- src/Hasmin/Parser/Internal.hs +3/−3
- src/Hasmin/Types/BgSize.hs +18/−4
- src/Hasmin/Types/Dimension.hs +7/−8
- src/Hasmin/Types/FilterFunction.hs +30/−12
- src/Hasmin/Types/Position.hs +22/−9
- src/Hasmin/Types/RepeatStyle.hs +5/−1
- src/Hasmin/Types/Selector.hs +46/−35
- src/Hasmin/Types/Shadow.hs +1/−0
- src/Hasmin/Types/TimingFunction.hs +14/−8
- tests/Hasmin/Parser/InternalSpec.hs +25/−16
- tests/Hasmin/TestUtils.hs +126/−3
- tests/Hasmin/Types/BgSizeSpec.hs +35/−0
- tests/Hasmin/Types/ColorSpec.hs +13/−58
- tests/Hasmin/Types/DeclarationSpec.hs +9/−10
- tests/Hasmin/Types/DimensionSpec.hs +30/−9
- tests/Hasmin/Types/FilterFunctionSpec.hs +22/−15
- tests/Hasmin/Types/GradientSpec.hs +3/−8
- tests/Hasmin/Types/PositionSpec.hs +11/−6
- tests/Hasmin/Types/RepeatStyleSpec.hs +19/−5
- tests/Hasmin/Types/SelectorSpec.hs +5/−5
- tests/Hasmin/Types/ShadowSpec.hs +0/−2
- tests/Hasmin/Types/StringSpec.hs +2/−4
- tests/Hasmin/Types/StylesheetSpec.hs +3/−4
- tests/Hasmin/Types/TimingFunctionSpec.hs +23/−16
- tests/Hasmin/Types/TransformFunctionSpec.hs +9/−10
- tests/Hasmin/Types/ValueSpec.hs +42/−0
hasmin.cabal view
@@ -1,11 +1,11 @@ name: hasmin -version: 0.3.1.3 +version: 0.3.2 license: BSD3 license-file: LICENSE author: (c) 2017 Cristian Adrián Ontivero <cristianontivero@gmail.com> maintainer: Cristian Adrián Ontivero <cristianontivero@gmail.com> synopsis: "A CSS Minifier" -homepage: https://github.com/contivero/hasmin/ +homepage: https://github.com/contivero/hasmin#readme bug-reports: https://github.com/contivero/hasmin/issues category: Text build-type: Simple @@ -13,9 +13,10 @@ cabal-version: >=1.10 description: A CSS minifier which not only aims at reducing the amount of bytes of the - output, but also at improving gzip compression. By default, the output is the - minified CSS file, but hasmin allows also its compression into gzip using - Google's Zopfli library. + output, but also at improving gzip compression. It may be used as a library, + or a stand-alone executable. For the library, refer to the Hasmin module + documentation. For the program: the output is the minified CSS file, but + hasmin allows also its compression into gzip using Google's Zopfli library. . To use it: ./hasmin input.css > output.css . @@ -37,9 +38,9 @@ , optparse-applicative >=0.11 && <0.14 , parsers >=0.12.3 && <0.13 , text >=1.2 && <1.3 - , hopfli >=0.2 && <0.3 + , hopfli >=0.2 && <0.4 , bytestring >=0.10.2.0 && <0.11 - , gitrev >=1.0.0 && <=1.2.0 + , gitrev >=1.0.0 && <1.4 , matrix >=0.3.4 && <0.4 , mtl >=2.2.1 && <2.3 , numbers >=3000.2.0.0 && <3000.3 @@ -62,19 +63,19 @@ , Hasmin.Types.Numeric , Hasmin.Types.TransformFunction , Hasmin.Types.Value + , Hasmin.Types.RepeatStyle + , Hasmin.Types.FilterFunction + , Hasmin.Types.Position + , Hasmin.Types.BgSize , Hasmin.Utils + , Hasmin.Types.TimingFunction other-modules: Hasmin.Parser.Utils , Hasmin.Properties , Hasmin.Types.Selector - , Hasmin.Types.BgSize - , Hasmin.Types.FilterFunction , Hasmin.Types.Gradient , Hasmin.Types.PercentageLength - , Hasmin.Types.Position - , Hasmin.Types.RepeatStyle , Hasmin.Types.Shadow , Hasmin.Types.String - , Hasmin.Types.TimingFunction build-depends: base >=4.9 && <5.1 , attoparsec >=0.12 && <0.14 , bytestring >=0.10.2.0 && <0.11 @@ -103,6 +104,8 @@ , Hasmin.Types.PositionSpec , Hasmin.Types.RepeatStyleSpec , Hasmin.Types.SelectorSpec + , Hasmin.Types.ValueSpec + , Hasmin.Types.BgSizeSpec , Hasmin.Types.ShadowSpec , Hasmin.Types.StringSpec , Hasmin.Types.StylesheetSpec
src/Hasmin/Parser/Internal.hs view
@@ -171,12 +171,12 @@ s <- option Nothing (Just <$> parseSign) x <- option mempty digits case x of- [] -> ciN *> (AB (Nwith s Nothing) <$> (skipComments *> option Nothing (Just <$> bValue)))+ [] -> ciN *> skipComments *> option (A s Nothing) (AB s Nothing <$> bValue) _ -> do n <- option False (ciN $> True) let a = read x :: Int if n- then AB (Nwith s (Just a)) <$> (skipComments *> option Nothing (Just <$> bValue))- else pure $ AB NoValue (Just $ getSign s * a)+ then skipComments *> option (A s (Just a)) (AB s (Just a) <$> bValue)+ else pure $ B (getSign s * a) where ciN = satisfy (\c -> c == 'N' || c == 'n') parseSign = (char '-' $> Minus) <|> (char '+' $> Plus) getSign (Just Minus) = -1
src/Hasmin/Types/BgSize.hs view
@@ -9,11 +9,14 @@ -- ----------------------------------------------------------------------------- module Hasmin.Types.BgSize (- BgSize(..), Auto(..)+ BgSize(..)+ , Auto(..) ) where+ import Control.Monad.Reader (ask) import Data.Monoid ((<>)) import Data.Text.Lazy.Builder (singleton)+ import Hasmin.Types.Class import Hasmin.Types.PercentageLength @@ -26,8 +29,20 @@ data BgSize = Cover | Contain | BgSize (Either PercentageLength Auto) (Maybe (Either PercentageLength Auto))- deriving (Eq, Show)+ deriving Show +instance Eq BgSize where+ Cover == Cover = True+ Contain == Contain = True+ BgSize a b == BgSize c d = ftsArgEq a c && b `equals` d+ where equals (Just (Right Auto)) Nothing = True+ equals Nothing (Just (Right Auto)) = True+ equals (Just (Left x)) (Just (Left y)) = isZero x && isZero y || x == y+ equals x y = x == y+ ftsArgEq (Left x) (Left y) = isZero x && isZero y || x == y+ ftsArgEq x y = x == y+ _ == _ = False+ instance ToText BgSize where toBuilder Cover = "cover" toBuilder Contain = "contain"@@ -42,11 +57,10 @@ pure $ if True {- shouldMinifyBgSize conf -} then minifyBgSize b else b- where minFirst (Left a) = Left <$> minifyWith a+ where minFirst (Left a) = Left <$> minifyWith a minFirst (Right Auto) = pure (Right Auto) minifyWith x = pure x minifyBgSize :: BgSize -> BgSize minifyBgSize (BgSize l (Just (Right Auto))) = BgSize l Nothing minifyBgSize x = x-
src/Hasmin/Types/Dimension.hs view
@@ -32,7 +32,6 @@ import Control.Monad.Reader (asks) import Data.Monoid ((<>)) import Data.Text.Lazy.Builder (singleton, fromText)-import Data.Typeable (Typeable) import Hasmin.Types.Class import Hasmin.Types.Numeric import Hasmin.Config@@ -134,13 +133,13 @@ -- the shortest equivalent representation. If there is more than one, returns -- the latest found to "normalize" values, hopefully improving gzip compression. minDim :: (Unit a, ToText a) => (Number -> a -> b) -> Number -> a -> [a] -> b+minDim constructor r u [] = constructor r u minDim constructor r u (x:xs) | currentLength < newLength = minDim constructor r u xs | otherwise = minDim constructor equivValue x xs where equivValue = convertTo x r u currentLength = textualLength r + textualLength u newLength = textualLength equivValue + textualLength x-minDim constructor r u [] = constructor r u class Unit a where convertTo :: a -> Number -> a -> Number@@ -175,7 +174,7 @@ convertTo _ = const data AngleUnit = Deg | Grad | Rad | Turn- deriving (Show, Eq, Typeable)+ deriving (Show, Eq) instance ToText AngleUnit where toBuilder Deg = "deg" toBuilder Grad = "grad"@@ -254,7 +253,7 @@ toQuarterMilimeter d _ = d toPoints :: Number -> DistanceUnit -> Number-toPoints d IN = d / 72+toPoints d IN = d * 72 toPoints d CM = d * (72 / 2.54) toPoints d MM = d * (72 / 25.4) toPoints d Q = d * (72 / 101.6)@@ -263,7 +262,7 @@ toPoints d _ = d toPica :: Number -> DistanceUnit -> Number-toPica d IN = d / 6+toPica d IN = d * 6 toPica d CM = d * (6 / 2.54) toPica d MM = d * (6 / 25.4) toPica d Q = d * (6 / 101.6)@@ -277,7 +276,7 @@ toPixels d MM = d * (96 / 25.4) toPixels d Q = d * (96 / 101.6) toPixels d PT = d * (4 / 3)-toPixels d PC = d / 6+toPixels d PC = d * 16 toPixels d _ = d ------------------------------------------------------------------------------ rationalPi :: Number@@ -331,9 +330,9 @@ toDpcm :: Number -> ResolutionUnit -> Number toDpcm d Dpi = d / 2.54 toDpcm d Dpcm = d-toDpcm d Dppx = d * (2.54 / 96)+toDpcm d Dppx = d * (96 / 2.54) toDppx :: Number -> ResolutionUnit -> Number toDppx d Dpi = d / 96-toDppx d Dpcm = d * (96 / 2.54)+toDppx d Dpcm = d * (2.54 / 96) toDppx d Dppx = d
src/Hasmin/Types/FilterFunction.hs view
@@ -38,8 +38,23 @@ | Sepia (Either Number Percentage) | HueRotate Angle | DropShadow Distance Distance (Maybe Distance) (Maybe Color)- deriving (Eq, Show)-+ deriving (Show)+instance Eq FilterFunction where+ Blur a == Blur b = a == b+ Brightness a == Brightness b = filterFunctionEquality a b+ Contrast a == Contrast b = filterFunctionEquality a b+ Grayscale a == Grayscale b = filterFunctionEquality a b+ Invert a == Invert b = filterFunctionEquality a b+ Opacity a == Opacity b = filterFunctionEquality a b+ Saturate a == Saturate b = filterFunctionEquality a b+ Sepia a == Sepia b = filterFunctionEquality a b+ HueRotate a == HueRotate b = a == b+ DropShadow a b c d == DropShadow e f g h =+ a == e && b == f && d == h && c `thirdValueEq` g+ where thirdValueEq Nothing (Just (Distance 0 _)) = True+ thirdValueEq (Just (Distance 0 _)) Nothing = True+ thirdValueEq x y = x == y+ _ == _ = False instance ToText FilterFunction where toBuilder (Blur d) = "blur(" <> toBuilder d <> singleton ')' toBuilder (Brightness np) = "brightness(" <> toBuilder np <> singleton ')'@@ -55,7 +70,6 @@ maybeToBuilder = maybe mempty (\x -> singleton ' ' <> toBuilder x) in "drop-shadow(" <> toBuilder l1 <> singleton ' ' <> toBuilder l2 <> maybeToBuilder ml <> maybeToBuilder mc <> singleton ')'- instance Minifiable FilterFunction where minifyWith (Blur a) = Blur <$> minifyWith a minifyWith (HueRotate a) = HueRotate <$> minifyWith a@@ -90,14 +104,18 @@ pure $ if shouldMinifyFilterFunctions conf then either convertNumber convertPercentage x else x+ where convertNumber n+ | 0 < n && n < 0.1 = Right $ toPercentage (n * 100)+ | otherwise = Left n+ convertPercentage p+ | p == 0 = Left 0+ | 0 < p && p < 10 = Right p+ | otherwise = Left $ toNumber (p / 100) -convertNumber :: Number -> Either Number Percentage-convertNumber x- | 0 < x && x < 0.1 = Right $ toPercentage (x * 100)- | otherwise = Left x+filterFunctionEquality :: Either Number Percentage+ -> Either Number Percentage -> Bool+filterFunctionEquality (Left a) (Left b) = toRational a == toRational b+filterFunctionEquality (Right a) (Right b) = toRational a == toRational b+filterFunctionEquality (Left a) (Right b) = toRational a == toRational b/100+filterFunctionEquality (Right a) (Left b) = toRational a/100 == toRational b -convertPercentage :: Percentage -> Either Number Percentage-convertPercentage p- | p == 0 = Left 0- | 0 < p && p < 10 = Right p- | otherwise = Left $ toNumber (p / 100)
src/Hasmin/Types/Position.hs view
@@ -9,7 +9,11 @@ -- ----------------------------------------------------------------------------- module Hasmin.Types.Position (- Position(..), PosKeyword(..), minifyPosition, p50, l0+ Position(..)+ , PosKeyword(..)+ , minifyPosition+ , p50+ , l0 ) where import Data.Text (Text)@@ -21,15 +25,12 @@ import Hasmin.Types.PercentageLength import Hasmin.Utils -data Position = Position { origin1 :: Maybe PosKeyword- , offset1 :: Maybe PercentageLength- , origin2 :: Maybe PosKeyword- , offset2 :: Maybe PercentageLength- } deriving (Eq, Show)--data PosKeyword = PosCenter | PosLeft | PosRight | PosTop | PosBottom+data PosKeyword = PosCenter + | PosLeft+ | PosRight + | PosTop+ | PosBottom deriving (Eq, Show)- instance ToText PosKeyword where toBuilder PosCenter = "center" toBuilder PosTop = "top"@@ -37,6 +38,13 @@ toBuilder PosBottom = "bottom" toBuilder PosLeft = "left" +-- TODO turn this into a proper algebraic type with all the cases, avoding Maybe+-- and making invalid Position values impossible.+data Position = Position { origin1 :: Maybe PosKeyword+ , offset1 :: Maybe PercentageLength+ , origin2 :: Maybe PosKeyword+ , offset2 :: Maybe PercentageLength+ } deriving (Show) instance Minifiable Position where minifyWith p = pure $ minifyPosition p @@ -203,6 +211,11 @@ | isZero x = (Just PosBottom, Nothing) | otherwise = (Just PosBottom, Just x) minAxis PosCenter x = (Just PosCenter, Just x)++instance Eq Position where+ a == b = minify a `equals` minify b+ where equals (Position a b c d) (Position e f g h) =+ a == e && b == f && c == g && d == h l0 :: Maybe PercentageLength l0 = Just (Right (Distance 0 Q))
src/Hasmin/Types/RepeatStyle.hs view
@@ -54,9 +54,13 @@ RSPair RsRepeat (Just RsRepeat) == RSPair RsRepeat (Just RsRepeat) = True RSPair x Nothing == RSPair y Nothing = x == y a@(RSPair _ Nothing) == b@(RSPair _ _) = b == a+ RSPair x y == RSPair z w = x == z && y == w _ == _ = False -data RSKeyword = RsRepeat | RsSpace | RsRound | RsNoRepeat+data RSKeyword = RsRepeat + | RsSpace+ | RsRound+ | RsNoRepeat deriving (Eq, Show) instance ToText RSKeyword where toBuilder RsRepeat = "repeat"
src/Hasmin/Types/Selector.hs view
@@ -16,7 +16,6 @@ , Combinator(..) , Sign(..) , AnPlusB(..)- , AValue(..) , Att(..) , specialPseudoElements ) where@@ -198,19 +197,9 @@ Left _ -> a Right s -> Lang (removeQuotes s) else a- minifyWith a@(FunctionalPseudoClass2 i n) = do- conf <- ask- pure $ if shouldMinifyMicrosyntax conf- then FunctionalPseudoClass2 i (minifyAnPlusB n)- else a- minifyWith a@(FunctionalPseudoClass3 i n cs) = do- conf <- ask- pure $ if shouldMinifyMicrosyntax conf- then FunctionalPseudoClass3 i (minifyAnPlusB n) cs- else a- minifyWith (FunctionalPseudoClass1 i cs) = do- newcs <- mapM minifyWith cs- pure $ FunctionalPseudoClass1 i newcs+ minifyWith (FunctionalPseudoClass1 i cs) = FunctionalPseudoClass1 i <$> mapM minifyWith cs+ minifyWith (FunctionalPseudoClass2 i n) = FunctionalPseudoClass2 i <$> minifyWith n+ minifyWith (FunctionalPseudoClass3 i n cs) = FunctionalPseudoClass3 i <$> minifyWith n <*> pure cs minifyWith x = pure x data Sign = Plus | Minus@@ -225,7 +214,7 @@ toBuilder Plus = singleton '+' toBuilder Minus = singleton '-' -data AValue = Nwith (Maybe Sign) (Maybe Int) -- at least a lone 'n'+{- data AValue = Nwith (Maybe Sign) (Maybe Int) -- at least a lone 'n' | NoValue -- The "An" part is omitted. deriving (Eq, Show) instance ToText AValue where@@ -243,6 +232,7 @@ then Nothing else Just x minifyAValue NoValue = NoValue+-} -- We could maybe model the AB constructor with an Either, -- to make sure AB NoValue Nothing isn't possible (which is invalid).@@ -250,36 +240,57 @@ -- for example +6 vs 6, -0 vs 0 vs +0. data AnPlusB = Even | Odd- | AB AValue (Maybe Int)+ | A (Maybe Sign) (Maybe Int) -- "sign n number", e.g. +3n, -2n, 1n.+ | B Int -- "sign number", e.g. +1, +2, 3.+ | AB (Maybe Sign) (Maybe Int) Int -- "sign n number sign number", e.g. 2n+1 deriving (Eq, Show) instance ToText AnPlusB where- toBuilder Even = "even"- toBuilder Odd = "odd"- toBuilder (AB a b) = toBuilder a <> bToBuilder b- where bToBuilder- | a == NoValue = maybe (singleton '0') toBuilder- | otherwise = maybe mempty (\x -> bSign x <> toBuilder x)- bSign x+ toBuilder Even = "even"+ toBuilder Odd = "odd"+ toBuilder (B b) = toBuilder b+ toBuilder (A ms mi) = an2Builder ms mi+ toBuilder (AB ms mi b) = an2Builder ms mi <> bSign b <> toBuilder b+ where bSign x | x < 0 = mempty | otherwise = singleton '+' +-- Used to print "An" values+an2Builder :: Maybe Sign -> Maybe Int -> Builder+an2Builder ms mi = maybeToBuilder ms <> maybeToBuilder mi <> singleton 'n'+ where maybeToBuilder :: ToText a => Maybe a -> Builder+ maybeToBuilder = maybe mempty toBuilder++instance Minifiable AnPlusB where+ minifyWith x = do+ conf <- ask+ pure $ if shouldMinifyMicrosyntax conf+ then minifyAnPlusB x+ else x+ minifyAnPlusB :: AnPlusB -> AnPlusB-minifyAnPlusB Even = AB (Nwith Nothing (Just 2)) Nothing-minifyAnPlusB (AB n@(Nwith s a) (Just b))- | isPositive s && a == Just 2 =- if b == 1 || odd b && b < 0+minifyAnPlusB Even = A Nothing (Just 2)+minifyAnPlusB (A ms mi) =+ case mi of+ Just 0 -> B 0+ _ -> uncurry A (minifyAN ms mi)+minifyAnPlusB (AB _ (Just 0) b) = B b+minifyAnPlusB (AB ms mi b)+ | isPositive ms && mi == Just 2 =+ if b == 1 || b < 0 && odd b then Odd else if even b && b <= 0 then minifyAnPlusB Even- else AB (minifyAValue n) (Just b)- | otherwise = AB (minifyAValue n) $ if b == 0- then Nothing- else Just b-minifyAnPlusB (AB n@Nwith{} Nothing) = AB (minifyAValue n) Nothing+ else AB ms' mi' b+ | otherwise = if b == 0+ then A ms' mi'+ else AB ms' mi' b+ where (ms', mi') = minifyAN ms mi minifyAnPlusB x = x--- instance Specificity SimpleSelector where- -- specificity (IdSel _) = (0,1,0,0)- -- specificity (ClassSel _) = (0,0,1,0)++minifyAN :: Maybe Sign -> Maybe Int -> (Maybe Sign, Maybe Int)+minifyAN (Just Plus) i = minifyAN Nothing i+minifyAN s (Just 1) = minifyAN s Nothing+minifyAN s i = (s, i) type AttId = Text type AttValue = Either Text StringType
src/Hasmin/Types/Shadow.hs view
@@ -16,6 +16,7 @@ import Data.Monoid ((<>)) import Data.Text.Lazy.Builder (singleton) import Data.Bool (bool)+ import Hasmin.Types.Class import Hasmin.Types.Color import Hasmin.Types.Dimension
src/Hasmin/Types/TimingFunction.hs view
@@ -9,7 +9,8 @@ -- ----------------------------------------------------------------------------- module Hasmin.Types.TimingFunction (- TimingFunction(..), StepsSecondParam(..)+ TimingFunction(..)+ , StepsSecondParam(..) ) where import Control.Monad.Reader (ask)@@ -29,8 +30,13 @@ -- 3. <https://developer.mozilla.org/en-US/docs/Web/CSS/timing-function Mozilla summary> data TimingFunction = CubicBezier Number Number Number Number | Steps Int (Maybe StepsSecondParam)- | Ease | EaseIn | EaseInOut | EaseOut- | Linear | StepEnd | StepStart+ | Ease+ | EaseIn+ | EaseInOut+ | EaseOut+ | Linear+ | StepEnd+ | StepStart deriving (Show) instance Eq TimingFunction where@@ -66,12 +72,12 @@ toSteps _ = Nothing -data StepsSecondParam = Start | End -- End is the default value+data StepsSecondParam = Start+ | End -- End is the default value deriving (Eq, Show) instance ToText StepsSecondParam where toBuilder Start = "start" toBuilder End = "end"- instance ToText TimingFunction where toBuilder (CubicBezier a b c d) = "cubic-bezier(" <> mconcatIntersperse toBuilder (singleton ',') [a,b,c,d]@@ -89,9 +95,9 @@ instance Minifiable TimingFunction where minifyWith x = do conf <- ask- if shouldMinifyTimingFunctions conf- then pure $ minifyTimingFunction x- else pure x+ pure $ if shouldMinifyTimingFunctions conf+ then minifyTimingFunction x+ else x minifyTimingFunction :: TimingFunction -> TimingFunction minifyTimingFunction x@(CubicBezier a b c 1)
tests/Hasmin/Parser/InternalSpec.hs view
@@ -17,7 +17,7 @@ mapM_ (matchSpecWithDesc declaration) declarationTestInfo declarationTestInfo :: [(String, Text, Text)]-declarationTestInfo = +declarationTestInfo = [("parses declaration with unknown property and value, ending in ';'", "a:b;", "a:b") ,("parses declaration with unknown property and value, ending in '}'",@@ -35,8 +35,8 @@ ] declarationParsersTests :: Spec-declarationParsersTests = - describe "Declaration parsers tests" $ do+declarationParsersTests =+ describe "Declaration parsers tests" $ it "Parses valid font-style values, and fails with invalids" $ do fontStyle `shouldSucceedOn` ("normal" :: Text) fontStyle `shouldSucceedOn` ("italic" :: Text)@@ -47,28 +47,35 @@ styleRuleParser :: Spec describe "style rule parser" $ do it "parses rule whose final declaration ends in ';'" $ do- "a { margin : 0;}" ~> (styleRule + "a { margin : 0;}" ~> (styleRule -} styleRuleTestInfo :: [(String, Text, Text)]-styleRuleTestInfo = +styleRuleTestInfo = [("simple style rule", "a { margin : 0 ;}", "a{margin:0}")- ,("handles unset", + ,("handles unset", "h1 {\n border: unset\n}", "h1{border:unset}")- ,("handles initial", + ,("handles initial", "camelCase {\r color: initial;}", "camelCase{color:initial}")- ,("handles inherit", + ,("handles inherit", "h1 {\n\tpadding: inherit\f}", "h1{padding:inherit}") ] selectorParserTests :: Spec selectorParserTests =- describe "selector parser tests" $- mapM_ (matchSpecWithDesc selector) selectorTestInfo+ 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 = +selectorTestInfo = [("Element", "h6", "h6") ,("Element with namespace", "ns|body", "ns|body") ,("Element with any namespace", "*|p", "*|p")@@ -78,9 +85,10 @@ --,("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 starting with a number", "#\\5\\#-\\.5", "#5#-.5")+ -- ,("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")@@ -146,9 +154,9 @@ ,("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") + "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") + "E:visited", "E:visited") ,("an E element during active action", "E:active","E:active") ,("an E element during hover action",@@ -196,11 +204,12 @@ -- mapM_ (matchSpecWithDesc stylesheet) stylesheetTestInfo --stylesheetTestInfo :: [(String, Text, Text)]---stylesheetTestInfo = +--stylesheetTestInfo = spec :: Spec spec = do declarationParserTests selectorParserTests+ selectorParserFailures declarationParsersTests main :: IO ()
tests/Hasmin/TestUtils.hs view
@@ -1,16 +1,35 @@ {-# LANGUAGE OverloadedStrings #-} -module Hasmin.TestUtils where+module Hasmin.TestUtils (+ module Hasmin.TestUtils+ , module Test.QuickCheck+ , module Test.Hspec+ ) where import Test.Hspec+import Test.QuickCheck+import Test.Hspec.Attoparsec (parseSatisfies, (~>)) import Data.Text (Text, unpack, singleton)-import Test.Hspec.Attoparsec (parseSatisfies, (~>)) import Data.Attoparsec.Text (Parser)+import Control.Applicative (liftA2, liftA3)+import Control.Monad (liftM4)++import Hasmin.Types.BgSize import Hasmin.Types.Class+import Hasmin.Types.Color import Hasmin.Types.Declaration+import Hasmin.Types.Dimension+import Hasmin.Types.FilterFunction+import Hasmin.Types.Numeric+import Hasmin.Types.Position+import Hasmin.Types.TimingFunction import Hasmin.Utils +-- | Check that a color is equivalent to their minified representation form+prop_minificationEq :: (Minifiable a, Eq a) => a -> Bool+prop_minificationEq d = minify d == d+ -- Given a parser and a 3-tuple, prints a test description, -- applies the parser, and compares its result with the expected result matchSpecWithDesc :: ToText a => Parser a -> (String, Text, Text) -> Spec@@ -22,7 +41,111 @@ matchSpec parser (textToParse, expectedResult) = it (unpack textToParse) $ (toText <$> (textToParse ~> parser)) `parseSatisfies` (== expectedResult) -data Declarations = Declarations [Declaration]+mkGen :: [a] -> Gen a+mkGen xs = (xs !!) <$> choose (0, length xs - 1)++newtype Declarations = Declarations [Declaration] instance ToText Declarations where toText (Declarations ds) = mconcatIntersperse toText (singleton ';') ds++instance Arbitrary Distance where+ arbitrary = liftA2 Distance arbitrary distanceUnit+ -- TODO keep it DRY, avoid repeating constructor list here (i.e. SPOF)+ where distanceUnit = mkGen [IN, CM, MM, Q, PC, PT, PX, EM, EX,+ CH, VH, VW, VMIN, VMAX, REM]++instance Arbitrary Angle where+ arbitrary = liftA2 Angle arbitrary angleUnit+ where angleUnit = mkGen [Deg, Grad, Rad, Turn]++instance Arbitrary Duration where+ arbitrary = liftA2 Duration arbitrary durationUnit+ where durationUnit = mkGen [S, Ms]++instance Arbitrary Frequency where+ arbitrary = liftA2 Frequency arbitrary frequencyUnit+ where frequencyUnit = mkGen [Hz, Khz]++instance Arbitrary Resolution where+ arbitrary = liftA2 Resolution arbitrary resolutionUnit+ where resolutionUnit = mkGen [Dpi, Dpcm, Dppx]++instance Arbitrary Number where+ arbitrary = toNumber <$> (arbitrary :: Gen Rational)++instance Arbitrary PosKeyword where+ arbitrary = mkGen [PosCenter, PosLeft, PosRight, PosTop, PosBottom]++-- instance Arbitrary Position where+ -- arbitrary = oneof [ fmap (\x -> Position x Nothing Nothing Nothing) arbitrary ]++instance Arbitrary Percentage where+ arbitrary = fmap Percentage (arbitrary :: Gen Rational)++instance Arbitrary FilterFunction where+ arbitrary = oneof+ [ Blur <$> arbitrary+ , Brightness <$> arbitrary+ , Contrast <$> arbitrary+ , Grayscale <$> arbitrary+ , Invert <$> arbitrary+ , Opacity <$> arbitrary+ , Saturate <$> arbitrary+ , Sepia <$> arbitrary+ , HueRotate <$> arbitrary+ , liftM4 DropShadow arbitrary arbitrary arbitrary arbitrary+ ]++instance Arbitrary BgSize where+ arbitrary = liftA2 BgSize arbitrary arbitrary++instance Arbitrary Auto where+ arbitrary = pure Auto++instance Arbitrary StepsSecondParam where+ arbitrary = oneof [pure Start, pure End]++instance Arbitrary TimingFunction where+ arbitrary = oneof [ liftM4 CubicBezier arbitrary arbitrary arbitrary arbitrary+ , liftA2 Steps arbitrary arbitrary+ , pure Ease+ , pure EaseIn+ , pure EaseInOut+ , pure EaseOut+ , pure Linear+ , pure StepEnd+ , pure StepStart+ ]++instance Arbitrary Color where+ arbitrary = oneof [ fmap Named colorKeyword+ , liftA3 mkHex3 hexChar hexChar hexChar+ , liftA3 mkHex6 hexString hexString hexString+ , liftM4 mkHex4 hexChar hexChar hexChar hexChar+ , liftM4 mkHex8 hexString hexString hexString hexString+ , liftA3 mkRGBInt intRange intRange intRange+ , liftA3 mkRGBPer ratRange ratRange ratRange + , liftM4 mkRGBAInt intRange intRange intRange alphaRange+ , liftM4 mkRGBAPer ratRange ratRange ratRange alphaRange + , liftA3 mkHSL hueRange ratRange ratRange+ , liftM4 mkHSLA hueRange ratRange ratRange alphaRange + ]+ where intRange = choose (0, 255)+ ratRange = toPercentage <$> (choose (0, 100) :: Gen Float)+ alphaRange = toAlphavalue <$> (choose (0, 1) :: Gen Float)+ hueRange = choose (0, 360)++-- | Generates color keywords uniformly distributed +colorKeyword :: Gen Text+colorKeyword = mkGen $ fmap fst keywordColors++-- | Generates a hexadecimal character uniformly distributed +hexChar :: Gen Char+hexChar = mkGen hexadecimals++hexString :: Gen String+hexString = liftA2 (\x y -> [x,y]) hexChar hexChar++hexadecimals :: String+hexadecimals = "0123456789abcdef"
+ tests/Hasmin/Types/BgSizeSpec.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE OverloadedStrings #-}++module Hasmin.Types.BgSizeSpec where++import Data.Text (Text)+import Hasmin.Parser.Value+import Hasmin.Types.Class+import Hasmin.Types.BgSize+import Hasmin.TestUtils++quickcheckBgSize :: Spec+quickcheckBgSize =+ describe "Quickcheck tests for <bg-size>" .+ it "Minified <bg-size> maintains semantical equivalence" $+ property (prop_minificationEq :: BgSize -> Bool)++bgSizeTests :: Spec+bgSizeTests =+ describe "<bg-size> minification tests" $+ mapM_ (matchSpec f) bgSizeTestsInfo+ where f = minify <$> value++bgSizeTestsInfo :: [(Text, Text)]+bgSizeTestsInfo =+ [("cover" ,"cover")+ ,("contain", "contain")+ ]++spec :: Spec+spec = do+ quickcheckBgSize+ bgSizeTests ++main :: IO ()+main = hspec spec
tests/Hasmin/Types/ColorSpec.hs view
@@ -2,72 +2,34 @@ module Hasmin.Types.ColorSpec where -import Test.Hspec-import Test.QuickCheck-import Hasmin.Parser.Value-import Hasmin.TestUtils--import Test.Hspec.Attoparsec (parseSatisfies, (~>))-import Data.Text (Text)+import Control.Applicative (liftA2)+import Control.Monad import Data.Foldable import Data.Maybe (fromJust)-import Control.Monad-import Hasmin.Types.Color+import Data.Text (Text)+import Test.Hspec.Attoparsec (parseSatisfies, (~>))++import Hasmin.Parser.Value+import Hasmin.TestUtils import Hasmin.Types.Class+import Hasmin.Types.Color import Hasmin.Types.Numeric -instance Arbitrary Color where- arbitrary = oneof [ fmap (fromJust . mkNamed) colorKeyword- , liftM3 mkHex3 hexChar hexChar hexChar- , liftM3 mkHex6 hexString hexString hexString- , liftM4 mkHex4 hexChar hexChar hexChar hexChar- , liftM4 mkHex8 hexString hexString hexString hexString- , liftM3 mkRGBInt intRange intRange intRange- , liftM3 mkRGBPer ratRange ratRange ratRange - , liftM4 mkRGBAInt intRange intRange intRange alphaRange- , liftM4 mkRGBAPer ratRange ratRange ratRange alphaRange - , liftM3 mkHSL hueRange ratRange ratRange- , liftM4 mkHSLA hueRange ratRange ratRange alphaRange - ]- where intRange = choose (0, 255)- ratRange = toPercentage <$> (choose (0, 100) :: Gen Float)- alphaRange = toAlphavalue <$> (choose (0, 1) :: Gen Float)- hueRange = choose (0, 360)---- | Generates color keywords uniformly distributed -colorKeyword :: Gen Text-colorKeyword = do- index <- choose (0, len)- pure $ keywords !! index- where len = length keywordColors - 1- keywords = fmap fst keywordColors---- | Generates a hexadecimal character uniformly distributed -hexChar :: Gen Char-hexChar = (hexadecimals !!) <$> choose (0, length hexadecimals - 1)--hexString :: Gen String-hexString = (\x y -> [x,y]) <$> hexChar <*> hexChar---- | Check that a color is equivalent to their minified representation form-prop_minificationEq :: Color -> Bool-prop_minificationEq c = c == minifyColor c- colorTests :: Spec colorTests =- describe "Color datatype tests" $- it "color minify semantically equivalent" $- quickCheck prop_minificationEq+ describe "<color> tests" .+ it "minified color is semantically equivalent" $+ property (prop_minificationEq :: Color -> Bool) colorParserTests :: Spec colorParserTests =- describe "Color Parser tests" $+ describe "Color Parser tests" . it "succeeds in parsing and minifying different red color representations" $ traverse_ ((`parseSatisfies` (==(fromJust $ mkNamed "red"))) . (~> (minifyColor <$> color))) redColor colorParserSpacesAndCommentsTests :: Spec colorParserSpacesAndCommentsTests =- describe "Color Parser test" $+ describe "Color Parser test" . it "succeeds in parsing different yellow color representations with spaces and comments in-between" $ traverse_ ((`parseSatisfies` (==(fromJust $ mkNamed "yellow"))) . (~> color)) commentsAndSpacesInColors @@ -83,13 +45,6 @@ , "hsla(0,100%,50%,1)", "hsla(0,100%,50%,1.0)" , "hsla(360,100%,50%,1)", "hsla(360,100%,50%,1.0)" ]---- | All 4096 (16^3) possible 3 char shorthands-allHex3 :: [String]-allHex3 = replicateM 3 hexadecimals--hexadecimals :: String-hexadecimals = "0123456789abcdef" -- Every color is yellow commentsAndSpacesInColors :: [Text]
tests/Hasmin/Types/DeclarationSpec.hs view
@@ -2,13 +2,12 @@ module Hasmin.Types.DeclarationSpec where -import Test.Hspec +import Data.Foldable (traverse_) +import Data.Monoid ((<>)) +import Data.Text (Text) + import Hasmin.Parser.Internal import Hasmin.TestUtils - -import Data.Text (Text) -import Data.Monoid ((<>)) -import Data.Foldable (traverse_) import Hasmin.Types.Class import Hasmin.Types.Declaration @@ -255,7 +254,7 @@ ] minifyDecTests :: Spec -minifyDecTests = do +minifyDecTests = describe "minifyDec function" $ mapM_ (matchSpec f) minifyDecTestsInfo where f = minify <$> declaration @@ -371,7 +370,7 @@ -- Tests for shorthands whose initial values are "medium none currentColor", -- and can go on any order. anyOrderShorthandTests :: Spec -anyOrderShorthandTests = do +anyOrderShorthandTests = describe "Shorthands that accept values in any order (mostly: medium none currentColor)" $ traverse_ (mapM_ (matchSpec (minify <$> declaration))) d where d = fmap f shorthandsToTest @@ -426,13 +425,13 @@ -- background-size: The first value gives the width of the corresponding image, -- the second value its height. Its initial value is 'auto', and it doesn't -- inherit. If only one value is given the second is assumed to be ‘auto’. - ,("background-size: 96px", "background-size:1in") + ,("background-size: 96px", "background-size:6pc") ,("background-size: auto", "background-size:auto") ,("background-size: unset", "background-size:auto") ,("background-size: initial", "background-size:auto") ,("background-size: auto auto", "background-size:auto") - ,("background-size: auto 96px", "background-size:auto 1in") - ,("background-size: 96px auto", "background-size:1in") + ,("background-size: auto 96px", "background-size:auto 6pc") + ,("background-size: 96px auto", "background-size:6pc") ] spec :: Spec
tests/Hasmin/Types/DimensionSpec.hs view
@@ -2,17 +2,32 @@ module Hasmin.Types.DimensionSpec where -import Test.Hspec---import Test.QuickCheck---import Hasmin.Parser.Internal hiding (property)+import Control.Applicative (liftA2) ---import Test.Hspec.Attoparsec (shouldParse, parseSatisfies, (~>)) import Hasmin.Types.Dimension+import Hasmin.Types.Numeric+import Hasmin.Types.Class+import Hasmin.TestUtils +dimensionTests :: Spec+dimensionTests =+ describe "Dimension tests with quickcheck" $ do+ it "Minified <length>s are equivalent to the original ones" $+ property (prop_minificationEq :: Distance -> Bool)+ it "Minified <angle>s are equivalent to the original ones" $+ property (prop_minificationEq :: Angle -> Bool)+ it "Minified <time>s are equivalent to the original ones" $+ property (prop_minificationEq :: Duration -> Bool)+ it "Minified <frequency>s are equivalent to the original ones" $+ property (prop_minificationEq :: Frequency -> Bool)+ it "Minified <resolution>s are equivalent to the original ones" $+ property (prop_minificationEq :: Resolution -> Bool)+ spec :: Spec-spec =- describe "<length> units equivalences" $ do- describe "inches conversions" $ do+spec = do+ dimensionTests+ describe "<length> units equivalences" $ do+ -- inches conversions it "1in == 2.54cm" $ Distance 1 IN `shouldBe` Distance 2.54 CM it "1in == 25.4cm" $@@ -25,7 +40,7 @@ Distance 1 IN `shouldBe` Distance 6 PC it "1in == 96px" $ Distance 1 IN `shouldBe` Distance 96 PX- describe "pixel conversions" $ do+ -- pixel conversions it "48px == .5in" $ Distance 48 PX `shouldBe` Distance 0.5 IN it "72px == 1.905cm" $@@ -38,7 +53,7 @@ Distance 12 PX `shouldBe` Distance 9 PT it "16px == 1pc" $ Distance 16 PX `shouldBe` Distance 1 PC- describe "centimeter conversions" $ do+ -- centimeter conversion it "5.08cm == 2in" $ Distance 5.08 CM `shouldBe` Distance 2 IN it "1cm == 10mm" $@@ -51,6 +66,12 @@ Distance 1.27 CM `shouldBe` Distance 3 PC it "1.27cm == 48px" $ Distance 1.27 CM `shouldBe` Distance 48 PX+ -- describe "<time> conversions" $ do+ -- it "" $ Duration 1 S `shouldBe` Duration 1000 Ms+ -- describe "<frequency> conversions" $ do+ -- it "" $ Frequency 1 Khz `shouldBe` Frequency 1000 Hz+ -- describe "<resolution> conversions" $ do+ -- it "" $ Resolution 96 Dpi `shouldBe` Resolution 1 Dppx main :: IO () main = hspec spec
tests/Hasmin/Types/FilterFunctionSpec.hs view
@@ -2,19 +2,24 @@ module Hasmin.Types.FilterFunctionSpec where -import Test.Hspec- import Data.Text (Text) import Hasmin.Parser.Value import Hasmin.Types.Class+import Hasmin.Types.FilterFunction import Hasmin.TestUtils filterTests :: Spec-filterTests = +filterTests = describe "<filter-function> minification tests" $ mapM_ (matchSpecWithDesc f) filterTestsInfo where f = minify <$> value- ++quickcheckFilter :: Spec+quickcheckFilter =+ describe "Quickcheck <filter-function> tests" $+ it "Minified <filter-function> maintains semantical equivalence" $+ property (prop_minificationEq :: FilterFunction -> Bool)+ filterTestsInfo :: [(String, Text, Text)] filterTestsInfo = [("Minifies the <color> value in drop-shadow()"@@ -35,22 +40,24 @@ ,("Minifies 100% in contrast()" ,"contrast(100%)" ,"contrast(1)")- ,("Minifies 0% in contrast()"- ,"contrast(0%)"- ,"contrast(0)")- ,("Minifies <percentage> multiple of 10 in brightness()"+ ,("Minifies 0% to 0"+ ,"grayscale(0%)"+ ,"grayscale(0)")+ ,("Minifies <percentage> multiple of 10" ,"brightness(50%)" ,"brightness(.5)")- ,("Normalizes <percentage> to <number> when > 10 in contrast()"- ,"contrast(11%)"- ,"contrast(.11)")- ,("Does not minify <percentage> when > 0 and < 10 in contrast()"- ,"contrast(8%)"- ,"contrast(8%)")+ ,("Normalizes <percentage> to <number> when > 10"+ ,"invert(11%)"+ ,"invert(.11)")+ ,("Does not minify <percentage> when > 0 and < 10"+ ,"opacity(8%)"+ ,"opacity(8%)") ] spec :: Spec-spec = filterTests+spec = do+ filterTests+ quickcheckFilter main :: IO () main = hspec spec
tests/Hasmin/Types/GradientSpec.hs view
@@ -2,14 +2,9 @@ module Hasmin.Types.GradientSpec where -import Test.Hspec--- import Test.QuickCheck--- import Hasmin.Parser.Internal---- import Test.Hspec.Attoparsec (shouldParse, parseSatisfies, (~>)) import Control.Monad.Reader (runReader) import Data.Text (Text)--- import Data.Attoparsec.Text (Parser)+ import Hasmin.Parser.Value import Hasmin.Types.Class import Hasmin.Config@@ -54,8 +49,8 @@ ,"linear-gradient(72deg, pink 3em, peru 3em)" ,"linear-gradient(72deg,pink 3em,peru 0)") ,("Reduces a <length> value to 0 when it is less than the greatest previous one"- ,"linear-gradient(72deg, pink 1in, peru 90px)" -- 1in == 96px- ,"linear-gradient(72deg,pink 1in,peru 0)")+ ,"linear-gradient(72deg, pink 6pc, peru 90px)" -- 6pc == 96px+ ,"linear-gradient(72deg,pink 6pc,peru 0)") ,("Removes default start and end <percentage> values" ,"linear-gradient(pink 0%, peru 100%)" ,"linear-gradient(pink,peru)")
tests/Hasmin/Types/PositionSpec.hs view
@@ -2,17 +2,22 @@ module Hasmin.Types.PositionSpec where -import Test.Hspec+import Data.Text (Text)+import Control.Monad (liftM4)+ import Hasmin.Parser.Value import Hasmin.TestUtils--import Data.Text (Text) import Hasmin.Types.Class+import Hasmin.Types.Position+import Hasmin.Types.Numeric positionMinificationTests :: Spec positionMinificationTests =- describe "position minification" $- mapM_ (matchSpec f) positionMinificationTestsInfo+ describe "<position> minification" $+ mapM_ (matchSpec f) positionMinificationTestsInfo+ -- it "Minifies <position> properly" $+ -- it "Minified <position> maintains semantical equivalence" $ do+ -- property (prop_minificationEq :: Position -> Bool) where f = minify <$> position positionMinificationTestsInfo :: [(Text, Text)]@@ -132,7 +137,7 @@ ] spec :: Spec-spec = do positionMinificationTests +spec = positionMinificationTests main :: IO () main = hspec spec
tests/Hasmin/Types/RepeatStyleSpec.hs view
@@ -2,19 +2,30 @@ module Hasmin.Types.RepeatStyleSpec where -import Test.Hspec- import Data.Text (Text)+import Control.Applicative (liftA2) import Hasmin.Parser.Value import Hasmin.Types.Class+import Hasmin.Types.RepeatStyle import Hasmin.TestUtils repeatStyleTests :: Spec-repeatStyleTests = - describe "<repeat-style> minification tests" $+repeatStyleTests =+ describe "<repeat-style> minification tests" $ do+ it "Minified <repeat-style> maintains semantic equivalence" $ + property (prop_minificationEq :: RepeatStyle -> Bool) mapM_ (matchSpec f) repeatStyleTestsInfo where f = minify <$> repeatStyle- ++instance Arbitrary RepeatStyle where+ arbitrary = frequency [(1, pure RepeatX)+ ,(1, pure RepeatY)+ ,(6, liftA2 RSPair arbitrary arbitrary)+ ]++instance Arbitrary RSKeyword where+ arbitrary = mkGen [RsRepeat, RsSpace, RsRound, RsNoRepeat]+ repeatStyleTestsInfo :: [(Text, Text)] repeatStyleTestsInfo = [("repeat no-repeat", "repeat-x")@@ -23,6 +34,9 @@ ,("repeat repeat", "repeat") ,("space space", "space") ,("round round", "round")+ ,("round space", "round space")+ ,("repeat-x", "repeat-x")+ ,("repeat-y", "repeat-y") ] spec :: Spec
tests/Hasmin/Types/SelectorSpec.hs view
@@ -2,14 +2,14 @@ module Hasmin.Types.SelectorSpec where -import Test.Hspec-import Hasmin.Parser.Internal-import Hasmin.TestUtils- import Control.Monad.Reader (runReader) import Data.Text (Text)-import Hasmin.Types.Class+ import Hasmin.Config+import Hasmin.Parser.Internal+import Hasmin.TestUtils+import Hasmin.Types.Class+ anplusbMinificationTests :: Spec anplusbMinificationTests =
tests/Hasmin/Types/ShadowSpec.hs view
@@ -2,8 +2,6 @@ module Hasmin.Types.ShadowSpec where -import Test.Hspec- import Data.Text (Text) import Hasmin.Parser.Value import Hasmin.Types.Class
tests/Hasmin/Types/StringSpec.hs view
@@ -2,13 +2,11 @@ module Hasmin.Types.StringSpec where -import Test.Hspec+import Data.Text (Text)+ import Hasmin.Parser.Value import Hasmin.TestUtils--import Data.Text (Text) import Hasmin.Types.Class- quotesNormalizationTests :: Spec quotesNormalizationTests =
tests/Hasmin/Types/StylesheetSpec.hs view
@@ -2,13 +2,12 @@ module Hasmin.Types.StylesheetSpec where -import Test.Hspec+import Data.Text (Text)+ import Hasmin.Parser.Internal import Hasmin.TestUtils import Hasmin.Types.Class -import Data.Text (Text)- atRuleTests :: Spec atRuleTests = do describe "at rules parsing and printing" $@@ -73,7 +72,7 @@ -- "@counter-style circled-alpha{system:fixed;symbols:Ⓐ Ⓑ Ⓒ;suffix:" "}") -- ,("@font-feature-values Jupiter Sans { @swash { delicate: 1; flowing: 2; } }", -- "@font-feature-values Jupiter Sans{@swash{delicate:1;flowing:2}}",- ] + ] spec :: Spec spec = atRuleTests
tests/Hasmin/Types/TimingFunctionSpec.hs view
@@ -7,6 +7,7 @@ import Data.Text (Text) import Hasmin.Parser.Value import Hasmin.Types.Class+import Hasmin.Types.TimingFunction import Hasmin.TestUtils timingFunctionTests :: Spec@@ -15,28 +16,34 @@ mapM_ (matchSpec f) timingFunctionTestsInfo where f = minify <$> timingFunction +quickcheckTimingFunction :: Spec+quickcheckTimingFunction =+ describe "<timing-function> quickcheck tests" .+ it "Minified <timing-function> maintains semantical equivalence" $+ property (prop_minificationEq :: TimingFunction -> Bool)+ timingFunctionTestsInfo :: [(Text, Text)] timingFunctionTestsInfo = [("cubic-bezier(0.25, 0.1, 0.25, 1)", "ease")- ,("cubic-bezier(0.42, 0, 1, 1)","ease-in")- ,("cubic-bezier(0.42, 0, 0.58, 1)","ease-in-out")- ,("cubic-bezier(0, 0, 1, 1)","linear")- ,("cubic-bezier(0, 0, 0.58, 1)","ease-out")- ,("cubic-bezier(0.42, 0, 0.5, 1)","cubic-bezier(.42,0,.5,1)")- ,("cubic-bezier(0, 0, 0.5, 1)","cubic-bezier(0,0,.5,1)")- ,("cubic-bezier(0, 0.3, 0.5, 1)","cubic-bezier(0,.3,.5,1)")- ,("cubic-bezier(0.6, 0, 0.5, 1)","cubic-bezier(.6,0,.5,1)")- ,("steps(1, end)","step-end")- ,("steps(2, end)","steps(2)")- ,("steps(1)","step-end")- ,("steps(1, start)","step-start")- ,("steps(2)","steps(2)")- ,("steps(2, start)","steps(2,start)")+ ,("cubic-bezier(0.42, 0, 1, 1)", "ease-in")+ ,("cubic-bezier(0.42, 0, 0.58, 1)", "ease-in-out")+ ,("cubic-bezier(0, 0, 1, 1)", "linear")+ ,("cubic-bezier(0, 0, 0.58, 1)", "ease-out")+ ,("cubic-bezier(0.42, 0, 0.5, 1)", "cubic-bezier(.42,0,.5,1)")+ ,("cubic-bezier(0, 0, 0.5, 1)", "cubic-bezier(0,0,.5,1)")+ ,("cubic-bezier(0, 0.3, 0.5, 1)", "cubic-bezier(0,.3,.5,1)")+ ,("cubic-bezier(0.6, 0, 0.5, 1)", "cubic-bezier(.6,0,.5,1)")+ ,("steps(1, end)", "step-end")+ ,("steps(2, end)", "steps(2)")+ ,("steps(1)", "step-end")+ ,("steps(1, start)", "step-start")+ ,("steps(2)", "steps(2)")+ ,("steps(2, start)", "steps(2,start)") ] spec :: Spec-spec = timingFunctionTests+spec = do timingFunctionTests+ quickcheckTimingFunction main :: IO () main = hspec spec-
tests/Hasmin/Types/TransformFunctionSpec.hs view
@@ -2,17 +2,16 @@ module Hasmin.Types.TransformFunctionSpec where -import Test.Hspec-import Hasmin.Parser.Value-import Hasmin.TestUtils--import Control.Monad.Reader (runReader) import Control.Applicative ((<|>))+import Control.Monad.Reader (runReader) import Data.Text (Text)++import Hasmin.Config+import Hasmin.Parser.Value+import Hasmin.TestUtils import Hasmin.Types.Class import Hasmin.Types.TransformFunction import Hasmin.Types.Value-import Hasmin.Config transformTests :: Spec transformTests =@@ -20,14 +19,14 @@ mapM_ (matchSpec (minify <$> textualvalue)) transformTestsInfo combinationTests :: Spec-combinationTests = +combinationTests = describe "transform function combination" $ mapM_ (matchSpecWithDesc (g <$> (values "transform" <|> valuesFallback))) functionCombinationTestsInfo where g = mkValues . fmap TransformV . f . fmap (\(TransformV t) -> t) . valuesToList f x = runReader (combine x) defaultConfig transformTestsInfo :: [(Text, Text)]-transformTestsInfo = +transformTestsInfo = [("translate(0)", "skew(0)") ,("translateX(0)", "skew(0)") ,("translateY(0)", "skew(0)")@@ -47,7 +46,7 @@ ] functionCombinationTestsInfo :: [(String, Text, Text)]-functionCombinationTestsInfo = +functionCombinationTestsInfo = [("Combines consecutive absolute value translate() functions into one", "translate(10px) translate(0) translate(10px)", "translate(20px)") ,("Don't combine an skew(90deg), since tan(90deg) = ∞",@@ -62,7 +61,7 @@ spec :: Spec-spec = do transformTests +spec = do transformTests combinationTests main :: IO ()
+ tests/Hasmin/Types/ValueSpec.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE OverloadedStrings #-}++module Hasmin.Types.ValueSpec where++import Data.Monoid ((<>))+import Data.Text (Text)++import Hasmin.Parser.Internal+import Hasmin.Parser.Value+import Hasmin.TestUtils+import Hasmin.Types.Class+import Hasmin.Types.Declaration++valueTests :: Spec+valueTests = do+ describe "value" $+ mapM_ (matchSpec (minify <$> value)) valueTestsInfo+ describe "<bg-layer> value" $+ mapM_ (matchSpec (minify <$> declaration)) bgTests++valueTestsInfo :: [(Text, Text)]+valueTestsInfo =+ [("360deg", "1turn")+ ,("1000ms", "1s")+ ,("1000Hz", "1khz")+ ,("steps(5, end)", "steps(5)")+ ,("local('DejaVu Serif')", "local(dejavu serif)")+ ]++bgTests :: [(Text, Text)]+bgTests =+ [("background: url('test.jpg') center / 10% auto, no-repeat repeat fixed #f00"+ , "background:url(test.jpg) 50%/10%,repeat-y fixed red")+ ,("background: 0 0, none"+ , "background:none,none") -- replaced because none is more frequent than 0 0 (i.e. for compression)+ ]++spec :: Spec+spec = valueTests++main :: IO ()+main = hspec spec