content: {-# language ScopedTypeVariables #-}
  module Eve.Internal.ListenersSpec where
  
  import Test.Hspec
  
  import Fixtures
  import Eve
  
  import Control.Monad.State
  import Control.Lens
  
  basicAction :: AppT AppState Identity String
  basicAction = do
    addListener (const (store .= "new") :: CustomEvent -> AppT AppState Identity ())
    dispatchEvent_ CustomEvent
    use store
  
  delayedExit :: App ()
  delayedExit = do
    addListener (const exit :: CustomEvent -> App ())
    dispatchEventAsync (return CustomEvent)
    store .= "new"
  
  removeListenersTest :: AppT AppState Identity String
  removeListenersTest = do
    listId <- addListener (const (store .= "new") :: CustomEvent -> AppT AppState Identity ())
    removeListener listId
    dispatchEvent_ CustomEvent
    use store
  
  asyncEventsTest :: App ()
  asyncEventsTest = do
    addListener (const exit :: CustomEvent -> App ())
    asyncEventProvider (\d -> d CustomEvent)
    store .= "new"
  
  localStateIsolationTest :: ActionT AppState NestedStates Identity String
  localStateIsolationTest = do
    addLocalListener (const (nestedString .= "new") :: CustomEvent -> ActionT AppState NestedStates Identity ())
    dispatchEvent_ CustomEvent
    use nestedString
  
  globalStateIsolationTest :: AppT AppState Identity String
  globalStateIsolationTest = do
    addListener (const (store .= "new") :: CustomEvent -> AppT AppState Identity ())
    runActionOver nestedStates $ do
      dispatchLocalEvent_ CustomEvent
    use store
  
  listenerPassThroughTest :: AppT AppState Identity String
  listenerPassThroughTest = do
    let nestedAction :: ActionT AppState NestedStates Identity ()
        nestedAction = do
          addListener (const (store .= "new") :: CustomEvent -> AppT AppState Identity ())
          dispatchEvent CustomEvent
    runActionOver nestedStates nestedAction
    use store
  
  
  data OtherEvent = OtherEvent
  multiAsyncEventsTest :: App ()
  multiAsyncEventsTest = do
    addListener (const exit :: CustomEvent -> App ())
    addListener (const (store .= "new") :: OtherEvent -> App ())
    asyncEventProvider (\d -> d OtherEvent >> d CustomEvent)
  
  spec :: Spec
  spec = do
    describe "dispatchEvent/addListener" $
      it "Triggers Listeners" $ fst (noIOTest basicAction) `shouldBe` "new"
  
    describe "local events" $ do
      it "local state is isolated from global events" $ fst (noIOTest $ runAction localStateIsolationTest) `shouldBe` "default"
      it "global state is isolated from local events" $ fst (noIOTest globalStateIsolationTest) `shouldBe` "default"
      it "global event actions pass through from local state" $ fst (noIOTest listenerPassThroughTest) `shouldBe` "new"
  
    describe "dispatchEventAsync" $ do
      delayedExitState <- ioTest delayedExit
      it "Triggers Listeners Eventually" $ (delayedExitState ^. store) `shouldBe` "new"
  
    describe "removeListener" $
      it "Removes Listeners" $ fst (noIOTest removeListenersTest) `shouldBe` "default"
  
    describe "asyncEventProvider" $ do
      asyncEventsResult <- ioTest asyncEventsTest
      it "Provides events eventually" $ (asyncEventsResult ^. store) `shouldBe` "new"
      multiAsyncEventsResult <- ioTest multiAsyncEventsTest
      it "Can provide different event types" $ (multiAsyncEventsResult ^. store) `shouldBe` "new"
  