Search code examples
f#playwright-sharp

How to use Playwright Sharp in F# for Browser Automation


I guess this is a classic C# to F# conversion I haven't quite got my head around.

I am trying to automate a browser using the quick start

https://playwright.dev/dotnet/docs/intro

The C# code is

using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.GotoAsync("http://www.bing.com");
await page.ScreenshotAsync(path: outputFile);

I've made a start but getting a bit lost already.

let playwright: Playwright = PlaywrightSharp.Playwright.CreateAsync() |> Async.AwaitTask

Incorrect type. What am I doing wrong here?

Error   FS0001  This expression was expected to have type
    'Playwright'    
but here has type
    'Async<IPlaywright>'    

Solution

  • One way to do this is with a F#'s built-in support for async computation expressions. The translation would look something like this:

    let (~~) = Async.AwaitTask
    
    async {
        use! playwright = ~~Playwright.CreateAsync()
        let! browser = ~~playwright.Chromium.LaunchAsync()
        let! page = ~~browser.NewPageAsync()
        do! ~~page.GoToAsync("http://www.slashdot.com") |> Async.Ignore
        do! ~~page.ScreenshotAsync(outputFile) |> Async.Ignore
    } |> Async.RunSynchronously
    

    There are a few subtleties here that you'll need to know about:

    • F# has its own async type, called Async<'T>. I've used Async.AwaitTask to convert from C#-style tasks, and defined a prefix operator, ~~, to make this look a bit cleaner.
    • F# doesn't support DisposeAsync in async computation expressions yet, so the browser doesn't get disposed of properly. If you want, you can add do! browser.DisposeAsync().AsTask() |> Async.AwaitTask at the end of the block to do this manually.
    • F# requires us to explicitly ignore unwanted return values, which I've done via Async.Ignore.