Cbplaywright
Collection of skills for the ColdBox Platform and Claude Plugin
npx -y skills add ColdBox/skills --skill cbplaywrightAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use this skill when writing end-to-end browser tests in ColdBox/BoxLang using the cbplaywright module (a Playwright wrapper). Covers test bundle setup, browser/page lifecycle, navigation, selectors, assertions, form interactions, screenshots, network interception, the Page Object pattern, and CI pipeline integration.
SKILL.md
6.2 KB, as published. Nobody here has run it
CBPlaywright Skill
When to Use This Skill
Load this skill when:
- Writing browser-level end-to-end tests for ColdBox applications
- Automating form submissions, navigation, or multi-step flows
- Taking screenshots or recording traces for visual regression
- Intercepting or mocking network requests in tests
- Structuring E2E tests with the Page Object pattern
Installation
box install cbplaywright --saveDev
Install Playwright browsers (run once after installation):
box playwright install
Core API
Test Bundle Setup
// tests/e2e/UserFlowSpec.bx
class extends="cbplaywright.models.PlaywrightTestCase" {
// Browser type: chromium | firefox | webkit
this.browser = "chromium"
this.headless = true
this.baseURL = "http://localhost:8500"
function run( spec ) {
describe( "User registration flow", () => {
it( "can register a new user", ( browser ) => {
var page = browser.newPage()
// test body
page.close()
} )
} )
}
}
Navigation & Waiting
page.navigate( baseURL & "/register" )
page.waitForLoadState( "networkidle" )
page.waitForURL( baseURL & "/dashboard" )
Selectors
page.locator( "input[name='email']" )
page.locator( "#submitBtn" )
page.locator( "text=Sign In" )
page.locator( "role=button[name='Submit']" )
page.getByPlaceholder( "Email address" )
page.getByLabel( "Password" )
page.getByRole( "link", { name: "Forgot password?" } )
page.getByText( "Welcome back" )
Interactions
page.locator( "input[name='email']" ).fill( "[email protected]" )
page.locator( "input[name='password']" ).fill( "secret123" )
page.locator( "#loginBtn" ).click()
page.locator( "select[name='role']" ).selectOption( "admin" )
page.locator( "input[type='checkbox']" ).check()
page.locator( "input[type='checkbox']" ).uncheck()
page.keyboard.press( "Enter" )
Assertions
expect( page.locator( "h1" ) ).toBeVisible()
expect( page.locator( ".alert-success" ) ).toContainText( "Registration successful" )
expect( page.url() ).toContain( "/dashboard" )
expect( page.locator( "table tbody tr" ) ).toHaveCount( 5 )
expect( page.locator( "input[name='email']" ) ).toHaveValue( "[email protected]" )
expect( page.locator( "#logoutBtn" ) ).toBeEnabled()
Screenshots & Traces
page.screenshot( { path: expandPath( "/tests/screenshots/home.png" ) } )
// Full-page screenshot
page.screenshot( { path: expandPath( "/tests/screenshots/full.png" ), fullPage: true } )
Network Interception
// Mock an API response
page.route( "**/api/users", ( route ) => {
route.fulfill( {
status : 200,
contentType : "application/json",
body : serializeJSON( [ { id: 1, name: "Mock User" } ] )
} )
} )
Production Patterns
Page Object Pattern
// tests/e2e/pages/LoginPage.bx
class {
property name="page";
property name="baseURL" default="http://localhost:8500";
LoginPage function init( page, baseURL = "http://localhost:8500" ) {
variables.page = page
variables.baseURL = baseURL
return this
}
LoginPage function navigate() {
page.navigate( baseURL & "/login" )
return this
}
LoginPage function fillCredentials( email, password ) {
page.locator( "input[name='email']" ).fill( email )
page.locator( "input[name='password']" ).fill( password )
return this
}
void function submit() {
page.locator( "button[type='submit']" ).click()
page.waitForLoadState( "networkidle" )
}
string function getErrorMessage() {
return page.locator( ".alert-danger" ).innerText()
}
}
// In the test spec
it( "shows error for bad credentials", ( browser ) => {
var page = browser.newPage()
var loginPage = new tests.e2e.pages.LoginPage( page )
loginPage
.navigate()
.fillCredentials( "[email protected]", "wrongpassword" )
.submit()
expect( loginPage.getErrorMessage() ).toContain( "Invalid credentials" )
page.close()
} )
Full CRUD Flow Test
it( "can create and delete a user", ( browser ) => {
var page = browser.newPage()
// Login
page.navigate( baseURL & "/login" )
page.getByLabel( "Email" ).fill( "[email protected]" )
page.getByLabel( "Password" ).fill( "Admin1234!" )
page.getByRole( "button", { name: "Sign In" } ).click()
page.waitForURL( "**/dashboard" )
// Create user
page.navigate( baseURL & "/users/new" )
page.getByLabel( "Name" ).fill( "E2E Test User" )
page.getByLabel( "Email" ).fill( "e2e_#createUUID()#@example.com" )
page.getByRole( "button", { name: "Save" } ).click()
expect( page.locator( ".alert-success" ) ).toBeVisible()
// Delete the user
page.locator( "button.delete-user:last-of-type" ).click()
page.locator( ".confirm-dialog .btn-danger" ).click()
expect( page.locator( ".alert-success" ) ).toContainText( "User deleted" )
page.close()
} )
Best Practices
- Use Page Objects for any flow with more than 3 interactions — keeps tests readable
- Use
getByRoleandgetByLabelselectors — more robust than CSS selectors when UI changes - Always close pages in teardown or at the end of each test to avoid leaked browser contexts
- Use
waitForLoadState( "networkidle" )after navigation or actions that trigger async requests - Store base URL in a single property — avoids hardcoding across hundreds of tests
- Mock external APIs with
page.route()to isolate tests from third-party flakiness - Run in headless mode in CI and only enable headed mode for local debugging
Documentation
- cbplaywright: https://github.com/coldbox-modules/cbplaywright
- Playwright API: https://playwright.dev/docs/api/class-page
Gives 0 of the 12 instructions most e2e browser skills give
Counted across 407 of the 410 authors here whose files we hold, read 2026-08-06
- use page object model patternin 35 of 407, across 25 files
- Snapshot to get element refsin 24 of 407, across 14 files
- keep tests independentin 23 of 407, across 18 files
- Interact using refs from the latest snapshotin 23 of 407, across 11 files
- clean up test data after each testin 21 of 407, across 15 files
- test user behavior not implementationin 20 of 407, across 14 files
- quarantine flaky tests explicitlyin 19 of 407, across 10 files
- wait for specific network conditionsin 18 of 407, across 8 files
- re-snapshot after navigation or dom changesin 17 of 407, across 10 files
- Detect running dev servers before writing test codein 17 of 407, across 7 files
- use web-first assertionsin 17 of 407, across 14 files
- capture screenshots or videos on test failurein 17 of 407, across 14 files
Said here and by no other author read
- Always close pages in teardown or test end
- Store the base URL in a single property
- Mock external APIs with page route
- Run in headless mode in CI pipelines
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.