@@ -0,0 +1,290 @@
3+import Control.Exception
7+import Data.List.NonEmpty (NonEmpty(..))
8+import qualified Data.List.NonEmpty as NE
10+import Data.Text (Text)
12+import Text.Shakespeare.Text (lt)
14+import Text.XML.DOM.Parser
17+data TestStructure = TestStructure
20+ , tsBools :: NonEmpty Bool
21+ } deriving (Eq, Show)
23+testStructureFromDom :: (Monad m) => DomParserT Identity m TestStructure
24+testStructureFromDom = do
25+ tsName <- inElem "name" fromDom
26+ tsInts <- inElemAll "int" intFromDom
27+ tsBools <- inElemNe "bool" boolFromDom
28+ return TestStructure{..}
30+instance FromDom TestStructure where
31+ fromDom = testStructureFromDom
34+docStruct = parseText_ def [lt|
35+<?xml version="1.0" encoding="utf-8"?>
45+docNone = parseText_ def [lt|
46+<?xml version="1.0" encoding="utf-8"?>
52+docEmpty = parseText_ def [lt|
53+<?xml version="1.0" encoding="utf-8"?>
60+docSimple = parseText_ def [lt|
61+<?xml version="1.0" encoding="utf-8"?>
67+docMultiple :: Document
68+docMultiple = parseText_ def [lt|
69+<?xml version="1.0" encoding="utf-8"?>
78+docDeep = parseText_ def [lt|
79+<?xml version="1.0" encoding="utf-8"?>
81+ <a><b>content</b></a>
85+docDeepWithContent :: Document
86+docDeepWithContent = parseText_ def [lt|
87+<?xml version="1.0" encoding="utf-8"?>
89+ <a>content<b>content</b>content</a>
93+docDeepMultiple1 :: Document
94+docDeepMultiple1 = parseText_ def [lt|
95+<?xml version="1.0" encoding="utf-8"?>
108+docDeepMultiple2 :: Document
109+docDeepMultiple2 = parseText_ def [lt|
110+<?xml version="1.0" encoding="utf-8"?>
124+ :: String -- ^ Name of spec
125+ -> Document -- ^ Document to parse
126+ -> (a -> Maybe String) -- ^ Value checker, Nothing if all ok
127+ -> DomParser Identity a -- ^ Parser itself
129+specParser name doc check parser = it name $ do
130+ result <- either throwIO return $ runDomParser doc parser
131+ case check result of
132+ Nothing -> return ()
133+ Just e -> throwIO $ ErrorCall e
137+ => String -- ^ Name of spec
138+ -> Document -- ^ Document to parse
139+ -> a -- ^ Value parser should return
140+ -> DomParser Identity a -- ^ Parser itself
142+specParserEq name doc a parser = specParser name doc check parser
144+ check x | x == a = Nothing
145+ | otherwise = Just $ "should be " ++ show a
146+ ++ " but got " ++ show x
152+ -> DomParser Identity a
154+specParserFailed name doc parser = it name $ example $ do
155+ let result = runDomParser doc parser
157+ Right a -> fail $ "Expected parser to fail, but returned" ++ (show a)
162+ => String -- ^ Name of spec
164+ -> [Text] -- ^ Expteced path in error
165+ -> DomParser Identity a
167+specParserFailedPath name doc path parser = it name $ example $ do
168+ let result = runDomParser doc parser
170+ Right a -> fail $ "Expected parser to fail, but returned" ++ (show a)
171+ Left (ParserErrors errs) -> case errs ^? folded . pePath of
172+ Nothing -> fail $ "Parser failed, but got no errors to analyze path"
173+ Just p | p == path -> return ()
174+ | otherwise -> fail $ "Path is not exptected: " <> show p
176+combinationsSpec :: Spec
177+combinationsSpec = do
178+ describe "inElem" $ do
179+ let parser = inElem "a" textFromDom
180+ describe "succeeds" $ do
181+ specParserEq "docSimple" docSimple "content" parser
182+ specParserEq "docMultiple" docMultiple "content1" parser
183+ describe "fails" $ do
184+ specParserFailed "docNone" docNone parser
185+ specParserFailed "docEmpty" docEmpty parser
187+ describe "inElemAll" $ do
188+ let parser = inElemAll "a" textFromDom
189+ describe "succeeds" $ do
190+ specParserEq "docNone" docNone [] parser
191+ specParserEq "docSimple" docSimple ["content"] parser
192+ specParserEq "docMultiple" docMultiple
193+ ["content1", "content2", "content3"] parser
194+ describe "fails" $ do
195+ specParserFailed "docEmpty" docEmpty parser
197+ describe "inElemMay" $ do
198+ let parser = inElemMay "a" textFromDom
199+ describe "succeeds" $ do
200+ specParserEq "docNone" docNone Nothing parser
201+ specParserEq "docSimple" docSimple (Just "content") parser
202+ specParserEq "docMultiple" docMultiple (Just "content1") parser
203+ describe "fails" $ do
204+ specParserFailed "docEmpty" docEmpty parser
206+ describe "inElemNe" $ do
207+ let parser = inElemNe "a" textFromDom
208+ describe "succeeds" $ do
209+ specParserEq "docSimple" docSimple (pure "content") parser
210+ specParserEq "docMultiple" docMultiple
211+ (NE.fromList ["content1", "content2", "content3"]) parser
212+ describe "fails" $ do
213+ specParserFailed "docNone" docNone parser
214+ specParserFailed "docEmpty" docEmpty parser
216+ describe "diveElem" $ do
217+ describe "single element" $ do
218+ let parser = diveElem "a" $ inElem "b" textFromDom
219+ describe "succeeds" $ do
220+ specParserEq "docDeepMultiple1" docDeepMultiple1 "content1" parser
221+ specParserEq "docDeepMultiple2" docDeepMultiple2 "content1" parser
222+ describe "fails" $ do
223+ specParserFailedPath "docSimple" docSimple ["root", "a", "b"] parser
224+ describe "multiple elements" $ do
226+ parser = diveElem "a" $ inElemAll "b" textFromDom
227+ result = ["content1", "content2", "content3"]
228+ describe "succeeds" $ do
229+ specParserEq "docDeepMultiple1" docDeepMultiple1 result parser
230+ specParserEq "docDeepMultiple2" docDeepMultiple2 result parser
231+ specParserEq "docSimple" docSimple [] parser
233+ describe "ignoreEmpty" $ do
234+ describe "mandatory element" $ do
235+ let parser = inElem "a" $ ignoreEmpty textFromDom
236+ describe "succeeds" $ do
237+ specParserEq "docEmpty" docEmpty Nothing parser
238+ specParserEq "docSimple" docSimple (Just "content") parser
239+ specParserEq "docMultiple" docMultiple (Just "content1") parser
240+ describe "fails" $ do
241+ specParserFailed "docNone" docNone parser
242+ describe "optional element" $ do
243+ let parser = fmap join $ inElemMay "a" $ ignoreEmpty textFromDom
244+ describe "succeeds" $ do
245+ specParserEq "docNone" docNone Nothing parser
246+ specParserEq "docEmpty" docEmpty Nothing parser
247+ specParserEq "docSimple" docSimple (Just "content") parser
248+ specParserEq "docMultiple" docMultiple (Just "content1") parser
250+ describe "checkCurrentName" $ do
251+ describe "succeeded" $ do
252+ specParserEq "root element" docSimple () $ do
253+ checkCurrentName "root"
254+ specParserEq "inner element" docSimple () $ do
256+ checkCurrentName "a"
257+ describe "fails" $ do
258+ specParserFailedPath "root element" docSimple ["toor"] $ do
259+ checkCurrentName "toor"
260+ specParserFailedPath "inner element" docSimple ["root", "b"] $ do
261+ inElem "a" $ checkCurrentName "b"
265+ describe "fails if inner element found" $ do
266+ specParserFailed "docDeep" docDeep $ do
267+ inElem "a" $ textFromDom
268+ specParserFailed "docDeepWithContent" docDeepWithContent $ do
269+ inElem "a" $ textFromDom
273+ describe "succeeds" $ do
275+ result = TestStructure
278+ , tsBools = pure True }
279+ specParserEq "docStruct" docStruct result fromDom
280+ describe "fails" $ do
281+ specParserFailed "docSimple" docSimple testStructureFromDom
285+ describe "combinators" $ do
287+ describe "content parsing" $ do
289+ describe "expected struct" $ do