Skip to main content
Version: 1.x

Selector Reference

In this guide
  • Choose the right selector fields for your use case
  • Find controls by ID, type, properties, binding path, and ancestry
  • Scope selectors to views and dialogs
  • Use the CSS-style ui5= locator syntax as an alternative
  • Avoid brittle DOM-based selectors

Selectors are the primary way to find UI5 controls on a page. Praman uses UI5Selector objects that query the UI5 runtime's control registry — not the DOM.

Common mistake

Do not use DOM IDs (e.g., #__xmlview0--btn1) as selectors. UI5 generates these IDs at runtime and they change across views, components, and UI5 versions. Always use the control's stable ID or other semantic selector fields.

// Wrong — brittle runtime-generated DOM ID
await ui5.control({ id: '__xmlview0--btn1' });

// Correct — stable application ID
await ui5.control({ id: 'saveBtn' });

// Better — semantic selector resilient to ID changes
await ui5.control({
controlType: 'sap.m.Button',
properties: { text: 'Save' },
});

:::info Locator Syntax You can also find controls using CSS-style syntax with page.locator('ui5=...'). This returns a standard Playwright Locator and supports combinators, label matching, and positional pseudo-classes. See Finding Controls with Locators. :::

UI5Selector Fields

FieldTypeDescription
controlTypestringFully qualified UI5 type (e.g., 'sap.m.Button'). When provided as a string literal, narrows the return type to a control-specific interface.
idstring | RegExpControl ID or pattern
viewNamestringOwning view name for scoped discovery
viewIdstringOwning view ID for scoped discovery
propertiesRecord<string, unknown>Property matchers (key-value pairs)
bindingPathRecord<string, string>OData binding path matchers
i18NTextRecord<string, string>i18n text matchers (translated values)
ancestorUI5SelectorParent control must match this selector
descendantUI5SelectorChild control must match this selector
interactionUI5InteractionSub-control targeting (idSuffix, domChildWith)
searchOpenDialogsbooleanAlso search controls inside open dialogs

All fields are optional. At least one must be provided.

Which selector fields do I need?

Use this decision tree to pick the most resilient selector for your situation:

Do you know the control's stable ID?
├── YES → Is the ID unique across all views?
│ ├── YES → Use { id: 'myId' }
│ └── NO → Add viewName: { id: 'myId', viewName: 'app.view.Detail' }

└── NO → Do you know the OData binding path?
├── YES → Use { controlType, bindingPath }
│ (most resilient — survives ID refactoring)

└── NO → Do you know a unique property value?
├── YES → Use { controlType, properties }
│ e.g., { controlType: 'sap.m.Button', properties: { text: 'Save' } }

└── NO → Is the control inside a specific parent?
├── YES → Use { controlType, ancestor }
└── NO → Use { controlType, viewName }

Examples

By ID

const button = await ui5.control({ id: 'saveBtn' });

By ID with RegExp

const button = await ui5.control({ id: /submit/i });

By Control Type

const buttons = await ui5.controls({ controlType: 'sap.m.Button' });

:::tip Typed Returns When you use controlType with ui5.control() (singular), the return type narrows to the specific typed interface (e.g., UI5Button). See Typed Control Returns for full details and examples. :::

By Type + Properties

const input = await ui5.control({
controlType: 'sap.m.Input',
properties: { placeholder: 'Enter vendor' },
});

Scoped to View

const field = await ui5.control({
controlType: 'sap.m.Input',
viewName: 'myApp.view.Detail',
properties: { value: '' },
});

By Binding Path

const field = await ui5.control({
controlType: 'sap.m.Input',
bindingPath: { value: '/PurchaseOrder/Vendor' },
});

With Ancestor

const cellInput = await ui5.control({
controlType: 'sap.m.Input',
ancestor: {
controlType: 'sap.m.Table',
id: 'poTable',
},
});

With Descendant

const form = await ui5.control({
controlType: 'sap.ui.layout.form.SimpleForm',
descendant: {
controlType: 'sap.m.Input',
properties: { placeholder: 'Vendor' },
},
});

Interaction Sub-Targeting

// Target the arrow button inside a ComboBox
const combo = await ui5.control({
controlType: 'sap.m.ComboBox',
id: 'countrySelect',
interaction: { idSuffix: 'arrow' },
});

Search Inside Dialogs

const dialogButton = await ui5.control({
controlType: 'sap.m.Button',
properties: { text: 'OK' },
searchOpenDialogs: true,
});

Selector String Format

Praman registers a ui5= custom selector engine with Playwright, enabling usage in page.locator(). Two formats are supported:

JSON Format

Pass a UI5Selector object as a JSON string:

const locator = page.locator('ui5={"controlType":"sap.m.Button","properties":{"text":"Save"}}');
await locator.click();

CSS Format

Use CSS-like syntax to match controls by type, ID, and properties:

page.locator("ui5=sap.m.Button[text='Save']");
page.locator('ui5=sap.m.Input:labeled("Vendor")');
page.locator('ui5=sap.m.InputBase::subclass');

For the complete CSS and XPath syntax reference, see Finding Controls with Locators.

Control Type Cheat Sheet

ControlcontrolTypeCommon Properties
Buttonsap.m.Buttontext, icon, type, enabled
Inputsap.m.Inputvalue, placeholder, enabled, editable
Textsap.m.Texttext
Labelsap.m.Labeltext, required
Selectsap.m.SelectselectedKey
ComboBoxsap.m.ComboBoxvalue, selectedKey
CheckBoxsap.m.CheckBoxselected, text
DatePickersap.m.DatePickervalue, dateValue
TextAreasap.m.TextAreavalue, rows
Linksap.m.Linktext, href
GenericTilesap.m.GenericTileheader, subheader
Tablesap.m.TableheaderText
SmartFieldsap.ui.comp.smartfield.SmartFieldvalue, editable
SmartTablesap.ui.comp.smarttable.SmartTableentitySet, useTablePersonalisation
SmartFilterBarsap.ui.comp.smartfilterbar.SmartFilterBarentitySet

:::note SmartField Inner Controls SmartField wraps inner controls (Input, ComboBox, etc.). getControlType() returns sap.ui.comp.smartfield.SmartField, not the inner control type. Use properties matching to target by value rather than relying on the inner control type. :::

Discovery Strategies

Praman uses a multi-strategy chain for control discovery:

  1. LRU Cache (200 entries, 5s TTL) — instant for repeat lookups
  2. Direct IDsap.ui.getCore().byId(id) for exact ID matches
  3. RecordReplay — SAP's RecordReplay API (requires UI5 >= 1.94)
  4. Registry Scan — full ElementRegistry scan as fallback

Configure the strategy chain:

import { defineConfig } from 'playwright-praman';

export default defineConfig({
discoveryStrategies: ['direct-id', 'recordreplay'],
});

FAQ

Can I use a RegExp for the id field?

Yes. RegExp IDs are useful when the control ID includes a view prefix that may change:

await ui5.control({ id: /vendorInput/ });

This matches any control whose ID contains vendorInput, such as myApp--detailView--vendorInput. RegExp selectors are more resilient than exact ID strings but less precise — ensure the pattern is specific enough to match only one control.

When should I use bindingPath instead of properties?

Use bindingPath when you want to find a control by its OData model binding rather than its displayed value. This is more resilient because binding paths are defined in the XML view and do not change with i18n translations or user data. For example, { bindingPath: { value: '/PurchaseOrder/Vendor' } } finds the vendor input regardless of what text it currently displays.

How do I find controls inside a dialog?

Add searchOpenDialogs: true to your selector. Without this flag, the discovery engine only searches the main view's control tree and skips controls rendered in overlay dialogs.

await ui5.control({
controlType: 'sap.m.Button',
properties: { text: 'Confirm' },
searchOpenDialogs: true,
});
What is the difference between ancestor and descendant?

ancestor scopes the search to controls that have a matching parent higher in the tree. descendant finds controls that contain a matching child. Use ancestor when you know the parent container (e.g., find an Input inside a specific Table). Use descendant when you know the child content (e.g., find a Form that contains a specific Input).

Next steps