packages feed

hsass-0.7.0: test/Text/Sass/EncodingSpec.hs

module Text.Sass.EncodingSpec where

import           Control.Concurrent.MVar
import qualified Data.ByteString           as BS
import qualified Data.Text                 as T
import qualified Data.Text.Encoding        as TE
import           GHC.IO.Encoding           (setFileSystemEncoding, utf8)
import           System.IO
import           System.IO.Temp
import           Test.Hspec
import           Text.Sass
import qualified Text.Sass.Values.Internal as VI

opts :: SassOptions
opts = def { sassStripEncodingInfo = True }

testString :: String
testString =  "Zażółcić gęślą jaźń"

testInput :: String
testInput = "a { content: '" ++ testString ++ "'; }"

expectedResult :: Either a String
expectedResult = Right $ "a {\n  content: '" ++ testString ++ "'; }\n"

main :: IO ()
main = hspec spec

testValue :: SassValue -> Expectation
testValue v =
    (VI.toNativeValue v >>= VI.fromNativeValue) `shouldReturn` v

storeFunc :: MVar String -> SassValue -> IO SassValue
storeFunc m (SassList [SassString s] _) = do
    putMVar m s
    return $ SassString s
storeFunc _ _ = fail "Unknown value"

storeFuncSig :: MVar String -> SassFunction
storeFuncSig = SassFunction "store($s)" . storeFunc

returnFunc :: SassValue -> IO SassValue
returnFunc _ = return $ SassString $ "'" ++ testString ++ "'"

returnFuncSig :: SassFunction
returnFuncSig = SassFunction "ret()" returnFunc

nonUTFNameSig :: SassFunction
nonUTFNameSig = SassFunction "zażółcić()" returnFunc

importFunction :: FilePath -> String -> String -> IO [SassImport]
importFunction _ "src"  _ = return [makeSourceImport testInput]
importFunction p "path" _ = return [makePathImport p "."]
importFunction _ _ _      = fail "Unknown import"

importers :: FilePath -> [SassImporter]
importers p = [SassImporter 1 $ importFunction p]

withNonASCIIContentFile :: (String -> IO a) -> IO a
withNonASCIIContentFile f =
    withSystemTempFile "styles.scss" $ \p h -> do
        BS.hPutStr h (TE.encodeUtf8 $ T.pack testInput)
        hClose h
        f p

withNonASCIIPathFile :: (String -> IO a) -> IO a
withNonASCIIPathFile f = do
    setFileSystemEncoding utf8
    res <- withSystemTempFile "pl-zażółcić.scss" $ \p h -> do
        BS.hPutStr h (TE.encodeUtf8 $ T.pack testInput)
        hClose h
        f p
    return res

spec :: Spec
spec = do
    it "should preserve UTF-8 encoding when compiling Strings" $
        compileString testInput opts `shouldReturn` expectedResult

    it "should preserve UTF-8 encoding when compiling files" $
        withNonASCIIContentFile $ \p ->
            compileFile p opts `shouldReturn` expectedResult

    it "should correctly encode UTF-8 chars in source paths" $
        withNonASCIIPathFile $ \p ->
            compileFile p opts `shouldReturn` expectedResult

    it "should correctly encode UTF-8 chars in included files path" $
        withNonASCIIPathFile $ \p ->
            withSystemTempFile "styles.scss" $ \impP impH -> do
                let cnt = "@import \"" ++ p ++ "\";"
                BS.hPutStr impH $ TE.encodeUtf8 $ T.pack cnt
                hClose impH

                Right res <- compileFile impP opts :: ExtendedResult
                includes <- resultIncludes res
                includes `shouldSatisfy` elem p

    it "should correctly encode/decode UTF-8 SassStrings" $
        testValue $ SassString testString

    it "should correctly encode/decode UTF-8 units in SassNumbers" $
        testValue $ SassNumber 1 testString

    it "should correctly encode/decode UTF-8 SassWarnings" $
        testValue $ SassWarning testString

    it "should correctly encode/decode UTF-8 SassErrors" $
        testValue $ SassError testString

    it "should correctly encode/decode list of UTF-8 SassStrings" $
        testValue $ SassList [SassString "Zażółcić", SassString "gęślą"] SassSeparatorComma

    it "should correctly UTF-8 decode function arguments" $ do
        arg <- newEmptyMVar
        let fopts = opts { sassFunctions = Just [ storeFuncSig arg ] }
        _ <- compileString ("a {foo: store('" ++ testString ++ "');}") fopts :: StringResult
        tryTakeMVar arg `shouldReturn` Just testString

    it "should correctly UTF-8 encode function return value" $ do
        let fopts = opts { sassFunctions = Just [ returnFuncSig ] }
        compileString "a {content: ret(); }" fopts `shouldReturn` expectedResult

    it "should correctly UTF-8 encode function names" $ do
        let fopts = opts { sassFunctions = Just [ nonUTFNameSig ] }
        compileString "a {content: zażółcić(); }" fopts
            `shouldReturn` expectedResult

    it "should correctly encode UTF-8 string from importer" $
        let iopts = opts { sassImporters = Just $ importers "" }
         in compileString "@import 'src';" iopts `shouldReturn` expectedResult

    it "should correctly encode UTF-8 path from importer" $
        withNonASCIIPathFile $ \p ->
            let iopts = opts { sassImporters = Just $ importers p }
            in compileString "@import 'path';" iopts `shouldReturn` expectedResult