How to use Playwright Sharp in F# for Browser Automation

661 views Asked by At

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>'    
3

There are 3 answers

2
Brian Berns On BEST ANSWER

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.
0
Koenig Lear On

You can use the TaskBuilder framework https://github.com/rspeele/TaskBuilder.fs

dotnet add package Taskbuilder.fs

and write

task {    
    use! playwright =  Playwright.CreateAsync() 
    let! browser =  playwright.Chromium.LaunchAsync()
    let! page = browser.NewPageAsync()
    
    let! response = page.GoToAsync("http://www.bing.com")
    let! title =  page.GetTitleAsync()

    printfn "%s" title

}
|> Task.WaitAll
1
bisen2 On

As of F# 6, tasks are now natively supported through the task computation expression. The syntax will be almost identical to the async computation expression from Brian Berns's answer, except that you no longer need to call Async.AwaitTask on each let!, use!, or do! binding.

let screenshotTask =
  task {
    use! playwright = Playwright.CreateAsync ()
    use! browser = playwright.Chromium.LaunchAsync ()
    let! page = browser.NewPageAsync ()
    let! _ = page.GotoAsync "https://www.bing.com"
    let! _ = page.ScreenshotAsync (PageScreenshotOptions(Path = "screenshot.png"))
    return ()
  }
screenshotTask.Wait ()