packages feed

webdriver 0.13.0.0 → 0.14.0.0

raw patch · 8 files changed

+334/−120 lines, 8 filesdep ~aesondep ~basedep ~bytestringnew-component:exe:demo

Dependency ranges changed: aeson, base, bytestring, exceptions, http-client, http-types, text

Files

CHANGELOG.md view
@@ -1,66 +1,84 @@ # Change Log +## 0.14.0.0+* Fix MVar bug in teardownWebDriverContext.+* Make driverConfigLogDir optional (#206).+* Add "Test.WebDriver.WD" module for simple usage.+ ## 0.13.0.0-* Deprecated function `elemInfo` removed-* `deleteVisibleCookies` renamed to `deleteCookies`.+* Added support for Selenium 3 and 4 with W3C WebDriver protocol compatibility.+* Dropped legacy Selenium JSON wire protocol support.+* Reworked [createSession](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:createSession) into [startSession](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver.html#v:startSession), which uses [WebDriverContext](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Types.html#t:WebDriverContext) for process lifecycle management.+* Replaced [WebDriver](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Class.html#t:WebDriver) class with [WebDriverBase](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Types.html#t:WebDriverBase) and switched from `MonadBaseControl IO` to `MonadUnliftIO`.+* Simplified module hierarchy with centralized imports under `Test.WebDriver`.+* Enhanced browser profile handling with in-memory zip archiving instead of on-disk preparation.+* Added comprehensive test suite with matrix testing across Selenium versions and browsers.+* Added browser log retrieval functions: [withRecordLogsViaBiDi](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:withRecordLogsViaBiDi) and [getLogs](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:getLogs).+* Now you can manage the script, page load, and implicit wait timeouts in a single operation with [getTimeouts](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:getTimeouts)/[setTimeouts](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:setTimeouts).+* The [getWindowSize](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:getWindowSize)/[setWindowSize](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:setWindowSize)/[getWindowPos](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:getWindowPos)/[setWindowPos](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:setWindowPos) functions have been replaced with [getWindowRect](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:getWindowRect)/[setWindowRect](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:setWindowRect).+* The [elemPos](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:elemPos)/[elemSize](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:elemSize) functions have been replaced with [elemRect](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:elemRect).+* [deleteCookieByName](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:deleteCookieByName) has been renamed to [deleteCookie](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:deleteCookie) and the pointless old [deleteCookie](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:deleteCookie) removed.+* [screenshotBase64](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:screenshotBase64) has been removed and [screenshotElement](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:screenshotElement) added.+* [deleteVisibleCookies](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:deleteVisibleCookies) renamed to [deleteCookies](https://hackage-content.haskell.org/package/webdriver-0.13.0.0/docs/Test-WebDriver-Commands.html#v:deleteCookies).+* Deprecated function [elemInfo](https://hackage.haskell.org/package/webdriver-0.12.0.0/docs/Test-WebDriver-Commands.html#v:elemInfo) removed.  ## 0.12.0.1 * Switch from `data-default-class` to `data-default` to address https://github.com/commercialhaskell/stackage/issues/7545. This is weirdly complicated: it looks like versions of `data-default` from `0.5.2` to `0.8.0.0` may pull in unnecessary (to us) `data-default-instances-*` dependencies. However, what we've done here should build with all versions, so we won't worry about it. At the next breaking change I'd like to remove `data-default` entirely.  ## 0.12.0.0-* Support aeson-2.2.0+* Support aeson-2.2.0.  ## 0.11.0.0-* Support GHC 9.6-* Fix a link to the wrong GitHub branch in package description-* Remove instances for deprecated ListT and ErrorT+* Support GHC 9.6.+* Fix a link to the wrong GitHub branch in package description.+* Remove instances for deprecated ListT and ErrorT.  ## 0.10.0.1-* Update links to reflect this package's new home at https://github.com/haskell-webdriver/haskell-webdriver/+* Update links to reflect this package's new home at https://github.com/haskell-webdriver/haskell-webdriver/.  ## 0.10.0.0-* Add Aeson 2 compatibility to support GHC 9.0 and 9.2-* Derive MonadMask instance for the WD monad-* Fix parameter name in the `focusWindow` function-* Fix Cookie ToJSON instance-* Convert a couple noReturn calls to ignoreReturn to avoid a crash-* Switch to hpack-* Switch to GitHub Actions CI-* Remove cabal flags and shorten extra-source-files+* Add Aeson 2 compatibility to support GHC 9.0 and 9.2.+* Derive MonadMask instance for the WD monad.+* Fix parameter name in the `focusWindow` function.+* Fix Cookie ToJSON instance.+* Convert a couple noReturn calls to ignoreReturn to avoid a crash.+* Switch to hpack.+* Switch to GitHub Actions CI.+* Remove cabal flags and shorten extra-source-files.  ## 0.9.0.1-* Fixed build errors when building against aeson-1.4.3.0+* Fixed build errors when building against aeson-1.4.3.0.  ## 0.9  ### Breaking API changes-* Changed the type of the `cookExpiry` field of `Cookie` from `Maybe Integer` to `Maybe Double`. This fixes parsing issues with some Selenium server implementations+* Changed the type of the `cookExpiry` field of `Cookie` from `Maybe Integer` to `Maybe Double`. This fixes parsing issues with some Selenium server implementations. * Changed `elemPos` and `elemSize` to return `Float` pairs instead of `Int` pairs. This fixes parsing issues with some Selemium server implementations. * Removed the `Element` argument from the `sendRawKeys` function. This argument is not used in modern Selenium versions.  ### New API features * Added a `LogDebug` constructor to the `LogLevel` type. * Added `ffAccpetInsecureCerts` capability for `Firefox` geckodriver.-* The constructor for `ExpectFailed` is now exported so that it can be caught properly by exception handlers+* The constructor for `ExpectFailed` is now exported so that it can be caught properly by exception handlers. * Added GHC callstack support. `BadJSON` exceptions are now caught and rethrown with `error` calls to improve stack traces.  ### W3C standard compatibility fixes-* Fixed `ToJSON Element` instance so that it accepts both old OSS and new W3C element format (fixes compatibility issue with Selenium 3+ versions)-* Changed the `maximize` API call to use POST instead of GET+* Fixed `ToJSON Element` instance so that it accepts both old OSS and new W3C element format (fixes compatibility issue with Selenium 3+ versions).+* Changed the `maximize` API call to use POST instead of GET.  ### Other Selenium compatibility fixes-* Fixed an error with some versions of chromedriver when using `closeWindow`+* Fixed an error with some versions of chromedriver when using `closeWindow`.  ## 0.8.5-* Added support for experimental Chrome options that are not part of the API-* Added a Phantomjs constructor for the Browser type-* Changed the type of StackFrame line numbers from Word to Int to avoid parse errors in newer Aeson versions (sometimes the server returns negative line numbers)-* Fixed support for http-client > 0.5+* Added support for experimental Chrome options that are not part of the API.+* Added a Phantomjs constructor for the Browser type.+* Changed the type of StackFrame line numbers from Word to Int to avoid parse errors in newer Aeson versions (sometimes the server returns negative line numbers).+* Fixed support for http-client > 0.5.  ## 0.8.4 * Added a new `Test.WebDriver.Common.Keys` module with named constants for use with `sendKeys`-* Updated old URLs in documentation-* Introduced support for http-client 5.0+* Updated old URLs in documentation.+* Introduced support for http-client 5.0.  ## 0.8.3 * Removed most upper bounds on dependencies in our cabal file to avoid stackage version madness.@@ -70,19 +88,19 @@ * Added new `WebDriver` instance for `ExceptT`.  ## 0.8.1-* Previously internal convenience functions `noReturn` and `ignoreReturn` are now exported in Test.WebDriver.JSON+* Previously internal convenience functions `noReturn` and `ignoreReturn` are now exported in Test.WebDriver.JSON. * `elemInfo` is now deprecated due to it being phased out in the Marionette (Firefox) driver. It will likely be removed once Selenium 4 is released. * Fixed an issue causing PAC settings to not work.  ## 0.8.0.4-* Quick fix to parse "unsupported command" errors when using Marionette driver (Selenium + Marionette has nonstandard behavior when reporting that error type)+* Quick fix to parse "unsupported command" errors when using Marionette driver (Selenium + Marionette has nonstandard behavior when reporting that error type).  ## 0.8.0.3-* Fixed build errors for GHC < 7.10. webdriver now builds with GHC stable releases 7.4.2, 7.6.3, and 7.8.4+* Fixed build errors for GHC < 7.10. webdriver now builds with GHC stable releases 7.4.2, 7.6.3, and 7.8.4. * Fixed support for bytestring 0.9.*  ## 0.8.0.2-* Fixed issue introduced in 0.8 that caused build failure when using aeson < 0.10+* Fixed issue introduced in 0.8 that caused build failure when using aeson < 0.10.  ## 0.8.0.1 * findElems and and findElemsFrom were accidentally changed to return a single Element in the last release. This has been fixed.@@ -115,15 +133,15 @@  ### Reworked custom HTTP headers interface * Support for custom request headers was added rather hastily, resulting in several functions having explicit `RequestHeaders` parameters. The interface has been reworked now so that custom request headers are stored inside `WDSession` and explicit `RequestHeaders` parameters have been removed.-* There's also now a distinction in configuration between `wdAuthHeaders` which are used only during initial session creation, and `wdRequestHeaders`, which are used with all other HTTP requests-* Two new utility functions were added to make working with custom HTTP headers easier: `withRequestHeaders` and `withAuthHeaders`+* There's also now a distinction in configuration between `wdAuthHeaders` which are used only during initial session creation, and `wdRequestHeaders`, which are used with all other HTTP requests.+* Two new utility functions were added to make working with custom HTTP headers easier: `withRequestHeaders` and `withAuthHeaders`.  ### Clean-up and dependency changes * Removed a whole mess of unusued import and deprecation warnings when building with GHC 7.10 * We now enforce an attoparsec lower bound of 0.10 (there was no lower bound before) * The unnecessary dependency on mtl is now removed.-* Added some monad transformer instances for WebDriver and WDSessionState that were mysteriously missing: strict WriterT, ReaderT, ListT-* data-default dependency was changed to data-default-class+* Added some monad transformer instances for WebDriver and WDSessionState that were mysteriously missing: strict WriterT, ReaderT, ListT.+* data-default dependency was changed to data-default-class.  ## 0.7 @@ -133,8 +151,8 @@  ### Top-level API (exposed by`Test.WebDriver` module)   * `withSession` is now overloaded over the new constraint alias `WDSessionStateControl` ([see below](#typeclass-api)). This means that it can be used with monad transformer stacks over `WD` without any lifting required.-  * Added several new `WDConfig` convenience functions: `modifyCaps`, `useBrowser`, `useVersion`, `usePlatform`, `useProxy`; these functions are overloaded over the new `HasCapabilities` constraint alias ([see below](#typeclass-api))-  * Reworked and improved session history API+  * Added several new `WDConfig` convenience functions: `modifyCaps`, `useBrowser`, `useVersion`, `usePlatform`, `useProxy`; these functions are overloaded over the new `HasCapabilities` constraint alias ([see below](#typeclass-api)).+  * Reworked and improved session history API.     * Added a `SessionHistory` record type to replace the old `(Request, Response ByteString)` type. The new type has the same data as the previous tuple, but additionally records the number of attempted HTTP retries, and instead of `Response ByteString` uses `Either SomeException (Response ByteString)` so that HTTP request errors can be logged.     * Removed `wdKeepSessHist` field from `WDConfig` and replaced it with `wdHistoryConfig`, which uses a new `SessionHistoryConfig` type. @@ -155,7 +173,7 @@       ```  ### Implicit waits API (`Test.WebDriver.Commands.Wait`)-* Added functions for checking some expected conditions in explicit waits: `expectNotStale`, `expectAlertOpen`, `catchFailedCommand`+* Added functions for checking some expected conditions in explicit waits: `expectNotStale`, `expectAlertOpen`, `catchFailedCommand`.  ### Typeclass API * `WDSessionState` is now a superclass of `Monad` and `Applicative` instead of `MonadBaseControl IO`. This makes the class significantly more flexible in how it can be used, as it no longer requires `IO` as a base monad.@@ -186,45 +204,45 @@  ### Minor API changes (not exposed to `Test.WebDriver` module) * `Test.WebDriver.Session` changes-  * new function `mostRecentHistory` added-  * `lastHTTPRequest` renamed to `mostRecentHTTPRequest` (for consistency)-  * `mkSession` moved from `Test.WebDriver.Config` to `Test.WebDriver.Session`+  * new function `mostRecentHistory` added.+  * `lastHTTPRequest` renamed to `mostRecentHTTPRequest` (for consistency).+  * `mkSession` moved from `Test.WebDriver.Config` to `Test.WebDriver.Session`. * `Test.WebDriver.Internal` changes   * `sendHTTPRequest` now returns `(Either SomeException (Response ByteString))` and catches any exceptions that occur during the request. When using default configuration, the exceptions are also saved in 'wdSessHist'.   * `Test.WebDriver.Internal` no longer defines and exports exception types. All exception defined here previously have been moved to an unexposed `Test.WebDriver.Exceptions.Internal` module. These types are still exported in the usual places.  ### Dependencies-* Now supports http-types up to 0.9+* Now supports http-types up to 0.9.  ## 0.6.3.1-* Fixed an issue with aeson 0.10 support+* Fixed an issue with aeson 0.10 support.  ## 0.6.3-* Support aeson 0.10-* Added support for multiple HTTP attempts per command request, using the new WDConfig field wdHTTPRetryCount+* Support aeson 0.10.+* Added support for multiple HTTP attempts per command request, using the new WDConfig field wdHTTPRetryCount.  ## 0.6.2.1-* Supports vector 0.11, aeson 0.9, attoparsec 0.13+* Supports vector 0.11, aeson 0.9, attoparsec 0.13.  ## 0.6.2-* Supports GHC 7.10-* Supports reworked Chrome capabilities used by newer versions of WebDriver-* Servers that return empty JSON strings for commands with no return value will no longer cause parse errors+* Supports GHC 7.10.+* Supports reworked Chrome capabilities used by newer versions of WebDriver.+* Servers that return empty JSON strings for commands with no return value will no longer cause parse errors.  ## 0.6.1-* Added the ability to pass HTTP request headers at session creation-* Fixed an issue involving an obsolete JSON representation of Chrome capabilities-* Relax upper bound on exceptions dependency+* Added the ability to pass HTTP request headers at session creation.+* Fixed an issue involving an obsolete JSON representation of Chrome capabilities.+* Relax upper bound on exceptions dependency.  ## 0.6.0.4-* Support for monad-control 1.0+* Support for monad-control 1.0.  ## 0.6.0.3-* Relaxed upper bounds on text and http-client versions+* Relaxed upper bounds on text and http-client versions.  ## 0.6.0.2-* Added support for aeson > 0.8 and network > 2.6-* Added support for the "X-Response-Body-Start" HTTP header used for error responses in newer http-client versions+* Added support for aeson > 0.8 and network > 2.6.+* Added support for the "X-Response-Body-Start" HTTP header used for error responses in newer http-client versions.  ## 0.6.0.1 * Fixed Haddock parse errors. No code changes introduced in this version.@@ -232,10 +250,10 @@ ## 0.6 * Rather than WDSession serving dual roles as configuration and state, its functionality has been split into 2 respective types: WDConfig and WDSession. * runSession now takes a WDConfig instead of WDSession and Capabilities parameters.-* runSession no longer closes its session on successful completion; use finallyClose or closeOnException for this behavior-* The old Test.WebDriver.Classes module has been split into Test.WebDriver.Session and Test.WebDriver.Class-* SessionState typeclass renamed to WDSessionState-* We now use the http-client package instead of HTTP. This is reflected in the addition of Manager fields in both WDConfig and WDSession+* runSession no longer closes its session on successful completion; use finallyClose or closeOnException for this behavior.+* The old Test.WebDriver.Classes module has been split into Test.WebDriver.Session and Test.WebDriver.Class.+* SessionState typeclass renamed to WDSessionState.+* We now use the http-client package instead of HTTP. This is reflected in the addition of Manager fields in both WDConfig and WDSession.  ## 0.5.5 * Added optional HTTP history tracking for debugging purposes.@@ -245,50 +263,50 @@ * Relaxed dependencies on mtl, network and scientific.  ## 0.5.3.3-* Relaxed text dependency up to 1.2+* Relaxed text dependency up to 1.2.  ## 0.5.3.2 ### bug fixes-* fixed remaining compilation problems with aeson. now supports aeson >= 0.6.2.0 && < 0.8+* fixed remaining compilation problems with aeson. now supports aeson >= 0.6.2.0 && < 0.8. * now depends on directory-tree instead of filesystem-trees. this fixes several problems with firefox/chrome profile support.  ## 0.5.3.1 ### bug fixes-* fixed compilation error with aeson 0.7 and greater+* fixed compilation error with aeson 0.7 and greater.  ## 0.5.3  ### new features-* SessionNotCreated constructor added to FailedCommandType-* new command deleteCookieByName added+* SessionNotCreated constructor added to FailedCommandType.+* new command deleteCookieByName added.  ###b ug fixes-* asyncJS now properly distinguishes between a null return and a script timeout-* fixed a change in waitWhile causing the opposite of expected behavior+* asyncJS now properly distinguishes between a null return and a script timeout.+* fixed a change in waitWhile causing the opposite of expected behavior.  ## 0.5.2 ### API changes-* added many new Internet Explorer capabilities-* added additionalCaps to Capabilities, which allows support for non-standard capabilities-* Browser type now supports non-standard browsers via the new Browser constructor-* added support for the new unexpectedAlertBehaviour capability+* added many new Internet Explorer capabilities.+* added additionalCaps to Capabilities, which allows support for non-standard capabilities.+* Browser type now supports non-standard browsers via the new Browser constructor.+* added support for the new unexpectedAlertBehaviour capability.  ### new features-* new command getApplicationCacheStatus supported-* error reporting for unknown commands now slightly improved+* new command getApplicationCacheStatus supported.+* error reporting for unknown commands now slightly improved.  ### bug fixes-* internal request URIs are now properly percent-encoded-* improved handling of browser-specific capabilities-* fixed incompatability with new session creation protocol changes introduced in selenium 2.35-* updated to work with Aeson 0.6.2 and onward+* internal request URIs are now properly percent-encoded.+* improved handling of browser-specific capabilities.+* fixed incompatability with new session creation protocol changes introduced in selenium 2.35.+* updated to work with Aeson 0.6.2 and onward.  ## hs-webdriver 0.5.1 ### API changes * Test.WebDriver.Internal.FailedCommandInfo now stores screenshots as lazy bytestrings * Test.WebDriver.Common.Profile now stores PreparedProfile as a lazy bytestring * Test.WebDriver.Chrome.Extension now stores ChromeExtension as a lazy bytestring-* The LogPref type has been renamed to LogLevel to reflect its use within the new log interface+* The LogPref type has been renamed to LogLevel to reflect its use within the new log interface.  ### new features * a new log interface as specified by the webdriver standard. This includes the functions getLogs and getLogTypes, and the types LogType and LogEntry.@@ -318,7 +336,7 @@  ### bug fixes * fixed a typo in the export list of Firefox.Profile; deleteFile is now correctly exported instead of removeFile from System.Directory-* fixed an error in the JSON representation of MouseButton+* fixed an error in the JSON representation of MouseButton.  ### new features * A new module, Test.WebDriver.Commands.Internal, which exports some low-level functions used to implement the high-level interface. This makes it possible for library users to extend hs-webdriver with nonstandard or unimplemented features.@@ -339,20 +357,20 @@ * It's now possible to add entire directories to a profile in pure code using addFile and addExtension. * new functions in Common.Profile: unionProfiles, onProfileFiles, onProfilePrefs * new function in Commands.Wait: onTimeout-* the WD monad now has a MonadCatchIO instance, as an alternative to lifted-base for exception handling+* the WD monad now has a MonadCatchIO instance, as an alternative to lifted-base for exception handling.   ## hs-webdriver 0.3.2.1  ### bug fixes-* Removed a bug in waitWhile' that resulted in an infinite loop-* Fixed the incorrect representation of JSON profiles-* Fixed relative path issues when zipping profile directories from disk+* Removed a bug in waitWhile' that resulted in an infinite loop.+* Fixed the incorrect representation of JSON profiles.+* Fixed relative path issues when zipping profile directories from disk.  ## hs-webdriver 0.3.2  ### bug fixes-* Changed the constraint on filesystrem-trees to avoid a broken version+* Changed the constraint on filesystrem-trees to avoid a broken version. * Added the missing exports for addFile and deleteFile in Common.Profile and Firefox.Profile  ### new features@@ -403,11 +421,11 @@ ### API changes * FailedCommandInfo changed so that it stores a WDSession rather than just a Maybe SessionId, thus providing server host and port information as well as the session ID. * As a result, mkFailedCommandInfo is now String -> WD FailedCommandInfo, since it requires access to the WDSession state.-* HTML5StorageType changed to the more accurate WebStorageType+* HTML5StorageType changed to the more accurate WebStorageType.  ### new features-* general documentation improvements-* the uploadFile, uploadRawFile, and uploadZipEntry functions, which support uploading file contents to the remote server+* general documentation improvements.+* the uploadFile, uploadRawFile, and uploadZipEntry functions, which support uploading file contents to the remote server.  ## hs-webdriver 0.1 @@ -415,8 +433,8 @@ * getWindowSize, setWindowSize, getWindowPos, and setWindowPos have all been deprived of their WindowHandle argument. This is due to the fact that using unfocused windows with those commands causes undefined behavior.  ### new features-* the mkCookie function for convenient cookie construction-* the focusFrame function for focusing to individual frames-* the setPageLoadTimeout function-* the maximize function for maximizing windows-* support for HTML 5 web storage+* the mkCookie function for convenient cookie construction.+* the focusFrame function for focusing to individual frames.+* the setPageLoadTimeout function.+* the maximize function for maximizing windows.+* support for HTML 5 web storage.
+ app/Main.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE NumericUnderscores #-}++module Main where++import Control.Monad.IO.Class+import Control.Monad.Logger+import Test.WebDriver+import Test.WebDriver.Capabilities+import Test.WebDriver.WD+import UnliftIO.Concurrent (threadDelay)+import UnliftIO.Directory+import UnliftIO.Exception+++main :: IO ()+main = do+  -- You must have a compatible chrome/chromedriver on your PATH for this to work+  Just chromedriverBin <- findExecutable "chromedriver"+  Just chromeBin <- findExecutable "google-chrome-stable"++  let caps = defaultCaps {+        _capabilitiesGoogChromeOptions = Just $ defaultChromeOptions {+            _chromeOptionsBinary = Just chromeBin+            }+        }++  let driverConfig = DriverConfigChromedriver {+        driverConfigChromedriver = chromedriverBin+        , driverConfigChromedriverFlags = []+        , driverConfigChrome = chromeBin+        , driverConfigLogDir = Nothing+        }++  runStdoutLoggingT $ bracket mkEmptyWebDriverContext teardownWebDriverContext $ \wdc -> do+    sess <- startSession wdc driverConfig caps "session1"+    runWD sess $ do+      openPage "http://www.xkcd.com"+      liftIO $ threadDelay 5_000_000++    sess2 <- startSession wdc driverConfig caps "session2"+    runWD sess2 $ do+      openPage "http://www.smbc-comics.com"+      liftIO $ threadDelay 5_000_000
src/Test/WebDriver.hs view
@@ -27,6 +27,10 @@ <https://github.com/mozilla/geckodriver/issues/1946>). So, this library will spin up a separate @geckodriver@ process for every session. +For an example of using this package by itself, see "Test.WebDriver.WD". For a+full test framework integrated with this package, see+[sandwich-webdriver](https://hackage.haskell.org/package/sandwich-webdriver).+ -}  module Test.WebDriver (@@ -172,8 +176,9 @@ -- not shut driver processes. closeSession' :: (WebDriverBase m, MonadLogger m) => Session -> m () closeSession' (Session { sessionId=(SessionId sessId), .. }) = do-  response <- doCommandBase sessionDriver methodDelete ("/session/" <> sessId) Null-  logInfoN [i|Close result: #{response}|]+  _response <- doCommandBase sessionDriver methodDelete ("/session/" <> sessId) Null+  -- TODO: throw an exception if this failed?+  return ()  -- | Create a manual 'Driver' to use with 'startSession''/'closeSession''. mkManualDriver :: MonadIO m =>@@ -197,15 +202,15 @@     , _driverManager = manager     , _driverProcess = Nothing     , _driverLogAsync = Nothing-    , _driverConfig = DriverConfigChromedriver "" [] "" "" -- Not used+    , _driverConfig = DriverConfigChromedriver "" [] "" Nothing -- Not used     }  -- | Tear down all sessions and processes associated with a 'WebDriverContext'. teardownWebDriverContext :: (WebDriverBase m, MonadLogger m) => WebDriverContext -> m ()-teardownWebDriverContext wdc@(WebDriverContext {..}) = do+teardownWebDriverContext (WebDriverContext {..}) = do   modifyMVar_ _webDriverSessions $ \sessions -> do     forM_ [sess | (_, sess) <- M.toList sessions] $ \sess ->-      closeSession wdc sess+      closeSession' sess     return mempty    modifyMVar_ _webDriverSelenium $ \case
src/Test/WebDriver/LaunchDriver.hs view
@@ -63,27 +63,34 @@    let hostname = "localhost" -  (logFilePath, logFileHandle) <- liftIO $ openTempFile (driverConfigLogDir driverConfig) ((driverBaseName driverConfig) <> ".log")-  logDebugN [i|Logging driver output to #{logFilePath}|]+  maybeLogFileHandle <- case driverConfigLogDir driverConfig of+    Nothing -> return Nothing+    Just logDir -> do+      (logFilePath, logFileHandle) <- liftIO $ openTempFile logDir ((driverBaseName driverConfig) <> ".log")+      logDebugN [i|Logging driver output to #{logFilePath}|]+      return $ Just logFileHandle    let handler (e :: SomeException) = do         terminateProcess p         now <- liftIO getCurrentTime-        liftIO (T.hPutStrLn logFileHandle [i|(#{now}) haskell-webdriver: process ending with exception: #{e}|])-        liftIO (hClose logFileHandle)+        whenJust maybeLogFileHandle $ \logFileHandle -> do+          liftIO (T.hPutStrLn logFileHandle [i|(#{now}) haskell-webdriver: process ending with exception: #{e}|])+          liftIO (hClose logFileHandle)    flip withException handler $ do     -- Read from the (combined) output stream until we see the up and running message     maybeReady <- timeout 30_000_000 $ fix $ \loop -> do       line <- fmap T.pack $ liftIO $ hGetLine hRead-      liftIO $ T.hPutStrLn logFileHandle line+      whenJust maybeLogFileHandle $ \logFileHandle ->+        liftIO $ T.hPutStrLn logFileHandle line       unless (Prelude.any (`T.isInfixOf` line) (needles driverConfig)) loop     when (isNothing maybeReady) $       throwIO DriverNoReadyMessage -    logAsync <- async $ flip finally (liftIO $ hClose logFileHandle) $ forever $ do+    logAsync <- async $ flip finally (liftIO $ whenJust maybeLogFileHandle hClose) $ forever $ do       line <- liftIO (T.hGetLine hRead)-      liftIO $ T.hPutStrLn logFileHandle line+      whenJust maybeLogFileHandle $ \logFileHandle ->+        liftIO $ T.hPutStrLn logFileHandle line      flip onException (cancel logAsync) $ do       -- Wait for a successful connection to the server socket@@ -176,12 +183,12 @@  getSubDriverArgs :: Monad m => DriverConfig -> m [FilePath] getSubDriverArgs (DriverConfigChromedriver {..}) = do-  let chromedriverLog = driverConfigLogDir </> "chromedriver.log"-  return [-    "-Dwebdriver.chrome.logfile=" <> chromedriverLog-    , "-Dwebdriver.chrome.verboseLogging=true"-    , "-Dwebdriver.chrome.driver=" <> driverConfigChromedriver-    ]+  let logArgs = case driverConfigLogDir of+        Just d -> ["-Dwebdriver.chrome.logfile=" <> (d </> "chromedriver.log")+                  , "-Dwebdriver.chrome.verboseLogging=true"+                  ]+        Nothing -> []+  return (["-Dwebdriver.chrome.driver=" <> driverConfigChromedriver] <> logArgs) getSubDriverArgs (DriverConfigGeckodriver {..}) = do   return [     "-Dwebdriver.gecko.driver=" <> driverConfigGeckodriver@@ -238,3 +245,6 @@   case _driverLogAsync of     Just x -> cancel x     Nothing -> return ()++whenJust :: Applicative m => Maybe a -> (a -> m ()) -> m ()+whenJust mg f = maybe (pure ()) f mg
src/Test/WebDriver/Types.hs view
@@ -105,7 +105,7 @@     -- | Drivers to configure Selenium to use.     , driverConfigSubDrivers :: [DriverConfig]     -- | Directory in which to place driver logs.-    , driverConfigLogDir :: FilePath+    , driverConfigLogDir :: Maybe FilePath     }   | DriverConfigGeckodriver {       -- | Path to @geckodriver@ binary.@@ -115,7 +115,7 @@       -- | Path to @firefox@ binary.       , driverConfigFirefox :: FilePath       -- | Directory in which to place driver logs.-      , driverConfigLogDir :: FilePath+      , driverConfigLogDir :: Maybe FilePath       }   | DriverConfigChromedriver {       -- | Path to @chromedriver@ binary.@@ -125,7 +125,7 @@       -- | Path to @chrome@ binary.       , driverConfigChrome :: FilePath       -- | Directory in which to place driver logs.-      , driverConfigLogDir :: FilePath+      , driverConfigLogDir :: Maybe FilePath       }  -- | An opaque identifier for a WebDriver session. These handles are produced by
+ src/Test/WebDriver/WD.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE ViewPatterns #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++{-|++This module contains the 'WD' monad, which serves as an example of how to use+this library in a standalone fashion.++For more complex usage, you'll probably want to skip this module and write+'WebDriverBase' and 'SessionState' instances for your own monads.++You can find a full example in the project repo at+<https://github.com/haskell-webdriver/haskell-webdriver/blob/main/app/Main.hs>.++-}++module Test.WebDriver.WD (+  WD(..)+  , runWD+  ) where++import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)+import Control.Monad.IO.Class+import Control.Monad.IO.Unlift+import Control.Monad.Logger+import Control.Monad.Reader+import qualified Data.Aeson as A+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import Data.String.Interpolate+import qualified Data.Text as T+import qualified Network.HTTP.Client as HC+import Network.HTTP.Types.Status as N+import Test.WebDriver+import Test.WebDriver.Types+import UnliftIO.Exception++#if MIN_VERSION_aeson(2,0,0)+import qualified Data.Aeson.Key as A+import qualified Data.Aeson.KeyMap as KM++aesonLookup :: T.Text -> KM.KeyMap v -> Maybe v+aesonLookup = KM.lookup . A.fromText+#else+import qualified Data.HashMap.Strict as HM++aesonLookup :: (Eq k, Hashable k) => k -> HM.HashMap k v -> Maybe v+aesonLookup = HM.lookup+#endif++newtype WD a = WD (ReaderT Session (LoggingT IO) a)+  deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadCatch, MonadMask, MonadUnliftIO, MonadReader Session, MonadLogger)++doCommandBaseWithLogging :: (+  MonadLogger m, MonadUnliftIO m, A.ToJSON p+  ) => Driver -> Method -> T.Text -> p -> m (HC.Response BL.ByteString)+doCommandBaseWithLogging driver method path args = do+  let req = mkDriverRequest driver method path args+  logDebugN [i|--> #{HC.method req} #{HC.path req}#{HC.queryString req} (#{showRequestBody (HC.requestBody req)})|]+  response <- tryAny (liftIO $ HC.httpLbs req (_driverManager driver)) >>= either throwIO return+  let (N.Status code _) = HC.responseStatus response++  if | code >= 200 && code < 300 -> case A.eitherDecode (HC.responseBody response) of+         -- For successful responses, try to pull out the "value" and show it+         Right (A.Object (aesonLookup "value" -> Just value)) -> logDebugN [i|<-- #{code} #{A.encode value}|]+         _ -> logDebugN [i|<-- #{code} #{HC.responseBody response}|]+     -- For non-successful responses, log the entire response.+     -- Reading the WebDriver spec, it would probably be sufficient to just show the "value" as above,+     -- plus the HTTP status message.+     | otherwise -> logDebugN [i|<-- #{code} #{response}|]+  return response++  where+    showRequestBody :: HC.RequestBody -> B.ByteString+    showRequestBody (HC.RequestBodyLBS bytes) = BL.toStrict bytes+    showRequestBody (HC.RequestBodyBS bytes) = bytes+    showRequestBody _ = "<request body>"++instance WebDriverBase WD where+  doCommandBase = doCommandBaseWithLogging++instance WebDriverBase (LoggingT IO) where+  doCommandBase = doCommandBaseWithLogging++instance SessionState WD where+  getSession = ask++-- The following is a more general SessionState for MonadReader monads, but it+-- requires UndecidableInstances+-- class HasSession a where+--   extractSession :: a -> Session+-- instance HasSession Session where+--   extractSession = id+-- instance (HasSession a, MonadReader a m) => SessionState m where+--   getSession = asks extractSession++runWD :: Session -> WD a -> LoggingT IO a+runWD sess (WD wd) = runReaderT wd sess
tests/Main.hs view
@@ -45,7 +45,7 @@               driverConfigJava = java               , driverConfigSeleniumJar = seleniumJar               , driverConfigSubDrivers = subDrivers-              , driverConfigLogDir = dir+              , driverConfigLogDir = Just dir               , driverConfigJavaFlags = []               , driverConfigSeleniumVersion = Just Selenium3               }@@ -62,7 +62,7 @@               driverConfigJava = java               , driverConfigSeleniumJar = seleniumJar               , driverConfigSubDrivers = subDrivers-              , driverConfigLogDir = dir+              , driverConfigLogDir = Just dir               , driverConfigJavaFlags = []               , driverConfigSeleniumVersion = Just Selenium4               }@@ -77,7 +77,7 @@             return $ DriverConfigChromedriver {               driverConfigChromedriver = chromedriver               , driverConfigChrome = chrome-              , driverConfigLogDir = dir+              , driverConfigLogDir = Just dir               , driverConfigChromedriverFlags = []               } @@ -91,7 +91,7 @@             return $ DriverConfigGeckodriver {               driverConfigGeckodriver = geckodriver               , driverConfigFirefox = firefox-              , driverConfigLogDir = dir+              , driverConfigLogDir = Just dir               , driverConfigGeckodriverFlags = []               } @@ -134,14 +134,14 @@     DriverConfigChromedriver {         driverConfigChromedriver = browserDependenciesChromeChromedriver         , driverConfigChrome = browserDependenciesChromeChrome-        , driverConfigLogDir = dir+        , driverConfigLogDir = Just dir         , driverConfigChromedriverFlags = []         }]   BrowserDependenciesFirefox {..} -> return [     DriverConfigGeckodriver {         driverConfigGeckodriver = browserDependenciesFirefoxGeckodriver         , driverConfigFirefox = browserDependenciesFirefoxFirefox-        , driverConfigLogDir = dir+        , driverConfigLogDir = Just dir         , driverConfigGeckodriverFlags = []     }] 
webdriver.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           webdriver-version:        0.13.0.0+version:        0.14.0.0 synopsis:       a Haskell client for the Selenium WebDriver protocol description:    A WebDriver client for Haskell.                 You can use it to automate browser sessions for testing, system administration, etc.@@ -15,6 +15,8 @@                 * <https://www.selenium.dev/>                 * <https://github.com/mozilla/geckodriver>                 * <https://developer.chrome.com/docs/chromedriver>+                .+                See the announcement blog post [here](https://thomasjm.github.io/posts/webdriver-13) to learn about the changes in @0.13.0.0@. category:       Web, Browser, Testing, WebDriver, Selenium homepage:       https://github.com/haskell-webdriver/haskell-webdriver#readme bug-reports:    https://github.com/haskell-webdriver/haskell-webdriver/issues@@ -48,6 +50,7 @@       Test.WebDriver.Profile       Test.WebDriver.Types       Test.WebDriver.Waits+      Test.WebDriver.WD   other-modules:       Test.WebDriver.Capabilities.Aeson       Test.WebDriver.Capabilities.ChromeOptions@@ -112,6 +115,7 @@     , http-types >=0.8     , microlens-th >=0.4.0.0     , monad-logger+    , mtl     , network     , network-uri >=2.6     , random@@ -127,6 +131,38 @@     , unordered-containers >=0.1.3     , websockets >=0.13     , zip-archive >=0.1.1.8+  default-language: Haskell2010++executable demo+  main-is: Main.hs+  other-modules:+      Paths_webdriver+  hs-source-dirs:+      app+  default-extensions:+      FlexibleContexts+      FlexibleInstances+      LambdaCase+      NamedFieldPuns+      OverloadedStrings+      QuasiQuotes+      RecordWildCards+      ScopedTypeVariables+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2 -W+  build-depends:+      aeson+    , base+    , bytestring+    , exceptions+    , http-client+    , http-types+    , monad-logger+    , mtl+    , string-interpolate+    , text+    , unliftio+    , unliftio-core+    , webdriver   default-language: Haskell2010  test-suite tests