Skip to content
77 changes: 2 additions & 75 deletions articles/flow/testing/browserless/component-query.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ order: 20

= Querying Components in Browserless Tests

The [classname]`BrowserlessTest` base class is able to get the instantiated view, but child components may not always be accessible directly. For example, components may be stored in fields with private visibility or they may even not be referenced at all in the view class.
The [classname]`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, [classname]`BrowserlessTest` provides a component query functionality that lets you search the component tree for the components you need to interact with in test methods.

Expand Down Expand Up @@ -49,7 +49,7 @@ TextField nameField = $view(TextField.class)
.withPropertyValue(TextField::getLabel, "First name")
.single();

// Get all TextFields in the view that satisfies the conditions
// 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 = $view(TextField.class)
Expand All @@ -75,77 +75,4 @@ TextField textField = $view(VerticalLayout.class)
----


== Custom Testers

Custom testers are available for components that give a testing API for the component or one extending it. Testers are annotated using the [annotationname]`@Tests` annotation, which specifies which components the tester is for.

Getting a generic tester using [methodname]`test(Component.class, component)` checks the available testers to determine whether one exists that `Tests` the component or its supertype.

By default, tester implementations are scanned from the `com.vaadin.flow.component` package, so adding a custom tester to the package that extends [classname]`ComponentTester` makes it immediately available.

To have the custom testers in another package, the test needs to be annotated with [annotationname]`@ComponentTesterPackages` containing the packages to scan for testers.

.Defining custom tester package
[source,java]
----
@ComponentTesterPackages("com.example.application.views.personform")
class PersonFormViewTest extends BrowserlessTest {
}
----

Custom tester classes can use other testers internally, as demonstrated in [classname]`PhoneNumberFieldTester`.

.Sample custom tester for a `CustomField`
[source,java]
----
// Tests defines the components this tester should be used for automatically
@Tests(PersonFormView.PhoneNumberField.class)
public class PhoneNumberFieldTester extends ComponentTester<PersonFormView.PhoneNumberField> {
// Other testers can be used inside the custom tester
final ComboBoxTester<ComboBox<String>, String> combo_;
final TextFieldTester<TextField, String> number_;

public PhoneNumberFieldWrap(PersonFormView.PhoneNumberField component) {
super(component);
combo_ = new ComboBoxTester<>(
getComponent().countryCode);
number_ = new TextFieldTester<>(getComponent().number);
}

public List<String> getCountryCodes() {
return combo_.getSuggestionItems();
}

public void setCountryCode(String code) {
ensureComponentIsUsable();
if(!getCountryCodes().contains(code)) {
throw new IllegalArgumentException("Given code isn't available for selection");
}
combo_.selectItem(code);
}

public void setNumber(String number) {
ensureComponentIsUsable();
number_.setValue(number);
}

public String getValue() {
return getComponent().generateModelValue();
}

}
----

.`PhoneNumberField.java`
[source,java]
----
static class PhoneNumberField extends CustomField<String> {
ComboBox<String> countryCode = new ComboBox<>();
TextField number = new TextField();

// ...
}
----


[discussion-id]`DDC7D136-1A56-44FC-B256-C15DB7645EDC`
149 changes: 149 additions & 0 deletions articles/flow/testing/browserless/component-testers.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
title: Component Testers
page-title: How to use and build component testers for browserless testing
description: Using built-in testers and building custom ones for your own components.
meta-description: Learn how to use Vaadin's built-in component testers and build custom testers for your own components in browserless tests.
order: 18
---


= Component Testers

Component testers simulate user interactions in browserless tests. Each tester wraps a specific component type and provides methods that mirror what a real user can do -- clicking, typing, selecting items, and reading displayed values.


== Using Component Testers

Wrap any component with `test()` to get a tester for it:

[source,java]
----
test(textField).setValue("Jane");
test(button).click();
String text = test(notification).getText();
----

The `test()` method returns a tester matched to the component's type. You can also request a specific tester type explicitly:

[source,java]
----
TextFieldTester tester = test(TextFieldTester.class, textField);
----


=== Usability Checks

Before performing any action, testers verify that the component is in a usable state. An action fails with a clear error message if the component is:

- Not visible
- Not enabled
- Not attached to the UI
- Behind a modal overlay

This catches common issues where a test passes by calling the Java API directly, but the corresponding user action would be impossible in a browser.


=== Common Testers

The following table shows frequently used testers and their key methods:

[cols="1,2"]
|===
| Component | Key Tester Methods

| [classname]`TextField`
| [methodname]`setValue(String)`, [methodname]`getValue()`

| [classname]`Button`
| [methodname]`click()`

| [classname]`Checkbox`
| [methodname]`setChecked(boolean)`

| [classname]`ComboBox`
| [methodname]`selectItem(value)`, [methodname]`getSuggestionItems()`

| [classname]`Grid`
| [methodname]`getRow(index)`, [methodname]`size()`, [methodname]`getCellText(row, column)`

| [classname]`Notification`
| [methodname]`getText()`
|===


== Building Custom Testers

When you create custom components, you can build testers for them too. Custom testers extend [classname]`ComponentTester` and use the [annotationname]`@Tests` annotation to declare which component they test.


=== Defining a Custom Tester

[source,java]
----
// Tests defines the components this tester should be used for automatically
@Tests(PersonFormView.PhoneNumberField.class)
public class PhoneNumberFieldTester extends ComponentTester<PersonFormView.PhoneNumberField> {
// Other testers can be used inside the custom tester
final ComboBoxTester<ComboBox<String>, String> combo_;
final TextFieldTester<TextField, String> number_;

public PhoneNumberFieldWrap(PersonFormView.PhoneNumberField component) {
super(component);
combo_ = new ComboBoxTester<>(
getComponent().countryCode);
number_ = new TextFieldTester<>(getComponent().number);
}

public List<String> getCountryCodes() {
return combo_.getSuggestionItems();
}

public void setCountryCode(String code) {
ensureComponentIsUsable();
if(!getCountryCodes().contains(code)) {
throw new IllegalArgumentException("Given code isn't available for selection");
}
combo_.selectItem(code);
}

public void setNumber(String number) {
ensureComponentIsUsable();
number_.setValue(number);
}

public String getValue() {
return getComponent().generateModelValue();
}

}
----

.`PhoneNumberField.java`
[source,java]
----
static class PhoneNumberField extends CustomField<String> {
ComboBox<String> countryCode = new ComboBox<>();
TextField number = new TextField();

// ...
}
----

Custom testers can use other testers internally, as shown above with `ComboBoxTester` and `TextFieldTester`.


=== Registering Custom Testers

By default, tester implementations are scanned from the `com.vaadin.flow.component` package, so adding a custom tester to that package makes it immediately available.

To place custom testers in another package, annotate the test class with [annotationname]`@ComponentTesterPackages`:

[source,java]
----
@ComponentTesterPackages("com.example.application.views.personform")
class PersonFormViewTest extends BrowserlessTest {
}
----


[discussion-id]`A1B2C3D4-E5F6-7890-ABCD-EF1234567890`
Loading