I'm working through the Haskell WebDriver selenium package for testing, here.
I have this example:
import Test.WebDriver
firefoxConfig :: WDConfig
firefoxConfig = defaultConfig
main :: IO ()
main = runSession firefoxConfig $ do
openPage "http://google.com"
searchInput <- findElem ( ByCSS "input[type='text']" )
sendKeys "Hello, World!" searchInput
submit searchInput
closeSession
The getting started section makes clear that the selenium client requires a selenium server to communicate with
java -jar selenium-server-standalone-*.jar
Without it running, you get this:
ghci λ> main
*** Exception: FailedConnectionException2 "127.0.0.1" 4444 False connect: does not exist (Connection refused)
I'd like to wrap my whole test script in a function that initializes the selenium-server, records its pid, and kill(pid) after I run the session. That is, for the duration of my existing main, I'd like to call the java selenium-server into existence, but I'd like it to stop existing as soon as the call finishes.
In python I'd do this by something like defining a __enter__()
and an __exit__()
with other tear-down stuff, subprocess.Popen
, record the id, kill it, then calling
with Browser() as b:
do_stuff
I get the sense the runSession entity is the kind of thing I would need to replicate to wrap startup and teardown like this in the sense that it takes the firefoxConfig $ do
block as an argument and I want to do that, too.
However, I can't quite understand the types from interrogating runSession, how to make this kind of thing:
ghci λ> :t runSession
runSession
:: Test.WebDriver.Config.WebDriverConfig conf =>
conf -> WD a -> IO a
I think I'd be looking for some kind of withMonad I could apply to this which is applied to the do
. I think the syntax would be some kind of...
import Test.WebDriver
import System.Process
firefoxConfig :: WDConfig
firefoxConfig = defaultConfig
withBrowser :: Monad a -> Monad a -- maybe this type?
withBrowser = do
r <- createProcess (proc "java -jar selenium-server-standalone-*.jar" [])
-- other magic here?
main :: IO ()
main = withBrowser $ runSession firefoxConfig $ do
openPage "http://google.com"
searchInput <- findElem ( ByCSS "input[type='text']" )
sendKeys "Hello, World!" searchInput
submit searchInput
closeSession
How would I achieve this? Is monad right at all? Is there a more Haskell idiom or strategy for this?
You basically just want bracket
from https://hackage.haskell.org/package/base-4.9.0.0/docs/Control-Exception.html#v:bracket .
It allows you to specify setup and teardown IO actions to to run around a third action. It automatically handles feeding the output of the setup action into both the main and teardown actions, do your setup action should just give the PID as it's result, so the teardown action will be told what PID to kill.
Something like:
withBrowser browserAction
= bracket startSelenium killSelenium (const browserAction)
(Where I've assumed you don't want the main action to have to take an argument for the pid, so I used const
to ignore it)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With