BoxLang 🚀 A New JVM Dynamic Language Learn More...
|:------------------------------------------------------: |
| ⚡︎ B o x L a n g ⚡︎
| Dynamic : Modular : Productive
|:------------------------------------------------------: |
Copyright Since 2023 by Ortus Solutions, Corp
www.boxlang.io | www.ortussolutions.com
Fluent browser automation and testing for BoxLang, powered by Microsoft Playwright. Drive Chromium, Firefox and WebKit, test web apps, mock the network, test APIs, and render HTML to PDF or images.
Documentation: bxplaywright.boxlang.io (also as llms.txt for AI agents)
Two distributions, same module (playwright), same API:
| Module | Size | Node.js |
|---|---|---|
bx-playwright
| ~4 MB | Downloaded by bxPlaywright
install for your OS |
bx-playwright-full
| ~206 MB | Bundled for every platform (offline friendly) |
install-bx-module bx-playwright
bxPlaywright install # driver + Node.js + Chromium
bxPlaywright install firefox webkit
bxPlaywright doctor # check everything
Requires BoxLang 1.17+ and Java 21+.
playwright().visit( "https://boxlang.io" )
.assertTitleContains( "BoxLang" )
.click( "Docs" )
.screenshot( "docs.png" )
.quit()
// Scoped work, cleaned up automatically
playwright( "mobile" ).browse( ( page ) => {
page.visit( "http://localhost:8080/login" )
.fill( "Email", "[email protected]" ) // by label, placeholder or name
.fill( "Password", "secret" )
.click( "Sign in" ) // by button or link text
.assertPathIs( "/dashboard" )
.assertSee( "Welcome" )
} )
// One-shot helpers
playwright().screenshot( "https://boxlang.io", "home.png", { fullPage : true } )
playwright().pdf( "https://boxlang.io", "home.pdf", { format : "A4" } )
html = playwright().content( "https://boxlang.io" ) // rendered HTML
<bx:playwrightRender type="pdf" path="invoice.pdf" format="A4" margin="1cm">
<h1>Invoice #invoice.id#</h1>
</bx:playwrightRender>
| Area | API |
|---|---|
| Entry | playwright( [profile], [options] ),
.visit(), .browse(),
.newContext(), .newPage(),
.request(), .render(), .close()
|
| Selectors | @testId, CSS / XPath
(#id, .class, h1,
//div), or visible text (labels for
fill, buttons and links for click) |
| Actions | click, dblclick,
fill, type, clear,
press, check, uncheck,
select, upload, hover,
focus, drag, scrollTo
|
| Finders | locator, byRole,
byText, byLabel,
byPlaceholder, byTestId,
byAltText, byTitle,
frame, within
|
| Locators | first, last,
nth (1-based), filter,
visible, all, count, texts
|
| Assertions | assertSee,
assertDontSee, assertTitle,
assertPathIs, assertUrlIs,
assertVisible, assertMissing,
assertText, assertValue,
assertChecked, assertCount, ... |
| Expect | page.expect( "h1" ).toHaveText(
"Hi" ), .not().toBeVisible(),
toHaveURL, toHaveCount,
toMatchAriaSnapshot, ... |
| Network | intercept( "**/api/users"
).respondJson( data ), .respond(),
.abort(), .resume(), .handle()
|
| Events | onConsole,
onPageError, onDialog,
onRequest, onResponse,
waitForPopup, waitForDownload
|
| Output | screenshot, pdf,
content, text, html,
snapshot (accessibility tree for AI agents) |
Assertions retry until they pass (web-first). Failures throw
Playwright.AssertionFailed with Playwright's message.
Other errors: Playwright.Timeout,
Playwright.ActionFailed,
Playwright.InvalidOption,
Playwright.InvalidProfile, Playwright.NotInstalled.
playwright( "mobile" ), playwright( [
"android", "dark" ] ). Built-in:
default, chromium, firefox,
webkit, chrome, chrome-beta,
edge, hd, laptop,
macbook, desktop, 4k,
mobile/iphone, iphone-se,
mobile-landscape,
android/pixel, galaxy,
tablet/ipad, android-tablet,
dark, light, reduced-motion,
high-contrast, headed, debug,
record, ci, offline,
print, screenshot.
Add your own in the module settings, extending any profile:
"modules": {
"playwright": {
"settings": {
"baseURL": "http://localhost:8080",
"profiles": { "staging": { "extends": "desktop", "baseURL": "https://staging.example.com" } }
}
}
}
Resolution order (last wins): module settings, profiles,
BX_PLAYWRIGHT_* environment variables
(BROWSER, HEADLESS, BASEURL;
PROFILE picks the default profile), per-call options.
bxPlaywright <verb> (or boxlang
module:playwright <verb>): install,
install-node, install-deps,
uninstall, doctor, version,
devices, profiles, clean,
codegen, open, screenshot,
pdf, show-trace, mcp,
run, completions, help. Add
--json for machine readable output. Bash completions are
installed with the module.
./gradlew downloadBoxLang
./gradlew shadowJar test # unit and integration tests
PLAYWRIGHT_E2E=true ./gradlew shadowJar test # plus real browser tests (downloads Node.js and Chromium once)
./gradlew shadowJar -Pflavor=full # build bx-playwright-full
./gradlew spotlessApply # Ortus formatting
See AGENTS.md for the architecture and conventions, and PLAN.md for the roadmap.
BoxLang is a professional open-source project and it is completely funded by the community and Ortus Solutions, Corp. Ortus Patreons get many benefits like a cfcasts account, a FORGEBOX Pro account and so much more. If you are interested in becoming a sponsor, please visit our patronage page: https://patreon.com/ortussolutions
"I am the way, and the truth, and the life; no one comes to the Father, but by me (JESUS)" Jn 14:1-12
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
docs/ to GitHub Pages at bxplaywright.boxlang.io by the docs.yml workflowplaywright() BIF: fluent browser automation with smart selectors, chainable actions, web-first assertions (inline and expect() style), network mocking, events, popups, downloads, screenshots, PDF, rendered content and accessibility snapshotsbrowse() with automatic cleanup and multi-user pages, one-shot screenshot(), pdf(), content() and render()request() for API testingbx:playwrightRender component: render HTML to PDF, PNG, JPEG or WebP with Chromiumextends, BX_PLAYWRIGHT_* environment overridesoff, on, only-on-failure, retain-on-failure)bxPlaywright CLI with bash completions and --json output: install, install-node, install-deps, uninstall, doctor, version, devices, profiles, clean, codegen, open, screenshot, pdf, show-trace, mcp, run, completions, helpbx-playwright (downloads Node.js) and bx-playwright-full (bundles Node.js)assertScreenshotMatches() with baselines, pixel diff and diff images (snapshots setting, BX_PLAYWRIGHT_UPDATE_SNAPSHOTS)assertNoConsoleErrors(), assertNoSmoke(), axe-core accessibility() and assertNoAccessibilityIssues()macro() extensionssoft()emulate(), device(), clock() and freezeTime()session( name, setup ) caches logged in storage statecodegen translates recorded actions into the bx-playwright DSLsnapshot() with element refs, help() introspection, aiTools() for bx-aiexamples/ executed in CI, bxSites documentation in docs/render() with baseURL no longer needs the bx-esapi moduleartifacts.directory resolves against the current directory instead of the module folderclick( "text" ) clicks the matching button or link even when another element with the same text (like a heading) comes firstassertSee() and assertDontSee() only count rendered text: text in hidden elements is not seenMETA-INF/services registration, so bx:playwrightRender is found in installed modules (the build now fails if it is missing)assertVisible(), assertMissing(), isVisible(), waitFor() and waitForText() judge every match: a hidden element with the same text or selector no longer hides a visible one, and assertMissing() no longer hits strict mode errors with several hidden matchescount( text ) and expect( text ).toHaveCount() count every element with the text instead of at most oneassertCount() resolves @alias selectors from page objects and componentsassertPathIs() is case sensitive and works for URLs without a host, such as file://assertNoSmoke() reports JavaScript errors on every visited URL, also after a URL that had errorsfreezeTime() accepts BoxLang datesfilter( { has, hasNot } ) and screenshot mask options accept Locators: no more duplication errors, and locators built from the page (now scoped with :scope) match inside hasassertScreenshotMatches() resolves a relative directory against the working directory instead of the installed modulesoft() adds its failures to the outer soft() instead of losing themLocator.texts() returns the text of visible elements onlyLocator.nth( 0 ) throws Playwright.InvalidOption instead of returning the last elementupload( files ) on a locator uploads to the locator itself, like fill( value ) (codegen output such as page.getByLabel( "Resume" ).upload( "cv.pdf" ) works)input[placeholder="Your email"], button:has-text("Sign in")) and Playwright chains (div >> text=Foo, nav >> nth=0) are used as selectors instead of visible textclick( "text" ) waits briefly for a matching button or link before falling back to visible text, so a button rendered a moment later still wins over a same text headingclick( "Save" ) prefers the button or link named exactly "Save" over one named "Save draft"fill( "Email" ) follows its documented priority (exact label, label, placeholder, name) instead of document order, so "Backup email" or a placeholder no longer wins over the "Email" labelnull reach Playwright (e.g. viewport : null disables the fixed viewport); nulls are only skipped for primitive optionsPlaywright.UnsupportedPlatform error, and Windows ARM64 works when nodePath is set (using the Windows x64 layout, like Playwright Java)amd64, x86_64) and arm64 (aarch64, arm64) CPUs are accepted; other architectures (x86, arm, ppc64le, s390x, riscv64) no longer silently map to x64bx:playwrightRender no longer overrides options={ type : "png" } with a PDF, and an invalid viewport such as 1200xabc throws Playwright.InvalidOptionPW_LANG_NAME=java, so Playwright's help and hints stop suggesting mvn exec:java commandsnodePath that does not exist or is not executable is no longer reported as available; using it fails with Playwright.NotInstalled naming the bad pathdefault profile is empty, so the browser, headless and viewport module settings in boxlang.json are no longer ignoredplaywright( struct, struct ) merges the second struct over the first instead of dropping ityyyyMMdd-HHmmss date mask (minutes were used in place of the month)render() and bx:playwrightRender resolve relative links and assets against baseURL (a <base href> is added to the markup)render() accepts a WIDTHxHEIGHT viewport string and throws Playwright.InvalidOption for other non-struct valuesrequest().close() stops the Playwright driver that request() started, so API clients no longer leak a driver processvisit() closes the new context (and the manager it started) when the navigation fails, then rethrows the errorsession() setup never loads the session configured on the manager: creating it no longer fails with "does not exist yet" and refresh starts from a clean pagef1e2BrowserContext.close() always closes and forgets the context even when collecting screenshots or traces fails, and browse() no longer hides the callback error behind a close errora b and a-b no longer share one); simple names such as admin keep their filesnapshots.directory resolves against the current directory instead of the module foldercodegen --output writes relative paths to the current directory (not the module folder) and reports the absolute pathcodegen --output file and -o file (space separated) work: flags that take a value accept the next argument, and it is no longer forwarded to Playwright as a URLcodegen produces BoxLang that runs: # and quotes in strings are escaped, Java escapes resolved, Pattern.compile() becomes page.regex() with flags, enum constants become strings, locator().contentFrame() becomes frame(), popups, downloads and dialogs become callbacks, and multi-line aria snapshots are kept--json prints only valid JSON for install, install-node and codegen (progress and Playwright output go to standard error), and passthrough verbs no longer forward --json to Playwrighthelp nope --json and nope --json print a Playwright.InvalidOption JSON error and exit with 1doctor fails (and install stops with an error) when the Node.js runtime cannot run, such as an explicit nodePath that does not exist, instead of reporting it as okversion reports the Node.js runtime actually used and its source, or nonemcp uses the configured browser (--browser=chromium by default) instead of the branded Chrome that install does not install--x="abc keeps its value: quotes are only stripped when the value starts and ends with the same quoteinstall --with-dep) with Playwright.InvalidOption listing the valid ones, instead of ignoring them
$
box install bx-playwright-full