I am trying to build an automated feature testing suite using webdriver
and polysemy
in Haskell. I've gotten as far as defining proper effects and interpreting them into a webdriver WD monad, but now I'm stuck.
I have a value of type Member BrowserMaster r => Sem r ()
where BrowserMaster
is my custom capability.
And this is the interpreter:
runBrowserMaster :: Members [Embed WD.WD, Embed IO] r => Sem (BrowserMaster ': r) a -> Sem r a
runBrowserMaster = interpret $ \case
ClickElement bmSelector ->
let action = (WD.findElem (bmSelectoToSelector bmSelector) >>= WD.click :: WD.WD ())
in embed action
{- ... -}
Now I'm wondering how to convert the Embed WD.WD
effect into Embed IO
, so that I end up with just one.
I tried to craft an interpreter:
runWebDriver :: Member (Embed IO) r => Sem (Embed WD.WD ': r) a -> Sem r a
runWebDriver = interpret $
\a -> embed $ runSession chromeConfig . finallyClose $ do
setImplicitWait 60000
setWindowSize (1024, 768)
unEmbed a
(Here runSession chromeConfig . finallyClose
is a WD a -> IO a
)
It does work, but it seems to fire up a new browser session for each of the commands instead of starting it just once, doing everything within and closing.
I have an intuition that it has to do something with resource acquisition and release, but I just cannot get my head around this to be able to put it all together.
Keep in mind that each interpreter will be executed each time an action of the BrowserMaster
effect is executed. So every time it runs the runWebDriver
interpreter, which explains why it creates, runs and close the session.
I think what you want to do is instead to create/delete the session once, and execute your whole code in this session.
Also, since WD
is already a wrapper around IO
, I think it's unnecessary to embed both effects.
I am not familiar with your code nor the webdriver
library, but I assume this would be something along the lines of:
main :: IO ()
main = runSession chromeConfig . finallyClose $ do
setImplicitWait 60000
setWindowSize (1024, 768)
runM . runBrowserMaster $ myBusinessCode
runBrowserMaster :: Member (Embed WD.WD) r => Sem (BrowserMaster ': r) a -> Sem r a
runBrowserMaster = interpret $ \case
ClickElement bmSelector ->
let action = (WD.findElem (bmSelectoToSelector bmSelector) >>= WD.click :: WD.WD ())
in embed action
{- ... -}
Note: If you need to run some IO
code in the interpreter, use liftIO
to make it an WD
action instead, e.g. liftIO $ putStrLn "Hello world"
.
PS: I recommend renaming the runBrowserMaster
interpreter to something like browserMasterToWD
as it better represents what it does: interpret the BrowserMaster
effect in terms of an WD
action.