Overview
What changed in 25.3, in plain words:
- The version number now says which Vaadin the release is for. This release is
25.3.0, not1.2.0. From here on a Browserless Test version carries the version of the Vaadin release it targets — and it targets exactly that one. - Seven components that only had the generic tester got one of their own — grid context menus,
TreeGrid, GridPro,Switch,SplitLayout,CardandAvatarGroup. A grid's right-click menu, for instance, can now be opened on a row and its items clicked; before, the attempt failed with "is not usable because it is not attached". - A tester now does what the browser does, and refuses what the browser refuses. Text a user could never type — longer than
maxLength, or a character the field filters out — is rejected instead of committed, while a value that is merely invalid is committed and leaves the field invalid, exactly as on screen. Interactions report themselves as coming from the client, so application code that branches onisFromClient()takes the same path in a test as in a browser. - One area of a component can be searched on its own. A query can be scoped to a named slot, and Dialog, Card, SplitLayout and LoginOverlay testers offer
findInHeader/findInFooterand friends. Dialog header and footer content had no route at all before this. - A test can configure its own Vaadin environment.
@BrowserlessTestConfigsets application properties, feature flags andLookupservices for a single class or a single method — no system properties, nothing to reset, nothing leaking into the next test. - A grid's header, footer and editor components are part of the component tree again, so
find(...)returns them. Plus a round of unused dependencies removed.
Breaking Changes
-
Versions follow Vaadin from 25.3 on (#229)
Every module is released as
25.3.0instead of1.2.0, and a build from source uses25.3-SNAPSHOT. One Browserless Test version targets one Vaadin version, so the numbers now say so. The older1.0and1.1branches keep their own numbering. -
GridTester.getCellComponentreturns the component the grid rendered (#250)It used to render the cell again on every call and attach the fresh copy to the grid, so a test never acted on the component the grid shows. Reading the same cell twice now gives the same instance, nothing is attached any more (so
find(...)no longer reports leftover copies), a row the client has not asked for is scrolled into view first, and a cell the grid renders nothing for — a hidden column, or a data provider whose items are not equal across fetches — throwsIllegalStateException.getCellTextreads that same rendered component and returns""rather thannullfor a renderer that produced nothing. The old behaviour is still available asrenderCellComponent(...). -
TextFieldTester.setValueandTextAreaTester.setValuerefuse values the browser physically prevents (#252)Text longer than
maxLength, or containing a characterallowedCharPatternblocks, now fails withIllegalArgumentExceptioninstead of being committed. Validation-only constraints —minLength,pattern, required — are unchanged: they still commit the value and leave the field invalid, because that is what the browser does. -
NumberFieldTester,DatePickerTester,TimePickerTesterandDateTimePickerTestercommit invalid values instead of throwing (#211)A value outside
min/max, off thestepscale, or an empty value on a required field is committed and leaves the field invalid, as in the browser. Tests that wrapped these calls inassertThrows(IllegalArgumentException.class, ...)must assert on the newisValid()instead. -
test(gridContextMenu)returns aGridContextMenuTester(#232)Source-incompatible only for code that assigns the result to an explicitly typed
ComponentTestervariable. Closing an overlay now also simulates the browser'sclosedevent for everyContextMenuBase, so a closed grid context menu is detached from the UI like any other overlay. -
test(treeGrid)returns aTreeGridTester(#189)Source-incompatible only for code that assigns the result to an explicitly typed
GridTester<Grid<V>, V>; widen it toGridTester<? extends Grid<V>, V>or usevar. Already compiled code is unaffected. -
Tester-driven changes report as user actions (#188, #249)
Value changes and context menu open/close go through the client path, so the resulting events have
isFromClient() == true. Tests assertingfalsefor a tester-driven change need updating. A refused open now leaves the menu closed and detached instead of half-opened. -
A read-only component counts as not usable (#139)
The check moved into
ComponentTester#isUsable(), so it applies to everyHasValuecomponent — the date, time, date-time, combo box, multi-select combo box, select and list box testers ignored read-only state before. -
More interaction methods check usability first (#184)
Selecting an item in a disabled
ComboBoxor uploading a file to an invisibleUploadnow throws, like every other interaction method already did. -
A grid's header, footer and editor components are found by
find(...)(#203)They were missing from the component tree. Tests that asserted the old "no such component" behaviour, or that count all components in a view containing a grid, may need updating. A component in a merged header cell is still reported exactly once.
-
Looking up a menu item by text considers visible items only (#238)
A hidden item sharing its text with a visible one no longer makes the path ambiguous. The only lookups that change outcome are those that used to fail.
-
Spring and Quarkus lookup services moved to
frameworkLookupServices()(#148)They are always registered now, so overriding
lookupServices()adds to them instead of replacing them, and the resultingLookupholds more services than before. Feature flags are handled per test environment, so toggling one no longer needs development mode and no longer writesvaadin-featureflags.propertiesinto the project folder. -
CheckboxGroupTester.updateSelectionis private (#190) — an internal helper that modelled nothing a user does, and that the annotation processor therefore copied onto the generatedCheckboxGroupLocator.
Deprecations and Removals
MockInstantiatoris deprecated for removal (#205) — the mocked services use the real instantiator directly, so the wrapper mocks nothing any more.MockInstantiator.create(...)returns the given instantiator unchanged.BaseBrowserlessTest.lookupServices()is deprecated (#148) in favour of the test configuration. Existing overrides keep working.- The deprecated
$and$viewquery methods now name 26.0 as their removal version (#229) — they promised2.0, a version this numbering scheme never reaches.
New Features
New component testers
-
GridContextMenuTester, with open on row (#232)Open the menu the way a user does, then click an item. The row and column reach the application through the menu's own events, opening and closing report as user gestures, and the tester refuses what a user cannot do — an invisible column, an already open menu, a row a dynamic content handler rejects.
GridContextMenuTester<GridContextMenu<Person>, Person> menu = test(grid).contextMenu(1); menu.open(); menu.clickItem("Edit"); Assertions.assertEquals("Bob", view.editedPerson.getName());
-
TreeGridTester, with expand and collapse (#189)expand(int)/collapse(int)mirror a click on the tree toggle, soExpandEventandCollapseEventcome from the client;isExpanded(int)andhasChildren(int)read a row's state, and a hierarchy column's cell text is read from the toggle. -
SplitLayoutTester,CardTesterandAvatarGroupTester(#197) — drag a splitter, and read what a card or an avatar group shows. -
GridProTester(#149) andSwitchTesterfor the experimentalSwitchcomponent (#143).
Finding components
-
withinSlot(name)onComponentQueryandLocator(#220) — find what a host puts in a named slot. This neededwithAttribute("slot", ...)before, which missed nested content and found nothing at all for components likeDialog. -
Slot-scoped finders on the dialog, card, split layout and login overlay testers (#227)
findInHeader/findInFooteronDialogTesterandCardTester,findInPrimary/findInSecondaryonSplitLayoutTester,findInFooter/findInCustomFormAreaonLoginOverlayTester. Each checks the component is usable first, and content nested deeper inside the slot is found too.DialogTester dialog = test(view.editDialog); dialog.open(); dialog.findInFooter(Button.class).withText("Save").single().click();
-
getItemTexts()on the menu testers (#242) —MenuBarTester,ContextMenuTesterandGridContextMenuTesterreport what a menu shows, hidden items skipped, addressed by the same text pathclickItem(...)takes. -
Component internals are looked up on the whole class hierarchy (#224) — testers broke as soon as you tested your own subclass of a Vaadin component.
Interactions closer to the browser
- A uniform
clear()in every value tester (#168) —clear()models deleting the contents from the keyboard, which the user can always do, and the newclickClearButton()models clicking the clear button, which needs that button on screen. - User-driven dialog dismissal (#183) —
pressEscape()andclickOutside()go through the path the browser uses, so a close action listener can veto the close. check()/uncheck()andswitchOn()/switchOff()(#185) — put a checkbox or switch into the state the test needs instead of flipping it.closeDetails()andtoggleDetails(...)onAccordionTester(#186).deselect(int)anddeselectAll()onGridTester(#187), through the same from-client path asselect().stepUp()/stepDown()andisValid()onNumberFieldTester(#191) — click the +/- buttons a number or integer field can show.UploadTesterenforces the client-side constraints (#198) —maxFiles,maxFileSizeand the accepted file types are checked before the file reaches the handler, and a rejected file fires aFileRejectedEventas in a browser.getLastUploadStatus()andensureUploaded()show what became of each file.
Test environment
-
Per-test Vaadin configuration (#148)
@BrowserlessTestConfigsets application properties, feature flags andLookupservices for one test class or one test method; class and method values merge, the method wins. The same settings can be built in code withBrowserlessConfigurationand handed to the extensions or the application context builders. Properties are in place before the servlet starts and flags before the Vaadin service is created, so aVaadinServiceInitListeneralready sees them.@ViewPackages(classes = CheckoutView.class) @BrowserlessTestConfig(featureFlags = "collaborationEngineBackend") class CheckoutViewTest extends BrowserlessTest { @Test @BrowserlessTestConfig(featureFlags = "collaborationEngineBackend=false") void fallbackCheckout_isShown() { CheckoutView view = navigate(CheckoutView.class); } }
-
HttpServletRequest.changeSessionId()rotates the session id for real (#208) — it threwUnsupportedOperationException, so an application doing its own session-fixation protection after login could not be tested. -
navigate(location, viewType)accepts a query string (#204) —navigate("orders/1?tab=history", View.class)failed with "Base path can not contain query separator=?". Errors thrown while entering a view are no longer wrapped in anInvocationTargetExceptioneither. -
Every
Instantiatormethod reaches the real instantiator (#205) — the Kotlin delegation in the old wrapper only forwarded abstract members, so an application-widePageTitleGeneratorregistered as a Spring bean, among others, was silently ignored.
Fixes
ImageTesteris anHtmlComponentTester(#165) — followingImagebecoming anHtmlComponentin Flow, sinceimgis a void element.- Slotted children are found through
ComponentUtil.getAllChildren()(#142) — for components such asCardandDialog, which filter non-default content out ofgetChildren(). - GridPro locator entries go to the commercial aggregator (#162) — they landed in the core interface, which forced javac to load
GridProand broke compilation in projects withoutvaadin-grid-proon the classpath. - Service listeners fire through the
VaadinServiceevent bus (#159) — the private collections they were read from by reflection moved into the new bus in Flow 25.3.
Documentation
- What
find()can and cannot see (#219, #236) — a query walks the server-side component tree, so per-item rendered components and closed overlay content are not in it; the docs now name the testers that do reach them, includingGridTester.contextMenu(row)for grid context menus. - How shared signal writes are confirmed (#202) — the write is visible at once, but the returned
SignalOperationcompletes only after the test drains the pending signal tasks. CLAUDE.md,CONVENTIONS.mdand aguidelines/folder (#222), adapted from Flow.
Dependency Updates
- Targets Vaadin 25.3 (from 25.2). Java 21+ as before.
kotlin-reflectremoved (#160) — the one runtime use, thehreflookup intoPrettyString(), is plain Java reflection now, which also dumps the href of components that only expose a getter, such asRouterLink.mockito-core,karibu-dsl,jakarta.annotation-apiand the redundantjunit-platform-enginedropped, andkotlin-stdlib-jdk8replaced bykotlin-stdlib(#161).