restman-0.7.3.0: test/LibSpec.hs
{-# language OverloadedStrings #-}
module LibSpec (tests) where
import Data.Maybe (isJust, isNothing)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as Text
import Test.Tasty
import Test.Tasty.HUnit
import qualified Network.HTTP.Client as HC
import Brick.Types (CursorLocation(..), Location(..))
import qualified Data.CaseInsensitive as CI
import Graphics.Vty.Attributes
(Attr(attrStyle), MaybeDefault(..), Style, currentAttr, reverseVideo)
import qualified Graphics.Vty.Attributes as VA
import Graphics.Vty.Image (charFill, emptyImage, imageHeight, imageWidth)
import HTTP.Client (UseDefaultHeaders(..))
import Lib
( chooseCursor
, cleanPayload
, focusAttr
, focusStyle
, growWith
, lbsToText
, moveFocusNext
, moveFocusPrev
, noSyntaxFound
, splitExtraSpace
, syntaxHighlightResponse
)
import Types
( AppR(..)
, AppS(..)
, CustomHeaderColumn(..)
, CustomHeaderR(..)
, RangeInAlign(..)
)
import UI (mkInitialState)
tests :: TestTree
tests = testGroup "Lib"
[ splitExtraSpaceTests
, cleanPayloadTests
, lbsToTextTests
, focusStyleTests
, focusAttrTests
, moveFocusNextTests
, moveFocusPrevTests
, moveFocusNextCustomHeaderTests
, moveFocusPrevCustomHeaderTests
, growSpacesTests
, chooseCursorTests
, syntaxHighlightResponseTests
]
-- ---------------------------------------------------------------------------
-- splitExtraSpace
-- ---------------------------------------------------------------------------
splitExtraSpaceTests :: TestTree
splitExtraSpaceTests = testGroup "splitExtraSpace"
[ testCase "Min 0 = (0, 0)" $
splitExtraSpace Min 0 @?= (0, 0)
, testCase "Min 1 = (0, 1)" $
splitExtraSpace Min 1 @?= (0, 1)
, testCase "Min 5 = (0, 5)" $
splitExtraSpace Min 5 @?= (0, 5)
, testCase "Min: left is always 0" $
fst (splitExtraSpace Min 10) @?= 0
, testCase "Max 0 = (0, 0)" $
splitExtraSpace Max 0 @?= (0, 0)
, testCase "Max 1 = (1, 0)" $
splitExtraSpace Max 1 @?= (1, 0)
, testCase "Max 5 = (5, 0)" $
splitExtraSpace Max 5 @?= (5, 0)
, testCase "Max: right is always 0" $
snd (splitExtraSpace Max 10) @?= 0
, testCase "MidL 0 = (0, 0)" $
splitExtraSpace MidL 0 @?= (0, 0)
, testCase "MidL 1 = (0, 1)" $
splitExtraSpace MidL 1 @?= (0, 1)
, testCase "MidL 2 = (1, 1)" $
splitExtraSpace MidL 2 @?= (1, 1)
, testCase "MidL 3 = (1, 2) (left gets the smaller half)" $
splitExtraSpace MidL 3 @?= (1, 2)
, testCase "MidL: left + right = n (even)" $
let (l, r) = splitExtraSpace MidL 6 in l + r @?= 6
, testCase "MidL: left + right = n (odd)" $
let (l, r) = splitExtraSpace MidL 7 in l + r @?= 7
, testCase "MidG 0 = (0, 0)" $
splitExtraSpace MidG 0 @?= (0, 0)
, testCase "MidG 1 = (1, 0)" $
splitExtraSpace MidG 1 @?= (1, 0)
, testCase "MidG 2 = (1, 1)" $
splitExtraSpace MidG 2 @?= (1, 1)
, testCase "MidG 3 = (2, 1) (left gets the larger half)" $
splitExtraSpace MidG 3 @?= (2, 1)
, testCase "MidG: left + right = n (even)" $
let (l, r) = splitExtraSpace MidG 6 in l + r @?= 6
, testCase "MidG: left + right = n (odd)" $
let (l, r) = splitExtraSpace MidG 7 in l + r @?= 7
, testCase "MidL and MidG are symmetric for even n" $
splitExtraSpace MidL 8 @?= splitExtraSpace MidG 8
, testCase "MidL and MidG swap halves for odd n" $
let (lL, rL) = splitExtraSpace MidL 5
(lG, rG) = splitExtraSpace MidG 5
in (lL, rL) @?= (rG, lG)
]
-- ---------------------------------------------------------------------------
-- cleanPayload
-- ---------------------------------------------------------------------------
cleanPayloadTests :: TestTree
cleanPayloadTests = testGroup "cleanPayload"
[ testCase "empty input gives empty output" $
cleanPayload LBS.empty @?= Text.empty
, testCase "plain ASCII is unchanged" $
cleanPayload "hello world" @?= "hello world"
, testCase "ESC is stripped" $
cleanPayload "\ESCfoo" @?= "foo"
, testCase "multiple ESC characters are all stripped" $
cleanPayload "\ESCa\ESCb" @?= "ab"
, testCase "vertical tab is stripped" $
cleanPayload "\vfoo" @?= "foo"
, testCase "tab is expanded to 4 spaces" $
cleanPayload "\thello" @?= " hello"
, testCase "multiple tabs each expand to 4 spaces" $
cleanPayload "\t\t" @?= " "
, testCase "ESC stripped before tab is expanded" $
cleanPayload "\ESC\tfoo" @?= " foo"
, testCase "newlines are preserved" $
cleanPayload "line1\nline2" @?= "line1\nline2"
, testCase "carriage return is preserved" $
cleanPayload "foo\rbar" @?= "foo\rbar"
, testCase "only special chars gives empty output" $
cleanPayload "\ESC\v" @?= Text.empty
, testCase "ESC and vtab together are both stripped" $
cleanPayload "\ESC\vhello" @?= "hello"
, testCase "tab followed by ESC: tab expands, ESC stripped" $
cleanPayload "\t\ESC" @?= " "
]
-- ---------------------------------------------------------------------------
-- lbsToText
-- ---------------------------------------------------------------------------
lbsToTextTests :: TestTree
lbsToTextTests = testGroup "lbsToText"
[ testCase "empty bytes give empty text" $
lbsToText LBS.empty @?= Text.empty
, testCase "ASCII roundtrips" $
lbsToText "hello" @?= "hello"
, testCase "UTF-8 multi-byte sequence decodes correctly" $
-- 0xC3 0xA9 is the UTF-8 encoding of U+00E9 (é)
lbsToText "\xc3\xa9" @?= "\xe9"
, testCase "invalid UTF-8 decodes leniently to replacement character" $
-- 0xFF is never a valid UTF-8 byte; lenientDecode substitutes U+FFFD
lbsToText "\xff" @?= "\xfffd"
]
-- ---------------------------------------------------------------------------
-- focusStyle
-- ---------------------------------------------------------------------------
focusStyleTests :: TestTree
focusStyleTests = testGroup "focusStyle"
[ testCase "focusStyle 0 = reverseVideo" $
focusStyle (0 :: Style) @?= reverseVideo
, testCase "focusStyle reverseVideo = 0" $
focusStyle reverseVideo @?= (0 :: Style)
, testCase "applying twice is identity (involutory)" $
focusStyle (focusStyle (0 :: Style)) @?= (0 :: Style)
, testCase "applying twice to reverseVideo is identity" $
focusStyle (focusStyle reverseVideo) @?= reverseVideo
]
-- ---------------------------------------------------------------------------
-- focusAttr
-- ---------------------------------------------------------------------------
focusAttrTests :: TestTree
focusAttrTests = testGroup "focusAttr"
[ testCase "KeepCurrent style becomes SetTo reverseVideo" $
-- currentAttr has KeepCurrent for all fields
attrStyle (focusAttr currentAttr) @?= SetTo reverseVideo
, testCase "Default style becomes SetTo reverseVideo" $
attrStyle (focusAttr currentAttr { attrStyle = Default }) @?= SetTo reverseVideo
, testCase "SetTo 0 becomes SetTo reverseVideo" $
attrStyle (focusAttr currentAttr { attrStyle = SetTo 0 }) @?= SetTo reverseVideo
, testCase "SetTo reverseVideo becomes SetTo 0" $
attrStyle (focusAttr currentAttr { attrStyle = SetTo reverseVideo }) @?= SetTo (0 :: Style)
, testCase "non-style fields are unchanged" $
let a = focusAttr currentAttr
in (attrStyle a == SetTo reverseVideo) @?= True
]
-- ---------------------------------------------------------------------------
-- moveFocusNext (focus cycle with no custom headers)
-- ---------------------------------------------------------------------------
moveFocusNextTests :: TestTree
moveFocusNextTests = testGroup "moveFocusNext"
[ testCase "MethodEditor -> UrlEditor" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = base mgr UrlEditor -- start at UrlEditor, override to MethodEditor
$ mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing
focus (moveFocusNext s) @?= UrlEditor
, testCase "UrlEditor -> DefaultHeadersToggle" $ do
mgr <- HC.newManager HC.defaultManagerSettings
-- mkInitialState starts focus at UrlEditor
let s = mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing
focus (moveFocusNext s) @?= DefaultHeadersToggle
, testCase "DefaultHeadersToggle -> AddCustomHeader (no headers)" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = withFocus DefaultHeadersToggle
(mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
focus (moveFocusNext s) @?= AddCustomHeader
, testCase "AddCustomHeader -> ResponseBodyView" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = withFocus AddCustomHeader
(mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
focus (moveFocusNext s) @?= ResponseBodyView
, testCase "ResponseBodyView -> MethodEditor" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = withFocus ResponseBodyView
(mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
focus (moveFocusNext s) @?= MethodEditor
, testCase "MethodSelector -> MethodEditor (invalid focus)" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = withFocus MethodSelector
(mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
focus (moveFocusNext s) @?= MethodEditor
]
where
withFocus f s = s { focus = f }
base _mgr _f = withFocus MethodEditor
-- ---------------------------------------------------------------------------
-- moveFocusPrev (focus cycle with no custom headers)
-- ---------------------------------------------------------------------------
moveFocusPrevTests :: TestTree
moveFocusPrevTests = testGroup "moveFocusPrev"
[ testCase "MethodEditor -> ResponseBodyView" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = withFocus MethodEditor
(mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
focus (moveFocusPrev s) @?= ResponseBodyView
, testCase "UrlEditor -> MethodEditor" $ do
mgr <- HC.newManager HC.defaultManagerSettings
-- mkInitialState starts focus at UrlEditor
let s = mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing
focus (moveFocusPrev s) @?= MethodEditor
, testCase "DefaultHeadersToggle -> UrlEditor" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = withFocus DefaultHeadersToggle
(mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
focus (moveFocusPrev s) @?= UrlEditor
, testCase "AddCustomHeader -> DefaultHeadersToggle (no headers)" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = withFocus AddCustomHeader
(mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
focus (moveFocusPrev s) @?= DefaultHeadersToggle
, testCase "ResponseBodyView -> AddCustomHeader" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = withFocus ResponseBodyView
(mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
focus (moveFocusPrev s) @?= AddCustomHeader
, testCase "MethodSelector -> ResponseBodyView (invalid focus)" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = withFocus MethodSelector
(mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
focus (moveFocusPrev s) @?= ResponseBodyView
]
where
withFocus f s = s { focus = f }
-- ---------------------------------------------------------------------------
-- moveFocusNext / moveFocusPrev with custom headers
-- ---------------------------------------------------------------------------
-- One fake header
oneHeader :: [(Bool, (CI.CI BS.ByteString, BS.ByteString))]
oneHeader = [(True, (CI.mk "X-Test", "value"))]
-- Two fake headers
twoHeaders :: [(Bool, (CI.CI BS.ByteString, BS.ByteString))]
twoHeaders = [ (True, (CI.mk "X-First", "one"))
, (True, (CI.mk "X-Second", "two"))
]
moveFocusNextCustomHeaderTests :: TestTree
moveFocusNextCustomHeaderTests = testGroup "moveFocusNext (custom headers)"
[ testCase "DefaultHeadersToggle -> ExistingCustomHeader 0 ActiveToggle (with 1 header)" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = DefaultHeadersToggle, customHeaders = oneHeader }
focus (moveFocusNext s) @?=
ExistingCustomHeader (MkCustomHeaderR { listIndex = 0, column = ActiveToggle })
, testCase "ExistingCustomHeader 0 ActiveToggle -> NameEditor" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = ExistingCustomHeader (MkCustomHeaderR 0 ActiveToggle)
, customHeaders = oneHeader }
focus (moveFocusNext s) @?=
ExistingCustomHeader (MkCustomHeaderR { listIndex = 0, column = NameEditor })
, testCase "ExistingCustomHeader 0 NameEditor -> ValueEditor" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = ExistingCustomHeader (MkCustomHeaderR 0 NameEditor)
, customHeaders = oneHeader }
focus (moveFocusNext s) @?=
ExistingCustomHeader (MkCustomHeaderR { listIndex = 0, column = ValueEditor })
, testCase "ExistingCustomHeader 0 ValueEditor (last header) -> AddCustomHeader" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = ExistingCustomHeader (MkCustomHeaderR 0 ValueEditor)
, customHeaders = oneHeader }
focus (moveFocusNext s) @?= AddCustomHeader
, testCase "ExistingCustomHeader 0 ValueEditor (2 headers) -> next header ActiveToggle" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = ExistingCustomHeader (MkCustomHeaderR 0 ValueEditor)
, customHeaders = twoHeaders }
focus (moveFocusNext s) @?=
ExistingCustomHeader (MkCustomHeaderR { listIndex = 1, column = ActiveToggle })
]
moveFocusPrevCustomHeaderTests :: TestTree
moveFocusPrevCustomHeaderTests = testGroup "moveFocusPrev (custom headers)"
[ testCase "AddCustomHeader -> ExistingCustomHeader 0 ValueEditor (1 header)" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = AddCustomHeader, customHeaders = oneHeader }
focus (moveFocusPrev s) @?=
ExistingCustomHeader (MkCustomHeaderR { listIndex = 0, column = ValueEditor })
, testCase "ExistingCustomHeader 0 NameEditor -> ActiveToggle" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = ExistingCustomHeader (MkCustomHeaderR 0 NameEditor)
, customHeaders = oneHeader }
focus (moveFocusPrev s) @?=
ExistingCustomHeader (MkCustomHeaderR { listIndex = 0, column = ActiveToggle })
, testCase "ExistingCustomHeader 0 ValueEditor -> NameEditor" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = ExistingCustomHeader (MkCustomHeaderR 0 ValueEditor)
, customHeaders = oneHeader }
focus (moveFocusPrev s) @?=
ExistingCustomHeader (MkCustomHeaderR { listIndex = 0, column = NameEditor })
, testCase "ExistingCustomHeader 0 ActiveToggle (first header) -> DefaultHeadersToggle" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = ExistingCustomHeader (MkCustomHeaderR 0 ActiveToggle)
, customHeaders = oneHeader }
focus (moveFocusPrev s) @?= DefaultHeadersToggle
, testCase "ExistingCustomHeader 1 ActiveToggle -> previous header ValueEditor" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ focus = ExistingCustomHeader (MkCustomHeaderR 1 ActiveToggle)
, customHeaders = twoHeaders }
focus (moveFocusPrev s) @?=
ExistingCustomHeader (MkCustomHeaderR { listIndex = 0, column = ValueEditor })
]
-- ---------------------------------------------------------------------------
-- growWith / growSpaces
-- ---------------------------------------------------------------------------
growSpacesTests :: TestTree
growSpacesTests = testGroup "growSpaces"
[ testCase "grow emptyImage to 5 wide 3 tall: width == 5" $ do
let img = growWith (charFill VA.currentAttr ' ') 5 Min 3 Min emptyImage
imageWidth img @?= 5
, testCase "grow emptyImage to 5 wide 3 tall: height == 3" $ do
let img = growWith (charFill VA.currentAttr ' ') 5 Min 3 Min emptyImage
imageHeight img @?= 3
, testCase "grow to 0 wide 0 tall is a no-op on emptyImage" $ do
let img = growWith (charFill VA.currentAttr ' ') 0 Min 0 Min emptyImage
imageWidth img @?= 0
imageHeight img @?= 0
, testCase "image already wider than min: width unchanged" $ do
let src = charFill VA.currentAttr '*' 10 1
img = growWith (charFill VA.currentAttr ' ') 5 Min 1 Min src
imageWidth img @?= 10
, testCase "image already taller than min: height unchanged" $ do
let src = charFill VA.currentAttr '*' 1 10
img = growWith (charFill VA.currentAttr ' ') 1 Min 5 Min src
imageHeight img @?= 10
, testCase "Max wide alignment: grows to min width" $ do
let img = growWith (charFill VA.currentAttr ' ') 10 Max 1 Min (charFill VA.currentAttr '*' 2 1)
imageWidth img @?= 10
, testCase "Max vert alignment: grows to min height" $ do
let img = growWith (charFill VA.currentAttr ' ') 1 Min 5 Max (charFill VA.currentAttr '*' 1 2)
imageHeight img @?= 5
, testCase "MidL wide alignment: grows to min width" $ do
let img = growWith (charFill VA.currentAttr ' ') 6 MidL 1 Min (charFill VA.currentAttr '*' 2 1)
imageWidth img @?= 6
, testCase "MidG wide alignment: grows to min width" $ do
let img = growWith (charFill VA.currentAttr ' ') 6 MidG 1 Min (charFill VA.currentAttr '*' 2 1)
imageWidth img @?= 6
]
-- ---------------------------------------------------------------------------
-- chooseCursor
-- ---------------------------------------------------------------------------
chooseCursorTests :: TestTree
chooseCursorTests = testGroup "chooseCursor"
[ testCase "no overlay, empty cursor list -> Nothing" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing
assertBool "expected Nothing" (isNothing (chooseCursor s []))
, testCase "no overlay, UrlEditor cursor in list -> returns it" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing
loc = CursorLocation (Location (0, 0)) (Just UrlEditor) True
result = chooseCursor s [loc]
assertBool "expected Just" (isJust result)
, testCase "no overlay, wrong name cursor -> Nothing" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing
loc = CursorLocation (Location (0, 0)) (Just MethodEditor) True
assertBool "expected Nothing" (isNothing (chooseCursor s [loc]))
, testCase "overlay present, empty cursor list -> Nothing" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ overlayState = Just undefined }
assertBool "expected Nothing" (isNothing (chooseCursor s []))
, testCase "overlay present, MethodSelector cursor -> returns it" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ overlayState = Just undefined }
loc = CursorLocation (Location (0, 0)) (Just MethodSelector) True
result = chooseCursor s [loc]
assertBool "expected Just" (isJust result)
, testCase "overlay present, non-MethodSelector cursor -> Nothing" $ do
mgr <- HC.newManager HC.defaultManagerSettings
let s = (mkInitialState mgr "GET" ReplaceDefaultHeaders [] Nothing Nothing)
{ overlayState = Just undefined }
loc = CursorLocation (Location (0, 0)) (Just UrlEditor) True
assertBool "expected Nothing" (isNothing (chooseCursor s [loc]))
]
-- ---------------------------------------------------------------------------
-- syntaxHighlightResponse / noSyntaxFound
-- ---------------------------------------------------------------------------
syntaxHighlightResponseTests :: TestTree
syntaxHighlightResponseTests = testGroup "syntaxHighlightResponse"
[ testCase "Nothing payload -> non-empty 'no response body' image" $ do
let img = syntaxHighlightResponse Nothing Nothing
assertBool "expected non-empty image" (imageWidth img > 0 || imageHeight img > 0)
, testCase "Just payload, Nothing content-type -> non-empty image" $ do
let img = syntaxHighlightResponse (Just "hello world") Nothing
assertBool "expected non-empty image" (imageWidth img > 0 || imageHeight img > 0)
, testCase "Just payload, Just application/json content-type -> non-empty image" $ do
let img = syntaxHighlightResponse (Just "{\"a\":1}") (Just "application/json")
assertBool "expected non-empty image" (imageWidth img > 0 || imageHeight img > 0)
, testCase "Just payload, Just unknown content-type -> same height as noSyntaxFound" $ do
let img = syntaxHighlightResponse (Just "data") (Just "application/x-unknown-restman-test")
img2 = noSyntaxFound "data"
imageHeight img @?= imageHeight img2
, testCase "Just payload, Just text/plain content-type -> non-empty image" $ do
let img = syntaxHighlightResponse (Just "hello world") (Just "text/plain")
assertBool "expected non-empty image" (imageWidth img > 0 || imageHeight img > 0)
, testCase "Just payload, Just text/html content-type -> non-empty image" $ do
let img = syntaxHighlightResponse (Just "<p>hello</p>") (Just "text/html")
assertBool "expected non-empty image" (imageWidth img > 0 || imageHeight img > 0)
, testCase "noSyntaxFound empty text -> non-empty image (header line)" $ do
let img = noSyntaxFound Text.empty
assertBool "expected non-empty image" (imageWidth img > 0 || imageHeight img > 0)
, testCase "noSyntaxFound single-line text -> height is 2 (header + 1 line)" $ do
let img = noSyntaxFound "hello world"
imageHeight img @?= 2
, testCase "noSyntaxFound two-line text -> height is 3 (header + 2 lines)" $ do
let img = noSyntaxFound "line1\nline2"
imageHeight img @?= 3
]