packages feed

stache-2.2.0: mustache-spec/Spec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}

module Main (main) where

import Control.Monad
import Data.Aeson
import Data.ByteString (ByteString)
import Data.FileEmbed (embedFile)
import Data.Map (Map, (!))
import qualified Data.Map as M
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import Data.Yaml
import Test.Hspec
import Text.Megaparsec
import Text.Mustache
import Text.Mustache.Parser

-- | Representation of information contained in a Mustache spec file.
data SpecFile = SpecFile
  { -- | Textual overview of this spec file
    specOverview :: Text,
    -- | The actual collection of tests
    specTests :: [Test]
  }

instance FromJSON SpecFile where
  parseJSON = withObject "Mustache spec file" $ \o -> do
    specOverview <- o .: "overview"
    specTests <- o .: "tests"
    return SpecFile {..}

-- | Representation of a single test.
data Test = Test
  { testName :: String,
    testDesc :: String,
    testData :: Value,
    testTemplate :: Text,
    testExpected :: Text,
    testPartials :: Map Text Text
  }

instance FromJSON Test where
  parseJSON = withObject "Test" $ \o -> do
    testName <- o .: "name"
    testDesc <- o .: "desc"
    testData <- o .: "data"
    testTemplate <- o .: "template"
    testExpected <- o .: "expected"
    testPartials <- o .:? "partials" .!= M.empty
    return Test {..}

main :: IO ()
main = hspec spec

spec :: Spec
spec = do
  specData "Comments" $(embedFile "specification/comments.yml")
  specData "Delimiters" $(embedFile "specification/delimiters.yml")
  specData "Interpolation" $(embedFile "specification/interpolation.yml")
  specData "Inverted" $(embedFile "specification/inverted.yml")
  specData "Partials" $(embedFile "specification/partials.yml")
  specData "Sections" $(embedFile "specification/sections.yml")

specData :: String -> ByteString -> Spec
specData aspect bytes = describe aspect $ do
  let handleError = expectationFailure . errorBundlePretty
  case decodeEither' bytes of
    Left err ->
      it "should load YAML specs first" $
        expectationFailure (prettyPrintParseException err)
    Right SpecFile {..} ->
      forM_ specTests $ \Test {..} ->
        it (testName ++ ": " ++ testDesc) $
          case compileMustacheText (PName $ T.pack testName) testTemplate of
            Left perr -> handleError perr
            Right Template {..} -> do
              ps1 <- forM (M.keys testPartials) $ \k -> do
                let pname = PName k
                case parseMustache (T.unpack k) (testPartials ! k) of
                  Left perr -> handleError perr >> undefined
                  Right ns -> return (pname, ns)
              let ps2 = M.fromList ps1 `M.union` templateCache
              TL.toStrict (renderMustache (Template templateActual ps2) testData)
                `shouldBe` testExpected