Skip to main content

Command Palette

Search for a command to run...

Using the Zig built LightPanda browser for web automation via playwright-go

Updated
4 min read

I am working on a browser automation thingy and want to try supporting LightPanda as I am interested in seeing if it may be a good lightweight alternative to Chromium for CI (Continuous Integration) tests. Komaso (that’s Chichewa, you can read it as as but also), I am just curious about their use of Zig and find the project interesting.

Connecting the gopher to the panda

So in order to get this to work in basi, I first needed to see if we can swap out Chrome/Chromium for LightPanda using the Chrome Developer Protocol in playwright-go, the Go library we use in basi, to talk to browsers to tell them what to do. I saw a demo/examples the showing LightPanda use via Playwright NodeJS , so this should be possible, right?.

Download and run Light Panda

Download LightPanda Browser from GitHub releases and then run it with the CDP services enabled on a port of our choice.

NOTE: the name of the binary you run may be different, depending on your OS and which binary you downloaded. The example below shows for Linux on x86

$ lightpanda-x86_64-linux serve --port 9226 --log_level debug

Implementing the Go program to talk to LightPanda

Create a new Go module project

$ mkdir lightpanda-go-example

$ cd lightpanda-go-example

$ go mod init lightpandagoexample

Next, Paste the following code in a file named main.go

This program starts a connection to LightPanda via Playwright by connecting to the browser using Chrome Devtools Protocol. After it connects, we try to automate a simple flow of going to the Playwright website and checking that the Link to the Community page contains the right text.

package main

import (
    "fmt"
    "log"

    playwrightgo "github.com/playwright-community/playwright-go"
)

func main() {
    err := run()
    if err != nil {
        log.Fatal(err)
    }
}
func run() error {

    err := playwrightgo.Install(&playwrightgo.RunOptions{
        Browsers: []string{"chromium"},
        DryRun:   true,
    })
    if err != nil {
        return fmt.Errorf("could not launch playwright: %w", err)
    }

    pw, err := playwrightgo.Run()
    if err != nil {
        return fmt.Errorf("could not launch playwright: %w", err)
    }

    browser, err := pw.Chromium.ConnectOverCDP("ws://localhost:9226")
    if err != nil {
        return fmt.Errorf("could not launch LightPanda via CDP: %w", err)
    }
    context, err := browser.NewContext()
    if err != nil {
        return fmt.Errorf("could not create context: %w", err)
    }
    page, err := context.NewPage()
    if err != nil {
        return fmt.Errorf("could not create page: %w", err)
    }

    _, err = page.Goto("https://playwright.dev/")
    if err != nil {
        return fmt.Errorf("could not goto: %w", err)
    }

    loc := page.Locator(`a[href="/community/welcome"]`)
    assertions := playwrightgo.NewPlaywrightAssertions()
    assert := assertions.Locator(loc)

    err = assert.ToHaveText("Community")
    if err != nil {
        return fmt.Errorf("could assert community link: %w", err)
    }

    err = browser.Close()
    if err != nil {
        return fmt.Errorf("could not close browser: %w", err)
    }
    err = pw.Stop()
    if err != nil {
        return fmt.Errorf("could not stop Playwright: %w", err)
    }

    return nil

}

Side note: Let’s compare the same thing with playwright-go (directly) vs using basi

For comparison here is how the same test would be written in a file named example.basi and executed with $ basi run example.basi

Goto "https://playwright.dev/"
Find "a[href='/community/welcome']"
ExpectText "Community"

Mandatory for now: Patch playwright-go library

At the time of writing, using playwright-go with lightpanda will only work if you clone playwright-go and checkout to the changes in the pull request I opened which fix an issue with playwright-go’s handling of the response from LightPanda’s implementation of the CDP.

NOTE: this step is mandatory to make this example work until the PR is merged or similar changes applied. Since playwright-go is looking for new maintainers, I can’t say when that will be

Here is how to patch a local copy of playwright-go :-

$ git clone https://github.com/playwright-community/playwright-go

$ cd ./playwright-go

# Checkout to the PR I opened
$ git fetch origin pull/581/head:main-with-cdp-fix

$ git checkout main-with-cdp-fix

Edit your go.mod to add a replace directive and make sure to point the path to the repository we just cloned, above :

replace github.com/playwright-community/playwright-go => /path/to/cloned/playwright-go

Run and build the Go program

You can now run the Go program

$ go mod tidy

$ go run main.go

That should work if you followed the steps. Nice, we just used LightPanda browse for automated e2en browser tests!

The way forward

The Zig community is putting out great projects, from TigerBeetle, Ghostty, more. I was intrigued by LightPanda, a headless web browser and was curious if I could use it from a browser automation tool I an working on that’s intended to make authoring and running e2e browser tests more approachable. It’s definitely possible! Being able to connect to LightPanda from Go via playwright means there’s a path to add support for it to basi and hopefully that means making the testing more lightweight especially for CI.

Currently, LightPanda doesn’t support a few things including Form file uploads and Screenshots. So we cannot replace Chrome/Chromium yet but I’m sure LightPanda will grow into maturity as it gets more use and development. Will definitely be watching the space, maybe I may just learn enough Zig to help out too. Even if that doesn’t happen, it’s always fun to tinker with new technology and see the potential of how one can use it today or in the future.

Hope you found this interesting. Thanks.