Skip to content

DramaFinder 2.0.0

DramaFinder 2.0 makes lookups strict and actions synchronous with the server:

  • label lookups match the whole label;
  • a label is the visible label: an aria-label is the accessible name, and the query builder finds it and the placeholder;
  • every action waits for the Vaadin round-trip it triggers;
  • a typed query builder gives every element the same lookup grammar;
  • the Spring Boot base class ships in the library.

1.x test suites compile unchanged, unless a test class declares its own navigate, assertAt, assertPath or assertQueryParameter, or calls the removed AccessibleNameLocator (see below). The getByLabel factories of components without a visible label are deprecated. Lookups that pass part of a label, an aria-label or a placeholder to getByLabel fail at runtime, as does a test that asserts a state while a request is still in flight. Read the migration guide before upgrading. The jar also ships it, at META-INF/dramafinder/MIGRATION-2.0.md.

Breaking changes

Label lookups match exactly by default (#207)

Every factory that finds a component by its label, text, header, title or name now matches the whole value, case-sensitively (whitespace-normalized), and applies no .first(). TextFieldElement.getByLabel(page, "Name") no longer resolves to a "Nickname" field, and two components sharing a label raise a Playwright strict-mode error instead of resolving to the first one in the DOM.

Partial matching is now explicit: getByLabelContaining(Page|Locator, String) next to every getByLabel, and ButtonElement.getByTextContaining(Page|Locator, String). Both are case-insensitive substring matches, still without .first().

52 public factory methods change. P = 1.x matched part of the label, C = 1.x matched a differently-cased label, D = 1.x picked the first of several matches.

Field getByLabel — P C D (replacement getByLabelContaining for part of a label; an aria-label or placeholder is no longer a label, see the next section), on Page and Locator unless noted:

  • BigDecimalFieldElement, ComboBoxElement, DatePickerElement, DateTimePickerElement, EmailFieldElement, IntegerFieldElement, MultiSelectComboBoxElement, NumberFieldElement, SelectElement, SliderElement, TextAreaElement, TextFieldElement, TimePickerElement — getByLabel(Page, String) and getByLabel(Locator, String) (26 methods)
  • PasswordFieldElement.getByLabel(Page, String)
  • SelectElement.getByLabel now matches the visible label only, and has no placeholder fallback. The 1.x lookup matched the value button's name, which includes the selected value, so it broke after a selection.

Other getByLabel (replacement getByLabelContaining):

  • P C D: BreadcrumbsElement.getByLabel(Page, String), CheckboxElement.getByLabel(Page, String), CheckboxGroupElement.getByLabel(Page|Locator, String), CustomFieldElement.getByLabel(Page|Locator, String) (also behind CustomFieldElement.getField(Class, String)), ListBoxElement.getByLabel(Page, String), RadioButtonGroupElement.getByLabel(Page, String), SideNavigationElement.getByLabel(Page, String), SwitchElement.getByLabel(Page, String)
  • P C: ButtonElement.getByLabel(Page, String), MenuBarElement.getByLabel(Page, String), MenuElement.getByLabel(Page, String), PopoverElement.getByLabel(Page, String)

Button text — P C (replacement getByTextContaining): ButtonElement.getByText(Page, String), ButtonElement.getByText(Locator, String). The name is the aria-label, or else the button's own text. An open tooltip is no longer part of it; in 1.x the lookup stopped matching after a hover.

Headers, titles, summaries, button text (no …Containing variant):

  • P C: DialogElement.getByHeaderText(Page, String), ConfirmDialogElement.getByHeaderText(Page, String), LoginOverlayElement.getByTitle(Page, String), CardElement.getByTitle(Page|Locator, String), DetailsElement.getBySummaryText(Page, String) (now the summary's accessible name)
  • P C D: UploadElement.getByButtonText(Page, String)

Avatar name — D only: AvatarElement.getByName(Page|Locator, String).

The next section narrows what a label is; the list above names the 2.0 methods whichever way a 1.x test used them.

These keep their 1.x matching: BadgeElement.getByText, NotificationElement.getByText, TooltipElement.getByText and VirtualListElement.getItemByText (documented "contains" helpers). So do the item lookups inside containers, which were already exact since 1.2.0.

A label is the visible label (#214)

getByLabel matches the text a component shows in its own label slot, and nothing else. An aria-label (or aria-labelledby text) is the component's accessible name; a placeholder is neither. In 1.x getByLabel matched all three, while the query builder's withLabel matched only the visible label, so the two disagreed. Now getByLabel is the same lookup as withLabel, and a test checks they stay in step.

The 1.x getByLabel relied on Use in 2.0
the whole visible label getByLabel, unchanged
part of it, or another case getByLabelContaining
an aria-label, or unknown (closest to 1.x) find(page, X.class).withAccessibleNameContaining(..)
the placeholder find(page, X.class).withPlaceholder(..)
  • 21 elements with a visible label match it only: the text, number, date and time fields, combo boxes, select, slider, checkbox, switch, checkbox and radio groups, custom field, side nav and side-nav item. Each has getByLabel and getByLabelContaining with a Page and a Locator overload; Password field, Switch, Radio group, Side nav, Checkbox and Side-nav item gain the missing ones.
  • 7 elements without a visible label keep a getByLabel that matches their accessible name, now deprecated for removal in 3.0: Breadcrumbs, ListBox, MenuBar, Menu, MenuItem and Popover (use find(..).withAccessibleName(..)), and Button (use getByText). MenuElement can now be used with find.
  • withAccessibleNameContaining is on every query, and withPlaceholder now also on Select (while no value is selected) and DateTimePicker (its date placeholder).
  • CustomFieldElement.getField(Class, String) finds a wrapped field by its whole accessible name.
  • AccessibleNameLocator is removed. Custom elements use the query builder.
  • ViewOutline suggests getByLabel only for a visible label, and withAccessibleName / withPlaceholder otherwise.

Actions wait for the Vaadin server round-trip (#209)

Every element action that can reach the server now returns only once Vaadin Flow has no request in flight. This covers click, setValue, clear, check/uncheck, selectItem, key presses, hoverTarget, focus/blur, upload, setProperty and the rest. A getter called right after an action reads the state the server produced. In 1.x only setValue and a few date, slider and checkbox-group methods waited.

The property setters wait too: setMinLength, setMaxLength, setPattern, setPlaceholder and setAllowedCharPattern, setStep/setMin/setMax on number fields, the progress bar's setters, CheckboxElement.setIndeterminate and RadioButtonGroupElement.selectByValue. So does ContextMenuElement.openOn. A unit test fails when an action is added without the wait.

A test that asserts the in-flight state (a loading indicator, a disabled-while-saving button) now sees the final state. Wrap that action in AutoWait.withoutWaiting(() -> ...), or pass -Ddramafinder.autoWait=false to restore the 1.x timing for a whole run. An explicit waitForVaadinIdle() always waits; call it after actions on a raw Playwright Locator.

The full suite ran about 7 % longer: an idle check is a single quick evaluation in the page.

New protected methods on AbstractBasePlaywrightIT (#211)

AbstractBasePlaywrightIT gains navigate(Class<?>), navigate(Class<?>, Object), assertAt(Class<?>), assertPath(String) and assertQueryParameter(String, String) (see "Navigation by route class"). A test class that already declares a method with one of these names and parameter types now overrides it. If the return type differs, or the method is less visible than protected, the class no longer compiles. Rename the test's own method, or drop it in favour of the inherited one.

New features

Typed query builder (#206)

One lookup grammar for every element, alongside the factories:

import static org.vaadin.addons.dramafinder.element.query.ElementQueries.find;

TextFieldElement name = find(page, TextFieldElement.class).withLabel("Name").single();
dialog.find(ButtonElement.class).withText("Delete").single().click();
find(page, ButtonElement.class).withLabel("Save"); // does not compile
  • Each element offers only the filters that fit it, checked at compile time: withLabel / withLabelContaining on labelled components, withText / withTextContaining on buttons, tabs, badges and similar, withPlaceholder on input fields, the select and the date-time picker.
  • Every element has withId, withTestId, withAttribute, withClassName, withAccessibleName, withAccessibleNameContaining and a refine(...) escape hatch.
  • Terminals: single() (strict), first(), get(int), all(), count(), exists(), getLocator(). Queries are immutable and stay lazy.
  • Text filters read the component's own text. Slotted icons, tooltips and shadow-rendered numbers are left out, so a badge with an icon matches its text.
  • withAccessibleName never matches through a nested component: a dialog is not found by the name of a field it holds.
  • Custom components join by implementing Findable<Q> with a query type.

getById and getByTestId on every element (#210)

The id and test-id factories now exist on 52 elements, each with a Page and a Locator overload: getById(page, "save") and getByTestId(page, "save-button"). In 1.x getById existed only on AppLayout, FormLayout, Grid, TreeGrid and Tabs.

  • The test id is the data-testid attribute that Flow's Component.setTestId(...) sets.
  • Matches are exact, limited to the element's own tag, and fail on duplicates.
  • They are fallbacks for components without a user-visible name.
  • The five 1.x getById methods now also check the tag, and accept ids that are not valid CSS identifiers.
  • Not offered on Notification, Tooltip, Menu, ContextMenu and MenuItem, whose wrapped tag is not where Flow puts the id.

AbstractBasePlaywrightIT navigates by view class instead of a hand-built URL, and asserts where the browser is, retrying until it gets there:

navigate(ProfileView.class);                                        // nav/profile
navigate(ArticleView.class, 42);                                    // HasUrlParameter
navigate(UserEditView.class, new RouteParameters("userId", "42")); // user/:userId/edit
assertAt(ProfileView.class);           // e.g. after a login redirect or a guard
assertPath("nav/profile");
assertQueryParameter("tab", "2");      // null asserts the parameter is absent
  • The path comes from @Route, or Flow's naming convention when it has no value. It is prefixed by the @RoutePrefix of the route's layout and its @ParentLayout chain, honouring absolute. So a renamed route breaks at the navigate call, not silently.
  • navigate waits for Vaadin to finish loading. assertAt accepts any value for a template's parameters or a HasUrlParameter.
  • RouteNavigation offers the same with an explicit Page and base URL, for tests that do not extend AbstractBasePlaywrightIT.
  • com.vaadin:flow-server is a provided dependency: only tests that navigate by class need it. AbstractBasePlaywrightIT still loads without it.

Spring Boot base class in the library (#212)

org.vaadin.addons.dramafinder.spring.SpringPlaywrightIT replaces the class that each Spring Boot project copied from the skill's template:

public class OrderViewIT extends SpringPlaywrightIT {   // no annotation needed
    @Override
    public String getView() {
        return "orders";
    }
}
  • It carries @SpringBootTest(webEnvironment = RANDOM_PORT). Re-annotate the test to change properties or the web environment (DEFINED_PORT works too).
  • getUrl() comes from the running server: the local port, the servlet context path, and https when server.ssl.enabled is set.
  • Spring stays optional. DramaFinder declares spring-boot-test as provided, so it never adds Spring to a project's classpath. Projects without Spring keep extending AbstractBasePlaywrightIT. A Spring Boot project already has spring-boot-starter-test.
  • A project's own SpringPlaywrightIT copy keeps working. Delete it and switch the imports to org.vaadin.addons.dramafinder.spring.SpringPlaywrightIT, then drop the now-redundant @SpringBootTest(webEnvironment = RANDOM_PORT) from the tests.

Improvements

  • ViewOutline suggests getByTestId for a component without a label, before falling back to getById. It no longer annotates ambiguous lookups as substring matches, because labels now match in full (#207, #210).
  • ButtonQuery / TextQuery gain withAccessibleNameContaining(String) (#207).

Fixes

Found while making lookups exact (#207):

  • SelectElement.getByLabel stopped finding the select once a value was picked.
  • DetailsElement.getBySummaryText missed summaries whose text sits in a <span>.
  • ButtonElement.getByText and ButtonQuery.withAccessibleName stopped matching a button while its tooltip was open.

SliderElement.setValue throws IllegalStateException on a disabled or read-only slider (#166). In 1.x it wrote the value from a script, which the disabled input did not block: the browser showed the new value while the server ignored it, and assertValue passed on a slider no user could move.

Agent skills and documentation

  • Migration guide MIGRATION-2.0.md, bundled in the jar (manifest entry Dramafinder-Migration-2-0), linked from the README ("Upgrading from 1.x"), llms.txt and START-HERE.md.
  • vaadin-playwright-test skill bumped to 0.13.0 (from 0.6.0). It now asks for whole labels, tells a label from an accessible name, uses navigation by route class, and explains the query builder. Its bundled api-reference.md gains a "Typed queries" section, AutoWait and RouteNavigation under "Test setup", and a find query column plus "+ id, test id" in the element index.
  • vaadin-playwright-screenshot skill bumped to 0.2.1.
  • The vaadin-playwright-test skill now uses the library's SpringPlaywrightIT from 2.0 on, and generates its template copy only for projects pinned to 1.x (#212).
  • AGENTS.md documents the exact-matching rules, the label rule, the typed query pattern, the waitAfterAction() rule for new actions and the id factories.
  • GLOSSARY.md defines label, accessible name, placeholder and item.
  • The obsolete Vaadin Directory publish workflow is removed (#197).

Full changelog: https://github.com/parttio/dramafinder/compare/dramafinder-1.2.4...dramafinder-2.0.0