Component Locators
- Generated Entry Points
- Filter Chain
- Resolution
- Seeding a Locator with a Direct Reference
- Custom Locators
A locator is a fluent object that combines a ComponentQuery filter chain with the actions of a ComponentTester. A single expression replaces the common test(find(…).single()) pattern and the locator is reusable across UI changes — its resolution cache rewinds automatically when filters are reapplied, or explicitly via invalidate().
|
Note
|
Locator entry points are currently available on BrowserlessUIContext (the multi-user/multi-window API). For tests that extend BrowserlessTest or use the JUnit 6 extensions, continue to use component queries with test().
|
Generated Entry Points
For every built-in Vaadin component tester, a typed find<Component>() method is available on the window. The method name mirrors the component type, and the returned locator exposes both the filter chain and the tester’s action methods:
Source code
Java
var window = app.newUser().newWindow();
window.navigate(CartView.class);
window.findTextField().withId("name").setValue("World");
window.findButton().withCaption("Save").click();
Assertions.assertEquals("Saved: World",
window.findSpan().withId("echo").getText());For testers whose value type isn’t pinned by the component (typically Grid and ComboBox), the entry point takes a witness:
Source code
Java
Person first = window.findGrid(Person.class).getRow(0);
int rows = window.findGrid(Person.class).size();Filter Chain
Locators share their filter vocabulary with ComponentQuery:
| Method | Description |
|---|---|
| Matches the component with the given |
| Matches by exact caption or by caption substring. |
| Matches by exact text content or by substring. |
| Matches components carrying all (or none) of the given CSS class names. |
| Matches components with (or without) the given theme value. |
| Matches by attribute presence and value. |
| Matches |
| Matches components satisfying a custom predicate. |
| Scopes the search to descendants of the given component (or of the component matched by the given locator — resolved lazily on first action). |
| Picks the n-th match (1-based) when the filter chain yields more than one. |
| Escape hatch for filters not exposed on the locator (for example, |
Source code
Java
window.findButton()
.with(q -> q.withPropertyValue(Button::getText, "Save"))
.click();Resolution
Most locator chains end in a tester action (click(), setValue(), …), which resolves the locator to a single component and caches the result for the rest of the chain. The base locator also exposes resolution methods directly:
| Method | Description |
|---|---|
| Resolves to a single matching component (or the |
| Returns all matching components. Bypasses the cache. |
| Returns |
| Rewinds the cache and clears any |
Filter methods themselves clear only the resolution cache, so a locator can be re-used safely across UI mutations without re-applying its filters. atIndex(n) is sticky — it is part of the filter chain — so only invalidate() resets the pick.
Source code
Java
var save = window.findButton().withCaption("Save");
window.findTextField().withId("name").setValue("first");
save.click();
// Same locator instance, fresh resolution after the UI changed
window.findTextField().withId("name").setValue("second");
save.invalidate().click();Seeding a Locator with a Direct Reference
When the test already holds a reference to a specific component (for example, an exposed field of a composite), use(component) returns a locator pinned to that instance. The locator skips the type-based search and applies any further filter on top of the identity match:
Source code
Java
PersonForm form = window.find(PersonForm.class).single();
window.use(form.nameField).setValue("Ada");
window.use(form.emailField).setValue("ada@example.com");
window.use(form.submit).click();Custom Locators
For composites, page objects, or domain-specific widgets, subclass Locator with the recursive self-type so filter steps stay chainable, and expose the actions you want the test to see. Scope inner queries with inside(this) so they only match descendants of the resolved composite:
Source code
Java
public class PersonFormLocator
extends Locator<PersonForm, PersonFormLocator> {
public PersonFormLocator() {
super(PersonForm.class);
}
public PersonFormLocator fillIn(String name, String email) {
new TextFieldLocator().withId("pf-name").inside(this).setValue(name);
new TextFieldLocator().withId("pf-email").inside(this).setValue(email);
return this;
}
public void submit() {
new ButtonLocator().withId("pf-submit").inside(this).click();
}
}Tests reach the custom locator through find(Supplier<L>):
Source code
Java
window.find(PersonFormLocator::new)
.fillIn("Ada", "ada@example.com")
.submit();2E7A4F31-6B98-4D2A-9C18-5F3E8A7B1C04