packages feed

purescript-bridge-0.13.0.0: test/Spec.hs

{-# LANGUAGE CPP                   #-}
{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE TypeSynonymInstances  #-}

module Main where
import qualified Data.Map                                  as Map
import           Data.Monoid                               ((<>))
import           Data.Proxy
import qualified Data.Text                                 as T
import           Language.PureScript.Bridge
import           Language.PureScript.Bridge.TypeParameters
import           Language.PureScript.Bridge.CodeGenSwitches
import           Test.Hspec                                (Spec, describe,
                                                            hspec, it)
import           Test.Hspec.Expectations.Pretty
import           TestData

main :: IO ()
main = hspec allTests


allTests :: Spec
allTests = do
  describe "buildBridge" $ do
    let settings = defaultSettings
    it "tests with Int" $
      let bst = buildBridge defaultBridge (mkTypeInfo (Proxy :: Proxy Int))
          ti  = TypeInfo { _typePackage    = ""
                       , _typeModule     = "Prim"
                       , _typeName       = "Int"
                       , _typeParameters = []}
       in bst `shouldBe` ti
    it "tests with custom type Foo" $
      let prox = Proxy :: Proxy Foo
          bst = bridgeSumType (buildBridge defaultBridge) (order prox $ mkSumType prox)
          st = SumType
                TypeInfo { _typePackage = "" , _typeModule = "TestData" , _typeName = "Foo" , _typeParameters = [] }
                [ DataConstructor { _sigConstructor = "Foo" , _sigValues = Left [] }
                , DataConstructor
                  { _sigConstructor = "Bar"
                  , _sigValues = Left [ TypeInfo { _typePackage = "" , _typeModule = "Prim" , _typeName = "Int" , _typeParameters = [] } ]
                  }
                , DataConstructor
                  { _sigConstructor = "FooBar"
                  , _sigValues = Left [ TypeInfo { _typePackage = "" , _typeModule = "Prim" , _typeName = "Int" , _typeParameters = [] }
                                      , TypeInfo { _typePackage = "" , _typeModule = "Prim" , _typeName = "String" , _typeParameters = [] }
                                      ]
                  }
                ]
                [Eq, Ord, Generic]
       in bst `shouldBe` st
    it "tests generation of for custom type Foo" $
     let prox = Proxy :: Proxy Foo
         recType = bridgeSumType (buildBridge defaultBridge) (order prox $ mkSumType prox)
         recTypeText = sumTypeToText settings recType
         txt = T.stripEnd $
               T.unlines [ "data Foo ="
                         , "    Foo"
                         , "  | Bar Int"
                         , "  | FooBar Int String"
                         , ""
                         , "derive instance eqFoo :: Eq Foo"
                         , "derive instance ordFoo :: Ord Foo"
                         , "derive instance genericFoo :: Generic Foo"
                         , ""
                         , "--------------------------------------------------------------------------------"
                         , "_Foo :: Prism' Foo Unit"
                         , "_Foo = prism' (\\_ -> Foo) f"
                         , "  where"
                         , "    f Foo = Just unit"
                         , "    f _ = Nothing"
                         , ""
                         , "_Bar :: Prism' Foo Int"
                         , "_Bar = prism' Bar f"
                         , "  where"
                         , "    f (Bar a) = Just $ a"
                         , "    f _ = Nothing"
                         , ""
                         , "_FooBar :: Prism' Foo { a :: Int, b :: String }"
                         , "_FooBar = prism' (\\{ a, b } -> FooBar a b) f"
                         , "  where"
                         , "    f (FooBar a b) = Just $ { a: a, b: b }"
                         , "    f _ = Nothing"
                         , ""
                         , "--------------------------------------------------------------------------------"
                         ]
     in recTypeText `shouldBe` txt
    it "tests the generation of a whole (dummy) module" $
      let advanced = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy (Bar A B M1 C)))
          modules = sumTypeToModule advanced Map.empty
          m = head . map (moduleToText settings) . Map.elems $ modules
          txt = T.unlines [ "-- File auto generated by purescript-bridge! --"
                          , "module TestData where"
                          , ""
                          , "import Data.Either (Either)"
                          , "import Data.Lens (Iso', Lens', Prism', lens, prism')"
                          , "import Data.Lens.Iso.Newtype (_Newtype)"
                          , "import Data.Lens.Record (prop)"
                          , "import Data.Maybe (Maybe, Maybe(..))"
                          , "import Data.Newtype (class Newtype)"
                          , "import Data.Symbol (SProxy(SProxy))"
                          , ""
                          , "import Prelude"
                          , "import Data.Generic (class Generic)"
                          , ""
                          , "data Bar a b m c ="
                          , "    Bar1 (Maybe a)"
                          , "  | Bar2 (Either a b)"
                          , "  | Bar3 a"
                          , "  | Bar4 {"
                          , "      myMonadicResult :: m b"
                          , "    }"
                          , ""
                          , "derive instance genericBar :: (Generic a, Generic b, Generic (m b)) => Generic (Bar a b m c)"
                          , ""
                          , "--------------------------------------------------------------------------------"
                          , "_Bar1 :: forall a b m c. Prism' (Bar a b m c) (Maybe a)"
                          , "_Bar1 = prism' Bar1 f"
                          , "  where"
                          , "    f (Bar1 a) = Just $ a"
                          , "    f _ = Nothing"
                          , ""
                          , "_Bar2 :: forall a b m c. Prism' (Bar a b m c) (Either a b)"
                          , "_Bar2 = prism' Bar2 f"
                          , "  where"
                          , "    f (Bar2 a) = Just $ a"
                          , "    f _ = Nothing"
                          , ""
                          , "_Bar3 :: forall a b m c. Prism' (Bar a b m c) a"
                          , "_Bar3 = prism' Bar3 f"
                          , "  where"
                          , "    f (Bar3 a) = Just $ a"
                          , "    f _ = Nothing"
                          , ""
                          , "_Bar4 :: forall a b m c. Prism' (Bar a b m c) { myMonadicResult :: m b }"
                          , "_Bar4 = prism' Bar4 f"
                          , "  where"
                          , "    f (Bar4 r) = Just r"
                          , "    f _ = Nothing"
                          , ""
                          , "--------------------------------------------------------------------------------"
                          ]
      in m `shouldBe` txt
    it "test generation of constructor optics" $
      let bar = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy (Bar A B M1 C)))
          foo = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy Foo))
          barOptics = constructorOptics bar
          fooOptics = constructorOptics foo
          txt = T.unlines [
                            "_Bar1 :: forall a b m c. Prism' (Bar a b m c) (Maybe a)"
                          , "_Bar1 = prism' Bar1 f"
                          , "  where"
                          , "    f (Bar1 a) = Just $ a"
                          , "    f _ = Nothing"
                          , ""
                          , "_Bar2 :: forall a b m c. Prism' (Bar a b m c) (Either a b)"
                          , "_Bar2 = prism' Bar2 f"
                          , "  where"
                          , "    f (Bar2 a) = Just $ a"
                          , "    f _ = Nothing"
                          , ""
                          , "_Bar3 :: forall a b m c. Prism' (Bar a b m c) a"
                          , "_Bar3 = prism' Bar3 f"
                          , "  where"
                          , "    f (Bar3 a) = Just $ a"
                          , "    f _ = Nothing"
                          , ""
                          , "_Bar4 :: forall a b m c. Prism' (Bar a b m c) { myMonadicResult :: m b }"
                          , "_Bar4 = prism' Bar4 f"
                          , "  where"
                          , "    f (Bar4 r) = Just r"
                          , "    f _ = Nothing"
                          , ""
                          , "_Foo :: Prism' Foo Unit"
                          , "_Foo = prism' (\\_ -> Foo) f"
                          , "  where"
                          , "    f Foo = Just unit"
                          , "    f _ = Nothing"
                          , ""
                          , "_Bar :: Prism' Foo Int"
                          , "_Bar = prism' Bar f"
                          , "  where"
                          , "    f (Bar a) = Just $ a"
                          , "    f _ = Nothing"
                          , ""
                          , "_FooBar :: Prism' Foo { a :: Int, b :: String }"
                          , "_FooBar = prism' (\\{ a, b } -> FooBar a b) f"
                          , "  where"
                          , "    f (FooBar a b) = Just $ { a: a, b: b }"
                          , "    f _ = Nothing"
                          , ""
                          ]
      in (barOptics <> fooOptics) `shouldBe` txt
    it "tests generation of record optics" $
      let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy (SingleRecord A B)))
          bar = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy (Bar A B M1 C)))
          barOptics = recordOptics bar
          recTypeOptics = recordOptics recType
          txt = T.unlines [
                            "a :: forall a b. Lens' (SingleRecord a b) a"
                          , "a = _Newtype <<< prop (SProxy :: SProxy \"_a\")"
                          , ""
                          , "b :: forall a b. Lens' (SingleRecord a b) b"
                          , "b = _Newtype <<< prop (SProxy :: SProxy \"_b\")"
                          , ""
                          ]
      in (barOptics <> recTypeOptics) `shouldBe` txt
    it "tests generation of newtypes for record data type" $
      let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy (SingleRecord A B)))
          recTypeText = sumTypeToText settings recType
          txt = T.stripEnd $
                T.unlines [ "newtype SingleRecord a b ="
                          , "    SingleRecord {"
                          , "      _a :: a"
                          , "    , _b :: b"
                          , "    , c :: String"
                          , "    }"
                          , ""
                          , "derive instance genericSingleRecord :: (Generic a, Generic b) => Generic (SingleRecord a b)"
                          , "derive instance newtypeSingleRecord :: Newtype (SingleRecord a b) _"
                          , ""
                          , "--------------------------------------------------------------------------------"
                          , "_SingleRecord :: forall a b. Iso' (SingleRecord a b) { _a :: a, _b :: b, c :: String}"
                          , "_SingleRecord = _Newtype"
                          ,""
                          , "a :: forall a b. Lens' (SingleRecord a b) a"
                          , "a = _Newtype <<< prop (SProxy :: SProxy \"_a\")"
                          , ""
                          , "b :: forall a b. Lens' (SingleRecord a b) b"
                          , "b = _Newtype <<< prop (SProxy :: SProxy \"_b\")"
                          , ""
                          , "--------------------------------------------------------------------------------"
                          ]
      in recTypeText `shouldBe` txt
    it "tests generation of newtypes for haskell newtype" $
      let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy SomeNewtype))
          recTypeText = sumTypeToText settings recType
          txt = T.stripEnd $
                T.unlines [ "newtype SomeNewtype ="
                          , "    SomeNewtype Int"
                          , ""
                          , "derive instance genericSomeNewtype :: Generic SomeNewtype"
                          , "derive instance newtypeSomeNewtype :: Newtype SomeNewtype _"
                          , ""
                          , "--------------------------------------------------------------------------------"
                          , "_SomeNewtype :: Iso' SomeNewtype Int"
                          , "_SomeNewtype = _Newtype"
                          , "--------------------------------------------------------------------------------"
                          ]
      in recTypeText `shouldBe` txt
    it "tests generation of newtypes for haskell data type with one argument" $
      let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy SingleValueConstr))
          recTypeText = sumTypeToText settings recType
          txt = T.stripEnd $
                T.unlines [ "newtype SingleValueConstr ="
                          , "    SingleValueConstr Int"
                          , ""
                          , "derive instance genericSingleValueConstr :: Generic SingleValueConstr"
                          , "derive instance newtypeSingleValueConstr :: Newtype SingleValueConstr _"
                          , ""
                          , "--------------------------------------------------------------------------------"
                          , "_SingleValueConstr :: Iso' SingleValueConstr Int"
                          , "_SingleValueConstr = _Newtype"
                          , "--------------------------------------------------------------------------------"
                          ]
      in recTypeText `shouldBe` txt
    it "tests generation for haskell data type with one constructor, two arguments" $
      let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy SingleProduct))
          recTypeText = sumTypeToText settings recType
          txt = T.stripEnd $
                T.unlines [ "data SingleProduct ="
                          , "    SingleProduct String Int"
                          , ""
                          , "derive instance genericSingleProduct :: Generic SingleProduct"
                          , ""
                          , "--------------------------------------------------------------------------------"
                          , "_SingleProduct :: Prism' SingleProduct { a :: String, b :: Int }"
                          , "_SingleProduct = prism' (\\{ a, b } -> SingleProduct a b) f"
                          , "  where"
                          , "    f (SingleProduct a b) = Just $ { a: a, b: b }"
                          , ""
                          , "--------------------------------------------------------------------------------"
                          ]
      in recTypeText `shouldBe` txt
    it "tests that sum types with multiple constructors don't generate record optics" $
      let recType = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy TwoRecords))
          recTypeOptics = recordOptics recType
      in recTypeOptics `shouldBe` "" -- No record optics for multi-constructors

  describe "buildBridge without lens-code-gen" $ do
    let settings = getSettings noLenses
    it "tests generation of for custom type Foo" $
      let proxy = Proxy :: Proxy Foo
          recType = bridgeSumType (buildBridge defaultBridge) (order proxy $ mkSumType proxy)
          recTypeText = sumTypeToText settings recType
          txt = T.unlines [ "data Foo ="
                          , "    Foo"
                          , "  | Bar Int"
                          , "  | FooBar Int String"
                          , ""
                          , "derive instance eqFoo :: Eq Foo"
                          , "derive instance ordFoo :: Ord Foo"
                          , "derive instance genericFoo :: Generic Foo"
                          ]
      in recTypeText `shouldBe` txt
    it "tests the generation of a whole (dummy) module" $
      let advanced' = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy (Bar A B M1 C)))
          modules = sumTypeToModule advanced' Map.empty
          m = head . map (moduleToText settings) . Map.elems $ modules
          txt = T.unlines [ "-- File auto generated by purescript-bridge! --"
                          , "module TestData where"
                          , ""
                          , "import Data.Either (Either)"
                          , "import Data.Maybe (Maybe, Maybe(..))"
                          , "import Data.Newtype (class Newtype)"
                          , ""
                          , "import Prelude"
                          , "import Data.Generic (class Generic)"
                          , ""
                          , "data Bar a b m c ="
                          , "    Bar1 (Maybe a)"
                          , "  | Bar2 (Either a b)"
                          , "  | Bar3 a"
                          , "  | Bar4 {"
                          , "      myMonadicResult :: m b"
                          , "    }"
                          , ""
                          , "derive instance genericBar :: (Generic a, Generic b, Generic (m b)) => Generic (Bar a b m c)"
                          , ""
                          ]
      in m `shouldBe` txt
    it "tests generation of newtypes for record data type" $
      let recType' = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy (SingleRecord A B)))
          recTypeText = sumTypeToText settings recType'
          txt = T.unlines [ "newtype SingleRecord a b ="
                          , "    SingleRecord {"
                          , "      _a :: a"
                          , "    , _b :: b"
                          , "    , c :: String"
                          , "    }"
                          , ""
                          , "derive instance genericSingleRecord :: (Generic a, Generic b) => Generic (SingleRecord a b)"
                          , "derive instance newtypeSingleRecord :: Newtype (SingleRecord a b) _"
                          ]
      in recTypeText `shouldBe` txt
    it "tests generation of newtypes for haskell newtype" $
      let recType' = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy SomeNewtype))
          recTypeText = sumTypeToText settings recType'
          txt = T.unlines [ "newtype SomeNewtype ="
                          , "    SomeNewtype Int"
                          , ""
                          , "derive instance genericSomeNewtype :: Generic SomeNewtype"
                          , "derive instance newtypeSomeNewtype :: Newtype SomeNewtype _"
                          ]
      in recTypeText `shouldBe` txt
    it "tests generation of newtypes for haskell data type with one argument" $
      let recType' = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy SingleValueConstr))
          recTypeText = sumTypeToText settings recType'
          txt = T.unlines [ "newtype SingleValueConstr ="
                          , "    SingleValueConstr Int"
                          , ""
                          , "derive instance genericSingleValueConstr :: Generic SingleValueConstr"
                          , "derive instance newtypeSingleValueConstr :: Newtype SingleValueConstr _"
                          ]
      in recTypeText `shouldBe` txt
    it "tests generation for haskell data type with one constructor, two arguments" $
      let recType' = bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy SingleProduct))
          recTypeText = sumTypeToText settings recType'
          txt = T.unlines [ "data SingleProduct ="
                          , "    SingleProduct String Int"
                          , ""
                          , "derive instance genericSingleProduct :: Generic SingleProduct"
                          ]
      in recTypeText `shouldBe` txt