packages feed

zephyr-0.5.3: test/Test/Lib.hs

{-# LANGUAGE NoImplicitPrelude #-}

module Test.Lib (spec) where

import Control.Monad.Except
import Data.Text (Text)
import qualified Data.Text as T
import System.Exit (ExitCode (..))
import System.Process (readProcessWithExitCode)
import Test.HUnit (assertEqual)
import Test.Hspec
import Test.Utils
import Prelude hiding (exp)

data LibTest = LibTest
  { libTestEntries :: [Text],
    libTestZephyrOptions :: Maybe [Text],
    libTestJs :: Text,
    -- | true if it should run without error, false if it should error
    libTestShouldPass :: Bool
  }

libTests :: [LibTest]
libTests =
  [ LibTest
      ["Unsafe.Coerce.Test.unsafeX"]
      Nothing
      ( "import { unsafeX } from './dce-output/Unsafe.Coerce.Test/index.js';"
          <> "unsafeX(1)(1);"
      )
      True,
    LibTest
      ["Foreign.Test.add"]
      Nothing
      ( "import { add } from './dce-output/Foreign.Test/index.js';"
          <> "add(1)(1);"
      )
      True,
    LibTest
      ["Foreign.Test.add"]
      Nothing
      ( "import { mult } from './dce-output/Foreign.Test/index.js';"
          <> "mult(1)(1);"
      )
      False,
    LibTest
      ["Foreign.Test.add"]
      Nothing
      "import { mult } from './dce-output/Foreign.Test/foreign.js';"
      False,
    LibTest
      ["Foreign.Test.snowflake"]
      Nothing
      ( "import { snowflake } from './dce-output/Foreign.Test/index.js';"
          <> "if(snowflake !== '❄'){console.error(`'${snowflake}' !== '❄'`); throw 'Error';}"
      )
      True,
    LibTest
      ["Foreign.Test.b"]
      Nothing
      ( "import { b } from './dce-output/Foreign.Test/index.js';"
          <> "if(b() !== 5) throw 'Error';"
      )
      True,
    LibTest
      ["Eval.makeAppQueue"]
      Nothing
      "import { makeAppQueue } from './dce-output/Eval/index.js';"
      True,
    LibTest
      ["Eval.evalUnderArrayLiteral"]
      Nothing
      "import { evalUnderArrayLiteral } from './dce-output/Eval/index.js';"
      True,
    LibTest
      ["Eval.evalUnderObjectLiteral"]
      Nothing
      "import { evalUnderObjectLiteral } from './dce-output/Eval/index.js';"
      True,
    LibTest
      ["Eval.evalVars"]
      Nothing
      "import { evalVars } from './dce-output/Eval/index.js';"
      True,
    LibTest
      ["Eval"]
      Nothing
      "import { evalVars } from './dce-output/Eval/index.js';"
      True,
    LibTest
      ["Eval.recordUpdate"]
      Nothing
      ( "import * as E from './dce-output/Eval/index.js';"
          <> "var foo = E.recordUpdate({ foo: '', bar: 0 })(E.Foo.create('foo'));"
          <> "if (foo.foo != 'foo') {"
          <> "  console.error(foo);"
          <> "  throw 'Error';"
          <> "}"
      )
      True,
    LibTest
      ["Literals.fromAnArray"]
      Nothing
      ( "import * as lits from './dce-output/Literals/index.js';"
          <> "if (lits.fromAnArray == null || lits.AStr == null || lits.AInt != null) {"
          <> "  throw 'Error';"
          <> "}"
      )
      True,
    LibTest
      ["Literals.fromAnObject"]
      Nothing
      ( "import * as lits from './dce-output/Literals/index.js';"
          <> "if (lits.fromAnObject == null || lits.AStr == null || lits.AInt != null) {"
          <> "  throw 'Error';"
          <> "}"
      )
      True,
    -- Control.Alt re-exports map from Data.Functor
    LibTest
      ["Control.Alt.map"]
      Nothing
      "import { map } from './dce-output/Control.Alt/index.js';"
      True,
    LibTest
      ["Data.Array.span"]
      Nothing
      ( "import { span } from './dce-output/Data.Array/index.js';"
          <> "const a = [1, 3, 2, 4, 5];"
          <> "const p = (x) => x % 2 === 1;"
          <> "const r = span(p)(a);"
          <> "const expected = { init: [1, 3], rest: [2, 4, 5] };"
          <> "console.log(r);"
          <> "if (!r.init.every((e, i) => e === expected.init[i])) throw 'Error';"
          <> "if (!r.rest.every((e, i) => e === expected.rest[i])) throw 'Error';"
      )
      True
  ]

assertLib :: LibTest -> Expectation
assertLib l = do
  res <- runExceptT . runLibTest $ l
  assertEqual "lib should run" (Right ()) res

runLibTest :: LibTest -> ExceptT TestError IO ()
runLibTest
  LibTest
    { libTestEntries,
      libTestZephyrOptions,
      libTestJs,
      libTestShouldPass
    } = do
    spagoBuild "LibTest"
    runZephyr "LibTest" libTestEntries libTestZephyrOptions
    (ecNode, stdNode, errNode) <-
      lift $
        readProcessWithExitCode
          "node"
          [ "--input-type=module",
            "-e",
            T.unpack libTestJs
          ]
          ""
    when
      (libTestShouldPass && ecNode /= ExitSuccess)
      (throwError $ NodeError "LibTest (should pass)" ecNode stdNode errNode)
    when
      (not libTestShouldPass && ecNode == ExitSuccess)
      (throwError $ NodeError "LibTest (should fail)" ecNode stdNode errNode)

spec :: Spec
spec =
  changeDir "test/lib-tests" $
    context "test-lib" $
      forM_ libTests $ \l ->
        specify (T.unpack $ T.intercalate (T.pack " ") $ libTestEntries l) $ assertLib l