{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ViewPatterns #-}
module Main
( main
) where
import Data.Foldable (find, forM_)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Ide.Plugin.ExplicitImports as ExplicitImports
import System.FilePath ((<.>), (</>))
import Test.Hls
explicitImportsPlugin :: PluginTestDescriptor ExplicitImports.Log
explicitImportsPlugin = mkPluginTestDescriptor ExplicitImports.descriptor "explicitImports"
longModule :: T.Text
longModule = "F" <> T.replicate 80 "o"
main :: IO ()
main = defaultTestRunner $
testGroup
"Make imports explicit"
[ codeActionGoldenTest "UsualCase" 3 0
, codeLensGoldenTest "UsualCase" 0
, testCase "No CodeAction when exported" $
runSessionWithServer explicitImportsPlugin testDataDir $ do
doc <- openDoc "Exported.hs" "haskell"
action <- getCodeActions doc (pointRange 3 0)
liftIO $ action @?= []
, testCase "No CodeLens when exported" $
runSessionWithServer explicitImportsPlugin testDataDir $ do
doc <- openDoc "Exported.hs" "haskell"
lenses <- getCodeLenses doc
liftIO $ lenses @?= []
, testGroup "Title abbreviation"
[ testCase "not abbreviated" $
let i = "import " <> T.replicate 70 "F" <> " (Athing, Bthing, Cthing)"
in ExplicitImports.abbreviateImportTitle i @?= i
, testCase "abbreviated in module name" $
let i = "import " <> T.replicate 120 "F" <> " (Athing, Bthing, Cthing)"
o = "import " <> T.replicate 97 "F" <> " ... (3 items)"
in ExplicitImports.abbreviateImportTitle i @?= o
, testCase "abbreviated in import list" $
let i = "import " <> T.replicate 78 "F" <> " (Athing, Bthing, Cthing, Dthing, Ething)"
o = "import " <> T.replicate 78 "F" <> " (Athing, Bthing, ... (3 items))"
in ExplicitImports.abbreviateImportTitle i @?= o
-- This one breaks earlier in the same import item, but still splits the list in the same place
, testCase "abbreviated in import list (slightly shorter module)" $
let i = "import " <> T.replicate 76 "F" <> " (Athing, Bthing, Cthing, Dthing, Ething)"
o = "import " <> T.replicate 76 "F" <> " (Athing, Bthing, ... (3 items))"
in ExplicitImports.abbreviateImportTitle i @?= o
-- This one breaks later in the same import item, but still splits the list in the same place
, testCase "abbreviated in import list (slightly longer module)" $
let i = "import " <> T.replicate 80 "F" <> " (Athing, Bthing, Cthing, Dthing, Ething)"
o = "import " <> T.replicate 80 "F" <> " (Athing, Bthing, ... (3 items))"
in ExplicitImports.abbreviateImportTitle i @?= o
]
]
-- code action tests
codeActionGoldenTest :: FilePath -> Int -> Int -> TestTree
codeActionGoldenTest fp l c = goldenWithExplicitImports fp $ \doc -> do
actions <- getCodeActions doc (pointRange l c)
case find ((== Just "Make all imports explicit") . caTitle) actions of
Just (InR x) -> executeCodeAction x
_ -> liftIO $ assertFailure "Unable to find CodeAction"
caTitle :: (Command |? CodeAction) -> Maybe Text
caTitle (InR CodeAction {_title}) = Just _title
caTitle _ = Nothing
-- code lens tests
codeLensGoldenTest :: FilePath -> Int -> TestTree
codeLensGoldenTest fp codeLensIdx = goldenWithExplicitImports fp $ \doc -> do
codeLens <- (!! codeLensIdx) <$> getCodeLensesBy isExplicitImports doc
mapM_ executeCmd
[c | CodeLens{_command = Just c} <- [codeLens]]
getCodeLensesBy :: (CodeLens -> Bool) -> TextDocumentIdentifier -> Session [CodeLens]
getCodeLensesBy f doc = filter f <$> getCodeLenses doc
isExplicitImports :: CodeLens -> Bool
isExplicitImports (CodeLens _ (Just (Command _ cmd _)) _)
| ":explicitImports:" `T.isInfixOf` cmd = True
isExplicitImports _ = False
-- Execute command and wait for result
executeCmd :: Command -> Session ()
executeCmd cmd = do
executeCommand cmd
_resp <- skipManyTill anyMessage (message SWorkspaceApplyEdit)
-- liftIO $ print _resp
return ()
-- helpers
goldenWithExplicitImports :: FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree
goldenWithExplicitImports fp = goldenWithHaskellDoc explicitImportsPlugin (fp <> " (golden)") testDataDir fp "expected" "hs"
testDataDir :: String
testDataDir = "test" </> "testdata"
pointRange :: Int -> Int -> Range
pointRange
(subtract 1 -> fromIntegral -> line)
(subtract 1 -> fromIntegral -> col) =
Range (Position line col) (Position line $ col + 1)