TestBox Browser Testing

Run TestBox and ColdBox browser specs with the bx-playwright module deployed in a BoxLang server.

On this page

TestBox Browser Testing

TestBox 7.2+ and ColdBox 8.3+ run real browser tests from BoxLang specs. Add @browser to a spec and TestBox gives it a browser, browser matchers and failure artifacts. The browser runs on the server that executes TestBox, not in your own browser.

@browser
@browserProfile( "ci" )
@baseURL( "http://localhost:8080" )
class extends="testbox.system.BaseSpec" {

	function run() {
		describe( "Login", () => {
			it( "signs in", () => {
				browse( ( page ) => {
					page.visit( "/login" )
						.fill( "Email", "dev@example.com" )
						.fill( "Password", "secret" )
						.click( "Sign in" )

					expect( page ).toHavePath( "/dashboard" )
					expect( page ).toSee( "Welcome" )
				} )
			} )
		} )
	}

}

Install the module in the server

Deploy bx-playwright to the BoxLang runtime that runs the TestBox web application. For a CommandBox server, install it when the server is first set up:

{
	"scripts": {
		"onServerInitialInstall": "install bx-playwright --nosave"
	}
}

No separate operating-system BoxLang installation is needed: TestBox asks the deployed module to download its browser on first use, see Provision the browser.

Browser specs

A spec gets browser support when its class, or a class it extends, has any of these annotations, so @browser is optional when another one is present:

AnnotationPurpose
@browserTurn on browser support
@browserProfile( "ci" )The bx-playwright profile for the bundle
@baseURL( "http://localhost:8080" )Relative URLs in visit() resolve against it
@browserAutoInstall( false )Never download a missing browser, see below

TestBox mixes these methods into the spec, which calls them unqualified:

MethodWhat it does
browse( callback, options )Runs the callback with fresh, isolated pages, closes them, and attaches failure artifacts to the spec
browserAvailable()True when browser specs can run here, handy for skip = !browserAvailable()
ensureBrowserInstalled()Installs the profile's browser, for example from beforeAll()
getBrowserSupport()The bundle's testbox.system.browser.BrowserSupport
closeBrowser()Closes the bundle browser now
this.playwright()The bundle's bx-playwright manager. An unqualified playwright() is still the bx-playwright BIF, which returns a new manager the bundle does not close
  • Methods the spec declares itself are kept.
  • Browser matchers include toSee(), toHaveTitle(), toHavePath(), toHaveText(), toBeVisible() and toHaveCount(). Every bx-playwright assertion works too: page.assertSee( "Welcome" ).
  • The bundle shares one browser, started on first use. The runner closes it after the bundle, even when afterAll() throws, so your beforeAll() and afterAll() need no cleanup or super calls.
  • testbox.system.BrowserSpec is an optional base class: testbox.system.BaseSpec with @browser. Use the annotation when the spec already extends another base class.
  • For logged-in specs, log in once with a saved session and pass { session : "name" } to browse().
  • Artifact policies come from the profile, see Testing.

ColdBox

ColdBox 8.3+ uses the same annotation on coldbox.system.testing.BaseTestCase, which adds routeURL(), visitRoute() and assertRouteIs() for named routes:

@appMapping( "/root" )
@browser
@baseURL( "http://localhost:8080" )
class extends="coldbox.system.testing.BaseTestCase" {

	function run() {
		describe( "Users", () => {
			it( "shows a user", () => {
				browse( ( page ) => {
					visitRoute( page, "users.show", { id : 5 } )
					assertRouteIs( page, "users.show" )
					expect( page ).toSee( "User 5" )
				} )
			} )
		} )
	}

}

See the ColdBox Browser Testing guide.

Provision the browser

Automatic (default)

When a spec first uses browse() or this.playwright(), TestBox asks the bx-playwright module loaded in the web server to install the profile's browser, using the module's own Node.js runtime and browser cache. The first run needs network access and takes longer; later runs reuse the cache.

Before the first spec

Install from a lifecycle hook, for example when automatic installation is disabled:

@browser
@browserAutoInstall( false )
@browserProfile( "ci" )
class extends="testbox.system.BaseSpec" {

	function beforeAll() {
		ensureBrowserInstalled()
	}

	function run() {
		// Browser specs
	}

}

With @browserAutoInstall( false ) and no browser installed, launching fails with setup guidance. Server code can call the underlying BIF directly: playwrightEnsureBrowser( "chromium" ).

Preinstalled from the CLI

To download browsers before the web app starts, or when the server cannot reach the download host, install them from a standalone BoxLang installation into a shared cache:

install-bx-module bx-playwright
BX_PLAYWRIGHT_HOME=/var/cache/myapp-playwright bxplaywright install chromium
  • Point the server's module at the same cache: set its home to /var/cache/myapp-playwright, or the same browsersPath in both runtimes.
  • Use the same bx-playwright version in the CLI and the server, so both expect the same browser revision.
  • Make sure the server's operating-system user can read and write the cache.
  • Add @browserAutoInstall( false ) above the spec's class so a web request never downloads a browser.

Runtime requirements

  • The BoxLang web server loads the module under the playwright mapping.
  • The server process can write to the bx-playwright home and browser cache.
  • The first browser installation needs outbound network access unless the cache is already provisioned.
  • On Linux, native browser libraries are a host or container dependency. TestBox does not install operating-system packages from a web request.

For CI, cache the server's bx-playwright home, or preinstall into the shared cache before the server starts. See BoxLang Web Applications for server paths and permissions, and Testing for the standalone CLI workflow.

Edit this page Download Markdown Last updated Oct 13, 2026, 5:01:48 PM