Compare commits
7 commits
main
...
hablo2-the
Author | SHA1 | Date | |
---|---|---|---|
fd88140d44 | |||
feb647bcb4 | |||
111c6ca3f7 | |||
9a1184006c | |||
6780a18764 | |||
c256f96ab4 | |||
e2d79dd77e |
19 changed files with 238 additions and 239 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,5 @@
|
||||||
/dist-newstyle/*
|
/dist-newstyle/*
|
||||||
.ghc.environment.*
|
.ghc.environment.*
|
||||||
|
*.o
|
||||||
|
*.hi
|
||||||
|
Main
|
||||||
|
|
12
README.md
12
README.md
|
@ -62,6 +62,18 @@ mkdir -p ~/.sjw
|
||||||
cp -r src/ ~/.sjw/unitJS
|
cp -r src/ ~/.sjw/unitJS
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Then, the static data used by hablo need to be available in a XDG path. Depending on whether you are installing for your user or system-wide, you may want to perform something either
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp -R share ~/.local/share/hablo
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp -R share /usr/local/share/hablo
|
||||||
|
```
|
||||||
|
|
||||||
### Using hablo (tutorials)
|
### Using hablo (tutorials)
|
||||||
|
|
||||||
Wanna give it a try ? Start by [generating your blog](https://git.marvid.fr/Tissevert/hablo/wiki/Generating%20your%20blog)
|
Wanna give it a try ? Start by [generating your blog](https://git.marvid.fr/Tissevert/hablo/wiki/Generating%20your%20blog)
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
(use-modules (gnu packages haskell-xyz)
|
(use-modules ((guix build-system haskell) #:select (hackage-uri haskell-build-system))
|
||||||
(gnu packages haskell-web)
|
((guix download) #:select (url-fetch))
|
||||||
(guix build-system haskell)
|
((guix licenses) #:select (bsd-3))
|
||||||
(guix download)
|
((guix packages) #:select (base32 origin package)))
|
||||||
(guix gexp)
|
|
||||||
(guix git-download)
|
|
||||||
(guix licenses)
|
|
||||||
(guix packages))
|
|
||||||
|
|
||||||
(package
|
(package
|
||||||
(name "ghc-template")
|
(name "ghc-template")
|
||||||
|
|
16
guix.scm
16
guix.scm
|
@ -1,4 +1,5 @@
|
||||||
(use-modules (gnu packages haskell-xyz)
|
(use-modules (gnu packages haskell-check)
|
||||||
|
(gnu packages haskell-xyz)
|
||||||
(gnu packages haskell-web)
|
(gnu packages haskell-web)
|
||||||
(guix build-system haskell)
|
(guix build-system haskell)
|
||||||
(guix download)
|
(guix download)
|
||||||
|
@ -19,9 +20,11 @@
|
||||||
#:recursive? #t
|
#:recursive? #t
|
||||||
#:select? (git-predicate %source-dir)))
|
#:select? (git-predicate %source-dir)))
|
||||||
(build-system haskell-build-system)
|
(build-system haskell-build-system)
|
||||||
|
(propagated-inputs (list sjw-unitjs))
|
||||||
(inputs
|
(inputs
|
||||||
(list ghc-aeson
|
(list ghc-aeson
|
||||||
ghc-attoparsec
|
ghc-attoparsec
|
||||||
|
ghc-hunit
|
||||||
ghc-lucid
|
ghc-lucid
|
||||||
ghc-optparse-applicative
|
ghc-optparse-applicative
|
||||||
ghc-parsec
|
ghc-parsec
|
||||||
|
@ -29,10 +32,19 @@
|
||||||
ghc-sjw
|
ghc-sjw
|
||||||
ghc-template
|
ghc-template
|
||||||
ghc-xdg-basedir))
|
ghc-xdg-basedir))
|
||||||
|
(arguments
|
||||||
|
`(#:phases
|
||||||
|
(modify-phases %standard-phases
|
||||||
|
(add-after 'install 'copy-static-resources
|
||||||
|
(lambda* (#:key outputs #:allow-other-keys)
|
||||||
|
(let ((out (assoc-ref outputs "out")))
|
||||||
|
(copy-recursively "share" (string-append out "/share/hablo"))))))))
|
||||||
(native-search-paths
|
(native-search-paths
|
||||||
(list
|
(list
|
||||||
(search-path-specification (variable "SJW_PATH")
|
(search-path-specification (variable "SJW_PATH")
|
||||||
(files '("lib/SJW")))))
|
(files '("lib/SJW")))
|
||||||
|
(search-path-specification (variable "XDG_DATA_DIRS")
|
||||||
|
(files '("share")))))
|
||||||
(home-page "https://git.marvid.fr/Tissevert/SJW")
|
(home-page "https://git.marvid.fr/Tissevert/SJW")
|
||||||
(synopsis "The Simple Javascript Wrench")
|
(synopsis "The Simple Javascript Wrench")
|
||||||
(description
|
(description
|
||||||
|
|
12
hablo.cabal
12
hablo.cabal
|
@ -23,9 +23,6 @@ maintainer: tissevert+devel@marvid.fr
|
||||||
category: Web
|
category: Web
|
||||||
extra-source-files: CHANGELOG.md
|
extra-source-files: CHANGELOG.md
|
||||||
build-type: Simple
|
build-type: Simple
|
||||||
data-dir: share
|
|
||||||
data-files: js/*.js
|
|
||||||
defaultWording.conf
|
|
||||||
|
|
||||||
library
|
library
|
||||||
exposed-modules: Arguments
|
exposed-modules: Arguments
|
||||||
|
@ -46,7 +43,6 @@ library
|
||||||
, JSON
|
, JSON
|
||||||
, Markdown
|
, Markdown
|
||||||
, Page
|
, Page
|
||||||
, Paths_hablo
|
|
||||||
, Pretty
|
, Pretty
|
||||||
, RSS
|
, RSS
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
|
@ -65,13 +61,13 @@ library
|
||||||
, time >= 1.8.0 && < 1.12
|
, time >= 1.8.0 && < 1.12
|
||||||
, SJW >= 0.1.2 && < 0.2
|
, SJW >= 0.1.2 && < 0.2
|
||||||
, unix >= 2.7.2 && < 2.8
|
, unix >= 2.7.2 && < 2.8
|
||||||
|
, xdg-basedir >= 0.2.2 && < 0.3
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
executable hablo
|
executable hablo
|
||||||
main-is: src/Main.hs
|
main-is: src/Main.hs
|
||||||
other-modules: Paths_hablo
|
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: base
|
build-depends: base
|
||||||
, hablo
|
, hablo
|
||||||
|
@ -80,8 +76,8 @@ executable hablo
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
test-suite tests
|
test-suite tests
|
||||||
type: detailed-0.9
|
type: exitcode-stdio-1.0
|
||||||
test-module: Tests
|
main-is: Main.hs
|
||||||
other-modules: Mock.Arguments
|
other-modules: Mock.Arguments
|
||||||
, Mock.Article
|
, Mock.Article
|
||||||
, Mock.ArticlesList
|
, Mock.ArticlesList
|
||||||
|
@ -99,11 +95,11 @@ test-suite tests
|
||||||
, XML.Card.Component
|
, XML.Card.Component
|
||||||
, XML.Card.Output
|
, XML.Card.Output
|
||||||
build-depends: base
|
build-depends: base
|
||||||
, Cabal
|
|
||||||
, containers
|
, containers
|
||||||
, directory
|
, directory
|
||||||
, filepath
|
, filepath
|
||||||
, hablo
|
, hablo
|
||||||
|
, HUnit
|
||||||
, lucid
|
, lucid
|
||||||
, mtl
|
, mtl
|
||||||
, text
|
, text
|
||||||
|
|
|
@ -2,22 +2,23 @@
|
||||||
module Arguments (
|
module Arguments (
|
||||||
Arguments(..)
|
Arguments(..)
|
||||||
, get
|
, get
|
||||||
|
, version
|
||||||
) where
|
) where
|
||||||
|
|
||||||
#if !MIN_VERSION_base(4,11,0)
|
#if !MIN_VERSION_base(4,11,0)
|
||||||
import Data.Monoid ((<>))
|
import Data.Monoid ((<>))
|
||||||
#endif
|
#endif
|
||||||
import Data.Version (showVersion)
|
import Control.Applicative ((<**>), optional)
|
||||||
import Control.Applicative ((<|>), (<**>), optional)
|
import Options.Applicative
|
||||||
import Options.Applicative (
|
( Parser, ReadM, argument, auto, eitherReader, execParser, fullDesc, header
|
||||||
Parser, ReadM, argument, auto, eitherReader, execParser, flag', fullDesc
|
, help, helper, info, infoOption, long, metavar, short, str, switch, value )
|
||||||
, header, help, helper, info, long, metavar, short, str, switch, value
|
|
||||||
)
|
|
||||||
import qualified Options.Applicative as Optparse (option)
|
import qualified Options.Applicative as Optparse (option)
|
||||||
import qualified Paths_hablo as Hablo (version)
|
|
||||||
import System.FilePath (dropTrailingPathSeparator, isValid)
|
import System.FilePath (dropTrailingPathSeparator, isValid)
|
||||||
|
|
||||||
data Arguments = BlogConfig {
|
version :: String
|
||||||
|
version = "0.3.0.0"
|
||||||
|
|
||||||
|
data Arguments = Arguments {
|
||||||
sourceDir :: FilePath
|
sourceDir :: FilePath
|
||||||
, articlesPath :: Maybe FilePath
|
, articlesPath :: Maybe FilePath
|
||||||
, bannerPath :: Maybe FilePath
|
, bannerPath :: Maybe FilePath
|
||||||
|
@ -35,7 +36,6 @@ data Arguments = BlogConfig {
|
||||||
, siteURL :: Maybe String
|
, siteURL :: Maybe String
|
||||||
, wording :: Maybe FilePath
|
, wording :: Maybe FilePath
|
||||||
}
|
}
|
||||||
| Version
|
|
||||||
|
|
||||||
option :: ReadM a -> Char -> String -> String -> String -> Parser (Maybe a)
|
option :: ReadM a -> Char -> String -> String -> String -> Parser (Maybe a)
|
||||||
option readM aShort aLong aMetavar aHelpMessage =
|
option readM aShort aLong aMetavar aHelpMessage =
|
||||||
|
@ -47,8 +47,8 @@ option readM aShort aLong aMetavar aHelpMessage =
|
||||||
<> help aHelpMessage
|
<> help aHelpMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
blogConfig :: Parser Arguments
|
arguments :: Parser Arguments
|
||||||
blogConfig = BlogConfig
|
arguments = Arguments
|
||||||
<$> argument filePath (value "." <> metavar "INPUT_DIR")
|
<$> argument filePath (value "." <> metavar "INPUT_DIR")
|
||||||
<*> option filePath 'a' "articles" "DIRECTORY"
|
<*> option filePath 'a' "articles" "DIRECTORY"
|
||||||
"relative path to the directory containing the articles within INPUT_DIR"
|
"relative path to the directory containing the articles within INPUT_DIR"
|
||||||
|
@ -81,16 +81,6 @@ blogConfig = BlogConfig
|
||||||
<*> option filePath 'u' "site-url" "URL" "URL where the blog is published"
|
<*> option filePath 'u' "site-url" "URL" "URL where the blog is published"
|
||||||
<*> option filePath 'w' "wording" "FILE" "path to the file containing the wording to use"
|
<*> option filePath 'w' "wording" "FILE" "path to the file containing the wording to use"
|
||||||
|
|
||||||
version :: Parser Arguments
|
|
||||||
version = flag' Version (
|
|
||||||
long "version"
|
|
||||||
<> short 'v'
|
|
||||||
<> help "print the version number"
|
|
||||||
)
|
|
||||||
|
|
||||||
arguments :: Parser Arguments
|
|
||||||
arguments = blogConfig <|> version
|
|
||||||
|
|
||||||
filePath :: ReadM FilePath
|
filePath :: ReadM FilePath
|
||||||
filePath = eitherReader $ \path ->
|
filePath = eitherReader $ \path ->
|
||||||
if isValid path
|
if isValid path
|
||||||
|
@ -98,8 +88,10 @@ filePath = eitherReader $ \path ->
|
||||||
else Left "This string doesn't represent a valid path"
|
else Left "This string doesn't represent a valid path"
|
||||||
|
|
||||||
get :: IO Arguments
|
get :: IO Arguments
|
||||||
get = do
|
get = execParser
|
||||||
execParser $
|
(info (arguments <**> showVersion <**> helper)
|
||||||
info
|
(fullDesc <> header ("Hablo v" ++ version)))
|
||||||
(arguments <**> helper)
|
where
|
||||||
(fullDesc <> header ("Hablo v" ++ showVersion Hablo.version))
|
showVersion = infoOption
|
||||||
|
version
|
||||||
|
(long "version" <> short 'v' <> help "print current version")
|
||||||
|
|
|
@ -12,7 +12,7 @@ import Data.Map (Map)
|
||||||
import qualified Data.Map as Map (empty, fromList, keys, map, union)
|
import qualified Data.Map as Map (empty, fromList, keys, map, union)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as Text (pack)
|
import qualified Data.Text as Text (pack)
|
||||||
import Paths_hablo (getDataFileName)
|
import Files (getXDGData)
|
||||||
import Text.ParserCombinators.Parsec (
|
import Text.ParserCombinators.Parsec (
|
||||||
Parser
|
Parser
|
||||||
, (<|>)
|
, (<|>)
|
||||||
|
@ -64,6 +64,6 @@ wordingP = Map.map Text.pack . Map.fromList <$>
|
||||||
|
|
||||||
build :: Arguments -> IO Wording
|
build :: Arguments -> IO Wording
|
||||||
build arguments = do
|
build arguments = do
|
||||||
defaultWording <- getDataFileName "defaultWording.conf"
|
defaultWording <- getXDGData "defaultWording.conf"
|
||||||
let wordingFiles = maybe id (:) (wording arguments) $ [defaultWording]
|
let wordingFiles = maybe id (:) (wording arguments) $ [defaultWording]
|
||||||
Wording <$> foldM addWording Map.empty wordingFiles
|
Wording <$> foldM addWording Map.empty wordingFiles
|
||||||
|
|
16
src/Files.hs
16
src/Files.hs
|
@ -3,9 +3,12 @@ module Files (
|
||||||
, absoluteLink
|
, absoluteLink
|
||||||
, filePath
|
, filePath
|
||||||
, find
|
, find
|
||||||
|
, getXDGData
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import System.Directory (doesDirectoryExist, doesFileExist, listDirectory)
|
import System.Directory (doesDirectoryExist, doesFileExist, doesPathExist, listDirectory)
|
||||||
|
import System.Environment.XDG.BaseDir (getAllDataDirs)
|
||||||
|
import System.Exit (die)
|
||||||
import System.FilePath ((</>))
|
import System.FilePath ((</>))
|
||||||
|
|
||||||
data File = File FilePath | Dir FilePath
|
data File = File FilePath | Dir FilePath
|
||||||
|
@ -30,3 +33,14 @@ find path =
|
||||||
filePath (Dir path) >>= emptyIfMissing (fmap ((path </>) <$>) . listDirectory)
|
filePath (Dir path) >>= emptyIfMissing (fmap ((path </>) <$>) . listDirectory)
|
||||||
where
|
where
|
||||||
emptyIfMissing = either (\_ -> return [])
|
emptyIfMissing = either (\_ -> return [])
|
||||||
|
|
||||||
|
getXDGData :: FilePath -> IO FilePath
|
||||||
|
getXDGData resource = getAllDataDirs relativePath >>= findFirst
|
||||||
|
where
|
||||||
|
relativePath = "hablo" </> resource
|
||||||
|
findFirst [] = die ("Install is broken: "
|
||||||
|
<> relativePath
|
||||||
|
<> " directory wasn't found in any of $XDG_DATA_DIRS")
|
||||||
|
findFirst (path:paths) = do
|
||||||
|
fileExists <- doesPathExist path
|
||||||
|
if fileExists then pure path else findFirst paths
|
||||||
|
|
|
@ -13,8 +13,8 @@ import Data.ByteString.Lazy (
|
||||||
)
|
)
|
||||||
import Data.ByteString.Lazy.Char8 (pack)
|
import Data.ByteString.Lazy.Char8 (pack)
|
||||||
import Data.Text.Encoding (encodeUtf8)
|
import Data.Text.Encoding (encodeUtf8)
|
||||||
|
import Files (getXDGData)
|
||||||
import JSON (exportBlog)
|
import JSON (exportBlog)
|
||||||
import Paths_hablo (getDataDir)
|
|
||||||
import Pretty ((.$))
|
import Pretty ((.$))
|
||||||
import SJW (compile, source)
|
import SJW (compile, source)
|
||||||
import System.Directory (createDirectoryIfMissing)
|
import System.Directory (createDirectoryIfMissing)
|
||||||
|
@ -44,7 +44,7 @@ generateConfig destinationDir = do
|
||||||
|
|
||||||
generateMain :: FilePath -> IO ()
|
generateMain :: FilePath -> IO ()
|
||||||
generateMain destinationDir = do
|
generateMain destinationDir = do
|
||||||
habloSources <- (</> "js") <$> getDataDir
|
habloSources <- getXDGData "js"
|
||||||
compile (source [destinationDir, "unitJS", habloSources])
|
compile (source [destinationDir, "unitJS", habloSources])
|
||||||
>>= either abort (output . fst)
|
>>= either abort (output . fst)
|
||||||
where
|
where
|
||||||
|
|
15
src/Main.hs
15
src/Main.hs
|
@ -1,23 +1,16 @@
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
import Arguments (Arguments(..))
|
import Arguments (Arguments(..), version)
|
||||||
import qualified Arguments (get)
|
import qualified Arguments (get)
|
||||||
import qualified Blog (build)
|
import qualified Blog (build)
|
||||||
import Control.Monad.Reader (runReaderT)
|
import Control.Monad.Reader (runReaderT)
|
||||||
import Data.Version (showVersion)
|
|
||||||
import qualified HTML (generate)
|
import qualified HTML (generate)
|
||||||
import qualified JS (generate)
|
import qualified JS (generate)
|
||||||
import qualified Paths_hablo as Hablo (version)
|
|
||||||
import qualified RSS (generate)
|
import qualified RSS (generate)
|
||||||
import System.Exit (exitSuccess)
|
import System.Exit (exitSuccess)
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = Arguments.get >>= Blog.build >>= runReaderT
|
||||||
arguments <- Arguments.get
|
(do HTML.generate
|
||||||
case arguments of
|
|
||||||
Version -> (putStrLn $ showVersion Hablo.version) >> exitSuccess
|
|
||||||
config@(BlogConfig {}) -> Blog.build config >>= runReaderT (do
|
|
||||||
HTML.generate
|
|
||||||
JS.generate
|
JS.generate
|
||||||
RSS.generate
|
RSS.generate)
|
||||||
)
|
|
||||||
|
|
9
src/TODO
9
src/TODO
|
@ -1,3 +1,6 @@
|
||||||
pre {
|
- dans Arguments.hs, vérifier que l'implémentation de option est pas déjà dans la lib optparse-applicative
|
||||||
white-space: pre-wrap;
|
=> ah non en fait c'est une fonction locale au fichier juste mal nommée, corriger ça
|
||||||
}
|
- le Wording, c'était quand même pas l'idée du siècle -> ça pourrait pas tout revenir dans un thème qui serait du HTML ?
|
||||||
|
- faque Template aussi
|
||||||
|
- «Pretty» Oo ? mais c'est juste des Utils en fait
|
||||||
|
- améliorer un peu la gestion des erreurs ? (ExceptT IO…) pas prio mais ça nuirait pas
|
||||||
|
|
10
test/Main.hs
Normal file
10
test/Main.hs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import qualified Structure (test)
|
||||||
|
import Test.HUnit (Test(..), runTestTTAndExit)
|
||||||
|
import qualified XML.Card (test)
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = runTestTTAndExit $ TestList
|
||||||
|
[ XML.Card.test
|
||||||
|
, Structure.test ]
|
|
@ -16,7 +16,7 @@ import Arguments (Arguments(..))
|
||||||
import Utils (testDataPath)
|
import Utils (testDataPath)
|
||||||
|
|
||||||
defaultArticles :: Arguments
|
defaultArticles :: Arguments
|
||||||
defaultArticles = BlogConfig {
|
defaultArticles = Arguments {
|
||||||
sourceDir = testDataPath "Structure/defaultArticles"
|
sourceDir = testDataPath "Structure/defaultArticles"
|
||||||
, articlesPath = Nothing
|
, articlesPath = Nothing
|
||||||
, bannerPath = Nothing
|
, bannerPath = Nothing
|
||||||
|
|
|
@ -5,36 +5,37 @@ module Structure (
|
||||||
import Arguments (Arguments(..))
|
import Arguments (Arguments(..))
|
||||||
import Blog (Path)
|
import Blog (Path)
|
||||||
import qualified Blog.Path as Path (build)
|
import qualified Blog.Path as Path (build)
|
||||||
import Distribution.TestSuite
|
|
||||||
import qualified Mock.Arguments as Arguments
|
import qualified Mock.Arguments as Arguments
|
||||||
import qualified Mock.Blog.Path as Path
|
import qualified Mock.Blog.Path as Path
|
||||||
import System.Directory (withCurrentDirectory)
|
import System.Directory (withCurrentDirectory)
|
||||||
import Utils (simpleTest, tag)
|
import Test.HUnit (Test(..), assertEqual)
|
||||||
|
import Utils (labeled, testGroup)
|
||||||
|
|
||||||
checkPath :: Arguments -> Maybe Path -> IO Progress
|
checkPath :: Arguments -> Maybe Path -> Test
|
||||||
checkPath input expected = do
|
checkPath input expected = TestCase . withCurrentDirectory root $
|
||||||
withCurrentDirectory root $ do
|
either (\_ -> Nothing) Just <$> Path.build root input
|
||||||
actual <- either (\_ -> Nothing) Just <$> Path.build root input
|
>>= assertEqual "Incorrect path detected by hablo" expected
|
||||||
return . Finished $
|
|
||||||
if actual == expected
|
|
||||||
then Pass
|
|
||||||
else Fail $ "Expected " ++ show expected ++ " but got " ++ show actual
|
|
||||||
where
|
where
|
||||||
root = sourceDir input
|
root = sourceDir input
|
||||||
|
|
||||||
test :: Test
|
test :: Test
|
||||||
test = tag "structure" . testGroup "Blog structure" $ simpleTest <$> [
|
test = testGroup "Blog structure" $ labeled
|
||||||
("empty structure", checkPath Arguments.emptyBlog Nothing)
|
[ ("empty structure", checkPath Arguments.emptyBlog Nothing)
|
||||||
, ("default articles", checkPath Arguments.defaultArticles $ Just Path.defaultArticles)
|
, ("default articles"
|
||||||
|
, checkPath Arguments.defaultArticles $ Just Path.defaultArticles)
|
||||||
, ("default pages", checkPath Arguments.defaultPages $ Just Path.defaultPages)
|
, ("default pages", checkPath Arguments.defaultPages $ Just Path.defaultPages)
|
||||||
, ("both default", checkPath Arguments.bothDefault $ Just Path.bothDefault)
|
, ("both default", checkPath Arguments.bothDefault $ Just Path.bothDefault)
|
||||||
, ("custom articles", checkPath Arguments.customArticles $ Just Path.customArticles)
|
, ("custom articles"
|
||||||
|
, checkPath Arguments.customArticles $ Just Path.customArticles)
|
||||||
, ("custom pages", checkPath Arguments.customPages $ Just Path.customPages)
|
, ("custom pages", checkPath Arguments.customPages $ Just Path.customPages)
|
||||||
, ("both custom", checkPath Arguments.bothCustom $ Just Path.bothCustom)
|
, ("both custom", checkPath Arguments.bothCustom $ Just Path.bothCustom)
|
||||||
, ("custom articles, default pages"
|
, ("custom articles, default pages"
|
||||||
, checkPath Arguments.customArticlesDefaultPages $ Just Path.customArticlesDefaultPages)
|
, (checkPath
|
||||||
|
Arguments.customArticlesDefaultPages
|
||||||
|
(Just Path.customArticlesDefaultPages)))
|
||||||
, ("custom pages, default articles"
|
, ("custom pages, default articles"
|
||||||
, checkPath Arguments.customPagesDefaultArticles $ Just Path.customPagesDefaultArticles)
|
, (checkPath
|
||||||
|
Arguments.customPagesDefaultArticles
|
||||||
|
(Just Path.customPagesDefaultArticles)))
|
||||||
, ("bad custom articles", checkPath Arguments.badCustomArticles $ Nothing)
|
, ("bad custom articles", checkPath Arguments.badCustomArticles $ Nothing)
|
||||||
, ("bad custom pages", checkPath Arguments.badCustomPages $ Nothing)
|
, ("bad custom pages", checkPath Arguments.badCustomPages $ Nothing) ]
|
||||||
]
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
module Tests (
|
|
||||||
tests
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Distribution.TestSuite
|
|
||||||
import qualified Structure (test)
|
|
||||||
import Utils (tag)
|
|
||||||
import qualified XML.Card (test)
|
|
||||||
|
|
||||||
tests :: IO [Test]
|
|
||||||
tests = return $ tag "xml" <$> [
|
|
||||||
XML.Card.test
|
|
||||||
, Structure.test
|
|
||||||
]
|
|
|
@ -1,49 +1,17 @@
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
module Utils (
|
module Utils
|
||||||
assertAll
|
( labeled
|
||||||
, assertEqual
|
|
||||||
, simpleTest
|
|
||||||
, tag
|
|
||||||
, testDataPath
|
, testDataPath
|
||||||
) where
|
, testGroup ) where
|
||||||
|
|
||||||
import Distribution.TestSuite
|
|
||||||
import System.FilePath ((</>))
|
import System.FilePath ((</>))
|
||||||
import Text.Printf (printf)
|
import Test.HUnit (Test(..))
|
||||||
|
|
||||||
tagInstance :: String -> TestInstance -> TestInstance
|
|
||||||
tagInstance tagName testInstance = testInstance {
|
|
||||||
tags = tagName : (tags testInstance)
|
|
||||||
}
|
|
||||||
|
|
||||||
tag :: String -> Test -> Test
|
|
||||||
tag tagName (Test testInstance) = Test (tagInstance tagName testInstance)
|
|
||||||
tag tagName group = group {groupTests = tag tagName <$> groupTests group}
|
|
||||||
|
|
||||||
simpleTest :: (String, IO Progress) -> Test
|
|
||||||
simpleTest (name, run) = Test testInstance
|
|
||||||
where
|
|
||||||
testInstance = TestInstance {
|
|
||||||
run
|
|
||||||
, name
|
|
||||||
, tags = []
|
|
||||||
, options = []
|
|
||||||
, setOption = \_ _ -> Right testInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
wrong :: Show a => String -> a -> a -> IO Progress
|
|
||||||
wrong message expected actual = return . Finished . Fail $
|
|
||||||
printf "%s: %s vs. %s" message (show expected) (show actual)
|
|
||||||
|
|
||||||
assertAll :: [(Bool, IO Progress, String)] -> IO Progress
|
|
||||||
assertAll = foldr assert (return $ Finished Pass)
|
|
||||||
where
|
|
||||||
assert (bool, badIssue, checkMessage) next =
|
|
||||||
if bool then return $ Progress checkMessage next else badIssue
|
|
||||||
|
|
||||||
assertEqual :: (Show a, Eq a) => String -> a -> a -> (Bool, IO Progress, String)
|
|
||||||
assertEqual what a b =
|
|
||||||
(a == b, wrong (what ++ " do not match !") a b, what ++ " ok")
|
|
||||||
|
|
||||||
testDataPath :: FilePath -> FilePath
|
testDataPath :: FilePath -> FilePath
|
||||||
testDataPath = ("test" </>)
|
testDataPath = ("test" </>)
|
||||||
|
|
||||||
|
testGroup :: String -> [Test] -> Test
|
||||||
|
testGroup name = TestLabel name . TestList
|
||||||
|
|
||||||
|
labeled :: [(String, Test)] -> [Test]
|
||||||
|
labeled = fmap (uncurry TestLabel)
|
||||||
|
|
|
@ -2,10 +2,10 @@ module XML.Card (
|
||||||
test
|
test
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Distribution.TestSuite
|
import Test.HUnit (Test)
|
||||||
import Utils (tag)
|
import Utils (testGroup)
|
||||||
import qualified XML.Card.Component as Component (test)
|
import qualified XML.Card.Component as Component (test)
|
||||||
import qualified XML.Card.Output as Output (test)
|
import qualified XML.Card.Output as Output (test)
|
||||||
|
|
||||||
test :: Test
|
test :: Test
|
||||||
test = tag "card" $ testGroup "Cards" [Component.test, Output.test]
|
test = testGroup "Cards" [Component.test, Output.test]
|
||||||
|
|
|
@ -7,83 +7,84 @@ import Blog (Blog)
|
||||||
import Control.Monad.IO.Class (MonadIO(..))
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
import Control.Monad.Reader (runReaderT)
|
import Control.Monad.Reader (runReaderT)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Distribution.TestSuite
|
|
||||||
import DOM.Card (HasCard(..))
|
import DOM.Card (HasCard(..))
|
||||||
import Mock.Blog as Blog (simple)
|
import Mock.Blog as Blog (simple)
|
||||||
import Mock.Article as Article (noDescription, noImage, simple)
|
import Mock.Article as Article (noDescription, noImage, simple)
|
||||||
import Mock.ArticlesList as ArticlesList (
|
import Mock.ArticlesList as ArticlesList (
|
||||||
longMain, longTesting, shortMain, shortTesting
|
longMain, longTesting, shortMain, shortTesting
|
||||||
)
|
)
|
||||||
import Utils (assertAll, assertEqual, simpleTest, tag)
|
import Test.HUnit (Test(..), assertEqual)
|
||||||
|
import Utils (labeled, testGroup)
|
||||||
|
|
||||||
check :: HasCard a => IO Blog -> a -> (Text, Text, Maybe String, String, String) -> IO Progress
|
check :: HasCard a => IO Blog -> a -> (Text, Text, Maybe String, String, String) -> IO ()
|
||||||
check getBlog input (expectedCT, expectedD, expectedI, expectedT, expectedU) =
|
check getBlog input (expectedCT, expectedD, expectedI, expectedT, expectedU) =
|
||||||
getBlog >>= runReaderT (
|
getBlog >>= runReaderT checkCard
|
||||||
sequence [
|
where
|
||||||
assertEqual "card types" expectedCT <$> cardType input
|
checkCard = do
|
||||||
, assertEqual "descriptions" expectedD <$> description input
|
liftIO . assertEqual "card types" expectedCT =<< cardType input
|
||||||
, assertEqual "images" expectedI <$> image input
|
liftIO . assertEqual "descriptions" expectedD =<< description input
|
||||||
, assertEqual "titles" expectedT <$> title input
|
liftIO . assertEqual "images" expectedI =<< image input
|
||||||
, assertEqual "urls" expectedU <$> urlPath input
|
liftIO . assertEqual "titles" expectedT =<< title input
|
||||||
] >>= liftIO . assertAll
|
liftIO . assertEqual "urls" expectedU =<< urlPath input
|
||||||
)
|
|
||||||
|
|
||||||
articleCard :: Test
|
articleCard :: Test
|
||||||
articleCard = tag "article" . testGroup "Article cards" $ simpleTest <$> [
|
articleCard = testGroup "Article cards" $ labeled
|
||||||
("simple article components", check Blog.simple Article.simple (
|
[ ("simple article components"
|
||||||
"article"
|
, TestCase $ check Blog.simple Article.simple
|
||||||
|
( "article"
|
||||||
, "It's a test"
|
, "It's a test"
|
||||||
, Just "test.png"
|
, Just "test.png"
|
||||||
, "Some test"
|
, "Some test"
|
||||||
, "articles/test.html"
|
, "articles/test.html" ))
|
||||||
))
|
, ("article components without description"
|
||||||
, ("article components without description", check Blog.simple Article.noDescription (
|
, TestCase $ check Blog.simple Article.noDescription
|
||||||
"article"
|
( "article"
|
||||||
, "A new article on The Test Blog"
|
, "A new article on The Test Blog"
|
||||||
, Just "test.png"
|
, Just "test.png"
|
||||||
, "Some test"
|
, "Some test"
|
||||||
, "articles/test.html"
|
, "articles/test.html" ))
|
||||||
))
|
, ("article components without image"
|
||||||
, ("article components without image", check Blog.simple Article.noImage (
|
, TestCase $ check Blog.simple Article.noImage
|
||||||
"article"
|
( "article"
|
||||||
, "It's a test"
|
, "It's a test"
|
||||||
, Nothing
|
, Nothing
|
||||||
, "Some test"
|
, "Some test"
|
||||||
, "articles/test.html"
|
, "articles/test.html" )) ]
|
||||||
))
|
|
||||||
]
|
|
||||||
|
|
||||||
articlesListCard :: Test
|
articlesListCard :: Test
|
||||||
articlesListCard = tag "articlesList" . testGroup "Articles list cards" $ simpleTest <$> [
|
articlesListCard = testGroup "Articles list cards" $ labeled
|
||||||
("short untagged page component", ArticlesList.shortMain >>= (flip (check Blog.simple) (
|
[ ("short untagged page component"
|
||||||
"website"
|
, TestCase (ArticlesList.shortMain >>=
|
||||||
|
(flip (check Blog.simple)
|
||||||
|
( "website"
|
||||||
, "Latest articles"
|
, "Latest articles"
|
||||||
, Nothing
|
, Nothing
|
||||||
, "The Test Blog"
|
, "The Test Blog"
|
||||||
, "index.html"
|
, "index.html"))))
|
||||||
)))
|
, ("long untagged page component"
|
||||||
, ("long untagged page component", ArticlesList.longMain >>= (flip (check Blog.simple) (
|
, TestCase (ArticlesList.longMain >>=
|
||||||
"website"
|
(flip (check Blog.simple)
|
||||||
|
( "website"
|
||||||
, "All articles"
|
, "All articles"
|
||||||
, Nothing
|
, Nothing
|
||||||
, "The Test Blog"
|
, "The Test Blog"
|
||||||
, "all.html"
|
, "all.html"))))
|
||||||
)))
|
, ("short tagged page component"
|
||||||
, ("short tagged page component", ArticlesList.shortTesting >>= (flip (check Blog.simple) (
|
, TestCase (ArticlesList.shortTesting >>=
|
||||||
"website"
|
(flip (check Blog.simple)
|
||||||
|
( "website"
|
||||||
, "Latest articles tagged testing"
|
, "Latest articles tagged testing"
|
||||||
, Nothing
|
, Nothing
|
||||||
, "The Test Blog - testing"
|
, "The Test Blog - testing"
|
||||||
, "testing/index.html"
|
, "testing/index.html"))))
|
||||||
)))
|
, ("long tagged page component"
|
||||||
, ("long tagged page component", ArticlesList.longTesting >>= (flip (check Blog.simple) (
|
, TestCase (ArticlesList.longTesting >>=
|
||||||
"website"
|
(flip (check Blog.simple)
|
||||||
|
( "website"
|
||||||
, "All articles tagged testing"
|
, "All articles tagged testing"
|
||||||
, Nothing
|
, Nothing
|
||||||
, "The Test Blog - testing"
|
, "The Test Blog - testing"
|
||||||
, "testing/all.html"
|
, "testing/all.html")))) ]
|
||||||
)))
|
|
||||||
]
|
|
||||||
|
|
||||||
test :: Test
|
test :: Test
|
||||||
test = tag "component" $ testGroup "Cards components" [articleCard, articlesListCard]
|
test = testGroup "Cards components" [articleCard, articlesListCard]
|
||||||
|
|
|
@ -6,7 +6,6 @@ import Blog (Blog(..), URL(..))
|
||||||
import Control.Monad.IO.Class (MonadIO(..))
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
import Control.Monad.Reader (asks, runReaderT)
|
import Control.Monad.Reader (asks, runReaderT)
|
||||||
import qualified Data.Text.Lazy.IO as Lazy (readFile)
|
import qualified Data.Text.Lazy.IO as Lazy (readFile)
|
||||||
import Distribution.TestSuite
|
|
||||||
import DOM.Card (HasCard(..), make)
|
import DOM.Card (HasCard(..), make)
|
||||||
import Lucid (renderTextT)
|
import Lucid (renderTextT)
|
||||||
import Mock.Blog as Blog (noCards, simple)
|
import Mock.Blog as Blog (noCards, simple)
|
||||||
|
@ -16,34 +15,47 @@ import Mock.ArticlesList as ArticlesList (
|
||||||
)
|
)
|
||||||
import Pretty ((.$))
|
import Pretty ((.$))
|
||||||
import System.FilePath ((</>))
|
import System.FilePath ((</>))
|
||||||
import Utils (assertAll, assertEqual, simpleTest, tag, testDataPath)
|
import Test.HUnit (Test(..), assertEqual)
|
||||||
|
import Utils (labeled, testDataPath, testGroup)
|
||||||
|
|
||||||
check :: HasCard a => IO Blog -> a -> FilePath -> IO Progress
|
check :: HasCard a => IO Blog -> a -> FilePath -> IO ()
|
||||||
check getBlog input expectedFile =
|
check getBlog input expectedFile = getBlog >>= runReaderT checkHTML
|
||||||
getBlog >>= runReaderT (do
|
where
|
||||||
|
checkHTML = do
|
||||||
actual <- renderTextT $ maybe (return ()) (DOM.Card.make input) =<< (asks $urls.$cards)
|
actual <- renderTextT $ maybe (return ()) (DOM.Card.make input) =<< (asks $urls.$cards)
|
||||||
expected <- liftIO . Lazy.readFile $ testDataPath "XML/Card/Output" </> expectedFile
|
expected <- liftIO . Lazy.readFile $ testDataPath "XML/Card/Output" </> expectedFile
|
||||||
liftIO $ assertAll [
|
liftIO $ assertEqual "card HTML output" expected actual
|
||||||
assertEqual "card HTML output" expected actual
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
articleCard :: Test
|
articleCard :: Test
|
||||||
articleCard = tag "article" . testGroup "Article cards" $ simpleTest <$> [
|
articleCard = testGroup "Article cards" $ labeled
|
||||||
("simple article output", check Blog.simple Article.simple "simple.html")
|
[ ("simple article output"
|
||||||
, ("article output without description", check Blog.simple Article.noDescription "noDescription.html")
|
, TestCase $ check Blog.simple Article.simple "simple.html")
|
||||||
, ("article output without image", check Blog.simple Article.noImage "noImage.html")
|
, ("article output without description"
|
||||||
, ("no card article output", check Blog.noCards Article.simple "/dev/null")
|
, TestCase $ check Blog.simple Article.noDescription "noDescription.html")
|
||||||
]
|
, ("article output without image"
|
||||||
|
, TestCase $ check Blog.simple Article.noImage "noImage.html")
|
||||||
|
, ("no card article output"
|
||||||
|
, TestCase $ check Blog.noCards Article.simple "/dev/null") ]
|
||||||
|
|
||||||
articlesListCard :: Test
|
articlesListCard :: Test
|
||||||
articlesListCard = tag "article" . testGroup "Article cards" $ simpleTest <$> [
|
articlesListCard = testGroup "Article list cards" $ labeled
|
||||||
("short untagged page output", ArticlesList.shortMain >>= flip (check Blog.simple) "shortMain.html")
|
[ ("short untagged page output"
|
||||||
, ("long untagged page output", ArticlesList.longMain >>= flip (check Blog.simple) "longMain.html")
|
, TestCase
|
||||||
, ("short tagged page output", ArticlesList.shortTesting >>= flip (check Blog.simple) "shortTesting.html")
|
(ArticlesList.shortMain >>= flip (check Blog.simple) "shortMain.html"))
|
||||||
, ("long tagged page output", ArticlesList.longTesting >>= flip (check Blog.simple) "longTesting.html")
|
, ("long untagged page output"
|
||||||
, ("no card articlesList output", ArticlesList.shortMain >>= flip (check Blog.noCards) "/dev/null")
|
, TestCase
|
||||||
]
|
(ArticlesList.longMain >>= flip (check Blog.simple) "longMain.html"))
|
||||||
|
, ("short tagged page output"
|
||||||
|
, TestCase
|
||||||
|
(ArticlesList.shortTesting
|
||||||
|
>>= flip (check Blog.simple) "shortTesting.html"))
|
||||||
|
, ("long tagged page output"
|
||||||
|
, TestCase
|
||||||
|
(ArticlesList.longTesting
|
||||||
|
>>= flip (check Blog.simple) "longTesting.html"))
|
||||||
|
, ("no card articlesList output"
|
||||||
|
, TestCase
|
||||||
|
(ArticlesList.shortMain >>= flip (check Blog.noCards) "/dev/null")) ]
|
||||||
|
|
||||||
test :: Test
|
test :: Test
|
||||||
test = tag "output" $ testGroup "Cards outputs" [articleCard, articlesListCard]
|
test = testGroup "Cards outputs" [articleCard, articlesListCard]
|
||||||
|
|
Loading…
Reference in a new issue