Skip to content

25.3.0

Latest

Choose a tag to compare

@ZheSun88 ZheSun88 released this 22 Sep 07:32
· 2 commits to 25.3 since this release
6423bc2

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, not 1.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, Card and AvatarGroup. 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 on isFromClient() 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 / findInFooter and friends. Dialog header and footer content had no route at all before this.
  • A test can configure its own Vaadin environment. @BrowserlessTestConfig sets application properties, feature flags and Lookup services 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.

All changes

Breaking Changes

  • Versions follow Vaadin from 25.3 on (#229)

    Every module is released as 25.3.0 instead of 1.2.0, and a build from source uses 25.3-SNAPSHOT. One Browserless Test version targets one Vaadin version, so the numbers now say so. The older 1.0 and 1.1 branches keep their own numbering.

  • GridTester.getCellComponent returns 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 — throws IllegalStateException. getCellText reads that same rendered component and returns "" rather than null for a renderer that produced nothing. The old behaviour is still available as renderCellComponent(...).

  • TextFieldTester.setValue and TextAreaTester.setValue refuse values the browser physically prevents (#252)

    Text longer than maxLength, or containing a character allowedCharPattern blocks, now fails with IllegalArgumentException instead 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, TimePickerTester and DateTimePickerTester commit invalid values instead of throwing (#211)

    A value outside min / max, off the step scale, or an empty value on a required field is committed and leaves the field invalid, as in the browser. Tests that wrapped these calls in assertThrows(IllegalArgumentException.class, ...) must assert on the new isValid() instead.

  • test(gridContextMenu) returns a GridContextMenuTester (#232)

    Source-incompatible only for code that assigns the result to an explicitly typed ComponentTester variable. Closing an overlay now also simulates the browser's closed event for every ContextMenuBase, so a closed grid context menu is detached from the UI like any other overlay.

  • test(treeGrid) returns a TreeGridTester (#189)

    Source-incompatible only for code that assigns the result to an explicitly typed GridTester<Grid<V>, V>; widen it to GridTester<? extends Grid<V>, V> or use var. 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 asserting false for 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 every HasValue component — 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 ComboBox or uploading a file to an invisible Upload now 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 resulting Lookup holds more services than before. Feature flags are handled per test environment, so toggling one no longer needs development mode and no longer writes vaadin-featureflags.properties into the project folder.

  • CheckboxGroupTester.updateSelection is private (#190) — an internal helper that modelled nothing a user does, and that the annotation processor therefore copied onto the generated CheckboxGroupLocator.

Deprecations and Removals

  • MockInstantiator is 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 $view query methods now name 26.0 as their removal version (#229) — they promised 2.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, so ExpandEvent and CollapseEvent come from the client; isExpanded(int) and hasChildren(int) read a row's state, and a hierarchy column's cell text is read from the toggle.

  • SplitLayoutTester, CardTester and AvatarGroupTester (#197) — drag a splitter, and read what a card or an avatar group shows.

  • GridProTester (#149) and SwitchTester for the experimental Switch component (#143).

Finding components

  • withinSlot(name) on ComponentQuery and Locator (#220) — find what a host puts in a named slot. This needed withAttribute("slot", ...) before, which missed nested content and found nothing at all for components like Dialog.

  • Slot-scoped finders on the dialog, card, split layout and login overlay testers (#227)

    findInHeader / findInFooter on DialogTester and CardTester, findInPrimary / findInSecondary on SplitLayoutTester, findInFooter / findInCustomFormArea on LoginOverlayTester. 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, ContextMenuTester and GridContextMenuTester report what a menu shows, hidden items skipped, addressed by the same text path clickItem(...) 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 new clickClearButton() models clicking the clear button, which needs that button on screen.
  • User-driven dialog dismissal (#183) — pressEscape() and clickOutside() go through the path the browser uses, so a close action listener can veto the close.
  • check() / uncheck() and switchOn() / switchOff() (#185) — put a checkbox or switch into the state the test needs instead of flipping it.
  • closeDetails() and toggleDetails(...) on AccordionTester (#186).
  • deselect(int) and deselectAll() on GridTester (#187), through the same from-client path as select().
  • stepUp() / stepDown() and isValid() on NumberFieldTester (#191) — click the +/- buttons a number or integer field can show.
  • UploadTester enforces the client-side constraints (#198) — maxFiles, maxFileSize and the accepted file types are checked before the file reaches the handler, and a rejected file fires a FileRejectedEvent as in a browser. getLastUploadStatus() and ensureUploaded() show what became of each file.

Test environment

  • Per-test Vaadin configuration (#148)

    @BrowserlessTestConfig sets application properties, feature flags and Lookup services for one test class or one test method; class and method values merge, the method wins. The same settings can be built in code with BrowserlessConfiguration and 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 a VaadinServiceInitListener already 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 threw UnsupportedOperationException, 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 an InvocationTargetException either.

  • Every Instantiator method reaches the real instantiator (#205) — the Kotlin delegation in the old wrapper only forwarded abstract members, so an application-wide PageTitleGenerator registered as a Spring bean, among others, was silently ignored.

Fixes

  • ImageTester is an HtmlComponentTester (#165) — following Image becoming an HtmlComponent in Flow, since img is a void element.
  • Slotted children are found through ComponentUtil.getAllChildren() (#142) — for components such as Card and Dialog, which filter non-default content out of getChildren().
  • GridPro locator entries go to the commercial aggregator (#162) — they landed in the core interface, which forced javac to load GridPro and broke compilation in projects without vaadin-grid-pro on the classpath.
  • Service listeners fire through the VaadinService event 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, including GridTester.contextMenu(row) for grid context menus.
  • How shared signal writes are confirmed (#202) — the write is visible at once, but the returned SignalOperation completes only after the test drains the pending signal tasks.
  • CLAUDE.md, CONVENTIONS.md and a guidelines/ folder (#222), adapted from Flow.

Dependency Updates

  • Targets Vaadin 25.3 (from 25.2). Java 21+ as before.
  • kotlin-reflect removed (#160) — the one runtime use, the href lookup in toPrettyString(), is plain Java reflection now, which also dumps the href of components that only expose a getter, such as RouterLink.
  • mockito-core, karibu-dsl, jakarta.annotation-api and the redundant junit-platform-engine dropped, and kotlin-stdlib-jdk8 replaced by kotlin-stdlib (#161).

Changes since 25.3.0-rc1

All changes