Docs

Querying Components in Browserless Tests

Details and examples on accessing components within a browserless test.

The BrowserlessTest base class can get the instantiated view, but child components may not always be directly accessible. For example, components may be stored in private fields or may not be referenced at all in the view class.

To overcome this limitation, BrowserlessTest provides a component query functionality that lets you search the component tree for the components you need to interact with in test methods.

Component Queries

You can get a ComponentQuery object by calling the find() method, specifying the type of the component you are searching for.

Once the query is ready with all conditions configured, use a terminal operator to retrieve the components that it found. Examples of terminal operators are single(), last(), atIndex(), all(), id(), and testId().

Source code
Java
// Get the TextField
TextField nameField = find(TextField.class).single();
Note
BrowserlessTest retains $() and $view() as deprecated aliases for find() and findInView(), scheduled for removal in 2.0. The newer composition-based APIs (BrowserlessExtension, BrowserlessClassExtension, and BrowserlessUIContext) only expose find() and findInView().

Scoping Queries

You can also restrict search scope to the children of the current view by using the findInView() method, or even to another component by using find(MyComponent.class, rootComponent).

Source code
Java
// Get the TextField in the current view
TextField nameField = findInView(TextField.class).single();

// Get the TextField nested in a container
TextField nameField = find(TextField.class, view.formLayout).single();

The query object has many filtering methods that can be used to refine the search.

Source code
Java
// Get the TextField with the given label
TextField nameField = findInView(TextField.class)
        .withLabel("First name")
        .single();

// Get all TextFields in the view that satisfy the conditions
Predicate<TextField> fieldHasNotValue = field -> field.getOptionalValue().isEmpty();
Predicate<TextField> fieldIsInvalid = TextField::isInvalid;
List<TextField> textField = findInView(TextField.class)
        .withCondition(fieldHasNotValue.or(fieldIsInvalid))
        .all();

Filtering Methods

The following table lists all available filter methods, grouped by category:

Method Description

Text & Caption

withText(String)

Matches components whose text content equals the given string exactly.

withTextContaining(String)

Matches components whose text content contains the given text fragment.

withCaption(String)

Matches components whose caption equals the given string exactly.

withCaptionContaining(String)

Matches components whose caption contains the given text fragment.

Labels

withLabel(String)

Matches components whose label equals the given string exactly. Use this for form fields (TextField, ComboBox, and so on) where the end user identifies a field by its label.

withLabelContaining(String)

Matches components whose label contains the given text fragment.

withAriaLabel(String)

Matches components whose aria-label equals the given string exactly. Useful for components like Button that don’t carry a visible label property but identify themselves to assistive technology via aria-label.

withAriaLabelContaining(String)

Matches components whose aria-label contains the given text fragment.

withPlaceholder(String)

Matches HasPlaceholder components whose placeholder equals the given string exactly. Useful for toolbar or search fields that omit a stacked label and identify themselves to the user through placeholder text instead.

withPlaceholderContaining(String)

Matches HasPlaceholder components whose placeholder contains the given text fragment.

CSS Classes & Themes

withClassName(String…​)

Matches components that have all of the given CSS class names.

withoutClassName(String…​)

Excludes components that have any of the given CSS class names.

withTheme(ThemeVariant)

Matches components that have the given theme variant — for example, withTheme(ButtonVariant.LUMO_PRIMARY). The typed variant supports IDE completion and turns typos into compile errors.

withoutTheme(ThemeVariant)

Excludes components that have the given theme variant.

Attributes

withAttribute(String)

Matches components that have the given attribute, regardless of value.

withAttribute(String, String)

Matches components that have the given attribute with the specified value.

withoutAttribute(String)

Excludes components that have the given attribute.

Slots

withinSlot(String)

Matches components that sit in the given named slot of the component that hosts them. See Filtering by Slot.

Value & Properties

withValue(V)

Matches HasValue components whose current value equals the given value.

withPropertyValue(Function, V)

Matches components where the given getter returns the expected value.

withCondition(Predicate)

Matches components that satisfy a custom predicate.

Identity

withId(String)

Matches the component with the given id.

withTestId(String)

Matches the component with the given test ID (the data-testid attribute set with Component.setTestId()). Test IDs are treated as unique: at most one match is expected.

Note
Deprecated withTheme(String) and withoutTheme(String) overloads remain available for filtering on theme names that aren’t surfaced through a ThemeVariant enum, such as custom themes.

Here are a few examples:

Source code
Java
// Find a button by its text
Button save = find(Button.class).withText("Save").single();

// Find a TextField by its label
TextField name = find(TextField.class).withLabel("First name").single();

// Find the primary button
Button primary = find(Button.class)
        .withTheme(ButtonVariant.LUMO_PRIMARY).single();

// Find a TextField by CSS class
TextField styled = find(TextField.class).withClassName("highlighted").single();

// Find checkboxes with a specific value
Checkbox checked = find(Checkbox.class).withValue(true).single();

