{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad.IO.Class (liftIO)
import Data.Maybe (fromJust)
import Language.Haskell.LSP.Test
import Language.Haskell.LSP.Types
( CompletionItem (..)
, Diagnostic (..)
, DiagnosticSeverity (..)
, Hover (..)
, HoverContents (..)
, MarkupContent (..)
, Position (..)
, Range (..)
)
import Test.Tasty
import Test.Tasty.Hspec
import qualified Data.Text as T
import qualified GHC.IO.Encoding
baseDir :: FilePath -> FilePath
baseDir d = "tests/fixtures/" <> d
hoveringSpec :: FilePath -> Spec
hoveringSpec dir =
describe "Dhall.Hover"
$ it "reports types on hover"
$ runSession "dhall-lsp-server" fullCaps dir
$ do
docId <- openDoc "Types.dhall" "dhall"
let typePos = Position 0 5
functionPos = Position 2 7
extractContents = _contents . fromJust
getValue = T.unpack . _value
typeHover <- getHover docId typePos
funcHover <- getHover docId functionPos
liftIO $ do
case (extractContents typeHover, extractContents funcHover) of
(HoverContents typeContent, HoverContents functionContent) -> do
getValue typeContent `shouldBe` "Type"
getValue functionContent `shouldBe` "\8704(_isAdmin : Bool) \8594 { home : Text, name : Text }"
_ -> error "test failed"
pure ()
lintingSpec :: FilePath -> Spec
lintingSpec fixtureDir =
describe "Dhall.Lint" $ do
it "reports unused bindings"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
_ <- openDoc "UnusedBindings.dhall" "dhall"
diags <- waitForDiagnosticsSource "Dhall.Lint"
liftIO $ diags `shouldBe`
[ Diagnostic
{ _range = Range
{_start = Position { _line = 2, _character = 10 }
, _end = Position { _line = 2, _character = 36 }
}
, _severity = Just DsHint
, _code = Nothing
, _source = Just "Dhall.Lint"
, _message = "Unused let binding 'bob'"
, _tags = Nothing
, _relatedInformation = Nothing
}
, Diagnostic
{ _range = Range
{ _start = Position { _line = 4, _character = 11 }
, _end = Position { _line = 4, _character = 38 }
}
, _severity = Just DsHint
, _code = Nothing
, _source = Just "Dhall.Lint"
, _message = "Unused let binding 'carl'"
, _tags = Nothing
, _relatedInformation = Nothing
}
]
pure ()
it "reports multiple hints"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
_ <- openDoc "SuperfluousIn.dhall" "dhall"
diags <- waitForDiagnosticsSource "Dhall.Lint"
liftIO $ length diags `shouldBe` 2
let diag1 = head diags
diag2 = diags !! 1
liftIO $ do
_severity diag1 `shouldBe` Just DsHint
T.unpack (_message diag1) `shouldContain` "Superfluous 'in'"
_severity diag2 `shouldBe` Just DsHint
T.unpack (_message diag2) `shouldContain` "Unused let binding"
codeCompletionSpec :: FilePath -> Spec
codeCompletionSpec fixtureDir =
describe "Dhall.Completion" $ do
it "suggests user defined types"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
docId <- openDoc "CustomTypes.dhall" "dhall"
cs <- getCompletions docId (Position {_line = 2, _character = 35})
liftIO $ do
let firstItem = head cs
_label firstItem `shouldBe` "Config"
_detail firstItem `shouldBe` Just "Type"
it "suggests user defined functions"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
docId <- openDoc "CustomFunctions.dhall" "dhall"
cs <- getCompletions docId (Position {_line = 6, _character = 7})
liftIO $ do
let firstItem = head cs
_label firstItem `shouldBe` "makeUser"
_detail firstItem `shouldBe` Just "\8704(user : Text) \8594 { home : Text }"
it "suggests user defined bindings"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
docId <- openDoc "Bindings.dhall" "dhall"
cs <- getCompletions docId (Position {_line = 0, _character = 59})
liftIO $ do
let firstItem = head cs
_label firstItem `shouldBe` "bob"
_detail firstItem `shouldBe` Just "Text"
it "suggests functions from imports"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
docId <- openDoc "ImportedFunctions.dhall" "dhall"
cs <- getCompletions docId (Position {_line = 0, _character = 33})
liftIO $ do
let [ firstItem, secondItem ] = cs
_label firstItem `shouldBe` "`make user`"
_label secondItem `shouldBe` "makeUser"
_detail firstItem `shouldBe` Just "\8704(user : Text) \8594 { home : Text }"
_detail secondItem `shouldBe` Just "\8704(user : Text) \8594 { home : Text }"
it "suggests union alternatives"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
docId <- openDoc "Union.dhall" "dhall"
cs <- getCompletions docId (Position {_line = 2, _character = 10})
liftIO $ do
let [ firstItem, secondItem ] = cs
_label firstItem `shouldBe` "A"
_label secondItem `shouldBe` "`B C`"
_detail firstItem `shouldBe` Just "\8704(A : Text) \8594 < A : Text | `B C` >"
_detail secondItem `shouldBe` Just "< A : Text | `B C` >"
diagnosticsSpec :: FilePath -> Spec
diagnosticsSpec fixtureDir = do
describe "Dhall.TypeCheck" $ do
it "reports unbound variables"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
_ <- openDoc "UnboundVar.dhall" "dhall"
[diag] <- waitForDiagnosticsSource "Dhall.TypeCheck"
liftIO $ do
_severity diag `shouldBe` Just DsError
T.unpack (_message diag) `shouldContain` "Unbound variable"
it "reports wrong type"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
_ <- openDoc "WrongType.dhall" "dhall"
[diag] <- waitForDiagnosticsSource "Dhall.TypeCheck"
liftIO $ do
_severity diag `shouldBe` Just DsError
T.unpack (_message diag) `shouldContain` "Expression doesn't match annotation"
describe "Dhall.Import" $ do
it "reports invalid imports"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
_ <- openDoc "InvalidImport.dhall" "dhall"
[diag] <- waitForDiagnosticsSource "Dhall.Import"
liftIO $ do
_severity diag `shouldBe` Just DsError
T.unpack (_message diag) `shouldContain` "Invalid input"
it "reports missing imports"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
_ <- openDoc "MissingImport.dhall" "dhall"
[diag] <- waitForDiagnosticsSource "Dhall.Import"
liftIO $ do
_severity diag `shouldBe` Just DsError
T.unpack (_message diag) `shouldContain` "Missing file"
describe "Dhall.Parser"
$ it "reports invalid syntax"
$ runSession "dhall-lsp-server" fullCaps fixtureDir
$ do
_ <- openDoc "InvalidSyntax.dhall" "dhall"
[diag] <- waitForDiagnosticsSource "Dhall.Parser"
liftIO $ _severity diag `shouldBe` Just DsError
main :: IO ()
main = do
GHC.IO.Encoding.setLocaleEncoding GHC.IO.Encoding.utf8
diagnostics <- testSpec "Diagnostics" (diagnosticsSpec (baseDir "diagnostics"))
linting <- testSpec "Linting" (lintingSpec (baseDir "linting"))
completion <- testSpec "Completion" (codeCompletionSpec (baseDir "completion"))
hovering <- testSpec "Hovering" (hoveringSpec (baseDir "hovering"))
defaultMain
( testGroup "Tests"
[ diagnostics,
linting,
completion,
hovering
]
)