Page Objects, Components and Macros
Keep selectors and flows in classes, reuse widgets, add your own methods.
On this page
Page Objects, Components and Macros
Page objects
// pages/LoginPage.bx
class extends="models.PageObject@playwright" {
url = "/login"
elements = { email : "#email", password : "input[type=password]", submit : "button" }
function at() {
page.assertTitle( "Login" )
}
function loginAs( required string email, string password = "secret" ) {
page.fill( "@email", email ).fill( "@password", password ).click( "@submit" )
return page.on( new pages.DashboardPage() )
}
}
home = page.visit( new pages.LoginPage() ).loginAs( "luis@ortus.com" )
home.assertSee( "Dashboard" )
page.visit( pageObject )goes to itsurl, binds it and runsat().page.on( pageObject )binds without navigating.elementsbecome@nameselectors. Inside the class,pageis the bound page.- Page methods work on the page object too and keep returning it.
Components
// pages/UserMenu.bx
class extends="models.PageComponent@playwright" {
selector = "nav.user"
elements = { logout : "a.logout" }
function name() {
return text( "span" )
}
}
page.within( new pages.UserMenu(), ( menu ) => menu.click( "@logout" ) )
menu = page.component( new pages.UserMenu() )
Every page action is available on a component, scoped to its root.
Macros
playwright().macro( "loginAs", ( page, email ) => page.visit( "/login" ).fill( "Email", email ).click( "Sign in" ) )
page.loginAs( "luis@ortus.com" ).assertSee( "Welcome" )
playwright().macro( "shout", ( locator ) => uCase( locator.text() ), "locator" )
Macros are global. Remove them with removeMacro( name, target ).