Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[yesod-test] Expose hook helpers #1749

Open
parsonsmatt opened this issue Mar 10, 2022 · 1 comment
Open

[yesod-test] Expose hook helpers #1749

parsonsmatt opened this issue Mar 10, 2022 · 1 comment

Comments

@parsonsmatt
Copy link
Contributor

In the work app, I've created a few extras:

instance YesodDispatch site => Example (a -> SIO (YesodExampleData site) b) where
  type Arg (a -> SIO (YesodExampleData site) b) =
    (TestApp site, a)

  evaluateExample example' params action =
      Hspec.evaluateExample
          (action $ \((site, middleware), a) -> do
              app <- toWaiAppPlain site
              _ <- evalSIO (example' a) YesodExampleData
                  { yedApp = middleware app
                  , yedSite = site
                  , yedCookies = M.empty
                  , yedResponse = Nothing
                  }
              return ())
          params
          ($ ())

-- this is extremely dangerous. i should definitely NOT do this as soon as
-- yesod-test exports the SIO constructor. or even just evalSIO.

evalSIO :: SIO s a -> s -> IO a
evalSIO sio s =
  let (ReaderT f) = unsafeCoerce sio
  in
    newIORef s >>= f

beforeApp
  :: YesodDispatch site
  => SIO (YesodExampleData site) a
  -> SpecWith (TestApp site, a)
  -> SpecWith (TestApp site)
beforeApp action =
  beforeWith $ \testapp@(site, middleware) -> do
    app <- toWaiAppPlain site
    let yesodExampleData =
          YesodExampleData
            { yedApp = middleware app
            , yedSite = site
            , yedCookies = M.empty
            , yedResponse = Nothing
            }
    a <- evalSIO action yesodExampleData
    pure (testapp, a)

beforeWithApp
  :: YesodDispatch site
  => (a -> SIO (YesodExampleData site) b)
  -> SpecWith (TestApp site, b)
  -> SpecWith (TestApp site, a)
beforeWithApp action =
 beforeWith $ \(testapp@(site, middleware), a) -> do
    app <- toWaiAppPlain site
    let yesodExampleData =
          YesodExampleData
            { yedApp = middleware app
            , yedSite = site
            , yedCookies = M.empty
            , yedResponse = Nothing
            }
    b <- evalSIO (action a) yesodExampleData
    pure (testapp, b)

This allows you to share test setup nicely and nest that context.

spec = withApp $ do
    beforeApp setupUser $ do
       it "something only a user needs" $ \(Entity userId User{..} -> do
           -- this is in YesodExample 
    beforeAppWith (\user -> (,) user <$> setupOrganization user) $ do
        it "now it has a user and an organization" $ \(user, org) -> do
           -- heck yeah

Other hooks could be exposed in a similar way.

@parsonsmatt
Copy link
Contributor Author

So there's a problem.

Because YesodExampleData is created and then discarded only in the Example instance, we don't actually have a way to persist the state of get "/" between the hook and the test case.

We can change the type of Arg (YesodExample site a) from TestApp site to YesodExampleData site.

This delegates the actual creation of the YesodExampleData from the Example instance to the before call that actually creates the site.

But, this is a pretty big potentially breaking change. The Arg type changing means that every app that is built with the Yesod templates will not work, because withApp in those templates is returning a TestApp App and not a YesodExampleData App.

Furthermore, to support all of the features of yesod-test using hspec's hooks, we have to change the type of some of the fields on YesodExampleData.

A beneft is that we get to delete all of the custom test tree behavior, and all the y functions become type-speciified aliases of their hspec equivalents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant