diff --git a/articles/flow/testing/browserless/component-testers.adoc b/articles/flow/testing/browserless/component-testers.adoc index 79a16df6e6..c058f66cd4 100644 --- a/articles/flow/testing/browserless/component-testers.adoc +++ b/articles/flow/testing/browserless/component-testers.adoc @@ -27,6 +27,8 @@ test(button).click(); String value = textField.getValue(); ---- +A tester drives the component through the same path the browser uses, so a listener that branches on [methodname]`isFromClient()` behaves in a test as it does in a running application. + The `test()` method returns a tester matched to the component's type. You can also request a specific tester type explicitly: [source,java] @@ -57,16 +59,16 @@ The following table shows frequently used testers and their key methods: | Component | Key Tester Methods | [classname]`TextField` -| [methodname]`setValue(String)`, [methodname]`clear()` +| [methodname]`setValue(String)`, [methodname]`clear()`, [since:com.vaadin:vaadin@V25.3]#[methodname]`clickClearButton()`# | [classname]`Button` | [methodname]`click()` | [classname]`Checkbox` -| [methodname]`click()` (toggles checked state) +| [methodname]`click()` (toggles checked state), [since:com.vaadin:vaadin@V25.3]#[methodname]`check()`, [methodname]`uncheck()`# | [classname]`NumberField` -| [methodname]`setValue(double)` +| [methodname]`setValue(double)`, [methodname]`clear()`, [since:com.vaadin:vaadin@V25.3]#[methodname]`stepUp()`, [methodname]`stepDown()`, [methodname]`isValid()`# | [classname]`Select` | [methodname]`selectItem(String)`, [methodname]`selectItem(int)` @@ -74,23 +76,35 @@ The following table shows frequently used testers and their key methods: | [classname]`ComboBox` | [methodname]`selectItem(String)`, [methodname]`getSuggestionItems()` +| [classname]`DatePicker`, [classname]`TimePicker` +| [methodname]`setValue(value)`, [since:com.vaadin:vaadin@V25.3]#[methodname]`clear()`, [methodname]`clickClearButton()`, [methodname]`isValid()`# + +| [classname]`DateTimePicker` +| [methodname]`setValue(value)`, [since:com.vaadin:vaadin@V25.3]#[methodname]`clear()`, [methodname]`isValid()`# + | [classname]`Grid` -| [methodname]`getRow(index)`, [methodname]`size()`, [methodname]`getCellText(row, column)` +| [methodname]`getRow(index)`, [methodname]`size()`, [methodname]`getCellText(row, column)`, [methodname]`select(int)`, [since:com.vaadin:vaadin@V25.3]#[methodname]`deselect(int)`, [methodname]`deselectAll()`# + +| [since:com.vaadin:vaadin@V25.3]#[classname]`TreeGrid`# +| [classname]`TreeGridTester`: [methodname]`expand(int)`, [methodname]`collapse(int)`, [methodname]`isExpanded(int)`, [methodname]`hasChildren(int)`, plus everything [classname]`GridTester` offers | [classname]`Notification` | [methodname]`getText()` | [classname]`Dialog` -| [methodname]`open()`, [methodname]`close()` +| [methodname]`open()`, [methodname]`close()`, [since:com.vaadin:vaadin@V25.3]#[methodname]`pressEscape()`, [methodname]`clickOutside()`# | [classname]`ConfirmDialog` | [methodname]`open()`, [methodname]`confirm()`, [methodname]`cancel()`, [methodname]`reject()` | [classname]`Upload` -| [methodname]`upload(File)`, [methodname]`uploadAll(File...)` +| [methodname]`upload(File)`, [methodname]`uploadAll(File...)`, [since:com.vaadin:vaadin@V25.3]#[methodname]`getLastUploadStatus()`, [methodname]`ensureUploaded()`# + +| [classname]`Accordion` +| [classname]`AccordionTester`: [methodname]`openDetails(String)`, [methodname]`isOpen(String)`, [since:com.vaadin:vaadin@V25.3]#[methodname]`closeDetails()`, [methodname]`closeDetails(String)`, [methodname]`toggleDetails(String)`# | [since:com.vaadin:vaadin@V25.2]#[classname]`Breadcrumbs`# -| [classname]`BreadcrumbsTester`: [methodname]`getItemTexts()`, [methodname]`clickItem(String)`, [methodname]`clickItem(int)` +| [classname]`BreadcrumbsTester`: [methodname]`getItemTexts()`, [methodname]`getItemPaths()`, [methodname]`clickItem(String)`, [methodname]`clickItem(int)` | [since:com.vaadin:vaadin@V25.2]#[classname]`MasterDetailLayout`# | [classname]`MasterDetailLayoutTester`: [methodname]`getMaster()`, [methodname]`getDetail()`, [methodname]`getDetailPlaceholder()`, [methodname]`isDetailPlaceholderVisible()` @@ -108,10 +122,34 @@ The following table shows frequently used testers and their key methods: | [classname]`DecimalRangeSliderTester`: [methodname]`setValue(DecimalRangeSliderValue)`, [methodname]`setStart(Double)`, [methodname]`setEnd(Double)`, [methodname]`incrementStart()`, [methodname]`decrementEnd()`, ... | [since:com.vaadin:vaadin@V25.3]#[classname]`Switch`# -| [classname]`SwitchTester`: [methodname]`isOn()`, [methodname]`click()` +| [classname]`SwitchTester`: [methodname]`isOn()`, [methodname]`click()`, [methodname]`switchOn()`, [methodname]`switchOff()` | [since:com.vaadin:vaadin@V25.3]#[classname]`GridPro`# | [classname]`GridProTester`: [methodname]`setValue(row, column, value)`, plus everything [classname]`GridTester` offers + +| [since:com.vaadin:vaadin@V25.3]#[classname]`GridContextMenu`# +| [classname]`GridContextMenuTester`, obtained with [methodname]`test(grid).contextMenu(row)`: [methodname]`open()`, [methodname]`open(row)`, [methodname]`clickItem(String, String...)`, [methodname]`getItemTexts()` + +| [classname]`LoginOverlay` +| [methodname]`openOverlay()`, [methodname]`isOpen()`, [since:com.vaadin:vaadin@V25.3]#[methodname]`findInFooter(Class)`, [methodname]`findInCustomFormArea(Class)`# + +| [since:com.vaadin:vaadin@V25.3]#[classname]`SplitLayout`# +| [classname]`SplitLayoutTester`: [methodname]`dragSplitterTo(double)`, [methodname]`getSplitterPosition()`, [methodname]`getPrimaryComponent()`, [methodname]`getSecondaryComponent()` + +| [since:com.vaadin:vaadin@V25.3]#[classname]`Card`# +| [classname]`CardTester`: [methodname]`getTitleAsText()`, [methodname]`getSubtitleAsText()`, [methodname]`getHeader()`, [methodname]`getMedia()`, [methodname]`getFooterComponents()` + +| [since:com.vaadin:vaadin@V25.3]#[classname]`AvatarGroup`# +| [classname]`AvatarGroupTester`: [methodname]`getVisibleItems()`, [methodname]`getOverflowItems()`, [methodname]`getOverflowAbbreviation()` + +| [since:com.vaadin:vaadin@V25.2]#[classname]`Dashboard`# +| [classname]`DashboardTester`: [methodname]`moveWidget(widget, toIndex)`, [methodname]`resizeWidget(widget, colspan, rowspan)`, [methodname]`removeWidget(widget)` + +| [since:com.vaadin:vaadin@V25.2]#[classname]`Text`# +| [classname]`TextTester`: [methodname]`getText()` + +| [since:com.vaadin:vaadin@V25.2]#[classname]`Markdown`# +| [classname]`MarkdownTester`: [methodname]`getContent()` |=== @@ -150,30 +188,57 @@ Button next = test(splitLayout).findInPrimary(Button.class).single(); === Constraint Enforcement -Testers enforce the same constraints that apply to user input in a browser. If a component has constraints like minimum or maximum values, required fields, or step intervals, the tester rejects values that violate them. This ensures your tests reflect actual user behavior. +Testers enforce the constraints that a browser enforces, and they stop where a browser stops. + +A constraint that keeps the value out of the field makes the tester throw. The maximum length of a text field and an allowed character pattern work this way, because a browser drops the keystrokes that break them: + +[source,java] +---- +TextField code = new TextField("Code"); +code.setMaxLength(5); + +// Throws: a browser stops the typing at five characters +test(code).setValue("123456"); +---- -For example, a [classname]`NumberField` with a minimum of `0` rejects negative values through the tester: +[since:com.vaadin:vaadin@V25.3]#A constraint that only marks the field invalid works differently: a browser commits such a value, and so does the tester.# This covers `min`, `max`, `step`, and a required value on [classname]`NumberField`, [classname]`DatePicker`, [classname]`TimePicker`, and [classname]`DateTimePicker`. Assert the outcome with [methodname]`isValid()`, which runs the component's own default validator and honors an invalid state set from outside, such as the state a [classname]`Binder` sets: [source,java] ---- NumberField amount = new NumberField("Amount"); amount.setMin(0); -amount.setStep(0.01); -// This throws an exception — the value violates the minimum constraint +// Committed, as in a browser, and the field is left invalid test(amount).setValue(-5.0); + +Assertions.assertFalse(test(amount).isValid()); ---- -To test that your application handles invalid input correctly, set values directly through the component's Java API instead of the tester: +To test how the application reacts to a value that no user can produce, set that value through the component's Java API instead of the tester: [source,java] ---- -// Bypass tester constraints to test validation logic -amount.setValue(-5.0); -// Now verify that your application reacts appropriately +// Bypass the tester to exercise the validation logic +code.setValue("123456"); ---- +[role="since:com.vaadin:vaadin@V25.3"] +=== Clearing a Value + +Emptying a field works the same way in every text, number, date, and time tester. [methodname]`clear()` models deleting the contents from the keyboard, which a user can do whether or not the component shows a clear button. [methodname]`clickClearButton()` models clicking that button, and fails when the button is hidden: + +[source,java] +---- +test(name).clear(); // Always available +test(name).clickClearButton(); // Requires a visible clear button +---- + +A field can legitimately end up invalid once emptied, a required field for instance, so [methodname]`clear()` never refuses. + +Selection components use their own wording for the same action: [methodname]`deselectItem()` on [classname]`RadioButtonGroupTester`, [methodname]`clearSelection()` on [classname]`MultiSelectListBoxTester`, and [methodname]`deselectAll()` on [classname]`GridTester`. On [classname]`ComboBoxTester`, [methodname]`selectItem(null)` clears the selection without a clear button. + + [role="since:com.vaadin:vaadin@V25.3"] === Upload Constraints