diff options
author | PhilFreeman <> | 2017-11-15 02:50:00 (GMT) |
---|---|---|
committer | hdiff <hdiff@hdiff.luite.com> | 2017-11-15 02:50:00 (GMT) |
commit | b725646839c21c8b8d698c862b6f3b7e88acaa8e (patch) | |
tree | 4c57d5ac61077ccf9170929092736fcf017c33b3 /tests | |
parent | bc80a9937fd209e5553541b5abddf8010f1e3b31 (diff) |
version 0.11.70.11.7
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Language/PureScript/Ide/CompletionSpec.hs | 34 | ||||
-rw-r--r-- | tests/Language/PureScript/Ide/ImportsSpec.hs | 151 | ||||
-rw-r--r-- | tests/Language/PureScript/Ide/RebuildSpec.hs | 14 | ||||
-rw-r--r-- | tests/Language/PureScript/Ide/SourceFileSpec.hs | 6 | ||||
-rw-r--r-- | tests/Language/PureScript/Ide/Test.hs | 10 | ||||
-rw-r--r-- | tests/Main.hs | 6 | ||||
-rw-r--r-- | tests/TestDocs.hs | 270 | ||||
-rw-r--r-- | tests/TestHierarchy.hs | 65 | ||||
-rw-r--r-- | tests/TestPsci/CommandTest.hs | 6 | ||||
-rw-r--r-- | tests/TestPsci/CompletionTest.hs | 10 | ||||
-rw-r--r-- | tests/TestPsci/TestEnv.hs | 39 | ||||
-rw-r--r-- | tests/support/bower.json | 32 | ||||
-rw-r--r-- | tests/support/pscide/src/CompletionSpecDocs.purs | 13 |
13 files changed, 543 insertions, 113 deletions
diff --git a/tests/Language/PureScript/Ide/CompletionSpec.hs b/tests/Language/PureScript/Ide/CompletionSpec.hs index 255d697..4df331a 100644 --- a/tests/Language/PureScript/Ide/CompletionSpec.hs +++ b/tests/Language/PureScript/Ide/CompletionSpec.hs @@ -5,10 +5,12 @@ module Language.PureScript.Ide.CompletionSpec where import Protolude import Language.PureScript as P +import Language.PureScript.Ide.Test as Test +import Language.PureScript.Ide.Command as Command import Language.PureScript.Ide.Completion -import Language.PureScript.Ide.Test import Language.PureScript.Ide.Types import Test.Hspec +import System.FilePath reexportMatches :: [Match IdeDeclarationAnn] reexportMatches = @@ -21,6 +23,15 @@ reexportMatches = matches :: [(Match IdeDeclarationAnn, [P.ModuleName])] matches = map (\d -> (Match (mn "Main", d), [mn "Main"])) [ ideKind "Kind", ideType "Type" Nothing [] ] +typ :: Text -> Command +typ txt = Type txt [] Nothing + +load :: [Text] -> Command +load = LoadSync . map Test.mn + +rebuildSync :: FilePath -> Command +rebuildSync fp = RebuildSync ("src" </> fp) Nothing + spec :: Spec spec = describe "Applying completion options" $ do it "keeps all matches if maxResults is not specified" $ do @@ -32,3 +43,24 @@ spec = describe "Applying completion options" $ do it "groups reexports for a single identifier" $ do applyCompletionOptions (defaultCompletionOptions { coGroupReexports = True }) reexportMatches `shouldBe` [(Match (mn "A", ideKind "Kind"), [mn "A", mn "B"])] + + it "gets simple docs on definition itself" $ do + ([_, (Right (CompletionResult [ result ]))], _) <- Test.inProject $ + Test.runIde [ load ["CompletionSpecDocs"] + , typ "something" + ] + result `shouldSatisfy` \res -> complDocumentation res == Just "Doc x\n" + + it "gets multiline docs" $ do + ([_, (Right (CompletionResult [ result ]))], _) <- Test.inProject $ + Test.runIde [ load ["CompletionSpecDocs"] + , typ "multiline" + ] + result `shouldSatisfy` \res -> complDocumentation res == Just "This is\na multi-line\ncomment\n" + + it "gets simple docs on type annotation" $ do + ([_, (Right (CompletionResult [ result ]))], _) <- Test.inProject $ + Test.runIde [ load ["CompletionSpecDocs"] + , typ "withType" + ] + result `shouldSatisfy` \res -> complDocumentation res == Just "Doc *123*\n"
\ No newline at end of file diff --git a/tests/Language/PureScript/Ide/ImportsSpec.hs b/tests/Language/PureScript/Ide/ImportsSpec.hs index e95309f..b7c8196 100644 --- a/tests/Language/PureScript/Ide/ImportsSpec.hs +++ b/tests/Language/PureScript/Ide/ImportsSpec.hs @@ -2,7 +2,7 @@ {-# LANGUAGE NoImplicitPrelude #-} module Language.PureScript.Ide.ImportsSpec where -import Protolude +import Protolude hiding (moduleName) import Data.Maybe (fromJust) import qualified Language.PureScript as P @@ -29,6 +29,15 @@ simpleFile = , "myFunc x y = x + y" ] +hidingFile :: [Text] +hidingFile = + [ "module Main where" + , "import Prelude" + , "import Data.Maybe hiding (maybe)" + , "" + , "myFunc x y = x + y" + ] + syntaxErrorFile :: [Text] syntaxErrorFile = [ "module Main where" @@ -37,8 +46,8 @@ syntaxErrorFile = , "myFunc =" ] -splitSimpleFile :: (P.ModuleName, [Text], [Import], [Text]) -splitSimpleFile = fromRight (sliceImportSection simpleFile) +testSliceImportSection :: [Text] -> (P.ModuleName, [Text], [Import], [Text]) +testSliceImportSection = fromRight . sliceImportSection where fromRight = fromJust . rightToMaybe @@ -99,17 +108,19 @@ spec = do shouldBe (prettyPrintImport' maybeImport) "import Data.Maybe (Maybe(Just))" describe "import commands" $ do - let simpleFileImports = let (_, _, i, _) = splitSimpleFile in i - addValueImport i mn is = - prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideValue i Nothing)) mn is) - addOpImport op mn is = - prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideValueOp op (P.Qualified Nothing (Left "")) 2 Nothing Nothing)) mn is) - addDtorImport i t mn is = - prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideDtor i t Nothing)) mn is) - addTypeImport i mn is = - prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideType i Nothing [])) mn is) - addKindImport i mn is = - prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideKind i)) mn is) + let simpleFileImports = let (_, _, i, _) = testSliceImportSection simpleFile in i + hidingFileImports = let (_, _, i, _) = testSliceImportSection hidingFile in i + addValueImport i mn q is = + prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideValue i Nothing)) mn q is) + addOpImport op mn q is = + prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideValueOp op (P.Qualified q (Left "")) 2 Nothing Nothing)) mn q is) + addDtorImport i t mn q is = + prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideDtor i t Nothing)) mn q is) + addTypeImport i mn q is = + prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideType i Nothing [])) mn q is) + addKindImport i mn q is = + prettyPrintImportSection (addExplicitImport' (_idaDeclaration (Test.ideKind i)) mn q is) + qualify s = Just (Test.mn s) it "adds an implicit unqualified import to a file without any imports" $ shouldBe (addImplicitImport' [] (P.moduleNameFromString "Data.Map")) @@ -127,86 +138,172 @@ spec = do , "" , "import Data.Map as Map" ] + it "adds a qualified import and maintains proper grouping for implicit hiding imports" $ + shouldBe + (addQualifiedImport' hidingFileImports (Test.mn "Data.Map") (Test.mn "Map")) + [ "import Data.Maybe hiding (maybe)" + , "import Prelude" + , "" + , "import Data.Map as Map" + ] it "adds an explicit unqualified import to a file without any imports" $ shouldBe - (addValueImport "head" (P.moduleNameFromString "Data.Array") []) + (addValueImport "head" (P.moduleNameFromString "Data.Array") Nothing []) ["import Data.Array (head)"] + it "adds an explicit qualified import to a file without any imports" $ + shouldBe + (addValueImport "head" (P.moduleNameFromString "Data.Array") (qualify "Array") []) + ["import Data.Array (head) as Array"] it "adds an explicit unqualified import" $ shouldBe - (addValueImport "head" (P.moduleNameFromString "Data.Array") simpleFileImports) + (addValueImport "head" (P.moduleNameFromString "Data.Array") Nothing simpleFileImports) [ "import Prelude" , "" , "import Data.Array (head)" ] + it "adds an explicit qualified import" $ + shouldBe + (addValueImport "head" (P.moduleNameFromString "Data.Array") (qualify "Array") simpleFileImports) + [ "import Prelude" + , "" + , "import Data.Array (head) as Array" + ] it "doesn't add an import if the containing module is imported implicitly" $ shouldBe - (addValueImport "const" (P.moduleNameFromString "Prelude") simpleFileImports) + (addValueImport "const" (P.moduleNameFromString "Prelude") Nothing simpleFileImports) ["import Prelude"] + let Right (_, _, qualifiedImports, _) = sliceImportSection (withImports ["import Data.Array as Array"]) + it "doesn't add a qualified explicit import if the containing module is imported qualified" $ + shouldBe + (addValueImport "length" (P.moduleNameFromString "Data.Array") (qualify "Array") qualifiedImports) + ["import Prelude" + , "" + , "import Data.Array as Array"] let Right (_, _, explicitImports, _) = sliceImportSection (withImports ["import Data.Array (tail)"]) it "adds an identifier to an explicit import list" $ shouldBe - (addValueImport "head" (P.moduleNameFromString "Data.Array") explicitImports) + (addValueImport "head" (P.moduleNameFromString "Data.Array") Nothing explicitImports) [ "import Prelude" , "" , "import Data.Array (head, tail)" ] + let Right (_, _, explicitQualImports, _) = sliceImportSection (withImports ["import Data.Array (tail) as Array"]) + it "adds an identifier to an explicit qualified import list" $ + shouldBe + (addValueImport "head" (P.moduleNameFromString "Data.Array") (qualify "Array") explicitQualImports) + [ "import Prelude" + , "" + , "import Data.Array (head, tail) as Array" + ] it "adds a kind to an explicit import list" $ shouldBe - (addKindImport "Effect" (P.moduleNameFromString "Control.Monad.Eff") simpleFileImports) + (addKindImport "Effect" (P.moduleNameFromString "Control.Monad.Eff") Nothing simpleFileImports) [ "import Prelude" , "" , "import Control.Monad.Eff (kind Effect)" ] + it "adds a kind to an explicit qualified import list" $ + shouldBe + (addKindImport "Effect" (P.moduleNameFromString "Control.Monad.Eff") (qualify "Eff") simpleFileImports) + [ "import Prelude" + , "" + , "import Control.Monad.Eff (kind Effect) as Eff" + ] it "adds an operator to an explicit import list" $ shouldBe - (addOpImport "<~>" (P.moduleNameFromString "Data.Array") explicitImports) + (addOpImport "<~>" (P.moduleNameFromString "Data.Array") Nothing explicitImports) [ "import Prelude" , "" , "import Data.Array (tail, (<~>))" ] + it "adds an operator to an explicit qualified import list" $ + shouldBe + (addOpImport "<~>" (P.moduleNameFromString "Data.Array") (qualify "Array") explicitQualImports) + [ "import Prelude" + , "" + , "import Data.Array (tail, (<~>)) as Array" + ] it "adds a type with constructors without automatically adding an open import of said constructors " $ shouldBe - (addTypeImport "Maybe" (P.moduleNameFromString "Data.Maybe") simpleFileImports) + (addTypeImport "Maybe" (P.moduleNameFromString "Data.Maybe") Nothing simpleFileImports) [ "import Prelude" , "" , "import Data.Maybe (Maybe)" ] it "adds the type for a given DataConstructor" $ shouldBe - (addDtorImport "Just" "Maybe" (P.moduleNameFromString "Data.Maybe") simpleFileImports) + (addDtorImport "Just" "Maybe" (P.moduleNameFromString "Data.Maybe") Nothing simpleFileImports) [ "import Prelude" , "" , "import Data.Maybe (Maybe(..))" ] + it "adds the type for a given DataConstructor qualified" $ + shouldBe + (addDtorImport "Just" "Maybe" (P.moduleNameFromString "Data.Maybe") (qualify "M") simpleFileImports) + [ "import Prelude" + , "" + , "import Data.Maybe (Maybe(..)) as M" + ] it "adds a dataconstructor to an existing type import" $ do let Right (_, _, typeImports, _) = sliceImportSection (withImports ["import Data.Maybe (Maybe)"]) shouldBe - (addDtorImport "Just" "Maybe" (P.moduleNameFromString "Data.Maybe") typeImports) + (addDtorImport "Just" "Maybe" (P.moduleNameFromString "Data.Maybe") Nothing typeImports) + [ "import Prelude" + , "" + , "import Data.Maybe (Maybe(..))" + ] + it "adding a type to an existing import of that type is noop" $ do + let Right (_, _, typeImports, _) = sliceImportSection (withImports ["import Data.Maybe (Maybe)"]) + shouldBe + (addTypeImport "Maybe" (P.moduleNameFromString "Data.Maybe") Nothing typeImports) + [ "import Prelude" + , "" + , "import Data.Maybe (Maybe)" + ] + it "adding a type to an existing import of that type with its constructors is noop" $ do + let Right (_, _, typeImports, _) = sliceImportSection (withImports ["import Data.Maybe (Maybe (..))"]) + shouldBe + (addTypeImport "Maybe" (P.moduleNameFromString "Data.Maybe") Nothing typeImports) [ "import Prelude" , "" , "import Data.Maybe (Maybe(..))" ] + it "adds a dataconstructor to an existing qualified type import" $ do + let Right (_, _, typeImports, _) = sliceImportSection (withImports ["import Data.Maybe (Maybe) as M"]) + shouldBe + (addDtorImport "Just" "Maybe" (P.moduleNameFromString "Data.Maybe") (qualify "M") typeImports) + [ "import Prelude" + , "" + , "import Data.Maybe (Maybe(..)) as M" + ] it "doesn't add a dataconstructor to an existing type import with open dtors" $ do let Right (_, _, typeImports, _) = sliceImportSection (withImports ["import Data.Maybe (Maybe(..))"]) shouldBe - (addDtorImport "Just" "Maybe" (P.moduleNameFromString "Data.Maybe") typeImports) + (addDtorImport "Just" "Maybe" (P.moduleNameFromString "Data.Maybe") Nothing typeImports) [ "import Prelude" , "" , "import Data.Maybe (Maybe(..))" ] it "doesn't add an identifier to an explicit import list if it's already imported" $ shouldBe - (addValueImport "tail" (P.moduleNameFromString "Data.Array") explicitImports) + (addValueImport "tail" (P.moduleNameFromString "Data.Array") Nothing explicitImports) [ "import Prelude" , "" , "import Data.Array (tail)" ] + it "doesn't add an identifier to an explicit qualified import list if it's already imported qualified" $ + shouldBe + (addValueImport "tail" (P.moduleNameFromString "Data.Array") (qualify "Array") explicitQualImports) + [ "import Prelude" + , "" + , "import Data.Array (tail) as Array" + ] describe "explicit import sorting" $ do -- given some basic import skeleton let Right (_, _, baseImports, _) = sliceImportSection $ withImports ["import Control.Monad (ap)"] moduleName = (P.moduleNameFromString "Control.Monad") - addImport imports import' = addExplicitImport' import' moduleName imports + addImport imports import' = addExplicitImport' import' moduleName Nothing imports valueImport ident = _idaDeclaration (Test.ideValue ident Nothing) typeImport name = _idaDeclaration (Test.ideType name Nothing []) classImport name = _idaDeclaration (Test.ideTypeClass name P.kindType []) @@ -244,7 +341,7 @@ implImport mn = addExplicitImport :: Text -> Command addExplicitImport i = - Command.Import ("src" </> "ImportsSpec.purs") Nothing [] (Command.AddImportForIdentifier i) + Command.Import ("src" </> "ImportsSpec.purs") Nothing [] (Command.AddImportForIdentifier i Nothing) importShouldBe :: [Text] -> [Text] -> Expectation importShouldBe res importSection = diff --git a/tests/Language/PureScript/Ide/RebuildSpec.hs b/tests/Language/PureScript/Ide/RebuildSpec.hs index 9c00312..03ea688 100644 --- a/tests/Language/PureScript/Ide/RebuildSpec.hs +++ b/tests/Language/PureScript/Ide/RebuildSpec.hs @@ -4,6 +4,7 @@ module Language.PureScript.Ide.RebuildSpec where import Protolude +import Language.PureScript.AST.SourcePos (spanName) import Language.PureScript.Ide.Command import Language.PureScript.Ide.Completion import Language.PureScript.Ide.Matcher @@ -16,10 +17,10 @@ load :: [Text] -> Command load = LoadSync . map Test.mn rebuild :: FilePath -> Command -rebuild fp = Rebuild ("src" </> fp) +rebuild fp = Rebuild ("src" </> fp) Nothing rebuildSync :: FilePath -> Command -rebuildSync fp = RebuildSync ("src" </> fp) +rebuildSync fp = RebuildSync ("src" </> fp) Nothing spec :: Spec spec = describe "Rebuilding single modules" $ do @@ -60,3 +61,12 @@ spec = describe "Rebuilding single modules" $ do Test.runIde [ rebuildSync "RebuildSpecWithHiddenIdent.purs" , Complete [] (flexMatcher "hid") (Just (Test.mn "RebuildSpecWithHiddenIdent")) defaultCompletionOptions] complIdentifier result `shouldBe` "hidden" + it "uses the specified `actualFile` for location information (in editor mode)" $ do + let editorConfig = Test.defConfig { confEditorMode = True } + ([_, (Right (CompletionResult [ result ]))], _) <- Test.inProject $ + Test.runIde' + editorConfig + emptyIdeState + [ RebuildSync ("src" </> "RebuildSpecWithHiddenIdent.purs") (Just "actualFile") + , Complete [] (flexMatcher "hid") (Just (Test.mn "RebuildSpecWithHiddenIdent")) defaultCompletionOptions] + map spanName (complLocation result) `shouldBe` Just "actualFile" diff --git a/tests/Language/PureScript/Ide/SourceFileSpec.hs b/tests/Language/PureScript/Ide/SourceFileSpec.hs index 1bf01f4..dbcfed9 100644 --- a/tests/Language/PureScript/Ide/SourceFileSpec.hs +++ b/tests/Language/PureScript/Ide/SourceFileSpec.hs @@ -22,8 +22,8 @@ ann1 = (span1, []) ann2 = (span2, []) typeAnnotation1, value1, synonym1, class1, class2, data1, data2, valueFixity, typeFixity, foreign1, foreign2, foreign3, member1 :: P.Declaration -typeAnnotation1 = P.TypeDeclaration ann1 (P.Ident "value1") P.REmpty -value1 = P.ValueDeclaration ann1 (P.Ident "value1") P.Public [] [] +typeAnnotation1 = P.TypeDeclaration (P.TypeDeclarationData ann1 (P.Ident "value1") P.REmpty) +value1 = P.ValueDecl ann1 (P.Ident "value1") P.Public [] [] synonym1 = P.TypeSynonymDeclaration ann1 (P.ProperName "Synonym1") [] P.REmpty class1 = P.TypeClassDeclaration ann1 (P.ProperName "Class1") [] [] [] [] class2 = P.TypeClassDeclaration ann1 (P.ProperName "Class2") [] [] [] [member1] @@ -44,7 +44,7 @@ typeFixity = foreign1 = P.ExternDeclaration ann1 (P.Ident "foreign1") P.REmpty foreign2 = P.ExternDataDeclaration ann1 (P.ProperName "Foreign2") P.kindType foreign3 = P.ExternKindDeclaration ann1 (P.ProperName "Foreign3") -member1 = P.TypeDeclaration ann2 (P.Ident "member1") P.REmpty +member1 = P.TypeDeclaration (P.TypeDeclarationData ann2 (P.Ident "member1") P.REmpty) spec :: Spec spec = do diff --git a/tests/Language/PureScript/Ide/Test.hs b/tests/Language/PureScript/Ide/Test.hs index dd48b8f..d9d50ae 100644 --- a/tests/Language/PureScript/Ide/Test.hs +++ b/tests/Language/PureScript/Ide/Test.hs @@ -20,10 +20,12 @@ import qualified Language.PureScript as P defConfig :: IdeConfiguration defConfig = - IdeConfiguration { confLogLevel = LogNone - , confOutputPath = "output/" - , confGlobs = ["src/*.purs"] - } + IdeConfiguration + { confLogLevel = LogNone + , confOutputPath = "output/" + , confGlobs = ["src/*.purs"] + , confEditorMode = False + } runIde' :: IdeConfiguration -> IdeState -> [Command] -> IO ([Either IdeError Success], IdeState) runIde' conf s cs = do diff --git a/tests/Main.hs b/tests/Main.hs index acfce36..1622bd4 100644 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -10,10 +10,11 @@ import Prelude.Compat import qualified TestCompiler import qualified TestDocs +import qualified TestHierarchy +import qualified TestPrimDocs import qualified TestPsci import qualified TestPscIde import qualified TestPscPublish -import qualified TestPrimDocs import qualified TestUtils import System.IO (hSetEncoding, stdout, stderr, utf8) @@ -29,6 +30,9 @@ main = do TestCompiler.main heading "Documentation test suite" TestDocs.main + heading "Hierarchy test suite" + TestHierarchy.main + heading "Prim documentation test suite" TestPrimDocs.main heading "psc-publish test suite" TestPscPublish.main diff --git a/tests/TestDocs.hs b/tests/TestDocs.hs index d3dbbdb..e486988 100644 --- a/tests/TestDocs.hs +++ b/tests/TestDocs.hs @@ -12,8 +12,10 @@ import Prelude.Compat import Control.Arrow (first) import Control.Monad.IO.Class (liftIO) +import Data.List (findIndex) import Data.Foldable -import Data.Maybe (fromMaybe) +import Safe (headMay) +import Data.Maybe (fromMaybe, mapMaybe) import Data.Monoid import Data.Text (Text) import qualified Data.Text as T @@ -27,9 +29,10 @@ import Language.PureScript.Docs.AsMarkdown (codeToString) import qualified Language.PureScript.Publish as Publish import qualified Language.PureScript.Publish.ErrorsWarnings as Publish -import Web.Bower.PackageMeta (parsePackageName) +import Web.Bower.PackageMeta (parsePackageName, runPackageName) import TestUtils +import Test.Hspec (Spec, it, context, expectationFailure, runIO, hspec) publishOpts :: Publish.PublishOptions publishOpts = Publish.defaultPublishOptions @@ -39,23 +42,54 @@ publishOpts = Publish.defaultPublishOptions } where testVersion = ("v999.0.0", Version [999,0,0] []) +getPackage :: IO (Either Publish.PackageError (Docs.Package Docs.NotYetKnown)) +getPackage = + pushd "examples/docs" $ + Publish.preparePackage "bower.json" "resolutions.json" publishOpts + main :: IO () -main = pushd "examples/docs" $ do - res <- Publish.preparePackage "bower.json" "resolutions.json" publishOpts - case res of - Left e -> Publish.printErrorToStdout e >> exitFailure - Right pkg@Docs.Package{..} -> - forM_ testCases $ \(P.moduleNameFromString -> mn, pragmas) -> - let mdl = takeJust ("module not found in docs: " ++ T.unpack (P.runModuleName mn)) - (find ((==) mn . Docs.modName) pkgModules) - linksCtx = Docs.getLinksContext pkg - in forM_ pragmas (\a -> runAssertionIO a linksCtx mdl) +main = hspec spec + +spec :: Spec +spec = do + pkg@Docs.Package{..} <- runIO $ do + res <- getPackage + case res of + Left e -> + Publish.printErrorToStdout e >> exitFailure + Right p -> + pure p + + let linksCtx = Docs.getLinksContext pkg + + context "Language.PureScript.Docs" $ + forM_ testCases $ \(mnString, assertions) -> do + let mn = P.moduleNameFromString mnString + mdl = find ((==) mn . Docs.modName) pkgModules + + context ("in module " ++ T.unpack mnString) $ do + case mdl of + Nothing -> + it "exists in docs output" $ + expectationFailure ("module not found in docs: " ++ T.unpack mnString) + Just mdl' -> + toHspec linksCtx mdl' assertions + where + toHspec :: Docs.LinksContext -> Docs.Module -> [DocsAssertion] -> Spec + toHspec linksCtx mdl assertions = + forM_ assertions $ \a -> + it (T.unpack (displayAssertion a)) $ do + case runAssertion a linksCtx mdl of + Pass -> + pure () + Fail reason -> + expectationFailure (T.unpack (displayAssertionFailure reason)) takeJust :: String -> Maybe a -> a takeJust msg = fromMaybe (error msg) -data Assertion +data DocsAssertion -- | Assert that a particular declaration is documented with the given -- children = ShouldBeDocumented P.ModuleName Text [Text] @@ -72,7 +106,10 @@ data Assertion | ShouldHaveFunDeps P.ModuleName Text [([Text],[Text])] -- | Assert that a particular value declaration exists, and its type -- satisfies the given predicate. - | ValueShouldHaveTypeSignature P.ModuleName Text (ShowFn (P.Type -> Bool)) + | ValueShouldHaveTypeSignature P.ModuleName Text (P.Type -> Bool) + -- | Assert that a particular instance declaration exists under some class or + -- type declaration, and that its type satisfies the given predicate. + | InstanceShouldHaveTypeSignature P.ModuleName Text Text (P.Type -> Bool) -- | Assert that a particular type alias exists, and its corresponding -- type, when rendered, matches a given string exactly -- fields: module, type synonym name, expected type @@ -88,14 +125,47 @@ data Assertion -- declaration title, title of linked declaration, namespace of linked -- declaration, destination of link. | ShouldHaveLink P.ModuleName Text Text Docs.Namespace Docs.LinkLocation - deriving (Show) - -newtype ShowFn a = ShowFn a - -instance Show (ShowFn a) where - show _ = "<function>" - -data AssertionFailure + -- | Assert that a given declaration comes before another in the output + | ShouldComeBefore P.ModuleName Text Text + +displayAssertion :: DocsAssertion -> Text +displayAssertion = \case + ShouldBeDocumented mn decl children -> + showQual mn decl <> " should be documented" <> + (if not (null children) + then " with children: " <> T.pack (show children) + else "") + ShouldNotBeDocumented mn decl -> + showQual mn decl <> " should not be documented" + ChildShouldNotBeDocumented mn decl child -> + showQual mn decl <> " should not have " <> child <> " as a child declaration" + ShouldBeConstrained mn decl constraint -> + showQual mn decl <> " should have a " <> constraint <> " constraint" + ShouldHaveFunDeps mn decl fundeps -> + showQual mn decl <> " should have fundeps: " <> T.pack (show fundeps) + ValueShouldHaveTypeSignature mn decl _ -> + "the type signature for " <> showQual mn decl <> + " should satisfy the given predicate" + InstanceShouldHaveTypeSignature _ parent instName _ -> + "the instance " <> instName <> " (under " <> parent <> ") should have" <> + " a type signature satisfying the given predicate" + TypeSynonymShouldRenderAs mn synName code -> + "the RHS of the type synonym " <> showQual mn synName <> + " should be rendered as " <> code + ShouldHaveDocComment mn decl excerpt -> + "the string " <> T.pack (show excerpt) <> " should appear in the" <> + " doc-comments for " <> showQual mn decl + ShouldHaveReExport inPkg -> + "there should be some re-exports from " <> + showInPkg P.runModuleName inPkg + ShouldHaveLink mn decl targetTitle targetNs _ -> + "the rendered code for " <> showQual mn decl <> " should contain a link" <> + " to " <> targetTitle <> " (" <> T.pack (show targetNs) <> ")" + ShouldComeBefore mn declA declB -> + showQual mn declA <> " should come before " <> showQual mn declB <> + " in the docs" + +data DocsAssertionFailure -- | A declaration was not documented, but should have been = NotDocumented P.ModuleName Text -- | The expected list of child declarations did not match the actual list @@ -111,16 +181,16 @@ data AssertionFailure -- | A declaration had the wrong "type" (ie, value, type, type class) -- Fields: declaration title, expected "type", actual "type". | WrongDeclarationType P.ModuleName Text Text Text - -- | A value declaration had the wrong type (in the sense of "type - -- checking"), eg, because the inferred type was used when the explicit type - -- should have been. + -- | A declaration had the wrong type (in the sense of "type checking"), eg, + -- because the inferred type was used when the explicit type should have + -- been. -- Fields: module name, declaration name, actual type. - | ValueDeclarationWrongType P.ModuleName Text P.Type + | DeclarationWrongType P.ModuleName Text P.Type -- | A Type synonym has been rendered in an unexpected format -- Fields: module name, declaration name, expected rendering, actual rendering | TypeSynonymMismatch P.ModuleName Text Text Text -- | A doc comment was not found or did not match what was expected - -- Fields: module name, expected substring, actual comments + -- Fields: module name, declaration, actual comments | DocCommentMissing P.ModuleName Text (Maybe Text) -- | A module was missing re-exports from a particular module. -- Fields: module name, expected re-export, actual re-exports. @@ -136,14 +206,54 @@ data AssertionFailure -- declaration, title of the linked declaration, expected location, actual -- location. | BadLinkLocation P.ModuleName Text Text Docs.LinkLocation Docs.LinkLocation - deriving (Show) - -data AssertionResult + -- | Declarations were in the wrong order + | WrongOrder P.ModuleName Text Text + +displayAssertionFailure :: DocsAssertionFailure -> Text +displayAssertionFailure = \case + NotDocumented _ decl -> + decl <> " was not documented, but should have been" + ChildrenNotDocumented _ decl children -> + decl <> " had the wrong children; got " <> T.pack (show children) + Documented _ decl -> + decl <> " was documented, but should not have been" + ChildDocumented _ decl child -> + decl <> " had " <> child <> " as a child" + ConstraintMissing _ decl constraint -> + decl <> " did not have a " <> constraint <> " constraint" + FunDepMissing _ decl fundeps -> + decl <> " had the wrong fundeps; got " <> T.pack (show fundeps) + WrongDeclarationType _ decl expected actual -> + "expected " <> decl <> " to be a " <> expected <> " declaration, but it" <> + " was a " <> actual <> " declaration" + DeclarationWrongType _ decl actual -> + decl <> " had the wrong type; got " <> T.pack (P.prettyPrintType actual) + TypeSynonymMismatch _ decl expected actual -> + "expected the RHS of " <> decl <> " to be " <> expected <> + "; got " <> actual + DocCommentMissing _ decl actual -> + "the doc-comment for " <> decl <> " did not contain the expected substring;" <> + " got " <> T.pack (show actual) + ReExportMissing _ expected actuals -> + "expected to see some re-exports from " <> + showInPkg P.runModuleName expected <> + "; instead only saw re-exports from " <> + T.pack (show (map (showInPkg P.runModuleName) actuals)) + LinkedDeclarationMissing _ decl target -> + "expected to find a link to " <> target <> " within the rendered code" <> + " for " <> decl <> ", but no such link was found" + BadLinkLocation _ decl target expected actual -> + "in rendered code for " <> decl <> ", bad link location for " <> target <> + ": expected " <> T.pack (show expected) <> + " got " <> T.pack (show actual) + WrongOrder _ before after -> + "expected to see " <> before <> " before " <> after + +data DocsAssertionResult = Pass - | Fail AssertionFailure - deriving (Show) + | Fail DocsAssertionFailure -runAssertion :: Assertion -> Docs.LinksContext -> Docs.Module -> AssertionResult +runAssertion :: DocsAssertion -> Docs.LinksContext -> Docs.Module -> DocsAssertionResult runAssertion assertion linksCtx Docs.Module{..} = case assertion of ShouldBeDocumented mn decl children -> @@ -193,18 +303,39 @@ runAssertion assertion linksCtx Docs.Module{..} = Fail (WrongDeclarationType mn decl "value" (Docs.declInfoToString declInfo)) - ValueShouldHaveTypeSignature mn decl (ShowFn tyPredicate) -> + ValueShouldHaveTypeSignature mn decl tyPredicate -> findDecl mn decl $ \Docs.Declaration{..} -> case declInfo of Docs.ValueDeclaration ty -> if tyPredicate ty then Pass - else Fail - (ValueDeclarationWrongType mn decl ty) + else Fail (DeclarationWrongType mn decl ty) _ -> Fail (WrongDeclarationType mn decl "value" (Docs.declInfoToString declInfo)) + InstanceShouldHaveTypeSignature mn parent decl tyPredicate -> + case find ((==) parent . Docs.declTitle) (declarationsFor mn) >>= findTarget of + Just ty -> + if tyPredicate ty + then Pass + else Fail (DeclarationWrongType mn decl ty) + Nothing -> + Fail (NotDocumented mn decl) + + where + findTarget = + headMay . + mapMaybe (extractInstanceType . Docs.cdeclInfo) . + filter (\cdecl -> Docs.cdeclTitle cdecl == decl) . + Docs.declChildren + + extractInstanceType = \case + (Docs.ChildInstance _ ty) -> + Just ty + _ -> + Nothing + TypeSynonymShouldRenderAs mn decl expected -> findDecl mn decl $ \Docs.Declaration{..} -> case declInfo of @@ -244,6 +375,23 @@ runAssertion assertion linksCtx Docs.Module{..} = Nothing -> Fail (LinkedDeclarationMissing mn decl destTitle) + ShouldComeBefore mn before after -> + let + decls = declarationsFor mn + + indexOf :: Text -> Maybe Int + indexOf title = findIndex ((==) title . Docs.declTitle) decls + in + case (indexOf before, indexOf after) of + (Just i, Just j) -> + if i < j + then Pass + else Fail (WrongOrder mn before after) + (Nothing, _) -> + Fail (NotDocumented mn before) + (_, Nothing) -> + Fail (NotDocumented mn after) + where declarationsFor mn = if mn == modName @@ -287,16 +435,7 @@ checkConstrained ty tyClass = matches className = (==) className . P.runProperName . P.disqualify . P.constraintClass -runAssertionIO :: Assertion -> Docs.LinksContext -> Docs.Module -> IO () -runAssertionIO assertion linksCtx mdl = do - putStrLn ("In " ++ T.unpack (P.runModuleName (Docs.modName mdl)) ++ ": " ++ show assertion) - case runAssertion assertion linksCtx mdl of - Pass -> pure () - Fail reason -> do - putStrLn ("Failed: " <> show reason) - exitFailure - -testCases :: [(Text, [Assertion])] +testCases :: [(Text, [DocsAssertion])] testCases = [ ("Example", [ -- From dependencies @@ -374,9 +513,9 @@ testCases = ]) , ("ExplicitTypeSignatures", - [ ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "explicit" (ShowFn (hasTypeVar "something")) - , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "anInt" (ShowFn (P.tyInt ==)) - , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "aNumber" (ShowFn (P.tyNumber ==)) + [ ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "explicit" (hasTypeVar "something") + , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "anInt" (P.tyInt ==) + , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "aNumber" (P.tyNumber ==) ]) , ("ConstrainedArgument", @@ -392,6 +531,13 @@ testCases = , ValueShouldHaveTypeSignature (n "TypeOpAliases") "test3" (renderedType "forall a b c d. a ~> (b ~> c) ~> d") , ValueShouldHaveTypeSignature (n "TypeOpAliases") "test4" (renderedType "forall a b c d. ((a ~> b) ~> c) ~> d") , ValueShouldHaveTypeSignature (n "TypeOpAliases") "third" (renderedType "forall a b c. a × b × c -> c") + + , ShouldBeDocumented (n "TypeOpAliases") "Tuple" ["Tuple","showTuple", "testLEither", "testREither"] + , ShouldBeDocumented (n "TypeOpAliases") "Either" ["Left", "Right","testLEither", "testREither"] + , ShouldBeDocumented (n "TypeOpAliases") "Show" ["show","showTuple"] + + , InstanceShouldHaveTypeSignature (n "TypeOpAliases") "Either" "testLEither" (renderedType "TestL (Either Int (Tuple Int String))") + , InstanceShouldHaveTypeSignature (n "TypeOpAliases") "Either" "testREither" (renderedType "TestR (Either (Tuple Int Int) String)") ]) , ("DocComments", @@ -410,6 +556,14 @@ testCases = [ ShouldBeDocumented (n "ChildDeclOrder") "Two" ["First", "Second", "showTwo", "fooTwo"] , ShouldBeDocumented (n "ChildDeclOrder") "Foo" ["foo1", "foo2", "fooTwo", "fooInt"] ]) + + , ("DeclOrder", + shouldBeOrdered (n "DeclOrder") + ["A", "x1", "X2", "x3", "X4", "B"]) + + , ("DeclOrderNoExportList", + shouldBeOrdered (n "DeclOrderNoExportList") + [ "x1", "x3", "X2", "X4", "A", "B" ]) ] where @@ -422,5 +576,19 @@ testCases = isVar varName (P.TypeVar name) | varName == T.unpack name = True isVar _ _ = False - renderedType expected = - ShowFn $ \ty -> codeToString (Docs.renderType ty) == expected + renderedType expected ty = + codeToString (Docs.renderType ty) == expected + + shouldBeOrdered mn declNames = + zipWith (ShouldComeBefore mn) declNames (tail declNames) + +showQual :: P.ModuleName -> Text -> Text +showQual mn decl = + P.runModuleName mn <> "." <> decl + +showInPkg :: (a -> Text) -> Docs.InPackage a -> Text +showInPkg f = \case + Docs.Local x -> + f x <> " (local)" + Docs.FromDep pkgName x -> + f x <> " (from dep: " <> runPackageName pkgName <> ")" diff --git a/tests/TestHierarchy.hs b/tests/TestHierarchy.hs new file mode 100644 index 0000000..98bea9a --- /dev/null +++ b/tests/TestHierarchy.hs @@ -0,0 +1,65 @@ +{-# LANGUAGE OverloadedStrings #-} +module TestHierarchy where + +import Language.PureScript.Hierarchy +import qualified Language.PureScript as P +import Test.Hspec (describe, hspec, it, shouldBe) + +main :: IO () +main = hspec $ do + describe "Language.PureScript.Hierarchy" $ do + describe "prettyPrint" $ do + it "creates just the node when there is no relation" $ do + let superMap = SuperMap (Left $ P.ProperName "A") + + let prettyPrinted = prettyPrint superMap + + prettyPrinted `shouldBe` " A;" + + it "creates a relation when there is one" $ do + let superMap = SuperMap (Right $ (P.ProperName "A", P.ProperName "B")) + + let prettyPrinted = prettyPrint superMap + + prettyPrinted `shouldBe` " A -> B;" + + describe "typeClassGraph" $ do + it "doesn't generate a graph if there are no type classes" $ do + let mainModule = P.Module + (P.internalModuleSourceSpan "<hierarchy>") + [] + (P.ModuleName [P.ProperName "Main"]) + [] + Nothing + + let graph = typeClassGraph mainModule + + graph `shouldBe` Nothing + + it "generates usable graphviz graphs" $ do + let declarations = + [ P.TypeClassDeclaration + (P.internalModuleSourceSpan "<A>", []) + (P.ProperName "A") + [] + [] + [] + [] + , P.TypeClassDeclaration + (P.internalModuleSourceSpan "<B>", []) + (P.ProperName "B") + [] + [P.Constraint (P.Qualified Nothing $ P.ProperName "A") [] Nothing] + [] + [] + ] + let mainModule = P.Module + (P.internalModuleSourceSpan "<hierarchy>") + [] + (P.ModuleName [P.ProperName "Main"]) + declarations + Nothing + + let graph = typeClassGraph mainModule + + graph `shouldBe` Just (Graph (GraphName "Main") (Digraph "digraph Main {\n A;\n A -> B;\n}")) diff --git a/tests/TestPsci/CommandTest.hs b/tests/TestPsci/CommandTest.hs index f1e36b2..57e7742 100644 --- a/tests/TestPsci/CommandTest.hs +++ b/tests/TestPsci/CommandTest.hs @@ -32,3 +32,9 @@ commandTests = context "commandTests" $ do run ":reload" ms' <- psciImportedModules <$> get length ms' `equalsTo` 3 + + specPSCi ":complete" $ do + ":complete ma" `prints` [] + ":complete Data.Functor.ma" `prints` (unlines (map ("Data.Functor." ++ ) ["map", "mapFlipped"])) + run "import Data.Functor" + ":complete ma" `prints` (unlines ["map", "mapFlipped"]) diff --git a/tests/TestPsci/CompletionTest.hs b/tests/TestPsci/CompletionTest.hs index fef5f7b..1040561 100644 --- a/tests/TestPsci/CompletionTest.hs +++ b/tests/TestPsci/CompletionTest.hs @@ -12,7 +12,6 @@ import Data.List (sort) import qualified Data.Text as T import qualified Language.PureScript as P import Language.PureScript.Interactive -import System.Console.Haskeline import TestPsci.TestEnv (initTestPSCiEnv) import TestUtils (getSupportModuleNames) @@ -29,7 +28,7 @@ completionTestData supportModuleNames = -- basic directives [ (":h", [":help"]) , (":r", [":reload"]) - , (":c", [":clear"]) + , (":c", [":clear", ":complete"]) , (":q", [":quit"]) , (":b", [":browse"]) @@ -88,10 +87,9 @@ completionTestData supportModuleNames = assertCompletedOk :: (String, [String]) -> Spec assertCompletedOk (line, expecteds) = specify line $ do - (unusedR, completions) <- runCM (completion' (reverse line, "")) - let unused = reverse unusedR - let actuals = map ((unused ++) . replacement) completions - sort expecteds `shouldBe` sort actuals + results <- runCM (completion' (reverse line, "")) + let actuals = formatCompletions results + sort actuals `shouldBe` sort expecteds runCM :: CompletionM a -> IO a runCM act = do diff --git a/tests/TestPsci/TestEnv.hs b/tests/TestPsci/TestEnv.hs index 8f71d9a..fdf0ca9 100644 --- a/tests/TestPsci/TestEnv.hs +++ b/tests/TestPsci/TestEnv.hs @@ -56,26 +56,41 @@ jsEval = liftIO $ do Just (ExitFailure _, _, err) -> putStrLn err >> exitFailure Nothing -> putStrLn "Couldn't find node.js" >> exitFailure --- | Run a PSCi command and evaluate the output with 'eval'. -runAndEval :: String -> TestPSCi () -> TestPSCi () -runAndEval comm eval = +-- | Run a PSCi command and evaluate its outputs: +-- * jsOutputEval is used to evaluate compiled JS output by PSCi +-- * printedOutputEval is used to evaluate text printed directly by PSCi itself +runAndEval :: String -> TestPSCi () -> (String -> TestPSCi ()) -> TestPSCi () +runAndEval comm jsOutputEval textOutputEval = case parseCommand comm of Left errStr -> liftIO $ putStrLn errStr >> exitFailure Right command -> - -- the JS result can be ignored, as it's already written in a source file - -- for the detail, please refer to Interactive.hs - handleCommand (\_ -> eval) (return ()) (\_ -> return ()) command + -- The JS result is ignored, as it's already written in a JS source file. + -- For the detail, please refer to Interactive.hs + handleCommand (\_ -> jsOutputEval) (return ()) textOutputEval command --- | Run a PSCi command and ignore the output +-- | Run a PSCi command, evaluate compiled JS, and ignore evaluation output and printed output run :: String -> TestPSCi () -run comm = runAndEval comm $ jsEval *> return () +run comm = runAndEval comm evalJsAndIgnore ignorePrinted + where + evalJsAndIgnore = jsEval *> return () + ignorePrinted _ = return () -- | A lifted evaluation of Hspec 'shouldBe' for the TestPSCi equalsTo :: (Eq a, Show a) => a -> a -> TestPSCi () equalsTo x y = liftIO $ x `shouldBe` y --- | An assertion to check if a command evaluates to a string +-- | An assertion to check command evaluated javascript output against a given string evaluatesTo :: String -> String -> TestPSCi () -evaluatesTo command expected = runAndEval command $ do - actual <- jsEval - actual `equalsTo` (expected ++ "\n") +evaluatesTo command expected = runAndEval command evalJsAndCompare ignorePrinted + where + evalJsAndCompare = do + actual <- jsEval + actual `equalsTo` (expected ++ "\n") + ignorePrinted _ = return () + +-- | An assertion to check command PSCi printed output against a given string +prints :: String -> String -> TestPSCi () +prints command expected = runAndEval command evalJsAndIgnore evalPrinted + where + evalJsAndIgnore = jsEval *> return () + evalPrinted s = s `equalsTo` expected diff --git a/tests/support/bower.json b/tests/support/bower.json index 932650f..0973f7a 100644 --- a/tests/support/bower.json +++ b/tests/support/bower.json @@ -1,22 +1,42 @@ { "name": "purescript-test-suite-support", "dependencies": { - "purescript-arrays": "4.0.0", + "purescript-arrays": "4.1.2", "purescript-assert": "3.0.0", + "purescript-bifunctors": "3.0.0", "purescript-console": "3.0.0", + "purescript-control": "3.3.0", + "purescript-distributive": "3.0.0", "purescript-eff": "3.1.0", + "purescript-either": "3.1.0", + "purescript-foldable-traversable": "3.4.0", "purescript-functions": "3.0.0", + "purescript-gen": "1.1.0", "purescript-generics": "4.0.0", - "purescript-generics-rep": "5.0.0", - "purescript-lists": "4.6.0", + "purescript-generics-rep": "5.1.0", + "purescript-globals": "3.0.0", + "purescript-identity": "3.1.0", + "purescript-integers": "3.1.0", + "purescript-invariant": "3.0.0", + "purescript-lazy": "3.0.0", + "purescript-lists": "4.9.0", + "purescript-math": "2.1.0", + "purescript-maybe": "3.0.0", + "purescript-monoid": "3.1.0", "purescript-newtype": "2.0.0", - "purescript-partial": "1.2.0", - "purescript-prelude": "3.0.0", + "purescript-nonempty": "4.0.0", + "purescript-partial": "1.2.1", + "purescript-prelude": "3.1.0", + "purescript-proxy": "2.1.0", "purescript-psci-support": "3.0.0", "purescript-st": "3.0.0", + "purescript-strings": "3.3.0", "purescript-symbols": "3.0.0", "purescript-tailrec": "3.3.0", - "purescript-typelevel-prelude": "2.0.0", + "purescript-tuples": "4.1.0", + "purescript-type-equality": "2.1.0", + "purescript-typelevel-prelude": "2.3.0", + "purescript-unfoldable": "3.0.0", "purescript-unsafe-coerce": "3.0.0" } } diff --git a/tests/support/pscide/src/CompletionSpecDocs.purs b/tests/support/pscide/src/CompletionSpecDocs.purs new file mode 100644 index 0000000..1c92a37 --- /dev/null +++ b/tests/support/pscide/src/CompletionSpecDocs.purs @@ -0,0 +1,13 @@ +module CompletionSpecDocs where + +-- | Doc x +something = "something" + +-- | Doc *123* +withType :: Int +withType = 42 + +-- | This is +-- | a multi-line +-- | comment +multiline = "multiline"
\ No newline at end of file |