/
Spec.hs
448 lines (348 loc) · 16 KB
/
Spec.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.Text as T
import qualified Text.Pandoc as P
import Control.Monad.Trans.Writer
import Data.Default (def)
import Data.Either (fromRight)
import Data.Text (Text)
import Test.Tasty
import Test.Tasty.Hspec
import Text.Pandoc.Definition
import Text.Pandoc.Utils
-- * Testing data
-- ** Partial level
blockPara :: Block
blockPara = Para [Str "abcd"]
expectedInlinePartial :: Block
expectedInlinePartial = Para [Str "ABCD"]
expectedInlinePartialL :: Block
expectedInlinePartialL = Para [Str "abcd", Str "abcd"]
-- ** Pandoc level
docPara :: Pandoc
docPara = Pandoc (Meta mempty) [blockPara]
expectedInline :: Pandoc
expectedInline = Pandoc (Meta mempty) [Para [Str "ABCD"]]
expectedBlock :: Pandoc
expectedBlock = Pandoc (Meta mempty) [Plain [Str "abcd"]]
expectedInlineL :: Pandoc
expectedInlineL = Pandoc (Meta mempty) [Para [Str "abcd", Str "abcd"]]
expectedBlockL :: Pandoc
expectedBlockL = Pandoc (Meta mempty) [blockPara, blockPara]
-- ** Composition results
compPara :: Block
compPara = Para [Str "abcd"]
compPara2 :: Block
compPara2 = Para [Str "abcd", Str "efgh"]
expectedDup :: Block
expectedDup = Para [Str "abcd", Str "abcd"]
expectedMerge :: Block
expectedMerge = Para [Str "abcdefgh"]
expectedDupMerge :: Block
expectedDupMerge = Para [Str "abcdabcd"]
expectedMergeDup :: Block
expectedMergeDup = Para [Str "abcd", Str "abcd"]
-- * Filters
-- ** Plain filters
capFilterInline :: Inline -> Inline
capFilterInline (Str str) = Str $ T.toUpper str
capFilterInline x = x
uncapFilterInline :: Inline -> Inline
uncapFilterInline (Str str) = Str $ T.toLower str
uncapFilterInline x = x
unParaFilterBlock :: Block -> Block
unParaFilterBlock (Para ils) = Plain ils
unParaFilterBlock x = x
dupFilterInline :: Inline -> [Inline]
dupFilterInline (Str str) = [Str str, Str str]
dupFilterInline x = [x]
mergeFilterInline :: [Inline] -> [Inline]
mergeFilterInline (Str str1 : Str str2 : xs) = Str (str1 <> str2) : xs
mergeFilterInline x = x
dupFilterBlock :: Block -> [Block]
dupFilterBlock (Para ils) = [Para ils, Para ils]
dupFilterBlock x = [x]
-- ** Monadic filters
extractFilterInlineM :: Inline -> Writer Text Inline
extractFilterInlineM il@(Str str) = tell str >> return (capFilterInline il)
extractFilterInlineM x = return x
extractFilterInlineML :: Inline -> Writer Text [Inline]
extractFilterInlineML il@(Str str) = tell str >> return (dupFilterInline il)
extractFilterInlineML x = return [x]
extractFilter :: Inline -> Writer Text Inline
extractFilter il@(Str str) = tell str >> return il
extractFilter x = return x
-- * Readme example
-- ** Filters
behead :: Block -> Block
behead (Header n _ xs) | n >= 2 = Para [Emph xs]
behead x = x
beheadFilter :: PandocFilter
beheadFilter = mkFilter behead
beheadPandoc :: Pandoc -> Pandoc
beheadPandoc = convertFilter behead
delink :: Inline -> [Inline]
delink (Link _ txt _) = txt
delink x = [x]
delinkFilter :: PandocFilter
delinkFilter = mkFilter delink
delinkPandoc :: Pandoc -> Pandoc
delinkPandoc = convertFilter delink
myFilter :: PandocFilter
myFilter = beheadFilter <> delinkFilter
-- ** Documents
readmeText :: Text
readmeText = T.strip $ T.unlines
[ "## Heading"
, "Hello, [Pandoc](https://pandoc.org)."
]
readmeDoc :: Pandoc
readmeDoc = Pandoc (Meta mempty)
[ Header 2 ("heading", [], []) [Str "Heading"]
, Para [ Str "Hello,"
, Space
, Link ("",[],[]) [Str "Pandoc"] ("https://pandoc.org","")
, Str "."
]
]
expectedHtml :: Text
expectedHtml = T.strip $ T.unlines
[ "<p><em>Heading</em></p>"
, "<p>Hello, Pandoc.</p>"
]
expectedDoc :: Pandoc
expectedDoc = Pandoc (Meta mempty)
[ Para [Emph [Str "Heading"]]
, Para [ Str "Hello,"
, Space
, Str "Pandoc"
, Str "."
]
]
-- ** The example
mdToHtml
:: Text -- ^ Input markdown string
-> Either P.PandocError Text -- ^ Html string or error
mdToHtml md = P.runPure $ do
doc <- P.readMarkdown def md
let doc' = sequenceFilters [beheadFilter, delinkFilter] doc
P.writeHtml5String def doc'
mdToHtmlCompose
:: Text -- ^ Input markdown string
-> Either P.PandocError Text -- ^ Html string or error
mdToHtmlCompose md = P.runPure $ do
doc <- P.readMarkdown def md
let doc' = applyFilter myFilter doc
P.writeHtml5String def doc'
convertSpec :: Spec
convertSpec = parallel $ do
describe "mkFilter" $ do
it "converts a -> a filter to Pandoc -> Pandoc filter" $ do
applyFilter (mkFilter capFilterInline) docPara `shouldBe` expectedInline
applyFilter (mkFilter unParaFilterBlock) docPara `shouldBe` expectedBlock
it "converts a -> a filter to b -> b partial filter" $
applyFilter (mkFilter capFilterInline) blockPara `shouldBe` expectedInlinePartial
it "converts a -> [a] filter to Pandoc -> Pandoc filter" $ do
applyFilter (mkFilter dupFilterInline) docPara `shouldBe` expectedInlineL
applyFilter (mkFilter dupFilterBlock) docPara `shouldBe` expectedBlockL
it "converts a -> [a] filter to b -> b partial filter" $
applyFilter (mkFilter dupFilterInline) blockPara `shouldBe` expectedInlinePartialL
it "converts a -> m a filter to Pandoc -> m Pandoc filter" $ do
let (doc, s) = runWriter $ applyFilterM (mkFilter extractFilterInlineM) docPara
doc `shouldBe` expectedInline
s `shouldBe` T.pack "abcd"
it "converts a -> m a filter to b -> m b filter" $ do
let (bl, s) = runWriter $ applyFilterM (mkFilter extractFilterInlineM) blockPara
bl `shouldBe` expectedInlinePartial
s `shouldBe` T.pack "abcd"
it "converts a -> m [a] filter to Pandoc -> m Pandoc filter" $ do
let (doc, s) = runWriter $ applyFilterM (mkFilter extractFilterInlineML) docPara
doc `shouldBe` expectedInlineL
s `shouldBe` T.pack "abcd"
it "converts a -> m [a] filter to b -> m b filter" $ do
let (bl, s) = runWriter $ applyFilterM (mkFilter extractFilterInlineML) blockPara
bl `shouldBe` expectedInlinePartialL
s `shouldBe` T.pack "abcd"
it "converts PartialFilter a to PartialFilter b" $ do
let fInline = mkFilter capFilterInline :: PartialFilter Inline
fBlock = mkFilter fInline :: PartialFilter Block
fPandoc = mkFilter fInline :: PandocFilter
applyFilter fBlock blockPara `shouldBe` expectedInlinePartial
applyFilter fPandoc docPara `shouldBe` expectedInline
it "converts PartialFilterM m a to PartialFilterM m b" $ do
let fInline :: PartialFilterM (Writer Text) Inline
fInline = mkFilter extractFilterInlineM
fBlock = mkFilter fInline :: PartialFilterM (Writer Text) Block
fPandoc = mkFilter fInline :: PandocFilterM (Writer Text)
let (doc, s) = runWriter $ applyFilterM fPandoc docPara
doc `shouldBe` expectedInline
s `shouldBe` T.pack "abcd"
let (bl, s') = runWriter $ applyFilterM fBlock blockPara
bl `shouldBe` expectedInlinePartial
s' `shouldBe` T.pack "abcd"
describe "convertFilter" $ do
it "converts a -> a filter to Pandoc -> Pandoc filter" $ do
convertFilter capFilterInline docPara `shouldBe` expectedInline
convertFilter unParaFilterBlock docPara `shouldBe` expectedBlock
it "converts a -> a filter to b -> b partial filter" $
convertFilter capFilterInline blockPara `shouldBe` expectedInlinePartial
it "converts a -> [a] filter to Pandoc -> Pandoc filter" $ do
convertFilter dupFilterInline docPara `shouldBe` expectedInlineL
convertFilter dupFilterBlock docPara `shouldBe` expectedBlockL
it "converts a -> [a] filter to b -> b partial filter" $
convertFilter dupFilterInline blockPara `shouldBe` expectedInlinePartialL
it "converts a -> m a filter to Pandoc -> m Pandoc filter" $ do
let (doc, s) = runWriter $ convertFilterM extractFilterInlineM docPara
doc `shouldBe` expectedInline
s `shouldBe` T.pack "abcd"
it "converts a -> m a filter to b -> m b filter" $ do
let (bl, s) = runWriter $ convertFilterM extractFilterInlineM blockPara
bl `shouldBe` expectedInlinePartial
s `shouldBe` T.pack "abcd"
it "converts a -> m [a] filter to Pandoc -> m Pandoc filter" $ do
let (doc, s) = runWriter $ convertFilterM extractFilterInlineML docPara
doc `shouldBe` expectedInlineL
s `shouldBe` T.pack "abcd"
it "converts a -> m [a] filter to b -> m b filter" $ do
let (bl, s) = runWriter $ convertFilterM extractFilterInlineML blockPara
bl `shouldBe` expectedInlinePartialL
s `shouldBe` T.pack "abcd"
describe "getFilter" $ do
it "converts PartialFilter a to a -> a" $ do
let fInline = mkFilter capFilterInline :: PartialFilter Inline
getFilter fInline (Str "abcd") `shouldBe` Str "ABCD"
it "converts PartialFilterM m a to a -> m a" $ do
let fInline :: PartialFilterM (Writer Text) Inline
fInline = mkFilter extractFilterInlineM
let (doc, s) = runWriter $ getFilterM fInline (Str "abcd")
doc `shouldBe` Str "ABCD"
s `shouldBe` T.pack "abcd"
it "converts PartialFilter a to b -> b" $ do
let fInline = mkFilter capFilterInline :: PartialFilter Inline
fBlock = getFilter fInline :: Block -> Block
fPandoc = getFilter fInline :: Pandoc -> Pandoc
fBlock blockPara `shouldBe` expectedInlinePartial
fPandoc docPara `shouldBe` expectedInline
it "converts PartialFilterM m a to b -> m b" $ do
let fInline :: PartialFilterM (Writer Text) Inline
fInline = mkFilter extractFilterInlineM
fBlock = getFilterM fInline :: Block -> Writer Text Block
fPandoc = getFilterM fInline :: Pandoc -> Writer Text Pandoc
let (doc, s) = runWriter $ fPandoc docPara
doc `shouldBe` expectedInline
s `shouldBe` T.pack "abcd"
let (bl, s') = runWriter $ fBlock blockPara
bl `shouldBe` expectedInlinePartial
s' `shouldBe` T.pack "abcd"
describe "mkConcatedFilter" $ do
let cap = mkConcatedFilter [uncapFilterInline, capFilterInline]
uncap = mkConcatedFilter [capFilterInline, uncapFilterInline]
it "concats filter from left to right" $ do
applyFilter cap docPara `shouldBe` expectedInline
applyFilter uncap docPara `shouldBe` docPara
describe "toFilterM" $
it "converts a -> a filter to a -> m a filter" $ do
let capFilterM = toFilterM $ mkFilter capFilterInline
applyFilterM capFilterM (Str "abcd") `shouldBe` Just (Str "ABCD")
composeSpec :: Spec
composeSpec = parallel $ do
let dup = mkFilter dupFilterInline
merge = mkFilter mergeFilterInline
extract = mkFilter extractFilter
describe "applyFilter" $ do
it "applys dup correctly" $
applyFilter dup compPara `shouldBe` expectedDup
it "applys merge correctly" $
applyFilter merge compPara2 `shouldBe` expectedMerge
describe "sequenceFilters" $ do
it "applys PartialFilter composition from left to right" $ do
applyFilter (dup <> merge) compPara `shouldBe` expectedDupMerge
applyFilter (merge <> dup) compPara `shouldBe` expectedMergeDup
it "applys PartialFilterM composition from left to right" $ do
let (doc, s) = runWriter $ applyFilterM (extract <> toFilterM dup) compPara
doc `shouldBe` expectedDup
s `shouldBe` T.pack "abcd"
let (doc', s') = runWriter $ applyFilterM (toFilterM dup <> extract) compPara
doc' `shouldBe` expectedDup
s' `shouldBe` T.pack "abcdabcd"
describe "monoid instance" $ do
it "applys PartialFilter composition from left to right" $ do
sequenceFilters [dup, merge] compPara `shouldBe` expectedDupMerge
sequenceFilters [merge, dup] compPara `shouldBe` expectedMergeDup
it "applys PartialFilterM composition from left to right" $ do
let (doc, s) = runWriter $ sequenceFiltersM [extract, toFilterM dup] compPara
doc `shouldBe` expectedDup
s `shouldBe` T.pack "abcd"
let (doc', s') = runWriter $ sequenceFiltersM [toFilterM dup, extract] compPara
doc' `shouldBe` expectedDup
s' `shouldBe` T.pack "abcdabcd"
let dupInl = mkFilter dupFilterInline :: PartialFilter [Inline]
mergeInl = mkFilter mergeFilterInline :: PartialFilter [Inline]
extractInl = mkFilter extractFilter :: PartialFilterM (Writer Text) [Inline]
describe "getConcatedFilter" $ do
it "converts [PartialFilter a] to a -> a, applied from left to right" $ do
getConcatedFilter [dupInl, mergeInl] [Str "abcd"] `shouldBe` [Str "abcdabcd"]
getConcatedFilter [mergeInl, dupInl] [Str "abcd"] `shouldBe` [Str "abcd", Str "abcd"]
it "converts [PartialFilterM m a] to a -> m a, applied from left to right" $ do
let (doc, s) = runWriter $ getConcatedFilterM [extractInl, toFilterM dupInl] [Str "abcd"]
doc `shouldBe` [Str "abcd", Str "abcd"]
s `shouldBe` T.pack "abcd"
let (doc', s') = runWriter $ getConcatedFilterM [toFilterM dupInl, extractInl] [Str "abcd"]
doc' `shouldBe` [Str "abcd", Str "abcd"]
s' `shouldBe` T.pack "abcdabcd"
it "converts [PartialFilter a] to b -> b, applied from left to right" $ do
getConcatedFilter [dupInl, mergeInl] compPara `shouldBe` expectedDupMerge
getConcatedFilter [mergeInl, dupInl] compPara `shouldBe` expectedMergeDup
it "converts [PartialFilterM m a] to b -> m b, applied from left to right" $ do
let (doc, s) = runWriter $ getConcatedFilterM [extractInl, toFilterM dupInl] compPara
doc `shouldBe` expectedDup
s `shouldBe` T.pack "abcd"
let (doc', s') = runWriter $ getConcatedFilterM [toFilterM dupInl, extractInl] compPara
doc' `shouldBe` expectedDup
s' `shouldBe` T.pack "abcdabcd"
readmeSpec :: Spec
readmeSpec = parallel $
describe "readme example" $ do
it "processes filter examples correctly on AST level" $ do
sequenceFilters [beheadFilter, delinkFilter] readmeDoc `shouldBe` expectedDoc
applyFilter myFilter readmeDoc `shouldBe` expectedDoc
(delinkPandoc . beheadPandoc) readmeDoc `shouldBe` expectedDoc
it "processes filter examples correctly on Text level" $ do
fromRight "" (mdToHtml readmeText) `shouldBe` expectedHtml
fromRight "" (mdToHtmlCompose readmeText) `shouldBe` expectedHtml
attrBuilderSpec :: Spec
attrBuilderSpec = parallel $ do
let testAttr = ("id", ["test"], [("k", "v")])
describe "addClass" $
it "adds a new class to attributes" $ do
nullAttr `addClass` "test" `shouldBe` ("", ["test"], [])
nullAttr `addClass` "test" `addClass` "test2" `shouldBe` ("", ["test2", "test"], [])
testAttr `addClass` "test2" `shouldBe` ("id", ["test2", "test"], [("k", "v")])
describe "addClasses" $
it "adds new classes to attributes" $ do
nullAttr `addClasses` ["test", "test2"] `shouldBe` ("", ["test", "test2"], [])
testAttr `addClasses` ["test1", "test2"] `shouldBe` ("id", ["test1", "test2", "test"], [("k", "v")])
describe "addKVPair" $
it "adds a new kv pair to attributes" $ do
nullAttr `addKVPair` ("k", "v") `shouldBe` ("", [], [("k", "v")])
testAttr `addKVPair` ("k2", "v2") `shouldBe` ("id", ["test"], [("k2", "v2"), ("k", "v")])
describe "addKVPairs" $
it "adds new kv pairs to attributes" $ do
nullAttr `addKVPairs` [("k", "v"), ("k2", "v2")] `shouldBe` ("", [], [("k", "v"), ("k2", "v2")])
testAttr `addKVPairs` [("k1", "v1"), ("k2", "v2")] `shouldBe` ("id", ["test"], [("k1", "v1"), ("k2", "v2"), ("k", "v")])
describe "setId" $
it "sets id of attributes" $ do
nullAttr `setId` "id" `shouldBe` ("id", [], [])
testAttr `setId` "id2" `shouldBe` ("id2", ["test"], [("k", "v")])
main :: IO ()
main = do
testConvert <- testSpec "Filter conversion" convertSpec
testCompose <- testSpec "Filter composition" composeSpec
testReadme <- testSpec "Readme examples" readmeSpec
testAttrBuilder <- testSpec "Attr builder" attrBuilderSpec
defaultMain $ testGroup "Tests"
[ testConvert
, testCompose
, testReadme
, testAttrBuilder
]