| Copyright | (c) 2025 John Walker Adrian Glouftsis |
|---|---|
| License | BSD-3-Clause |
| Maintainer | theghostjw@gmail.com |
| Stability | experimental |
| Safe Haskell | None |
| Language | Haskell2010 |
WebDriverPreCore
Description
Overview
This library provides typed definitions for the W3C WebDriver Protocol, supporting both the HTML and the BiDi protocols.
This library is intended as a foundation for building WebDriver client implementations. It is type constructors only, and does not include any executable client code.
If you are writing a webdriver client, this library will save you the effort of analysing the specs and implementing the protocol types and JSON instances.
If you are looking for a library to enable you to interact with web pages directly then you need a fully implemented client library which this library is not.
For a fully implemented (HTTP) WebDriver client, consider an alternative such as haskell-webdriver
Which Protocol?
- BiDi is a newer websocket protocol. It supports a wider range of features including real-time event subscriptions, improved handling of multiple browsing contexts, and enhanced scripting capabilities.
- HTML is the original WebDriver protocol, primarily focused on browser automation using HTTP requests. It is widely supported by existing WebDriver implementations.
Note that the Bidi protocol is still evolving, and API coverage varies between drivers. Depending on your use case and the WebDriver server you are targeting, you may choose to implement either or both protocols.
For an introduction to WebDriver Bidi and its use cases, see:
- WebDriver BiDi: Future of browser automation
- Example of WebDriver BiDi implementation in wdio (TypeScript) framework
Module Organisation
Both HTTP and BiDi protocols follow a the same module structure. Each has two modules:
1. An API module
Contains type constructors that generate a payload corresponding to each endpoint/command in the relevant WebDriver specification.
2. A Protocol module Contains:
- types used by the API functions
- fallback functions that allow the user to generate payloads that diverge from API
HTTP
Example:
-- Calling the API (navigateTo) function to generate the navigation command payload
let goExample :: Command ()
goExample = navigateTo (Session "session-id-123") $ MkUrl "https://example.com"
-- The result of the API function call contains HTTP request details required for the driver Command including the path and body (a JSON payload) if applicable.
Post
{ description = "Navigate To"
, path =
[ "session" , "b07baa96-046a-4e11-ad1e-94eda436502b" , "url" ]
, body =
fromList
[ ( "url"
, String
"https://example.com"
)
]
}
WebDriverPreCore.HTTP.Protocol
Protocol types including Command, request parameters, and response and error types
such as URL, and Session.
BiDi
API functions for BiDi commands and event subscriptions. Each command function returns a Command value with the WebSocket message specification and response type.
Example:
-- Calling the API function to generate the command payload
let navCommand :: Command NavigateResult
navCommand = browsingContextNavigate MkNavigate
{ context = "context-id-123"
, url = "https://example.com"
, wait = Just Interactive
}
-- The result is a Command value with the method and params (a JSON payload) to send to the WebSocket
navCommand = MkCommand
{ method = KnownCommand BrowsingContextNavigate
, params = fromList [("context", String "context-id-123"),
("url", String "https://example.com"),
("wait", String "interactive")]
}
WebDriverPreCore.BiDi.Protocol
Protocol types including Command, Event, request parameters, and response and error types
such as KnownCommand.
Using this Library to Implement a WebDriver Client
Implementing a WebDriver client involves:
- Writing a runner to send HTTP requests or WebSocket messages to WebDriver
- Creating some kind of abstraction, such as handles, a typeclass, or the use of an effects library to call the runner and hence lift the static type constructors provided by the API into IO actions.
- Handling errors using the shared error types
Example implementations for both BiDi and HTTP runners, in this case using the handle pattern), can be found in the test repository.
Example Implementation (Single Endpoint)
Using navigation as an example with both protocols, the client implementation is implemented as follows:
HTTP
The Runner takes commands and sends HTTP requests to the driver and parses responses (implemented in HTTP.Runner)
An Actions Type mirrors the type API functions with the response wrapped in IO (implemented in HTTP.Actions)
An Actions Implementation an instance of the Actions type that uses the runner to send commands (implemented in HTTP.Actions)
import WebDriverPreCore.HTTP.API qualified as API
import WebDriverPreCore.HTTP.Protocol (Session, URL)
-- The Actions type
data HttpActions = MkHttpActions
{ navigateTo :: Session -> URL -> IO ()
, ...
-- the rest of the API
}
-- The Actions implementation
mkActions :: HttpRunner -> HttpActions
mkActions runner = MkHttpActions
{ navigateTo = sessionId url ->
runner.runHttpCommand $ API.navigateTo sessionId url
, ...
-- the rest of the API
}
With the above in place, a run or withActions function can be created to provide the user a means to perform WebDriver operations.
User's (of the Client Implementation) Module
A user of the client implementation would import the Actions module for performing operations and the WebDriverPreCore.HTTP.Protocol module for the related types and utility functions.
import WebDriverPreCore.HTTP.Protocol
import HTTP.Actions
import HTTP.Runner (mkRunner)
import HTTP.DemoUtils (HttpDemo, runDemo, sessionDemo)
-- >>> runDemo navigate
demoNavigate :: HttpDemo
demoNavigate =
sessionDemo "navigate" action
where
action :: Session -> HttpActions -> IO ()
action sesId MkHttpActions {..} = do
navigateTo sesId $ MkUrl "https://example.com/index.html"
Full example is available in the test repository
BiDi
A BiDi client implementation follows the same pattern as HTTP, with a runner, actions type, and actions implementation. It is, however, more complicated than HTTP due to the asynchronous nature of WebSocket communication and event handling, the need to create correlation IDs for commands and the need to manage subscriptions for events.
Example implementations can be found in the test repository
Deprecated Modules (Removal planned ~ 2027-02-01)
⚠️ The following modules are deprecated. Please migrate to their replacements.
WebDriverPreCore.Http Deprecated: Use WebDriverPreCore.HTTP.Protocol instead
This module re-exported various HTTP protocol components. The functionality has been reorganized into the Protocol module.
WebDriverPreCore.HTTP.SpecDefinition (formerly WebDriverPreCore.Http.SpecDefinition)
Deprecated: Use WebDriverPreCore.HTTP.API instead
This module contained the HTTP API endpoint functions. It has been renamed to better reflect the distinction between API (functions) and Protocol (types).
WebDriverPreCore.HTTP.HttpResponse (formerly WebDriverPreCore.Http.HttpResponse)
Deprecated: This type is implementation specific and has been removed. The parser supplied in this type is now implicit, as the return types of all Commands are instances of FromJSON.
Migration for Deprecated Modules
See the ChangeLog for details