> Markdown version of [Component Locators](https://vaadin.com/docs/next/flow/testing/browserless/locators). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# Component Locators (since V25.2)

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 built in to `BrowserlessUIContext` (the multi-user/multi-window API) and to the JUnit 6 extensions (`BrowserlessExtension` and `BrowserlessClassExtension`). Tests that extend `BrowserlessTest`, `SpringBrowserlessTest`, or `QuarkusBrowserlessTest` don’t expose locator entry points by default — to avoid clashing with helper methods existing tests may already define. Opt in by declaring that the test class implements `Locators` (see [Opting In](#opting-in) below). This may become the default in a future release. For projects on commercial Vaadin components, see [Commercial Components](#commercial-components) for the parallel `CommercialLocators` opt-in.

## <a id="generated-entry-points"></a>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. For an `EchoView` with a **Name** field, a **Save** button, and a span with the ID `echo`:

```java
var window = app.newUser().newWindow();
window.navigate(EchoView.class);

window.findTextField().withLabel("Name").setValue("World");
window.findButton().withText("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:

```java
Person first = window.findGrid(Person.class).getRow(0);
int rows = window.findGrid(Person.class).size();
```

## <a id="filter-chain"></a>Filter Chain

Locators share their filter vocabulary with [ComponentQuery](https://vaadin.com/docs/next/flow/testing/browserless/component-query.md#filtering-methods). This includes `withinSlot(String)` (since V25.3); see [Filtering by Slot](https://vaadin.com/docs/next/flow/testing/browserless/component-query.md#slots) for slot ownership and nesting. Locator-specific operations are:

|                                          |                                                                                                                  |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Method                                   | Description                                                                                                      |
| `inside(Component)` / `inside(Locator)`  | Scopes the search to descendants. A locator scope resolves lazily on the first action.                           |
| `atIndex(int)`                           | Selects the n-th match, using a one-based index.                                                                 |
| `with(UnaryOperator<ComponentQuery<C>>)` | Applies query operations not directly exposed by the locator, such as `withPropertyValue` and `withResultsSize`. |

> **Note:** Capability-specific filters — label, text, aria-label, placeholder, value, and theme — only appear on locators whose component actually supports the capability. They are contributed by mixin interfaces (`HasLabelFilter`, `HasTextFilter`, `HasAriaLabelFilter`, `HasPlaceholderFilter`, `HasValueFilter`, and `HasThemeFilter`) that generated locators implement based on the component’s interfaces. For example, `findButton().withText(…​)` compiles while `findButton().withLabel(…​)` is a compile error, because `Button` has text but no label. Custom locators can implement the mixins that match their component.

```java
window.findButton()
        .with(q -> q.withPropertyValue(Button::getText, "Save"))
        .click();
```

## <a id="resolution"></a>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                                                                                                                                                  |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `component()`  | Resolves to a single matching component (or the `atIndex(n)` pick). Caches the result; subsequent calls return the same instance until the cache is cleared. |
| `components()` | Returns all matching components. Bypasses the cache.                                                                                                         |
| `exists()`     | Returns `true` if the filter chain matches at least one component.                                                                                           |
| `invalidate()` | Rewinds the cache and clears any `atIndex(n)` pick. Use after a UI change that may have replaced or detached the resolved component.                         |

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.

```java
var save = window.findButton().withText("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();
```

## <a id="seeding-a-locator-with-a-direct-reference"></a>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:

```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();
```

## <a id="custom-locators"></a>Custom Locators

A custom locator extends `Locator<C, L>` with its own type as `L` to preserve fluent return types. Inner locators use `inside(this)` to scope queries to the resolved composite. `find(Supplier<L>)` creates a custom locator through its factory.

For a worked example, see [Test a Custom Component](https://vaadin.com/docs/next/building-apps/testing/browserless/test-custom-components.md).

## <a id="opting-in"></a>Opting in for `BrowserlessTest`

To use locator entry points in a test class that extends `BrowserlessTest`, `SpringBrowserlessTest`, or `QuarkusBrowserlessTest`, declare that the class implements `Locators`:

```java
@SpringBootTest
class CartViewLocatorsTest extends SpringBrowserlessTest implements Locators {

    @Test
    void addItem_increasesCartSize() {
        navigate(CartView.class);
        findButton().withText("Add to cart").click();

        Assertions.assertEquals("1 item",
                findSpan().withId("cart-size").getText());
    }
}
```

This may become the default in a future release, at which point the explicit `implements` clause can be dropped.

## <a id="commercial-components"></a>Commercial Components

`CommercialLocators` extends `Locators` and adds typed entry points for commercial component testers (`Chart`, `GridPro`, …​). The substitution is orthogonal to where you opt in: use `CommercialLocators` instead of `Locators` whenever you would otherwise reference `Locators`.

```java
@SpringBootTest
class ChartViewTest extends SpringBrowserlessTest
        implements CommercialLocators {

    @Test
    void chart_isRendered() {
        navigate(ChartView.class);

        Assertions.assertNotNull(
                findChart().withId("sales").getComponent());
    }
}
```

The same substitution applies to a custom JUnit 6 extension subclass. For surfaces where you can’t substitute directly — such as the framework-constructed `BrowserlessUIContext` in the multi-user API — pass a commercial locator factory to the generic `find(Supplier<L>)` entry point: for example, `window.find(ChartLocator::new)`.

`2E7A4F31-6B98-4D2A-9C18-5F3E8A7B1C04`
