Drama Finder uses sophisticated locator patterns to handle Vaadin’s shadow DOM structure and component composition. Understanding these patterns helps you work with complex components and create custom element wrappers.
Elements delegate to specific internal locators for different operations. This pattern ensures actions and assertions target the correct DOM element within a component’s shadow DOM.
public class TextFieldElement extends VaadinElement implements FocusableElement, HasAriaLabelElement, HasEnabledElement { // Delegate focus operations to input @Override public Locator getFocusLocator() { return getInputLocator(); } // Delegate aria-label to input @Override public Locator getAriaLabelLocator() { return getInputLocator(); } // Delegate enabled state to input @Override public Locator getEnabledLocator() { return getInputLocator(); }}
Different component aspects (focus, enablement, aria-label) may target different internal elements. Locator delegation ensures each operation uses the correct target.
Pitfall: Calling methods on the wrong locator (component root vs input).Solution: Use getInputLocator() for value/focus operations, getLocator() for component-level attributes.
// Wrong: Checking disabled on component rootboolean disabled = textField.getLocator().getAttribute("disabled") != null; // May not work!// Correct: Checking disabled on input elementboolean disabled = textField.getInputLocator().getAttribute("disabled") != null;// Even better: Use the element's assertiontextField.assertDisabled();
// Pierces shadow DOM automaticallygetLocator().locator("[part~='input']");getLocator().locator("[slot='prefix']");getLocator().locator(".error-message");
// Does NOT pierce shadow DOM (explicit xpath prefix)getLocator().locator("xpath=./*[not(@slot)][1]");// Use for direct children onlygetLocator().locator("xpath=./vaadin-button[1]");
Prefer CSS selectors over XPath for Vaadin components. CSS selectors automatically pierce shadow DOM and are more readable.
Complex components compose simpler elements internally:
public class DateTimePickerElement extends VaadinElement { private final DatePickerElement datePickerElement; private final TimePickerElement timePickerElement; public DateTimePickerElement(Locator locator) { super(locator); // Compose internal elements datePickerElement = new DatePickerElement( locator.locator(DatePickerElement.FIELD_TAG_NAME) ); timePickerElement = new TimePickerElement( locator.locator(TimePickerElement.FIELD_TAG_NAME) ); } public DatePickerElement getDatePicker() { return datePickerElement; } public TimePickerElement getTimePicker() { return timePickerElement; }}
// Access the composite elementDateTimePickerElement dateTime = DateTimePickerElement.getByLabel(page, "Appointment");// Access internal elementsdateTime.getDatePicker().setValue(LocalDate.now());dateTime.getTimePicker().setValue(LocalTime.of(14, 30));// Or use the composite's own methodsdateTime.setValue(LocalDateTime.now());
public class CellElement { private final Locator tableCell; // The <td> element private final Locator cellContent; // The vaadin-grid-cell-content // Get the table cell (for structure) public Locator getTableCellLocator() { return tableCell; } // Get the cell content (for interaction) public Locator getCellContentLocator() { return cellContent; } // Click delegates to content public void click() { cellContent.click(); }}
// Combine locators with .and()Locator button = page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Save")) .and(page.locator("vaadin-button")) .and(page.locator("[theme~='primary']"));// Or combine with .or()Locator submitButtons = page.locator("[type='submit']") .or(page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Submit")));
public class CustomFieldElement extends VaadinElement { public static final String FIELD_TAG_NAME = "custom-field"; public CustomFieldElement(Locator locator) { super(locator); }}
public class CustomFieldElement extends VaadinElement implements HasInputFieldElement, FocusableElement, HasEnabledElement { // Mixins provide default implementations}