packages feed

summoner-2.1.0.0: test/Test/Golden.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}

{- | Golden tests for @summoner@.

In the @test/golden@ folder we now have two projects that are created by
@summoner@.

* @fullProject@ – the project that has all options enabled.
* @smallProject@ – the project that has limited options enabled.

The purpose of this tests is to check that with the corresponding 'Settings'
the tool creates the expected project.

-}

module Test.Golden
       ( goldenSpec
       ) where

import Data.TreeDiff (ToExpr, ansiWlEditExprCompact, ediff)
import System.Directory (doesDirectoryExist, listDirectory)
import System.FilePath (takeFileName, (</>))
import Test.Hspec (Spec, describe, expectationFailure, it)

import Summoner (Settings)
import Summoner.Golden (cabalFull, cabalMinimal, fullBatteries, stackFull)
import Summoner.Template (createProjectTemplate)
import Summoner.Tree (TreeFs (..))

import qualified Data.Text as T


goldenSpec :: Spec
goldenSpec = describe "golden tests" $ do
    it "correctly scaffolds the 'cabal-minimal' project" $
        checkProject "examples/cabal-minimal" cabalMinimal
    it "correctly scaffolds the 'cabal-full' project" $
        checkProject "examples/cabal-full" cabalFull
    it "correctly scaffolds the 'stack-full' project" $
        checkProject "examples/stack-full" stackFull
    it "correctly scaffolds the 'full-batteries' project" $
        checkProject "examples/full-batteries" fullBatteries
  where
    checkProject :: FilePath -> Settings -> IO ()
    checkProject path settings = do
        goldenFs  <- sortTree <$> readTreeFs path
        let testFs = sortTree $ createProjectTemplate settings
        when (goldenFs /= testFs) $ do
            putTextLn $ show $ ansiWlEditExprCompact $ ediff goldenFs testFs
            expectationFailure "Golden and scaffolded project don't match"

readTreeFs :: FilePath -> IO TreeFs
readTreeFs filePath = doesDirectoryExist filePath >>= \case
    True -> do
        dirs <- listDirectory filePath
        Dir (takeFileName filePath) <$> traverse (\dir -> readTreeFs $ filePath </> dir) dirs
    False -> do
        -- Normalize line endings (CRLF -> LF) for cross-platform compatibility
        content <- T.filter (/= '\r') . decodeUtf8 <$> readFileBS filePath
        pure $ File (takeFileName filePath) content

sortTree :: TreeFs -> TreeFs
sortTree = \case
    file@(File _ _) -> file
    Dir path fs     -> Dir path $ sort $ map sortTree fs

-- Orphan instances

instance ToExpr TreeFs