From aiup-vaadin-jooq
Creates Playwright browser-based integration tests for Vaadin views using Drama Finder library for type-safe, accessibility-first element wrappers. Triggers on requests for Playwright tests, E2E tests, or Vaadin UI testing.
npx claudepluginhub ai-unified-process/marketplace --plugin aiup-vaadin-jooqThis skill uses the workspace's default tool permissions.
Create Playwright integration tests for the Vaadin view specified in $ARGUMENTS. Tests run in a real browser against a running application. Use the Drama Finder library for type-safe, accessibility-first element lookups — never raw Playwright locators.
Guides creating page objects and refactoring Playwright tests using Page Object Model patterns for maintainability, reusability, and scalability. Covers locators, principles, and TypeScript examples.
Write Playwright E2E tests using fixtures and best practices. Use when creating E2E tests, writing browser automation tests, or testing user flows.
Generates Playwright tests from user stories, URLs, components, or features. Explores codebase, uses templates for auth, CRUD, checkout, and follows best practices for locators and assertions.
Share bugs, ideas, or general feedback.
Create Playwright integration tests for the Vaadin view specified in $ARGUMENTS. Tests run in a real browser against a running application. Use the Drama Finder library for type-safe, accessibility-first element lookups — never raw Playwright locators.
Tests extend AbstractBasePlaywrightIT from Drama Finder, which handles browser lifecycle, page creation, and Vaadin synchronization automatically.
<dependency>
<groupId>org.vaadin.addons</groupId>
<artifactId>dramafinder</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
page.locator("vaadin-text-field") — use Drama Finder element wrappersThread.sleep() or page.waitForTimeout() — Drama Finder assertions auto-retrygetAttribute()/isVisible() directly in assertions — they don't auto-retryUse existing test data from Flyway migrations in src/test/resources/db/migration. If your test creates data, clean up in @AfterEach.
Use templates/ExampleViewIT.java as the starting point for new test classes.
Drama Finder uses ARIA roles and accessible names — not CSS selectors. This makes tests resilient to DOM changes and enforces accessibility. API documentation of Drama Finder: https://parttio-dramafinder-4.mintlify.app/api/vaadin-element
TextFieldElement nameField = TextFieldElement.getByLabel(page, "Full Name");
DatePickerElement birthDate = DatePickerElement.getByLabel(page, "Birth Date");
ComboBoxElement country = ComboBoxElement.getByLabel(page, "Country");
CheckboxElement active = CheckboxElement.getByLabel(page, "Active");
ButtonElement save = ButtonElement.getByText(page, "Save");
GridElement grid = GridElement.getById(page, "customer-grid");
GridElement grid = GridElement.get(page);
DialogElement dialog = new DialogElement(page);
NotificationElement notif = new NotificationElement(page);
DialogElement dialog = DialogElement.getByHeaderText(page, "Confirm Delete");
When multiple elements share the same label, scope the lookup to a container:
DialogElement dialog = DialogElement.getByHeaderText(page, "Edit Person");
TextFieldElement name = TextFieldElement.getByLabel(dialog.getLocator(), "Name");
ButtonElement confirm = ButtonElement.getByText(dialog.getLocator(), "Confirm");
| Role | Element Classes |
|---|---|
| TEXTBOX | TextFieldElement, EmailFieldElement, PasswordFieldElement, TextAreaElement |
| SPINBUTTON | IntegerFieldElement, BigDecimalFieldElement, NumberFieldElement |
| COMBOBOX | ComboBoxElement, DatePickerElement, TimePickerElement, DateTimePickerElement |
| BUTTON | ButtonElement |
| CHECKBOX | CheckboxElement |
| RADIO | RadioButtonElement |
| DIALOG | DialogElement |
| GRID | GridElement |
For icon-only buttons, set setAriaLabel("Close") on the server side, then find with ButtonElement.getByText(page, "Close").
TextFieldElement tf = TextFieldElement.getByLabel(page, "Username");
tf.setValue("john.doe");
tf.clear();
tf.getValue();
tf.assertValue("john.doe");
tf.assertVisible();
tf.assertEnabled();
tf.assertValid();
tf.assertInvalid();
tf.assertRequired();
tf.assertErrorMessage("Field is required");
tf.assertPattern("\\d{7}");
tf.assertMinLength(6);
tf.assertMaxLength(7);
tf.assertHelperHasText("Enter 7 digits");
Sub-locators: getInputLocator(), getHelperLocator(), getErrorMessageLocator(), getPrefixLocator(), getSuffixLocator()
ButtonElement btn = ButtonElement.getByText(page, "Save");
btn.click();
btn.assertEnabled();
btn.assertDisabled();
btn.assertVisible();
btn.assertTheme("primary");
btn.assertCssClass("custom-btn");
btn.focus();
btn.assertIsFocused();
GridElement grid = GridElement.get(page);
// Row counts
int total = grid.getTotalRowCount(); // all rows (including non-rendered)
int visible = grid.getRenderedRowCount(); // only rendered in viewport
// Headers
List<String> headers = grid.getHeaderCellContents();
// Cell access
var cell = grid.findCell(0, 0); // by row/column index
var cell = grid.findCell(0, "Email"); // by row index + column header
// Row operations
var row = grid.findRow(0);
grid.select(0);
grid.deselect(0);
// Lazy loading — scrolls automatically to the row
var distantRow = grid.findRow(9000);
// Sorting
var header = grid.findHeaderCellByText("Name");
header.get().clickSort();
// Scrolling
grid.scrollToRow(500);
grid.scrollToStart();
grid.scrollToEnd();
// Wait for async data
grid.waitForGridToStopLoading();
// Select all
grid.checkSelectAll();
grid.getSelectedItemCount();
ComboBoxElement cb = ComboBoxElement.getByLabel(page, "Country");
cb.selectItem("Germany");
cb.filterAndSelectItem("Ger", "Germany"); // for lazy-loading
cb.setFilter("search text");
cb.open();
cb.close();
cb.getValue();
cb.assertValue("Germany");
cb.assertOpened();
cb.assertClosed();
cb.assertItemCount(5);
cb.assertReadOnly();
DatePickerElement dp = DatePickerElement.getByLabel(page, "Birth Date");
dp.setValue(LocalDate.of(1990, 1, 15));
dp.setValue("15/01/1990"); // string format
dp.getValueAsLocalDate();
dp.assertValue(LocalDate.of(1990, 1, 15));
CheckboxElement cb = CheckboxElement.getByLabel(page, "Accept Terms");
cb.check();
cb.uncheck();
cb.assertChecked();
cb.assertNotChecked();
cb.isIndeterminate();
cb.assertIndeterminate();
DialogElement dialog = DialogElement.getByHeaderText(page, "Confirm Delete");
dialog.assertOpen();
dialog.assertClosed();
dialog.getHeaderLocator();
dialog.getContentLocator();
dialog.getFooterLocator();
NotificationElement notif = new NotificationElement(page);
notif.assertOpen();
notif.assertClosed();
notif.getContentLocator();
AccordionElement acc = new AccordionElement(locator);
acc.openPanel("Details");
acc.closePanel("Details");
acc.assertPanelOpened("Details");
acc.assertPanelClosed("Details");
acc.assertPanelCount(3);
All assert*() methods auto-retry until the condition is met or timeout (default 5 seconds). This eliminates the need for manual waits.
| Category | Methods |
|---|---|
| Visibility | assertVisible(), assertHidden() |
| State | assertEnabled(), assertDisabled() |
| Focus | assertIsFocused(), assertIsNotFocused() |
| Value | assertValue("...") (text fields, combo boxes, date pickers) |
| Validation | assertValid(), assertInvalid(), assertRequired() |
| Error message | assertErrorMessage("..."), getErrorMessageLocator() |
| Checked | assertChecked(), assertNotChecked(), assertIndeterminate() |
| Dialog | assertOpen(), assertClosed() |
| ARIA | assertAriaLabel("...") |
| Styling | assertTheme("..."), assertCssClass("...") |
| Tooltip | assertTooltipHasText("...") |
| Prefix/Suffix | assertPrefixHasText("..."), assertSuffixHasText("...") |
| Grid row count | assertThat(grid.getTotalRowCount()).isGreaterThan(0) |
| Grid cell content | assertThat(cell.get().getCellContentLocator()).hasText("...") |
Each element has two locator levels — use the right one:
getLocator() — the component root. Use for: theme, class, opened, invalid attributesgetInputLocator() — the internal input element. Use for: value, maxlength, pattern, placeholder, focus, disabledCSS selectors pierce shadow DOM automatically. XPath does NOT.
Part selectors for internal elements: input, clear-button, toggle-button, prefix, suffix
@Nested classes with @DisplayName)AbstractBasePlaywrightIT with @SpringBootTest and @LocalServerPortgetUrl() (return http://localhost:<port>/) and getView() (return the route)@AfterEach./mvnw verify -Pit to verifyisGreaterThan() for grid counts, add waitForGridToStopLoading() for async grids.first() automatically; scope to container for precisiongetInputLocator() for value/focus, getLocator() for component attributes./mvnw verify -Pit -Dheadless=false -Dit.test=YourTestIT