Finding Components by Test ID

If a component has a test ID assigned with Component.setTestId(), the testId() terminal operator looks it up directly, pairing with the existing id() operator:

Source code
Java
// In the view
Button submit = new Button("Submit");
submit.setTestId("submit-button");

// In the test
Button submitButton = find(Button.class).testId("submit-button");

To combine a test ID with other filter conditions, use the withTestId() filter method instead and finish the chain with a regular terminal operator. Since test IDs are expected to be unique, both forms fail if more than one component matches. See Getting Started for more on test IDs.

Filtering by Slot

Components such as Card, Dialog, and SplitLayout place content in named slots. An unfiltered query returns matches from all of them, and withinSlot() narrows the result to one slot:

Source code
Java
// Only the buttons the card put in its footer
List<Button> footerButtons = test(card).find(Button.class)
        .withinSlot("footer").all();

A component is in slot name when it carries slot="name" itself, or when an element that carries it is one of its ancestors. Everything below a slot belongs to that slot, however deeply nested, and when slots nest, the outermost one — the one closest to the search context — decides. This makes withinSlot() different from withAttribute("slot", name), which matches only a component that is itself the slot root.

Slot names are the ones the component uses in the browser, and they’re component specific: Card and ConfirmDialog name their header slot header, while Dialog names it header-content. A name that no component is slotted under produces no results. Printing the component tree with toPrettyTree() shows the slots as @slot='…​'.

For the slots of a component that has a tester, the tester’s own finders are usually shorter; see Slot-Scoped Finders.

Checking Existence

Use exists() to check whether a query has results without throwing an exception when none are found:

Source code
Java
if (find(Notification.class).exists()) {
    // A notification is open
}

Result Count Assertions

You can assert the number of results directly in the query chain:

Source code
Java
// Expect exactly 3 text fields
List<TextField> fields = find(TextField.class).withResultsSize(3).all();

// Expect between 1 and 5 results
List<Button> buttons = find(Button.class).withResultsSize(1, 5).all();

// Expect at least 1 result
List<Grid> grids = find(Grid.class).withMinResults(1).all();

Chaining Queries

You may sometimes need to do a query for components nested inside the UI, in a hierarchy composed of many different types of components. To simplify such situations, the query object offers methods to chain a new query starting with a found component, so that a complex query can be created in a fluent way. The thenOn() method and its variants, for example thenOnFirst(), provide you with a new query object for the given component type, setting the search scope to the component selected from the current query.

Source code
Chained Query Example
// Search for all 'VerticalLayout's in the view
TextField textField = findInView(VerticalLayout.class)
        // take the second one and start searching for 'TextField's
        .thenOn(2, TextField.class)
        // filter for disabled 'TextField's
        .withCondition(tf -> !tf.isEnabled())
        // and get the last one
        .last();

Components a Query Cannot Find

A query walks the server-side component tree. A component that another component renders per item, and the content of an overlay that is closed, are not in that tree, and a query returns an empty result for them instead of an error. The failure therefore reads as though the component was never created.

Components a Renderer Creates

A component column creates its component while rendering a row, and no row renders on its own in a browserless test:

Source code
Java
grid.addComponentColumn(person -> new Checkbox(person.isSubscriber()))
        .setKey("subscriber");

// Finds nothing: no row has rendered yet
List<Checkbox> checkboxes = find(Checkbox.class).all();

GridTester renders the cell on request:

Source code
Java
Checkbox checkbox = (Checkbox) test(grid).getCellComponent(0, "subscriber");
test(checkbox).click();

The tester hands out the component the grid actually rendered, which is the one a browser shows: reading the same cell twice gives the same instance, and that instance is replaced when the row renders anew, after refreshItem(…​) for example. A row the client has not asked for yet is scrolled into view first, the way a user reaches it, and a cell the grid does not render at all, such as one in a hidden column, throws.

renderCellComponent(row, column) renders a cell on its own instead, and attaches the copy to the grid so that it can be used. Every call renders the cell again and leaves the copy behind, so a later query reports every one of them. It is for the cells the grid does not render, and for tests written against the earlier behavior of getCellComponent, which rendered a copy per call.

A LitRenderer column has no server-side component at all. Read it with getCellText(row, column), getLitRendererPropertyValue(…​), or invokeLitRendererFunction(…​).

A component set as a column header, footer, or editor is part of the tree, and a query finds it. Such a component is reported once, even when it sits in a header cell that spans several columns.

Overlay Content

The content of an overlay, such as a context menu, is attached to the UI only while the overlay is open, so a top-level query does not see it. Query it through the overlay’s own tester, or open the overlay first. See Testing Overlay Components for both approaches.

Source code
Java
// Empty while the menu is closed
find(Div.class).withText("Rename").all();

test(menu).open();

// One match
find(Div.class).withText("Rename").all();

For a worked example, see Test User Interactions.

DDC7D136-1A56-44FC-B256-C15DB7645EDC

Updated