{-# LANGUAGE BlockArguments #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeOperators #-} module Main (main) where import Data.Foldable (find) import Data.Text (Text) import qualified Ide.Plugin.QualifyImportedNames as QualifyImportedNames import System.FilePath (()) import Test.Hls (CodeAction (CodeAction, _title), Command (Command), IdeState, MonadIO (liftIO), PluginDescriptor, Position (Position), Range (Range), Session, TestName, TestTree, TextDocumentIdentifier, assertBool, assertFailure, defaultTestRunner, executeCodeAction, getCodeActions, goldenWithHaskellDoc, openDoc, rename, runSessionWithServer, testCase, testGroup, type (|?) (InR), (@?=)) import Prelude -- 1's based data Point = Point { line :: !Int, column :: !Int } makePoint line column | line >= 1 && column >= 1 = Point line column | otherwise = error "Line or column is less than 1." isNotEmpty :: Foldable f => f a -> Bool isNotEmpty = not . isEmpty isEmpty :: Foldable f => f a -> Bool isEmpty = null makeCodeActionNotFoundAtString :: Point -> String makeCodeActionNotFoundAtString Point {..} = "CodeAction not found at line: " <> show line <> ", column: " <> show column makeCodeActionFoundAtString :: Point -> String makeCodeActionFoundAtString Point {..} = "CodeAction found at line: " <> show line <> ", column: " <> show column main :: IO () main = defaultTestRunner $ testGroup "Qualify Imported Names" [ testCase "No CodeAction when not at import" $ runSessionWithServer pluginDescriptor testDataDir $ do let point = makePoint 1 1 document <- openDoc "NoImport.hs" "haskell" actions <- getCodeActions document $ pointToRange point liftIO $ assertBool (makeCodeActionFoundAtString point) (isEmpty actions) , testCase "No CodeAction when import is qualified" $ runSessionWithServer pluginDescriptor testDataDir $ do let point = makePoint 3 1 document <- openDoc "QualifiedImport.hs" "haskell" actions <- getCodeActions document $ pointToRange point liftIO $ assertBool (makeCodeActionFoundAtString point) (isEmpty actions) , codeActionGoldenTest "CodeAction qualifies names with alias if imported module is aliased" "AliasedImport" (makePoint 3 1) , codeActionGoldenTest "CodeAction qualifies names with module name if imported module is not aliased" "UnaliasedImport" (makePoint 3 1) , codeActionGoldenTest "CodeAction qualifies only names in import's explicit non-hiding list" "ExplicitImport" (makePoint 4 1) , codeActionGoldenTest "CodeAction qualifies only names outside of import's explicit hiding list" "ExplicitHidingImport" (makePoint 4 1) , codeActionGoldenTest "CodeAction can qualify names not defined in modules they are imported from" "Reexported" (makePoint 3 1) , codeActionGoldenTest "CodeAction can qualify explicitly imported Prelude" "ExplicitPrelude" (makePoint 3 1) , codeActionGoldenTest "CodeAction qualifies only imported names" "OnlyImportedNames" (makePoint 3 1) , codeActionGoldenTest "CodeAction qualifies parenthesized operators properly" "Parenthesized" (makePoint 3 1) , codeActionGoldenTest "CodeAction qualifies backticked operators properly" "Backticked" (makePoint 3 1) , codeActionGoldenTest "CodeAction qualifies parenthesized and backticked operators on the same line properly" "SameLine" (makePoint 3 1) , codeActionGoldenTest "CodeAction doesn't qualify already qualified names" "NoDoubleQualify" (makePoint 3 1) ] codeActionGoldenTest :: TestName -> FilePath -> Point -> TestTree codeActionGoldenTest testCaseName goldenFilename point = goldenWithQualifyImportedNames testCaseName goldenFilename $ \document -> do actions <- getCodeActions document $ pointToRange point case find ((== Just "Qualify imported names") . getCodeActionTitle) actions of Just (InR codeAction) -> executeCodeAction codeAction _ -> liftIO $ assertFailure $ makeCodeActionNotFoundAtString point testDataDir :: String testDataDir = "test" "data" pluginDescriptor :: PluginDescriptor IdeState pluginDescriptor = QualifyImportedNames.descriptor "qualifyImportedNames" getCodeActionTitle :: (Command |? CodeAction) -> Maybe Text getCodeActionTitle commandOrCodeAction | InR CodeAction {_title} <- commandOrCodeAction = Just _title | otherwise = Nothing goldenWithQualifyImportedNames :: TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree goldenWithQualifyImportedNames testName path = goldenWithHaskellDoc pluginDescriptor testName testDataDir path "expected" "hs" pointToRange :: Point -> Range pointToRange Point {..} | line <- fromIntegral $ subtract 1 line , column <- fromIntegral $ subtract 1 column = Range (Position line column) (Position line $ column + 1)