Writing good tests with Drama Finder requires following Playwright best practices while leveraging the Vaadin-specific element wrappers. This guide distills lessons from the Drama Finder codebase and community experience.
When working with elements inside containers, use scoped lookups:
// Get a dialogDialogElement dialog = DialogElement.get(page);// Find button within the dialogButtonElement confirmButton = ButtonElement.getByText(dialog.getLocator(), "Confirm");// Not recommended: Could match buttons outside the dialogButtonElement confirmButton = ButtonElement.getByText(page, "Confirm");
Playwright assertions automatically retry until they pass or timeout, making tests resilient to timing issues.
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;@Testpublic void testButtonEnabled() { ButtonElement button = ButtonElement.getByText(page, "Submit"); button.click(); // Auto-retries until button is disabled or times out assertThat(button.getLocator()).not().hasAttribute("disabled", "");}
Drama Finder provides assertion methods that handle common patterns correctly:
// Good: Proper null handling and retry logictextField.assertMinLength(6);textField.assertHelperHasText("Enter at least 6 characters");button.assertEnabled();comboBox.assertValue("Expected Value");// Less ideal: Raw Playwright assertions (more verbose)assertThat(textField.getInputLocator()).hasAttribute("minLength", "6");assertThat(textField.getHelperLocator()).hasText("Enter at least 6 characters");assertThat(button.getLocator()).not().hasAttribute("disabled", "");
Playwright CSS selectors pierce shadow DOM by default, but XPath does not:
// Good: CSS pierces shadow DOM automaticallygetLocator().locator("[slot='input']");getLocator().locator("[part~='clear-button']");// Use xpath only for direct children (doesn't pierce)getLocator().locator("xpath=./*[not(@slot)][1]");
Avoid complex XPath selectors. Prefer CSS with part and slot attributes for Vaadin components.
Playwright automatically waits for elements to be actionable:
// Playwright waits for:// 1. Element to be attached to DOM// 2. Element to be visible// 3. Element to be stable (not animating)// 4. Element to be enabledbutton.click();textField.setValue("value");checkbox.check();
Only use custom waits when built-in waits aren’t sufficient:
// Good: Wait for specific business logic conditionpage.waitForFunction( "() => window.myApp && window.myApp.isDataLoaded()");// Good: Wait for Vaadin Flow to be ready (already in AbstractBasePlaywrightIT)page.waitForFunction(WAIT_FOR_VAADIN_SCRIPT);
Test names should describe the behavior being tested:
// Good: Describes what and why@Test public void testValidationErrorAppearsWhenRequiredFieldIsEmpty()@Test public void testComboBoxFiltersItemsWhenUserTypesSearchTerm()@Test public void testDatePickerRejectsDateOutsideAllowedRange()// Bad: Generic or implementation-focused@Test public void testTextField()@Test public void test1()@Test public void testSetValue()
When possible, use real user interactions over programmatic changes:
// Good: Real user interactiontextField.getInputLocator().fill("value");textField.getInputLocator().press("Enter");// Less ideal: Direct manipulationtextField.getInputLocator().evaluate("el => el.value = 'value'");textField.getLocator().dispatchEvent("change");
/** * Selects an item from the combo box dropdown. * Opens the dropdown if not already open, filters to the item, * and clicks the matching option. * * @param itemText the visible text of the item to select * @throws PlaywrightException if the item is not found */public void selectItem(String itemText) { open(); getOverlayLocator() .locator("vaadin-combo-box-item") .filter(new Locator.FilterOptions().setHasText(itemText)) .click();}
/** * Returns the helper text content. * Note: If a component is slotted as helper, this returns * the text content of that component. * * @return the helper text, or null if no helper is set */public String getHelperText() { return getHelperLocator().textContent();}