content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE TypeApplications #-}
  
  module Formatting (tests) where
  
  import Control.Exception
  import Data.Aeson (SumEncoding(UntaggedValue), defaultOptions, sumEncoding, tagSingleConstructors)
  import Data.Aeson.TypeScript.TH
  import Data.Proxy
  import Data.String.Interpolate
  import Test.Hspec
  
  
  data D = S | F deriving (Eq, Show)
  $(deriveTypeScript defaultOptions ''D)
  
  data D2 = S2 | F2 deriving (Eq, Show)
  $(deriveTypeScript defaultOptions ''D2)
  
  -- A.encode U --> "[]"
  data Unit = U deriving (Eq, Show)
  $(deriveTypeScript defaultOptions ''Unit)
  
  -- A.encode UTagSingle --> "\"UTagSingle\""
  data UnitTagSingle = UTagSingle deriving (Eq, Show)
  $(deriveTypeScript (defaultOptions { tagSingleConstructors = True, sumEncoding = UntaggedValue }) ''UnitTagSingle)
  
  data PrimeInType' = PrimeInType
  $(deriveTypeScript defaultOptions ''PrimeInType')
  
  data PrimeInConstr = PrimeInConstr'
  $(deriveTypeScript defaultOptions ''PrimeInConstr)
  
  data FooBar =
    Foo {
      -- | @no-emit-typescript
      recordString :: String
      , recordInt :: Int
      }
    |
    -- | @no-emit-typescript
    Bar {
        barInt :: Int
    }
  $(deriveTypeScript defaultOptions ''FooBar)
  
  data NormalConstructors =
    -- | @no-emit-typescript
    Con1 String
    | Con2 Int
  $(deriveTypeScript defaultOptions ''NormalConstructors)
  
  tests :: Spec
  tests = describe "Formatting" $ do
    describe "when given a Sum Type" $ do
      describe "and the TypeAlias format option is set" $
        it "should generate a TS string literal type" $
          formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @D Proxy) `shouldBe`
            [i|type D = "S" | "F";|]
  
      describe "and the Enum format option is set" $ do
        it "should generate a TS Enum" $
          formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy) `shouldBe`
            [i|enum D { S="S", F="F" }|]
  
        it "should generate a TS Enum with multiple" $
          formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy <> getTypeScriptDeclarations @D2 Proxy) `shouldBe`
            [__i|enum D { S="S", F="F" }
  
                 enum D2 { S2="S2", F2="F2" }|]
  
        it "should generate a normal type from Unit, singe tagSingleConstructors=False by default" $
          formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @Unit Proxy) `shouldBe`
            [__i|type Unit = IU;
  
                 type IU = void[];|]
  
        it "should generate a suitable enum from UnitTagSingle" $
          formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @UnitTagSingle Proxy) `shouldBe`
            [__i|enum UnitTagSingle { UTagSingle="UTagSingle" }|]
  
      describe "and the EnumWithType format option is set" $ do
        it "should generate a TS Enum with a type declaration" $
          formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = EnumWithType }) (getTypeScriptDeclarations @D Proxy) `shouldBe`
            [i|enum DEnum { S="S", F="F" }\n\ntype D = keyof typeof DEnum;|]
  
        it "should also work for UnitTagSingle" $
          formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = EnumWithType }) (getTypeScriptDeclarations @UnitTagSingle Proxy) `shouldBe`
            [i|enum UnitTagSingleEnum { UTagSingle="UTagSingle" }\n\ntype UnitTagSingle = keyof typeof UnitTagSingleEnum;|]
  
    describe "when the name has an apostrophe" $ do
      describe "in the type" $ do
        it "throws an error" $ do
          evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInType' Proxy)) `shouldThrow` anyErrorCall
  
      describe "in the constructor" $ do
        it "throws an error" $ do
          evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInConstr Proxy)) `shouldThrow` anyErrorCall
  
  #if MIN_VERSION_template_haskell(2,18,0)
    describe "when @no-emit-typescript is present" $ do
      it [i|works on records and constructors of record types|] $ do
        formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @FooBar Proxy) `shouldBe` [i|type FooBar = IFoo;\n\ninterface IFoo {\n  tag: "Foo";\n  recordInt: number;\n}|]
  
      it [i|works on normal constructors|] $ do
        formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @NormalConstructors Proxy) `shouldBe` [i|type NormalConstructors = ICon2;\n\ninterface ICon2 {\n  tag: "Con2";\n  contents: number;\n}|]
  #endif
  
  main :: IO ()
  main = hspec tests
  